vocs 2.0.17 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/dist/config.d.ts +1 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +1 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/globals.d.ts +16 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal/config.d.ts +28 -0
  11. package/dist/internal/config.d.ts.map +1 -1
  12. package/dist/internal/config.js +10 -2
  13. package/dist/internal/config.js.map +1 -1
  14. package/dist/internal/llms.d.ts +21 -2
  15. package/dist/internal/llms.d.ts.map +1 -1
  16. package/dist/internal/llms.js +41 -1
  17. package/dist/internal/llms.js.map +1 -1
  18. package/dist/internal/markdown-negotiation.d.ts +36 -0
  19. package/dist/internal/markdown-negotiation.d.ts.map +1 -0
  20. package/dist/internal/markdown-negotiation.js +81 -0
  21. package/dist/internal/markdown-negotiation.js.map +1 -0
  22. package/dist/internal/markdown.d.ts.map +1 -1
  23. package/dist/internal/markdown.js +5 -0
  24. package/dist/internal/markdown.js.map +1 -1
  25. package/dist/internal/openapi/anchors.d.ts +25 -0
  26. package/dist/internal/openapi/anchors.d.ts.map +1 -0
  27. package/dist/internal/openapi/anchors.js +37 -0
  28. package/dist/internal/openapi/anchors.js.map +1 -0
  29. package/dist/internal/openapi/app.d.ts +89 -0
  30. package/dist/internal/openapi/app.d.ts.map +1 -0
  31. package/dist/internal/openapi/app.js +62 -0
  32. package/dist/internal/openapi/app.js.map +1 -0
  33. package/dist/internal/openapi/index.d.ts +8 -0
  34. package/dist/internal/openapi/index.d.ts.map +1 -0
  35. package/dist/internal/openapi/index.js +5 -0
  36. package/dist/internal/openapi/index.js.map +1 -0
  37. package/dist/internal/openapi/markdown.d.ts +42 -0
  38. package/dist/internal/openapi/markdown.d.ts.map +1 -0
  39. package/dist/internal/openapi/markdown.js +235 -0
  40. package/dist/internal/openapi/markdown.js.map +1 -0
  41. package/dist/internal/openapi/openapi.d.ts +187 -0
  42. package/dist/internal/openapi/openapi.d.ts.map +1 -0
  43. package/dist/internal/openapi/openapi.js +44 -0
  44. package/dist/internal/openapi/openapi.js.map +1 -0
  45. package/dist/internal/openapi/openrpc.d.ts +90 -0
  46. package/dist/internal/openapi/openrpc.d.ts.map +1 -0
  47. package/dist/internal/openapi/openrpc.js +213 -0
  48. package/dist/internal/openapi/openrpc.js.map +1 -0
  49. package/dist/internal/openapi/parser.d.ts +181 -0
  50. package/dist/internal/openapi/parser.d.ts.map +1 -0
  51. package/dist/internal/openapi/parser.js +329 -0
  52. package/dist/internal/openapi/parser.js.map +1 -0
  53. package/dist/internal/openapi/registry.d.ts +36 -0
  54. package/dist/internal/openapi/registry.d.ts.map +1 -0
  55. package/dist/internal/openapi/registry.js +79 -0
  56. package/dist/internal/openapi/registry.js.map +1 -0
  57. package/dist/internal/openapi/sample.d.ts +115 -0
  58. package/dist/internal/openapi/sample.d.ts.map +1 -0
  59. package/dist/internal/openapi/sample.js +434 -0
  60. package/dist/internal/openapi/sample.js.map +1 -0
  61. package/dist/internal/openapi/search.d.ts +19 -0
  62. package/dist/internal/openapi/search.d.ts.map +1 -0
  63. package/dist/internal/openapi/search.js +98 -0
  64. package/dist/internal/openapi/search.js.map +1 -0
  65. package/dist/internal/openapi/sidebar.d.ts +30 -0
  66. package/dist/internal/openapi/sidebar.d.ts.map +1 -0
  67. package/dist/internal/openapi/sidebar.js +67 -0
  68. package/dist/internal/openapi/sidebar.js.map +1 -0
  69. package/dist/internal/openapi/union.d.ts +36 -0
  70. package/dist/internal/openapi/union.d.ts.map +1 -0
  71. package/dist/internal/openapi/union.js +69 -0
  72. package/dist/internal/openapi/union.js.map +1 -0
  73. package/dist/internal/search.d.ts.map +1 -1
  74. package/dist/internal/search.js +20 -0
  75. package/dist/internal/search.js.map +1 -1
  76. package/dist/internal/vite-plugins.d.ts +12 -0
  77. package/dist/internal/vite-plugins.d.ts.map +1 -1
  78. package/dist/internal/vite-plugins.js +102 -11
  79. package/dist/internal/vite-plugins.js.map +1 -1
  80. package/dist/react/Badge.d.ts +2 -3
  81. package/dist/react/Badge.d.ts.map +1 -1
  82. package/dist/react/Layout.client.d.ts.map +1 -1
  83. package/dist/react/Layout.client.js +2 -2
  84. package/dist/react/Layout.client.js.map +1 -1
  85. package/dist/react/OpenApi.d.ts +6 -0
  86. package/dist/react/OpenApi.d.ts.map +1 -0
  87. package/dist/react/OpenApi.js +6 -0
  88. package/dist/react/OpenApi.js.map +1 -0
  89. package/dist/react/internal/CodeToHtml.client.d.ts +53 -2
  90. package/dist/react/internal/CodeToHtml.client.d.ts.map +1 -1
  91. package/dist/react/internal/CodeToHtml.client.js +154 -21
  92. package/dist/react/internal/CodeToHtml.client.js.map +1 -1
  93. package/dist/react/internal/Sidebar.d.ts.map +1 -1
  94. package/dist/react/internal/Sidebar.js +99 -2
  95. package/dist/react/internal/Sidebar.js.map +1 -1
  96. package/dist/react/internal/openapi/CodeSample.client.d.ts +21 -0
  97. package/dist/react/internal/openapi/CodeSample.client.d.ts.map +1 -0
  98. package/dist/react/internal/openapi/CodeSample.client.js +134 -0
  99. package/dist/react/internal/openapi/CodeSample.client.js.map +1 -0
  100. package/dist/react/internal/openapi/CollapsibleChildren.client.d.ts +17 -0
  101. package/dist/react/internal/openapi/CollapsibleChildren.client.d.ts.map +1 -0
  102. package/dist/react/internal/openapi/CollapsibleChildren.client.js +18 -0
  103. package/dist/react/internal/openapi/CollapsibleChildren.client.js.map +1 -0
  104. package/dist/react/internal/openapi/Disclosure.client.d.ts +28 -0
  105. package/dist/react/internal/openapi/Disclosure.client.d.ts.map +1 -0
  106. package/dist/react/internal/openapi/Disclosure.client.js +38 -0
  107. package/dist/react/internal/openapi/Disclosure.client.js.map +1 -0
  108. package/dist/react/internal/openapi/Endpoints.d.ts +26 -0
  109. package/dist/react/internal/openapi/Endpoints.d.ts.map +1 -0
  110. package/dist/react/internal/openapi/Endpoints.js +33 -0
  111. package/dist/react/internal/openapi/Endpoints.js.map +1 -0
  112. package/dist/react/internal/openapi/EndpointsView.d.ts +24 -0
  113. package/dist/react/internal/openapi/EndpointsView.d.ts.map +1 -0
  114. package/dist/react/internal/openapi/EndpointsView.js +26 -0
  115. package/dist/react/internal/openapi/EndpointsView.js.map +1 -0
  116. package/dist/react/internal/openapi/EnumValues.client.d.ts +14 -0
  117. package/dist/react/internal/openapi/EnumValues.client.d.ts.map +1 -0
  118. package/dist/react/internal/openapi/EnumValues.client.js +20 -0
  119. package/dist/react/internal/openapi/EnumValues.client.js.map +1 -0
  120. package/dist/react/internal/openapi/HeadingAnchor.d.ts +15 -0
  121. package/dist/react/internal/openapi/HeadingAnchor.d.ts.map +1 -0
  122. package/dist/react/internal/openapi/HeadingAnchor.js +12 -0
  123. package/dist/react/internal/openapi/HeadingAnchor.js.map +1 -0
  124. package/dist/react/internal/openapi/OpenApiPage.d.ts +79 -0
  125. package/dist/react/internal/openapi/OpenApiPage.d.ts.map +1 -0
  126. package/dist/react/internal/openapi/OpenApiPage.js +72 -0
  127. package/dist/react/internal/openapi/OpenApiPage.js.map +1 -0
  128. package/dist/react/internal/openapi/Operation.d.ts +25 -0
  129. package/dist/react/internal/openapi/Operation.d.ts.map +1 -0
  130. package/dist/react/internal/openapi/Operation.js +101 -0
  131. package/dist/react/internal/openapi/Operation.js.map +1 -0
  132. package/dist/react/internal/openapi/Playground.client.d.ts +33 -0
  133. package/dist/react/internal/openapi/Playground.client.d.ts.map +1 -0
  134. package/dist/react/internal/openapi/Playground.client.js +170 -0
  135. package/dist/react/internal/openapi/Playground.client.js.map +1 -0
  136. package/dist/react/internal/openapi/PropertyExample.client.d.ts +17 -0
  137. package/dist/react/internal/openapi/PropertyExample.client.d.ts.map +1 -0
  138. package/dist/react/internal/openapi/PropertyExample.client.js +21 -0
  139. package/dist/react/internal/openapi/PropertyExample.client.js.map +1 -0
  140. package/dist/react/internal/openapi/Reference.d.ts +55 -0
  141. package/dist/react/internal/openapi/Reference.d.ts.map +1 -0
  142. package/dist/react/internal/openapi/Reference.js +42 -0
  143. package/dist/react/internal/openapi/Reference.js.map +1 -0
  144. package/dist/react/internal/openapi/Schema.d.ts +110 -0
  145. package/dist/react/internal/openapi/Schema.d.ts.map +1 -0
  146. package/dist/react/internal/openapi/Schema.js +239 -0
  147. package/dist/react/internal/openapi/Schema.js.map +1 -0
  148. package/dist/react/internal/openapi/SchemaUnion.client.d.ts +25 -0
  149. package/dist/react/internal/openapi/SchemaUnion.client.d.ts.map +1 -0
  150. package/dist/react/internal/openapi/SchemaUnion.client.js +48 -0
  151. package/dist/react/internal/openapi/SchemaUnion.client.js.map +1 -0
  152. package/dist/react/internal/openapi/anchor-navigation.client.d.ts +47 -0
  153. package/dist/react/internal/openapi/anchor-navigation.client.d.ts.map +1 -0
  154. package/dist/react/internal/openapi/anchor-navigation.client.js +120 -0
  155. package/dist/react/internal/openapi/anchor-navigation.client.js.map +1 -0
  156. package/dist/react/internal/openapi/auth.d.ts +28 -0
  157. package/dist/react/internal/openapi/auth.d.ts.map +1 -0
  158. package/dist/react/internal/openapi/auth.js +75 -0
  159. package/dist/react/internal/openapi/auth.js.map +1 -0
  160. package/dist/react/useLayout.d.ts +2 -0
  161. package/dist/react/useLayout.d.ts.map +1 -1
  162. package/dist/react/useLayout.js +2 -0
  163. package/dist/react/useLayout.js.map +1 -1
  164. package/dist/server/handlers.d.ts +1 -1
  165. package/dist/server/handlers.d.ts.map +1 -1
  166. package/dist/server/handlers.js +26 -5
  167. package/dist/server/handlers.js.map +1 -1
  168. package/dist/server/og-assets.d.ts +5 -0
  169. package/dist/server/og-assets.d.ts.map +1 -0
  170. package/dist/server/og-assets.js +11 -0
  171. package/dist/server/og-assets.js.map +1 -0
  172. package/dist/server/openapi/assets.d.ts +33 -0
  173. package/dist/server/openapi/assets.d.ts.map +1 -0
  174. package/dist/server/openapi/assets.generated.d.ts +9 -0
  175. package/dist/server/openapi/assets.generated.d.ts.map +1 -0
  176. package/dist/server/openapi/assets.generated.js +1091 -0
  177. package/dist/server/openapi/assets.generated.js.map +1 -0
  178. package/dist/server/openapi/assets.js +32 -0
  179. package/dist/server/openapi/assets.js.map +1 -0
  180. package/dist/server/openapi/handler.d.ts +103 -0
  181. package/dist/server/openapi/handler.d.ts.map +1 -0
  182. package/dist/server/openapi/handler.js +198 -0
  183. package/dist/server/openapi/handler.js.map +1 -0
  184. package/dist/server/openapi/handler.test.d.ts +2 -0
  185. package/dist/server/openapi/handler.test.d.ts.map +1 -0
  186. package/dist/server/openapi/handler.test.js +203 -0
  187. package/dist/server/openapi/handler.test.js.map +1 -0
  188. package/dist/server/openapi/html.d.ts +16 -0
  189. package/dist/server/openapi/html.d.ts.map +1 -0
  190. package/dist/server/openapi/html.js +75 -0
  191. package/dist/server/openapi/html.js.map +1 -0
  192. package/dist/server/openapi/pages.d.ts +33 -0
  193. package/dist/server/openapi/pages.d.ts.map +1 -0
  194. package/dist/server/openapi/pages.js +130 -0
  195. package/dist/server/openapi/pages.js.map +1 -0
  196. package/dist/server/openapi/pages.test.d.ts +2 -0
  197. package/dist/server/openapi/pages.test.d.ts.map +1 -0
  198. package/dist/server/openapi/pages.test.js +94 -0
  199. package/dist/server/openapi/pages.test.js.map +1 -0
  200. package/dist/server/openapi/state.d.ts +42 -0
  201. package/dist/server/openapi/state.d.ts.map +1 -0
  202. package/dist/server/openapi/state.js +101 -0
  203. package/dist/server/openapi/state.js.map +1 -0
  204. package/dist/styles/index.css +16 -0
  205. package/dist/styles/markdown.css +9 -7
  206. package/dist/styles/openapi-playground.css +80 -0
  207. package/dist/styles/openapi.css +660 -0
  208. package/dist/vite.d.ts.map +1 -1
  209. package/dist/vite.js +1 -0
  210. package/dist/vite.js.map +1 -1
  211. package/dist/waku/internal/middleware/md-router.d.ts +0 -4
  212. package/dist/waku/internal/middleware/md-router.d.ts.map +1 -1
  213. package/dist/waku/internal/middleware/md-router.js +3 -48
  214. package/dist/waku/internal/middleware/md-router.js.map +1 -1
  215. package/dist/waku/internal/patches/adapters/vercel-build-enhancer.js +1 -1
  216. package/dist/waku/internal/patches/adapters/vercel-build-enhancer.js.map +1 -1
  217. package/dist/waku/internal/patches/router.d.ts.map +1 -1
  218. package/dist/waku/internal/patches/router.js +114 -1
  219. package/dist/waku/internal/patches/router.js.map +1 -1
  220. package/package.json +5 -1
  221. package/src/config.ts +1 -0
  222. package/src/globals.d.ts +16 -0
  223. package/src/index.ts +1 -0
  224. package/src/internal/config.ts +40 -1
  225. package/src/internal/llms.ts +51 -1
  226. package/src/internal/markdown-negotiation.test.ts +42 -0
  227. package/src/internal/markdown-negotiation.ts +95 -0
  228. package/src/internal/markdown.ts +5 -0
  229. package/src/internal/openapi/anchors.ts +44 -0
  230. package/src/internal/openapi/app.ts +127 -0
  231. package/src/internal/openapi/index.ts +24 -0
  232. package/src/internal/openapi/markdown.test.ts +115 -0
  233. package/src/internal/openapi/markdown.ts +275 -0
  234. package/src/internal/openapi/openapi.ts +212 -0
  235. package/src/internal/openapi/openrpc.test.ts +239 -0
  236. package/src/internal/openapi/openrpc.ts +295 -0
  237. package/src/internal/openapi/parser.test.ts +203 -0
  238. package/src/internal/openapi/parser.ts +613 -0
  239. package/src/internal/openapi/registry.test.ts +89 -0
  240. package/src/internal/openapi/registry.ts +89 -0
  241. package/src/internal/openapi/sample.test.ts +283 -0
  242. package/src/internal/openapi/sample.ts +562 -0
  243. package/src/internal/openapi/search.test.ts +62 -0
  244. package/src/internal/openapi/search.ts +108 -0
  245. package/src/internal/openapi/sidebar.test.ts +131 -0
  246. package/src/internal/openapi/sidebar.ts +94 -0
  247. package/src/internal/openapi/union.test.ts +51 -0
  248. package/src/internal/openapi/union.ts +74 -0
  249. package/src/internal/search.ts +20 -0
  250. package/src/internal/test/virtual-config.stub.ts +14 -0
  251. package/src/internal/vite-plugins.ts +106 -11
  252. package/src/openapi-app/App.tsx +64 -0
  253. package/src/openapi-app/blocks.tsx +33 -0
  254. package/src/openapi-app/client.tsx +25 -0
  255. package/src/openapi-app/links.test.ts +84 -0
  256. package/src/openapi-app/links.ts +66 -0
  257. package/src/openapi-app/payload.ts +20 -0
  258. package/src/openapi-app/virtual/config.ts +7 -0
  259. package/src/openapi-app/virtual/group-icons.ts +2 -0
  260. package/src/openapi-app/virtual/langs.ts +6 -0
  261. package/src/openapi-app/virtual/openapi.ts +10 -0
  262. package/src/openapi-app/virtual/search-index.ts +21 -0
  263. package/src/openapi-app/virtual/slots.ts +4 -0
  264. package/src/openapi-app/virtual/user-styles.ts +2 -0
  265. package/src/openapi-app/waku.tsx +154 -0
  266. package/src/react/Badge.tsx +2 -3
  267. package/src/react/Layout.client.tsx +17 -4
  268. package/src/react/OpenApi.tsx +5 -0
  269. package/src/react/internal/CodeToHtml.client.tsx +283 -22
  270. package/src/react/internal/Sidebar.tsx +126 -22
  271. package/src/react/internal/openapi/CodeSample.client.tsx +294 -0
  272. package/src/react/internal/openapi/CollapsibleChildren.client.tsx +41 -0
  273. package/src/react/internal/openapi/Disclosure.client.tsx +67 -0
  274. package/src/react/internal/openapi/Endpoints.tsx +58 -0
  275. package/src/react/internal/openapi/EndpointsView.tsx +76 -0
  276. package/src/react/internal/openapi/EnumValues.client.tsx +49 -0
  277. package/src/react/internal/openapi/HeadingAnchor.tsx +28 -0
  278. package/src/react/internal/openapi/OpenApiPage.tsx +173 -0
  279. package/src/react/internal/openapi/Operation.test.tsx +101 -0
  280. package/src/react/internal/openapi/Operation.tsx +335 -0
  281. package/src/react/internal/openapi/Playground.client.tsx +234 -0
  282. package/src/react/internal/openapi/PropertyExample.client.tsx +55 -0
  283. package/src/react/internal/openapi/Reference.tsx +120 -0
  284. package/src/react/internal/openapi/Schema.tsx +467 -0
  285. package/src/react/internal/openapi/SchemaUnion.client.tsx +123 -0
  286. package/src/react/internal/openapi/anchor-navigation.client.ts +154 -0
  287. package/src/react/internal/openapi/auth.ts +69 -0
  288. package/src/react/useLayout.ts +4 -0
  289. package/src/server/handlers.ts +31 -6
  290. package/src/server/og-assets.ts +14 -0
  291. package/src/server/openapi/assets.generated.ts +1093 -0
  292. package/src/server/openapi/assets.ts +57 -0
  293. package/src/server/openapi/handler.test.ts +244 -0
  294. package/src/server/openapi/handler.ts +277 -0
  295. package/src/server/openapi/html.ts +84 -0
  296. package/src/server/openapi/pages.test.ts +111 -0
  297. package/src/server/openapi/pages.ts +153 -0
  298. package/src/server/openapi/state.ts +136 -0
  299. package/src/styles/index.css +16 -0
  300. package/src/styles/markdown.css +9 -7
  301. package/src/styles/openapi-playground.css +80 -0
  302. package/src/styles/openapi.css +660 -0
  303. package/src/vite.ts +1 -0
  304. package/src/waku/internal/middleware/md-router.ts +8 -52
  305. package/src/waku/internal/patches/adapters/vercel-build-enhancer.ts +1 -1
  306. package/src/waku/internal/patches/router.ts +131 -1
@@ -0,0 +1,173 @@
1
+ import { specs } from 'virtual:vocs/openapi'
2
+ import type { ReactNode } from 'react'
3
+ import { Layout } from '../../Layout.js'
4
+ import * as MdxPageContext from '../../MdxPageContext.js'
5
+ import { Endpoints } from './Endpoints.js'
6
+ import { HeadingAnchor } from './HeadingAnchor.js'
7
+ import { PlaygroundProvider } from './Playground.client.js'
8
+ import { Prose, ReferenceGroup, ReferenceOverview } from './Reference.js'
9
+
10
+ /**
11
+ * Layout for OpenAPI pages.
12
+ *
13
+ * Single-column pages (the overview and consumer guide pages) show the normal
14
+ * outline (the regular Vocs right gutter) — this is the default. The two-column
15
+ * operation (category) pages opt out (`outline={false}`), since the sidebar
16
+ * already exposes every operation as an anchor and the sticky playground
17
+ * occupies the right column.
18
+ */
19
+ function OpenApiLayout(props: {
20
+ children: React.ReactNode
21
+ outline?: boolean | undefined
22
+ width?: 'default' | 'full'
23
+ }) {
24
+ return (
25
+ <MdxPageContext.Provider
26
+ frontmatter={{ outline: props.outline ?? true, content: { width: props.width ?? 'full' } }}
27
+ >
28
+ <Layout>{props.children}</Layout>
29
+ </MdxPageContext.Provider>
30
+ )
31
+ }
32
+
33
+ /**
34
+ * Renders a consumer-authored "guide" page (a normal MDX page mounted under an
35
+ * OpenAPI section path, e.g. `pages/api/auth.mdx`).
36
+ *
37
+ * Uses the same full-bleed layout as the section landing page (the left
38
+ * centering gutter collapses to the sidebar, so content sits flush after it).
39
+ * Like the landing page it is single-column, so it keeps the normal right
40
+ * gutter/TOC outline (the default). The authored prose is wrapped in
41
+ * `[data-v-openapi-guide]`, which caps it to the readable content width and
42
+ * reapplies markdown block rhythm/heading treatment, so the guide keeps a
43
+ * regular page's article width and padding while matching the gutter layout of
44
+ * the rest of the section.
45
+ *
46
+ * For authored guide pages the frontmatter `title`/`description` are
47
+ * intentionally not rendered as an on-page header; the authored MDX owns its own
48
+ * headings (like a normal page). Trait pages (`x-traitTag`) opt in via `header`,
49
+ * since their title/subtitle come from the spec tag and the body has no heading.
50
+ */
51
+ export function OpenApiGuide(props: OpenApiGuide.Props) {
52
+ return (
53
+ <OpenApiLayout width="full">
54
+ {props.title ? <title>{props.title}</title> : null}
55
+ <div data-v-openapi-guide>
56
+ {props.header && props.title ? (
57
+ <header data-v-openapi-header>
58
+ <h1 data-v data-v-openapi-h1 id={props.id}>
59
+ {props.title}
60
+ {props.id ? <HeadingAnchor id={props.id} /> : null}
61
+ </h1>
62
+ {props.description ? <Prose markdown={props.description} attr="description" /> : null}
63
+ </header>
64
+ ) : null}
65
+ {props.children}
66
+ </div>
67
+ </OpenApiLayout>
68
+ )
69
+ }
70
+
71
+ export declare namespace OpenApiGuide {
72
+ type Props = {
73
+ /** Authored MDX content. */
74
+ children?: ReactNode | undefined
75
+ /** Document `<title>` and page heading (from the page's frontmatter). */
76
+ title?: string | undefined
77
+ /** Subtitle Markdown rendered below the heading. */
78
+ description?: string | undefined
79
+ /**
80
+ * Render `title`/`description` as an on-page header above the content (used
81
+ * by trait pages, whose Markdown body has no heading of its own).
82
+ */
83
+ header?: boolean | undefined
84
+ /** Anchor id for the rendered header heading (enables a copy-link anchor). */
85
+ id?: string | undefined
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Renders an isolated, auto-generated OpenAPI reference section (Vite/RSC site
91
+ * integration). The actual content is rendered by the framework-agnostic
92
+ * {@link file://./Reference.tsx `Reference`} components; this is the Waku
93
+ * adapter that resolves the spec from `virtual:vocs/openapi` and wraps it in the
94
+ * site `Layout`.
95
+ *
96
+ * Behavior depends on the optional `group` prop:
97
+ *
98
+ * - with `group`: renders a single category page.
99
+ * - without `group`: renders an overview/landing page.
100
+ *
101
+ * An optional `intro` (a consumer-authored MDX "override" page mounted at the
102
+ * same route) replaces the auto-generated header while the generated body still
103
+ * renders below it automatically.
104
+ */
105
+ export function OpenApiPage(props: OpenApiPage.Props) {
106
+ const ir = specs[props.mount]
107
+
108
+ if (!ir)
109
+ return (
110
+ <OpenApiLayout>
111
+ <p>No OpenAPI spec is mounted at {props.mount}.</p>
112
+ </OpenApiLayout>
113
+ )
114
+
115
+ // Single category page.
116
+ if (props.group) {
117
+ const group = ir.groups.find((candidate) => candidate.id === props.group)
118
+ if (!group)
119
+ return (
120
+ <OpenApiLayout>
121
+ <p>
122
+ No category “{props.group}” exists in the API mounted at {props.mount}.
123
+ </p>
124
+ </OpenApiLayout>
125
+ )
126
+
127
+ return (
128
+ <OpenApiLayout outline={false}>
129
+ <title>{props.title ?? `${group.name} · ${ir.info.title}`}</title>
130
+ <PlaygroundProvider client={ir.client} mount={ir.path}>
131
+ <ReferenceGroup ir={ir} group={group} intro={props.intro} />
132
+ </PlaygroundProvider>
133
+ </OpenApiLayout>
134
+ )
135
+ }
136
+
137
+ // Overview / landing page.
138
+ return (
139
+ <OpenApiLayout width="full">
140
+ <title>{props.title ?? ir.info.title}</title>
141
+ <ReferenceOverview
142
+ ir={ir}
143
+ intro={props.intro}
144
+ endpoints={props.endpoints ? <Endpoints path={ir.path || undefined} /> : undefined}
145
+ />
146
+ </OpenApiLayout>
147
+ )
148
+ }
149
+
150
+ export declare namespace OpenApiPage {
151
+ type Props = {
152
+ /** Mount path identifying which spec to render (e.g. `/api`). */
153
+ mount: string
154
+ /**
155
+ * Category id to render. When omitted, renders the overview page listing
156
+ * every category.
157
+ */
158
+ group?: string | undefined
159
+ /**
160
+ * Consumer-authored override content rendered in place of the auto-generated
161
+ * header (spec title/description). The generated body still renders below.
162
+ */
163
+ intro?: ReactNode | undefined
164
+ /** Document `<title>` override (e.g. from the override page's frontmatter). */
165
+ title?: string | undefined
166
+ /**
167
+ * Render the domain/endpoint list on the overview page (ignored when `group`
168
+ * or `intro` is set). The standalone handler enables this so its
169
+ * Introduction lists every endpoint by default.
170
+ */
171
+ endpoints?: boolean | undefined
172
+ }
173
+ }
@@ -0,0 +1,101 @@
1
+ import { renderToStaticMarkup } from 'react-dom/server'
2
+ import { describe, expect, test } from 'vitest'
3
+ import type * as OpenApi from '../../../internal/openapi/index.js'
4
+ import { Operation } from './Operation.js'
5
+ import { Schema } from './Schema.js'
6
+
7
+ const operation: OpenApi.IrOperation = {
8
+ id: 'getpet',
9
+ method: 'GET',
10
+ path: '/pets/{petId}',
11
+ summary: 'Get a pet',
12
+ description: 'Returns a **single** pet by id.',
13
+ parameters: [
14
+ {
15
+ name: 'petId',
16
+ in: 'path',
17
+ required: true,
18
+ description: 'The id of the pet',
19
+ schema: { type: 'string' },
20
+ },
21
+ { name: 'expand', in: 'query', schema: { type: 'boolean' } },
22
+ ],
23
+ requestBody: undefined,
24
+ responses: [
25
+ {
26
+ status: '200',
27
+ description: 'A pet',
28
+ content: [
29
+ {
30
+ mediaType: 'application/json',
31
+ schema: {
32
+ type: 'object',
33
+ required: ['id'],
34
+ properties: {
35
+ id: { type: 'string', description: 'Pet id' },
36
+ name: { type: 'string' },
37
+ },
38
+ },
39
+ },
40
+ ],
41
+ headers: [],
42
+ },
43
+ { status: '404', description: 'Not found', content: [], headers: [] },
44
+ ],
45
+ }
46
+
47
+ describe('Operation', () => {
48
+ const html = renderToStaticMarkup(<Operation operation={operation} />)
49
+
50
+ test('renders the heading, method and path', () => {
51
+ expect(html).toContain('Get a pet')
52
+ expect(html).toContain('id="getpet"')
53
+ expect(html).toContain('>GET<')
54
+ expect(html).toContain('/pets/{petId}')
55
+ })
56
+
57
+ test('renders the description as markdown', () => {
58
+ expect(html).toContain('single</strong>')
59
+ })
60
+
61
+ test('renders parameters grouped by location', () => {
62
+ expect(html).toContain('Path Parameters')
63
+ expect(html).toContain('Query Parameters')
64
+ expect(html).toContain('petId')
65
+ expect(html).toContain('The id of the pet')
66
+ expect(html).toContain('required')
67
+ })
68
+
69
+ test('renders responses with status and schema', () => {
70
+ expect(html).toContain('>200<')
71
+ expect(html).toContain('A pet')
72
+ expect(html).toContain('>404<')
73
+ // Response schema property names.
74
+ expect(html).toContain('Pet id')
75
+ })
76
+ })
77
+
78
+ describe('Schema', () => {
79
+ test('renders nested object properties recursively', () => {
80
+ const html = renderToStaticMarkup(
81
+ <Schema
82
+ schema={{
83
+ type: 'object',
84
+ required: ['owner'],
85
+ properties: {
86
+ owner: {
87
+ type: 'object',
88
+ properties: { name: { type: 'string', description: 'Owner name' } },
89
+ },
90
+ },
91
+ }}
92
+ />,
93
+ )
94
+ expect(html).toContain('owner')
95
+ expect(html).toContain('Owner name')
96
+ })
97
+
98
+ test('renders nothing for a schemaless value', () => {
99
+ expect(renderToStaticMarkup(<Schema schema={undefined} />)).toBe('')
100
+ })
101
+ })
@@ -0,0 +1,335 @@
1
+ import LucideChevronRight from '~icons/lucide/chevron-right'
2
+ import * as Markdown from '../../../internal/markdown.js'
3
+ import {
4
+ mediaIdBase,
5
+ requestBodyIdBase,
6
+ responseIdBase,
7
+ slug,
8
+ } from '../../../internal/openapi/anchors.js'
9
+ import type * as OpenApi from '../../../internal/openapi/index.js'
10
+ import { codeSamples, responseSamples } from '../../../internal/openapi/sample.js'
11
+ import { methodVariant } from '../../../internal/openapi/sidebar.js'
12
+ import { unwrapSingleVariant } from '../../../internal/openapi/union.js'
13
+ import { Badge } from '../../Badge.js'
14
+ import { CodeSample } from './CodeSample.client.js'
15
+ import { CollapsibleChildren } from './CollapsibleChildren.client.js'
16
+ import { Disclosure } from './Disclosure.client.js'
17
+ import { HeadingAnchor } from './HeadingAnchor.js'
18
+ import { TestRequestButton } from './Playground.client.js'
19
+ import {
20
+ enumValues,
21
+ PropertyRow,
22
+ Schema,
23
+ schemaExample,
24
+ schemaMeta,
25
+ typeLabel,
26
+ UnionView,
27
+ unionVariants,
28
+ } from './Schema.js'
29
+
30
+ const parameterGroups: { in: OpenApi.IrParameter['in']; title: string }[] = [
31
+ { in: 'path', title: 'Path Parameters' },
32
+ { in: 'query', title: 'Query Parameters' },
33
+ { in: 'header', title: 'Header Parameters' },
34
+ { in: 'cookie', title: 'Cookie Parameters' },
35
+ // JSON-RPC method params (expanded from an OpenRPC document).
36
+ { in: 'rpc', title: 'Parameters' },
37
+ ]
38
+
39
+ export function Operation(props: Operation.Props) {
40
+ const { operation, server, headingLevel = 3, separator = false } = props
41
+ const title = operation.summary || `${operation.method} ${operation.path}`
42
+ const Heading = `h${headingLevel}` as 'h2' | 'h3'
43
+
44
+ const samples = codeSamples(operation, server)
45
+ const responses = responseSamples(operation)
46
+
47
+ return (
48
+ <section
49
+ data-v-openapi-operation
50
+ data-separator={separator || undefined}
51
+ data-deprecated={operation.deprecated || undefined}
52
+ >
53
+ {/* Header: title, endpoint, description. */}
54
+ <div data-v-openapi-operation-header>
55
+ <Heading data-v data-v-openapi-operation-title id={operation.id}>
56
+ {title}
57
+ {operation.deprecated && <Badge variant="warning">Deprecated</Badge>}
58
+ <HeadingAnchor id={operation.id} />
59
+ </Heading>
60
+ <div data-v-openapi-endpoint>
61
+ <Badge variant={methodVariant(operation.method)}>{operation.method}</Badge>
62
+ <code data-v-openapi-endpoint-path>{operation.path}</code>
63
+ </div>
64
+ {operation.description && <Prose markdown={operation.description} />}
65
+ </div>
66
+
67
+ {/* Body: parameters, request body, responses. */}
68
+ <div data-v-openapi-operation-body>
69
+ {parameterGroups.map((group) => {
70
+ const params = operation.parameters.filter((parameter) => parameter.in === group.in)
71
+ if (params.length === 0) return null
72
+ return (
73
+ <Section key={group.in} id={`${operation.id}-${slug(group.title)}`} title={group.title}>
74
+ <ParameterList parameters={params} idPrefix={operation.id} />
75
+ </Section>
76
+ )
77
+ })}
78
+
79
+ {operation.requestBody && !operation.requestBody.hidden && (
80
+ <Section id={`${operation.id}-request-body`} title="Request Body">
81
+ <Content
82
+ content={operation.requestBody.content}
83
+ idBase={requestBodyIdBase(operation.id)}
84
+ />
85
+ </Section>
86
+ )}
87
+
88
+ {operation.responses.length > 0 && (
89
+ <Section id={`${operation.id}-responses`} title="Responses">
90
+ <div data-v-openapi-responses>
91
+ {operation.responses.map((response) => (
92
+ <ResponseRow key={response.status} operationId={operation.id} response={response} />
93
+ ))}
94
+ </div>
95
+ </Section>
96
+ )}
97
+ </div>
98
+
99
+ {/* Aside: request/response sample. Sticky beside the docs on large
100
+ screens; on small screens it moves directly below the header (above
101
+ the body) via the grid template in openapi.css. */}
102
+ {samples.length > 0 && (
103
+ <div data-v-openapi-operation-aside>
104
+ <CodeSample
105
+ samples={samples}
106
+ responses={responses}
107
+ action={
108
+ <TestRequestButton
109
+ method={operation.method}
110
+ path={operation.path}
111
+ example={operation.rpcExample}
112
+ />
113
+ }
114
+ />
115
+ </div>
116
+ )}
117
+ </section>
118
+ )
119
+ }
120
+
121
+ export declare namespace Operation {
122
+ type Props = {
123
+ operation: OpenApi.IrOperation
124
+ /** Base server URL used to build request code samples. */
125
+ server?: string | undefined
126
+ /**
127
+ * Heading level for the operation title. Use `2` on per-category pages
128
+ * (category name is `<h1>`) and `3` on the single-page layout (category is
129
+ * `<h2>`).
130
+ *
131
+ * @default 3
132
+ */
133
+ headingLevel?: 2 | 3 | undefined
134
+ /**
135
+ * Renders a top border separating this operation from the previous one.
136
+ * Pass `true` for every operation except the first.
137
+ *
138
+ * @default false
139
+ */
140
+ separator?: boolean | undefined
141
+ }
142
+ }
143
+
144
+ function Section(props: { id: string; title: string; children: React.ReactNode }) {
145
+ return (
146
+ <section data-v-openapi-section>
147
+ <h3 data-v data-v-openapi-section-title id={props.id}>
148
+ {props.title}
149
+ <HeadingAnchor id={props.id} />
150
+ </h3>
151
+ {props.children}
152
+ </section>
153
+ )
154
+ }
155
+
156
+ function ParameterList(props: { parameters: OpenApi.IrParameter[]; idPrefix: string }) {
157
+ return (
158
+ <div data-v-openapi-params>
159
+ {props.parameters.map((parameter) => {
160
+ // Unwrap a nullable single-variant union (e.g. `oneOf: [null, X]`) so X's
161
+ // type and nested properties render instead of a bare `null | object`.
162
+ const schema = parameter.schema
163
+ ? (unwrapSingleVariant(parameter.schema) ?? parameter.schema)
164
+ : parameter.schema
165
+ const union = unionVariants(schema)
166
+ return (
167
+ <PropertyRow
168
+ key={parameter.name}
169
+ id={`${props.idPrefix}-${slug(parameter.name)}`}
170
+ name={parameter.name}
171
+ type={union ? undefined : typeLabel(schema)}
172
+ values={union ? undefined : enumValues(schema)}
173
+ meta={union ? [] : schemaMeta(schema)}
174
+ example={union ? undefined : parameterExample(parameter)}
175
+ required={parameter.required}
176
+ deprecated={parameter.deprecated}
177
+ description={parameter.description}
178
+ >
179
+ {union ? (
180
+ <UnionView union={union} depth={1} prefix={`${parameter.name}.`} />
181
+ ) : (
182
+ schema &&
183
+ hasChildSchema(schema) && (
184
+ <CollapsibleChildren>
185
+ <div data-v-openapi-children>
186
+ <Schema
187
+ schema={schema}
188
+ depth={1}
189
+ prefix={`${parameter.name}${schema['type'] === 'array' ? '[]' : ''}.`}
190
+ />
191
+ </div>
192
+ </CollapsibleChildren>
193
+ )
194
+ )}
195
+ </PropertyRow>
196
+ )
197
+ })}
198
+ </div>
199
+ )
200
+ }
201
+
202
+ /** Resolves a parameter/header example, preferring the item-level value. */
203
+ function parameterExample(parameter: {
204
+ example?: unknown
205
+ schema?: Record<string, unknown> | undefined
206
+ }): string | undefined {
207
+ if (parameter.example !== undefined)
208
+ return typeof parameter.example === 'string'
209
+ ? parameter.example
210
+ : JSON.stringify(parameter.example)
211
+ return schemaExample(parameter.schema)
212
+ }
213
+
214
+ /** Whether a parameter's schema has nested object/array properties to expand. */
215
+ function hasChildSchema(schema: Record<string, unknown>): boolean {
216
+ if (schema['properties']) return true
217
+ if (schema['type'] === 'array' && schema['items'])
218
+ return hasChildSchema(schema['items'] as Record<string, unknown>)
219
+ return false
220
+ }
221
+
222
+ function Content(props: {
223
+ content: OpenApi.IrMediaType[]
224
+ hideMediaType?: boolean
225
+ idBase?: string | undefined
226
+ }) {
227
+ if (props.content.length === 0) return null
228
+ return (
229
+ <div data-v-openapi-content>
230
+ {props.content.map((media) => {
231
+ const schemaIdBase = props.idBase
232
+ ? mediaIdBase(props.idBase, media.mediaType, props.content.length)
233
+ : undefined
234
+ return (
235
+ <div key={media.mediaType} data-v-openapi-media>
236
+ {!props.hideMediaType && <code data-v-openapi-media-type>{media.mediaType}</code>}
237
+ {media.schema ? (
238
+ <Schema schema={media.schema} idBase={schemaIdBase} />
239
+ ) : media.example !== undefined ? (
240
+ <Example value={media.example} />
241
+ ) : null}
242
+ </div>
243
+ )
244
+ })}
245
+ </div>
246
+ )
247
+ }
248
+
249
+ function ResponseRow(props: { operationId: string; response: OpenApi.IrResponse }) {
250
+ const { operationId, response } = props
251
+ const headers = response.headers ?? []
252
+ const idBase = responseIdBase(operationId, response.status)
253
+ const variant =
254
+ response.status.startsWith('2') || response.status === 'default'
255
+ ? 'success'
256
+ : response.status.startsWith('4') || response.status.startsWith('5')
257
+ ? 'danger'
258
+ : 'info'
259
+ const hasContent = response.content.length > 0 || headers.length > 0
260
+ const header = (
261
+ <>
262
+ {hasContent ? (
263
+ <LucideChevronRight data-v-openapi-response-chevron />
264
+ ) : (
265
+ <span data-v-openapi-response-spacer aria-hidden />
266
+ )}
267
+ <Badge variant={variant} data-v-openapi-response-status>
268
+ {response.status}
269
+ </Badge>
270
+ {response.description && (
271
+ <span data-v-openapi-response-description title={response.description}>
272
+ {response.description}
273
+ </span>
274
+ )}
275
+ </>
276
+ )
277
+
278
+ // Responses without a body have nothing to expand — render a static row.
279
+ if (!hasContent)
280
+ return (
281
+ <div data-v-openapi-response data-static>
282
+ {header}
283
+ </div>
284
+ )
285
+
286
+ return (
287
+ <div data-v-openapi-response>
288
+ <Disclosure trigger={header}>
289
+ <div data-v-openapi-response-content>
290
+ {headers.length > 0 && (
291
+ <CollapsibleChildren label="Headers">
292
+ <div data-v-openapi-params>
293
+ {headers.map((item) => (
294
+ <PropertyRow
295
+ key={item.name}
296
+ name={item.name}
297
+ type={typeLabel(item.schema)}
298
+ values={enumValues(item.schema)}
299
+ meta={schemaMeta(item.schema)}
300
+ example={parameterExample(item)}
301
+ required={item.required}
302
+ deprecated={item.deprecated}
303
+ description={item.description}
304
+ />
305
+ ))}
306
+ </div>
307
+ </CollapsibleChildren>
308
+ )}
309
+ {response.content.length > 0 && (
310
+ <Content content={response.content} hideMediaType idBase={idBase} />
311
+ )}
312
+ </div>
313
+ </Disclosure>
314
+ </div>
315
+ )
316
+ }
317
+
318
+ function Example(props: { value: unknown }) {
319
+ return (
320
+ <pre data-v-openapi-example>
321
+ <code>{JSON.stringify(props.value, null, 2)}</code>
322
+ </pre>
323
+ )
324
+ }
325
+
326
+ function Prose(props: { markdown: string }) {
327
+ return (
328
+ <div
329
+ data-v-openapi-prose
330
+ data-v-content
331
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: server-rendered trusted spec content
332
+ dangerouslySetInnerHTML={{ __html: Markdown.toHtml(props.markdown) }}
333
+ />
334
+ )
335
+ }