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,467 @@
1
+ import { Fragment } from 'react'
2
+ import * as Markdown from '../../../internal/markdown.js'
3
+ import { type SchemaPath, schemaPropertyId } from '../../../internal/openapi/anchors.js'
4
+ import {
5
+ unionVariantSchemas,
6
+ unionVariantSegment,
7
+ unwrapSingleVariant,
8
+ } from '../../../internal/openapi/union.js'
9
+ import { Badge } from '../../Badge.js'
10
+ import { CollapsibleChildren } from './CollapsibleChildren.client.js'
11
+ import { EnumValues } from './EnumValues.client.js'
12
+ import { HeadingAnchor } from './HeadingAnchor.js'
13
+ import { PropertyExample } from './PropertyExample.client.js'
14
+ import { SchemaUnion } from './SchemaUnion.client.js'
15
+
16
+ type SchemaObject = Record<string, unknown>
17
+
18
+ const maxDepth = 6
19
+
20
+ /**
21
+ * Renders a human-readable type label for a JSON Schema (post-dereference).
22
+ */
23
+ export function typeLabel(schema: SchemaObject | undefined): string {
24
+ if (!schema) return 'unknown'
25
+
26
+ if (Array.isArray(schema['type'])) return (schema['type'] as string[]).join(' | ')
27
+
28
+ const composite = (schema['oneOf'] ?? schema['anyOf']) as SchemaObject[] | undefined
29
+ if (composite) return composite.map(typeLabel).join(' | ')
30
+
31
+ if (Array.isArray(schema['allOf']))
32
+ return (schema['allOf'] as SchemaObject[]).map(typeLabel).join(' & ')
33
+
34
+ if (schema['type'] === 'array') return `${typeLabel(schema['items'] as SchemaObject)}[]`
35
+
36
+ if (typeof schema['type'] === 'string') {
37
+ const format = schema['format'] ? ` <${schema['format']}>` : ''
38
+ return `${schema['type']}${format}`
39
+ }
40
+
41
+ // Enum without an explicit `type`: the literal values are rendered separately
42
+ // as a values list (see {@link enumValues}), so just label it `enum`.
43
+ if (Array.isArray(schema['enum'])) return 'enum'
44
+
45
+ if (schema['properties']) return 'object'
46
+
47
+ return 'unknown'
48
+ }
49
+
50
+ /**
51
+ * Detects a `oneOf`/`anyOf` union (unwrapping array item schemas) worth showing
52
+ * as a {@link SchemaUnion} variant picker, returning its variants. Trivial
53
+ * `null` members are dropped, and plain scalar unions with no extra info (e.g.
54
+ * `string | number`) keep their inline type label instead — the picker is only
55
+ * used when at least one variant carries something to show (object properties,
56
+ * an enum, a `const`, a format, or a title).
57
+ */
58
+ export function unionVariants(schema: SchemaObject | undefined): unionVariants.Result | undefined {
59
+ const variants = unionVariantSchemas(schema)
60
+ if (!variants) return undefined
61
+ const target =
62
+ schema?.['type'] === 'array' && schema['items'] ? (schema['items'] as SchemaObject) : schema
63
+ const oneOf = target?.['oneOf']
64
+ return {
65
+ kind: oneOf ? 'One of' : 'Any of',
66
+ variants: variants.map((member, index) => ({
67
+ name: variantName(member, index),
68
+ schema: member,
69
+ })),
70
+ }
71
+ }
72
+
73
+ /** A human-readable label for a single union variant. */
74
+ function variantName(member: SchemaObject, index: number): string {
75
+ if (typeof member['title'] === 'string' && member['title']) return member['title']
76
+ if ('const' in member) return stringify(member['const'])
77
+ const values = enumValues(member)
78
+ if (values && values.length > 0) {
79
+ const joined = values.join(' | ')
80
+ if (joined.length <= 32) return joined
81
+ return 'enum'
82
+ }
83
+ return typeLabel(member) || `Option ${index + 1}`
84
+ }
85
+
86
+ export declare namespace unionVariants {
87
+ type Result = {
88
+ kind: string
89
+ variants: { name: string; schema: SchemaObject }[]
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Extracts a schema's enum literals as display strings (strings unquoted, other
95
+ * values JSON-stringified), unwrapping array item schemas. These are rendered
96
+ * as a separate "values" list rather than a wrapping inline union, matching the
97
+ * Scalar/Stripe reference UI.
98
+ */
99
+ export function enumValues(schema: SchemaObject | undefined): string[] | undefined {
100
+ if (!schema) return undefined
101
+ const target =
102
+ schema['type'] === 'array' && schema['items'] ? (schema['items'] as SchemaObject) : schema
103
+ const values = target['enum']
104
+ if (!Array.isArray(values) || values.length === 0) return undefined
105
+ return values.map((value) => (typeof value === 'string' ? value : JSON.stringify(value)))
106
+ }
107
+
108
+ /**
109
+ * Builds the constraint metadata shown beneath a property/parameter type as
110
+ * `{ label, value }` pairs (e.g. `min 0`, `max 100`, `default 10`), in the
111
+ * style of Scalar/Stripe references. The type itself is rendered separately
112
+ * (see {@link typeLabel}).
113
+ */
114
+ export function schemaMeta(schema: SchemaObject | undefined): schemaMeta.Entry[] {
115
+ if (!schema) return []
116
+ const parts: schemaMeta.Entry[] = []
117
+
118
+ const num = (key: string) =>
119
+ typeof schema[key] === 'number' ? (schema[key] as number) : undefined
120
+ const min = num('minimum') ?? num('minLength') ?? num('minItems')
121
+ const max = num('maximum') ?? num('maxLength') ?? num('maxItems')
122
+ if (min !== undefined) parts.push({ label: 'min', value: String(min) })
123
+ if (max !== undefined) parts.push({ label: 'max', value: String(max) })
124
+
125
+ if ('const' in schema) parts.push({ label: 'const', value: stringify(schema['const']) })
126
+ if ('default' in schema) parts.push({ label: 'default', value: stringify(schema['default']) })
127
+
128
+ return parts
129
+ }
130
+
131
+ export declare namespace schemaMeta {
132
+ type Entry = { label: string; value: string }
133
+ }
134
+
135
+ function stringify(value: unknown): string {
136
+ if (typeof value === 'string') return value
137
+ return JSON.stringify(value)
138
+ }
139
+
140
+ /**
141
+ * Extracts a representative example value from a schema (`example` or the first
142
+ * entry of `examples`), returned as a display string.
143
+ */
144
+ export function schemaExample(schema: SchemaObject | undefined): string | undefined {
145
+ if (!schema) return undefined
146
+ if ('example' in schema) return stringify(schema['example'])
147
+ const examples = schema['examples']
148
+ if (Array.isArray(examples) && examples.length > 0) return stringify(examples[0])
149
+ return undefined
150
+ }
151
+
152
+ /**
153
+ * A single parameter/property row: name + inline metadata, an optional
154
+ * markdown description, and optional nested content (child schema). Rows are
155
+ * separated by a top border rather than wrapped in individual cards.
156
+ */
157
+ export function PropertyRow(props: PropertyRow.Props) {
158
+ const {
159
+ id,
160
+ name,
161
+ prefix,
162
+ type,
163
+ values,
164
+ meta,
165
+ example,
166
+ required,
167
+ deprecated,
168
+ description,
169
+ children,
170
+ } = props
171
+ const inline: { key: string; node: React.ReactNode }[] = [
172
+ ...(type
173
+ ? [
174
+ {
175
+ key: 'type',
176
+ node: (
177
+ <code data-v data-v-openapi-type>
178
+ {type}
179
+ </code>
180
+ ),
181
+ },
182
+ ]
183
+ : []),
184
+ ...meta.map((item) => ({
185
+ key: item.label,
186
+ node: (
187
+ <span>
188
+ <span data-v-openapi-meta-label>{item.label} </span>
189
+ <span data-v-openapi-meta-value>{item.value}</span>
190
+ </span>
191
+ ),
192
+ })),
193
+ ]
194
+ return (
195
+ <div data-v-openapi-property id={id}>
196
+ <div data-v-openapi-property-head>
197
+ <span data-v-openapi-property-name>
198
+ {prefix && <span data-v-openapi-property-prefix>{prefix}</span>}
199
+ <span data-v-openapi-property-label>{name}</span>
200
+ </span>
201
+ {required && (
202
+ <Badge variant="warning" data-v-openapi-property-required>
203
+ Required
204
+ </Badge>
205
+ )}
206
+ {deprecated && <Badge variant="note">Deprecated</Badge>}
207
+ {id && <HeadingAnchor id={id} />}
208
+ </div>
209
+ {inline.length > 0 && (
210
+ <div data-v-openapi-property-meta>
211
+ {inline.map((item, index) => (
212
+ <Fragment key={item.key}>
213
+ {index > 0 && <span data-v-openapi-meta-sep>·</span>}
214
+ {item.node}
215
+ </Fragment>
216
+ ))}
217
+ </div>
218
+ )}
219
+ {description && (
220
+ <div
221
+ data-v-openapi-property-description
222
+ data-v-content
223
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: server-rendered trusted spec content
224
+ dangerouslySetInnerHTML={{ __html: Markdown.toHtml(description) }}
225
+ />
226
+ )}
227
+ {values && values.length > 0 && <EnumValues values={values} />}
228
+ {example !== undefined && <PropertyExample id={id} value={example} />}
229
+ {children}
230
+ </div>
231
+ )
232
+ }
233
+
234
+ export declare namespace PropertyRow {
235
+ type Props = {
236
+ /** Anchor id; when set, renders a copy-link heading anchor beside the name. */
237
+ id?: string | undefined
238
+ name: string
239
+ /** Muted ancestor path shown before the name (e.g. `error.details[].`). */
240
+ prefix?: string | undefined
241
+ /** Human-readable type label rendered as an inline code block. */
242
+ type?: string | undefined
243
+ /** Enum literals rendered as a vertical "values" list below the type. */
244
+ values?: string[] | undefined
245
+ /** Constraint metadata (min/max/const/default) rendered below the type. */
246
+ meta: schemaMeta.Entry[]
247
+ /** Example value rendered below the description. */
248
+ example?: string | undefined
249
+ required?: boolean | undefined
250
+ deprecated?: boolean | undefined
251
+ description?: string | undefined
252
+ children?: React.ReactNode
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Recursively renders a schema's properties as a list of rows. Nested
258
+ * object/array properties are indented beneath their parent.
259
+ */
260
+ export function Schema(props: Schema.Props) {
261
+ const { schema: rawSchema, depth = 0, prefix = '', idBase, path = [] } = props
262
+ if (!rawSchema || depth > maxDepth) return null
263
+
264
+ // Unwrap a nullable/optional single-variant union (e.g. `oneOf: [null, X]`) to
265
+ // its sole member so X's type and properties render instead of nothing.
266
+ const schema = unwrapSingleVariant(rawSchema) ?? rawSchema
267
+
268
+ // Render oneOf/anyOf unions as a variant picker rather than a type string.
269
+ const union = unionVariants(schema)
270
+ if (union)
271
+ return <UnionView union={union} depth={depth} prefix={prefix} idBase={idBase} path={path} />
272
+
273
+ // Unwrap arrays to show the item schema's properties (path is unchanged: array
274
+ // items share the array property's anchor path).
275
+ if (schema['type'] === 'array' && schema['items'])
276
+ return (
277
+ <Schema
278
+ schema={schema['items'] as SchemaObject}
279
+ depth={depth}
280
+ prefix={prefix}
281
+ idBase={idBase}
282
+ path={path}
283
+ />
284
+ )
285
+
286
+ // Merge allOf members for display.
287
+ const allOf = schema['allOf'] as SchemaObject[] | undefined
288
+ const properties = (schema['properties'] ??
289
+ allOf?.reduce<Record<string, SchemaObject>>((acc, member) => {
290
+ Object.assign(acc, (member['properties'] as Record<string, SchemaObject>) ?? {})
291
+ return acc
292
+ }, {})) as Record<string, SchemaObject> | undefined
293
+
294
+ if (!properties || Object.keys(properties).length === 0) return null
295
+
296
+ const required = new Set((schema['required'] as string[] | undefined) ?? [])
297
+
298
+ return (
299
+ <div data-v-openapi-schema>
300
+ {Object.entries(properties).map(([name, rawProperty]) => {
301
+ // Unwrap a nullable single-variant union so the member's type label and
302
+ // nested properties surface (e.g. an optional object's children).
303
+ const property = unwrapSingleVariant(rawProperty) ?? rawProperty
304
+ const childPrefix = `${prefix}${name}${property['type'] === 'array' ? '[]' : ''}.`
305
+ const childPath = [...path, name]
306
+ const id = idBase ? schemaPropertyId(idBase, childPath) : undefined
307
+ const propertyUnion = unionVariants(property)
308
+ return (
309
+ <PropertyRow
310
+ key={name}
311
+ id={id}
312
+ name={name}
313
+ prefix={prefix || undefined}
314
+ type={propertyUnion ? undefined : typeLabel(property)}
315
+ values={propertyUnion ? undefined : enumValues(property)}
316
+ meta={propertyUnion ? [] : schemaMeta(property)}
317
+ example={propertyUnion ? undefined : schemaExample(property)}
318
+ required={required.has(name)}
319
+ deprecated={property['deprecated'] === true}
320
+ description={
321
+ typeof property['description'] === 'string'
322
+ ? (property['description'] as string)
323
+ : undefined
324
+ }
325
+ >
326
+ {propertyUnion
327
+ ? depth < maxDepth && (
328
+ <UnionView
329
+ union={propertyUnion}
330
+ depth={depth + 1}
331
+ prefix={childPrefix}
332
+ idBase={idBase}
333
+ path={childPath}
334
+ />
335
+ )
336
+ : depth < maxDepth &&
337
+ hasChildren(property) && (
338
+ <CollapsibleChildren>
339
+ <div data-v-openapi-children>
340
+ <Schema
341
+ schema={property}
342
+ depth={depth + 1}
343
+ prefix={childPrefix}
344
+ idBase={idBase}
345
+ path={childPath}
346
+ />
347
+ </div>
348
+ </CollapsibleChildren>
349
+ )}
350
+ </PropertyRow>
351
+ )
352
+ })}
353
+ </div>
354
+ )
355
+ }
356
+
357
+ export declare namespace Schema {
358
+ type Props = {
359
+ schema: Record<string, unknown> | undefined
360
+ depth?: number | undefined
361
+ /** Muted ancestor path prepended to each child row's name. */
362
+ prefix?: string | undefined
363
+ /**
364
+ * When set, each property row gets an element id of
365
+ * `schemaPropertyId(idBase, path)` so response example lines can link to it.
366
+ */
367
+ idBase?: string | undefined
368
+ /** Schema path (chain of property names) leading to this schema. */
369
+ path?: SchemaPath | undefined
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Renders a union's variant picker: each panel shows the variant's description
375
+ * followed by its schema. Panels are server-rendered; the client picker mounts
376
+ * only the selected one.
377
+ */
378
+ export function UnionView(props: {
379
+ union: unionVariants.Result
380
+ depth: number
381
+ prefix: string
382
+ idBase?: string | undefined
383
+ path?: SchemaPath | undefined
384
+ }) {
385
+ const { union, depth, prefix, idBase, path = [] } = props
386
+ return (
387
+ <SchemaUnion
388
+ kind={union.kind}
389
+ names={union.variants.map((variant) => variant.name)}
390
+ panels={union.variants.map((variant, index) => (
391
+ <VariantPanel
392
+ key={variant.name}
393
+ schema={variant.schema}
394
+ depth={depth}
395
+ prefix={prefix}
396
+ idBase={idBase}
397
+ path={[...path, unionVariantSegment(index)]}
398
+ />
399
+ ))}
400
+ />
401
+ )
402
+ }
403
+
404
+ /**
405
+ * Renders the body of a single union variant: its description, plus either the
406
+ * nested object schema (for object-like variants) or an inline type/meta line
407
+ * and enum values (for scalar variants).
408
+ */
409
+ function VariantPanel(props: {
410
+ schema: SchemaObject
411
+ depth: number
412
+ prefix: string
413
+ idBase?: string | undefined
414
+ path?: SchemaPath | undefined
415
+ }) {
416
+ const { schema, depth, prefix, idBase, path = [] } = props
417
+ const description = schema['description']
418
+ const items = (schema['type'] === 'array' ? schema['items'] : undefined) as
419
+ | SchemaObject
420
+ | undefined
421
+ const objectish = Boolean(
422
+ schema['properties'] || schema['allOf'] || items?.['properties'] || items?.['allOf'],
423
+ )
424
+ const meta = schemaMeta(schema)
425
+ const values = enumValues(schema)
426
+ return (
427
+ <div data-v-openapi-variant>
428
+ {!objectish && (
429
+ <div data-v-openapi-property-meta>
430
+ <code data-v data-v-openapi-type>
431
+ {typeLabel(schema)}
432
+ </code>
433
+ {meta.map((item) => (
434
+ <Fragment key={item.label}>
435
+ <span data-v-openapi-meta-sep>·</span>
436
+ <span>
437
+ <span data-v-openapi-meta-label>{item.label} </span>
438
+ <span data-v-openapi-meta-value>{item.value}</span>
439
+ </span>
440
+ </Fragment>
441
+ ))}
442
+ </div>
443
+ )}
444
+ {typeof description === 'string' && (
445
+ <div
446
+ data-v-openapi-property-description
447
+ data-v-content
448
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: server-rendered trusted spec content
449
+ dangerouslySetInnerHTML={{ __html: Markdown.toHtml(description) }}
450
+ />
451
+ )}
452
+ {!objectish && values && values.length > 0 && <EnumValues values={values} />}
453
+ {objectish && (
454
+ <Schema schema={schema} depth={depth} prefix={prefix} idBase={idBase} path={path} />
455
+ )}
456
+ </div>
457
+ )
458
+ }
459
+
460
+ /** Whether a schema exposes nested properties worth rendering as child rows. */
461
+ function hasChildren(schema: SchemaObject): boolean {
462
+ if (schema['properties']) return true
463
+ if (schema['allOf']) return true
464
+ if (schema['type'] === 'array' && schema['items'])
465
+ return hasChildren(schema['items'] as SchemaObject)
466
+ return false
467
+ }
@@ -0,0 +1,123 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef, useState } from 'react'
4
+ import LucideChevronDown from '~icons/lucide/chevron-down'
5
+ import { registerDisclosure } from './anchor-navigation.client.js'
6
+
7
+ /**
8
+ * Renders a `oneOf`/`anyOf` schema union as a variant picker (in the style of
9
+ * Scalar's "Any of …" selector) instead of a long `a | b | c` type string.
10
+ * The dropdown switches which variant's schema is shown below it.
11
+ *
12
+ * All variant panels are kept mounted (only the selected one is visible) so a
13
+ * property inside an unselected variant is still present in the DOM. Each panel
14
+ * registers itself with the navigation registry so {@link revealAnchor} can
15
+ * select the variant containing a target before scrolling to it — letting a
16
+ * response example line expand the union as deep as needed.
17
+ *
18
+ * Styling lives in `openapi.css` keyed on the `data-v-openapi-union*` attributes.
19
+ */
20
+ export function SchemaUnion(props: SchemaUnion.Props) {
21
+ const { kind, names, panels } = props
22
+ const [open, setOpen] = useState(false)
23
+ const [selected, setSelected] = useState<number | null>(null)
24
+ return (
25
+ <div data-v-openapi-union>
26
+ <div data-v-openapi-union-head>
27
+ <button
28
+ type="button"
29
+ onClick={() => setOpen((value) => !value)}
30
+ data-v-openapi-union-trigger
31
+ >
32
+ <span data-v-openapi-union-kind>{kind}</span>
33
+ {selected === null ? (
34
+ <span data-v-openapi-union-value>Select a value</span>
35
+ ) : (
36
+ <span data-v-openapi-union-value>{names[selected]}</span>
37
+ )}
38
+ <LucideChevronDown data-v-openapi-union-chevron />
39
+ </button>
40
+ {open && (
41
+ <>
42
+ <button
43
+ type="button"
44
+ aria-label="Close"
45
+ tabIndex={-1}
46
+ data-v-openapi-dropdown-backdrop
47
+ onClick={() => setOpen(false)}
48
+ />
49
+ <ul data-v-openapi-union-menu>
50
+ {names.map((name, index) => (
51
+ <li key={name}>
52
+ <button
53
+ type="button"
54
+ onClick={() => {
55
+ setSelected(index)
56
+ setOpen(false)
57
+ }}
58
+ data-v-openapi-union-option
59
+ data-selected={index === selected || undefined}
60
+ >
61
+ <span data-v-openapi-union-radio>
62
+ {index === selected && <span data-v-openapi-union-radio-dot />}
63
+ </span>
64
+ <span data-v-openapi-union-option-label>{name}</span>
65
+ </button>
66
+ </li>
67
+ ))}
68
+ </ul>
69
+ </>
70
+ )}
71
+ </div>
72
+ {panels.map((panel, index) => (
73
+ <VariantPanelSlot
74
+ // biome-ignore lint/suspicious/noArrayIndexKey: panels are static and never reordered
75
+ key={index}
76
+ visible={index === selected}
77
+ onReveal={() => setSelected(index)}
78
+ >
79
+ {panel}
80
+ </VariantPanelSlot>
81
+ ))}
82
+ </div>
83
+ )
84
+ }
85
+
86
+ /**
87
+ * Wraps a single variant panel: always mounted (so its contents are navigable)
88
+ * but only laid out when selected. Registers the panel so navigating to an
89
+ * anchor inside it selects this variant.
90
+ */
91
+ function VariantPanelSlot(props: {
92
+ visible: boolean
93
+ onReveal: () => void
94
+ children: React.ReactNode
95
+ }) {
96
+ const { visible, onReveal, children } = props
97
+ const ref = useRef<HTMLDivElement | null>(null)
98
+ const onRevealRef = useRef(onReveal)
99
+ onRevealRef.current = onReveal
100
+
101
+ useEffect(() => {
102
+ const panel = ref.current
103
+ if (!panel) return
104
+ return registerDisclosure(panel, () => onRevealRef.current())
105
+ }, [])
106
+
107
+ return (
108
+ <div ref={ref} hidden={!visible} data-v-openapi-union-panel data-visible={visible || undefined}>
109
+ {children}
110
+ </div>
111
+ )
112
+ }
113
+
114
+ export declare namespace SchemaUnion {
115
+ type Props = {
116
+ /** Leading label, e.g. `Any of` or `One of`. */
117
+ kind: string
118
+ /** Display name for each variant. */
119
+ names: string[]
120
+ /** Server-rendered schema panel for each variant. */
121
+ panels: React.ReactNode[]
122
+ }
123
+ }