blume 2.0.0 → 2.0.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 (454) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +110 -0
  3. package/README.md +2 -2
  4. package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
  5. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  6. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  7. package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
  8. package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
  9. package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
  10. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  11. package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
  12. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  13. package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
  14. package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
  15. package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
  16. package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
  17. package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
  18. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  19. package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
  20. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  21. package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
  22. package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
  23. package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
  24. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  25. package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
  26. package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
  27. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  28. package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
  29. package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
  30. package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
  31. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  32. package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
  33. package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
  34. package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
  35. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  36. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  37. package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
  38. package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
  39. package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
  40. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  41. package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
  42. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  43. package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
  44. package/dist/cli/chunk-j85scx15.js.map +10 -0
  45. package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
  46. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  47. package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
  48. package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
  49. package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
  50. package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
  51. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  52. package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
  53. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  54. package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
  55. package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
  56. package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
  57. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  58. package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
  59. package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
  60. package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
  61. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  62. package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
  63. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  64. package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
  65. package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
  66. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  67. package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
  68. package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
  69. package/dist/cli/index.js +160 -35
  70. package/dist/cli/index.js.map +4 -4
  71. package/dist/types/ai/agent-readability.d.ts +1 -1
  72. package/dist/types/ai/agent-surface.d.ts +32 -0
  73. package/dist/types/ai/api/paths.d.ts +1 -1
  74. package/dist/types/ai/api-catalog.d.ts +7 -1
  75. package/dist/types/ai/ask-context.d.ts +14 -7
  76. package/dist/types/ai/ask.d.ts +43 -43
  77. package/dist/types/ai/component-markdown.d.ts +4 -4
  78. package/dist/types/ai/index.d.ts +3 -3
  79. package/dist/types/ai/openapi-components.d.ts +1 -1
  80. package/dist/types/ai/relative-links.d.ts +9 -4
  81. package/dist/types/ai/serializers.d.ts +1 -1
  82. package/dist/types/ai/static-expression.d.ts +28 -0
  83. package/dist/types/ai/visibility.d.ts +1 -1
  84. package/dist/types/analytics/databuddy.d.ts +43 -0
  85. package/dist/types/analytics/index.d.ts +2 -0
  86. package/dist/types/analytics/schema.d.ts +14 -0
  87. package/dist/types/cli/env.d.ts +5 -0
  88. package/dist/types/cli/init/scaffold.d.ts +19 -3
  89. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  90. package/dist/types/core/base-path.d.ts +9 -0
  91. package/dist/types/core/config-input.d.ts +21 -21
  92. package/dist/types/core/config.d.ts +5 -5
  93. package/dist/types/core/data.d.ts +3 -3
  94. package/dist/types/core/graph.d.ts +2 -0
  95. package/dist/types/core/i18n-ui.d.ts +37 -8
  96. package/dist/types/core/i18n.d.ts +7 -1
  97. package/dist/types/core/links.d.ts +3 -1
  98. package/dist/types/core/load-module.d.ts +10 -0
  99. package/dist/types/core/locale-links.d.ts +12 -2
  100. package/dist/types/core/meta.d.ts +5 -1
  101. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  102. package/dist/types/core/navigation.d.ts +38 -0
  103. package/dist/types/core/ordering-prefix.d.ts +4 -0
  104. package/dist/types/core/safe-links.d.ts +3 -1
  105. package/dist/types/core/schema.d.ts +57 -18
  106. package/dist/types/core/sources/github-releases.d.ts +5 -0
  107. package/dist/types/core/sources/lower.d.ts +14 -2
  108. package/dist/types/core/sources/normalize.d.ts +10 -1
  109. package/dist/types/core/sources/remote.d.ts +11 -1
  110. package/dist/types/core/sources/resolve.d.ts +12 -0
  111. package/dist/types/core/sources/types.d.ts +31 -0
  112. package/dist/types/core/types.d.ts +9 -0
  113. package/dist/types/core/unrecognized-keys.d.ts +1 -1
  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/cloudflare-negotiation.d.ts +3 -2
  117. package/dist/types/deploy/headers.d.ts +31 -7
  118. package/dist/types/deploy/node-headers.d.ts +43 -8
  119. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  120. package/dist/types/deploy/platforms/node.d.ts +6 -5
  121. package/dist/types/deploy/platforms/types.d.ts +7 -0
  122. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  123. package/dist/types/deploy/redirects.d.ts +7 -2
  124. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  125. package/dist/types/openapi/model.d.ts +20 -6
  126. package/dist/types/search/documents.d.ts +1 -1
  127. package/dist/types/search/orama-index.d.ts +1 -1
  128. package/dist/types/search/sync/algolia.d.ts +3 -1
  129. package/docs/01-quickstart.mdx +3 -2
  130. package/docs/02-deployment.mdx +21 -12
  131. package/docs/03-upgrading.mdx +22 -9
  132. package/docs/04-migrating.mdx +4 -4
  133. package/docs/08-faq.mdx +12 -5
  134. package/docs/advanced/changelog.mdx +1 -1
  135. package/docs/advanced/custom-pages.mdx +5 -3
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +5 -5
  138. package/docs/cli/doctor.mdx +4 -4
  139. package/docs/cli/evals.mdx +9 -9
  140. package/docs/cli/index.mdx +6 -3
  141. package/docs/cli/translate.mdx +8 -8
  142. package/docs/configuration/analytics.mdx +23 -2
  143. package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
  144. package/docs/configuration/customization.mdx +5 -4
  145. package/docs/configuration/index.mdx +7 -5
  146. package/docs/configuration/meta.ts +1 -1
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +1 -1
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -3
  151. package/docs/content/index.mdx +4 -2
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +5 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -12
  156. package/docs/content/versioning.mdx +1 -0
  157. package/docs/discoverability/agent-discovery.mdx +22 -8
  158. package/docs/discoverability/index.mdx +3 -3
  159. package/docs/discoverability/llms-txt.mdx +2 -5
  160. package/docs/discoverability/markdown.mdx +5 -3
  161. package/docs/discoverability/mcp.mdx +4 -0
  162. package/docs/discoverability/metadata.mdx +3 -2
  163. package/docs/discoverability/open-graph.mdx +6 -4
  164. package/docs/discoverability/rss.mdx +3 -1
  165. package/docs/index.mdx +2 -2
  166. package/docs/references/asyncapi.mdx +1 -1
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +3 -3
  169. package/package.json +1 -1
  170. package/skills/blume/SKILL.md +5 -5
  171. package/skills/blume-migrate/SKILL.md +6 -6
  172. package/skills/blume-migrate/references/docusaurus.md +6 -6
  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 +1 -1
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +4 -4
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/api/paths.ts +1 -1
  182. package/src/ai/api/spec.ts +15 -2
  183. package/src/ai/api-catalog.ts +7 -1
  184. package/src/ai/ask-context.ts +21 -9
  185. package/src/ai/ask-data.ts +28 -14
  186. package/src/ai/ask.ts +84 -71
  187. package/src/ai/component-markdown.ts +43 -37
  188. package/src/ai/cors.ts +3 -3
  189. package/src/ai/index.ts +16 -16
  190. package/src/ai/link-headers.ts +8 -3
  191. package/src/ai/llms.ts +4 -3
  192. package/src/ai/markdown.ts +23 -11
  193. package/src/ai/mcp/server.ts +78 -7
  194. package/src/ai/openapi-components.ts +1 -1
  195. package/src/ai/relative-links.ts +78 -10
  196. package/src/ai/serializers.ts +1 -1
  197. package/src/ai/static-expression.ts +416 -0
  198. package/src/ai/visibility.ts +46 -15
  199. package/src/analytics/databuddy.ts +67 -0
  200. package/src/analytics/head.ts +4 -0
  201. package/src/analytics/index.ts +2 -0
  202. package/src/analytics/posthog.ts +21 -3
  203. package/src/analytics/schema.ts +2 -0
  204. package/src/astro/generate.ts +90 -45
  205. package/src/astro/module-types.ts +1 -1
  206. package/src/astro/runtime-deps.ts +6 -6
  207. package/src/astro/templates.ts +87 -49
  208. package/src/audit/catalog.ts +2 -2
  209. package/src/audit/checks/assets.ts +25 -5
  210. package/src/audit/checks/content.ts +20 -2
  211. package/src/audit/checks/i18n.ts +46 -8
  212. package/src/audit/checks/indexability.ts +39 -19
  213. package/src/audit/checks/links.ts +14 -0
  214. package/src/audit/checks/llms.ts +6 -3
  215. package/src/audit/checks/network.ts +3 -1
  216. package/src/audit/checks/og-image.ts +10 -0
  217. package/src/audit/checks/robots.ts +6 -1
  218. package/src/audit/checks/sitemap.ts +56 -31
  219. package/src/audit/checks/social.ts +21 -2
  220. package/src/audit/crawl.ts +88 -15
  221. package/src/audit/report.ts +54 -17
  222. package/src/audit/run.ts +1 -0
  223. package/src/audit/types.ts +18 -0
  224. package/src/audit/url.ts +37 -6
  225. package/src/blume-modules.d.ts +2 -2
  226. package/src/cli/build-failure.ts +50 -0
  227. package/src/cli/commands/audit.ts +8 -1
  228. package/src/cli/commands/build.ts +19 -5
  229. package/src/cli/commands/check.ts +2 -0
  230. package/src/cli/commands/doctor.ts +7 -5
  231. package/src/cli/commands/eval.ts +4 -10
  232. package/src/cli/commands/init.ts +15 -5
  233. package/src/cli/commands/migrate.ts +2 -2
  234. package/src/cli/commands/preview.ts +15 -0
  235. package/src/cli/commands/sync.ts +2 -0
  236. package/src/cli/commands/translate.ts +12 -9
  237. package/src/cli/commands/upgrade.ts +13 -2
  238. package/src/cli/eject-scripts.ts +32 -7
  239. package/src/cli/env.ts +12 -1
  240. package/src/cli/init/scaffold.ts +117 -11
  241. package/src/cli/init/starter-spec.ts +235 -0
  242. package/src/cli/required-secrets.ts +3 -3
  243. package/src/components/content/AccordionItem.astro +26 -22
  244. package/src/components/content/Badge.astro +2 -9
  245. package/src/components/content/Card.astro +2 -2
  246. package/src/components/content/Component.astro +9 -3
  247. package/src/components/content/Expandable.astro +5 -1
  248. package/src/components/content/Frame.astro +2 -7
  249. package/src/components/content/GithubInfo.astro +12 -2
  250. package/src/components/content/Prompt.astro +2 -7
  251. package/src/components/content/Tabs.astro +54 -5
  252. package/src/components/content/Tile.astro +1 -1
  253. package/src/components/content/Tooltip.astro +69 -7
  254. package/src/components/content/Tree.astro +7 -2
  255. package/src/components/content/TypeTable.astro +10 -5
  256. package/src/components/content/Update.astro +8 -2
  257. package/src/components/content/auto-type-table.ts +4 -1
  258. package/src/components/content/badge-color.ts +17 -0
  259. package/src/components/content/base-href.ts +18 -3
  260. package/src/components/content/inline-markdown.ts +27 -7
  261. package/src/components/copy-feedback.ts +36 -9
  262. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  263. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
  264. package/src/components/islands/hooks.ts +21 -14
  265. package/src/components/islands/webmcp.ts +12 -8
  266. package/src/components/layout/Banner.astro +23 -4
  267. package/src/components/layout/Header.astro +32 -19
  268. package/src/components/layout/Logo.astro +5 -0
  269. package/src/components/layout/NavSelector.astro +6 -1
  270. package/src/components/layout/NavTabMenu.astro +133 -0
  271. package/src/components/layout/NavTree.astro +15 -6
  272. package/src/components/layout/NavTreeCache.astro +5 -2
  273. package/src/components/layout/NavTreeScript.astro +45 -5
  274. package/src/components/layout/PageLayout.astro +28 -14
  275. package/src/components/layout/Pagination.astro +7 -7
  276. package/src/components/layout/ReferenceLayout.astro +5 -2
  277. package/src/components/layout/RootLayout.astro +27 -14
  278. package/src/components/layout/Search.astro +18 -14
  279. package/src/components/layout/analytics-client.ts +3 -1
  280. package/src/components/layout/drawer-inert.ts +1 -1
  281. package/src/components/openapi/GraphqlType.astro +11 -3
  282. package/src/components/openapi/MessageComposer.astro +1 -1
  283. package/src/components/openapi/Operation.astro +21 -5
  284. package/src/components/openapi/PanelTabs.astro +4 -1
  285. package/src/components/openapi/Playground.astro +8 -2
  286. package/src/components/openapi/RequestPanel.astro +9 -5
  287. package/src/components/openapi/SchemaProperty.astro +11 -33
  288. package/src/components/openapi/SchemaTable.astro +27 -52
  289. package/src/components/openapi/description.ts +2 -2
  290. package/src/components/openapi/helpers.ts +90 -12
  291. package/src/components/openapi/message-composer.ts +8 -0
  292. package/src/components/openapi/message-model.ts +12 -2
  293. package/src/components/openapi/message.ts +4 -1
  294. package/src/components/openapi/operation-model.ts +40 -8
  295. package/src/components/openapi/panel.ts +29 -5
  296. package/src/components/openapi/playground-client.ts +37 -7
  297. package/src/components/openapi/playground-schema.ts +25 -6
  298. package/src/components/openapi/request.ts +2 -0
  299. package/src/components/openapi/schema-tree.ts +203 -0
  300. package/src/components/openapi/snippets.ts +30 -15
  301. package/src/components/openapi/validate-json.ts +1 -1
  302. package/src/core/base-path.ts +15 -0
  303. package/src/core/code-fences.ts +1 -1
  304. package/src/core/config-input.ts +23 -23
  305. package/src/core/config.ts +21 -7
  306. package/src/core/data.ts +3 -3
  307. package/src/core/diagnostics.ts +217 -30
  308. package/src/core/graph.ts +120 -11
  309. package/src/core/i18n-ui.ts +74 -11
  310. package/src/core/i18n.ts +13 -2
  311. package/src/core/links.ts +7 -4
  312. package/src/core/load-module.ts +20 -0
  313. package/src/core/locale-links.ts +17 -17
  314. package/src/core/manifest.ts +3 -2
  315. package/src/core/meta.ts +69 -6
  316. package/src/core/nav-diagnostics.ts +56 -1
  317. package/src/core/navigation.ts +245 -76
  318. package/src/core/ordering-prefix.ts +27 -0
  319. package/src/core/project-graph.ts +23 -1
  320. package/src/core/request-body.ts +1 -1
  321. package/src/core/safe-href.ts +53 -1
  322. package/src/core/safe-links.ts +11 -2
  323. package/src/core/schema.ts +95 -34
  324. package/src/core/server-features.ts +2 -2
  325. package/src/core/sources/assets.ts +83 -41
  326. package/src/core/sources/contentful-rich-text.ts +3 -2
  327. package/src/core/sources/contentful.ts +25 -12
  328. package/src/core/sources/filesystem.ts +5 -1
  329. package/src/core/sources/github-releases.ts +106 -5
  330. package/src/core/sources/lexical.ts +9 -4
  331. package/src/core/sources/lower.ts +79 -26
  332. package/src/core/sources/mdx-remote.ts +1 -0
  333. package/src/core/sources/normalize.ts +132 -30
  334. package/src/core/sources/notion.ts +26 -10
  335. package/src/core/sources/obsidian.ts +17 -3
  336. package/src/core/sources/payload.ts +1 -0
  337. package/src/core/sources/portable-text.ts +63 -44
  338. package/src/core/sources/remote.ts +18 -2
  339. package/src/core/sources/resolve.ts +52 -33
  340. package/src/core/sources/sanity.ts +1 -0
  341. package/src/core/sources/strapi-blocks.ts +9 -4
  342. package/src/core/sources/strapi.ts +1 -0
  343. package/src/core/sources/types.ts +31 -0
  344. package/src/core/types.ts +9 -0
  345. package/src/core/ui-packs/ar.ts +17 -5
  346. package/src/core/ui-packs/bg.ts +17 -5
  347. package/src/core/ui-packs/bn.ts +17 -5
  348. package/src/core/ui-packs/ca.ts +17 -5
  349. package/src/core/ui-packs/cs.ts +17 -5
  350. package/src/core/ui-packs/da.ts +17 -5
  351. package/src/core/ui-packs/de.ts +17 -5
  352. package/src/core/ui-packs/el.ts +17 -5
  353. package/src/core/ui-packs/es.ts +17 -5
  354. package/src/core/ui-packs/fa.ts +17 -5
  355. package/src/core/ui-packs/fi.ts +17 -5
  356. package/src/core/ui-packs/fr.ts +17 -5
  357. package/src/core/ui-packs/he.ts +17 -5
  358. package/src/core/ui-packs/hi.ts +17 -5
  359. package/src/core/ui-packs/hr.ts +17 -5
  360. package/src/core/ui-packs/hu.ts +17 -5
  361. package/src/core/ui-packs/id.ts +17 -5
  362. package/src/core/ui-packs/it.ts +17 -5
  363. package/src/core/ui-packs/ja.ts +17 -5
  364. package/src/core/ui-packs/ko.ts +17 -5
  365. package/src/core/ui-packs/nl.ts +17 -5
  366. package/src/core/ui-packs/no.ts +17 -5
  367. package/src/core/ui-packs/pl.ts +17 -5
  368. package/src/core/ui-packs/pt-br.ts +17 -5
  369. package/src/core/ui-packs/pt.ts +17 -5
  370. package/src/core/ui-packs/ro.ts +17 -5
  371. package/src/core/ui-packs/ru.ts +17 -5
  372. package/src/core/ui-packs/sk.ts +17 -5
  373. package/src/core/ui-packs/sr.ts +17 -5
  374. package/src/core/ui-packs/sv.ts +17 -5
  375. package/src/core/ui-packs/th.ts +17 -5
  376. package/src/core/ui-packs/tr.ts +17 -5
  377. package/src/core/ui-packs/uk.ts +17 -5
  378. package/src/core/ui-packs/vi.ts +17 -5
  379. package/src/core/ui-packs/zh-tw.ts +17 -5
  380. package/src/core/ui-packs/zh.ts +17 -5
  381. package/src/core/unrecognized-keys.ts +1 -1
  382. package/src/core/version-cut.ts +17 -2
  383. package/src/core/versions.ts +4 -1
  384. package/src/deploy/adapters/node.ts +5 -2
  385. package/src/deploy/adapters/registry.ts +2 -1
  386. package/src/deploy/adapters/types.ts +13 -1
  387. package/src/deploy/artifacts.ts +25 -5
  388. package/src/deploy/cloudflare-negotiation.ts +23 -4
  389. package/src/deploy/headers.ts +67 -51
  390. package/src/deploy/node-headers.ts +148 -27
  391. package/src/deploy/platforms/cloudflare.ts +1 -0
  392. package/src/deploy/platforms/netlify.ts +82 -5
  393. package/src/deploy/platforms/node.ts +7 -5
  394. package/src/deploy/platforms/static.ts +1 -0
  395. package/src/deploy/platforms/types.ts +7 -0
  396. package/src/deploy/platforms/vercel.ts +9 -3
  397. package/src/deploy/redirects.ts +14 -3
  398. package/src/deploy/vercel-negotiation.ts +35 -3
  399. package/src/eval/agents.ts +10 -2
  400. package/src/eval/run.ts +25 -0
  401. package/src/markdown/base-links.ts +55 -8
  402. package/src/markdown/index.ts +6 -3
  403. package/src/markdown/relative-links.ts +3 -23
  404. package/src/markdown/route-snapshot.ts +37 -0
  405. package/src/og/card.ts +20 -4
  406. package/src/og/derive.ts +145 -4
  407. package/src/openapi/graphql-build.ts +28 -2
  408. package/src/openapi/model.ts +77 -13
  409. package/src/openapi/render-mdx.ts +10 -1
  410. package/src/registry/eject.ts +135 -37
  411. package/src/search/documents.ts +24 -3
  412. package/src/search/orama-index.ts +1 -1
  413. package/src/search/sync/algolia.ts +36 -2
  414. package/src/sources/registry.ts +5 -0
  415. package/src/theme/entry.ts +11 -4
  416. package/src/translate/agents.ts +6 -1
  417. package/src/translate/ledger.ts +26 -3
  418. package/src/translate/meta.ts +11 -3
  419. package/src/translate/report.ts +1 -1
  420. package/src/translate/run.ts +11 -5
  421. package/src/translate/validate.ts +10 -1
  422. package/src/translate/work-list.ts +36 -3
  423. package/src/upgrade/upgrade.ts +37 -5
  424. package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
  425. package/dist/cli/chunk-5a2z0198.js.map +0 -10
  426. package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
  427. package/dist/cli/chunk-79njf86q.js.map +0 -11
  428. package/dist/cli/chunk-a9kptbw5.js.map +0 -14
  429. package/dist/cli/chunk-abh8yjkn.js +0 -31
  430. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  431. package/dist/cli/chunk-bctazmbk.js.map +0 -10
  432. package/dist/cli/chunk-beat36xx.js.map +0 -10
  433. package/dist/cli/chunk-d1tadaw7.js.map +0 -10
  434. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  435. package/dist/cli/chunk-j8mw0za6.js.map +0 -35
  436. package/dist/cli/chunk-mnqj32sj.js.map +0 -12
  437. package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
  438. package/dist/cli/chunk-nk3ts2xk.js +0 -51
  439. package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
  440. package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
  441. package/dist/cli/chunk-tzne8qfq.js.map +0 -10
  442. package/dist/cli/chunk-xaz13gwg.js.map +0 -182
  443. package/dist/cli/chunk-z01ze5c1.js.map +0 -10
  444. package/dist/cli/chunk-z1f5arsg.js.map +0 -36
  445. package/dist/cli/chunk-zg2gtj10.js.map +0 -35
  446. /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
  447. /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
  448. /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
  449. /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
  450. /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
  451. /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
  452. /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
  453. /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
  454. /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
@@ -12,7 +12,7 @@ import { flattenPages } from "./nav-utils.ts";
12
12
  // `SearchFn` it returns. Only the configured provider's SDK is bundled.
13
13
 
14
14
  interface Props {
15
- askEnabled?: boolean;
15
+ assistantEnabled?: boolean;
16
16
  navigation?: Navigation;
17
17
  /** Curated empty-state pages; falls back to the first sidebar pages when empty or omitted. */
18
18
  popularPages?: { icon?: string; label: string; route: string }[];
@@ -27,7 +27,7 @@ interface Props {
27
27
  }
28
28
 
29
29
  const {
30
- askEnabled = false,
30
+ assistantEnabled = false,
31
31
  navigation,
32
32
  popularPages,
33
33
  strings,
@@ -60,13 +60,14 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
60
60
 
61
61
  <blume-search
62
62
  class="contents"
63
- data-ask={askEnabled ? "" : undefined}
63
+ data-ask={assistantEnabled ? "" : undefined}
64
64
  data-i18n-all={s.all}
65
- data-i18n-ask={s.askAi}
66
- data-i18n-ask-hint={s.askAiHint}
65
+ data-i18n-ask={s.assistant}
66
+ data-i18n-ask-hint={s.assistantHint}
67
67
  data-i18n-dev={s.devOnly}
68
68
  data-i18n-empty={s.noResults}
69
69
  data-i18n-error={s.error}
70
+ data-i18n-latest={s.latest}
70
71
  data-i18n-popular={s.popular}
71
72
  data-i18n-results={s.results}
72
73
  data-locale={locale || undefined}
@@ -309,7 +310,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
309
310
  loadFailed = false;
310
311
  /** First-open client/index load still in flight. */
311
312
  loading = false;
312
- askEnabled = false;
313
+ assistantEnabled = false;
313
314
  popular: PopularPage[] = [];
314
315
  selectables: Selectable[] = [];
315
316
  selectedIndex = -1;
@@ -323,11 +324,12 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
323
324
  devOnlyMsg = "Search is available in the production build.";
324
325
  noResultsMsg = "No results found.";
325
326
  errorMsg = "Something went wrong. Please try again.";
326
- askMsg = "Ask AI";
327
+ askMsg = "Assistant";
327
328
  askHintMsg = "Get an instant answer from AI";
328
329
  allMsg = "All";
329
330
  popularMsg = "Popular";
330
331
  resultsMsg = "Results";
332
+ latestMsg = "latest";
331
333
  // The active locale to filter to (null when i18n is off), and whether the
332
334
  // reader has opted to search across every language instead.
333
335
  locale: string | null = null;
@@ -351,7 +353,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
351
353
  !event.shiftKey &&
352
354
  !event.altKey
353
355
  ) {
354
- // ⌘K toggles, mirroring the Ask AI panel's ⌘I: pressing it with
356
+ // ⌘K toggles, mirroring the assistant panel's ⌘I: pressing it with
355
357
  // the dialog open must close it, not re-showModal an open dialog
356
358
  // (an InvalidStateError on older engines).
357
359
  event.preventDefault();
@@ -382,6 +384,8 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
382
384
  this.getAttribute("data-i18n-popular") || this.popularMsg;
383
385
  this.resultsMsg =
384
386
  this.getAttribute("data-i18n-results") || this.resultsMsg;
387
+ this.latestMsg =
388
+ this.getAttribute("data-i18n-latest") || this.latestMsg;
385
389
  this.locale = this.getAttribute("data-locale");
386
390
  // "" (the current docs) is a real version value, so a presence flag —
387
391
  // not the attribute's truthiness — decides whether filtering is on.
@@ -394,7 +398,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
394
398
  this.results = this.querySelector("[data-blume-search-results]")!;
395
399
  this.message = this.querySelector("[data-blume-search-message]")!;
396
400
  this.preview = this.querySelector("[data-blume-search-preview]")!;
397
- this.askEnabled = this.hasAttribute("data-ask");
401
+ this.assistantEnabled = this.hasAttribute("data-ask");
398
402
 
399
403
  const popularScript = this.querySelector(
400
404
  "[data-blume-search-popular]"
@@ -636,7 +640,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
636
640
 
637
641
  this.renderFilters(result.sections);
638
642
 
639
- if (this.askEnabled) {
643
+ if (this.assistantEnabled) {
640
644
  const group = this.addGroup(this.askMsg);
641
645
  const ask = this.createAskRow(query);
642
646
  group.appendChild(ask.el);
@@ -650,7 +654,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
650
654
  group.appendChild(item.el);
651
655
  this.selectables.push(item);
652
656
  }
653
- } else if (!this.askEnabled) {
657
+ } else if (!this.assistantEnabled) {
654
658
  this.setMessage(this.noResultsMsg);
655
659
  }
656
660
 
@@ -658,7 +662,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
658
662
  }
659
663
 
660
664
  renderEmpty() {
661
- if (this.askEnabled) {
665
+ if (this.assistantEnabled) {
662
666
  const group = this.addGroup(this.askMsg);
663
667
  const ask = this.createAskRow("");
664
668
  group.appendChild(ask.el);
@@ -765,7 +769,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
765
769
  // reader knows they're about to leave the docs they're viewing.
766
770
  const versionTag =
767
771
  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>`
772
+ ? `<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
773
  : "";
770
774
  el.innerHTML = `
771
775
  <span class="mt-0.5 shrink-0 text-muted-foreground">${svg("file")}</span>
@@ -874,7 +878,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
874
878
  const query = this.input.value.trim();
875
879
  this.dialog.close();
876
880
  window.dispatchEvent(
877
- new CustomEvent("blume:open-ask-ai", { detail: { query } })
881
+ new CustomEvent("blume:open-assistant", { detail: { query } })
878
882
  );
879
883
  } else if (item.url) {
880
884
  // Close first so the dialog isn't left open over the transition;
@@ -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 };
@@ -82,6 +83,7 @@ const trackGlobals = (
82
83
  attempt(() => (w.__blumeGtmLayer ?? w.dataLayer)?.push({ ...props, event }));
83
84
  // Privacy-first counters take a name, some with properties.
84
85
  attempt(() => w.plausible?.(event, { props }));
86
+ attempt(() => w.databuddy?.track?.(event, props));
85
87
  attempt(() => w.fathom?.trackEvent?.(event));
86
88
  // Pirsch stringifies `meta` values in place, so it gets its own copy and
87
89
  // the `blume:track` listeners still see the original types.
@@ -96,7 +98,7 @@ const trackGlobals = (
96
98
  * @param props Properties every provider receives.
97
99
  * @param local Properties only the `blume:track` CustomEvent carries — free
98
100
  * text a site may bridge to a provider on its own terms, but that must not
99
- * reach third parties unasked (a reader's Ask AI question, for instance).
101
+ * reach third parties unasked (a reader's question to the assistant, for instance).
100
102
  */
101
103
  export const track = (
102
104
  event: string,
@@ -119,7 +119,7 @@ export const syncDrawerInert = (): void => {
119
119
  });
120
120
  document.addEventListener("astro:after-swap", sync);
121
121
  document.addEventListener("keydown", (event) => {
122
- // An open dialog (search, Ask AI) closes itself on Escape first.
122
+ // An open dialog (search, the assistant) closes itself on Escape first.
123
123
  if (
124
124
  event.key !== "Escape" ||
125
125
  !isOpen() ||
@@ -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 = (
@@ -123,7 +133,13 @@ const model =
123
133
  requestBody,
124
134
  schemas,
125
135
  security,
126
- servers: doc.servers ?? [],
136
+ // An operation's own `servers` override its path item's, which
137
+ // override the document's.
138
+ servers: effectiveServers(
139
+ operation.servers,
140
+ pathItem?.servers,
141
+ doc.servers
142
+ ),
127
143
  })
128
144
  : null;
129
145
  const sample = model ? buildRequest(model, defaultValues(model)) : null;
@@ -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 { withBase } 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`; `withBase` adds it
29
+ // and passes an external proxy URL through untouched.
30
+ const proxyUrl = proxy ? withBase(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,20 @@
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
+ responseExample,
7
+ type SchemaLike,
8
+ type SpecValue,
9
+ toJson,
10
+ } from "./helpers.ts";
6
11
  import { languageSamplePanels } from "./sample-panels.ts";
7
12
  import PanelTabs from "./PanelTabs.astro";
8
13
  import type { RequestSample, SampleLanguage } from "./snippets.ts";
9
14
 
10
15
  interface MediaTypeLike {
11
16
  schema?: SchemaLike;
12
- example?: unknown;
17
+ example?: SpecValue;
18
+ examples?: SpecValue;
13
19
  }
14
20
 
15
21
  interface ResponseLike {
@@ -38,9 +44,7 @@ const responsePanels = await Promise.all(
38
44
  Object.entries(response.content ?? {}).find(([type]) =>
39
45
  type.includes("json")
40
46
  )?.[1] ?? Object.values(response.content ?? {})[0];
41
- const example = media
42
- ? (media.example ?? exampleValue(media.schema, schemas))
43
- : undefined;
47
+ const example = media ? responseExample(media, schemas) : undefined;
44
48
  const html =
45
49
  example === undefined || example === null
46
50
  ? null
@@ -3,33 +3,23 @@ import Description from "./Description.astro";
3
3
  import {
4
4
  constraints,
5
5
  isNullable,
6
- refName,
7
6
  resolveSchema,
8
7
  type SchemaLike,
9
8
  typeLabel,
10
9
  } from "./helpers.ts";
10
+ import { rowExpands, type SchemaRow } from "./schema-tree.ts";
11
11
  import SchemaTable from "./SchemaTable.astro";
12
12
 
13
13
  interface Props {
14
- name: string;
15
- schema: SchemaLike;
16
- required?: boolean;
14
+ /** One planned row of the parent table (see `schema-tree.ts`). */
15
+ row: SchemaRow;
17
16
  schemas: Record<string, SchemaLike>;
18
- seen?: string[];
19
17
  expandAll?: boolean;
20
18
  }
21
19
 
22
- const {
23
- name,
24
- schema,
25
- required = false,
26
- schemas,
27
- seen = [],
28
- expandAll = false,
29
- } = Astro.props;
20
+ const { row, schemas, expandAll = false } = Astro.props;
21
+ const { name, schema, required } = row;
30
22
 
31
- const refLabel = typeof schema.$ref === "string" ? refName(schema.$ref) : null;
32
- const circular = refLabel !== null && seen.includes(refLabel);
33
23
  const resolved = resolveSchema(schemas, schema);
34
24
 
35
25
  const type = typeLabel(schema);
@@ -39,20 +29,9 @@ const nullable = isNullable(resolved);
39
29
  const enumValues = Array.isArray(resolved.enum) ? resolved.enum : null;
40
30
  const limits = constraints(resolved);
41
31
 
42
- const types = Array.isArray(resolved.type) ? resolved.type : [resolved.type];
43
- const isArray = types.includes("array");
44
- const items = isArray ? resolveSchema(schemas, resolved.items) : null;
45
-
46
- const hasObjectShape = (candidate: SchemaLike): boolean =>
47
- Boolean(
48
- candidate.properties ||
49
- candidate.allOf ||
50
- candidate.oneOf ||
51
- candidate.anyOf ||
52
- typeof candidate.$ref === "string"
53
- );
54
- const expandable =
55
- !circular && (hasObjectShape(resolved) || Boolean(items && hasObjectShape(items)));
32
+ // Circles and schemas expanded elsewhere in the tree have no disclosure; the
33
+ // type label names them.
34
+ const children = rowExpands(row) ? row.children : undefined;
56
35
  ---
57
36
 
58
37
  {/* The row's vertical scale is 8 / 12 / 16px, and every other reference table copies it. A
@@ -103,17 +82,16 @@ const expandable =
103
82
  )
104
83
  }
105
84
  {
106
- expandable && (
85
+ children && (
107
86
  <details class="mt-3" open={expandAll}>
108
87
  <summary class="cursor-pointer select-none text-accent text-xs hover:underline">
109
88
  <span class="[details[open]>summary_&]:hidden">Show properties</span>
110
89
  <span class="hidden [details[open]>summary_&]:inline">Hide properties</span>
111
90
  </summary>
112
- <div class="mt-3 border-border border-l pl-4">
91
+ <div class="mt-3 border-border border-s ps-4">
113
92
  <SchemaTable
114
- schema={schema}
93
+ node={children.node}
115
94
  schemas={schemas}
116
- seen={seen}
117
95
  expandAll={expandAll}
118
96
  />
119
97
  </div>
@@ -1,86 +1,61 @@
1
1
  ---
2
- import {
3
- objectProperties,
4
- refName,
5
- resolveSchema,
6
- type SchemaLike,
7
- typeLabel,
8
- } from "./helpers.ts";
2
+ import type { SchemaLike } from "./helpers.ts";
3
+ import { type SchemaNode, schemaTree } from "./schema-tree.ts";
9
4
  import SchemaProperty from "./SchemaProperty.astro";
10
5
 
11
6
  interface Props {
12
- schema: SchemaLike;
7
+ /** The schema to render, planned here when no `node` is given. */
8
+ schema?: SchemaLike;
9
+ /** An already-planned subtree: how nested tables render. */
10
+ node?: SchemaNode;
13
11
  schemas: Record<string, SchemaLike>;
14
- /** `$ref` names already on the current branch, to break circular schemas. */
15
- seen?: string[];
16
12
  expandAll?: boolean;
17
13
  }
18
14
 
19
- const { schema, schemas, seen = [], expandAll = false } = Astro.props;
15
+ const { schema = {}, node: planned, schemas, expandAll = false } = Astro.props;
20
16
 
21
- // Track the ref so a self-referential model stops instead of recursing forever.
22
- const refLabel =
23
- typeof schema.$ref === "string" ? refName(schema.$ref) : null;
24
- const circular = refLabel !== null && seen.includes(refLabel);
25
- const nextSeen = refLabel ? [...seen, refLabel] : seen;
26
- const resolved = circular ? schema : resolveSchema(schemas, schema);
27
-
28
- const types = Array.isArray(resolved.type) ? resolved.type : [resolved.type];
29
- const isArray = types.includes("array");
30
- // Keep array items unresolved so a self-referential item `$ref` stays trackable
31
- // via `seen` — resolving here would drop the name and loop forever.
32
- const items = isArray ? (resolved.items ?? null) : null;
33
- const branches = resolved.oneOf ?? resolved.anyOf ?? null;
34
-
35
- const { properties, required } = circular
36
- ? { properties: [] as [string, SchemaLike][], required: new Set<string>() }
37
- : objectProperties(resolved, schemas);
17
+ // The whole tree is planned once, at the top-level table, so that which named
18
+ // schemas expand is decided for the tree as a whole (see `schema-tree.ts`).
19
+ const node = planned ?? schemaTree(schema, schemas);
38
20
  ---
39
21
 
40
22
  {
41
- circular ? (
23
+ node.kind === "circular" ? (
42
24
  <div class="text-muted-foreground text-sm">
43
- Circular reference to <code class="text-foreground">{refLabel}</code>.
25
+ Circular reference to <code class="text-foreground">{node.name}</code>.
44
26
  </div>
45
- ) : isArray ? (
27
+ ) : node.kind === "array" ? (
46
28
  <div>
47
29
  <div class="mb-2 text-muted-foreground text-xs">
48
- Array of <code class="text-foreground">{typeLabel(items ?? {})}</code>
30
+ Array of <code class="text-foreground">{node.itemsLabel}</code>
49
31
  </div>
50
- {items && (
51
- <Astro.self schema={items} schemas={schemas} seen={nextSeen} expandAll={expandAll} />
32
+ {node.items && (
33
+ <Astro.self node={node.items.node} schemas={schemas} expandAll={expandAll} />
52
34
  )}
53
35
  </div>
54
- ) : branches ? (
36
+ ) : node.kind === "branches" ? (
55
37
  <div class="flex flex-col gap-3">
56
- <div class="text-muted-foreground text-xs">
57
- {resolved.oneOf ? "One of" : "Any of"}:
58
- </div>
59
- {branches.map((branch, index) => (
38
+ <div class="text-muted-foreground text-xs">{node.label}:</div>
39
+ {node.branches.map((branch) => (
60
40
  <div class="rounded-blume border border-border p-3">
61
41
  <div class="mb-2 font-medium text-foreground text-xs">
62
- {typeLabel(branch) || `Option ${index + 1}`}
42
+ {branch.label}
63
43
  </div>
64
- <Astro.self schema={branch} schemas={schemas} seen={nextSeen} expandAll={expandAll} />
44
+ <Astro.self node={branch.slot.node} schemas={schemas} expandAll={expandAll} />
65
45
  </div>
66
46
  ))}
67
47
  </div>
68
- ) : properties.length > 0 ? (
48
+ ) : node.kind === "properties" ? (
69
49
  <div class="not-prose">
70
- {properties.map(([name, prop]) => (
71
- <SchemaProperty
72
- name={name}
73
- schema={prop}
74
- required={required.has(name)}
75
- schemas={schemas}
76
- seen={nextSeen}
77
- expandAll={expandAll}
78
- />
50
+ {node.rows.map((row) => (
51
+ <SchemaProperty row={row} schemas={schemas} expandAll={expandAll} />
79
52
  ))}
80
53
  </div>
81
54
  ) : (
82
55
  <div class="text-muted-foreground text-sm">
83
- <code class="text-foreground">{typeLabel(resolved)}</code>
56
+ <code class="text-foreground">
57
+ {node.kind === "named" ? node.name : node.label}
58
+ </code>
84
59
  </div>
85
60
  )
86
61
  }
@@ -14,7 +14,7 @@ import { Marked } from "marked";
14
14
  * `marked` rather than the site's own Markdown pipeline: the pipeline is async, plugin-laden and
15
15
  * built for whole documents, while these are thousands of short strings per build — a large
16
16
  * reference renders tens of thousands of them. `marked` is synchronous, already a dependency,
17
- * and already how the Ask AI island renders model Markdown.
17
+ * and already how the assistant island renders model Markdown.
18
18
  */
19
19
  const TABLE = /<table>[\s\S]*?<\/table>/gu;
20
20
 
@@ -30,7 +30,7 @@ const escapeHtml = (text: string): string =>
30
30
  text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
31
31
 
32
32
  const markdown = new Marked({
33
- // `breaks` is deliberately NOT set, unlike the Ask AI island. Docstring prose is hard-wrapped at
33
+ // `breaks` is deliberately NOT set, unlike the assistant island. Docstring prose is hard-wrapped at
34
34
  // 72 or 79 columns, so honouring single newlines would break every sentence mid-flow at exactly
35
35
  // the width the source file happened to use.
36
36
  breaks: false,