blume 1.7.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +245 -0
  3. package/README.md +34 -21
  4. package/dist/cli/{chunk-vacwm2hv.js → chunk-27g6wdth.js} +2 -2
  5. package/dist/cli/{chunk-ps4m1xh4.js → chunk-2hn4b8z7.js} +513 -550
  6. package/dist/cli/chunk-2hn4b8z7.js.map +12 -0
  7. package/dist/cli/chunk-5shv93fd.js +39 -0
  8. package/dist/cli/chunk-5shv93fd.js.map +10 -0
  9. package/dist/cli/chunk-6crbhc3x.js +361 -0
  10. package/dist/cli/chunk-6crbhc3x.js.map +14 -0
  11. package/dist/cli/{chunk-8cjtbafj.js → chunk-6hsn950k.js} +109 -383
  12. package/dist/cli/chunk-6hsn950k.js.map +10 -0
  13. package/dist/cli/chunk-6vm74dry.js +148 -0
  14. package/dist/cli/chunk-6vm74dry.js.map +10 -0
  15. package/dist/cli/{chunk-yg63d42r.js → chunk-79jhk4py.js} +87 -58
  16. package/dist/cli/chunk-79jhk4py.js.map +35 -0
  17. package/dist/cli/chunk-82bbrxdn.js +51 -0
  18. package/dist/cli/chunk-82bbrxdn.js.map +10 -0
  19. package/dist/cli/chunk-abh8yjkn.js +31 -0
  20. package/dist/cli/chunk-abh8yjkn.js.map +10 -0
  21. package/dist/cli/chunk-ah61y8py.js +75 -0
  22. package/dist/cli/chunk-ah61y8py.js.map +11 -0
  23. package/dist/cli/{chunk-dwgcp5sm.js → chunk-ce574jw2.js} +1 -1
  24. package/dist/cli/chunk-ch6g3ar0.js +102 -0
  25. package/dist/cli/chunk-ch6g3ar0.js.map +10 -0
  26. package/dist/cli/chunk-dh8cwk36.js +279 -0
  27. package/dist/cli/chunk-dh8cwk36.js.map +10 -0
  28. package/dist/cli/{chunk-rqy0s5wh.js → chunk-epjnccmv.js} +17 -16
  29. package/dist/cli/{chunk-rqy0s5wh.js.map → chunk-epjnccmv.js.map} +3 -3
  30. package/dist/cli/chunk-f2z5v128.js +97 -0
  31. package/dist/cli/chunk-f2z5v128.js.map +10 -0
  32. package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
  33. package/dist/cli/chunk-fa25z98p.js.map +11 -0
  34. package/dist/cli/{chunk-1jefwnfs.js → chunk-fs23ddbb.js} +1076 -2592
  35. package/dist/cli/chunk-fs23ddbb.js.map +35 -0
  36. package/dist/cli/{chunk-e7f42gdj.js → chunk-fxypxtvm.js} +2 -2
  37. package/dist/cli/{chunk-bvwwhd84.js → chunk-fz5wtpmh.js} +15 -15
  38. package/dist/cli/{chunk-bvwwhd84.js.map → chunk-fz5wtpmh.js.map} +1 -1
  39. package/dist/cli/chunk-hdpx1tax.js +91 -0
  40. package/dist/cli/chunk-hdpx1tax.js.map +10 -0
  41. package/dist/cli/{chunk-ct47dqpx.js → chunk-jts8mvcz.js} +67 -7
  42. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  43. package/dist/cli/{chunk-ahnw3kxw.js → chunk-jwyddg7y.js} +27 -22
  44. package/dist/cli/chunk-jwyddg7y.js.map +15 -0
  45. package/dist/cli/{chunk-cjtn640a.js → chunk-kdp5q7ke.js} +43 -20
  46. package/dist/cli/chunk-kdp5q7ke.js.map +10 -0
  47. package/dist/cli/{chunk-5g0w1e2c.js → chunk-kpf8rrjc.js} +28 -16
  48. package/dist/cli/{chunk-5g0w1e2c.js.map → chunk-kpf8rrjc.js.map} +4 -4
  49. package/dist/cli/chunk-m3vmjgmq.js +133 -0
  50. package/dist/cli/chunk-m3vmjgmq.js.map +10 -0
  51. package/dist/cli/{chunk-b27xqwn9.js → chunk-mb2919y2.js} +9 -5
  52. package/dist/cli/chunk-mb2919y2.js.map +10 -0
  53. package/dist/cli/{chunk-5qk08vmp.js → chunk-q5163e60.js} +133 -54
  54. package/dist/cli/chunk-q5163e60.js.map +11 -0
  55. package/dist/cli/chunk-qkqwkpte.js +12437 -0
  56. package/dist/cli/chunk-qkqwkpte.js.map +182 -0
  57. package/dist/cli/{chunk-4x36ddpw.js → chunk-qs4q5p4e.js} +81 -87
  58. package/dist/cli/chunk-qs4q5p4e.js.map +10 -0
  59. package/dist/cli/{chunk-0xjyb285.js → chunk-qwsrynx5.js} +15 -5
  60. package/dist/cli/{chunk-0xjyb285.js.map → chunk-qwsrynx5.js.map} +4 -4
  61. package/dist/cli/chunk-s1p84fyh.js +261 -0
  62. package/dist/cli/chunk-s1p84fyh.js.map +10 -0
  63. package/dist/cli/chunk-s6jhgk0q.js +176 -0
  64. package/dist/cli/chunk-s6jhgk0q.js.map +11 -0
  65. package/dist/cli/chunk-vtk4a6dg.js +374 -0
  66. package/dist/cli/chunk-vtk4a6dg.js.map +10 -0
  67. package/dist/cli/{chunk-k79xp7av.js → chunk-wgm7m9qk.js} +230 -700
  68. package/dist/cli/chunk-wgm7m9qk.js.map +36 -0
  69. package/dist/cli/chunk-wm7js3j9.js +145 -0
  70. package/dist/cli/chunk-wm7js3j9.js.map +11 -0
  71. package/dist/cli/chunk-yt5n7ppj.js +79 -0
  72. package/dist/cli/chunk-yt5n7ppj.js.map +10 -0
  73. package/dist/cli/{chunk-3r45185y.js → chunk-yw7dm696.js} +9 -11
  74. package/dist/cli/{chunk-3r45185y.js.map → chunk-yw7dm696.js.map} +3 -3
  75. package/dist/cli/{chunk-nn13znc2.js → chunk-zxcczpyx.js} +1 -1
  76. package/dist/cli/chunk-zxh4d9vy.js +122 -0
  77. package/dist/cli/chunk-zxh4d9vy.js.map +11 -0
  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-context.d.ts +7 -7
  85. package/dist/types/ai/ask.d.ts +368 -0
  86. package/dist/types/ai/changelog-markdown.d.ts +2 -0
  87. package/dist/types/ai/component-markdown.d.ts +2 -2
  88. package/dist/types/ai/index.d.ts +24 -0
  89. package/dist/types/ai/link-headers.d.ts +24 -0
  90. package/dist/types/ai/llms.d.ts +25 -0
  91. package/dist/types/ai/markdown.d.ts +45 -0
  92. package/dist/types/ai/mcp/discovery.d.ts +68 -0
  93. package/dist/types/ai/mcp/tools.d.ts +16 -0
  94. package/dist/types/ai/openapi-components.d.ts +43 -0
  95. package/dist/types/ai/relative-links.d.ts +26 -0
  96. package/dist/types/ai/serializers.d.ts +15 -0
  97. package/dist/types/ai/skills.d.ts +42 -0
  98. package/dist/types/ai/tar.d.ts +25 -0
  99. package/dist/types/ai/visibility.d.ts +17 -0
  100. package/dist/types/ai/web-bot-auth.d.ts +16 -0
  101. package/dist/types/analytics/adobe.d.ts +35 -0
  102. package/dist/types/analytics/amplitude.d.ts +50 -0
  103. package/dist/types/analytics/clarity.d.ts +31 -0
  104. package/dist/types/analytics/clearbit.d.ts +29 -0
  105. package/dist/types/analytics/cloudflare.d.ts +41 -0
  106. package/dist/types/analytics/fathom.d.ts +41 -0
  107. package/dist/types/analytics/google-analytics.d.ts +44 -0
  108. package/dist/types/analytics/google-tag-manager.d.ts +40 -0
  109. package/dist/types/analytics/head.d.ts +30 -0
  110. package/dist/types/analytics/heap.d.ts +40 -0
  111. package/dist/types/analytics/hightouch.d.ts +44 -0
  112. package/dist/types/analytics/hotjar.d.ts +33 -0
  113. package/dist/types/analytics/index.d.ts +61 -0
  114. package/dist/types/analytics/inline.d.ts +17 -0
  115. package/dist/types/analytics/logrocket.d.ts +44 -0
  116. package/dist/types/analytics/mixpanel.d.ts +67 -0
  117. package/dist/types/analytics/pirsch.d.ts +42 -0
  118. package/dist/types/analytics/plausible.d.ts +56 -0
  119. package/dist/types/analytics/posthog.d.ts +45 -0
  120. package/dist/types/analytics/schema.d.ts +320 -0
  121. package/dist/types/analytics/script.d.ts +46 -0
  122. package/dist/types/analytics/segment.d.ts +50 -0
  123. package/dist/types/analytics/vercel.d.ts +48 -0
  124. package/dist/types/astro/integration.d.ts +76 -0
  125. package/dist/types/astro/markdown-negotiation.d.ts +23 -0
  126. package/dist/types/astro/module-types.d.ts +14 -0
  127. package/dist/types/astro/pages.d.ts +44 -0
  128. package/dist/types/cli/env.d.ts +12 -0
  129. package/dist/types/cli/init/scaffold.d.ts +154 -0
  130. package/dist/types/components/layout/nav-utils.d.ts +11 -0
  131. package/dist/types/core/adapter.d.ts +47 -0
  132. package/dist/types/core/api-name.d.ts +7 -0
  133. package/dist/types/core/changelog-index.d.ts +13 -0
  134. package/dist/types/core/config-input.d.ts +197 -526
  135. package/dist/types/core/config.d.ts +61 -35
  136. package/dist/types/core/content-assets.d.ts +76 -0
  137. package/dist/types/core/custom-pages.d.ts +33 -0
  138. package/dist/types/core/data.d.ts +30 -15
  139. package/dist/types/core/define-components.d.ts +12 -9
  140. package/dist/types/core/deployment-env.d.ts +6 -11
  141. package/dist/types/core/frontmatter.d.ts +10 -0
  142. package/dist/types/core/graph.d.ts +18 -0
  143. package/dist/types/core/heading-markers.d.ts +54 -0
  144. package/dist/types/core/i18n-ui.d.ts +12 -10
  145. package/dist/types/core/i18n.d.ts +87 -0
  146. package/dist/types/core/includes.d.ts +138 -0
  147. package/dist/types/core/last-modified.d.ts +47 -0
  148. package/dist/types/core/links.d.ts +95 -0
  149. package/dist/types/core/locale-links.d.ts +60 -0
  150. package/dist/types/core/manifest.d.ts +17 -0
  151. package/dist/types/core/meta.d.ts +38 -0
  152. package/dist/types/core/nav-diagnostics.d.ts +26 -0
  153. package/dist/types/core/navigation.d.ts +6 -0
  154. package/dist/types/core/node-require.d.ts +19 -0
  155. package/dist/types/core/package-json.d.ts +13 -0
  156. package/dist/types/core/probe.d.ts +42 -0
  157. package/dist/types/core/project-graph.d.ts +51 -0
  158. package/dist/types/core/project.d.ts +2 -0
  159. package/dist/types/core/safe-href.d.ts +2 -0
  160. package/dist/types/core/safe-links.d.ts +26 -0
  161. package/dist/types/core/schema.d.ts +2284 -454
  162. package/dist/types/core/site-url.d.ts +16 -0
  163. package/dist/types/core/sources/assets.d.ts +36 -0
  164. package/dist/types/core/sources/cache.d.ts +37 -0
  165. package/dist/types/core/sources/collection.d.ts +28 -0
  166. package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
  167. package/dist/types/core/sources/contentful.d.ts +40 -0
  168. package/dist/types/core/sources/filesystem.d.ts +24 -0
  169. package/dist/types/core/sources/github-releases.d.ts +31 -0
  170. package/dist/types/core/sources/json.d.ts +30 -0
  171. package/dist/types/core/sources/lexical.d.ts +19 -0
  172. package/dist/types/core/sources/lower.d.ts +75 -0
  173. package/dist/types/core/sources/mdx-remote.d.ts +28 -0
  174. package/dist/types/core/sources/normalize.d.ts +147 -0
  175. package/dist/types/core/sources/notion.d.ts +131 -0
  176. package/dist/types/core/sources/obsidian.d.ts +46 -0
  177. package/dist/types/core/sources/payload.d.ts +39 -0
  178. package/dist/types/core/sources/portable-text.d.ts +42 -0
  179. package/dist/types/core/sources/read.d.ts +23 -0
  180. package/dist/types/core/sources/remote.d.ts +74 -0
  181. package/dist/types/core/sources/resolve.d.ts +19 -0
  182. package/dist/types/core/sources/sanity.d.ts +43 -0
  183. package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
  184. package/dist/types/core/sources/strapi.d.ts +33 -0
  185. package/dist/types/core/sources/types.d.ts +7 -0
  186. package/dist/types/core/sources/watch.d.ts +45 -0
  187. package/dist/types/core/text-width.d.ts +11 -0
  188. package/dist/types/core/types.d.ts +15 -1
  189. package/dist/types/core/unrecognized-keys.d.ts +7 -0
  190. package/dist/types/core/versions.d.ts +72 -0
  191. package/dist/types/core/yaml.d.ts +9 -0
  192. package/dist/types/deploy/adapter-output.d.ts +45 -0
  193. package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
  194. package/dist/types/deploy/adapters/index.d.ts +29 -0
  195. package/dist/types/deploy/adapters/netlify.d.ts +37 -0
  196. package/dist/types/deploy/adapters/node.d.ts +37 -0
  197. package/dist/types/deploy/adapters/registry.d.ts +133 -0
  198. package/dist/types/deploy/adapters/types.d.ts +71 -0
  199. package/dist/types/deploy/adapters/vercel.d.ts +38 -0
  200. package/dist/types/deploy/artifacts.d.ts +65 -0
  201. package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
  202. package/dist/types/deploy/function-bundle.d.ts +80 -0
  203. package/dist/types/deploy/headers.d.ts +50 -0
  204. package/dist/types/deploy/node-headers.d.ts +42 -0
  205. package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
  206. package/dist/types/deploy/platforms/index.d.ts +18 -0
  207. package/dist/types/deploy/platforms/netlify.d.ts +13 -0
  208. package/dist/types/deploy/platforms/node.d.ts +11 -0
  209. package/dist/types/deploy/platforms/paths.d.ts +27 -0
  210. package/dist/types/deploy/platforms/static.d.ts +10 -0
  211. package/dist/types/deploy/platforms/types.d.ts +104 -0
  212. package/dist/types/deploy/platforms/vercel.d.ts +32 -0
  213. package/dist/types/deploy/redirects.d.ts +53 -0
  214. package/dist/types/deploy/robots.d.ts +8 -0
  215. package/dist/types/deploy/rss.d.ts +31 -0
  216. package/dist/types/deploy/sitemap.d.ts +21 -0
  217. package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
  218. package/dist/types/markdown/code-title.d.ts +32 -0
  219. package/dist/types/markdown/fence-meta.d.ts +23 -0
  220. package/dist/types/markdown/themes.d.ts +3 -3
  221. package/dist/types/openapi/asyncapi.d.ts +129 -0
  222. package/dist/types/openapi/graphql-build.d.ts +8 -0
  223. package/dist/types/openapi/graphql.d.ts +122 -0
  224. package/dist/types/openapi/model.d.ts +158 -0
  225. package/dist/types/openapi/parse.d.ts +57 -0
  226. package/dist/types/openapi/references.d.ts +37 -25
  227. package/dist/types/openapi/render-mdx.d.ts +33 -0
  228. package/dist/types/openapi/sentence.d.ts +7 -0
  229. package/dist/types/openapi/signature.d.ts +10 -0
  230. package/dist/types/openapi/source.d.ts +22 -0
  231. package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
  232. package/dist/types/reference/asyncapi.d.ts +166 -0
  233. package/dist/types/reference/graphql.d.ts +181 -0
  234. package/dist/types/reference/index.d.ts +32 -0
  235. package/dist/types/reference/openapi.d.ts +165 -0
  236. package/dist/types/reference/options.d.ts +157 -0
  237. package/dist/types/reference/scalar.d.ts +136 -0
  238. package/dist/types/reference/schema.d.ts +630 -0
  239. package/dist/types/search/adapters/algolia.d.ts +32 -0
  240. package/dist/types/search/adapters/flexsearch.d.ts +12 -0
  241. package/dist/types/search/adapters/index.d.ts +31 -0
  242. package/dist/types/search/adapters/mixedbread.d.ts +22 -0
  243. package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
  244. package/dist/types/search/adapters/orama.d.ts +13 -0
  245. package/dist/types/search/adapters/pagefind.d.ts +12 -0
  246. package/dist/types/search/adapters/registry.d.ts +228 -0
  247. package/dist/types/search/adapters/types.d.ts +34 -0
  248. package/dist/types/search/adapters/typesense.d.ts +42 -0
  249. package/dist/types/search/build.d.ts +23 -0
  250. package/dist/types/search/documents.d.ts +90 -0
  251. package/dist/types/search/facets.d.ts +3 -0
  252. package/dist/types/search/orama-index.d.ts +1 -1
  253. package/dist/types/search/sync/algolia.d.ts +14 -0
  254. package/dist/types/search/sync/index.d.ts +14 -0
  255. package/dist/types/search/sync/orama-cloud.d.ts +10 -0
  256. package/dist/types/search/sync/typesense.d.ts +14 -0
  257. package/dist/types/sources/contentful.d.ts +68 -0
  258. package/dist/types/sources/custom.d.ts +20 -0
  259. package/dist/types/sources/filesystem.d.ts +42 -0
  260. package/dist/types/sources/github-releases.d.ts +46 -0
  261. package/dist/types/sources/index.d.ts +48 -0
  262. package/dist/types/sources/mdx-remote.d.ts +63 -0
  263. package/dist/types/sources/notion.d.ts +65 -0
  264. package/dist/types/sources/obsidian.d.ts +34 -0
  265. package/dist/types/sources/payload.d.ts +68 -0
  266. package/dist/types/sources/registry.d.ts +1414 -0
  267. package/dist/types/sources/sanity.d.ts +69 -0
  268. package/dist/types/sources/shared.d.ts +46 -0
  269. package/dist/types/sources/strapi.d.ts +66 -0
  270. package/dist/types/theme/icon-kind.d.ts +11 -0
  271. package/dist/types/theme/icons.d.ts +20 -0
  272. package/docs/01-quickstart.mdx +18 -18
  273. package/docs/02-deployment.mdx +52 -28
  274. package/docs/03-upgrading.mdx +364 -0
  275. package/docs/04-migrating.mdx +58 -0
  276. package/docs/08-faq.mdx +5 -5
  277. package/docs/advanced/blog.mdx +13 -6
  278. package/docs/advanced/changelog.mdx +23 -35
  279. package/docs/advanced/custom-pages.mdx +22 -10
  280. package/docs/advanced/meta.ts +1 -8
  281. package/docs/advanced/skills.mdx +8 -0
  282. package/docs/cli/audit.mdx +646 -0
  283. package/docs/cli/doctor.mdx +29 -0
  284. package/docs/{reference/eval.mdx → cli/evals.mdx} +12 -11
  285. package/docs/cli/index.mdx +105 -0
  286. package/docs/cli/meta.ts +7 -0
  287. package/docs/{reference → cli}/translate.mdx +9 -9
  288. package/docs/cli/validate.mdx +41 -0
  289. package/docs/cli/version.mdx +40 -0
  290. package/docs/configuration/analytics.mdx +350 -59
  291. package/docs/configuration/assistant.mdx +357 -0
  292. package/docs/configuration/customization.mdx +16 -10
  293. package/docs/configuration/index.mdx +50 -31
  294. package/docs/configuration/meta.ts +1 -1
  295. package/docs/configuration/search.mdx +76 -55
  296. package/docs/configuration/theming.mdx +24 -15
  297. package/docs/content/components.mdx +21 -5
  298. package/docs/{reference → content}/frontmatter.mdx +35 -1
  299. package/docs/content/i18n.mdx +9 -7
  300. package/docs/content/includes.mdx +2 -4
  301. package/docs/content/index.mdx +1 -1
  302. package/docs/content/islands.mdx +11 -6
  303. package/docs/content/meta.mdx +1 -1
  304. package/docs/content/meta.ts +1 -0
  305. package/docs/content/navigation.mdx +9 -7
  306. package/docs/content/sources.mdx +145 -50
  307. package/docs/content/syntax.mdx +16 -12
  308. package/docs/content/versioning.mdx +3 -3
  309. package/docs/discoverability/agent-discovery.mdx +12 -12
  310. package/docs/discoverability/index.mdx +4 -4
  311. package/docs/discoverability/json-api.mdx +4 -4
  312. package/docs/discoverability/llms-txt.mdx +6 -6
  313. package/docs/discoverability/markdown.mdx +4 -4
  314. package/docs/discoverability/mcp.mdx +11 -11
  315. package/docs/discoverability/sitemap-and-robots.mdx +3 -3
  316. package/docs/index.mdx +4 -4
  317. package/docs/references/asyncapi.mdx +59 -0
  318. package/docs/{advanced → references}/graphql.mdx +45 -32
  319. package/docs/{reference → references}/meta.ts +2 -2
  320. package/docs/references/openapi.mdx +171 -0
  321. package/docs/references/scalar.mdx +64 -0
  322. package/package.json +42 -8
  323. package/skills/blume/SKILL.md +23 -10
  324. package/skills/blume-migrate/SKILL.md +22 -21
  325. package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
  326. package/skills/blume-migrate/references/docusaurus.md +5 -4
  327. package/skills/blume-migrate/references/fumadocs.md +4 -4
  328. package/skills/blume-migrate/references/mintlify.md +23 -8
  329. package/skills/blume-migrate/references/monorepo.md +6 -6
  330. package/skills/blume-migrate/references/starlight.md +4 -4
  331. package/src/ai/agent-readability.ts +25 -26
  332. package/src/ai/ai-catalog.ts +50 -32
  333. package/src/ai/api/handlers.ts +46 -11
  334. package/src/ai/api/paths.ts +1 -1
  335. package/src/ai/api-catalog.ts +8 -8
  336. package/src/ai/ask-context.ts +7 -7
  337. package/src/ai/ask-data.ts +3 -3
  338. package/src/ai/ask.ts +637 -100
  339. package/src/ai/changelog-markdown.ts +91 -0
  340. package/src/ai/component-markdown.ts +328 -12
  341. package/src/ai/cors.ts +3 -3
  342. package/src/ai/index.ts +45 -0
  343. package/src/ai/link-headers.ts +7 -6
  344. package/src/ai/llms.ts +20 -15
  345. package/src/ai/markdown.ts +35 -5
  346. package/src/ai/mcp/data.ts +9 -5
  347. package/src/ai/openapi-components.ts +5 -2
  348. package/src/ai/relative-links.ts +170 -0
  349. package/src/ai/serializers.ts +3 -3
  350. package/src/ai/skills.ts +1 -1
  351. package/src/ai/visibility.ts +1 -1
  352. package/src/ai/web-bot-auth.ts +2 -2
  353. package/src/analytics/adobe.ts +46 -0
  354. package/src/analytics/amplitude.ts +79 -0
  355. package/src/analytics/clarity.ts +46 -0
  356. package/src/analytics/clearbit.ts +47 -0
  357. package/src/analytics/cloudflare.ts +67 -0
  358. package/src/analytics/fathom.ts +64 -0
  359. package/src/analytics/google-analytics.ts +84 -0
  360. package/src/analytics/google-tag-manager.ts +61 -0
  361. package/src/analytics/head.ts +130 -0
  362. package/src/analytics/heap.ts +65 -0
  363. package/src/analytics/hightouch.ts +77 -0
  364. package/src/analytics/hotjar.ts +47 -0
  365. package/src/analytics/index.ts +71 -0
  366. package/src/analytics/inline.ts +21 -0
  367. package/src/analytics/logrocket.ts +71 -0
  368. package/src/analytics/mixpanel.ts +92 -0
  369. package/src/analytics/pirsch.ts +66 -0
  370. package/src/analytics/plausible.ts +83 -0
  371. package/src/analytics/posthog.ts +78 -0
  372. package/src/analytics/schema.ts +60 -0
  373. package/src/analytics/script.ts +60 -0
  374. package/src/analytics/segment.ts +85 -0
  375. package/src/analytics/vercel.ts +50 -0
  376. package/src/astro/adapter-root.ts +7 -9
  377. package/src/astro/component-slots.ts +131 -91
  378. package/src/astro/generate.ts +129 -361
  379. package/src/astro/integration.ts +2 -6
  380. package/src/astro/module-types.ts +1 -1
  381. package/src/astro/pages.ts +13 -101
  382. package/src/astro/render-deps.ts +379 -0
  383. package/src/astro/runtime-deps.ts +201 -0
  384. package/src/astro/templates.ts +575 -562
  385. package/src/audit/agent.ts +24 -0
  386. package/src/audit/catalog.ts +2 -2
  387. package/src/audit/checks/assets.ts +2 -2
  388. package/src/audit/checks/dns-aid.ts +1 -1
  389. package/src/audit/checks/duplicates.ts +3 -1
  390. package/src/audit/checks/i18n.ts +1 -1
  391. package/src/audit/checks/indexability.ts +7 -7
  392. package/src/audit/checks/links.ts +2 -2
  393. package/src/audit/checks/llms.ts +6 -6
  394. package/src/audit/checks/network.ts +3 -3
  395. package/src/audit/checks/og-image.ts +2 -2
  396. package/src/audit/checks/robots.ts +1 -1
  397. package/src/audit/checks/sitemap.ts +2 -2
  398. package/src/audit/checks/social.ts +1 -1
  399. package/src/audit/run.ts +4 -3
  400. package/src/audit/terms.ts +31 -0
  401. package/src/audit/url.ts +13 -13
  402. package/src/blume-modules.d.ts +2 -2
  403. package/src/cli/command-meta.ts +10 -0
  404. package/src/cli/commands/audit.ts +40 -21
  405. package/src/cli/commands/build.ts +71 -316
  406. package/src/cli/commands/check.ts +1 -0
  407. package/src/cli/commands/dev.ts +40 -1
  408. package/src/cli/commands/doctor.ts +92 -14
  409. package/src/cli/commands/eject.ts +44 -7
  410. package/src/cli/commands/eval.ts +5 -5
  411. package/src/cli/commands/init.ts +161 -41
  412. package/src/cli/commands/migrate.ts +121 -0
  413. package/src/cli/commands/preview.ts +7 -1
  414. package/src/cli/commands/translate.ts +5 -5
  415. package/src/cli/commands/upgrade.ts +141 -0
  416. package/src/cli/commands/version.ts +57 -44
  417. package/src/cli/eject-scripts.ts +121 -6
  418. package/src/cli/index.ts +11 -1
  419. package/src/cli/init/install.ts +70 -0
  420. package/src/cli/init/questions.ts +7 -0
  421. package/src/cli/init/scaffold.ts +459 -75
  422. package/src/cli/lazy-command.ts +37 -1
  423. package/src/cli/prepare.ts +40 -6
  424. package/src/cli/required-secrets.ts +39 -12
  425. package/src/cli/unknown-flags.ts +266 -0
  426. package/src/cli/yarn-pnp.ts +52 -0
  427. package/src/components/content/AccordionItem.astro +7 -1
  428. package/src/components/content/Card.astro +4 -3
  429. package/src/components/content/ColorItem.astro +22 -4
  430. package/src/components/content/GithubInfo.astro +2 -2
  431. package/src/components/content/Prompt.astro +25 -25
  432. package/src/components/content/Tabs.astro +3 -1
  433. package/src/components/content/Tile.astro +2 -3
  434. package/src/components/content/Tooltip.astro +57 -9
  435. package/src/components/content/content-strings.ts +34 -0
  436. package/src/components/content/diff.ts +1 -1
  437. package/src/components/content/mermaid-element.ts +13 -2
  438. package/src/components/content/prompt-markdown.ts +292 -0
  439. package/src/components/content/tooltip-id.ts +41 -0
  440. package/src/components/copy-feedback.ts +1 -1
  441. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  442. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +23 -23
  443. package/src/components/islands/hooks.ts +36 -11
  444. package/src/components/layout/Analytics.astro +21 -79
  445. package/src/components/layout/Banner.astro +3 -1
  446. package/src/components/layout/DiscoveryLinks.astro +69 -0
  447. package/src/components/layout/Header.astro +93 -30
  448. package/src/components/layout/LanguageSwitcher.astro +4 -1
  449. package/src/components/layout/Logo.astro +23 -2
  450. package/src/components/layout/NavSelector.astro +13 -2
  451. package/src/components/layout/NavTree.astro +12 -3
  452. package/src/components/layout/NavTreeScript.astro +17 -3
  453. package/src/components/layout/PageActions.astro +3 -3
  454. package/src/components/layout/PageFeedback.astro +11 -1
  455. package/src/components/layout/PageLayout.astro +58 -11
  456. package/src/components/layout/Pagination.astro +7 -7
  457. package/src/components/layout/ReferenceLayout.astro +22 -13
  458. package/src/components/layout/RootLayout.astro +75 -72
  459. package/src/components/layout/Search.astro +43 -19
  460. package/src/components/layout/WebMcp.astro +1 -1
  461. package/src/components/layout/analytics-client.ts +73 -16
  462. package/src/components/layout/drawer-inert.ts +113 -15
  463. package/src/components/layout/dropdown-clamp.ts +105 -0
  464. package/src/components/layout/head-scripts.ts +15 -6
  465. package/src/components/layout/nav-utils.ts +17 -0
  466. package/src/components/layout/search/algolia.ts +8 -8
  467. package/src/components/layout/search/orama-cloud.ts +9 -7
  468. package/src/components/layout/search/typesense.ts +11 -17
  469. package/src/components/openapi/MessageComposer.astro +2 -2
  470. package/src/components/openapi/Playground.astro +2 -2
  471. package/src/components/openapi/description.ts +2 -2
  472. package/src/components/openapi/playground-client.ts +79 -10
  473. package/src/core/changelog-index.ts +24 -0
  474. package/src/core/code-fences.ts +1 -1
  475. package/src/core/component-overrides.ts +399 -154
  476. package/src/core/config-input.ts +201 -566
  477. package/src/core/config.ts +131 -42
  478. package/src/core/custom-pages.ts +105 -0
  479. package/src/core/data.ts +34 -15
  480. package/src/core/define-components.ts +12 -9
  481. package/src/core/deployment-env.ts +18 -74
  482. package/src/core/diagnostics.ts +14 -7
  483. package/src/core/graph.ts +69 -25
  484. package/src/core/i18n-ui.ts +41 -11
  485. package/src/core/i18n.ts +17 -0
  486. package/src/core/includes.ts +156 -38
  487. package/src/core/last-modified.ts +6 -11
  488. package/src/core/links.ts +166 -2
  489. package/src/core/manifest.ts +10 -3
  490. package/src/core/navigation.ts +115 -16
  491. package/src/core/new-tab.ts +35 -0
  492. package/src/core/node-require.ts +21 -0
  493. package/src/core/project-graph.ts +42 -26
  494. package/src/core/project.ts +9 -4
  495. package/src/core/request-body.ts +61 -0
  496. package/src/core/safe-href.ts +28 -0
  497. package/src/core/safe-links.ts +68 -0
  498. package/src/core/schema.ts +528 -745
  499. package/src/core/server-features.ts +10 -11
  500. package/src/core/sources/assets.ts +47 -11
  501. package/src/core/sources/collection.ts +67 -0
  502. package/src/core/sources/contentful-rich-text.ts +285 -0
  503. package/src/core/sources/contentful.ts +173 -0
  504. package/src/core/sources/github-releases.ts +51 -1
  505. package/src/core/sources/json.ts +71 -0
  506. package/src/core/sources/lexical.ts +195 -0
  507. package/src/core/sources/lower.ts +226 -0
  508. package/src/core/sources/normalize.ts +52 -2
  509. package/src/core/sources/notion.ts +39 -28
  510. package/src/core/sources/payload.ts +135 -0
  511. package/src/core/sources/portable-text.ts +11 -16
  512. package/src/core/sources/remote.ts +226 -0
  513. package/src/core/sources/resolve.ts +104 -166
  514. package/src/core/sources/sanity.ts +16 -49
  515. package/src/core/sources/strapi-blocks.ts +124 -0
  516. package/src/core/sources/strapi.ts +191 -0
  517. package/src/core/sources/types.ts +12 -1
  518. package/src/core/types.ts +15 -1
  519. package/src/core/ui-packs/ar.ts +10 -7
  520. package/src/core/ui-packs/bg.ts +10 -7
  521. package/src/core/ui-packs/bn.ts +10 -7
  522. package/src/core/ui-packs/ca.ts +7 -6
  523. package/src/core/ui-packs/cs.ts +10 -7
  524. package/src/core/ui-packs/da.ts +10 -7
  525. package/src/core/ui-packs/de.ts +10 -7
  526. package/src/core/ui-packs/el.ts +7 -6
  527. package/src/core/ui-packs/es.ts +7 -6
  528. package/src/core/ui-packs/fa.ts +10 -7
  529. package/src/core/ui-packs/fi.ts +10 -7
  530. package/src/core/ui-packs/fr.ts +7 -6
  531. package/src/core/ui-packs/he.ts +10 -7
  532. package/src/core/ui-packs/hi.ts +10 -7
  533. package/src/core/ui-packs/hr.ts +10 -7
  534. package/src/core/ui-packs/hu.ts +10 -7
  535. package/src/core/ui-packs/id.ts +10 -7
  536. package/src/core/ui-packs/it.ts +7 -6
  537. package/src/core/ui-packs/ja.ts +7 -6
  538. package/src/core/ui-packs/ko.ts +7 -6
  539. package/src/core/ui-packs/nl.ts +10 -7
  540. package/src/core/ui-packs/no.ts +10 -7
  541. package/src/core/ui-packs/pl.ts +10 -7
  542. package/src/core/ui-packs/pt-br.ts +7 -6
  543. package/src/core/ui-packs/pt.ts +7 -6
  544. package/src/core/ui-packs/ro.ts +10 -7
  545. package/src/core/ui-packs/ru.ts +10 -7
  546. package/src/core/ui-packs/sk.ts +10 -7
  547. package/src/core/ui-packs/sr.ts +10 -7
  548. package/src/core/ui-packs/sv.ts +10 -7
  549. package/src/core/ui-packs/th.ts +7 -6
  550. package/src/core/ui-packs/tr.ts +10 -7
  551. package/src/core/ui-packs/uk.ts +10 -7
  552. package/src/core/ui-packs/vi.ts +7 -6
  553. package/src/core/ui-packs/zh-tw.ts +7 -6
  554. package/src/core/ui-packs/zh.ts +7 -6
  555. package/src/core/unrecognized-keys.ts +10 -0
  556. package/src/core/version-cut.ts +116 -8
  557. package/src/deploy/adapter-output.ts +57 -97
  558. package/src/deploy/adapters/cloudflare.ts +39 -0
  559. package/src/deploy/adapters/index.ts +41 -0
  560. package/src/deploy/adapters/netlify.ts +34 -0
  561. package/src/deploy/adapters/node.ts +34 -0
  562. package/src/deploy/adapters/registry.ts +127 -0
  563. package/src/deploy/adapters/types.ts +92 -0
  564. package/src/deploy/adapters/vercel.ts +35 -0
  565. package/src/deploy/artifacts.ts +55 -27
  566. package/src/deploy/cloudflare-negotiation.ts +179 -100
  567. package/src/deploy/function-bundle.ts +18 -3
  568. package/src/deploy/headers.ts +124 -23
  569. package/src/deploy/node-headers.ts +198 -0
  570. package/src/deploy/platforms/cloudflare.ts +312 -0
  571. package/src/deploy/platforms/index.ts +67 -0
  572. package/src/deploy/platforms/netlify.ts +52 -0
  573. package/src/deploy/platforms/node.ts +40 -0
  574. package/src/deploy/platforms/paths.ts +42 -0
  575. package/src/deploy/platforms/static.ts +29 -0
  576. package/src/deploy/platforms/types.ts +112 -0
  577. package/src/deploy/platforms/vercel.ts +193 -0
  578. package/src/deploy/redirects.ts +30 -18
  579. package/src/deploy/robots.ts +3 -3
  580. package/src/deploy/rss.ts +2 -2
  581. package/src/deploy/sitemap.ts +2 -2
  582. package/src/deploy/vercel-negotiation.ts +2 -2
  583. package/src/eval/agents.ts +32 -1
  584. package/src/eval/findings.ts +19 -11
  585. package/src/eval/report.ts +10 -2
  586. package/src/markdown/external-links.ts +65 -0
  587. package/src/markdown/include.ts +45 -27
  588. package/src/markdown/index.ts +34 -9
  589. package/src/markdown/inline-code.ts +12 -6
  590. package/src/markdown/relative-links.ts +324 -0
  591. package/src/markdown/themes.ts +3 -3
  592. package/src/migrate/migrate.ts +149 -0
  593. package/src/openapi/parse.ts +64 -16
  594. package/src/openapi/proxy.ts +62 -10
  595. package/src/openapi/references.ts +147 -147
  596. package/src/openapi/render-mdx.ts +32 -10
  597. package/src/openapi/scalar.ts +15 -13
  598. package/src/openapi/sentence.ts +14 -0
  599. package/src/openapi/source.ts +37 -21
  600. package/src/openapi/spec-dependency-error.ts +15 -0
  601. package/src/reference/asyncapi.ts +83 -0
  602. package/src/reference/graphql.ts +89 -0
  603. package/src/reference/index.ts +40 -0
  604. package/src/reference/openapi.ts +83 -0
  605. package/src/reference/options.ts +201 -0
  606. package/src/reference/scalar.ts +136 -0
  607. package/src/reference/schema.ts +88 -0
  608. package/src/registry/eject.ts +109 -53
  609. package/src/search/adapters/algolia.ts +46 -0
  610. package/src/search/adapters/flexsearch.ts +29 -0
  611. package/src/search/adapters/index.ts +39 -0
  612. package/src/search/adapters/mixedbread.ts +39 -0
  613. package/src/search/adapters/orama-cloud.ts +51 -0
  614. package/src/search/adapters/orama.ts +24 -0
  615. package/src/search/adapters/pagefind.ts +27 -0
  616. package/src/search/adapters/registry.ts +131 -0
  617. package/src/search/adapters/types.ts +46 -0
  618. package/src/search/adapters/typesense.ts +57 -0
  619. package/src/search/build.ts +8 -4
  620. package/src/search/documents.ts +8 -2
  621. package/src/search/orama-index.ts +1 -1
  622. package/src/search/sync/algolia.ts +12 -11
  623. package/src/search/sync/index.ts +35 -20
  624. package/src/search/sync/orama-cloud.ts +12 -9
  625. package/src/search/sync/typesense.ts +15 -13
  626. package/src/sources/contentful.ts +63 -0
  627. package/src/sources/custom.ts +38 -0
  628. package/src/sources/filesystem.ts +51 -0
  629. package/src/sources/github-releases.ts +52 -0
  630. package/src/sources/index.ts +60 -0
  631. package/src/sources/mdx-remote.ts +76 -0
  632. package/src/sources/notion.ts +63 -0
  633. package/src/sources/obsidian.ts +38 -0
  634. package/src/sources/payload.ts +60 -0
  635. package/src/sources/registry.ts +182 -0
  636. package/src/sources/sanity.ts +66 -0
  637. package/src/sources/shared.ts +52 -0
  638. package/src/sources/strapi.ts +58 -0
  639. package/src/theme/entry.ts +24 -2
  640. package/src/translate/report.ts +40 -7
  641. package/src/upgrade/upgrade.ts +499 -0
  642. package/dist/cli/chunk-0qymqwzz.js +0 -164
  643. package/dist/cli/chunk-0qymqwzz.js.map +0 -15
  644. package/dist/cli/chunk-1jefwnfs.js.map +0 -48
  645. package/dist/cli/chunk-2mzebbbz.js +0 -69
  646. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  647. package/dist/cli/chunk-4x36ddpw.js.map +0 -11
  648. package/dist/cli/chunk-5093q3n7.js +0 -68
  649. package/dist/cli/chunk-5093q3n7.js.map +0 -10
  650. package/dist/cli/chunk-5qk08vmp.js.map +0 -11
  651. package/dist/cli/chunk-7s8hm3b6.js +0 -5347
  652. package/dist/cli/chunk-7s8hm3b6.js.map +0 -58
  653. package/dist/cli/chunk-8cjtbafj.js.map +0 -13
  654. package/dist/cli/chunk-97r59kpr.js +0 -381
  655. package/dist/cli/chunk-97r59kpr.js.map +0 -12
  656. package/dist/cli/chunk-ahnw3kxw.js.map +0 -15
  657. package/dist/cli/chunk-b27xqwn9.js.map +0 -10
  658. package/dist/cli/chunk-bf6bt1xt.js +0 -185
  659. package/dist/cli/chunk-bf6bt1xt.js.map +0 -11
  660. package/dist/cli/chunk-cjtn640a.js.map +0 -10
  661. package/dist/cli/chunk-ct47dqpx.js.map +0 -11
  662. package/dist/cli/chunk-esphfr8p.js +0 -107
  663. package/dist/cli/chunk-esphfr8p.js.map +0 -11
  664. package/dist/cli/chunk-ex56aa81.js +0 -1016
  665. package/dist/cli/chunk-ex56aa81.js.map +0 -13
  666. package/dist/cli/chunk-garjf5z9.js +0 -30
  667. package/dist/cli/chunk-garjf5z9.js.map +0 -10
  668. package/dist/cli/chunk-js7saxwm.js +0 -1045
  669. package/dist/cli/chunk-js7saxwm.js.map +0 -22
  670. package/dist/cli/chunk-k79xp7av.js.map +0 -39
  671. package/dist/cli/chunk-ps4m1xh4.js.map +0 -15
  672. package/dist/cli/chunk-q4rae3bg.js +0 -60
  673. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  674. package/dist/cli/chunk-rz9jmfhz.js +0 -108
  675. package/dist/cli/chunk-rz9jmfhz.js.map +0 -10
  676. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  677. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  678. package/dist/cli/chunk-vrfp10qk.js +0 -81
  679. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  680. package/dist/cli/chunk-yg63d42r.js.map +0 -34
  681. package/docs/advanced/api-reference.mdx +0 -240
  682. package/docs/configuration/ask-ai.mdx +0 -256
  683. package/docs/reference/cli.mdx +0 -197
  684. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  685. package/src/components/content/changelog-element.ts +0 -69
  686. package/src/search/providers.ts +0 -91
  687. /package/dist/cli/{chunk-vacwm2hv.js.map → chunk-27g6wdth.js.map} +0 -0
  688. /package/dist/cli/{chunk-dwgcp5sm.js.map → chunk-ce574jw2.js.map} +0 -0
  689. /package/dist/cli/{chunk-e7f42gdj.js.map → chunk-fxypxtvm.js.map} +0 -0
  690. /package/dist/cli/{chunk-nn13znc2.js.map → chunk-zxcczpyx.js.map} +0 -0
@@ -28,9 +28,10 @@ import type { ResolvedConfig } from "../core/schema.ts";
28
28
  import { buildSearchIndex } from "../search/build.ts";
29
29
  import { syncSearchProvider } from "../search/sync/index.ts";
30
30
  import { readsHeaderFiles } from "./adapter-output.ts";
31
- 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";
32
34
  import {
33
- buildNetlifyRedirects,
34
35
  buildRedirectManifest,
35
36
  buildVercelConfig,
36
37
  platformRedirects,
@@ -63,38 +64,63 @@ export interface ArtifactLogger {
63
64
  }
64
65
 
65
66
  /**
66
- * Emit platform redirect files for a static build (adapters wire redirects
67
- * natively). Always writes the manifest; writes `_redirects`/`vercel.json` only
68
- * 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
69
74
  * git-integration builds read `vercel.json` from the repository root only —
70
75
  * the copy emitted here takes effect when the dist folder itself is deployed
71
76
  * directly via the Vercel CLI.
72
77
  */
73
78
  const emitRedirectFiles = async (
74
- config: ResolvedConfig,
79
+ project: BlumeProject,
75
80
  distDir: string,
76
81
  logger: ArtifactLogger
77
82
  ): Promise<void> => {
78
- const redirects = platformRedirects(config);
79
- if (redirects.length === 0 || config.deployment.output !== "static") {
83
+ const { config } = project;
84
+ if (config.deployment.options.output !== "static") {
80
85
  return;
81
86
  }
82
- await writeFile(
83
- join(distDir, "blume-redirects.json"),
84
- buildRedirectManifest(redirects),
85
- "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))
86
100
  );
87
- const platformFiles = [
88
- { content: buildNetlifyRedirects(redirects), name: "_redirects" },
89
- { content: buildVercelConfig(redirects), name: "vercel.json" },
90
- ];
91
101
  await Promise.all(
92
- platformFiles.map((file) =>
93
- existsSync(join(distDir, file.name))
94
- ? Promise.resolve()
95
- : 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
+ )
96
110
  )
97
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
+ }
98
124
  logger.info(`Emitted redirect files for ${redirects.length} redirect(s)`);
99
125
  };
100
126
 
@@ -161,7 +187,7 @@ export const emitHeaderFiles = async (
161
187
  };
162
188
 
163
189
  /**
164
- * 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
165
191
  * skills surface and llms.txt (which lists them) read the same set. Empty
166
192
  * when the feature is off, the directory is missing, nothing in it is
167
193
  * publishable (each with a warning), or a user-shipped
@@ -172,14 +198,14 @@ const collectConfiguredSkills = async (
172
198
  distDir: string,
173
199
  logger: ArtifactLogger
174
200
  ): Promise<SkillArtifact[]> => {
175
- const configured = project.config.ai.skills;
201
+ const configured = project.config.agents.skills;
176
202
  if (!configured) {
177
203
  return [];
178
204
  }
179
205
  const dir = resolve(project.context.root, configured);
180
206
  if (!existsSync(dir)) {
181
207
  logger.warn(
182
- `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.`
183
209
  );
184
210
  return [];
185
211
  }
@@ -191,7 +217,9 @@ const collectConfiguredSkills = async (
191
217
  logger.warn(warning);
192
218
  }
193
219
  if (skills.length === 0) {
194
- logger.warn(`ai.skills: no publishable skills found in "${configured}".`);
220
+ logger.warn(
221
+ `agents.skills: no publishable skills found in "${configured}".`
222
+ );
195
223
  }
196
224
  return skills;
197
225
  };
@@ -324,7 +352,7 @@ export const publishBuildArtifacts = async (
324
352
  logger: ArtifactLogger,
325
353
  indexSearch: (outDir: string) => Promise<number> = buildSearchIndex
326
354
  ): Promise<void> => {
327
- if (project.config.search.provider === "pagefind") {
355
+ if (project.config.search.provider.mode === "pagefind") {
328
356
  logger.info("Building search index");
329
357
  const indexed = await indexSearch(distDir);
330
358
  logger.info(`Indexed ${indexed} page(s) for search`);
@@ -340,7 +368,7 @@ export const publishBuildArtifacts = async (
340
368
 
341
369
  // Collected once: llms.txt lists the skills the build publishes below.
342
370
  const skills = await collectConfiguredSkills(project, distDir, logger);
343
- if (project.config.ai.llmsTxt.enabled) {
371
+ if (project.config.agents.llmsTxt.enabled) {
344
372
  await publishLlmsFiles(project, distDir, skills, logger);
345
373
  }
346
374
 
@@ -376,6 +404,6 @@ export const publishBuildArtifacts = async (
376
404
  await emitWellKnownFiles(project.config, distDir, skills, logger);
377
405
  await emitAgentSkills(project, distDir, skills, logger);
378
406
 
379
- await emitRedirectFiles(project.config, distDir, logger);
407
+ await emitRedirectFiles(project, distDir, logger);
380
408
  await emitHeaderFiles(project, distDir, logger);
381
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
  };