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
@@ -1,5 +1,4 @@
1
1
  import { needsPlaygroundProxy } from "../openapi/references.ts";
2
- import { searchProviderMeta } from "../search/providers.ts";
3
2
  import type { ResolvedConfig } from "./schema.ts";
4
3
 
5
4
  /**
@@ -8,23 +7,23 @@ import type { ResolvedConfig } from "./schema.ts";
8
7
  */
9
8
  export const serverFeatures = (config: ResolvedConfig): string[] => {
10
9
  const features: string[] = [];
11
- if (config.ai.ask?.enabled && !config.ai.ask.endpoint) {
12
- features.push("Ask AI");
10
+ if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
11
+ features.push("Assistant");
13
12
  }
14
13
  // The hosted MCP server is a live JSON-RPC endpoint, so it needs a runtime.
15
- if (config.ai.mcp.enabled) {
14
+ if (config.agents.mcp.enabled) {
16
15
  features.push("MCP server");
17
16
  }
18
- // The built-in playground proxy (`playground.proxy: true` on the OpenAPI or
19
- // GraphQL block) is a live fetch endpoint at `/_api-proxy`; an external
20
- // proxy URL (string) or a proxy-less playground stays fully static.
17
+ // The built-in playground proxy (`playground.proxy: true` on an `openapi()`
18
+ // or `graphql()` adapter) is a live fetch endpoint at `/_api-proxy`; an
19
+ // external proxy URL (string) or a proxy-less playground stays fully static.
21
20
  if (needsPlaygroundProxy(config)) {
22
21
  features.push("API playground proxy");
23
22
  }
24
- // Mixedbread (and any future provider) that proxies queries through a secret
25
- // server endpoint can't run on a static build.
26
- if (searchProviderMeta(config.search.provider).requiresServer) {
27
- features.push(`Search (${config.search.provider})`);
23
+ // A `server` adapter (Mixedbread) proxies queries through a secret server
24
+ // endpoint, so it can't run on a static build.
25
+ if (config.search.provider.mode === "server") {
26
+ features.push(`Search (${config.search.provider.kind})`);
28
27
  }
29
28
  return features;
30
29
  };
@@ -67,6 +67,13 @@ export interface AssetContext {
67
67
  * download at once. Defaults to no gate.
68
68
  */
69
69
  limit?: <T>(task: () => Promise<T>) => Promise<T>;
70
+ /**
71
+ * Downloads in progress, keyed by asset stem. A source shares one table
72
+ * (alongside `limit`) across every page it materializes, so two pages that
73
+ * reference the same asset share one download instead of racing to publish
74
+ * the same file. Defaults to a table private to this call.
75
+ */
76
+ inFlight?: Map<string, Promise<string>>;
70
77
  /** Abort a download that hasn't completed within this many milliseconds. */
71
78
  timeoutMs?: number;
72
79
  }
@@ -128,6 +135,7 @@ export const materializeAssets = async (
128
135
  ): Promise<{ markdown: string; diagnostics: Diagnostic[] }> => {
129
136
  const doFetch = ctx.fetchImpl ?? globalThis.fetch;
130
137
  const limit = ctx.limit ?? ((task) => task());
138
+ const inFlight = ctx.inFlight ?? new Map<string, Promise<string>>();
131
139
  const timeoutMs = ctx.timeoutMs ?? DEFAULT_TIMEOUT_MS;
132
140
  const diagnostics: Diagnostic[] = [];
133
141
 
@@ -151,13 +159,7 @@ export const materializeAssets = async (
151
159
  }
152
160
 
153
161
  /** Fetch one asset into the asset dir and return its file name. */
154
- const download = async (url: string): Promise<string> => {
155
- // Hash the query-less URL: CMS asset URLs are pre-signed, so the query
156
- // changes on every fetch of the same file — hashing it would mint a new
157
- // file each refresh and re-dirty the content digest. Two real assets
158
- // sharing scheme+host+path and differing only in query are rare enough to
159
- // accept colliding.
160
- const stem = hashText(url.split("?")[0] ?? url);
162
+ const download = async (url: string, stem: string): Promise<string> => {
161
163
  const urlExt = extFromUrl(url);
162
164
  // The name is known up front whenever the path has an extension (every
163
165
  // Notion upload does), so a file from an earlier run is reused as is;
@@ -189,9 +191,8 @@ export const materializeAssets = async (
189
191
  // megabytes where an image was a hundred kilobytes. Write to a temporary
190
192
  // name unique to this attempt and rename on completion, so a download that
191
193
  // dies midway never leaves a truncated file the next run would trust as
192
- // complete, and two pages fetching the same asset at once (the gate bounds
193
- // concurrency, it doesn't dedupe) never write into each other's file —
194
- // both publish identical bytes and the last rename wins.
194
+ // complete. Two pages fetching the same asset at once share one download
195
+ // through `inFlight` (below), so no two attempts ever publish one file.
195
196
  const part = `${target}.${randomUUID()}${PART_SUFFIX}`;
196
197
  try {
197
198
  await writeFile(part, res.body);
@@ -203,11 +204,46 @@ export const materializeAssets = async (
203
204
  return file;
204
205
  };
205
206
 
207
+ /**
208
+ * Publish one asset, joining a download another page already has in
209
+ * flight. The gate sits inside the shared promise, so a page waiting on
210
+ * another page's download holds no slot of its own. A waiter whose
211
+ * publisher failed makes its own attempt, exactly as it would have without
212
+ * sharing — one transient error must not fail every page naming the asset.
213
+ */
214
+ const publish = async (url: string): Promise<string> => {
215
+ // Hash the query-less URL: CMS asset URLs are pre-signed, so the query
216
+ // changes on every fetch of the same file — hashing it would mint a new
217
+ // file each refresh and re-dirty the content digest. Two real assets
218
+ // sharing scheme+host+path and differing only in query are rare enough to
219
+ // accept colliding.
220
+ const stem = hashText(url.split("?")[0] ?? url);
221
+ const pending = inFlight.get(stem);
222
+ if (pending) {
223
+ try {
224
+ return await pending;
225
+ } catch {
226
+ // The failed download has left the table by now: join whichever
227
+ // waiter retried first, or go it alone.
228
+ return await publish(url);
229
+ }
230
+ }
231
+ const own = (async () => {
232
+ try {
233
+ return await limit(() => download(url, stem));
234
+ } finally {
235
+ inFlight.delete(stem);
236
+ }
237
+ })();
238
+ inFlight.set(stem, own);
239
+ return await own;
240
+ };
241
+
206
242
  const rewrites = new Map<string, string>();
207
243
  await Promise.all(
208
244
  [...urls].map(async (url) => {
209
245
  try {
210
- const file = await limit(() => download(url));
246
+ const file = await publish(url);
211
247
  rewrites.set(url, `${ctx.assetsBaseUrl}/${file}`);
212
248
  } catch (error) {
213
249
  // SAFETY: everything thrown in this block is an Error — the manual
@@ -0,0 +1,67 @@
1
+ import { isAbsolute, join, resolve } from "pathe";
2
+
3
+ import {
4
+ DEFAULT_CONTENT_EXCLUDE,
5
+ DEFAULT_CONTENT_INCLUDE,
6
+ } from "../../sources/filesystem.ts";
7
+ import type { ContentSourceAdapter } from "../../sources/registry.ts";
8
+ import type { ResolvedConfig } from "../schema.ts";
9
+
10
+ /** Resolve a source `root` against the project root (absolute passes through). */
11
+ export const resolveSourceRoot = (projectRoot: string, root: string): string =>
12
+ isAbsolute(root) ? root : join(resolve(projectRoot), root);
13
+
14
+ /** The configured sources of one adapter `kind`, in config order. */
15
+ export const sourcesOfKind = <Kind extends ContentSourceAdapter["kind"]>(
16
+ config: ResolvedConfig,
17
+ kind: Kind
18
+ ): Extract<ContentSourceAdapter, { kind: Kind }>[] =>
19
+ config.content.sources.filter(
20
+ (source): source is Extract<ContentSourceAdapter, { kind: Kind }> =>
21
+ source.kind === kind
22
+ );
23
+
24
+ /**
25
+ * The generated `docs` glob collection: its base directory and the include /
26
+ * exclude globs applied under it. Astro's glob loader ids each entry by its
27
+ * path relative to `base`, and a filesystem source ids each entry relative to
28
+ * its own root — so the two only agree when the collection is rooted at that
29
+ * source. The collection therefore roots at the first `filesystem()` source
30
+ * (the implicit one, for a zero-config project). Further filesystem sources
31
+ * must share that root and partition it with globs: every filesystem source's
32
+ * `include` feeds the collection, and only a pattern every one of them
33
+ * excludes is excluded, so no source's pages are globbed away. A source
34
+ * rooted elsewhere can't share the base and is caught by the entry-id guard in
35
+ * `scanProject`. With no filesystem source at all the collection globs nothing
36
+ * and its base is the default `docs` directory, which only anchors paths.
37
+ */
38
+ export interface DocsCollection {
39
+ base: string;
40
+ include: string[];
41
+ exclude: string[];
42
+ }
43
+
44
+ export const resolveDocsCollection = (
45
+ config: ResolvedConfig,
46
+ projectRoot: string
47
+ ): DocsCollection => {
48
+ const [first, ...rest] = sourcesOfKind(config, "filesystem");
49
+ if (!first) {
50
+ return {
51
+ base: resolveSourceRoot(projectRoot, "docs"),
52
+ exclude: DEFAULT_CONTENT_EXCLUDE,
53
+ include: DEFAULT_CONTENT_INCLUDE,
54
+ };
55
+ }
56
+ const include = [
57
+ ...new Set([first, ...rest].flatMap((source) => source.options.include)),
58
+ ];
59
+ const exclude = first.options.exclude.filter((pattern) =>
60
+ rest.every((source) => source.options.exclude.includes(pattern))
61
+ );
62
+ return {
63
+ base: resolveSourceRoot(projectRoot, first.options.root),
64
+ exclude,
65
+ include,
66
+ };
67
+ };
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Contentful rich text → Markdown. Covers the block, inline, and mark types
3
+ * the Rich Text editor emits; embedded entries fall through to a serializer
4
+ * keyed by content type or are noted in a comment. Links to assets and
5
+ * entries arrive unresolved from the Delivery API (`data.target` is a
6
+ * `sys` link), so the source hands in resolvers over the response's
7
+ * `includes`; a document the SDK already resolved (with `fields` inline)
8
+ * lowers the same way.
9
+ */
10
+ import type { JsonObject } from "./json.ts";
11
+ import { asString, getPath, objectsIn } from "./json.ts";
12
+ import type { InlineMarks } from "./lower.ts";
13
+ import {
14
+ absoluteUrl,
15
+ blockquote,
16
+ headingPrefix,
17
+ image,
18
+ joinBlocks,
19
+ listItem,
20
+ markdownDocument,
21
+ renderInline,
22
+ renderLink,
23
+ unsupported,
24
+ writesMdx,
25
+ } from "./lower.ts";
26
+
27
+ /** An asset's file as a Markdown image needs it. */
28
+ export interface ContentfulAsset {
29
+ description?: string;
30
+ title?: string;
31
+ url: string;
32
+ }
33
+
34
+ export interface ContentfulRichTextOptions {
35
+ /** Resolve an asset link (`data.target.sys.id`) to its file. */
36
+ resolveAsset?: (id: string) => ContentfulAsset | null;
37
+ /** Resolve an entry link to the entry (`sys` plus `fields`). */
38
+ resolveEntry?: (id: string) => JsonObject | null;
39
+ /**
40
+ * Serializers for embedded entries, keyed by content type id; return
41
+ * Markdown or MDX. Setting any writes the source's entries as MDX.
42
+ */
43
+ serializers?: Record<string, (entry: JsonObject) => string>;
44
+ }
45
+
46
+ const HEADING = /^heading-(?<level>[1-6])$/u;
47
+ const CELL_PIPE = /\|/gu;
48
+ const CELL_BREAK = /\n+/gu;
49
+
50
+ /** The file behind an asset entry, or null when it carries none. */
51
+ export const assetFromEntry = (asset: JsonObject): ContentfulAsset | null => {
52
+ const url = asString(getPath(asset, "fields.file.url"));
53
+ if (!url) {
54
+ return null;
55
+ }
56
+ return {
57
+ description: asString(getPath(asset, "fields.description")),
58
+ title: asString(getPath(asset, "fields.title")),
59
+ url: absoluteUrl(url),
60
+ };
61
+ };
62
+
63
+ const children = (node: JsonObject): JsonObject[] => objectsIn(node.content);
64
+
65
+ const marksOf = (node: JsonObject): InlineMarks => {
66
+ const marks: InlineMarks = {};
67
+ for (const mark of objectsIn(node.marks)) {
68
+ switch (asString(mark.type)) {
69
+ case "bold": {
70
+ marks.bold = true;
71
+ break;
72
+ }
73
+ case "italic": {
74
+ marks.italic = true;
75
+ break;
76
+ }
77
+ case "code": {
78
+ marks.code = true;
79
+ break;
80
+ }
81
+ case "strikethrough": {
82
+ marks.strike = true;
83
+ break;
84
+ }
85
+ default:
86
+ }
87
+ }
88
+ return marks;
89
+ };
90
+
91
+ /**
92
+ * The linked object a node points at: inline when the document was resolved
93
+ * (`data.target.fields`), else through the resolver by `data.target.sys.id`.
94
+ */
95
+ const linkedTarget = (
96
+ node: JsonObject,
97
+ resolve?: (id: string) => JsonObject | null
98
+ ): JsonObject | null => {
99
+ const target = getPath(node, "data.target");
100
+ if (getPath(node, "data.target.fields") !== undefined) {
101
+ // SAFETY: `data.target.fields` resolved through the object, so `target`
102
+ // is that object.
103
+ return target as JsonObject;
104
+ }
105
+ const id = asString(getPath(node, "data.target.sys.id"));
106
+ return id ? (resolve?.(id) ?? null) : null;
107
+ };
108
+
109
+ const linkedAsset = (
110
+ node: JsonObject,
111
+ options: ContentfulRichTextOptions
112
+ ): ContentfulAsset | null => {
113
+ if (getPath(node, "data.target.fields") !== undefined) {
114
+ const inline = linkedTarget(node);
115
+ return inline ? assetFromEntry(inline) : null;
116
+ }
117
+ const id = asString(getPath(node, "data.target.sys.id"));
118
+ return id ? (options.resolveAsset?.(id) ?? null) : null;
119
+ };
120
+
121
+ const embeddedEntry = (
122
+ node: JsonObject,
123
+ options: ContentfulRichTextOptions
124
+ ): string => {
125
+ const entry = linkedTarget(node, options.resolveEntry);
126
+ const contentType = entry
127
+ ? asString(getPath(entry, "sys.contentType.sys.id"))
128
+ : undefined;
129
+ const serializer = contentType
130
+ ? options.serializers?.[contentType]
131
+ : undefined;
132
+ if (entry && serializer) {
133
+ return serializer(entry);
134
+ }
135
+ return unsupported(
136
+ `Contentful embedded entry${contentType ? `: ${contentType}` : ""}`,
137
+ writesMdx(options.serializers)
138
+ );
139
+ };
140
+
141
+ const renderInlines = (
142
+ nodes: JsonObject[],
143
+ options: ContentfulRichTextOptions
144
+ ): string =>
145
+ nodes
146
+ // oxlint-disable-next-line no-use-before-define -- mutual recursion: a link's label is itself inline content
147
+ .map((node) => renderInlineNode(node, options))
148
+ .join("");
149
+
150
+ const renderInlineNode = (
151
+ node: JsonObject,
152
+ options: ContentfulRichTextOptions
153
+ ): string => {
154
+ switch (asString(node.nodeType)) {
155
+ case "text": {
156
+ return renderInline(asString(node.value) ?? "", marksOf(node));
157
+ }
158
+ case "hyperlink": {
159
+ return renderLink(
160
+ renderInlines(children(node), options),
161
+ asString(getPath(node, "data.uri"))
162
+ );
163
+ }
164
+ case "asset-hyperlink": {
165
+ return renderLink(
166
+ renderInlines(children(node), options),
167
+ linkedAsset(node, options)?.url
168
+ );
169
+ }
170
+ case "embedded-entry-inline": {
171
+ return embeddedEntry(node, options);
172
+ }
173
+ default: {
174
+ // entry-hyperlink (no route to point at) and anything unknown: the label.
175
+ return renderInlines(children(node), options);
176
+ }
177
+ }
178
+ };
179
+
180
+ const renderList = (
181
+ items: JsonObject[],
182
+ marker: (index: number) => string,
183
+ options: ContentfulRichTextOptions
184
+ ): string =>
185
+ items
186
+ .map((item, index) =>
187
+ listItem(
188
+ marker(index),
189
+ // A single newline between an item's paragraph and its nested list
190
+ // keeps the list tight.
191
+ children(item)
192
+ // oxlint-disable-next-line no-use-before-define -- mutual recursion: an item holds blocks, including nested lists
193
+ .map((child) => renderBlock(child, options))
194
+ .filter((block) => block !== "")
195
+ .join("\n")
196
+ )
197
+ )
198
+ .join("\n");
199
+
200
+ const cellText = (
201
+ cell: JsonObject,
202
+ options: ContentfulRichTextOptions
203
+ ): string =>
204
+ children(cell)
205
+ .map((paragraph) => renderInlines(children(paragraph), options))
206
+ .join(" ")
207
+ .replaceAll(CELL_BREAK, " ")
208
+ .replaceAll(CELL_PIPE, String.raw`\|`);
209
+
210
+ const tableRow = (cells: string[]): string => `| ${cells.join(" | ")} |`;
211
+
212
+ const renderTable = (
213
+ node: JsonObject,
214
+ options: ContentfulRichTextOptions
215
+ ): string => {
216
+ const rows = children(node).map((row) =>
217
+ children(row).map((cell) => cellText(cell, options))
218
+ );
219
+ const [head, ...body] = rows;
220
+ if (!head) {
221
+ return "";
222
+ }
223
+ return [
224
+ tableRow(head),
225
+ tableRow(head.map(() => "---")),
226
+ ...body.map((cells) => tableRow(cells)),
227
+ ].join("\n");
228
+ };
229
+
230
+ const renderBlock = (
231
+ node: JsonObject,
232
+ options: ContentfulRichTextOptions
233
+ ): string => {
234
+ const type = asString(node.nodeType) ?? "";
235
+ const heading = HEADING.exec(type)?.groups?.level;
236
+ if (heading) {
237
+ return `${headingPrefix(Number(heading))}${renderInlines(children(node), options)}`;
238
+ }
239
+ switch (type) {
240
+ case "paragraph": {
241
+ return renderInlines(children(node), options);
242
+ }
243
+ case "blockquote": {
244
+ return blockquote(
245
+ joinBlocks(children(node).map((child) => renderBlock(child, options)))
246
+ );
247
+ }
248
+ case "unordered-list": {
249
+ return renderList(children(node), () => "-", options);
250
+ }
251
+ case "ordered-list": {
252
+ return renderList(children(node), (index) => `${index + 1}.`, options);
253
+ }
254
+ case "hr": {
255
+ return "---";
256
+ }
257
+ case "embedded-asset-block": {
258
+ const asset = linkedAsset(node, options);
259
+ // Contentful's own rich-text renderer uses the description as alt text
260
+ // and the title only as a fallback.
261
+ return asset
262
+ ? image(asset.description ?? asset.title ?? "", asset.url)
263
+ : "";
264
+ }
265
+ case "embedded-entry-block": {
266
+ return embeddedEntry(node, options);
267
+ }
268
+ case "table": {
269
+ return renderTable(node, options);
270
+ }
271
+ default: {
272
+ return unsupported(
273
+ `Contentful node: ${type}`,
274
+ writesMdx(options.serializers)
275
+ );
276
+ }
277
+ }
278
+ };
279
+
280
+ /** Serialize a rich text document (`nodeType: "document"`) to Markdown. */
281
+ export const contentfulRichTextToMarkdown = (
282
+ doc: JsonObject,
283
+ options: ContentfulRichTextOptions = {}
284
+ ): string =>
285
+ markdownDocument(children(doc).map((node) => renderBlock(node, options)));
@@ -0,0 +1,173 @@
1
+ import {
2
+ assetFromEntry,
3
+ contentfulRichTextToMarkdown,
4
+ } from "./contentful-rich-text.ts";
5
+ import type { JsonObject } from "./json.ts";
6
+ import {
7
+ asNumber,
8
+ asObject,
9
+ asString,
10
+ getPath,
11
+ isJsonObject,
12
+ objectsIn,
13
+ } from "./json.ts";
14
+ import { writesMdx } from "./lower.ts";
15
+ import type { RemoteFieldMap, RestClient } from "./remote.ts";
16
+ import {
17
+ documentEntry,
18
+ fetchJson,
19
+ queryString,
20
+ remoteSource,
21
+ } from "./remote.ts";
22
+ import type { ContentSource, SourceContext, SourceEntry } from "./types.ts";
23
+
24
+ export interface ContentfulSourceOptions {
25
+ name: string;
26
+ prefix?: string;
27
+ /** Space id. */
28
+ space: string;
29
+ /** Environment id; default `master`. */
30
+ environment?: string;
31
+ /** The content type id whose entries become pages. */
32
+ contentType: string;
33
+ /** Locale code to fetch; omit for the space's default locale. */
34
+ locale?: string;
35
+ /**
36
+ * Field ids mapping an entry onto Blume meta + body. Paths resolve against
37
+ * the entry's `fields`, with `sys` reachable as `sys.<key>`. Defaults:
38
+ * `title`, `description`, `slug`, `body`, `sys.updatedAt`.
39
+ */
40
+ fields?: RemoteFieldMap;
41
+ /** Extra query parameters for the entries request (`fields.section: "sdk"`). */
42
+ params?: Record<string, string>;
43
+ /** Serializers for embedded entries, keyed by content type id. */
44
+ serializers?: Record<string, (entry: JsonObject) => string>;
45
+ /** Delivery API token; defaults to `CONTENTFUL_ACCESS_TOKEN`. */
46
+ token?: string;
47
+ /** Preview API token for `--preview`; defaults to `CONTENTFUL_PREVIEW_TOKEN`. */
48
+ previewToken?: string;
49
+ /** Opt-in dev polling interval (seconds); omit to freeze for the session. */
50
+ pollInterval?: number;
51
+ /** Injected for tests; defaults to the global `fetch`. */
52
+ fetchImpl?: typeof fetch;
53
+ }
54
+
55
+ const DELIVERY_HOST = "https://cdn.contentful.com";
56
+ const PREVIEW_HOST = "https://preview.contentful.com";
57
+ const PAGE_SIZE = 100;
58
+
59
+ const DEFAULT_FIELDS: Required<RemoteFieldMap> = {
60
+ body: "body",
61
+ description: "description",
62
+ lastModified: "sys.updatedAt",
63
+ slug: "slug",
64
+ title: "title",
65
+ };
66
+
67
+ /** The included objects of a page, keyed by `sys.id`. */
68
+ const byId = (objects: JsonObject[]): Map<string, JsonObject> =>
69
+ new Map(
70
+ objects.map((object) => [asString(getPath(object, "sys.id")) ?? "", object])
71
+ );
72
+
73
+ /**
74
+ * Contentful content source. Pages through the Delivery API (the Preview API
75
+ * under `--preview`), maps each entry's fields to frontmatter, and lowers its
76
+ * rich text body to Markdown with the page's `includes` resolving asset and
77
+ * entry links.
78
+ */
79
+ export const contentfulSource = (
80
+ options: ContentfulSourceOptions,
81
+ ctx?: SourceContext
82
+ ): ContentSource => {
83
+ const fields = { ...DEFAULT_FIELDS, ...options.fields };
84
+
85
+ const toEntry = (
86
+ item: JsonObject,
87
+ assets: Map<string, JsonObject>,
88
+ linked: Map<string, JsonObject>
89
+ ): SourceEntry => {
90
+ const sys = asObject(item.sys) ?? {};
91
+ const id = asString(sys.id) ?? "";
92
+ // Field paths resolve against the entry's fields, with `sys` beside them.
93
+ const view: JsonObject = { ...asObject(item.fields), sys };
94
+ return documentEntry(
95
+ view,
96
+ fields,
97
+ id,
98
+ (body) =>
99
+ isJsonObject(body)
100
+ ? contentfulRichTextToMarkdown(body, {
101
+ resolveAsset: (assetId) => {
102
+ const asset = assets.get(assetId);
103
+ return asset ? assetFromEntry(asset) : null;
104
+ },
105
+ resolveEntry: (entryId) => linked.get(entryId) ?? null,
106
+ serializers: options.serializers,
107
+ })
108
+ : "",
109
+ false,
110
+ writesMdx(options.serializers)
111
+ );
112
+ };
113
+
114
+ const fetchEntries = async (): Promise<SourceEntry[]> => {
115
+ const preview = ctx?.preview ?? false;
116
+ const deliveryToken = options.token ?? process.env.CONTENTFUL_ACCESS_TOKEN;
117
+ const previewToken =
118
+ options.previewToken ?? process.env.CONTENTFUL_PREVIEW_TOKEN;
119
+ // The Preview API rejects delivery tokens, so falling back to one would
120
+ // only trade a clear config error for a 401.
121
+ if (preview && !previewToken) {
122
+ throw new Error(
123
+ "Contentful --preview needs a Preview API token: set previewToken or CONTENTFUL_PREVIEW_TOKEN."
124
+ );
125
+ }
126
+ const token = preview ? previewToken : deliveryToken;
127
+ const client: RestClient = {
128
+ fetchImpl: options.fetchImpl,
129
+ headers: token ? { authorization: `Bearer ${token}` } : {},
130
+ };
131
+ const host = preview ? PREVIEW_HOST : DELIVERY_HOST;
132
+ const base = `${host}/spaces/${options.space}/environments/${options.environment ?? "master"}/entries`;
133
+ const entries: SourceEntry[] = [];
134
+ let skip = 0;
135
+ let more = true;
136
+ while (more) {
137
+ // The user's params go first so the paging controls always win — a
138
+ // `params` key that shadowed them would refetch the same page forever.
139
+ const query = queryString({
140
+ ...options.params,
141
+ content_type: options.contentType,
142
+ include: "2",
143
+ limit: String(PAGE_SIZE),
144
+ locale: options.locale,
145
+ skip: String(skip),
146
+ });
147
+ // oxlint-disable-next-line no-await-in-loop -- pages are sequential: each response says whether another exists.
148
+ const page = asObject(await fetchJson(`${base}?${query}`, client));
149
+ if (!page) {
150
+ throw new Error("Contentful returned a non-object response");
151
+ }
152
+ const assets = byId(objectsIn(getPath(page, "includes.Asset")));
153
+ const linked = byId(objectsIn(getPath(page, "includes.Entry")));
154
+ const items = objectsIn(page.items);
155
+ for (const item of items) {
156
+ entries.push(toEntry(item, assets, linked));
157
+ }
158
+ skip += items.length;
159
+ more = items.length > 0 && skip < (asNumber(page.total) ?? 0);
160
+ }
161
+ return entries;
162
+ };
163
+
164
+ return remoteSource(
165
+ {
166
+ fetchEntries,
167
+ name: options.name,
168
+ pollInterval: options.pollInterval,
169
+ prefix: options.prefix,
170
+ },
171
+ ctx
172
+ );
173
+ };