blume 2.0.1 → 2.0.3

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 (492) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
  3. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
  4. package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
  5. package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
  6. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
  7. package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
  8. package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
  9. package/dist/cli/chunk-35d4wj9f.js.map +15 -0
  10. package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
  11. package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
  12. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
  13. package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
  14. package/dist/cli/chunk-5f86nr5m.js.map +11 -0
  15. package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
  16. package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
  17. package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
  18. package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
  19. package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
  20. package/dist/cli/chunk-6dsbexzp.js.map +10 -0
  21. package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
  22. package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
  23. package/dist/cli/chunk-9t7a85s3.js.map +10 -0
  24. package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
  25. package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
  26. package/dist/cli/chunk-acanzt5p.js.map +10 -0
  27. package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
  28. package/dist/cli/chunk-akbpwfxc.js.map +10 -0
  29. package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
  30. package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
  31. package/dist/cli/chunk-c8chx29p.js.map +36 -0
  32. package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
  33. package/dist/cli/chunk-crgn1q09.js.map +10 -0
  34. package/dist/cli/chunk-e04dxsz1.js +39 -0
  35. package/dist/cli/chunk-e04dxsz1.js.map +10 -0
  36. package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
  37. package/dist/cli/chunk-ey84smr6.js.map +10 -0
  38. package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
  39. package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
  40. package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
  41. package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
  42. package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
  43. package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
  44. package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
  45. package/dist/cli/chunk-j85vccga.js.map +36 -0
  46. package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
  47. package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
  48. package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
  49. package/dist/cli/chunk-p73c0m7w.js.map +11 -0
  50. package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
  51. package/dist/cli/chunk-pehfxfta.js.map +10 -0
  52. package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
  53. package/dist/cli/chunk-pv29h0wf.js.map +190 -0
  54. package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
  55. package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
  56. package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
  57. package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
  58. package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
  59. package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
  60. package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
  61. package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
  62. package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
  63. package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
  64. package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
  65. package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
  66. package/dist/cli/chunk-yfyb25rh.js.map +14 -0
  67. package/dist/cli/index.js +162 -35
  68. package/dist/cli/index.js.map +4 -4
  69. package/dist/types/ai/agent-surface.d.ts +32 -0
  70. package/dist/types/ai/api-catalog.d.ts +7 -1
  71. package/dist/types/ai/ask-context.d.ts +7 -0
  72. package/dist/types/ai/component-markdown.d.ts +4 -4
  73. package/dist/types/ai/link-headers.d.ts +8 -1
  74. package/dist/types/ai/openapi-components.d.ts +5 -2
  75. package/dist/types/ai/relative-links.d.ts +11 -4
  76. package/dist/types/ai/skills.d.ts +4 -1
  77. package/dist/types/ai/static-expression.d.ts +28 -0
  78. package/dist/types/ai/tar.d.ts +1 -3
  79. package/dist/types/analytics/databuddy.d.ts +43 -0
  80. package/dist/types/analytics/index.d.ts +4 -0
  81. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  82. package/dist/types/analytics/schema.d.ts +28 -0
  83. package/dist/types/astro/integration.d.ts +3 -2
  84. package/dist/types/cli/env.d.ts +5 -0
  85. package/dist/types/cli/init/scaffold.d.ts +19 -3
  86. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  87. package/dist/types/core/base-path.d.ts +24 -1
  88. package/dist/types/core/config-input.d.ts +13 -11
  89. package/dist/types/core/config.d.ts +2 -2
  90. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  91. package/dist/types/core/graph.d.ts +2 -0
  92. package/dist/types/core/heading-markers.d.ts +5 -7
  93. package/dist/types/core/i18n-ui.d.ts +31 -0
  94. package/dist/types/core/i18n.d.ts +9 -1
  95. package/dist/types/core/last-modified.d.ts +10 -0
  96. package/dist/types/core/links.d.ts +3 -1
  97. package/dist/types/core/load-module.d.ts +10 -0
  98. package/dist/types/core/locale-links.d.ts +12 -2
  99. package/dist/types/core/meta.d.ts +13 -1
  100. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  101. package/dist/types/core/navigation.d.ts +38 -0
  102. package/dist/types/core/ordering-prefix.d.ts +4 -0
  103. package/dist/types/core/safe-links.d.ts +3 -1
  104. package/dist/types/core/schema.d.ts +59 -13
  105. package/dist/types/core/sources/github-releases.d.ts +5 -0
  106. package/dist/types/core/sources/lower.d.ts +38 -13
  107. package/dist/types/core/sources/normalize.d.ts +23 -2
  108. package/dist/types/core/sources/remote.d.ts +11 -1
  109. package/dist/types/core/sources/resolve.d.ts +12 -0
  110. package/dist/types/core/sources/types.d.ts +31 -0
  111. package/dist/types/core/sources/watch.d.ts +12 -5
  112. package/dist/types/core/standard-schema.d.ts +5 -0
  113. package/dist/types/core/types.d.ts +9 -0
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/artifacts.d.ts +6 -4
  117. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  118. package/dist/types/deploy/headers.d.ts +36 -7
  119. package/dist/types/deploy/node-headers.d.ts +43 -8
  120. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  121. package/dist/types/deploy/platforms/node.d.ts +6 -5
  122. package/dist/types/deploy/platforms/types.d.ts +15 -0
  123. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  124. package/dist/types/deploy/redirects.d.ts +31 -15
  125. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  126. package/dist/types/markdown/directives.d.ts +62 -0
  127. package/dist/types/markdown/features.d.ts +21 -0
  128. package/dist/types/markdown/mdast.d.ts +63 -0
  129. package/dist/types/openapi/asyncapi.d.ts +4 -2
  130. package/dist/types/openapi/model.d.ts +20 -6
  131. package/dist/types/search/sync/algolia.d.ts +3 -1
  132. package/docs/01-quickstart.mdx +3 -2
  133. package/docs/02-deployment.mdx +20 -9
  134. package/docs/08-faq.mdx +10 -3
  135. package/docs/advanced/changelog.mdx +1 -1
  136. package/docs/advanced/custom-pages.mdx +7 -5
  137. package/docs/cli/audit.mdx +19 -3
  138. package/docs/cli/doctor.mdx +2 -2
  139. package/docs/cli/evals.mdx +4 -4
  140. package/docs/cli/index.mdx +4 -1
  141. package/docs/cli/translate.mdx +4 -4
  142. package/docs/cli/version.mdx +1 -1
  143. package/docs/configuration/analytics.mdx +42 -2
  144. package/docs/configuration/assistant.mdx +1 -1
  145. package/docs/configuration/customization.mdx +6 -3
  146. package/docs/configuration/index.mdx +5 -3
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +2 -2
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -1
  151. package/docs/content/index.mdx +8 -4
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +7 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -16
  156. package/docs/content/syntax.mdx +14 -0
  157. package/docs/content/versioning.mdx +2 -1
  158. package/docs/discoverability/agent-discovery.mdx +22 -8
  159. package/docs/discoverability/index.mdx +4 -3
  160. package/docs/discoverability/llms-txt.mdx +2 -5
  161. package/docs/discoverability/markdown.mdx +6 -4
  162. package/docs/discoverability/mcp.mdx +4 -0
  163. package/docs/discoverability/metadata.mdx +3 -2
  164. package/docs/discoverability/open-graph.mdx +8 -4
  165. package/docs/discoverability/rss.mdx +4 -2
  166. package/docs/references/asyncapi.mdx +2 -2
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +6 -4
  169. package/package.json +1 -1
  170. package/skills/blume-migrate/SKILL.md +6 -6
  171. package/skills/blume-migrate/references/docusaurus.md +6 -6
  172. package/skills/blume-migrate/references/fumadocs.md +1 -1
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +2 -2
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +2 -2
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/ai-catalog.ts +6 -2
  182. package/src/ai/api/handlers.ts +2 -2
  183. package/src/ai/api/spec.ts +17 -4
  184. package/src/ai/api-catalog.ts +13 -3
  185. package/src/ai/ask-context.ts +14 -2
  186. package/src/ai/ask-data.ts +26 -12
  187. package/src/ai/changelog-markdown.ts +2 -2
  188. package/src/ai/component-markdown.ts +43 -37
  189. package/src/ai/link-headers.ts +22 -8
  190. package/src/ai/llms.ts +22 -10
  191. package/src/ai/markdown.ts +23 -11
  192. package/src/ai/mcp/discovery.ts +2 -2
  193. package/src/ai/mcp/query.ts +2 -2
  194. package/src/ai/mcp/server.ts +80 -9
  195. package/src/ai/openapi-components.ts +22 -6
  196. package/src/ai/relative-links.ts +99 -15
  197. package/src/ai/serializers.ts +2 -1
  198. package/src/ai/skills.ts +14 -1
  199. package/src/ai/static-expression.ts +416 -0
  200. package/src/ai/tar.ts +139 -12
  201. package/src/ai/visibility.ts +45 -14
  202. package/src/analytics/databuddy.ts +67 -0
  203. package/src/analytics/head.ts +8 -0
  204. package/src/analytics/index.ts +7 -0
  205. package/src/analytics/one-dollar-stats.ts +86 -0
  206. package/src/analytics/posthog.ts +21 -3
  207. package/src/analytics/schema.ts +4 -0
  208. package/src/astro/generate.ts +72 -28
  209. package/src/astro/include-hmr.ts +31 -12
  210. package/src/astro/include-refresh.ts +19 -8
  211. package/src/astro/integration.ts +108 -14
  212. package/src/astro/runtime-modules.ts +28 -13
  213. package/src/astro/templates.ts +139 -57
  214. package/src/audit/catalog.ts +2 -2
  215. package/src/audit/checks/assets.ts +25 -5
  216. package/src/audit/checks/content.ts +20 -2
  217. package/src/audit/checks/i18n.ts +46 -8
  218. package/src/audit/checks/indexability.ts +39 -19
  219. package/src/audit/checks/links.ts +22 -1
  220. package/src/audit/checks/llms.ts +6 -3
  221. package/src/audit/checks/network.ts +3 -1
  222. package/src/audit/checks/og-image.ts +10 -0
  223. package/src/audit/checks/robots.ts +6 -1
  224. package/src/audit/checks/sitemap.ts +56 -31
  225. package/src/audit/checks/social.ts +21 -2
  226. package/src/audit/crawl.ts +88 -15
  227. package/src/audit/graph.ts +3 -1
  228. package/src/audit/report.ts +54 -17
  229. package/src/audit/run.ts +13 -9
  230. package/src/audit/snapshot.ts +5 -0
  231. package/src/audit/types.ts +18 -0
  232. package/src/audit/url.ts +37 -6
  233. package/src/cli/args.ts +32 -0
  234. package/src/cli/build-failure.ts +50 -0
  235. package/src/cli/commands/audit.ts +11 -1
  236. package/src/cli/commands/build.ts +19 -5
  237. package/src/cli/commands/check.ts +2 -0
  238. package/src/cli/commands/doctor.ts +3 -1
  239. package/src/cli/commands/eval.ts +20 -18
  240. package/src/cli/commands/init.ts +15 -5
  241. package/src/cli/commands/preview.ts +15 -0
  242. package/src/cli/commands/sync.ts +2 -0
  243. package/src/cli/commands/translate.ts +18 -18
  244. package/src/cli/commands/upgrade.ts +11 -0
  245. package/src/cli/commands/validate.ts +3 -1
  246. package/src/cli/dev-lock.ts +157 -37
  247. package/src/cli/eject-scripts.ts +32 -7
  248. package/src/cli/env.ts +12 -1
  249. package/src/cli/init/scaffold.ts +117 -11
  250. package/src/cli/init/starter-spec.ts +235 -0
  251. package/src/cli/report-format.ts +11 -6
  252. package/src/components/colors.ts +19 -0
  253. package/src/components/content/AccordionItem.astro +26 -22
  254. package/src/components/content/Badge.astro +7 -12
  255. package/src/components/content/Card.astro +2 -2
  256. package/src/components/content/Component.astro +11 -5
  257. package/src/components/content/Expandable.astro +5 -1
  258. package/src/components/content/Frame.astro +2 -7
  259. package/src/components/content/GithubInfo.astro +12 -2
  260. package/src/components/content/Prompt.astro +2 -7
  261. package/src/components/content/Tab.astro +0 -1
  262. package/src/components/content/Tabs.astro +58 -5
  263. package/src/components/content/Tile.astro +1 -1
  264. package/src/components/content/Tooltip.astro +69 -7
  265. package/src/components/content/Tree.astro +7 -2
  266. package/src/components/content/TypeTable.astro +10 -5
  267. package/src/components/content/Update.astro +8 -2
  268. package/src/components/content/auto-type-table.ts +4 -1
  269. package/src/components/content/badge-color.ts +20 -0
  270. package/src/components/content/base-href.ts +14 -22
  271. package/src/components/content/inline-markdown.ts +27 -7
  272. package/src/components/copy-feedback.ts +35 -8
  273. package/src/components/islands/assistant.tsx +37 -6
  274. package/src/components/islands/base-path.ts +47 -10
  275. package/src/components/islands/hooks.ts +42 -21
  276. package/src/components/islands/webmcp.ts +16 -10
  277. package/src/components/layout/Banner.astro +23 -4
  278. package/src/components/layout/Breadcrumbs.astro +5 -2
  279. package/src/components/layout/DiscoveryLinks.astro +9 -5
  280. package/src/components/layout/Header.astro +23 -10
  281. package/src/components/layout/LanguageSwitcher.astro +2 -2
  282. package/src/components/layout/Logo.astro +5 -0
  283. package/src/components/layout/NavSelector.astro +8 -3
  284. package/src/components/layout/NavTabMenu.astro +133 -0
  285. package/src/components/layout/NavTree.astro +31 -12
  286. package/src/components/layout/NavTreeCache.astro +5 -2
  287. package/src/components/layout/NavTreeScript.astro +45 -5
  288. package/src/components/layout/PageActions.astro +4 -3
  289. package/src/components/layout/PageLayout.astro +28 -13
  290. package/src/components/layout/Pagination.astro +3 -3
  291. package/src/components/layout/ReferenceLayout.astro +4 -1
  292. package/src/components/layout/RootLayout.astro +25 -12
  293. package/src/components/layout/Search.astro +38 -14
  294. package/src/components/layout/VersionBanner.astro +2 -2
  295. package/src/components/layout/analytics-client.ts +14 -0
  296. package/src/components/layout/toc-active.ts +41 -0
  297. package/src/components/layout/toc-element.ts +8 -14
  298. package/src/components/openapi/ApiTagOperations.astro +2 -2
  299. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  300. package/src/components/openapi/GraphqlChip.astro +2 -2
  301. package/src/components/openapi/GraphqlType.astro +11 -3
  302. package/src/components/openapi/MessageComposer.astro +1 -1
  303. package/src/components/openapi/Operation.astro +23 -5
  304. package/src/components/openapi/PanelTabs.astro +4 -1
  305. package/src/components/openapi/Playground.astro +8 -2
  306. package/src/components/openapi/RequestPanel.astro +13 -6
  307. package/src/components/openapi/SchemaProperty.astro +11 -33
  308. package/src/components/openapi/SchemaTable.astro +34 -52
  309. package/src/components/openapi/async.ts +38 -6
  310. package/src/components/openapi/helpers.ts +100 -12
  311. package/src/components/openapi/message-composer.ts +14 -1
  312. package/src/components/openapi/message-model.ts +12 -2
  313. package/src/components/openapi/message.ts +21 -3
  314. package/src/components/openapi/operation-model.ts +119 -17
  315. package/src/components/openapi/panel.ts +31 -5
  316. package/src/components/openapi/param-style.ts +181 -0
  317. package/src/components/openapi/playground-client.ts +110 -23
  318. package/src/components/openapi/playground-schema.ts +25 -6
  319. package/src/components/openapi/request.ts +192 -26
  320. package/src/components/openapi/schema-tree.ts +209 -0
  321. package/src/components/openapi/snippets.ts +113 -19
  322. package/src/components/openapi/validate-json.ts +1 -1
  323. package/src/components/openapi/ws-client.ts +18 -2
  324. package/src/core/base-path.ts +69 -8
  325. package/src/core/config-input.ts +13 -11
  326. package/src/core/config.ts +18 -4
  327. package/src/core/diagnostics.ts +219 -30
  328. package/src/core/directive-diagnostics.ts +99 -0
  329. package/src/core/frontmatter.ts +21 -18
  330. package/src/core/graph.ts +120 -11
  331. package/src/core/heading-markers.ts +5 -18
  332. package/src/core/i18n-ui.ts +39 -2
  333. package/src/core/i18n.ts +44 -5
  334. package/src/core/last-modified.ts +25 -3
  335. package/src/core/links.ts +7 -4
  336. package/src/core/load-module.ts +20 -0
  337. package/src/core/locale-links.ts +22 -18
  338. package/src/core/manifest.ts +3 -2
  339. package/src/core/meta.ts +89 -12
  340. package/src/core/nav-diagnostics.ts +56 -1
  341. package/src/core/navigation.ts +272 -83
  342. package/src/core/ordering-prefix.ts +27 -0
  343. package/src/core/project-graph.ts +50 -6
  344. package/src/core/safe-href.ts +53 -1
  345. package/src/core/safe-links.ts +11 -2
  346. package/src/core/schema.ts +94 -18
  347. package/src/core/sources/assets.ts +83 -41
  348. package/src/core/sources/contentful-rich-text.ts +28 -20
  349. package/src/core/sources/contentful.ts +25 -12
  350. package/src/core/sources/filesystem.ts +25 -3
  351. package/src/core/sources/github-releases.ts +131 -11
  352. package/src/core/sources/lexical.ts +30 -20
  353. package/src/core/sources/lower.ts +276 -56
  354. package/src/core/sources/mdx-remote.ts +52 -16
  355. package/src/core/sources/normalize.ts +456 -119
  356. package/src/core/sources/notion.ts +77 -34
  357. package/src/core/sources/obsidian.ts +40 -9
  358. package/src/core/sources/payload.ts +1 -0
  359. package/src/core/sources/portable-text.ts +85 -49
  360. package/src/core/sources/remote.ts +18 -2
  361. package/src/core/sources/resolve.ts +52 -33
  362. package/src/core/sources/sanity.ts +1 -0
  363. package/src/core/sources/strapi-blocks.ts +28 -19
  364. package/src/core/sources/strapi.ts +1 -0
  365. package/src/core/sources/types.ts +31 -0
  366. package/src/core/sources/watch.ts +20 -7
  367. package/src/core/standard-schema.ts +10 -6
  368. package/src/core/types.ts +9 -0
  369. package/src/core/ui-packs/ar.ts +13 -0
  370. package/src/core/ui-packs/bg.ts +13 -0
  371. package/src/core/ui-packs/bn.ts +13 -0
  372. package/src/core/ui-packs/ca.ts +13 -0
  373. package/src/core/ui-packs/cs.ts +13 -0
  374. package/src/core/ui-packs/da.ts +13 -0
  375. package/src/core/ui-packs/de.ts +13 -0
  376. package/src/core/ui-packs/el.ts +13 -0
  377. package/src/core/ui-packs/es.ts +13 -0
  378. package/src/core/ui-packs/fa.ts +13 -0
  379. package/src/core/ui-packs/fi.ts +13 -0
  380. package/src/core/ui-packs/fr.ts +13 -0
  381. package/src/core/ui-packs/he.ts +13 -0
  382. package/src/core/ui-packs/hi.ts +13 -0
  383. package/src/core/ui-packs/hr.ts +13 -0
  384. package/src/core/ui-packs/hu.ts +13 -0
  385. package/src/core/ui-packs/id.ts +13 -0
  386. package/src/core/ui-packs/it.ts +13 -0
  387. package/src/core/ui-packs/ja.ts +13 -0
  388. package/src/core/ui-packs/ko.ts +13 -0
  389. package/src/core/ui-packs/nl.ts +13 -0
  390. package/src/core/ui-packs/no.ts +13 -0
  391. package/src/core/ui-packs/pl.ts +13 -0
  392. package/src/core/ui-packs/pt-br.ts +13 -0
  393. package/src/core/ui-packs/pt.ts +13 -0
  394. package/src/core/ui-packs/ro.ts +13 -0
  395. package/src/core/ui-packs/ru.ts +13 -0
  396. package/src/core/ui-packs/sk.ts +13 -0
  397. package/src/core/ui-packs/sr.ts +13 -0
  398. package/src/core/ui-packs/sv.ts +13 -0
  399. package/src/core/ui-packs/th.ts +13 -0
  400. package/src/core/ui-packs/tr.ts +13 -0
  401. package/src/core/ui-packs/uk.ts +13 -0
  402. package/src/core/ui-packs/vi.ts +13 -0
  403. package/src/core/ui-packs/zh-tw.ts +13 -0
  404. package/src/core/ui-packs/zh.ts +13 -0
  405. package/src/core/version-cut.ts +69 -17
  406. package/src/core/versions.ts +4 -1
  407. package/src/deploy/adapters/node.ts +5 -2
  408. package/src/deploy/adapters/registry.ts +2 -1
  409. package/src/deploy/adapters/types.ts +13 -1
  410. package/src/deploy/artifacts.ts +52 -11
  411. package/src/deploy/cloudflare-negotiation.ts +27 -16
  412. package/src/deploy/headers.ts +75 -55
  413. package/src/deploy/node-headers.ts +148 -27
  414. package/src/deploy/platforms/cloudflare.ts +13 -3
  415. package/src/deploy/platforms/netlify.ts +83 -5
  416. package/src/deploy/platforms/node.ts +8 -5
  417. package/src/deploy/platforms/static.ts +2 -0
  418. package/src/deploy/platforms/types.ts +15 -0
  419. package/src/deploy/platforms/vercel.ts +10 -3
  420. package/src/deploy/redirects.ts +68 -21
  421. package/src/deploy/robots.ts +2 -2
  422. package/src/deploy/rss.ts +4 -3
  423. package/src/deploy/sitemap.ts +7 -5
  424. package/src/deploy/vercel-negotiation.ts +35 -3
  425. package/src/eval/agents.ts +10 -2
  426. package/src/eval/run.ts +25 -0
  427. package/src/markdown/base-links.ts +74 -29
  428. package/src/markdown/directives.ts +242 -36
  429. package/src/markdown/features.ts +17 -0
  430. package/src/markdown/index.ts +13 -9
  431. package/src/markdown/mdast.ts +5 -2
  432. package/src/markdown/relative-links.ts +15 -27
  433. package/src/markdown/route-snapshot.ts +37 -0
  434. package/src/og/card.ts +149 -9
  435. package/src/og/derive.ts +156 -5
  436. package/src/og/index.ts +1 -0
  437. package/src/openapi/asyncapi.ts +4 -2
  438. package/src/openapi/graphql-build.ts +28 -2
  439. package/src/openapi/model.ts +99 -27
  440. package/src/openapi/proxy.ts +63 -10
  441. package/src/openapi/render-mdx.ts +51 -3
  442. package/src/registry/eject.ts +297 -43
  443. package/src/search/adapters/version-scope.ts +30 -0
  444. package/src/search/documents.ts +89 -27
  445. package/src/search/popular.ts +2 -1
  446. package/src/search/sync/algolia.ts +36 -2
  447. package/src/seo/jsonld.ts +7 -3
  448. package/src/sources/registry.ts +5 -0
  449. package/src/theme/entry.ts +11 -4
  450. package/src/translate/agents.ts +6 -1
  451. package/src/translate/ledger.ts +26 -3
  452. package/src/translate/meta.ts +76 -24
  453. package/src/translate/run.ts +14 -8
  454. package/src/translate/validate.ts +14 -2
  455. package/src/translate/work-list.ts +91 -19
  456. package/src/upgrade/upgrade.ts +36 -4
  457. package/dist/cli/chunk-27g6wdth.js.map +0 -10
  458. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  459. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  460. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  461. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  462. package/dist/cli/chunk-82bbrxdn.js +0 -51
  463. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  464. package/dist/cli/chunk-abh8yjkn.js +0 -31
  465. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  466. package/dist/cli/chunk-ce574jw2.js +0 -23
  467. package/dist/cli/chunk-ce574jw2.js.map +0 -10
  468. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  469. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  470. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  471. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  472. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  473. package/dist/cli/chunk-jwyddg7y.js.map +0 -15
  474. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  475. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  476. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  477. package/dist/cli/chunk-q5163e60.js.map +0 -11
  478. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  479. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  480. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  481. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  482. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  483. package/dist/cli/chunk-yw7dm696.js.map +0 -14
  484. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
  485. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
  486. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
  487. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
  488. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
  489. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
  490. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
  491. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
  492. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
@@ -1,9 +1,13 @@
1
1
  ---
2
+ import data from "blume:data";
3
+
2
4
  import { EN_UI } from "../../core/i18n-ui.ts";
3
5
  import type { UIStrings } from "../../core/i18n-ui.ts";
4
6
  import type { Navigation } from "../../core/types.ts";
7
+ import { scopesVersions } from "../../search/adapters/version-scope.ts";
5
8
  import { resolvePopularIconMarkup } from "../../search/popular-icon.ts";
6
9
  import Icon from "../Icon.astro";
10
+ import { withBase, withMountedBase } from "../islands/base-path.ts";
7
11
  import { flattenPages } from "./nav-utils.ts";
8
12
 
9
13
  // The provider-specific loader is generated per project into `blume:search-client`
@@ -21,7 +25,8 @@ interface Props {
21
25
  locale?: string;
22
26
  /**
23
27
  * Docs version to filter results to (`""` = the current docs — a meaningful
24
- * value, so `null`/omitted is what disables version filtering).
28
+ * value, so `null`/omitted is what disables version filtering). Ignored when
29
+ * the configured provider can't scope by version.
25
30
  */
26
31
  version?: string | null;
27
32
  }
@@ -32,27 +37,41 @@ const {
32
37
  popularPages,
33
38
  strings,
34
39
  locale,
35
- version = null,
40
+ version: viewedVersion = null,
36
41
  } = Astro.props;
42
+ // Pagefind, Orama Cloud, and Mixedbread search every version whatever the
43
+ // dialog asks, so on those an "All versions" toggle would change nothing:
44
+ // treat the site as unversioned for search instead.
45
+ const version = scopesVersions(data.config.search.provider)
46
+ ? viewedVersion
47
+ : null;
37
48
  // Merge over the English baseline per key (rather than `strings ?? …`) so a
38
49
  // partial — or empty `{}` — strings object still resolves every label to a
39
50
  // default, matching the pattern PageActions uses for its own dictionary.
40
51
  const s = { ...EN_UI.search, ...strings };
41
52
 
42
53
  // Resolve popular icons to markup here (icon set is server-only). Unknown
43
- // names fall through to the island's file glyph.
54
+ // names fall through to the island's file glyph. Each entry's served href is
55
+ // resolved here too: a configured entry is authored, so a base written by hand
56
+ // is kept (`withBase`); the sidebar fallback links generated routes, which
57
+ // always go under the base (`withMountedBase`).
44
58
  const popular =
45
59
  popularPages && popularPages.length > 0
46
60
  ? popularPages.map((page) => ({
61
+ href: withBase(page.route),
47
62
  icon: resolvePopularIconMarkup(
48
63
  page.icon,
49
64
  import.meta.env.BASE_URL ?? "/"
50
65
  ),
51
66
  label: page.label,
52
- route: page.route,
53
67
  }))
54
68
  : navigation
55
- ? flattenPages(navigation.sidebar).slice(0, 6)
69
+ ? flattenPages(navigation.sidebar)
70
+ .slice(0, 6)
71
+ .map((page) => ({
72
+ href: withMountedBase(page.route),
73
+ label: page.label,
74
+ }))
56
75
  : [];
57
76
 
58
77
  const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
@@ -67,6 +86,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
67
86
  data-i18n-dev={s.devOnly}
68
87
  data-i18n-empty={s.noResults}
69
88
  data-i18n-error={s.error}
89
+ data-i18n-latest={s.latest}
70
90
  data-i18n-popular={s.popular}
71
91
  data-i18n-results={s.results}
72
92
  data-locale={locale || undefined}
@@ -219,7 +239,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
219
239
  <script>
220
240
  import { navigate } from "astro:transitions/client";
221
241
  import { chromeIcons as icons } from "../../theme/chrome-icons.ts";
222
- import { prefixBase } from "../islands/base-path.ts";
242
+ import { mountBase } from "../islands/base-path.ts";
223
243
  import { escape as escapeHtml } from "html-escaper";
224
244
 
225
245
  import { highlight, matchSnippet } from "./search/types.ts";
@@ -233,10 +253,11 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
233
253
  }
234
254
 
235
255
  interface PopularPage {
256
+ /** The served URL, base included (resolved server-side). */
257
+ href: string;
236
258
  /** Pre-resolved inline SVG from the server; absent falls back to `file`. */
237
259
  icon?: string;
238
260
  label: string;
239
- route: string;
240
261
  }
241
262
 
242
263
  const GRID_COLS = "md:grid-cols-[22rem_minmax(0,1fr)]";
@@ -328,6 +349,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
328
349
  allMsg = "All";
329
350
  popularMsg = "Popular";
330
351
  resultsMsg = "Results";
352
+ latestMsg = "latest";
331
353
  // The active locale to filter to (null when i18n is off), and whether the
332
354
  // reader has opted to search across every language instead.
333
355
  locale: string | null = null;
@@ -382,6 +404,8 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
382
404
  this.getAttribute("data-i18n-popular") || this.popularMsg;
383
405
  this.resultsMsg =
384
406
  this.getAttribute("data-i18n-results") || this.resultsMsg;
407
+ this.latestMsg =
408
+ this.getAttribute("data-i18n-latest") || this.latestMsg;
385
409
  this.locale = this.getAttribute("data-locale");
386
410
  // "" (the current docs) is a real version value, so a presence flag —
387
411
  // not the attribute's truthiness — decides whether filtering is on.
@@ -667,7 +691,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
667
691
  if (this.popular.length > 0) {
668
692
  const group = this.addGroup(this.popularMsg);
669
693
  for (const page of this.popular) {
670
- const item = this.createLinkRow(page.route, page.label, page.icon);
694
+ const item = this.createLinkRow(page.href, page.label, page.icon);
671
695
  group.appendChild(item.el);
672
696
  this.selectables.push(item);
673
697
  }
@@ -751,9 +775,10 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
751
775
 
752
776
  createHitRow(hit: SearchHit, query: string): Selectable {
753
777
  const el = document.createElement("a");
754
- // Index URLs are base-less logical routes; prefix the deployment base so
755
- // clicking a result lands on the page's real served URL.
756
- const href = prefixBase(import.meta.env.BASE_URL, hit.url);
778
+ // Index URLs are base-less logical routes; mount them under the
779
+ // deployment base so clicking a result lands on the page's real served
780
+ // URL, even when the route starts with the base's own name.
781
+ const href = mountBase(import.meta.env.BASE_URL, hit.url);
757
782
  el.href = href;
758
783
  el.className = `${ROW_CLASS} ${MARK}`;
759
784
  // `line-clamp-2` already sets `display`, so no `block` here (it would
@@ -765,7 +790,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
765
790
  // reader knows they're about to leave the docs they're viewing.
766
791
  const versionTag =
767
792
  this.versioned && hit.version !== undefined && hit.version !== this.version
768
- ? `<span class="ms-2 inline-block rounded-full bg-muted px-1.5 py-0.5 align-middle text-[0.65rem] text-muted-foreground">${escapeHtml(hit.version || "latest")}</span>`
793
+ ? `<span class="ms-2 inline-block rounded-full bg-muted px-1.5 py-0.5 align-middle text-[0.65rem] text-muted-foreground">${escapeHtml(hit.version || this.latestMsg)}</span>`
769
794
  : "";
770
795
  el.innerHTML = `
771
796
  <span class="mt-0.5 shrink-0 text-muted-foreground">${svg("file")}</span>
@@ -778,9 +803,8 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
778
803
  return item;
779
804
  }
780
805
 
781
- createLinkRow(url: string, label: string, icon?: string): Selectable {
806
+ createLinkRow(href: string, label: string, icon?: string): Selectable {
782
807
  const el = document.createElement("a");
783
- const href = prefixBase(import.meta.env.BASE_URL, url);
784
808
  el.href = href;
785
809
  el.className = ROW_CLASS;
786
810
  // `icon` is server-resolved markup (built-in Lucide, `<img>`, or
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { withBase } from "../islands/base-path.ts";
2
+ import { withMountedBase } from "../islands/base-path.ts";
3
3
  import Icon from "../Icon.astro";
4
4
 
5
5
  // The "you're viewing an old version" notice, shared by RootLayout and
@@ -29,7 +29,7 @@ const { notice } = Astro.props;
29
29
  <span>{notice.message}</span>
30
30
  <a
31
31
  class="inline-flex items-center gap-1 font-medium underline underline-offset-2"
32
- href={withBase(notice.latestHref)}
32
+ href={withMountedBase(notice.latestHref)}
33
33
  >
34
34
  {notice.latestLabel}
35
35
  <Icon name="arrow-right" size={14} />
@@ -29,6 +29,7 @@ interface AnalyticsWindow {
29
29
  analytics?: { track?: (event: string, props?: TrackProps) => void };
30
30
  clarity?: (command: "event", event: string) => void;
31
31
  dataLayer?: DataLayer;
32
+ databuddy?: { track?: (event: string, props?: TrackProps) => void };
32
33
  fathom?: { trackEvent?: (event: string) => void };
33
34
  gtag?: (command: "event", event: string, props?: TrackProps) => void;
34
35
  heap?: { track?: (event: string, props?: TrackProps) => void };
@@ -39,6 +40,9 @@ interface AnalyticsWindow {
39
40
  pirsch?: (event: string, options?: { meta?: TrackProps }) => void;
40
41
  plausible?: (event: string, options?: { props?: TrackProps }) => void;
41
42
  posthog?: { capture?: (event: string, props?: TrackProps) => void };
43
+ stonks?: {
44
+ event?: (event: string, props?: Record<string, string>) => void;
45
+ };
42
46
  }
43
47
 
44
48
  /** Run one provider call; its failure must not reach the others or the caller. */
@@ -82,7 +86,17 @@ const trackGlobals = (
82
86
  attempt(() => (w.__blumeGtmLayer ?? w.dataLayer)?.push({ ...props, event }));
83
87
  // Privacy-first counters take a name, some with properties.
84
88
  attempt(() => w.plausible?.(event, { props }));
89
+ attempt(() => w.databuddy?.track?.(event, props));
85
90
  attempt(() => w.fathom?.trackEvent?.(event));
91
+ // OneDollarStats only takes string property values.
92
+ attempt(() =>
93
+ w.stonks?.event?.(
94
+ event,
95
+ Object.fromEntries(
96
+ Object.entries(props).map(([key, value]) => [key, String(value)])
97
+ )
98
+ )
99
+ );
86
100
  // Pirsch stringifies `meta` values in place, so it gets its own copy and
87
101
  // the `blume:track` listeners still see the original types.
88
102
  attempt(() => w.pirsch?.(event, { meta: { ...props } }));
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The scrollspy's choice of the active "On this page" entry, kept apart from
3
+ * the `<blume-toc>` element (toc-element.ts) so it runs without a DOM.
4
+ */
5
+
6
+ /** The two measurements the scrollspy takes of a heading. */
7
+ export interface MeasuredHeading {
8
+ getBoundingClientRect: () => { top: number };
9
+ getClientRects: () => { length: number };
10
+ }
11
+
12
+ /**
13
+ * The entry for the section being read: the last heading at or above the
14
+ * trigger line (the first heading before any has reached it), or the last
15
+ * heading once the page is scrolled to the bottom.
16
+ *
17
+ * Only rendered headings compete. A heading inside a hidden tab panel
18
+ * (`display: none`) has no boxes and measures `top: 0`, so it would always
19
+ * read as scrolled past and hold the highlight from the top of the page.
20
+ */
21
+ export const activeTocEntry = <Entry extends { heading: MeasuredHeading }>(
22
+ entries: readonly Entry[],
23
+ triggerOffset: number,
24
+ scrolledToBottom: boolean
25
+ ): Entry | null => {
26
+ const rendered = entries.filter(
27
+ ({ heading }) => heading.getClientRects().length > 0
28
+ );
29
+ if (scrolledToBottom) {
30
+ return rendered.at(-1) ?? null;
31
+ }
32
+ // Headings are in document order, so the last one whose top has reached the
33
+ // trigger line is the section currently being read; default to the first.
34
+ let active = rendered[0] ?? null;
35
+ for (const entry of rendered) {
36
+ if (entry.heading.getBoundingClientRect().top <= triggerOffset) {
37
+ active = entry;
38
+ }
39
+ }
40
+ return active;
41
+ };
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * The active heading is chosen by rect (the last heading at or above a trigger
8
8
  * line just below the sticky header), so it stays correct for sections taller
9
- * than the viewport and at the bottom of the page. An IntersectionObserver is the
9
+ * than the viewport and at the bottom of the page; headings that aren't rendered
10
+ * (inside a hidden tab panel) are skipped (see toc-active.ts). An IntersectionObserver is the
10
11
  * cheap primary trigger — it only fires as headings cross the band near the top —
11
12
  * and a passive, rAF-throttled scroll listener covers the one case it can't: a
12
13
  * final section too short to push its heading past the trigger line.
@@ -14,6 +15,8 @@
14
15
  * Imported for its side effect (registers the element) from RootLayout's script.
15
16
  */
16
17
 
18
+ import { activeTocEntry } from "./toc-active.ts";
19
+
17
20
  interface TocEntry {
18
21
  heading: HTMLElement;
19
22
  link: HTMLAnchorElement;
@@ -93,19 +96,10 @@ class BlumeToc extends HTMLElement {
93
96
  const scrolledToBottom =
94
97
  window.innerHeight + window.scrollY >=
95
98
  document.documentElement.scrollHeight - 2;
96
- if (scrolledToBottom) {
97
- return this.#entries.at(-1)?.link ?? null;
98
- }
99
-
100
- // Headings are in document order, so the last one whose top has reached the
101
- // trigger line is the section currently being read; default to the first.
102
- let active = this.#entries[0]?.link ?? null;
103
- for (const { heading, link } of this.#entries) {
104
- if (heading.getBoundingClientRect().top <= TRIGGER_OFFSET) {
105
- active = link;
106
- }
107
- }
108
- return active;
99
+ return (
100
+ activeTocEntry(this.#entries, TRIGGER_OFFSET, scrolledToBottom)?.link ??
101
+ null
102
+ );
109
103
  }
110
104
  }
111
105
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import specs from "blume:openapi";
3
3
  import { specOf } from "../../openapi/model.ts";
4
- import { withBase } from "../islands/base-path.ts";
4
+ import { withMountedBase } from "../islands/base-path.ts";
5
5
  import MethodBadge from "./MethodBadge.astro";
6
6
 
7
7
  // The operation-link list for one tag section of an API overview page. The
@@ -27,7 +27,7 @@ const operations = Object.values(specOf(specs, source)?.operations ?? {}).filter
27
27
  <li>
28
28
  <a
29
29
  class="flex items-start gap-3 rounded-blume border border-border p-3 text-inherit no-underline! transition-colors hover:border-accent hover:bg-muted hover:no-underline!"
30
- href={withBase(operation.route)}
30
+ href={withMountedBase(operation.route)}
31
31
  >
32
32
  <MethodBadge class="mt-0.5 shrink-0" method={operation.method} />
33
33
  {/* Title over path, stacked, so a long summary and a long route each
@@ -70,7 +70,7 @@ const security = resolveAsyncApiSecurity(
70
70
  );
71
71
  const parameters = channelParameters(channel, document);
72
72
  const messages = operation ? operationMessages(operation, channel, document) : [];
73
- const protocol = protocolOf(operation, channel, servers);
73
+ const protocol = protocolOf(operation, channel, servers, document.components);
74
74
 
75
75
  /** Declared example first, else a sampled value from the payload schema. */
76
76
  const exampleOf = (message: (typeof messages)[number]["message"]): unknown =>
@@ -115,8 +115,8 @@ const samplePanels = sample
115
115
  ? await languageSamplePanels(languages, sample, data.config.codeThemes)
116
116
  : [];
117
117
 
118
- const operationBindings = bindingGroups(operation?.bindings);
119
- const channelBindings = bindingGroups(channel?.bindings);
118
+ const operationBindings = bindingGroups(operation?.bindings, document.components);
119
+ const channelBindings = bindingGroups(channel?.bindings, document.components);
120
120
  ---
121
121
 
122
122
  {
@@ -144,7 +144,10 @@ const channelBindings = bindingGroups(channel?.bindings);
144
144
  // Headers may be multi-format too; undefined with `headers`
145
145
  // present means an inline-unrenderable schema format.
146
146
  const headers = schemaOf(named.message.headers);
147
- const messageBindings = bindingGroups(named.message.bindings);
147
+ const messageBindings = bindingGroups(
148
+ named.message.bindings,
149
+ document.components
150
+ );
148
151
  return (
149
152
  <section class="mt-6">
150
153
  <div
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { withBase } from "../islands/base-path.ts";
2
+ import { withMountedBase } from "../islands/base-path.ts";
3
3
 
4
4
  /**
5
5
  * A schema name (or full type display like `[Pet!]!`) rendered as a link when
@@ -21,7 +21,7 @@ const { name, route, class: className } = Astro.props;
21
21
  route ? (
22
22
  <a
23
23
  class:list={["font-mono text-accent hover:underline", className]}
24
- href={withBase(route)}
24
+ href={withMountedBase(route)}
25
25
  >
26
26
  {name}
27
27
  </a>
@@ -1,4 +1,5 @@
1
1
  ---
2
+ import { isSafeHref } from "../../core/safe-href.ts";
2
3
  import type { GraphqlDocument } from "../../openapi/graphql.ts";
3
4
  import { graphqlTypeDef } from "../../openapi/graphql.ts";
4
5
  import type { ApiOperationRef, ApiSpecData } from "../../openapi/model.ts";
@@ -65,9 +66,16 @@ const SECTION_HEADING = "mb-2 font-semibold text-foreground text-sm";
65
66
  {type.specifiedByUrl && (
66
67
  <div class="mb-4 text-muted-foreground text-sm">
67
68
  Specified by{" "}
68
- <a class="text-accent hover:underline" href={type.specifiedByUrl}>
69
- {type.specifiedByUrl}
70
- </a>
69
+ {/* The URL comes from the schema, someone else's content: a
70
+ `javascript:` one would run on the docs origin when clicked, so
71
+ only a safe destination becomes a link. */}
72
+ {isSafeHref(type.specifiedByUrl) ? (
73
+ <a class="text-accent hover:underline" href={type.specifiedByUrl}>
74
+ {type.specifiedByUrl}
75
+ </a>
76
+ ) : (
77
+ <code class="break-all">{type.specifiedByUrl}</code>
78
+ )}
71
79
  </div>
72
80
  )}
73
81
  <GraphqlFieldsTable
@@ -133,7 +133,7 @@ const BUTTON_QUIET =
133
133
  Payload
134
134
  {
135
135
  model.payload.contentType && (
136
- <span class="ml-1.5 font-mono normal-case">
136
+ <span class="ms-1.5 font-mono normal-case">
137
137
  {model.payload.contentType}
138
138
  </span>
139
139
  )
@@ -6,8 +6,13 @@ import {
6
6
  type ParameterLike,
7
7
  resolveComponentRef,
8
8
  type SchemaLike,
9
+ type SpecValue,
9
10
  } from "./helpers.ts";
10
- import { operationModel } from "./operation-model.ts";
11
+ import {
12
+ effectiveServers,
13
+ operationModel,
14
+ type ServerLike,
15
+ } from "./operation-model.ts";
11
16
  import { buildRequest, defaultValues } from "./request.ts";
12
17
  import {
13
18
  effectiveSecurity,
@@ -35,7 +40,8 @@ interface Props {
35
40
 
36
41
  interface MediaTypeLike {
37
42
  schema?: SchemaLike;
38
- example?: unknown;
43
+ example?: SpecValue;
44
+ examples?: SpecValue;
39
45
  }
40
46
 
41
47
  interface RequestBodyLike {
@@ -59,6 +65,7 @@ interface FullOperation {
59
65
  requestBody?: RequestBodyLike;
60
66
  responses?: Record<string, ResponseLike>;
61
67
  security?: SecurityRequirementLike[];
68
+ servers?: ServerLike[];
62
69
  }
63
70
 
64
71
  const { source, id } = Astro.props;
@@ -73,7 +80,10 @@ const isGraphql = spec?.kind === "graphql";
73
80
  const doc = (spec?.document ?? {}) as {
74
81
  paths?: Record<
75
82
  string,
76
- Record<string, unknown> & { parameters?: ParameterLike[] }
83
+ Record<string, unknown> & {
84
+ parameters?: ParameterLike[];
85
+ servers?: ServerLike[];
86
+ }
77
87
  >;
78
88
  components?: {
79
89
  schemas?: Record<string, SchemaLike>;
@@ -83,7 +93,7 @@ const doc = (spec?.document ?? {}) as {
83
93
  securitySchemes?: Record<string, SecuritySchemeLike>;
84
94
  };
85
95
  security?: SecurityRequirementLike[];
86
- servers?: { url?: string }[];
96
+ servers?: ServerLike[];
87
97
  };
88
98
  const pathItem = ref ? doc.paths?.[ref.path] : undefined;
89
99
  const operation = (
@@ -117,13 +127,20 @@ const security = resolveSecurity(
117
127
  const model =
118
128
  ref && operation
119
129
  ? operationModel({
130
+ components,
120
131
  method: ref.method,
121
132
  parameters: params,
122
133
  path: ref.path,
123
134
  requestBody,
124
135
  schemas,
125
136
  security,
126
- servers: doc.servers ?? [],
137
+ // An operation's own `servers` override its path item's, which
138
+ // override the document's.
139
+ servers: effectiveServers(
140
+ operation.servers,
141
+ pathItem?.servers,
142
+ doc.servers
143
+ ),
127
144
  })
128
145
  : null;
129
146
  const sample = model ? buildRequest(model, defaultValues(model)) : null;
@@ -179,6 +196,7 @@ const languages = sampleLanguages(spec?.codeSamples ?? []);
179
196
  />
180
197
  )}
181
198
  <RequestPanel
199
+ components={components}
182
200
  languages={languages}
183
201
  responses={responses}
184
202
  sample={sample}
@@ -62,7 +62,9 @@ const TAB_CLASS =
62
62
  </div>
63
63
  <blume-panel-tabs class="block overflow-hidden rounded-blume border border-border bg-background">
64
64
  <div class="flex items-center justify-between gap-2 border-border border-b px-3">
65
- <div class="flex flex-wrap gap-4" role="tablist">
65
+ <div aria-label={heading} class="flex flex-wrap gap-4" role="tablist">
66
+ {/* Roving tabindex: only the selected tab is in the Tab order, and
67
+ the arrow keys, Home, and End move between tabs (panel.ts). */}
66
68
  {panels.map((panel, index) => (
67
69
  <button
68
70
  aria-controls={idFor("panel", panel.key)}
@@ -71,6 +73,7 @@ const TAB_CLASS =
71
73
  data-panel-tab={panel.key}
72
74
  id={idFor("tab", panel.key)}
73
75
  role="tab"
76
+ tabindex={index === 0 ? "0" : "-1"}
74
77
  type="button"
75
78
  >
76
79
  {panel.label}
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import { stableId } from "../content/tooltip-id.ts";
3
+ import { withMountedBase } from "../islands/base-path.ts";
3
4
 
4
5
  import { paramKey, type PlaygroundModel } from "./request.ts";
5
6
 
@@ -23,6 +24,11 @@ interface Props {
23
24
 
24
25
  const { model, proxy, slug, operation } = Astro.props;
25
26
 
27
+ // The built-in proxy route carries the site `basePath` but, like every
28
+ // internal URL in the spec data, not `deployment.base`; `withMountedBase`
29
+ // adds it and passes an external proxy URL through untouched.
30
+ const proxyUrl = proxy ? withMountedBase(proxy) : undefined;
31
+
26
32
  // `</script>`-proof embedding: `<` only ever occurs inside JSON strings,
27
33
  // where the \u escape is equivalent, so a spec description can never close
28
34
  // the model script early and inject markup.
@@ -46,7 +52,7 @@ const CHECKBOX_ROW =
46
52
 
47
53
  <blume-playground
48
54
  class="mb-6 block"
49
- data-proxy={proxy || undefined}
55
+ data-proxy={proxyUrl}
50
56
  data-storage-key={`blume-playground:${slug}:${operation}`}
51
57
  >
52
58
  <script
@@ -194,7 +200,7 @@ const CHECKBOX_ROW =
194
200
  <div class={GROUP}>
195
201
  <div class={GROUP_HEADING}>
196
202
  Body
197
- <span class="ml-1.5 font-mono normal-case">
203
+ <span class="ms-1.5 font-mono normal-case">
198
204
  {model.body.contentType}
199
205
  </span>
200
206
  </div>
@@ -2,14 +2,21 @@
2
2
  import data from "blume:data";
3
3
 
4
4
  import { highlightCode } from "../../markdown/index.ts";
5
- import { exampleValue, type SchemaLike, toJson } from "./helpers.ts";
5
+ import {
6
+ type ComponentsLike,
7
+ responseExample,
8
+ type SchemaLike,
9
+ type SpecValue,
10
+ toJson,
11
+ } from "./helpers.ts";
6
12
  import { languageSamplePanels } from "./sample-panels.ts";
7
13
  import PanelTabs from "./PanelTabs.astro";
8
14
  import type { RequestSample, SampleLanguage } from "./snippets.ts";
9
15
 
10
16
  interface MediaTypeLike {
11
17
  schema?: SchemaLike;
12
- example?: unknown;
18
+ example?: SpecValue;
19
+ examples?: SpecValue;
13
20
  }
14
21
 
15
22
  interface ResponseLike {
@@ -18,13 +25,15 @@ interface ResponseLike {
18
25
  }
19
26
 
20
27
  interface Props {
28
+ /** The document's `components`, which `$ref`'d examples resolve against. */
29
+ components?: ComponentsLike;
21
30
  sample: RequestSample;
22
31
  languages: SampleLanguage[];
23
32
  responses: Record<string, ResponseLike>;
24
33
  schemas: Record<string, SchemaLike>;
25
34
  }
26
35
 
27
- const { sample, languages, responses, schemas } = Astro.props;
36
+ const { components, sample, languages, responses, schemas } = Astro.props;
28
37
 
29
38
  const requestPanels = await languageSamplePanels(
30
39
  languages,
@@ -38,9 +47,7 @@ const responsePanels = await Promise.all(
38
47
  Object.entries(response.content ?? {}).find(([type]) =>
39
48
  type.includes("json")
40
49
  )?.[1] ?? Object.values(response.content ?? {})[0];
41
- const example = media
42
- ? (media.example ?? exampleValue(media.schema, schemas))
43
- : undefined;
50
+ const example = media ? responseExample(media, schemas, components) : undefined;
44
51
  const html =
45
52
  example === undefined || example === null
46
53
  ? null