blume 2.1.0 → 2.1.2

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 (450) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  3. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  4. package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
  5. package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
  6. package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
  7. package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
  8. package/dist/cli/chunk-124fvmzq.js.map +220 -0
  9. package/dist/cli/chunk-150jk7km.js +52 -0
  10. package/dist/cli/chunk-150jk7km.js.map +10 -0
  11. package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
  12. package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
  13. package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
  14. package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
  15. package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
  16. package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
  17. package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
  18. package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
  19. package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
  20. package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
  21. package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
  22. package/dist/cli/chunk-60hz265r.js.map +10 -0
  23. package/dist/cli/chunk-74wfhv79.js +65 -0
  24. package/dist/cli/chunk-74wfhv79.js.map +10 -0
  25. package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
  26. package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
  27. package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
  28. package/dist/cli/chunk-ave1wcbr.js.map +14 -0
  29. package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
  30. package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
  31. package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
  32. package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
  33. package/dist/cli/chunk-ckrcm08h.js +324 -0
  34. package/dist/cli/chunk-ckrcm08h.js.map +14 -0
  35. package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
  36. package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
  37. package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
  38. package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
  39. package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
  40. package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
  41. package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
  42. package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
  43. package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
  44. package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
  45. package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
  46. package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
  47. package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
  48. package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
  49. package/dist/cli/chunk-j1x92mx9.js.map +10 -0
  50. package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
  51. package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
  52. package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
  53. package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
  54. package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
  55. package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
  56. package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
  57. package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
  58. package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
  59. package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
  60. package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
  61. package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
  62. package/dist/cli/chunk-twy7psdz.js.map +10 -0
  63. package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
  64. package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
  65. package/dist/cli/index.js +24 -19
  66. package/dist/cli/index.js.map +3 -3
  67. package/dist/types/ai/ask-context.d.ts +6 -2
  68. package/dist/types/ai/ask.d.ts +24 -5
  69. package/dist/types/ai/component-markdown.d.ts +2 -0
  70. package/dist/types/ai/openapi-components.d.ts +4 -1
  71. package/dist/types/analytics/clics.d.ts +37 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +18 -0
  74. package/dist/types/astro/integration.d.ts +11 -4
  75. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  76. package/dist/types/astro/pages.d.ts +10 -0
  77. package/dist/types/cli/init/scaffold.d.ts +1 -1
  78. package/dist/types/components/content/api-field.d.ts +4 -1
  79. package/dist/types/components/content/api-page.d.ts +6 -1
  80. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  81. package/dist/types/components/openapi/helpers.d.ts +22 -1
  82. package/dist/types/components/openapi/security.d.ts +10 -0
  83. package/dist/types/components/openapi/snippets.d.ts +60 -0
  84. package/dist/types/core/changelog-index.d.ts +8 -0
  85. package/dist/types/core/config-input.d.ts +37 -6
  86. package/dist/types/core/data.d.ts +4 -0
  87. package/dist/types/core/i18n-ui.d.ts +2 -0
  88. package/dist/types/core/links.d.ts +8 -0
  89. package/dist/types/core/powered-by.d.ts +12 -0
  90. package/dist/types/core/probe.d.ts +2 -0
  91. package/dist/types/core/project-graph.d.ts +6 -0
  92. package/dist/types/core/redirect-patterns.d.ts +11 -3
  93. package/dist/types/core/schema.d.ts +228 -3
  94. package/dist/types/core/sources/cache.d.ts +3 -0
  95. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  96. package/dist/types/core/sources/contentful.d.ts +17 -1
  97. package/dist/types/core/sources/lexical.d.ts +5 -0
  98. package/dist/types/core/sources/lower.d.ts +10 -2
  99. package/dist/types/core/sources/normalize.d.ts +20 -1
  100. package/dist/types/core/sources/notion.d.ts +8 -0
  101. package/dist/types/core/sources/remote.d.ts +37 -10
  102. package/dist/types/core/sources/sanity.d.ts +1 -1
  103. package/dist/types/core/types.d.ts +17 -0
  104. package/dist/types/deploy/adapter-output.d.ts +2 -1
  105. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  106. package/dist/types/deploy/adapters/node.d.ts +58 -23
  107. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  108. package/dist/types/deploy/adapters/types.d.ts +3 -2
  109. package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
  110. package/dist/types/deploy/node-headers.d.ts +12 -4
  111. package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
  112. package/dist/types/deploy/platforms/node.d.ts +3 -1
  113. package/dist/types/deploy/platforms/types.d.ts +18 -7
  114. package/dist/types/deploy/platforms/vercel.d.ts +21 -2
  115. package/dist/types/deploy/redirects.d.ts +17 -1
  116. package/dist/types/deploy/sitemap.d.ts +2 -1
  117. package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
  118. package/dist/types/narration/build.d.ts +34 -12
  119. package/dist/types/narration/provider.d.ts +96 -8
  120. package/dist/types/openapi/checks.d.ts +27 -0
  121. package/dist/types/openapi/graphql.d.ts +6 -0
  122. package/dist/types/openapi/model.d.ts +15 -4
  123. package/dist/types/openapi/parse.d.ts +3 -0
  124. package/dist/types/openapi/references.d.ts +16 -0
  125. package/dist/types/openapi/sentence.d.ts +11 -1
  126. package/dist/types/ratelimit/index.d.ts +1 -1
  127. package/dist/types/ratelimit/schema.d.ts +8 -0
  128. package/dist/types/ratelimit/upstash.d.ts +39 -14
  129. package/dist/types/reference/graphql.d.ts +76 -1
  130. package/dist/types/reference/index.d.ts +1 -1
  131. package/dist/types/reference/openapi.d.ts +2 -2
  132. package/dist/types/reference/options.d.ts +37 -1
  133. package/dist/types/reference/schema.d.ts +84 -0
  134. package/dist/types/search/adapters/registry.d.ts +3 -0
  135. package/dist/types/search/adapters/typesense.d.ts +14 -4
  136. package/dist/types/search/build.d.ts +3 -1
  137. package/dist/types/search/orama-index.d.ts +9 -9
  138. package/dist/types/search/sync/algolia.d.ts +5 -2
  139. package/dist/types/search/sync/index.d.ts +3 -1
  140. package/dist/types/search/sync/typesense.d.ts +11 -7
  141. package/dist/types/seo/jsonld.d.ts +11 -6
  142. package/dist/types/sources/contentful.d.ts +16 -0
  143. package/dist/types/sources/github-releases.d.ts +8 -1
  144. package/dist/types/sources/notion.d.ts +8 -0
  145. package/dist/types/sources/payload.d.ts +2 -0
  146. package/dist/types/sources/registry.d.ts +63 -0
  147. package/dist/types/sources/shared.d.ts +3 -0
  148. package/dist/types/sources/strapi.d.ts +2 -0
  149. package/docs/01-quickstart.mdx +4 -2
  150. package/docs/02-deployment.mdx +59 -13
  151. package/docs/03-upgrading.mdx +2 -1
  152. package/docs/04-migrating.mdx +9 -9
  153. package/docs/08-faq.mdx +12 -12
  154. package/docs/advanced/changelog.mdx +30 -2
  155. package/docs/advanced/custom-pages.mdx +1 -1
  156. package/docs/advanced/skills.mdx +1 -1
  157. package/docs/cli/audit.mdx +30 -7
  158. package/docs/cli/doctor.mdx +6 -2
  159. package/docs/cli/evals.mdx +4 -4
  160. package/docs/cli/translate.mdx +1 -0
  161. package/docs/cli/validate.mdx +4 -3
  162. package/docs/configuration/analytics.mdx +19 -1
  163. package/docs/configuration/assistant.mdx +6 -2
  164. package/docs/configuration/customization.mdx +4 -2
  165. package/docs/configuration/export.mdx +5 -3
  166. package/docs/configuration/index.mdx +4 -2
  167. package/docs/configuration/narration.mdx +43 -6
  168. package/docs/configuration/rate-limiting.mdx +23 -2
  169. package/docs/configuration/search.mdx +37 -14
  170. package/docs/content/components.mdx +2 -2
  171. package/docs/content/frontmatter.mdx +5 -3
  172. package/docs/content/i18n.mdx +2 -2
  173. package/docs/content/includes.mdx +1 -1
  174. package/docs/content/navigation.mdx +8 -6
  175. package/docs/content/sources/contentful.mdx +9 -4
  176. package/docs/content/sources/custom.mdx +7 -3
  177. package/docs/content/sources/github-releases.mdx +12 -2
  178. package/docs/content/sources/index.mdx +3 -3
  179. package/docs/content/sources/notion.mdx +17 -2
  180. package/docs/content/sources/obsidian.mdx +3 -1
  181. package/docs/content/sources/payload.mdx +5 -2
  182. package/docs/content/sources/remote-mdx.mdx +6 -0
  183. package/docs/content/sources/sanity.mdx +4 -2
  184. package/docs/content/sources/strapi.mdx +5 -2
  185. package/docs/content/syntax.mdx +2 -2
  186. package/docs/discoverability/agent-discovery.mdx +1 -1
  187. package/docs/discoverability/index.mdx +1 -1
  188. package/docs/discoverability/json-api.mdx +2 -0
  189. package/docs/discoverability/llms-txt.mdx +1 -1
  190. package/docs/discoverability/markdown.mdx +3 -1
  191. package/docs/discoverability/mcp.mdx +6 -0
  192. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  193. package/docs/discoverability/structured-data.mdx +8 -6
  194. package/docs/references/api-pages.mdx +1 -1
  195. package/docs/references/asyncapi.mdx +5 -1
  196. package/docs/references/graphql.mdx +15 -1
  197. package/docs/references/openapi.mdx +64 -8
  198. package/package.json +9 -9
  199. package/skills/blume/SKILL.md +1 -1
  200. package/skills/blume-migrate/SKILL.md +2 -2
  201. package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
  202. package/skills/blume-migrate/references/docusaurus.md +4 -4
  203. package/skills/blume-migrate/references/fumadocs.md +17 -13
  204. package/skills/blume-migrate/references/monorepo.md +5 -5
  205. package/skills/blume-migrate/references/nextra.md +8 -5
  206. package/skills/blume-migrate/references/starlight.md +9 -7
  207. package/src/ai/ask-context.ts +33 -3
  208. package/src/ai/ask-limits.ts +13 -0
  209. package/src/ai/ask.ts +24 -6
  210. package/src/ai/changelog-markdown.ts +11 -6
  211. package/src/ai/component-markdown.ts +1 -1
  212. package/src/ai/cors.ts +12 -3
  213. package/src/ai/mcp/query.ts +14 -6
  214. package/src/ai/openapi-components.ts +28 -4
  215. package/src/ai/relative-links.ts +4 -38
  216. package/src/analytics/clics.ts +58 -0
  217. package/src/analytics/head.ts +5 -0
  218. package/src/analytics/index.ts +2 -0
  219. package/src/analytics/schema.ts +2 -0
  220. package/src/astro/generate.ts +91 -56
  221. package/src/astro/integration.ts +72 -22
  222. package/src/astro/markdown-negotiation.ts +3 -0
  223. package/src/astro/module-types.ts +3 -1
  224. package/src/astro/pages.ts +23 -0
  225. package/src/astro/render-deps.ts +10 -5
  226. package/src/astro/runtime-deps.ts +23 -2
  227. package/src/astro/templates.ts +129 -81
  228. package/src/audit/catalog.ts +19 -3
  229. package/src/audit/checks/links.ts +14 -1
  230. package/src/audit/checks/network.ts +110 -6
  231. package/src/audit/checks/redirects.ts +6 -4
  232. package/src/audit/checks/robots.ts +58 -2
  233. package/src/audit/run.ts +3 -0
  234. package/src/audit/types.ts +2 -0
  235. package/src/audit/url.ts +14 -5
  236. package/src/blume-modules.d.ts +5 -1
  237. package/src/cli/args.ts +51 -0
  238. package/src/cli/build-failure.ts +23 -0
  239. package/src/cli/commands/audit.ts +10 -1
  240. package/src/cli/commands/dev.ts +18 -2
  241. package/src/cli/commands/doctor.ts +45 -12
  242. package/src/cli/commands/eval.ts +36 -4
  243. package/src/cli/commands/init.ts +6 -4
  244. package/src/cli/commands/validate.ts +10 -1
  245. package/src/cli/init/scaffold.ts +9 -6
  246. package/src/cli/prepare.ts +6 -3
  247. package/src/cli/required-secrets.ts +3 -2
  248. package/src/components/content/ApiPlayground.astro +6 -2
  249. package/src/components/content/api-field.ts +4 -1
  250. package/src/components/content/api-page.ts +31 -7
  251. package/src/components/content/mermaid-element.ts +15 -1
  252. package/src/components/content/view-switcher.ts +7 -4
  253. package/src/components/islands/hooks.ts +27 -1
  254. package/src/components/layout/NavTree.astro +49 -5
  255. package/src/components/layout/NavTreeScript.astro +18 -0
  256. package/src/components/layout/PageActions.astro +139 -24
  257. package/src/components/layout/PageLayout.astro +13 -3
  258. package/src/components/layout/RootLayout.astro +27 -14
  259. package/src/components/layout/Search.astro +17 -3
  260. package/src/components/layout/TableOfContents.astro +24 -17
  261. package/src/components/layout/analytics-client.ts +8 -8
  262. package/src/components/layout/head-scripts.ts +6 -1
  263. package/src/components/layout/search/algolia.ts +10 -1
  264. package/src/components/layout/search/analytics.ts +42 -14
  265. package/src/components/layout/search/endpoint.ts +48 -5
  266. package/src/components/layout/search/pagefind.ts +114 -5
  267. package/src/components/layout/search/types.ts +12 -0
  268. package/src/components/layout/search/typesense.ts +18 -6
  269. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  270. package/src/components/openapi/Authorization.astro +10 -0
  271. package/src/components/openapi/GraphqlOperation.astro +5 -0
  272. package/src/components/openapi/Operation.astro +24 -1
  273. package/src/components/openapi/ParametersTable.astro +8 -4
  274. package/src/components/openapi/Playground.astro +7 -1
  275. package/src/components/openapi/RequestBody.astro +19 -2
  276. package/src/components/openapi/Responses.astro +1 -0
  277. package/src/components/openapi/SchemaTable.astro +15 -3
  278. package/src/components/openapi/async-snippets.ts +61 -5
  279. package/src/components/openapi/async.ts +123 -29
  280. package/src/components/openapi/graphql-helpers.ts +35 -12
  281. package/src/components/openapi/helpers.ts +36 -2
  282. package/src/components/openapi/message-model.ts +54 -11
  283. package/src/components/openapi/message.ts +14 -4
  284. package/src/components/openapi/operation-model.ts +16 -7
  285. package/src/components/openapi/playground-client.ts +77 -58
  286. package/src/components/openapi/playground-schema.ts +1 -1
  287. package/src/components/openapi/schema-tree.ts +44 -14
  288. package/src/components/openapi/security.ts +10 -0
  289. package/src/components/openapi/snippets.ts +5 -3
  290. package/src/components/openapi/validate-json.ts +4 -3
  291. package/src/components/powered-by-middleware.ts +33 -0
  292. package/src/core/changelog-index.ts +31 -1
  293. package/src/core/component-diagnostics.ts +53 -0
  294. package/src/core/component-overrides.ts +32 -0
  295. package/src/core/config-input.ts +42 -6
  296. package/src/core/data.ts +2 -0
  297. package/src/core/diagnostics.ts +5 -0
  298. package/src/core/i18n-ui.ts +2 -0
  299. package/src/core/includes.ts +1 -1
  300. package/src/core/links.ts +47 -17
  301. package/src/core/powered-by.ts +12 -0
  302. package/src/core/probe.ts +26 -6
  303. package/src/core/project-graph.ts +59 -18
  304. package/src/core/redirect-patterns.ts +36 -7
  305. package/src/core/schema.ts +26 -6
  306. package/src/core/sources/cache.ts +6 -0
  307. package/src/core/sources/contentful-rich-text.ts +29 -6
  308. package/src/core/sources/contentful.ts +135 -25
  309. package/src/core/sources/filesystem.ts +26 -4
  310. package/src/core/sources/github-releases.ts +12 -1
  311. package/src/core/sources/lexical.ts +15 -10
  312. package/src/core/sources/lower.ts +19 -3
  313. package/src/core/sources/mdx-remote.ts +62 -10
  314. package/src/core/sources/normalize.ts +227 -41
  315. package/src/core/sources/notion.ts +34 -10
  316. package/src/core/sources/obsidian.ts +16 -3
  317. package/src/core/sources/payload.ts +38 -15
  318. package/src/core/sources/remote.ts +72 -15
  319. package/src/core/sources/resolve.ts +11 -1
  320. package/src/core/sources/sanity.ts +43 -25
  321. package/src/core/sources/strapi.ts +2 -2
  322. package/src/core/types.ts +18 -0
  323. package/src/core/ui-packs/ar.ts +1 -0
  324. package/src/core/ui-packs/bg.ts +1 -0
  325. package/src/core/ui-packs/bn.ts +1 -0
  326. package/src/core/ui-packs/ca.ts +1 -0
  327. package/src/core/ui-packs/cs.ts +1 -0
  328. package/src/core/ui-packs/da.ts +1 -0
  329. package/src/core/ui-packs/de.ts +1 -0
  330. package/src/core/ui-packs/el.ts +1 -0
  331. package/src/core/ui-packs/es.ts +1 -0
  332. package/src/core/ui-packs/fa.ts +1 -0
  333. package/src/core/ui-packs/fi.ts +1 -0
  334. package/src/core/ui-packs/fr.ts +1 -0
  335. package/src/core/ui-packs/he.ts +1 -0
  336. package/src/core/ui-packs/hi.ts +1 -0
  337. package/src/core/ui-packs/hr.ts +1 -0
  338. package/src/core/ui-packs/hu.ts +1 -0
  339. package/src/core/ui-packs/id.ts +1 -0
  340. package/src/core/ui-packs/it.ts +1 -0
  341. package/src/core/ui-packs/ja.ts +1 -0
  342. package/src/core/ui-packs/ko.ts +1 -0
  343. package/src/core/ui-packs/nl.ts +1 -0
  344. package/src/core/ui-packs/no.ts +1 -0
  345. package/src/core/ui-packs/pl.ts +1 -0
  346. package/src/core/ui-packs/pt-br.ts +1 -0
  347. package/src/core/ui-packs/pt.ts +1 -0
  348. package/src/core/ui-packs/ro.ts +1 -0
  349. package/src/core/ui-packs/ru.ts +1 -0
  350. package/src/core/ui-packs/sk.ts +1 -0
  351. package/src/core/ui-packs/sr.ts +1 -0
  352. package/src/core/ui-packs/sv.ts +1 -0
  353. package/src/core/ui-packs/th.ts +1 -0
  354. package/src/core/ui-packs/tr.ts +1 -0
  355. package/src/core/ui-packs/uk.ts +1 -0
  356. package/src/core/ui-packs/vi.ts +1 -0
  357. package/src/core/ui-packs/zh-tw.ts +1 -0
  358. package/src/core/ui-packs/zh.ts +1 -0
  359. package/src/deploy/adapter-output.ts +7 -2
  360. package/src/deploy/adapters/cloudflare.ts +7 -5
  361. package/src/deploy/adapters/node.ts +47 -9
  362. package/src/deploy/adapters/types.ts +7 -3
  363. package/src/deploy/artifacts.ts +2 -1
  364. package/src/deploy/cloudflare-negotiation.ts +36 -7
  365. package/src/deploy/headers.ts +15 -1
  366. package/src/deploy/node-headers.ts +32 -20
  367. package/src/deploy/platforms/cloudflare.ts +14 -9
  368. package/src/deploy/platforms/netlify.ts +2 -1
  369. package/src/deploy/platforms/node.ts +12 -2
  370. package/src/deploy/platforms/types.ts +21 -7
  371. package/src/deploy/platforms/vercel.ts +115 -17
  372. package/src/deploy/redirects.ts +66 -6
  373. package/src/deploy/rss.ts +3 -1
  374. package/src/deploy/sitemap.ts +39 -5
  375. package/src/deploy/vercel-negotiation.ts +325 -103
  376. package/src/eval/agents.ts +18 -1
  377. package/src/eval/prompts.ts +6 -3
  378. package/src/eval/report.ts +34 -13
  379. package/src/eval/run.ts +5 -0
  380. package/src/markdown/base-links.ts +15 -14
  381. package/src/markdown/package-commands.ts +9 -8
  382. package/src/markdown/relative-links.ts +3 -2
  383. package/src/narration/build.ts +218 -40
  384. package/src/narration/provider.ts +89 -9
  385. package/src/openapi/checks.ts +273 -0
  386. package/src/openapi/graphql-build.ts +21 -0
  387. package/src/openapi/graphql.ts +11 -0
  388. package/src/openapi/model.ts +15 -3
  389. package/src/openapi/parse.ts +127 -2
  390. package/src/openapi/references.ts +43 -3
  391. package/src/openapi/render-mdx.ts +68 -8
  392. package/src/openapi/sentence.ts +29 -2
  393. package/src/openapi/source.ts +39 -14
  394. package/src/ratelimit/index.ts +1 -1
  395. package/src/ratelimit/runtime.ts +37 -19
  396. package/src/ratelimit/upstash.ts +40 -10
  397. package/src/reference/graphql.ts +15 -1
  398. package/src/reference/index.ts +1 -0
  399. package/src/reference/openapi.ts +2 -2
  400. package/src/reference/options.ts +29 -1
  401. package/src/registry/eject.ts +8 -2
  402. package/src/search/adapters/locale-scope.ts +30 -0
  403. package/src/search/adapters/typesense.ts +14 -4
  404. package/src/search/build.ts +21 -3
  405. package/src/search/orama-index.ts +163 -31
  406. package/src/search/source-pages.ts +29 -0
  407. package/src/search/sync/algolia.ts +99 -9
  408. package/src/search/sync/index.ts +52 -13
  409. package/src/search/sync/typesense.ts +72 -27
  410. package/src/seo/jsonld.ts +38 -20
  411. package/src/sources/contentful.ts +12 -0
  412. package/src/sources/github-releases.ts +7 -1
  413. package/src/sources/notion.ts +7 -0
  414. package/src/sources/shared.ts +3 -0
  415. package/src/theme/color.ts +9 -10
  416. package/src/theme/entry.ts +43 -6
  417. package/src/translate/validate.ts +23 -1
  418. package/dist/cli/chunk-0gz36akr.js.map +0 -12
  419. package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
  420. package/dist/cli/chunk-43zcf4gz.js.map +0 -36
  421. package/dist/cli/chunk-4ejxbzee.js +0 -39
  422. package/dist/cli/chunk-4ejxbzee.js.map +0 -10
  423. package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
  424. package/dist/cli/chunk-713mngdq.js.map +0 -14
  425. package/dist/cli/chunk-9xr4dgee.js.map +0 -10
  426. package/dist/cli/chunk-amrb6hgh.js +0 -145
  427. package/dist/cli/chunk-amrb6hgh.js.map +0 -11
  428. package/dist/cli/chunk-nd1821sk.js.map +0 -10
  429. package/dist/cli/chunk-rekqz4qg.js.map +0 -10
  430. package/dist/cli/chunk-ta27feev.js.map +0 -10
  431. package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
  432. package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
  433. package/dist/cli/chunk-zf748avv.js.map +0 -37
  434. /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
  435. /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
  436. /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
  437. /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
  438. /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
  439. /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
  440. /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
  441. /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
  442. /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
  443. /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
  444. /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
  445. /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
  446. /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
  447. /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
  448. /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
  449. /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
  450. /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
- import { readFile, writeFile } from "node:fs/promises";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
 
4
- import { join } from "pathe";
4
+ import { dirname, join } from "pathe";
5
5
 
6
6
  import { crossOriginDiscoveryPaths } from "../../ai/ai-catalog.ts";
7
7
  import {
@@ -20,7 +20,10 @@ import {
20
20
  SIGNATURES_DIRECTORY_PATH,
21
21
  SIGNATURES_DIRECTORY_TYPE,
22
22
  } from "../../ai/web-bot-auth.ts";
23
+ import { mountBasePath, normalizeBasePath } from "../../core/base-path.ts";
23
24
  import type { BlumeProject } from "../../core/project-graph.ts";
25
+ import { isPatternPath, regexRedirect } from "../../core/redirect-patterns.ts";
26
+ import type { ResolvedConfig } from "../../core/schema.ts";
24
27
  import type { ProjectContext } from "../../core/types.ts";
25
28
  import { VERCEL_ADAPTER_PACKAGE } from "../adapters/vercel.ts";
26
29
  import {
@@ -37,7 +40,9 @@ import {
37
40
  import {
38
41
  injectNegotiationRoutes,
39
42
  injectRedirectRoutes,
43
+ rebaseAdapterRoutes,
40
44
  } from "../vercel-negotiation.ts";
45
+ import type { VercelRoute } from "../vercel-negotiation.ts";
41
46
  import { adapterRoot, toSiteUrl } from "./paths.ts";
42
47
  import type { BuildLog, DeployPlatform, RedirectFile } from "./types.ts";
43
48
 
@@ -56,6 +61,14 @@ export const VERCEL_JSON_FILE: RedirectFile = {
56
61
  const buildOutputDir = (context: ProjectContext): string =>
57
62
  join(adapterRoot(context), ".vercel", "output");
58
63
 
64
+ /** The static files a server build serves, under the base's directory. */
65
+ const staticDir = (context: ProjectContext, base: string): string =>
66
+ join(buildOutputDir(context), "static", base);
67
+
68
+ /** The project's normalized `deployment.base`, `""` for none. */
69
+ const deploymentBase = (project: BlumeProject): string =>
70
+ normalizeBasePath(project.config.deployment.options.base);
71
+
59
72
  /**
60
73
  * Refuse to ship a function bundle that would crash at runtime: a bare import
61
74
  * the adapter's dependency trace silently dropped (see
@@ -99,18 +112,21 @@ export const emitVercelNegotiation = async (
99
112
  log: BuildLog
100
113
  ): Promise<void> => {
101
114
  const { config, context } = project;
102
- const outputDir = buildOutputDir(context);
103
- const configPath = join(outputDir, "config.json");
115
+ const configPath = join(buildOutputDir(context), "config.json");
104
116
  if (!existsSync(configPath)) {
105
117
  return;
106
118
  }
119
+ const base = deploymentBase(project);
107
120
  const routePaths = markdownRoutePaths(project);
121
+ // Keyed by the file's path in `static/`, under the base's directory.
108
122
  const overrides: Record<string, string> = {};
109
123
  if (hasApiCatalog(config)) {
110
- overrides[API_CATALOG_PATH.slice(1)] = API_CATALOG_TYPE;
124
+ overrides[mountBasePath(base, API_CATALOG_PATH).slice(1)] =
125
+ API_CATALOG_TYPE;
111
126
  }
112
127
  if (config.agents.webBotAuth.keys.length > 0) {
113
- overrides[SIGNATURES_DIRECTORY_PATH.slice(1)] = SIGNATURES_DIRECTORY_TYPE;
128
+ overrides[mountBasePath(base, SIGNATURES_DIRECTORY_PATH).slice(1)] =
129
+ SIGNATURES_DIRECTORY_TYPE;
114
130
  }
115
131
  // The homepage rewrite serves `/index.md` from the static layer, so its
116
132
  // `x-markdown-tokens` estimate has to ride the routing config; the runtime
@@ -120,7 +136,7 @@ export const emitVercelNegotiation = async (
120
136
  // The Markdown and JSON 404 routes point at the prerendered twins; only
121
137
  // wire each when the build actually emitted it (a project that owns `/404`
122
138
  // gets none).
123
- const staticDir = join(outputDir, "static");
139
+ const builtDir = staticDir(context, base);
124
140
  const injected = injectNegotiationRoutes(
125
141
  await readFile(configPath, "utf-8"),
126
142
  routePaths,
@@ -128,13 +144,15 @@ export const emitVercelNegotiation = async (
128
144
  overrides,
129
145
  home ? markdownTokenCount(agentMarkdown(home)) : undefined,
130
146
  {
131
- json: existsSync(join(staticDir, "404.json")),
132
- markdown: existsSync(join(staticDir, "404.md")),
147
+ json: existsSync(join(builtDir, "404.json")),
148
+ markdown: existsSync(join(builtDir, "404.md")),
133
149
  },
134
150
  crossOriginDiscoveryPaths(config),
135
151
  // Downloaded content assets are prerendered static files; only a build
136
152
  // that has them needs the SVG sandbox route.
137
- existsSync(join(staticDir, "blume-assets"))
153
+ existsSync(join(builtDir, "blume-assets")),
154
+ base,
155
+ config.poweredBy
138
156
  );
139
157
  if (injected === null) {
140
158
  log.warn(
@@ -175,6 +193,76 @@ export const emitVercelPatternRedirects = async (
175
193
  await writeFile(configPath, injected, "utf-8");
176
194
  };
177
195
 
196
+ type Redirect = ResolvedConfig["redirects"][number];
197
+
198
+ /**
199
+ * The exact redirects as Build Output routes, matched the way the pattern ones
200
+ * are (see `regexRedirect`): the served path, a trailing slash optional.
201
+ */
202
+ const exactRedirectRoutes = (redirects: Redirect[]): VercelRoute[] =>
203
+ redirects
204
+ .filter((redirect) => !isPatternPath(redirect.from))
205
+ .map(({ from, status, to }) => ({
206
+ headers: { Location: encodeURI(to) },
207
+ src: regexRedirect({ parts: [{ kind: "text", text: from }], status, to })
208
+ .source,
209
+ status,
210
+ }));
211
+
212
+ /**
213
+ * Move a server build's static files under `deployment.base`.
214
+ * `@astrojs/vercel` copies them to the root of the Build Output tree's
215
+ * `static/`, while the pages request `<base>/_astro/…` and link to
216
+ * `<base>/…`, so nothing the site asks for would be found. Moved to
217
+ * `static/<base>/`, they're served at the base, as the routes that
218
+ * {@link rebaseVercelRoutes} moves there expect.
219
+ */
220
+ export const moveStaticUnderBase = async (
221
+ context: ProjectContext,
222
+ base: string
223
+ ): Promise<void> => {
224
+ const root = staticDir(context, "");
225
+ if (!base || !existsSync(root)) {
226
+ return;
227
+ }
228
+ // Through a sibling, since the target lies inside the directory it moves.
229
+ const staging = join(buildOutputDir(context), "static-unbased");
230
+ await rename(root, staging);
231
+ const target = staticDir(context, base);
232
+ await mkdir(dirname(target), { recursive: true });
233
+ await rename(staging, target);
234
+ };
235
+
236
+ /**
237
+ * Move the adapter's routing config under `deployment.base` to match the
238
+ * static files (see `rebaseAdapterRoutes`), its exact redirects rebuilt from
239
+ * the configured ones. Resolves to false when the config can't be read: the
240
+ * adapter's routes would then miss every path under the base.
241
+ */
242
+ export const rebaseVercelRoutes = async (
243
+ project: BlumeProject,
244
+ log: BuildLog
245
+ ): Promise<boolean> => {
246
+ const base = deploymentBase(project);
247
+ const configPath = join(buildOutputDir(project.context), "config.json");
248
+ if (!base || !existsSync(configPath)) {
249
+ return true;
250
+ }
251
+ const rebased = rebaseAdapterRoutes(
252
+ await readFile(configPath, "utf-8"),
253
+ base,
254
+ exactRedirectRoutes(platformRedirects(project))
255
+ );
256
+ if (rebased === null) {
257
+ log.error(
258
+ `Could not move the routes in .vercel/output/config.json under the deployment base "${base}", so the site would not be served there.`
259
+ );
260
+ return false;
261
+ }
262
+ await writeFile(configPath, rebased, "utf-8");
263
+ return true;
264
+ };
265
+
178
266
  /**
179
267
  * Vercel. A server build's Build Output tree lands at `.vercel/output` — at
180
268
  * the project root, because the adapter is handed that root up front: its
@@ -182,12 +270,15 @@ export const emitVercelPatternRedirects = async (
182
270
  * hidden runtime silently drops the function's chunks and `node_modules`.
183
271
  * Static assets are served from the tree's `static/` half, so the deploy
184
272
  * artifacts are written there; headers (the discovery files', the sandbox on
185
- * downloaded SVGs) arrive through the routing config rather than a
186
- * `_headers` file, which Vercel never reads.
273
+ * downloaded SVGs, `X-Powered-By`) arrive through the routing config rather
274
+ * than a `_headers` file, which Vercel never reads. The adapter ignores
275
+ * `deployment.base`, so under one the build moves the static files and the
276
+ * routes beneath it.
187
277
  */
188
278
  export const vercelPlatform: DeployPlatform = {
189
279
  astro: {
190
- config: {},
280
+ config: () => ({}),
281
+ configOptions: [],
191
282
  options: () => ({}),
192
283
  package: VERCEL_ADAPTER_PACKAGE,
193
284
  },
@@ -210,12 +301,19 @@ export const vercelPlatform: DeployPlatform = {
210
301
  if (!ok) {
211
302
  return false;
212
303
  }
304
+ // A verify build moves its static files too, where the budget gate reads
305
+ // them (`serverStaticDir`).
306
+ await moveStaticUnderBase(context, deploymentBase(project));
213
307
  // An isolated verify only proves the bundle would ship; the routing
214
308
  // config is a deploy artifact and stays untouched.
215
- if (!isolated) {
216
- await emitVercelNegotiation(project, log);
217
- await emitVercelPatternRedirects(project, log);
309
+ if (isolated) {
310
+ return true;
311
+ }
312
+ if (!(await rebaseVercelRoutes(project, log))) {
313
+ return false;
218
314
  }
315
+ await emitVercelNegotiation(project, log);
316
+ await emitVercelPatternRedirects(project, log);
219
317
  return true;
220
318
  },
221
319
  hiddenRuntime: {
@@ -231,5 +329,5 @@ export const vercelPlatform: DeployPlatform = {
231
329
  redirectFiles: [VERCEL_JSON_FILE],
232
330
  serverClientUnderBase: false,
233
331
  serverOutputDir: buildOutputDir,
234
- serverStaticDir: (context) => join(buildOutputDir(context), "static"),
332
+ serverStaticDir: staticDir,
235
333
  };
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  normalizeBasePath,
3
+ normalizePath,
4
+ stripBasePath,
3
5
  withAuthoredBasePath,
4
6
  withBasePath,
5
7
  withComposedBasePath,
@@ -11,6 +13,7 @@ import {
11
13
  compileRedirects,
12
14
  exactFirst,
13
15
  expandRedirect,
16
+ isPatternPath,
14
17
  underscoreRedirect,
15
18
  vercelRedirect,
16
19
  } from "../core/redirect-patterns.ts";
@@ -109,9 +112,60 @@ export const applyBaseToPlatformRedirects = (
109
112
  : redirects;
110
113
  };
111
114
 
115
+ /**
116
+ * The raw-Markdown copies Blume serves beside every page (`/guide.md`,
117
+ * `/guide.mdx`; see `rawMarkdownEndpointTemplate`).
118
+ */
119
+ const MIRROR_EXTENSIONS = [".md", ".mdx"] as const;
120
+
121
+ /**
122
+ * Where a page's Markdown copy is served, from the page's served path and the
123
+ * deployment base that path carries: the endpoints name the root `index`.
124
+ */
125
+ const mirrorPath = (path: string, base: string, extension: string): string => {
126
+ const route = stripBasePath(base, path);
127
+ return `${base}${route === "/" ? "/index" : route}${extension}`;
128
+ };
129
+
130
+ /**
131
+ * Based `redirects`, each exact one that moves a page followed by the same
132
+ * redirect for the page's Markdown copies: from a path no page is served at
133
+ * to one a page is, `/old.md` goes to `/new.md` (and `.mdx` alike), where it
134
+ * would otherwise 404 for an agent still reading the old URL. A pattern gets
135
+ * none; one that carries its capture over (`/beta/:slug*` → `/v2/:slug*`)
136
+ * already moves `/beta/guide.md` along. `pages` are the served page routes
137
+ * (carrying `basePath`, not `deployment.base`), and `bases` the deployment
138
+ * base each end carries: Astro's config leaves it off `from` (see
139
+ * {@link applyBaseToAstroRedirects}).
140
+ */
141
+ export const withMirrorRedirects = (
142
+ redirects: Redirect[],
143
+ pages: RouteSet,
144
+ bases: { from: string; to: string }
145
+ ): Redirect[] =>
146
+ redirects.flatMap((redirect) => {
147
+ const from = normalizePath(redirect.from);
148
+ const to = normalizePath(redirect.to);
149
+ const movesPage =
150
+ !isPatternPath(redirect.from) &&
151
+ servesRoute(pages, stripBasePath(bases.to, to)) &&
152
+ !servesRoute(pages, stripBasePath(bases.from, from));
153
+ return movesPage
154
+ ? [
155
+ redirect,
156
+ ...MIRROR_EXTENSIONS.map((extension) => ({
157
+ from: mirrorPath(from, bases.from, extension),
158
+ status: redirect.status,
159
+ to: mirrorPath(to, bases.to, extension),
160
+ })),
161
+ ]
162
+ : [redirect];
163
+ });
164
+
112
165
  /**
113
166
  * The configured redirects as the host platform matches them, via
114
- * {@link applyBaseToPlatformRedirects}, exact paths ahead of patterns: hosts
167
+ * {@link applyBaseToPlatformRedirects} plus the Markdown copies of each moved
168
+ * page ({@link withMirrorRedirects}), exact paths ahead of patterns: hosts
115
169
  * try rules in order, and an exact redirect wins over a pattern that also
116
170
  * covers its path. The one basing every consumer must share: the emitted
117
171
  * redirect files and the server wrappers all compare these paths against
@@ -122,12 +176,18 @@ export const platformRedirects = (project: {
122
176
  manifest: Pick<BlumeProject["manifest"], "routes">;
123
177
  }): Redirect[] => {
124
178
  const { config } = project;
179
+ const base = normalizeBasePath(config.deployment.options.base);
180
+ const pages = routeSetFor(project.manifest.routes);
125
181
  return exactFirst(
126
- applyBaseToPlatformRedirects(
127
- config.redirects,
128
- config.basePath,
129
- config.deployment.options.base ?? "",
130
- routeSetFor(project.manifest.routes)
182
+ withMirrorRedirects(
183
+ applyBaseToPlatformRedirects(
184
+ config.redirects,
185
+ config.basePath,
186
+ base,
187
+ pages
188
+ ),
189
+ pages,
190
+ { from: base, to: base }
131
191
  )
132
192
  );
133
193
  };
package/src/deploy/rss.ts CHANGED
@@ -78,7 +78,9 @@ export const buildRssFeeds = (project: BlumeProject): RssFeed[] => {
78
78
  const items: RssItem[] = pages
79
79
  .map((page) => ({
80
80
  date: pageDate(page),
81
- description: page.description,
81
+ // A page with only a meta description (a GitHub release, whose
82
+ // summary goes in `seo.description`) still describes its item.
83
+ description: page.description ?? page.meta.seo.description,
82
84
  // Encode like the sitemap does: a route with spaces or non-ASCII
83
85
  // must still yield a valid <link>/<guid> URL after XML decoding.
84
86
  link: encodeURI(`${base}${mountBasePath(deployBase, page.route)}`),
@@ -6,7 +6,11 @@ import {
6
6
  discoverPagesSync,
7
7
  hasGeneratedChangelog,
8
8
  } from "../astro/pages.ts";
9
- import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
9
+ import {
10
+ mountBasePath,
11
+ normalizeBasePath,
12
+ normalizePath,
13
+ } from "../core/base-path.ts";
10
14
  import type { BlumeProject } from "../core/project-graph.ts";
11
15
  import { siteRoot } from "../core/site-url.ts";
12
16
 
@@ -30,6 +34,30 @@ const lastmodTag = (value: string | undefined): string => {
30
34
  : `<lastmod>${date.toISOString().slice(0, 10)}</lastmod>`;
31
35
  };
32
36
 
37
+ /**
38
+ * Whether a page's own `seo.canonical` names another URL. The sitemap lists
39
+ * canonical URLs only, so such a page stays out — `blume audit` reports a
40
+ * listed one as NON_CANONICAL_IN_SITEMAP and expects it unlisted. Compared by
41
+ * decoded path, as the audit does: `served` is the page's path with the
42
+ * deployment base mounted, which the canonical carries too.
43
+ */
44
+ const canonicalElsewhere = (
45
+ canonical: string | undefined,
46
+ served: string
47
+ ): boolean => {
48
+ if (!canonical) {
49
+ return false;
50
+ }
51
+ const { pathname } = new URL(canonical);
52
+ let path = pathname;
53
+ try {
54
+ path = decodeURI(pathname);
55
+ } catch {
56
+ // A malformed escape stays as written; it can't match the served path.
57
+ }
58
+ return normalizePath(path) !== normalizePath(served);
59
+ };
60
+
33
61
  /** One emitted sitemap artifact: its dist-root filename and XML body. */
34
62
  export interface SitemapFile {
35
63
  name: string;
@@ -62,7 +90,8 @@ ${urls.join("\n")}
62
90
  * can't see: custom `.astro` pages (most importantly a custom landing `/`) and
63
91
  * the generated `/changelog` index. Returns null when the sitemap is disabled
64
92
  * or no `site` is configured (absolute URLs are required for a valid sitemap).
65
- * Drafts, hidden, and `noindex` pages are excluded.
93
+ * Drafts, hidden, and `noindex` pages are excluded, and so is a page whose
94
+ * `seo.canonical` points elsewhere.
66
95
  *
67
96
  * Sites within the per-file URL cap get the single classic `sitemap.xml`;
68
97
  * larger sites get `sitemap.xml` as a sitemap index over numbered
@@ -87,7 +116,9 @@ export const buildSitemapFiles = (
87
116
  // or when their canonical points at a still-existing latest equivalent —
88
117
  // listing a URL whose canonical says "index the other page" invites the
89
118
  // noindexed-page-in-sitemap incoherence Docusaurus is known for. A page
90
- // that exists only in an archived version stays listed (self-canonical).
119
+ // that exists only in an archived version stays listed (self-canonical),
120
+ // and a page's own `seo.canonical` wins over the version's default, as it
121
+ // does in the page head (see `canonicalElsewhere`).
91
122
  const { versions } = project.config;
92
123
  const archivedById = new Map(
93
124
  (versions?.archived ?? []).map((version) => [version.id, version])
@@ -109,6 +140,7 @@ export const buildSitemapFiles = (
109
140
  return Boolean(
110
141
  archived?.noindex ||
111
142
  (archived?.canonical === "latest" &&
143
+ !page.meta.seo.canonical &&
112
144
  currentKeys?.has(`${page.versionKey}\u0000${page.locale}`))
113
145
  );
114
146
  };
@@ -128,16 +160,18 @@ export const buildSitemapFiles = (
128
160
  urls.push(` <url><loc>${loc}</loc>${lastmodTag(lastModified)}</url>`);
129
161
  };
130
162
  for (const page of project.graph.pages) {
163
+ const served = mountBasePath(deployBase, page.route);
131
164
  if (
132
165
  page.meta.draft ||
133
166
  page.meta.sidebar.hidden ||
134
167
  page.meta.seo.noindex ||
135
168
  ERROR_ROUTES.has(page.route) ||
136
- archivedExcluded(page)
169
+ archivedExcluded(page) ||
170
+ canonicalElsewhere(page.meta.seo.canonical, served)
137
171
  ) {
138
172
  continue;
139
173
  }
140
- pushUrl(mountBasePath(deployBase, page.route), page.lastModified);
174
+ pushUrl(served, page.lastModified);
141
175
  }
142
176
  // Custom `.astro` pages and the generated changelog index mount outside
143
177
  // `basePath` (they're injected at their pattern — see `blumeIntegration`), so