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
@@ -0,0 +1,256 @@
1
+ import { exec, query } from "jsonpath-rfc9535";
2
+ import { z } from "zod";
3
+
4
+ import type { JsonValue } from "../core/adapter.ts";
5
+
6
+ /**
7
+ * OpenAPI Overlays: the OpenAPI Initiative's format for changing an API
8
+ * description without editing it — an ordered list of actions, each an RFC
9
+ * 9535 JSONPath `target` with an `update` to merge in, a `copy` of another
10
+ * node to merge in, or `remove`. A spec generated by another tool or owned by
11
+ * another team can then carry docs-only changes (better descriptions, hidden
12
+ * internal operations, a public server URL) in a file of its own.
13
+ *
14
+ * Implements Overlay Specification 1.0 and 1.1 over `jsonpath-rfc9535`, which
15
+ * passes the JSONPath compliance suite in full, as the spec requires. The
16
+ * documents here are already parsed; `parse.ts` reads the files.
17
+ */
18
+
19
+ /** An overlay document or action that doesn't follow the spec. */
20
+ export class OverlayError extends Error {
21
+ constructor(message: string) {
22
+ super(message);
23
+ this.name = "OverlayError";
24
+ }
25
+ }
26
+
27
+ /** An object node, the only kind that holds named properties. */
28
+ interface JsonObject {
29
+ [key: string]: JsonValue;
30
+ }
31
+
32
+ /** A node that holds other nodes: where a match's parent can be. */
33
+ type JsonContainer = JsonObject | JsonValue[];
34
+
35
+ const isObject = (value: JsonValue | undefined): value is JsonObject =>
36
+ typeof value === "object" && value !== null && !Array.isArray(value);
37
+
38
+ /**
39
+ * A parsed YAML or JSON mapping, as the document an overlay acts on: the
40
+ * parser's own types say nothing of JSON, but what it parses is JSON.
41
+ */
42
+ export const isJsonObject = <Value>(
43
+ value: Value
44
+ ): value is Value & JsonObject =>
45
+ typeof value === "object" && value !== null && !Array.isArray(value);
46
+
47
+ const isContainer = (value: JsonValue | undefined): value is JsonContainer =>
48
+ typeof value === "object" && value !== null;
49
+
50
+ const isIndex = (key: string | number | undefined): key is number =>
51
+ typeof key === "number";
52
+
53
+ /** An object, an array, or anything else: what update merging keys on. */
54
+ type MergeKind = "array" | "object" | "primitive";
55
+
56
+ const mergeKindOf = (value: JsonValue): MergeKind => {
57
+ if (Array.isArray(value)) {
58
+ return "array";
59
+ }
60
+ return isObject(value) ? "object" : "primitive";
61
+ };
62
+
63
+ /** A merge kind in a sentence. */
64
+ const MERGE_KIND_PHRASE: Record<MergeKind, string> = {
65
+ array: "an array",
66
+ object: "an object",
67
+ primitive: "a primitive value",
68
+ };
69
+
70
+ const actionSchema = z.looseObject({
71
+ /** A JSONPath expression selecting one node to merge into the targets (1.1). */
72
+ copy: z.string().optional(),
73
+ description: z.string().optional(),
74
+ /** Remove every target from the map or array it's in. */
75
+ remove: z.boolean().default(false),
76
+ /** An RFC 9535 JSONPath expression selecting the nodes to act on. */
77
+ target: z.string().min(1),
78
+ /** A value merged into every target. */
79
+ update: z.json().optional(),
80
+ });
81
+
82
+ /** A validated overlay action. */
83
+ type OverlayAction = z.output<typeof actionSchema>;
84
+
85
+ const overlaySchema = z.looseObject({
86
+ actions: z.array(actionSchema).min(1),
87
+ extends: z.string().optional(),
88
+ info: z.looseObject({ title: z.string(), version: z.string() }),
89
+ overlay: z.string().regex(/^1\.[01]\.\d+$/u, {
90
+ message: "must be an Overlay Specification version, 1.0.x or 1.1.x",
91
+ }),
92
+ });
93
+
94
+ /** A validated overlay document. */
95
+ export type OverlayDocument = z.output<typeof overlaySchema>;
96
+
97
+ /** Validate a parsed overlay document, naming the file in any error. */
98
+ export const overlayDocument = <Value>(
99
+ value: Value,
100
+ name: string
101
+ ): OverlayDocument => {
102
+ const parsed = overlaySchema.safeParse(value);
103
+ if (parsed.success) {
104
+ return parsed.data;
105
+ }
106
+ const [issue] = parsed.error.issues;
107
+ const at = issue?.path.length ? `\`${issue.path.join(".")}\` ` : "";
108
+ throw new OverlayError(
109
+ `${name} is not an overlay document: ${at}${issue?.message ?? "invalid"}.`
110
+ );
111
+ };
112
+
113
+ /** A matched node, where it sits, and how to reach it. */
114
+ interface Match {
115
+ key?: string | number;
116
+ parent?: JsonContainer;
117
+ value: JsonValue;
118
+ }
119
+
120
+ /** The nodes `expression` selects, each with its parent (none for the root). */
121
+ const select = (document: JsonValue, expression: string): Match[] => {
122
+ const matches: Match[] = [];
123
+ exec(document, expression, (value, path) => {
124
+ let parent: JsonValue = document;
125
+ for (const key of path.slice(0, -1)) {
126
+ // SAFETY: the path is one the query walked, so every step is a key of
127
+ // the container before it.
128
+ parent = (parent as Record<string | number, JsonValue>)[key] ?? null;
129
+ }
130
+ const key = path.at(-1);
131
+ matches.push(
132
+ key === undefined || !isContainer(parent)
133
+ ? { value }
134
+ : { key, parent, value }
135
+ );
136
+ });
137
+ return matches;
138
+ };
139
+
140
+ /**
141
+ * Merge `patch` into `target` as the spec says: objects recursively, arrays
142
+ * by concatenation, a primitive replaced by a primitive. Returns the merged
143
+ * value; any other pairing throws.
144
+ */
145
+ const merge = (target: JsonValue, patch: JsonValue, at: string): JsonValue => {
146
+ if (isObject(target) && isObject(patch)) {
147
+ for (const [key, value] of Object.entries(patch)) {
148
+ // Only recurse into the target's own keys, so a `__proto__` or
149
+ // `constructor` key can't reach a prototype.
150
+ target[key] = Object.hasOwn(target, key)
151
+ ? merge(target[key] ?? null, value, `${at}.${key}`)
152
+ : structuredClone(value);
153
+ }
154
+ return target;
155
+ }
156
+ if (Array.isArray(target) && Array.isArray(patch)) {
157
+ target.push(...structuredClone(patch));
158
+ return target;
159
+ }
160
+ if (
161
+ mergeKindOf(target) === "primitive" &&
162
+ mergeKindOf(patch) === "primitive"
163
+ ) {
164
+ return patch;
165
+ }
166
+ throw new OverlayError(
167
+ `can't merge ${MERGE_KIND_PHRASE[mergeKindOf(patch)]} into ${MERGE_KIND_PHRASE[mergeKindOf(target)]} at ${at}`
168
+ );
169
+ };
170
+
171
+ /** Remove each match from its container, later siblings first. */
172
+ const removeAll = (matches: Match[]): void => {
173
+ for (const { key, parent } of matches.toReversed()) {
174
+ if (Array.isArray(parent) && isIndex(key)) {
175
+ parent.splice(key, 1);
176
+ } else if (isObject(parent) && key !== undefined && !isIndex(key)) {
177
+ // oxlint-disable-next-line typescript/no-dynamic-delete -- an overlay target's key is only known at run time
178
+ delete parent[key];
179
+ } else {
180
+ throw new OverlayError("can't remove the document itself");
181
+ }
182
+ }
183
+ };
184
+
185
+ /**
186
+ * Merge `patch` into every match, which must all be objects, all arrays, or
187
+ * all primitives. A primitive match is replaced in its parent.
188
+ */
189
+ const mergeAll = (matches: Match[], patch: JsonValue, target: string) => {
190
+ const kinds = new Set(matches.map((match) => mergeKindOf(match.value)));
191
+ if (kinds.size > 1) {
192
+ throw new OverlayError(
193
+ `its target selects a mix of ${[...kinds].toSorted().join(", ")} values, and an update needs all one kind`
194
+ );
195
+ }
196
+ for (const { key, parent, value } of matches) {
197
+ const merged = merge(value, patch, target);
198
+ if (merged !== value && parent !== undefined && key !== undefined) {
199
+ // SAFETY: `key` came from walking `parent`, so it indexes it.
200
+ (parent as Record<string | number, JsonValue>)[key] = merged;
201
+ }
202
+ }
203
+ };
204
+
205
+ /** The single node a `copy` expression selects, cloned. */
206
+ const copied = (document: JsonValue, expression: string): JsonValue => {
207
+ const values = query(document, expression);
208
+ const [value] = values;
209
+ if (values.length !== 1 || value === undefined) {
210
+ throw new OverlayError(
211
+ `its copy expression "${expression}" selects ${values.length} nodes, and copy needs exactly one`
212
+ );
213
+ }
214
+ return structuredClone(value);
215
+ };
216
+
217
+ const applyAction = (document: JsonValue, action: OverlayAction): void => {
218
+ const matches = select(document, action.target);
219
+ if (matches.length === 0) {
220
+ return;
221
+ }
222
+ if (action.remove) {
223
+ removeAll(matches);
224
+ return;
225
+ }
226
+ const patch =
227
+ action.update ??
228
+ (action.copy === undefined ? undefined : copied(document, action.copy));
229
+ if (patch !== undefined) {
230
+ mergeAll(matches, patch, action.target);
231
+ }
232
+ };
233
+
234
+ /**
235
+ * Apply an overlay to a parsed document, in place and in action order: each
236
+ * action sees the result of the one before. A target that matches nothing
237
+ * changes nothing. Throws an {@link OverlayError} naming the overlay file and
238
+ * the action that failed.
239
+ */
240
+ export const applyOverlay = (
241
+ document: JsonValue,
242
+ overlay: OverlayDocument,
243
+ name: string
244
+ ): void => {
245
+ for (const [index, action] of overlay.actions.entries()) {
246
+ try {
247
+ applyAction(document, action);
248
+ } catch (error) {
249
+ // SAFETY: the JSONPath parser and this module throw Error instances.
250
+ const { message } = error as Error;
251
+ throw new OverlayError(
252
+ `${name} action ${index + 1} (${action.target}): ${message}`
253
+ );
254
+ }
255
+ }
256
+ };
@@ -4,7 +4,7 @@ import { setTimeout as sleep } from "node:timers/promises";
4
4
  import type * as ConverterModule from "@asyncapi/converter";
5
5
  import { normalize, upgrade } from "@scalar/openapi-parser";
6
6
  import pRetry, { AbortError } from "p-retry";
7
- import { isAbsolute, join } from "pathe";
7
+ import { dirname, isAbsolute, join, resolve } from "pathe";
8
8
  import type * as UndiciModule from "undici";
9
9
 
10
10
  import {
@@ -15,15 +15,21 @@ import { nodeRequire } from "../core/node-require.ts";
15
15
  import { hashText } from "../core/sources/cache.ts";
16
16
  import type { AsyncApiDocument } from "./asyncapi.ts";
17
17
  import { normalizeAsyncApiDocument } from "./asyncapi.ts";
18
+ import { operationLabel } from "./checks.ts";
19
+ import type { SpecIssue } from "./checks.ts";
18
20
  import { buildGraphqlDocument } from "./graphql-build.ts";
19
21
  import type { GraphqlDocument } from "./graphql.ts";
22
+ import { HTTP_METHODS } from "./model.ts";
20
23
  import type { ApiDocument } from "./model.ts";
24
+ import { applyOverlay, isJsonObject, overlayDocument } from "./overlay.ts";
21
25
  import { SpecDependencyError } from "./spec-dependency-error.ts";
22
26
 
23
27
  /**
24
28
  * Spec loading and normalization. Blume reuses Scalar's parser
25
29
  * (`@scalar/openapi-parser`) to read a spec (YAML or JSON), then upgrade Swagger
26
30
  * 2.0 / OpenAPI 3.0 documents to 3.1 so the renderer only handles one shape.
31
+ * OpenAPI 3.2 is backward compatible with 3.1, so a 3.2 document is read as
32
+ * written.
27
33
  * Internal `$ref`s are deliberately left in place (see `model.ts`).
28
34
  *
29
35
  * Remote (`http(s)`) specs are fetched defensively — bounded per attempt, retried
@@ -57,6 +63,8 @@ const PROXY_ENV_VARS = [
57
63
 
58
64
  export interface ParsedSpec {
59
65
  document: ApiDocument;
66
+ /** `x-codeSamples` entries whose `$ref` couldn't be inlined. */
67
+ issues: SpecIssue[];
60
68
  warnings: string[];
61
69
  }
62
70
 
@@ -75,6 +83,11 @@ export class InvalidSpecError extends Error {
75
83
 
76
84
  /** Where and whether to cache a remote spec's text between runs. */
77
85
  export interface SpecFetchOptions {
86
+ /**
87
+ * Overlay documents (local paths or `http(s)` URLs) applied in order to
88
+ * the parsed spec, before it's upgraded. See `overlay.ts`.
89
+ */
90
+ overlays?: readonly string[];
78
91
  /** Dir for a last-good on-disk copy of a remote spec (offline fallback). */
79
92
  cacheDir?: string;
80
93
  /**
@@ -310,13 +323,177 @@ const readSpecText = async (
310
323
  const isApiDocument = <Value>(value: Value): value is Value & ApiDocument =>
311
324
  typeof value === "object" && value !== null;
312
325
 
326
+ /**
327
+ * Read a spec and apply its overlays, in order, to the parsed document as
328
+ * written (before any upgrade: an overlay targets its spec's own version).
329
+ * The overlays are read like the spec, local or remote, cached alike.
330
+ */
331
+ const readOverlaid = async (
332
+ spec: string,
333
+ root: string,
334
+ options: SpecFetchOptions
335
+ ): Promise<{ document: ReturnType<typeof normalize>; warnings: string[] }> => {
336
+ const [read, ...overlays] = await Promise.all(
337
+ [spec, ...(options.overlays ?? [])].map((path) =>
338
+ readSpecText(path, root, options)
339
+ )
340
+ );
341
+ const document = normalize(read?.text ?? "");
342
+ for (const [index, overlay] of overlays.entries()) {
343
+ const name = options.overlays?.[index] ?? "";
344
+ const parsed = overlayDocument(normalize(overlay.text), name);
345
+ // A spec that isn't a mapping has nothing to overlay; the caller
346
+ // rejects it as it would without overlays.
347
+ if (isJsonObject(document)) {
348
+ applyOverlay(document, parsed, name);
349
+ }
350
+ }
351
+ return {
352
+ document,
353
+ warnings: [read, ...overlays].flatMap((entry) => entry?.warnings ?? []),
354
+ };
355
+ };
356
+
357
+ /**
358
+ * A spec with its overlays applied, as JSON text: what the Scalar embed
359
+ * inlines, since it can't apply overlays itself.
360
+ */
361
+ export const readOverlaidSpec = async (
362
+ spec: string,
363
+ root: string,
364
+ options: SpecFetchOptions
365
+ ): Promise<{ text: string; warnings: string[] }> => {
366
+ const { document, warnings } = await readOverlaid(spec, root, options);
367
+ return { text: JSON.stringify(document), warnings };
368
+ };
369
+
370
+ /** A `$ref` in place of an `x-codeSamples` entry's `source`. */
371
+ interface SampleRef {
372
+ $ref: string;
373
+ }
374
+
375
+ /** An `x-codeSamples` entry whose `source` is a `$ref` rather than the code. */
376
+ const isRefSample = (value: unknown): value is { source: SampleRef } =>
377
+ isJsonObject(value) &&
378
+ isJsonObject(value.source) &&
379
+ typeof value.source.$ref === "string";
380
+
381
+ /**
382
+ * Where a code sample's `$ref` points, in the form `readSpecText` reads: a
383
+ * URL, or a path resolved against the spec's own location. `undefined` for
384
+ * a JSON pointer (`#/…`, or a file with a fragment), which names a spot
385
+ * inside a document rather than a file of code.
386
+ */
387
+ const sampleTarget = (
388
+ ref: string,
389
+ spec: string,
390
+ root: string
391
+ ): string | undefined => {
392
+ if (ref.includes("#")) {
393
+ return undefined;
394
+ }
395
+ if (URL_SPEC.test(ref)) {
396
+ return ref;
397
+ }
398
+ if (URL_SPEC.test(spec)) {
399
+ return new URL(ref, spec).href;
400
+ }
401
+ return resolve(dirname(isAbsolute(spec) ? spec : join(root, spec)), ref);
402
+ };
403
+
404
+ /**
405
+ * Inline each `x-codeSamples` entry whose `source` is a `$ref` to a file of
406
+ * its own, as Redocly's docs describe (`source: { $ref: ../code_samples/… }`):
407
+ * `redocly bundle` inlines those, and a spec read as written doesn't. Each
408
+ * file is read relative to the spec,
409
+ * the way the spec itself was (local, or fetched and cached), so the sample
410
+ * renders like a written-out one. An entry that can't be read is left out of
411
+ * the page, with a warning, rather than dropped without a sign.
412
+ */
413
+ const inlineSampleRefs = async (
414
+ document: ApiDocument,
415
+ spec: string,
416
+ root: string,
417
+ options: SpecFetchOptions
418
+ ): Promise<SpecIssue[]> => {
419
+ const reads: Promise<SpecIssue | undefined>[] = [];
420
+ const inline = async (
421
+ entry: { source: SampleRef | string },
422
+ ref: string,
423
+ label: string
424
+ ): Promise<SpecIssue | undefined> => {
425
+ const target = sampleTarget(ref, spec, root);
426
+ if (target === undefined) {
427
+ return {
428
+ code: "BLUME_OPENAPI_CODE_SAMPLE_REF",
429
+ message: `${label} has an \`x-codeSamples\` entry whose \`source\` is a $ref to "${ref}", a spot inside a document, which Blume doesn't resolve, so the sample is left out.`,
430
+ suggestion:
431
+ "Point the `$ref` at a file that holds only the sample's code, or write the code out in `source`.",
432
+ };
433
+ }
434
+ try {
435
+ const { text } = await readSpecText(target, root, options);
436
+ entry.source = text;
437
+ return undefined;
438
+ } catch (error) {
439
+ // SAFETY: a failed read throws a Node error, and a failed fetch an
440
+ // Error (`attemptFetch` wraps anything else); only the message is shown.
441
+ const { message } = error as Error;
442
+ return {
443
+ code: "BLUME_OPENAPI_CODE_SAMPLE_REF",
444
+ message: `${label} has an \`x-codeSamples\` entry whose \`source\` is a $ref to "${ref}", which couldn't be read (${message}), so the sample is left out.`,
445
+ suggestion:
446
+ "Check the path, which is relative to the spec, or write the code out in `source`.",
447
+ };
448
+ }
449
+ };
450
+ const visit = (
451
+ name: string,
452
+ item: NonNullable<ApiDocument["paths"]>[string] | undefined,
453
+ webhook: boolean
454
+ ): void => {
455
+ if (!isJsonObject(item) || "$ref" in item) {
456
+ return;
457
+ }
458
+ for (const method of HTTP_METHODS) {
459
+ const operation = item[method];
460
+ // The spelling `customCodeSamples` renders: the current one wins.
461
+ const samples = isJsonObject(operation)
462
+ ? (operation["x-codeSamples"] ?? operation["x-code-samples"])
463
+ : undefined;
464
+ for (const entry of Array.isArray(samples) ? samples : []) {
465
+ if (isRefSample(entry)) {
466
+ reads.push(
467
+ inline(
468
+ entry,
469
+ entry.source.$ref,
470
+ operationLabel(name, method, webhook)
471
+ )
472
+ );
473
+ }
474
+ }
475
+ }
476
+ };
477
+ for (const [path, item] of Object.entries(document.paths ?? {})) {
478
+ visit(path, item, false);
479
+ }
480
+ for (const [name, item] of Object.entries(document.webhooks ?? {})) {
481
+ visit(name, item, true);
482
+ }
483
+ const issues = await Promise.all(reads);
484
+ return issues.filter((issue) => issue !== undefined);
485
+ };
486
+
313
487
  export const parseSpec = async (
314
488
  spec: string,
315
489
  root: string,
316
490
  options: SpecFetchOptions = {}
317
491
  ): Promise<ParsedSpec> => {
318
- const { text, warnings } = await readSpecText(spec, root, options);
319
- const normalized = normalize(text);
492
+ const { document: normalized, warnings } = await readOverlaid(
493
+ spec,
494
+ root,
495
+ options
496
+ );
320
497
  const { specification } = upgrade(normalized);
321
498
  // Reject a non-mapping here so the renderer never sees a non-document.
322
499
  if (!isApiDocument(specification)) {
@@ -324,7 +501,8 @@ export const parseSpec = async (
324
501
  `${spec} is not a valid OpenAPI document (expected a YAML or JSON object).`
325
502
  );
326
503
  }
327
- return { document: specification, warnings };
504
+ const issues = await inlineSampleRefs(specification, spec, root, options);
505
+ return { document: specification, issues, warnings };
328
506
  };
329
507
 
330
508
  export interface ParsedAsyncApiSpec {
@@ -1,6 +1,9 @@
1
+ import { isAbsolute, join } from "pathe";
2
+
1
3
  import { normalizeRoute, withBasePath } from "../core/base-path.ts";
2
4
  import type { ResolvedConfig } from "../core/schema.ts";
3
5
  import { trimChar } from "../core/trim.ts";
6
+ import type { GraphqlAuthOptions } from "../reference/options.ts";
4
7
  import type { ScalarOptions } from "../reference/scalar.ts";
5
8
  import type { ResolvedReferenceAdapter } from "../reference/schema.ts";
6
9
 
@@ -28,8 +31,8 @@ export type BlumeReferenceKind = Exclude<ReferenceKind, "scalar">;
28
31
 
29
32
  /** Per-adapter display options for the Blume renderer. */
30
33
  export interface ReferenceDisplay {
31
- /** Code-sample languages shown per operation. */
32
- codeSamples: string[];
34
+ /** Code-sample languages shown per operation; `false` for none. */
35
+ codeSamples: string[] | false;
33
36
  /** Whether nested schema rows start expanded. */
34
37
  expandSchemas: boolean;
35
38
  /**
@@ -54,6 +57,12 @@ export interface ReferenceSource {
54
57
  */
55
58
  basePath: string;
56
59
  label: string;
60
+ /**
61
+ * The source's own `label`, when it sets one: it names the sidebar group
62
+ * of the source's route. An unlabeled source's group keeps the name its
63
+ * route gives it.
64
+ */
65
+ groupLabel?: string;
57
66
  /** Whether generated pages are included in llms.txt/llms-full.txt. */
58
67
  includeInLlms: boolean;
59
68
  /** Whether generated pages are included in site search. */
@@ -67,11 +76,18 @@ export interface ReferenceSource {
67
76
  seoDescriptionSuffix: boolean;
68
77
  /** Local path or `http(s)` URL, verbatim from config. */
69
78
  spec: string;
79
+ /**
80
+ * OpenAPI Overlay documents applied to `spec` in order (`openapi()` and
81
+ * `scalar()` only).
82
+ */
83
+ overlays?: string[];
70
84
  /**
71
85
  * URL of the live GraphQL endpoint the playground and code samples target
72
86
  * (GraphQL only — a schema, unlike an OpenAPI document, names no server).
73
87
  */
74
88
  endpoint?: string;
89
+ /** How the GraphQL endpoint authenticates (GraphQL only). */
90
+ auth?: GraphqlAuthOptions;
75
91
  /**
76
92
  * The `scalar()` adapter's own options (`scalar` kind only): `theme` plus
77
93
  * any Scalar config forwarded verbatim to `<ScalarComponent>`, which takes
@@ -176,6 +192,7 @@ const scalarOptionsOf = (
176
192
 
177
193
  /** A source row with every kind's fields reconciled onto one shape. */
178
194
  interface SourceRow {
195
+ auth?: GraphqlAuthOptions;
179
196
  endpoint?: string;
180
197
  includeInLlms: boolean;
181
198
  includeInSearch: boolean;
@@ -184,13 +201,14 @@ interface SourceRow {
184
201
  route?: string;
185
202
  seoDescriptionSuffix: boolean;
186
203
  spec: string;
204
+ overlays?: string[];
187
205
  }
188
206
 
189
207
  /**
190
208
  * One row per source, with the kind-specific fields already reconciled: a
191
- * GraphQL source's `endpoint` falls back to the adapter-wide default (the
192
- * common single-schema case pairs it with the `spec` shorthand); the other
193
- * kinds have no endpoint at all. A Scalar source carries only `noindex` of
209
+ * GraphQL source's `endpoint` and `auth` fall back to the adapter-wide
210
+ * defaults (the common single-schema case pairs them with the `spec`
211
+ * shorthand); the other kinds have neither. A Scalar source carries only `noindex` of
194
212
  * the per-source controls — the embed sits outside search and llms.txt, so
195
213
  * the other two read as off.
196
214
  */
@@ -198,6 +216,7 @@ const sourceRowsOf = (adapter: ResolvedReferenceAdapter): SourceRow[] => {
198
216
  if (adapter.kind === "graphql") {
199
217
  return adapter.options.sources.map((source) => ({
200
218
  ...source,
219
+ auth: source.auth ?? adapter.options.auth,
201
220
  endpoint: source.endpoint ?? adapter.options.endpoint,
202
221
  }));
203
222
  }
@@ -252,9 +271,18 @@ const referencesFor = (
252
271
  if (adapter.kind === "scalar") {
253
272
  reference.scalar = scalarOptionsOf(adapter.options);
254
273
  }
274
+ if (source.label !== undefined) {
275
+ reference.groupLabel = source.label;
276
+ }
255
277
  if (source.endpoint !== undefined) {
256
278
  reference.endpoint = source.endpoint;
257
279
  }
280
+ if (source.auth !== undefined) {
281
+ reference.auth = source.auth;
282
+ }
283
+ if (source.overlays !== undefined) {
284
+ reference.overlays = source.overlays;
285
+ }
258
286
  return reference;
259
287
  });
260
288
  };
@@ -339,6 +367,27 @@ export const blumeReferences = (
339
367
  return result;
340
368
  };
341
369
 
370
+ /** A spec or overlay the parser fetches rather than reads from disk. */
371
+ const REMOTE_SPEC = /^https?:\/\//u;
372
+
373
+ /**
374
+ * The local files every reference reads, as absolute paths: each spec and
375
+ * overlay that isn't an `http(s)` URL, resolved from the project root the way
376
+ * the parser resolves them. `blume dev` watches these, so an edit to a spec
377
+ * regenerates its reference like an edit to a page does.
378
+ */
379
+ export const referenceSpecFiles = (
380
+ config: ResolvedConfig,
381
+ root: string
382
+ ): string[] => [
383
+ ...new Set(
384
+ resolveReferences(config)
385
+ .flatMap((ref) => [ref.spec, ...(ref.overlays ?? [])])
386
+ .filter((path) => !REMOTE_SPEC.test(path))
387
+ .map((path) => (isAbsolute(path) ? path : join(root, path)))
388
+ ),
389
+ ];
390
+
342
391
  /** Whether any reference is Scalar-rendered (gates the Scalar pages). */
343
392
  export const hasScalarReferences = (config: ResolvedConfig): boolean =>
344
393
  resolveReferences(config).some((ref) => ref.kind === "scalar");
@@ -363,11 +412,18 @@ export const builtinProxyReferences = (
363
412
  ref.display.playground.proxy === true
364
413
  );
365
414
 
415
+ /**
416
+ * Whether hand-written endpoint pages (`api` frontmatter) send their Try it
417
+ * requests through the built-in proxy: `api.playground.proxy: true`.
418
+ */
419
+ export const apiPagesUseBuiltinProxy = (config: ResolvedConfig): boolean =>
420
+ config.api.playground.enabled && config.api.playground.proxy === true;
421
+
366
422
  /**
367
423
  * Whether the built-in playground CORS proxy endpoint (`/_api-proxy`) must be
368
424
  * generated: some Blume-rendered reference's playground opted into it with
369
- * `proxy: true`. Shared by the server feature gate and the generator so the
370
- * two can never disagree.
425
+ * `proxy: true`, or the hand-written endpoint pages' did. Shared by the server
426
+ * feature gate and the generator so the two can never disagree.
371
427
  */
372
428
  export const needsPlaygroundProxy = (config: ResolvedConfig): boolean =>
373
- builtinProxyReferences(config).length > 0;
429
+ builtinProxyReferences(config).length > 0 || apiPagesUseBuiltinProxy(config);
@@ -270,7 +270,8 @@ const operationDescription = (
270
270
  // method (see `extractGraphqlOperations`).
271
271
  subject = `${operation.path} ${GRAPHQL_MEMBER_PHRASES[operation.method as GraphqlMember]}`;
272
272
  } else {
273
- subject = `${operation.method.toUpperCase()} ${operation.path} endpoint`;
273
+ // A webhook is a request the API sends, named rather than pathed.
274
+ subject = `${operation.method.toUpperCase()} ${operation.path} ${operation.webhook ? "webhook" : "endpoint"}`;
274
275
  }
275
276
  const suffix = `Reference for the ${subject} in the ${apiPhrase(spec)}.`;
276
277
  // Room for the joining space and the period `asSentence` may add, so a
@@ -348,6 +349,9 @@ export const operationMdx = (
348
349
  sidebar.badge = method;
349
350
  }
350
351
  const tags = graphql ? [operation.tag] : [operation.tag, method];
352
+ if (operation.webhook) {
353
+ tags.push("Webhook");
354
+ }
351
355
  return {
352
356
  body: withDescription(
353
357
  description,