zudoku 0.65.2 → 0.66.0

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 (268) hide show
  1. package/dist/app/sentry.js +1 -1
  2. package/dist/config/validators/BuildSchema.js +18 -3
  3. package/dist/config/validators/BuildSchema.js.map +1 -1
  4. package/dist/config/validators/validate.d.ts +45 -3
  5. package/dist/config/validators/validate.js +7 -1
  6. package/dist/config/validators/validate.js.map +1 -1
  7. package/dist/flat-config.d.ts +9 -2
  8. package/dist/lib/components/Mermaid.d.ts +7 -0
  9. package/dist/lib/components/Mermaid.js +42 -0
  10. package/dist/lib/components/Mermaid.js.map +1 -0
  11. package/dist/lib/components/PagefindSearchMeta.d.ts +8 -0
  12. package/dist/lib/components/PagefindSearchMeta.js +7 -0
  13. package/dist/lib/components/PagefindSearchMeta.js.map +1 -0
  14. package/dist/lib/components/Zudoku.js +2 -5
  15. package/dist/lib/components/Zudoku.js.map +1 -1
  16. package/dist/lib/core/RouteGuard.js +1 -1
  17. package/dist/lib/core/RouteGuard.js.map +1 -1
  18. package/dist/lib/core/plugins.d.ts +3 -3
  19. package/dist/lib/oas/parser/index.d.ts +1 -0
  20. package/dist/lib/oas/parser/index.js.map +1 -1
  21. package/dist/lib/plugins/openapi/DownloadSchemaButton.d.ts +3 -0
  22. package/dist/lib/plugins/openapi/DownloadSchemaButton.js +47 -0
  23. package/dist/lib/plugins/openapi/DownloadSchemaButton.js.map +1 -0
  24. package/dist/lib/plugins/openapi/Endpoint.js +3 -6
  25. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  26. package/dist/lib/plugins/openapi/GeneratedExampleSidecarBox.js +1 -1
  27. package/dist/lib/plugins/openapi/OasProvider.js +22 -13
  28. package/dist/lib/plugins/openapi/OasProvider.js.map +1 -1
  29. package/dist/lib/plugins/openapi/OperationList.js +12 -6
  30. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  31. package/dist/lib/plugins/openapi/OperationListItem.js +2 -2
  32. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  33. package/dist/lib/plugins/openapi/ParameterList.js +1 -1
  34. package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
  35. package/dist/lib/plugins/openapi/ResponsesSidecarBox.d.ts +2 -1
  36. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +4 -3
  37. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  38. package/dist/lib/plugins/openapi/SchemaList.js +2 -1
  39. package/dist/lib/plugins/openapi/SchemaList.js.map +1 -1
  40. package/dist/lib/plugins/openapi/Sidecar.js +9 -1
  41. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  42. package/dist/lib/plugins/openapi/index.js +11 -5
  43. package/dist/lib/plugins/openapi/index.js.map +1 -1
  44. package/dist/lib/plugins/openapi/interfaces.d.ts +16 -13
  45. package/dist/lib/plugins/openapi/playground/Playground.js +6 -2
  46. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  47. package/dist/lib/plugins/openapi/schema/SchemaView.d.ts +1 -1
  48. package/dist/lib/plugins/openapi/schema/SchemaView.js +14 -6
  49. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
  50. package/dist/lib/plugins/openapi/schema/utils.d.ts +2 -2
  51. package/dist/lib/plugins/openapi/schema/utils.js.map +1 -1
  52. package/dist/lib/plugins/openapi/util/getRoutes.d.ts +6 -1
  53. package/dist/lib/plugins/openapi/util/getRoutes.js +29 -2
  54. package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
  55. package/dist/lib/plugins/search-pagefind/IndexingDialog.d.ts +3 -0
  56. package/dist/lib/plugins/search-pagefind/IndexingDialog.js +64 -0
  57. package/dist/lib/plugins/search-pagefind/IndexingDialog.js.map +1 -0
  58. package/dist/lib/plugins/search-pagefind/PagefindSearch.js +22 -5
  59. package/dist/lib/plugins/search-pagefind/PagefindSearch.js.map +1 -1
  60. package/dist/lib/plugins/search-pagefind/ResultList.js +5 -4
  61. package/dist/lib/plugins/search-pagefind/ResultList.js.map +1 -1
  62. package/dist/lib/shiki.d.ts +1 -1
  63. package/dist/lib/shiki.js +18 -12
  64. package/dist/lib/shiki.js.map +1 -1
  65. package/dist/lib/ui/Button.d.ts +2 -2
  66. package/dist/lib/ui/Button.js +9 -8
  67. package/dist/lib/ui/Button.js.map +1 -1
  68. package/dist/lib/ui/ButtonGroup.d.ts +11 -0
  69. package/dist/lib/ui/ButtonGroup.js +28 -0
  70. package/dist/lib/ui/ButtonGroup.js.map +1 -0
  71. package/dist/lib/ui/Command.js +2 -2
  72. package/dist/lib/ui/Command.js.map +1 -1
  73. package/dist/lib/ui/DropdownMenu.d.ts +21 -23
  74. package/dist/lib/ui/DropdownMenu.js +47 -32
  75. package/dist/lib/ui/DropdownMenu.js.map +1 -1
  76. package/dist/lib/ui/Kbd.d.ts +3 -0
  77. package/dist/lib/ui/Kbd.js +10 -0
  78. package/dist/lib/ui/Kbd.js.map +1 -0
  79. package/dist/lib/util/MdxComponents.d.ts +1 -0
  80. package/dist/lib/util/MdxComponents.js +2 -0
  81. package/dist/lib/util/MdxComponents.js.map +1 -1
  82. package/dist/lib/util/flattenAllOf.js +27 -4
  83. package/dist/lib/util/flattenAllOf.js.map +1 -1
  84. package/dist/lib/util/flattenAllOf.test.js +67 -12
  85. package/dist/lib/util/flattenAllOf.test.js.map +1 -1
  86. package/dist/vite/api/SchemaManager.d.ts +5 -0
  87. package/dist/vite/api/SchemaManager.js +24 -0
  88. package/dist/vite/api/SchemaManager.js.map +1 -1
  89. package/dist/vite/api/SchemaManager.test.js +67 -0
  90. package/dist/vite/api/SchemaManager.test.js.map +1 -1
  91. package/dist/vite/config.js +8 -2
  92. package/dist/vite/config.js.map +1 -1
  93. package/dist/vite/dev-server.js +25 -0
  94. package/dist/vite/dev-server.js.map +1 -1
  95. package/dist/vite/pagefind-dev-index.d.ts +16 -0
  96. package/dist/vite/pagefind-dev-index.js +68 -0
  97. package/dist/vite/pagefind-dev-index.js.map +1 -0
  98. package/dist/vite/plugin-api.js +41 -3
  99. package/dist/vite/plugin-api.js.map +1 -1
  100. package/dist/vite/prerender/prerender.js +1 -19
  101. package/dist/vite/prerender/prerender.js.map +1 -1
  102. package/dist/vite/prerender/utils.d.ts +2 -0
  103. package/dist/vite/prerender/utils.js +24 -0
  104. package/dist/vite/prerender/utils.js.map +1 -0
  105. package/lib/Button-CynVW1JV.js +53 -0
  106. package/lib/Button-CynVW1JV.js.map +1 -0
  107. package/lib/ClaudeLogo-PxFjou9w.js +69 -0
  108. package/lib/ClaudeLogo-PxFjou9w.js.map +1 -0
  109. package/lib/{Command-CUcrW3qs.js → Command-BpT1iBE6.js} +25 -25
  110. package/lib/Command-BpT1iBE6.js.map +1 -0
  111. package/lib/Drawer-Ci7XwhqT.js.map +1 -1
  112. package/lib/DropdownMenu-C8SX_-S_.js +104 -0
  113. package/lib/DropdownMenu-C8SX_-S_.js.map +1 -0
  114. package/lib/{ErrorAlert-D5LKLFOd.js → ErrorAlert-BqjbNHIn.js} +1017 -1015
  115. package/lib/{ErrorAlert-D5LKLFOd.js.map → ErrorAlert-BqjbNHIn.js.map} +1 -1
  116. package/lib/IndexingDialog-B5zCiUKr.js +100 -0
  117. package/lib/IndexingDialog-B5zCiUKr.js.map +1 -0
  118. package/lib/MdxPage-CVFatbHw.js +210 -0
  119. package/lib/MdxPage-CVFatbHw.js.map +1 -0
  120. package/lib/Mermaid-CIFixY6C.js +102 -0
  121. package/lib/Mermaid-CIFixY6C.js.map +1 -0
  122. package/lib/{OAuthErrorPage-oXnxcJg4.js → OAuthErrorPage-Dup79DJk.js} +7 -7
  123. package/lib/{OAuthErrorPage-oXnxcJg4.js.map → OAuthErrorPage-Dup79DJk.js.map} +1 -1
  124. package/lib/OasProvider-BJeMq29o.js +40 -0
  125. package/lib/OasProvider-BJeMq29o.js.map +1 -0
  126. package/lib/{OperationList-DeBe8FvT.js → OperationList-ff3ZvQsO.js} +1950 -1814
  127. package/lib/OperationList-ff3ZvQsO.js.map +1 -0
  128. package/lib/{RouteGuard-Brz95MSt.js → RouteGuard-BXy13JSz.js} +19 -19
  129. package/lib/{RouteGuard-Brz95MSt.js.map → RouteGuard-BXy13JSz.js.map} +1 -1
  130. package/lib/{RouterError-VGZB_wg4.js → RouterError-CKOZTsDD.js} +3 -3
  131. package/lib/{RouterError-VGZB_wg4.js.map → RouterError-CKOZTsDD.js.map} +1 -1
  132. package/lib/{SchemaList-DnlwQJCO.js → SchemaList-BSC1KM3v.js} +28 -27
  133. package/lib/SchemaList-BSC1KM3v.js.map +1 -0
  134. package/lib/{SchemaView-CgDcUNbC.js → SchemaView-CgwJ9gtb.js} +198 -187
  135. package/lib/SchemaView-CgwJ9gtb.js.map +1 -0
  136. package/lib/Select-VmDZ-nKe.js +337 -0
  137. package/lib/Select-VmDZ-nKe.js.map +1 -0
  138. package/lib/{SignUp-D2mmQOkg.js → SignUp-Pm_LGm6T.js} +13 -13
  139. package/lib/{SignUp-D2mmQOkg.js.map → SignUp-Pm_LGm6T.js.map} +1 -1
  140. package/lib/{SyntaxHighlight-C19vH0V_.js → SyntaxHighlight-bkmst3oV.js} +654 -622
  141. package/lib/SyntaxHighlight-bkmst3oV.js.map +1 -0
  142. package/lib/{Toc-CBWfFCVf.js → Toc-TUXNFbKl.js} +2 -2
  143. package/lib/{Toc-CBWfFCVf.js.map → Toc-TUXNFbKl.js.map} +1 -1
  144. package/lib/{ZudokuContext-BUZ5hkWB.js → ZudokuContext-np1wheDl.js} +8 -8
  145. package/lib/{ZudokuContext-BUZ5hkWB.js.map → ZudokuContext-np1wheDl.js.map} +1 -1
  146. package/lib/___vite-browser-external_commonjs-proxy-Cga3HsWk.js +9 -0
  147. package/lib/___vite-browser-external_commonjs-proxy-Cga3HsWk.js.map +1 -0
  148. package/lib/{chunk-PVWAREVJ-BMhpCH5D.js → chunk-PVWAREVJ-dLIqswPy.js} +5 -5
  149. package/lib/{chunk-PVWAREVJ-BMhpCH5D.js.map → chunk-PVWAREVJ-dLIqswPy.js.map} +1 -1
  150. package/lib/{circular-BpeO0esG.js → circular-XPj_dwqA.js} +2 -2
  151. package/lib/{circular-BpeO0esG.js.map → circular-XPj_dwqA.js.map} +1 -1
  152. package/lib/createServer-D01nCTNp.js +16693 -0
  153. package/lib/createServer-D01nCTNp.js.map +1 -0
  154. package/lib/{errors-D7xzOd8X.js → errors-B0hNTPFO.js} +3 -3
  155. package/lib/{errors-D7xzOd8X.js.map → errors-B0hNTPFO.js.map} +1 -1
  156. package/lib/{hook-CMeoxziF.js → hook-CvSwcbk6.js} +3 -3
  157. package/lib/{hook-CMeoxziF.js.map → hook-CvSwcbk6.js.map} +1 -1
  158. package/lib/{index-C_-XS4lp.js → index-Bjc_QsUR.js} +765 -745
  159. package/lib/{index-C_-XS4lp.js.map → index-Bjc_QsUR.js.map} +1 -1
  160. package/lib/index-CrcNWbel.js.map +1 -1
  161. package/lib/index-DnMgJWrI.js +133 -0
  162. package/lib/index-DnMgJWrI.js.map +1 -0
  163. package/lib/{index-CF7_erXq.js → index-DscsS121.js} +2 -2
  164. package/lib/{index-CF7_erXq.js.map → index-DscsS121.js.map} +1 -1
  165. package/lib/{index-CPws05Tb.js → index-mfkNWYG-.js} +10 -10
  166. package/lib/{index-CPws05Tb.js.map → index-mfkNWYG-.js.map} +1 -1
  167. package/lib/{index.esm-BnYHxCYC.js → index.esm-DtzT_KoE.js} +20 -20
  168. package/lib/{index.esm-BnYHxCYC.js.map → index.esm-DtzT_KoE.js.map} +1 -1
  169. package/lib/{invariant-Bm-FVUQE.js → invariant-CGOLuIIz.js} +3 -3
  170. package/lib/{invariant-Bm-FVUQE.js.map → invariant-CGOLuIIz.js.map} +1 -1
  171. package/lib/{mutation-BSU0xu4m.js → mutation-BlmnL5qL.js} +2 -2
  172. package/lib/{mutation-BSU0xu4m.js.map → mutation-BlmnL5qL.js.map} +1 -1
  173. package/lib/ui/ActionButton.js +1 -1
  174. package/lib/ui/Button.js +25 -24
  175. package/lib/ui/Button.js.map +1 -1
  176. package/lib/ui/ButtonGroup.js +77 -0
  177. package/lib/ui/ButtonGroup.js.map +1 -0
  178. package/lib/ui/Command.js +3 -3
  179. package/lib/ui/Command.js.map +1 -1
  180. package/lib/ui/DropdownMenu.js +227 -140
  181. package/lib/ui/DropdownMenu.js.map +1 -1
  182. package/lib/ui/Kbd.js +32 -0
  183. package/lib/ui/Kbd.js.map +1 -0
  184. package/lib/ui/SyntaxHighlight.js +3 -3
  185. package/lib/zudoku.__internal.js +8 -8
  186. package/lib/zudoku.auth-auth0.js +1 -1
  187. package/lib/zudoku.auth-azureb2c.js +4 -4
  188. package/lib/zudoku.auth-clerk.js +2 -2
  189. package/lib/zudoku.auth-openid.js +5 -5
  190. package/lib/zudoku.auth-supabase.js +5 -5
  191. package/lib/zudoku.components.js +7 -7
  192. package/lib/zudoku.hooks.js +11 -24
  193. package/lib/zudoku.hooks.js.map +1 -1
  194. package/lib/zudoku.mermaid.js +10 -0
  195. package/lib/zudoku.mermaid.js.map +1 -0
  196. package/lib/zudoku.plugin-api-catalog.js +6 -6
  197. package/lib/zudoku.plugin-api-keys.js +223 -198
  198. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  199. package/lib/zudoku.plugin-custom-pages.js +1 -1
  200. package/lib/zudoku.plugin-markdown.js +1 -1
  201. package/lib/zudoku.plugin-openapi.js +3 -3
  202. package/lib/zudoku.plugin-redirect.js +1 -1
  203. package/lib/zudoku.plugin-search-pagefind.js +184 -226
  204. package/lib/zudoku.plugin-search-pagefind.js.map +1 -1
  205. package/lib/zudoku.plugins.js.map +1 -1
  206. package/lib/zudoku.router.js +2 -2
  207. package/package.json +29 -21
  208. package/src/app/sentry.ts +1 -1
  209. package/src/lib/components/Mermaid.tsx +68 -0
  210. package/src/lib/components/PagefindSearchMeta.tsx +14 -0
  211. package/src/lib/components/Zudoku.tsx +4 -7
  212. package/src/lib/core/RouteGuard.tsx +1 -1
  213. package/src/lib/core/plugins.ts +2 -2
  214. package/src/lib/oas/parser/index.ts +2 -0
  215. package/src/lib/plugins/openapi/DownloadSchemaButton.tsx +115 -0
  216. package/src/lib/plugins/openapi/Endpoint.tsx +20 -27
  217. package/src/lib/plugins/openapi/GeneratedExampleSidecarBox.tsx +1 -1
  218. package/src/lib/plugins/openapi/OasProvider.tsx +30 -17
  219. package/src/lib/plugins/openapi/OperationList.tsx +39 -21
  220. package/src/lib/plugins/openapi/OperationListItem.tsx +5 -5
  221. package/src/lib/plugins/openapi/ParameterList.tsx +2 -2
  222. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +22 -2
  223. package/src/lib/plugins/openapi/SchemaList.tsx +4 -0
  224. package/src/lib/plugins/openapi/Sidecar.tsx +17 -1
  225. package/src/lib/plugins/openapi/index.tsx +16 -7
  226. package/src/lib/plugins/openapi/interfaces.ts +16 -7
  227. package/src/lib/plugins/openapi/playground/Playground.tsx +6 -2
  228. package/src/lib/plugins/openapi/schema/SchemaView.tsx +36 -27
  229. package/src/lib/plugins/openapi/schema/utils.ts +5 -2
  230. package/src/lib/plugins/openapi/util/getRoutes.tsx +35 -3
  231. package/src/lib/plugins/search-pagefind/IndexingDialog.tsx +163 -0
  232. package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +61 -22
  233. package/src/lib/plugins/search-pagefind/ResultList.tsx +8 -3
  234. package/src/lib/shiki.ts +21 -12
  235. package/src/lib/ui/Button.tsx +10 -10
  236. package/src/lib/ui/ButtonGroup.tsx +82 -0
  237. package/src/lib/ui/Command.tsx +3 -3
  238. package/src/lib/ui/DropdownMenu.tsx +226 -170
  239. package/src/lib/ui/Kbd.tsx +28 -0
  240. package/src/lib/util/MdxComponents.tsx +2 -0
  241. package/src/lib/util/flattenAllOf.test.ts +71 -19
  242. package/src/lib/util/flattenAllOf.ts +29 -8
  243. package/src/shiki/langs/markdown-nix.js +1 -0
  244. package/src/shiki/langs/openscad.js +1 -0
  245. package/dist/vite/create-pagefind-index.d.ts +0 -4
  246. package/dist/vite/create-pagefind-index.js +0 -12
  247. package/dist/vite/create-pagefind-index.js.map +0 -1
  248. package/lib/Button-B3ucvvQw.js +0 -52
  249. package/lib/Button-B3ucvvQw.js.map +0 -1
  250. package/lib/Command-CUcrW3qs.js.map +0 -1
  251. package/lib/DropdownMenu-BZ2NKQ3K.js +0 -126
  252. package/lib/DropdownMenu-BZ2NKQ3K.js.map +0 -1
  253. package/lib/MdxPage-hOCN-u-L.js +0 -240
  254. package/lib/MdxPage-hOCN-u-L.js.map +0 -1
  255. package/lib/OasProvider-D88vmZFt.js +0 -36
  256. package/lib/OasProvider-D88vmZFt.js.map +0 -1
  257. package/lib/OperationList-DeBe8FvT.js.map +0 -1
  258. package/lib/Pagination-lLSoHnxa.js +0 -37
  259. package/lib/Pagination-lLSoHnxa.js.map +0 -1
  260. package/lib/SchemaList-DnlwQJCO.js.map +0 -1
  261. package/lib/SchemaView-CgDcUNbC.js.map +0 -1
  262. package/lib/Select-DFRCS31-.js +0 -399
  263. package/lib/Select-DFRCS31-.js.map +0 -1
  264. package/lib/SyntaxHighlight-C19vH0V_.js.map +0 -1
  265. package/lib/createServer-DIptgGTi.js +0 -13018
  266. package/lib/createServer-DIptgGTi.js.map +0 -1
  267. package/lib/useExposedProps-U3pmsHaG.js +0 -113
  268. package/lib/useExposedProps-U3pmsHaG.js.map +0 -1
@@ -18,9 +18,12 @@ import { CategoryHeading } from "../../components/CategoryHeading.js";
18
18
  import { useApiIdentities } from "../../components/context/ZudokuContext.js";
19
19
  import { Heading } from "../../components/Heading.js";
20
20
  import { Markdown } from "../../components/Markdown.js";
21
+ import { PagefindSearchMeta } from "../../components/PagefindSearchMeta.js";
21
22
  import { Pagination } from "../../components/Pagination.js";
23
+ import { joinUrl } from "../../util/joinUrl.js";
22
24
  import { useCreateQuery } from "./client/useCreateQuery.js";
23
25
  import { useOasConfig } from "./context.js";
26
+ import { DownloadSchemaButton } from "./DownloadSchemaButton.js";
24
27
  import { Endpoint } from "./Endpoint.js";
25
28
  import { graphql } from "./graphql/index.js";
26
29
  import { UNTAGGED_PATH } from "./index.js";
@@ -155,7 +158,7 @@ export const OperationList = ({
155
158
  tag?: string;
156
159
  untagged?: boolean;
157
160
  }) => {
158
- const { input, type, versions, version, options } = useOasConfig();
161
+ const { path, input, type, versions, version, options } = useOasConfig();
159
162
  const { tag: tagFromParams } = useParams<"tag">();
160
163
  const query = useCreateQuery(OperationsForTagQuery, {
161
164
  input,
@@ -244,12 +247,20 @@ export const OperationList = ({
244
247
  const tagTitle = schema.tag.extensions?.["x-displayName"] ?? schema.tag.name;
245
248
  const helmetTitle = [tagTitle, title].filter(Boolean).join(" - ");
246
249
 
250
+ const downloadUrl =
251
+ typeof input === "string"
252
+ ? type === "url"
253
+ ? input
254
+ : joinUrl(path, version, input.split("/").pop())
255
+ : undefined;
256
+
247
257
  return (
248
258
  <div
249
259
  className="pt-(--padding-content-top)"
250
260
  data-pagefind-filter="section:openapi"
251
261
  data-pagefind-meta="section:openapi"
252
262
  >
263
+ <PagefindSearchMeta name="category">{title}</PagefindSearchMeta>
253
264
  <Helmet>
254
265
  {helmetTitle && <title>{helmetTitle}</title>}
255
266
  {metaDescription && (
@@ -262,7 +273,7 @@ export const OperationList = ({
262
273
  className="w-full"
263
274
  defaultOpen={options?.expandApiInformation}
264
275
  >
265
- <div className="flex flex-col gap-y-4 sm:flex-row justify-around items-start sm:items-end">
276
+ <div className="flex flex-col gap-4 sm:flex-row justify-around items-start sm:items-end">
266
277
  <div className="flex flex-col flex-1 gap-2">
267
278
  <CategoryHeading>{title}</CategoryHeading>
268
279
  <Heading
@@ -282,25 +293,32 @@ export const OperationList = ({
282
293
  <Endpoint />
283
294
  </div>
284
295
  <div className="flex flex-col gap-4 sm:items-end">
285
- {showVersions && (
286
- <Select
287
- // biome-ignore lint/style/noNonNullAssertion: is guaranteed to be defined
288
- onValueChange={(version) => navigate(versions[version]!)}
289
- defaultValue={version}
290
- disabled={!hasMultipleVersions}
291
- >
292
- <SelectTrigger className="w-[180px]">
293
- <SelectValue placeholder="Select version" />
294
- </SelectTrigger>
295
- <SelectContent>
296
- {Object.entries(versions).map(([version]) => (
297
- <SelectItem key={version} value={version}>
298
- {version}
299
- </SelectItem>
300
- ))}
301
- </SelectContent>
302
- </Select>
303
- )}
296
+ <div className="flex gap-2 items-center">
297
+ {showVersions && (
298
+ <Select
299
+ onValueChange={(version) =>
300
+ // biome-ignore lint/style/noNonNullAssertion: is guaranteed to be defined
301
+ navigate(versions[version]!.path)
302
+ }
303
+ defaultValue={version}
304
+ disabled={!hasMultipleVersions}
305
+ >
306
+ <SelectTrigger className="w-[180px]" size="sm">
307
+ <SelectValue placeholder="Select version" />
308
+ </SelectTrigger>
309
+ <SelectContent>
310
+ {Object.entries(versions).map(([version, { label }]) => (
311
+ <SelectItem key={version} value={version}>
312
+ {label}
313
+ </SelectItem>
314
+ ))}
315
+ </SelectContent>
316
+ </Select>
317
+ )}
318
+ {options?.schemaDownload?.enabled && downloadUrl && (
319
+ <DownloadSchemaButton downloadUrl={downloadUrl} />
320
+ )}
321
+ </div>
304
322
  {schema.description && (
305
323
  <CollapsibleTrigger className="flex items-center gap-1 text-sm font-medium text-muted-foreground group">
306
324
  <span>API information</span>
@@ -1,9 +1,9 @@
1
- import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
2
1
  import { useState } from "react";
3
2
  import { Badge } from "zudoku/ui/Badge.js";
4
3
  import { Separator } from "zudoku/ui/Separator.js";
5
4
  import { Heading } from "../../components/Heading.js";
6
5
  import { Markdown } from "../../components/Markdown.js";
6
+ import { PagefindSearchMeta } from "../../components/PagefindSearchMeta.js";
7
7
  import { cn } from "../../util/cn.js";
8
8
  import { groupBy } from "../../util/groupBy.js";
9
9
  import { renderIf } from "../../util/renderIf.js";
@@ -134,9 +134,9 @@ export const OperationListItem = ({
134
134
  id={`${operation.slug}/request-body`}
135
135
  >
136
136
  {operation.summary && (
137
- <VisuallyHidden>
137
+ <PagefindSearchMeta>
138
138
  {operation.summary} &rsaquo;{" "}
139
- </VisuallyHidden>
139
+ </PagefindSearchMeta>
140
140
  )}
141
141
  Request Body{" "}
142
142
  {operation.requestBody?.required === false ? (
@@ -154,9 +154,9 @@ export const OperationListItem = ({
154
154
  <>
155
155
  <Heading level={3} id={`${operation.slug}/responses`}>
156
156
  {operation.summary && (
157
- <VisuallyHidden>
157
+ <PagefindSearchMeta>
158
158
  {operation.summary} &rsaquo;{" "}
159
- </VisuallyHidden>
159
+ </PagefindSearchMeta>
160
160
  )}
161
161
  Responses
162
162
  </Heading>
@@ -35,14 +35,14 @@ export const ParameterList = ({
35
35
  <Frame>
36
36
  <FramePanel className="p-0!">
37
37
  <ItemGroup className="overflow-clip">
38
- {sortedParameters.map((parameter) => (
38
+ {sortedParameters.map((parameter, index) => (
39
39
  <Fragment key={`${parameter.name}-${parameter.in}`}>
40
+ {index > 0 && <ItemSeparator />}
40
41
  <ParameterListItem
41
42
  parameter={parameter}
42
43
  id={id}
43
44
  group={group}
44
45
  />
45
- <ItemSeparator />
46
46
  </Fragment>
47
47
  ))}
48
48
  </ItemGroup>
@@ -1,4 +1,4 @@
1
- import { ChevronsDownUpIcon, ChevronsUpDownIcon } from "lucide-react";
1
+ import { ChevronsDownUpIcon, ChevronsUpDownIcon, InfoIcon } from "lucide-react";
2
2
  import { useEffect, useState } from "react";
3
3
  import { Button } from "zudoku/components";
4
4
  import {
@@ -7,6 +7,12 @@ import {
7
7
  CollapsibleTrigger,
8
8
  } from "zudoku/ui/Collapsible.js";
9
9
  import { NativeSelect, NativeSelectOption } from "zudoku/ui/NativeSelect.js";
10
+ import {
11
+ Tooltip,
12
+ TooltipContent,
13
+ TooltipProvider,
14
+ TooltipTrigger,
15
+ } from "zudoku/ui/Tooltip.js";
10
16
  import type { ResponseItem } from "./graphql/graphql.js";
11
17
  import * as SidecarBox from "./SidecarBox.js";
12
18
  import { SidecarExamples } from "./SidecarExamples.js";
@@ -16,11 +22,13 @@ export const ResponsesSidecarBox = ({
16
22
  selectedResponse,
17
23
  isOnScreen,
18
24
  shouldLazyHighlight,
25
+ isGenerated,
19
26
  }: {
20
27
  responses: ResponseItem[];
21
28
  selectedResponse?: string;
22
29
  isOnScreen: boolean;
23
30
  shouldLazyHighlight?: boolean;
31
+ isGenerated?: boolean;
24
32
  }) => {
25
33
  const [internalSelectedResponse, setInternalSelectedResponse] = useState(
26
34
  selectedResponse ?? responses[0]?.statusCode,
@@ -57,8 +65,20 @@ export const ResponsesSidecarBox = ({
57
65
  </Button>
58
66
  </CollapsibleTrigger>
59
67
  Example Responses
68
+ {isGenerated && (
69
+ <TooltipProvider>
70
+ <Tooltip>
71
+ <TooltipTrigger asChild>
72
+ <InfoIcon size={13} />
73
+ </TooltipTrigger>
74
+ <TooltipContent>
75
+ This example is auto-generated from the schema.
76
+ </TooltipContent>
77
+ </Tooltip>
78
+ </TooltipProvider>
79
+ )}
60
80
  </div>
61
- <div className="group-data-[state=closed]/collapsible:hidden">
81
+ <div className="group-data-[state=closed]/collapsible:invisible">
62
82
  <NativeSelect
63
83
  className="text-xs h-fit py-1 -my-1 bg-background"
64
84
  value={internalSelectedResponse}
@@ -16,6 +16,7 @@ import { CategoryHeading } from "../../components/CategoryHeading.js";
16
16
  import { Heading } from "../../components/Heading.js";
17
17
  import { Markdown } from "../../components/Markdown.js";
18
18
  import { Toc } from "../../components/navigation/Toc.js";
19
+ import { PagefindSearchMeta } from "../../components/PagefindSearchMeta.js";
19
20
  import { useCreateQuery } from "./client/useCreateQuery.js";
20
21
  import { useOasConfig } from "./context.js";
21
22
  import { graphql } from "./graphql/gql.js";
@@ -70,6 +71,9 @@ export function SchemaList() {
70
71
  data-pagefind-filter="section:openapi"
71
72
  data-pagefind-meta="section:openapi"
72
73
  >
74
+ <PagefindSearchMeta name="category">
75
+ {data.schema.title}
76
+ </PagefindSearchMeta>
73
77
  <Helmet>
74
78
  <title>Schemas {showVersions ? version : ""}</title>
75
79
  <meta name="description" content="List of schemas used by the API." />
@@ -267,7 +267,7 @@ export const Sidecar = ({
267
267
  />
268
268
  ) : null}
269
269
 
270
- {hasResponseExamples && (
270
+ {hasResponseExamples ? (
271
271
  <ResponsesSidecarBox
272
272
  isOnScreen={isOnScreen}
273
273
  shouldLazyHighlight={shouldLazyHighlight}
@@ -286,6 +286,22 @@ export const Sidecar = ({
286
286
  : response.content,
287
287
  }))}
288
288
  />
289
+ ) : (
290
+ <ResponsesSidecarBox
291
+ isGenerated
292
+ isOnScreen={isOnScreen}
293
+ shouldLazyHighlight={shouldLazyHighlight}
294
+ selectedResponse={selectedResponse}
295
+ responses={operation.responses.map((response) => ({
296
+ ...response,
297
+ content: response.content?.map((content) => ({
298
+ ...content,
299
+ examples: content.schema
300
+ ? [{ name: "", value: generateSchemaExample(content.schema) }]
301
+ : content.examples,
302
+ })),
303
+ }))}
304
+ />
289
305
  )}
290
306
  </aside>
291
307
  );
@@ -13,7 +13,7 @@ import type { OasPluginConfig } from "./interfaces.js";
13
13
  import type { PlaygroundContentProps } from "./playground/Playground.js";
14
14
  import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
15
15
  import { createNavigationCategory } from "./util/createNavigationCategory.js";
16
- import { getRoutes, getVersions } from "./util/getRoutes.js";
16
+ import { getRoutes, getVersionMetadata } from "./util/getRoutes.js";
17
17
 
18
18
  export const GetNavigationOperationsQuery = graphql(`
19
19
  query GetNavigationOperations($input: JSON!, $type: SchemaType!) {
@@ -54,14 +54,19 @@ export const openApiPlugin = (config: OasPluginConfig): ZudokuPlugin => {
54
54
  return {
55
55
  getHead: () => {
56
56
  if (config.type === "url" && !config.skipPreload) {
57
- return (
57
+ const urls = Array.isArray(config.input)
58
+ ? config.input.map((v) => v.input)
59
+ : [config.input];
60
+
61
+ return urls.map((url) => (
58
62
  <link
63
+ key={url}
64
+ href={url}
59
65
  rel="preload"
60
- href={config.input}
61
66
  as="fetch"
62
67
  crossOrigin="anonymous"
63
68
  />
64
- );
69
+ ));
65
70
  }
66
71
 
67
72
  if (config.server) {
@@ -111,10 +116,14 @@ export const openApiPlugin = (config: OasPluginConfig): ZudokuPlugin => {
111
116
 
112
117
  try {
113
118
  const versionParam = match?.params.version;
114
- const version = versionParam ?? getVersions(config).at(0);
119
+ const { versions } = getVersionMetadata(config);
120
+ const version = versionParam ?? versions.at(0);
115
121
  const { type } = config;
116
- // biome-ignore lint/style/noNonNullAssertion: version is guaranteed to be defined
117
- const input = type === "file" ? config.input[version!] : config.input;
122
+
123
+ const input = Array.isArray(config.input)
124
+ ? (config.input.find((v) => v.path === version)?.input ??
125
+ config.input[0]?.input)
126
+ : config.input;
118
127
 
119
128
  const query = createQuery(client, GetNavigationOperationsQuery, {
120
129
  type,
@@ -5,15 +5,21 @@ import type { OperationsFragmentFragment } from "./graphql/graphql.js";
5
5
 
6
6
  type DynamicInput = () => Promise<unknown>;
7
7
 
8
+ export type VersionedInput<T> = Array<{
9
+ path: string;
10
+ label?: string;
11
+ input: T;
12
+ }>;
13
+
8
14
  type OasSource =
9
- | { type: "url"; input: string }
10
- | { type: "file"; input: { [version: string]: DynamicInput } }
15
+ | { type: "url"; input: string | VersionedInput<string> }
16
+ | { type: "file"; input: VersionedInput<DynamicInput> }
11
17
  | { type: "raw"; input: string };
12
18
 
13
- export type ContextOasSource =
14
- | { type: "url"; input: string }
15
- | { type: "file"; input: DynamicInput }
16
- | { type: "raw"; input: string };
19
+ export type ContextOasSource = {
20
+ type: "url" | "file" | "raw";
21
+ input: string | DynamicInput;
22
+ };
17
23
 
18
24
  type Example = {
19
25
  name: string;
@@ -66,6 +72,9 @@ type BaseOasConfig = {
66
72
  showVersionSelect?: "always" | "if-available" | "hide";
67
73
  expandAllTags?: boolean;
68
74
  expandApiInformation?: boolean;
75
+ schemaDownload?: {
76
+ enabled: boolean;
77
+ };
69
78
  transformExamples?: TransformExamplesFn;
70
79
  generateCodeSnippet?: GenerateCodeSnippetFn;
71
80
  };
@@ -76,5 +85,5 @@ export type OasPluginConfig = BaseOasConfig & OasSource;
76
85
  export type OasPluginContext = BaseOasConfig &
77
86
  ContextOasSource & {
78
87
  version?: string;
79
- versions: Record<string, string>;
88
+ versions: Record<string, { path: string; label: string }>;
80
89
  };
@@ -247,7 +247,11 @@ export const Playground = ({
247
247
 
248
248
  const request = new Request(
249
249
  createUrl(server ?? selectedServer, url, data),
250
- { method, headers, body },
250
+ {
251
+ method,
252
+ headers,
253
+ body: ["GET", "HEAD"].includes(method.toUpperCase()) ? null : body,
254
+ },
251
255
  );
252
256
 
253
257
  if (data.identity !== NO_IDENTITY) {
@@ -383,7 +387,7 @@ export const Playground = ({
383
387
  value={selectedServer}
384
388
  defaultValue={selectedServer}
385
389
  >
386
- <SelectTrigger className="p-0 border-none flex-row-reverse bg-transparent text-xs gap-0.5 h-auto translate-y-[4px]">
390
+ <SelectTrigger className="p-0 h-fit shadow-none border-none flex-row-reverse bg-transparent text-xs gap-0.5 translate-y-[4px]">
387
391
  <SelectValue />
388
392
  </SelectTrigger>
389
393
  <SelectContent>
@@ -17,7 +17,7 @@ import { ParamInfos } from "../ParamInfos.js";
17
17
  import { SchemaExampleAndDefault } from "./SchemaExampleAndDefault.js";
18
18
  import { SchemaPropertyItem } from "./SchemaPropertyItem.js";
19
19
  import { UnionView } from "./UnionView.js";
20
- import { isBasicType } from "./utils.js";
20
+ import { isArrayType, isBasicType } from "./utils.js";
21
21
 
22
22
  const renderMarkdown = (content?: string) =>
23
23
  content && (
@@ -91,10 +91,20 @@ export const SchemaView = ({
91
91
  return renderBasicSchema(schema, cardHeader, embedded);
92
92
  }
93
93
 
94
- if (schema.type === "array" && typeof schema.items === "object") {
95
- return <SchemaView schema={schema.items} cardHeader={cardHeader} />;
94
+ if (isArrayType(schema) && typeof schema.items === "object") {
95
+ const wrappedSchema: SchemaObject = {
96
+ type: "object",
97
+ properties: { "": schema },
98
+ };
99
+
100
+ return (
101
+ <SchemaView schema={wrappedSchema} cardHeader={cardHeader} defaultOpen />
102
+ );
96
103
  }
97
104
 
105
+ const additionalObjectProperties = typeof schema.additionalProperties ===
106
+ "object" && <SchemaView schema={schema.additionalProperties} embedded />;
107
+
98
108
  if (schema.type === "object") {
99
109
  const groupedProperties = groupBy(
100
110
  Object.entries(schema.properties ?? {}),
@@ -106,32 +116,31 @@ export const SchemaView = ({
106
116
  : "optional";
107
117
  },
108
118
  );
119
+
109
120
  const groupNames = ["required", "optional", "deprecated"] as const;
121
+ const groups = groupNames.flatMap((group) => {
122
+ const properties = groupedProperties[group];
123
+ return properties ? { group, properties } : [];
124
+ });
110
125
 
111
- const additionalObjectProperties = typeof schema.additionalProperties ===
112
- "object" && <SchemaView schema={schema.additionalProperties} embedded />;
113
-
114
- const itemsList = groupNames.map(
115
- (group, index) =>
116
- groupedProperties[group] && (
117
- <Fragment key={group}>
118
- {index > 0 && <ItemSeparator />}
119
- <ItemGroup className="overflow-clip">
120
- {groupedProperties[group].map(([name, schema], index) => (
121
- <Fragment key={name}>
122
- {index > 0 && <ItemSeparator />}
123
- <SchemaPropertyItem
124
- name={name}
125
- schema={schema}
126
- group={group}
127
- defaultOpen={defaultOpen}
128
- />
129
- </Fragment>
130
- ))}
131
- </ItemGroup>
132
- </Fragment>
133
- ),
134
- );
126
+ const itemsList = groups.map(({ group, properties }, index) => (
127
+ <Fragment key={group}>
128
+ {index > 0 && <ItemSeparator />}
129
+ <ItemGroup className="overflow-clip">
130
+ {properties.map(([name, schema], index) => (
131
+ <Fragment key={name}>
132
+ {index > 0 && <ItemSeparator />}
133
+ <SchemaPropertyItem
134
+ name={name}
135
+ schema={schema}
136
+ group={group}
137
+ defaultOpen={defaultOpen}
138
+ />
139
+ </Fragment>
140
+ ))}
141
+ </ItemGroup>
142
+ </Fragment>
143
+ ));
135
144
 
136
145
  if (embedded) {
137
146
  return itemsList;
@@ -1,5 +1,8 @@
1
1
  import { CIRCULAR_REF } from "../../../oas/graphql/circular.js";
2
- import type { SchemaObject } from "../../../oas/parser/index.js";
2
+ import type {
3
+ ArraySchemaObject,
4
+ SchemaObject,
5
+ } from "../../../oas/parser/index.js";
3
6
 
4
7
  export const isBasicType = (
5
8
  type: unknown,
@@ -8,7 +11,7 @@ export const isBasicType = (
8
11
  ["string", "number", "boolean", "integer", "null"].includes(type)) ||
9
12
  (Array.isArray(type) && type.every(isBasicType));
10
13
 
11
- export const isArrayType = (value: SchemaObject) =>
14
+ export const isArrayType = (value: SchemaObject): value is ArraySchemaObject =>
12
15
  value.type === "array" ||
13
16
  // schema.type might be an array of types, so we need to check if "array" is one of them
14
17
  (Array.isArray(value.type) && value.type.includes("array"));
@@ -148,8 +148,18 @@ const createVersionRoutes = (
148
148
  ];
149
149
  };
150
150
 
151
- export const getVersions = (config: OasPluginConfig) =>
152
- config.type === "file" ? Object.keys(config.input) : [];
151
+ export const getVersionMetadata = (config: OasPluginConfig) => {
152
+ if (config.type === "raw" || !Array.isArray(config.input)) {
153
+ return { versions: [], labels: {} };
154
+ }
155
+
156
+ return {
157
+ versions: config.input.map((v) => v.path),
158
+ labels: Object.fromEntries(
159
+ config.input.map((v) => [v.path, v.label ?? v.path]),
160
+ ),
161
+ };
162
+ };
153
163
 
154
164
  export const getRoutes = ({
155
165
  basePath,
@@ -161,10 +171,33 @@ export const getRoutes = ({
161
171
  basePath: string;
162
172
  }): RouteObject[] => {
163
173
  const tagPages = config.tagPages;
174
+ const { versions } = getVersionMetadata(config);
164
175
 
165
176
  // If the config does not provide tag pages the catch-all
166
177
  // route handles all operations on a single page
167
178
  if (!tagPages) {
179
+ // If there are versions, create versioned routes even without tag pages
180
+ if (versions.length > 0) {
181
+ const versionsInPath =
182
+ versions.length > 1 ? [undefined, ...versions] : [undefined];
183
+
184
+ return versionsInPath.map((version) => {
185
+ const versionPath = joinUrl(basePath, version);
186
+ return createOasProvider({
187
+ basePath,
188
+ version,
189
+ routePath: versionPath,
190
+ routes: [
191
+ createNonTagPagesRoute({ path: `${versionPath}/:tag?` }),
192
+ ...createAdditionalRoutes(versionPath),
193
+ ],
194
+ client,
195
+ config,
196
+ });
197
+ });
198
+ }
199
+
200
+ // No versions, single route
168
201
  return [
169
202
  createOasProvider({
170
203
  basePath,
@@ -179,7 +212,6 @@ export const getRoutes = ({
179
212
  ];
180
213
  }
181
214
 
182
- const versions = getVersions(config);
183
215
  // The latest version always is added as index path
184
216
  const versionsInPath =
185
217
  versions.length > 1 ? [undefined, ...versions] : [undefined];