blume 1.7.2 → 2.0.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 (677) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +227 -0
  3. package/README.md +34 -21
  4. package/dist/cli/chunk-11j0384y.js +148 -0
  5. package/dist/cli/chunk-11j0384y.js.map +10 -0
  6. package/dist/cli/{chunk-xhtpx3ff.js → chunk-1w8dp3qb.js} +17 -16
  7. package/dist/cli/{chunk-xhtpx3ff.js.map → chunk-1w8dp3qb.js.map} +3 -3
  8. package/dist/cli/chunk-2q1dwty4.js +75 -0
  9. package/dist/cli/chunk-2q1dwty4.js.map +11 -0
  10. package/dist/cli/chunk-41za066z.js +122 -0
  11. package/dist/cli/chunk-41za066z.js.map +11 -0
  12. package/dist/cli/chunk-5a2z0198.js +133 -0
  13. package/dist/cli/chunk-5a2z0198.js.map +10 -0
  14. package/dist/cli/{chunk-9he6crym.js → chunk-6k8vp3ta.js} +25 -13
  15. package/dist/cli/{chunk-9he6crym.js.map → chunk-6k8vp3ta.js.map} +4 -4
  16. package/dist/cli/{chunk-mt76t7dj.js → chunk-79njf86q.js} +133 -54
  17. package/dist/cli/chunk-79njf86q.js.map +11 -0
  18. package/dist/cli/{chunk-3w7b2vcx.js → chunk-7ez8ny0t.js} +2 -2
  19. package/dist/cli/{chunk-688e0dde.js → chunk-88cpgt6h.js} +1 -1
  20. package/dist/cli/chunk-a9kptbw5.js +361 -0
  21. package/dist/cli/chunk-a9kptbw5.js.map +14 -0
  22. package/dist/cli/chunk-abh8yjkn.js +31 -0
  23. package/dist/cli/chunk-abh8yjkn.js.map +10 -0
  24. package/dist/cli/chunk-b5aj94ah.js +91 -0
  25. package/dist/cli/chunk-b5aj94ah.js.map +10 -0
  26. package/dist/cli/{chunk-hs3gbh8p.js → chunk-bctazmbk.js} +9 -5
  27. package/dist/cli/chunk-bctazmbk.js.map +10 -0
  28. package/dist/cli/chunk-beat36xx.js +279 -0
  29. package/dist/cli/chunk-beat36xx.js.map +10 -0
  30. package/dist/cli/chunk-bnbmcwfb.js +145 -0
  31. package/dist/cli/chunk-bnbmcwfb.js.map +11 -0
  32. package/dist/cli/{chunk-9bkjd11x.js → chunk-bw22s759.js} +15 -5
  33. package/dist/cli/{chunk-9bkjd11x.js.map → chunk-bw22s759.js.map} +4 -4
  34. package/dist/cli/chunk-by2290sx.js +39 -0
  35. package/dist/cli/chunk-by2290sx.js.map +10 -0
  36. package/dist/cli/chunk-d1tadaw7.js +79 -0
  37. package/dist/cli/chunk-d1tadaw7.js.map +10 -0
  38. package/dist/cli/{chunk-t3tj0dgr.js → chunk-d80hr03s.js} +24 -19
  39. package/dist/cli/chunk-d80hr03s.js.map +15 -0
  40. package/dist/cli/chunk-ernrthtr.js +97 -0
  41. package/dist/cli/chunk-ernrthtr.js.map +10 -0
  42. package/dist/cli/chunk-f2972sbt.js +374 -0
  43. package/dist/cli/chunk-f2972sbt.js.map +10 -0
  44. package/dist/cli/{chunk-exeeb35e.js → chunk-f7t03s3g.js} +2 -2
  45. package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
  46. package/dist/cli/chunk-fa25z98p.js.map +11 -0
  47. package/dist/cli/{chunk-12dxjqk7.js → chunk-fh5hj5jt.js} +44 -21
  48. package/dist/cli/chunk-fh5hj5jt.js.map +10 -0
  49. package/dist/cli/{chunk-8cd8tj54.js → chunk-j8mw0za6.js} +86 -57
  50. package/dist/cli/chunk-j8mw0za6.js.map +35 -0
  51. package/dist/cli/chunk-jts8mvcz.js +106 -0
  52. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  53. package/dist/cli/{chunk-ejjx8znq.js → chunk-mnqj32sj.js} +505 -536
  54. package/dist/cli/chunk-mnqj32sj.js.map +12 -0
  55. package/dist/cli/{chunk-196vjxp9.js → chunk-mwt1k8n7.js} +100 -372
  56. package/dist/cli/chunk-mwt1k8n7.js.map +10 -0
  57. package/dist/cli/chunk-nk3ts2xk.js +51 -0
  58. package/dist/cli/chunk-nk3ts2xk.js.map +10 -0
  59. package/dist/cli/{chunk-n9sra6sy.js → chunk-pat2zzwc.js} +10 -14
  60. package/dist/cli/{chunk-n9sra6sy.js.map → chunk-pat2zzwc.js.map} +2 -2
  61. package/dist/cli/chunk-pnnvybbk.js +176 -0
  62. package/dist/cli/chunk-pnnvybbk.js.map +11 -0
  63. package/dist/cli/{chunk-cvky9gb2.js → chunk-sqn5t4q0.js} +81 -87
  64. package/dist/cli/chunk-sqn5t4q0.js.map +10 -0
  65. package/dist/cli/{chunk-eevwt1sc.js → chunk-tzne8qfq.js} +15 -15
  66. package/dist/cli/{chunk-eevwt1sc.js.map → chunk-tzne8qfq.js.map} +1 -1
  67. package/dist/cli/chunk-xaz13gwg.js +12449 -0
  68. package/dist/cli/chunk-xaz13gwg.js.map +182 -0
  69. package/dist/cli/chunk-y3e45rc8.js +102 -0
  70. package/dist/cli/chunk-y3e45rc8.js.map +10 -0
  71. package/dist/cli/chunk-z01ze5c1.js +261 -0
  72. package/dist/cli/chunk-z01ze5c1.js.map +10 -0
  73. package/dist/cli/{chunk-5n7t497w.js → chunk-z1f5arsg.js} +247 -755
  74. package/dist/cli/chunk-z1f5arsg.js.map +36 -0
  75. package/dist/cli/{chunk-hdm2dkd2.js → chunk-zg2gtj10.js} +1086 -2596
  76. package/dist/cli/chunk-zg2gtj10.js.map +35 -0
  77. package/dist/cli/{chunk-ppfvdcd4.js → chunk-zxccj738.js} +1 -1
  78. package/dist/cli/index.js +214 -57
  79. package/dist/cli/index.js.map +8 -7
  80. package/dist/types/ai/agent-readability.d.ts +52 -0
  81. package/dist/types/ai/ai-catalog.d.ts +42 -0
  82. package/dist/types/ai/api/paths.d.ts +17 -0
  83. package/dist/types/ai/api-catalog.d.ts +18 -0
  84. package/dist/types/ai/ask.d.ts +368 -0
  85. package/dist/types/ai/changelog-markdown.d.ts +2 -0
  86. package/dist/types/ai/component-markdown.d.ts +2 -2
  87. package/dist/types/ai/index.d.ts +24 -0
  88. package/dist/types/ai/link-headers.d.ts +24 -0
  89. package/dist/types/ai/llms.d.ts +25 -0
  90. package/dist/types/ai/markdown.d.ts +45 -0
  91. package/dist/types/ai/mcp/discovery.d.ts +68 -0
  92. package/dist/types/ai/mcp/tools.d.ts +16 -0
  93. package/dist/types/ai/openapi-components.d.ts +43 -0
  94. package/dist/types/ai/relative-links.d.ts +26 -0
  95. package/dist/types/ai/serializers.d.ts +15 -0
  96. package/dist/types/ai/skills.d.ts +42 -0
  97. package/dist/types/ai/tar.d.ts +25 -0
  98. package/dist/types/ai/visibility.d.ts +17 -0
  99. package/dist/types/ai/web-bot-auth.d.ts +16 -0
  100. package/dist/types/analytics/adobe.d.ts +35 -0
  101. package/dist/types/analytics/amplitude.d.ts +50 -0
  102. package/dist/types/analytics/clarity.d.ts +31 -0
  103. package/dist/types/analytics/clearbit.d.ts +29 -0
  104. package/dist/types/analytics/cloudflare.d.ts +41 -0
  105. package/dist/types/analytics/fathom.d.ts +41 -0
  106. package/dist/types/analytics/google-analytics.d.ts +44 -0
  107. package/dist/types/analytics/google-tag-manager.d.ts +40 -0
  108. package/dist/types/analytics/head.d.ts +30 -0
  109. package/dist/types/analytics/heap.d.ts +40 -0
  110. package/dist/types/analytics/hightouch.d.ts +44 -0
  111. package/dist/types/analytics/hotjar.d.ts +33 -0
  112. package/dist/types/analytics/index.d.ts +61 -0
  113. package/dist/types/analytics/inline.d.ts +17 -0
  114. package/dist/types/analytics/logrocket.d.ts +44 -0
  115. package/dist/types/analytics/mixpanel.d.ts +67 -0
  116. package/dist/types/analytics/pirsch.d.ts +42 -0
  117. package/dist/types/analytics/plausible.d.ts +56 -0
  118. package/dist/types/analytics/posthog.d.ts +45 -0
  119. package/dist/types/analytics/schema.d.ts +320 -0
  120. package/dist/types/analytics/script.d.ts +46 -0
  121. package/dist/types/analytics/segment.d.ts +50 -0
  122. package/dist/types/analytics/vercel.d.ts +48 -0
  123. package/dist/types/astro/integration.d.ts +76 -0
  124. package/dist/types/astro/markdown-negotiation.d.ts +23 -0
  125. package/dist/types/astro/module-types.d.ts +14 -0
  126. package/dist/types/astro/pages.d.ts +44 -0
  127. package/dist/types/cli/env.d.ts +12 -0
  128. package/dist/types/cli/init/scaffold.d.ts +154 -0
  129. package/dist/types/components/layout/nav-utils.d.ts +11 -0
  130. package/dist/types/core/adapter.d.ts +47 -0
  131. package/dist/types/core/api-name.d.ts +7 -0
  132. package/dist/types/core/changelog-index.d.ts +13 -0
  133. package/dist/types/core/config-input.d.ts +209 -505
  134. package/dist/types/core/config.d.ts +60 -34
  135. package/dist/types/core/content-assets.d.ts +76 -0
  136. package/dist/types/core/custom-pages.d.ts +33 -0
  137. package/dist/types/core/data.d.ts +28 -11
  138. package/dist/types/core/define-components.d.ts +12 -9
  139. package/dist/types/core/deployment-env.d.ts +6 -11
  140. package/dist/types/core/frontmatter.d.ts +10 -0
  141. package/dist/types/core/graph.d.ts +18 -0
  142. package/dist/types/core/heading-markers.d.ts +54 -0
  143. package/dist/types/core/i18n-ui.d.ts +6 -2
  144. package/dist/types/core/i18n.d.ts +87 -0
  145. package/dist/types/core/includes.d.ts +138 -0
  146. package/dist/types/core/last-modified.d.ts +47 -0
  147. package/dist/types/core/links.d.ts +95 -0
  148. package/dist/types/core/locale-links.d.ts +60 -0
  149. package/dist/types/core/manifest.d.ts +17 -0
  150. package/dist/types/core/meta.d.ts +38 -0
  151. package/dist/types/core/nav-diagnostics.d.ts +26 -0
  152. package/dist/types/core/navigation.d.ts +6 -0
  153. package/dist/types/core/node-require.d.ts +19 -0
  154. package/dist/types/core/package-json.d.ts +13 -0
  155. package/dist/types/core/probe.d.ts +42 -0
  156. package/dist/types/core/project-graph.d.ts +51 -0
  157. package/dist/types/core/project.d.ts +2 -0
  158. package/dist/types/core/safe-href.d.ts +2 -0
  159. package/dist/types/core/safe-links.d.ts +26 -0
  160. package/dist/types/core/schema.d.ts +2291 -441
  161. package/dist/types/core/site-url.d.ts +16 -0
  162. package/dist/types/core/sources/assets.d.ts +36 -0
  163. package/dist/types/core/sources/cache.d.ts +37 -0
  164. package/dist/types/core/sources/collection.d.ts +28 -0
  165. package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
  166. package/dist/types/core/sources/contentful.d.ts +40 -0
  167. package/dist/types/core/sources/filesystem.d.ts +24 -0
  168. package/dist/types/core/sources/github-releases.d.ts +31 -0
  169. package/dist/types/core/sources/json.d.ts +30 -0
  170. package/dist/types/core/sources/lexical.d.ts +19 -0
  171. package/dist/types/core/sources/lower.d.ts +75 -0
  172. package/dist/types/core/sources/mdx-remote.d.ts +28 -0
  173. package/dist/types/core/sources/normalize.d.ts +147 -0
  174. package/dist/types/core/sources/notion.d.ts +131 -0
  175. package/dist/types/core/sources/obsidian.d.ts +46 -0
  176. package/dist/types/core/sources/payload.d.ts +39 -0
  177. package/dist/types/core/sources/portable-text.d.ts +42 -0
  178. package/dist/types/core/sources/read.d.ts +23 -0
  179. package/dist/types/core/sources/remote.d.ts +74 -0
  180. package/dist/types/core/sources/resolve.d.ts +19 -0
  181. package/dist/types/core/sources/sanity.d.ts +43 -0
  182. package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
  183. package/dist/types/core/sources/strapi.d.ts +33 -0
  184. package/dist/types/core/sources/types.d.ts +7 -0
  185. package/dist/types/core/sources/watch.d.ts +45 -0
  186. package/dist/types/core/text-width.d.ts +11 -0
  187. package/dist/types/core/types.d.ts +20 -1
  188. package/dist/types/core/unrecognized-keys.d.ts +7 -0
  189. package/dist/types/core/versions.d.ts +72 -0
  190. package/dist/types/core/yaml.d.ts +9 -0
  191. package/dist/types/deploy/adapter-output.d.ts +45 -0
  192. package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
  193. package/dist/types/deploy/adapters/index.d.ts +29 -0
  194. package/dist/types/deploy/adapters/netlify.d.ts +37 -0
  195. package/dist/types/deploy/adapters/node.d.ts +37 -0
  196. package/dist/types/deploy/adapters/registry.d.ts +133 -0
  197. package/dist/types/deploy/adapters/types.d.ts +71 -0
  198. package/dist/types/deploy/adapters/vercel.d.ts +38 -0
  199. package/dist/types/deploy/artifacts.d.ts +65 -0
  200. package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
  201. package/dist/types/deploy/function-bundle.d.ts +80 -0
  202. package/dist/types/deploy/headers.d.ts +50 -0
  203. package/dist/types/deploy/node-headers.d.ts +42 -0
  204. package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
  205. package/dist/types/deploy/platforms/index.d.ts +18 -0
  206. package/dist/types/deploy/platforms/netlify.d.ts +13 -0
  207. package/dist/types/deploy/platforms/node.d.ts +11 -0
  208. package/dist/types/deploy/platforms/paths.d.ts +27 -0
  209. package/dist/types/deploy/platforms/static.d.ts +10 -0
  210. package/dist/types/deploy/platforms/types.d.ts +104 -0
  211. package/dist/types/deploy/platforms/vercel.d.ts +32 -0
  212. package/dist/types/deploy/redirects.d.ts +53 -0
  213. package/dist/types/deploy/robots.d.ts +8 -0
  214. package/dist/types/deploy/rss.d.ts +31 -0
  215. package/dist/types/deploy/sitemap.d.ts +21 -0
  216. package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
  217. package/dist/types/markdown/code-title.d.ts +32 -0
  218. package/dist/types/markdown/fence-meta.d.ts +23 -0
  219. package/dist/types/markdown/themes.d.ts +3 -3
  220. package/dist/types/openapi/asyncapi.d.ts +129 -0
  221. package/dist/types/openapi/graphql-build.d.ts +8 -0
  222. package/dist/types/openapi/graphql.d.ts +122 -0
  223. package/dist/types/openapi/model.d.ts +158 -0
  224. package/dist/types/openapi/parse.d.ts +57 -0
  225. package/dist/types/openapi/references.d.ts +37 -25
  226. package/dist/types/openapi/render-mdx.d.ts +33 -0
  227. package/dist/types/openapi/sentence.d.ts +7 -0
  228. package/dist/types/openapi/signature.d.ts +10 -0
  229. package/dist/types/openapi/source.d.ts +22 -0
  230. package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
  231. package/dist/types/reference/asyncapi.d.ts +166 -0
  232. package/dist/types/reference/graphql.d.ts +181 -0
  233. package/dist/types/reference/index.d.ts +32 -0
  234. package/dist/types/reference/openapi.d.ts +165 -0
  235. package/dist/types/reference/options.d.ts +157 -0
  236. package/dist/types/reference/scalar.d.ts +136 -0
  237. package/dist/types/reference/schema.d.ts +630 -0
  238. package/dist/types/search/adapters/algolia.d.ts +32 -0
  239. package/dist/types/search/adapters/flexsearch.d.ts +12 -0
  240. package/dist/types/search/adapters/index.d.ts +31 -0
  241. package/dist/types/search/adapters/mixedbread.d.ts +22 -0
  242. package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
  243. package/dist/types/search/adapters/orama.d.ts +13 -0
  244. package/dist/types/search/adapters/pagefind.d.ts +12 -0
  245. package/dist/types/search/adapters/registry.d.ts +228 -0
  246. package/dist/types/search/adapters/types.d.ts +34 -0
  247. package/dist/types/search/adapters/typesense.d.ts +42 -0
  248. package/dist/types/search/build.d.ts +23 -0
  249. package/dist/types/search/documents.d.ts +90 -0
  250. package/dist/types/search/facets.d.ts +3 -0
  251. package/dist/types/search/sync/algolia.d.ts +14 -0
  252. package/dist/types/search/sync/index.d.ts +14 -0
  253. package/dist/types/search/sync/orama-cloud.d.ts +10 -0
  254. package/dist/types/search/sync/typesense.d.ts +14 -0
  255. package/dist/types/sources/contentful.d.ts +68 -0
  256. package/dist/types/sources/custom.d.ts +20 -0
  257. package/dist/types/sources/filesystem.d.ts +42 -0
  258. package/dist/types/sources/github-releases.d.ts +46 -0
  259. package/dist/types/sources/index.d.ts +48 -0
  260. package/dist/types/sources/mdx-remote.d.ts +63 -0
  261. package/dist/types/sources/notion.d.ts +65 -0
  262. package/dist/types/sources/obsidian.d.ts +34 -0
  263. package/dist/types/sources/payload.d.ts +68 -0
  264. package/dist/types/sources/registry.d.ts +1414 -0
  265. package/dist/types/sources/sanity.d.ts +69 -0
  266. package/dist/types/sources/shared.d.ts +46 -0
  267. package/dist/types/sources/strapi.d.ts +66 -0
  268. package/dist/types/theme/icon-kind.d.ts +11 -0
  269. package/dist/types/theme/icons.d.ts +20 -0
  270. package/docs/01-quickstart.mdx +18 -18
  271. package/docs/02-deployment.mdx +50 -26
  272. package/docs/03-upgrading.mdx +351 -0
  273. package/docs/04-migrating.mdx +58 -0
  274. package/docs/08-faq.mdx +3 -3
  275. package/docs/advanced/blog.mdx +13 -6
  276. package/docs/advanced/changelog.mdx +23 -35
  277. package/docs/advanced/custom-pages.mdx +20 -8
  278. package/docs/advanced/meta.ts +1 -8
  279. package/docs/advanced/skills.mdx +8 -0
  280. package/docs/cli/audit.mdx +646 -0
  281. package/docs/cli/doctor.mdx +29 -0
  282. package/docs/{reference/eval.mdx → cli/evals.mdx} +9 -8
  283. package/docs/cli/index.mdx +105 -0
  284. package/docs/cli/meta.ts +7 -0
  285. package/docs/{reference → cli}/translate.mdx +1 -1
  286. package/docs/cli/validate.mdx +41 -0
  287. package/docs/cli/version.mdx +40 -0
  288. package/docs/configuration/analytics.mdx +350 -59
  289. package/docs/configuration/ask-ai.mdx +176 -61
  290. package/docs/configuration/customization.mdx +15 -9
  291. package/docs/configuration/index.mdx +52 -31
  292. package/docs/configuration/search.mdx +75 -54
  293. package/docs/configuration/theming.mdx +24 -15
  294. package/docs/content/components.mdx +21 -5
  295. package/docs/{reference → content}/frontmatter.mdx +37 -1
  296. package/docs/content/i18n.mdx +8 -6
  297. package/docs/content/includes.mdx +2 -4
  298. package/docs/content/index.mdx +1 -1
  299. package/docs/content/islands.mdx +10 -5
  300. package/docs/content/meta.mdx +1 -1
  301. package/docs/content/meta.ts +1 -0
  302. package/docs/content/navigation.mdx +12 -7
  303. package/docs/content/sources.mdx +145 -50
  304. package/docs/content/syntax.mdx +26 -12
  305. package/docs/content/versioning.mdx +3 -3
  306. package/docs/discoverability/agent-discovery.mdx +91 -10
  307. package/docs/discoverability/index.mdx +5 -5
  308. package/docs/discoverability/json-api.mdx +4 -4
  309. package/docs/discoverability/llms-txt.mdx +6 -6
  310. package/docs/discoverability/markdown.mdx +4 -4
  311. package/docs/discoverability/mcp.mdx +11 -11
  312. package/docs/discoverability/sitemap-and-robots.mdx +3 -3
  313. package/docs/index.mdx +2 -2
  314. package/docs/references/asyncapi.mdx +59 -0
  315. package/docs/{advanced → references}/graphql.mdx +45 -32
  316. package/docs/{reference → references}/meta.ts +2 -2
  317. package/docs/references/openapi.mdx +171 -0
  318. package/docs/references/scalar.mdx +64 -0
  319. package/package.json +42 -8
  320. package/skills/blume/SKILL.md +21 -8
  321. package/skills/blume-migrate/SKILL.md +22 -21
  322. package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
  323. package/skills/blume-migrate/references/docusaurus.md +5 -4
  324. package/skills/blume-migrate/references/fumadocs.md +4 -4
  325. package/skills/blume-migrate/references/mintlify.md +23 -8
  326. package/skills/blume-migrate/references/monorepo.md +6 -6
  327. package/skills/blume-migrate/references/starlight.md +4 -4
  328. package/src/ai/agent-readability.ts +26 -22
  329. package/src/ai/ai-catalog.ts +259 -0
  330. package/src/ai/api/handlers.ts +46 -11
  331. package/src/ai/api-catalog.ts +8 -8
  332. package/src/ai/ask-data.ts +1 -1
  333. package/src/ai/ask.ts +624 -100
  334. package/src/ai/changelog-markdown.ts +91 -0
  335. package/src/ai/component-markdown.ts +328 -12
  336. package/src/ai/index.ts +45 -0
  337. package/src/ai/link-headers.ts +19 -6
  338. package/src/ai/llms.ts +26 -15
  339. package/src/ai/markdown.ts +35 -5
  340. package/src/ai/mcp/data.ts +9 -5
  341. package/src/ai/mcp/discovery.ts +1 -1
  342. package/src/ai/openapi-components.ts +4 -1
  343. package/src/ai/relative-links.ts +170 -0
  344. package/src/ai/serializers.ts +2 -2
  345. package/src/ai/skills.ts +1 -1
  346. package/src/ai/web-bot-auth.ts +2 -2
  347. package/src/analytics/adobe.ts +46 -0
  348. package/src/analytics/amplitude.ts +79 -0
  349. package/src/analytics/clarity.ts +46 -0
  350. package/src/analytics/clearbit.ts +47 -0
  351. package/src/analytics/cloudflare.ts +67 -0
  352. package/src/analytics/fathom.ts +64 -0
  353. package/src/analytics/google-analytics.ts +84 -0
  354. package/src/analytics/google-tag-manager.ts +61 -0
  355. package/src/analytics/head.ts +130 -0
  356. package/src/analytics/heap.ts +65 -0
  357. package/src/analytics/hightouch.ts +77 -0
  358. package/src/analytics/hotjar.ts +47 -0
  359. package/src/analytics/index.ts +71 -0
  360. package/src/analytics/inline.ts +21 -0
  361. package/src/analytics/logrocket.ts +71 -0
  362. package/src/analytics/mixpanel.ts +92 -0
  363. package/src/analytics/pirsch.ts +66 -0
  364. package/src/analytics/plausible.ts +83 -0
  365. package/src/analytics/posthog.ts +78 -0
  366. package/src/analytics/schema.ts +60 -0
  367. package/src/analytics/script.ts +60 -0
  368. package/src/analytics/segment.ts +85 -0
  369. package/src/analytics/vercel.ts +50 -0
  370. package/src/astro/adapter-root.ts +7 -9
  371. package/src/astro/component-slots.ts +131 -91
  372. package/src/astro/generate.ts +114 -345
  373. package/src/astro/integration.ts +2 -6
  374. package/src/astro/pages.ts +13 -101
  375. package/src/astro/render-deps.ts +379 -0
  376. package/src/astro/runtime-deps.ts +201 -0
  377. package/src/astro/templates.ts +554 -541
  378. package/src/audit/agent.ts +24 -0
  379. package/src/audit/catalog.ts +2 -2
  380. package/src/audit/checks/assets.ts +2 -2
  381. package/src/audit/checks/dns-aid.ts +1 -1
  382. package/src/audit/checks/duplicates.ts +3 -1
  383. package/src/audit/checks/i18n.ts +1 -1
  384. package/src/audit/checks/indexability.ts +7 -7
  385. package/src/audit/checks/links.ts +2 -2
  386. package/src/audit/checks/llms.ts +6 -6
  387. package/src/audit/checks/network.ts +3 -3
  388. package/src/audit/checks/og-image.ts +2 -2
  389. package/src/audit/checks/robots.ts +1 -1
  390. package/src/audit/checks/sitemap.ts +2 -2
  391. package/src/audit/checks/social.ts +1 -1
  392. package/src/audit/run.ts +4 -3
  393. package/src/audit/terms.ts +31 -0
  394. package/src/audit/url.ts +13 -13
  395. package/src/cli/command-meta.ts +10 -0
  396. package/src/cli/commands/audit.ts +39 -20
  397. package/src/cli/commands/build.ts +71 -314
  398. package/src/cli/commands/check.ts +1 -0
  399. package/src/cli/commands/dev.ts +40 -1
  400. package/src/cli/commands/doctor.ts +90 -14
  401. package/src/cli/commands/eject.ts +44 -7
  402. package/src/cli/commands/eval.ts +2 -2
  403. package/src/cli/commands/init.ts +161 -41
  404. package/src/cli/commands/migrate.ts +121 -0
  405. package/src/cli/commands/preview.ts +7 -1
  406. package/src/cli/commands/translate.ts +2 -2
  407. package/src/cli/commands/upgrade.ts +141 -0
  408. package/src/cli/commands/version.ts +57 -44
  409. package/src/cli/eject-scripts.ts +121 -6
  410. package/src/cli/index.ts +11 -1
  411. package/src/cli/init/install.ts +70 -0
  412. package/src/cli/init/questions.ts +7 -0
  413. package/src/cli/init/scaffold.ts +459 -75
  414. package/src/cli/lazy-command.ts +37 -1
  415. package/src/cli/prepare.ts +40 -6
  416. package/src/cli/required-secrets.ts +38 -11
  417. package/src/cli/unknown-flags.ts +266 -0
  418. package/src/cli/yarn-pnp.ts +52 -0
  419. package/src/components/content/AccordionItem.astro +7 -1
  420. package/src/components/content/Card.astro +4 -3
  421. package/src/components/content/ColorItem.astro +22 -4
  422. package/src/components/content/GithubInfo.astro +2 -2
  423. package/src/components/content/Prompt.astro +25 -25
  424. package/src/components/content/Tabs.astro +3 -1
  425. package/src/components/content/Tile.astro +2 -3
  426. package/src/components/content/Tooltip.astro +57 -9
  427. package/src/components/content/content-strings.ts +34 -0
  428. package/src/components/content/diff.ts +1 -1
  429. package/src/components/content/mermaid-element.ts +13 -2
  430. package/src/components/content/prompt-markdown.ts +292 -0
  431. package/src/components/content/tooltip-id.ts +41 -0
  432. package/src/components/islands/hooks.ts +75 -3
  433. package/src/components/layout/Analytics.astro +21 -79
  434. package/src/components/layout/Banner.astro +3 -1
  435. package/src/components/layout/DiscoveryLinks.astro +69 -0
  436. package/src/components/layout/Header.astro +83 -20
  437. package/src/components/layout/LanguageSwitcher.astro +4 -1
  438. package/src/components/layout/Logo.astro +23 -2
  439. package/src/components/layout/NavSelector.astro +13 -2
  440. package/src/components/layout/NavTree.astro +12 -3
  441. package/src/components/layout/NavTreeScript.astro +17 -3
  442. package/src/components/layout/PageActions.astro +3 -3
  443. package/src/components/layout/PageFeedback.astro +11 -1
  444. package/src/components/layout/PageLayout.astro +52 -5
  445. package/src/components/layout/ReferenceLayout.astro +21 -12
  446. package/src/components/layout/RootLayout.astro +69 -43
  447. package/src/components/layout/Search.astro +30 -6
  448. package/src/components/layout/WebMcp.astro +1 -1
  449. package/src/components/layout/analytics-client.ts +101 -15
  450. package/src/components/layout/drawer-inert.ts +113 -15
  451. package/src/components/layout/dropdown-clamp.ts +105 -0
  452. package/src/components/layout/head-scripts.ts +15 -6
  453. package/src/components/layout/nav-utils.ts +17 -0
  454. package/src/components/layout/search/algolia.ts +8 -8
  455. package/src/components/layout/search/orama-cloud.ts +9 -7
  456. package/src/components/layout/search/typesense.ts +11 -17
  457. package/src/components/openapi/MessageComposer.astro +2 -2
  458. package/src/components/openapi/Playground.astro +2 -2
  459. package/src/components/openapi/playground-client.ts +79 -10
  460. package/src/core/changelog-index.ts +24 -0
  461. package/src/core/component-overrides.ts +399 -154
  462. package/src/core/config-input.ts +212 -543
  463. package/src/core/config.ts +130 -41
  464. package/src/core/custom-pages.ts +105 -0
  465. package/src/core/data.ts +32 -11
  466. package/src/core/define-components.ts +12 -9
  467. package/src/core/deployment-env.ts +18 -74
  468. package/src/core/diagnostics.ts +14 -7
  469. package/src/core/graph.ts +69 -25
  470. package/src/core/i18n-ui.ts +6 -2
  471. package/src/core/i18n.ts +17 -0
  472. package/src/core/includes.ts +156 -38
  473. package/src/core/last-modified.ts +6 -11
  474. package/src/core/links.ts +166 -2
  475. package/src/core/manifest.ts +10 -3
  476. package/src/core/navigation.ts +137 -19
  477. package/src/core/new-tab.ts +35 -0
  478. package/src/core/node-require.ts +21 -0
  479. package/src/core/project-graph.ts +42 -26
  480. package/src/core/project.ts +9 -4
  481. package/src/core/request-body.ts +61 -0
  482. package/src/core/safe-href.ts +28 -0
  483. package/src/core/safe-links.ts +68 -0
  484. package/src/core/schema.ts +531 -721
  485. package/src/core/server-features.ts +8 -9
  486. package/src/core/sources/assets.ts +47 -11
  487. package/src/core/sources/collection.ts +67 -0
  488. package/src/core/sources/contentful-rich-text.ts +285 -0
  489. package/src/core/sources/contentful.ts +173 -0
  490. package/src/core/sources/github-releases.ts +51 -1
  491. package/src/core/sources/json.ts +71 -0
  492. package/src/core/sources/lexical.ts +195 -0
  493. package/src/core/sources/lower.ts +226 -0
  494. package/src/core/sources/normalize.ts +52 -2
  495. package/src/core/sources/notion.ts +39 -28
  496. package/src/core/sources/payload.ts +135 -0
  497. package/src/core/sources/portable-text.ts +11 -16
  498. package/src/core/sources/remote.ts +226 -0
  499. package/src/core/sources/resolve.ts +104 -166
  500. package/src/core/sources/sanity.ts +16 -49
  501. package/src/core/sources/strapi-blocks.ts +124 -0
  502. package/src/core/sources/strapi.ts +191 -0
  503. package/src/core/sources/types.ts +12 -1
  504. package/src/core/types.ts +20 -1
  505. package/src/core/ui-packs/ar.ts +6 -2
  506. package/src/core/ui-packs/bg.ts +6 -2
  507. package/src/core/ui-packs/bn.ts +6 -2
  508. package/src/core/ui-packs/ca.ts +3 -1
  509. package/src/core/ui-packs/cs.ts +6 -2
  510. package/src/core/ui-packs/da.ts +6 -2
  511. package/src/core/ui-packs/de.ts +6 -2
  512. package/src/core/ui-packs/el.ts +3 -1
  513. package/src/core/ui-packs/es.ts +3 -1
  514. package/src/core/ui-packs/fa.ts +6 -2
  515. package/src/core/ui-packs/fi.ts +6 -2
  516. package/src/core/ui-packs/fr.ts +3 -1
  517. package/src/core/ui-packs/he.ts +6 -2
  518. package/src/core/ui-packs/hi.ts +6 -2
  519. package/src/core/ui-packs/hr.ts +6 -2
  520. package/src/core/ui-packs/hu.ts +6 -2
  521. package/src/core/ui-packs/id.ts +6 -2
  522. package/src/core/ui-packs/it.ts +3 -1
  523. package/src/core/ui-packs/ja.ts +3 -1
  524. package/src/core/ui-packs/ko.ts +3 -1
  525. package/src/core/ui-packs/nl.ts +6 -2
  526. package/src/core/ui-packs/no.ts +6 -2
  527. package/src/core/ui-packs/pl.ts +6 -2
  528. package/src/core/ui-packs/pt-br.ts +3 -1
  529. package/src/core/ui-packs/pt.ts +3 -1
  530. package/src/core/ui-packs/ro.ts +6 -2
  531. package/src/core/ui-packs/ru.ts +6 -2
  532. package/src/core/ui-packs/sk.ts +6 -2
  533. package/src/core/ui-packs/sr.ts +6 -2
  534. package/src/core/ui-packs/sv.ts +6 -2
  535. package/src/core/ui-packs/th.ts +3 -1
  536. package/src/core/ui-packs/tr.ts +6 -2
  537. package/src/core/ui-packs/uk.ts +6 -2
  538. package/src/core/ui-packs/vi.ts +3 -1
  539. package/src/core/ui-packs/zh-tw.ts +3 -1
  540. package/src/core/ui-packs/zh.ts +3 -1
  541. package/src/core/unrecognized-keys.ts +10 -0
  542. package/src/core/version-cut.ts +116 -8
  543. package/src/deploy/adapter-output.ts +57 -97
  544. package/src/deploy/adapters/cloudflare.ts +39 -0
  545. package/src/deploy/adapters/index.ts +41 -0
  546. package/src/deploy/adapters/netlify.ts +34 -0
  547. package/src/deploy/adapters/node.ts +34 -0
  548. package/src/deploy/adapters/registry.ts +127 -0
  549. package/src/deploy/adapters/types.ts +92 -0
  550. package/src/deploy/adapters/vercel.ts +35 -0
  551. package/src/deploy/artifacts.ts +67 -28
  552. package/src/deploy/cloudflare-negotiation.ts +179 -100
  553. package/src/deploy/function-bundle.ts +18 -3
  554. package/src/deploy/headers.ts +129 -22
  555. package/src/deploy/node-headers.ts +198 -0
  556. package/src/deploy/platforms/cloudflare.ts +312 -0
  557. package/src/deploy/platforms/index.ts +67 -0
  558. package/src/deploy/platforms/netlify.ts +52 -0
  559. package/src/deploy/platforms/node.ts +40 -0
  560. package/src/deploy/platforms/paths.ts +42 -0
  561. package/src/deploy/platforms/static.ts +29 -0
  562. package/src/deploy/platforms/types.ts +112 -0
  563. package/src/deploy/platforms/vercel.ts +193 -0
  564. package/src/deploy/redirects.ts +30 -18
  565. package/src/deploy/robots.ts +3 -3
  566. package/src/deploy/rss.ts +2 -2
  567. package/src/deploy/sitemap.ts +2 -2
  568. package/src/deploy/vercel-negotiation.ts +27 -4
  569. package/src/eval/agents.ts +32 -1
  570. package/src/eval/findings.ts +19 -11
  571. package/src/eval/report.ts +10 -2
  572. package/src/markdown/external-links.ts +65 -0
  573. package/src/markdown/include.ts +45 -27
  574. package/src/markdown/index.ts +34 -9
  575. package/src/markdown/inline-code.ts +12 -6
  576. package/src/markdown/relative-links.ts +324 -0
  577. package/src/markdown/themes.ts +3 -3
  578. package/src/migrate/migrate.ts +149 -0
  579. package/src/openapi/parse.ts +64 -16
  580. package/src/openapi/proxy.ts +62 -10
  581. package/src/openapi/references.ts +147 -147
  582. package/src/openapi/render-mdx.ts +32 -10
  583. package/src/openapi/scalar.ts +15 -13
  584. package/src/openapi/sentence.ts +14 -0
  585. package/src/openapi/source.ts +37 -21
  586. package/src/openapi/spec-dependency-error.ts +15 -0
  587. package/src/reference/asyncapi.ts +83 -0
  588. package/src/reference/graphql.ts +89 -0
  589. package/src/reference/index.ts +40 -0
  590. package/src/reference/openapi.ts +83 -0
  591. package/src/reference/options.ts +201 -0
  592. package/src/reference/scalar.ts +136 -0
  593. package/src/reference/schema.ts +88 -0
  594. package/src/registry/eject.ts +97 -42
  595. package/src/search/adapters/algolia.ts +46 -0
  596. package/src/search/adapters/flexsearch.ts +29 -0
  597. package/src/search/adapters/index.ts +39 -0
  598. package/src/search/adapters/mixedbread.ts +39 -0
  599. package/src/search/adapters/orama-cloud.ts +51 -0
  600. package/src/search/adapters/orama.ts +24 -0
  601. package/src/search/adapters/pagefind.ts +27 -0
  602. package/src/search/adapters/registry.ts +131 -0
  603. package/src/search/adapters/types.ts +46 -0
  604. package/src/search/adapters/typesense.ts +57 -0
  605. package/src/search/build.ts +33 -7
  606. package/src/search/documents.ts +6 -0
  607. package/src/search/sync/algolia.ts +12 -11
  608. package/src/search/sync/index.ts +35 -20
  609. package/src/search/sync/orama-cloud.ts +12 -9
  610. package/src/search/sync/typesense.ts +15 -13
  611. package/src/sources/contentful.ts +63 -0
  612. package/src/sources/custom.ts +38 -0
  613. package/src/sources/filesystem.ts +51 -0
  614. package/src/sources/github-releases.ts +52 -0
  615. package/src/sources/index.ts +60 -0
  616. package/src/sources/mdx-remote.ts +76 -0
  617. package/src/sources/notion.ts +63 -0
  618. package/src/sources/obsidian.ts +38 -0
  619. package/src/sources/payload.ts +60 -0
  620. package/src/sources/registry.ts +182 -0
  621. package/src/sources/sanity.ts +66 -0
  622. package/src/sources/shared.ts +52 -0
  623. package/src/sources/strapi.ts +58 -0
  624. package/src/theme/entry.ts +47 -2
  625. package/src/translate/report.ts +40 -7
  626. package/src/upgrade/upgrade.ts +499 -0
  627. package/dist/cli/chunk-12dxjqk7.js.map +0 -10
  628. package/dist/cli/chunk-196vjxp9.js.map +0 -13
  629. package/dist/cli/chunk-2mzebbbz.js +0 -69
  630. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  631. package/dist/cli/chunk-30e87n55.js +0 -108
  632. package/dist/cli/chunk-30e87n55.js.map +0 -10
  633. package/dist/cli/chunk-450a7rcr.js +0 -185
  634. package/dist/cli/chunk-450a7rcr.js.map +0 -11
  635. package/dist/cli/chunk-5n7t497w.js.map +0 -40
  636. package/dist/cli/chunk-61j18dwk.js +0 -5342
  637. package/dist/cli/chunk-61j18dwk.js.map +0 -58
  638. package/dist/cli/chunk-88by27n5.js +0 -17
  639. package/dist/cli/chunk-88by27n5.js.map +0 -10
  640. package/dist/cli/chunk-8cd8tj54.js.map +0 -34
  641. package/dist/cli/chunk-aztttvb3.js +0 -381
  642. package/dist/cli/chunk-aztttvb3.js.map +0 -12
  643. package/dist/cli/chunk-cvky9gb2.js.map +0 -11
  644. package/dist/cli/chunk-ejjx8znq.js.map +0 -15
  645. package/dist/cli/chunk-fmceyezb.js +0 -1007
  646. package/dist/cli/chunk-fmceyezb.js.map +0 -13
  647. package/dist/cli/chunk-hdm2dkd2.js.map +0 -48
  648. package/dist/cli/chunk-hs3gbh8p.js.map +0 -10
  649. package/dist/cli/chunk-jbj4qhfw.js +0 -30
  650. package/dist/cli/chunk-jbj4qhfw.js.map +0 -10
  651. package/dist/cli/chunk-mqb2ka8m.js +0 -68
  652. package/dist/cli/chunk-mqb2ka8m.js.map +0 -10
  653. package/dist/cli/chunk-mt76t7dj.js.map +0 -11
  654. package/dist/cli/chunk-q4rae3bg.js +0 -60
  655. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  656. package/dist/cli/chunk-ra1v2nc2.js +0 -35
  657. package/dist/cli/chunk-ra1v2nc2.js.map +0 -10
  658. package/dist/cli/chunk-t3tj0dgr.js.map +0 -15
  659. package/dist/cli/chunk-tqa1s0k8.js +0 -69
  660. package/dist/cli/chunk-tqa1s0k8.js.map +0 -11
  661. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  662. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  663. package/dist/cli/chunk-vkrsvbr5.js +0 -107
  664. package/dist/cli/chunk-vkrsvbr5.js.map +0 -11
  665. package/dist/cli/chunk-vrfp10qk.js +0 -81
  666. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  667. package/dist/cli/chunk-wjt80jps.js +0 -1049
  668. package/dist/cli/chunk-wjt80jps.js.map +0 -24
  669. package/docs/advanced/api-reference.mdx +0 -240
  670. package/docs/reference/cli.mdx +0 -197
  671. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  672. package/src/components/content/changelog-element.ts +0 -69
  673. package/src/search/providers.ts +0 -91
  674. /package/dist/cli/{chunk-3w7b2vcx.js.map → chunk-7ez8ny0t.js.map} +0 -0
  675. /package/dist/cli/{chunk-688e0dde.js.map → chunk-88cpgt6h.js.map} +0 -0
  676. /package/dist/cli/{chunk-exeeb35e.js.map → chunk-f7t03s3g.js.map} +0 -0
  677. /package/dist/cli/{chunk-ppfvdcd4.js.map → chunk-zxccj738.js.map} +0 -0
@@ -4,6 +4,11 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { dirname, join, resolve } from "pathe";
5
5
 
6
6
  import { buildAgentReadability } from "../ai/agent-readability.ts";
7
+ import {
8
+ AI_CATALOG_PATH,
9
+ ARD_MANIFEST_PATH,
10
+ buildAiCatalog,
11
+ } from "../ai/ai-catalog.ts";
7
12
  import { API_CATALOG_PATH, buildApiCatalog } from "../ai/api-catalog.ts";
8
13
  import { buildHomeLinkHeader } from "../ai/link-headers.ts";
9
14
  import { buildLlmsFiles } from "../ai/llms.ts";
@@ -23,9 +28,10 @@ import type { ResolvedConfig } from "../core/schema.ts";
23
28
  import { buildSearchIndex } from "../search/build.ts";
24
29
  import { syncSearchProvider } from "../search/sync/index.ts";
25
30
  import { readsHeaderFiles } from "./adapter-output.ts";
26
- import { buildNetlifyHeaders } from "./headers.ts";
31
+ import { buildNetlifyHeaders, buildVercelHeaders } from "./headers.ts";
32
+ import { deployPlatform } from "./platforms/index.ts";
33
+ import { VERCEL_JSON_FILE } from "./platforms/vercel.ts";
27
34
  import {
28
- buildNetlifyRedirects,
29
35
  buildRedirectManifest,
30
36
  buildVercelConfig,
31
37
  platformRedirects,
@@ -58,38 +64,63 @@ export interface ArtifactLogger {
58
64
  }
59
65
 
60
66
  /**
61
- * Emit platform redirect files for a static build (adapters wire redirects
62
- * natively). Always writes the manifest; writes `_redirects`/`vercel.json` only
63
- * when the user hasn't shipped one via public/. Note that Vercel's
67
+ * Emit platform redirect files for a static build (a server build answers
68
+ * redirects at request time): the files the deployment's platform reads
69
+ * (`_redirects`, `vercel.json` — every one of them when no host is named),
70
+ * each only when the user hasn't shipped one via public/, plus the
71
+ * `blume-redirects.json` manifest when no host is named. `vercel.json` also
72
+ * carries the header rules `_headers` gives the other hosts, since Vercel reads
73
+ * no `_headers`, so it is written even with no redirects. Note that Vercel's
64
74
  * git-integration builds read `vercel.json` from the repository root only —
65
75
  * the copy emitted here takes effect when the dist folder itself is deployed
66
76
  * directly via the Vercel CLI.
67
77
  */
68
78
  const emitRedirectFiles = async (
69
- config: ResolvedConfig,
79
+ project: BlumeProject,
70
80
  distDir: string,
71
81
  logger: ArtifactLogger
72
82
  ): Promise<void> => {
73
- const redirects = platformRedirects(config);
74
- if (redirects.length === 0 || config.deployment.output !== "static") {
83
+ const { config } = project;
84
+ if (config.deployment.options.output !== "static") {
75
85
  return;
76
86
  }
77
- await writeFile(
78
- join(distDir, "blume-redirects.json"),
79
- buildRedirectManifest(redirects),
80
- "utf-8"
87
+ const platform = deployPlatform(config.deployment);
88
+ const redirects = platformRedirects(config);
89
+ const vercelHeaders = platform.redirectFiles.includes(VERCEL_JSON_FILE)
90
+ ? buildVercelHeaders(
91
+ config,
92
+ buildHomeLinkHeader(config, markdownRoutePaths(project))
93
+ )
94
+ : [];
95
+ const files = platform.redirectFiles.filter(
96
+ (file) =>
97
+ !existsSync(join(distDir, file.name)) &&
98
+ (redirects.length > 0 ||
99
+ (file === VERCEL_JSON_FILE && vercelHeaders.length > 0))
81
100
  );
82
- const platformFiles = [
83
- { content: buildNetlifyRedirects(redirects), name: "_redirects" },
84
- { content: buildVercelConfig(redirects), name: "vercel.json" },
85
- ];
86
101
  await Promise.all(
87
- platformFiles.map((file) =>
88
- existsSync(join(distDir, file.name))
89
- ? Promise.resolve()
90
- : writeFile(join(distDir, file.name), file.content, "utf-8")
102
+ files.map((file) =>
103
+ writeFile(
104
+ join(distDir, file.name),
105
+ file === VERCEL_JSON_FILE
106
+ ? buildVercelConfig(redirects, vercelHeaders)
107
+ : file.build(redirects),
108
+ "utf-8"
109
+ )
91
110
  )
92
111
  );
112
+ if (redirects.length === 0) {
113
+ return;
114
+ }
115
+ // The manifest is for a host Blume can't name, where nothing reads the
116
+ // platform files above.
117
+ if (platform.kind === "static") {
118
+ await writeFile(
119
+ join(distDir, "blume-redirects.json"),
120
+ buildRedirectManifest(redirects),
121
+ "utf-8"
122
+ );
123
+ }
93
124
  logger.info(`Emitted redirect files for ${redirects.length} redirect(s)`);
94
125
  };
95
126
 
@@ -156,7 +187,7 @@ export const emitHeaderFiles = async (
156
187
  };
157
188
 
158
189
  /**
159
- * Collect the Agent Skills `ai.skills` publishes, once per build, so both the
190
+ * Collect the Agent Skills `agents.skills` publishes, once per build, so both the
160
191
  * skills surface and llms.txt (which lists them) read the same set. Empty
161
192
  * when the feature is off, the directory is missing, nothing in it is
162
193
  * publishable (each with a warning), or a user-shipped
@@ -167,14 +198,14 @@ const collectConfiguredSkills = async (
167
198
  distDir: string,
168
199
  logger: ArtifactLogger
169
200
  ): Promise<SkillArtifact[]> => {
170
- const configured = project.config.ai.skills;
201
+ const configured = project.config.agents.skills;
171
202
  if (!configured) {
172
203
  return [];
173
204
  }
174
205
  const dir = resolve(project.context.root, configured);
175
206
  if (!existsSync(dir)) {
176
207
  logger.warn(
177
- `ai.skills points at "${configured}" (${dir}), which does not exist; no skills published.`
208
+ `agents.skills points at "${configured}" (${dir}), which does not exist; no skills published.`
178
209
  );
179
210
  return [];
180
211
  }
@@ -186,7 +217,9 @@ const collectConfiguredSkills = async (
186
217
  logger.warn(warning);
187
218
  }
188
219
  if (skills.length === 0) {
189
- logger.warn(`ai.skills: no publishable skills found in "${configured}".`);
220
+ logger.warn(
221
+ `agents.skills: no publishable skills found in "${configured}".`
222
+ );
190
223
  }
191
224
  return skills;
192
225
  };
@@ -234,8 +267,12 @@ const emitAgentSkills = async (
234
267
  const emitWellKnownFiles = async (
235
268
  config: ResolvedConfig,
236
269
  distDir: string,
270
+ skills: readonly SkillArtifact[],
237
271
  logger: ArtifactLogger
238
272
  ): Promise<void> => {
273
+ // One document, two paths: the ai-catalog spec's well-known URI and the
274
+ // ARD v0.91 one (see `ai/ai-catalog.ts`).
275
+ const aiCatalog = buildAiCatalog(config, skills);
239
276
  const files = [
240
277
  {
241
278
  content: buildSignaturesDirectory(config),
@@ -247,6 +284,8 @@ const emitWellKnownFiles = async (
247
284
  label: "RFC 9727",
248
285
  path: API_CATALOG_PATH,
249
286
  },
287
+ { content: aiCatalog, label: "AI Catalog", path: AI_CATALOG_PATH },
288
+ { content: aiCatalog, label: "ARD manifest", path: ARD_MANIFEST_PATH },
250
289
  ];
251
290
  for (const file of files) {
252
291
  const target = join(distDir, file.path.slice(1));
@@ -313,7 +352,7 @@ export const publishBuildArtifacts = async (
313
352
  logger: ArtifactLogger,
314
353
  indexSearch: (outDir: string) => Promise<number> = buildSearchIndex
315
354
  ): Promise<void> => {
316
- if (project.config.search.provider === "pagefind") {
355
+ if (project.config.search.provider.mode === "pagefind") {
317
356
  logger.info("Building search index");
318
357
  const indexed = await indexSearch(distDir);
319
358
  logger.info(`Indexed ${indexed} page(s) for search`);
@@ -329,7 +368,7 @@ export const publishBuildArtifacts = async (
329
368
 
330
369
  // Collected once: llms.txt lists the skills the build publishes below.
331
370
  const skills = await collectConfiguredSkills(project, distDir, logger);
332
- if (project.config.ai.llmsTxt.enabled) {
371
+ if (project.config.agents.llmsTxt.enabled) {
333
372
  await publishLlmsFiles(project, distDir, skills, logger);
334
373
  }
335
374
 
@@ -362,9 +401,9 @@ export const publishBuildArtifacts = async (
362
401
  logger.info("Generated agent-readability.json");
363
402
  }
364
403
 
365
- await emitWellKnownFiles(project.config, distDir, logger);
404
+ await emitWellKnownFiles(project.config, distDir, skills, logger);
366
405
  await emitAgentSkills(project, distDir, skills, logger);
367
406
 
368
- await emitRedirectFiles(project.config, distDir, logger);
407
+ await emitRedirectFiles(project, distDir, logger);
369
408
  await emitHeaderFiles(project, distDir, logger);
370
409
  };
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * `Accept: text/markdown` content negotiation for Cloudflare server builds.
3
3
  *
4
- * Blume prerenders every content page — even under `deployment.output:
5
- * "server"` — and on Cloudflare the ASSETS binding serves those files before
4
+ * Blume prerenders every content page — even in a server build (`deployment:
5
+ * cloudflare()`) — and on Cloudflare the ASSETS binding serves those files before
6
6
  * the Worker script runs, so no server-side code (Astro middleware included)
7
7
  * ever sees a content-page request. Worse, even a content-page request that
8
8
  * does reach the Worker is answered by `@astrojs/cloudflare`'s handler
@@ -13,10 +13,11 @@
13
13
  * Negotiation therefore needs two coordinated pieces, both applied to the
14
14
  * adapter's emitted deploy bundle after `astro build`:
15
15
  *
16
- * 1. `assets.run_worker_first` in `dist/server/wrangler.json`, scoped to the
17
- * content routes so the platform routes their requests to the Worker
18
- * instead of serving the static HTML directly (other assets keep their
19
- * zero-Worker fast path).
16
+ * 1. `assets.run_worker_first` in `dist/server/wrangler.json`, claiming every
17
+ * path except the static files that never negotiate, so the platform
18
+ * routes page requests — and requests for pages that do not exist — to
19
+ * the Worker instead of serving the static HTML directly (see
20
+ * `buildRunWorkerFirstRules`).
20
21
  * 2. A generated entry Worker that fronts the adapter's: when the client
21
22
  * prefers `text/markdown` it serves the page's prerendered `.md` mirror
22
23
  * from the ASSETS binding, and it delegates everything else to the Astro
@@ -32,11 +33,9 @@
32
33
  * they sit in the manifest's asset set, which the handler serves from the
33
34
  * binding first. So whenever a worker-first rule claims a page JSON URL, the
34
35
  * wrapper answers it from the binding itself, keyed by the exact set of
35
- * documents the build emitted. The generated rule sets that would claim every
36
- * `.json` file — a subpath base and the coarse fallback — exempt `*.json`
37
- * outright, keeping those files on the zero-Worker path; the wrapper branch
38
- * covers the remaining claims (a content section under `/api`, or
39
- * user-configured rules).
36
+ * documents the build emitted. The generated rule set claims them (a miss
37
+ * must reach the API's `PAGE_NOT_FOUND` answer, see `STATIC_EXEMPTIONS`), and
38
+ * so do user-configured rules or a bare `true`.
40
39
  *
41
40
  * Cloudflare does not apply `_headers` to worker-first routes, so the wrapper
42
41
  * also re-stamps what the static layer would otherwise add on the routes it
@@ -62,9 +61,30 @@
62
61
  * its keys are full served URLs, which is exactly what the Worker sees.
63
62
  * Redirects outside every worker-first rule still never invoke the Worker;
64
63
  * the static layer serves them from `_redirects` as before.
64
+ *
65
+ * Missing pages negotiate too. Under an explicit `run_worker_first` list the
66
+ * platform answers every request outside the rules from the static layer —
67
+ * an asset miss included, as the nearest `404.html` — which is why the rules
68
+ * claim every path: a request for a URL no page backs then reaches the
69
+ * Worker, and the Astro Worker answers it with the HTML 404 shell from the
70
+ * binding (`not_found_handling: "404-page"`). When the client prefers
71
+ * Markdown the wrapper swaps that shell for the prerendered `/404.md` twin,
72
+ * and when it prefers JSON for the `/404.json` problem document, keeping the
73
+ * 404 status either way — the counterpart of the miss-phase routes in
74
+ * `deploy/vercel-negotiation.ts`. A raw `.md` or `.json` URL no file backs
75
+ * asks for the same twin implicitly, but on this platform such a request only
76
+ * reaches the Worker for `.json` (the generated set keeps `.md` on the static
77
+ * layer for its `_headers`). Only an HTML 404 is swapped, so an API endpoint's
78
+ * own problem document is never overwritten.
65
79
  */
66
80
 
81
+ import {
82
+ API_NAVIGATION_PATH,
83
+ API_PAGES_PATH,
84
+ OPENAPI_PATH,
85
+ } from "../ai/api/paths.ts";
67
86
  import { normalizePath } from "../core/base-path.ts";
87
+ import type { NotFoundVariants } from "./vercel-negotiation.ts";
68
88
 
69
89
  /** Filename of the generated wrapper Worker, next to the adapter's entry. */
70
90
  export const NEGOTIATION_WORKER_FILE = "blume-worker.mjs";
@@ -72,28 +92,42 @@ export const NEGOTIATION_WORKER_FILE = "blume-worker.mjs";
72
92
  /**
73
93
  * Wrangler's limits on `assets.run_worker_first`: at most 100 rules of at
74
94
  * most 100 characters each. A rule set over either limit fails
75
- * `wrangler deploy` outright, so the builder falls back to a coarse set.
95
+ * `wrangler deploy` outright, so the injection is skipped instead — only
96
+ * user-configured rules can push the generated set over.
76
97
  */
77
98
  const MAX_RULES = 100;
78
99
  const MAX_RULE_LENGTH = 100;
79
100
 
80
101
  /**
81
- * Coarse fallback when the grouped rules would exceed Wrangler's limits:
82
- * route everything through the Worker except the fingerprinted build assets
83
- * and the raw AI-ready endpoints, whose `charset=utf-8` comes from `_headers`
84
- * (not applied on worker-first routes) and whose responses never negotiate.
85
- * The prerendered `.json` documents are exempted for the same reason, and
86
- * because the Astro Worker would hand the per-page ones to the `/api/`
87
- * catch-all (see the module comment); a miss on an exempted path still
88
- * invokes the Worker, so unknown `.json` URLs keep their problem document.
102
+ * The paths the generated rules leave on the static layer, relative to the
103
+ * deployment base: the fingerprinted build assets, and the files whose
104
+ * response headers come from `_headers` — which Cloudflare does not apply to
105
+ * a response the Worker produced — and which never negotiate. Those are the
106
+ * raw AI-ready endpoints (`charset=utf-8` on `.md`/`.mdx`/`.txt`) and the
107
+ * `.well-known` discovery documents (the extensionless api-catalog's content
108
+ * type, the CORS headers).
109
+ *
110
+ * The JSON Blume writes at fixed paths stays there too, but JSON as a whole
111
+ * does not: a negative rule outranks every positive one, so a `*.json`
112
+ * exemption would keep a request for a page JSON that doesn't exist
113
+ * (`/api/docs/pages/nope.json`) off the Worker, and the platform would answer
114
+ * it with an empty 404 instead of the API's `PAGE_NOT_FOUND` problem document.
115
+ * The per-page documents themselves reach the wrapper, which serves them from
116
+ * the binding (see the module comment); any other JSON goes through the Astro
117
+ * Worker, which serves a static file from the binding as well.
89
118
  */
90
- const FALLBACK_RULES = [
91
- "/*",
92
- "!/_astro/*",
93
- "!/*.md",
94
- "!/*.mdx",
95
- "!/*.txt",
96
- "!/*.json",
119
+ const STATIC_EXEMPTIONS = [
120
+ "/_astro/*",
121
+ "/*.md",
122
+ "/*.mdx",
123
+ "/*.txt",
124
+ "/.well-known/*",
125
+ "/404.json",
126
+ "/agent-readability.json",
127
+ "/blume-search.json",
128
+ OPENAPI_PATH,
129
+ API_PAGES_PATH,
130
+ API_NAVIGATION_PATH,
97
131
  ];
98
132
 
99
133
  /**
@@ -129,68 +163,26 @@ const ruleBody = (rule: string): string =>
129
163
  isNegativeRule(rule) ? rule.slice(1) : rule;
130
164
 
131
165
  /**
132
- * The `run_worker_first` rules for the given content routes: the routes that
133
- * must reach the Worker for negotiation, grouped by first path segment so the
134
- * set stays far under Wrangler's 100-rule cap on real sites. Nested groups
135
- * get a `/{segment}/*` glob plus negative rules exempting their raw
136
- * `.md`/`.mdx` mirrors; a bare route gets its exact path in both request
137
- * spellings (with and without the trailing slash) so no unrelated URL pays
138
- * the Worker hop. On a subpath deploy the whole base is routed as one group —
139
- * every route lives under it anyway — with the raw endpoints and the
140
- * prerendered `.json` documents exempted like the coarse fallback does.
166
+ * The `run_worker_first` rules: every path under the deployment base goes
167
+ * through the Worker except the {@link STATIC_EXEMPTIONS}. Claiming
168
+ * everything — rather than only the content routes — is what lets a
169
+ * *missing* page negotiate: under an explicit rule list the platform answers
170
+ * any request outside every rule from the static layer, an asset miss
171
+ * included (as the nearest `404.html`), so a rule set scoped to the content
172
+ * routes would never let the Worker see a request for a URL that has no
173
+ * page. The price is a Worker hop on every page request the site does serve.
174
+ * On a subpath deploy the base is claimed in both request spellings.
141
175
  *
142
176
  * Configured redirects need no exemption from these rules: the wrapper Worker
143
177
  * answers any it claims from its baked-in redirect table with the configured
144
178
  * status (see the module comment).
145
179
  */
146
- export const buildRunWorkerFirstRules = (
147
- routePaths: readonly string[],
148
- base?: string
149
- ): string[] => {
180
+ export const buildRunWorkerFirstRules = (base?: string): string[] => {
150
181
  const prefix = encodeURI(basePrefix(base));
151
- if (prefix) {
152
- return [
153
- prefix,
154
- `${prefix}/*`,
155
- `!${prefix}/*.md`,
156
- `!${prefix}/*.mdx`,
157
- `!${prefix}/*.txt`,
158
- `!${prefix}/*.json`,
159
- `!${prefix}/_astro/*`,
160
- ];
161
- }
162
- const groups = new Map<string, { bare: boolean; nested: boolean }>();
163
- let home = false;
164
- for (const route of routePaths) {
165
- if (route === "/") {
166
- home = true;
167
- continue;
168
- }
169
- const segments = route.split("/").filter(Boolean);
170
- const head = segments[0] ?? "";
171
- const group = groups.get(head) ?? { bare: false, nested: false };
172
- if (segments.length === 1) {
173
- group.bare = true;
174
- } else {
175
- group.nested = true;
176
- }
177
- groups.set(head, group);
178
- }
179
- const rules: string[] = home ? ["/"] : [];
180
- const negatives: string[] = [];
181
- for (const [head, group] of groups) {
182
- const segment = `/${encodeURI(head)}`;
183
- if (group.bare) {
184
- rules.push(segment);
185
- }
186
- if (group.nested) {
187
- rules.push(`${segment}/*`);
188
- negatives.push(`!${segment}/*.md`, `!${segment}/*.mdx`);
189
- } else {
190
- rules.push(`${segment}/`);
191
- }
192
- }
193
- return [...rules, ...negatives];
182
+ const exemptions = STATIC_EXEMPTIONS.map((path) => `!${prefix}${path}`);
183
+ return prefix
184
+ ? [prefix, `${prefix}/*`, ...exemptions]
185
+ : [`/*`, ...exemptions];
194
186
  };
195
187
 
196
188
  /**
@@ -268,6 +260,12 @@ export interface NegotiationWorkerOptions {
268
260
  * the `/api/` catch-all (see the module comment).
269
261
  */
270
262
  pageJsonPaths?: readonly string[];
263
+ /**
264
+ * Which prerendered 404 twins the build emitted (`404.md`, `404.json`), so
265
+ * the wrapper only substitutes a twin that exists. A project that owns
266
+ * `/404` emits neither and keeps the HTML answer.
267
+ */
268
+ notFound?: NotFoundVariants;
271
269
  }
272
270
 
273
271
  /**
@@ -287,6 +285,10 @@ export const buildNegotiationWorker = (
287
285
  const homeTokens = JSON.stringify(
288
286
  options.homeTokens === undefined ? null : String(options.homeTokens)
289
287
  );
288
+ const notFound = JSON.stringify({
289
+ json: options.notFound?.json === true,
290
+ markdown: options.notFound?.markdown === true,
291
+ });
290
292
  // Keyed by the normalized served path; the runtime lookup decodes and
291
293
  // trims the request path the same way, so both spellings of a URL match.
292
294
  // The destination is percent-encoded here because it ships as a `Location`
@@ -309,9 +311,11 @@ export const buildNegotiationWorker = (
309
311
  // per-page JSON document is served from the binding (the Astro Worker would
310
312
  // route it to the \`/api/\` catch-all, because Astro resolves a prerendered
311
313
  // dynamic route to the first live route on the same path). Every other
312
- // request is delegated to the Astro Worker untouched. \`_headers\` does not
313
- // apply to worker-first routes, so the homepage Link header and the Markdown
314
- // charset are re-stamped here.
314
+ // request is delegated to the Astro Worker untouched — except a missing
315
+ // page, whose HTML 404 shell is swapped for the prerendered Markdown or JSON
316
+ // 404 twin when the client prefers one. \`_headers\` does not apply to
317
+ // worker-first routes, so the homepage Link header and the Markdown charset
318
+ // are re-stamped here.
315
319
  import server from ${JSON.stringify(options.mainSpecifier)};
316
320
 
317
321
  const ROUTES = new Set(${routes});
@@ -321,6 +325,7 @@ const ASSETS_BINDING = ${binding};
321
325
  const HOME_LINK_HEADER = ${homeLinkHeader};
322
326
  const HOME_TOKENS = ${homeTokens};
323
327
  const REDIRECTS = ${redirects};
328
+ const NOT_FOUND = ${notFound};
324
329
 
325
330
  // Configured redirects live in \`_redirects\`, which only the static layer
326
331
  // reads — a worker-first route never reaches it. Answering from this baked-in
@@ -383,20 +388,93 @@ const parseAccept = (accept) =>
383
388
  return { q: Number.isNaN(q) ? 1 : q, type };
384
389
  });
385
390
 
386
- const prefersMarkdown = (accept) => {
391
+ // Whether the client explicitly prefers one of \`types\` over HTML. Browsers
392
+ // never send these, so an ordinary page request is false.
393
+ const prefers = (accept, types) => {
387
394
  if (!accept) {
388
395
  return false;
389
396
  }
390
- let markdownQ = -1;
397
+ let wantedQ = -1;
391
398
  let htmlQ = 0;
392
399
  for (const { q, type } of parseAccept(accept)) {
393
- if (type === "text/markdown" || type === "text/x-markdown") {
394
- markdownQ = Math.max(markdownQ, q);
400
+ if (types.includes(type)) {
401
+ wantedQ = Math.max(wantedQ, q);
395
402
  } else if (type === "text/html") {
396
403
  htmlQ = Math.max(htmlQ, q);
397
404
  }
398
405
  }
399
- return markdownQ > 0 && markdownQ >= htmlQ;
406
+ return wantedQ > 0 && wantedQ >= htmlQ;
407
+ };
408
+
409
+ const prefersMarkdown = (accept) =>
410
+ prefers(accept, ["text/markdown", "text/x-markdown"]);
411
+
412
+ const prefersJson = (accept) =>
413
+ prefers(accept, ["application/json", "application/problem+json"]);
414
+
415
+ // The prerendered 404 twins, in the order a client asking for both is
416
+ // answered: which twin, when a client prefers it, the raw URL extension that
417
+ // asks for it implicitly, where it lives, and the content type to re-pin.
418
+ const NOT_FOUND_TWINS = [
419
+ {
420
+ contentType: "text/markdown; charset=utf-8",
421
+ extension: /\\.mdx?$/u,
422
+ key: "markdown",
423
+ path: "/404.md",
424
+ prefers: prefersMarkdown,
425
+ },
426
+ {
427
+ contentType: "application/problem+json; charset=utf-8",
428
+ extension: /\\.json$/u,
429
+ key: "json",
430
+ path: "/404.json",
431
+ prefers: prefersJson,
432
+ },
433
+ ];
434
+
435
+ // A missing page comes back from the Astro Worker as the HTML 404 shell (the
436
+ // binding's \`not_found_handling\`); a client that prefers Markdown or JSON —
437
+ // or asked for a raw \`.md\`/\`.json\` URL no file backs — gets the prerendered
438
+ // twin instead, with the same 404 status. Anything but an HTML 404 (an API
439
+ // endpoint's own problem document, a missing image) is left alone. The twin
440
+ // is fetched without the request's conditional headers, so a stray ETag match
441
+ // cannot turn the 404 into a 304.
442
+ const notFoundTwin = async (request, url, response, assets) => {
443
+ if (response.status !== 404 || assets === undefined) {
444
+ return null;
445
+ }
446
+ const type = response.headers.get("content-type") ?? "";
447
+ if (!type.startsWith("text/html")) {
448
+ return null;
449
+ }
450
+ const accept = request.headers.get("accept");
451
+ for (const twin of NOT_FOUND_TWINS) {
452
+ if (!NOT_FOUND[twin.key]) {
453
+ continue;
454
+ }
455
+ const negotiated = twin.prefers(accept);
456
+ if (!negotiated && !twin.extension.test(url.pathname)) {
457
+ continue;
458
+ }
459
+ const asset = await assets.fetch(
460
+ new Request(new URL(BASE_PREFIX + twin.path, url), {
461
+ method: request.method,
462
+ })
463
+ );
464
+ if (!asset.ok) {
465
+ return null;
466
+ }
467
+ const patched = new Response(asset.body, {
468
+ headers: asset.headers,
469
+ status: 404,
470
+ });
471
+ patched.headers.set("content-type", twin.contentType);
472
+ if (negotiated) {
473
+ patched.headers.append("vary", "Accept");
474
+ }
475
+ return patched;
476
+ }
477
+ return null;
400
478
  };
401
479
 
402
480
  const markdownVariantUrl = (rawUrl) => {
@@ -487,6 +565,10 @@ export default {
487
565
  }
488
566
  }
489
567
  const response = await server.fetch(request, env, context);
568
+ const twin = await notFoundTwin(request, url, response, assets);
569
+ if (twin !== null) {
570
+ return twin;
571
+ }
490
572
  if (variant === null && !(home && HOME_LINK_HEADER !== null)) {
491
573
  return response;
492
574
  }
@@ -531,13 +613,13 @@ export interface WorkerNegotiationOptions extends Omit<
531
613
  * updated config text plus the wrapper module, or `null` when there is
532
614
  * nothing to do or nowhere safe to do it: no routes, unparsable config, no
533
615
  * usable `main` or assets binding (the wrapper serves the `.md` mirrors from
534
- * it), an already-swapped `main` (the original entry is unrecoverable), or a
535
- * rule set that cannot fit Wrangler's limits even after the coarse fallback.
616
+ * it), an already-swapped `main` (the original entry is unrecoverable), or
617
+ * user-configured rules that push the set over Wrangler's limits.
536
618
  *
537
619
  * The configured redirects are baked into the wrapper, which answers any the
538
620
  * worker-first rules claim with the exact configured status (see the module
539
- * comment) — whichever rules do the claiming: the generated groups, the
540
- * coarse fallback's `/*`, or the user's own (including a bare `true`). A
621
+ * comment) — whichever rules do the claiming: the generated `/*`, or the
622
+ * user's own (including a bare `true`). A
541
623
  * redirect at a content route's own path is never baked: the page owns it,
542
624
  * and answering a redirect there would take a real page off the air. When
543
625
  * `null` is returned no rule set is written at all, so every request stays on
@@ -594,13 +676,10 @@ export const injectWorkerNegotiation = (
594
676
  redirect.from.startsWith("/") &&
595
677
  !guardRoutes.has(normalizePath(redirect.from))
596
678
  );
597
- let rules = mergeRunWorkerFirstRules(
679
+ const rules = mergeRunWorkerFirstRules(
598
680
  assets.run_worker_first,
599
- buildRunWorkerFirstRules(options.routePaths, options.base)
681
+ buildRunWorkerFirstRules(options.base)
600
682
  );
601
- if (!withinWranglerLimits(rules)) {
602
- rules = mergeRunWorkerFirstRules(assets.run_worker_first, FALLBACK_RULES);
603
- }
604
683
  if (!withinWranglerLimits(rules)) {
605
684
  return null;
606
685
  }
@@ -6,6 +6,10 @@ import { init, parse } from "es-module-lexer";
6
6
  import { dirname, join, relative } from "pathe";
7
7
  import { z } from "zod";
8
8
 
9
+ import {
10
+ commandsFor,
11
+ detectProjectPackageManager,
12
+ } from "../cli/init/scaffold.ts";
9
13
  import { packageRoot } from "../core/package-root.ts";
10
14
 
11
15
  /**
@@ -271,16 +275,27 @@ export interface FunctionBundleVerdict {
271
275
  message: string;
272
276
  }
273
277
 
278
+ /**
279
+ * The command that adds a dev dependency with the project's package manager
280
+ * (`pnpm add -D`, `npm install -D`, …), for the fix a verdict prints. The
281
+ * missing packages are an isolated-linker problem, so the project is often
282
+ * not on npm at all.
283
+ */
284
+ export const addDevDependencyCommand = async (root: string): Promise<string> =>
285
+ `${commandsFor(await detectProjectPackageManager(root)).add} -D`;
286
+
274
287
  /**
275
288
  * Describe a function's missing packages and how to fix them. Vercel's trace
276
289
  * only reaches packages resolvable from the project root, so the remedy is a
277
290
  * root-level dependency entry for each — the same mirror rule Blume's native
278
- * dependencies (`sharp`, `takumi-js`) already follow.
291
+ * dependencies (`sharp`, `takumi-js`) already follow — added with
292
+ * `addDevCommand` (see {@link addDevDependencyCommand}).
279
293
  */
280
294
  export const functionBundleVerdict = (
281
295
  audit: FunctionBundleAudit,
282
296
  root: string,
283
- ownDependencies: ReadonlySet<string>
297
+ ownDependencies: ReadonlySet<string>,
298
+ addDevCommand = "npm install -D"
284
299
  ): FunctionBundleVerdict => {
285
300
  const names = audit.missing.map((entry) => entry.name);
286
301
  const lines = audit.missing.map(
@@ -291,7 +306,7 @@ export const functionBundleVerdict = (
291
306
  `The Vercel function bundle at ${relative(root, audit.dir) || audit.dir} is missing packages its server code imports, so the deployed function would fail on every request with ERR_MODULE_NOT_FOUND:`,
292
307
  ...lines,
293
308
  "Vercel's dependency trace only includes packages resolvable from the project root; under an isolated linker (pnpm, Bun's isolated mode) Blume's own dependencies are not. Add them to the project's package.json so the trace can find them:",
294
- ` npm install -D ${names.join(" ")}`,
309
+ ` ${addDevCommand} ${names.join(" ")}`,
295
310
  ].join("\n");
296
311
  return { fatal, message };
297
312
  };