blume 2.1.0 → 2.1.2

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 (450) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  3. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  4. package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
  5. package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
  6. package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
  7. package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
  8. package/dist/cli/chunk-124fvmzq.js.map +220 -0
  9. package/dist/cli/chunk-150jk7km.js +52 -0
  10. package/dist/cli/chunk-150jk7km.js.map +10 -0
  11. package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
  12. package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
  13. package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
  14. package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
  15. package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
  16. package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
  17. package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
  18. package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
  19. package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
  20. package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
  21. package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
  22. package/dist/cli/chunk-60hz265r.js.map +10 -0
  23. package/dist/cli/chunk-74wfhv79.js +65 -0
  24. package/dist/cli/chunk-74wfhv79.js.map +10 -0
  25. package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
  26. package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
  27. package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
  28. package/dist/cli/chunk-ave1wcbr.js.map +14 -0
  29. package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
  30. package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
  31. package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
  32. package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
  33. package/dist/cli/chunk-ckrcm08h.js +324 -0
  34. package/dist/cli/chunk-ckrcm08h.js.map +14 -0
  35. package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
  36. package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
  37. package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
  38. package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
  39. package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
  40. package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
  41. package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
  42. package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
  43. package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
  44. package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
  45. package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
  46. package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
  47. package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
  48. package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
  49. package/dist/cli/chunk-j1x92mx9.js.map +10 -0
  50. package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
  51. package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
  52. package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
  53. package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
  54. package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
  55. package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
  56. package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
  57. package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
  58. package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
  59. package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
  60. package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
  61. package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
  62. package/dist/cli/chunk-twy7psdz.js.map +10 -0
  63. package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
  64. package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
  65. package/dist/cli/index.js +24 -19
  66. package/dist/cli/index.js.map +3 -3
  67. package/dist/types/ai/ask-context.d.ts +6 -2
  68. package/dist/types/ai/ask.d.ts +24 -5
  69. package/dist/types/ai/component-markdown.d.ts +2 -0
  70. package/dist/types/ai/openapi-components.d.ts +4 -1
  71. package/dist/types/analytics/clics.d.ts +37 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +18 -0
  74. package/dist/types/astro/integration.d.ts +11 -4
  75. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  76. package/dist/types/astro/pages.d.ts +10 -0
  77. package/dist/types/cli/init/scaffold.d.ts +1 -1
  78. package/dist/types/components/content/api-field.d.ts +4 -1
  79. package/dist/types/components/content/api-page.d.ts +6 -1
  80. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  81. package/dist/types/components/openapi/helpers.d.ts +22 -1
  82. package/dist/types/components/openapi/security.d.ts +10 -0
  83. package/dist/types/components/openapi/snippets.d.ts +60 -0
  84. package/dist/types/core/changelog-index.d.ts +8 -0
  85. package/dist/types/core/config-input.d.ts +37 -6
  86. package/dist/types/core/data.d.ts +4 -0
  87. package/dist/types/core/i18n-ui.d.ts +2 -0
  88. package/dist/types/core/links.d.ts +8 -0
  89. package/dist/types/core/powered-by.d.ts +12 -0
  90. package/dist/types/core/probe.d.ts +2 -0
  91. package/dist/types/core/project-graph.d.ts +6 -0
  92. package/dist/types/core/redirect-patterns.d.ts +11 -3
  93. package/dist/types/core/schema.d.ts +228 -3
  94. package/dist/types/core/sources/cache.d.ts +3 -0
  95. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  96. package/dist/types/core/sources/contentful.d.ts +17 -1
  97. package/dist/types/core/sources/lexical.d.ts +5 -0
  98. package/dist/types/core/sources/lower.d.ts +10 -2
  99. package/dist/types/core/sources/normalize.d.ts +20 -1
  100. package/dist/types/core/sources/notion.d.ts +8 -0
  101. package/dist/types/core/sources/remote.d.ts +37 -10
  102. package/dist/types/core/sources/sanity.d.ts +1 -1
  103. package/dist/types/core/types.d.ts +17 -0
  104. package/dist/types/deploy/adapter-output.d.ts +2 -1
  105. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  106. package/dist/types/deploy/adapters/node.d.ts +58 -23
  107. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  108. package/dist/types/deploy/adapters/types.d.ts +3 -2
  109. package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
  110. package/dist/types/deploy/node-headers.d.ts +12 -4
  111. package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
  112. package/dist/types/deploy/platforms/node.d.ts +3 -1
  113. package/dist/types/deploy/platforms/types.d.ts +18 -7
  114. package/dist/types/deploy/platforms/vercel.d.ts +21 -2
  115. package/dist/types/deploy/redirects.d.ts +17 -1
  116. package/dist/types/deploy/sitemap.d.ts +2 -1
  117. package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
  118. package/dist/types/narration/build.d.ts +34 -12
  119. package/dist/types/narration/provider.d.ts +96 -8
  120. package/dist/types/openapi/checks.d.ts +27 -0
  121. package/dist/types/openapi/graphql.d.ts +6 -0
  122. package/dist/types/openapi/model.d.ts +15 -4
  123. package/dist/types/openapi/parse.d.ts +3 -0
  124. package/dist/types/openapi/references.d.ts +16 -0
  125. package/dist/types/openapi/sentence.d.ts +11 -1
  126. package/dist/types/ratelimit/index.d.ts +1 -1
  127. package/dist/types/ratelimit/schema.d.ts +8 -0
  128. package/dist/types/ratelimit/upstash.d.ts +39 -14
  129. package/dist/types/reference/graphql.d.ts +76 -1
  130. package/dist/types/reference/index.d.ts +1 -1
  131. package/dist/types/reference/openapi.d.ts +2 -2
  132. package/dist/types/reference/options.d.ts +37 -1
  133. package/dist/types/reference/schema.d.ts +84 -0
  134. package/dist/types/search/adapters/registry.d.ts +3 -0
  135. package/dist/types/search/adapters/typesense.d.ts +14 -4
  136. package/dist/types/search/build.d.ts +3 -1
  137. package/dist/types/search/orama-index.d.ts +9 -9
  138. package/dist/types/search/sync/algolia.d.ts +5 -2
  139. package/dist/types/search/sync/index.d.ts +3 -1
  140. package/dist/types/search/sync/typesense.d.ts +11 -7
  141. package/dist/types/seo/jsonld.d.ts +11 -6
  142. package/dist/types/sources/contentful.d.ts +16 -0
  143. package/dist/types/sources/github-releases.d.ts +8 -1
  144. package/dist/types/sources/notion.d.ts +8 -0
  145. package/dist/types/sources/payload.d.ts +2 -0
  146. package/dist/types/sources/registry.d.ts +63 -0
  147. package/dist/types/sources/shared.d.ts +3 -0
  148. package/dist/types/sources/strapi.d.ts +2 -0
  149. package/docs/01-quickstart.mdx +4 -2
  150. package/docs/02-deployment.mdx +59 -13
  151. package/docs/03-upgrading.mdx +2 -1
  152. package/docs/04-migrating.mdx +9 -9
  153. package/docs/08-faq.mdx +12 -12
  154. package/docs/advanced/changelog.mdx +30 -2
  155. package/docs/advanced/custom-pages.mdx +1 -1
  156. package/docs/advanced/skills.mdx +1 -1
  157. package/docs/cli/audit.mdx +30 -7
  158. package/docs/cli/doctor.mdx +6 -2
  159. package/docs/cli/evals.mdx +4 -4
  160. package/docs/cli/translate.mdx +1 -0
  161. package/docs/cli/validate.mdx +4 -3
  162. package/docs/configuration/analytics.mdx +19 -1
  163. package/docs/configuration/assistant.mdx +6 -2
  164. package/docs/configuration/customization.mdx +4 -2
  165. package/docs/configuration/export.mdx +5 -3
  166. package/docs/configuration/index.mdx +4 -2
  167. package/docs/configuration/narration.mdx +43 -6
  168. package/docs/configuration/rate-limiting.mdx +23 -2
  169. package/docs/configuration/search.mdx +37 -14
  170. package/docs/content/components.mdx +2 -2
  171. package/docs/content/frontmatter.mdx +5 -3
  172. package/docs/content/i18n.mdx +2 -2
  173. package/docs/content/includes.mdx +1 -1
  174. package/docs/content/navigation.mdx +8 -6
  175. package/docs/content/sources/contentful.mdx +9 -4
  176. package/docs/content/sources/custom.mdx +7 -3
  177. package/docs/content/sources/github-releases.mdx +12 -2
  178. package/docs/content/sources/index.mdx +3 -3
  179. package/docs/content/sources/notion.mdx +17 -2
  180. package/docs/content/sources/obsidian.mdx +3 -1
  181. package/docs/content/sources/payload.mdx +5 -2
  182. package/docs/content/sources/remote-mdx.mdx +6 -0
  183. package/docs/content/sources/sanity.mdx +4 -2
  184. package/docs/content/sources/strapi.mdx +5 -2
  185. package/docs/content/syntax.mdx +2 -2
  186. package/docs/discoverability/agent-discovery.mdx +1 -1
  187. package/docs/discoverability/index.mdx +1 -1
  188. package/docs/discoverability/json-api.mdx +2 -0
  189. package/docs/discoverability/llms-txt.mdx +1 -1
  190. package/docs/discoverability/markdown.mdx +3 -1
  191. package/docs/discoverability/mcp.mdx +6 -0
  192. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  193. package/docs/discoverability/structured-data.mdx +8 -6
  194. package/docs/references/api-pages.mdx +1 -1
  195. package/docs/references/asyncapi.mdx +5 -1
  196. package/docs/references/graphql.mdx +15 -1
  197. package/docs/references/openapi.mdx +64 -8
  198. package/package.json +9 -9
  199. package/skills/blume/SKILL.md +1 -1
  200. package/skills/blume-migrate/SKILL.md +2 -2
  201. package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
  202. package/skills/blume-migrate/references/docusaurus.md +4 -4
  203. package/skills/blume-migrate/references/fumadocs.md +17 -13
  204. package/skills/blume-migrate/references/monorepo.md +5 -5
  205. package/skills/blume-migrate/references/nextra.md +8 -5
  206. package/skills/blume-migrate/references/starlight.md +9 -7
  207. package/src/ai/ask-context.ts +33 -3
  208. package/src/ai/ask-limits.ts +13 -0
  209. package/src/ai/ask.ts +24 -6
  210. package/src/ai/changelog-markdown.ts +11 -6
  211. package/src/ai/component-markdown.ts +1 -1
  212. package/src/ai/cors.ts +12 -3
  213. package/src/ai/mcp/query.ts +14 -6
  214. package/src/ai/openapi-components.ts +28 -4
  215. package/src/ai/relative-links.ts +4 -38
  216. package/src/analytics/clics.ts +58 -0
  217. package/src/analytics/head.ts +5 -0
  218. package/src/analytics/index.ts +2 -0
  219. package/src/analytics/schema.ts +2 -0
  220. package/src/astro/generate.ts +91 -56
  221. package/src/astro/integration.ts +72 -22
  222. package/src/astro/markdown-negotiation.ts +3 -0
  223. package/src/astro/module-types.ts +3 -1
  224. package/src/astro/pages.ts +23 -0
  225. package/src/astro/render-deps.ts +10 -5
  226. package/src/astro/runtime-deps.ts +23 -2
  227. package/src/astro/templates.ts +129 -81
  228. package/src/audit/catalog.ts +19 -3
  229. package/src/audit/checks/links.ts +14 -1
  230. package/src/audit/checks/network.ts +110 -6
  231. package/src/audit/checks/redirects.ts +6 -4
  232. package/src/audit/checks/robots.ts +58 -2
  233. package/src/audit/run.ts +3 -0
  234. package/src/audit/types.ts +2 -0
  235. package/src/audit/url.ts +14 -5
  236. package/src/blume-modules.d.ts +5 -1
  237. package/src/cli/args.ts +51 -0
  238. package/src/cli/build-failure.ts +23 -0
  239. package/src/cli/commands/audit.ts +10 -1
  240. package/src/cli/commands/dev.ts +18 -2
  241. package/src/cli/commands/doctor.ts +45 -12
  242. package/src/cli/commands/eval.ts +36 -4
  243. package/src/cli/commands/init.ts +6 -4
  244. package/src/cli/commands/validate.ts +10 -1
  245. package/src/cli/init/scaffold.ts +9 -6
  246. package/src/cli/prepare.ts +6 -3
  247. package/src/cli/required-secrets.ts +3 -2
  248. package/src/components/content/ApiPlayground.astro +6 -2
  249. package/src/components/content/api-field.ts +4 -1
  250. package/src/components/content/api-page.ts +31 -7
  251. package/src/components/content/mermaid-element.ts +15 -1
  252. package/src/components/content/view-switcher.ts +7 -4
  253. package/src/components/islands/hooks.ts +27 -1
  254. package/src/components/layout/NavTree.astro +49 -5
  255. package/src/components/layout/NavTreeScript.astro +18 -0
  256. package/src/components/layout/PageActions.astro +139 -24
  257. package/src/components/layout/PageLayout.astro +13 -3
  258. package/src/components/layout/RootLayout.astro +27 -14
  259. package/src/components/layout/Search.astro +17 -3
  260. package/src/components/layout/TableOfContents.astro +24 -17
  261. package/src/components/layout/analytics-client.ts +8 -8
  262. package/src/components/layout/head-scripts.ts +6 -1
  263. package/src/components/layout/search/algolia.ts +10 -1
  264. package/src/components/layout/search/analytics.ts +42 -14
  265. package/src/components/layout/search/endpoint.ts +48 -5
  266. package/src/components/layout/search/pagefind.ts +114 -5
  267. package/src/components/layout/search/types.ts +12 -0
  268. package/src/components/layout/search/typesense.ts +18 -6
  269. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  270. package/src/components/openapi/Authorization.astro +10 -0
  271. package/src/components/openapi/GraphqlOperation.astro +5 -0
  272. package/src/components/openapi/Operation.astro +24 -1
  273. package/src/components/openapi/ParametersTable.astro +8 -4
  274. package/src/components/openapi/Playground.astro +7 -1
  275. package/src/components/openapi/RequestBody.astro +19 -2
  276. package/src/components/openapi/Responses.astro +1 -0
  277. package/src/components/openapi/SchemaTable.astro +15 -3
  278. package/src/components/openapi/async-snippets.ts +61 -5
  279. package/src/components/openapi/async.ts +123 -29
  280. package/src/components/openapi/graphql-helpers.ts +35 -12
  281. package/src/components/openapi/helpers.ts +36 -2
  282. package/src/components/openapi/message-model.ts +54 -11
  283. package/src/components/openapi/message.ts +14 -4
  284. package/src/components/openapi/operation-model.ts +16 -7
  285. package/src/components/openapi/playground-client.ts +77 -58
  286. package/src/components/openapi/playground-schema.ts +1 -1
  287. package/src/components/openapi/schema-tree.ts +44 -14
  288. package/src/components/openapi/security.ts +10 -0
  289. package/src/components/openapi/snippets.ts +5 -3
  290. package/src/components/openapi/validate-json.ts +4 -3
  291. package/src/components/powered-by-middleware.ts +33 -0
  292. package/src/core/changelog-index.ts +31 -1
  293. package/src/core/component-diagnostics.ts +53 -0
  294. package/src/core/component-overrides.ts +32 -0
  295. package/src/core/config-input.ts +42 -6
  296. package/src/core/data.ts +2 -0
  297. package/src/core/diagnostics.ts +5 -0
  298. package/src/core/i18n-ui.ts +2 -0
  299. package/src/core/includes.ts +1 -1
  300. package/src/core/links.ts +47 -17
  301. package/src/core/powered-by.ts +12 -0
  302. package/src/core/probe.ts +26 -6
  303. package/src/core/project-graph.ts +59 -18
  304. package/src/core/redirect-patterns.ts +36 -7
  305. package/src/core/schema.ts +26 -6
  306. package/src/core/sources/cache.ts +6 -0
  307. package/src/core/sources/contentful-rich-text.ts +29 -6
  308. package/src/core/sources/contentful.ts +135 -25
  309. package/src/core/sources/filesystem.ts +26 -4
  310. package/src/core/sources/github-releases.ts +12 -1
  311. package/src/core/sources/lexical.ts +15 -10
  312. package/src/core/sources/lower.ts +19 -3
  313. package/src/core/sources/mdx-remote.ts +62 -10
  314. package/src/core/sources/normalize.ts +227 -41
  315. package/src/core/sources/notion.ts +34 -10
  316. package/src/core/sources/obsidian.ts +16 -3
  317. package/src/core/sources/payload.ts +38 -15
  318. package/src/core/sources/remote.ts +72 -15
  319. package/src/core/sources/resolve.ts +11 -1
  320. package/src/core/sources/sanity.ts +43 -25
  321. package/src/core/sources/strapi.ts +2 -2
  322. package/src/core/types.ts +18 -0
  323. package/src/core/ui-packs/ar.ts +1 -0
  324. package/src/core/ui-packs/bg.ts +1 -0
  325. package/src/core/ui-packs/bn.ts +1 -0
  326. package/src/core/ui-packs/ca.ts +1 -0
  327. package/src/core/ui-packs/cs.ts +1 -0
  328. package/src/core/ui-packs/da.ts +1 -0
  329. package/src/core/ui-packs/de.ts +1 -0
  330. package/src/core/ui-packs/el.ts +1 -0
  331. package/src/core/ui-packs/es.ts +1 -0
  332. package/src/core/ui-packs/fa.ts +1 -0
  333. package/src/core/ui-packs/fi.ts +1 -0
  334. package/src/core/ui-packs/fr.ts +1 -0
  335. package/src/core/ui-packs/he.ts +1 -0
  336. package/src/core/ui-packs/hi.ts +1 -0
  337. package/src/core/ui-packs/hr.ts +1 -0
  338. package/src/core/ui-packs/hu.ts +1 -0
  339. package/src/core/ui-packs/id.ts +1 -0
  340. package/src/core/ui-packs/it.ts +1 -0
  341. package/src/core/ui-packs/ja.ts +1 -0
  342. package/src/core/ui-packs/ko.ts +1 -0
  343. package/src/core/ui-packs/nl.ts +1 -0
  344. package/src/core/ui-packs/no.ts +1 -0
  345. package/src/core/ui-packs/pl.ts +1 -0
  346. package/src/core/ui-packs/pt-br.ts +1 -0
  347. package/src/core/ui-packs/pt.ts +1 -0
  348. package/src/core/ui-packs/ro.ts +1 -0
  349. package/src/core/ui-packs/ru.ts +1 -0
  350. package/src/core/ui-packs/sk.ts +1 -0
  351. package/src/core/ui-packs/sr.ts +1 -0
  352. package/src/core/ui-packs/sv.ts +1 -0
  353. package/src/core/ui-packs/th.ts +1 -0
  354. package/src/core/ui-packs/tr.ts +1 -0
  355. package/src/core/ui-packs/uk.ts +1 -0
  356. package/src/core/ui-packs/vi.ts +1 -0
  357. package/src/core/ui-packs/zh-tw.ts +1 -0
  358. package/src/core/ui-packs/zh.ts +1 -0
  359. package/src/deploy/adapter-output.ts +7 -2
  360. package/src/deploy/adapters/cloudflare.ts +7 -5
  361. package/src/deploy/adapters/node.ts +47 -9
  362. package/src/deploy/adapters/types.ts +7 -3
  363. package/src/deploy/artifacts.ts +2 -1
  364. package/src/deploy/cloudflare-negotiation.ts +36 -7
  365. package/src/deploy/headers.ts +15 -1
  366. package/src/deploy/node-headers.ts +32 -20
  367. package/src/deploy/platforms/cloudflare.ts +14 -9
  368. package/src/deploy/platforms/netlify.ts +2 -1
  369. package/src/deploy/platforms/node.ts +12 -2
  370. package/src/deploy/platforms/types.ts +21 -7
  371. package/src/deploy/platforms/vercel.ts +115 -17
  372. package/src/deploy/redirects.ts +66 -6
  373. package/src/deploy/rss.ts +3 -1
  374. package/src/deploy/sitemap.ts +39 -5
  375. package/src/deploy/vercel-negotiation.ts +325 -103
  376. package/src/eval/agents.ts +18 -1
  377. package/src/eval/prompts.ts +6 -3
  378. package/src/eval/report.ts +34 -13
  379. package/src/eval/run.ts +5 -0
  380. package/src/markdown/base-links.ts +15 -14
  381. package/src/markdown/package-commands.ts +9 -8
  382. package/src/markdown/relative-links.ts +3 -2
  383. package/src/narration/build.ts +218 -40
  384. package/src/narration/provider.ts +89 -9
  385. package/src/openapi/checks.ts +273 -0
  386. package/src/openapi/graphql-build.ts +21 -0
  387. package/src/openapi/graphql.ts +11 -0
  388. package/src/openapi/model.ts +15 -3
  389. package/src/openapi/parse.ts +127 -2
  390. package/src/openapi/references.ts +43 -3
  391. package/src/openapi/render-mdx.ts +68 -8
  392. package/src/openapi/sentence.ts +29 -2
  393. package/src/openapi/source.ts +39 -14
  394. package/src/ratelimit/index.ts +1 -1
  395. package/src/ratelimit/runtime.ts +37 -19
  396. package/src/ratelimit/upstash.ts +40 -10
  397. package/src/reference/graphql.ts +15 -1
  398. package/src/reference/index.ts +1 -0
  399. package/src/reference/openapi.ts +2 -2
  400. package/src/reference/options.ts +29 -1
  401. package/src/registry/eject.ts +8 -2
  402. package/src/search/adapters/locale-scope.ts +30 -0
  403. package/src/search/adapters/typesense.ts +14 -4
  404. package/src/search/build.ts +21 -3
  405. package/src/search/orama-index.ts +163 -31
  406. package/src/search/source-pages.ts +29 -0
  407. package/src/search/sync/algolia.ts +99 -9
  408. package/src/search/sync/index.ts +52 -13
  409. package/src/search/sync/typesense.ts +72 -27
  410. package/src/seo/jsonld.ts +38 -20
  411. package/src/sources/contentful.ts +12 -0
  412. package/src/sources/github-releases.ts +7 -1
  413. package/src/sources/notion.ts +7 -0
  414. package/src/sources/shared.ts +3 -0
  415. package/src/theme/color.ts +9 -10
  416. package/src/theme/entry.ts +43 -6
  417. package/src/translate/validate.ts +23 -1
  418. package/dist/cli/chunk-0gz36akr.js.map +0 -12
  419. package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
  420. package/dist/cli/chunk-43zcf4gz.js.map +0 -36
  421. package/dist/cli/chunk-4ejxbzee.js +0 -39
  422. package/dist/cli/chunk-4ejxbzee.js.map +0 -10
  423. package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
  424. package/dist/cli/chunk-713mngdq.js.map +0 -14
  425. package/dist/cli/chunk-9xr4dgee.js.map +0 -10
  426. package/dist/cli/chunk-amrb6hgh.js +0 -145
  427. package/dist/cli/chunk-amrb6hgh.js.map +0 -11
  428. package/dist/cli/chunk-nd1821sk.js.map +0 -10
  429. package/dist/cli/chunk-rekqz4qg.js.map +0 -10
  430. package/dist/cli/chunk-ta27feev.js.map +0 -10
  431. package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
  432. package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
  433. package/dist/cli/chunk-zf748avv.js.map +0 -37
  434. /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
  435. /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
  436. /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
  437. /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
  438. /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
  439. /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
  440. /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
  441. /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
  442. /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
  443. /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
  444. /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
  445. /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
  446. /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
  447. /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
  448. /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
  449. /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
  450. /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
@@ -1,29 +1,109 @@
1
1
  import { z } from "zod";
2
2
 
3
- import { GATEWAY_API_KEY_ENV, sharedOptions } from "../ai/ask.ts";
3
+ import {
4
+ GATEWAY_API_KEY_ENV,
5
+ OPENAI_API_KEY_ENV,
6
+ sharedOptions,
7
+ } from "../ai/ask.ts";
4
8
  import { adapterDescriptorSchema } from "../core/adapter.ts";
5
9
 
6
10
  /** The speech model narration uses when `gateway()` names none. */
7
11
  export const DEFAULT_NARRATION_MODEL = "openai/tts-1-hd";
8
12
 
9
- /** The voice narration uses when `gateway()` names none. */
13
+ /** The voice narration uses when the provider names none. */
10
14
  export const DEFAULT_NARRATION_VOICE = "alloy";
11
15
 
12
16
  /**
13
- * `narration.provider`: the `gateway()` descriptor from `blume/ai`, read with
14
- * speech defaults. `reasoning` is an assistant option and is rejected here;
15
- * `voice` and `instructions` are narration options and are rejected by the
16
- * assistant.
17
+ * The AI SDK provider `openai()` narration speaks through, with or without a
18
+ * `baseUrl`: `@ai-sdk/openai-compatible` has no speech models.
17
19
  */
18
- export const narrationProviderSchema = adapterDescriptorSchema(
20
+ export const OPENAI_SPEECH_DEP = "@ai-sdk/openai";
21
+
22
+ /**
23
+ * Whether `url` carries a username or password, which `fetch` refuses to
24
+ * send and whose error message would print them. A URL that doesn't parse
25
+ * has none.
26
+ */
27
+ export const hasCredentials = (url: string): boolean => {
28
+ const parsed = URL.parse(url);
29
+ return Boolean(parsed?.username || parsed?.password);
30
+ };
31
+
32
+ /** The narration options both providers take on top of the shared ones. */
33
+ const speechOptions = {
34
+ instructions: z.string().min(1).optional(),
35
+ voice: z.string().min(1).default(DEFAULT_NARRATION_VOICE),
36
+ };
37
+
38
+ const gatewayNarrationSchema = adapterDescriptorSchema(
19
39
  "gateway",
20
40
  z.strictObject({
21
41
  ...sharedOptions(GATEWAY_API_KEY_ENV),
22
- instructions: z.string().min(1).optional(),
42
+ ...speechOptions,
23
43
  model: z.string().min(1).default(DEFAULT_NARRATION_MODEL),
24
- voice: z.string().min(1).default(DEFAULT_NARRATION_VOICE),
25
44
  })
26
45
  );
27
46
 
47
+ const openaiNarrationSchema = adapterDescriptorSchema(
48
+ "openai",
49
+ z.strictObject({
50
+ ...sharedOptions(OPENAI_API_KEY_ENV),
51
+ ...speechOptions,
52
+ // Replaces the shared default: whether it's OPENAI_API_KEY depends on
53
+ // the `baseUrl` (see below).
54
+ apiKeyEnv: z.string().min(1).optional(),
55
+ // An unparsable URL is left to `z.url()`.
56
+ baseUrl: z
57
+ .url()
58
+ .refine((url) => !hasCredentials(url), {
59
+ message:
60
+ "Credentials don't go in the baseUrl: name the env var holding the key in apiKeyEnv.",
61
+ })
62
+ .optional(),
63
+ model: z.string().min(1),
64
+ })
65
+ );
66
+
67
+ /**
68
+ * `narration.provider`: the `gateway()` or `openai()` descriptor from
69
+ * `blume/ai`, read with speech defaults. `reasoning` (and `openai()`'s
70
+ * `name`, which speech models don't read) are assistant options and are
71
+ * rejected here; `voice` and `instructions` are narration options and are
72
+ * rejected by the assistant.
73
+ *
74
+ * `openai()` is read for what narration needs, not what the assistant does:
75
+ * it speaks through `@ai-sdk/openai` whatever the `baseUrl`, and a custom
76
+ * endpoint gets only the key `apiKeyEnv` names, never `OPENAI_API_KEY` by
77
+ * default, so it may take none at all. A key that is named is required.
78
+ */
79
+ export const narrationProviderSchema = z
80
+ .discriminatedUnion("kind", [gatewayNarrationSchema, openaiNarrationSchema])
81
+ .transform((provider) => {
82
+ if (provider.kind !== "openai") {
83
+ return provider;
84
+ }
85
+ const { options } = provider;
86
+ const apiKeyEnv =
87
+ options.apiKeyEnv ?? (options.baseUrl ? undefined : OPENAI_API_KEY_ENV);
88
+ return {
89
+ ...provider,
90
+ options: apiKeyEnv ? { ...options, apiKeyEnv } : options,
91
+ requiredSecrets: apiKeyEnv ? [apiKeyEnv] : [],
92
+ runtimeDeps: [OPENAI_SPEECH_DEP],
93
+ };
94
+ });
95
+
28
96
  /** A validated narration provider, defaults filled in. */
29
97
  export type NarrationProvider = z.output<typeof narrationProviderSchema>;
98
+
99
+ /** A validated `gateway()` narration provider. */
100
+ export type GatewayNarrationProvider = Extract<
101
+ NarrationProvider,
102
+ { kind: "gateway" }
103
+ >;
104
+
105
+ /** A validated `openai()` narration provider. */
106
+ export type OpenAINarrationProvider = Extract<
107
+ NarrationProvider,
108
+ { kind: "openai" }
109
+ >;
@@ -0,0 +1,273 @@
1
+ import type { OperationObject } from "@scalar/openapi-types/3.2";
2
+
3
+ import type { ApiDocument } from "./model.ts";
4
+ import { HTTP_METHODS } from "./model.ts";
5
+
6
+ /**
7
+ * Targeted checks for spec mistakes that would otherwise render silently
8
+ * wrong. This is not a validator — a linter such as Spectral or oasdiff owns
9
+ * that — and a spec that fails one of these still renders. Each check names
10
+ * one mistake whose symptom on the page is quiet: a path placeholder sent
11
+ * as literal text, a Try it input whose value goes nowhere, an Authorization
12
+ * section with no scheme to describe. Each reports a warning with its own
13
+ * code, so a build says what's wrong instead of shipping it.
14
+ */
15
+
16
+ /** One mistake a check found, before the source names the spec it's in. */
17
+ export interface SpecIssue {
18
+ code: string;
19
+ message: string;
20
+ suggestion: string;
21
+ }
22
+
23
+ /** A `{name}` placeholder in a path template. */
24
+ const PATH_TEMPLATE = /\{(?<name>[^{}]+)\}/gu;
25
+
26
+ /** A reference to a parameter declared under `components.parameters`. */
27
+ const PARAMETER_REF = /^#\/components\/parameters\/(?<name>[^/]+)$/u;
28
+
29
+ /** How many operations a security-scheme warning names before counting the rest. */
30
+ const NAMED_OPERATIONS = 3;
31
+
32
+ /** One entry of a document's `paths` or `webhooks` map. */
33
+ type PathItem = NonNullable<ApiDocument["paths"]>[string];
34
+
35
+ /**
36
+ * How a warning names an operation: `GET /pets`, or a webhook by its name
37
+ * (`Webhook "newPet" (POST)`), since a webhook has no path.
38
+ */
39
+ export const operationLabel = (
40
+ name: string,
41
+ method: string,
42
+ webhook: boolean
43
+ ): string =>
44
+ webhook
45
+ ? `Webhook "${name}" (${method.toUpperCase()})`
46
+ : `${method.toUpperCase()} ${name}`;
47
+
48
+ /** One entry of a `parameters` list: a parameter, or a `$ref` to one. */
49
+ type ParameterEntry = NonNullable<OperationObject["parameters"]>[number];
50
+
51
+ // The runtime object checks stand guard because the document was parsed from
52
+ // arbitrary YAML/JSON: a spec can put a scalar or null where the type
53
+ // promises an object.
54
+ const isObject = <Value>(value: Value): value is NonNullable<Value> =>
55
+ typeof value === "object" && value !== null;
56
+
57
+ /** A parameter's `name`, which a hand-written spec may have left out or mistyped. */
58
+ const isName = <Value>(value: Value): value is Value & string =>
59
+ typeof value === "string";
60
+
61
+ /** A declared list, or none when a hand-written spec put something else there. */
62
+ const listOf = <Item>(value: Item[] | undefined): NonNullable<Item>[] =>
63
+ Array.isArray(value) ? value.filter(isObject) : [];
64
+
65
+ /** A resolved parameter: one that isn't a `$ref`. */
66
+ type Parameter = Extract<ParameterEntry, { in: string }>;
67
+
68
+ /**
69
+ * The parameters an operation declares, its path item's and its own, `$ref`s
70
+ * resolved through `components.parameters`. One that doesn't resolve is left
71
+ * out, as the page leaves it out.
72
+ */
73
+ const parametersOf = (
74
+ document: ApiDocument,
75
+ item: PathItem,
76
+ operation: OperationObject
77
+ ): Parameter[] => {
78
+ const components = document.components?.parameters ?? {};
79
+ return [
80
+ ...listOf<ParameterEntry>(item.parameters),
81
+ ...listOf<ParameterEntry>(operation.parameters),
82
+ ].flatMap((entry) => {
83
+ const refName =
84
+ "$ref" in entry
85
+ ? PARAMETER_REF.exec(entry.$ref)?.groups?.name
86
+ : undefined;
87
+ const parameter = (refName ? components[refName] : undefined) ?? entry;
88
+ return "in" in parameter ? [parameter] : [];
89
+ });
90
+ };
91
+
92
+ /**
93
+ * A path whose template and declared path parameters disagree. A placeholder
94
+ * with no parameter gets no input in Try it and no row in the table, so
95
+ * requests send it as literal text; a parameter with no placeholder gets an
96
+ * input whose value never reaches the URL.
97
+ */
98
+ const pathParameterIssues = (
99
+ signature: string,
100
+ path: string,
101
+ declared: Set<string>
102
+ ): SpecIssue[] => {
103
+ const used = new Set(
104
+ [...path.matchAll(PATH_TEMPLATE)].map((match) => match.groups?.name ?? "")
105
+ );
106
+ const issues: SpecIssue[] = [];
107
+ for (const name of used) {
108
+ if (!declared.has(name)) {
109
+ issues.push({
110
+ code: "BLUME_OPENAPI_PATH_PARAMETER_MISSING",
111
+ message: `${signature} has {${name}} in its path but declares no "${name}" path parameter, so Try it and the code samples send "{${name}}" as literal text.`,
112
+ suggestion: `Declare "${name}" in the operation's or the path's \`parameters\`, with \`in: path\` and \`required: true\`.`,
113
+ });
114
+ }
115
+ }
116
+ for (const name of declared) {
117
+ if (!used.has(name)) {
118
+ issues.push({
119
+ code: "BLUME_OPENAPI_PATH_PARAMETER_UNUSED",
120
+ message: `${signature} declares a "${name}" path parameter, but its path has no {${name}}, so the value Try it asks for never reaches the URL.`,
121
+ suggestion: `Add {${name}} to the path, or remove the parameter.`,
122
+ });
123
+ }
124
+ }
125
+ return issues;
126
+ };
127
+
128
+ /** The scheme names a `security` list requires. */
129
+ const requiredSchemes = (
130
+ security: ApiDocument["security"] | OperationObject["security"]
131
+ ): string[] =>
132
+ listOf(security).flatMap((requirement) => Object.keys(requirement));
133
+
134
+ /**
135
+ * Security schemes a requirement names that `components.securitySchemes`
136
+ * doesn't define: the Authorization section has nothing to describe, and
137
+ * Try it and the code samples send no credential for them. One warning per
138
+ * scheme, naming where it's required: the root `security`, or the first few
139
+ * operations that list it.
140
+ */
141
+ const securitySchemeIssues = (
142
+ document: ApiDocument,
143
+ requirers: Map<string, string[]>
144
+ ): SpecIssue[] => {
145
+ const defined = document.components?.securitySchemes ?? {};
146
+ const root = new Set(requiredSchemes(document.security));
147
+ const issues: SpecIssue[] = [];
148
+ for (const scheme of new Set([...root, ...requirers.keys()])) {
149
+ if (Object.hasOwn(defined, scheme)) {
150
+ continue;
151
+ }
152
+ const operations = requirers.get(scheme) ?? [];
153
+ const more = operations.length - NAMED_OPERATIONS;
154
+ const where = root.has(scheme)
155
+ ? "The spec's root `security` requires"
156
+ : `${operations.slice(0, NAMED_OPERATIONS).join(", ")}${more > 0 ? ` and ${more} more` : ""} ${operations.length === 1 ? "requires" : "require"}`;
157
+ issues.push({
158
+ code: "BLUME_OPENAPI_UNKNOWN_SECURITY_SCHEME",
159
+ message: `${where} the "${scheme}" security scheme, which \`components.securitySchemes\` doesn't define, so the Authorization section can't describe it and requests send no credential for it.`,
160
+ suggestion: `Define "${scheme}" in \`components.securitySchemes\`, or fix the name in \`security\`.`,
161
+ });
162
+ }
163
+ return issues;
164
+ };
165
+
166
+ /**
167
+ * An operation's `in: querystring` parameter (OpenAPI 3.2: the whole query
168
+ * string as one value), which the parameter table, Try it, and the code
169
+ * samples don't handle yet, so it would drop out of all three without a sign.
170
+ */
171
+ const querystringIssue = (signature: string): SpecIssue => ({
172
+ code: "BLUME_OPENAPI_UNSUPPORTED",
173
+ message: `${signature} declares an \`in: querystring\` parameter, which OpenAPI 3.2 added and Blume doesn't render yet, so the page has no row or Try it input for it and the code samples leave the query string out.`,
174
+ suggestion:
175
+ "Describe the query string in the operation's `description` until Blume renders `querystring` parameters.",
176
+ });
177
+
178
+ /**
179
+ * A path item's `additionalOperations` (OpenAPI 3.2: methods beyond the ones
180
+ * a path item names directly, like `COPY`), which the extractor doesn't
181
+ * collect yet, so those operations would be missing without a sign.
182
+ */
183
+ const additionalOperationsIssue = (
184
+ label: string,
185
+ methods: string[]
186
+ ): SpecIssue => ({
187
+ code: "BLUME_OPENAPI_UNSUPPORTED",
188
+ message: `${label} declares \`additionalOperations\` (${methods.join(", ")}), which OpenAPI 3.2 added and Blume doesn't render yet, so those operations are missing from the reference.`,
189
+ suggestion:
190
+ "Document those operations on a page of their own until Blume renders `additionalOperations`.",
191
+ });
192
+
193
+ /**
194
+ * A root `x-webhooks` and no `webhooks`. It's the extension OpenAPI 3.0 tools
195
+ * read for webhooks, and upgrading a 3.0 spec renames it to `webhooks`; a spec
196
+ * already at 3.1 or later isn't upgraded, so it stays an extension and its
197
+ * webhooks would be missing without a sign.
198
+ */
199
+ const xWebhooksIssues = (document: ApiDocument): SpecIssue[] =>
200
+ "x-webhooks" in document && document.webhooks === undefined
201
+ ? [
202
+ {
203
+ code: "BLUME_OPENAPI_X_WEBHOOKS",
204
+ message:
205
+ "The spec declares its webhooks under `x-webhooks`, the extension OpenAPI 3.0 tools read. Blume reads it only from a 3.0 spec, which it upgrades, so these webhooks are missing from the reference.",
206
+ suggestion:
207
+ "Rename `x-webhooks` to `webhooks`, the field OpenAPI 3.1 added for them.",
208
+ },
209
+ ]
210
+ : [];
211
+
212
+ /**
213
+ * Check a parsed document for the mistakes above, and for the OpenAPI 3.2
214
+ * features Blume doesn't render yet. `$ref` path items are skipped: the
215
+ * extractor already reports them as missing from the reference.
216
+ */
217
+ export const specIssues = (document: ApiDocument): SpecIssue[] => {
218
+ const issues: SpecIssue[] = [...xWebhooksIssues(document)];
219
+ // Scheme name -> the operations whose own `security` lists it.
220
+ const requirers = new Map<string, string[]>();
221
+ const visit = (name: string, item: PathItem, webhook: boolean): void => {
222
+ if (!isObject(item) || "$ref" in item) {
223
+ return;
224
+ }
225
+ const additional = isObject(item.additionalOperations)
226
+ ? Object.keys(item.additionalOperations)
227
+ : [];
228
+ if (additional.length > 0) {
229
+ issues.push(
230
+ additionalOperationsIssue(
231
+ webhook ? `Webhook "${name}"` : `Path "${name}"`,
232
+ additional
233
+ )
234
+ );
235
+ }
236
+ for (const method of HTTP_METHODS) {
237
+ const operation = item[method];
238
+ if (!isObject(operation)) {
239
+ continue;
240
+ }
241
+ const signature = operationLabel(name, method, webhook);
242
+ for (const scheme of requiredSchemes(operation.security)) {
243
+ requirers.set(scheme, [...(requirers.get(scheme) ?? []), signature]);
244
+ }
245
+ const parameters = parametersOf(document, item, operation);
246
+ // A webhook is keyed by its name, not a path: it has no template.
247
+ if (!webhook) {
248
+ issues.push(
249
+ ...pathParameterIssues(
250
+ signature,
251
+ name,
252
+ new Set(
253
+ parameters
254
+ .filter((parameter) => parameter.in === "path")
255
+ .map((parameter) => parameter.name)
256
+ .filter(isName)
257
+ )
258
+ )
259
+ );
260
+ }
261
+ if (parameters.some((parameter) => parameter.in === "querystring")) {
262
+ issues.push(querystringIssue(signature));
263
+ }
264
+ }
265
+ };
266
+ for (const [path, item] of Object.entries(document.paths ?? {})) {
267
+ visit(path, item, false);
268
+ }
269
+ for (const [name, item] of Object.entries(document.webhooks ?? {})) {
270
+ visit(name, item, true);
271
+ }
272
+ return [...issues, ...securitySchemeIssues(document, requirers)];
273
+ };
@@ -26,6 +26,7 @@ import {
26
26
  parse,
27
27
  print,
28
28
  specifiedDirectives,
29
+ valueFromASTUntyped,
29
30
  visit,
30
31
  } from "graphql";
31
32
 
@@ -34,6 +35,7 @@ import type {
34
35
  GraphqlEnumValue,
35
36
  GraphqlField as ModelField,
36
37
  GraphqlInputValue,
38
+ GraphqlJsonValue,
37
39
  GraphqlTypeDef,
38
40
  GraphqlTypeRef,
39
41
  } from "./graphql.ts";
@@ -68,6 +70,21 @@ const defaultLiteral = (
68
70
  return literal ? print(literal) : undefined;
69
71
  };
70
72
 
73
+ /**
74
+ * The same default as the JSON a client sends in `variables`, which the
75
+ * example variables start from.
76
+ */
77
+ const defaultJson = (
78
+ input: GraphQLArgument | GraphQLInputField
79
+ ): GraphqlJsonValue | undefined => {
80
+ const literal = input.default?.literal;
81
+ // SAFETY: a const literal has no variables, so it lowers to JSON values
82
+ // only: numbers, strings, booleans, null, enum names, lists and objects.
83
+ return literal
84
+ ? (valueFromASTUntyped(literal) as GraphqlJsonValue)
85
+ : undefined;
86
+ };
87
+
71
88
  const inputValue = (
72
89
  input: GraphQLArgument | GraphQLInputField
73
90
  ): GraphqlInputValue => {
@@ -80,6 +97,10 @@ const inputValue = (
80
97
  if (printed !== undefined) {
81
98
  value.default = printed;
82
99
  }
100
+ const json = defaultJson(input);
101
+ if (json !== undefined) {
102
+ value.defaultValue = json;
103
+ }
83
104
  // `deprecationReason` is null on non-deprecated members in some builds;
84
105
  // absent means "not deprecated" downstream, so both collapse to absent.
85
106
  if (
@@ -58,6 +58,15 @@ export interface GraphqlTypeRef {
58
58
  name: string;
59
59
  }
60
60
 
61
+ /** A GraphQL input value as JSON carries it in `variables` (enums by name). */
62
+ export type GraphqlJsonValue =
63
+ | string
64
+ | number
65
+ | boolean
66
+ | null
67
+ | GraphqlJsonValue[]
68
+ | { [key: string]: GraphqlJsonValue };
69
+
61
70
  /** An argument or input-object field. */
62
71
  export interface GraphqlInputValue {
63
72
  name: string;
@@ -65,6 +74,8 @@ export interface GraphqlInputValue {
65
74
  type: GraphqlTypeRef;
66
75
  /** Default value printed as a GraphQL literal (`10`, `"asc"`, `[1, 2]`). */
67
76
  default?: string;
77
+ /** The same default as JSON, for the example variables. */
78
+ defaultValue?: GraphqlJsonValue;
68
79
  deprecationReason?: string;
69
80
  }
70
81
 
@@ -1,5 +1,6 @@
1
- import type { Document, OperationObject } from "@scalar/openapi-types/3.1";
1
+ import type { Document, OperationObject } from "@scalar/openapi-types/3.2";
2
2
 
3
+ import type { GraphqlAuthOptions } from "../reference/options.ts";
3
4
  import type {
4
5
  AsyncApiAction,
5
6
  AsyncApiDocument,
@@ -26,10 +27,18 @@ export { slugify } from "./references.ts";
26
27
  * per-operation route so it becomes a first-class Blume page.
27
28
  */
28
29
 
29
- /** A normalized OpenAPI 3.1 document, internal `$ref`s intact. */
30
+ /**
31
+ * A normalized OpenAPI document, internal `$ref`s intact. Typed as 3.2, which
32
+ * is backward compatible with 3.1, so one type covers both the upgraded 3.1
33
+ * documents and the 3.2 ones read as written.
34
+ */
30
35
  export type ApiDocument = Document;
31
36
 
32
- /** The HTTP methods an OpenAPI path item may declare, in spec order. */
37
+ /**
38
+ * The HTTP methods an OpenAPI path item may declare, in spec order. `query`
39
+ * is OpenAPI 3.2's (the QUERY method: a safe request that carries a body); a
40
+ * 3.1 path item can't declare it, so listing it changes nothing there.
41
+ */
33
42
  export const HTTP_METHODS = [
34
43
  "get",
35
44
  "put",
@@ -39,6 +48,7 @@ export const HTTP_METHODS = [
39
48
  "head",
40
49
  "patch",
41
50
  "trace",
51
+ "query",
42
52
  ] as const;
43
53
 
44
54
  export type HttpMethod = (typeof HTTP_METHODS)[number];
@@ -140,6 +150,8 @@ export interface ApiSpecData {
140
150
  * (GraphQL only; OpenAPI documents carry their servers in the document).
141
151
  */
142
152
  endpoint?: string;
153
+ /** How the GraphQL endpoint authenticates (GraphQL only). */
154
+ auth?: GraphqlAuthOptions;
143
155
  /** Operations keyed by {@link ApiOperationRef.key}. */
144
156
  operations: Record<string, ApiOperationRef>;
145
157
  tags: ApiTagRef[];
@@ -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,8 +15,11 @@ 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";
21
24
  import { applyOverlay, isJsonObject, overlayDocument } from "./overlay.ts";
22
25
  import { SpecDependencyError } from "./spec-dependency-error.ts";
@@ -25,6 +28,8 @@ import { SpecDependencyError } from "./spec-dependency-error.ts";
25
28
  * Spec loading and normalization. Blume reuses Scalar's parser
26
29
  * (`@scalar/openapi-parser`) to read a spec (YAML or JSON), then upgrade Swagger
27
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.
28
33
  * Internal `$ref`s are deliberately left in place (see `model.ts`).
29
34
  *
30
35
  * Remote (`http(s)`) specs are fetched defensively — bounded per attempt, retried
@@ -58,6 +63,8 @@ const PROXY_ENV_VARS = [
58
63
 
59
64
  export interface ParsedSpec {
60
65
  document: ApiDocument;
66
+ /** `x-codeSamples` entries whose `$ref` couldn't be inlined. */
67
+ issues: SpecIssue[];
61
68
  warnings: string[];
62
69
  }
63
70
 
@@ -360,6 +367,123 @@ export const readOverlaidSpec = async (
360
367
  return { text: JSON.stringify(document), warnings };
361
368
  };
362
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
+
363
487
  export const parseSpec = async (
364
488
  spec: string,
365
489
  root: string,
@@ -377,7 +501,8 @@ export const parseSpec = async (
377
501
  `${spec} is not a valid OpenAPI document (expected a YAML or JSON object).`
378
502
  );
379
503
  }
380
- return { document: specification, warnings };
504
+ const issues = await inlineSampleRefs(specification, spec, root, options);
505
+ return { document: specification, issues, warnings };
381
506
  };
382
507
 
383
508
  export interface ParsedAsyncApiSpec {