docusaurus-theme-openapi-docs 4.3.7 → 4.5.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 (51) hide show
  1. package/lib/theme/ApiExplorer/ApiCodeBlock/Content/String.js +3 -3
  2. package/lib/theme/ApiExplorer/ApiCodeBlock/CopyButton/index.d.ts +5 -1
  3. package/lib/theme/ApiExplorer/Authorization/slice.js +2 -2
  4. package/lib/theme/ApiExplorer/Body/slice.js +2 -2
  5. package/lib/theme/ApiExplorer/CodeSnippets/index.d.ts +3 -3
  6. package/lib/theme/ApiExplorer/FormFileUpload/_FormFileUpload.scss +4 -2
  7. package/lib/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +4 -4
  8. package/lib/theme/ApiExplorer/Request/index.js +27 -6
  9. package/lib/theme/ApiExplorer/Request/makeRequest.d.ts +1 -1
  10. package/lib/theme/ApiExplorer/Request/makeRequest.js +3 -2
  11. package/lib/theme/ApiExplorer/Response/slice.js +2 -2
  12. package/lib/theme/ApiExplorer/Server/slice.js +2 -2
  13. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +1 -1
  14. package/lib/theme/ApiExplorer/buildPostmanRequest.js +119 -46
  15. package/lib/theme/ApiExplorer/index.js +58 -2
  16. package/lib/theme/CodeSamples/_CodeSamples.scss +3 -0
  17. package/lib/theme/CodeSamples/index.d.ts +8 -0
  18. package/lib/theme/{ResponseSamples → CodeSamples}/index.js +4 -4
  19. package/lib/theme/ParamsItem/index.d.ts +1 -4
  20. package/lib/theme/ParamsItem/index.js +4 -1
  21. package/lib/theme/ResponseExamples/index.js +9 -9
  22. package/lib/theme/Schema/index.js +73 -133
  23. package/lib/theme/SchemaItem/index.js +34 -0
  24. package/lib/theme/styles.scss +1 -1
  25. package/lib/types.d.ts +5 -116
  26. package/package.json +5 -3
  27. package/src/theme/ApiExplorer/ApiCodeBlock/Content/String.tsx +3 -3
  28. package/src/theme/ApiExplorer/ApiCodeBlock/CopyButton/index.tsx +5 -1
  29. package/src/theme/ApiExplorer/Body/index.tsx +1 -2
  30. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +3 -3
  31. package/src/theme/ApiExplorer/FormFileUpload/_FormFileUpload.scss +4 -2
  32. package/src/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +4 -4
  33. package/src/theme/ApiExplorer/Request/index.tsx +28 -5
  34. package/src/theme/ApiExplorer/Request/makeRequest.ts +4 -3
  35. package/src/theme/ApiExplorer/buildPostmanRequest.ts +48 -18
  36. package/src/theme/ApiExplorer/index.tsx +4 -2
  37. package/src/theme/CodeSamples/_CodeSamples.scss +3 -0
  38. package/src/theme/{ResponseSamples → CodeSamples}/index.tsx +5 -10
  39. package/src/theme/ParamsItem/index.tsx +7 -6
  40. package/src/theme/ResponseExamples/index.tsx +6 -9
  41. package/src/theme/Schema/index.tsx +69 -96
  42. package/src/theme/SchemaItem/index.tsx +27 -0
  43. package/src/theme/styles.scss +1 -1
  44. package/src/types.ts +5 -115
  45. package/tsconfig.tsbuildinfo +1 -1
  46. package/lib/theme/ResponseSamples/_ResponseSamples.scss +0 -3
  47. package/lib/theme/ResponseSamples/index.d.ts +0 -8
  48. package/src/theme/ApiExplorer/postman-collection.d.ts +0 -10
  49. package/src/theme/ApiExplorer/react-modal.d.ts +0 -8
  50. package/src/theme/ResponseSamples/_ResponseSamples.scss +0 -3
  51. package/src/theme-translations.d.ts +0 -9
@@ -13,7 +13,8 @@
13
13
 
14
14
  &:hover {
15
15
  border: 2px dashed var(--ifm-color-primary);
16
- background: linear-gradient(
16
+ background:
17
+ linear-gradient(
17
18
  var(--openapi-dropzone-hover-shim),
18
19
  var(--openapi-dropzone-hover-shim)
19
20
  ),
@@ -38,7 +39,8 @@
38
39
  font-size: var(--ifm-code-font-size);
39
40
  border: 2px dashed var(--ifm-color-primary);
40
41
 
41
- background: linear-gradient(
42
+ background:
43
+ linear-gradient(
42
44
  var(--openapi-dropzone-hover-shim),
43
45
  var(--openapi-dropzone-hover-shim)
44
46
  ),
@@ -45,8 +45,8 @@
45
45
  line-height: 1.5;
46
46
 
47
47
  transition-property: color, background, border-color, box-shadow;
48
- transition-duration: 100ms, 100ms, 100ms,
49
- var(--ifm-button-transition-duration);
48
+ transition-duration:
49
+ 100ms, 100ms, 100ms, var(--ifm-button-transition-duration);
50
50
  transition-timing-function: cubic-bezier(0.08, 0.52, 0.52, 1);
51
51
 
52
52
  -webkit-user-select: none;
@@ -85,8 +85,8 @@
85
85
  padding: 0.5rem 1rem;
86
86
  font-size: 12px;
87
87
  transition-property: color, background, border-color, box-shadow;
88
- transition-duration: 100ms, 100ms, 100ms,
89
- var(--ifm-button-transition-duration);
88
+ transition-duration:
89
+ 100ms, 100ms, 100ms, var(--ifm-button-transition-duration);
90
90
  transition-timing-function: cubic-bezier(0.08, 0.52, 0.52, 1);
91
91
  user-select: none;
92
92
  white-space: nowrap;
@@ -26,7 +26,7 @@ import Server from "@theme/ApiExplorer/Server";
26
26
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
27
27
  import { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
28
28
  import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
29
- import sdk from "postman-collection";
29
+ import * as sdk from "postman-collection";
30
30
  import { FormProvider, useForm } from "react-hook-form";
31
31
 
32
32
  import makeRequest from "./makeRequest";
@@ -95,15 +95,38 @@ function Request({ item }: { item: ApiItem }) {
95
95
 
96
96
  const methods = useForm({ shouldFocusError: false });
97
97
 
98
+ const handleEventStream = async (res) => {
99
+ res.headers && dispatch(setHeaders(Object.fromEntries(res.headers)));
100
+ dispatch(setCode(res.status));
101
+
102
+ const reader = res.body.getReader();
103
+ const decoder = new TextDecoder();
104
+ let result = "";
105
+ while (true) {
106
+ const { done, value } = await reader.read();
107
+ if (done) break;
108
+ result += decoder.decode(value, { stream: true });
109
+ dispatch(setResponse(result));
110
+ }
111
+ };
112
+
113
+ const handleResponse = async (res) => {
114
+ dispatch(setResponse(await res.text()));
115
+ dispatch(setCode(res.status));
116
+ res.headers && dispatch(setHeaders(Object.fromEntries(res.headers)));
117
+ };
118
+
98
119
  const onSubmit = async (data) => {
99
120
  dispatch(setResponse("Fetching..."));
100
121
  try {
101
122
  await delay(1200);
102
123
  const res = await makeRequest(postmanRequest, proxy, body);
103
- dispatch(setResponse(await res.text()));
104
- dispatch(setCode(res.status));
105
- res.headers && dispatch(setHeaders(Object.fromEntries(res.headers)));
106
- } catch (e: any) {
124
+ if (res.headers.get("content-type")?.includes("text/event-stream")) {
125
+ await handleEventStream(res);
126
+ } else {
127
+ await handleResponse(res);
128
+ }
129
+ } catch (e) {
107
130
  console.log(e);
108
131
  dispatch(setResponse("Connection failed"));
109
132
  dispatch(clearCode());
@@ -6,7 +6,7 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  import { Body } from "@theme/ApiExplorer/Body/slice";
9
- import sdk from "postman-collection";
9
+ import * as sdk from "postman-collection";
10
10
 
11
11
  function fetchWithtimeout(
12
12
  url: string,
@@ -156,8 +156,9 @@ async function makeRequest(
156
156
  myHeaders.delete("Content-Type");
157
157
 
158
158
  myBody = new FormData();
159
- if (Array.isArray(request.body.formdata.members)) {
160
- for (const data of request.body.formdata.members) {
159
+ const members = (request.body as any)?.formdata?.members;
160
+ if (Array.isArray(members)) {
161
+ for (const data of members) {
161
162
  if (data.key && data.value.content) {
162
163
  myBody.append(data.key, data.value.content);
163
164
  }
@@ -12,7 +12,7 @@ import {
12
12
  ServerObject,
13
13
  } from "docusaurus-plugin-openapi-docs/src/openapi/types";
14
14
  import cloneDeep from "lodash/cloneDeep";
15
- import sdk from "postman-collection";
15
+ import * as sdk from "postman-collection";
16
16
 
17
17
  type Param = {
18
18
  value?: string | string[];
@@ -73,7 +73,7 @@ function setQueryParams(postman: sdk.Request, queryParams: Param[]) {
73
73
  ([key, val]) =>
74
74
  new sdk.QueryParam({
75
75
  key: `${param.name}[${key}]`,
76
- value: val,
76
+ value: String(val),
77
77
  })
78
78
  );
79
79
  } else if (param.explode) {
@@ -81,7 +81,7 @@ function setQueryParams(postman: sdk.Request, queryParams: Param[]) {
81
81
  ([key, val]) =>
82
82
  new sdk.QueryParam({
83
83
  key: key,
84
- value: val,
84
+ value: String(val),
85
85
  })
86
86
  );
87
87
  } else {
@@ -181,7 +181,10 @@ function setPathParams(postman: sdk.Request, pathParams: Param[]) {
181
181
  });
182
182
  });
183
183
 
184
- postman.url.variables.assimilate(source, false);
184
+ postman.url.variables.assimilate(
185
+ source.filter((v): v is sdk.Variable => v !== undefined),
186
+ false
187
+ );
185
188
  }
186
189
 
187
190
  function buildCookie(cookieParams: Param[]) {
@@ -207,7 +210,9 @@ function buildCookie(cookieParams: Param[]) {
207
210
  ([key, val]) =>
208
211
  new sdk.Cookie({
209
212
  key: key,
210
- value: val,
213
+ value: String(val),
214
+ domain: "",
215
+ path: "",
211
216
  })
212
217
  );
213
218
  } else {
@@ -217,6 +222,8 @@ function buildCookie(cookieParams: Param[]) {
217
222
  value: Object.entries(jsonResult)
218
223
  .map(([key, val]) => `${key},${val}`)
219
224
  .join(","),
225
+ domain: "",
226
+ path: "",
220
227
  });
221
228
  }
222
229
  }
@@ -224,7 +231,9 @@ function buildCookie(cookieParams: Param[]) {
224
231
  // Handle scalar values
225
232
  return new sdk.Cookie({
226
233
  key: param.name,
227
- value: param.value,
234
+ value: String(param.value),
235
+ domain: "",
236
+ path: "",
228
237
  });
229
238
  }
230
239
  }
@@ -430,10 +439,9 @@ function buildPostmanRequest(
430
439
  clonedPostman.url.host = [url];
431
440
  }
432
441
 
433
- setQueryParams(clonedPostman, queryParams);
434
- setPathParams(clonedPostman, pathParams);
442
+ const enhancedQueryParams = [...queryParams];
443
+ const enhancedCookieParams = [...cookieParams];
435
444
 
436
- const cookie = buildCookie(cookieParams);
437
445
  let otherHeaders = [];
438
446
 
439
447
  let selectedAuth: Scheme[] = [];
@@ -491,24 +499,46 @@ function buildPostmanRequest(
491
499
  continue;
492
500
  }
493
501
 
494
- // API Key
502
+ // API Key in header
495
503
  if (a.type === "apiKey" && a.in === "header") {
496
504
  const { apiKey } = auth.data[a.key];
497
- if (apiKey === undefined) {
498
- otherHeaders.push({
499
- key: a.name,
500
- value: `<${a.name ?? a.type}>`,
501
- });
502
- continue;
503
- }
504
505
  otherHeaders.push({
505
506
  key: a.name,
506
- value: apiKey,
507
+ value: apiKey || `<${a.name ?? a.type}>`,
508
+ });
509
+ continue;
510
+ }
511
+
512
+ // API Key in query
513
+ if (a.type === "apiKey" && a.in === "query") {
514
+ const { apiKey } = auth.data[a.key];
515
+ enhancedQueryParams.push({
516
+ name: a.name,
517
+ in: "query",
518
+ value: apiKey || `<${a.name ?? a.type}>`,
519
+ });
520
+ continue;
521
+ }
522
+
523
+ // API Key in cookie
524
+ if (a.type === "apiKey" && a.in === "cookie") {
525
+ const { apiKey } = auth.data[a.key];
526
+ enhancedCookieParams.push({
527
+ name: a.name,
528
+ in: "cookie",
529
+ value: apiKey || `<${a.name ?? a.type}>`,
507
530
  });
508
531
  continue;
509
532
  }
510
533
  }
511
534
 
535
+ // Use the enhanced params that might include API keys
536
+ setQueryParams(clonedPostman, enhancedQueryParams);
537
+ setPathParams(clonedPostman, pathParams);
538
+
539
+ // Use enhanced cookie params that might include API keys
540
+ const cookie = buildCookie(enhancedCookieParams);
541
+
512
542
  setHeaders(
513
543
  clonedPostman,
514
544
  contentType,
@@ -12,7 +12,7 @@ import Request from "@theme/ApiExplorer/Request";
12
12
  import Response from "@theme/ApiExplorer/Response";
13
13
  import SecuritySchemes from "@theme/ApiExplorer/SecuritySchemes";
14
14
  import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
15
- import sdk from "postman-collection";
15
+ import * as sdk from "postman-collection";
16
16
 
17
17
  function ApiExplorer({
18
18
  item,
@@ -21,7 +21,9 @@ function ApiExplorer({
21
21
  item: NonNullable<ApiItem>;
22
22
  infoPath: string;
23
23
  }) {
24
- const postman = new sdk.Request(item.postman);
24
+ const postman = new sdk.Request(
25
+ item.postman ? (item.postman as any).toJSON() : {}
26
+ );
25
27
 
26
28
  return (
27
29
  <>
@@ -0,0 +1,3 @@
1
+ .openapi-code__code-samples-container {
2
+ margin-top: 2rem;
3
+ }
@@ -11,21 +11,16 @@ import CodeBlock from "@theme/CodeBlock";
11
11
  import { Language } from "prism-react-renderer";
12
12
 
13
13
  export interface Props {
14
- readonly responseExample: string;
14
+ readonly example: string;
15
15
  readonly language: Language;
16
16
  }
17
17
 
18
- function ResponseSamples({
19
- responseExample,
20
- language,
21
- }: Props): React.JSX.Element {
18
+ function CodeSamples({ example, language }: Props): React.JSX.Element {
22
19
  return (
23
- <div className="openapi-code__response-samples-container">
24
- <CodeBlock language={language ? language : "json"}>
25
- {responseExample}
26
- </CodeBlock>
20
+ <div className="openapi-code__code-samples-container">
21
+ <CodeBlock language={language ? language : "json"}>{example}</CodeBlock>
27
22
  </div>
28
23
  );
29
24
  }
30
25
 
31
- export default ResponseSamples;
26
+ export default CodeSamples;
@@ -16,10 +16,6 @@ import clsx from "clsx";
16
16
  import { getQualifierMessage, getSchemaName } from "../../markdown/schema";
17
17
  import { guard, toString } from "../../markdown/utils";
18
18
 
19
- interface Map<T> {
20
- [key: string]: T;
21
- }
22
-
23
19
  export interface ExampleObject {
24
20
  summary?: string;
25
21
  description?: string;
@@ -32,7 +28,7 @@ export interface Props {
32
28
  param: {
33
29
  description: string;
34
30
  example: any;
35
- examples: Map<ExampleObject>;
31
+ examples: Record<string, ExampleObject>;
36
32
  name: string;
37
33
  required: boolean;
38
34
  deprecated: boolean;
@@ -70,9 +66,14 @@ function ParamsItem({ param, ...rest }: Props) {
70
66
  let schema = param.schema;
71
67
  let defaultValue: string | undefined;
72
68
 
73
- if (!schema || !schema?.type) {
69
+ if (!schema) {
74
70
  schema = { type: "any" };
75
71
  }
72
+
73
+ if (!schema.type) {
74
+ schema.type = "any";
75
+ }
76
+
76
77
  if (schema) {
77
78
  if (schema.items) {
78
79
  defaultValue = schema.items.default;
@@ -7,8 +7,8 @@
7
7
 
8
8
  import React from "react";
9
9
 
10
+ import CodeSamples from "@theme/CodeSamples";
10
11
  import Markdown from "@theme/Markdown";
11
- import ResponseSamples from "@theme/ResponseSamples";
12
12
  import TabItem from "@theme/TabItem";
13
13
  import { sampleResponseFromSchema } from "docusaurus-plugin-openapi-docs/lib/openapi/createResponseExample";
14
14
  import format from "xml-formatter";
@@ -78,10 +78,7 @@ export const ResponseExamples: React.FC<ResponseExamplesProps> = ({
78
78
  {exampleValue.summary}
79
79
  </Markdown>
80
80
  )}
81
- <ResponseSamples
82
- responseExample={responseExample}
83
- language={language}
84
- />
81
+ <CodeSamples example={responseExample} language={language} />
85
82
  </TabItem>
86
83
  );
87
84
  }
@@ -120,7 +117,7 @@ export const ResponseExample: React.FC<ResponseExampleProps> = ({
120
117
  {responseExample.summary}
121
118
  </Markdown>
122
119
  )}
123
- <ResponseSamples responseExample={exampleContent} language={language} />
120
+ <CodeSamples example={exampleContent} language={language} />
124
121
  </TabItem>
125
122
  );
126
123
  };
@@ -167,7 +164,7 @@ export const ExampleFromSchema: React.FC<ExampleFromSchemaProps> = ({
167
164
  return (
168
165
  // @ts-ignore
169
166
  <TabItem label="Example (auto)" value="Example (auto)">
170
- <ResponseSamples responseExample={xmlExample} language="xml" />
167
+ <CodeSamples example={xmlExample} language="xml" />
171
168
  </TabItem>
172
169
  );
173
170
  }
@@ -180,8 +177,8 @@ export const ExampleFromSchema: React.FC<ExampleFromSchemaProps> = ({
180
177
  return (
181
178
  // @ts-ignore
182
179
  <TabItem label="Example (auto)" value="Example (auto)">
183
- <ResponseSamples
184
- responseExample={JSON.stringify(responseExample, null, 2)}
180
+ <CodeSamples
181
+ example={JSON.stringify(responseExample, null, 2)}
185
182
  language="json"
186
183
  />
187
184
  </TabItem>
@@ -21,7 +21,10 @@ import {
21
21
  getQualifierMessage,
22
22
  getSchemaName,
23
23
  } from "docusaurus-plugin-openapi-docs/lib/markdown/schema";
24
- import { SchemaObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
24
+ import {
25
+ SchemaObject,
26
+ SchemaType,
27
+ } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
25
28
  import isEmpty from "lodash/isEmpty";
26
29
 
27
30
  // eslint-disable-next-line import/no-extraneous-dependencies
@@ -113,7 +116,7 @@ const AnyOneOf: React.FC<SchemaProps> = ({ schema, schemaType }) => {
113
116
  </span>
114
117
  <SchemaTabs>
115
118
  {schema[type]?.map((anyOneSchema: any, index: number) => {
116
- const label = anyOneSchema.title || `MOD${index + 1}`;
119
+ const label = anyOneSchema.title || anyOneSchema.type;
117
120
  return (
118
121
  // @ts-ignore
119
122
  <TabItem
@@ -122,9 +125,7 @@ const AnyOneOf: React.FC<SchemaProps> = ({ schema, schemaType }) => {
122
125
  value={`${index}-item-properties`}
123
126
  >
124
127
  {/* Handle primitive types directly */}
125
- {["string", "number", "integer", "boolean"].includes(
126
- anyOneSchema.type
127
- ) && (
128
+ {(isPrimitive(anyOneSchema) || anyOneSchema.const) && (
128
129
  <SchemaItem
129
130
  collapsible={false}
130
131
  name={undefined}
@@ -352,7 +353,8 @@ const DiscriminatorNode: React.FC<DiscriminatorNodeProps> = ({
352
353
  }
353
354
 
354
355
  const subProperties = subSchema.properties || mergedSubSchema.properties;
355
- if (subProperties[discriminator.propertyName]) {
356
+ // Add a safeguard check to avoid referencing subProperties if it's undefined
357
+ if (subProperties && subProperties[discriminator.propertyName]) {
356
358
  if (schema.properties) {
357
359
  schema.properties![discriminator.propertyName] = {
358
360
  ...schema.properties![discriminator.propertyName],
@@ -502,88 +504,42 @@ const Items: React.FC<{
502
504
  schema: any;
503
505
  schemaType: "request" | "response";
504
506
  }> = ({ schema, schemaType }) => {
505
- // Handles case when schema.items has properties
506
- if (schema.items?.properties) {
507
- return (
508
- <>
509
- <OpeningArrayBracket />
510
- <Properties schema={schema.items} schemaType={schemaType} />
511
- <ClosingArrayBracket />
512
- </>
513
- );
507
+ // Process schema.items to handle allOf merging
508
+ let itemsSchema = schema.items;
509
+ if (schema.items?.allOf) {
510
+ itemsSchema = mergeAllOf(schema.items) as SchemaObject;
514
511
  }
515
512
 
516
- // Handles case when schema.items has additionalProperties
517
- if (schema.items?.additionalProperties) {
518
- return (
519
- <>
520
- <OpeningArrayBracket />
521
- <AdditionalProperties schema={schema.items} schemaType={schemaType} />
522
- <ClosingArrayBracket />
523
- </>
524
- );
525
- }
513
+ // Handle complex schemas with multiple schema types
514
+ const hasOneOfAnyOf = itemsSchema?.oneOf || itemsSchema?.anyOf;
515
+ const hasProperties = itemsSchema?.properties;
516
+ const hasAdditionalProperties = itemsSchema?.additionalProperties;
526
517
 
527
- // Handles case when schema.items has oneOf or anyOf
528
- if (schema.items?.oneOf || schema.items?.anyOf) {
518
+ if (hasOneOfAnyOf || hasProperties || hasAdditionalProperties) {
529
519
  return (
530
520
  <>
531
521
  <OpeningArrayBracket />
532
- <AnyOneOf schema={schema.items} schemaType={schemaType} />
522
+ {hasOneOfAnyOf && (
523
+ <AnyOneOf schema={itemsSchema} schemaType={schemaType} />
524
+ )}
525
+ {hasProperties && (
526
+ <Properties schema={itemsSchema} schemaType={schemaType} />
527
+ )}
528
+ {hasAdditionalProperties && (
529
+ <AdditionalProperties schema={itemsSchema} schemaType={schemaType} />
530
+ )}
533
531
  <ClosingArrayBracket />
534
532
  </>
535
533
  );
536
534
  }
537
535
 
538
- // Handles case when schema.items has allOf
539
- if (schema.items?.allOf) {
540
- const mergedSchemas = mergeAllOf(schema.items) as SchemaObject;
541
-
542
- // Handles combo anyOf/oneOf + properties
543
- if (
544
- (mergedSchemas.oneOf || mergedSchemas.anyOf) &&
545
- mergedSchemas.properties
546
- ) {
547
- return (
548
- <>
549
- <OpeningArrayBracket />
550
- <AnyOneOf schema={mergedSchemas} schemaType={schemaType} />
551
- <Properties schema={mergedSchemas} schemaType={schemaType} />
552
- <ClosingArrayBracket />
553
- </>
554
- );
555
- }
556
-
557
- // Handles only anyOf/oneOf
558
- if (mergedSchemas.oneOf || mergedSchemas.anyOf) {
559
- return (
560
- <>
561
- <OpeningArrayBracket />
562
- <AnyOneOf schema={mergedSchemas} schemaType={schemaType} />
563
- <ClosingArrayBracket />
564
- </>
565
- );
566
- }
567
-
568
- // Handles properties
569
- if (mergedSchemas.properties) {
570
- return (
571
- <>
572
- <OpeningArrayBracket />
573
- <Properties schema={mergedSchemas} schemaType={schemaType} />
574
- <ClosingArrayBracket />
575
- </>
576
- );
577
- }
578
- }
579
-
580
536
  // Handles basic types (string, number, integer, boolean, object)
581
537
  if (
582
- schema.items?.type === "string" ||
583
- schema.items?.type === "number" ||
584
- schema.items?.type === "integer" ||
585
- schema.items?.type === "boolean" ||
586
- schema.items?.type === "object"
538
+ itemsSchema?.type === "string" ||
539
+ itemsSchema?.type === "number" ||
540
+ itemsSchema?.type === "integer" ||
541
+ itemsSchema?.type === "boolean" ||
542
+ itemsSchema?.type === "object"
587
543
  ) {
588
544
  return (
589
545
  <div style={{ marginLeft: ".5rem" }}>
@@ -591,9 +547,9 @@ const Items: React.FC<{
591
547
  <SchemaItem
592
548
  collapsible={false}
593
549
  name="" // No name for array items
594
- schemaName={getSchemaName(schema.items)}
595
- qualifierMessage={getQualifierMessage(schema.items)}
596
- schema={schema.items}
550
+ schemaName={getSchemaName(itemsSchema)}
551
+ qualifierMessage={getQualifierMessage(itemsSchema)}
552
+ schema={itemsSchema}
597
553
  discriminator={false}
598
554
  children={null}
599
555
  />
@@ -606,7 +562,7 @@ const Items: React.FC<{
606
562
  return (
607
563
  <>
608
564
  <OpeningArrayBracket />
609
- {Object.entries(schema.items || {}).map(([key, val]: [string, any]) => (
565
+ {Object.entries(itemsSchema || {}).map(([key, val]: [string, any]) => (
610
566
  <SchemaEdge
611
567
  key={key}
612
568
  name={key}
@@ -840,6 +796,25 @@ const SchemaEdge: React.FC<SchemaEdgeProps> = ({
840
796
  );
841
797
  };
842
798
 
799
+ function renderChildren(
800
+ schema: SchemaObject,
801
+ schemaType: "request" | "response"
802
+ ) {
803
+ return (
804
+ <>
805
+ {schema.oneOf && <AnyOneOf schema={schema} schemaType={schemaType} />}
806
+ {schema.anyOf && <AnyOneOf schema={schema} schemaType={schemaType} />}
807
+ {schema.properties && (
808
+ <Properties schema={schema} schemaType={schemaType} />
809
+ )}
810
+ {schema.additionalProperties && (
811
+ <AdditionalProperties schema={schema} schemaType={schemaType} />
812
+ )}
813
+ {schema.items && <Items schema={schema} schemaType={schemaType} />}
814
+ </>
815
+ );
816
+ }
817
+
843
818
  const SchemaNode: React.FC<SchemaProps> = ({ schema, schemaType }) => {
844
819
  if (
845
820
  (schemaType === "request" && schema.readOnly) ||
@@ -888,10 +863,6 @@ const SchemaNode: React.FC<SchemaProps> = ({ schema, schemaType }) => {
888
863
  );
889
864
  }
890
865
 
891
- if (schema.oneOf || schema.anyOf) {
892
- return <AnyOneOf schema={schema} schemaType={schemaType} />;
893
- }
894
-
895
866
  // Handle primitives
896
867
  if (
897
868
  schema.type &&
@@ -917,19 +888,21 @@ const SchemaNode: React.FC<SchemaProps> = ({ schema, schemaType }) => {
917
888
  );
918
889
  }
919
890
 
920
- return (
921
- <div>
922
- {schema.oneOf && <AnyOneOf schema={schema} schemaType={schemaType} />}
923
- {schema.anyOf && <AnyOneOf schema={schema} schemaType={schemaType} />}
924
- {schema.properties && (
925
- <Properties schema={schema} schemaType={schemaType} />
926
- )}
927
- {schema.additionalProperties && (
928
- <AdditionalProperties schema={schema} schemaType={schemaType} />
929
- )}
930
- {schema.items && <Items schema={schema} schemaType={schemaType} />}
931
- </div>
932
- );
891
+ return renderChildren(schema, schemaType);
933
892
  };
934
893
 
935
894
  export default SchemaNode;
895
+
896
+ type PrimitiveSchemaType = Exclude<SchemaType, "object" | "array">;
897
+
898
+ const PRIMITIVE_TYPES: Record<PrimitiveSchemaType, true> = {
899
+ string: true,
900
+ number: true,
901
+ integer: true,
902
+ boolean: true,
903
+ null: true,
904
+ } as const;
905
+
906
+ const isPrimitive = (schema: SchemaObject) => {
907
+ return PRIMITIVE_TYPES[schema.type as PrimitiveSchemaType];
908
+ };