zudoku 0.25.2 → 0.26.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 (264) hide show
  1. package/dist/app/demo.js +0 -1
  2. package/dist/app/demo.js.map +1 -1
  3. package/dist/app/standalone.js +0 -1
  4. package/dist/app/standalone.js.map +1 -1
  5. package/dist/cli/dev/handler.js +2 -2
  6. package/dist/cli/dev/handler.js.map +1 -1
  7. package/dist/config/validators/InputSidebarSchema.d.ts +2 -2
  8. package/dist/config/validators/common.d.ts +28 -28
  9. package/dist/config/validators/icon-types.d.ts +1 -1
  10. package/dist/config/validators/validate.d.ts +16 -16
  11. package/dist/lib/components/navigation/SidebarCategory.js +1 -1
  12. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  13. package/dist/lib/oas/graphql/index.d.ts +3 -0
  14. package/dist/lib/oas/graphql/index.js +12 -13
  15. package/dist/lib/oas/graphql/index.js.map +1 -1
  16. package/dist/lib/plugins/openapi/ColorizedParam.d.ts +10 -2
  17. package/dist/lib/plugins/openapi/ColorizedParam.js +16 -7
  18. package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
  19. package/dist/lib/plugins/openapi/OperationList.js +3 -0
  20. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  21. package/dist/lib/plugins/openapi/ParameterListItem.js +3 -2
  22. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  23. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +2 -0
  24. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  25. package/dist/lib/plugins/openapi/RequestBodySidecarBox.d.ts +3 -2
  26. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +3 -6
  27. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  28. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +2 -6
  29. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  30. package/dist/lib/plugins/openapi/Sidecar.js +9 -6
  31. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  32. package/dist/lib/plugins/openapi/SidecarBox.js +1 -1
  33. package/dist/lib/plugins/openapi/SidecarBox.js.map +1 -1
  34. package/dist/lib/plugins/openapi/{ExampleDisplay.d.ts → SidecarExamples.d.ts} +3 -6
  35. package/dist/lib/plugins/openapi/SidecarExamples.js +65 -0
  36. package/dist/lib/plugins/openapi/SidecarExamples.js.map +1 -0
  37. package/dist/lib/plugins/openapi/client/GraphQLClient.d.ts +1 -1
  38. package/dist/lib/plugins/openapi/client/GraphQLClient.js +22 -93
  39. package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -1
  40. package/dist/lib/plugins/openapi/client/createServer.d.ts +2 -1
  41. package/dist/lib/plugins/openapi/client/createServer.js +5 -2
  42. package/dist/lib/plugins/openapi/client/createServer.js.map +1 -1
  43. package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
  44. package/dist/lib/plugins/openapi/client/useCreateQuery.js +2 -13
  45. package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
  46. package/dist/lib/plugins/openapi/index.d.ts +2 -1
  47. package/dist/lib/plugins/openapi/index.js.map +1 -1
  48. package/dist/lib/plugins/openapi/playground/EnumSelector.d.ts +8 -0
  49. package/dist/lib/plugins/openapi/playground/EnumSelector.js +21 -0
  50. package/dist/lib/plugins/openapi/playground/EnumSelector.js.map +1 -0
  51. package/dist/lib/plugins/openapi/playground/PathParams.js +9 -4
  52. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  53. package/dist/lib/plugins/openapi/playground/Playground.d.ts +3 -0
  54. package/dist/lib/plugins/openapi/playground/Playground.js +5 -2
  55. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  56. package/dist/lib/plugins/openapi/playground/QueryParams.js +23 -8
  57. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  58. package/dist/lib/plugins/openapi/schema/SchemaComponents.js +2 -1
  59. package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -1
  60. package/dist/lib/plugins/openapi/util/generateSchemaExample.js +19 -11
  61. package/dist/lib/plugins/openapi/util/generateSchemaExample.js.map +1 -1
  62. package/dist/lib/ui/Badge.d.ts +1 -1
  63. package/dist/lib/ui/Badge.js +2 -1
  64. package/dist/lib/ui/Badge.js.map +1 -1
  65. package/dist/lib/ui/Button.d.ts +1 -1
  66. package/dist/lib/ui/Checkbox.d.ts +8 -2
  67. package/dist/lib/ui/Checkbox.js +13 -1
  68. package/dist/lib/ui/Checkbox.js.map +1 -1
  69. package/dist/lib/util/traverse.d.ts +8 -1
  70. package/dist/lib/util/traverse.js +7 -3
  71. package/dist/lib/util/traverse.js.map +1 -1
  72. package/dist/vite/api/schema-codegen.d.ts +12 -0
  73. package/dist/vite/api/schema-codegen.js +62 -0
  74. package/dist/vite/api/schema-codegen.js.map +1 -0
  75. package/dist/vite/api/schema-codegen.test.d.ts +1 -0
  76. package/dist/vite/api/schema-codegen.test.js +247 -0
  77. package/dist/vite/api/schema-codegen.test.js.map +1 -0
  78. package/dist/vite/config.js +0 -7
  79. package/dist/vite/config.js.map +1 -1
  80. package/dist/vite/config.test.js +5 -1
  81. package/dist/vite/config.test.js.map +1 -1
  82. package/dist/vite/plugin-api.js +110 -82
  83. package/dist/vite/plugin-api.js.map +1 -1
  84. package/dist/vite/plugin-component.js +0 -1
  85. package/dist/vite/plugin-component.js.map +1 -1
  86. package/lib/{AnchorLink-DFZZbmvr.js → AnchorLink-_Vu02ceN.js} +3 -3
  87. package/lib/{AnchorLink-DFZZbmvr.js.map → AnchorLink-_Vu02ceN.js.map} +1 -1
  88. package/lib/{AuthenticationPlugin-D7G3me8L.js → AuthenticationPlugin-DNXBcsVN.js} +4 -4
  89. package/lib/{AuthenticationPlugin-D7G3me8L.js.map → AuthenticationPlugin-DNXBcsVN.js.map} +1 -1
  90. package/lib/{Button-DeAoTouo.js → Button-oroWHXAy.js} +3 -3
  91. package/lib/{Button-DeAoTouo.js.map → Button-oroWHXAy.js.map} +1 -1
  92. package/lib/{CategoryHeading-CBconmtI.js → CategoryHeading-MYL1u_6K.js} +3 -3
  93. package/lib/{CategoryHeading-CBconmtI.js.map → CategoryHeading-MYL1u_6K.js.map} +1 -1
  94. package/lib/Command-D5DE0DD7.js +611 -0
  95. package/lib/Command-D5DE0DD7.js.map +1 -0
  96. package/lib/{Markdown-CZDLNOFc.js → Markdown-BrfrjEk_.js} +1041 -1041
  97. package/lib/{Markdown-CZDLNOFc.js.map → Markdown-BrfrjEk_.js.map} +1 -1
  98. package/lib/{MdxPage-DKMbBROv.js → MdxPage-LNZLj_A5.js} +24 -24
  99. package/lib/{MdxPage-DKMbBROv.js.map → MdxPage-LNZLj_A5.js.map} +1 -1
  100. package/lib/{OperationList-BLdHAQ39.js → OperationList-PCwzTp1r.js} +1899 -1877
  101. package/lib/OperationList-PCwzTp1r.js.map +1 -0
  102. package/lib/{Route-DYwKZ_c_.js → Route-Pzk6qwIk.js} +5 -5
  103. package/lib/{Route-DYwKZ_c_.js.map → Route-Pzk6qwIk.js.map} +1 -1
  104. package/lib/{Select-B_IxRUUC.js → Select-DkOpAG0c.js} +36 -36
  105. package/lib/{Select-B_IxRUUC.js.map → Select-DkOpAG0c.js.map} +1 -1
  106. package/lib/{SlotletProvider-pfc9oejW.js → SlotletProvider-DPbx9KdU.js} +53 -53
  107. package/lib/{SlotletProvider-pfc9oejW.js.map → SlotletProvider-DPbx9KdU.js.map} +1 -1
  108. package/lib/{Spinner-DuxJLLNE.js → Spinner-C5gHXrVz.js} +2 -2
  109. package/lib/{Spinner-DuxJLLNE.js.map → Spinner-C5gHXrVz.js.map} +1 -1
  110. package/lib/{SyntaxHighlight-Bz-lOJtH.js → SyntaxHighlight-CJCSPG1F.js} +297 -301
  111. package/lib/{SyntaxHighlight-Bz-lOJtH.js.map → SyntaxHighlight-CJCSPG1F.js.map} +1 -1
  112. package/lib/{ZudokuContext-hmLMUdf2.js → ZudokuContext-D3ayHjP-.js} +357 -356
  113. package/lib/ZudokuContext-D3ayHjP-.js.map +1 -0
  114. package/lib/{chunk-D52XG6IA-Dl7HLe6j.js → chunk-SYFQ2XB5-KWlHsT7t.js} +407 -410
  115. package/lib/chunk-SYFQ2XB5-KWlHsT7t.js.map +1 -0
  116. package/lib/context-rwLGh-6_.js +22 -0
  117. package/lib/{context-h_UkBLvr.js.map → context-rwLGh-6_.js.map} +1 -1
  118. package/lib/{createServer-Bf5_6o6G.js → createServer-BcaswoFO.js} +4363 -5302
  119. package/lib/createServer-BcaswoFO.js.map +1 -0
  120. package/lib/{hook-CHq7pFyz.js → hook-DUyACbIK.js} +17 -17
  121. package/lib/{hook-CHq7pFyz.js.map → hook-DUyACbIK.js.map} +1 -1
  122. package/lib/index-CaILD1AV.js +1292 -0
  123. package/lib/index-CaILD1AV.js.map +1 -0
  124. package/lib/index-Djenk2Hj.js +36 -0
  125. package/lib/{index-CPNSgwSb.js.map → index-Djenk2Hj.js.map} +1 -1
  126. package/lib/{index-CBXSgjaE.js → index-Dl3Yl0yb.js} +65 -69
  127. package/lib/index-Dl3Yl0yb.js.map +1 -0
  128. package/lib/index-TaRXY2w1.js +43 -0
  129. package/lib/index-TaRXY2w1.js.map +1 -0
  130. package/lib/{index.esm-BSV1C092.js → index.esm-9-TF9KQB.js} +52 -52
  131. package/lib/{index.esm-BSV1C092.js.map → index.esm-9-TF9KQB.js.map} +1 -1
  132. package/lib/index.esm-CrSoEshU.js +1207 -0
  133. package/lib/index.esm-CrSoEshU.js.map +1 -0
  134. package/lib/{jsx-runtime-Dx-03ztt.js → jsx-runtime-Bdg6XQ1m.js} +135 -135
  135. package/lib/{jsx-runtime-Dx-03ztt.js.map → jsx-runtime-Bdg6XQ1m.js.map} +1 -1
  136. package/lib/post-processors/removeExtensions.js +3 -3
  137. package/lib/post-processors/traverse.js +11 -8
  138. package/lib/post-processors/traverse.js.map +1 -1
  139. package/lib/{prism-bash.min-DadFsM4Z.js → prism-bash.min-HHIMdNJ_.js} +4 -4
  140. package/lib/{prism-bash.min-DadFsM4Z.js.map → prism-bash.min-HHIMdNJ_.js.map} +1 -1
  141. package/lib/{prism-csharp.min-DUwvItt4.js → prism-csharp.min-bQAo2pmx.js} +33 -33
  142. package/lib/{prism-csharp.min-DUwvItt4.js.map → prism-csharp.min-bQAo2pmx.js.map} +1 -1
  143. package/lib/{prism-java.min-BtgBR4yd.js → prism-java.min-BpvsOuIa.js} +12 -12
  144. package/lib/{prism-java.min-BtgBR4yd.js.map → prism-java.min-BpvsOuIa.js.map} +1 -1
  145. package/lib/{prism-markdown.min-F3U-vPBi.js → prism-markdown.min-C0Qn0m-5.js} +30 -30
  146. package/lib/{prism-markdown.min-F3U-vPBi.js.map → prism-markdown.min-C0Qn0m-5.js.map} +1 -1
  147. package/lib/{prism-ruby.min-DeDXCp1r.js → prism-ruby.min-Dx9KO9ds.js} +16 -16
  148. package/lib/{prism-ruby.min-DeDXCp1r.js.map → prism-ruby.min-Dx9KO9ds.js.map} +1 -1
  149. package/lib/prism-typescript.min-CD7H2IYQ.js.map +1 -1
  150. package/lib/state-mM7uaXTW.js +202 -0
  151. package/lib/state-mM7uaXTW.js.map +1 -0
  152. package/lib/ui/Accordion.js +1 -1
  153. package/lib/ui/ActionButton.js +3 -3
  154. package/lib/ui/Alert.js +2 -2
  155. package/lib/ui/AlertDialog.js +1 -1
  156. package/lib/ui/Badge.js +4 -3
  157. package/lib/ui/Badge.js.map +1 -1
  158. package/lib/ui/Breadcrumb.js +1 -1
  159. package/lib/ui/Button.js +2 -2
  160. package/lib/ui/Callout.js +1 -1
  161. package/lib/ui/Card.js +1 -1
  162. package/lib/ui/Carousel.js +3 -3
  163. package/lib/ui/Carousel.js.map +1 -1
  164. package/lib/ui/Checkbox.js +26 -15
  165. package/lib/ui/Checkbox.js.map +1 -1
  166. package/lib/ui/Command.js +14 -550
  167. package/lib/ui/Command.js.map +1 -1
  168. package/lib/ui/Dialog.js +1 -1
  169. package/lib/ui/Drawer.js +520 -519
  170. package/lib/ui/Drawer.js.map +1 -1
  171. package/lib/ui/DropdownMenu.js +1 -1
  172. package/lib/ui/Form.js +2 -2
  173. package/lib/ui/HoverCard.js +1 -1
  174. package/lib/ui/Input.js +1 -1
  175. package/lib/ui/Label.js +2 -2
  176. package/lib/ui/Pagination.js +7 -7
  177. package/lib/ui/Popover.js +1 -1
  178. package/lib/ui/Progress.js +1 -1
  179. package/lib/ui/RadioGroup.js +1 -1
  180. package/lib/ui/ScrollArea.js +1 -1
  181. package/lib/ui/Select.js +1 -1
  182. package/lib/ui/Skeleton.js +1 -1
  183. package/lib/ui/Slider.js +1 -1
  184. package/lib/ui/Switch.js +1 -1
  185. package/lib/ui/Tabs.js +1 -1
  186. package/lib/ui/Textarea.js +1 -1
  187. package/lib/ui/Toggle.js +2 -2
  188. package/lib/ui/ToggleGroup.js +1 -1
  189. package/lib/ui/Tooltip.js +1 -1
  190. package/lib/{useExposedProps-DE9lR6MF.js → useExposedProps-BBHR7aLM.js} +2 -2
  191. package/lib/{useExposedProps-DE9lR6MF.js.map → useExposedProps-BBHR7aLM.js.map} +1 -1
  192. package/lib/zudoku.auth-auth0.js +1 -1
  193. package/lib/zudoku.auth-clerk.js +18 -18
  194. package/lib/zudoku.auth-openid.js +5 -5
  195. package/lib/zudoku.components.js +492 -477
  196. package/lib/zudoku.components.js.map +1 -1
  197. package/lib/zudoku.plugin-api-catalog.js +5 -5
  198. package/lib/zudoku.plugin-api-keys.js +7 -7
  199. package/lib/zudoku.plugin-custom-pages.js +3 -3
  200. package/lib/zudoku.plugin-markdown.js +2 -2
  201. package/lib/zudoku.plugin-openapi.js +5 -5
  202. package/lib/zudoku.plugin-redirect.js +1 -1
  203. package/lib/zudoku.plugin-search-inkeep.js +9 -9
  204. package/package.json +48 -53
  205. package/src/app/demo.tsx +0 -1
  206. package/src/app/standalone.tsx +0 -1
  207. package/src/lib/components/navigation/SidebarCategory.tsx +2 -2
  208. package/src/lib/oas/graphql/index.ts +19 -15
  209. package/src/lib/plugins/openapi/ColorizedParam.tsx +29 -12
  210. package/src/lib/plugins/openapi/OperationList.tsx +4 -0
  211. package/src/lib/plugins/openapi/ParameterListItem.tsx +5 -7
  212. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +2 -0
  213. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +9 -8
  214. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +5 -8
  215. package/src/lib/plugins/openapi/Sidecar.tsx +14 -7
  216. package/src/lib/plugins/openapi/SidecarBox.tsx +1 -1
  217. package/src/lib/plugins/openapi/SidecarExamples.tsx +163 -0
  218. package/src/lib/plugins/openapi/client/GraphQLClient.tsx +28 -120
  219. package/src/lib/plugins/openapi/client/createServer.ts +6 -2
  220. package/src/lib/plugins/openapi/client/useCreateQuery.ts +2 -17
  221. package/src/lib/plugins/openapi/index.tsx +2 -1
  222. package/src/lib/plugins/openapi/playground/EnumSelector.tsx +86 -0
  223. package/src/lib/plugins/openapi/playground/PathParams.tsx +72 -64
  224. package/src/lib/plugins/openapi/playground/Playground.tsx +26 -13
  225. package/src/lib/plugins/openapi/playground/QueryParams.tsx +102 -73
  226. package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +4 -7
  227. package/src/lib/plugins/openapi/util/generateSchemaExample.ts +26 -11
  228. package/src/lib/ui/Badge.tsx +2 -1
  229. package/src/lib/ui/Checkbox.tsx +24 -7
  230. package/src/lib/util/traverse.ts +15 -5
  231. package/dist/lib/plugins/openapi/ExampleDisplay.js +0 -78
  232. package/dist/lib/plugins/openapi/ExampleDisplay.js.map +0 -1
  233. package/dist/lib/plugins/openapi/client/worker.d.ts +0 -4
  234. package/dist/lib/plugins/openapi/client/worker.js +0 -29
  235. package/dist/lib/plugins/openapi/client/worker.js.map +0 -1
  236. package/dist/lib/plugins/openapi-worker.d.ts +0 -1
  237. package/dist/lib/plugins/openapi-worker.js +0 -8
  238. package/dist/lib/plugins/openapi-worker.js.map +0 -1
  239. package/lib/Dialog-Bxv1yEIg.js +0 -67
  240. package/lib/Dialog-Bxv1yEIg.js.map +0 -1
  241. package/lib/OperationList-BLdHAQ39.js.map +0 -1
  242. package/lib/ZudokuContext-hmLMUdf2.js.map +0 -1
  243. package/lib/assets/index-C7jnHK4b.js +0 -4841
  244. package/lib/assets/index-C7jnHK4b.js.map +0 -1
  245. package/lib/assets/worker-Cbp2r2BQ.js +0 -18592
  246. package/lib/assets/worker-Cbp2r2BQ.js.map +0 -1
  247. package/lib/chunk-D52XG6IA-Dl7HLe6j.js.map +0 -1
  248. package/lib/context-h_UkBLvr.js +0 -22
  249. package/lib/createServer-Bf5_6o6G.js.map +0 -1
  250. package/lib/index-BNx95gkf.js +0 -1284
  251. package/lib/index-BNx95gkf.js.map +0 -1
  252. package/lib/index-CBXSgjaE.js.map +0 -1
  253. package/lib/index-CPNSgwSb.js +0 -36
  254. package/lib/index-DyBL--Kz.js +0 -826
  255. package/lib/index-DyBL--Kz.js.map +0 -1
  256. package/lib/index.esm-BnnBRKJX.js +0 -1214
  257. package/lib/index.esm-BnnBRKJX.js.map +0 -1
  258. package/lib/state-CFQsUZUP.js +0 -202
  259. package/lib/state-CFQsUZUP.js.map +0 -1
  260. package/lib/zudoku.openapi-worker.js +0 -15
  261. package/lib/zudoku.openapi-worker.js.map +0 -1
  262. package/src/lib/plugins/openapi/ExampleDisplay.tsx +0 -163
  263. package/src/lib/plugins/openapi/client/worker.ts +0 -44
  264. package/src/lib/plugins/openapi-worker.ts +0 -11
@@ -1,6 +1,6 @@
1
1
  import { useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { HTTPSnippet } from "@zudoku/httpsnippet";
3
- import { Fragment, useMemo, useTransition } from "react";
3
+ import { Fragment, useMemo, useState, useTransition } from "react";
4
4
  import { useSearchParams } from "react-router";
5
5
  import { useSelectedServerStore } from "../../authentication/state.js";
6
6
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
@@ -107,6 +107,7 @@ export const Sidecar = ({
107
107
 
108
108
  const [searchParams, setSearchParams] = useSearchParams();
109
109
  const [, startTransition] = useTransition();
110
+ const [selectedExample, setSelectedExample] = useState<unknown>();
110
111
 
111
112
  const selectedLang = searchParams.get("lang") ?? "shell";
112
113
 
@@ -141,9 +142,11 @@ export const Sidecar = ({
141
142
  const { selectedServer } = useSelectedServerStore();
142
143
 
143
144
  const code = useMemo(() => {
144
- const example = requestBodyContent?.[0]?.schema
145
- ? generateSchemaExample(requestBodyContent[0].schema as SchemaObject)
146
- : undefined;
145
+ const example =
146
+ selectedExample ??
147
+ (requestBodyContent?.[0]?.schema
148
+ ? generateSchemaExample(requestBodyContent[0].schema as SchemaObject)
149
+ : undefined);
147
150
 
148
151
  const snippet = new HTTPSnippet({
149
152
  method: operation.method.toLocaleUpperCase(),
@@ -166,6 +169,7 @@ export const Sidecar = ({
166
169
 
167
170
  return getConverted(snippet, selectedLang);
168
171
  }, [
172
+ selectedExample,
169
173
  requestBodyContent,
170
174
  operation.method,
171
175
  operation.path,
@@ -181,7 +185,7 @@ export const Sidecar = ({
181
185
  className="flex flex-col overflow-hidden sticky top-[--scroll-padding] gap-4"
182
186
  >
183
187
  <SidecarBox.Root>
184
- <SidecarBox.Head className="flex justify-between items-center flex-nowrap py-4 gap-2 text-xs">
188
+ <SidecarBox.Head className="flex justify-between items-center flex-nowrap py-2.5 gap-2 text-xs">
185
189
  <span className="font-mono break-words">
186
190
  <span className={cn("font-semibold", methodTextColor)}>
187
191
  {operation.method.toLocaleUpperCase()}
@@ -209,7 +213,7 @@ export const Sidecar = ({
209
213
  />
210
214
  </CollapsibleCode>
211
215
  </SidecarBox.Body>
212
- <SidecarBox.Footer className="flex items-center text-xs gap-2 justify-end py-3">
216
+ <SidecarBox.Footer className="flex items-center text-xs gap-2 justify-end py-2.5">
213
217
  <span>Show example in</span>
214
218
  <SimpleSelect
215
219
  className="self-start max-w-[150px]"
@@ -229,7 +233,10 @@ export const Sidecar = ({
229
233
  )}
230
234
  </SidecarBox.Root>
231
235
  {isOnScreen && requestBodyContent && (
232
- <RequestBodySidecarBox content={requestBodyContent} />
236
+ <RequestBodySidecarBox
237
+ content={requestBodyContent}
238
+ onExampleChange={setSelectedExample}
239
+ />
233
240
  )}
234
241
  {isOnScreen && operation.responses.length > 0 && (
235
242
  <ResponsesSidecarBox
@@ -16,7 +16,7 @@ export const Root = ({ children, className }: BaseComponentProps) => (
16
16
  export const Head = ({ children, className }: BaseComponentProps) => (
17
17
  <div
18
18
  className={cn(
19
- "border-b bg-muted dark:bg-transparent text-card-foreground p-3",
19
+ "border-b bg-muted dark:bg-transparent text-card-foreground p-3 py-2.5",
20
20
  className,
21
21
  )}
22
22
  >
@@ -0,0 +1,163 @@
1
+ import { useEffect, useState } from "react";
2
+ import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
3
+ import { type SchemaObject } from "../../oas/graphql/index.js";
4
+ import { CollapsibleCode } from "./CollapsibleCode.js";
5
+ import type { OperationListItemResult } from "./OperationList.js";
6
+ import * as SidecarBox from "./SidecarBox.js";
7
+ import { SimpleSelect } from "./SimpleSelect.js";
8
+ import { generateSchemaExample } from "./util/generateSchemaExample.js";
9
+
10
+ export type Content = NonNullable<
11
+ NonNullable<OperationListItemResult["requestBody"]>["content"]
12
+ >;
13
+ export type Example = NonNullable<
14
+ NonNullable<Content[number]["examples"]>
15
+ >[number];
16
+
17
+ const formatForDisplay = (value: unknown): string => {
18
+ if (value == null) return "No example";
19
+ if (typeof value === "string") return value.trim();
20
+ return JSON.stringify(value, null, 2);
21
+ };
22
+
23
+ const getLanguage = (mediaType?: string): string => {
24
+ if (!mediaType) return "plain";
25
+ const languages: Record<string, string> = {
26
+ "application/json": "json",
27
+ "application/xml": "xml",
28
+ "application/x-yaml": "yaml",
29
+ "text/csv": "csv",
30
+ "application/javascript": "javascript",
31
+ "application/graphql": "graphql",
32
+ };
33
+ return languages[mediaType] ?? "plain";
34
+ };
35
+
36
+ export type SidecarExamplesProps = {
37
+ content: Content;
38
+ description?: string;
39
+ onExampleChange?: (example: unknown) => void;
40
+ };
41
+
42
+ export const SidecarExamples = ({
43
+ content,
44
+ description,
45
+ onExampleChange,
46
+ }: SidecarExamplesProps) => {
47
+ const [selectedContentTypeIndex, setSelectedContentTypeIndex] = useState(0);
48
+ const [selectedExampleIndex, setSelectedExampleIndex] = useState(0);
49
+
50
+ // Get the effective content (handle single item array case)
51
+ const effectiveContent =
52
+ Array.isArray(content) && content.length === 1
53
+ ? content[0]
54
+ : content[selectedContentTypeIndex];
55
+
56
+ // Get example value, with fallback to schema-generated example
57
+ const examples = effectiveContent?.examples ?? [];
58
+ const selectedExample = examples[selectedExampleIndex];
59
+
60
+ let exampleValue = undefined;
61
+ if (selectedExample) {
62
+ // If it's a wrapped example with a value field, use that
63
+ exampleValue =
64
+ "value" in selectedExample ? selectedExample.value : selectedExample;
65
+ } else if (effectiveContent?.schema) {
66
+ // No example provided, generate one from schema
67
+ exampleValue = generateSchemaExample(
68
+ effectiveContent.schema as SchemaObject,
69
+ );
70
+ }
71
+
72
+ useEffect(() => {
73
+ onExampleChange?.(exampleValue);
74
+ }, [exampleValue, onExampleChange]);
75
+
76
+ const formattedExample = formatForDisplay(exampleValue);
77
+ const language = getLanguage(effectiveContent?.mediaType);
78
+
79
+ const hasContent = examples.length > 0 || content.length > 0;
80
+
81
+ return (
82
+ <>
83
+ <SidecarBox.Body className="p-0">
84
+ {selectedExample?.externalValue ? (
85
+ <div className="p-2">
86
+ <a
87
+ href={selectedExample.externalValue}
88
+ target="_blank"
89
+ rel="noopener noreferrer"
90
+ className="text-xs text-primary hover:underline"
91
+ >
92
+ View External Example →
93
+ </a>
94
+ </div>
95
+ ) : (
96
+ <CollapsibleCode>
97
+ <SyntaxHighlight
98
+ language={language}
99
+ noBackground
100
+ copyable
101
+ className="[--scrollbar-color:gray] text-xs max-h-[500px] p-2"
102
+ code={formattedExample}
103
+ />
104
+ </CollapsibleCode>
105
+ )}
106
+ {selectedExample?.description && (
107
+ <div className="border-t text-xs px-3 py-1.5 text-muted-foreground">
108
+ {selectedExample.description}
109
+ </div>
110
+ )}
111
+ </SidecarBox.Body>
112
+ {hasContent && (
113
+ <SidecarBox.Footer className="text-xs p-0">
114
+ {description && (
115
+ <div className="text-muted-foreground text-xs border-b px-3 py-2">
116
+ {description}
117
+ </div>
118
+ )}
119
+ <div className="flex items-center gap-2 justify-between min-w-0 px-3 py-2">
120
+ <div className="flex items-center gap-2 min-w-0">
121
+ {content.length > 1 ? (
122
+ <SimpleSelect
123
+ className="max-w-[200px]"
124
+ value={selectedContentTypeIndex.toString()}
125
+ onChange={(e) =>
126
+ setSelectedContentTypeIndex(Number(e.target.value))
127
+ }
128
+ options={content.map((c, index) => ({
129
+ value: index.toString(),
130
+ label: c.mediaType,
131
+ }))}
132
+ />
133
+ ) : (
134
+ <span className="font-mono text-[11px]">
135
+ {content[0]?.mediaType}
136
+ </span>
137
+ )}
138
+ </div>
139
+ {examples.length > 1 && (
140
+ <div className="flex items-center gap-1">
141
+ <SimpleSelect
142
+ className="max-w-[180px]"
143
+ value={selectedExampleIndex.toString()}
144
+ onChange={(e) =>
145
+ setSelectedExampleIndex(Number(e.target.value))
146
+ }
147
+ options={examples.map((example, index) => ({
148
+ value: index.toString(),
149
+ label:
150
+ example.summary ||
151
+ example.name ||
152
+ example.description ||
153
+ `Example ${index + 1}`,
154
+ }))}
155
+ />
156
+ </div>
157
+ )}
158
+ </div>
159
+ </SidecarBox.Footer>
160
+ )}
161
+ </>
162
+ );
163
+ };
@@ -1,34 +1,16 @@
1
1
  import type { GraphQLError } from "graphql/error/index.js";
2
- import { ulid } from "ulidx";
3
- import { initializeWorker } from "zudoku/openapi-worker";
4
2
  import { ZudokuError } from "../../../util/invariant.js";
5
3
  import type { TypedDocumentString } from "../graphql/graphql.js";
6
4
  import type { OpenApiPluginOptions } from "../index.js";
7
5
  import type { LocalServer } from "./createServer.js";
8
- import type { WorkerGraphQLMessage } from "./worker.js";
9
6
 
10
7
  let localServerPromise: Promise<LocalServer> | undefined;
11
- let worker: SharedWorker | undefined;
12
8
 
13
9
  type GraphQLResponse<TResult> = {
14
10
  errors?: GraphQLError[];
15
11
  data: TResult;
16
12
  };
17
13
 
18
- const resolveVariables = async (variables?: unknown) => {
19
- if (!variables) return;
20
-
21
- if (
22
- typeof variables === "object" &&
23
- "type" in variables &&
24
- variables.type === "file" &&
25
- "input" in variables &&
26
- typeof variables.input === "function"
27
- ) {
28
- variables.input = await variables.input();
29
- }
30
- };
31
-
32
14
  const throwIfError = (response: GraphQLResponse<unknown>) => {
33
15
  if (!response.errors?.[0]) return;
34
16
 
@@ -39,119 +21,45 @@ const throwIfError = (response: GraphQLResponse<unknown>) => {
39
21
  };
40
22
 
41
23
  export class GraphQLClient {
42
- readonly #mode: "remote" | "in-memory" | "worker";
43
- #pendingRequests = new Map<string, (value: any) => void>();
44
- #port: MessagePort | undefined;
24
+ constructor(private readonly config: OpenApiPluginOptions) {}
45
25
 
46
- constructor(private config: OpenApiPluginOptions) {
47
- if (config.server) {
48
- this.#mode = "remote";
49
- } else if (config.inMemory || typeof SharedWorker === "undefined") {
50
- this.#mode = "in-memory";
51
- } else {
52
- this.#mode = "worker";
26
+ #getLocalServer = async () => {
27
+ if (!localServerPromise) {
28
+ localServerPromise = import("./createServer.js").then((m) =>
29
+ m.createServer(this.config),
30
+ );
53
31
  }
54
- }
32
+ return localServerPromise;
33
+ };
55
34
 
56
- #initializeLocalServer = () =>
57
- import("./createServer.js").then((m) => m.createServer());
35
+ #executeFetch = async (init: RequestInit): Promise<Response> => {
36
+ if (this.config.server) {
37
+ return fetch(this.config.server, init);
38
+ }
39
+
40
+ const localServer = await this.#getLocalServer();
41
+ return localServer.fetch("http://localhost/graphql", init);
42
+ };
58
43
 
59
44
  fetch = async <TResult, TVariables>(
60
45
  query: TypedDocumentString<TResult, TVariables>,
61
46
  ...[variables]: TVariables extends Record<string, never> ? [] : [TVariables]
62
- ) => {
47
+ ): Promise<TResult> => {
63
48
  const operationName = query.match(/query (\w+)/)?.[1];
64
49
 
65
- await resolveVariables(variables);
66
-
67
- const body = JSON.stringify({ query, variables, operationName });
68
-
69
- switch (this.#mode) {
70
- case "remote": {
71
- const response = await fetch(this.config.server!, {
72
- method: "POST",
73
- body,
74
- headers: { "Content-Type": "application/json" },
75
- });
76
-
77
- if (!response.ok) {
78
- throw new Error("Network response was not ok");
79
- }
80
-
81
- const result = (await response.json()) as GraphQLResponse<TResult>;
82
- throwIfError(result);
83
-
84
- return result.data;
85
- }
86
-
87
- case "in-memory": {
88
- if (!localServerPromise) {
89
- localServerPromise = this.#initializeLocalServer();
90
- }
91
-
92
- const localServer = await localServerPromise;
93
- if (!localServer) throw new Error("Local server not initialized");
50
+ const response = await this.#executeFetch({
51
+ method: "POST",
52
+ body: JSON.stringify({ query, variables, operationName }),
53
+ headers: { "Content-Type": "application/json" },
54
+ });
94
55
 
95
- const response = await localServer.fetch(
96
- new Request("http://localhost/graphql", {
97
- method: "POST",
98
- body,
99
- headers: { "Content-Type": "application/json" },
100
- }),
101
- );
102
-
103
- if (!response.ok) {
104
- throw new Error("Network response was not ok");
105
- }
106
-
107
- const result = (await response.json()) as GraphQLResponse<TResult>;
108
- throwIfError(result);
109
-
110
- return result.data;
111
- }
112
-
113
- case "worker": {
114
- if (!worker) {
115
- worker = initializeWorker();
116
- }
117
-
118
- if (!this.#port) {
119
- const channel = new MessageChannel();
120
-
121
- worker.port.postMessage({ port: channel.port2 }, [channel.port2]);
122
-
123
- this.#port = channel.port1;
124
-
125
- this.#port.onmessage = (e: MessageEvent<WorkerGraphQLMessage>) => {
126
- const { id, body } = e.data;
127
- const resolve = this.#pendingRequests.get(id);
128
- if (resolve) {
129
- const result = JSON.parse(body);
130
- resolve(result);
131
- this.#pendingRequests.delete(id);
132
- } else {
133
- // eslint-disable-next-line no-console
134
- console.error(`No pending request found for id: ${id}`);
135
- }
136
- };
137
-
138
- this.#port.start();
139
- }
140
-
141
- const id = ulid();
142
-
143
- const resultPromise = new Promise<GraphQLResponse<TResult>>(
144
- (resolve) => {
145
- this.#pendingRequests.set(id, resolve);
146
- this.#port!.postMessage({ id, body } as WorkerGraphQLMessage);
147
- },
148
- );
56
+ if (!response.ok) {
57
+ throw new Error("Network response was not ok");
58
+ }
149
59
 
150
- const result = await resultPromise;
151
- throwIfError(result);
60
+ const result = (await response.json()) as GraphQLResponse<TResult>;
61
+ throwIfError(result);
152
62
 
153
- return result.data;
154
- }
155
- }
63
+ return result.data;
156
64
  };
157
65
  }
@@ -1,13 +1,17 @@
1
1
  import { useLogger } from "@envelop/core";
2
2
  import { createGraphQLServer } from "../../../oas/graphql/index.js";
3
+ import type { OpenApiPluginOptions } from "../index.js";
3
4
 
4
5
  const map = new Map<string, number>();
5
6
 
6
7
  /**
7
8
  * Creates the GraphQL server
8
9
  */
9
- export const createServer = () =>
10
+ export const createServer = (config: OpenApiPluginOptions) =>
10
11
  createGraphQLServer({
12
+ context: {
13
+ schemaImports: config.schemaImports,
14
+ },
11
15
  plugins: [
12
16
  // eslint-disable-next-line react-hooks/rules-of-hooks
13
17
  useLogger({
@@ -22,7 +26,7 @@ export const createServer = () =>
22
26
  if (start) {
23
27
  // eslint-disable-next-line no-console
24
28
  console.log(
25
- `${args.operationName} query took ${performance.now() - start}ms`,
29
+ `[zudoku:debug] ${args.operationName} query took ${performance.now() - start}ms`,
26
30
  );
27
31
  map.delete(`${startEvent}-${args.operationName}`);
28
32
  }
@@ -1,5 +1,4 @@
1
- import hashit from "object-hash";
2
- import { useContext, useMemo } from "react";
1
+ import { useContext } from "react";
3
2
  import type { TypedDocumentString } from "../graphql/graphql.js";
4
3
  import { GraphQLContext } from "./GraphQLContext.js";
5
4
 
@@ -12,22 +11,8 @@ export const useCreateQuery = <TResult, TVariables>(
12
11
  throw new Error("useGraphQL must be used within a GraphQLProvider");
13
12
  }
14
13
 
15
- const hash = useMemo(() => {
16
- if (
17
- typeof variables[0] === "object" &&
18
- variables[0] != null &&
19
- "input" in variables[0] &&
20
- typeof variables[0].input === "function"
21
- ) {
22
- // This is a pre-hashed name to ensure that the query key is consistent across server and client
23
- return variables[0].input.name;
24
- }
25
-
26
- return hashit(variables[0] ?? {});
27
- }, [variables]);
28
-
29
14
  return {
30
15
  queryFn: () => graphQLClient.fetch(query, ...variables),
31
- queryKey: [query, hash],
16
+ queryKey: [query, variables[0]],
32
17
  } as const;
33
18
  };
@@ -7,6 +7,7 @@ import { CirclePlayIcon, LogInIcon } from "lucide-react";
7
7
  import type { SidebarItem } from "../../../config/validators/SidebarSchema.js";
8
8
  import { useAuth } from "../../authentication/hook.js";
9
9
  import { ColorMap } from "../../components/navigation/SidebarBadge.js";
10
+ import type { SchemaImports } from "../../oas/graphql/index.js";
10
11
  import { Button } from "../../ui/Button.js";
11
12
  import { joinPath } from "../../util/joinPath.js";
12
13
  import { GraphQLClient } from "./client/GraphQLClient.js";
@@ -35,7 +36,7 @@ const GetCategoriesQuery = graphql(`
35
36
  }
36
37
  `);
37
38
 
38
- type InternalOasPluginConfig = { inMemory?: boolean };
39
+ type InternalOasPluginConfig = { schemaImports?: SchemaImports };
39
40
 
40
41
  const MethodColorMap: Record<string, keyof typeof ColorMap> = {
41
42
  get: "green",
@@ -0,0 +1,86 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Command,
4
+ CommandEmpty,
5
+ CommandInput,
6
+ CommandItem,
7
+ CommandList,
8
+ } from "../../../ui/Command.js";
9
+ import {
10
+ Popover,
11
+ PopoverContent,
12
+ PopoverTrigger,
13
+ } from "../../../ui/Popover.js";
14
+ import { cn } from "../../../util/cn.js";
15
+
16
+ interface EnumSelectorProps {
17
+ value: string;
18
+ enumValues: string[];
19
+ onChange: (value: string) => void;
20
+ onValueSelected: () => void;
21
+ }
22
+
23
+ export const EnumSelector = ({
24
+ value,
25
+ enumValues,
26
+ onChange,
27
+ onValueSelected,
28
+ }: EnumSelectorProps) => {
29
+ const [searchValue, setSearchValue] = useState("");
30
+ const [open, setOpen] = useState(false);
31
+
32
+ return (
33
+ <Popover open={open} onOpenChange={setOpen}>
34
+ <PopoverTrigger asChild>
35
+ <button
36
+ type="button"
37
+ role="combobox"
38
+ className={cn(
39
+ "px-3 py-2 w-full border-0 shadow-none text-xs font-mono text-start hover:bg-accent/40 rounded border-transparent hover:bg-accent",
40
+ !value && "text-muted-foreground",
41
+ )}
42
+ >
43
+ {value || "Select value"}
44
+ </button>
45
+ </PopoverTrigger>
46
+ <PopoverContent
47
+ className="p-0 w-[--radix-popover-trigger-width] "
48
+ align="start"
49
+ sideOffset={3}
50
+ alignOffset={-3}
51
+ side="bottom"
52
+ >
53
+ <Command className="max-h-[180px]">
54
+ <CommandInput
55
+ placeholder="Enter value"
56
+ className="h-9 bg-transparent "
57
+ onValueChange={setSearchValue}
58
+ onKeyDown={(e) => {
59
+ if (e.key === "Enter") {
60
+ onChange(searchValue);
61
+ onValueSelected();
62
+ setOpen(false);
63
+ }
64
+ }}
65
+ />
66
+ <CommandList>
67
+ <CommandEmpty>Use "{searchValue}"</CommandEmpty>
68
+ {enumValues.map((enumValue) => (
69
+ <CommandItem
70
+ key={enumValue}
71
+ value={enumValue}
72
+ onSelect={(selected) => {
73
+ onChange(selected);
74
+ onValueSelected();
75
+ setOpen(false);
76
+ }}
77
+ >
78
+ {enumValue}
79
+ </CommandItem>
80
+ ))}
81
+ </CommandList>
82
+ </Command>
83
+ </PopoverContent>
84
+ </Popover>
85
+ );
86
+ };