blume 2.0.1 → 2.0.3

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 (492) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
  3. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
  4. package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
  5. package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
  6. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
  7. package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
  8. package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
  9. package/dist/cli/chunk-35d4wj9f.js.map +15 -0
  10. package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
  11. package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
  12. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
  13. package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
  14. package/dist/cli/chunk-5f86nr5m.js.map +11 -0
  15. package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
  16. package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
  17. package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
  18. package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
  19. package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
  20. package/dist/cli/chunk-6dsbexzp.js.map +10 -0
  21. package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
  22. package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
  23. package/dist/cli/chunk-9t7a85s3.js.map +10 -0
  24. package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
  25. package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
  26. package/dist/cli/chunk-acanzt5p.js.map +10 -0
  27. package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
  28. package/dist/cli/chunk-akbpwfxc.js.map +10 -0
  29. package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
  30. package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
  31. package/dist/cli/chunk-c8chx29p.js.map +36 -0
  32. package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
  33. package/dist/cli/chunk-crgn1q09.js.map +10 -0
  34. package/dist/cli/chunk-e04dxsz1.js +39 -0
  35. package/dist/cli/chunk-e04dxsz1.js.map +10 -0
  36. package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
  37. package/dist/cli/chunk-ey84smr6.js.map +10 -0
  38. package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
  39. package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
  40. package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
  41. package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
  42. package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
  43. package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
  44. package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
  45. package/dist/cli/chunk-j85vccga.js.map +36 -0
  46. package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
  47. package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
  48. package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
  49. package/dist/cli/chunk-p73c0m7w.js.map +11 -0
  50. package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
  51. package/dist/cli/chunk-pehfxfta.js.map +10 -0
  52. package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
  53. package/dist/cli/chunk-pv29h0wf.js.map +190 -0
  54. package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
  55. package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
  56. package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
  57. package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
  58. package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
  59. package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
  60. package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
  61. package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
  62. package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
  63. package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
  64. package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
  65. package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
  66. package/dist/cli/chunk-yfyb25rh.js.map +14 -0
  67. package/dist/cli/index.js +162 -35
  68. package/dist/cli/index.js.map +4 -4
  69. package/dist/types/ai/agent-surface.d.ts +32 -0
  70. package/dist/types/ai/api-catalog.d.ts +7 -1
  71. package/dist/types/ai/ask-context.d.ts +7 -0
  72. package/dist/types/ai/component-markdown.d.ts +4 -4
  73. package/dist/types/ai/link-headers.d.ts +8 -1
  74. package/dist/types/ai/openapi-components.d.ts +5 -2
  75. package/dist/types/ai/relative-links.d.ts +11 -4
  76. package/dist/types/ai/skills.d.ts +4 -1
  77. package/dist/types/ai/static-expression.d.ts +28 -0
  78. package/dist/types/ai/tar.d.ts +1 -3
  79. package/dist/types/analytics/databuddy.d.ts +43 -0
  80. package/dist/types/analytics/index.d.ts +4 -0
  81. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  82. package/dist/types/analytics/schema.d.ts +28 -0
  83. package/dist/types/astro/integration.d.ts +3 -2
  84. package/dist/types/cli/env.d.ts +5 -0
  85. package/dist/types/cli/init/scaffold.d.ts +19 -3
  86. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  87. package/dist/types/core/base-path.d.ts +24 -1
  88. package/dist/types/core/config-input.d.ts +13 -11
  89. package/dist/types/core/config.d.ts +2 -2
  90. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  91. package/dist/types/core/graph.d.ts +2 -0
  92. package/dist/types/core/heading-markers.d.ts +5 -7
  93. package/dist/types/core/i18n-ui.d.ts +31 -0
  94. package/dist/types/core/i18n.d.ts +9 -1
  95. package/dist/types/core/last-modified.d.ts +10 -0
  96. package/dist/types/core/links.d.ts +3 -1
  97. package/dist/types/core/load-module.d.ts +10 -0
  98. package/dist/types/core/locale-links.d.ts +12 -2
  99. package/dist/types/core/meta.d.ts +13 -1
  100. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  101. package/dist/types/core/navigation.d.ts +38 -0
  102. package/dist/types/core/ordering-prefix.d.ts +4 -0
  103. package/dist/types/core/safe-links.d.ts +3 -1
  104. package/dist/types/core/schema.d.ts +59 -13
  105. package/dist/types/core/sources/github-releases.d.ts +5 -0
  106. package/dist/types/core/sources/lower.d.ts +38 -13
  107. package/dist/types/core/sources/normalize.d.ts +23 -2
  108. package/dist/types/core/sources/remote.d.ts +11 -1
  109. package/dist/types/core/sources/resolve.d.ts +12 -0
  110. package/dist/types/core/sources/types.d.ts +31 -0
  111. package/dist/types/core/sources/watch.d.ts +12 -5
  112. package/dist/types/core/standard-schema.d.ts +5 -0
  113. package/dist/types/core/types.d.ts +9 -0
  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/artifacts.d.ts +6 -4
  117. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  118. package/dist/types/deploy/headers.d.ts +36 -7
  119. package/dist/types/deploy/node-headers.d.ts +43 -8
  120. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  121. package/dist/types/deploy/platforms/node.d.ts +6 -5
  122. package/dist/types/deploy/platforms/types.d.ts +15 -0
  123. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  124. package/dist/types/deploy/redirects.d.ts +31 -15
  125. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  126. package/dist/types/markdown/directives.d.ts +62 -0
  127. package/dist/types/markdown/features.d.ts +21 -0
  128. package/dist/types/markdown/mdast.d.ts +63 -0
  129. package/dist/types/openapi/asyncapi.d.ts +4 -2
  130. package/dist/types/openapi/model.d.ts +20 -6
  131. package/dist/types/search/sync/algolia.d.ts +3 -1
  132. package/docs/01-quickstart.mdx +3 -2
  133. package/docs/02-deployment.mdx +20 -9
  134. package/docs/08-faq.mdx +10 -3
  135. package/docs/advanced/changelog.mdx +1 -1
  136. package/docs/advanced/custom-pages.mdx +7 -5
  137. package/docs/cli/audit.mdx +19 -3
  138. package/docs/cli/doctor.mdx +2 -2
  139. package/docs/cli/evals.mdx +4 -4
  140. package/docs/cli/index.mdx +4 -1
  141. package/docs/cli/translate.mdx +4 -4
  142. package/docs/cli/version.mdx +1 -1
  143. package/docs/configuration/analytics.mdx +42 -2
  144. package/docs/configuration/assistant.mdx +1 -1
  145. package/docs/configuration/customization.mdx +6 -3
  146. package/docs/configuration/index.mdx +5 -3
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +2 -2
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -1
  151. package/docs/content/index.mdx +8 -4
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +7 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -16
  156. package/docs/content/syntax.mdx +14 -0
  157. package/docs/content/versioning.mdx +2 -1
  158. package/docs/discoverability/agent-discovery.mdx +22 -8
  159. package/docs/discoverability/index.mdx +4 -3
  160. package/docs/discoverability/llms-txt.mdx +2 -5
  161. package/docs/discoverability/markdown.mdx +6 -4
  162. package/docs/discoverability/mcp.mdx +4 -0
  163. package/docs/discoverability/metadata.mdx +3 -2
  164. package/docs/discoverability/open-graph.mdx +8 -4
  165. package/docs/discoverability/rss.mdx +4 -2
  166. package/docs/references/asyncapi.mdx +2 -2
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +6 -4
  169. package/package.json +1 -1
  170. package/skills/blume-migrate/SKILL.md +6 -6
  171. package/skills/blume-migrate/references/docusaurus.md +6 -6
  172. package/skills/blume-migrate/references/fumadocs.md +1 -1
  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 +2 -2
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +2 -2
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/ai-catalog.ts +6 -2
  182. package/src/ai/api/handlers.ts +2 -2
  183. package/src/ai/api/spec.ts +17 -4
  184. package/src/ai/api-catalog.ts +13 -3
  185. package/src/ai/ask-context.ts +14 -2
  186. package/src/ai/ask-data.ts +26 -12
  187. package/src/ai/changelog-markdown.ts +2 -2
  188. package/src/ai/component-markdown.ts +43 -37
  189. package/src/ai/link-headers.ts +22 -8
  190. package/src/ai/llms.ts +22 -10
  191. package/src/ai/markdown.ts +23 -11
  192. package/src/ai/mcp/discovery.ts +2 -2
  193. package/src/ai/mcp/query.ts +2 -2
  194. package/src/ai/mcp/server.ts +80 -9
  195. package/src/ai/openapi-components.ts +22 -6
  196. package/src/ai/relative-links.ts +99 -15
  197. package/src/ai/serializers.ts +2 -1
  198. package/src/ai/skills.ts +14 -1
  199. package/src/ai/static-expression.ts +416 -0
  200. package/src/ai/tar.ts +139 -12
  201. package/src/ai/visibility.ts +45 -14
  202. package/src/analytics/databuddy.ts +67 -0
  203. package/src/analytics/head.ts +8 -0
  204. package/src/analytics/index.ts +7 -0
  205. package/src/analytics/one-dollar-stats.ts +86 -0
  206. package/src/analytics/posthog.ts +21 -3
  207. package/src/analytics/schema.ts +4 -0
  208. package/src/astro/generate.ts +72 -28
  209. package/src/astro/include-hmr.ts +31 -12
  210. package/src/astro/include-refresh.ts +19 -8
  211. package/src/astro/integration.ts +108 -14
  212. package/src/astro/runtime-modules.ts +28 -13
  213. package/src/astro/templates.ts +139 -57
  214. package/src/audit/catalog.ts +2 -2
  215. package/src/audit/checks/assets.ts +25 -5
  216. package/src/audit/checks/content.ts +20 -2
  217. package/src/audit/checks/i18n.ts +46 -8
  218. package/src/audit/checks/indexability.ts +39 -19
  219. package/src/audit/checks/links.ts +22 -1
  220. package/src/audit/checks/llms.ts +6 -3
  221. package/src/audit/checks/network.ts +3 -1
  222. package/src/audit/checks/og-image.ts +10 -0
  223. package/src/audit/checks/robots.ts +6 -1
  224. package/src/audit/checks/sitemap.ts +56 -31
  225. package/src/audit/checks/social.ts +21 -2
  226. package/src/audit/crawl.ts +88 -15
  227. package/src/audit/graph.ts +3 -1
  228. package/src/audit/report.ts +54 -17
  229. package/src/audit/run.ts +13 -9
  230. package/src/audit/snapshot.ts +5 -0
  231. package/src/audit/types.ts +18 -0
  232. package/src/audit/url.ts +37 -6
  233. package/src/cli/args.ts +32 -0
  234. package/src/cli/build-failure.ts +50 -0
  235. package/src/cli/commands/audit.ts +11 -1
  236. package/src/cli/commands/build.ts +19 -5
  237. package/src/cli/commands/check.ts +2 -0
  238. package/src/cli/commands/doctor.ts +3 -1
  239. package/src/cli/commands/eval.ts +20 -18
  240. package/src/cli/commands/init.ts +15 -5
  241. package/src/cli/commands/preview.ts +15 -0
  242. package/src/cli/commands/sync.ts +2 -0
  243. package/src/cli/commands/translate.ts +18 -18
  244. package/src/cli/commands/upgrade.ts +11 -0
  245. package/src/cli/commands/validate.ts +3 -1
  246. package/src/cli/dev-lock.ts +157 -37
  247. package/src/cli/eject-scripts.ts +32 -7
  248. package/src/cli/env.ts +12 -1
  249. package/src/cli/init/scaffold.ts +117 -11
  250. package/src/cli/init/starter-spec.ts +235 -0
  251. package/src/cli/report-format.ts +11 -6
  252. package/src/components/colors.ts +19 -0
  253. package/src/components/content/AccordionItem.astro +26 -22
  254. package/src/components/content/Badge.astro +7 -12
  255. package/src/components/content/Card.astro +2 -2
  256. package/src/components/content/Component.astro +11 -5
  257. package/src/components/content/Expandable.astro +5 -1
  258. package/src/components/content/Frame.astro +2 -7
  259. package/src/components/content/GithubInfo.astro +12 -2
  260. package/src/components/content/Prompt.astro +2 -7
  261. package/src/components/content/Tab.astro +0 -1
  262. package/src/components/content/Tabs.astro +58 -5
  263. package/src/components/content/Tile.astro +1 -1
  264. package/src/components/content/Tooltip.astro +69 -7
  265. package/src/components/content/Tree.astro +7 -2
  266. package/src/components/content/TypeTable.astro +10 -5
  267. package/src/components/content/Update.astro +8 -2
  268. package/src/components/content/auto-type-table.ts +4 -1
  269. package/src/components/content/badge-color.ts +20 -0
  270. package/src/components/content/base-href.ts +14 -22
  271. package/src/components/content/inline-markdown.ts +27 -7
  272. package/src/components/copy-feedback.ts +35 -8
  273. package/src/components/islands/assistant.tsx +37 -6
  274. package/src/components/islands/base-path.ts +47 -10
  275. package/src/components/islands/hooks.ts +42 -21
  276. package/src/components/islands/webmcp.ts +16 -10
  277. package/src/components/layout/Banner.astro +23 -4
  278. package/src/components/layout/Breadcrumbs.astro +5 -2
  279. package/src/components/layout/DiscoveryLinks.astro +9 -5
  280. package/src/components/layout/Header.astro +23 -10
  281. package/src/components/layout/LanguageSwitcher.astro +2 -2
  282. package/src/components/layout/Logo.astro +5 -0
  283. package/src/components/layout/NavSelector.astro +8 -3
  284. package/src/components/layout/NavTabMenu.astro +133 -0
  285. package/src/components/layout/NavTree.astro +31 -12
  286. package/src/components/layout/NavTreeCache.astro +5 -2
  287. package/src/components/layout/NavTreeScript.astro +45 -5
  288. package/src/components/layout/PageActions.astro +4 -3
  289. package/src/components/layout/PageLayout.astro +28 -13
  290. package/src/components/layout/Pagination.astro +3 -3
  291. package/src/components/layout/ReferenceLayout.astro +4 -1
  292. package/src/components/layout/RootLayout.astro +25 -12
  293. package/src/components/layout/Search.astro +38 -14
  294. package/src/components/layout/VersionBanner.astro +2 -2
  295. package/src/components/layout/analytics-client.ts +14 -0
  296. package/src/components/layout/toc-active.ts +41 -0
  297. package/src/components/layout/toc-element.ts +8 -14
  298. package/src/components/openapi/ApiTagOperations.astro +2 -2
  299. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  300. package/src/components/openapi/GraphqlChip.astro +2 -2
  301. package/src/components/openapi/GraphqlType.astro +11 -3
  302. package/src/components/openapi/MessageComposer.astro +1 -1
  303. package/src/components/openapi/Operation.astro +23 -5
  304. package/src/components/openapi/PanelTabs.astro +4 -1
  305. package/src/components/openapi/Playground.astro +8 -2
  306. package/src/components/openapi/RequestPanel.astro +13 -6
  307. package/src/components/openapi/SchemaProperty.astro +11 -33
  308. package/src/components/openapi/SchemaTable.astro +34 -52
  309. package/src/components/openapi/async.ts +38 -6
  310. package/src/components/openapi/helpers.ts +100 -12
  311. package/src/components/openapi/message-composer.ts +14 -1
  312. package/src/components/openapi/message-model.ts +12 -2
  313. package/src/components/openapi/message.ts +21 -3
  314. package/src/components/openapi/operation-model.ts +119 -17
  315. package/src/components/openapi/panel.ts +31 -5
  316. package/src/components/openapi/param-style.ts +181 -0
  317. package/src/components/openapi/playground-client.ts +110 -23
  318. package/src/components/openapi/playground-schema.ts +25 -6
  319. package/src/components/openapi/request.ts +192 -26
  320. package/src/components/openapi/schema-tree.ts +209 -0
  321. package/src/components/openapi/snippets.ts +113 -19
  322. package/src/components/openapi/validate-json.ts +1 -1
  323. package/src/components/openapi/ws-client.ts +18 -2
  324. package/src/core/base-path.ts +69 -8
  325. package/src/core/config-input.ts +13 -11
  326. package/src/core/config.ts +18 -4
  327. package/src/core/diagnostics.ts +219 -30
  328. package/src/core/directive-diagnostics.ts +99 -0
  329. package/src/core/frontmatter.ts +21 -18
  330. package/src/core/graph.ts +120 -11
  331. package/src/core/heading-markers.ts +5 -18
  332. package/src/core/i18n-ui.ts +39 -2
  333. package/src/core/i18n.ts +44 -5
  334. package/src/core/last-modified.ts +25 -3
  335. package/src/core/links.ts +7 -4
  336. package/src/core/load-module.ts +20 -0
  337. package/src/core/locale-links.ts +22 -18
  338. package/src/core/manifest.ts +3 -2
  339. package/src/core/meta.ts +89 -12
  340. package/src/core/nav-diagnostics.ts +56 -1
  341. package/src/core/navigation.ts +272 -83
  342. package/src/core/ordering-prefix.ts +27 -0
  343. package/src/core/project-graph.ts +50 -6
  344. package/src/core/safe-href.ts +53 -1
  345. package/src/core/safe-links.ts +11 -2
  346. package/src/core/schema.ts +94 -18
  347. package/src/core/sources/assets.ts +83 -41
  348. package/src/core/sources/contentful-rich-text.ts +28 -20
  349. package/src/core/sources/contentful.ts +25 -12
  350. package/src/core/sources/filesystem.ts +25 -3
  351. package/src/core/sources/github-releases.ts +131 -11
  352. package/src/core/sources/lexical.ts +30 -20
  353. package/src/core/sources/lower.ts +276 -56
  354. package/src/core/sources/mdx-remote.ts +52 -16
  355. package/src/core/sources/normalize.ts +456 -119
  356. package/src/core/sources/notion.ts +77 -34
  357. package/src/core/sources/obsidian.ts +40 -9
  358. package/src/core/sources/payload.ts +1 -0
  359. package/src/core/sources/portable-text.ts +85 -49
  360. package/src/core/sources/remote.ts +18 -2
  361. package/src/core/sources/resolve.ts +52 -33
  362. package/src/core/sources/sanity.ts +1 -0
  363. package/src/core/sources/strapi-blocks.ts +28 -19
  364. package/src/core/sources/strapi.ts +1 -0
  365. package/src/core/sources/types.ts +31 -0
  366. package/src/core/sources/watch.ts +20 -7
  367. package/src/core/standard-schema.ts +10 -6
  368. package/src/core/types.ts +9 -0
  369. package/src/core/ui-packs/ar.ts +13 -0
  370. package/src/core/ui-packs/bg.ts +13 -0
  371. package/src/core/ui-packs/bn.ts +13 -0
  372. package/src/core/ui-packs/ca.ts +13 -0
  373. package/src/core/ui-packs/cs.ts +13 -0
  374. package/src/core/ui-packs/da.ts +13 -0
  375. package/src/core/ui-packs/de.ts +13 -0
  376. package/src/core/ui-packs/el.ts +13 -0
  377. package/src/core/ui-packs/es.ts +13 -0
  378. package/src/core/ui-packs/fa.ts +13 -0
  379. package/src/core/ui-packs/fi.ts +13 -0
  380. package/src/core/ui-packs/fr.ts +13 -0
  381. package/src/core/ui-packs/he.ts +13 -0
  382. package/src/core/ui-packs/hi.ts +13 -0
  383. package/src/core/ui-packs/hr.ts +13 -0
  384. package/src/core/ui-packs/hu.ts +13 -0
  385. package/src/core/ui-packs/id.ts +13 -0
  386. package/src/core/ui-packs/it.ts +13 -0
  387. package/src/core/ui-packs/ja.ts +13 -0
  388. package/src/core/ui-packs/ko.ts +13 -0
  389. package/src/core/ui-packs/nl.ts +13 -0
  390. package/src/core/ui-packs/no.ts +13 -0
  391. package/src/core/ui-packs/pl.ts +13 -0
  392. package/src/core/ui-packs/pt-br.ts +13 -0
  393. package/src/core/ui-packs/pt.ts +13 -0
  394. package/src/core/ui-packs/ro.ts +13 -0
  395. package/src/core/ui-packs/ru.ts +13 -0
  396. package/src/core/ui-packs/sk.ts +13 -0
  397. package/src/core/ui-packs/sr.ts +13 -0
  398. package/src/core/ui-packs/sv.ts +13 -0
  399. package/src/core/ui-packs/th.ts +13 -0
  400. package/src/core/ui-packs/tr.ts +13 -0
  401. package/src/core/ui-packs/uk.ts +13 -0
  402. package/src/core/ui-packs/vi.ts +13 -0
  403. package/src/core/ui-packs/zh-tw.ts +13 -0
  404. package/src/core/ui-packs/zh.ts +13 -0
  405. package/src/core/version-cut.ts +69 -17
  406. package/src/core/versions.ts +4 -1
  407. package/src/deploy/adapters/node.ts +5 -2
  408. package/src/deploy/adapters/registry.ts +2 -1
  409. package/src/deploy/adapters/types.ts +13 -1
  410. package/src/deploy/artifacts.ts +52 -11
  411. package/src/deploy/cloudflare-negotiation.ts +27 -16
  412. package/src/deploy/headers.ts +75 -55
  413. package/src/deploy/node-headers.ts +148 -27
  414. package/src/deploy/platforms/cloudflare.ts +13 -3
  415. package/src/deploy/platforms/netlify.ts +83 -5
  416. package/src/deploy/platforms/node.ts +8 -5
  417. package/src/deploy/platforms/static.ts +2 -0
  418. package/src/deploy/platforms/types.ts +15 -0
  419. package/src/deploy/platforms/vercel.ts +10 -3
  420. package/src/deploy/redirects.ts +68 -21
  421. package/src/deploy/robots.ts +2 -2
  422. package/src/deploy/rss.ts +4 -3
  423. package/src/deploy/sitemap.ts +7 -5
  424. package/src/deploy/vercel-negotiation.ts +35 -3
  425. package/src/eval/agents.ts +10 -2
  426. package/src/eval/run.ts +25 -0
  427. package/src/markdown/base-links.ts +74 -29
  428. package/src/markdown/directives.ts +242 -36
  429. package/src/markdown/features.ts +17 -0
  430. package/src/markdown/index.ts +13 -9
  431. package/src/markdown/mdast.ts +5 -2
  432. package/src/markdown/relative-links.ts +15 -27
  433. package/src/markdown/route-snapshot.ts +37 -0
  434. package/src/og/card.ts +149 -9
  435. package/src/og/derive.ts +156 -5
  436. package/src/og/index.ts +1 -0
  437. package/src/openapi/asyncapi.ts +4 -2
  438. package/src/openapi/graphql-build.ts +28 -2
  439. package/src/openapi/model.ts +99 -27
  440. package/src/openapi/proxy.ts +63 -10
  441. package/src/openapi/render-mdx.ts +51 -3
  442. package/src/registry/eject.ts +297 -43
  443. package/src/search/adapters/version-scope.ts +30 -0
  444. package/src/search/documents.ts +89 -27
  445. package/src/search/popular.ts +2 -1
  446. package/src/search/sync/algolia.ts +36 -2
  447. package/src/seo/jsonld.ts +7 -3
  448. package/src/sources/registry.ts +5 -0
  449. package/src/theme/entry.ts +11 -4
  450. package/src/translate/agents.ts +6 -1
  451. package/src/translate/ledger.ts +26 -3
  452. package/src/translate/meta.ts +76 -24
  453. package/src/translate/run.ts +14 -8
  454. package/src/translate/validate.ts +14 -2
  455. package/src/translate/work-list.ts +91 -19
  456. package/src/upgrade/upgrade.ts +36 -4
  457. package/dist/cli/chunk-27g6wdth.js.map +0 -10
  458. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  459. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  460. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  461. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  462. package/dist/cli/chunk-82bbrxdn.js +0 -51
  463. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  464. package/dist/cli/chunk-abh8yjkn.js +0 -31
  465. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  466. package/dist/cli/chunk-ce574jw2.js +0 -23
  467. package/dist/cli/chunk-ce574jw2.js.map +0 -10
  468. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  469. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  470. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  471. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  472. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  473. package/dist/cli/chunk-jwyddg7y.js.map +0 -15
  474. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  475. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  476. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  477. package/dist/cli/chunk-q5163e60.js.map +0 -11
  478. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  479. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  480. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  481. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  482. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  483. package/dist/cli/chunk-yw7dm696.js.map +0 -14
  484. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
  485. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
  486. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
  487. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
  488. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
  489. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
  490. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
  491. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
  492. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
package/src/ai/tar.ts CHANGED
@@ -22,42 +22,169 @@ export interface TarEntry {
22
22
  path: string;
23
23
  }
24
24
 
25
- /** ustar `name` field capacity; skill layouts are shallow, so no `prefix`. */
25
+ /** ustar `name` field capacity. */
26
26
  const NAME_MAX = 100;
27
+ /** ustar `prefix` field capacity; `prefix/name` reaches 255 bytes. */
28
+ const PREFIX_MAX = 155;
29
+
30
+ /** ustar header byte offsets nanotar leaves for us to patch. */
31
+ const CHECKSUM_OFFSET = 148;
32
+ const CHECKSUM_SIZE = 8;
33
+ const TYPEFLAG_OFFSET = 156;
34
+ const PREFIX_OFFSET = 345;
35
+ const BLOCK_SIZE = 512;
36
+
37
+ /** The two zero blocks that mark the end of an archive. */
38
+ const END_OF_ARCHIVE = 2 * BLOCK_SIZE;
39
+ /** tar's default record size (20 blocks), which the archive is padded to. */
40
+ const RECORD_SIZE = 20 * BLOCK_SIZE;
41
+
42
+ /** The PAX extended-header typeflag (`x`). */
43
+ const PAX_TYPEFLAG = 0x78;
27
44
 
28
45
  const encoder = new TextEncoder();
29
46
 
47
+ const byteLength = (value: string): number => encoder.encode(value).byteLength;
48
+
49
+ /** `value` cut to at most `max` UTF-8 bytes, on a character boundary. */
50
+ const truncateBytes = (value: string, max: number): string =>
51
+ value.slice(0, encoder.encodeInto(value, new Uint8Array(max)).read);
52
+
53
+ /** One header nanotar writes, plus the fields it cannot. */
54
+ interface Block {
55
+ content: Uint8Array;
56
+ mode: string;
57
+ name: string;
58
+ /** The PAX extended header ahead of an over-long path. */
59
+ pax?: boolean;
60
+ /** ustar `prefix`, joined to `name` with a `/` by readers. */
61
+ prefix?: string;
62
+ }
63
+
64
+ /**
65
+ * Split a path over the `name` field at a `/` so the head fits `prefix` and
66
+ * the tail fits `name`. The longest head that fits leaves the shortest tail,
67
+ * so if that tail is still too long no split works.
68
+ */
69
+ const splitUstar = (
70
+ path: string
71
+ ): { name: string; prefix: string } | undefined => {
72
+ let slash = path.lastIndexOf("/");
73
+ while (slash > 0 && byteLength(path.slice(0, slash)) > PREFIX_MAX) {
74
+ slash = path.lastIndexOf("/", slash - 1);
75
+ }
76
+ const name = path.slice(slash + 1);
77
+ if (slash <= 0 || byteLength(name) > NAME_MAX) {
78
+ return undefined;
79
+ }
80
+ return { name, prefix: path.slice(0, slash) };
81
+ };
82
+
83
+ /**
84
+ * A PAX `path` record: `"<length> path=<value>\n"`, where the decimal length
85
+ * counts the whole record including its own digits.
86
+ */
87
+ const paxPathRecord = (path: string): Uint8Array => {
88
+ const body = ` path=${path}\n`;
89
+ const size = byteLength(body);
90
+ const length = size + String(size + String(size).length).length;
91
+ return encoder.encode(`${length}${body}`);
92
+ };
93
+
94
+ /**
95
+ * The header blocks for one entry. A path that fits `name` is written as is;
96
+ * up to 255 bytes it splits across `prefix` and `name`; beyond that (or with
97
+ * no usable split) a PAX extended header carries the full path and the ustar
98
+ * `name` holds a truncated fallback for readers that ignore PAX.
99
+ */
100
+ const entryBlocks = (entry: TarEntry): Block[] => {
101
+ const mode = entry.executable ? "755" : "644";
102
+ const file = { content: entry.content, mode };
103
+ if (byteLength(entry.path) <= NAME_MAX) {
104
+ return [{ ...file, name: entry.path }];
105
+ }
106
+ const split = splitUstar(entry.path);
107
+ if (split) {
108
+ return [{ ...file, ...split }];
109
+ }
110
+ const base = entry.path.slice(entry.path.lastIndexOf("/") + 1);
111
+ return [
112
+ {
113
+ content: paxPathRecord(entry.path),
114
+ mode: "644",
115
+ name: truncateBytes(`PaxHeader/${base}`, NAME_MAX),
116
+ pax: true,
117
+ },
118
+ { ...file, name: truncateBytes(base, NAME_MAX) },
119
+ ];
120
+ };
121
+
122
+ /** Recompute a patched header's checksum (nanotar's octal format). */
123
+ const writeChecksum = (header: Uint8Array): void => {
124
+ header.fill(0x20, CHECKSUM_OFFSET, CHECKSUM_OFFSET + CHECKSUM_SIZE);
125
+ const sum = header.reduce((total, byte) => total + byte, 0);
126
+ header.fill(0, CHECKSUM_OFFSET, CHECKSUM_OFFSET + CHECKSUM_SIZE);
127
+ encoder.encodeInto(
128
+ sum.toString(8),
129
+ header.subarray(CHECKSUM_OFFSET, CHECKSUM_OFFSET + CHECKSUM_SIZE)
130
+ );
131
+ };
132
+
30
133
  /**
31
134
  * Build a gzipped ustar archive of the given files, in the given order. Paths
32
- * longer than the ustar `name` field or escaping the archive root throw to
33
- * surface a programming error — nanotar would silently truncate an oversized
34
- * name into a corrupt archive, so the validation stays here.
135
+ * escaping the archive root throw to surface a programming error.
35
136
  */
36
137
  export const buildTarGz = (entries: readonly TarEntry[]): Uint8Array => {
37
138
  for (const entry of entries) {
38
- if (encoder.encode(entry.path).byteLength > NAME_MAX) {
39
- throw new Error(`tar path exceeds ${NAME_MAX} bytes: ${entry.path}`);
40
- }
41
139
  if (entry.path.startsWith("/") || entry.path.split("/").includes("..")) {
42
140
  throw new Error(`tar path must be archive-relative: ${entry.path}`);
43
141
  }
44
142
  }
143
+ const blocks = entries.flatMap(entryBlocks);
45
144
  const tar = createTar(
46
- entries.map((entry) => ({
145
+ blocks.map((block) => ({
47
146
  // Root-owned, epoch-mtime, empty owner names: every field a rebuild
48
147
  // could vary is pinned so the archive bytes are a function of content.
49
148
  attrs: {
50
149
  gid: 0,
51
150
  group: "",
52
- mode: entry.executable ? "755" : "644",
151
+ mode: block.mode,
53
152
  mtime: 0,
54
153
  uid: 0,
55
154
  user: "",
56
155
  },
57
- data: entry.content,
58
- name: entry.path,
156
+ data: block.content,
157
+ name: block.name,
59
158
  }))
60
159
  );
160
+ // nanotar writes neither `prefix` nor the PAX typeflag, so patch those
161
+ // headers in place and re-sum them.
162
+ let offset = 0;
163
+ for (const block of blocks) {
164
+ const header = tar.subarray(offset, offset + BLOCK_SIZE);
165
+ if (block.prefix) {
166
+ encoder.encodeInto(
167
+ block.prefix,
168
+ header.subarray(PREFIX_OFFSET, PREFIX_OFFSET + PREFIX_MAX)
169
+ );
170
+ writeChecksum(header);
171
+ }
172
+ if (block.pax) {
173
+ header[TYPEFLAG_OFFSET] = PAX_TYPEFLAG;
174
+ writeChecksum(header);
175
+ }
176
+ offset +=
177
+ BLOCK_SIZE * (1 + Math.ceil(block.content.byteLength / BLOCK_SIZE));
178
+ }
179
+ // nanotar pads the entries to a whole record but writes no end-of-archive
180
+ // marker, so entries ending within two blocks of a record boundary leave
181
+ // fewer than the two zero blocks readers expect (GNU tar warns "A lone zero
182
+ // block"). Pad to the first record that fits both; for every other archive
183
+ // that is the size nanotar already wrote, so its bytes are unchanged.
184
+ const archive = new Uint8Array(
185
+ Math.ceil((offset + END_OF_ARCHIVE) / RECORD_SIZE) * RECORD_SIZE
186
+ );
187
+ archive.set(tar.subarray(0, offset));
61
188
  // Sync gzip with a pinned level; Node writes no timestamp into the header.
62
- return new Uint8Array(gzipSync(tar, { level: 9 }));
189
+ return new Uint8Array(gzipSync(archive, { level: 9 }));
63
190
  };
@@ -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,12 +87,12 @@ 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
 
@@ -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";
@@ -11,6 +12,7 @@ import { hightouchHead } from "./hightouch.ts";
11
12
  import { hotjarHead } from "./hotjar.ts";
12
13
  import { logrocketHead } from "./logrocket.ts";
13
14
  import { mixpanelHead } from "./mixpanel.ts";
15
+ import { oneDollarStatsHead } from "./one-dollar-stats.ts";
14
16
  import { pirschHead } from "./pirsch.ts";
15
17
  import { plausibleHead } from "./plausible.ts";
16
18
  import { posthogHead } from "./posthog.ts";
@@ -64,6 +66,9 @@ const adapterScripts = (
64
66
  case "cloudflare": {
65
67
  return cloudflareHead(adapter.options);
66
68
  }
69
+ case "databuddy": {
70
+ return databuddyHead(adapter.options);
71
+ }
67
72
  case "fathom": {
68
73
  return fathomHead(adapter.options);
69
74
  }
@@ -88,6 +93,9 @@ const adapterScripts = (
88
93
  case "mixpanel": {
89
94
  return mixpanelHead(adapter.options);
90
95
  }
96
+ case "one-dollar-stats": {
97
+ return oneDollarStatsHead(adapter.options);
98
+ }
91
99
  case "pirsch": {
92
100
  return pirschHead(adapter.options);
93
101
  }
@@ -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";
@@ -56,6 +58,11 @@ export type {
56
58
  MixpanelOptions,
57
59
  MixpanelRegion,
58
60
  } from "./mixpanel.ts";
61
+ export { oneDollarStats } from "./one-dollar-stats.ts";
62
+ export type {
63
+ OneDollarStatsAdapter,
64
+ OneDollarStatsOptions,
65
+ } from "./one-dollar-stats.ts";
59
66
  export { pirsch } from "./pirsch.ts";
60
67
  export type { PirschAdapter, PirschOptions } from "./pirsch.ts";
61
68
  export { plausible } from "./plausible.ts";
@@ -0,0 +1,86 @@
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 OneDollarStats' install snippet loads. */
8
+ export const ONE_DOLLAR_STATS_SCRIPT_SRC =
9
+ "https://assets.onedollarstats.com/stonks.js";
10
+
11
+ /** The options {@link oneDollarStats} checks itself. */
12
+ export interface OneDollarStatsNamedOptions {
13
+ /**
14
+ * A bare host name, e.g. `docs.example.com`. The tracker reports every
15
+ * event under it on every host, not just `localhost`.
16
+ */
17
+ hostname?: string;
18
+ }
19
+
20
+ /**
21
+ * Options for {@link oneDollarStats}: any tracker setting, forwarded as a
22
+ * `data-` attribute on the tag (`hostname` → `data-hostname`, `devmode`,
23
+ * `url`, `autocollect`, `"hash-routing"`, …).
24
+ */
25
+ export type OneDollarStatsOptions = OneDollarStatsNamedOptions & {
26
+ [setting: string]: string;
27
+ };
28
+
29
+ export const oneDollarStatsOptionsSchema = z
30
+ .object({
31
+ hostname: z
32
+ .string()
33
+ .regex(/^[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)*(?::\d+)?$/u, {
34
+ message:
35
+ "`hostname` must be a bare host name like `docs.example.com`, without `https://`, a path, a query, or a fragment.",
36
+ })
37
+ .optional(),
38
+ })
39
+ .catchall(z.string());
40
+
41
+ export type OneDollarStatsAdapter = AdapterDescriptor<
42
+ "one-dollar-stats",
43
+ OneDollarStatsOptions
44
+ >;
45
+
46
+ export const oneDollarStatsAdapterSchema = adapterDescriptorSchema(
47
+ "one-dollar-stats",
48
+ oneDollarStatsOptionsSchema
49
+ );
50
+
51
+ /**
52
+ * OneDollarStats. Needs no keys — the dashboard matches events to a site by
53
+ * the domain they come from.
54
+ */
55
+ export const oneDollarStats = (
56
+ options: OneDollarStatsOptions = {}
57
+ ): OneDollarStatsAdapter => ({
58
+ kind: "one-dollar-stats",
59
+ options,
60
+ requiredSecrets: [],
61
+ runtimeDeps: [],
62
+ });
63
+
64
+ /**
65
+ * The deferred tag from the install instructions, with every option as its
66
+ * own `data-` attribute. The tracker reads them from `document.currentScript`,
67
+ * so the tag stays a classic external script. It hooks `pushState` and
68
+ * `popstate` itself, so client-router navigations need no extra hook.
69
+ *
70
+ * The tracker turns hash routing on whenever `data-hash-routing` is present,
71
+ * whatever its value, so `"hash-routing": "false"` leaves the attribute off.
72
+ */
73
+ export const oneDollarStatsHead = (
74
+ options: OneDollarStatsOptions
75
+ ): HeadScript[] => {
76
+ const attributes: HeadScript["attributes"] = {};
77
+ for (const [setting, value] of Object.entries(options)) {
78
+ if (setting === "hash-routing" && value === "false") {
79
+ continue;
80
+ }
81
+ attributes[`data-${setting}`] = value;
82
+ }
83
+ attributes.defer = true;
84
+ attributes.src = ONE_DOLLAR_STATS_SCRIPT_SRC;
85
+ return [{ attributes, content: null }];
86
+ };
@@ -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";
@@ -13,6 +14,7 @@ import { hightouchAdapterSchema } from "./hightouch.ts";
13
14
  import { hotjarAdapterSchema } from "./hotjar.ts";
14
15
  import { logrocketAdapterSchema } from "./logrocket.ts";
15
16
  import { mixpanelAdapterSchema } from "./mixpanel.ts";
17
+ import { oneDollarStatsAdapterSchema } from "./one-dollar-stats.ts";
16
18
  import { pirschAdapterSchema } from "./pirsch.ts";
17
19
  import { plausibleAdapterSchema } from "./plausible.ts";
18
20
  import { posthogAdapterSchema } from "./posthog.ts";
@@ -27,6 +29,7 @@ export const analyticsAdapterSchema = z.discriminatedUnion("kind", [
27
29
  clarityAdapterSchema,
28
30
  clearbitAdapterSchema,
29
31
  cloudflareAdapterSchema,
32
+ databuddyAdapterSchema,
30
33
  fathomAdapterSchema,
31
34
  googleAnalyticsAdapterSchema,
32
35
  googleTagManagerAdapterSchema,
@@ -35,6 +38,7 @@ export const analyticsAdapterSchema = z.discriminatedUnion("kind", [
35
38
  hotjarAdapterSchema,
36
39
  logrocketAdapterSchema,
37
40
  mixpanelAdapterSchema,
41
+ oneDollarStatsAdapterSchema,
38
42
  pirschAdapterSchema,
39
43
  plausibleAdapterSchema,
40
44
  posthogAdapterSchema,