blume 2.0.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (455) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
  12. package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
  13. package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
  14. package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
  15. package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
  16. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  17. package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
  18. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
  19. package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
  20. package/dist/cli/{chunk-2z928egk.js → chunk-5wj2qwn2.js} +5 -5
  21. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  22. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  23. package/dist/cli/{chunk-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
  24. package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
  25. package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
  26. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
  27. package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
  28. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  29. package/dist/cli/{chunk-b07cmahc.js → chunk-amrb6hgh.js} +2 -2
  30. package/dist/cli/chunk-cm89scdv.js +443 -0
  31. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  32. package/dist/cli/{chunk-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
  33. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  34. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  35. package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
  36. package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
  37. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  38. package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
  39. package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
  40. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  41. package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
  42. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
  43. package/dist/cli/chunk-kwrg63tm.js +31 -0
  44. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  45. package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
  46. package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
  47. package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
  48. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
  49. package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
  50. package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
  51. package/dist/cli/{chunk-ey84smr6.js.map → chunk-py62fewh.js.map} +1 -1
  52. package/dist/cli/chunk-q3wxxq39.js +126 -0
  53. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  54. package/dist/cli/{chunk-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
  55. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  56. package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
  57. package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
  58. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  59. package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
  60. package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
  61. package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
  62. package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
  63. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
  64. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  65. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  66. package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
  67. package/dist/cli/chunk-zf748avv.js.map +37 -0
  68. package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
  69. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
  70. package/dist/cli/index.js +26 -20
  71. package/dist/cli/index.js.map +5 -5
  72. package/dist/types/ai/agent-surface.d.ts +8 -7
  73. package/dist/types/ai/ai-catalog.d.ts +7 -0
  74. package/dist/types/ai/ask-context.d.ts +22 -0
  75. package/dist/types/ai/ask.d.ts +271 -58
  76. package/dist/types/ai/index.d.ts +2 -2
  77. package/dist/types/ai/site-skill.d.ts +26 -0
  78. package/dist/types/ai/skills.d.ts +16 -0
  79. package/dist/types/astro/integration.d.ts +8 -0
  80. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  81. package/dist/types/captcha/index.d.ts +28 -0
  82. package/dist/types/captcha/schema.d.ts +29 -0
  83. package/dist/types/captcha/turnstile.d.ts +28 -0
  84. package/dist/types/components/content/api-field.d.ts +59 -0
  85. package/dist/types/components/content/api-page.d.ts +95 -0
  86. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  87. package/dist/types/components/openapi/helpers.d.ts +137 -0
  88. package/dist/types/components/openapi/security.d.ts +85 -0
  89. package/dist/types/consent/ethyca.d.ts +48 -0
  90. package/dist/types/consent/index.d.ts +37 -0
  91. package/dist/types/consent/native.d.ts +28 -0
  92. package/dist/types/consent/osano.d.ts +42 -0
  93. package/dist/types/consent/schema.d.ts +110 -0
  94. package/dist/types/core/config-input.d.ts +187 -20
  95. package/dist/types/core/data.d.ts +49 -4
  96. package/dist/types/core/footer.d.ts +8 -0
  97. package/dist/types/core/i18n-ui.d.ts +86 -0
  98. package/dist/types/core/includes.d.ts +8 -2
  99. package/dist/types/core/localizable.d.ts +7 -0
  100. package/dist/types/core/page-modes.d.ts +29 -0
  101. package/dist/types/core/redirect-patterns.d.ts +144 -0
  102. package/dist/types/core/related.d.ts +40 -0
  103. package/dist/types/core/schema.d.ts +551 -56
  104. package/dist/types/core/sources/read.d.ts +5 -0
  105. package/dist/types/core/types.d.ts +1 -1
  106. package/dist/types/core/variables.d.ts +42 -0
  107. package/dist/types/deploy/node-headers.d.ts +13 -2
  108. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  109. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  110. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  111. package/dist/types/deploy/redirects.d.ts +39 -11
  112. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  113. package/dist/types/markdown/code-title.d.ts +17 -0
  114. package/dist/types/markdown/features.d.ts +3 -2
  115. package/dist/types/narration/build.d.ts +70 -0
  116. package/dist/types/narration/provider.d.ts +26 -0
  117. package/dist/types/narration/script.d.ts +154 -0
  118. package/dist/types/openapi/model.d.ts +12 -6
  119. package/dist/types/openapi/overlay.d.ts +54 -0
  120. package/dist/types/openapi/parse.d.ts +13 -0
  121. package/dist/types/openapi/references.d.ts +14 -4
  122. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  123. package/dist/types/ratelimit/index.d.ts +27 -0
  124. package/dist/types/ratelimit/memory.d.ts +36 -0
  125. package/dist/types/ratelimit/schema.d.ts +111 -0
  126. package/dist/types/ratelimit/upstash.d.ts +21 -0
  127. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  128. package/dist/types/reference/asyncapi.d.ts +10 -10
  129. package/dist/types/reference/graphql.d.ts +13 -10
  130. package/dist/types/reference/openapi.d.ts +33 -12
  131. package/dist/types/reference/options.d.ts +45 -5
  132. package/dist/types/reference/scalar.d.ts +13 -0
  133. package/dist/types/reference/schema.d.ts +60 -36
  134. package/dist/types/search/documents.d.ts +12 -1
  135. package/dist/types/search/orama-index.d.ts +4 -0
  136. package/dist/types/seo/metatags.d.ts +20 -0
  137. package/docs/02-deployment.mdx +41 -5
  138. package/docs/03-upgrading.mdx +4 -4
  139. package/docs/04-migrating.mdx +1 -1
  140. package/docs/advanced/changelog.mdx +2 -2
  141. package/docs/advanced/skills.mdx +14 -0
  142. package/docs/cli/audit.mdx +21 -1
  143. package/docs/cli/index.mdx +1 -0
  144. package/docs/configuration/analytics.mdx +3 -1
  145. package/docs/configuration/assistant.mdx +188 -36
  146. package/docs/configuration/consent.mdx +100 -0
  147. package/docs/configuration/customization.mdx +4 -4
  148. package/docs/configuration/index.mdx +63 -5
  149. package/docs/configuration/meta.ts +3 -0
  150. package/docs/configuration/narration.mdx +119 -0
  151. package/docs/configuration/rate-limiting.mdx +69 -0
  152. package/docs/configuration/search.mdx +29 -1
  153. package/docs/configuration/theming.mdx +3 -1
  154. package/docs/content/components.mdx +106 -0
  155. package/docs/content/frontmatter.mdx +76 -0
  156. package/docs/content/i18n.mdx +38 -3
  157. package/docs/content/includes.mdx +16 -0
  158. package/docs/content/meta.mdx +1 -0
  159. package/docs/content/meta.ts +1 -0
  160. package/docs/content/navigation.mdx +28 -6
  161. package/docs/content/sources/contentful.mdx +32 -0
  162. package/docs/content/sources/custom.mdx +39 -0
  163. package/docs/content/sources/github-releases.mdx +31 -0
  164. package/docs/content/sources/index.mdx +91 -0
  165. package/docs/content/sources/meta.ts +16 -0
  166. package/docs/content/sources/notion.mdx +32 -0
  167. package/docs/content/sources/obsidian.mdx +37 -0
  168. package/docs/content/sources/payload.mdx +30 -0
  169. package/docs/content/sources/remote-mdx.mdx +40 -0
  170. package/docs/content/sources/sanity.mdx +29 -0
  171. package/docs/content/sources/strapi.mdx +31 -0
  172. package/docs/content/syntax.mdx +67 -2
  173. package/docs/content/variables.mdx +41 -0
  174. package/docs/discoverability/agent-discovery.mdx +29 -2
  175. package/docs/discoverability/index.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/metadata.mdx +17 -0
  178. package/docs/references/api-pages.mdx +74 -0
  179. package/docs/references/asyncapi.mdx +1 -1
  180. package/docs/references/graphql.mdx +2 -2
  181. package/docs/references/meta.ts +1 -1
  182. package/docs/references/openapi.mdx +102 -3
  183. package/docs/references/scalar.mdx +3 -1
  184. package/package.json +36 -1
  185. package/skills/blume/SKILL.md +3 -0
  186. package/skills/blume-migrate/SKILL.md +5 -5
  187. package/skills/blume-migrate/references/docusaurus.md +3 -3
  188. package/skills/blume-migrate/references/mintlify.md +37 -19
  189. package/skills/blume-migrate/references/nextra.md +2 -2
  190. package/skills/blume-migrate/references/starlight.md +2 -1
  191. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  192. package/skills/blume-write-skill/SKILL.md +82 -0
  193. package/src/ai/agent-readability.ts +2 -1
  194. package/src/ai/agent-surface.ts +9 -7
  195. package/src/ai/ai-catalog.ts +6 -3
  196. package/src/ai/ask-context.ts +73 -20
  197. package/src/ai/ask-data.ts +6 -0
  198. package/src/ai/ask-tools.ts +142 -0
  199. package/src/ai/ask.ts +354 -76
  200. package/src/ai/component-markdown.ts +75 -0
  201. package/src/ai/cors.ts +2 -0
  202. package/src/ai/index.ts +12 -0
  203. package/src/ai/llms.ts +6 -2
  204. package/src/ai/markdown.ts +2 -1
  205. package/src/ai/mcp/data.ts +6 -0
  206. package/src/ai/openapi-components.ts +9 -3
  207. package/src/ai/site-skill.ts +274 -0
  208. package/src/ai/skills.ts +35 -2
  209. package/src/ai/write-skill.ts +97 -0
  210. package/src/astro/generate.ts +132 -18
  211. package/src/astro/index.ts +1 -0
  212. package/src/astro/integration.ts +35 -0
  213. package/src/astro/module-types.ts +6 -0
  214. package/src/astro/templates.ts +283 -30
  215. package/src/astro/variables.ts +31 -0
  216. package/src/audit/catalog.ts +19 -0
  217. package/src/audit/checks/contrast.ts +183 -0
  218. package/src/audit/checks/i18n.ts +2 -3
  219. package/src/audit/checks/indexability.ts +2 -3
  220. package/src/audit/checks/links.ts +5 -12
  221. package/src/audit/checks/sitemap.ts +2 -3
  222. package/src/audit/redirects.ts +46 -15
  223. package/src/audit/run.ts +2 -0
  224. package/src/audit/types.ts +2 -1
  225. package/src/blume-modules.d.ts +7 -0
  226. package/src/captcha/client.ts +152 -0
  227. package/src/captcha/hcaptcha.ts +36 -0
  228. package/src/captcha/index.ts +28 -0
  229. package/src/captcha/schema.ts +32 -0
  230. package/src/captcha/turnstile.ts +38 -0
  231. package/src/captcha/verify.ts +71 -0
  232. package/src/cli/command-meta.ts +5 -0
  233. package/src/cli/commands/doctor.ts +22 -16
  234. package/src/cli/commands/skill.ts +96 -0
  235. package/src/cli/index.ts +5 -0
  236. package/src/cli/required-secrets.ts +71 -29
  237. package/src/components/BlumePage.astro +2 -0
  238. package/src/components/content/ApiEndpoint.astro +18 -0
  239. package/src/components/content/ApiField.astro +79 -0
  240. package/src/components/content/ApiPlayground.astro +62 -0
  241. package/src/components/content/ApiRail.astro +19 -0
  242. package/src/components/content/Color.astro +1 -0
  243. package/src/components/content/FileTree.astro +1 -0
  244. package/src/components/content/GithubInfo.astro +1 -0
  245. package/src/components/content/Panel.astro +1 -0
  246. package/src/components/content/ParamField.astro +25 -0
  247. package/src/components/content/RequestExample.astro +17 -0
  248. package/src/components/content/ResponseExample.astro +17 -0
  249. package/src/components/content/ResponseField.astro +32 -0
  250. package/src/components/content/Step.astro +1 -0
  251. package/src/components/content/Tile.astro +1 -0
  252. package/src/components/content/Tooltip.astro +1 -0
  253. package/src/components/content/Tree.astro +1 -0
  254. package/src/components/content/TypeTable.astro +4 -1
  255. package/src/components/content/View.astro +21 -0
  256. package/src/components/content/ViewSwitcher.astro +168 -0
  257. package/src/components/content/api-field.ts +100 -0
  258. package/src/components/content/api-page.ts +224 -0
  259. package/src/components/content/view-switcher.ts +71 -0
  260. package/src/components/index.ts +2 -0
  261. package/src/components/islands/Assistant.astro +10 -1
  262. package/src/components/islands/assistant.tsx +104 -14
  263. package/src/components/islands/code-question.ts +62 -0
  264. package/src/components/islands/hooks.ts +110 -21
  265. package/src/components/islands/support-link.ts +68 -0
  266. package/src/components/layout/Analytics.astro +11 -2
  267. package/src/components/layout/Banner.astro +14 -10
  268. package/src/components/layout/Breadcrumbs.astro +6 -2
  269. package/src/components/layout/ConsentBanner.astro +62 -0
  270. package/src/components/layout/ConsentHead.astro +38 -0
  271. package/src/components/layout/Directory.astro +94 -0
  272. package/src/components/layout/DrawerSwitchers.astro +51 -0
  273. package/src/components/layout/Empty.astro +2 -2
  274. package/src/components/layout/Header.astro +122 -80
  275. package/src/components/layout/LanguageSwitcher.astro +48 -15
  276. package/src/components/layout/NarrationPlayer.astro +1085 -0
  277. package/src/components/layout/NavSelector.astro +33 -7
  278. package/src/components/layout/PageFeedback.astro +54 -49
  279. package/src/components/layout/PageLayout.astro +71 -3
  280. package/src/components/layout/ReferenceLayout.astro +17 -6
  281. package/src/components/layout/RelatedPages.astro +42 -0
  282. package/src/components/layout/RootLayout.astro +308 -45
  283. package/src/components/layout/Search.astro +31 -17
  284. package/src/components/layout/SiteFooter.astro +124 -0
  285. package/src/components/layout/SiteMetatags.astro +16 -0
  286. package/src/components/layout/consent/native.ts +79 -0
  287. package/src/components/layout/consent/types.ts +37 -0
  288. package/src/components/layout/footer-links.ts +53 -0
  289. package/src/components/layout/footer-socials.ts +87 -0
  290. package/src/components/layout/locale-redirect.ts +107 -0
  291. package/src/components/layout/nav-utils.ts +83 -0
  292. package/src/components/layout/page-feedback.ts +96 -0
  293. package/src/components/layout/search/analytics.ts +98 -0
  294. package/src/components/layout/search/flexsearch.ts +17 -5
  295. package/src/components/layout/search/orama-cloud.ts +26 -14
  296. package/src/components/layout/search/types.ts +20 -0
  297. package/src/components/layout/search/typesense.ts +5 -1
  298. package/src/components/openapi/Operation.astro +161 -13
  299. package/src/components/openapi/RequestBody.astro +6 -4
  300. package/src/components/openapi/RequestPanel.astro +59 -7
  301. package/src/components/openapi/Responses.astro +6 -4
  302. package/src/components/openapi/async-snippets.ts +4 -4
  303. package/src/components/openapi/code-samples.ts +97 -0
  304. package/src/components/openapi/security.ts +6 -3
  305. package/src/components/openapi/snippets.ts +869 -8
  306. package/src/components/props.ts +6 -0
  307. package/src/consent/client.ts +95 -0
  308. package/src/consent/clients.ts +19 -0
  309. package/src/consent/ethyca.ts +71 -0
  310. package/src/consent/head.ts +24 -0
  311. package/src/consent/index.ts +37 -0
  312. package/src/consent/init.ts +29 -0
  313. package/src/consent/native.ts +36 -0
  314. package/src/consent/osano.ts +59 -0
  315. package/src/consent/schema.ts +30 -0
  316. package/src/core/builtin-tags.ts +5 -0
  317. package/src/core/client-address.ts +19 -0
  318. package/src/core/config-input.ts +194 -20
  319. package/src/core/data.ts +44 -4
  320. package/src/core/diagnostics.ts +3 -2
  321. package/src/core/footer.ts +26 -0
  322. package/src/core/graph.ts +39 -36
  323. package/src/core/i18n-ui.ts +82 -2
  324. package/src/core/includes.ts +20 -3
  325. package/src/core/links.ts +16 -2
  326. package/src/core/localizable.ts +26 -0
  327. package/src/core/navigation.ts +43 -12
  328. package/src/core/page-modes.ts +56 -0
  329. package/src/core/project-graph.ts +111 -5
  330. package/src/core/redirect-patterns.ts +491 -0
  331. package/src/core/related.ts +131 -0
  332. package/src/core/schema.ts +318 -64
  333. package/src/core/sources/normalize.ts +2 -1
  334. package/src/core/sources/read.ts +8 -3
  335. package/src/core/types.ts +1 -1
  336. package/src/core/ui-packs/ar.ts +43 -0
  337. package/src/core/ui-packs/bg.ts +45 -0
  338. package/src/core/ui-packs/bn.ts +43 -0
  339. package/src/core/ui-packs/ca.ts +46 -0
  340. package/src/core/ui-packs/cs.ts +44 -0
  341. package/src/core/ui-packs/da.ts +45 -0
  342. package/src/core/ui-packs/de.ts +46 -0
  343. package/src/core/ui-packs/el.ts +45 -0
  344. package/src/core/ui-packs/es.ts +46 -0
  345. package/src/core/ui-packs/fa.ts +45 -0
  346. package/src/core/ui-packs/fi.ts +45 -0
  347. package/src/core/ui-packs/fr.ts +46 -0
  348. package/src/core/ui-packs/he.ts +43 -0
  349. package/src/core/ui-packs/hi.ts +44 -0
  350. package/src/core/ui-packs/hr.ts +45 -0
  351. package/src/core/ui-packs/hu.ts +44 -0
  352. package/src/core/ui-packs/id.ts +45 -0
  353. package/src/core/ui-packs/it.ts +45 -0
  354. package/src/core/ui-packs/ja.ts +45 -0
  355. package/src/core/ui-packs/ko.ts +44 -0
  356. package/src/core/ui-packs/nl.ts +46 -0
  357. package/src/core/ui-packs/no.ts +44 -0
  358. package/src/core/ui-packs/pl.ts +45 -0
  359. package/src/core/ui-packs/pt-br.ts +46 -0
  360. package/src/core/ui-packs/pt.ts +45 -0
  361. package/src/core/ui-packs/ro.ts +45 -0
  362. package/src/core/ui-packs/ru.ts +45 -0
  363. package/src/core/ui-packs/sk.ts +44 -0
  364. package/src/core/ui-packs/sr.ts +45 -0
  365. package/src/core/ui-packs/sv.ts +44 -0
  366. package/src/core/ui-packs/th.ts +43 -0
  367. package/src/core/ui-packs/tr.ts +45 -0
  368. package/src/core/ui-packs/uk.ts +45 -0
  369. package/src/core/ui-packs/vi.ts +45 -0
  370. package/src/core/ui-packs/zh-tw.ts +43 -0
  371. package/src/core/ui-packs/zh.ts +43 -0
  372. package/src/core/variables.ts +103 -0
  373. package/src/deploy/artifacts.ts +77 -8
  374. package/src/deploy/cloudflare-negotiation.ts +35 -5
  375. package/src/deploy/headers.ts +2 -1
  376. package/src/deploy/node-headers.ts +63 -9
  377. package/src/deploy/platforms/cloudflare.ts +32 -0
  378. package/src/deploy/platforms/netlify.ts +33 -1
  379. package/src/deploy/platforms/vercel.ts +37 -2
  380. package/src/deploy/redirects.ts +91 -24
  381. package/src/deploy/vercel-negotiation.ts +36 -0
  382. package/src/markdown/api-rail.ts +259 -0
  383. package/src/markdown/code-title.ts +35 -9
  384. package/src/markdown/features.ts +3 -2
  385. package/src/markdown/fence-meta.ts +2 -0
  386. package/src/markdown/include.ts +11 -1
  387. package/src/markdown/index.ts +28 -5
  388. package/src/markdown/math.ts +4 -4
  389. package/src/markdown/variables.ts +56 -0
  390. package/src/markdown/views.ts +82 -0
  391. package/src/narration/build.ts +412 -0
  392. package/src/narration/player.ts +249 -0
  393. package/src/narration/provider.ts +29 -0
  394. package/src/narration/script.ts +592 -0
  395. package/src/og/card.ts +7 -6
  396. package/src/openapi/model.ts +57 -24
  397. package/src/openapi/overlay.ts +256 -0
  398. package/src/openapi/parse.ts +55 -2
  399. package/src/openapi/references.ts +21 -5
  400. package/src/openapi/render-mdx.ts +5 -1
  401. package/src/openapi/scalar.ts +40 -6
  402. package/src/openapi/source.ts +1 -1
  403. package/src/ratelimit/cloudflare.ts +54 -0
  404. package/src/ratelimit/index.ts +30 -0
  405. package/src/ratelimit/memory.ts +45 -0
  406. package/src/ratelimit/runtime.ts +229 -0
  407. package/src/ratelimit/schema.ts +28 -0
  408. package/src/ratelimit/upstash.ts +30 -0
  409. package/src/ratelimit/wrangler.ts +70 -0
  410. package/src/reference/asyncapi.ts +2 -2
  411. package/src/reference/graphql.ts +5 -2
  412. package/src/reference/openapi.ts +21 -7
  413. package/src/reference/options.ts +51 -7
  414. package/src/reference/scalar.ts +33 -8
  415. package/src/registry/eject.ts +20 -4
  416. package/src/registry/registry.ts +7 -0
  417. package/src/search/documents.ts +26 -2
  418. package/src/search/orama-index.ts +28 -3
  419. package/src/search/pagefind-ranking.ts +39 -0
  420. package/src/search/sync/algolia.ts +15 -4
  421. package/src/search/sync/orama-cloud.ts +3 -0
  422. package/src/search/sync/typesense.ts +5 -0
  423. package/src/seo/metatags.ts +67 -0
  424. package/src/theme/chrome-icons.ts +1 -0
  425. package/src/theme/color.ts +500 -0
  426. package/src/theme/entry.ts +56 -3
  427. package/src/theme/palette.ts +121 -45
  428. package/src/translate/prompts.ts +1 -0
  429. package/src/translate/validate.ts +8 -2
  430. package/src/upgrade/upgrade.ts +4 -171
  431. package/dist/cli/chunk-364znk6q.js +0 -75
  432. package/dist/cli/chunk-364znk6q.js.map +0 -11
  433. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  434. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  435. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  436. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  437. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  438. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  439. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  440. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  441. package/docs/content/sources.mdx +0 -334
  442. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
  443. /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
  444. /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
  445. /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
  446. /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
  447. /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
  448. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
  449. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
  450. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
  451. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
  452. /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
  453. /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
  454. /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
  455. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
@@ -270,6 +270,89 @@ export const getPagination = (flat: FlatPage[], route: string) => {
270
270
  };
271
271
  };
272
272
 
273
+ /** A group node in the sidebar tree. */
274
+ export type NavGroup = Extract<NavNode, { kind: "group" }>;
275
+
276
+ /**
277
+ * The group whose own page `route` is, when that page lists the group's
278
+ * pages: its `directory` (inherited when the group sets none) is `card` or
279
+ * `accordion`. `null` on every other page.
280
+ */
281
+ export const directoryGroupForRoute = (
282
+ nodes: NavNode[],
283
+ route: string
284
+ ): NavGroup | null => {
285
+ for (const node of nodes) {
286
+ if (node.kind !== "group") {
287
+ continue;
288
+ }
289
+ if (
290
+ node.route === route &&
291
+ (node.directory === "card" || node.directory === "accordion")
292
+ ) {
293
+ return node;
294
+ }
295
+ const nested = directoryGroupForRoute(node.children, route);
296
+ if (nested) {
297
+ return nested;
298
+ }
299
+ }
300
+ return null;
301
+ };
302
+
303
+ /** One entry in a group page's directory: a page, or a subgroup and its pages. */
304
+ export interface DirectoryEntry {
305
+ description?: string;
306
+ /** The page, or the subgroup's own page (its first page when it has none). */
307
+ href: string;
308
+ icon?: string;
309
+ label: string;
310
+ /** A subgroup's pages in sidebar order, its own page first; absent for a page. */
311
+ pages?: FlatPage[];
312
+ }
313
+
314
+ /**
315
+ * What a group's page lists: the group's pages and subgroups, in sidebar
316
+ * order, without the page itself or external links. A subgroup is described
317
+ * by its own page, and one with no page to link to is left out.
318
+ */
319
+ export const directoryEntries = (group: NavGroup): DirectoryEntry[] =>
320
+ group.children.flatMap((child): DirectoryEntry[] => {
321
+ if (child.kind === "page") {
322
+ return child.pageId && child.route !== group.route
323
+ ? [
324
+ {
325
+ description: child.description,
326
+ href: child.route,
327
+ icon: child.icon,
328
+ label: child.label,
329
+ },
330
+ ]
331
+ : [];
332
+ }
333
+ const listed = flattenPages(child.children);
334
+ const pages =
335
+ child.route && !listed.some((page) => page.route === child.route)
336
+ ? [{ label: child.label, route: child.route }, ...listed]
337
+ : listed;
338
+ const [first] = pages;
339
+ if (!first) {
340
+ return [];
341
+ }
342
+ const own = child.children.find(
343
+ (node) => node.kind === "page" && node.route === child.route
344
+ );
345
+ return [
346
+ {
347
+ description: own?.kind === "page" ? own.description : undefined,
348
+ href: child.route ?? first.route,
349
+ icon: child.icon,
350
+ label: child.label,
351
+ pages,
352
+ },
353
+ ];
354
+ });
355
+
273
356
  /**
274
357
  * A stable id for every group in a sidebar — `g<n>` by pre-order position in
275
358
  * the full tree. The layout hands `NavTree` a scoped view of that tree (a
@@ -0,0 +1,96 @@
1
+ import { track } from "./analytics-client.ts";
2
+ /**
3
+ * The "Was this page helpful?" rating (`PageFeedback.astro`). A rating is
4
+ * sent as a `feedback` analytics event, the buttons give way to a thank-you,
5
+ * and, with `feedback.comments` on, a box below asks the reader to say more.
6
+ * A comment they send goes out as a `feedback_comment` event with the same
7
+ * rating, path, and title, so the two line up in a dashboard.
8
+ *
9
+ * Both go through analytics, so under a consent layer (`consent`) the widget
10
+ * renders hidden and only shows once the reader allows analytics: from
11
+ * anyone else, an answer would go nowhere.
12
+ *
13
+ * Runs on the initial load and again after every client-router swap, which
14
+ * rebuilds the widget from server-rendered markup, so the handlers always
15
+ * bind to the freshly swapped-in elements.
16
+ */
17
+ import type { BlumeConsent } from "./consent/types.ts";
18
+
19
+ /** The longest comment the box takes. */
20
+ export const COMMENT_MAX_LENGTH = 1000;
21
+
22
+ /**
23
+ * Whether an answer would reach analytics: the site has no consent layer, or
24
+ * the reader has allowed analytics.
25
+ */
26
+ const answersReachAnalytics = (): boolean => {
27
+ // SAFETY: the consent init script (`consent/init.ts`) is the only writer of
28
+ // `window.blumeConsent`; without a consent layer it's absent.
29
+ const { blumeConsent } = window as Window & { blumeConsent?: BlumeConsent };
30
+ return blumeConsent === undefined || blumeConsent.analytics === true;
31
+ };
32
+
33
+ /**
34
+ * Show the page's rating only while its answers would reach analytics. Runs
35
+ * when the widget is wired and again on every `blume:consent` change, so a
36
+ * reader who accepts the banner gets the rating right away.
37
+ */
38
+ export const syncFeedbackVisibility = (): void => {
39
+ const root = document.querySelector<HTMLElement>(
40
+ "[data-blume-page-feedback]"
41
+ );
42
+ if (root) {
43
+ root.hidden = !answersReachAnalytics();
44
+ }
45
+ };
46
+
47
+ /** Wire the page's rating, when it has one. */
48
+ export const initFeedback = (): void => {
49
+ const root = document.querySelector("[data-blume-page-feedback]");
50
+ if (!root) {
51
+ return;
52
+ }
53
+ syncFeedbackVisibility();
54
+ const actions = root.querySelector("[data-feedback-actions]");
55
+ const thanks = root.querySelector<HTMLElement>("[data-feedback-thanks]");
56
+ const form = root.querySelector<HTMLFormElement>("[data-feedback-form]");
57
+ let helpful = "";
58
+ const rate = (value: string) => {
59
+ helpful = value;
60
+ track("feedback", {
61
+ helpful,
62
+ path: location.pathname,
63
+ title: document.title,
64
+ });
65
+ actions?.classList.add("hidden");
66
+ thanks?.classList.remove("hidden");
67
+ if (form) {
68
+ form.hidden = false;
69
+ }
70
+ // The clicked button just disappeared under focus; hand focus to the
71
+ // (live) thanks line so the reader isn't dropped at the top of the page.
72
+ // The comment box is the next stop from there.
73
+ thanks?.focus();
74
+ };
75
+ for (const button of root.querySelectorAll<HTMLElement>(
76
+ "[data-feedback-value]"
77
+ )) {
78
+ button.addEventListener("click", () =>
79
+ rate(button.dataset.feedbackValue ?? "")
80
+ );
81
+ }
82
+ form?.addEventListener("submit", (event) => {
83
+ event.preventDefault();
84
+ const comment = form.querySelector("textarea")?.value.trim() ?? "";
85
+ if (comment) {
86
+ track("feedback_comment", {
87
+ comment: comment.slice(0, COMMENT_MAX_LENGTH),
88
+ helpful,
89
+ path: location.pathname,
90
+ title: document.title,
91
+ });
92
+ }
93
+ form.hidden = true;
94
+ thanks?.focus();
95
+ });
96
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Search analytics: what readers search for, what they find, and what they
3
+ * open, sent through the configured analytics adapters (`track`).
4
+ *
5
+ * The dialog searches on every keystroke, so a query is only recorded once it
6
+ * settles: a second without typing, or the reader picking a result or closing
7
+ * the dialog. Each settled query is one `search` event with the number of
8
+ * results, so a dashboard can list the queries that found nothing. Picking a
9
+ * result is a `search_select` event with its position, for click-through. The
10
+ * same query settling twice in a row (a filter toggled back and forth) counts
11
+ * once.
12
+ */
13
+ import type { TrackProps } from "../analytics-client.ts";
14
+
15
+ /** The longest query sent: past this it's rarely a search term. */
16
+ export const MAX_QUERY_CHARS = 100;
17
+
18
+ /** How long a query sits untouched before it counts as settled. */
19
+ export const SETTLE_MS = 1000;
20
+
21
+ /** Sends one analytics event. */
22
+ export type SendEvent = (event: string, props: TrackProps) => void;
23
+
24
+ /** The timer a tracker waits on; injectable for tests. */
25
+ export interface SettleTimer {
26
+ cancel: (handle: number) => void;
27
+ start: (callback: () => void, ms: number) => number;
28
+ }
29
+
30
+ /** What the dialog tells the tracker. */
31
+ export interface SearchTracker {
32
+ /** The dialog closed; record the query it was showing. */
33
+ closed: () => void;
34
+ /** The reader picked result `position` (1-based) for `query`. */
35
+ selected: (query: string, position: number, url: string) => void;
36
+ /** Results for `query` are on screen. */
37
+ settled: (query: string, results: number) => void;
38
+ }
39
+
40
+ /** A query and how many results it found. */
41
+ interface PendingSearch {
42
+ query: string;
43
+ results: number;
44
+ }
45
+
46
+ const browserTimer: SettleTimer = {
47
+ cancel: (handle) => clearTimeout(handle),
48
+ start: (callback, ms) => Number(setTimeout(callback, ms)),
49
+ };
50
+
51
+ /** A tracker for one search dialog. */
52
+ export const createSearchTracker = (
53
+ send: SendEvent,
54
+ timer: SettleTimer = browserTimer
55
+ ): SearchTracker => {
56
+ let pending: PendingSearch | undefined;
57
+ let handle: number | undefined;
58
+ let last: string | undefined;
59
+
60
+ const flush = (): void => {
61
+ if (handle !== undefined) {
62
+ timer.cancel(handle);
63
+ handle = undefined;
64
+ }
65
+ if (pending && pending.query !== last) {
66
+ send("search", {
67
+ path: location.pathname,
68
+ query: pending.query,
69
+ results: pending.results,
70
+ });
71
+ last = pending.query;
72
+ }
73
+ pending = undefined;
74
+ };
75
+
76
+ return {
77
+ closed: () => {
78
+ flush();
79
+ last = undefined;
80
+ },
81
+ selected: (query, position, url) => {
82
+ flush();
83
+ send("search_select", {
84
+ path: location.pathname,
85
+ position,
86
+ query: query.slice(0, MAX_QUERY_CHARS),
87
+ url,
88
+ });
89
+ },
90
+ settled: (query, results) => {
91
+ if (handle !== undefined) {
92
+ timer.cancel(handle);
93
+ }
94
+ pending = { query: query.slice(0, MAX_QUERY_CHARS), results };
95
+ handle = timer.start(flush, SETTLE_MS);
96
+ },
97
+ };
98
+ };
@@ -1,6 +1,6 @@
1
1
  import { Document } from "flexsearch";
2
2
 
3
- import { buildResult, RESULT_POOL } from "./types.ts";
3
+ import { buildResult, byBoost, RESULT_POOL } from "./types.ts";
4
4
  import type { IndexedDocument, SearchFn } from "./types.ts";
5
5
 
6
6
  /**
@@ -11,7 +11,7 @@ import type { IndexedDocument, SearchFn } from "./types.ts";
11
11
  type SearchDocument = Pick<
12
12
  IndexedDocument,
13
13
  "content" | "description" | "route" | "title"
14
- >;
14
+ > & { keywords: string };
15
15
 
16
16
  /**
17
17
  * FlexSearch: reuse the same static `blume-search.json` Orama ships, but build
@@ -29,11 +29,14 @@ export const createSearch = async (opts: {
29
29
  const byRoute = new Map(documents.map((doc) => [doc.route, doc]));
30
30
 
31
31
  const index = new Document<SearchDocument>({
32
- document: { id: "route", index: ["title", "description", "content"] },
32
+ document: {
33
+ id: "route",
34
+ index: ["title", "keywords", "description", "content"],
35
+ },
33
36
  tokenize: "forward",
34
37
  });
35
38
  for (const doc of documents) {
36
- index.add(doc);
39
+ index.add({ ...doc, keywords: doc.keywords?.join(" ") ?? "" });
37
40
  }
38
41
 
39
42
  // FlexSearch's in-memory search is synchronous, so the SearchFn resolves
@@ -63,6 +66,15 @@ export const createSearch = async (opts: {
63
66
  }
64
67
  }
65
68
  }
66
- return Promise.resolve(buildResult(matched, query, options?.section));
69
+ // FlexSearch returns an order, not scores: rank by position, then apply
70
+ // each page's `search.boost` to that.
71
+ const ranked = byBoost(
72
+ matched.map((doc, position) => ({
73
+ boost: doc.boost,
74
+ match: doc,
75
+ score: 1 / (position + 1),
76
+ }))
77
+ );
78
+ return Promise.resolve(buildResult(ranked, query, options?.section));
67
79
  };
68
80
  };
@@ -1,7 +1,13 @@
1
1
  import { OramaClient } from "@oramacloud/client";
2
2
 
3
3
  import type { OramaCloudOptions } from "../../../search/adapters/orama-cloud.ts";
4
- import { excerptFor, highlight, SEARCH_LIMIT } from "./types.ts";
4
+ import {
5
+ byBoost,
6
+ excerptFor,
7
+ highlight,
8
+ RESULT_POOL,
9
+ SEARCH_LIMIT,
10
+ } from "./types.ts";
5
11
  import type { SearchFn } from "./types.ts";
6
12
 
7
13
  interface OramaCloudRecord {
@@ -9,6 +15,7 @@ interface OramaCloudRecord {
9
15
  title: string;
10
16
  description?: string;
11
17
  content?: string;
18
+ boost?: number;
12
19
  }
13
20
 
14
21
  /**
@@ -25,25 +32,30 @@ export const createSearch = (opts: OramaCloudOptions): SearchFn => {
25
32
  endpoint,
26
33
  });
27
34
  return async (query, options) => {
35
+ // A pool beyond the visible limit, so each page's `search.boost` can
36
+ // lift a hit into view before the list is cut.
28
37
  const results = await client.search({
29
- limit: SEARCH_LIMIT,
38
+ limit: RESULT_POOL,
30
39
  term: query,
31
40
  // The sync carries `locale` on every record so an i18n site can scope
32
41
  // hosted results to the active language.
33
42
  ...(options?.locale && { where: { locale: options.locale } }),
34
43
  });
35
- const hits = (results?.hits ?? []).map((hit) => {
36
- const doc: OramaCloudRecord = hit.document;
37
- return {
38
- content: doc.content ?? "",
39
- excerpt: highlight(
40
- excerptFor(doc.description ?? "", doc.content ?? "", query),
41
- query
42
- ),
43
- title: highlight(doc.title, query),
44
- url: doc.url,
45
- };
46
- });
44
+ const ranked = byBoost(
45
+ (results?.hits ?? []).map((hit) => {
46
+ const doc: OramaCloudRecord = hit.document;
47
+ return { boost: doc.boost, match: doc, score: hit.score };
48
+ })
49
+ );
50
+ const hits = ranked.slice(0, SEARCH_LIMIT).map((doc) => ({
51
+ content: doc.content ?? "",
52
+ excerpt: highlight(
53
+ excerptFor(doc.description ?? "", doc.content ?? "", query),
54
+ query
55
+ ),
56
+ title: highlight(doc.title, query),
57
+ url: doc.url,
58
+ }));
47
59
  return { hits, sections: [] };
48
60
  };
49
61
  };
@@ -53,8 +53,28 @@ export interface IndexedDocument {
53
53
  section?: string;
54
54
  locale?: string;
55
55
  version?: string;
56
+ /** The page's relevance multiplier (`search.boost`). */
57
+ boost?: number;
58
+ /** Extra terms the page is found by (`search.keywords`). */
59
+ keywords?: string[];
56
60
  }
57
61
 
62
+ /** Something ranked with a relevance score, and the page it belongs to. */
63
+ export interface ScoredMatch<Match> {
64
+ boost?: number;
65
+ match: Match;
66
+ score: number;
67
+ }
68
+
69
+ /**
70
+ * Re-rank matches by score times their page's `search.boost`. The sort is
71
+ * stable, so equal scores keep the provider's order.
72
+ */
73
+ export const byBoost = <Match>(scored: ScoredMatch<Match>[]): Match[] =>
74
+ scored
75
+ .toSorted((a, b) => b.score * (b.boost ?? 1) - a.score * (a.boost ?? 1))
76
+ .map(({ match }) => match);
77
+
58
78
  /** Max results surfaced in the dialog. */
59
79
  export const SEARCH_LIMIT = 12;
60
80
 
@@ -34,7 +34,11 @@ export const createSearch = (opts: TypesenseOptions): SearchFn => {
34
34
  {
35
35
  per_page: SEARCH_LIMIT,
36
36
  q: query,
37
- query_by: "title,description,content",
37
+ query_by: "title,keywords,description,content",
38
+ // Relevance first, in ten bands, then each page's `search.boost`
39
+ // within a band: a boost lifts a page past similar matches without
40
+ // floating it over clearly better ones.
41
+ sort_by: "_text_match(buckets: 10):desc,boost:desc",
38
42
  // The sync marks `locale` and `version` as facets so hosted results
39
43
  // scope to the active language and the viewed docs version (the
40
44
  // current docs upload as "current").