blume 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +110 -0
  3. package/README.md +2 -2
  4. package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
  5. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  6. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  7. package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
  8. package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
  9. package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
  10. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  11. package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
  12. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  13. package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
  14. package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
  15. package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
  16. package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
  17. package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
  18. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  19. package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
  20. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  21. package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
  22. package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
  23. package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
  24. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  25. package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
  26. package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
  27. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  28. package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
  29. package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
  30. package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
  31. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  32. package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
  33. package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
  34. package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
  35. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  36. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  37. package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
  38. package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
  39. package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
  40. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  41. package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
  42. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  43. package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
  44. package/dist/cli/chunk-j85scx15.js.map +10 -0
  45. package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
  46. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  47. package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
  48. package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
  49. package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
  50. package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
  51. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  52. package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
  53. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  54. package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
  55. package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
  56. package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
  57. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  58. package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
  59. package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
  60. package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
  61. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  62. package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
  63. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  64. package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
  65. package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
  66. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  67. package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
  68. package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
  69. package/dist/cli/index.js +160 -35
  70. package/dist/cli/index.js.map +4 -4
  71. package/dist/types/ai/agent-readability.d.ts +1 -1
  72. package/dist/types/ai/agent-surface.d.ts +32 -0
  73. package/dist/types/ai/api/paths.d.ts +1 -1
  74. package/dist/types/ai/api-catalog.d.ts +7 -1
  75. package/dist/types/ai/ask-context.d.ts +14 -7
  76. package/dist/types/ai/ask.d.ts +43 -43
  77. package/dist/types/ai/component-markdown.d.ts +4 -4
  78. package/dist/types/ai/index.d.ts +3 -3
  79. package/dist/types/ai/openapi-components.d.ts +1 -1
  80. package/dist/types/ai/relative-links.d.ts +9 -4
  81. package/dist/types/ai/serializers.d.ts +1 -1
  82. package/dist/types/ai/static-expression.d.ts +28 -0
  83. package/dist/types/ai/visibility.d.ts +1 -1
  84. package/dist/types/analytics/databuddy.d.ts +43 -0
  85. package/dist/types/analytics/index.d.ts +2 -0
  86. package/dist/types/analytics/schema.d.ts +14 -0
  87. package/dist/types/cli/env.d.ts +5 -0
  88. package/dist/types/cli/init/scaffold.d.ts +19 -3
  89. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  90. package/dist/types/core/base-path.d.ts +9 -0
  91. package/dist/types/core/config-input.d.ts +21 -21
  92. package/dist/types/core/config.d.ts +5 -5
  93. package/dist/types/core/data.d.ts +3 -3
  94. package/dist/types/core/graph.d.ts +2 -0
  95. package/dist/types/core/i18n-ui.d.ts +37 -8
  96. package/dist/types/core/i18n.d.ts +7 -1
  97. package/dist/types/core/links.d.ts +3 -1
  98. package/dist/types/core/load-module.d.ts +10 -0
  99. package/dist/types/core/locale-links.d.ts +12 -2
  100. package/dist/types/core/meta.d.ts +5 -1
  101. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  102. package/dist/types/core/navigation.d.ts +38 -0
  103. package/dist/types/core/ordering-prefix.d.ts +4 -0
  104. package/dist/types/core/safe-links.d.ts +3 -1
  105. package/dist/types/core/schema.d.ts +57 -18
  106. package/dist/types/core/sources/github-releases.d.ts +5 -0
  107. package/dist/types/core/sources/lower.d.ts +14 -2
  108. package/dist/types/core/sources/normalize.d.ts +10 -1
  109. package/dist/types/core/sources/remote.d.ts +11 -1
  110. package/dist/types/core/sources/resolve.d.ts +12 -0
  111. package/dist/types/core/sources/types.d.ts +31 -0
  112. package/dist/types/core/types.d.ts +9 -0
  113. package/dist/types/core/unrecognized-keys.d.ts +1 -1
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  117. package/dist/types/deploy/headers.d.ts +31 -7
  118. package/dist/types/deploy/node-headers.d.ts +43 -8
  119. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  120. package/dist/types/deploy/platforms/node.d.ts +6 -5
  121. package/dist/types/deploy/platforms/types.d.ts +7 -0
  122. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  123. package/dist/types/deploy/redirects.d.ts +7 -2
  124. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  125. package/dist/types/openapi/model.d.ts +20 -6
  126. package/dist/types/search/documents.d.ts +1 -1
  127. package/dist/types/search/orama-index.d.ts +1 -1
  128. package/dist/types/search/sync/algolia.d.ts +3 -1
  129. package/docs/01-quickstart.mdx +3 -2
  130. package/docs/02-deployment.mdx +21 -12
  131. package/docs/03-upgrading.mdx +22 -9
  132. package/docs/04-migrating.mdx +4 -4
  133. package/docs/08-faq.mdx +12 -5
  134. package/docs/advanced/changelog.mdx +1 -1
  135. package/docs/advanced/custom-pages.mdx +5 -3
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +5 -5
  138. package/docs/cli/doctor.mdx +4 -4
  139. package/docs/cli/evals.mdx +9 -9
  140. package/docs/cli/index.mdx +6 -3
  141. package/docs/cli/translate.mdx +8 -8
  142. package/docs/configuration/analytics.mdx +23 -2
  143. package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
  144. package/docs/configuration/customization.mdx +5 -4
  145. package/docs/configuration/index.mdx +7 -5
  146. package/docs/configuration/meta.ts +1 -1
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +1 -1
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -3
  151. package/docs/content/index.mdx +4 -2
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +5 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -12
  156. package/docs/content/versioning.mdx +1 -0
  157. package/docs/discoverability/agent-discovery.mdx +22 -8
  158. package/docs/discoverability/index.mdx +3 -3
  159. package/docs/discoverability/llms-txt.mdx +2 -5
  160. package/docs/discoverability/markdown.mdx +5 -3
  161. package/docs/discoverability/mcp.mdx +4 -0
  162. package/docs/discoverability/metadata.mdx +3 -2
  163. package/docs/discoverability/open-graph.mdx +6 -4
  164. package/docs/discoverability/rss.mdx +3 -1
  165. package/docs/index.mdx +2 -2
  166. package/docs/references/asyncapi.mdx +1 -1
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +3 -3
  169. package/package.json +1 -1
  170. package/skills/blume/SKILL.md +5 -5
  171. package/skills/blume-migrate/SKILL.md +6 -6
  172. package/skills/blume-migrate/references/docusaurus.md +6 -6
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +1 -1
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +4 -4
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/api/paths.ts +1 -1
  182. package/src/ai/api/spec.ts +15 -2
  183. package/src/ai/api-catalog.ts +7 -1
  184. package/src/ai/ask-context.ts +21 -9
  185. package/src/ai/ask-data.ts +28 -14
  186. package/src/ai/ask.ts +84 -71
  187. package/src/ai/component-markdown.ts +43 -37
  188. package/src/ai/cors.ts +3 -3
  189. package/src/ai/index.ts +16 -16
  190. package/src/ai/link-headers.ts +8 -3
  191. package/src/ai/llms.ts +4 -3
  192. package/src/ai/markdown.ts +23 -11
  193. package/src/ai/mcp/server.ts +78 -7
  194. package/src/ai/openapi-components.ts +1 -1
  195. package/src/ai/relative-links.ts +78 -10
  196. package/src/ai/serializers.ts +1 -1
  197. package/src/ai/static-expression.ts +416 -0
  198. package/src/ai/visibility.ts +46 -15
  199. package/src/analytics/databuddy.ts +67 -0
  200. package/src/analytics/head.ts +4 -0
  201. package/src/analytics/index.ts +2 -0
  202. package/src/analytics/posthog.ts +21 -3
  203. package/src/analytics/schema.ts +2 -0
  204. package/src/astro/generate.ts +90 -45
  205. package/src/astro/module-types.ts +1 -1
  206. package/src/astro/runtime-deps.ts +6 -6
  207. package/src/astro/templates.ts +87 -49
  208. package/src/audit/catalog.ts +2 -2
  209. package/src/audit/checks/assets.ts +25 -5
  210. package/src/audit/checks/content.ts +20 -2
  211. package/src/audit/checks/i18n.ts +46 -8
  212. package/src/audit/checks/indexability.ts +39 -19
  213. package/src/audit/checks/links.ts +14 -0
  214. package/src/audit/checks/llms.ts +6 -3
  215. package/src/audit/checks/network.ts +3 -1
  216. package/src/audit/checks/og-image.ts +10 -0
  217. package/src/audit/checks/robots.ts +6 -1
  218. package/src/audit/checks/sitemap.ts +56 -31
  219. package/src/audit/checks/social.ts +21 -2
  220. package/src/audit/crawl.ts +88 -15
  221. package/src/audit/report.ts +54 -17
  222. package/src/audit/run.ts +1 -0
  223. package/src/audit/types.ts +18 -0
  224. package/src/audit/url.ts +37 -6
  225. package/src/blume-modules.d.ts +2 -2
  226. package/src/cli/build-failure.ts +50 -0
  227. package/src/cli/commands/audit.ts +8 -1
  228. package/src/cli/commands/build.ts +19 -5
  229. package/src/cli/commands/check.ts +2 -0
  230. package/src/cli/commands/doctor.ts +7 -5
  231. package/src/cli/commands/eval.ts +4 -10
  232. package/src/cli/commands/init.ts +15 -5
  233. package/src/cli/commands/migrate.ts +2 -2
  234. package/src/cli/commands/preview.ts +15 -0
  235. package/src/cli/commands/sync.ts +2 -0
  236. package/src/cli/commands/translate.ts +12 -9
  237. package/src/cli/commands/upgrade.ts +13 -2
  238. package/src/cli/eject-scripts.ts +32 -7
  239. package/src/cli/env.ts +12 -1
  240. package/src/cli/init/scaffold.ts +117 -11
  241. package/src/cli/init/starter-spec.ts +235 -0
  242. package/src/cli/required-secrets.ts +3 -3
  243. package/src/components/content/AccordionItem.astro +26 -22
  244. package/src/components/content/Badge.astro +2 -9
  245. package/src/components/content/Card.astro +2 -2
  246. package/src/components/content/Component.astro +9 -3
  247. package/src/components/content/Expandable.astro +5 -1
  248. package/src/components/content/Frame.astro +2 -7
  249. package/src/components/content/GithubInfo.astro +12 -2
  250. package/src/components/content/Prompt.astro +2 -7
  251. package/src/components/content/Tabs.astro +54 -5
  252. package/src/components/content/Tile.astro +1 -1
  253. package/src/components/content/Tooltip.astro +69 -7
  254. package/src/components/content/Tree.astro +7 -2
  255. package/src/components/content/TypeTable.astro +10 -5
  256. package/src/components/content/Update.astro +8 -2
  257. package/src/components/content/auto-type-table.ts +4 -1
  258. package/src/components/content/badge-color.ts +17 -0
  259. package/src/components/content/base-href.ts +18 -3
  260. package/src/components/content/inline-markdown.ts +27 -7
  261. package/src/components/copy-feedback.ts +36 -9
  262. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  263. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
  264. package/src/components/islands/hooks.ts +21 -14
  265. package/src/components/islands/webmcp.ts +12 -8
  266. package/src/components/layout/Banner.astro +23 -4
  267. package/src/components/layout/Header.astro +32 -19
  268. package/src/components/layout/Logo.astro +5 -0
  269. package/src/components/layout/NavSelector.astro +6 -1
  270. package/src/components/layout/NavTabMenu.astro +133 -0
  271. package/src/components/layout/NavTree.astro +15 -6
  272. package/src/components/layout/NavTreeCache.astro +5 -2
  273. package/src/components/layout/NavTreeScript.astro +45 -5
  274. package/src/components/layout/PageLayout.astro +28 -14
  275. package/src/components/layout/Pagination.astro +7 -7
  276. package/src/components/layout/ReferenceLayout.astro +5 -2
  277. package/src/components/layout/RootLayout.astro +27 -14
  278. package/src/components/layout/Search.astro +18 -14
  279. package/src/components/layout/analytics-client.ts +3 -1
  280. package/src/components/layout/drawer-inert.ts +1 -1
  281. package/src/components/openapi/GraphqlType.astro +11 -3
  282. package/src/components/openapi/MessageComposer.astro +1 -1
  283. package/src/components/openapi/Operation.astro +21 -5
  284. package/src/components/openapi/PanelTabs.astro +4 -1
  285. package/src/components/openapi/Playground.astro +8 -2
  286. package/src/components/openapi/RequestPanel.astro +9 -5
  287. package/src/components/openapi/SchemaProperty.astro +11 -33
  288. package/src/components/openapi/SchemaTable.astro +27 -52
  289. package/src/components/openapi/description.ts +2 -2
  290. package/src/components/openapi/helpers.ts +90 -12
  291. package/src/components/openapi/message-composer.ts +8 -0
  292. package/src/components/openapi/message-model.ts +12 -2
  293. package/src/components/openapi/message.ts +4 -1
  294. package/src/components/openapi/operation-model.ts +40 -8
  295. package/src/components/openapi/panel.ts +29 -5
  296. package/src/components/openapi/playground-client.ts +37 -7
  297. package/src/components/openapi/playground-schema.ts +25 -6
  298. package/src/components/openapi/request.ts +2 -0
  299. package/src/components/openapi/schema-tree.ts +203 -0
  300. package/src/components/openapi/snippets.ts +30 -15
  301. package/src/components/openapi/validate-json.ts +1 -1
  302. package/src/core/base-path.ts +15 -0
  303. package/src/core/code-fences.ts +1 -1
  304. package/src/core/config-input.ts +23 -23
  305. package/src/core/config.ts +21 -7
  306. package/src/core/data.ts +3 -3
  307. package/src/core/diagnostics.ts +217 -30
  308. package/src/core/graph.ts +120 -11
  309. package/src/core/i18n-ui.ts +74 -11
  310. package/src/core/i18n.ts +13 -2
  311. package/src/core/links.ts +7 -4
  312. package/src/core/load-module.ts +20 -0
  313. package/src/core/locale-links.ts +17 -17
  314. package/src/core/manifest.ts +3 -2
  315. package/src/core/meta.ts +69 -6
  316. package/src/core/nav-diagnostics.ts +56 -1
  317. package/src/core/navigation.ts +245 -76
  318. package/src/core/ordering-prefix.ts +27 -0
  319. package/src/core/project-graph.ts +23 -1
  320. package/src/core/request-body.ts +1 -1
  321. package/src/core/safe-href.ts +53 -1
  322. package/src/core/safe-links.ts +11 -2
  323. package/src/core/schema.ts +95 -34
  324. package/src/core/server-features.ts +2 -2
  325. package/src/core/sources/assets.ts +83 -41
  326. package/src/core/sources/contentful-rich-text.ts +3 -2
  327. package/src/core/sources/contentful.ts +25 -12
  328. package/src/core/sources/filesystem.ts +5 -1
  329. package/src/core/sources/github-releases.ts +106 -5
  330. package/src/core/sources/lexical.ts +9 -4
  331. package/src/core/sources/lower.ts +79 -26
  332. package/src/core/sources/mdx-remote.ts +1 -0
  333. package/src/core/sources/normalize.ts +132 -30
  334. package/src/core/sources/notion.ts +26 -10
  335. package/src/core/sources/obsidian.ts +17 -3
  336. package/src/core/sources/payload.ts +1 -0
  337. package/src/core/sources/portable-text.ts +63 -44
  338. package/src/core/sources/remote.ts +18 -2
  339. package/src/core/sources/resolve.ts +52 -33
  340. package/src/core/sources/sanity.ts +1 -0
  341. package/src/core/sources/strapi-blocks.ts +9 -4
  342. package/src/core/sources/strapi.ts +1 -0
  343. package/src/core/sources/types.ts +31 -0
  344. package/src/core/types.ts +9 -0
  345. package/src/core/ui-packs/ar.ts +17 -5
  346. package/src/core/ui-packs/bg.ts +17 -5
  347. package/src/core/ui-packs/bn.ts +17 -5
  348. package/src/core/ui-packs/ca.ts +17 -5
  349. package/src/core/ui-packs/cs.ts +17 -5
  350. package/src/core/ui-packs/da.ts +17 -5
  351. package/src/core/ui-packs/de.ts +17 -5
  352. package/src/core/ui-packs/el.ts +17 -5
  353. package/src/core/ui-packs/es.ts +17 -5
  354. package/src/core/ui-packs/fa.ts +17 -5
  355. package/src/core/ui-packs/fi.ts +17 -5
  356. package/src/core/ui-packs/fr.ts +17 -5
  357. package/src/core/ui-packs/he.ts +17 -5
  358. package/src/core/ui-packs/hi.ts +17 -5
  359. package/src/core/ui-packs/hr.ts +17 -5
  360. package/src/core/ui-packs/hu.ts +17 -5
  361. package/src/core/ui-packs/id.ts +17 -5
  362. package/src/core/ui-packs/it.ts +17 -5
  363. package/src/core/ui-packs/ja.ts +17 -5
  364. package/src/core/ui-packs/ko.ts +17 -5
  365. package/src/core/ui-packs/nl.ts +17 -5
  366. package/src/core/ui-packs/no.ts +17 -5
  367. package/src/core/ui-packs/pl.ts +17 -5
  368. package/src/core/ui-packs/pt-br.ts +17 -5
  369. package/src/core/ui-packs/pt.ts +17 -5
  370. package/src/core/ui-packs/ro.ts +17 -5
  371. package/src/core/ui-packs/ru.ts +17 -5
  372. package/src/core/ui-packs/sk.ts +17 -5
  373. package/src/core/ui-packs/sr.ts +17 -5
  374. package/src/core/ui-packs/sv.ts +17 -5
  375. package/src/core/ui-packs/th.ts +17 -5
  376. package/src/core/ui-packs/tr.ts +17 -5
  377. package/src/core/ui-packs/uk.ts +17 -5
  378. package/src/core/ui-packs/vi.ts +17 -5
  379. package/src/core/ui-packs/zh-tw.ts +17 -5
  380. package/src/core/ui-packs/zh.ts +17 -5
  381. package/src/core/unrecognized-keys.ts +1 -1
  382. package/src/core/version-cut.ts +17 -2
  383. package/src/core/versions.ts +4 -1
  384. package/src/deploy/adapters/node.ts +5 -2
  385. package/src/deploy/adapters/registry.ts +2 -1
  386. package/src/deploy/adapters/types.ts +13 -1
  387. package/src/deploy/artifacts.ts +25 -5
  388. package/src/deploy/cloudflare-negotiation.ts +23 -4
  389. package/src/deploy/headers.ts +67 -51
  390. package/src/deploy/node-headers.ts +148 -27
  391. package/src/deploy/platforms/cloudflare.ts +1 -0
  392. package/src/deploy/platforms/netlify.ts +82 -5
  393. package/src/deploy/platforms/node.ts +7 -5
  394. package/src/deploy/platforms/static.ts +1 -0
  395. package/src/deploy/platforms/types.ts +7 -0
  396. package/src/deploy/platforms/vercel.ts +9 -3
  397. package/src/deploy/redirects.ts +14 -3
  398. package/src/deploy/vercel-negotiation.ts +35 -3
  399. package/src/eval/agents.ts +10 -2
  400. package/src/eval/run.ts +25 -0
  401. package/src/markdown/base-links.ts +55 -8
  402. package/src/markdown/index.ts +6 -3
  403. package/src/markdown/relative-links.ts +3 -23
  404. package/src/markdown/route-snapshot.ts +37 -0
  405. package/src/og/card.ts +20 -4
  406. package/src/og/derive.ts +145 -4
  407. package/src/openapi/graphql-build.ts +28 -2
  408. package/src/openapi/model.ts +77 -13
  409. package/src/openapi/render-mdx.ts +10 -1
  410. package/src/registry/eject.ts +135 -37
  411. package/src/search/documents.ts +24 -3
  412. package/src/search/orama-index.ts +1 -1
  413. package/src/search/sync/algolia.ts +36 -2
  414. package/src/sources/registry.ts +5 -0
  415. package/src/theme/entry.ts +11 -4
  416. package/src/translate/agents.ts +6 -1
  417. package/src/translate/ledger.ts +26 -3
  418. package/src/translate/meta.ts +11 -3
  419. package/src/translate/report.ts +1 -1
  420. package/src/translate/run.ts +11 -5
  421. package/src/translate/validate.ts +10 -1
  422. package/src/translate/work-list.ts +36 -3
  423. package/src/upgrade/upgrade.ts +37 -5
  424. package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
  425. package/dist/cli/chunk-5a2z0198.js.map +0 -10
  426. package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
  427. package/dist/cli/chunk-79njf86q.js.map +0 -11
  428. package/dist/cli/chunk-a9kptbw5.js.map +0 -14
  429. package/dist/cli/chunk-abh8yjkn.js +0 -31
  430. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  431. package/dist/cli/chunk-bctazmbk.js.map +0 -10
  432. package/dist/cli/chunk-beat36xx.js.map +0 -10
  433. package/dist/cli/chunk-d1tadaw7.js.map +0 -10
  434. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  435. package/dist/cli/chunk-j8mw0za6.js.map +0 -35
  436. package/dist/cli/chunk-mnqj32sj.js.map +0 -12
  437. package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
  438. package/dist/cli/chunk-nk3ts2xk.js +0 -51
  439. package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
  440. package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
  441. package/dist/cli/chunk-tzne8qfq.js.map +0 -10
  442. package/dist/cli/chunk-xaz13gwg.js.map +0 -182
  443. package/dist/cli/chunk-z01ze5c1.js.map +0 -10
  444. package/dist/cli/chunk-z1f5arsg.js.map +0 -36
  445. package/dist/cli/chunk-zg2gtj10.js.map +0 -35
  446. /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
  447. /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
  448. /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
  449. /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
  450. /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
  451. /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
  452. /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
  453. /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
  454. /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
@@ -0,0 +1,416 @@
1
+ import type { EvaluatedValue } from "./component-markdown.ts";
2
+
3
+ /**
4
+ * Read an MDX attribute expression (`prop={...}`) as literal data, without
5
+ * running it. The agent Markdown downleveler converts components on every
6
+ * page it is handed — plain `.md` pages Astro never executes, and content a
7
+ * CMS or a release feed supplies — so an expression is parsed, never
8
+ * evaluated: only data a reader could see by looking at it is recovered.
9
+ *
10
+ * Accepted: string, number, boolean, `null`, and `undefined` literals; array
11
+ * and object literals, nested; unary minus on a number; template literals
12
+ * whose substitutions are themselves accepted; and `frontmatter.<path>`
13
+ * member access (dot steps, or bracketed string and number literals),
14
+ * resolved against the page's front matter the way `prop={frontmatter.status}`
15
+ * resolves when Astro renders the page. Comments are skipped. Anything else —
16
+ * a call, an operator, a spread, a shorthand property, any other name — is
17
+ * not static, and the caller keeps the component's JSX as written.
18
+ */
19
+
20
+ /** The outcome of reading an expression: its value, or `ok: false`. */
21
+ export interface StaticExpressionResult {
22
+ ok: boolean;
23
+ value: EvaluatedValue;
24
+ }
25
+
26
+ /**
27
+ * Stop reading: the expression isn't literal data. The throw unwinds the
28
+ * recursive descent to {@link readStaticExpression}, which reports it.
29
+ */
30
+ const notStatic = (): never => {
31
+ throw new Error("not a static expression");
32
+ };
33
+
34
+ const isText = (value: EvaluatedValue): value is string =>
35
+ typeof value === "string";
36
+
37
+ const isNumber = (value: EvaluatedValue): value is number =>
38
+ typeof value === "number";
39
+
40
+ /** A nested map of values: an object literal or a front-matter mapping. */
41
+ const isValueMap = (
42
+ value: EvaluatedValue
43
+ ): value is { [key: string]: EvaluatedValue } =>
44
+ typeof value === "object" &&
45
+ value !== null &&
46
+ !Array.isArray(value) &&
47
+ !(value instanceof Date);
48
+
49
+ /** Whitespace, a line comment, or a block comment. */
50
+ const SPACE = /\s+|\/\/[^\n\r]*|\/\*[\s\S]*?\*\//uy;
51
+
52
+ /** An IdentifierName (ASCII): object keys, member names, keywords. */
53
+ const IDENTIFIER = /[$A-Z_a-z][\w$]*/uy;
54
+
55
+ /**
56
+ * A numeric literal: hex, octal, and binary forms, or a decimal with an
57
+ * optional fraction and exponent, `_` separators allowed between digits. A
58
+ * legacy octal (`017`) matches only its leading `0`, and the check on the
59
+ * following character rejects it, as strict-mode JavaScript does.
60
+ */
61
+ const NUMBER =
62
+ /0[Xx][\dA-Fa-f](?:_?[\dA-Fa-f])*|0[Oo][0-7](?:_?[0-7])*|0[Bb][01](?:_?[01])*|(?:(?:0|[1-9](?:_?\d)*)(?:\.(?:\d(?:_?\d)*)?)?|\.\d(?:_?\d)*)(?:[Ee][+-]?\d(?:_?\d)*)?/uy;
63
+
64
+ /** A character that may not directly follow a number (`1n`, `1px`, `017`). */
65
+ const AFTER_NUMBER = /[\w$.]/u;
66
+
67
+ const DIGIT = /\d/u;
68
+ const HEX_DIGITS = /^[\dA-Fa-f]+$/u;
69
+
70
+ /** A canonical array index: `0`, `12`, never `01`. */
71
+ const INDEX = /^(?:0|[1-9]\d*)$/u;
72
+
73
+ /** The largest Unicode code point, the cap on a `\u{…}` escape. */
74
+ const MAX_CODE_POINT = 0x10_ff_ff;
75
+
76
+ /** U+2028 and U+2029, the line terminators outside ASCII. */
77
+ const LINE_SEPARATORS = new Set([
78
+ String.fromCodePoint(0x20_28),
79
+ String.fromCodePoint(0x20_29),
80
+ ]);
81
+
82
+ const SIMPLE_ESCAPES = new Map([
83
+ ["b", "\b"],
84
+ ["f", "\f"],
85
+ ["n", "\n"],
86
+ ["r", "\r"],
87
+ ["t", "\t"],
88
+ ["v", "\v"],
89
+ ]);
90
+
91
+ /** A key that, set through a literal, would replace the object's prototype. */
92
+ const PROTO_KEY = "__proto__";
93
+
94
+ /**
95
+ * `base[key]` on a front-matter value, own data only: a missing key reads as
96
+ * `undefined` (as it does at render time), stepping into `null` or
97
+ * `undefined` is not static (it would throw), and inherited members —
98
+ * methods, `constructor` — are never reached.
99
+ */
100
+ const member = (base: EvaluatedValue, key: string): EvaluatedValue => {
101
+ if (base === null || base === undefined) {
102
+ return notStatic();
103
+ }
104
+ if (isText(base) || Array.isArray(base)) {
105
+ if (key === "length") {
106
+ return base.length;
107
+ }
108
+ return INDEX.test(key) ? base[Number(key)] : undefined;
109
+ }
110
+ return isValueMap(base) && Object.hasOwn(base, key) ? base[key] : undefined;
111
+ };
112
+
113
+ /** A recursive-descent reader over one expression's source. */
114
+ class Reader {
115
+ readonly frontmatter: Record<string, EvaluatedValue> | undefined;
116
+ pos = 0;
117
+ readonly source: string;
118
+
119
+ constructor(
120
+ source: string,
121
+ frontmatter: Record<string, EvaluatedValue> | undefined
122
+ ) {
123
+ this.source = source;
124
+ this.frontmatter = frontmatter;
125
+ }
126
+
127
+ /** The character under the cursor (`""` at the end). */
128
+ get char(): string {
129
+ return this.source.charAt(this.pos);
130
+ }
131
+
132
+ get done(): boolean {
133
+ return this.pos >= this.source.length;
134
+ }
135
+
136
+ /** Match a sticky pattern at the cursor and consume it. */
137
+ match(pattern: RegExp): string | undefined {
138
+ pattern.lastIndex = this.pos;
139
+ const found = pattern.exec(this.source)?.[0];
140
+ if (found !== undefined) {
141
+ this.pos += found.length;
142
+ }
143
+ return found;
144
+ }
145
+
146
+ /** Skip whitespace and comments. */
147
+ space(): void {
148
+ let skipped = this.match(SPACE);
149
+ while (skipped !== undefined) {
150
+ skipped = this.match(SPACE);
151
+ }
152
+ }
153
+
154
+ /** Consume `expected` (after any space), or stop. */
155
+ expect(expected: string): void {
156
+ this.space();
157
+ if (!this.source.startsWith(expected, this.pos)) {
158
+ notStatic();
159
+ }
160
+ this.pos += expected.length;
161
+ }
162
+
163
+ value(): EvaluatedValue {
164
+ this.space();
165
+ const { char } = this;
166
+ if (char === "{") {
167
+ return this.object();
168
+ }
169
+ if (char === "[") {
170
+ return this.array();
171
+ }
172
+ if (char === '"' || char === "'") {
173
+ return this.string(char);
174
+ }
175
+ if (char === "`") {
176
+ return this.template();
177
+ }
178
+ if (char === "-") {
179
+ this.pos += 1;
180
+ // `--1` is a decrement, not two negations.
181
+ const operand = this.char === "-" ? notStatic() : this.value();
182
+ return isNumber(operand) ? -operand : notStatic();
183
+ }
184
+ return this.number() ?? this.name();
185
+ }
186
+
187
+ number(): number | undefined {
188
+ const text = this.match(NUMBER);
189
+ if (text === undefined) {
190
+ return;
191
+ }
192
+ if (
193
+ AFTER_NUMBER.test(this.char) ||
194
+ (text === "0" && DIGIT.test(this.char))
195
+ ) {
196
+ return notStatic();
197
+ }
198
+ return Number(text.replaceAll("_", ""));
199
+ }
200
+
201
+ /** A keyword literal, or `frontmatter` and its member steps. */
202
+ name(): EvaluatedValue {
203
+ const name = this.match(IDENTIFIER);
204
+ if (name === "true") {
205
+ return true;
206
+ }
207
+ if (name === "false") {
208
+ return false;
209
+ }
210
+ if (name === "null") {
211
+ return null;
212
+ }
213
+ if (name === "undefined") {
214
+ return undefined;
215
+ }
216
+ if (name !== "frontmatter") {
217
+ return notStatic();
218
+ }
219
+ let current: EvaluatedValue = this.frontmatter;
220
+ for (;;) {
221
+ this.space();
222
+ if (this.char === ".") {
223
+ this.pos += 1;
224
+ this.space();
225
+ current = member(current, this.match(IDENTIFIER) ?? notStatic());
226
+ } else if (this.char === "[") {
227
+ this.pos += 1;
228
+ const key = this.value();
229
+ this.expect("]");
230
+ current = member(
231
+ current,
232
+ isText(key) || isNumber(key) ? String(key) : notStatic()
233
+ );
234
+ } else {
235
+ return current;
236
+ }
237
+ }
238
+ }
239
+
240
+ object(): EvaluatedValue {
241
+ this.pos += 1;
242
+ const out: { [key: string]: EvaluatedValue } = {};
243
+ for (;;) {
244
+ this.space();
245
+ if (this.char === "}") {
246
+ this.pos += 1;
247
+ return out;
248
+ }
249
+ const key = this.key();
250
+ this.expect(":");
251
+ out[key] = this.value();
252
+ this.space();
253
+ if (this.char === ",") {
254
+ this.pos += 1;
255
+ } else if (this.char !== "}") {
256
+ return notStatic();
257
+ }
258
+ }
259
+ }
260
+
261
+ /** An object key: a name, a string, or a number (as its canonical string). */
262
+ key(): string {
263
+ const { char } = this;
264
+ let key: string;
265
+ if (char === '"' || char === "'") {
266
+ key = this.string(char);
267
+ } else {
268
+ const number = this.number();
269
+ key =
270
+ number === undefined
271
+ ? (this.match(IDENTIFIER) ?? notStatic())
272
+ : String(number);
273
+ }
274
+ // `{ __proto__: … }` sets the prototype rather than a property.
275
+ return key === PROTO_KEY ? notStatic() : key;
276
+ }
277
+
278
+ array(): EvaluatedValue[] {
279
+ this.pos += 1;
280
+ const out: EvaluatedValue[] = [];
281
+ for (;;) {
282
+ this.space();
283
+ if (this.char === "]") {
284
+ this.pos += 1;
285
+ return out;
286
+ }
287
+ out.push(this.value());
288
+ this.space();
289
+ if (this.char === ",") {
290
+ this.pos += 1;
291
+ } else if (this.char !== "]") {
292
+ return notStatic();
293
+ }
294
+ }
295
+ }
296
+
297
+ string(quote: string): string {
298
+ this.pos += 1;
299
+ let out = "";
300
+ for (;;) {
301
+ const { char } = this;
302
+ if (this.done || char === "\n" || char === "\r") {
303
+ return notStatic();
304
+ }
305
+ this.pos += 1;
306
+ if (char === quote) {
307
+ return out;
308
+ }
309
+ out += char === "\\" ? this.escape() : char;
310
+ }
311
+ }
312
+
313
+ template(): string {
314
+ this.pos += 1;
315
+ let out = "";
316
+ for (;;) {
317
+ if (this.done) {
318
+ return notStatic();
319
+ }
320
+ const { char } = this;
321
+ this.pos += 1;
322
+ if (char === "`") {
323
+ return out;
324
+ }
325
+ if (char === "\\") {
326
+ out += this.escape();
327
+ } else if (char === "$" && this.char === "{") {
328
+ this.pos += 1;
329
+ out += String(this.value());
330
+ this.expect("}");
331
+ } else if (char === "\r") {
332
+ // A raw line break in a template reads as `\n`, `\r\n` included.
333
+ if (this.char === "\n") {
334
+ this.pos += 1;
335
+ }
336
+ out += "\n";
337
+ } else {
338
+ out += char;
339
+ }
340
+ }
341
+ }
342
+
343
+ /** What an escape after `\` stands for, under strict-mode rules. */
344
+ escape(): string {
345
+ if (this.done) {
346
+ return notStatic();
347
+ }
348
+ const { char } = this;
349
+ this.pos += 1;
350
+ if (DIGIT.test(char)) {
351
+ // `\0` is NUL; octal escapes, `\8`, and `\9` are errors in strict code.
352
+ return char === "0" && !DIGIT.test(this.char) ? "\0" : notStatic();
353
+ }
354
+ if (char === "x") {
355
+ return String.fromCodePoint(this.hex(2));
356
+ }
357
+ if (char === "u") {
358
+ return String.fromCodePoint(
359
+ this.char === "{" ? this.codePoint() : this.hex(4)
360
+ );
361
+ }
362
+ if (char === "\r") {
363
+ // A line continuation: `\` and the line break stand for nothing.
364
+ if (this.char === "\n") {
365
+ this.pos += 1;
366
+ }
367
+ return "";
368
+ }
369
+ if (char === "\n" || LINE_SEPARATORS.has(char)) {
370
+ return "";
371
+ }
372
+ return SIMPLE_ESCAPES.get(char) ?? char;
373
+ }
374
+
375
+ /** Exactly `length` hex digits, as a code point. */
376
+ hex(length: number): number {
377
+ const digits = this.source.slice(this.pos, this.pos + length);
378
+ if (digits.length !== length || !HEX_DIGITS.test(digits)) {
379
+ return notStatic();
380
+ }
381
+ this.pos += length;
382
+ return Number.parseInt(digits, 16);
383
+ }
384
+
385
+ /** A braced `\u{…}` code point, the cursor on its `{`. */
386
+ codePoint(): number {
387
+ const end = this.source.indexOf("}", this.pos);
388
+ const digits = end === -1 ? "" : this.source.slice(this.pos + 1, end);
389
+ const code = HEX_DIGITS.test(digits) ? Number.parseInt(digits, 16) : -1;
390
+ if (code < 0 || code > MAX_CODE_POINT) {
391
+ return notStatic();
392
+ }
393
+ this.pos = end + 1;
394
+ return code;
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Read `raw` — an attribute expression's source — as static data, with
400
+ * `frontmatter` (the page's front matter, when the caller has it) behind
401
+ * `frontmatter.<path>` references. Never executes anything.
402
+ */
403
+ export const readStaticExpression = (
404
+ raw: string,
405
+ frontmatter?: Record<string, EvaluatedValue>
406
+ ): StaticExpressionResult => {
407
+ const reader = new Reader(raw, frontmatter);
408
+ try {
409
+ const value = reader.value();
410
+ reader.space();
411
+ return reader.done ? { ok: true, value } : { ok: false, value: undefined };
412
+ } catch {
413
+ // Not literal data (or nested past the stack): keep the JSX as written.
414
+ return { ok: false, value: undefined };
415
+ }
416
+ };
@@ -1,10 +1,44 @@
1
- // Mirrors the fence masking in `core/sources/assets.ts`: a fenced code sample
2
- // that *shows* `<Visibility>` markup must keep showing what the author wrote.
3
- const CODE_FENCE_BLOCK =
4
- /^(?<fence>`{3,}|~{3,})[^\n]*\n[\s\S]*?^\k<fence>[^\n]*(?=\n|$)/gmu;
1
+ import { nextFenceState } from "../core/code-fences.ts";
2
+ import type { FenceState } from "../core/code-fences.ts";
3
+
4
+ /** An inline code span on one line, as the other source scanners read it. */
5
+ const INLINE_CODE = /`[^`\n]*`/gu;
5
6
  // NUL delimiters cannot appear in authored markdown, so tokens never collide.
6
7
  // oxlint-disable-next-line no-control-regex -- the NUL is the collision guard.
7
- const FENCE_TOKEN = /\u0000blume-fence-(?<index>\d+)\u0000/gu;
8
+ const CODE_TOKEN = /\u0000blume-code-(?<index>\d+)\u0000/gu;
9
+
10
+ /**
11
+ * Swap every code sample for a one-line token — fenced blocks at any indent
12
+ * (a fence inside a `<Tab>` or `<Step>` is indented under it) and inline code
13
+ * spans — so markup that *shows* `<Visibility>` keeps showing what the author
14
+ * wrote. An unclosed fence runs to the end, as CommonMark reads it.
15
+ */
16
+ const maskCode = (markdown: string, stash: string[]): string => {
17
+ const token = (code: string): string => {
18
+ stash.push(code);
19
+ return `\u0000blume-code-${stash.length - 1}\u0000`;
20
+ };
21
+ const lines: string[] = [];
22
+ let fence: FenceState = null;
23
+ let block: string[] = [];
24
+ for (const line of markdown.split("\n")) {
25
+ const next = nextFenceState(line, fence);
26
+ if (fence === null && next === null) {
27
+ lines.push(line.replaceAll(INLINE_CODE, token));
28
+ } else {
29
+ block.push(line);
30
+ if (next === null) {
31
+ lines.push(token(block.join("\n")));
32
+ block = [];
33
+ }
34
+ }
35
+ fence = next;
36
+ }
37
+ if (block.length > 0) {
38
+ lines.push(token(block.join("\n")));
39
+ }
40
+ return lines.join("\n");
41
+ };
8
42
 
9
43
  /** The audience an output surface serves — the component's two `for` values. */
10
44
  export type VisibilityAudience = "agents" | "web";
@@ -36,12 +70,9 @@ export const applyAudienceVisibility = (
36
70
  markdown: string,
37
71
  audience: VisibilityAudience
38
72
  ): string => {
39
- // Mask fenced code blocks so documentation *about* Visibility survives.
40
- const fences: string[] = [];
41
- const masked = markdown.replace(CODE_FENCE_BLOCK, (block) => {
42
- fences.push(block);
43
- return `\u0000blume-fence-${fences.length - 1}\u0000`;
44
- });
73
+ // Mask code so documentation *about* Visibility survives.
74
+ const code: string[] = [];
75
+ const masked = maskCode(markdown, code);
45
76
 
46
77
  let touched = false;
47
78
  const filtered = masked
@@ -56,18 +87,18 @@ export const applyAudienceVisibility = (
56
87
 
57
88
  // Removing/unwrapping block-level tags leaves runs of blank lines behind;
58
89
  // collapse them only when something matched so untouched files round-trip
59
- // exactly. Fences are masked as single-line tokens, so they are unaffected.
90
+ // exactly. Code is masked as single-line tokens, so it is unaffected.
60
91
  const tidied = touched ? filtered.replaceAll(/\n{3,}/gu, "\n\n") : filtered;
61
92
 
62
93
  return tidied.replaceAll(
63
- FENCE_TOKEN,
64
- (token, index) => fences[Number(index)] ?? token
94
+ CODE_TOKEN,
95
+ (token, index) => code[Number(index)] ?? token
65
96
  );
66
97
  };
67
98
 
68
99
  /**
69
100
  * Resolve `<Visibility>` blocks for agent-facing Markdown (llms-full.txt, the
70
- * `.md`/`.mdx` mirrors, MCP tools, Ask AI grounding): `for="web"` content is
101
+ * `.md`/`.mdx` mirrors, MCP tools, assistant grounding): `for="web"` content is
71
102
  * removed and `for="agents"` content is unwrapped.
72
103
  */
73
104
  export const applyAgentVisibility = (markdown: string): string =>
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+
3
+ import type { AdapterDescriptor } from "../core/adapter.ts";
4
+ import { adapterDescriptorSchema } from "../core/adapter.ts";
5
+ import type { HeadScript } from "./head.ts";
6
+
7
+ /** The script Databuddy's install snippet loads. */
8
+ export const DATABUDDY_SCRIPT_SRC = "https://cdn.databuddy.cc/databuddy.js";
9
+
10
+ /** The options {@link databuddy} maps itself. */
11
+ export interface DatabuddyNamedOptions {
12
+ /** Client ID, from the website's settings in the Databuddy dashboard. */
13
+ clientId: string;
14
+ }
15
+
16
+ /**
17
+ * Options for {@link databuddy}: the client ID plus any other tracker setting,
18
+ * forwarded as a `data-` attribute on the tag (`"track-web-vitals"` →
19
+ * `data-track-web-vitals`, `"track-errors"`, `"track-outgoing-links"`,
20
+ * `"api-url"`, …).
21
+ */
22
+ export type DatabuddyOptions = DatabuddyNamedOptions & {
23
+ [setting: string]: string;
24
+ };
25
+
26
+ export const databuddyOptionsSchema = z
27
+ .object({
28
+ clientId: z.string().min(1),
29
+ })
30
+ .catchall(z.string());
31
+
32
+ export type DatabuddyAdapter = AdapterDescriptor<"databuddy", DatabuddyOptions>;
33
+
34
+ export const databuddyAdapterSchema = adapterDescriptorSchema(
35
+ "databuddy",
36
+ databuddyOptionsSchema
37
+ );
38
+
39
+ /**
40
+ * Databuddy. The client ID is public — it's in the tag on every page.
41
+ */
42
+ export const databuddy = (options: DatabuddyOptions): DatabuddyAdapter => ({
43
+ kind: "databuddy",
44
+ options,
45
+ requiredSecrets: [],
46
+ runtimeDeps: [],
47
+ });
48
+
49
+ /**
50
+ * The async tag from the install instructions, with `clientId` as
51
+ * `data-client-id` and every other option as its own `data-` attribute. The
52
+ * tracker skips setup when `window.databuddy` already exists, so there is no
53
+ * queue stub. Databuddy's script tracks history changes on its own, so
54
+ * client-router navigations need no extra hook.
55
+ */
56
+ export const databuddyHead = (options: DatabuddyOptions): HeadScript[] => {
57
+ const { clientId, ...settings } = options;
58
+ const attributes: HeadScript["attributes"] = {};
59
+ for (const [setting, value] of Object.entries(settings)) {
60
+ attributes[`data-${setting}`] = value;
61
+ }
62
+ attributes["data-client-id"] = clientId;
63
+ attributes.async = true;
64
+ attributes.crossorigin = "anonymous";
65
+ attributes.src = DATABUDDY_SCRIPT_SRC;
66
+ return [{ attributes, content: null }];
67
+ };
@@ -3,6 +3,7 @@ import { amplitudeHead } from "./amplitude.ts";
3
3
  import { clarityHead } from "./clarity.ts";
4
4
  import { clearbitHead } from "./clearbit.ts";
5
5
  import { cloudflareHead } from "./cloudflare.ts";
6
+ import { databuddyHead } from "./databuddy.ts";
6
7
  import { fathomHead } from "./fathom.ts";
7
8
  import { googleAnalyticsHead } from "./google-analytics.ts";
8
9
  import { googleTagManagerHead } from "./google-tag-manager.ts";
@@ -64,6 +65,9 @@ const adapterScripts = (
64
65
  case "cloudflare": {
65
66
  return cloudflareHead(adapter.options);
66
67
  }
68
+ case "databuddy": {
69
+ return databuddyHead(adapter.options);
70
+ }
67
71
  case "fathom": {
68
72
  return fathomHead(adapter.options);
69
73
  }
@@ -30,6 +30,8 @@ export { clearbit } from "./clearbit.ts";
30
30
  export type { ClearbitAdapter, ClearbitOptions } from "./clearbit.ts";
31
31
  export { cloudflare } from "./cloudflare.ts";
32
32
  export type { CloudflareAdapter, CloudflareOptions } from "./cloudflare.ts";
33
+ export { databuddy } from "./databuddy.ts";
34
+ export type { DatabuddyAdapter, DatabuddyOptions } from "./databuddy.ts";
33
35
  export { fathom } from "./fathom.ts";
34
36
  export type { FathomAdapter, FathomOptions } from "./fathom.ts";
35
37
  export { googleAnalytics } from "./google-analytics.ts";
@@ -54,13 +54,30 @@ export const posthog = (options: PosthogOptions): PosthogAdapter => ({
54
54
  const POSTHOG_LOADER =
55
55
  '!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug getPageViewId captureTraceFeedback captureTraceMetric".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);';
56
56
 
57
- // PostHog's loader only captures a pageview per real page load, so the client
58
- // router's in-place swaps are captured here.
57
+ // With PostHog's classic pageview setting its loader captures one pageview
58
+ // per real page load, so the client router's in-place swaps are captured here.
59
59
  const POSTHOG_SPA_PAGEVIEWS = spaPageviews(
60
60
  "__blumePosthogPath",
61
61
  'posthog.capture("$pageview");'
62
62
  );
63
63
 
64
+ /**
65
+ * Whether Blume should capture client-router navigations itself, given the
66
+ * `posthog.init` options. Only when PostHog captures load-time pageviews and
67
+ * nothing more: `capture_pageview: false` turns pageviews off, and
68
+ * `capture_pageview: "history_change"` — the default under any `defaults`
69
+ * snapshot (`defaults: "2025-05-24"` in PostHog's current snippet), unless
70
+ * `capture_pageview` says otherwise — already captures every navigation, so
71
+ * sending another would count each one twice.
72
+ */
73
+ const capturesNavigations = (init: Record<string, JsonValue>): boolean => {
74
+ const setting = init.capture_pageview;
75
+ if (setting === undefined) {
76
+ return init.defaults === undefined || init.defaults === "unset";
77
+ }
78
+ return setting !== false && setting !== "history_change";
79
+ };
80
+
64
81
  /**
65
82
  * The inline loader-plus-init snippet. `key` and `host` are the options Blume
66
83
  * maps (`host` becomes `api_host`); everything else lands in `posthog.init`'s
@@ -69,10 +86,11 @@ const POSTHOG_SPA_PAGEVIEWS = spaPageviews(
69
86
  export const posthogHead = (options: PosthogOptions): HeadScript[] => {
70
87
  const { host, key, ...init } = options;
71
88
  const initOptions = { api_host: host ?? POSTHOG_DEFAULT_HOST, ...init };
89
+ const pageviews = capturesNavigations(init) ? POSTHOG_SPA_PAGEVIEWS : "";
72
90
  return [
73
91
  {
74
92
  attributes: {},
75
- content: `${POSTHOG_LOADER}posthog.init(${inlineJson(key)},${inlineJson(initOptions)});${POSTHOG_SPA_PAGEVIEWS}`,
93
+ content: `${POSTHOG_LOADER}posthog.init(${inlineJson(key)},${inlineJson(initOptions)});${pageviews}`,
76
94
  },
77
95
  ];
78
96
  };
@@ -5,6 +5,7 @@ import { amplitudeAdapterSchema } from "./amplitude.ts";
5
5
  import { clarityAdapterSchema } from "./clarity.ts";
6
6
  import { clearbitAdapterSchema } from "./clearbit.ts";
7
7
  import { cloudflareAdapterSchema } from "./cloudflare.ts";
8
+ import { databuddyAdapterSchema } from "./databuddy.ts";
8
9
  import { fathomAdapterSchema } from "./fathom.ts";
9
10
  import { googleAnalyticsAdapterSchema } from "./google-analytics.ts";
10
11
  import { googleTagManagerAdapterSchema } from "./google-tag-manager.ts";
@@ -27,6 +28,7 @@ export const analyticsAdapterSchema = z.discriminatedUnion("kind", [
27
28
  clarityAdapterSchema,
28
29
  clearbitAdapterSchema,
29
30
  cloudflareAdapterSchema,
31
+ databuddyAdapterSchema,
30
32
  fathomAdapterSchema,
31
33
  googleAnalyticsAdapterSchema,
32
34
  googleTagManagerAdapterSchema,