blume 2.1.0 → 2.1.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 (450) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  3. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  4. package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
  5. package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
  6. package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
  7. package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
  8. package/dist/cli/chunk-124fvmzq.js.map +220 -0
  9. package/dist/cli/chunk-150jk7km.js +52 -0
  10. package/dist/cli/chunk-150jk7km.js.map +10 -0
  11. package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
  12. package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
  13. package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
  14. package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
  15. package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
  16. package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
  17. package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
  18. package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
  19. package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
  20. package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
  21. package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
  22. package/dist/cli/chunk-60hz265r.js.map +10 -0
  23. package/dist/cli/chunk-74wfhv79.js +65 -0
  24. package/dist/cli/chunk-74wfhv79.js.map +10 -0
  25. package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
  26. package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
  27. package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
  28. package/dist/cli/chunk-ave1wcbr.js.map +14 -0
  29. package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
  30. package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
  31. package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
  32. package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
  33. package/dist/cli/chunk-ckrcm08h.js +324 -0
  34. package/dist/cli/chunk-ckrcm08h.js.map +14 -0
  35. package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
  36. package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
  37. package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
  38. package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
  39. package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
  40. package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
  41. package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
  42. package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
  43. package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
  44. package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
  45. package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
  46. package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
  47. package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
  48. package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
  49. package/dist/cli/chunk-j1x92mx9.js.map +10 -0
  50. package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
  51. package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
  52. package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
  53. package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
  54. package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
  55. package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
  56. package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
  57. package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
  58. package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
  59. package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
  60. package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
  61. package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
  62. package/dist/cli/chunk-twy7psdz.js.map +10 -0
  63. package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
  64. package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
  65. package/dist/cli/index.js +24 -19
  66. package/dist/cli/index.js.map +3 -3
  67. package/dist/types/ai/ask-context.d.ts +6 -2
  68. package/dist/types/ai/ask.d.ts +24 -5
  69. package/dist/types/ai/component-markdown.d.ts +2 -0
  70. package/dist/types/ai/openapi-components.d.ts +4 -1
  71. package/dist/types/analytics/clics.d.ts +37 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +18 -0
  74. package/dist/types/astro/integration.d.ts +11 -4
  75. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  76. package/dist/types/astro/pages.d.ts +10 -0
  77. package/dist/types/cli/init/scaffold.d.ts +1 -1
  78. package/dist/types/components/content/api-field.d.ts +4 -1
  79. package/dist/types/components/content/api-page.d.ts +6 -1
  80. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  81. package/dist/types/components/openapi/helpers.d.ts +22 -1
  82. package/dist/types/components/openapi/security.d.ts +10 -0
  83. package/dist/types/components/openapi/snippets.d.ts +60 -0
  84. package/dist/types/core/changelog-index.d.ts +8 -0
  85. package/dist/types/core/config-input.d.ts +37 -6
  86. package/dist/types/core/data.d.ts +4 -0
  87. package/dist/types/core/i18n-ui.d.ts +2 -0
  88. package/dist/types/core/links.d.ts +8 -0
  89. package/dist/types/core/powered-by.d.ts +12 -0
  90. package/dist/types/core/probe.d.ts +2 -0
  91. package/dist/types/core/project-graph.d.ts +6 -0
  92. package/dist/types/core/redirect-patterns.d.ts +11 -3
  93. package/dist/types/core/schema.d.ts +228 -3
  94. package/dist/types/core/sources/cache.d.ts +3 -0
  95. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  96. package/dist/types/core/sources/contentful.d.ts +17 -1
  97. package/dist/types/core/sources/lexical.d.ts +5 -0
  98. package/dist/types/core/sources/lower.d.ts +10 -2
  99. package/dist/types/core/sources/normalize.d.ts +20 -1
  100. package/dist/types/core/sources/notion.d.ts +8 -0
  101. package/dist/types/core/sources/remote.d.ts +37 -10
  102. package/dist/types/core/sources/sanity.d.ts +1 -1
  103. package/dist/types/core/types.d.ts +17 -0
  104. package/dist/types/deploy/adapter-output.d.ts +2 -1
  105. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  106. package/dist/types/deploy/adapters/node.d.ts +58 -23
  107. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  108. package/dist/types/deploy/adapters/types.d.ts +3 -2
  109. package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
  110. package/dist/types/deploy/node-headers.d.ts +12 -4
  111. package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
  112. package/dist/types/deploy/platforms/node.d.ts +3 -1
  113. package/dist/types/deploy/platforms/types.d.ts +18 -7
  114. package/dist/types/deploy/platforms/vercel.d.ts +21 -2
  115. package/dist/types/deploy/redirects.d.ts +17 -1
  116. package/dist/types/deploy/sitemap.d.ts +2 -1
  117. package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
  118. package/dist/types/narration/build.d.ts +34 -12
  119. package/dist/types/narration/provider.d.ts +96 -8
  120. package/dist/types/openapi/checks.d.ts +27 -0
  121. package/dist/types/openapi/graphql.d.ts +6 -0
  122. package/dist/types/openapi/model.d.ts +15 -4
  123. package/dist/types/openapi/parse.d.ts +3 -0
  124. package/dist/types/openapi/references.d.ts +16 -0
  125. package/dist/types/openapi/sentence.d.ts +11 -1
  126. package/dist/types/ratelimit/index.d.ts +1 -1
  127. package/dist/types/ratelimit/schema.d.ts +8 -0
  128. package/dist/types/ratelimit/upstash.d.ts +39 -14
  129. package/dist/types/reference/graphql.d.ts +76 -1
  130. package/dist/types/reference/index.d.ts +1 -1
  131. package/dist/types/reference/openapi.d.ts +2 -2
  132. package/dist/types/reference/options.d.ts +37 -1
  133. package/dist/types/reference/schema.d.ts +84 -0
  134. package/dist/types/search/adapters/registry.d.ts +3 -0
  135. package/dist/types/search/adapters/typesense.d.ts +14 -4
  136. package/dist/types/search/build.d.ts +3 -1
  137. package/dist/types/search/orama-index.d.ts +9 -9
  138. package/dist/types/search/sync/algolia.d.ts +5 -2
  139. package/dist/types/search/sync/index.d.ts +3 -1
  140. package/dist/types/search/sync/typesense.d.ts +11 -7
  141. package/dist/types/seo/jsonld.d.ts +11 -6
  142. package/dist/types/sources/contentful.d.ts +16 -0
  143. package/dist/types/sources/github-releases.d.ts +8 -1
  144. package/dist/types/sources/notion.d.ts +8 -0
  145. package/dist/types/sources/payload.d.ts +2 -0
  146. package/dist/types/sources/registry.d.ts +63 -0
  147. package/dist/types/sources/shared.d.ts +3 -0
  148. package/dist/types/sources/strapi.d.ts +2 -0
  149. package/docs/01-quickstart.mdx +4 -2
  150. package/docs/02-deployment.mdx +59 -13
  151. package/docs/03-upgrading.mdx +2 -1
  152. package/docs/04-migrating.mdx +9 -9
  153. package/docs/08-faq.mdx +12 -12
  154. package/docs/advanced/changelog.mdx +30 -2
  155. package/docs/advanced/custom-pages.mdx +1 -1
  156. package/docs/advanced/skills.mdx +1 -1
  157. package/docs/cli/audit.mdx +30 -7
  158. package/docs/cli/doctor.mdx +6 -2
  159. package/docs/cli/evals.mdx +4 -4
  160. package/docs/cli/translate.mdx +1 -0
  161. package/docs/cli/validate.mdx +4 -3
  162. package/docs/configuration/analytics.mdx +19 -1
  163. package/docs/configuration/assistant.mdx +6 -2
  164. package/docs/configuration/customization.mdx +4 -2
  165. package/docs/configuration/export.mdx +5 -3
  166. package/docs/configuration/index.mdx +4 -2
  167. package/docs/configuration/narration.mdx +43 -6
  168. package/docs/configuration/rate-limiting.mdx +23 -2
  169. package/docs/configuration/search.mdx +37 -14
  170. package/docs/content/components.mdx +2 -2
  171. package/docs/content/frontmatter.mdx +5 -3
  172. package/docs/content/i18n.mdx +2 -2
  173. package/docs/content/includes.mdx +1 -1
  174. package/docs/content/navigation.mdx +8 -6
  175. package/docs/content/sources/contentful.mdx +9 -4
  176. package/docs/content/sources/custom.mdx +7 -3
  177. package/docs/content/sources/github-releases.mdx +12 -2
  178. package/docs/content/sources/index.mdx +3 -3
  179. package/docs/content/sources/notion.mdx +17 -2
  180. package/docs/content/sources/obsidian.mdx +3 -1
  181. package/docs/content/sources/payload.mdx +5 -2
  182. package/docs/content/sources/remote-mdx.mdx +6 -0
  183. package/docs/content/sources/sanity.mdx +4 -2
  184. package/docs/content/sources/strapi.mdx +5 -2
  185. package/docs/content/syntax.mdx +2 -2
  186. package/docs/discoverability/agent-discovery.mdx +1 -1
  187. package/docs/discoverability/index.mdx +1 -1
  188. package/docs/discoverability/json-api.mdx +2 -0
  189. package/docs/discoverability/llms-txt.mdx +1 -1
  190. package/docs/discoverability/markdown.mdx +3 -1
  191. package/docs/discoverability/mcp.mdx +6 -0
  192. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  193. package/docs/discoverability/structured-data.mdx +8 -6
  194. package/docs/references/api-pages.mdx +1 -1
  195. package/docs/references/asyncapi.mdx +5 -1
  196. package/docs/references/graphql.mdx +15 -1
  197. package/docs/references/openapi.mdx +64 -8
  198. package/package.json +9 -9
  199. package/skills/blume/SKILL.md +1 -1
  200. package/skills/blume-migrate/SKILL.md +2 -2
  201. package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
  202. package/skills/blume-migrate/references/docusaurus.md +4 -4
  203. package/skills/blume-migrate/references/fumadocs.md +17 -13
  204. package/skills/blume-migrate/references/monorepo.md +5 -5
  205. package/skills/blume-migrate/references/nextra.md +8 -5
  206. package/skills/blume-migrate/references/starlight.md +9 -7
  207. package/src/ai/ask-context.ts +33 -3
  208. package/src/ai/ask-limits.ts +13 -0
  209. package/src/ai/ask.ts +24 -6
  210. package/src/ai/changelog-markdown.ts +11 -6
  211. package/src/ai/component-markdown.ts +1 -1
  212. package/src/ai/cors.ts +12 -3
  213. package/src/ai/mcp/query.ts +14 -6
  214. package/src/ai/openapi-components.ts +28 -4
  215. package/src/ai/relative-links.ts +4 -38
  216. package/src/analytics/clics.ts +58 -0
  217. package/src/analytics/head.ts +5 -0
  218. package/src/analytics/index.ts +2 -0
  219. package/src/analytics/schema.ts +2 -0
  220. package/src/astro/generate.ts +91 -56
  221. package/src/astro/integration.ts +72 -22
  222. package/src/astro/markdown-negotiation.ts +3 -0
  223. package/src/astro/module-types.ts +3 -1
  224. package/src/astro/pages.ts +23 -0
  225. package/src/astro/render-deps.ts +10 -5
  226. package/src/astro/runtime-deps.ts +23 -2
  227. package/src/astro/templates.ts +129 -81
  228. package/src/audit/catalog.ts +19 -3
  229. package/src/audit/checks/links.ts +14 -1
  230. package/src/audit/checks/network.ts +110 -6
  231. package/src/audit/checks/redirects.ts +6 -4
  232. package/src/audit/checks/robots.ts +58 -2
  233. package/src/audit/run.ts +3 -0
  234. package/src/audit/types.ts +2 -0
  235. package/src/audit/url.ts +14 -5
  236. package/src/blume-modules.d.ts +5 -1
  237. package/src/cli/args.ts +51 -0
  238. package/src/cli/build-failure.ts +23 -0
  239. package/src/cli/commands/audit.ts +10 -1
  240. package/src/cli/commands/dev.ts +18 -2
  241. package/src/cli/commands/doctor.ts +45 -12
  242. package/src/cli/commands/eval.ts +36 -4
  243. package/src/cli/commands/init.ts +6 -4
  244. package/src/cli/commands/validate.ts +10 -1
  245. package/src/cli/init/scaffold.ts +9 -6
  246. package/src/cli/prepare.ts +6 -3
  247. package/src/cli/required-secrets.ts +3 -2
  248. package/src/components/content/ApiPlayground.astro +6 -2
  249. package/src/components/content/api-field.ts +4 -1
  250. package/src/components/content/api-page.ts +31 -7
  251. package/src/components/content/mermaid-element.ts +15 -1
  252. package/src/components/content/view-switcher.ts +7 -4
  253. package/src/components/islands/hooks.ts +27 -1
  254. package/src/components/layout/NavTree.astro +49 -5
  255. package/src/components/layout/NavTreeScript.astro +18 -0
  256. package/src/components/layout/PageActions.astro +139 -24
  257. package/src/components/layout/PageLayout.astro +13 -3
  258. package/src/components/layout/RootLayout.astro +27 -14
  259. package/src/components/layout/Search.astro +17 -3
  260. package/src/components/layout/TableOfContents.astro +24 -17
  261. package/src/components/layout/analytics-client.ts +8 -8
  262. package/src/components/layout/head-scripts.ts +6 -1
  263. package/src/components/layout/search/algolia.ts +10 -1
  264. package/src/components/layout/search/analytics.ts +42 -14
  265. package/src/components/layout/search/endpoint.ts +48 -5
  266. package/src/components/layout/search/pagefind.ts +114 -5
  267. package/src/components/layout/search/types.ts +12 -0
  268. package/src/components/layout/search/typesense.ts +18 -6
  269. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  270. package/src/components/openapi/Authorization.astro +10 -0
  271. package/src/components/openapi/GraphqlOperation.astro +5 -0
  272. package/src/components/openapi/Operation.astro +24 -1
  273. package/src/components/openapi/ParametersTable.astro +8 -4
  274. package/src/components/openapi/Playground.astro +7 -1
  275. package/src/components/openapi/RequestBody.astro +19 -2
  276. package/src/components/openapi/Responses.astro +1 -0
  277. package/src/components/openapi/SchemaTable.astro +15 -3
  278. package/src/components/openapi/async-snippets.ts +61 -5
  279. package/src/components/openapi/async.ts +123 -29
  280. package/src/components/openapi/graphql-helpers.ts +35 -12
  281. package/src/components/openapi/helpers.ts +36 -2
  282. package/src/components/openapi/message-model.ts +54 -11
  283. package/src/components/openapi/message.ts +14 -4
  284. package/src/components/openapi/operation-model.ts +16 -7
  285. package/src/components/openapi/playground-client.ts +77 -58
  286. package/src/components/openapi/playground-schema.ts +1 -1
  287. package/src/components/openapi/schema-tree.ts +44 -14
  288. package/src/components/openapi/security.ts +10 -0
  289. package/src/components/openapi/snippets.ts +5 -3
  290. package/src/components/openapi/validate-json.ts +4 -3
  291. package/src/components/powered-by-middleware.ts +33 -0
  292. package/src/core/changelog-index.ts +31 -1
  293. package/src/core/component-diagnostics.ts +53 -0
  294. package/src/core/component-overrides.ts +32 -0
  295. package/src/core/config-input.ts +42 -6
  296. package/src/core/data.ts +2 -0
  297. package/src/core/diagnostics.ts +5 -0
  298. package/src/core/i18n-ui.ts +2 -0
  299. package/src/core/includes.ts +1 -1
  300. package/src/core/links.ts +47 -17
  301. package/src/core/powered-by.ts +12 -0
  302. package/src/core/probe.ts +26 -6
  303. package/src/core/project-graph.ts +59 -18
  304. package/src/core/redirect-patterns.ts +36 -7
  305. package/src/core/schema.ts +26 -6
  306. package/src/core/sources/cache.ts +6 -0
  307. package/src/core/sources/contentful-rich-text.ts +29 -6
  308. package/src/core/sources/contentful.ts +135 -25
  309. package/src/core/sources/filesystem.ts +26 -4
  310. package/src/core/sources/github-releases.ts +12 -1
  311. package/src/core/sources/lexical.ts +15 -10
  312. package/src/core/sources/lower.ts +19 -3
  313. package/src/core/sources/mdx-remote.ts +62 -10
  314. package/src/core/sources/normalize.ts +227 -41
  315. package/src/core/sources/notion.ts +34 -10
  316. package/src/core/sources/obsidian.ts +16 -3
  317. package/src/core/sources/payload.ts +38 -15
  318. package/src/core/sources/remote.ts +72 -15
  319. package/src/core/sources/resolve.ts +11 -1
  320. package/src/core/sources/sanity.ts +43 -25
  321. package/src/core/sources/strapi.ts +2 -2
  322. package/src/core/types.ts +18 -0
  323. package/src/core/ui-packs/ar.ts +1 -0
  324. package/src/core/ui-packs/bg.ts +1 -0
  325. package/src/core/ui-packs/bn.ts +1 -0
  326. package/src/core/ui-packs/ca.ts +1 -0
  327. package/src/core/ui-packs/cs.ts +1 -0
  328. package/src/core/ui-packs/da.ts +1 -0
  329. package/src/core/ui-packs/de.ts +1 -0
  330. package/src/core/ui-packs/el.ts +1 -0
  331. package/src/core/ui-packs/es.ts +1 -0
  332. package/src/core/ui-packs/fa.ts +1 -0
  333. package/src/core/ui-packs/fi.ts +1 -0
  334. package/src/core/ui-packs/fr.ts +1 -0
  335. package/src/core/ui-packs/he.ts +1 -0
  336. package/src/core/ui-packs/hi.ts +1 -0
  337. package/src/core/ui-packs/hr.ts +1 -0
  338. package/src/core/ui-packs/hu.ts +1 -0
  339. package/src/core/ui-packs/id.ts +1 -0
  340. package/src/core/ui-packs/it.ts +1 -0
  341. package/src/core/ui-packs/ja.ts +1 -0
  342. package/src/core/ui-packs/ko.ts +1 -0
  343. package/src/core/ui-packs/nl.ts +1 -0
  344. package/src/core/ui-packs/no.ts +1 -0
  345. package/src/core/ui-packs/pl.ts +1 -0
  346. package/src/core/ui-packs/pt-br.ts +1 -0
  347. package/src/core/ui-packs/pt.ts +1 -0
  348. package/src/core/ui-packs/ro.ts +1 -0
  349. package/src/core/ui-packs/ru.ts +1 -0
  350. package/src/core/ui-packs/sk.ts +1 -0
  351. package/src/core/ui-packs/sr.ts +1 -0
  352. package/src/core/ui-packs/sv.ts +1 -0
  353. package/src/core/ui-packs/th.ts +1 -0
  354. package/src/core/ui-packs/tr.ts +1 -0
  355. package/src/core/ui-packs/uk.ts +1 -0
  356. package/src/core/ui-packs/vi.ts +1 -0
  357. package/src/core/ui-packs/zh-tw.ts +1 -0
  358. package/src/core/ui-packs/zh.ts +1 -0
  359. package/src/deploy/adapter-output.ts +7 -2
  360. package/src/deploy/adapters/cloudflare.ts +7 -5
  361. package/src/deploy/adapters/node.ts +47 -9
  362. package/src/deploy/adapters/types.ts +7 -3
  363. package/src/deploy/artifacts.ts +2 -1
  364. package/src/deploy/cloudflare-negotiation.ts +36 -7
  365. package/src/deploy/headers.ts +15 -1
  366. package/src/deploy/node-headers.ts +32 -20
  367. package/src/deploy/platforms/cloudflare.ts +14 -9
  368. package/src/deploy/platforms/netlify.ts +2 -1
  369. package/src/deploy/platforms/node.ts +12 -2
  370. package/src/deploy/platforms/types.ts +21 -7
  371. package/src/deploy/platforms/vercel.ts +115 -17
  372. package/src/deploy/redirects.ts +66 -6
  373. package/src/deploy/rss.ts +3 -1
  374. package/src/deploy/sitemap.ts +39 -5
  375. package/src/deploy/vercel-negotiation.ts +325 -103
  376. package/src/eval/agents.ts +18 -1
  377. package/src/eval/prompts.ts +6 -3
  378. package/src/eval/report.ts +34 -13
  379. package/src/eval/run.ts +5 -0
  380. package/src/markdown/base-links.ts +15 -14
  381. package/src/markdown/package-commands.ts +9 -8
  382. package/src/markdown/relative-links.ts +3 -2
  383. package/src/narration/build.ts +218 -40
  384. package/src/narration/provider.ts +89 -9
  385. package/src/openapi/checks.ts +273 -0
  386. package/src/openapi/graphql-build.ts +21 -0
  387. package/src/openapi/graphql.ts +11 -0
  388. package/src/openapi/model.ts +15 -3
  389. package/src/openapi/parse.ts +127 -2
  390. package/src/openapi/references.ts +43 -3
  391. package/src/openapi/render-mdx.ts +68 -8
  392. package/src/openapi/sentence.ts +29 -2
  393. package/src/openapi/source.ts +39 -14
  394. package/src/ratelimit/index.ts +1 -1
  395. package/src/ratelimit/runtime.ts +37 -19
  396. package/src/ratelimit/upstash.ts +40 -10
  397. package/src/reference/graphql.ts +15 -1
  398. package/src/reference/index.ts +1 -0
  399. package/src/reference/openapi.ts +2 -2
  400. package/src/reference/options.ts +29 -1
  401. package/src/registry/eject.ts +8 -2
  402. package/src/search/adapters/locale-scope.ts +30 -0
  403. package/src/search/adapters/typesense.ts +14 -4
  404. package/src/search/build.ts +21 -3
  405. package/src/search/orama-index.ts +163 -31
  406. package/src/search/source-pages.ts +29 -0
  407. package/src/search/sync/algolia.ts +99 -9
  408. package/src/search/sync/index.ts +52 -13
  409. package/src/search/sync/typesense.ts +72 -27
  410. package/src/seo/jsonld.ts +38 -20
  411. package/src/sources/contentful.ts +12 -0
  412. package/src/sources/github-releases.ts +7 -1
  413. package/src/sources/notion.ts +7 -0
  414. package/src/sources/shared.ts +3 -0
  415. package/src/theme/color.ts +9 -10
  416. package/src/theme/entry.ts +43 -6
  417. package/src/translate/validate.ts +23 -1
  418. package/dist/cli/chunk-0gz36akr.js.map +0 -12
  419. package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
  420. package/dist/cli/chunk-43zcf4gz.js.map +0 -36
  421. package/dist/cli/chunk-4ejxbzee.js +0 -39
  422. package/dist/cli/chunk-4ejxbzee.js.map +0 -10
  423. package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
  424. package/dist/cli/chunk-713mngdq.js.map +0 -14
  425. package/dist/cli/chunk-9xr4dgee.js.map +0 -10
  426. package/dist/cli/chunk-amrb6hgh.js +0 -145
  427. package/dist/cli/chunk-amrb6hgh.js.map +0 -11
  428. package/dist/cli/chunk-nd1821sk.js.map +0 -10
  429. package/dist/cli/chunk-rekqz4qg.js.map +0 -10
  430. package/dist/cli/chunk-ta27feev.js.map +0 -10
  431. package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
  432. package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
  433. package/dist/cli/chunk-zf748avv.js.map +0 -37
  434. /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
  435. /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
  436. /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
  437. /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
  438. /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
  439. /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
  440. /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
  441. /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
  442. /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
  443. /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
  444. /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
  445. /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
  446. /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
  447. /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
  448. /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
  449. /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
  450. /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
@@ -21,17 +21,17 @@ Keep content where it is — set `content.root: "src/content/docs"`.
21
21
  | `logo` (`{ src }` or `{ light, dark, alt }`) | `logo` — move the referenced file into `public/` |
22
22
  | `logo.replacesTitle` | `logo: { text: "" }` (renders the mark alone) |
23
23
  | `favicon` | copy the file into `public/` (drop the config field — Blume auto-detects) |
24
- | `social` (array of `{ label, icon, href }`; pre-0.33 legacy: `{ github: url }` object) | derive **`github: { owner, repo }`** from the GitHub entry; other socials drop (report) |
24
+ | `social` (array of `{ label, icon, href }`; pre-0.33 legacy: `{ github: url }` object) | derive **`github: { owner, repo }`** from the GitHub entry; every other entry whose platform Blume knows → **`footer.socials`** (`{ discord: href, bluesky: href, … }`; the `x.com`/`twitter` icon → `x`; the platform list is under `footer` in `docs/configuration/index.mdx`); the rest (`mastodon`, `gitlab`, `rss`, …) drop (report) |
25
25
  | `editLink.baseUrl` (`…/edit/<branch>/<subdir?>`) | `github: { owner, repo, branch }` — **and any repo sub-path after the branch → `github.dir`** (a docs-in-subfolder repo breaks every edit link without it); **an origin other than `https://github.com` → `github.host`** (a GitHub Enterprise repo otherwise links to the public site) |
26
26
  | `sidebar` (array) | filesystem nav / `navigation.sidebar` (see below) |
27
27
  | `tableOfContents` (`false` or `{ minHeadingLevel, maxHeadingLevel }`) | `toc` — identical shape, 1:1 |
28
28
  | `markdown.headingLinks: false` | `markdown.headingAnchors: false` |
29
29
  | `expressiveCode.themes` | `markdown.code.theme: { light, dark }` — EC assigns by theme _type_, so match each theme by its darkness, not by array position |
30
30
  | `expressiveCode.styleOverrides` | drop → restyle via a root `theme.css` file |
31
- | `head` | **drop and report** (there is no `seo.metatags`); analytics `<script>` entries → one `script()` adapter each in the `analytics` list (from `blume/analytics`): `attrs.src` → `src`, an `async`/`defer` attr → `strategy`, every other `attrs` key → `attributes`, and an inline body (`content`) → `content` |
31
+ | `head` | per entry: a `meta` tag → **`seo.metatags`** (`{ [attrs.name ?? attrs.property]: attrs.content }` — verification tokens, `theme-color`, and the like), minus the tags Blume writes itself (`description`, `robots`, `og:title`/`og:description`/`og:image`/`og:url`/`og:type`/`og:site_name`, `twitter:*` card tags, `article:*` dates — the build refuses them and names the setting that owns each, e.g. `og:image` → `seo.og`, `twitter:site` → `seo.x.handle`); an analytics `<script>` → one `script()` adapter each in the `analytics` list (from `blume/analytics`): `attrs.src` → `src`, an `async`/`defer` attr → `strategy`, every other `attrs` key → `attributes`, and an inline body (`content`) → `content`; anything else (`link`, `style`, other scripts) → drop and report |
32
32
  | `lastUpdated: true` | `lastModified: "git"` |
33
33
  | `customCss` | drop → move into a root **`theme.css`** file (auto-picked-up; not a config field) |
34
- | `components` (overrides) | Blume's layout slots via `defineComponents({ layout: { Header, Search, Sidebar, TableOfContents, Footer, … } })` — a near-1:1 map; reach for `blume eject` only beyond those |
34
+ | `components` (overrides) | Blume's layout slots via `defineComponents({ layout: { … } })` in a root `components.ts`: `Header`, `Search`, `Sidebar`, `TableOfContents`, `Pagination` keep their names; `SiteTitle` → `Logo`; **`Footer` → `PageFooter`**, not Blume's `Footer` (that slot is the site footer; Starlight's `Footer` is the page-end last-updated date, pagination, and edit link, which Blume renders itself, so keep only what the override added). Other overrides (`Banner`, `PageTitle`, `Hero`, `EditLink`, `SocialIcons`, …) have no slot — fold their additions into `PageHeader`/`PageFooter`, or `blume eject`; a Starlight name left under `layout` matches no slot and does nothing. Rewrite reads of `Astro.locals.starlightRoute` against the slot's props |
35
35
  | `plugins` | **map, don't blanket-drop** — see Plugins below |
36
36
  | `pagination: false`, `pagefind`/Pagefind options, `titleDelimiter`, `credits`, `disable404Route`, `routeMiddleware` | drop (report) |
37
37
  | `locales` / `defaultLocale` | `i18n` (see below) |
@@ -40,7 +40,7 @@ Keep content where it is — set `content.root: "src/content/docs"`.
40
40
 
41
41
  Starlight's `sidebar` array → prefer letting Blume generate from the filesystem; use `navigation.sidebar` only for shapes files can't express:
42
42
 
43
- - `{ label, autogenerate: { directory: "d" } }` → **structure the folder and rely on filesystem nav** (label → the folder's `meta.ts` `title`). Do **not** map it to a sidebar item with `root:` — in an explicit Blume sidebar that renders a single page link, not the directory's children.
43
+ - `{ label, items: [{ autogenerate: { directory: "d" } }] }` (0.39 and later; before 0.39 the group held it directly, `{ label, autogenerate: { directory: "d" } }`) → **structure the folder and rely on filesystem nav** (label → the folder's `meta.ts` `title`; `autogenerate.collapsed` → `collapsed: true` in each subfolder's `meta.ts`). Do **not** map it to a sidebar item with `root:` — in an explicit Blume sidebar that renders a single page link, not the directory's children. Since 0.39 a group's `items` can mix manual entries with an `autogenerate` entry; move those manual pages into the folder (route change → `redirects`) so the folder holds the whole group.
44
44
  - string `"guides/intro"` → `"/guides/intro"`; `{ label?, slug }` → `/<slug>`.
45
45
  - `{ label, items: [...] }` → `{ label, items: [...] }` (recursive) — or better, a real folder.
46
46
  - `{ label?, link }` → `{ label, href: link }`.
@@ -59,7 +59,8 @@ Starlight's `sidebar` array → prefer letting Blume generate from the filesyste
59
59
  | `lastUpdated: false` (boolean) | **remove** (Blume's `lastModified` takes a date; a boolean is a build error) |
60
60
  | `prev: false` **and** `next: false` | `pagination: false` (hides both links) |
61
61
  | `prev` / `next` otherwise (one side, or a label/link) | **drop** (report — the links can only be hidden together, and their labels come from the linked pages) |
62
- | `template: splash` / `hero` | **no equivalent** — rebuild as a custom `.astro` page under `content.pages`; a `hero` on a normal page drops (report) |
62
+ | `template: splash` | `mode: center` (no sidebar, a wider centered column — the closest layout; report) |
63
+ | `hero` | **no key** — its tagline → a paragraph, its actions → `<Card>`s or links, its image → a Markdown image (report); for a designed landing page, rebuild it as a custom `.astro` page under `content.pages` (`pages/index.astro` on `PageLayout` wins over the content page) |
63
64
  | `banner`, `tableOfContents`, `editUrl`, `head` | drop (report) |
64
65
 
65
66
  ## Asides → directives (and the `.md` trap)
@@ -83,7 +84,8 @@ Starlight content is full of Expressive Code fence meta; Blume understands some
83
84
  - `title="file.js"` → works as-is (or use the space-title shorthand). Line ranges `{2-3}` → work as-is.
84
85
  - `ins=`/`del=` line marks → `// [!code ++]` / `// [!code --]` comments; `mark=` → `{ranges}` or `// [!code highlight]`.
85
86
  - `showLineNumbers` → `lineNumbers`.
86
- - **Drop:** `frame="terminal"`, `collapse=`, `wrap`, `"string"` and `/regex/` text markers (report if they carried meaning).
87
+ - `wrap` / `wrap=true` → a bare `wrap` (that block's long lines wrap); a site-wide `expressiveCode.defaultProps.wrap: true` → `markdown.code.wrap: true`.
88
+ - **Drop:** `frame="terminal"`, `collapse=`, `wrap=false`, `preserveIndent`/`hangingIndent`, `"string"` and `/regex/` text markers (report if they carried meaning).
87
89
  - ` ```diff lang="js" ` → a normal ` ```js ` fence with `[!code ++]`/`[!code --]` markers.
88
90
 
89
91
  ## Plugins — map, don't drop
@@ -114,4 +116,4 @@ Remove `@astrojs/starlight` (and plugin deps) from deps, delete the Starlight bi
114
116
 
115
117
  ## Dropped — report these
116
118
 
117
- Non-GitHub socials, badge variants, sidebar/item `attrs` + `translations`, `customCss` beyond `theme.css`, `head` entries, `routeMiddleware`, splash/hero pages (rebuild as custom pages), starlight-blog's tag and author pages, aside custom icons, EC frames/collapse/text markers, prev/next toggles, unmapped plugins, any `<Icon>` name with no Lucide equivalent.
119
+ Socials on a platform `footer.socials` lacks, badge variants, sidebar/item `attrs` + `translations`, `customCss` beyond `theme.css`, `head` entries other than meta tags and analytics scripts, a page's frontmatter `head`, `routeMiddleware`, splash layouts approximated with `mode: center` and hero blocks rebuilt as content, override components with no Blume slot, starlight-blog's tag and author pages, aside custom icons, EC frames/collapse/text markers, prev/next toggles, unmapped plugins, any `<Icon>` name with no Lucide equivalent.
@@ -622,10 +622,37 @@ export interface ReaderScope {
622
622
  filters: { locale: string | undefined; version: string | undefined };
623
623
  }
624
624
 
625
+ // The snapshot's locale codes, keyed lowercase, built once per snapshot.
626
+ const localeCodes = new WeakMap<AskData, Map<string, string>>();
627
+
628
+ /**
629
+ * The locale a path's first segment names (`/ja` or `/ja/pricing` → `ja`),
630
+ * when it's one the snapshot's documents carry: a page outside the docs,
631
+ * like a localized landing page, still says which language it's in.
632
+ */
633
+ const pathLocale = (data: AskData, path: string): string | undefined => {
634
+ let codes = localeCodes.get(data);
635
+ if (!codes) {
636
+ codes = new Map<string, string>();
637
+ for (const { locale } of data.documents) {
638
+ if (locale) {
639
+ codes.set(locale.toLowerCase(), locale);
640
+ }
641
+ }
642
+ localeCodes.set(data, codes);
643
+ }
644
+ const [, first = ""] = normalizeRoute(path).split("/");
645
+ return first ? codes.get(first.toLowerCase()) : undefined;
646
+ };
647
+
625
648
  /**
626
649
  * Where the reader is: their page, when it's in the snapshot, and the locale
627
- * and docs version retrieval keeps to. Without a page, a versioned site
628
- * grounds in the current docs rather than every archived copy of each page.
650
+ * and docs version retrieval keeps to. A page the snapshot doesn't have (the
651
+ * homepage, a custom page, or none at all from a cross-origin caller) keeps
652
+ * to the locale its path's first segment names, or the default locale, so
653
+ * translations of every page don't crowd each other out. Without a page, a
654
+ * versioned site grounds in the current docs rather than every archived copy
655
+ * of each page.
629
656
  */
630
657
  export const readerScope = (
631
658
  data: AskData,
@@ -635,10 +662,13 @@ export const readerScope = (
635
662
  const current = page?.path
636
663
  ? byRoute.get(normalizeRoute(page.path))
637
664
  : undefined;
665
+ const locale = current
666
+ ? current.locale
667
+ : (page?.path && pathLocale(data, page.path)) || data.defaultLocale;
638
668
  return {
639
669
  current,
640
670
  filters: {
641
- locale: current?.locale || undefined,
671
+ locale: locale || undefined,
642
672
  version: data.versioned ? (current?.version ?? "") : undefined,
643
673
  },
644
674
  };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * What one request to the assistant's generated route (`/api/ask`) may carry.
3
+ * The route answers a longer conversation with a `400` (see
4
+ * `askEndpointTemplate`), so the panel's client (`useAssistant`) sends only
5
+ * the latest turns that fit. The endpoint is unauthenticated, so the limits
6
+ * bound what one request can spend against the model.
7
+ */
8
+
9
+ /** The most messages, questions and answers together, one request sends. */
10
+ export const ASK_MAX_MESSAGES = 40;
11
+
12
+ /** The most characters the messages take as JSON (`JSON.stringify`). */
13
+ export const ASK_MAX_MESSAGES_CHARS = 24_000;
package/src/ai/ask.ts CHANGED
@@ -110,7 +110,8 @@ export interface AssistantGatewayOptions extends AssistantAdapterOptions {
110
110
  * How much the model reasons before answering. Sent as the AI SDK's
111
111
  * top-level `reasoning` option, which the gateway maps to the model's own
112
112
  * control (OpenAI's `reasoning_effort`, for example); the model has to
113
- * offer the level you pick. Omitted keeps the model's default.
113
+ * offer the level you pick. Omitted keeps the model's default. Assistant
114
+ * only.
114
115
  */
115
116
  reasoning?: AssistantReasoning;
116
117
  /** Narration only: the speech model's voice. Defaults to `alloy`. */
@@ -153,32 +154,47 @@ export const gateway = (
153
154
  // openai()
154
155
  // ---------------------------------------------------------------------------
155
156
 
156
- const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
157
+ export const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
157
158
 
158
159
  /** Options for {@link openai}. */
159
160
  export interface AssistantOpenAIOptions extends AssistantAdapterOptions {
161
+ /**
162
+ * Name of the env var holding the key. Defaults to `OPENAI_API_KEY`, except
163
+ * for narration with a `baseUrl`, which sends only a key named here.
164
+ */
165
+ apiKeyEnv?: string;
160
166
  /**
161
167
  * An OpenAI-compatible endpoint to call instead of OpenAI
162
168
  * (`https://llm.internal.example.com/v1`): a self-hosted model or an
163
169
  * internal gateway. The route then talks Chat Completions through
164
170
  * `@ai-sdk/openai-compatible`, and the docs tools are off unless
165
171
  * `ai.assistant.tools` turns them on, since the model may not call tools.
172
+ * For narration, a server with OpenAI's speech API (`/audio/speech`),
173
+ * called through `@ai-sdk/openai` and sent only the key `apiKeyEnv` names,
174
+ * if any.
166
175
  */
167
176
  baseUrl?: string;
168
- /** The model id (`gpt-5.5`). */
177
+ /**
178
+ * Narration only: how the voice should sound, for speech models that take
179
+ * instructions ("Read calmly, like a teacher").
180
+ */
181
+ instructions?: string;
182
+ /** The model id (`gpt-5.5`, or a speech model like `gpt-4o-mini-tts`). */
169
183
  model: string;
170
184
  /**
171
185
  * The provider name the AI SDK reports, which a custom endpoint also reads
172
186
  * its `providerOptions` under. Defaults to `openai`, or `openai-compatible`
173
- * with a `baseUrl`.
187
+ * with a `baseUrl`. Assistant only.
174
188
  */
175
189
  name?: string;
176
190
  /**
177
191
  * How much the model reasons before answering, sent as `reasoning_effort`;
178
192
  * with a `baseUrl`, the endpoint has to accept that parameter. Omitted
179
- * keeps the model's default.
193
+ * keeps the model's default. Assistant only.
180
194
  */
181
195
  reasoning?: AssistantReasoning;
196
+ /** Narration only: the speech model's voice. Defaults to `alloy`. */
197
+ voice?: string;
182
198
  }
183
199
 
184
200
  const openaiOptionsSchema = z.strictObject({
@@ -203,7 +219,9 @@ export const openaiAdapterSchema = adapterDescriptorSchema(
203
219
  * Route the assistant to OpenAI, or to any OpenAI-compatible endpoint with
204
220
  * `baseUrl`. Reads `OPENAI_API_KEY` and needs `@ai-sdk/openai` installed; a
205
221
  * custom endpoint needs `@ai-sdk/openai-compatible` instead, since most
206
- * don't serve OpenAI's Responses API.
222
+ * don't serve OpenAI's Responses API. Narration generates audio with it
223
+ * through `@ai-sdk/openai` either way, and with a `baseUrl` sends only the key
224
+ * `apiKeyEnv` names.
207
225
  */
208
226
  export const openai = (
209
227
  options: AssistantOpenAIOptions
@@ -1,4 +1,5 @@
1
1
  import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
2
+ import { withChangelogIndexText } from "../core/changelog-index.ts";
2
3
  import { EN_UI, resolveUIStrings } from "../core/i18n-ui.ts";
3
4
  import type { BlumeProject } from "../core/project-graph.ts";
4
5
  import { absoluteUrl } from "../core/site-url.ts";
@@ -45,12 +46,16 @@ const rowLine = (project: BlumeProject, { date, page }: IndexRow): string => {
45
46
 
46
47
  export const buildChangelogIndexMarkdown = (project: BlumeProject): string => {
47
48
  const { i18n } = project.config;
48
- const ui = i18n
49
- ? resolveUIStrings(i18n.defaultLocale, {
50
- defaultLocale: i18n.defaultLocale,
51
- overrides: i18n.ui,
52
- })
53
- : EN_UI;
49
+ const ui = withChangelogIndexText(
50
+ project.config,
51
+ i18n
52
+ ? resolveUIStrings(i18n.defaultLocale, {
53
+ defaultLocale: i18n.defaultLocale,
54
+ overrides: i18n.ui,
55
+ })
56
+ : EN_UI,
57
+ i18n?.defaultLocale
58
+ );
54
59
  const rows: IndexRow[] = project.graph.pages
55
60
  .filter(
56
61
  (page) =>
@@ -584,7 +584,7 @@ export const inlineCode = (text: string): string => {
584
584
  };
585
585
 
586
586
  /** Fence `code` so its opening/closing run outlengths any backticks inside. */
587
- const fencedBlock = (lang: string, code: string): string => {
587
+ export const fencedBlock = (lang: string, code: string): string => {
588
588
  const trimmed = code.replace(/(?<!\n)\n+$/u, "");
589
589
  const runs = trimmed.match(/`+/gu);
590
590
  const longest = runs ? Math.max(...runs.map((run) => run.length)) : 0;
package/src/ai/cors.ts CHANGED
@@ -66,6 +66,13 @@ interface RequestContext {
66
66
  request: Request;
67
67
  }
68
68
 
69
+ /**
70
+ * The response headers a cross-origin caller may read beyond the safelisted
71
+ * ones: the rate limit's `Retry-After`, so a caller can tell a reader how
72
+ * long to wait after a `429`.
73
+ */
74
+ const EXPOSED_HEADERS = "retry-after";
75
+
69
76
  /** Stamp the CORS headers on every response `handler` returns. */
70
77
  export const withCors =
71
78
  (
@@ -74,9 +81,8 @@ export const withCors =
74
81
  ): ((context: RequestContext) => Promise<Response>) =>
75
82
  async (context) => {
76
83
  const response = await handler(context);
77
- for (const [key, value] of Object.entries(
78
- corsHeaders(context.request, allowed)
79
- )) {
84
+ const headers = corsHeaders(context.request, allowed);
85
+ for (const [key, value] of Object.entries(headers)) {
80
86
  // `Vary` accumulates (the handler may already vary on something), the
81
87
  // rest replace.
82
88
  if (key === "vary") {
@@ -85,5 +91,8 @@ export const withCors =
85
91
  response.headers.set(key, value);
86
92
  }
87
93
  }
94
+ if (headers["access-control-allow-origin"]) {
95
+ response.headers.set("access-control-expose-headers", EXPOSED_HEADERS);
96
+ }
88
97
  return response;
89
98
  };
@@ -53,20 +53,28 @@ const contentTypesField = z.preprocess((value) => {
53
53
  }, z.array(z.string()).optional().describe('Only include pages of these content types (frontmatter `type`, e.g. `["doc", "rfc"]`). `list_pages` shows each page\'s type. Omit to include every type.'));
54
54
 
55
55
  /**
56
- * The optional facet filter `search_docs` and `list_pages` share. Only
57
- * string-valued entries survive; an empty `{}` means "no filter".
56
+ * The optional facet filter `search_docs` and `list_pages` share. A value
57
+ * is read the way `pageFacets` stores one: a string as-is, a number or
58
+ * boolean stringified (`{"priority": 1}` matches the facet `"1"`). Entries of
59
+ * any other shape are dropped; an empty `{}` means "no filter".
58
60
  */
59
- /** Accepts any plain object, so the string-valued entries can be sifted out. */
61
+ /** Accepts any plain object, so the usable entries can be sifted out. */
60
62
  const looseFacetObject = z.record(z.string(), z.unknown());
61
63
 
64
+ /** A filter value a facet can hold, stringified like `pageFacets` does. */
65
+ const facetFilterValue = z
66
+ .union([z.string(), z.number(), z.boolean()])
67
+ .transform(String);
68
+
62
69
  const filtersField = z.preprocess((value) => {
63
70
  const candidate = looseFacetObject.safeParse(value);
64
71
  if (!candidate.success) {
65
72
  return;
66
73
  }
67
- const entries = Object.entries(candidate.data).filter(
68
- (entry): entry is [string, string] => typeof entry[1] === "string"
69
- );
74
+ const entries = Object.entries(candidate.data).flatMap(([key, entry]) => {
75
+ const parsed = facetFilterValue.safeParse(entry);
76
+ return parsed.success ? [[key, parsed.data] as const] : [];
77
+ });
70
78
  return entries.length > 0 ? Object.fromEntries(entries) : undefined;
71
79
  }, z.record(z.string(), z.string()).optional().describe('Only include pages matching every facet, key → required value (e.g. `{"status": "enforced"}`). Facets are metadata the site declares per content type; `list_pages` shows each page\'s facet values. Omit for no facet filtering.'));
72
80
 
@@ -1,13 +1,24 @@
1
+ import { customCodeSamples } from "../components/openapi/code-samples.ts";
1
2
  import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
2
3
  import type { ApiOperationRef, OpenApiData } from "../openapi/model.ts";
3
- import { operationOf, specAddresses, specOf } from "../openapi/model.ts";
4
+ import {
5
+ operationObject,
6
+ operationOf,
7
+ specAddresses,
8
+ specOf,
9
+ } from "../openapi/model.ts";
4
10
  import { asSentence } from "../openapi/sentence.ts";
5
11
  import { operationSignature } from "../openapi/signature.ts";
6
12
  import type {
7
13
  ComponentMarkdown,
8
14
  EvaluatedValue,
9
15
  } from "./component-markdown.ts";
10
- import { inlineCode, isString, linkDestination } from "./component-markdown.ts";
16
+ import {
17
+ fencedBlock,
18
+ inlineCode,
19
+ isString,
20
+ linkDestination,
21
+ } from "./component-markdown.ts";
11
22
 
12
23
  /**
13
24
  * Spec-authored prose as one line of inline Markdown. Unlike the props the
@@ -62,7 +73,10 @@ const listItem = (
62
73
  * section shows. Parameters, schemas and responses are deliberately left out:
63
74
  * those are `operation-model.ts` plus each component's own preparation, and a
64
75
  * second implementation here would be free to disagree with the page. The
65
- * endpoint and what it does is the part that was missing altogether.
76
+ * endpoint and what it does is the part that was missing altogether. The
77
+ * spec's own `x-codeSamples` do come along, as fenced code: they're text the
78
+ * spec wrote out (often an SDK call no agent could derive from the
79
+ * endpoint), read through the same `customCodeSamples` the page renders.
66
80
  *
67
81
  * `specs` is the parsed `blume:openapi` data — empty when the project has no
68
82
  * API reference, in which case every serializer declines. `deployBase` is the
@@ -142,7 +156,17 @@ export const openapiComponentSerializers = (
142
156
  : "",
143
157
  operation.deprecated ? "**Deprecated.**" : "",
144
158
  ].filter(Boolean);
145
- return [signature, ...notes].join("\n\n");
159
+ // The spec's own `x-codeSamples`, labeled as their tabs are.
160
+ const object =
161
+ data.kind === "openapi" ? operationObject(data, operation) : undefined;
162
+ const samples = customCodeSamples({
163
+ "x-code-samples": object?.["x-code-samples"],
164
+ "x-codeSamples": object?.["x-codeSamples"],
165
+ }).map(
166
+ (sample) =>
167
+ `**${inlineText(sample.label)}**\n\n${fencedBlock(sample.lang, sample.source)}`
168
+ );
169
+ return [signature, ...notes, ...samples].join("\n\n");
146
170
  },
147
171
  } satisfies Record<string, ComponentMarkdown>;
148
172
  };
@@ -7,8 +7,6 @@ import {
7
7
  withAuthoredBasePath,
8
8
  withBasePath,
9
9
  } from "../core/base-path.ts";
10
- import { nextFenceState } from "../core/code-fences.ts";
11
- import type { FenceState } from "../core/code-fences.ts";
12
10
  import { localePrefix } from "../core/i18n.ts";
13
11
  import {
14
12
  buildFileRouteIndex,
@@ -49,10 +47,6 @@ export interface LinkedPage {
49
47
  /** Rewrite a page's relative page links to the routes they mean. */
50
48
  export type RelativeLinkRewriter = (text: string, page: LinkedPage) => string;
51
49
 
52
- /** A reference definition's target: `[label]: ./install` or `<./install>`. */
53
- const DEFINITION =
54
- /^(?<lead> {0,3}\[(?:[^\]\\]|\\.)+\]:[\t ]*)(?:<(?<angled>[^>]*)>|(?<bare>\S+))/u;
55
-
56
50
  /**
57
51
  * Whether a text could hold a relative page link at all: an inline link, a
58
52
  * reference definition, or a component `href` whose target isn't a URL with
@@ -88,31 +82,13 @@ const spliceLine = (line: string, splices: readonly Splice[]): string => {
88
82
  return out;
89
83
  };
90
84
 
91
- /** One line's reference-definition splice, when it defines a routed target. */
92
- const definitionSplice = (
93
- line: string,
94
- routed: (target: string) => string | undefined
95
- ): Splice | undefined => {
96
- const groups = DEFINITION.exec(line)?.groups;
97
- const lead = groups?.lead;
98
- const target = groups?.angled ?? groups?.bare;
99
- const route = target === undefined ? undefined : routed(target);
100
- if (lead === undefined || target === undefined || route === undefined) {
101
- return undefined;
102
- }
103
- // An angle-bracketed target starts one past its `<`.
104
- const angled = groups?.angled === undefined ? 0 : 1;
105
- return { column: lead.length + angled, length: target.length, text: route };
106
- };
107
-
108
85
  /**
109
- * Every routed splice in `text`, by 0-based line: links and images, then
110
- * definitions. An image target goes through `imaged`, everything else through
111
- * `routed`.
86
+ * Every routed splice in `text`, by 0-based line: links, images, and
87
+ * reference definitions, as `extractLinks` finds them. An image target goes
88
+ * through `imaged`, everything else through `routed`.
112
89
  */
113
90
  const collectSplices = (
114
91
  text: string,
115
- lines: readonly string[],
116
92
  routed: (target: string) => string | undefined,
117
93
  imaged: (target: string) => string | undefined
118
94
  ): Map<number, Splice[]> => {
@@ -130,16 +106,6 @@ const collectSplices = (
130
106
  });
131
107
  }
132
108
  }
133
- let fence: FenceState = null;
134
- for (const [index, line] of lines.entries()) {
135
- const next = nextFenceState(line, fence);
136
- const inFence = fence !== null || next !== null;
137
- fence = next;
138
- const splice = inFence ? undefined : definitionSplice(line, routed);
139
- if (splice !== undefined) {
140
- add(index, splice);
141
- }
142
- }
143
109
  return splices;
144
110
  };
145
111
 
@@ -242,7 +208,7 @@ export const relativeLinkRewriter = (
242
208
  };
243
209
 
244
210
  const lines = text.split("\n");
245
- const splices = collectSplices(text, lines, routed, imaged);
211
+ const splices = collectSplices(text, routed, imaged);
246
212
 
247
213
  return lines
248
214
  .map((line, index) => {
@@ -0,0 +1,58 @@
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 browser tracker in Clics' installation guide. */
8
+ export const CLICS_SCRIPT_SRC = "https://clics.dev/tracker.js";
9
+
10
+ /** Public project ID and optional tracker flags. */
11
+ export interface ClicsOptions {
12
+ /** Project ID from Configure → Tracking in the Clics dashboard. */
13
+ projectId: string;
14
+ /** Include localhost and 127.0.0.1 in a production build's reports. */
15
+ allowLocalhost?: boolean;
16
+ /** Turn off automatic outbound-link events. */
17
+ disableOutboundLinks?: boolean;
18
+ }
19
+
20
+ export const clicsOptionsSchema = z.strictObject({
21
+ allowLocalhost: z.boolean().optional(),
22
+ disableOutboundLinks: z.boolean().optional(),
23
+ projectId: z.string().min(1),
24
+ });
25
+
26
+ export type ClicsAdapter = AdapterDescriptor<"clics", ClicsOptions>;
27
+
28
+ export const clicsAdapterSchema = adapterDescriptorSchema(
29
+ "clics",
30
+ clicsOptionsSchema
31
+ );
32
+
33
+ /** Clics website analytics. The project ID is public in the tracker tag. */
34
+ export const clics = (options: ClicsOptions): ClicsAdapter => ({
35
+ kind: "clics",
36
+ options,
37
+ requiredSecrets: [],
38
+ runtimeDeps: [],
39
+ });
40
+
41
+ /**
42
+ * The documented deferred tracker tag. Clics reads these attributes from
43
+ * `document.currentScript` and tracks history changes without a Blume hook.
44
+ */
45
+ export const clicsHead = (options: ClicsOptions): HeadScript[] => {
46
+ const attributes: HeadScript["attributes"] = {
47
+ "data-project-id": options.projectId,
48
+ defer: true,
49
+ src: CLICS_SCRIPT_SRC,
50
+ };
51
+ if (options.allowLocalhost) {
52
+ attributes["data-allow-localhost"] = true;
53
+ }
54
+ if (options.disableOutboundLinks) {
55
+ attributes["data-disable-outbound-links"] = true;
56
+ }
57
+ return [{ attributes, content: null }];
58
+ };
@@ -2,6 +2,7 @@ import { adobeHead } from "./adobe.ts";
2
2
  import { amplitudeHead } from "./amplitude.ts";
3
3
  import { clarityHead } from "./clarity.ts";
4
4
  import { clearbitHead } from "./clearbit.ts";
5
+ import { clicsHead } from "./clics.ts";
5
6
  import { cloudflareHead } from "./cloudflare.ts";
6
7
  import { databuddyHead } from "./databuddy.ts";
7
8
  import { fathomHead } from "./fathom.ts";
@@ -47,6 +48,7 @@ export type HeadNode =
47
48
  * the one place that branches on `kind`. A provider whose install snippet is
48
49
  * two tags (an SDK plus its init call) emits two.
49
50
  */
51
+ // oxlint-disable-next-line complexity -- each adapter has one exhaustive, type-narrowed dispatch case
50
52
  const adapterScripts = (
51
53
  adapter: Exclude<AnalyticsAdapter, { kind: "vercel" }>
52
54
  ): HeadScript[] => {
@@ -63,6 +65,9 @@ const adapterScripts = (
63
65
  case "clearbit": {
64
66
  return clearbitHead(adapter.options);
65
67
  }
68
+ case "clics": {
69
+ return clicsHead(adapter.options);
70
+ }
66
71
  case "cloudflare": {
67
72
  return cloudflareHead(adapter.options);
68
73
  }
@@ -28,6 +28,8 @@ export { clarity } from "./clarity.ts";
28
28
  export type { ClarityAdapter, ClarityOptions } from "./clarity.ts";
29
29
  export { clearbit } from "./clearbit.ts";
30
30
  export type { ClearbitAdapter, ClearbitOptions } from "./clearbit.ts";
31
+ export { clics } from "./clics.ts";
32
+ export type { ClicsAdapter, ClicsOptions } from "./clics.ts";
31
33
  export { cloudflare } from "./cloudflare.ts";
32
34
  export type { CloudflareAdapter, CloudflareOptions } from "./cloudflare.ts";
33
35
  export { databuddy } from "./databuddy.ts";
@@ -4,6 +4,7 @@ import { adobeAdapterSchema } from "./adobe.ts";
4
4
  import { amplitudeAdapterSchema } from "./amplitude.ts";
5
5
  import { clarityAdapterSchema } from "./clarity.ts";
6
6
  import { clearbitAdapterSchema } from "./clearbit.ts";
7
+ import { clicsAdapterSchema } from "./clics.ts";
7
8
  import { cloudflareAdapterSchema } from "./cloudflare.ts";
8
9
  import { databuddyAdapterSchema } from "./databuddy.ts";
9
10
  import { fathomAdapterSchema } from "./fathom.ts";
@@ -28,6 +29,7 @@ export const analyticsAdapterSchema = z.discriminatedUnion("kind", [
28
29
  amplitudeAdapterSchema,
29
30
  clarityAdapterSchema,
30
31
  clearbitAdapterSchema,
32
+ clicsAdapterSchema,
31
33
  cloudflareAdapterSchema,
32
34
  databuddyAdapterSchema,
33
35
  fathomAdapterSchema,