blume 1.7.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (677) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +227 -0
  3. package/README.md +34 -21
  4. package/dist/cli/chunk-11j0384y.js +148 -0
  5. package/dist/cli/chunk-11j0384y.js.map +10 -0
  6. package/dist/cli/{chunk-xhtpx3ff.js → chunk-1w8dp3qb.js} +17 -16
  7. package/dist/cli/{chunk-xhtpx3ff.js.map → chunk-1w8dp3qb.js.map} +3 -3
  8. package/dist/cli/chunk-2q1dwty4.js +75 -0
  9. package/dist/cli/chunk-2q1dwty4.js.map +11 -0
  10. package/dist/cli/chunk-41za066z.js +122 -0
  11. package/dist/cli/chunk-41za066z.js.map +11 -0
  12. package/dist/cli/chunk-5a2z0198.js +133 -0
  13. package/dist/cli/chunk-5a2z0198.js.map +10 -0
  14. package/dist/cli/{chunk-9he6crym.js → chunk-6k8vp3ta.js} +25 -13
  15. package/dist/cli/{chunk-9he6crym.js.map → chunk-6k8vp3ta.js.map} +4 -4
  16. package/dist/cli/{chunk-mt76t7dj.js → chunk-79njf86q.js} +133 -54
  17. package/dist/cli/chunk-79njf86q.js.map +11 -0
  18. package/dist/cli/{chunk-3w7b2vcx.js → chunk-7ez8ny0t.js} +2 -2
  19. package/dist/cli/{chunk-688e0dde.js → chunk-88cpgt6h.js} +1 -1
  20. package/dist/cli/chunk-a9kptbw5.js +361 -0
  21. package/dist/cli/chunk-a9kptbw5.js.map +14 -0
  22. package/dist/cli/chunk-abh8yjkn.js +31 -0
  23. package/dist/cli/chunk-abh8yjkn.js.map +10 -0
  24. package/dist/cli/chunk-b5aj94ah.js +91 -0
  25. package/dist/cli/chunk-b5aj94ah.js.map +10 -0
  26. package/dist/cli/{chunk-hs3gbh8p.js → chunk-bctazmbk.js} +9 -5
  27. package/dist/cli/chunk-bctazmbk.js.map +10 -0
  28. package/dist/cli/chunk-beat36xx.js +279 -0
  29. package/dist/cli/chunk-beat36xx.js.map +10 -0
  30. package/dist/cli/chunk-bnbmcwfb.js +145 -0
  31. package/dist/cli/chunk-bnbmcwfb.js.map +11 -0
  32. package/dist/cli/{chunk-9bkjd11x.js → chunk-bw22s759.js} +15 -5
  33. package/dist/cli/{chunk-9bkjd11x.js.map → chunk-bw22s759.js.map} +4 -4
  34. package/dist/cli/chunk-by2290sx.js +39 -0
  35. package/dist/cli/chunk-by2290sx.js.map +10 -0
  36. package/dist/cli/chunk-d1tadaw7.js +79 -0
  37. package/dist/cli/chunk-d1tadaw7.js.map +10 -0
  38. package/dist/cli/{chunk-t3tj0dgr.js → chunk-d80hr03s.js} +24 -19
  39. package/dist/cli/chunk-d80hr03s.js.map +15 -0
  40. package/dist/cli/chunk-ernrthtr.js +97 -0
  41. package/dist/cli/chunk-ernrthtr.js.map +10 -0
  42. package/dist/cli/chunk-f2972sbt.js +374 -0
  43. package/dist/cli/chunk-f2972sbt.js.map +10 -0
  44. package/dist/cli/{chunk-exeeb35e.js → chunk-f7t03s3g.js} +2 -2
  45. package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
  46. package/dist/cli/chunk-fa25z98p.js.map +11 -0
  47. package/dist/cli/{chunk-12dxjqk7.js → chunk-fh5hj5jt.js} +44 -21
  48. package/dist/cli/chunk-fh5hj5jt.js.map +10 -0
  49. package/dist/cli/{chunk-8cd8tj54.js → chunk-j8mw0za6.js} +86 -57
  50. package/dist/cli/chunk-j8mw0za6.js.map +35 -0
  51. package/dist/cli/chunk-jts8mvcz.js +106 -0
  52. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  53. package/dist/cli/{chunk-ejjx8znq.js → chunk-mnqj32sj.js} +505 -536
  54. package/dist/cli/chunk-mnqj32sj.js.map +12 -0
  55. package/dist/cli/{chunk-196vjxp9.js → chunk-mwt1k8n7.js} +100 -372
  56. package/dist/cli/chunk-mwt1k8n7.js.map +10 -0
  57. package/dist/cli/chunk-nk3ts2xk.js +51 -0
  58. package/dist/cli/chunk-nk3ts2xk.js.map +10 -0
  59. package/dist/cli/{chunk-n9sra6sy.js → chunk-pat2zzwc.js} +10 -14
  60. package/dist/cli/{chunk-n9sra6sy.js.map → chunk-pat2zzwc.js.map} +2 -2
  61. package/dist/cli/chunk-pnnvybbk.js +176 -0
  62. package/dist/cli/chunk-pnnvybbk.js.map +11 -0
  63. package/dist/cli/{chunk-cvky9gb2.js → chunk-sqn5t4q0.js} +81 -87
  64. package/dist/cli/chunk-sqn5t4q0.js.map +10 -0
  65. package/dist/cli/{chunk-eevwt1sc.js → chunk-tzne8qfq.js} +15 -15
  66. package/dist/cli/{chunk-eevwt1sc.js.map → chunk-tzne8qfq.js.map} +1 -1
  67. package/dist/cli/chunk-xaz13gwg.js +12449 -0
  68. package/dist/cli/chunk-xaz13gwg.js.map +182 -0
  69. package/dist/cli/chunk-y3e45rc8.js +102 -0
  70. package/dist/cli/chunk-y3e45rc8.js.map +10 -0
  71. package/dist/cli/chunk-z01ze5c1.js +261 -0
  72. package/dist/cli/chunk-z01ze5c1.js.map +10 -0
  73. package/dist/cli/{chunk-5n7t497w.js → chunk-z1f5arsg.js} +247 -755
  74. package/dist/cli/chunk-z1f5arsg.js.map +36 -0
  75. package/dist/cli/{chunk-hdm2dkd2.js → chunk-zg2gtj10.js} +1086 -2596
  76. package/dist/cli/chunk-zg2gtj10.js.map +35 -0
  77. package/dist/cli/{chunk-ppfvdcd4.js → chunk-zxccj738.js} +1 -1
  78. package/dist/cli/index.js +214 -57
  79. package/dist/cli/index.js.map +8 -7
  80. package/dist/types/ai/agent-readability.d.ts +52 -0
  81. package/dist/types/ai/ai-catalog.d.ts +42 -0
  82. package/dist/types/ai/api/paths.d.ts +17 -0
  83. package/dist/types/ai/api-catalog.d.ts +18 -0
  84. package/dist/types/ai/ask.d.ts +368 -0
  85. package/dist/types/ai/changelog-markdown.d.ts +2 -0
  86. package/dist/types/ai/component-markdown.d.ts +2 -2
  87. package/dist/types/ai/index.d.ts +24 -0
  88. package/dist/types/ai/link-headers.d.ts +24 -0
  89. package/dist/types/ai/llms.d.ts +25 -0
  90. package/dist/types/ai/markdown.d.ts +45 -0
  91. package/dist/types/ai/mcp/discovery.d.ts +68 -0
  92. package/dist/types/ai/mcp/tools.d.ts +16 -0
  93. package/dist/types/ai/openapi-components.d.ts +43 -0
  94. package/dist/types/ai/relative-links.d.ts +26 -0
  95. package/dist/types/ai/serializers.d.ts +15 -0
  96. package/dist/types/ai/skills.d.ts +42 -0
  97. package/dist/types/ai/tar.d.ts +25 -0
  98. package/dist/types/ai/visibility.d.ts +17 -0
  99. package/dist/types/ai/web-bot-auth.d.ts +16 -0
  100. package/dist/types/analytics/adobe.d.ts +35 -0
  101. package/dist/types/analytics/amplitude.d.ts +50 -0
  102. package/dist/types/analytics/clarity.d.ts +31 -0
  103. package/dist/types/analytics/clearbit.d.ts +29 -0
  104. package/dist/types/analytics/cloudflare.d.ts +41 -0
  105. package/dist/types/analytics/fathom.d.ts +41 -0
  106. package/dist/types/analytics/google-analytics.d.ts +44 -0
  107. package/dist/types/analytics/google-tag-manager.d.ts +40 -0
  108. package/dist/types/analytics/head.d.ts +30 -0
  109. package/dist/types/analytics/heap.d.ts +40 -0
  110. package/dist/types/analytics/hightouch.d.ts +44 -0
  111. package/dist/types/analytics/hotjar.d.ts +33 -0
  112. package/dist/types/analytics/index.d.ts +61 -0
  113. package/dist/types/analytics/inline.d.ts +17 -0
  114. package/dist/types/analytics/logrocket.d.ts +44 -0
  115. package/dist/types/analytics/mixpanel.d.ts +67 -0
  116. package/dist/types/analytics/pirsch.d.ts +42 -0
  117. package/dist/types/analytics/plausible.d.ts +56 -0
  118. package/dist/types/analytics/posthog.d.ts +45 -0
  119. package/dist/types/analytics/schema.d.ts +320 -0
  120. package/dist/types/analytics/script.d.ts +46 -0
  121. package/dist/types/analytics/segment.d.ts +50 -0
  122. package/dist/types/analytics/vercel.d.ts +48 -0
  123. package/dist/types/astro/integration.d.ts +76 -0
  124. package/dist/types/astro/markdown-negotiation.d.ts +23 -0
  125. package/dist/types/astro/module-types.d.ts +14 -0
  126. package/dist/types/astro/pages.d.ts +44 -0
  127. package/dist/types/cli/env.d.ts +12 -0
  128. package/dist/types/cli/init/scaffold.d.ts +154 -0
  129. package/dist/types/components/layout/nav-utils.d.ts +11 -0
  130. package/dist/types/core/adapter.d.ts +47 -0
  131. package/dist/types/core/api-name.d.ts +7 -0
  132. package/dist/types/core/changelog-index.d.ts +13 -0
  133. package/dist/types/core/config-input.d.ts +209 -505
  134. package/dist/types/core/config.d.ts +60 -34
  135. package/dist/types/core/content-assets.d.ts +76 -0
  136. package/dist/types/core/custom-pages.d.ts +33 -0
  137. package/dist/types/core/data.d.ts +28 -11
  138. package/dist/types/core/define-components.d.ts +12 -9
  139. package/dist/types/core/deployment-env.d.ts +6 -11
  140. package/dist/types/core/frontmatter.d.ts +10 -0
  141. package/dist/types/core/graph.d.ts +18 -0
  142. package/dist/types/core/heading-markers.d.ts +54 -0
  143. package/dist/types/core/i18n-ui.d.ts +6 -2
  144. package/dist/types/core/i18n.d.ts +87 -0
  145. package/dist/types/core/includes.d.ts +138 -0
  146. package/dist/types/core/last-modified.d.ts +47 -0
  147. package/dist/types/core/links.d.ts +95 -0
  148. package/dist/types/core/locale-links.d.ts +60 -0
  149. package/dist/types/core/manifest.d.ts +17 -0
  150. package/dist/types/core/meta.d.ts +38 -0
  151. package/dist/types/core/nav-diagnostics.d.ts +26 -0
  152. package/dist/types/core/navigation.d.ts +6 -0
  153. package/dist/types/core/node-require.d.ts +19 -0
  154. package/dist/types/core/package-json.d.ts +13 -0
  155. package/dist/types/core/probe.d.ts +42 -0
  156. package/dist/types/core/project-graph.d.ts +51 -0
  157. package/dist/types/core/project.d.ts +2 -0
  158. package/dist/types/core/safe-href.d.ts +2 -0
  159. package/dist/types/core/safe-links.d.ts +26 -0
  160. package/dist/types/core/schema.d.ts +2291 -441
  161. package/dist/types/core/site-url.d.ts +16 -0
  162. package/dist/types/core/sources/assets.d.ts +36 -0
  163. package/dist/types/core/sources/cache.d.ts +37 -0
  164. package/dist/types/core/sources/collection.d.ts +28 -0
  165. package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
  166. package/dist/types/core/sources/contentful.d.ts +40 -0
  167. package/dist/types/core/sources/filesystem.d.ts +24 -0
  168. package/dist/types/core/sources/github-releases.d.ts +31 -0
  169. package/dist/types/core/sources/json.d.ts +30 -0
  170. package/dist/types/core/sources/lexical.d.ts +19 -0
  171. package/dist/types/core/sources/lower.d.ts +75 -0
  172. package/dist/types/core/sources/mdx-remote.d.ts +28 -0
  173. package/dist/types/core/sources/normalize.d.ts +147 -0
  174. package/dist/types/core/sources/notion.d.ts +131 -0
  175. package/dist/types/core/sources/obsidian.d.ts +46 -0
  176. package/dist/types/core/sources/payload.d.ts +39 -0
  177. package/dist/types/core/sources/portable-text.d.ts +42 -0
  178. package/dist/types/core/sources/read.d.ts +23 -0
  179. package/dist/types/core/sources/remote.d.ts +74 -0
  180. package/dist/types/core/sources/resolve.d.ts +19 -0
  181. package/dist/types/core/sources/sanity.d.ts +43 -0
  182. package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
  183. package/dist/types/core/sources/strapi.d.ts +33 -0
  184. package/dist/types/core/sources/types.d.ts +7 -0
  185. package/dist/types/core/sources/watch.d.ts +45 -0
  186. package/dist/types/core/text-width.d.ts +11 -0
  187. package/dist/types/core/types.d.ts +20 -1
  188. package/dist/types/core/unrecognized-keys.d.ts +7 -0
  189. package/dist/types/core/versions.d.ts +72 -0
  190. package/dist/types/core/yaml.d.ts +9 -0
  191. package/dist/types/deploy/adapter-output.d.ts +45 -0
  192. package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
  193. package/dist/types/deploy/adapters/index.d.ts +29 -0
  194. package/dist/types/deploy/adapters/netlify.d.ts +37 -0
  195. package/dist/types/deploy/adapters/node.d.ts +37 -0
  196. package/dist/types/deploy/adapters/registry.d.ts +133 -0
  197. package/dist/types/deploy/adapters/types.d.ts +71 -0
  198. package/dist/types/deploy/adapters/vercel.d.ts +38 -0
  199. package/dist/types/deploy/artifacts.d.ts +65 -0
  200. package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
  201. package/dist/types/deploy/function-bundle.d.ts +80 -0
  202. package/dist/types/deploy/headers.d.ts +50 -0
  203. package/dist/types/deploy/node-headers.d.ts +42 -0
  204. package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
  205. package/dist/types/deploy/platforms/index.d.ts +18 -0
  206. package/dist/types/deploy/platforms/netlify.d.ts +13 -0
  207. package/dist/types/deploy/platforms/node.d.ts +11 -0
  208. package/dist/types/deploy/platforms/paths.d.ts +27 -0
  209. package/dist/types/deploy/platforms/static.d.ts +10 -0
  210. package/dist/types/deploy/platforms/types.d.ts +104 -0
  211. package/dist/types/deploy/platforms/vercel.d.ts +32 -0
  212. package/dist/types/deploy/redirects.d.ts +53 -0
  213. package/dist/types/deploy/robots.d.ts +8 -0
  214. package/dist/types/deploy/rss.d.ts +31 -0
  215. package/dist/types/deploy/sitemap.d.ts +21 -0
  216. package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
  217. package/dist/types/markdown/code-title.d.ts +32 -0
  218. package/dist/types/markdown/fence-meta.d.ts +23 -0
  219. package/dist/types/markdown/themes.d.ts +3 -3
  220. package/dist/types/openapi/asyncapi.d.ts +129 -0
  221. package/dist/types/openapi/graphql-build.d.ts +8 -0
  222. package/dist/types/openapi/graphql.d.ts +122 -0
  223. package/dist/types/openapi/model.d.ts +158 -0
  224. package/dist/types/openapi/parse.d.ts +57 -0
  225. package/dist/types/openapi/references.d.ts +37 -25
  226. package/dist/types/openapi/render-mdx.d.ts +33 -0
  227. package/dist/types/openapi/sentence.d.ts +7 -0
  228. package/dist/types/openapi/signature.d.ts +10 -0
  229. package/dist/types/openapi/source.d.ts +22 -0
  230. package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
  231. package/dist/types/reference/asyncapi.d.ts +166 -0
  232. package/dist/types/reference/graphql.d.ts +181 -0
  233. package/dist/types/reference/index.d.ts +32 -0
  234. package/dist/types/reference/openapi.d.ts +165 -0
  235. package/dist/types/reference/options.d.ts +157 -0
  236. package/dist/types/reference/scalar.d.ts +136 -0
  237. package/dist/types/reference/schema.d.ts +630 -0
  238. package/dist/types/search/adapters/algolia.d.ts +32 -0
  239. package/dist/types/search/adapters/flexsearch.d.ts +12 -0
  240. package/dist/types/search/adapters/index.d.ts +31 -0
  241. package/dist/types/search/adapters/mixedbread.d.ts +22 -0
  242. package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
  243. package/dist/types/search/adapters/orama.d.ts +13 -0
  244. package/dist/types/search/adapters/pagefind.d.ts +12 -0
  245. package/dist/types/search/adapters/registry.d.ts +228 -0
  246. package/dist/types/search/adapters/types.d.ts +34 -0
  247. package/dist/types/search/adapters/typesense.d.ts +42 -0
  248. package/dist/types/search/build.d.ts +23 -0
  249. package/dist/types/search/documents.d.ts +90 -0
  250. package/dist/types/search/facets.d.ts +3 -0
  251. package/dist/types/search/sync/algolia.d.ts +14 -0
  252. package/dist/types/search/sync/index.d.ts +14 -0
  253. package/dist/types/search/sync/orama-cloud.d.ts +10 -0
  254. package/dist/types/search/sync/typesense.d.ts +14 -0
  255. package/dist/types/sources/contentful.d.ts +68 -0
  256. package/dist/types/sources/custom.d.ts +20 -0
  257. package/dist/types/sources/filesystem.d.ts +42 -0
  258. package/dist/types/sources/github-releases.d.ts +46 -0
  259. package/dist/types/sources/index.d.ts +48 -0
  260. package/dist/types/sources/mdx-remote.d.ts +63 -0
  261. package/dist/types/sources/notion.d.ts +65 -0
  262. package/dist/types/sources/obsidian.d.ts +34 -0
  263. package/dist/types/sources/payload.d.ts +68 -0
  264. package/dist/types/sources/registry.d.ts +1414 -0
  265. package/dist/types/sources/sanity.d.ts +69 -0
  266. package/dist/types/sources/shared.d.ts +46 -0
  267. package/dist/types/sources/strapi.d.ts +66 -0
  268. package/dist/types/theme/icon-kind.d.ts +11 -0
  269. package/dist/types/theme/icons.d.ts +20 -0
  270. package/docs/01-quickstart.mdx +18 -18
  271. package/docs/02-deployment.mdx +50 -26
  272. package/docs/03-upgrading.mdx +351 -0
  273. package/docs/04-migrating.mdx +58 -0
  274. package/docs/08-faq.mdx +3 -3
  275. package/docs/advanced/blog.mdx +13 -6
  276. package/docs/advanced/changelog.mdx +23 -35
  277. package/docs/advanced/custom-pages.mdx +20 -8
  278. package/docs/advanced/meta.ts +1 -8
  279. package/docs/advanced/skills.mdx +8 -0
  280. package/docs/cli/audit.mdx +646 -0
  281. package/docs/cli/doctor.mdx +29 -0
  282. package/docs/{reference/eval.mdx → cli/evals.mdx} +9 -8
  283. package/docs/cli/index.mdx +105 -0
  284. package/docs/cli/meta.ts +7 -0
  285. package/docs/{reference → cli}/translate.mdx +1 -1
  286. package/docs/cli/validate.mdx +41 -0
  287. package/docs/cli/version.mdx +40 -0
  288. package/docs/configuration/analytics.mdx +350 -59
  289. package/docs/configuration/ask-ai.mdx +176 -61
  290. package/docs/configuration/customization.mdx +15 -9
  291. package/docs/configuration/index.mdx +52 -31
  292. package/docs/configuration/search.mdx +75 -54
  293. package/docs/configuration/theming.mdx +24 -15
  294. package/docs/content/components.mdx +21 -5
  295. package/docs/{reference → content}/frontmatter.mdx +37 -1
  296. package/docs/content/i18n.mdx +8 -6
  297. package/docs/content/includes.mdx +2 -4
  298. package/docs/content/index.mdx +1 -1
  299. package/docs/content/islands.mdx +10 -5
  300. package/docs/content/meta.mdx +1 -1
  301. package/docs/content/meta.ts +1 -0
  302. package/docs/content/navigation.mdx +12 -7
  303. package/docs/content/sources.mdx +145 -50
  304. package/docs/content/syntax.mdx +26 -12
  305. package/docs/content/versioning.mdx +3 -3
  306. package/docs/discoverability/agent-discovery.mdx +91 -10
  307. package/docs/discoverability/index.mdx +5 -5
  308. package/docs/discoverability/json-api.mdx +4 -4
  309. package/docs/discoverability/llms-txt.mdx +6 -6
  310. package/docs/discoverability/markdown.mdx +4 -4
  311. package/docs/discoverability/mcp.mdx +11 -11
  312. package/docs/discoverability/sitemap-and-robots.mdx +3 -3
  313. package/docs/index.mdx +2 -2
  314. package/docs/references/asyncapi.mdx +59 -0
  315. package/docs/{advanced → references}/graphql.mdx +45 -32
  316. package/docs/{reference → references}/meta.ts +2 -2
  317. package/docs/references/openapi.mdx +171 -0
  318. package/docs/references/scalar.mdx +64 -0
  319. package/package.json +42 -8
  320. package/skills/blume/SKILL.md +21 -8
  321. package/skills/blume-migrate/SKILL.md +22 -21
  322. package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
  323. package/skills/blume-migrate/references/docusaurus.md +5 -4
  324. package/skills/blume-migrate/references/fumadocs.md +4 -4
  325. package/skills/blume-migrate/references/mintlify.md +23 -8
  326. package/skills/blume-migrate/references/monorepo.md +6 -6
  327. package/skills/blume-migrate/references/starlight.md +4 -4
  328. package/src/ai/agent-readability.ts +26 -22
  329. package/src/ai/ai-catalog.ts +259 -0
  330. package/src/ai/api/handlers.ts +46 -11
  331. package/src/ai/api-catalog.ts +8 -8
  332. package/src/ai/ask-data.ts +1 -1
  333. package/src/ai/ask.ts +624 -100
  334. package/src/ai/changelog-markdown.ts +91 -0
  335. package/src/ai/component-markdown.ts +328 -12
  336. package/src/ai/index.ts +45 -0
  337. package/src/ai/link-headers.ts +19 -6
  338. package/src/ai/llms.ts +26 -15
  339. package/src/ai/markdown.ts +35 -5
  340. package/src/ai/mcp/data.ts +9 -5
  341. package/src/ai/mcp/discovery.ts +1 -1
  342. package/src/ai/openapi-components.ts +4 -1
  343. package/src/ai/relative-links.ts +170 -0
  344. package/src/ai/serializers.ts +2 -2
  345. package/src/ai/skills.ts +1 -1
  346. package/src/ai/web-bot-auth.ts +2 -2
  347. package/src/analytics/adobe.ts +46 -0
  348. package/src/analytics/amplitude.ts +79 -0
  349. package/src/analytics/clarity.ts +46 -0
  350. package/src/analytics/clearbit.ts +47 -0
  351. package/src/analytics/cloudflare.ts +67 -0
  352. package/src/analytics/fathom.ts +64 -0
  353. package/src/analytics/google-analytics.ts +84 -0
  354. package/src/analytics/google-tag-manager.ts +61 -0
  355. package/src/analytics/head.ts +130 -0
  356. package/src/analytics/heap.ts +65 -0
  357. package/src/analytics/hightouch.ts +77 -0
  358. package/src/analytics/hotjar.ts +47 -0
  359. package/src/analytics/index.ts +71 -0
  360. package/src/analytics/inline.ts +21 -0
  361. package/src/analytics/logrocket.ts +71 -0
  362. package/src/analytics/mixpanel.ts +92 -0
  363. package/src/analytics/pirsch.ts +66 -0
  364. package/src/analytics/plausible.ts +83 -0
  365. package/src/analytics/posthog.ts +78 -0
  366. package/src/analytics/schema.ts +60 -0
  367. package/src/analytics/script.ts +60 -0
  368. package/src/analytics/segment.ts +85 -0
  369. package/src/analytics/vercel.ts +50 -0
  370. package/src/astro/adapter-root.ts +7 -9
  371. package/src/astro/component-slots.ts +131 -91
  372. package/src/astro/generate.ts +114 -345
  373. package/src/astro/integration.ts +2 -6
  374. package/src/astro/pages.ts +13 -101
  375. package/src/astro/render-deps.ts +379 -0
  376. package/src/astro/runtime-deps.ts +201 -0
  377. package/src/astro/templates.ts +554 -541
  378. package/src/audit/agent.ts +24 -0
  379. package/src/audit/catalog.ts +2 -2
  380. package/src/audit/checks/assets.ts +2 -2
  381. package/src/audit/checks/dns-aid.ts +1 -1
  382. package/src/audit/checks/duplicates.ts +3 -1
  383. package/src/audit/checks/i18n.ts +1 -1
  384. package/src/audit/checks/indexability.ts +7 -7
  385. package/src/audit/checks/links.ts +2 -2
  386. package/src/audit/checks/llms.ts +6 -6
  387. package/src/audit/checks/network.ts +3 -3
  388. package/src/audit/checks/og-image.ts +2 -2
  389. package/src/audit/checks/robots.ts +1 -1
  390. package/src/audit/checks/sitemap.ts +2 -2
  391. package/src/audit/checks/social.ts +1 -1
  392. package/src/audit/run.ts +4 -3
  393. package/src/audit/terms.ts +31 -0
  394. package/src/audit/url.ts +13 -13
  395. package/src/cli/command-meta.ts +10 -0
  396. package/src/cli/commands/audit.ts +39 -20
  397. package/src/cli/commands/build.ts +71 -314
  398. package/src/cli/commands/check.ts +1 -0
  399. package/src/cli/commands/dev.ts +40 -1
  400. package/src/cli/commands/doctor.ts +90 -14
  401. package/src/cli/commands/eject.ts +44 -7
  402. package/src/cli/commands/eval.ts +2 -2
  403. package/src/cli/commands/init.ts +161 -41
  404. package/src/cli/commands/migrate.ts +121 -0
  405. package/src/cli/commands/preview.ts +7 -1
  406. package/src/cli/commands/translate.ts +2 -2
  407. package/src/cli/commands/upgrade.ts +141 -0
  408. package/src/cli/commands/version.ts +57 -44
  409. package/src/cli/eject-scripts.ts +121 -6
  410. package/src/cli/index.ts +11 -1
  411. package/src/cli/init/install.ts +70 -0
  412. package/src/cli/init/questions.ts +7 -0
  413. package/src/cli/init/scaffold.ts +459 -75
  414. package/src/cli/lazy-command.ts +37 -1
  415. package/src/cli/prepare.ts +40 -6
  416. package/src/cli/required-secrets.ts +38 -11
  417. package/src/cli/unknown-flags.ts +266 -0
  418. package/src/cli/yarn-pnp.ts +52 -0
  419. package/src/components/content/AccordionItem.astro +7 -1
  420. package/src/components/content/Card.astro +4 -3
  421. package/src/components/content/ColorItem.astro +22 -4
  422. package/src/components/content/GithubInfo.astro +2 -2
  423. package/src/components/content/Prompt.astro +25 -25
  424. package/src/components/content/Tabs.astro +3 -1
  425. package/src/components/content/Tile.astro +2 -3
  426. package/src/components/content/Tooltip.astro +57 -9
  427. package/src/components/content/content-strings.ts +34 -0
  428. package/src/components/content/diff.ts +1 -1
  429. package/src/components/content/mermaid-element.ts +13 -2
  430. package/src/components/content/prompt-markdown.ts +292 -0
  431. package/src/components/content/tooltip-id.ts +41 -0
  432. package/src/components/islands/hooks.ts +75 -3
  433. package/src/components/layout/Analytics.astro +21 -79
  434. package/src/components/layout/Banner.astro +3 -1
  435. package/src/components/layout/DiscoveryLinks.astro +69 -0
  436. package/src/components/layout/Header.astro +83 -20
  437. package/src/components/layout/LanguageSwitcher.astro +4 -1
  438. package/src/components/layout/Logo.astro +23 -2
  439. package/src/components/layout/NavSelector.astro +13 -2
  440. package/src/components/layout/NavTree.astro +12 -3
  441. package/src/components/layout/NavTreeScript.astro +17 -3
  442. package/src/components/layout/PageActions.astro +3 -3
  443. package/src/components/layout/PageFeedback.astro +11 -1
  444. package/src/components/layout/PageLayout.astro +52 -5
  445. package/src/components/layout/ReferenceLayout.astro +21 -12
  446. package/src/components/layout/RootLayout.astro +69 -43
  447. package/src/components/layout/Search.astro +30 -6
  448. package/src/components/layout/WebMcp.astro +1 -1
  449. package/src/components/layout/analytics-client.ts +101 -15
  450. package/src/components/layout/drawer-inert.ts +113 -15
  451. package/src/components/layout/dropdown-clamp.ts +105 -0
  452. package/src/components/layout/head-scripts.ts +15 -6
  453. package/src/components/layout/nav-utils.ts +17 -0
  454. package/src/components/layout/search/algolia.ts +8 -8
  455. package/src/components/layout/search/orama-cloud.ts +9 -7
  456. package/src/components/layout/search/typesense.ts +11 -17
  457. package/src/components/openapi/MessageComposer.astro +2 -2
  458. package/src/components/openapi/Playground.astro +2 -2
  459. package/src/components/openapi/playground-client.ts +79 -10
  460. package/src/core/changelog-index.ts +24 -0
  461. package/src/core/component-overrides.ts +399 -154
  462. package/src/core/config-input.ts +212 -543
  463. package/src/core/config.ts +130 -41
  464. package/src/core/custom-pages.ts +105 -0
  465. package/src/core/data.ts +32 -11
  466. package/src/core/define-components.ts +12 -9
  467. package/src/core/deployment-env.ts +18 -74
  468. package/src/core/diagnostics.ts +14 -7
  469. package/src/core/graph.ts +69 -25
  470. package/src/core/i18n-ui.ts +6 -2
  471. package/src/core/i18n.ts +17 -0
  472. package/src/core/includes.ts +156 -38
  473. package/src/core/last-modified.ts +6 -11
  474. package/src/core/links.ts +166 -2
  475. package/src/core/manifest.ts +10 -3
  476. package/src/core/navigation.ts +137 -19
  477. package/src/core/new-tab.ts +35 -0
  478. package/src/core/node-require.ts +21 -0
  479. package/src/core/project-graph.ts +42 -26
  480. package/src/core/project.ts +9 -4
  481. package/src/core/request-body.ts +61 -0
  482. package/src/core/safe-href.ts +28 -0
  483. package/src/core/safe-links.ts +68 -0
  484. package/src/core/schema.ts +531 -721
  485. package/src/core/server-features.ts +8 -9
  486. package/src/core/sources/assets.ts +47 -11
  487. package/src/core/sources/collection.ts +67 -0
  488. package/src/core/sources/contentful-rich-text.ts +285 -0
  489. package/src/core/sources/contentful.ts +173 -0
  490. package/src/core/sources/github-releases.ts +51 -1
  491. package/src/core/sources/json.ts +71 -0
  492. package/src/core/sources/lexical.ts +195 -0
  493. package/src/core/sources/lower.ts +226 -0
  494. package/src/core/sources/normalize.ts +52 -2
  495. package/src/core/sources/notion.ts +39 -28
  496. package/src/core/sources/payload.ts +135 -0
  497. package/src/core/sources/portable-text.ts +11 -16
  498. package/src/core/sources/remote.ts +226 -0
  499. package/src/core/sources/resolve.ts +104 -166
  500. package/src/core/sources/sanity.ts +16 -49
  501. package/src/core/sources/strapi-blocks.ts +124 -0
  502. package/src/core/sources/strapi.ts +191 -0
  503. package/src/core/sources/types.ts +12 -1
  504. package/src/core/types.ts +20 -1
  505. package/src/core/ui-packs/ar.ts +6 -2
  506. package/src/core/ui-packs/bg.ts +6 -2
  507. package/src/core/ui-packs/bn.ts +6 -2
  508. package/src/core/ui-packs/ca.ts +3 -1
  509. package/src/core/ui-packs/cs.ts +6 -2
  510. package/src/core/ui-packs/da.ts +6 -2
  511. package/src/core/ui-packs/de.ts +6 -2
  512. package/src/core/ui-packs/el.ts +3 -1
  513. package/src/core/ui-packs/es.ts +3 -1
  514. package/src/core/ui-packs/fa.ts +6 -2
  515. package/src/core/ui-packs/fi.ts +6 -2
  516. package/src/core/ui-packs/fr.ts +3 -1
  517. package/src/core/ui-packs/he.ts +6 -2
  518. package/src/core/ui-packs/hi.ts +6 -2
  519. package/src/core/ui-packs/hr.ts +6 -2
  520. package/src/core/ui-packs/hu.ts +6 -2
  521. package/src/core/ui-packs/id.ts +6 -2
  522. package/src/core/ui-packs/it.ts +3 -1
  523. package/src/core/ui-packs/ja.ts +3 -1
  524. package/src/core/ui-packs/ko.ts +3 -1
  525. package/src/core/ui-packs/nl.ts +6 -2
  526. package/src/core/ui-packs/no.ts +6 -2
  527. package/src/core/ui-packs/pl.ts +6 -2
  528. package/src/core/ui-packs/pt-br.ts +3 -1
  529. package/src/core/ui-packs/pt.ts +3 -1
  530. package/src/core/ui-packs/ro.ts +6 -2
  531. package/src/core/ui-packs/ru.ts +6 -2
  532. package/src/core/ui-packs/sk.ts +6 -2
  533. package/src/core/ui-packs/sr.ts +6 -2
  534. package/src/core/ui-packs/sv.ts +6 -2
  535. package/src/core/ui-packs/th.ts +3 -1
  536. package/src/core/ui-packs/tr.ts +6 -2
  537. package/src/core/ui-packs/uk.ts +6 -2
  538. package/src/core/ui-packs/vi.ts +3 -1
  539. package/src/core/ui-packs/zh-tw.ts +3 -1
  540. package/src/core/ui-packs/zh.ts +3 -1
  541. package/src/core/unrecognized-keys.ts +10 -0
  542. package/src/core/version-cut.ts +116 -8
  543. package/src/deploy/adapter-output.ts +57 -97
  544. package/src/deploy/adapters/cloudflare.ts +39 -0
  545. package/src/deploy/adapters/index.ts +41 -0
  546. package/src/deploy/adapters/netlify.ts +34 -0
  547. package/src/deploy/adapters/node.ts +34 -0
  548. package/src/deploy/adapters/registry.ts +127 -0
  549. package/src/deploy/adapters/types.ts +92 -0
  550. package/src/deploy/adapters/vercel.ts +35 -0
  551. package/src/deploy/artifacts.ts +67 -28
  552. package/src/deploy/cloudflare-negotiation.ts +179 -100
  553. package/src/deploy/function-bundle.ts +18 -3
  554. package/src/deploy/headers.ts +129 -22
  555. package/src/deploy/node-headers.ts +198 -0
  556. package/src/deploy/platforms/cloudflare.ts +312 -0
  557. package/src/deploy/platforms/index.ts +67 -0
  558. package/src/deploy/platforms/netlify.ts +52 -0
  559. package/src/deploy/platforms/node.ts +40 -0
  560. package/src/deploy/platforms/paths.ts +42 -0
  561. package/src/deploy/platforms/static.ts +29 -0
  562. package/src/deploy/platforms/types.ts +112 -0
  563. package/src/deploy/platforms/vercel.ts +193 -0
  564. package/src/deploy/redirects.ts +30 -18
  565. package/src/deploy/robots.ts +3 -3
  566. package/src/deploy/rss.ts +2 -2
  567. package/src/deploy/sitemap.ts +2 -2
  568. package/src/deploy/vercel-negotiation.ts +27 -4
  569. package/src/eval/agents.ts +32 -1
  570. package/src/eval/findings.ts +19 -11
  571. package/src/eval/report.ts +10 -2
  572. package/src/markdown/external-links.ts +65 -0
  573. package/src/markdown/include.ts +45 -27
  574. package/src/markdown/index.ts +34 -9
  575. package/src/markdown/inline-code.ts +12 -6
  576. package/src/markdown/relative-links.ts +324 -0
  577. package/src/markdown/themes.ts +3 -3
  578. package/src/migrate/migrate.ts +149 -0
  579. package/src/openapi/parse.ts +64 -16
  580. package/src/openapi/proxy.ts +62 -10
  581. package/src/openapi/references.ts +147 -147
  582. package/src/openapi/render-mdx.ts +32 -10
  583. package/src/openapi/scalar.ts +15 -13
  584. package/src/openapi/sentence.ts +14 -0
  585. package/src/openapi/source.ts +37 -21
  586. package/src/openapi/spec-dependency-error.ts +15 -0
  587. package/src/reference/asyncapi.ts +83 -0
  588. package/src/reference/graphql.ts +89 -0
  589. package/src/reference/index.ts +40 -0
  590. package/src/reference/openapi.ts +83 -0
  591. package/src/reference/options.ts +201 -0
  592. package/src/reference/scalar.ts +136 -0
  593. package/src/reference/schema.ts +88 -0
  594. package/src/registry/eject.ts +97 -42
  595. package/src/search/adapters/algolia.ts +46 -0
  596. package/src/search/adapters/flexsearch.ts +29 -0
  597. package/src/search/adapters/index.ts +39 -0
  598. package/src/search/adapters/mixedbread.ts +39 -0
  599. package/src/search/adapters/orama-cloud.ts +51 -0
  600. package/src/search/adapters/orama.ts +24 -0
  601. package/src/search/adapters/pagefind.ts +27 -0
  602. package/src/search/adapters/registry.ts +131 -0
  603. package/src/search/adapters/types.ts +46 -0
  604. package/src/search/adapters/typesense.ts +57 -0
  605. package/src/search/build.ts +33 -7
  606. package/src/search/documents.ts +6 -0
  607. package/src/search/sync/algolia.ts +12 -11
  608. package/src/search/sync/index.ts +35 -20
  609. package/src/search/sync/orama-cloud.ts +12 -9
  610. package/src/search/sync/typesense.ts +15 -13
  611. package/src/sources/contentful.ts +63 -0
  612. package/src/sources/custom.ts +38 -0
  613. package/src/sources/filesystem.ts +51 -0
  614. package/src/sources/github-releases.ts +52 -0
  615. package/src/sources/index.ts +60 -0
  616. package/src/sources/mdx-remote.ts +76 -0
  617. package/src/sources/notion.ts +63 -0
  618. package/src/sources/obsidian.ts +38 -0
  619. package/src/sources/payload.ts +60 -0
  620. package/src/sources/registry.ts +182 -0
  621. package/src/sources/sanity.ts +66 -0
  622. package/src/sources/shared.ts +52 -0
  623. package/src/sources/strapi.ts +58 -0
  624. package/src/theme/entry.ts +47 -2
  625. package/src/translate/report.ts +40 -7
  626. package/src/upgrade/upgrade.ts +499 -0
  627. package/dist/cli/chunk-12dxjqk7.js.map +0 -10
  628. package/dist/cli/chunk-196vjxp9.js.map +0 -13
  629. package/dist/cli/chunk-2mzebbbz.js +0 -69
  630. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  631. package/dist/cli/chunk-30e87n55.js +0 -108
  632. package/dist/cli/chunk-30e87n55.js.map +0 -10
  633. package/dist/cli/chunk-450a7rcr.js +0 -185
  634. package/dist/cli/chunk-450a7rcr.js.map +0 -11
  635. package/dist/cli/chunk-5n7t497w.js.map +0 -40
  636. package/dist/cli/chunk-61j18dwk.js +0 -5342
  637. package/dist/cli/chunk-61j18dwk.js.map +0 -58
  638. package/dist/cli/chunk-88by27n5.js +0 -17
  639. package/dist/cli/chunk-88by27n5.js.map +0 -10
  640. package/dist/cli/chunk-8cd8tj54.js.map +0 -34
  641. package/dist/cli/chunk-aztttvb3.js +0 -381
  642. package/dist/cli/chunk-aztttvb3.js.map +0 -12
  643. package/dist/cli/chunk-cvky9gb2.js.map +0 -11
  644. package/dist/cli/chunk-ejjx8znq.js.map +0 -15
  645. package/dist/cli/chunk-fmceyezb.js +0 -1007
  646. package/dist/cli/chunk-fmceyezb.js.map +0 -13
  647. package/dist/cli/chunk-hdm2dkd2.js.map +0 -48
  648. package/dist/cli/chunk-hs3gbh8p.js.map +0 -10
  649. package/dist/cli/chunk-jbj4qhfw.js +0 -30
  650. package/dist/cli/chunk-jbj4qhfw.js.map +0 -10
  651. package/dist/cli/chunk-mqb2ka8m.js +0 -68
  652. package/dist/cli/chunk-mqb2ka8m.js.map +0 -10
  653. package/dist/cli/chunk-mt76t7dj.js.map +0 -11
  654. package/dist/cli/chunk-q4rae3bg.js +0 -60
  655. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  656. package/dist/cli/chunk-ra1v2nc2.js +0 -35
  657. package/dist/cli/chunk-ra1v2nc2.js.map +0 -10
  658. package/dist/cli/chunk-t3tj0dgr.js.map +0 -15
  659. package/dist/cli/chunk-tqa1s0k8.js +0 -69
  660. package/dist/cli/chunk-tqa1s0k8.js.map +0 -11
  661. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  662. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  663. package/dist/cli/chunk-vkrsvbr5.js +0 -107
  664. package/dist/cli/chunk-vkrsvbr5.js.map +0 -11
  665. package/dist/cli/chunk-vrfp10qk.js +0 -81
  666. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  667. package/dist/cli/chunk-wjt80jps.js +0 -1049
  668. package/dist/cli/chunk-wjt80jps.js.map +0 -24
  669. package/docs/advanced/api-reference.mdx +0 -240
  670. package/docs/reference/cli.mdx +0 -197
  671. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  672. package/src/components/content/changelog-element.ts +0 -69
  673. package/src/search/providers.ts +0 -91
  674. /package/dist/cli/{chunk-3w7b2vcx.js.map → chunk-7ez8ny0t.js.map} +0 -0
  675. /package/dist/cli/{chunk-688e0dde.js.map → chunk-88cpgt6h.js.map} +0 -0
  676. /package/dist/cli/{chunk-exeeb35e.js.map → chunk-f7t03s3g.js.map} +0 -0
  677. /package/dist/cli/{chunk-ppfvdcd4.js.map → chunk-zxccj738.js.map} +0 -0
@@ -1,3 +1,4 @@
1
+ import type { Heading, Nodes } from "mdast";
1
2
  import { fromMarkdown } from "mdast-util-from-markdown";
2
3
  import { gfmFromMarkdown } from "mdast-util-gfm";
3
4
  import { toString as mdastToString } from "mdast-util-to-string";
@@ -6,6 +7,7 @@ import stringWidth from "string-width";
6
7
 
7
8
  import matter from "../frontmatter.ts";
8
9
  import { PUBLIC_API_URL } from "../github.ts";
10
+ import { neutralizeUnsafeLinks } from "../safe-links.ts";
9
11
  import { columnsPrefix } from "../text-width.ts";
10
12
  import {
11
13
  hashText,
@@ -121,6 +123,47 @@ const releaseDescription = (body: string): string | undefined => {
121
123
  return `${head}…`;
122
124
  };
123
125
 
126
+ /** Every heading in a tree, in document order (lists and quotes included). */
127
+ const headingsIn = (node: Nodes): Heading[] => {
128
+ if (node.type === "heading") {
129
+ return [node];
130
+ }
131
+ const found: Heading[] = [];
132
+ if ("children" in node) {
133
+ for (const child of node.children) {
134
+ found.push(...headingsIn(child));
135
+ }
136
+ }
137
+ return found;
138
+ };
139
+
140
+ /**
141
+ * Lift the notes' headings so the shallowest is an h2. The release page's
142
+ * title is its h1, and changesets opens every section at `### Patch Changes`,
143
+ * which would skip a level on every page. Only ATX headings can sit deeper
144
+ * than h2, so each lift drops leading `#`s; headings are read from the parsed
145
+ * tree, so a `#` inside a code fence is never touched.
146
+ */
147
+ const liftHeadings = (body: string): string => {
148
+ const headings = headingsIn(
149
+ fromMarkdown(body, {
150
+ extensions: [gfm()],
151
+ mdastExtensions: [gfmFromMarkdown()],
152
+ })
153
+ );
154
+ const lift = Math.min(...headings.map((heading) => heading.depth)) - 2;
155
+ if (headings.length === 0 || lift <= 0) {
156
+ return body;
157
+ }
158
+ let lifted = body;
159
+ // Back to front, so each cut leaves the earlier offsets valid.
160
+ for (const heading of headings.toReversed()) {
161
+ const marker = lifted.indexOf("#", heading.position?.start.offset);
162
+ lifted = lifted.slice(0, marker) + lifted.slice(marker + lift);
163
+ }
164
+ return lifted;
165
+ };
166
+
124
167
  /** Slugify a tag into a stable, URL-safe source ref (`v1.2.0` -> `v1-2-0`). */
125
168
  const slugifyTag = (tag: string): string =>
126
169
  tag.toLowerCase().replaceAll(NON_SLUG, "-").replaceAll(EDGE_DASHES, "");
@@ -159,7 +202,12 @@ const releaseToEntry = (release: GithubRelease): SourceEntry => {
159
202
  const title = release.name?.trim() || release.tag_name;
160
203
  const date = release.published_at ?? release.created_at;
161
204
  const category = release.prerelease ? "Prerelease" : "Release";
162
- const body = (release.body ?? "").replaceAll("\r\n", "\n").trim();
205
+ // Release notes are the repository's content, not the site author's, so a
206
+ // link whose destination isn't a web, mail, or relative address
207
+ // (`javascript:`, `data:`) keeps only its label (see `safe-links.ts`).
208
+ const body = liftHeadings(
209
+ neutralizeUnsafeLinks((release.body ?? "").replaceAll("\r\n", "\n")).trim()
210
+ );
163
211
  // A summary in `seo.description` gives each release page a unique meta
164
212
  // description (instead of the site-wide fallback) without also rendering the
165
213
  // visible lede paragraph a top-level `description` would add.
@@ -286,6 +334,8 @@ export const githubReleasesSource = (
286
334
 
287
335
  return {
288
336
  load,
337
+ // Releases are written once, in one language: no locale can translate them.
338
+ monolingual: true,
289
339
  name: options.name,
290
340
  prefix: options.prefix,
291
341
  read,
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The JSON a CMS API returns: arbitrary values a query or REST endpoint
3
+ * hands back, typed as a closed union so a source narrows with predicates
4
+ * instead of casting.
5
+ */
6
+ export type JsonValue =
7
+ | string
8
+ | number
9
+ | boolean
10
+ | null
11
+ | JsonValue[]
12
+ | { [key: string]: JsonValue };
13
+
14
+ /** A JSON object: a document, a node in a rich-text tree, an API envelope. */
15
+ export interface JsonObject {
16
+ [key: string]: JsonValue;
17
+ }
18
+
19
+ /**
20
+ * A node a dot path can descend into. Arrays pass too (matching their runtime
21
+ * string-key indexing, e.g. `items.0`), so the predicate types them as the
22
+ * keyed form both traverse through.
23
+ */
24
+ export const isJsonObject = (
25
+ value: JsonValue | undefined
26
+ ): value is JsonObject => typeof value === "object" && value !== null;
27
+
28
+ /** Resolve a dot path (`slug.current`) against a document. */
29
+ export const getPath = (
30
+ doc: JsonObject,
31
+ path: string
32
+ ): JsonValue | undefined => {
33
+ let current: JsonValue | undefined = doc;
34
+ for (const key of path.split(".")) {
35
+ if (isJsonObject(current)) {
36
+ current = current[key];
37
+ } else {
38
+ return;
39
+ }
40
+ }
41
+ return current;
42
+ };
43
+
44
+ export const isStringValue = (value: JsonValue | undefined): value is string =>
45
+ typeof value === "string";
46
+
47
+ export const asString = (value: JsonValue | undefined): string | undefined =>
48
+ isStringValue(value) ? value : undefined;
49
+
50
+ /** The elements of an array value, or none when the value is not an array. */
51
+ export const asArray = (value: JsonValue | undefined): JsonValue[] =>
52
+ Array.isArray(value) ? value : [];
53
+
54
+ /** The object form of a value, or none when it is a scalar, array, or absent. */
55
+ export const asObject = (
56
+ value: JsonValue | undefined
57
+ ): JsonObject | undefined =>
58
+ isJsonObject(value) && !Array.isArray(value) ? value : undefined;
59
+
60
+ export const isNumberValue = (value: JsonValue | undefined): value is number =>
61
+ typeof value === "number";
62
+
63
+ export const asNumber = (value: JsonValue | undefined): number | undefined =>
64
+ isNumberValue(value) ? value : undefined;
65
+
66
+ /** The object elements of an array value; scalars and nested arrays are dropped. */
67
+ export const objectsIn = (value: JsonValue | undefined): JsonObject[] =>
68
+ asArray(value).flatMap((item) => {
69
+ const object = asObject(item);
70
+ return object ? [object] : [];
71
+ });
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Lexical (Payload's rich text editor) → Markdown. Covers the nodes Payload's
3
+ * default features emit — paragraphs, headings, lists (bullet, numbered,
4
+ * check), quotes, links, uploads, horizontal rules — and hands `block` /
5
+ * `inlineBlock` nodes to a serializer keyed by their `blockType`, or notes
6
+ * them in a comment.
7
+ */
8
+ import type { JsonObject } from "./json.ts";
9
+ import { asNumber, asObject, asString, getPath, objectsIn } from "./json.ts";
10
+ import type { InlineMarks } from "./lower.ts";
11
+ import {
12
+ absoluteUrl,
13
+ blockquote,
14
+ headingPrefix,
15
+ image,
16
+ indent,
17
+ listItem,
18
+ markdownDocument,
19
+ renderInline,
20
+ renderLink,
21
+ unsupported,
22
+ writesMdx,
23
+ } from "./lower.ts";
24
+
25
+ export interface LexicalOptions {
26
+ /** The CMS origin a relative upload URL (`/media/x.png`) resolves against. */
27
+ baseUrl?: string;
28
+ /** Serializers for `block`/`inlineBlock` nodes, keyed by `fields.blockType`. */
29
+ serializers?: Record<string, (fields: JsonObject) => string>;
30
+ }
31
+
32
+ // Lexical's text `format` bitmask.
33
+ const IS_BOLD = 1;
34
+ const IS_ITALIC = 2;
35
+ const IS_STRIKETHROUGH = 4;
36
+ const IS_CODE = 16;
37
+
38
+ /** Whether a power-of-two flag is set in the format bitmask. */
39
+ const hasFlag = (format: number, flag: number): boolean =>
40
+ Math.floor(format / flag) % 2 === 1;
41
+
42
+ const marksOf = (node: JsonObject): InlineMarks => {
43
+ const format = asNumber(node.format) ?? 0;
44
+ return {
45
+ bold: hasFlag(format, IS_BOLD),
46
+ code: hasFlag(format, IS_CODE),
47
+ italic: hasFlag(format, IS_ITALIC),
48
+ strike: hasFlag(format, IS_STRIKETHROUGH),
49
+ };
50
+ };
51
+
52
+ const children = (node: JsonObject): JsonObject[] => objectsIn(node.children);
53
+
54
+ const typeOf = (node: JsonObject): string => asString(node.type) ?? "";
55
+
56
+ const customBlock = (node: JsonObject, options: LexicalOptions): string => {
57
+ const fields = asObject(node.fields);
58
+ const blockType = fields ? asString(fields.blockType) : undefined;
59
+ const serializer = blockType ? options.serializers?.[blockType] : undefined;
60
+ if (fields && serializer) {
61
+ return serializer(fields);
62
+ }
63
+ return unsupported(
64
+ `Lexical block${blockType ? `: ${blockType}` : ""}`,
65
+ writesMdx(options.serializers)
66
+ );
67
+ };
68
+
69
+ const renderInlines = (nodes: JsonObject[], options: LexicalOptions): string =>
70
+ nodes
71
+ // oxlint-disable-next-line no-use-before-define -- mutual recursion: a link's label is itself inline content
72
+ .map((node) => renderInlineNode(node, options))
73
+ .join("");
74
+
75
+ const renderInlineNode = (
76
+ node: JsonObject,
77
+ options: LexicalOptions
78
+ ): string => {
79
+ switch (typeOf(node)) {
80
+ case "text": {
81
+ return renderInline(asString(node.text) ?? "", marksOf(node));
82
+ }
83
+ case "linebreak": {
84
+ return "\n";
85
+ }
86
+ case "tab": {
87
+ return "\t";
88
+ }
89
+ case "link":
90
+ case "autolink": {
91
+ // `fields.url` on Payload 3; `url` on older Lexical payloads. An
92
+ // internal link (`linkType: "internal"`) carries a document, not a
93
+ // URL, so its label stays.
94
+ return renderLink(
95
+ renderInlines(children(node), options),
96
+ asString(getPath(node, "fields.url")) ?? asString(node.url)
97
+ );
98
+ }
99
+ case "inlineBlock": {
100
+ return customBlock(node, options);
101
+ }
102
+ default: {
103
+ return renderInlines(children(node), options);
104
+ }
105
+ }
106
+ };
107
+
108
+ const renderUpload = (node: JsonObject, options: LexicalOptions): string => {
109
+ const value = asObject(node.value);
110
+ const url = value ? asString(value.url) : undefined;
111
+ if (!(value && url)) {
112
+ return unsupported(
113
+ "Lexical upload (fetch with depth 1 to populate it)",
114
+ writesMdx(options.serializers)
115
+ );
116
+ }
117
+ const alt = asString(value.alt) ?? asString(value.filename) ?? "";
118
+ return image(alt, absoluteUrl(url, options.baseUrl));
119
+ };
120
+
121
+ const renderList = (node: JsonObject, options: LexicalOptions): string => {
122
+ const listType = asString(node.listType) ?? "bullet";
123
+ const start = asNumber(node.start) ?? 1;
124
+ const items: string[] = [];
125
+ let lastMarker = "-";
126
+ let index = 0;
127
+ for (const item of children(node)) {
128
+ const kids = children(item);
129
+ const nested = kids.filter((kid) => typeOf(kid) === "list");
130
+ const inline = kids.filter((kid) => typeOf(kid) !== "list");
131
+ const sub = nested.map((list) => renderList(list, options)).join("\n");
132
+ if (inline.length === 0 && nested.length > 0) {
133
+ // Lexical nests a sub-list as its own item holding only the list; it
134
+ // hangs under the item before it, or stands alone when there is none.
135
+ items.push(items.length > 0 ? indent(sub, lastMarker.length + 1) : sub);
136
+ continue;
137
+ }
138
+ lastMarker = listType === "number" ? `${start + index}.` : "-";
139
+ let check = "";
140
+ if (listType === "check") {
141
+ check = item.checked === true ? "[x] " : "[ ] ";
142
+ }
143
+ const line = `${check}${renderInlines(inline, options)}`;
144
+ items.push(listItem(lastMarker, sub ? `${line}\n${sub}` : line));
145
+ index += 1;
146
+ }
147
+ return items.join("\n");
148
+ };
149
+
150
+ const renderBlock = (node: JsonObject, options: LexicalOptions): string => {
151
+ switch (typeOf(node)) {
152
+ case "paragraph": {
153
+ return renderInlines(children(node), options);
154
+ }
155
+ case "heading": {
156
+ const level = Number((asString(node.tag) ?? "h1").slice(1));
157
+ return `${headingPrefix(level)}${renderInlines(children(node), options)}`;
158
+ }
159
+ case "quote": {
160
+ return blockquote(renderInlines(children(node), options));
161
+ }
162
+ case "list": {
163
+ return renderList(node, options);
164
+ }
165
+ case "horizontalrule": {
166
+ return "---";
167
+ }
168
+ case "upload": {
169
+ return renderUpload(node, options);
170
+ }
171
+ case "block": {
172
+ return customBlock(node, options);
173
+ }
174
+ default: {
175
+ return unsupported(
176
+ `Lexical node: ${typeOf(node)}`,
177
+ writesMdx(options.serializers)
178
+ );
179
+ }
180
+ }
181
+ };
182
+
183
+ /**
184
+ * Serialize a Lexical editor state (`{ root: { children } }`, or the root
185
+ * node itself) to Markdown.
186
+ */
187
+ export const lexicalToMarkdown = (
188
+ state: JsonObject,
189
+ options: LexicalOptions = {}
190
+ ): string => {
191
+ const root = asObject(state.root) ?? state;
192
+ return markdownDocument(
193
+ children(root).map((node) => renderBlock(node, options))
194
+ );
195
+ };
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Shared primitives for lowering a CMS's rich-text tree to Markdown text.
3
+ * Every rich-text lowerer (Portable Text, Contentful, Lexical, Strapi
4
+ * blocks) renders inline runs, wraps them in block syntax, and joins blocks
5
+ * with blank lines; these helpers keep that Markdown identical across them.
6
+ */
7
+
8
+ import { isSafeHref } from "../safe-href.ts";
9
+
10
+ // Markdown/raw-HTML structure characters. Rich-text leaves are *plain text* —
11
+ // formatting arrives as marks, never as syntax in the text — so a literal
12
+ // `*`, `_`, `[`, backtick, `~`, or `<` typed in the CMS must render as
13
+ // itself. Unescaped, it opened emphasis or a code span mid-paragraph, and `<`
14
+ // let CMS prose inject raw HTML into the rendered page. `{` and `}` open an
15
+ // expression once an entry is written as MDX (see {@link writesMdx}). CommonMark
16
+ // backslash-escapes every ASCII punctuation character, so `\*` is always the
17
+ // literal asterisk, in `.md` and `.mdx` alike.
18
+ const MARKDOWN_SPECIALS = /[\\`*_{}[\]~<]/gu;
19
+
20
+ // An entity or numeric character reference (`&copy;`, `&#38;`) is decoded
21
+ // even in plain text, so one typed in the CMS would render as the character
22
+ // it names rather than as written. A bare `&` is left alone.
23
+ const CHARACTER_REFERENCE = /&(?=#?[a-z0-9]+;)/giu;
24
+
25
+ // Block syntax is only syntax at the start of a line: `# `, `- `, `+ ` and
26
+ // `1. `/`1) ` need the space (or line end) to become a heading or list item,
27
+ // while `>` opens a quote on its own, and a line of only `=` or `-` turns the
28
+ // line above it into a heading (or, alone, into a rule). A CMS paragraph that
29
+ // begins with one of these — a soft break inside it counts as a line start
30
+ // too — must stay prose.
31
+ const BLOCK_START =
32
+ /^(?<marker>[#+-]|\d+[.)])(?=[ \t]|$)|^(?<quote>>)|^(?<rule>=+|-{2,})(?=[ \t]*$)/gmu;
33
+
34
+ // MDX reads a top-level paragraph that opens with `import ` or `export ` as an
35
+ // ESM statement, and prose like "import the CSV first" then fails the whole
36
+ // page. The keyword's first letter as a character reference renders the same
37
+ // in `.md` and `.mdx`, and MDX no longer sees the keyword. Only a paragraph's
38
+ // first line counts; a blank line inside a run starts a new paragraph.
39
+ const ESM_START = /(?<=^|\n[ \t]*\n)(?<keyword>import|export)(?= )/gu;
40
+
41
+ const escapeBlockStart = (text: string): string =>
42
+ text.replaceAll(BLOCK_START, (marker: string) => {
43
+ // A numbered marker escapes its punctuation (`1\.`), the rest themselves.
44
+ const index = marker.search(/[.)]/u);
45
+ return index === -1
46
+ ? `\\${marker}`
47
+ : `${marker.slice(0, index)}\\${marker.slice(index)}`;
48
+ });
49
+
50
+ const escapeEsmStart = (text: string): string =>
51
+ text.replaceAll(
52
+ ESM_START,
53
+ (keyword: string) => `&#${keyword.codePointAt(0)};${keyword.slice(1)}`
54
+ );
55
+
56
+ export const escapeMarkdownText = (text: string): string =>
57
+ escapeEsmStart(
58
+ escapeBlockStart(
59
+ text
60
+ .replaceAll(MARKDOWN_SPECIALS, String.raw`\$&`)
61
+ .replaceAll(CHARACTER_REFERENCE, String.raw`\&`)
62
+ )
63
+ );
64
+
65
+ /** The marks a lowerer can put on an inline run. */
66
+ export interface InlineMarks {
67
+ bold?: boolean;
68
+ code?: boolean;
69
+ italic?: boolean;
70
+ strike?: boolean;
71
+ }
72
+
73
+ const BACKTICK_RUN = /`+/gu;
74
+
75
+ /** The longest run of backticks in `text`, 0 when there is none. */
76
+ const longestBacktickRun = (text: string): number => {
77
+ let longest = 0;
78
+ for (const run of text.match(BACKTICK_RUN) ?? []) {
79
+ longest = Math.max(longest, run.length);
80
+ }
81
+ return longest;
82
+ };
83
+
84
+ /**
85
+ * A code span whose delimiter outruns any backtick run inside it, padded with
86
+ * a space on each side when the code starts or ends with a backtick (the
87
+ * CommonMark rule that keeps the padding out of the rendered code).
88
+ */
89
+ export const codeSpan = (code: string): string => {
90
+ const fence = "`".repeat(longestBacktickRun(code) + 1);
91
+ const pad = code.startsWith("`") || code.endsWith("`") ? " " : "";
92
+ return `${fence}${pad}${code}${pad}${fence}`;
93
+ };
94
+
95
+ const EDGE_SPACE = /^(?<lead>\s*)(?<body>[\s\S]*?)(?<trail>\s*)$/u;
96
+
97
+ /**
98
+ * Wrap a text run in the Markdown for its marks. Code stays verbatim inside
99
+ * the backticks (an escape would render as a backslash); everything else is
100
+ * escaped first. Whitespace at either edge moves outside the delimiters,
101
+ * since `** bold **` is not strong emphasis but `**bold**` is — editors
102
+ * routinely bold a word together with the space after it.
103
+ */
104
+ export const renderInline = (text: string, marks: InlineMarks): string => {
105
+ const match = EDGE_SPACE.exec(text);
106
+ const lead = match?.groups?.lead ?? "";
107
+ const body = match?.groups?.body ?? "";
108
+ const trail = match?.groups?.trail ?? "";
109
+ if (body === "") {
110
+ return text;
111
+ }
112
+ let out = marks.code ? codeSpan(body) : escapeMarkdownText(body);
113
+ if (marks.bold) {
114
+ out = `**${out}**`;
115
+ }
116
+ if (marks.italic) {
117
+ out = `*${out}*`;
118
+ }
119
+ if (marks.strike) {
120
+ out = `~~${out}~~`;
121
+ }
122
+ return `${lead}${out}${trail}`;
123
+ };
124
+
125
+ // A destination with whitespace or parentheses ends early in `[label](…)`;
126
+ // CommonMark's pointy-bracket form carries it intact.
127
+ const UNSAFE_DESTINATION = /[\s()]/u;
128
+
129
+ /** A link or image destination as Markdown can carry it verbatim. */
130
+ export const destination = (url: string): string =>
131
+ UNSAFE_DESTINATION.test(url) ? `<${url}>` : url;
132
+
133
+ /**
134
+ * A Markdown link, or the label alone when the target is missing — or unsafe:
135
+ * CMS content is the editor's, not the site author's, so a destination that
136
+ * isn't a web, mail, or relative address (`javascript:`, `data:`) never
137
+ * becomes a clickable link on the docs site (see `safe-links.ts`).
138
+ */
139
+ export const renderLink = (label: string, href?: string): string =>
140
+ href && isSafeHref(href) ? `[${label}](${destination(href)})` : label;
141
+
142
+ /** The ATX prefix for a heading level, clamped to Markdown's six. */
143
+ export const headingPrefix = (level: number): string =>
144
+ `${"#".repeat(Math.min(Math.max(Math.trunc(level), 1), 6))} `;
145
+
146
+ /** Quote every line of a block. */
147
+ export const blockquote = (text: string): string =>
148
+ text
149
+ .split("\n")
150
+ .map((line) => (line === "" ? ">" : `> ${line}`))
151
+ .join("\n");
152
+
153
+ /**
154
+ * A list item: the marker on the first line, and every continuation line
155
+ * indented to the marker's width so nested blocks stay inside the item.
156
+ */
157
+ export const listItem = (marker: string, body: string): string => {
158
+ const [first = "", ...rest] = body.split("\n");
159
+ const pad = " ".repeat(marker.length + 1);
160
+ return [
161
+ `${marker} ${first}`,
162
+ ...rest.map((line) => (line === "" ? "" : `${pad}${line}`)),
163
+ ].join("\n");
164
+ };
165
+
166
+ /** Indent every non-empty line, for a nested list hanging off an item. */
167
+ export const indent = (text: string, width: number): string => {
168
+ const pad = " ".repeat(width);
169
+ return text
170
+ .split("\n")
171
+ .map((line) => (line === "" ? "" : `${pad}${line}`))
172
+ .join("\n");
173
+ };
174
+
175
+ /** A fenced code block whose fence outruns any backtick run in the code. */
176
+ export const codeFence = (code: string, language = ""): string => {
177
+ const fence = "`".repeat(Math.max(3, longestBacktickRun(code) + 1));
178
+ return `${fence}${language}\n${code}\n${fence}`;
179
+ };
180
+
181
+ /** A Markdown image; the alt is escaped so a `]` in a caption can't close it. */
182
+ export const image = (alt: string, url: string): string =>
183
+ `![${escapeMarkdownText(alt)}](${destination(url)})`;
184
+
185
+ /**
186
+ * Whether a lowerer's output is written as MDX. Serializers are how a CMS
187
+ * block becomes a Blume component, and components (like directives) only
188
+ * render in MDX, so configuring any serializer switches the source's
189
+ * rich-text bodies to `.mdx`. The escaping above keeps the rest of the
190
+ * lowered text valid there.
191
+ */
192
+ export const writesMdx = <Node>(
193
+ serializers?: Record<string, (node: Node) => string>
194
+ ): boolean => serializers !== undefined && Object.keys(serializers).length > 0;
195
+
196
+ /**
197
+ * A comment marking a node the lowerer has no Markdown for — an MDX comment
198
+ * when the output is MDX, which rejects `<!-- -->`.
199
+ */
200
+ export const unsupported = (what: string, mdx = false): string =>
201
+ mdx ? `{/* unsupported ${what} */}` : `<!-- unsupported ${what} -->`;
202
+
203
+ /** Blocks separated by blank lines; empty blocks are dropped. */
204
+ export const joinBlocks = (blocks: string[]): string =>
205
+ blocks.filter((block) => block !== "").join("\n\n");
206
+
207
+ /** A whole document: its blocks plus the trailing newline a file ends with. */
208
+ export const markdownDocument = (blocks: string[]): string =>
209
+ `${joinBlocks(blocks)}\n`;
210
+
211
+ const ABSOLUTE = /^[a-z][a-z0-9+.-]*:/iu;
212
+
213
+ /**
214
+ * A media URL as a static site can reference it. A protocol-relative
215
+ * Contentful asset (`//images.ctfassets.net/…`) gets `https:`; a path a
216
+ * self-hosted CMS serves (`/uploads/x.png`) resolves against the CMS origin.
217
+ */
218
+ export const absoluteUrl = (url: string, base?: string): string => {
219
+ if (url.startsWith("//")) {
220
+ return `https:${url}`;
221
+ }
222
+ if (ABSOLUTE.test(url) || base === undefined) {
223
+ return url;
224
+ }
225
+ return new URL(url, base).href;
226
+ };
@@ -675,17 +675,66 @@ const scanLinkLine = (
675
675
  return next;
676
676
  };
677
677
 
678
+ /**
679
+ * A component's string `href` (`<Card href="./install" />`): the attribute
680
+ * may sit on a later line than the tag name, where a formatter wraps a long
681
+ * element, so the gap between them spans lines. An expression-valued
682
+ * `href={…}` isn't a literal target, and a lowercase tag is raw HTML in a
683
+ * `.md` page, so neither is matched.
684
+ */
685
+ const COMPONENT_HREF =
686
+ /<[A-Z][\w.]*(?=[\s/>])[^<>]*?\shref=(?:"(?<double>[^"]*)"|'(?<single>[^']*)')/gu;
687
+
688
+ /** The body with fenced blocks and inline code blanked, shape preserved. */
689
+ const maskCode = (lines: readonly string[]): string => {
690
+ let fence: FenceState = null;
691
+ const masked = lines.map((line) => {
692
+ const next = nextFenceState(line, fence);
693
+ const inFence = fence !== null || next !== null;
694
+ fence = next;
695
+ return inFence
696
+ ? " ".repeat(line.length)
697
+ : line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
698
+ });
699
+ return masked.join("\n");
700
+ };
701
+
702
+ /** Every component `href` target in `body`, with its 1-based position. */
703
+ const componentHrefs = (
704
+ lines: readonly string[],
705
+ lineOffset: number
706
+ ): PageLink[] => {
707
+ const links: PageLink[] = [];
708
+ const text = maskCode(lines);
709
+ for (const match of text.matchAll(COMPONENT_HREF)) {
710
+ const target = match.groups?.double ?? match.groups?.single ?? "";
711
+ // The value ends one character (its closing quote) before the match does.
712
+ const at = match.index + match[0].length - target.length - 1;
713
+ const before = text.slice(0, at);
714
+ const lineStart = before.lastIndexOf("\n") + 1;
715
+ links.push({
716
+ column: at - lineStart + 1,
717
+ line: lineOffset + before.split("\n").length,
718
+ target,
719
+ });
720
+ }
721
+ return links;
722
+ };
723
+
678
724
  export const extractLinks = (body: string, lineOffset = 0): PageLink[] => {
679
725
  const links: PageLink[] = [];
680
726
  let fence: FenceState = null;
681
727
  let lineNumber = lineOffset;
682
728
 
683
- for (const line of body.split("\n")) {
729
+ const lines = body.split("\n");
730
+ for (const line of lines) {
684
731
  lineNumber += 1;
685
732
  fence = scanLinkLine(line, lineNumber, fence, links);
686
733
  }
687
734
 
688
- return links;
735
+ return body.includes("href=")
736
+ ? [...links, ...componentHrefs(lines, lineOffset)]
737
+ : links;
689
738
  };
690
739
 
691
740
  // Double-quoted strings hold JSX attribute values and JSON in `{...}` props; a
@@ -1172,6 +1221,7 @@ export const normalizeEntry = (
1172
1221
  lastModified: meta.lastModified ?? entry.lastModified,
1173
1222
  links: entryLinks(entry),
1174
1223
  meta,
1224
+ monolingual: ctx.source.monolingual,
1175
1225
  navPath,
1176
1226
  segments,
1177
1227
  source: { name: ctx.source.name, ref: entry.ref },