zudoku 0.42.2 → 0.43.1

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 (202) hide show
  1. package/dist/app/entry.client.js +21 -0
  2. package/dist/app/entry.client.js.map +1 -1
  3. package/dist/config/loader.d.ts +1 -16
  4. package/dist/config/loader.js +9 -90
  5. package/dist/config/loader.js.map +1 -1
  6. package/dist/config/validators/common.d.ts +193 -0
  7. package/dist/config/validators/common.js +2 -0
  8. package/dist/config/validators/common.js.map +1 -1
  9. package/dist/config/validators/validate.d.ts +70 -0
  10. package/dist/lib/components/Banner.js +1 -1
  11. package/dist/lib/components/Banner.js.map +1 -1
  12. package/dist/lib/components/Heading.d.ts +2 -2
  13. package/dist/lib/components/Heading.js +1 -1
  14. package/dist/lib/components/Heading.js.map +1 -1
  15. package/dist/lib/components/Layout.js +1 -1
  16. package/dist/lib/components/Layout.js.map +1 -1
  17. package/dist/lib/components/Main.js +3 -2
  18. package/dist/lib/components/Main.js.map +1 -1
  19. package/dist/lib/components/MobileTopNavigation.js +1 -1
  20. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  21. package/dist/lib/components/Pagination.js +2 -2
  22. package/dist/lib/components/Pagination.js.map +1 -1
  23. package/dist/lib/components/Search.js +1 -1
  24. package/dist/lib/components/Search.js.map +1 -1
  25. package/dist/lib/components/TopNavigation.js +1 -1
  26. package/dist/lib/components/TopNavigation.js.map +1 -1
  27. package/dist/lib/components/navigation/PoweredByZudoku.js +1 -1
  28. package/dist/lib/components/navigation/PoweredByZudoku.js.map +1 -1
  29. package/dist/lib/components/navigation/Sidebar.js +1 -1
  30. package/dist/lib/components/navigation/Sidebar.js.map +1 -1
  31. package/dist/lib/components/navigation/SidebarCategory.js +9 -5
  32. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  33. package/dist/lib/components/navigation/SidebarItem.js +2 -3
  34. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  35. package/dist/lib/components/navigation/Toc.js +2 -2
  36. package/dist/lib/components/navigation/Toc.js.map +1 -1
  37. package/dist/lib/core/ZudokuContext.d.ts +2 -1
  38. package/dist/lib/core/ZudokuContext.js.map +1 -1
  39. package/dist/lib/oas/parser/upgrade/index.d.ts +1 -0
  40. package/dist/lib/oas/parser/upgrade/index.js +76 -24
  41. package/dist/lib/oas/parser/upgrade/index.js.map +1 -1
  42. package/dist/lib/plugins/openapi/CollapsibleCode.js +1 -1
  43. package/dist/lib/plugins/openapi/OperationList.js +1 -1
  44. package/dist/lib/plugins/openapi/OperationListItem.js +4 -2
  45. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  46. package/dist/lib/plugins/openapi/ParamInfos.js +1 -0
  47. package/dist/lib/plugins/openapi/ParamInfos.js.map +1 -1
  48. package/dist/lib/plugins/openapi/SchemaList.js +1 -1
  49. package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +5 -0
  50. package/dist/lib/plugins/openapi/client/useCreateQuery.js +10 -4
  51. package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
  52. package/dist/lib/plugins/openapi/components/ConstValue.d.ts +5 -0
  53. package/dist/lib/plugins/openapi/components/ConstValue.js +6 -0
  54. package/dist/lib/plugins/openapi/components/ConstValue.js.map +1 -0
  55. package/dist/lib/plugins/openapi/index.d.ts +3 -4
  56. package/dist/lib/plugins/openapi/index.js +6 -4
  57. package/dist/lib/plugins/openapi/index.js.map +1 -1
  58. package/dist/lib/plugins/openapi/interfaces.d.ts +4 -3
  59. package/dist/lib/plugins/openapi/playground/IdentitySelector.js +1 -1
  60. package/dist/lib/plugins/openapi/playground/Playground.js +2 -2
  61. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  62. package/dist/lib/plugins/openapi/playground/QueryParams.d.ts +1 -1
  63. package/dist/lib/plugins/openapi/playground/QueryParams.js +1 -1
  64. package/dist/lib/plugins/openapi/playground/SubmitButton.d.ts +1 -1
  65. package/dist/lib/plugins/openapi/playground/SubmitButton.js +1 -1
  66. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.js +1 -1
  67. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +1 -1
  68. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +1 -1
  69. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js +6 -4
  70. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js.map +1 -1
  71. package/dist/lib/plugins/openapi/schema/SchemaView.js +8 -5
  72. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
  73. package/dist/lib/plugins/openapi/util/generateSchemaExample.js +40 -0
  74. package/dist/lib/plugins/openapi/util/generateSchemaExample.js.map +1 -1
  75. package/dist/lib/plugins/openapi/util/getRoutes.js +33 -3
  76. package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
  77. package/dist/lib/ui/SyntaxHighlight.js +1 -1
  78. package/dist/lib/ui/SyntaxHighlight.js.map +1 -1
  79. package/dist/lib/util/traverse.d.ts +1 -1
  80. package/dist/lib/util/traverse.js +6 -5
  81. package/dist/lib/util/traverse.js.map +1 -1
  82. package/dist/vite/build.js +1 -0
  83. package/dist/vite/build.js.map +1 -1
  84. package/dist/vite/config.js +1 -1
  85. package/dist/vite/config.js.map +1 -1
  86. package/dist/vite/dev-server.js +5 -2
  87. package/dist/vite/dev-server.js.map +1 -1
  88. package/dist/vite/html.d.ts +6 -2
  89. package/dist/vite/html.js +11 -8
  90. package/dist/vite/html.js.map +1 -1
  91. package/dist/vite/plugin-api.js +2 -2
  92. package/dist/vite/plugin-api.js.map +1 -1
  93. package/lib/{AuthenticationPlugin-ByDF051g.js → AuthenticationPlugin-BxoEZCSJ.js} +4 -4
  94. package/lib/{AuthenticationPlugin-ByDF051g.js.map → AuthenticationPlugin-BxoEZCSJ.js.map} +1 -1
  95. package/lib/{Markdown-mFpg_n9p.js → Markdown-DvdVn1O7.js} +1839 -1836
  96. package/lib/Markdown-DvdVn1O7.js.map +1 -0
  97. package/lib/{MdxPage-Cm7atiW3.js → MdxPage-DUcuusMU.js} +7 -7
  98. package/lib/{MdxPage-Cm7atiW3.js.map → MdxPage-DUcuusMU.js.map} +1 -1
  99. package/lib/{OasProvider-CDtbrUG_.js → OasProvider-CjMm8pB7.js} +8 -9
  100. package/lib/{OasProvider-CDtbrUG_.js.map → OasProvider-CjMm8pB7.js.map} +1 -1
  101. package/lib/{OperationList-DTyJIxKW.js → OperationList-BhJcPgGi.js} +1353 -1316
  102. package/lib/{OperationList-DTyJIxKW.js.map → OperationList-BhJcPgGi.js.map} +1 -1
  103. package/lib/Pagination-BgQxwq5j.js +48 -0
  104. package/lib/Pagination-BgQxwq5j.js.map +1 -0
  105. package/lib/{RouteGuard-DRtEu9nh.js → RouteGuard-D2gX29iI.js} +3 -3
  106. package/lib/{RouteGuard-DRtEu9nh.js.map → RouteGuard-D2gX29iI.js.map} +1 -1
  107. package/lib/{SchemaList-B9lvArDe.js → SchemaList-BexhT_Z0.js} +22 -23
  108. package/lib/{SchemaList-B9lvArDe.js.map → SchemaList-BexhT_Z0.js.map} +1 -1
  109. package/lib/SchemaView-Dt_-u8rW.js +365 -0
  110. package/lib/SchemaView-Dt_-u8rW.js.map +1 -0
  111. package/lib/{Select-CT64Ou40.js → Select-CYaEBIYK.js} +3 -3
  112. package/lib/{Select-CT64Ou40.js.map → Select-CYaEBIYK.js.map} +1 -1
  113. package/lib/{SlotletProvider-CS_nO-XT.js → SlotletProvider-wWbHYqWf.js} +7 -7
  114. package/lib/SlotletProvider-wWbHYqWf.js.map +1 -0
  115. package/lib/{SyntaxHighlight-CxhyyMkF.js → SyntaxHighlight-o7q0acut.js} +81 -81
  116. package/lib/{SyntaxHighlight-CxhyyMkF.js.map → SyntaxHighlight-o7q0acut.js.map} +1 -1
  117. package/lib/{Toc-D_tV8_Ri.js → Toc-BnN4zBb3.js} +17 -17
  118. package/lib/Toc-BnN4zBb3.js.map +1 -0
  119. package/lib/{chunk-BAXFHI7N-BVBOl9s0.js → chunk-BAXFHI7N-BLTsN6tl.js} +679 -659
  120. package/lib/{chunk-BAXFHI7N-BVBOl9s0.js.map → chunk-BAXFHI7N-BLTsN6tl.js.map} +1 -1
  121. package/lib/{circular-ByJI6Mci.js → circular-BWEIet3w.js} +4616 -5567
  122. package/lib/circular-BWEIet3w.js.map +1 -0
  123. package/lib/{createServer-IW7v5hWm.js → createServer-BQD3Eeqb.js} +1998 -1970
  124. package/lib/createServer-BQD3Eeqb.js.map +1 -0
  125. package/lib/{hook-CldJlP5c.js → hook-8GM2HXNM.js} +24 -24
  126. package/lib/{hook-CldJlP5c.js.map → hook-8GM2HXNM.js.map} +1 -1
  127. package/lib/index-CFf9AN-y.js +3208 -0
  128. package/lib/index-CFf9AN-y.js.map +1 -0
  129. package/lib/{index-DnQftvP4.js → index-DGNSSXgR.js} +77 -69
  130. package/lib/{index-DnQftvP4.js.map → index-DGNSSXgR.js.map} +1 -1
  131. package/lib/{mutation-DBQh7AOZ.js → mutation-Bq5bn7Hf.js} +2 -2
  132. package/lib/{mutation-DBQh7AOZ.js.map → mutation-Bq5bn7Hf.js.map} +1 -1
  133. package/lib/processors/traverse.js +9 -7
  134. package/lib/processors/traverse.js.map +1 -1
  135. package/lib/ui/SyntaxHighlight.js +1 -1
  136. package/lib/{useExposedProps-DbIZXspi.js → useExposedProps-DmTJxEXG.js} +2 -2
  137. package/lib/{useExposedProps-DbIZXspi.js.map → useExposedProps-DmTJxEXG.js.map} +1 -1
  138. package/lib/zudoku.auth-auth0.js +1 -1
  139. package/lib/zudoku.auth-clerk.js +2 -2
  140. package/lib/zudoku.auth-openid.js +3 -3
  141. package/lib/zudoku.components.js +6 -6
  142. package/lib/zudoku.hooks.js +1 -1
  143. package/lib/zudoku.plugin-api-catalog.js +5 -5
  144. package/lib/zudoku.plugin-api-keys.js +4 -4
  145. package/lib/zudoku.plugin-custom-pages.js +2 -2
  146. package/lib/zudoku.plugin-markdown.js +1 -1
  147. package/lib/zudoku.plugin-openapi.js +6 -5
  148. package/lib/zudoku.plugin-redirect.js +1 -1
  149. package/lib/zudoku.plugin-search-pagefind.js +3 -3
  150. package/package.json +4 -4
  151. package/src/app/entry.client.tsx +26 -0
  152. package/src/app/main.css +2 -2
  153. package/src/lib/components/Banner.tsx +1 -1
  154. package/src/lib/components/Heading.tsx +3 -3
  155. package/src/lib/components/Layout.tsx +1 -4
  156. package/src/lib/components/Main.tsx +4 -3
  157. package/src/lib/components/MobileTopNavigation.tsx +2 -2
  158. package/src/lib/components/Pagination.tsx +3 -3
  159. package/src/lib/components/Search.tsx +1 -1
  160. package/src/lib/components/TopNavigation.tsx +1 -1
  161. package/src/lib/components/navigation/PoweredByZudoku.tsx +6 -1
  162. package/src/lib/components/navigation/Sidebar.tsx +1 -1
  163. package/src/lib/components/navigation/SidebarCategory.tsx +9 -5
  164. package/src/lib/components/navigation/SidebarItem.tsx +2 -3
  165. package/src/lib/components/navigation/Toc.tsx +4 -4
  166. package/src/lib/core/ZudokuContext.ts +2 -1
  167. package/src/lib/oas/parser/upgrade/index.ts +97 -27
  168. package/src/lib/plugins/openapi/CollapsibleCode.tsx +2 -2
  169. package/src/lib/plugins/openapi/OperationList.tsx +1 -1
  170. package/src/lib/plugins/openapi/OperationListItem.tsx +15 -6
  171. package/src/lib/plugins/openapi/ParamInfos.tsx +1 -0
  172. package/src/lib/plugins/openapi/SchemaList.tsx +1 -1
  173. package/src/lib/plugins/openapi/client/useCreateQuery.ts +23 -4
  174. package/src/lib/plugins/openapi/components/ConstValue.tsx +24 -0
  175. package/src/lib/plugins/openapi/index.tsx +8 -7
  176. package/src/lib/plugins/openapi/interfaces.ts +4 -3
  177. package/src/lib/plugins/openapi/playground/IdentitySelector.tsx +2 -2
  178. package/src/lib/plugins/openapi/playground/Playground.tsx +5 -5
  179. package/src/lib/plugins/openapi/playground/QueryParams.tsx +2 -2
  180. package/src/lib/plugins/openapi/playground/SubmitButton.tsx +2 -2
  181. package/src/lib/plugins/openapi/playground/result-panel/RequestTab.tsx +2 -2
  182. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +1 -1
  183. package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +1 -1
  184. package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +6 -3
  185. package/src/lib/plugins/openapi/schema/SchemaView.tsx +31 -15
  186. package/src/lib/plugins/openapi/util/generateSchemaExample.ts +45 -0
  187. package/src/lib/plugins/openapi/util/getRoutes.tsx +67 -3
  188. package/src/lib/ui/SyntaxHighlight.tsx +2 -2
  189. package/src/lib/util/traverse.ts +8 -5
  190. package/lib/Markdown-mFpg_n9p.js.map +0 -1
  191. package/lib/Pagination-DLPL5z77.js +0 -48
  192. package/lib/Pagination-DLPL5z77.js.map +0 -1
  193. package/lib/SchemaView-DXjql-Bl.js +0 -356
  194. package/lib/SchemaView-DXjql-Bl.js.map +0 -1
  195. package/lib/SlotletProvider-CS_nO-XT.js.map +0 -1
  196. package/lib/Toc-D_tV8_Ri.js.map +0 -1
  197. package/lib/circular-ByJI6Mci.js.map +0 -1
  198. package/lib/context-Lrf2Y9bR.js +0 -22
  199. package/lib/context-Lrf2Y9bR.js.map +0 -1
  200. package/lib/createServer-IW7v5hWm.js.map +0 -1
  201. package/lib/index-Ckl3s_w-.js +0 -2184
  202. package/lib/index-Ckl3s_w-.js.map +0 -1
@@ -2,11 +2,11 @@ import * as Collapsible from "@radix-ui/react-collapsible";
2
2
  import { deepEqual } from "fast-equals";
3
3
  import { ChevronRightIcon } from "lucide-react";
4
4
  import { memo, useEffect, useState } from "react";
5
- import { NavLink, useMatch } from "react-router";
5
+ import { NavLink, useLocation, useMatch } from "react-router";
6
6
  import { Button } from "zudoku/ui/Button.js";
7
7
  import type { SidebarItemCategory } from "../../../config/validators/SidebarSchema.js";
8
8
  import { cn } from "../../util/cn.js";
9
- import { joinPath } from "../../util/joinPath.js";
9
+ import { joinUrl } from "../../util/joinUrl.js";
10
10
  import { navigationListItem, SidebarItem } from "./SidebarItem.js";
11
11
  import { useIsCategoryOpen } from "./utils.js";
12
12
 
@@ -19,6 +19,7 @@ const SidebarCategoryInner = ({
19
19
  }) => {
20
20
  const isCategoryOpen = useIsCategoryOpen(category);
21
21
  const [hasInteracted, setHasInteracted] = useState(false);
22
+ const location = useLocation();
22
23
 
23
24
  const isCollapsible = category.collapsible ?? true;
24
25
  const isCollapsed = category.collapsed ?? true;
@@ -51,7 +52,7 @@ const SidebarCategoryInner = ({
51
52
  size={16}
52
53
  className={cn(
53
54
  hasInteracted && "transition",
54
- "shrink-0 group-data-[state=open]:rotate-90",
55
+ "shrink-0 group-data-[state=open]:rotate-90 rtl:rotate-180",
55
56
  )}
56
57
  />
57
58
  </Button>
@@ -83,7 +84,10 @@ const SidebarCategoryInner = ({
83
84
  <Collapsible.Trigger className="group" asChild disabled={!isCollapsible}>
84
85
  {category.link?.type === "doc" ? (
85
86
  <NavLink
86
- to={joinPath(category.link.id)}
87
+ to={{
88
+ pathname: joinUrl(category.link.id),
89
+ search: location.search,
90
+ }}
87
91
  className={styles}
88
92
  onClick={() => {
89
93
  setHasInteracted(true);
@@ -117,7 +121,7 @@ const SidebarCategoryInner = ({
117
121
  "ms-6 my-1",
118
122
  )}
119
123
  >
120
- <ul className="relative after:absolute after:-left-[--padding-nav-item] after:translate-x-[1.5px] after:top-0 after:bottom-0 after:w-px after:bg-border">
124
+ <ul className="relative after:absolute after:-start-[--padding-nav-item] after:translate-x-[1.5px] after:top-0 after:bottom-0 after:w-px after:bg-border">
121
125
  {category.items.map((item) => (
122
126
  <SidebarItem
123
127
  key={
@@ -1,6 +1,6 @@
1
1
  import { cva } from "class-variance-authority";
2
2
  import { ExternalLinkIcon } from "lucide-react";
3
- import { NavLink, useLocation, useSearchParams } from "react-router";
3
+ import { NavLink, useLocation } from "react-router";
4
4
 
5
5
  import type { SidebarItem as SidebarItemType } from "../../../config/validators/SidebarSchema.js";
6
6
  import { joinUrl } from "../../util/joinUrl.js";
@@ -43,7 +43,6 @@ export const SidebarItem = ({
43
43
  }) => {
44
44
  const location = useLocation();
45
45
  const { activeAnchor } = useViewportAnchor();
46
- const [searchParams] = useSearchParams();
47
46
 
48
47
  switch (item.type) {
49
48
  case "category":
@@ -79,7 +78,7 @@ export const SidebarItem = ({
79
78
  to={{
80
79
  pathname: item.href.split("#")[0],
81
80
  hash: item.href.split("#")[1],
82
- search: searchParams.toString(),
81
+ search: location.search,
83
82
  }}
84
83
  {...{ [DATA_ANCHOR_ATTR]: item.href.split("#")[1] }}
85
84
  className={navigationListItem({
@@ -86,10 +86,10 @@ export const Toc = ({ entries }: { entries: TocEntry[] }) => {
86
86
  On this page
87
87
  </div>
88
88
  <div className="relative ms-2 ps-4">
89
- <div className="absolute inset-0 right-auto bg-border w-[2px]" />
89
+ <div className="absolute inset-0 end-auto bg-border w-[2px]" />
90
90
  <div
91
91
  className={cn(
92
- "absolute -left-px -translate-y-1 h-6 w-[4px] rounded bg-primary",
92
+ "absolute -start-px -translate-y-1 h-6 w-[4px] rounded bg-primary",
93
93
  paintedOnce.current &&
94
94
  "ease-out [transition:top_150ms,opacity_325ms]",
95
95
  )}
@@ -104,10 +104,10 @@ export const Toc = ({ entries }: { entries: TocEntry[] }) => {
104
104
  isActive={item.id === activeAnchor}
105
105
  key={item.id}
106
106
  item={item}
107
- className="pl-0"
107
+ className="ps-0"
108
108
  >
109
109
  {item.children && (
110
- <ul className="list-none pl-4 pt-2 space-y-2">
110
+ <ul className="list-none ps-4 pt-2 space-y-2">
111
111
  {item.children.map((child) => (
112
112
  <TocItem
113
113
  item={child}
@@ -56,7 +56,8 @@ type Metadata = Partial<{
56
56
  }>;
57
57
 
58
58
  type Page = Partial<{
59
- showPoweredBy: boolean;
59
+ dir?: "ltr" | "rtl";
60
+ showPoweredBy?: boolean;
60
61
  pageTitle?: string;
61
62
  logo?: {
62
63
  src: {
@@ -10,7 +10,7 @@ import type { OpenAPIDocument } from "../index.js";
10
10
 
11
11
  export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
12
12
  if (schema.openapi?.startsWith("3.0")) {
13
- schema.openapi = "3.1.0";
13
+ schema.openapi = "3.1.1";
14
14
  }
15
15
 
16
16
  schema = traverse(schema, (sub) => {
@@ -40,26 +40,17 @@ export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
40
40
  return sub;
41
41
  });
42
42
 
43
- schema = traverse(schema, (sub) => {
44
- // may be null or undefined
45
- if (sub.example) {
46
- const isExampleObject =
47
- typeof sub.example === "object" &&
48
- (sub.example.summary !== undefined ||
49
- sub.example.description !== undefined ||
50
- sub.example.value !== undefined ||
51
- sub.example.externalValue !== undefined);
52
-
53
- const exampleValue = isExampleObject
54
- ? sub.example
55
- : { value: sub.example };
56
-
57
- if (!sub.examples) {
58
- sub.examples = { default: exampleValue };
43
+ schema = traverse(schema, (sub, path) => {
44
+ if (sub.example !== undefined) {
45
+ // Arrays in schemas
46
+ if (isSchemaPath(path ?? [])) {
47
+ sub.examples = [sub.example];
59
48
  } else {
49
+ // Objects everywhere else
60
50
  sub.examples = {
61
- default: exampleValue,
62
- ...sub.examples,
51
+ default: {
52
+ value: sub.example,
53
+ },
63
54
  };
64
55
  }
65
56
  delete sub.example;
@@ -67,17 +58,54 @@ export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
67
58
  return sub;
68
59
  });
69
60
 
70
- schema = traverse(schema, (sub) => {
71
- if (sub.type === "object" && sub.properties !== undefined) {
72
- for (const [, value] of Object.entries(sub.properties)) {
73
- const v = (value ?? {}) as RecordAny;
74
- if (v.type === "string" && v.format === "binary") {
75
- v.contentEncoding = "application/octet-stream";
76
- delete v.format;
61
+ // Multipart file uploads with a binary file
62
+ schema = traverse(schema, (schema, path) => {
63
+ if (schema.type === "object" && schema.properties !== undefined) {
64
+ // Check if this is a multipart request body schema
65
+ const parentPath = path?.slice(0, -1);
66
+ const isMultipart = parentPath?.some((segment, index) => {
67
+ return (
68
+ segment === "content" && path?.[index + 1] === "multipart/form-data"
69
+ );
70
+ });
71
+
72
+ if (isMultipart) {
73
+ // Types
74
+ const entries: [string, any][] = Object.entries(schema.properties);
75
+
76
+ for (const [, value] of entries) {
77
+ if (
78
+ typeof value === "object" &&
79
+ value.type === "string" &&
80
+ value.format === "binary"
81
+ ) {
82
+ value.contentMediaType = "application/octet-stream";
83
+ delete value.format;
84
+ }
77
85
  }
78
86
  }
79
87
  }
80
- return sub;
88
+
89
+ return schema;
90
+ });
91
+
92
+ // Uploading a binary file in a POST request
93
+ schema = traverse(schema, (schema, path) => {
94
+ if (
95
+ path?.includes("content") &&
96
+ path.includes("application/octet-stream")
97
+ ) {
98
+ return {};
99
+ }
100
+
101
+ if (schema.type === "string" && schema.format === "binary") {
102
+ return {
103
+ type: "string",
104
+ contentMediaType: "application/octet-stream",
105
+ };
106
+ }
107
+
108
+ return schema;
81
109
  });
82
110
 
83
111
  schema = traverse(schema, (sub) => {
@@ -88,6 +116,7 @@ export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
88
116
  return sub;
89
117
  });
90
118
 
119
+ // Uploading an image with base64 encoding
91
120
  schema = traverse(schema, (sub) => {
92
121
  if (sub.type === "string" && sub.format === "base64") {
93
122
  return {
@@ -99,5 +128,46 @@ export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
99
128
  return sub;
100
129
  });
101
130
 
131
+ schema = traverse(schema, (schema, path) => {
132
+ if (schema.type === "string" && schema.format === "byte") {
133
+ const parentPath = path?.slice(0, -1);
134
+ const contentMediaType = parentPath?.find(
135
+ (_, index) => path?.[index - 1] === "content",
136
+ );
137
+
138
+ return {
139
+ type: "string",
140
+ contentEncoding: "base64",
141
+ contentMediaType,
142
+ };
143
+ }
144
+
145
+ return schema;
146
+ });
147
+
102
148
  return schema as OpenAPIDocument;
103
149
  };
150
+
151
+ export function isSchemaPath(path: string[]): boolean {
152
+ const schemaLocations = [
153
+ ["components", "schemas"],
154
+ "properties",
155
+ "items",
156
+ "allOf",
157
+ "anyOf",
158
+ "oneOf",
159
+ "not",
160
+ "additionalProperties",
161
+ ];
162
+
163
+ return (
164
+ schemaLocations.some((location) => {
165
+ if (Array.isArray(location)) {
166
+ return location.every((segment, index) => path[index] === segment);
167
+ }
168
+ return path.includes(location);
169
+ }) ||
170
+ path.includes("schema") ||
171
+ path.some((segment) => segment.endsWith("Schema"))
172
+ );
173
+ }
@@ -59,7 +59,7 @@ export const CollapsibleCode = ({
59
59
  >
60
60
  <div>
61
61
  <Button variant="outline" className="hidden group-hover:flex">
62
- <UnfoldVerticalIcon size={14} className="mr-1.5" />
62
+ <UnfoldVerticalIcon size={14} className="me-1.5" />
63
63
  Click to expand
64
64
  </Button>
65
65
  </div>
@@ -73,7 +73,7 @@ export const CollapsibleCode = ({
73
73
  <CollapsibleTrigger asChild>
74
74
  <Button variant="outline" size="sm">
75
75
  Collapse
76
- <FoldVerticalIcon size={14} className="ml-1.5" />
76
+ <FoldVerticalIcon size={14} className="ms-1.5" />
77
77
  </Button>
78
78
  </CollapsibleTrigger>
79
79
  </div>
@@ -246,7 +246,7 @@ export const OperationList = ({
246
246
  >
247
247
  {schema.tag.name ?? "Other endpoints"}
248
248
  {showVersions && (
249
- <span className="text-xl text-muted-foreground ml-1.5">
249
+ <span className="text-xl text-muted-foreground ms-1.5">
250
250
  {" "}
251
251
  ({version})
252
252
  </span>
@@ -11,6 +11,7 @@ import { OperationsFragment } from "./OperationList.js";
11
11
  import { ParameterList } from "./ParameterList.js";
12
12
  import { Sidecar } from "./Sidecar.js";
13
13
  import { SelectOnClick } from "./components/SelectOnClick.js";
14
+ import { useOasConfig } from "./context.js";
14
15
  import { type FragmentType, useFragment } from "./graphql/index.js";
15
16
  import { SchemaView } from "./schema/SchemaView.js";
16
17
  import { methodForColor } from "./util/methodToColor.js";
@@ -30,6 +31,7 @@ export const OperationListItem = ({
30
31
  operation.parameters ?? [],
31
32
  (param) => param.in,
32
33
  );
34
+ const { options } = useOasConfig();
33
35
 
34
36
  const first = operation.responses.at(0);
35
37
  const [selectedResponse, setSelectedResponse] = useState(first?.statusCode);
@@ -72,7 +74,12 @@ export const OperationListItem = ({
72
74
  </SelectOnClick>
73
75
  </div>
74
76
 
75
- <div className="flex flex-col gap-4">
77
+ <div
78
+ className={cn(
79
+ "flex flex-col gap-4",
80
+ options?.disableSidecar && "col-span-full",
81
+ )}
82
+ >
76
83
  {operation.description && (
77
84
  <Markdown
78
85
  className={`${ProseClasses} max-w-full prose-img:max-w-prose`}
@@ -165,11 +172,13 @@ export const OperationListItem = ({
165
172
  )}
166
173
  </div>
167
174
 
168
- <Sidecar
169
- selectedResponse={selectedResponse}
170
- onSelectResponse={setSelectedResponse}
171
- operation={operation}
172
- />
175
+ {renderIf(!options?.disableSidecar, () => (
176
+ <Sidecar
177
+ selectedResponse={selectedResponse}
178
+ onSelectResponse={setSelectedResponse}
179
+ operation={operation}
180
+ />
181
+ ))}
173
182
  </div>
174
183
  </div>
175
184
  );
@@ -36,6 +36,7 @@ const getSchemaInfos = (schema?: SchemaObject) => {
36
36
  : schema.type,
37
37
 
38
38
  schema.enum && "enum",
39
+ schema.const && "const",
39
40
  schema.format,
40
41
  schema.minimum && `min: ${schema.minimum}`,
41
42
  schema.maximum && `max: ${schema.maximum}`,
@@ -76,7 +76,7 @@ export function SchemaList() {
76
76
  >
77
77
  Schemas
78
78
  {showVersions && (
79
- <span className="text-xl text-muted-foreground ml-1.5">
79
+ <span className="text-xl text-muted-foreground ms-1.5">
80
80
  ({version})
81
81
  </span>
82
82
  )}
@@ -1,12 +1,29 @@
1
1
  import { stripIgnoredCharacters } from "graphql";
2
2
  import { use } from "react";
3
3
  import type { TypedDocumentString } from "../graphql/graphql.js";
4
+ import type { GraphQLClient } from "./GraphQLClient.js";
4
5
  import { GraphQLContext } from "./GraphQLContext.js";
5
6
 
6
7
  type NoExtraProps<T, U extends T = T> = U & {
7
8
  [K in Exclude<keyof U, keyof T>]?: never;
8
9
  };
9
10
 
11
+ type VarArgs<TVariables> =
12
+ TVariables extends Record<string, never> ? [] : [NoExtraProps<TVariables>];
13
+
14
+ export const createQuery = <TResult, TVariables>(
15
+ client: GraphQLClient,
16
+ query: TypedDocumentString<TResult, TVariables>,
17
+ ...[variables]: TVariables extends Record<string, never>
18
+ ? []
19
+ : [NoExtraProps<TVariables>]
20
+ ) => {
21
+ return {
22
+ queryFn: () => client.fetch(query, variables),
23
+ queryKey: [stripIgnoredCharacters(query.toString()), variables],
24
+ } as const;
25
+ };
26
+
10
27
  export const useCreateQuery = <TResult, TVariables>(
11
28
  query: TypedDocumentString<TResult, TVariables>,
12
29
  ...[variables]: TVariables extends Record<string, never>
@@ -19,8 +36,10 @@ export const useCreateQuery = <TResult, TVariables>(
19
36
  throw new Error("useGraphQL must be used within a GraphQLProvider");
20
37
  }
21
38
 
22
- return {
23
- queryFn: () => graphQLClient.fetch(query, variables),
24
- queryKey: [stripIgnoredCharacters(query.toString()), variables],
25
- } as const;
39
+ const args =
40
+ variables === undefined
41
+ ? ([] as VarArgs<TVariables>)
42
+ : ([variables] as VarArgs<TVariables>);
43
+
44
+ return createQuery(graphQLClient, query, ...args);
26
45
  };
@@ -0,0 +1,24 @@
1
+ import type { SchemaObject } from "../../../oas/parser/index.js";
2
+ import { SelectOnClick } from "./SelectOnClick.js";
3
+
4
+ export const ConstValue = ({
5
+ schema,
6
+ hideDescription = false,
7
+ }: {
8
+ schema: SchemaObject;
9
+ hideDescription?: boolean;
10
+ }) => {
11
+ return (
12
+ <div className="flex flex-col gap-1 text-xs">
13
+ <div>
14
+ <span className="text-muted-foreground">Const value: </span>
15
+ <SelectOnClick className="border rounded px-1 font-mono">
16
+ {schema.const}
17
+ </SelectOnClick>
18
+ {!hideDescription && schema.description && (
19
+ <div className="text-muted-foreground">{schema.description}</div>
20
+ )}
21
+ </div>
22
+ </div>
23
+ );
24
+ };
@@ -6,7 +6,8 @@ import { type ZudokuPlugin } from "../../core/plugins.js";
6
6
  import { Button } from "../../ui/Button.js";
7
7
  import { joinUrl } from "../../util/joinUrl.js";
8
8
  import { GraphQLClient } from "./client/GraphQLClient.js";
9
- import type { GetSidebarOperationsQuery } from "./graphql/graphql.js";
9
+ import { createQuery } from "./client/useCreateQuery.js";
10
+ import type { GetSidebarOperationsQuery as GetSidebarOperationsQueryResult } from "./graphql/graphql.js";
10
11
  import { graphql } from "./graphql/index.js";
11
12
  import { type OasPluginConfig } from "./interfaces.js";
12
13
  import type { PlaygroundContentProps } from "./playground/Playground.js";
@@ -14,7 +15,7 @@ import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
14
15
  import { createSidebarCategory } from "./util/createSidebarCategory.js";
15
16
  import { getRoutes, getVersions } from "./util/getRoutes.js";
16
17
 
17
- const GetSidebarOperationsQuery = graphql(`
18
+ export const GetSidebarOperationsQuery = graphql(`
18
19
  query GetSidebarOperations($input: JSON!, $type: SchemaType!) {
19
20
  schema(input: $input, type: $type) {
20
21
  tags {
@@ -39,7 +40,7 @@ const GetSidebarOperationsQuery = graphql(`
39
40
  `);
40
41
 
41
42
  export type OperationResult =
42
- GetSidebarOperationsQuery["schema"]["tags"][number]["operations"][number];
43
+ GetSidebarOperationsQueryResult["schema"]["tags"][number]["operations"][number];
43
44
 
44
45
  export type OpenApiPluginOptions = OasPluginConfig;
45
46
 
@@ -132,11 +133,11 @@ export const openApiPlugin = (config: OasPluginConfig): ZudokuPlugin => {
132
133
  const { type } = config;
133
134
  const input = type === "file" ? config.input[version!] : config.input;
134
135
 
135
- const data = await context.queryClient.ensureQueryData({
136
- queryKey: ["sidebar-operations-query", input],
137
- queryFn: () =>
138
- client.fetch(GetSidebarOperationsQuery, { type, input }),
136
+ const query = createQuery(client, GetSidebarOperationsQuery, {
137
+ type,
138
+ input,
139
139
  });
140
+ const data = await context.queryClient.ensureQueryData(query);
140
141
 
141
142
  const categories = data.schema.tags.flatMap((tag) => {
142
143
  if (!tag.name || tag.operations.length === 0) return [];
@@ -1,7 +1,7 @@
1
- import { AuthState } from "../../authentication/state.js";
2
- import { ZudokuContext } from "../../core/ZudokuContext.js";
1
+ import { type AuthState } from "../../authentication/state.js";
2
+ import { type ZudokuContext } from "../../core/ZudokuContext.js";
3
3
  import type { SchemaImports } from "../../oas/graphql/index.js";
4
- import { OperationListItemResult } from "./OperationList.js";
4
+ import { type OperationListItemResult } from "./OperationList.js";
5
5
 
6
6
  type DynamicInput = () => Promise<unknown>;
7
7
 
@@ -49,6 +49,7 @@ type BaseOasConfig = {
49
49
  options?: {
50
50
  examplesLanguage?: string;
51
51
  disablePlayground?: boolean;
52
+ disableSidecar?: boolean;
52
53
  showVersionSelect?: "always" | "if-available" | "hide";
53
54
  expandAllTags?: boolean;
54
55
  transformExamples?: transformExamples;
@@ -29,7 +29,7 @@ const IdentitySelector = ({
29
29
  <RadioGroupItem value={NO_IDENTITY} id="none">
30
30
  None
31
31
  </RadioGroupItem>
32
- <Label htmlFor="none" className="ml-2">
32
+ <Label htmlFor="none" className="ms-2">
33
33
  None
34
34
  </Label>
35
35
  </Label>
@@ -41,7 +41,7 @@ const IdentitySelector = ({
41
41
  <RadioGroupItem value={identity.id} id={identity.id}>
42
42
  {identity.label}
43
43
  </RadioGroupItem>
44
- <Label htmlFor={identity.id} className="ml-2">
44
+ <Label htmlFor={identity.id} className="ms-2">
45
45
  {identity.label}
46
46
  </Label>
47
47
  </Label>
@@ -379,7 +379,7 @@ export const Playground = ({
379
379
  />
380
380
 
381
381
  <div className="grid grid-cols-2 text-sm h-full">
382
- <div className="flex flex-col gap-4 p-4 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
382
+ <div className="flex flex-col gap-4 p-4 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:start-auto">
383
383
  <div className="flex gap-2 items-stretch">
384
384
  <div className="flex flex-1 items-center w-full border rounded-md">
385
385
  <div className="border-r p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono flex items-center">
@@ -406,25 +406,25 @@ export const Playground = ({
406
406
  Parameters
407
407
  {(formState.pathParams.some((p) => p.value !== "") ||
408
408
  formState.queryParams.some((p) => p.active)) && (
409
- <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
409
+ <div className="w-2 h-2 rounded-full bg-blue-400 ms-2" />
410
410
  )}
411
411
  </TabsTrigger>
412
412
  <TabsTrigger value="headers">
413
413
  Headers
414
414
  {formState.headers.filter((h) => h.active).length > 0 && (
415
- <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
415
+ <div className="w-2 h-2 rounded-full bg-blue-400 ms-2" />
416
416
  )}
417
417
  </TabsTrigger>
418
418
  <TabsTrigger value="auth">
419
419
  Auth
420
420
  {formState.identity !== NO_IDENTITY && (
421
- <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
421
+ <div className="w-2 h-2 rounded-full bg-blue-400 ms-2" />
422
422
  )}
423
423
  </TabsTrigger>
424
424
  <TabsTrigger value="body">
425
425
  Body
426
426
  {formState.body && (
427
- <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
427
+ <div className="w-2 h-2 rounded-full bg-blue-400 ms-2" />
428
428
  )}
429
429
  </TabsTrigger>
430
430
  </TabsList>
@@ -1,5 +1,5 @@
1
1
  import {
2
- Control,
2
+ type Control,
3
3
  Controller,
4
4
  useFieldArray,
5
5
  useFormContext,
@@ -44,7 +44,7 @@ export const QueryParams = ({
44
44
  render={({ field }) => (
45
45
  <Checkbox
46
46
  id={`queryParams.${i}.active`}
47
- className="mr-2"
47
+ className="me-2"
48
48
  checked={field.value}
49
49
  onCheckedChange={field.onChange}
50
50
  />
@@ -9,7 +9,7 @@ import {
9
9
  DropdownMenuTrigger,
10
10
  } from "zudoku/ui/DropdownMenu.js";
11
11
  import { RadioGroup, RadioGroupItem } from "zudoku/ui/RadioGroup.js";
12
- import { ApiIdentity } from "../../../core/ZudokuContext.js";
12
+ import { type ApiIdentity } from "../../../core/ZudokuContext.js";
13
13
  import { NO_IDENTITY } from "./Playground.js";
14
14
 
15
15
  const SubmitButton = ({
@@ -59,7 +59,7 @@ const SubmitButton = ({
59
59
  onMouseEnter={() => setDropdownValue(identity.id)}
60
60
  onMouseLeave={() => setDropdownValue(undefined)}
61
61
  >
62
- <RadioGroupItem value={identity.id} className="mr-2" />
62
+ <RadioGroupItem value={identity.id} className="me-2" />
63
63
 
64
64
  {identity.label}
65
65
  </DropdownMenuItem>
@@ -36,7 +36,7 @@ export const RequestTab = ({
36
36
  <span className="font-semibold">Headers</span>
37
37
  </CollapsibleTrigger>
38
38
  <CollapsibleContent>
39
- <div className="grid grid-cols-[auto,1fr] gap-x-8 gap-y-1 pl-1.5 pt-2">
39
+ <div className="grid grid-cols-[auto,1fr] gap-x-8 gap-y-1 ps-1.5 pt-2">
40
40
  {headers.map(([key, value]) => (
41
41
  <Fragment key={key}>
42
42
  <div className="text-primary">{key}</div>
@@ -53,7 +53,7 @@ export const RequestTab = ({
53
53
  <span className="font-semibold">Body</span>
54
54
  </CollapsibleTrigger>
55
55
  <CollapsibleContent>
56
- <div className="pl-0 pt-2">
56
+ <div className="ps-0 pt-2">
57
57
  <div
58
58
  className={cn(
59
59
  "whitespace-pre-wrap break-all bg-muted p-2 rounded-md",
@@ -135,7 +135,7 @@ export const ResponseTab = ({
135
135
  <span className="font-semibold">Headers</span>
136
136
  </CollapsibleTrigger>
137
137
  <CollapsibleContent>
138
- <div className="grid grid-cols-[auto,1fr] gap-x-8 gap-y-1 pl-1.5 pt-2 font-mono text-xs">
138
+ <div className="grid grid-cols-[auto,1fr] gap-x-8 gap-y-1 ps-1.5 pt-2 font-mono text-xs">
139
139
  {sortedHeaders.slice(0, 5).map(([key, value]) => (
140
140
  <Fragment key={key}>
141
141
  <div className="text-primary whitespace-pre">{key}</div>
@@ -61,7 +61,7 @@ export const ResultPanel = ({
61
61
  Response
62
62
  <span
63
63
  className={cn(
64
- "text-xs font-mono ml-1",
64
+ "text-xs font-mono ms-1",
65
65
  status === "2" && "text-green-500",
66
66
  status === "3" && "text-blue-500",
67
67
  status === "4" && "text-yellow-500",