blume 2.0.3 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (607) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +142 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
  5. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
  6. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  7. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  8. package/dist/cli/chunk-150jk7km.js +52 -0
  9. package/dist/cli/chunk-150jk7km.js.map +10 -0
  10. package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
  11. package/dist/cli/chunk-313vnn40.js.map +10 -0
  12. package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
  13. package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
  14. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  15. package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
  16. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  17. package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
  18. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  19. package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
  20. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
  21. package/dist/cli/chunk-66w9j7p6.js +322 -0
  22. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  23. package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
  24. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  25. package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
  26. package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
  27. package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
  28. package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
  29. package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
  30. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  31. package/dist/cli/chunk-cm89scdv.js +443 -0
  32. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  33. package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
  34. package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
  35. package/dist/cli/chunk-e6atmn4h.js.map +36 -0
  36. package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
  37. package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
  38. package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
  39. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
  40. package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
  41. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  42. package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
  43. package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
  44. package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
  45. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
  46. package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
  47. package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
  48. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  49. package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
  50. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  51. package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
  52. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  53. package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
  54. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
  55. package/dist/cli/chunk-j134vrp2.js +65 -0
  56. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  57. package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
  58. package/dist/cli/chunk-kwrg63tm.js +31 -0
  59. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  60. package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
  61. package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
  62. package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
  63. package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
  64. package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
  65. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  66. package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
  67. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
  68. package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
  69. package/dist/cli/chunk-nrns10mj.js.map +10 -0
  70. package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
  71. package/dist/cli/chunk-t2m5j4pr.js +104 -0
  72. package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
  73. package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
  74. package/dist/cli/chunk-tphac74f.js +126 -0
  75. package/dist/cli/chunk-tphac74f.js.map +11 -0
  76. package/dist/cli/chunk-w1r4vase.js +18 -0
  77. package/dist/cli/chunk-w1r4vase.js.map +10 -0
  78. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  79. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  80. package/dist/cli/index.js +31 -20
  81. package/dist/cli/index.js.map +5 -5
  82. package/dist/types/ai/agent-surface.d.ts +8 -7
  83. package/dist/types/ai/ai-catalog.d.ts +7 -0
  84. package/dist/types/ai/ask-context.d.ts +26 -0
  85. package/dist/types/ai/ask.d.ts +271 -58
  86. package/dist/types/ai/component-markdown.d.ts +2 -0
  87. package/dist/types/ai/index.d.ts +2 -2
  88. package/dist/types/ai/openapi-components.d.ts +4 -1
  89. package/dist/types/ai/site-skill.d.ts +26 -0
  90. package/dist/types/ai/skills.d.ts +16 -0
  91. package/dist/types/astro/integration.d.ts +9 -0
  92. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  93. package/dist/types/astro/pages.d.ts +10 -0
  94. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  95. package/dist/types/captcha/index.d.ts +28 -0
  96. package/dist/types/captcha/schema.d.ts +29 -0
  97. package/dist/types/captcha/turnstile.d.ts +28 -0
  98. package/dist/types/cli/init/scaffold.d.ts +1 -1
  99. package/dist/types/components/content/api-field.d.ts +62 -0
  100. package/dist/types/components/content/api-page.d.ts +100 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  103. package/dist/types/components/openapi/helpers.d.ts +158 -0
  104. package/dist/types/components/openapi/security.d.ts +95 -0
  105. package/dist/types/components/openapi/snippets.d.ts +60 -0
  106. package/dist/types/consent/ethyca.d.ts +48 -0
  107. package/dist/types/consent/index.d.ts +37 -0
  108. package/dist/types/consent/native.d.ts +28 -0
  109. package/dist/types/consent/osano.d.ts +42 -0
  110. package/dist/types/consent/schema.d.ts +110 -0
  111. package/dist/types/core/changelog-index.d.ts +8 -0
  112. package/dist/types/core/config-input.d.ts +217 -23
  113. package/dist/types/core/data.d.ts +53 -4
  114. package/dist/types/core/footer.d.ts +8 -0
  115. package/dist/types/core/i18n-ui.d.ts +88 -0
  116. package/dist/types/core/includes.d.ts +8 -2
  117. package/dist/types/core/links.d.ts +8 -0
  118. package/dist/types/core/localizable.d.ts +7 -0
  119. package/dist/types/core/page-modes.d.ts +29 -0
  120. package/dist/types/core/probe.d.ts +2 -0
  121. package/dist/types/core/project-graph.d.ts +6 -0
  122. package/dist/types/core/redirect-patterns.d.ts +152 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +670 -56
  125. package/dist/types/core/sources/cache.d.ts +3 -0
  126. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  127. package/dist/types/core/sources/contentful.d.ts +17 -1
  128. package/dist/types/core/sources/lexical.d.ts +5 -0
  129. package/dist/types/core/sources/normalize.d.ts +20 -1
  130. package/dist/types/core/sources/read.d.ts +5 -0
  131. package/dist/types/core/sources/remote.d.ts +37 -10
  132. package/dist/types/core/sources/sanity.d.ts +1 -1
  133. package/dist/types/core/types.d.ts +18 -1
  134. package/dist/types/core/variables.d.ts +42 -0
  135. package/dist/types/deploy/adapter-output.d.ts +2 -1
  136. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  137. package/dist/types/deploy/adapters/node.d.ts +58 -23
  138. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  139. package/dist/types/deploy/adapters/types.d.ts +3 -2
  140. package/dist/types/deploy/node-headers.d.ts +13 -2
  141. package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
  142. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  143. package/dist/types/deploy/platforms/node.d.ts +3 -1
  144. package/dist/types/deploy/platforms/types.d.ts +18 -7
  145. package/dist/types/deploy/platforms/vercel.d.ts +26 -1
  146. package/dist/types/deploy/redirects.d.ts +55 -11
  147. package/dist/types/deploy/sitemap.d.ts +2 -1
  148. package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
  149. package/dist/types/markdown/code-title.d.ts +17 -0
  150. package/dist/types/markdown/features.d.ts +3 -2
  151. package/dist/types/narration/build.d.ts +70 -0
  152. package/dist/types/narration/provider.d.ts +26 -0
  153. package/dist/types/narration/script.d.ts +154 -0
  154. package/dist/types/openapi/checks.d.ts +27 -0
  155. package/dist/types/openapi/graphql.d.ts +6 -0
  156. package/dist/types/openapi/model.d.ts +27 -10
  157. package/dist/types/openapi/overlay.d.ts +54 -0
  158. package/dist/types/openapi/parse.d.ts +16 -0
  159. package/dist/types/openapi/references.d.ts +30 -4
  160. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  161. package/dist/types/ratelimit/index.d.ts +27 -0
  162. package/dist/types/ratelimit/memory.d.ts +36 -0
  163. package/dist/types/ratelimit/schema.d.ts +119 -0
  164. package/dist/types/ratelimit/upstash.d.ts +46 -0
  165. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  166. package/dist/types/reference/asyncapi.d.ts +10 -10
  167. package/dist/types/reference/graphql.d.ts +89 -11
  168. package/dist/types/reference/index.d.ts +1 -1
  169. package/dist/types/reference/openapi.d.ts +33 -12
  170. package/dist/types/reference/options.d.ts +82 -6
  171. package/dist/types/reference/scalar.d.ts +13 -0
  172. package/dist/types/reference/schema.d.ts +144 -36
  173. package/dist/types/search/adapters/registry.d.ts +3 -0
  174. package/dist/types/search/adapters/typesense.d.ts +14 -4
  175. package/dist/types/search/build.d.ts +3 -1
  176. package/dist/types/search/documents.d.ts +12 -1
  177. package/dist/types/search/orama-index.d.ts +13 -9
  178. package/dist/types/search/sync/algolia.d.ts +5 -2
  179. package/dist/types/search/sync/index.d.ts +3 -1
  180. package/dist/types/search/sync/typesense.d.ts +11 -7
  181. package/dist/types/seo/metatags.d.ts +20 -0
  182. package/dist/types/sources/contentful.d.ts +16 -0
  183. package/dist/types/sources/github-releases.d.ts +8 -1
  184. package/dist/types/sources/payload.d.ts +2 -0
  185. package/dist/types/sources/registry.d.ts +54 -0
  186. package/dist/types/sources/shared.d.ts +3 -0
  187. package/dist/types/sources/strapi.d.ts +2 -0
  188. package/docs/01-quickstart.mdx +4 -2
  189. package/docs/02-deployment.mdx +71 -17
  190. package/docs/03-upgrading.mdx +4 -4
  191. package/docs/04-migrating.mdx +1 -1
  192. package/docs/advanced/changelog.mdx +13 -2
  193. package/docs/advanced/skills.mdx +15 -1
  194. package/docs/cli/audit.mdx +51 -8
  195. package/docs/cli/doctor.mdx +5 -1
  196. package/docs/cli/evals.mdx +4 -4
  197. package/docs/cli/index.mdx +1 -0
  198. package/docs/cli/translate.mdx +1 -0
  199. package/docs/cli/validate.mdx +4 -3
  200. package/docs/configuration/analytics.mdx +3 -1
  201. package/docs/configuration/assistant.mdx +194 -38
  202. package/docs/configuration/consent.mdx +100 -0
  203. package/docs/configuration/customization.mdx +8 -6
  204. package/docs/configuration/export.mdx +5 -3
  205. package/docs/configuration/index.mdx +64 -5
  206. package/docs/configuration/meta.ts +3 -0
  207. package/docs/configuration/narration.mdx +119 -0
  208. package/docs/configuration/rate-limiting.mdx +90 -0
  209. package/docs/configuration/search.mdx +63 -12
  210. package/docs/configuration/theming.mdx +3 -1
  211. package/docs/content/components.mdx +108 -2
  212. package/docs/content/frontmatter.mdx +80 -2
  213. package/docs/content/i18n.mdx +39 -4
  214. package/docs/content/includes.mdx +16 -0
  215. package/docs/content/meta.mdx +1 -0
  216. package/docs/content/meta.ts +1 -0
  217. package/docs/content/navigation.mdx +32 -10
  218. package/docs/content/sources/contentful.mdx +35 -0
  219. package/docs/content/sources/custom.mdx +43 -0
  220. package/docs/content/sources/github-releases.mdx +39 -0
  221. package/docs/content/sources/index.mdx +91 -0
  222. package/docs/content/sources/meta.ts +16 -0
  223. package/docs/content/sources/notion.mdx +32 -0
  224. package/docs/content/sources/obsidian.mdx +37 -0
  225. package/docs/content/sources/payload.mdx +31 -0
  226. package/docs/content/sources/remote-mdx.mdx +44 -0
  227. package/docs/content/sources/sanity.mdx +29 -0
  228. package/docs/content/sources/strapi.mdx +32 -0
  229. package/docs/content/syntax.mdx +69 -4
  230. package/docs/content/variables.mdx +41 -0
  231. package/docs/discoverability/agent-discovery.mdx +29 -2
  232. package/docs/discoverability/index.mdx +2 -2
  233. package/docs/discoverability/json-api.mdx +2 -0
  234. package/docs/discoverability/llms-txt.mdx +2 -2
  235. package/docs/discoverability/markdown.mdx +3 -1
  236. package/docs/discoverability/mcp.mdx +4 -0
  237. package/docs/discoverability/metadata.mdx +17 -0
  238. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  239. package/docs/references/api-pages.mdx +74 -0
  240. package/docs/references/asyncapi.mdx +4 -2
  241. package/docs/references/graphql.mdx +14 -2
  242. package/docs/references/meta.ts +1 -1
  243. package/docs/references/openapi.mdx +148 -7
  244. package/docs/references/scalar.mdx +3 -1
  245. package/package.json +43 -8
  246. package/skills/blume/SKILL.md +3 -0
  247. package/skills/blume-migrate/SKILL.md +6 -6
  248. package/skills/blume-migrate/references/docusaurus.md +7 -7
  249. package/skills/blume-migrate/references/fumadocs.md +17 -13
  250. package/skills/blume-migrate/references/mintlify.md +37 -19
  251. package/skills/blume-migrate/references/nextra.md +10 -7
  252. package/skills/blume-migrate/references/starlight.md +11 -8
  253. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  254. package/skills/blume-write-skill/SKILL.md +82 -0
  255. package/src/ai/agent-readability.ts +2 -1
  256. package/src/ai/agent-surface.ts +9 -7
  257. package/src/ai/ai-catalog.ts +6 -3
  258. package/src/ai/ask-context.ts +103 -20
  259. package/src/ai/ask-data.ts +6 -0
  260. package/src/ai/ask-limits.ts +13 -0
  261. package/src/ai/ask-tools.ts +142 -0
  262. package/src/ai/ask.ts +354 -76
  263. package/src/ai/changelog-markdown.ts +11 -6
  264. package/src/ai/component-markdown.ts +76 -1
  265. package/src/ai/cors.ts +14 -3
  266. package/src/ai/index.ts +12 -0
  267. package/src/ai/llms.ts +6 -2
  268. package/src/ai/markdown.ts +2 -1
  269. package/src/ai/mcp/data.ts +6 -0
  270. package/src/ai/mcp/query.ts +14 -6
  271. package/src/ai/openapi-components.ts +36 -6
  272. package/src/ai/relative-links.ts +4 -38
  273. package/src/ai/site-skill.ts +274 -0
  274. package/src/ai/skills.ts +35 -2
  275. package/src/ai/write-skill.ts +97 -0
  276. package/src/astro/generate.ts +210 -66
  277. package/src/astro/index.ts +1 -0
  278. package/src/astro/integration.ts +42 -4
  279. package/src/astro/markdown-negotiation.ts +3 -0
  280. package/src/astro/module-types.ts +9 -1
  281. package/src/astro/pages.ts +23 -0
  282. package/src/astro/templates.ts +406 -108
  283. package/src/astro/variables.ts +31 -0
  284. package/src/audit/catalog.ts +38 -3
  285. package/src/audit/checks/contrast.ts +183 -0
  286. package/src/audit/checks/i18n.ts +2 -3
  287. package/src/audit/checks/indexability.ts +2 -3
  288. package/src/audit/checks/links.ts +19 -13
  289. package/src/audit/checks/network.ts +110 -6
  290. package/src/audit/checks/redirects.ts +6 -4
  291. package/src/audit/checks/robots.ts +58 -2
  292. package/src/audit/checks/sitemap.ts +2 -3
  293. package/src/audit/redirects.ts +46 -15
  294. package/src/audit/run.ts +5 -0
  295. package/src/audit/types.ts +4 -1
  296. package/src/audit/url.ts +14 -5
  297. package/src/blume-modules.d.ts +12 -1
  298. package/src/captcha/client.ts +152 -0
  299. package/src/captcha/hcaptcha.ts +36 -0
  300. package/src/captcha/index.ts +28 -0
  301. package/src/captcha/schema.ts +32 -0
  302. package/src/captcha/turnstile.ts +38 -0
  303. package/src/captcha/verify.ts +71 -0
  304. package/src/cli/args.ts +51 -0
  305. package/src/cli/build-failure.ts +23 -0
  306. package/src/cli/command-meta.ts +5 -0
  307. package/src/cli/commands/audit.ts +10 -1
  308. package/src/cli/commands/dev.ts +18 -2
  309. package/src/cli/commands/doctor.ts +67 -28
  310. package/src/cli/commands/eval.ts +36 -4
  311. package/src/cli/commands/init.ts +6 -4
  312. package/src/cli/commands/skill.ts +96 -0
  313. package/src/cli/commands/validate.ts +10 -1
  314. package/src/cli/index.ts +5 -0
  315. package/src/cli/init/scaffold.ts +3 -3
  316. package/src/cli/prepare.ts +6 -3
  317. package/src/cli/required-secrets.ts +72 -30
  318. package/src/components/BlumePage.astro +2 -0
  319. package/src/components/content/ApiEndpoint.astro +18 -0
  320. package/src/components/content/ApiField.astro +79 -0
  321. package/src/components/content/ApiPlayground.astro +66 -0
  322. package/src/components/content/ApiRail.astro +19 -0
  323. package/src/components/content/Color.astro +1 -0
  324. package/src/components/content/FileTree.astro +1 -0
  325. package/src/components/content/GithubInfo.astro +1 -0
  326. package/src/components/content/Panel.astro +1 -0
  327. package/src/components/content/ParamField.astro +25 -0
  328. package/src/components/content/RequestExample.astro +17 -0
  329. package/src/components/content/ResponseExample.astro +17 -0
  330. package/src/components/content/ResponseField.astro +32 -0
  331. package/src/components/content/Step.astro +1 -0
  332. package/src/components/content/Tile.astro +1 -0
  333. package/src/components/content/Tooltip.astro +1 -0
  334. package/src/components/content/Tree.astro +1 -0
  335. package/src/components/content/TypeTable.astro +4 -1
  336. package/src/components/content/View.astro +21 -0
  337. package/src/components/content/ViewSwitcher.astro +168 -0
  338. package/src/components/content/api-field.ts +103 -0
  339. package/src/components/content/api-page.ts +248 -0
  340. package/src/components/content/mermaid-element.ts +15 -1
  341. package/src/components/content/view-switcher.ts +71 -0
  342. package/src/components/index.ts +2 -0
  343. package/src/components/islands/Assistant.astro +10 -1
  344. package/src/components/islands/assistant.tsx +104 -14
  345. package/src/components/islands/code-question.ts +62 -0
  346. package/src/components/islands/hooks.ts +136 -21
  347. package/src/components/islands/support-link.ts +68 -0
  348. package/src/components/layout/Analytics.astro +11 -2
  349. package/src/components/layout/Banner.astro +14 -10
  350. package/src/components/layout/Breadcrumbs.astro +6 -2
  351. package/src/components/layout/ConsentBanner.astro +62 -0
  352. package/src/components/layout/ConsentHead.astro +38 -0
  353. package/src/components/layout/Directory.astro +94 -0
  354. package/src/components/layout/DrawerSwitchers.astro +51 -0
  355. package/src/components/layout/Empty.astro +2 -2
  356. package/src/components/layout/Header.astro +122 -80
  357. package/src/components/layout/LanguageSwitcher.astro +48 -15
  358. package/src/components/layout/NarrationPlayer.astro +1085 -0
  359. package/src/components/layout/NavSelector.astro +33 -7
  360. package/src/components/layout/PageActions.astro +139 -24
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +71 -3
  363. package/src/components/layout/ReferenceLayout.astro +17 -6
  364. package/src/components/layout/RelatedPages.astro +42 -0
  365. package/src/components/layout/RootLayout.astro +318 -47
  366. package/src/components/layout/Search.astro +47 -19
  367. package/src/components/layout/SiteFooter.astro +124 -0
  368. package/src/components/layout/SiteMetatags.astro +16 -0
  369. package/src/components/layout/TableOfContents.astro +24 -17
  370. package/src/components/layout/consent/native.ts +79 -0
  371. package/src/components/layout/consent/types.ts +37 -0
  372. package/src/components/layout/footer-links.ts +53 -0
  373. package/src/components/layout/footer-socials.ts +87 -0
  374. package/src/components/layout/head-scripts.ts +6 -1
  375. package/src/components/layout/locale-redirect.ts +107 -0
  376. package/src/components/layout/nav-utils.ts +83 -0
  377. package/src/components/layout/page-feedback.ts +96 -0
  378. package/src/components/layout/search/algolia.ts +10 -1
  379. package/src/components/layout/search/analytics.ts +126 -0
  380. package/src/components/layout/search/endpoint.ts +48 -5
  381. package/src/components/layout/search/flexsearch.ts +17 -5
  382. package/src/components/layout/search/orama-cloud.ts +26 -14
  383. package/src/components/layout/search/pagefind.ts +114 -5
  384. package/src/components/layout/search/types.ts +32 -0
  385. package/src/components/layout/search/typesense.ts +19 -3
  386. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  387. package/src/components/openapi/Authorization.astro +10 -0
  388. package/src/components/openapi/GraphqlOperation.astro +5 -0
  389. package/src/components/openapi/Operation.astro +184 -13
  390. package/src/components/openapi/ParametersTable.astro +8 -4
  391. package/src/components/openapi/Playground.astro +7 -1
  392. package/src/components/openapi/RequestBody.astro +24 -5
  393. package/src/components/openapi/RequestPanel.astro +59 -7
  394. package/src/components/openapi/Responses.astro +7 -4
  395. package/src/components/openapi/SchemaTable.astro +15 -3
  396. package/src/components/openapi/async-snippets.ts +65 -9
  397. package/src/components/openapi/async.ts +123 -29
  398. package/src/components/openapi/code-samples.ts +97 -0
  399. package/src/components/openapi/graphql-helpers.ts +35 -12
  400. package/src/components/openapi/helpers.ts +36 -2
  401. package/src/components/openapi/message-model.ts +54 -11
  402. package/src/components/openapi/message.ts +14 -4
  403. package/src/components/openapi/operation-model.ts +16 -7
  404. package/src/components/openapi/playground-client.ts +24 -5
  405. package/src/components/openapi/playground-schema.ts +1 -1
  406. package/src/components/openapi/schema-tree.ts +44 -14
  407. package/src/components/openapi/security.ts +16 -3
  408. package/src/components/openapi/snippets.ts +873 -10
  409. package/src/components/openapi/validate-json.ts +4 -3
  410. package/src/components/props.ts +6 -0
  411. package/src/consent/client.ts +95 -0
  412. package/src/consent/clients.ts +19 -0
  413. package/src/consent/ethyca.ts +71 -0
  414. package/src/consent/head.ts +24 -0
  415. package/src/consent/index.ts +37 -0
  416. package/src/consent/init.ts +29 -0
  417. package/src/consent/native.ts +36 -0
  418. package/src/consent/osano.ts +59 -0
  419. package/src/consent/schema.ts +30 -0
  420. package/src/core/builtin-tags.ts +5 -0
  421. package/src/core/changelog-index.ts +31 -1
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/component-diagnostics.ts +53 -0
  424. package/src/core/component-overrides.ts +32 -0
  425. package/src/core/config-input.ts +225 -23
  426. package/src/core/data.ts +46 -4
  427. package/src/core/diagnostics.ts +8 -2
  428. package/src/core/footer.ts +26 -0
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/i18n-ui.ts +84 -2
  431. package/src/core/includes.ts +21 -4
  432. package/src/core/links.ts +63 -19
  433. package/src/core/localizable.ts +26 -0
  434. package/src/core/navigation.ts +43 -12
  435. package/src/core/page-modes.ts +56 -0
  436. package/src/core/probe.ts +26 -6
  437. package/src/core/project-graph.ts +163 -16
  438. package/src/core/redirect-patterns.ts +520 -0
  439. package/src/core/related.ts +131 -0
  440. package/src/core/schema.ts +339 -66
  441. package/src/core/sources/cache.ts +6 -0
  442. package/src/core/sources/contentful-rich-text.ts +29 -6
  443. package/src/core/sources/contentful.ts +135 -25
  444. package/src/core/sources/filesystem.ts +26 -4
  445. package/src/core/sources/github-releases.ts +12 -1
  446. package/src/core/sources/lexical.ts +15 -10
  447. package/src/core/sources/mdx-remote.ts +62 -10
  448. package/src/core/sources/normalize.ts +228 -41
  449. package/src/core/sources/notion.ts +7 -1
  450. package/src/core/sources/obsidian.ts +16 -3
  451. package/src/core/sources/payload.ts +38 -15
  452. package/src/core/sources/read.ts +8 -3
  453. package/src/core/sources/remote.ts +72 -15
  454. package/src/core/sources/resolve.ts +11 -1
  455. package/src/core/sources/sanity.ts +43 -25
  456. package/src/core/sources/strapi.ts +2 -2
  457. package/src/core/types.ts +19 -1
  458. package/src/core/ui-packs/ar.ts +44 -0
  459. package/src/core/ui-packs/bg.ts +46 -0
  460. package/src/core/ui-packs/bn.ts +44 -0
  461. package/src/core/ui-packs/ca.ts +47 -0
  462. package/src/core/ui-packs/cs.ts +45 -0
  463. package/src/core/ui-packs/da.ts +46 -0
  464. package/src/core/ui-packs/de.ts +47 -0
  465. package/src/core/ui-packs/el.ts +46 -0
  466. package/src/core/ui-packs/es.ts +47 -0
  467. package/src/core/ui-packs/fa.ts +46 -0
  468. package/src/core/ui-packs/fi.ts +46 -0
  469. package/src/core/ui-packs/fr.ts +47 -0
  470. package/src/core/ui-packs/he.ts +44 -0
  471. package/src/core/ui-packs/hi.ts +45 -0
  472. package/src/core/ui-packs/hr.ts +46 -0
  473. package/src/core/ui-packs/hu.ts +45 -0
  474. package/src/core/ui-packs/id.ts +46 -0
  475. package/src/core/ui-packs/it.ts +46 -0
  476. package/src/core/ui-packs/ja.ts +46 -0
  477. package/src/core/ui-packs/ko.ts +45 -0
  478. package/src/core/ui-packs/nl.ts +47 -0
  479. package/src/core/ui-packs/no.ts +45 -0
  480. package/src/core/ui-packs/pl.ts +46 -0
  481. package/src/core/ui-packs/pt-br.ts +47 -0
  482. package/src/core/ui-packs/pt.ts +46 -0
  483. package/src/core/ui-packs/ro.ts +46 -0
  484. package/src/core/ui-packs/ru.ts +46 -0
  485. package/src/core/ui-packs/sk.ts +45 -0
  486. package/src/core/ui-packs/sr.ts +46 -0
  487. package/src/core/ui-packs/sv.ts +45 -0
  488. package/src/core/ui-packs/th.ts +44 -0
  489. package/src/core/ui-packs/tr.ts +46 -0
  490. package/src/core/ui-packs/uk.ts +46 -0
  491. package/src/core/ui-packs/vi.ts +46 -0
  492. package/src/core/ui-packs/zh-tw.ts +44 -0
  493. package/src/core/ui-packs/zh.ts +44 -0
  494. package/src/core/variables.ts +103 -0
  495. package/src/deploy/adapter-output.ts +7 -2
  496. package/src/deploy/adapters/cloudflare.ts +7 -5
  497. package/src/deploy/adapters/node.ts +47 -9
  498. package/src/deploy/adapters/types.ts +7 -3
  499. package/src/deploy/artifacts.ts +79 -9
  500. package/src/deploy/cloudflare-negotiation.ts +35 -5
  501. package/src/deploy/headers.ts +2 -1
  502. package/src/deploy/node-headers.ts +63 -9
  503. package/src/deploy/platforms/cloudflare.ts +45 -9
  504. package/src/deploy/platforms/netlify.ts +35 -2
  505. package/src/deploy/platforms/node.ts +12 -2
  506. package/src/deploy/platforms/types.ts +21 -7
  507. package/src/deploy/platforms/vercel.ts +149 -17
  508. package/src/deploy/redirects.ts +151 -24
  509. package/src/deploy/rss.ts +3 -1
  510. package/src/deploy/sitemap.ts +39 -5
  511. package/src/deploy/vercel-negotiation.ts +326 -102
  512. package/src/eval/agents.ts +18 -1
  513. package/src/eval/prompts.ts +6 -3
  514. package/src/eval/report.ts +34 -13
  515. package/src/eval/run.ts +5 -0
  516. package/src/markdown/api-rail.ts +259 -0
  517. package/src/markdown/code-title.ts +35 -9
  518. package/src/markdown/features.ts +3 -2
  519. package/src/markdown/fence-meta.ts +2 -0
  520. package/src/markdown/include.ts +11 -1
  521. package/src/markdown/index.ts +28 -5
  522. package/src/markdown/math.ts +4 -4
  523. package/src/markdown/relative-links.ts +3 -2
  524. package/src/markdown/variables.ts +56 -0
  525. package/src/markdown/views.ts +82 -0
  526. package/src/narration/build.ts +412 -0
  527. package/src/narration/player.ts +249 -0
  528. package/src/narration/provider.ts +29 -0
  529. package/src/narration/script.ts +592 -0
  530. package/src/og/card.ts +7 -6
  531. package/src/openapi/checks.ts +273 -0
  532. package/src/openapi/graphql-build.ts +21 -0
  533. package/src/openapi/graphql.ts +11 -0
  534. package/src/openapi/model.ts +72 -27
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +182 -4
  537. package/src/openapi/references.ts +64 -8
  538. package/src/openapi/render-mdx.ts +5 -1
  539. package/src/openapi/scalar.ts +40 -6
  540. package/src/openapi/source.ts +40 -15
  541. package/src/ratelimit/cloudflare.ts +54 -0
  542. package/src/ratelimit/index.ts +30 -0
  543. package/src/ratelimit/memory.ts +45 -0
  544. package/src/ratelimit/runtime.ts +247 -0
  545. package/src/ratelimit/schema.ts +28 -0
  546. package/src/ratelimit/upstash.ts +60 -0
  547. package/src/ratelimit/wrangler.ts +70 -0
  548. package/src/reference/asyncapi.ts +2 -2
  549. package/src/reference/graphql.ts +20 -3
  550. package/src/reference/index.ts +1 -0
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +80 -8
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +23 -5
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/locale-scope.ts +30 -0
  557. package/src/search/adapters/typesense.ts +14 -4
  558. package/src/search/build.ts +21 -3
  559. package/src/search/documents.ts +26 -2
  560. package/src/search/orama-index.ts +191 -34
  561. package/src/search/pagefind-ranking.ts +39 -0
  562. package/src/search/source-pages.ts +29 -0
  563. package/src/search/sync/algolia.ts +111 -10
  564. package/src/search/sync/index.ts +52 -13
  565. package/src/search/sync/orama-cloud.ts +3 -0
  566. package/src/search/sync/typesense.ts +76 -26
  567. package/src/seo/metatags.ts +67 -0
  568. package/src/sources/contentful.ts +12 -0
  569. package/src/sources/github-releases.ts +7 -1
  570. package/src/sources/shared.ts +3 -0
  571. package/src/theme/chrome-icons.ts +1 -0
  572. package/src/theme/color.ts +500 -0
  573. package/src/theme/entry.ts +85 -6
  574. package/src/theme/palette.ts +121 -45
  575. package/src/translate/prompts.ts +1 -0
  576. package/src/translate/validate.ts +31 -3
  577. package/src/upgrade/upgrade.ts +4 -171
  578. package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
  579. package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
  580. package/dist/cli/chunk-35d4wj9f.js.map +0 -15
  581. package/dist/cli/chunk-364znk6q.js +0 -75
  582. package/dist/cli/chunk-364znk6q.js.map +0 -11
  583. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  584. package/dist/cli/chunk-8ktnccpt.js.map +0 -10
  585. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  586. package/dist/cli/chunk-b07cmahc.js +0 -145
  587. package/dist/cli/chunk-b07cmahc.js.map +0 -11
  588. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  589. package/dist/cli/chunk-e04dxsz1.js +0 -39
  590. package/dist/cli/chunk-e04dxsz1.js.map +0 -10
  591. package/dist/cli/chunk-j85vccga.js.map +0 -36
  592. package/dist/cli/chunk-p3v96n38.js.map +0 -10
  593. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  594. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  595. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  596. package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
  597. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  598. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  599. package/docs/content/sources.mdx +0 -334
  600. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
  601. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
  602. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
  603. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
  604. /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
  605. /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
  606. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
  607. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
@@ -9,14 +9,63 @@ interface PagefindResult {
9
9
  }>;
10
10
  }
11
11
 
12
- interface PagefindModule {
12
+ /** One Pagefind instance: a page language's index, plus any merged into it. */
13
+ interface PagefindInstance {
14
+ mergeIndex: (
15
+ url: string,
16
+ options: { baseUrl: string; language: string }
17
+ ) => Promise<void>;
13
18
  search: (query: string) => Promise<{ results: PagefindResult[] }>;
14
19
  }
15
20
 
21
+ interface PagefindModule {
22
+ createInstance: (options: {
23
+ basePath: string;
24
+ baseUrl: string;
25
+ }) => PagefindInstance;
26
+ }
27
+
28
+ /** `pagefind-entry.json`: the bundle's index for each language. */
29
+ interface PagefindEntry {
30
+ languages: Record<string, { page_count: number }>;
31
+ }
32
+
33
+ // Pagefind names each page after its built file (`quickstart/index.html`), so
34
+ // its URLs end in a slash. Blume serves pages without one (`trailingSlash:
35
+ // "never"`): hosts redirect the slashed URL, and `blume preview` 404s it.
36
+ const TRAILING_SLASH = /(?<=.)\/(?=[#?]|$)/u;
37
+
38
+ /**
39
+ * The language whose index Pagefind loads for a page, picked the way its
40
+ * `findIndex` does: the page's own, else its base language (`pt` for
41
+ * `pt-br`), else the language with the most pages (a locale with no
42
+ * translated pages has no index of its own).
43
+ */
44
+ const loadedLanguage = (
45
+ languages: PagefindEntry["languages"],
46
+ language: string
47
+ ): string | undefined => {
48
+ const [base = language] = language.split("-");
49
+ if (languages[language]) {
50
+ return language;
51
+ }
52
+ if (languages[base]) {
53
+ return base;
54
+ }
55
+ return Object.entries(languages).toSorted(
56
+ ([, a], [, b]) => b.page_count - a.page_count
57
+ )[0]?.[0];
58
+ };
59
+
16
60
  /**
17
61
  * Pagefind: load the index emitted into the built site and query it. The bundle
18
62
  * lives in the output (not `node_modules`), so it is imported at runtime by URL
19
63
  * — which is why this only works in the production build, not `dev`.
64
+ *
65
+ * Pagefind keeps an index per language and searches the one for the page's
66
+ * `<html lang>`, which Blume sets to the locale the dialog scopes to. A
67
+ * search scoped to a locale is that index alone; a search across every
68
+ * language (no `locale`) merges every other language's index into it.
20
69
  */
21
70
  export const createSearch = async (opts: {
22
71
  url: string;
@@ -24,25 +73,85 @@ export const createSearch = async (opts: {
24
73
  // The pagefind bundle lives in the built site (not node_modules) and is
25
74
  // resolved at runtime by URL — it can't be a static, code-splittable path.
26
75
  // SAFETY: the URL points at the `pagefind.js` module our own build emitted,
27
- // whose export contract (`search()`) is fixed by pagefind.
76
+ // whose export contract (`createInstance()`) is fixed by pagefind.
28
77
  // oxlint-disable-next-line react-doctor/no-dynamic-import-path
29
78
  const pagefind = (await import(
30
79
  /* @vite-ignore */
31
80
  opts.url
32
81
  )) as PagefindModule;
82
+ // The bundle's folder, which holds the entry file and every language's index.
83
+ const bundle = new URL("./", new URL(opts.url, document.baseURI));
84
+ // `baseUrl` "/" keeps result URLs base-less routes, like every other
85
+ // provider's: by default Pagefind prefixes the folder the bundle is served
86
+ // under, and the dialog adds the deployment base itself.
87
+ const create = (): PagefindInstance =>
88
+ pagefind.createInstance({ basePath: bundle.pathname, baseUrl: "/" });
89
+
90
+ // Every language's index: the page's own, and every other one merged in.
91
+ const everyLanguage = async (language: string): Promise<PagefindInstance> => {
92
+ const instance = create();
93
+ const response = await fetch(new URL("pagefind-entry.json", bundle));
94
+ // SAFETY: the entry file is the one Pagefind wrote into this bundle.
95
+ const { languages } = (await response.json()) as PagefindEntry;
96
+ const own = loadedLanguage(languages, language);
97
+ // Pagefind skips a merge whose path its own `basePath` starts with, as
98
+ // the same index twice, so the bundle is merged by its full URL.
99
+ await Promise.all(
100
+ Object.keys(languages)
101
+ .filter((code) => code !== own)
102
+ .map((code) =>
103
+ instance.mergeIndex(bundle.href, { baseUrl: "/", language: code })
104
+ )
105
+ );
106
+ return instance;
107
+ };
108
+
109
+ // Pagefind reads the page's language once, when an instance is created. A
110
+ // client-side language switch changes the page's language without a
111
+ // reload, so each language gets its own instance, created on its first
112
+ // search: after the switch, search moves to the new page's index. The
113
+ // every-language instance is one for the whole page load.
114
+ const instances = new Map<string, Promise<PagefindInstance>>();
115
+ const cached = (
116
+ key: string,
117
+ load: () => Promise<PagefindInstance>
118
+ ): Promise<PagefindInstance> => {
119
+ const existing = instances.get(key);
120
+ if (existing) {
121
+ return existing;
122
+ }
123
+ const loading = (async () => {
124
+ try {
125
+ return await load();
126
+ } catch (error) {
127
+ // A failed load (the entry file's fetch, say) is forgotten, so the
128
+ // next search retries it.
129
+ instances.delete(key);
130
+ throw error;
131
+ }
132
+ })();
133
+ instances.set(key, loading);
134
+ return loading;
135
+ };
136
+
33
137
  // Pagefind builds its own marked-up excerpt; we keep its `<mark>` highlights
34
138
  // (dropping any other markup — the excerpt is rendered via innerHTML) and
35
139
  // only highlight the title ourselves. It carries no section/breadcrumb data,
36
140
  // so pills stay hidden and the preview pane falls back to the excerpt.
37
- return async (query) => {
38
- const response = await pagefind.search(query);
141
+ return async (query, options) => {
142
+ // Lowercased like Pagefind's own reading, which names the entry's indexes.
143
+ const language = document.documentElement.lang.toLowerCase() || "unknown";
144
+ const instance = await (options?.locale
145
+ ? cached(language, () => Promise.resolve(create()))
146
+ : cached("*", () => everyLanguage(language)));
147
+ const response = await instance.search(query);
39
148
  const docs = await Promise.all(
40
149
  response.results.slice(0, SEARCH_LIMIT).map((result) => result.data())
41
150
  );
42
151
  const hits = docs.map((doc) => ({
43
152
  excerpt: sanitizeExcerpt(doc.excerpt),
44
153
  title: highlight(doc.meta?.title ?? doc.url, query),
45
- url: doc.url,
154
+ url: doc.url.replace(TRAILING_SLASH, ""),
46
155
  }));
47
156
  return { hits, sections: [] };
48
157
  };
@@ -43,6 +43,18 @@ export type SearchFn = (
43
43
  }
44
44
  ) => Promise<SearchResult>;
45
45
 
46
+ /** What a caller tells `createSearch` (`blume:search-client`) about its queries. */
47
+ export interface SearchClientOptions {
48
+ /**
49
+ * The queries come from a reader typing, as in the search dialog, so each
50
+ * one supersedes the last. The server-proxied client (Mixedbread), whose
51
+ * endpoint is rate-limited, then waits for a pause in typing before it
52
+ * sends a query and aborts the one a newer query supersedes. Other
53
+ * providers ignore it.
54
+ */
55
+ typing?: boolean;
56
+ }
57
+
46
58
  /** A document in the client-loaded `blume-search.json` index. */
47
59
  export interface IndexedDocument {
48
60
  route: string;
@@ -53,8 +65,28 @@ export interface IndexedDocument {
53
65
  section?: string;
54
66
  locale?: string;
55
67
  version?: string;
68
+ /** The page's relevance multiplier (`search.boost`). */
69
+ boost?: number;
70
+ /** Extra terms the page is found by (`search.keywords`). */
71
+ keywords?: string[];
56
72
  }
57
73
 
74
+ /** Something ranked with a relevance score, and the page it belongs to. */
75
+ export interface ScoredMatch<Match> {
76
+ boost?: number;
77
+ match: Match;
78
+ score: number;
79
+ }
80
+
81
+ /**
82
+ * Re-rank matches by score times their page's `search.boost`. The sort is
83
+ * stable, so equal scores keep the provider's order.
84
+ */
85
+ export const byBoost = <Match>(scored: ScoredMatch<Match>[]): Match[] =>
86
+ scored
87
+ .toSorted((a, b) => b.score * (b.boost ?? 1) - a.score * (a.boost ?? 1))
88
+ .map(({ match }) => match);
89
+
58
90
  /** Max results surfaced in the dialog. */
59
91
  export const SEARCH_LIMIT = 12;
60
92
 
@@ -17,10 +17,19 @@ interface TypesenseRecord extends Record<string, unknown> {
17
17
  * key. Documents are imported at build time by the sync step. Every adapter
18
18
  * option Blume doesn't read (`connectionTimeoutSeconds`, `numRetries`, …) is
19
19
  * the site's own client option and goes to the `Client` untouched; the named
20
- * connection options still decide `apiKey` and `nodes`.
20
+ * connection options still decide `apiKey` and `nodes`. `locale` is the
21
+ * sync's: Typesense tokenizes a query for the fields it searches.
21
22
  */
22
23
  export const createSearch = (opts: TypesenseOptions): SearchFn => {
23
- const { apiKey, collection, host, port, protocol, ...clientOptions } = opts;
24
+ const {
25
+ apiKey,
26
+ collection,
27
+ host,
28
+ locale: _locale,
29
+ port,
30
+ protocol,
31
+ ...clientOptions
32
+ } = opts;
24
33
  const client = new Client({
25
34
  ...clientOptions,
26
35
  apiKey,
@@ -34,7 +43,14 @@ export const createSearch = (opts: TypesenseOptions): SearchFn => {
34
43
  {
35
44
  per_page: SEARCH_LIMIT,
36
45
  q: query,
37
- query_by: "title,description,content",
46
+ query_by: "title,keywords,description,content",
47
+ // Relevance first, then each page's `search.boost` among pages that
48
+ // match equally well: a boost settles a tie without floating a page
49
+ // over a better match. Bucketing the relevance instead
50
+ // (`_text_match(buckets: 10)`) ranks by boost alone inside each
51
+ // bucket, so at equal boosts a bucket falls back to indexing order
52
+ // and the best match can land behind weaker ones.
53
+ sort_by: "_text_match:desc,boost:desc",
38
54
  // The sync marks `locale` and `version` as facets so hosted results
39
55
  // scope to the active language and the viewed docs version (the
40
56
  // current docs upload as "current").
@@ -7,11 +7,12 @@ import { asyncApiOperationObject } from "../../openapi/asyncapi.ts";
7
7
  import { operationOf, specOf } from "../../openapi/model.ts";
8
8
  import { highlightCode } from "../../markdown/index.ts";
9
9
  import {
10
- asyncApiSecurityEntries,
10
+ asyncApiSecurity,
11
11
  bindingGroups,
12
12
  channelParameters,
13
- channelServers,
13
+ kafkaTopic,
14
14
  messageLabel,
15
+ namedChannelServers,
15
16
  operationMessages,
16
17
  payloadSchema,
17
18
  protocolOf,
@@ -33,7 +34,6 @@ import PanelTabs from "./PanelTabs.astro";
33
34
  import ParametersTable from "./ParametersTable.astro";
34
35
  import SchemaTable from "./SchemaTable.astro";
35
36
  import type { SecuritySchemeLike } from "./security.ts";
36
- import { resolveAsyncApiSecurity } from "./security.ts";
37
37
  import OperationPanel from "./OperationPanel.astro";
38
38
 
39
39
  /**
@@ -61,9 +61,11 @@ const schemas = (document.components?.schemas ?? {}) as Record<
61
61
  string,
62
62
  SchemaLike
63
63
  >;
64
- const servers = channelServers(channel, document);
65
- const security = resolveAsyncApiSecurity(
66
- asyncApiSecurityEntries(operation, servers),
64
+ const namedServers = namedChannelServers(channel, document);
65
+ const servers = namedServers.map(({ server }) => server);
66
+ const security = asyncApiSecurity(
67
+ operation,
68
+ namedServers,
67
69
  document.components?.securitySchemes as
68
70
  | Record<string, SecuritySchemeLike>
69
71
  | undefined
@@ -96,10 +98,15 @@ const messagePanels = await Promise.all(
96
98
 
97
99
  // One derived model feeds the static default sample, the composer form, and
98
100
  // (client-side) the live samples + WebSocket frame — they can never drift.
101
+ // A Kafka channel binding's `topic` names the real topic when the address
102
+ // doesn't, so the samples use it.
103
+ const topic =
104
+ protocol === "kafka" ? kafkaTopic(channel, document.components) : undefined;
99
105
  const model = ref
100
106
  ? messageModel({
101
107
  action: ref.method === "send" ? "send" : "receive",
102
- address: ref.path,
108
+ address: topic ?? ref.path,
109
+ components: document.components,
103
110
  messages,
104
111
  parameters,
105
112
  protocol,
@@ -28,6 +28,13 @@ const { security } = Astro.props;
28
28
  Optional — this operation also accepts unauthenticated requests.
29
29
  </p>
30
30
  )}
31
+ {security.unauthenticatedServers &&
32
+ security.unauthenticatedServers.length > 0 && (
33
+ <p class="mb-2 text-muted-foreground text-sm">
34
+ No authorization needed on{" "}
35
+ {security.unauthenticatedServers.join(", ")}.
36
+ </p>
37
+ )}
31
38
  {security.alternatives.map((alternative, index) => (
32
39
  <>
33
40
  {index > 0 && (
@@ -47,6 +54,9 @@ const { security } = Astro.props;
47
54
  <span class="text-muted-foreground text-xs">
48
55
  {schemeLabel(resolved)}
49
56
  {carrier ? ` · ${carrier.in}` : ""}
57
+ {resolved.servers
58
+ ? ` · on ${resolved.servers.join(", ")}`
59
+ : ""}
50
60
  </span>
51
61
  {!security.optional && (
52
62
  <span class="font-medium text-[0.625rem] text-red-600 uppercase tracking-wide dark:text-red-400">
@@ -19,10 +19,12 @@ import {
19
19
  exampleVariables,
20
20
  graphqlPlaygroundModel,
21
21
  graphqlRoutes,
22
+ graphqlSecurity,
22
23
  } from "./graphql-helpers.ts";
23
24
  import { buildRequest, defaultValues } from "./request.ts";
24
25
  import { languageSamplePanels } from "./sample-panels.ts";
25
26
  import { sampleLanguages } from "./snippets.ts";
27
+ import Authorization from "./Authorization.astro";
26
28
  import Description from "./Description.astro";
27
29
  import GraphqlChip from "./GraphqlChip.astro";
28
30
  import GraphqlFieldsTable from "./GraphqlFieldsTable.astro";
@@ -107,6 +109,7 @@ const responsePanels =
107
109
  : [];
108
110
 
109
111
  const returnRoute = field ? routes.get(field.type.name) : undefined;
112
+ const security = spec ? graphqlSecurity(spec) : undefined;
110
113
  ---
111
114
 
112
115
  {
@@ -134,6 +137,7 @@ const returnRoute = field ? routes.get(field.type.name) : undefined;
134
137
  Deprecated: <span set:text={field.deprecationReason} />
135
138
  </p>
136
139
  )}
140
+ {security && <Authorization security={security} />}
137
141
  <GraphqlFieldsTable
138
142
  routes={routes}
139
143
  rows={field.args}
@@ -169,6 +173,7 @@ const returnRoute = field ? routes.get(field.type.name) : undefined;
169
173
  model={model}
170
174
  operation={id}
171
175
  proxy={spec.playground.proxy}
176
+ proxyConfig="graphql"
172
177
  slug={spec.slug}
173
178
  />
174
179
  )}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import specs from "blume:openapi";
3
- import { operationOf, specOf } from "../../openapi/model.ts";
3
+ import { HTTP_METHODS, operationOf, specOf } from "../../openapi/model.ts";
4
4
  import {
5
5
  mergeParameters,
6
6
  type ParameterLike,
@@ -21,10 +21,13 @@ import {
21
21
  type SecuritySchemeLike,
22
22
  } from "./security.ts";
23
23
  import { DEPRECATED_LABEL_CLASS } from "../colors.ts";
24
+ import { customCodeSamples } from "./code-samples.ts";
25
+ import type { CodeSamplesCarrier } from "./code-samples.ts";
24
26
  import { sampleLanguages } from "./snippets.ts";
25
27
  import AsyncApiOperation from "./AsyncApiOperation.astro";
26
28
  import GraphqlOperation from "./GraphqlOperation.astro";
27
29
  import Authorization from "./Authorization.astro";
30
+ import Description from "./Description.astro";
28
31
  import MethodBadge from "./MethodBadge.astro";
29
32
  import ParametersTable from "./ParametersTable.astro";
30
33
  import Playground from "./Playground.astro";
@@ -57,7 +60,18 @@ interface ResponseLike {
57
60
  content?: Record<string, MediaTypeLike>;
58
61
  }
59
62
 
60
- interface FullOperation {
63
+ /**
64
+ * One callback: a map of runtime URL expressions
65
+ * (`{$request.body#/callbackUrl}`) to the path item the API will call there,
66
+ * or a `$ref` to one under `components.callbacks`.
67
+ */
68
+ type CallbackLike = { $ref?: string } & Record<
69
+ string,
70
+ Record<string, unknown> | string | undefined
71
+ >;
72
+
73
+ interface FullOperation extends CodeSamplesCarrier {
74
+ callbacks?: Record<string, CallbackLike>;
61
75
  summary?: string;
62
76
  description?: string;
63
77
  deprecated?: boolean;
@@ -77,15 +91,17 @@ const ref = spec ? operationOf(spec, id) : undefined;
77
91
  const isAsyncApi = spec?.kind === "asyncapi";
78
92
  const isGraphql = spec?.kind === "graphql";
79
93
 
94
+ type PathItemLike = Record<string, unknown> & {
95
+ parameters?: ParameterLike[];
96
+ servers?: ServerLike[];
97
+ };
98
+
80
99
  const doc = (spec?.document ?? {}) as {
81
- paths?: Record<
82
- string,
83
- Record<string, unknown> & {
84
- parameters?: ParameterLike[];
85
- servers?: ServerLike[];
86
- }
87
- >;
100
+ paths?: Record<string, PathItemLike>;
101
+ // OpenAPI 3.1 webhooks: requests the API sends, keyed by name.
102
+ webhooks?: Record<string, PathItemLike>;
88
103
  components?: {
104
+ callbacks?: Record<string, CallbackLike>;
89
105
  schemas?: Record<string, SchemaLike>;
90
106
  parameters?: Record<string, ParameterLike>;
91
107
  requestBodies?: Record<string, RequestBodyLike>;
@@ -95,7 +111,12 @@ const doc = (spec?.document ?? {}) as {
95
111
  security?: SecurityRequirementLike[];
96
112
  servers?: ServerLike[];
97
113
  };
98
- const pathItem = ref ? doc.paths?.[ref.path] : undefined;
114
+ const isWebhook = ref?.webhook === true;
115
+ const pathItem = ref
116
+ ? isWebhook
117
+ ? doc.webhooks?.[ref.path]
118
+ : doc.paths?.[ref.path]
119
+ : undefined;
99
120
  const operation = (
100
121
  pathItem && ref ? pathItem[ref.method] : undefined
101
122
  ) as FullOperation | undefined;
@@ -110,6 +131,10 @@ const params = mergeParameters(
110
131
  const requestBody = operation?.requestBody
111
132
  ? resolveComponentRef(operation.requestBody, components, "requestBodies")
112
133
  : undefined;
134
+ // A request body table leaves out `readOnly` fields. A webhook's or
135
+ // callback's body is a request the API sends, so it reads like a response
136
+ // (and like its payload example): `readOnly` fields in, `writeOnly` out.
137
+ const bodyDirection = isWebhook ? "response" : "request";
113
138
  const responses = Object.fromEntries(
114
139
  Object.entries(operation?.responses ?? {}).map(([status, response]) => [
115
140
  status,
@@ -118,14 +143,16 @@ const responses = Object.fromEntries(
118
143
  );
119
144
 
120
145
  const security = resolveSecurity(
121
- effectiveSecurity(operation?.security, doc.security),
146
+ effectiveSecurity(operation?.security, doc.security, isWebhook),
122
147
  doc.components?.securitySchemes
123
148
  );
124
149
 
125
150
  // One derived model feeds the static default sample, the playground form,
126
- // and (client-side) the live samples + fetch — they can never drift apart.
151
+ // and (client-side) the live samples + fetch — they can never drift apart. A
152
+ // webhook has none: the API sends it, so there is nothing for a reader to
153
+ // call, and its rail shows the payload instead.
127
154
  const model =
128
- ref && operation
155
+ ref && operation && !isWebhook
129
156
  ? operationModel({
130
157
  components,
131
158
  method: ref.method,
@@ -145,6 +172,70 @@ const model =
145
172
  : null;
146
173
  const sample = model ? buildRequest(model, defaultValues(model)) : null;
147
174
  const languages = sampleLanguages(spec?.codeSamples ?? []);
175
+ // The spec's own `x-codeSamples`, shown ahead of the generated ones.
176
+ const customSamples = operation ? customCodeSamples(operation) : [];
177
+
178
+ /** One request the API makes back to the reader, flattened for rendering. */
179
+ interface CallbackEntry {
180
+ expression: string;
181
+ key: string;
182
+ method: string;
183
+ name: string;
184
+ operation: FullOperation;
185
+ /** The callback path item's own parameters, which each method inherits. */
186
+ pathParameters?: ParameterLike[];
187
+ }
188
+
189
+ // Callbacks: for each named callback (resolved through
190
+ // `components.callbacks`), each URL expression, each method it declares.
191
+ const callbacks: CallbackEntry[] = Object.entries(
192
+ operation?.callbacks ?? {}
193
+ ).flatMap(([name, raw]) => {
194
+ const callback = resolveComponentRef(raw, components, "callbacks");
195
+ return Object.entries(callback).flatMap(([expression, item]) => {
196
+ if (expression === "$ref" || typeof item !== "object" || item === null) {
197
+ return [];
198
+ }
199
+ return HTTP_METHODS.flatMap((method) => {
200
+ const entry = item[method];
201
+ return typeof entry === "object" && entry !== null
202
+ ? [
203
+ {
204
+ expression,
205
+ key: `${name}-${expression}-${method}`,
206
+ method,
207
+ name,
208
+ // SAFETY: a path item's method entry is an operation object; the
209
+ // object check above guards a spec that put a scalar there.
210
+ operation: entry as FullOperation,
211
+ // SAFETY: the object check above passed, and a path item's
212
+ // `parameters` is a parameter list, which `mergeParameters`
213
+ // reads the way it reads an operation's.
214
+ pathParameters: (item as PathItemLike).parameters,
215
+ },
216
+ ]
217
+ : [];
218
+ });
219
+ });
220
+ });
221
+
222
+ /** A callback's parameters, request body, and responses, `$ref`s resolved. */
223
+ const callbackParts = (entry: CallbackEntry) => ({
224
+ parameters: mergeParameters(
225
+ entry.pathParameters,
226
+ entry.operation.parameters,
227
+ components
228
+ ),
229
+ requestBody: entry.operation.requestBody
230
+ ? resolveComponentRef(entry.operation.requestBody, components, "requestBodies")
231
+ : undefined,
232
+ responses: Object.fromEntries(
233
+ Object.entries(entry.operation.responses ?? {}).map(([status, response]) => [
234
+ status,
235
+ resolveComponentRef(response, components, "responses"),
236
+ ])
237
+ ),
238
+ });
148
239
  ---
149
240
  {
150
241
  isAsyncApi ? (
@@ -160,6 +251,11 @@ const languages = sampleLanguages(spec?.codeSamples ?? []);
160
251
  <code class="break-all font-mono text-foreground text-sm">
161
252
  {ref.path}
162
253
  </code>
254
+ {isWebhook && (
255
+ <span class="rounded-md border border-border px-1.5 py-0.5 font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide">
256
+ Webhook
257
+ </span>
258
+ )}
163
259
  {operation.deprecated && (
164
260
  <span class={DEPRECATED_LABEL_CLASS}>
165
261
  deprecated
@@ -172,6 +268,7 @@ const languages = sampleLanguages(spec?.codeSamples ?? []);
172
268
  <ParametersTable parameters={params} schemas={schemas} />
173
269
  {requestBody && (
174
270
  <RequestBody
271
+ direction={bodyDirection}
175
272
  expandAll={spec.expandSchemas}
176
273
  requestBody={requestBody}
177
274
  schemas={schemas}
@@ -184,7 +281,79 @@ const languages = sampleLanguages(spec?.codeSamples ?? []);
184
281
  schemas={schemas}
185
282
  />
186
283
  )}
284
+ {callbacks.length > 0 && (
285
+ <section class="mt-10">
286
+ <div
287
+ aria-level="2"
288
+ class="mb-3 font-semibold text-foreground text-lg"
289
+ role="heading"
290
+ >
291
+ Callbacks
292
+ </div>
293
+ <div class="flex flex-col gap-6">
294
+ {callbacks.map((entry) => {
295
+ const parts = callbackParts(entry);
296
+ return (
297
+ <div class="rounded-blume border border-border p-4">
298
+ <div
299
+ aria-level="3"
300
+ class="font-semibold text-foreground"
301
+ role="heading"
302
+ >
303
+ {entry.operation.summary || entry.name}
304
+ </div>
305
+ <div class="mt-2 flex flex-wrap items-center gap-2">
306
+ <MethodBadge method={entry.method} />
307
+ <code class="break-all font-mono text-muted-foreground text-xs">
308
+ {entry.expression}
309
+ </code>
310
+ </div>
311
+ {entry.operation.description && (
312
+ <Description
313
+ class="mt-2 text-muted-foreground text-sm"
314
+ text={entry.operation.description}
315
+ />
316
+ )}
317
+ <ParametersTable
318
+ level={4}
319
+ parameters={parts.parameters}
320
+ schemas={schemas}
321
+ />
322
+ {parts.requestBody && (
323
+ <RequestBody
324
+ direction="response"
325
+ expandAll={spec.expandSchemas}
326
+ level={4}
327
+ requestBody={parts.requestBody}
328
+ schemas={schemas}
329
+ />
330
+ )}
331
+ {entry.operation.responses && (
332
+ <Responses
333
+ expandAll={spec.expandSchemas}
334
+ level={4}
335
+ responses={parts.responses}
336
+ schemas={schemas}
337
+ />
338
+ )}
339
+ </div>
340
+ );
341
+ })}
342
+ </div>
343
+ </section>
344
+ )}
187
345
  </div>
346
+ {isWebhook && (requestBody?.content || operation.responses) && (
347
+ <OperationPanel>
348
+ <RequestPanel
349
+ components={components}
350
+ customSamples={customSamples}
351
+ payload={requestBody?.content}
352
+ responses={responses}
353
+ schemas={schemas}
354
+ />
355
+ </OperationPanel>
356
+ )}
188
357
  {sample && model && (
189
358
  <OperationPanel>
190
359
  {spec.playground.enabled && (
@@ -192,11 +361,13 @@ const languages = sampleLanguages(spec?.codeSamples ?? []);
192
361
  model={model}
193
362
  operation={id}
194
363
  proxy={spec.playground.proxy}
364
+ proxyConfig="openapi"
195
365
  slug={spec.slug}
196
366
  />
197
367
  )}
198
368
  <RequestPanel
199
369
  components={components}
370
+ customSamples={customSamples}
200
371
  languages={languages}
201
372
  responses={responses}
202
373
  sample={sample}