blume 2.0.2 → 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 (601) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +113 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
  12. package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
  13. package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
  14. package/dist/cli/chunk-43zcf4gz.js.map +36 -0
  15. package/dist/cli/chunk-4ejxbzee.js +39 -0
  16. package/dist/cli/chunk-4ejxbzee.js.map +10 -0
  17. package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
  18. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  19. package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
  20. package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
  21. package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
  22. package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
  23. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  24. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  25. package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
  26. package/dist/cli/chunk-713mngdq.js.map +14 -0
  27. package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
  28. package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
  29. package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
  30. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  31. package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
  32. package/dist/cli/chunk-cm89scdv.js +443 -0
  33. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  34. package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
  35. package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
  36. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  37. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  38. package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
  39. package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
  40. package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
  41. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  42. package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
  43. package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
  44. package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
  45. package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
  46. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  47. package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
  48. package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
  49. package/dist/cli/chunk-kwrg63tm.js +31 -0
  50. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  51. package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
  52. package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
  53. package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
  54. package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
  55. package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
  56. package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
  57. package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
  58. package/dist/cli/chunk-q3wxxq39.js +126 -0
  59. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  60. package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
  61. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  62. package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
  63. package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
  64. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  65. package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
  66. package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
  67. package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
  68. package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
  69. package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
  70. package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
  71. package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
  72. package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
  73. package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
  74. package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
  75. package/dist/cli/chunk-zf748avv.js.map +37 -0
  76. package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
  77. package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
  78. package/dist/cli/index.js +28 -20
  79. package/dist/cli/index.js.map +5 -5
  80. package/dist/types/ai/agent-surface.d.ts +8 -7
  81. package/dist/types/ai/ai-catalog.d.ts +7 -0
  82. package/dist/types/ai/ask-context.d.ts +22 -0
  83. package/dist/types/ai/ask.d.ts +271 -58
  84. package/dist/types/ai/index.d.ts +2 -2
  85. package/dist/types/ai/link-headers.d.ts +8 -1
  86. package/dist/types/ai/openapi-components.d.ts +5 -2
  87. package/dist/types/ai/relative-links.d.ts +9 -7
  88. package/dist/types/ai/site-skill.d.ts +26 -0
  89. package/dist/types/ai/skills.d.ts +20 -1
  90. package/dist/types/ai/tar.d.ts +1 -3
  91. package/dist/types/analytics/index.d.ts +2 -0
  92. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  93. package/dist/types/analytics/schema.d.ts +14 -0
  94. package/dist/types/astro/integration.d.ts +11 -2
  95. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  96. package/dist/types/captcha/index.d.ts +28 -0
  97. package/dist/types/captcha/schema.d.ts +29 -0
  98. package/dist/types/captcha/turnstile.d.ts +28 -0
  99. package/dist/types/components/content/api-field.d.ts +59 -0
  100. package/dist/types/components/content/api-page.d.ts +95 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/helpers.d.ts +137 -0
  103. package/dist/types/components/openapi/security.d.ts +85 -0
  104. package/dist/types/consent/ethyca.d.ts +48 -0
  105. package/dist/types/consent/index.d.ts +37 -0
  106. package/dist/types/consent/native.d.ts +28 -0
  107. package/dist/types/consent/osano.d.ts +42 -0
  108. package/dist/types/consent/schema.d.ts +110 -0
  109. package/dist/types/core/base-path.d.ts +21 -7
  110. package/dist/types/core/config-input.d.ts +196 -27
  111. package/dist/types/core/data.d.ts +49 -4
  112. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  113. package/dist/types/core/footer.d.ts +8 -0
  114. package/dist/types/core/heading-markers.d.ts +5 -7
  115. package/dist/types/core/i18n-ui.d.ts +86 -0
  116. package/dist/types/core/i18n.d.ts +2 -0
  117. package/dist/types/core/includes.d.ts +8 -2
  118. package/dist/types/core/last-modified.d.ts +10 -0
  119. package/dist/types/core/localizable.d.ts +7 -0
  120. package/dist/types/core/meta.d.ts +8 -0
  121. package/dist/types/core/page-modes.d.ts +29 -0
  122. package/dist/types/core/redirect-patterns.d.ts +144 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +558 -56
  125. package/dist/types/core/sources/lower.d.ts +29 -16
  126. package/dist/types/core/sources/normalize.d.ts +13 -1
  127. package/dist/types/core/sources/read.d.ts +5 -0
  128. package/dist/types/core/sources/watch.d.ts +12 -5
  129. package/dist/types/core/standard-schema.d.ts +5 -0
  130. package/dist/types/core/types.d.ts +1 -1
  131. package/dist/types/core/variables.d.ts +42 -0
  132. package/dist/types/deploy/artifacts.d.ts +6 -4
  133. package/dist/types/deploy/headers.d.ts +5 -0
  134. package/dist/types/deploy/node-headers.d.ts +13 -2
  135. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  136. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  137. package/dist/types/deploy/platforms/types.d.ts +8 -0
  138. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  139. package/dist/types/deploy/redirects.d.ts +61 -22
  140. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  141. package/dist/types/markdown/code-title.d.ts +17 -0
  142. package/dist/types/markdown/directives.d.ts +62 -0
  143. package/dist/types/markdown/features.d.ts +24 -2
  144. package/dist/types/markdown/mdast.d.ts +63 -0
  145. package/dist/types/narration/build.d.ts +70 -0
  146. package/dist/types/narration/provider.d.ts +26 -0
  147. package/dist/types/narration/script.d.ts +154 -0
  148. package/dist/types/openapi/asyncapi.d.ts +4 -2
  149. package/dist/types/openapi/model.d.ts +12 -6
  150. package/dist/types/openapi/overlay.d.ts +54 -0
  151. package/dist/types/openapi/parse.d.ts +13 -0
  152. package/dist/types/openapi/references.d.ts +14 -4
  153. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  154. package/dist/types/ratelimit/index.d.ts +27 -0
  155. package/dist/types/ratelimit/memory.d.ts +36 -0
  156. package/dist/types/ratelimit/schema.d.ts +111 -0
  157. package/dist/types/ratelimit/upstash.d.ts +21 -0
  158. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  159. package/dist/types/reference/asyncapi.d.ts +10 -10
  160. package/dist/types/reference/graphql.d.ts +13 -10
  161. package/dist/types/reference/openapi.d.ts +33 -12
  162. package/dist/types/reference/options.d.ts +45 -5
  163. package/dist/types/reference/scalar.d.ts +13 -0
  164. package/dist/types/reference/schema.d.ts +60 -36
  165. package/dist/types/search/documents.d.ts +12 -1
  166. package/dist/types/search/orama-index.d.ts +4 -0
  167. package/dist/types/seo/metatags.d.ts +20 -0
  168. package/docs/02-deployment.mdx +44 -6
  169. package/docs/03-upgrading.mdx +4 -4
  170. package/docs/04-migrating.mdx +1 -1
  171. package/docs/08-faq.mdx +1 -1
  172. package/docs/advanced/changelog.mdx +2 -2
  173. package/docs/advanced/custom-pages.mdx +3 -3
  174. package/docs/advanced/skills.mdx +14 -0
  175. package/docs/cli/audit.mdx +38 -2
  176. package/docs/cli/evals.mdx +3 -3
  177. package/docs/cli/index.mdx +1 -0
  178. package/docs/cli/translate.mdx +3 -3
  179. package/docs/cli/version.mdx +1 -1
  180. package/docs/configuration/analytics.mdx +22 -1
  181. package/docs/configuration/assistant.mdx +188 -36
  182. package/docs/configuration/consent.mdx +100 -0
  183. package/docs/configuration/customization.mdx +6 -4
  184. package/docs/configuration/index.mdx +63 -5
  185. package/docs/configuration/meta.ts +3 -0
  186. package/docs/configuration/narration.mdx +119 -0
  187. package/docs/configuration/rate-limiting.mdx +69 -0
  188. package/docs/configuration/search.mdx +30 -2
  189. package/docs/configuration/theming.mdx +3 -1
  190. package/docs/content/components.mdx +107 -1
  191. package/docs/content/frontmatter.mdx +77 -1
  192. package/docs/content/i18n.mdx +40 -3
  193. package/docs/content/includes.mdx +16 -0
  194. package/docs/content/index.mdx +4 -2
  195. package/docs/content/islands.mdx +1 -1
  196. package/docs/content/meta.mdx +4 -1
  197. package/docs/content/meta.ts +1 -0
  198. package/docs/content/navigation.mdx +28 -6
  199. package/docs/content/sources/contentful.mdx +32 -0
  200. package/docs/content/sources/custom.mdx +39 -0
  201. package/docs/content/sources/github-releases.mdx +31 -0
  202. package/docs/content/sources/index.mdx +91 -0
  203. package/docs/content/sources/meta.ts +16 -0
  204. package/docs/content/sources/notion.mdx +32 -0
  205. package/docs/content/sources/obsidian.mdx +37 -0
  206. package/docs/content/sources/payload.mdx +30 -0
  207. package/docs/content/sources/remote-mdx.mdx +40 -0
  208. package/docs/content/sources/sanity.mdx +29 -0
  209. package/docs/content/sources/strapi.mdx +31 -0
  210. package/docs/content/syntax.mdx +81 -2
  211. package/docs/content/variables.mdx +41 -0
  212. package/docs/content/versioning.mdx +1 -1
  213. package/docs/discoverability/agent-discovery.mdx +30 -3
  214. package/docs/discoverability/index.mdx +3 -2
  215. package/docs/discoverability/llms-txt.mdx +1 -1
  216. package/docs/discoverability/markdown.mdx +2 -2
  217. package/docs/discoverability/metadata.mdx +17 -0
  218. package/docs/discoverability/open-graph.mdx +5 -3
  219. package/docs/discoverability/rss.mdx +1 -1
  220. package/docs/references/api-pages.mdx +74 -0
  221. package/docs/references/asyncapi.mdx +2 -2
  222. package/docs/references/graphql.mdx +3 -3
  223. package/docs/references/meta.ts +1 -1
  224. package/docs/references/openapi.mdx +106 -5
  225. package/docs/references/scalar.mdx +3 -1
  226. package/package.json +36 -1
  227. package/skills/blume/SKILL.md +3 -0
  228. package/skills/blume-migrate/SKILL.md +5 -5
  229. package/skills/blume-migrate/references/docusaurus.md +3 -3
  230. package/skills/blume-migrate/references/fumadocs.md +1 -1
  231. package/skills/blume-migrate/references/mintlify.md +37 -19
  232. package/skills/blume-migrate/references/nextra.md +3 -3
  233. package/skills/blume-migrate/references/starlight.md +2 -1
  234. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
  235. package/skills/blume-write-skill/SKILL.md +82 -0
  236. package/src/ai/agent-readability.ts +4 -3
  237. package/src/ai/agent-surface.ts +9 -7
  238. package/src/ai/ai-catalog.ts +12 -5
  239. package/src/ai/api/handlers.ts +2 -2
  240. package/src/ai/api/spec.ts +2 -2
  241. package/src/ai/api-catalog.ts +6 -2
  242. package/src/ai/ask-context.ts +73 -20
  243. package/src/ai/ask-data.ts +6 -0
  244. package/src/ai/ask-tools.ts +142 -0
  245. package/src/ai/ask.ts +354 -76
  246. package/src/ai/changelog-markdown.ts +2 -2
  247. package/src/ai/component-markdown.ts +75 -0
  248. package/src/ai/cors.ts +2 -0
  249. package/src/ai/index.ts +12 -0
  250. package/src/ai/link-headers.ts +14 -5
  251. package/src/ai/llms.ts +24 -9
  252. package/src/ai/markdown.ts +2 -1
  253. package/src/ai/mcp/data.ts +6 -0
  254. package/src/ai/mcp/discovery.ts +2 -2
  255. package/src/ai/mcp/query.ts +2 -2
  256. package/src/ai/mcp/server.ts +2 -2
  257. package/src/ai/openapi-components.ts +31 -9
  258. package/src/ai/relative-links.ts +42 -26
  259. package/src/ai/serializers.ts +2 -1
  260. package/src/ai/site-skill.ts +274 -0
  261. package/src/ai/skills.ts +49 -3
  262. package/src/ai/tar.ts +139 -12
  263. package/src/ai/write-skill.ts +97 -0
  264. package/src/analytics/head.ts +4 -0
  265. package/src/analytics/index.ts +5 -0
  266. package/src/analytics/one-dollar-stats.ts +86 -0
  267. package/src/analytics/schema.ts +2 -0
  268. package/src/astro/generate.ts +133 -19
  269. package/src/astro/include-hmr.ts +31 -12
  270. package/src/astro/include-refresh.ts +19 -8
  271. package/src/astro/index.ts +1 -0
  272. package/src/astro/integration.ts +143 -14
  273. package/src/astro/module-types.ts +6 -0
  274. package/src/astro/runtime-modules.ts +28 -13
  275. package/src/astro/templates.ts +364 -67
  276. package/src/astro/variables.ts +31 -0
  277. package/src/audit/catalog.ts +19 -0
  278. package/src/audit/checks/contrast.ts +183 -0
  279. package/src/audit/checks/i18n.ts +2 -3
  280. package/src/audit/checks/indexability.ts +2 -3
  281. package/src/audit/checks/links.ts +13 -13
  282. package/src/audit/checks/sitemap.ts +2 -3
  283. package/src/audit/graph.ts +3 -1
  284. package/src/audit/redirects.ts +46 -15
  285. package/src/audit/run.ts +14 -9
  286. package/src/audit/snapshot.ts +5 -0
  287. package/src/audit/types.ts +2 -1
  288. package/src/blume-modules.d.ts +7 -0
  289. package/src/captcha/client.ts +152 -0
  290. package/src/captcha/hcaptcha.ts +36 -0
  291. package/src/captcha/index.ts +28 -0
  292. package/src/captcha/schema.ts +32 -0
  293. package/src/captcha/turnstile.ts +38 -0
  294. package/src/captcha/verify.ts +71 -0
  295. package/src/cli/args.ts +32 -0
  296. package/src/cli/command-meta.ts +5 -0
  297. package/src/cli/commands/audit.ts +4 -1
  298. package/src/cli/commands/doctor.ts +25 -17
  299. package/src/cli/commands/eval.ts +19 -11
  300. package/src/cli/commands/skill.ts +96 -0
  301. package/src/cli/commands/translate.ts +9 -12
  302. package/src/cli/commands/validate.ts +3 -1
  303. package/src/cli/dev-lock.ts +157 -37
  304. package/src/cli/index.ts +5 -0
  305. package/src/cli/report-format.ts +11 -6
  306. package/src/cli/required-secrets.ts +71 -29
  307. package/src/components/BlumePage.astro +2 -0
  308. package/src/components/colors.ts +19 -0
  309. package/src/components/content/ApiEndpoint.astro +18 -0
  310. package/src/components/content/ApiField.astro +79 -0
  311. package/src/components/content/ApiPlayground.astro +62 -0
  312. package/src/components/content/ApiRail.astro +19 -0
  313. package/src/components/content/Badge.astro +5 -3
  314. package/src/components/content/Color.astro +1 -0
  315. package/src/components/content/Component.astro +2 -2
  316. package/src/components/content/FileTree.astro +1 -0
  317. package/src/components/content/GithubInfo.astro +1 -0
  318. package/src/components/content/Panel.astro +1 -0
  319. package/src/components/content/ParamField.astro +25 -0
  320. package/src/components/content/RequestExample.astro +17 -0
  321. package/src/components/content/ResponseExample.astro +17 -0
  322. package/src/components/content/ResponseField.astro +32 -0
  323. package/src/components/content/Step.astro +1 -0
  324. package/src/components/content/Tab.astro +0 -1
  325. package/src/components/content/Tabs.astro +4 -0
  326. package/src/components/content/Tile.astro +1 -0
  327. package/src/components/content/Tooltip.astro +1 -0
  328. package/src/components/content/Tree.astro +1 -0
  329. package/src/components/content/TypeTable.astro +4 -1
  330. package/src/components/content/View.astro +21 -0
  331. package/src/components/content/ViewSwitcher.astro +168 -0
  332. package/src/components/content/api-field.ts +100 -0
  333. package/src/components/content/api-page.ts +224 -0
  334. package/src/components/content/badge-color.ts +5 -2
  335. package/src/components/content/base-href.ts +13 -36
  336. package/src/components/content/view-switcher.ts +71 -0
  337. package/src/components/index.ts +2 -0
  338. package/src/components/islands/Assistant.astro +10 -1
  339. package/src/components/islands/assistant.tsx +125 -18
  340. package/src/components/islands/base-path.ts +47 -10
  341. package/src/components/islands/code-question.ts +62 -0
  342. package/src/components/islands/hooks.ts +145 -40
  343. package/src/components/islands/support-link.ts +68 -0
  344. package/src/components/islands/webmcp.ts +4 -2
  345. package/src/components/layout/Analytics.astro +11 -2
  346. package/src/components/layout/Banner.astro +14 -10
  347. package/src/components/layout/Breadcrumbs.astro +11 -4
  348. package/src/components/layout/ConsentBanner.astro +62 -0
  349. package/src/components/layout/ConsentHead.astro +38 -0
  350. package/src/components/layout/Directory.astro +94 -0
  351. package/src/components/layout/DiscoveryLinks.astro +9 -5
  352. package/src/components/layout/DrawerSwitchers.astro +51 -0
  353. package/src/components/layout/Empty.astro +2 -2
  354. package/src/components/layout/Header.astro +124 -82
  355. package/src/components/layout/LanguageSwitcher.astro +50 -17
  356. package/src/components/layout/NarrationPlayer.astro +1085 -0
  357. package/src/components/layout/NavSelector.astro +35 -9
  358. package/src/components/layout/NavTabMenu.astro +3 -3
  359. package/src/components/layout/NavTree.astro +16 -6
  360. package/src/components/layout/PageActions.astro +4 -3
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +78 -9
  363. package/src/components/layout/Pagination.astro +3 -3
  364. package/src/components/layout/ReferenceLayout.astro +17 -6
  365. package/src/components/layout/RelatedPages.astro +42 -0
  366. package/src/components/layout/RootLayout.astro +313 -50
  367. package/src/components/layout/Search.astro +64 -30
  368. package/src/components/layout/SiteFooter.astro +124 -0
  369. package/src/components/layout/SiteMetatags.astro +16 -0
  370. package/src/components/layout/VersionBanner.astro +2 -2
  371. package/src/components/layout/analytics-client.ts +12 -0
  372. package/src/components/layout/consent/native.ts +79 -0
  373. package/src/components/layout/consent/types.ts +37 -0
  374. package/src/components/layout/footer-links.ts +53 -0
  375. package/src/components/layout/footer-socials.ts +87 -0
  376. package/src/components/layout/locale-redirect.ts +107 -0
  377. package/src/components/layout/nav-utils.ts +83 -0
  378. package/src/components/layout/page-feedback.ts +96 -0
  379. package/src/components/layout/search/analytics.ts +98 -0
  380. package/src/components/layout/search/flexsearch.ts +17 -5
  381. package/src/components/layout/search/orama-cloud.ts +26 -14
  382. package/src/components/layout/search/types.ts +20 -0
  383. package/src/components/layout/search/typesense.ts +5 -1
  384. package/src/components/layout/toc-active.ts +41 -0
  385. package/src/components/layout/toc-element.ts +8 -14
  386. package/src/components/openapi/ApiTagOperations.astro +2 -2
  387. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  388. package/src/components/openapi/GraphqlChip.astro +2 -2
  389. package/src/components/openapi/Operation.astro +163 -13
  390. package/src/components/openapi/Playground.astro +4 -4
  391. package/src/components/openapi/RequestBody.astro +6 -4
  392. package/src/components/openapi/RequestPanel.astro +63 -8
  393. package/src/components/openapi/Responses.astro +6 -4
  394. package/src/components/openapi/SchemaTable.astro +7 -0
  395. package/src/components/openapi/async-snippets.ts +4 -4
  396. package/src/components/openapi/async.ts +38 -6
  397. package/src/components/openapi/code-samples.ts +97 -0
  398. package/src/components/openapi/helpers.ts +19 -9
  399. package/src/components/openapi/message-composer.ts +6 -1
  400. package/src/components/openapi/message.ts +17 -2
  401. package/src/components/openapi/operation-model.ts +81 -11
  402. package/src/components/openapi/panel.ts +4 -2
  403. package/src/components/openapi/param-style.ts +181 -0
  404. package/src/components/openapi/playground-client.ts +73 -16
  405. package/src/components/openapi/request.ts +190 -26
  406. package/src/components/openapi/schema-tree.ts +30 -24
  407. package/src/components/openapi/security.ts +6 -3
  408. package/src/components/openapi/snippets.ts +953 -13
  409. package/src/components/openapi/ws-client.ts +18 -2
  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/base-path.ts +62 -16
  421. package/src/core/builtin-tags.ts +5 -0
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/config-input.ts +203 -27
  424. package/src/core/data.ts +44 -4
  425. package/src/core/diagnostics.ts +5 -2
  426. package/src/core/directive-diagnostics.ts +99 -0
  427. package/src/core/footer.ts +26 -0
  428. package/src/core/frontmatter.ts +21 -18
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/heading-markers.ts +5 -18
  431. package/src/core/i18n-ui.ts +82 -2
  432. package/src/core/i18n.ts +31 -3
  433. package/src/core/includes.ts +20 -3
  434. package/src/core/last-modified.ts +25 -3
  435. package/src/core/links.ts +16 -2
  436. package/src/core/locale-links.ts +5 -1
  437. package/src/core/localizable.ts +26 -0
  438. package/src/core/meta.ts +33 -19
  439. package/src/core/navigation.ts +71 -20
  440. package/src/core/page-modes.ts +56 -0
  441. package/src/core/project-graph.ts +138 -10
  442. package/src/core/redirect-patterns.ts +491 -0
  443. package/src/core/related.ts +131 -0
  444. package/src/core/schema.ts +342 -68
  445. package/src/core/sources/contentful-rich-text.ts +27 -20
  446. package/src/core/sources/filesystem.ts +20 -2
  447. package/src/core/sources/github-releases.ts +25 -6
  448. package/src/core/sources/lexical.ts +23 -18
  449. package/src/core/sources/lower.ts +201 -34
  450. package/src/core/sources/mdx-remote.ts +51 -16
  451. package/src/core/sources/normalize.ts +327 -91
  452. package/src/core/sources/notion.ts +52 -25
  453. package/src/core/sources/obsidian.ts +23 -6
  454. package/src/core/sources/portable-text.ts +38 -21
  455. package/src/core/sources/read.ts +8 -3
  456. package/src/core/sources/strapi-blocks.ts +20 -16
  457. package/src/core/sources/watch.ts +20 -7
  458. package/src/core/standard-schema.ts +10 -6
  459. package/src/core/types.ts +1 -1
  460. package/src/core/ui-packs/ar.ts +43 -0
  461. package/src/core/ui-packs/bg.ts +45 -0
  462. package/src/core/ui-packs/bn.ts +43 -0
  463. package/src/core/ui-packs/ca.ts +46 -0
  464. package/src/core/ui-packs/cs.ts +44 -0
  465. package/src/core/ui-packs/da.ts +45 -0
  466. package/src/core/ui-packs/de.ts +46 -0
  467. package/src/core/ui-packs/el.ts +45 -0
  468. package/src/core/ui-packs/es.ts +46 -0
  469. package/src/core/ui-packs/fa.ts +45 -0
  470. package/src/core/ui-packs/fi.ts +45 -0
  471. package/src/core/ui-packs/fr.ts +46 -0
  472. package/src/core/ui-packs/he.ts +43 -0
  473. package/src/core/ui-packs/hi.ts +44 -0
  474. package/src/core/ui-packs/hr.ts +45 -0
  475. package/src/core/ui-packs/hu.ts +44 -0
  476. package/src/core/ui-packs/id.ts +45 -0
  477. package/src/core/ui-packs/it.ts +45 -0
  478. package/src/core/ui-packs/ja.ts +45 -0
  479. package/src/core/ui-packs/ko.ts +44 -0
  480. package/src/core/ui-packs/nl.ts +46 -0
  481. package/src/core/ui-packs/no.ts +44 -0
  482. package/src/core/ui-packs/pl.ts +45 -0
  483. package/src/core/ui-packs/pt-br.ts +46 -0
  484. package/src/core/ui-packs/pt.ts +45 -0
  485. package/src/core/ui-packs/ro.ts +45 -0
  486. package/src/core/ui-packs/ru.ts +45 -0
  487. package/src/core/ui-packs/sk.ts +44 -0
  488. package/src/core/ui-packs/sr.ts +45 -0
  489. package/src/core/ui-packs/sv.ts +44 -0
  490. package/src/core/ui-packs/th.ts +43 -0
  491. package/src/core/ui-packs/tr.ts +45 -0
  492. package/src/core/ui-packs/uk.ts +45 -0
  493. package/src/core/ui-packs/vi.ts +45 -0
  494. package/src/core/ui-packs/zh-tw.ts +43 -0
  495. package/src/core/ui-packs/zh.ts +43 -0
  496. package/src/core/variables.ts +103 -0
  497. package/src/core/version-cut.ts +52 -15
  498. package/src/deploy/artifacts.ts +104 -14
  499. package/src/deploy/cloudflare-negotiation.ts +39 -17
  500. package/src/deploy/headers.ts +10 -5
  501. package/src/deploy/node-headers.ts +64 -10
  502. package/src/deploy/platforms/cloudflare.ts +44 -3
  503. package/src/deploy/platforms/netlify.ts +34 -1
  504. package/src/deploy/platforms/node.ts +1 -0
  505. package/src/deploy/platforms/static.ts +1 -0
  506. package/src/deploy/platforms/types.ts +8 -0
  507. package/src/deploy/platforms/vercel.ts +38 -2
  508. package/src/deploy/redirects.ts +139 -36
  509. package/src/deploy/robots.ts +2 -2
  510. package/src/deploy/rss.ts +4 -3
  511. package/src/deploy/sitemap.ts +7 -5
  512. package/src/deploy/vercel-negotiation.ts +36 -0
  513. package/src/markdown/api-rail.ts +259 -0
  514. package/src/markdown/base-links.ts +34 -36
  515. package/src/markdown/code-title.ts +35 -9
  516. package/src/markdown/directives.ts +242 -36
  517. package/src/markdown/features.ts +20 -2
  518. package/src/markdown/fence-meta.ts +2 -0
  519. package/src/markdown/include.ts +11 -1
  520. package/src/markdown/index.ts +35 -11
  521. package/src/markdown/math.ts +4 -4
  522. package/src/markdown/mdast.ts +5 -2
  523. package/src/markdown/relative-links.ts +12 -4
  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 +136 -11
  531. package/src/og/derive.ts +41 -31
  532. package/src/og/index.ts +1 -0
  533. package/src/openapi/asyncapi.ts +4 -2
  534. package/src/openapi/model.ts +77 -36
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +55 -2
  537. package/src/openapi/proxy.ts +63 -10
  538. package/src/openapi/references.ts +21 -5
  539. package/src/openapi/render-mdx.ts +46 -3
  540. package/src/openapi/scalar.ts +40 -6
  541. package/src/openapi/source.ts +1 -1
  542. package/src/ratelimit/cloudflare.ts +54 -0
  543. package/src/ratelimit/index.ts +30 -0
  544. package/src/ratelimit/memory.ts +45 -0
  545. package/src/ratelimit/runtime.ts +229 -0
  546. package/src/ratelimit/schema.ts +28 -0
  547. package/src/ratelimit/upstash.ts +30 -0
  548. package/src/ratelimit/wrangler.ts +70 -0
  549. package/src/reference/asyncapi.ts +2 -2
  550. package/src/reference/graphql.ts +5 -2
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +51 -7
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +201 -28
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/version-scope.ts +30 -0
  557. package/src/search/documents.ts +93 -28
  558. package/src/search/orama-index.ts +28 -3
  559. package/src/search/pagefind-ranking.ts +39 -0
  560. package/src/search/popular.ts +2 -1
  561. package/src/search/sync/algolia.ts +15 -4
  562. package/src/search/sync/orama-cloud.ts +3 -0
  563. package/src/search/sync/typesense.ts +5 -0
  564. package/src/seo/jsonld.ts +7 -3
  565. package/src/seo/metatags.ts +67 -0
  566. package/src/theme/chrome-icons.ts +1 -0
  567. package/src/theme/color.ts +500 -0
  568. package/src/theme/entry.ts +56 -3
  569. package/src/theme/palette.ts +121 -45
  570. package/src/translate/meta.ts +68 -24
  571. package/src/translate/prompts.ts +1 -0
  572. package/src/translate/run.ts +3 -3
  573. package/src/translate/validate.ts +12 -3
  574. package/src/translate/work-list.ts +56 -17
  575. package/src/upgrade/upgrade.ts +4 -171
  576. package/dist/cli/chunk-3yce002v.js.map +0 -14
  577. package/dist/cli/chunk-5r8g91qn.js.map +0 -34
  578. package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
  579. package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
  580. package/dist/cli/chunk-ddndchfr.js.map +0 -14
  581. package/dist/cli/chunk-esh98wmb.js +0 -23
  582. package/dist/cli/chunk-esh98wmb.js.map +0 -10
  583. package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
  584. package/dist/cli/chunk-hr8ne106.js.map +0 -13
  585. package/dist/cli/chunk-j85scx15.js.map +0 -10
  586. package/dist/cli/chunk-mqc662a6.js +0 -75
  587. package/dist/cli/chunk-mqc662a6.js.map +0 -11
  588. package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
  589. package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
  590. package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
  591. package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
  592. package/dist/cli/chunk-xh43dwgw.js.map +0 -36
  593. package/docs/content/sources.mdx +0 -338
  594. /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
  595. /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
  596. /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
  597. /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
  598. /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
  599. /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
  600. /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
  601. /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
@@ -0,0 +1,97 @@
1
+ import { sampleLanguageInfo } from "./snippets.ts";
2
+ import type { SampleLanguageInfo } from "./snippets.ts";
3
+
4
+ /**
5
+ * Hand-written code samples from a spec: the `x-codeSamples` extension on an
6
+ * operation (Redocly's, which Mintlify, Speakeasy, and Stainless write too),
7
+ * or its older spelling `x-code-samples`. Each entry names a `lang`, holds
8
+ * the `source`, and may carry a `label`. They render as their own tabs ahead
9
+ * of the generated samples and, being fixed text, the playground leaves them
10
+ * as written.
11
+ */
12
+
13
+ /** One hand-written sample, ready to render as a tab. */
14
+ export interface CustomCodeSample {
15
+ /** A stable, unique tab key. */
16
+ key: string;
17
+ /** The tab label: the entry's own, else the language's name. */
18
+ label: string;
19
+ /** The Shiki language to highlight `source` with. */
20
+ lang: string;
21
+ source: string;
22
+ }
23
+
24
+ /** The two places an operation keeps them. */
25
+ export interface CodeSamplesCarrier {
26
+ "x-code-samples"?: unknown;
27
+ "x-codeSamples"?: unknown;
28
+ }
29
+
30
+ interface RawSample {
31
+ label?: string;
32
+ lang: string;
33
+ source: string;
34
+ }
35
+
36
+ const isRawSample = (value: unknown): value is RawSample =>
37
+ typeof value === "object" &&
38
+ value !== null &&
39
+ "lang" in value &&
40
+ typeof value.lang === "string" &&
41
+ "source" in value &&
42
+ typeof value.source === "string" &&
43
+ (!("label" in value) || typeof value.label === "string");
44
+
45
+ // Shell tools in an `x-codeSamples` entry are usually an SDK's CLI, not curl,
46
+ // so they're named for the shell rather than "cURL".
47
+ const SHELL_LANGS = new Set(["bash", "sh", "shell", "zsh"]);
48
+
49
+ /** The display name and highlighter for a sample's `lang`. */
50
+ const describe = (lang: string): SampleLanguageInfo => {
51
+ const lower = lang.toLowerCase();
52
+ if (SHELL_LANGS.has(lower)) {
53
+ return { label: "Shell", lang: "bash" };
54
+ }
55
+ // A language Blume doesn't generate keeps its own name, and Shiki tries
56
+ // it as written (it knows most languages by their common names).
57
+ return sampleLanguageInfo(lower) ?? { label: lang, lang: lower };
58
+ };
59
+
60
+ /**
61
+ * The operation's hand-written samples, with a unique tab label each. A label
62
+ * two samples share gains its language (`List plants · JavaScript`), and one
63
+ * that still repeats gains a number. Malformed entries are skipped.
64
+ */
65
+ export const customCodeSamples = (
66
+ operation: CodeSamplesCarrier
67
+ ): CustomCodeSample[] => {
68
+ const raw = operation["x-codeSamples"] ?? operation["x-code-samples"];
69
+ if (!Array.isArray(raw)) {
70
+ return [];
71
+ }
72
+ const entries = raw.filter(isRawSample).map((entry) => {
73
+ const language = describe(entry.lang);
74
+ return {
75
+ base: entry.label?.trim() || language.label,
76
+ language,
77
+ source: entry.source.replace(/\n$/u, ""),
78
+ };
79
+ });
80
+ const counts = new Map<string, number>();
81
+ for (const { base } of entries) {
82
+ counts.set(base, (counts.get(base) ?? 0) + 1);
83
+ }
84
+ const used = new Set<string>();
85
+ return entries.map(({ base, language, source }, index) => {
86
+ const first =
87
+ (counts.get(base) ?? 0) > 1 && base !== language.label
88
+ ? `${base} · ${language.label}`
89
+ : base;
90
+ let label = first;
91
+ for (let n = 2; used.has(label); n += 1) {
92
+ label = `${first} (${n})`;
93
+ }
94
+ used.add(label);
95
+ return { key: `x-${index}`, label, lang: language.lang, source };
96
+ });
97
+ };
@@ -335,12 +335,17 @@ const isRecord = (value: SpecValue): value is Record<string, SpecValue> =>
335
335
 
336
336
  /**
337
337
  * The example a parameter or media type declares: its `example`, else the
338
- * first inline `value` in its `examples` map. Scalar's upgrade moves every
339
- * OpenAPI 3.0 and Swagger 2.0 `example` into `examples.default.value`, and
340
- * 3.1 authors write that map directly, so reading `example` alone would drop
341
- * them all. `undefined` when nothing is declared.
338
+ * first `value` in its `examples` map — inline, or behind a `$ref` to
339
+ * `#/components/examples`, resolved against `components`. Scalar's upgrade
340
+ * moves every OpenAPI 3.0 and Swagger 2.0 `example` into
341
+ * `examples.default.value`, and 3.1 authors write that map directly, so
342
+ * reading `example` alone would drop them all. `undefined` when nothing is
343
+ * declared.
342
344
  */
343
- export const declaredExample = (carrier: ExampleCarrier): SpecValue => {
345
+ export const declaredExample = (
346
+ carrier: ExampleCarrier,
347
+ components?: ComponentsLike
348
+ ): SpecValue => {
344
349
  if (carrier.example !== undefined) {
345
350
  return carrier.example;
346
351
  }
@@ -349,8 +354,11 @@ export const declaredExample = (carrier: ExampleCarrier): SpecValue => {
349
354
  return undefined;
350
355
  }
351
356
  for (const entry of Object.values(examples)) {
352
- if (isRecord(entry) && entry.value !== undefined) {
353
- return entry.value;
357
+ const example = isRecord(entry)
358
+ ? resolveComponentRef(entry, components, "examples")
359
+ : undefined;
360
+ if (example?.value !== undefined) {
361
+ return example.value;
354
362
  }
355
363
  }
356
364
  return undefined;
@@ -363,9 +371,11 @@ export const declaredExample = (carrier: ExampleCarrier): SpecValue => {
363
371
  */
364
372
  export const responseExample = (
365
373
  media: ExampleCarrier & { schema?: SchemaLike },
366
- schemas: Record<string, SchemaLike>
374
+ schemas: Record<string, SchemaLike>,
375
+ components?: ComponentsLike
367
376
  ): SpecValue =>
368
- declaredExample(media) ?? exampleValue(media.schema, schemas, "response");
377
+ declaredExample(media, components) ??
378
+ exampleValue(media.schema, schemas, "response");
369
379
 
370
380
  /** Pretty-print a JSON value for an example/code block. */
371
381
  export const toJson = <T>(value: T): string => JSON.stringify(value, null, 2);
@@ -171,7 +171,12 @@ export const initComposer = (root: HTMLElement): void => {
171
171
  return;
172
172
  }
173
173
  if (state === "error") {
174
- setStatus(ERROR_MESSAGE, ERROR_TEXT);
174
+ // A detail is the browser refusing the URL before dialing; a failed
175
+ // handshake carries none.
176
+ setStatus(
177
+ detail ? `Couldn't connect: ${detail}` : ERROR_MESSAGE,
178
+ ERROR_TEXT
179
+ );
175
180
  return;
176
181
  }
177
182
  setStatus(
@@ -87,6 +87,17 @@ export const defaultMessageValues = (model: MessageModel): MessageValues => ({
87
87
 
88
88
  const PARAM_TEMPLATE = /\{(?<name>[^{}]+)\}/gu;
89
89
 
90
+ /**
91
+ * Protocols whose channel address is a URL path, where a parameter value must
92
+ * be percent-encoded. Everywhere else — a Kafka or MQTT topic, an AMQP routing
93
+ * key — the address is a name the value goes into as written: `sensor 1`,
94
+ * not `sensor%201`.
95
+ */
96
+ const URL_PROTOCOLS = { http: true, https: true, ws: true } satisfies Record<
97
+ string,
98
+ true
99
+ >;
100
+
90
101
  /**
91
102
  * A free-text base URL lowered onto the server shape the snippet builders and
92
103
  * the connect URL read. A full URL contributes its scheme, host, and path.
@@ -114,7 +125,8 @@ const customServer = (url: string): AsyncApiServerObject => {
114
125
  /**
115
126
  * THE one message builder: the composer's samples, its copy buttons, and the
116
127
  * live WebSocket frame all consume its output. Parameter values are
117
- * URL-encoded into the address; a parameter left blank keeps its `{name}`
128
+ * URL-encoded into the address when the protocol addresses channels by URL
129
+ * (see {@link URL_PROTOCOLS}); a parameter left blank keeps its `{name}`
118
130
  * template so the sample still reads as a template rather than a broken
119
131
  * address. Payload text that doesn't parse yields no payload — the editor is
120
132
  * showing the reader a validation error at that moment, and neither a sample
@@ -126,6 +138,9 @@ export const buildMessage = (
126
138
  ): MessageSample => {
127
139
  const custom = values.customUrl.trim();
128
140
  const picked = model.servers[values.server] ?? model.servers[0];
141
+ const encode = Object.hasOwn(URL_PROTOCOLS, model.protocol ?? "")
142
+ ? encodeURIComponent
143
+ : (value: string) => value;
129
144
  let payload: unknown;
130
145
  try {
131
146
  // SAFETY: JSON.parse returns `any`; widening it to `unknown` claims
@@ -138,7 +153,7 @@ export const buildMessage = (
138
153
  action: model.action,
139
154
  address: model.address.replaceAll(PARAM_TEMPLATE, (template, name) => {
140
155
  const value = values.params[String(name)] ?? "";
141
- return value === "" ? template : encodeURIComponent(value);
156
+ return value === "" ? template : encode(value);
142
157
  }),
143
158
  payload,
144
159
  server: custom === "" ? picked?.server : customServer(custom),
@@ -6,14 +6,21 @@ import {
6
6
  resolveSchema,
7
7
  toJson,
8
8
  } from "./helpers.ts";
9
- import type { ParameterLike, SchemaLike, SpecValue } from "./helpers.ts";
9
+ import type {
10
+ ComponentsLike,
11
+ ParameterLike,
12
+ SchemaLike,
13
+ SpecValue,
14
+ } from "./helpers.ts";
10
15
  import {
11
16
  declaredTypes,
12
17
  inputValue,
13
18
  scalarType,
14
19
  validationSchema,
15
20
  } from "./playground-schema.ts";
21
+ import { bodyEncoding } from "./request.ts";
16
22
  import type {
23
+ ParamSerialization,
17
24
  PlaygroundAuthInput,
18
25
  PlaygroundBody,
19
26
  PlaygroundBodyField,
@@ -34,6 +41,7 @@ interface MediaTypeLike {
34
41
  schema?: SchemaLike;
35
42
  example?: SpecValue;
36
43
  examples?: SpecValue;
44
+ encoding?: SpecValue;
37
45
  }
38
46
 
39
47
  /** A server entry: its URL template and the variables that fill it. */
@@ -70,6 +78,45 @@ const isExampleObject = (
70
78
  ): value is Record<string, SpecValue> =>
71
79
  typeof value === "object" && value !== null && !Array.isArray(value);
72
80
 
81
+ const isString = (value: SpecValue): value is string =>
82
+ typeof value === "string";
83
+
84
+ const isBoolean = (value: SpecValue): value is boolean =>
85
+ typeof value === "boolean";
86
+
87
+ /**
88
+ * The `style`/`explode` a parameter or form-body encoding declares, kept only
89
+ * when declared so the embedded model stays small; the request builder
90
+ * applies the OpenAPI defaults for the rest.
91
+ */
92
+ const serialization = (node: Record<string, SpecValue>): ParamSerialization => {
93
+ const rule: ParamSerialization = {};
94
+ if (isString(node.style)) {
95
+ rule.style = node.style;
96
+ }
97
+ if (isBoolean(node.explode)) {
98
+ rule.explode = node.explode;
99
+ }
100
+ return rule;
101
+ };
102
+
103
+ /** A form body's declared per-field `encoding`, when it names any style. */
104
+ const formEncoding = (
105
+ encoding: SpecValue
106
+ ): PlaygroundBody["encoding"] | undefined => {
107
+ if (!isExampleObject(encoding)) {
108
+ return undefined;
109
+ }
110
+ const fields: NonNullable<PlaygroundBody["encoding"]> = {};
111
+ for (const [name, entry] of Object.entries(encoding)) {
112
+ const rule = isExampleObject(entry) ? serialization(entry) : {};
113
+ if (Object.keys(rule).length > 0) {
114
+ fields[name] = rule;
115
+ }
116
+ }
117
+ return Object.keys(fields).length > 0 ? fields : undefined;
118
+ };
119
+
73
120
  /**
74
121
  * Playground inputs for the operation's parameters. Cookie params are skipped
75
122
  * (not supported in v1 — browsers won't let a page set arbitrary cookies).
@@ -80,7 +127,8 @@ const isExampleObject = (
80
127
  */
81
128
  const modelParams = (
82
129
  parameters: ParameterLike[],
83
- schemas: Record<string, SchemaLike>
130
+ schemas: Record<string, SchemaLike>,
131
+ components: ComponentsLike | undefined
84
132
  ): PlaygroundParam[] => {
85
133
  const params: PlaygroundParam[] = [];
86
134
  for (const param of parameters) {
@@ -102,9 +150,11 @@ const modelParams = (
102
150
  type: scalarType(param.schema, schemas),
103
151
  value: required
104
152
  ? inputValue(
105
- declaredExample(param) ?? exampleValue(param.schema, schemas)
153
+ declaredExample(param, components) ??
154
+ exampleValue(param.schema, schemas)
106
155
  )
107
156
  : "",
157
+ ...serialization(param),
108
158
  });
109
159
  }
110
160
  return params;
@@ -165,10 +215,16 @@ const bodyFields = (
165
215
  return fields;
166
216
  };
167
217
 
168
- /** The playground's body editor state, when the operation takes a request body. */
218
+ /**
219
+ * The playground's body editor state, when the operation takes a request body.
220
+ * JSON, form-urlencoded, and multipart bodies are edited as JSON (typed fields
221
+ * when flat) and serialized for their media type on send; any other media
222
+ * type is raw text, prefilled with a string example as written.
223
+ */
169
224
  const modelBody = (
170
225
  requestBody: { content?: Record<string, MediaTypeLike> } | undefined,
171
- schemas: Record<string, SchemaLike>
226
+ schemas: Record<string, SchemaLike>,
227
+ components: ComponentsLike | undefined
172
228
  ): PlaygroundBody | undefined => {
173
229
  const media = jsonContentType(requestBody?.content);
174
230
  if (!media) {
@@ -176,13 +232,25 @@ const modelBody = (
176
232
  }
177
233
  const [contentType, mediaType] = media;
178
234
  const exampleData =
179
- declaredExample(mediaType) ?? exampleValue(mediaType.schema, schemas);
180
- return {
235
+ declaredExample(mediaType, components) ??
236
+ exampleValue(mediaType.schema, schemas);
237
+ const encoding = bodyEncoding(contentType);
238
+ const raw = encoding === "raw";
239
+ const body: PlaygroundBody = {
181
240
  contentType,
182
- example: toJson(exampleData) ?? "",
183
- fields: bodyFields(mediaType.schema, schemas, exampleData),
241
+ example:
242
+ raw && isString(exampleData) ? exampleData : (toJson(exampleData) ?? ""),
243
+ fields: raw
244
+ ? undefined
245
+ : bodyFields(mediaType.schema, schemas, exampleData),
184
246
  schema: validationSchema(mediaType.schema, schemas),
185
247
  };
248
+ const fieldEncoding =
249
+ encoding === "form" ? formEncoding(mediaType.encoding) : undefined;
250
+ if (fieldEncoding) {
251
+ body.encoding = fieldEncoding;
252
+ }
253
+ return body;
186
254
  };
187
255
 
188
256
  const AUTHORIZATION_HEADER = { in: "header", name: "Authorization" } as const;
@@ -272,6 +340,8 @@ export const operationModel = (args: {
272
340
  /** The operation's effective servers (`effectiveServers` output). */
273
341
  servers: ServerLike[];
274
342
  schemas: Record<string, SchemaLike>;
343
+ /** The document's `components`, which `$ref`'d examples resolve against. */
344
+ components?: ComponentsLike;
275
345
  security: OperationSecurity;
276
346
  }): PlaygroundModel => ({
277
347
  // First alternative only — the spec's preferred way to authorize, matching
@@ -281,9 +351,9 @@ export const operationModel = (args: {
281
351
  return input ? [input] : [];
282
352
  }),
283
353
  authOptional: args.security.optional,
284
- body: modelBody(args.requestBody, args.schemas),
354
+ body: modelBody(args.requestBody, args.schemas, args.components),
285
355
  method: args.method.toUpperCase(),
286
- params: modelParams(args.parameters, args.schemas),
356
+ params: modelParams(args.parameters, args.schemas, args.components),
287
357
  path: args.path,
288
358
  // Variables resolve to their defaults: the samples and Send need a real URL.
289
359
  servers: args.servers.map((server) =>
@@ -40,10 +40,12 @@ class BlumePanelTabs extends HTMLElement {
40
40
  // wrap, Home and End jump to the ends, and focus follows selection.
41
41
  tab.addEventListener("keydown", (event) => {
42
42
  const last = tabs.length - 1;
43
+ // The strip runs right to left under dir="rtl", so the arrows swap.
44
+ const rtl = getComputedStyle(this).direction === "rtl";
43
45
  let next: number | undefined;
44
- if (event.key === "ArrowRight") {
46
+ if (event.key === (rtl ? "ArrowLeft" : "ArrowRight")) {
45
47
  next = index === last ? 0 : index + 1;
46
- } else if (event.key === "ArrowLeft") {
48
+ } else if (event.key === (rtl ? "ArrowRight" : "ArrowLeft")) {
47
49
  next = index === 0 ? last : index - 1;
48
50
  } else if (event.key === "Home") {
49
51
  next = 0;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * OpenAPI 3 parameter serialization — `style` and `explode` — for the
3
+ * playground's one request builder (`request.ts`). An array or object value
4
+ * has a different wire shape per style: `tags: ["dog", "cat"]` is
5
+ * `tags=dog&tags=cat` in a query (form, exploded), `dog,cat` in a path
6
+ * (simple), and `filter: { color: "red" }` is `filter[color]=red` as a
7
+ * deepObject. Ships in the client bundle with `request.ts`, so it stays
8
+ * dependency-free.
9
+ */
10
+
11
+ /** Parsed JSON: everything `JSON.parse` can produce. */
12
+ export type JsonValue =
13
+ | string
14
+ | number
15
+ | boolean
16
+ | null
17
+ | JsonValue[]
18
+ | { [key: string]: JsonValue };
19
+
20
+ /** A value lowered for serialization: a scalar, a list's items, or an object's entries. */
21
+ export type StyledValue =
22
+ | { kind: "scalar"; text: string }
23
+ | { kind: "list"; items: string[] }
24
+ | { kind: "object"; entries: [string, string][] };
25
+
26
+ // `typeof` checks live in named predicates (the form the oxlint anti-slop
27
+ // config sanctions).
28
+ const isString = (value: JsonValue): value is string =>
29
+ typeof value === "string";
30
+
31
+ /** Whether a parsed value is a plain JSON object. */
32
+ export const isJsonObject = (
33
+ value: JsonValue
34
+ ): value is { [key: string]: JsonValue } =>
35
+ typeof value === "object" && value !== null && !Array.isArray(value);
36
+
37
+ /** One member as wire text: a string verbatim, anything else as its JSON. */
38
+ export const memberText = (value: JsonValue): string =>
39
+ isString(value) ? value : JSON.stringify(value);
40
+
41
+ /** A parsed value, lowered: arrays to items, objects to entries, the rest to text. */
42
+ export const styledValue = (value: JsonValue): StyledValue => {
43
+ if (Array.isArray(value)) {
44
+ return { items: value.map(memberText), kind: "list" };
45
+ }
46
+ if (isJsonObject(value)) {
47
+ return {
48
+ entries: Object.entries(value).map(([key, member]) => [
49
+ key,
50
+ memberText(member),
51
+ ]),
52
+ kind: "object",
53
+ };
54
+ }
55
+ return { kind: "scalar", text: memberText(value) };
56
+ };
57
+
58
+ /**
59
+ * A playground input's text as a styled value. Only an array- or object-typed
60
+ * parameter reads its text as JSON — its prefill is the example's JSON
61
+ * (`["dog","cat"]`) — and only when the text parses to that shape; anything
62
+ * else is sent as the one scalar it reads as.
63
+ */
64
+ export const parseStyledValue = (text: string, type: string): StyledValue => {
65
+ if (type === "array" || type === "object") {
66
+ let parsed: JsonValue;
67
+ try {
68
+ parsed = JSON.parse(text);
69
+ } catch {
70
+ return { kind: "scalar", text };
71
+ }
72
+ if (type === "array" ? Array.isArray(parsed) : isJsonObject(parsed)) {
73
+ return styledValue(parsed);
74
+ }
75
+ }
76
+ return { kind: "scalar", text };
77
+ };
78
+
79
+ /** The OpenAPI 3 default style: `form` in a query or cookie, `simple` elsewhere. */
80
+ export const defaultStyle = (location: string): string =>
81
+ location === "query" || location === "cookie" ? "form" : "simple";
82
+
83
+ /** `explode` when the spec leaves it out: true for `form`, false for the rest. */
84
+ export const defaultExplode = (style: string): boolean => style === "form";
85
+
86
+ /**
87
+ * A list or object joined into one value: exploded object entries as
88
+ * `key=value` separated by `separator`, exploded items by `separator`, and
89
+ * the unexploded forms comma-separated (`a,b`, `key,value,key2,value2`).
90
+ */
91
+ const joined = (
92
+ value: StyledValue,
93
+ explode: boolean,
94
+ encode: (text: string) => string,
95
+ separator: string
96
+ ): string => {
97
+ if (value.kind === "scalar") {
98
+ return encode(value.text);
99
+ }
100
+ if (value.kind === "list") {
101
+ return value.items.map(encode).join(explode ? separator : ",");
102
+ }
103
+ return explode
104
+ ? value.entries
105
+ .map(([key, member]) => `${encode(key)}=${encode(member)}`)
106
+ .join(separator)
107
+ : value.entries.flat().map(encode).join(",");
108
+ };
109
+
110
+ /**
111
+ * A query parameter, or an `application/x-www-form-urlencoded` body field, as
112
+ * its percent-encoded `name=value` pairs in order. Exploded lists repeat the
113
+ * name (`tags=dog&tags=cat`) and exploded objects spread their entries
114
+ * (`color=red&size=L`) whatever the style; unexploded ones join with the
115
+ * style's delimiter — `,` for form, `%20` for spaceDelimited, `|` for
116
+ * pipeDelimited. deepObject nests entries under the name (`filter[color]=red`).
117
+ */
118
+ export const queryPairs = (
119
+ name: string,
120
+ value: StyledValue,
121
+ style: string,
122
+ explode: boolean
123
+ ): string[] => {
124
+ const key = encodeURIComponent(name);
125
+ if (value.kind === "object" && style === "deepObject") {
126
+ return value.entries.map(
127
+ ([entry, member]) =>
128
+ `${key}[${encodeURIComponent(entry)}]=${encodeURIComponent(member)}`
129
+ );
130
+ }
131
+ if (value.kind === "list" && explode) {
132
+ return value.items.map((item) => `${key}=${encodeURIComponent(item)}`);
133
+ }
134
+ if (value.kind === "object" && explode) {
135
+ return value.entries.map(
136
+ ([entry, member]) =>
137
+ `${encodeURIComponent(entry)}=${encodeURIComponent(member)}`
138
+ );
139
+ }
140
+ let delimiter = ",";
141
+ if (style === "spaceDelimited") {
142
+ delimiter = "%20";
143
+ } else if (style === "pipeDelimited") {
144
+ delimiter = "|";
145
+ }
146
+ const text =
147
+ value.kind === "scalar"
148
+ ? encodeURIComponent(value.text)
149
+ : (value.kind === "list" ? value.items : value.entries.flat())
150
+ .map(encodeURIComponent)
151
+ .join(delimiter);
152
+ return [`${key}=${text}`];
153
+ };
154
+
155
+ /**
156
+ * A path or header parameter's value: `simple` (`dog,cat`), or one of the
157
+ * path-only `label` (`.dog.cat`) and `matrix` (`;tags=dog;tags=cat`) styles.
158
+ * `encode` percent-encodes a path segment; a header passes values through.
159
+ */
160
+ export const templateValue = (
161
+ name: string,
162
+ value: StyledValue,
163
+ style: string,
164
+ explode: boolean,
165
+ encode: (text: string) => string
166
+ ): string => {
167
+ if (style === "label") {
168
+ return `.${joined(value, explode, encode, ".")}`;
169
+ }
170
+ if (style !== "matrix") {
171
+ return joined(value, explode, encode, ",");
172
+ }
173
+ const key = encode(name);
174
+ if (value.kind === "list" && explode) {
175
+ return value.items.map((item) => `;${key}=${encode(item)}`).join("");
176
+ }
177
+ if (value.kind === "object" && explode) {
178
+ return `;${joined(value, true, encode, ";")}`;
179
+ }
180
+ return `;${key}=${joined(value, false, encode, ",")}`;
181
+ };