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
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+
3
+ import type { AdapterDescriptor } from "../core/adapter.ts";
4
+ import { adapterDescriptorSchema } from "../core/adapter.ts";
5
+
6
+ /** Options for {@link turnstile}. */
7
+ export interface TurnstileOptions {
8
+ /** The widget's site key, from the Cloudflare dashboard. */
9
+ siteKey: string;
10
+ }
11
+
12
+ export const turnstileOptionsSchema = z.strictObject({
13
+ siteKey: z.string().min(1),
14
+ });
15
+
16
+ export type TurnstileAdapter = AdapterDescriptor<"turnstile", TurnstileOptions>;
17
+
18
+ export const turnstileAdapterSchema = adapterDescriptorSchema(
19
+ "turnstile",
20
+ turnstileOptionsSchema
21
+ );
22
+
23
+ /** Where the assistant's route checks a Turnstile token. */
24
+ export const TURNSTILE_VERIFY_URL =
25
+ "https://challenges.cloudflare.com/turnstile/v0/siteverify";
26
+
27
+ /**
28
+ * Cloudflare Turnstile. The site key is public and goes to the browser; the
29
+ * secret key the route checks tokens with comes from `TURNSTILE_SECRET_KEY`.
30
+ * Set the widget to Invisible or Managed in the Cloudflare dashboard: most
31
+ * readers never see a challenge.
32
+ */
33
+ export const turnstile = (options: TurnstileOptions): TurnstileAdapter => ({
34
+ kind: "turnstile",
35
+ options,
36
+ requiredSecrets: ["TURNSTILE_SECRET_KEY"],
37
+ runtimeDeps: [],
38
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The server half of the bot check: the generated assistant route verifies
3
+ * the token the browser sent with the question against the provider before
4
+ * it calls the model. A failed or unreachable check refuses the question
5
+ * (and logs why), so a server that can't reach the provider shows up as a
6
+ * refused question rather than a silently unprotected route.
7
+ */
8
+ import { clientAddressOf } from "../core/client-address.ts";
9
+ import type { ClientContext } from "../core/client-address.ts";
10
+ import { HCAPTCHA_VERIFY_URL } from "./hcaptcha.ts";
11
+ import type { CaptchaAdapter } from "./schema.ts";
12
+ import { TURNSTILE_VERIFY_URL } from "./turnstile.ts";
13
+
14
+ /** The longest token either provider issues, with room to spare. */
15
+ const MAX_TOKEN_LENGTH = 4096;
16
+
17
+ /** What the provider answers about a token. */
18
+ interface VerifyReply {
19
+ "error-codes"?: string[];
20
+ success?: boolean;
21
+ }
22
+
23
+ /** What the route hands the check from its runtime. */
24
+ export interface VerifyOptions {
25
+ /** For tests; defaults to the global `fetch`. */
26
+ fetch?: typeof fetch;
27
+ /** The provider's secret key. */
28
+ secret: string;
29
+ }
30
+
31
+ /**
32
+ * Whether the provider confirms `token` came from a person on this site. The
33
+ * route passes the request body's `captcha` when it's a string.
34
+ */
35
+ export const verifyCaptcha = async (
36
+ adapter: CaptchaAdapter,
37
+ token: string | undefined,
38
+ context: ClientContext,
39
+ options: VerifyOptions
40
+ ): Promise<boolean> => {
41
+ if (!token || token.length > MAX_TOKEN_LENGTH) {
42
+ return false;
43
+ }
44
+ const form = new URLSearchParams({ response: token, secret: options.secret });
45
+ const address = clientAddressOf(context);
46
+ if (address) {
47
+ form.set("remoteip", address);
48
+ }
49
+ if (adapter.kind === "hcaptcha") {
50
+ form.set("sitekey", adapter.options.siteKey);
51
+ }
52
+ try {
53
+ const response = await (options.fetch ?? fetch)(
54
+ adapter.kind === "hcaptcha" ? HCAPTCHA_VERIFY_URL : TURNSTILE_VERIFY_URL,
55
+ { body: form, method: "POST" }
56
+ );
57
+ // SAFETY: both providers answer JSON with a `success` flag; anything
58
+ // else reads as a failure below.
59
+ const reply = (await response.json()) as VerifyReply;
60
+ if (reply.success === true) {
61
+ return true;
62
+ }
63
+ console.warn(
64
+ `The ${adapter.kind} check refused a question:`,
65
+ reply["error-codes"] ?? response.status
66
+ );
67
+ } catch (error) {
68
+ console.error(`The ${adapter.kind} check could not be reached:`, error);
69
+ }
70
+ return false;
71
+ };
@@ -62,6 +62,11 @@ export const commandMeta = {
62
62
  description: "Preview the last production build.",
63
63
  name: "preview",
64
64
  },
65
+ skill: {
66
+ description:
67
+ "Write the docs site's agent skill with a coding agent, in place of the generated one.",
68
+ name: "skill",
69
+ },
65
70
  sync: {
66
71
  description: "Re-fetch remote content sources and regenerate the runtime.",
67
72
  name: "sync",
@@ -14,6 +14,7 @@ import {
14
14
  import { BlumeError } from "../../core/diagnostics.ts";
15
15
  import { packageRoot } from "../../core/package-root.ts";
16
16
  import { scanProject } from "../../core/project-graph.ts";
17
+ import type { ResolvedConfig } from "../../core/schema.ts";
17
18
  import { serverFeatures } from "../../core/server-features.ts";
18
19
  import type { Diagnostic } from "../../core/types.ts";
19
20
  import { unregisteredSnapshotDiagnostics } from "../../core/version-cut.ts";
@@ -71,6 +72,25 @@ const supportedNodeRange = (): string => {
71
72
  }
72
73
  };
73
74
 
75
+ /** The adapters a project runs, one line per kind, for the doctor summary. */
76
+ const integrationSummary = (config: ResolvedConfig): string[] => {
77
+ const { assistant } = config.ai;
78
+ let assistantSummary = "off";
79
+ if (assistant?.enabled) {
80
+ assistantSummary = assistant.endpoint
81
+ ? "external endpoint"
82
+ : assistant.provider.kind;
83
+ }
84
+ return [
85
+ `References: ${config.reference.map((adapter) => adapter.kind).join(", ") || "none"}`,
86
+ `Analytics: ${config.analytics.map((adapter) => adapter.kind).join(", ") || "none"}`,
87
+ `Consent: ${config.consent?.kind ?? "none"}`,
88
+ `Rate limiting: ${config.rateLimit?.kind ?? "off"}`,
89
+ `Sources: ${config.content.sources.map((source) => source.kind).join(", ")}`,
90
+ `Assistant: ${assistantSummary}`,
91
+ ];
92
+ };
93
+
74
94
  export const doctorCommand = defineCommand({
75
95
  args: {
76
96
  json: {
@@ -144,23 +164,9 @@ export const doctorCommand = defineCommand({
144
164
  logger.info(`Output: ${config.deployment.options.output}`);
145
165
  logger.info(`Adapter: ${config.deployment.kind}`);
146
166
  logger.info(`Search: ${config.search.provider.kind}`);
147
- logger.info(
148
- `References: ${config.reference.map((adapter) => adapter.kind).join(", ") || "none"}`
149
- );
150
- logger.info(
151
- `Analytics: ${config.analytics.map((adapter) => adapter.kind).join(", ") || "none"}`
152
- );
153
- logger.info(
154
- `Sources: ${config.content.sources.map((source) => source.kind).join(", ")}`
155
- );
156
- const { assistant } = config.ai;
157
- let assistantSummary = "off";
158
- if (assistant?.enabled) {
159
- assistantSummary = assistant.endpoint
160
- ? "external endpoint"
161
- : assistant.provider.kind;
167
+ for (const line of integrationSummary(config)) {
168
+ logger.info(line);
162
169
  }
163
- logger.info(`Assistant: ${assistantSummary}`);
164
170
  }
165
171
  } catch (error) {
166
172
  if (error instanceof BlumeError) {
@@ -0,0 +1,96 @@
1
+ import { defineCommand } from "citty";
2
+ import { join } from "pathe";
3
+
4
+ import {
5
+ planSkill,
6
+ WRITE_SKILL_DIR,
7
+ writeSkillPrompt,
8
+ } from "../../ai/write-skill.ts";
9
+ import { AGENTS, launchInstalledAgent } from "../../audit/agent.ts";
10
+ import type { AgentKind } from "../../audit/agent.ts";
11
+ import { BlumeError } from "../../core/diagnostics.ts";
12
+ import { packageRoot } from "../../core/package-root.ts";
13
+ import { scanProject } from "../../core/project-graph.ts";
14
+ import { getBlumeVersion } from "../../core/version.ts";
15
+ import { commandMeta } from "../command-meta.ts";
16
+ import { reportInternalError } from "../internal-error.ts";
17
+ import { logger, reportDiagnostics } from "../log.ts";
18
+
19
+ /**
20
+ * `blume skill` — write the docs site's agent skill with a coding agent. Every
21
+ * build publishes a generated skill at `/skill.md` (a map of the docs); this
22
+ * opens Claude Code or Codex on the `blume-write-skill` skill bundled in this
23
+ * package to write the richer one, grounded in the docs, under the same name
24
+ * so it takes the generated one's place. The judgment stays in the skill; the
25
+ * command is the front door, like `blume migrate`. Status lines go straight to
26
+ * stderr: consola drops info-level lines in test and CI environments.
27
+ */
28
+ export const skillCommand = defineCommand({
29
+ args: {
30
+ claude: {
31
+ description: "Write the skill with Claude Code.",
32
+ type: "boolean",
33
+ },
34
+ codex: {
35
+ description: "Write the skill with Codex.",
36
+ type: "boolean",
37
+ },
38
+ },
39
+ meta: commandMeta.skill,
40
+ async run({ args }) {
41
+ const root = process.cwd();
42
+ // SAFETY: AGENTS is a closed record keyed by AgentKind, so its keys are
43
+ // exactly the agent kinds.
44
+ const agents = (Object.keys(AGENTS) as AgentKind[]).filter(
45
+ (kind) => args[kind]
46
+ );
47
+ if (agents.length > 1) {
48
+ logger.error("Pass at most one of --codex or --claude.");
49
+ process.exit(1);
50
+ }
51
+ const [agent] = agents;
52
+
53
+ try {
54
+ // A scan, not `prepareProject`: the agent reads the content tree and
55
+ // the runtime isn't touched, so a running dev server is left alone.
56
+ const plan = planSkill(await scanProject(root, { mode: "build" }));
57
+ const skillDir = join(packageRoot(), WRITE_SKILL_DIR);
58
+ process.stderr.write(
59
+ ` ${plan.exists ? "Updating" : "Writing"} the "${plan.name}" skill at ${plan.file}.\n`
60
+ );
61
+
62
+ // Without an agent flag the command is the pointer for any other agent:
63
+ // the bundled skill's path, and the install line that puts the skill
64
+ // where agents look for it.
65
+ if (!agent) {
66
+ process.stderr.write(
67
+ ` blume skill writes it with a coding agent: rerun with --codex or --claude.\n Using another agent? Point it at ${join(skillDir, "SKILL.md")},\n or install the skill where your agent looks for skills: npx skills add haydenbleasel/blume --skill blume-write-skill\n`
68
+ );
69
+ return;
70
+ }
71
+
72
+ const cli = AGENTS[agent];
73
+ process.stderr.write(` Handing it to ${cli.name}…\n\n`);
74
+ const code = await launchInstalledAgent(
75
+ cli.bin,
76
+ writeSkillPrompt(plan, { skillDir, version: getBlumeVersion() })
77
+ );
78
+ if (code === null) {
79
+ logger.error(
80
+ `${cli.name} (\`${cli.bin}\`) was not found on PATH. Install it with \`${cli.install}\`.`
81
+ );
82
+ process.exit(1);
83
+ }
84
+ if (code !== 0) {
85
+ process.exit(code);
86
+ }
87
+ } catch (error) {
88
+ if (error instanceof BlumeError) {
89
+ reportDiagnostics([error.diagnostic], root);
90
+ process.exit(1);
91
+ }
92
+ reportInternalError(error);
93
+ process.exit(1);
94
+ }
95
+ },
96
+ });
package/src/cli/index.ts CHANGED
@@ -81,6 +81,11 @@ const main = defineCommand({
81
81
  () => import("./commands/preview.ts"),
82
82
  "previewCommand"
83
83
  ),
84
+ skill: lazyCommand(
85
+ commandMeta.skill,
86
+ () => import("./commands/skill.ts"),
87
+ "skillCommand"
88
+ ),
84
89
  sync: lazyCommand(
85
90
  commandMeta.sync,
86
91
  () => import("./commands/sync.ts"),
@@ -2,6 +2,60 @@ import { resolveAskBackend } from "../ai/ask.ts";
2
2
  import type { ResolvedConfig } from "../core/schema.ts";
3
3
  import type { Diagnostic } from "../core/types.ts";
4
4
 
5
+ /** A feature, and the secrets the adapter behind it reads. */
6
+ interface SecretOwner {
7
+ feature: string;
8
+ note?: string;
9
+ secrets: string[];
10
+ }
11
+
12
+ /**
13
+ * The adapters that name their own secrets. The built-ins mostly declare
14
+ * none: analytics ships public tokens in the page, consent managers load
15
+ * public scripts, reference adapters read public specs, and host adapters
16
+ * read platform-injected env.
17
+ */
18
+ const adapterSecretOwners = (config: ResolvedConfig): SecretOwner[] => {
19
+ const { consent, deployment, rateLimit } = config;
20
+ const { provider } = config.search;
21
+ const owners: SecretOwner[] = [
22
+ // Each search adapter declares the secrets its generated runtime reads.
23
+ { feature: `Search (${provider.kind})`, secrets: provider.requiredSecrets },
24
+ ...config.analytics.map((adapter) => ({
25
+ feature: `Analytics (${adapter.kind})`,
26
+ secrets: adapter.requiredSecrets,
27
+ })),
28
+ // Each content source adapter declares the env vars its fetch reads.
29
+ ...config.content.sources.map((source) => ({
30
+ feature: `Content source (${source.kind})`,
31
+ secrets: source.requiredSecrets,
32
+ })),
33
+ ...config.reference.map((adapter) => ({
34
+ feature: `API reference (${adapter.kind})`,
35
+ secrets: adapter.requiredSecrets,
36
+ })),
37
+ {
38
+ feature: `Deployment (${deployment.kind})`,
39
+ secrets: deployment.requiredSecrets,
40
+ },
41
+ ];
42
+ if (consent) {
43
+ owners.push({
44
+ feature: `Consent (${consent.kind})`,
45
+ secrets: consent.requiredSecrets,
46
+ });
47
+ }
48
+ // Upstash's endpoint and token.
49
+ if (rateLimit) {
50
+ owners.push({
51
+ feature: `Rate limiting (${rateLimit.kind})`,
52
+ note: "read at request time; without it the routes count in memory",
53
+ secrets: rateLimit.requiredSecrets,
54
+ });
55
+ }
56
+ return owners;
57
+ };
58
+
5
59
  /**
6
60
  * Warn early when an enabled feature needs a secret env var that isn't set, so
7
61
  * the failure surfaces at `blume dev`/`build` instead of at the first request in
@@ -26,47 +80,35 @@ export const checkRequiredSecrets = (config: ResolvedConfig): Diagnostic[] => {
26
80
 
27
81
  if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
28
82
  // The adapter descriptor names the env vars its route reads.
29
- const { provider } = config.ai.assistant;
83
+ const { captcha, provider } = config.ai.assistant;
30
84
  const backend = resolveAskBackend(provider);
31
85
  for (const env of provider.requiredSecrets) {
32
86
  requireSecret(`Assistant (${backend.label})`, env, backend.secretNote);
33
87
  }
34
- }
35
-
36
- // Each search adapter declares the secrets its generated runtime reads.
37
- const { provider } = config.search;
38
- for (const env of provider.requiredSecrets) {
39
- requireSecret(`Search (${provider.kind})`, env);
40
- }
41
-
42
- // Adapters name their own secrets; the built-in analytics adapters ship
43
- // public tokens in the page and declare none.
44
- for (const adapter of config.analytics) {
45
- for (const env of adapter.requiredSecrets) {
46
- requireSecret(`Analytics (${adapter.kind})`, env);
88
+ // The bot check's secret key, which the route verifies tokens with.
89
+ for (const env of captcha?.requiredSecrets ?? []) {
90
+ requireSecret(`Assistant bot check (${captcha?.kind})`, env);
47
91
  }
48
92
  }
49
93
 
50
- // Each content source adapter declares the env vars its fetch reads.
51
- for (const source of config.content.sources) {
52
- for (const env of source.requiredSecrets) {
53
- requireSecret(`Content source (${source.kind})`, env);
94
+ // Generated narration reads its key at build, not at runtime; without it the
95
+ // build skips the audio and pages read with browser voices.
96
+ const { narration } = config;
97
+ if (narration.enabled && narration.provider) {
98
+ for (const env of narration.provider.requiredSecrets) {
99
+ requireSecret(
100
+ "Narration audio",
101
+ env,
102
+ "read at build; without it pages use browser voices"
103
+ );
54
104
  }
55
105
  }
56
106
 
57
- // Adapters name their own secrets; the built-in reference adapters read
58
- // public specs and declare none.
59
- for (const adapter of config.reference) {
60
- for (const env of adapter.requiredSecrets) {
61
- requireSecret(`API reference (${adapter.kind})`, env);
107
+ for (const owner of adapterSecretOwners(config)) {
108
+ for (const env of owner.secrets) {
109
+ requireSecret(owner.feature, env, owner.note);
62
110
  }
63
111
  }
64
112
 
65
- // The deployment adapter names its own secrets; the built-in host adapters
66
- // read platform-injected env and declare none.
67
- for (const env of config.deployment.requiredSecrets) {
68
- requireSecret(`Deployment (${config.deployment.kind})`, env);
69
- }
70
-
71
113
  return diagnostics;
72
114
  };
@@ -49,6 +49,7 @@ import TreeRoot from "./content/Tree.astro";
49
49
  import TreeFile from "./content/TreeFile.astro";
50
50
  import TreeFolder from "./content/TreeFolder.astro";
51
51
  import TypeTable from "./content/TypeTable.astro";
52
+ import View from "./content/View.astro";
52
53
  import Visibility from "./content/Visibility.astro";
53
54
  import YouTube from "./content/YouTube.astro";
54
55
 
@@ -96,6 +97,7 @@ const components = {
96
97
  Tooltip,
97
98
  Tree,
98
99
  TypeTable,
100
+ View,
99
101
  Visibility,
100
102
  YouTube,
101
103
  ...extra,
@@ -0,0 +1,18 @@
1
+ ---
2
+ // The method and path at the top of a page with `api` frontmatter, as an
3
+ // OpenAPI operation page shows them. `markdown/api-rail.ts` places it.
4
+ import MethodBadge from "../openapi/MethodBadge.astro";
5
+ import { readEndpointSpec } from "./api-page.ts";
6
+
7
+ interface Props {
8
+ /** The endpoint, as the page's plugin wrote it. */
9
+ spec: string;
10
+ }
11
+
12
+ const { method, target } = readEndpointSpec(Astro.props.spec);
13
+ ---
14
+
15
+ <div class="not-prose mb-6 flex flex-wrap items-center gap-3">
16
+ <MethodBadge method={method} />
17
+ <code class="break-all font-mono text-foreground text-sm">{target}</code>
18
+ </div>
@@ -0,0 +1,79 @@
1
+ ---
2
+ // The row behind `<ParamField>` and `<ResponseField>`: the name with its
3
+ // labels, location, type, and flags, the default, then the description from
4
+ // the slot, which may hold any MDX, nested fields in an `<Expandable>`
5
+ // included. Styled like the OpenAPI reference's rows, so a hand-written API
6
+ // page reads like a generated one. Narration skips it, as it does type tables.
7
+ import { defaultLabel, isOn } from "./api-field.ts";
8
+ import type { FieldValue, FlagProp } from "./api-field.ts";
9
+
10
+ interface Props {
11
+ default?: FieldValue;
12
+ deprecated?: FlagProp;
13
+ location?: string;
14
+ name?: string;
15
+ post?: string[];
16
+ pre?: string[];
17
+ required?: FlagProp;
18
+ type?: string;
19
+ }
20
+
21
+ const {
22
+ default: value,
23
+ deprecated,
24
+ location,
25
+ name,
26
+ post = [],
27
+ pre = [],
28
+ required,
29
+ type,
30
+ } = Astro.props;
31
+ const shownDefault = defaultLabel(value);
32
+ const labelClass =
33
+ "rounded bg-muted px-1.5 py-0.5 font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide";
34
+ ---
35
+
36
+ <div
37
+ class="border-border border-b py-4"
38
+ data-blume-field
39
+ data-blume-narration="skip"
40
+ >
41
+ <div class="not-prose flex flex-wrap items-baseline gap-x-2 gap-y-1">
42
+ {pre.map((label) => <span class={labelClass}>{label}</span>)}
43
+ {name && <code class="font-mono text-foreground text-sm">{name}</code>}
44
+ {post.map((label) => <span class={labelClass}>{label}</span>)}
45
+ {location && <span class={labelClass}>{location}</span>}
46
+ {type && <span class="text-muted-foreground text-xs">{type}</span>}
47
+ {
48
+ isOn(required) && (
49
+ <span class="font-medium text-[0.625rem] text-red-600 uppercase tracking-wide dark:text-red-400">
50
+ required
51
+ </span>
52
+ )
53
+ }
54
+ {
55
+ isOn(deprecated) && (
56
+ <span class="font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide line-through">
57
+ deprecated
58
+ </span>
59
+ )
60
+ }
61
+ </div>
62
+ {
63
+ shownDefault !== null && (
64
+ <div class="not-prose mt-2 text-muted-foreground text-xs">
65
+ Default:{" "}
66
+ <code class="rounded bg-muted px-1 py-0.5 text-foreground">
67
+ {shownDefault}
68
+ </code>
69
+ </div>
70
+ )
71
+ }
72
+ {
73
+ Astro.slots.has("default") && (
74
+ <div class="mt-2 text-muted-foreground text-sm [&>:first-child]:mt-0 [&>:last-child]:mb-0">
75
+ <slot />
76
+ </div>
77
+ )
78
+ }
79
+ </div>
@@ -0,0 +1,62 @@
1
+ ---
2
+ // A hand-written endpoint's Try it panel and request samples, at the head of
3
+ // its rail: the page's `<ParamField>`s become the parameters and body of an
4
+ // operation model like a spec'd operation's (see `api-page.ts`), so the
5
+ // playground and the samples are the OpenAPI reference's own. A page's own
6
+ // `<RequestExample>` stands in for the generated samples, and its
7
+ // `playground` frontmatter can drop the Try it panel (`simple`).
8
+ import data from "blume:data";
9
+
10
+ import { operationModel } from "../openapi/operation-model.ts";
11
+ import Playground from "../openapi/Playground.astro";
12
+ import { buildRequest, defaultValues } from "../openapi/request.ts";
13
+ import RequestPanel from "../openapi/RequestPanel.astro";
14
+ import { sampleLanguages } from "../openapi/snippets.ts";
15
+ import {
16
+ endpointBody,
17
+ endpointParameters,
18
+ endpointSecurity,
19
+ endpointTarget,
20
+ readEndpointSpec,
21
+ } from "./api-page.ts";
22
+
23
+ interface Props {
24
+ /** The endpoint, as the page's plugin wrote it. */
25
+ spec: string;
26
+ }
27
+
28
+ const spec = readEndpointSpec(Astro.props.spec);
29
+ const { api } = data.config;
30
+ const { path, servers } = endpointTarget(spec.target, api.server);
31
+ const model = operationModel({
32
+ method: spec.method,
33
+ parameters: endpointParameters(spec.fields),
34
+ path,
35
+ requestBody: endpointBody(spec.fields),
36
+ schemas: {},
37
+ security: endpointSecurity(spec.authMethod, api.auth),
38
+ servers,
39
+ });
40
+ const tryIt = spec.playground !== "simple" && api.playground.enabled;
41
+ ---
42
+
43
+ {
44
+ tryIt && (
45
+ <Playground
46
+ model={model}
47
+ operation={`${spec.method} ${spec.target}`}
48
+ proxy={api.playground.proxy}
49
+ slug="api-pages"
50
+ />
51
+ )
52
+ }
53
+ {
54
+ !spec.requestExample && (
55
+ <RequestPanel
56
+ languages={sampleLanguages([])}
57
+ responses={{}}
58
+ sample={buildRequest(model, defaultValues(model))}
59
+ schemas={{}}
60
+ />
61
+ )
62
+ }
@@ -0,0 +1,19 @@
1
+ ---
2
+ // The column `<RequestExample>` and `<ResponseExample>` pin to, which
3
+ // `markdown/api-rail.ts` gathers at the end of the page. At `xl` it sits
4
+ // beside the page's content, which the layout pads to make room, and sticks
5
+ // under the header like an OpenAPI operation's sample panel; below `xl` it
6
+ // follows the content.
7
+ import OperationPanel from "../openapi/OperationPanel.astro";
8
+ ---
9
+
10
+ <div
11
+ class="not-prose mt-10 xl:absolute xl:inset-y-0 xl:end-0 xl:mt-0 xl:w-[28rem]"
12
+ data-blume-api-rail
13
+ >
14
+ <OperationPanel>
15
+ <div class="flex flex-col gap-4 [&>*]:my-0">
16
+ <slot />
17
+ </div>
18
+ </OperationPanel>
19
+ </div>
@@ -10,6 +10,7 @@ const { variant = "compact" } = Astro.props;
10
10
  : "grid grid-cols-2 gap-x-5 gap-y-4 sm:grid-cols-4",
11
11
  ]}
12
12
  data-blume-color={variant}
13
+ data-blume-narration="skip"
13
14
  >
14
15
  <slot />
15
16
  </div>
@@ -3,6 +3,7 @@
3
3
 
4
4
  <div
5
5
  class="not-prose my-6 rounded-blume border border-border p-5 text-sm [&_ul]:m-0! [&_ul]:list-none [&_ul]:ps-4 [&>ul]:ps-0"
6
+ data-blume-narration="skip"
6
7
  >
7
8
  <slot />
8
9
  </div>
@@ -73,6 +73,7 @@ const { content: strings } = contentStrings(data, Astro.currentLocale);
73
73
  owner && repo && (
74
74
  <a
75
75
  class="not-prose my-6 flex flex-col gap-2 rounded-blume border border-border p-4 no-underline! transition-colors hover:border-foreground/30 hover:bg-muted/40"
76
+ data-blume-narration="skip"
76
77
  href={`${host}/${owner}/${repo}`}
77
78
  {...NEW_TAB_ATTRS}
78
79
  >
@@ -4,6 +4,7 @@ const { title } = Astro.props;
4
4
 
5
5
  <aside
6
6
  class="not-prose my-6 overflow-hidden rounded-blume border border-border/80 bg-background shadow-sm shadow-border/30"
7
+ data-blume-narration="skip"
7
8
  data-blume-panel
8
9
  data-blume-right-rail-item
9
10
  >
@@ -0,0 +1,25 @@
1
+ ---
2
+ // `<ParamField query="limit" type="integer" required>`: a request parameter,
3
+ // named by the attribute that says where it goes (`path`, `query`, `header`,
4
+ // or `body`), with its description in the slot. On a page with `api`
5
+ // frontmatter the fields also build that page's playground.
6
+ import ApiField from "./ApiField.astro";
7
+ import { paramField } from "./api-field.ts";
8
+ import type { ParamFieldProps } from "./api-field.ts";
9
+
10
+ type Props = ParamFieldProps;
11
+
12
+ const { location, name } = paramField(Astro.props);
13
+ const { default: value, deprecated, required, type } = Astro.props;
14
+ ---
15
+
16
+ <ApiField
17
+ default={value}
18
+ deprecated={deprecated}
19
+ location={location}
20
+ name={name}
21
+ required={required}
22
+ type={type}
23
+ >
24
+ <slot />
25
+ </ApiField>