blume 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
  3. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  4. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  5. package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
  6. package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
  7. package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
  8. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  9. package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
  10. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  11. package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
  12. package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
  13. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
  14. package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
  15. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  16. package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
  17. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  18. package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
  19. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
  20. package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
  21. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  22. package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
  23. package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
  24. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  25. package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
  26. package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
  27. package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
  28. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  29. package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
  30. package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
  31. package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
  32. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  33. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  34. package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
  35. package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
  36. package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
  37. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  38. package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
  39. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  40. package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
  41. package/dist/cli/chunk-j85scx15.js.map +10 -0
  42. package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
  43. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  44. package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
  45. package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
  46. package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
  47. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  48. package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
  49. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  50. package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
  51. package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
  52. package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
  53. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  54. package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
  55. package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
  56. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  57. package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
  58. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  59. package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
  60. package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
  61. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  62. package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
  63. package/dist/cli/index.js +160 -35
  64. package/dist/cli/index.js.map +4 -4
  65. package/dist/types/ai/agent-surface.d.ts +32 -0
  66. package/dist/types/ai/api-catalog.d.ts +7 -1
  67. package/dist/types/ai/ask-context.d.ts +7 -0
  68. package/dist/types/ai/component-markdown.d.ts +4 -4
  69. package/dist/types/ai/relative-links.d.ts +9 -4
  70. package/dist/types/ai/static-expression.d.ts +28 -0
  71. package/dist/types/analytics/databuddy.d.ts +43 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +14 -0
  74. package/dist/types/cli/env.d.ts +5 -0
  75. package/dist/types/cli/init/scaffold.d.ts +19 -3
  76. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  77. package/dist/types/core/base-path.d.ts +9 -0
  78. package/dist/types/core/config-input.d.ts +4 -4
  79. package/dist/types/core/config.d.ts +2 -2
  80. package/dist/types/core/graph.d.ts +2 -0
  81. package/dist/types/core/i18n-ui.d.ts +31 -0
  82. package/dist/types/core/i18n.d.ts +7 -1
  83. package/dist/types/core/links.d.ts +3 -1
  84. package/dist/types/core/load-module.d.ts +10 -0
  85. package/dist/types/core/locale-links.d.ts +12 -2
  86. package/dist/types/core/meta.d.ts +5 -1
  87. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  88. package/dist/types/core/navigation.d.ts +38 -0
  89. package/dist/types/core/ordering-prefix.d.ts +4 -0
  90. package/dist/types/core/safe-links.d.ts +3 -1
  91. package/dist/types/core/schema.d.ts +52 -13
  92. package/dist/types/core/sources/github-releases.d.ts +5 -0
  93. package/dist/types/core/sources/lower.d.ts +14 -2
  94. package/dist/types/core/sources/normalize.d.ts +10 -1
  95. package/dist/types/core/sources/remote.d.ts +11 -1
  96. package/dist/types/core/sources/resolve.d.ts +12 -0
  97. package/dist/types/core/sources/types.d.ts +31 -0
  98. package/dist/types/core/types.d.ts +9 -0
  99. package/dist/types/deploy/adapters/node.d.ts +5 -2
  100. package/dist/types/deploy/adapters/types.d.ts +7 -0
  101. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  102. package/dist/types/deploy/headers.d.ts +31 -7
  103. package/dist/types/deploy/node-headers.d.ts +43 -8
  104. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  105. package/dist/types/deploy/platforms/node.d.ts +6 -5
  106. package/dist/types/deploy/platforms/types.d.ts +7 -0
  107. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  108. package/dist/types/deploy/redirects.d.ts +7 -2
  109. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  110. package/dist/types/openapi/model.d.ts +20 -6
  111. package/dist/types/search/sync/algolia.d.ts +3 -1
  112. package/docs/01-quickstart.mdx +3 -2
  113. package/docs/02-deployment.mdx +17 -8
  114. package/docs/08-faq.mdx +9 -2
  115. package/docs/advanced/changelog.mdx +1 -1
  116. package/docs/advanced/custom-pages.mdx +4 -2
  117. package/docs/cli/audit.mdx +2 -2
  118. package/docs/cli/doctor.mdx +2 -2
  119. package/docs/cli/evals.mdx +2 -2
  120. package/docs/cli/index.mdx +4 -1
  121. package/docs/cli/translate.mdx +1 -1
  122. package/docs/configuration/analytics.mdx +23 -2
  123. package/docs/configuration/assistant.mdx +1 -1
  124. package/docs/configuration/customization.mdx +4 -3
  125. package/docs/configuration/index.mdx +5 -3
  126. package/docs/configuration/search.mdx +1 -1
  127. package/docs/content/components.mdx +1 -1
  128. package/docs/content/frontmatter.mdx +5 -1
  129. package/docs/content/i18n.mdx +1 -1
  130. package/docs/content/index.mdx +4 -2
  131. package/docs/content/meta.mdx +5 -3
  132. package/docs/content/navigation.mdx +29 -4
  133. package/docs/content/sources.mdx +18 -12
  134. package/docs/content/versioning.mdx +1 -0
  135. package/docs/discoverability/agent-discovery.mdx +21 -7
  136. package/docs/discoverability/index.mdx +2 -2
  137. package/docs/discoverability/llms-txt.mdx +2 -5
  138. package/docs/discoverability/markdown.mdx +5 -3
  139. package/docs/discoverability/mcp.mdx +4 -0
  140. package/docs/discoverability/metadata.mdx +3 -2
  141. package/docs/discoverability/open-graph.mdx +6 -4
  142. package/docs/discoverability/rss.mdx +3 -1
  143. package/docs/references/asyncapi.mdx +1 -1
  144. package/docs/references/graphql.mdx +2 -2
  145. package/docs/references/openapi.mdx +3 -3
  146. package/package.json +1 -1
  147. package/skills/blume-migrate/SKILL.md +6 -6
  148. package/skills/blume-migrate/references/docusaurus.md +6 -6
  149. package/skills/blume-migrate/references/mintlify.md +3 -3
  150. package/skills/blume-migrate/references/monorepo.md +1 -1
  151. package/skills/blume-migrate/references/nextra.md +1 -1
  152. package/skills/blume-migrate/references/starlight.md +5 -5
  153. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  154. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  155. package/src/ai/agent-surface.ts +56 -0
  156. package/src/ai/api/spec.ts +15 -2
  157. package/src/ai/api-catalog.ts +7 -1
  158. package/src/ai/ask-context.ts +14 -2
  159. package/src/ai/ask-data.ts +26 -12
  160. package/src/ai/component-markdown.ts +43 -37
  161. package/src/ai/link-headers.ts +8 -3
  162. package/src/ai/llms.ts +4 -3
  163. package/src/ai/markdown.ts +23 -11
  164. package/src/ai/mcp/server.ts +78 -7
  165. package/src/ai/relative-links.ts +78 -10
  166. package/src/ai/static-expression.ts +416 -0
  167. package/src/ai/visibility.ts +45 -14
  168. package/src/analytics/databuddy.ts +67 -0
  169. package/src/analytics/head.ts +4 -0
  170. package/src/analytics/index.ts +2 -0
  171. package/src/analytics/posthog.ts +21 -3
  172. package/src/analytics/schema.ts +2 -0
  173. package/src/astro/generate.ts +71 -27
  174. package/src/astro/templates.ts +61 -23
  175. package/src/audit/catalog.ts +2 -2
  176. package/src/audit/checks/assets.ts +25 -5
  177. package/src/audit/checks/content.ts +20 -2
  178. package/src/audit/checks/i18n.ts +46 -8
  179. package/src/audit/checks/indexability.ts +39 -19
  180. package/src/audit/checks/links.ts +14 -0
  181. package/src/audit/checks/llms.ts +6 -3
  182. package/src/audit/checks/network.ts +3 -1
  183. package/src/audit/checks/og-image.ts +10 -0
  184. package/src/audit/checks/robots.ts +6 -1
  185. package/src/audit/checks/sitemap.ts +56 -31
  186. package/src/audit/checks/social.ts +21 -2
  187. package/src/audit/crawl.ts +88 -15
  188. package/src/audit/report.ts +54 -17
  189. package/src/audit/run.ts +1 -0
  190. package/src/audit/types.ts +18 -0
  191. package/src/audit/url.ts +37 -6
  192. package/src/cli/build-failure.ts +50 -0
  193. package/src/cli/commands/audit.ts +7 -0
  194. package/src/cli/commands/build.ts +19 -5
  195. package/src/cli/commands/check.ts +2 -0
  196. package/src/cli/commands/eval.ts +1 -7
  197. package/src/cli/commands/init.ts +15 -5
  198. package/src/cli/commands/preview.ts +15 -0
  199. package/src/cli/commands/sync.ts +2 -0
  200. package/src/cli/commands/translate.ts +9 -6
  201. package/src/cli/commands/upgrade.ts +11 -0
  202. package/src/cli/eject-scripts.ts +32 -7
  203. package/src/cli/env.ts +12 -1
  204. package/src/cli/init/scaffold.ts +117 -11
  205. package/src/cli/init/starter-spec.ts +235 -0
  206. package/src/components/content/AccordionItem.astro +26 -22
  207. package/src/components/content/Badge.astro +2 -9
  208. package/src/components/content/Card.astro +2 -2
  209. package/src/components/content/Component.astro +9 -3
  210. package/src/components/content/Expandable.astro +5 -1
  211. package/src/components/content/Frame.astro +2 -7
  212. package/src/components/content/GithubInfo.astro +12 -2
  213. package/src/components/content/Prompt.astro +2 -7
  214. package/src/components/content/Tabs.astro +54 -5
  215. package/src/components/content/Tile.astro +1 -1
  216. package/src/components/content/Tooltip.astro +69 -7
  217. package/src/components/content/Tree.astro +7 -2
  218. package/src/components/content/TypeTable.astro +10 -5
  219. package/src/components/content/Update.astro +8 -2
  220. package/src/components/content/auto-type-table.ts +4 -1
  221. package/src/components/content/badge-color.ts +17 -0
  222. package/src/components/content/base-href.ts +18 -3
  223. package/src/components/content/inline-markdown.ts +27 -7
  224. package/src/components/copy-feedback.ts +35 -8
  225. package/src/components/islands/assistant.tsx +16 -2
  226. package/src/components/islands/hooks.ts +7 -2
  227. package/src/components/islands/webmcp.ts +12 -8
  228. package/src/components/layout/Banner.astro +23 -4
  229. package/src/components/layout/Header.astro +22 -9
  230. package/src/components/layout/Logo.astro +5 -0
  231. package/src/components/layout/NavSelector.astro +6 -1
  232. package/src/components/layout/NavTabMenu.astro +133 -0
  233. package/src/components/layout/NavTree.astro +15 -6
  234. package/src/components/layout/NavTreeCache.astro +5 -2
  235. package/src/components/layout/NavTreeScript.astro +45 -5
  236. package/src/components/layout/PageLayout.astro +22 -8
  237. package/src/components/layout/ReferenceLayout.astro +4 -1
  238. package/src/components/layout/RootLayout.astro +21 -8
  239. package/src/components/layout/Search.astro +5 -1
  240. package/src/components/layout/analytics-client.ts +2 -0
  241. package/src/components/openapi/GraphqlType.astro +11 -3
  242. package/src/components/openapi/MessageComposer.astro +1 -1
  243. package/src/components/openapi/Operation.astro +21 -5
  244. package/src/components/openapi/PanelTabs.astro +4 -1
  245. package/src/components/openapi/Playground.astro +8 -2
  246. package/src/components/openapi/RequestPanel.astro +9 -5
  247. package/src/components/openapi/SchemaProperty.astro +11 -33
  248. package/src/components/openapi/SchemaTable.astro +27 -52
  249. package/src/components/openapi/helpers.ts +90 -12
  250. package/src/components/openapi/message-composer.ts +8 -0
  251. package/src/components/openapi/message-model.ts +12 -2
  252. package/src/components/openapi/message.ts +4 -1
  253. package/src/components/openapi/operation-model.ts +40 -8
  254. package/src/components/openapi/panel.ts +29 -5
  255. package/src/components/openapi/playground-client.ts +37 -7
  256. package/src/components/openapi/playground-schema.ts +25 -6
  257. package/src/components/openapi/request.ts +2 -0
  258. package/src/components/openapi/schema-tree.ts +203 -0
  259. package/src/components/openapi/snippets.ts +30 -15
  260. package/src/components/openapi/validate-json.ts +1 -1
  261. package/src/core/base-path.ts +15 -0
  262. package/src/core/config-input.ts +4 -4
  263. package/src/core/config.ts +18 -4
  264. package/src/core/diagnostics.ts +217 -30
  265. package/src/core/graph.ts +120 -11
  266. package/src/core/i18n-ui.ts +39 -2
  267. package/src/core/i18n.ts +13 -2
  268. package/src/core/links.ts +7 -4
  269. package/src/core/load-module.ts +20 -0
  270. package/src/core/locale-links.ts +17 -17
  271. package/src/core/manifest.ts +3 -2
  272. package/src/core/meta.ts +69 -6
  273. package/src/core/nav-diagnostics.ts +56 -1
  274. package/src/core/navigation.ts +245 -76
  275. package/src/core/ordering-prefix.ts +27 -0
  276. package/src/core/project-graph.ts +23 -1
  277. package/src/core/safe-href.ts +53 -1
  278. package/src/core/safe-links.ts +11 -2
  279. package/src/core/schema.ts +70 -14
  280. package/src/core/sources/assets.ts +83 -41
  281. package/src/core/sources/contentful-rich-text.ts +3 -2
  282. package/src/core/sources/contentful.ts +25 -12
  283. package/src/core/sources/filesystem.ts +5 -1
  284. package/src/core/sources/github-releases.ts +106 -5
  285. package/src/core/sources/lexical.ts +9 -4
  286. package/src/core/sources/lower.ts +79 -26
  287. package/src/core/sources/mdx-remote.ts +1 -0
  288. package/src/core/sources/normalize.ts +132 -30
  289. package/src/core/sources/notion.ts +26 -10
  290. package/src/core/sources/obsidian.ts +17 -3
  291. package/src/core/sources/payload.ts +1 -0
  292. package/src/core/sources/portable-text.ts +63 -44
  293. package/src/core/sources/remote.ts +18 -2
  294. package/src/core/sources/resolve.ts +52 -33
  295. package/src/core/sources/sanity.ts +1 -0
  296. package/src/core/sources/strapi-blocks.ts +9 -4
  297. package/src/core/sources/strapi.ts +1 -0
  298. package/src/core/sources/types.ts +31 -0
  299. package/src/core/types.ts +9 -0
  300. package/src/core/ui-packs/ar.ts +13 -0
  301. package/src/core/ui-packs/bg.ts +13 -0
  302. package/src/core/ui-packs/bn.ts +13 -0
  303. package/src/core/ui-packs/ca.ts +13 -0
  304. package/src/core/ui-packs/cs.ts +13 -0
  305. package/src/core/ui-packs/da.ts +13 -0
  306. package/src/core/ui-packs/de.ts +13 -0
  307. package/src/core/ui-packs/el.ts +13 -0
  308. package/src/core/ui-packs/es.ts +13 -0
  309. package/src/core/ui-packs/fa.ts +13 -0
  310. package/src/core/ui-packs/fi.ts +13 -0
  311. package/src/core/ui-packs/fr.ts +13 -0
  312. package/src/core/ui-packs/he.ts +13 -0
  313. package/src/core/ui-packs/hi.ts +13 -0
  314. package/src/core/ui-packs/hr.ts +13 -0
  315. package/src/core/ui-packs/hu.ts +13 -0
  316. package/src/core/ui-packs/id.ts +13 -0
  317. package/src/core/ui-packs/it.ts +13 -0
  318. package/src/core/ui-packs/ja.ts +13 -0
  319. package/src/core/ui-packs/ko.ts +13 -0
  320. package/src/core/ui-packs/nl.ts +13 -0
  321. package/src/core/ui-packs/no.ts +13 -0
  322. package/src/core/ui-packs/pl.ts +13 -0
  323. package/src/core/ui-packs/pt-br.ts +13 -0
  324. package/src/core/ui-packs/pt.ts +13 -0
  325. package/src/core/ui-packs/ro.ts +13 -0
  326. package/src/core/ui-packs/ru.ts +13 -0
  327. package/src/core/ui-packs/sk.ts +13 -0
  328. package/src/core/ui-packs/sr.ts +13 -0
  329. package/src/core/ui-packs/sv.ts +13 -0
  330. package/src/core/ui-packs/th.ts +13 -0
  331. package/src/core/ui-packs/tr.ts +13 -0
  332. package/src/core/ui-packs/uk.ts +13 -0
  333. package/src/core/ui-packs/vi.ts +13 -0
  334. package/src/core/ui-packs/zh-tw.ts +13 -0
  335. package/src/core/ui-packs/zh.ts +13 -0
  336. package/src/core/version-cut.ts +17 -2
  337. package/src/core/versions.ts +4 -1
  338. package/src/deploy/adapters/node.ts +5 -2
  339. package/src/deploy/adapters/registry.ts +2 -1
  340. package/src/deploy/adapters/types.ts +13 -1
  341. package/src/deploy/artifacts.ts +25 -5
  342. package/src/deploy/cloudflare-negotiation.ts +23 -4
  343. package/src/deploy/headers.ts +67 -51
  344. package/src/deploy/node-headers.ts +148 -27
  345. package/src/deploy/platforms/cloudflare.ts +1 -0
  346. package/src/deploy/platforms/netlify.ts +82 -5
  347. package/src/deploy/platforms/node.ts +7 -5
  348. package/src/deploy/platforms/static.ts +1 -0
  349. package/src/deploy/platforms/types.ts +7 -0
  350. package/src/deploy/platforms/vercel.ts +9 -3
  351. package/src/deploy/redirects.ts +14 -3
  352. package/src/deploy/vercel-negotiation.ts +35 -3
  353. package/src/eval/agents.ts +10 -2
  354. package/src/eval/run.ts +25 -0
  355. package/src/markdown/base-links.ts +55 -8
  356. package/src/markdown/index.ts +6 -3
  357. package/src/markdown/relative-links.ts +3 -23
  358. package/src/markdown/route-snapshot.ts +37 -0
  359. package/src/og/card.ts +20 -4
  360. package/src/og/derive.ts +145 -4
  361. package/src/openapi/graphql-build.ts +28 -2
  362. package/src/openapi/model.ts +77 -13
  363. package/src/openapi/render-mdx.ts +10 -1
  364. package/src/registry/eject.ts +119 -22
  365. package/src/search/documents.ts +22 -1
  366. package/src/search/sync/algolia.ts +36 -2
  367. package/src/sources/registry.ts +5 -0
  368. package/src/theme/entry.ts +11 -4
  369. package/src/translate/agents.ts +6 -1
  370. package/src/translate/ledger.ts +26 -3
  371. package/src/translate/meta.ts +11 -3
  372. package/src/translate/run.ts +11 -5
  373. package/src/translate/validate.ts +10 -1
  374. package/src/translate/work-list.ts +36 -3
  375. package/src/upgrade/upgrade.ts +36 -4
  376. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  377. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  378. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  379. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  380. package/dist/cli/chunk-82bbrxdn.js +0 -51
  381. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  382. package/dist/cli/chunk-abh8yjkn.js +0 -31
  383. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  384. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  385. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  386. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  387. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  388. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  389. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  390. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  391. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  392. package/dist/cli/chunk-q5163e60.js.map +0 -11
  393. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  394. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  395. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  396. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  397. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  398. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
  399. /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
  400. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
  401. /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
  402. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
  403. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
  404. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
  405. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
  406. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
  407. /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
  408. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
  409. /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
  410. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
@@ -33,12 +33,47 @@ const TIER_FLAGS: { tier: AuditTier; flag: string }[] = [
33
33
  interface CheckRollup {
34
34
  id: CheckId;
35
35
  count: number;
36
+ /** Distinct affected URLs — one page can carry several findings. */
37
+ pages: number;
36
38
  severity: DiagnosticSeverity;
37
39
  category: AuditCategory;
38
40
  title: string;
39
41
  findings: Diagnostic[];
40
42
  }
41
43
 
44
+ const SEVERITY_ORDER = {
45
+ error: 0,
46
+ info: 2,
47
+ warning: 1,
48
+ } satisfies Record<DiagnosticSeverity, number>;
49
+
50
+ /**
51
+ * The group's severity: the worst of its findings'. A check can downgrade a
52
+ * finding at runtime (an external link answering 503 is a warning, not the
53
+ * catalog's error), and the group's glyph must agree with the summary counts.
54
+ */
55
+ const worstSeverity = (findings: Diagnostic[]): DiagnosticSeverity => {
56
+ let worst: DiagnosticSeverity = "info";
57
+ for (const finding of findings) {
58
+ if (SEVERITY_ORDER[finding.severity] < SEVERITY_ORDER[worst]) {
59
+ worst = finding.severity;
60
+ }
61
+ }
62
+ return worst;
63
+ };
64
+
65
+ /** The first finding per affected URL, in report order. */
66
+ const firstPerPage = (findings: Diagnostic[]): Diagnostic[] => {
67
+ const seen = new Set<string | undefined>();
68
+ return findings.filter((finding) => {
69
+ if (seen.has(finding.url)) {
70
+ return false;
71
+ }
72
+ seen.add(finding.url);
73
+ return true;
74
+ });
75
+ };
76
+
42
77
  /**
43
78
  * Group findings by check. This is the difference between a report people read
44
79
  * and one they close: 214 pages × 6 findings is an unreadable wall, but "Meta
@@ -55,22 +90,18 @@ export const rollup = (diagnostics: Diagnostic[]): CheckRollup[] => {
55
90
  }
56
91
  }
57
92
 
58
- const order = {
59
- error: 0,
60
- info: 2,
61
- warning: 1,
62
- } satisfies Record<DiagnosticSeverity, number>;
63
93
  const checks = [...groups.entries()].map(([id, findings]) => {
64
94
  // SAFETY: audit findings are only ever created through `finding()`, whose
65
95
  // codes are the check catalog's ids.
66
96
  const checkId = id as CheckId;
67
- const { category, severity, title } = checkMeta(checkId);
97
+ const { category, title } = checkMeta(checkId);
68
98
  return {
69
99
  category,
70
100
  count: findings.length,
71
101
  findings,
72
102
  id: checkId,
73
- severity,
103
+ pages: firstPerPage(findings).length,
104
+ severity: worstSeverity(findings),
74
105
  title,
75
106
  };
76
107
  });
@@ -80,7 +111,7 @@ export const rollup = (diagnostics: Diagnostic[]): CheckRollup[] => {
80
111
  // and print "content" three separate times.
81
112
  const worst = new Map<AuditCategory, number>();
82
113
  for (const check of checks) {
83
- const rank = order[check.severity];
114
+ const rank = SEVERITY_ORDER[check.severity];
84
115
  worst.set(
85
116
  check.category,
86
117
  Math.min(worst.get(check.category) ?? rank, rank)
@@ -91,7 +122,7 @@ export const rollup = (diagnostics: Diagnostic[]): CheckRollup[] => {
91
122
  (a, b) =>
92
123
  (worst.get(a.category) ?? 0) - (worst.get(b.category) ?? 0) ||
93
124
  a.category.localeCompare(b.category) ||
94
- order[a.severity] - order[b.severity] ||
125
+ SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity] ||
95
126
  b.count - a.count
96
127
  );
97
128
  };
@@ -176,14 +207,16 @@ export const formatReport = (
176
207
  }
177
208
 
178
209
  const color = SEVERITY_COLOR[group.severity];
179
- const pages = `${group.count} page${group.count === 1 ? "" : "s"}`;
210
+ const pages = `${group.pages} page${group.pages === 1 ? "" : "s"}`;
180
211
  lines.push(
181
212
  ` ${color(`${GLYPH[group.severity]} ${group.title}`)} ${colors.dim(pages)}`
182
213
  );
183
214
 
215
+ // The preview lists affected pages, each once; --verbose lists every
216
+ // finding, since each one's message names a different specific.
184
217
  const shown = options.verbose
185
218
  ? group.findings
186
- : group.findings.slice(0, PREVIEW);
219
+ : firstPerPage(group.findings).slice(0, PREVIEW);
187
220
  for (const diagnostic of shown) {
188
221
  lines.push(findingLine(diagnostic, root));
189
222
  // The message names the specifics the rolled-up line can't — which target
@@ -192,16 +225,20 @@ export const formatReport = (
192
225
  lines.push(` ${colors.dim(diagnostic.message)}`);
193
226
  }
194
227
  }
195
- const hidden = group.count - shown.length;
228
+ const hidden = options.verbose ? 0 : group.pages - shown.length;
196
229
  if (hidden > 0) {
197
230
  lines.push(` ${colors.dim(`… and ${hidden} more (--verbose)`)}`);
198
231
  }
199
232
 
200
- // Every finding in a group shares the catalog's fix unless it overrode it,
201
- // so showing the first one's is showing the group's.
202
- const [first] = group.findings;
203
- const fix = first?.suggestion;
204
- if (fix) {
233
+ // Findings share the catalog's fix unless one overrode it (a navigation
234
+ // entry, a page generated from an API spec), so each distinct fix is
235
+ // printed once.
236
+ const fixes = new Set(
237
+ group.findings.flatMap((finding) =>
238
+ finding.suggestion ? [finding.suggestion] : []
239
+ )
240
+ );
241
+ for (const fix of fixes) {
205
242
  lines.push(` ${colors.cyan(`fix: ${fix}`)}`);
206
243
  }
207
244
  lines.push("");
package/src/audit/run.ts CHANGED
@@ -137,6 +137,7 @@ export const runAudit = async (options: AuditOptions): Promise<AuditResult> => {
137
137
 
138
138
  const crawl = await crawlStaticDir({
139
139
  basePath,
140
+ deployBase: normalizeBasePath(project.config.deployment.options.base),
140
141
  manifest: project.manifest,
141
142
  staticDir,
142
143
  });
@@ -113,6 +113,16 @@ export interface RedirectResolution {
113
113
  outcome: "ok" | "loop" | "broken" | "chain";
114
114
  }
115
115
 
116
+ /** One urlset file behind a sitemap, for the per-file size limits. */
117
+ export interface SitemapPart {
118
+ file: string;
119
+ /** Site path the file is served at, e.g. `/sitemap-1.xml`. */
120
+ url: string;
121
+ bytes: number;
122
+ /** How many `<url>` entries the file lists. */
123
+ urls: number;
124
+ }
125
+
116
126
  /** A parsed `sitemap.xml`. */
117
127
  export interface SitemapDoc {
118
128
  file: string;
@@ -121,6 +131,14 @@ export interface SitemapDoc {
121
131
  urls: string[];
122
132
  /** Each `<url>` block's `<lastmod>`, keyed by its `<loc>`. */
123
133
  lastmod?: Map<string, string>;
134
+ /**
135
+ * The child sitemaps' `<loc>`s, when `sitemap.xml` is a sitemap index —
136
+ * Blume writes one past 50,000 URLs. The crawler reads each child from the
137
+ * build and merges its URLs into `urls` and `lastmod`.
138
+ */
139
+ sitemaps?: string[];
140
+ /** The index's child urlset files, once the crawler has read them. */
141
+ parts?: SitemapPart[];
124
142
  /** Parse failure, when the document isn't usable. */
125
143
  error?: string;
126
144
  }
package/src/audit/url.ts CHANGED
@@ -43,11 +43,21 @@ export type ResolvedHref =
43
43
  | { kind: "internal"; path: string; hash: string }
44
44
  /** An absolute URL that resolves back to this site — should have been a path. */
45
45
  | { kind: "self-origin"; path: string; hash: string }
46
+ /**
47
+ * A path on this origin that lacks `deployment.base`. The host serves the
48
+ * whole build under the base, so nothing answers here — `path` is the path
49
+ * as requested, base missing.
50
+ */
51
+ | { kind: "outside-base"; path: string }
46
52
  /** An absolute URL on another origin. */
47
53
  | { kind: "external"; url: string }
48
54
  /** In-page anchor, `mailto:`, `tel:`, `javascript:`, data URI — not a page link. */
49
55
  | { kind: "ignored" };
50
56
 
57
+ /** Whether a served path sits under the deployment base (always, without one). */
58
+ const underBase = (deployBase: string, path: string): boolean =>
59
+ !deployBase || path === deployBase || path.startsWith(`${deployBase}/`);
60
+
51
61
  const NON_HTTP_SCHEME = /^(?!https?:)[a-z][a-z0-9+.-]*:/iu;
52
62
 
53
63
  /**
@@ -65,6 +75,20 @@ export const decodePath = (path: string): string => {
65
75
  }
66
76
  };
67
77
 
78
+ /**
79
+ * Whether a `--url` value is an absolute http(s) URL the network tier can
80
+ * probe. A bare host (`example.com`) has no scheme, and `localhost:4321`
81
+ * parses with `localhost:` as its scheme — both are rejected.
82
+ */
83
+ export const isHttpUrl = (input: string): boolean => {
84
+ try {
85
+ const { protocol } = new URL(input);
86
+ return protocol === "http:" || protocol === "https:";
87
+ } catch {
88
+ return false;
89
+ }
90
+ };
91
+
68
92
  /** The origin of `deployment.site`, or null when no site is configured. */
69
93
  export const siteOrigin = (site?: string): string | null => {
70
94
  if (!site) {
@@ -113,12 +137,14 @@ export const resolveHref = (
113
137
  return { kind: "ignored" };
114
138
  }
115
139
  if (origin && parsed.origin === origin) {
140
+ const served = decodePath(parsed.pathname);
141
+ if (!underBase(deployBase, served)) {
142
+ return { kind: "outside-base", path: normalizePath(served) };
143
+ }
116
144
  return {
117
145
  hash: parsed.hash.slice(1),
118
146
  kind: "self-origin",
119
- path: normalizePath(
120
- stripBasePath(deployBase, decodePath(parsed.pathname))
121
- ),
147
+ path: normalizePath(stripBasePath(deployBase, served)),
122
148
  };
123
149
  }
124
150
  return { kind: "external", url: parsed.toString() };
@@ -140,11 +166,16 @@ export const resolveHref = (
140
166
  } catch {
141
167
  return { kind: "ignored" };
142
168
  }
169
+ const served = decodePath(resolved.pathname);
170
+ // Only a root-relative href names the served path outright; a relative one
171
+ // (`./auth`) was resolved against the base-less page URL above, so it
172
+ // inherits the page's base by construction.
173
+ if (target.startsWith("/") && !underBase(deployBase, served)) {
174
+ return { kind: "outside-base", path: normalizePath(served) };
175
+ }
143
176
  return {
144
177
  hash: resolved.hash.slice(1),
145
178
  kind: "internal",
146
- path: normalizePath(
147
- stripBasePath(deployBase, decodePath(resolved.pathname))
148
- ),
179
+ path: normalizePath(stripBasePath(deployBase, served)),
149
180
  };
150
181
  };
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+
3
+ import type { Diagnostic } from "../core/types.ts";
4
+
5
+ /**
6
+ * The fields of an `astro build` rejection the report reads. Rolldown bundles
7
+ * every plugin failure (an MDX compile error, say) into one error whose
8
+ * `errors` holds each; a failure carries `loc` where the compiler pinned it,
9
+ * or only `id`, the module that failed.
10
+ */
11
+ const buildErrorSchema = z.looseObject({
12
+ errors: z.array(z.instanceof(Error)).optional(),
13
+ id: z.string().optional(),
14
+ loc: z
15
+ .looseObject({
16
+ column: z.number().optional(),
17
+ file: z.string().optional(),
18
+ line: z.number().optional(),
19
+ })
20
+ .optional(),
21
+ });
22
+
23
+ /** One failure as a diagnostic at the file (and position) it names. */
24
+ const failureDiagnostic = (error: Error): Diagnostic => {
25
+ const parsed = buildErrorSchema.safeParse(error);
26
+ const { id, loc } = parsed.success ? parsed.data : {};
27
+ // A named error (`MDXError`, `CompilerError`) says which step failed.
28
+ const name = error.name === "Error" ? "" : `${error.name}: `;
29
+ return {
30
+ code: "BLUME_BUILD_FAILED",
31
+ column: loc?.column,
32
+ file: loc?.file ?? id,
33
+ line: loc?.line,
34
+ message: `${name}${error.message}`,
35
+ severity: "error",
36
+ };
37
+ };
38
+
39
+ /**
40
+ * What `astro build` rejected with, as build diagnostics: one per failure
41
+ * Rolldown collected, else the error itself. A page Blume's own checks pass
42
+ * can still fail Astro's compile (MDX that doesn't parse) or render, and that
43
+ * is a problem in the site, reported at its file, rather than an internal
44
+ * error blaming Blume.
45
+ */
46
+ export const astroBuildDiagnostics = (error: Error): Diagnostic[] => {
47
+ const parsed = buildErrorSchema.safeParse(error);
48
+ const failures = parsed.success ? (parsed.data.errors ?? []) : [];
49
+ return (failures.length > 0 ? failures : [error]).map(failureDiagnostic);
50
+ };
@@ -11,6 +11,7 @@ import { formatCatalog, formatReport, reportJson } from "../../audit/report.ts";
11
11
  import { NoBuildError, runAudit } from "../../audit/run.ts";
12
12
  import type { AuditResult } from "../../audit/run.ts";
13
13
  import { checkTerms, unknownCheckTerms } from "../../audit/terms.ts";
14
+ import { isHttpUrl } from "../../audit/url.ts";
14
15
  import { BlumeError } from "../../core/diagnostics.ts";
15
16
  import { scanProject } from "../../core/project-graph.ts";
16
17
  import type { DiagnosticSeverity } from "../../core/types.ts";
@@ -162,6 +163,12 @@ export const auditCommand = defineCommand({
162
163
  logger.error(`--json and --${agent} are mutually exclusive.`);
163
164
  process.exit(1);
164
165
  }
166
+ if (args.url !== undefined && !isHttpUrl(args.url)) {
167
+ logger.error(
168
+ `Invalid --url "${args.url}": pass the deployment's full URL, scheme included (e.g. https://${args.url}).`
169
+ );
170
+ process.exit(1);
171
+ }
165
172
  const only = splitTerms(args.only);
166
173
  const skip = splitTerms(args.skip);
167
174
  rejectUnknownTerms([...only, ...skip]);
@@ -6,6 +6,7 @@ import { defineCommand } from "citty";
6
6
  import { join } from "pathe";
7
7
 
8
8
  import { publishBuildProject } from "../../astro/integration.ts";
9
+ import { BlumeError } from "../../core/diagnostics.ts";
9
10
  import { ensureGitignore } from "../../core/gitignore.ts";
10
11
  import type { BlumeProject } from "../../core/project-graph.ts";
11
12
  import { serverFeatures } from "../../core/server-features.ts";
@@ -21,10 +22,11 @@ import {
21
22
  removedBuildFlags,
22
23
  removedBuildFlagsAdvice,
23
24
  } from "../../upgrade/upgrade.ts";
25
+ import { astroBuildDiagnostics } from "../build-failure.ts";
24
26
  import { commandMeta } from "../command-meta.ts";
25
27
  import { refuseIfDevRunning } from "../dev-lock.ts";
26
28
  import { refuseIfEjected } from "../eject-scripts.ts";
27
- import { logger } from "../log.ts";
29
+ import { logger, reportDiagnostics } from "../log.ts";
28
30
  import { prepareProject } from "../prepare.ts";
29
31
 
30
32
  const BUDGET_JS = "budget-js";
@@ -333,10 +335,22 @@ export const buildCommand = defineCommand({
333
335
  publishBuildProject(project);
334
336
  }
335
337
 
336
- await build({
337
- logLevel: "info",
338
- root: project.context.outDir,
339
- });
338
+ try {
339
+ await build({
340
+ logLevel: "info",
341
+ root: project.context.outDir,
342
+ });
343
+ } catch (error) {
344
+ // Astro rejects when the site fails to compile or render (MDX that
345
+ // doesn't parse, a component that throws): report that as a build error
346
+ // at the file it names, not as an internal error blaming Blume. A
347
+ // BlumeError keeps its own diagnostic.
348
+ if (error instanceof BlumeError || !(error instanceof Error)) {
349
+ throw error;
350
+ }
351
+ reportDiagnostics(astroBuildDiagnostics(error), root);
352
+ process.exit(1);
353
+ }
340
354
 
341
355
  // A real build also prunes the cache; an isolated verify must not evict
342
356
  // cards a live dev server is still serving.
@@ -8,6 +8,7 @@ import { join } from "pathe";
8
8
  import { ensureGitignore } from "../../core/gitignore.ts";
9
9
  import { commandMeta } from "../command-meta.ts";
10
10
  import { refuseIfDevRunning } from "../dev-lock.ts";
11
+ import { refuseIfEjected } from "../eject-scripts.ts";
11
12
  import { logger } from "../log.ts";
12
13
  import { prepareProject } from "../prepare.ts";
13
14
 
@@ -30,6 +31,7 @@ export const checkCommand = defineCommand({
30
31
  meta: commandMeta.check,
31
32
  async run({ args }) {
32
33
  const root = process.cwd();
34
+ await refuseIfEjected(root, "check");
33
35
 
34
36
  // `blume check` regenerates `.blume` just like `build`, so it must refuse a
35
37
  // live dev server unless isolated. `--isolated` (or BLUME_RUNTIME_DIR)
@@ -19,7 +19,7 @@ import {
19
19
  warningLines,
20
20
  writeEvalReport,
21
21
  } from "../../eval/report.ts";
22
- import { runEval } from "../../eval/run.ts";
22
+ import { passFraction, runEval } from "../../eval/run.ts";
23
23
  import type { EvalResult } from "../../eval/run.ts";
24
24
  import { EvalsFileError, loadEvalsFile } from "../../eval/schema.ts";
25
25
  import { commandMeta } from "../command-meta.ts";
@@ -63,12 +63,6 @@ const launchAgentCode = async (
63
63
  }
64
64
  };
65
65
 
66
- /** The fraction of run (non-skipped) questions that passed. */
67
- export const passFraction = (result: EvalResult): number => {
68
- const ran = result.results.length - result.counts.skip;
69
- return ran === 0 ? 1 : result.counts.pass / ran;
70
- };
71
-
72
66
  interface EvalFlags {
73
67
  action?: string;
74
68
  agent: string;
@@ -11,9 +11,11 @@ import { collectAnswers } from "../init/questions.ts";
11
11
  import {
12
12
  applyPlan,
13
13
  buildPlan,
14
+ cdCommand,
14
15
  commandsFor,
15
16
  detectPackageManager,
16
17
  nextSteps,
18
+ outsidePnpmWorkspace,
17
19
  PACKAGE_MANAGERS,
18
20
  readExistingPackage,
19
21
  TEMPLATES,
@@ -25,7 +27,7 @@ import { logger } from "../log.ts";
25
27
 
26
28
  /** The `cd` line that opens every next-steps hint, when one is needed. */
27
29
  const cdStep = (answers: InitAnswers): string[] =>
28
- answers.directory === "." ? [] : [`cd ${answers.directory}`];
30
+ answers.directory === "." ? [] : [cdCommand(answers.directory)];
29
31
 
30
32
  /**
31
33
  * Install the scaffolded project's dependencies. Interactive runs hide the
@@ -99,8 +101,13 @@ const ejectScaffold = async (
99
101
  } catch (error) {
100
102
  // SAFETY: eject and the script rewrite throw Error instances; only the
101
103
  // message is surfaced in the fallback hint.
104
+ const { message } = error as Error;
105
+ // Only a skipped install explains the failure; after an install that ran,
106
+ // the error itself is the reason.
102
107
  logger.warn(
103
- `Scaffolded, but eject needs the project's dependencies installed to load blume.config.ts: ${(error as Error).message}`
108
+ needsInstall
109
+ ? `Scaffolded, but eject needs the project's dependencies installed to load blume.config.ts: ${message}`
110
+ : `Scaffolded, but eject failed: ${message}`
104
111
  );
105
112
  const steps = [...cd, ...install, `${commands.exec} blume eject --yes`];
106
113
  logger.box(`Next steps:\n\n ${steps.join("\n ")}`);
@@ -229,7 +236,7 @@ export const initCommand = defineCommand({
229
236
  { cwd, userAgent: process.env.npm_config_user_agent }
230
237
  );
231
238
  if (collected === null) {
232
- clack.cancel("Cancelled — nothing was written.");
239
+ clack.cancel("Canceled — nothing was written.");
233
240
  process.exit(0);
234
241
  }
235
242
  answers = collected;
@@ -279,8 +286,11 @@ export const initCommand = defineCommand({
279
286
  }
280
287
 
281
288
  // A newly written package.json is the only one `init` knows lists blume;
282
- // an existing one is left alone, and so are its dependencies.
283
- const shouldInstall = createdPackage && args.install;
289
+ // an existing one is left alone, and so are its dependencies. In a pnpm
290
+ // workspace that doesn't list the folder, an install would exit 0 without
291
+ // installing it, so that one waits until the note's change is made.
292
+ const shouldInstall =
293
+ createdPackage && args.install && !outsidePnpmWorkspace(root, answers);
284
294
  if (note) {
285
295
  (interactive ? clack.log : logger).warn(note);
286
296
  }
@@ -7,8 +7,10 @@ import { join } from "pathe";
7
7
  import { loadConfig } from "../../core/config.ts";
8
8
  import { resolveProjectContext } from "../../core/project.ts";
9
9
  import { deployOutputDir } from "../../deploy/adapter-output.ts";
10
+ import { deployPlatform } from "../../deploy/platforms/index.ts";
10
11
  import { parsePort } from "../args.ts";
11
12
  import { commandMeta } from "../command-meta.ts";
13
+ import { refuseIfEjected } from "../eject-scripts.ts";
12
14
  import { normalizeHost } from "../host-args.ts";
13
15
  import { logger } from "../log.ts";
14
16
 
@@ -20,9 +22,22 @@ export const previewCommand = defineCommand({
20
22
  meta: commandMeta.preview,
21
23
  async run({ args }) {
22
24
  const root = process.cwd();
25
+ await refuseIfEjected(root, "preview");
23
26
  const { config } = await loadConfig(root);
24
27
  const context = resolveProjectContext(root, config);
25
28
 
29
+ // Astro previews a server build through its adapter's preview entrypoint,
30
+ // and the Vercel and Netlify adapters declare none, so Astro would throw
31
+ // mid-command. Say so up front, with what to run instead.
32
+ const { deployment } = config;
33
+ const platform = deployPlatform(deployment);
34
+ if (deployment.options.output === "server" && platform.previewDeploy) {
35
+ logger.error(
36
+ `\`blume preview\` can't serve a ${deployment.kind}() server build: ${platform.astro?.package} has no local preview server. Run \`blume dev\` to try the site locally, or deploy a preview with \`${platform.previewDeploy}\`.`
37
+ );
38
+ process.exit(1);
39
+ }
40
+
26
41
  // `blume dev` writes `.blume/astro.config.mjs` too, so the runtime alone
27
42
  // doesn't mean there's anything to serve: the build output must exist.
28
43
  if (
@@ -10,6 +10,7 @@ import {
10
10
  } from "../../core/project.ts";
11
11
  import { commandMeta } from "../command-meta.ts";
12
12
  import { readDevLock } from "../dev-lock.ts";
13
+ import { refuseIfEjected } from "../eject-scripts.ts";
13
14
  import { logger } from "../log.ts";
14
15
  import { prepareProject } from "../prepare.ts";
15
16
 
@@ -28,6 +29,7 @@ export const syncCommand = defineCommand({
28
29
  meta: commandMeta.sync,
29
30
  async run({ args }) {
30
31
  const root = process.cwd();
32
+ await refuseIfEjected(root, "sync");
31
33
 
32
34
  // `--force` drops the snapshots so a stale or corrupt cache can't be served;
33
35
  // scoped to `.blume/cache`, so a running dev server's runtime is untouched.
@@ -4,7 +4,6 @@ import { AGENTS } from "../../audit/agent.ts";
4
4
  import type { AgentKind } from "../../audit/agent.ts";
5
5
  import { BlumeError } from "../../core/diagnostics.ts";
6
6
  import { i18nEnabled, localeCodes } from "../../core/i18n.ts";
7
- import { scanProject } from "../../core/project-graph.ts";
8
7
  import type { ResolvedI18nConfig } from "../../core/schema.ts";
9
8
  import { DEFAULT_TRANSLATE_TIMEOUT_MS } from "../../translate/agents.ts";
10
9
  import {
@@ -25,7 +24,10 @@ import {
25
24
  translateSummaryLine,
26
25
  } from "../../translate/report.ts";
27
26
  import { runTranslate } from "../../translate/run.ts";
28
- import { computeWorkList } from "../../translate/work-list.ts";
27
+ import {
28
+ computeWorkList,
29
+ scanForTranslation,
30
+ } from "../../translate/work-list.ts";
29
31
  import { commandMeta } from "../command-meta.ts";
30
32
  import { reportInternalError } from "../internal-error.ts";
31
33
  import { flushStdout, logger, reportDiagnostics } from "../log.ts";
@@ -188,10 +190,11 @@ export const translateCommand = defineCommand({
188
190
  const { agent, concurrency, timeoutS } = parseFlags(args);
189
191
 
190
192
  try {
191
- // `scanProject`, not `prepareProject`: translation reads the content
192
- // tree and writes source files, never `.blume/`, so it doesn't contend
193
- // with a running dev server. Same reasoning as `blume audit`/`eval`.
194
- const project = await scanProject(root, { mode: "build" });
193
+ // A scan, not `prepareProject`: translation reads the content tree and
194
+ // writes source files, never `.blume/`, so it doesn't contend with a
195
+ // running dev server. Same reasoning as `blume audit`/`eval`. Drafts
196
+ // are included (see `scanForTranslation`).
197
+ const project = await scanForTranslation(root);
195
198
  if (!i18nEnabled(project.config)) {
196
199
  logger.error(
197
200
  "i18n is not configured — add `i18n.locales` to blume.config to use `blume translate`."
@@ -12,6 +12,7 @@ import {
12
12
  bumpBlumeDependency,
13
13
  collectUpgradeFindings,
14
14
  isOutsideBlumeProject,
15
+ manualBumpAdvice,
15
16
  upgradePrompt,
16
17
  } from "../../upgrade/upgrade.ts";
17
18
  import { commandMeta } from "../command-meta.ts";
@@ -94,10 +95,20 @@ export const upgradeCommand = defineCommand({
94
95
  logger.warn(
95
96
  `No \`blume\` dependency in ./package.json; add blume@^${version} to the package that uses it.`
96
97
  );
98
+ } else if (bump.status === "manual") {
99
+ logger.warn(manualBumpAdvice(bump.range, version));
97
100
  }
98
101
 
99
102
  const findings = await collectUpgradeFindings(root);
100
103
  if (findings.length === 0) {
104
+ // Until the range the bump couldn't rewrite moves, the project still
105
+ // installs the old Blume, so it isn't ready yet.
106
+ if (bump.status === "manual") {
107
+ process.stderr.write(
108
+ ` The config, components.ts, and pages check out for Blume ${version}. Update blume's version as above, reinstall, then run \`${runner} blume build\` to confirm.\n`
109
+ );
110
+ process.exit(1);
111
+ }
101
112
  process.stderr.write(
102
113
  ` Ready for Blume ${version}: the config, components.ts, and pages check out. Run \`${runner} blume build\` to confirm.\n`
103
114
  );
@@ -6,6 +6,7 @@ import { join } from "pathe";
6
6
  import { EJECTED_CONFIG_HEADER } from "../astro/templates.ts";
7
7
  import { packageRoot } from "../core/package-root.ts";
8
8
  import { commandsFor, detectProjectPackageManager } from "./init/scaffold.ts";
9
+ import type { PackageManager } from "./init/scaffold.ts";
9
10
  import { logger } from "./log.ts";
10
11
 
11
12
  /** A JSON value, as `JSON.parse` of a manifest can return. */
@@ -136,22 +137,46 @@ export const isEjectedProject = (root: string): boolean => {
136
137
  );
137
138
  };
138
139
 
140
+ /** The Blume commands that run the hidden `.blume/` copy of the site. */
141
+ export type RuntimeCommand = "build" | "check" | "dev" | "preview" | "sync";
142
+
143
+ /**
144
+ * What an ejected app runs in place of each {@link RuntimeCommand}: its own
145
+ * `dev`/`build`/`preview` scripts (eject writes them), or Astro's CLI for the
146
+ * check and the content sync.
147
+ */
148
+ const ejectedEquivalent = (
149
+ pm: PackageManager,
150
+ command: RuntimeCommand
151
+ ): string => {
152
+ const { build, dev, exec } = commandsFor(pm);
153
+ return {
154
+ build,
155
+ check: `${exec} astro check`,
156
+ dev,
157
+ // Spelled like `dev`: npm runs a custom script only through `run`.
158
+ preview: pm === "npm" ? "npm run preview" : `${pm} preview`,
159
+ sync: `${exec} astro sync`,
160
+ }[command];
161
+ };
162
+
139
163
  /**
140
- * Stop `blume dev`/`blume build` in an ejected app. Either would regenerate
141
- * `.blume/` from `blume.config.ts` and run that hidden copy, ignoring every
142
- * edit made to the app since eject, so point at the app's own script under
143
- * the project's package manager instead.
164
+ * Stop a command that works on the hidden runtime in an ejected app. `dev`,
165
+ * `build`, `check`, and `sync` would regenerate `.blume/` from
166
+ * `blume.config.ts` and run that hidden copy, ignoring every edit made to the
167
+ * app since eject, and `preview` would look for that copy's build; point at
168
+ * the app's own command under the project's package manager instead.
144
169
  */
145
170
  export const refuseIfEjected = async (
146
171
  root: string,
147
- script: "build" | "dev"
172
+ command: RuntimeCommand
148
173
  ): Promise<void> => {
149
174
  if (!isEjectedProject(root)) {
150
175
  return;
151
176
  }
152
- const commands = commandsFor(await detectProjectPackageManager(root));
177
+ const pm = await detectProjectPackageManager(root);
153
178
  logger.error(
154
- `This project was ejected to a standalone Astro app, so \`blume ${script}\` would run a regenerated copy of the site instead of it. Run \`${commands[script]}\` instead.`
179
+ `This project was ejected to a standalone Astro app, so \`blume ${command}\` would run a regenerated copy of the site instead of it. Run \`${ejectedEquivalent(pm, command)}\` instead.`
155
180
  );
156
181
  process.exit(1);
157
182
  };