docusaurus-theme-openapi-docs 5.0.2 → 5.1.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 (143) hide show
  1. package/lib/markdown/schema.js +38 -15
  2. package/lib/markdown/schema.test.d.ts +1 -0
  3. package/lib/markdown/schema.test.js +86 -0
  4. package/lib/theme/ApiExplorer/ApiCodeBlock/Container/index.js +4 -2
  5. package/lib/theme/ApiExplorer/ApiCodeBlock/Content/String.js +9 -6
  6. package/lib/theme/ApiExplorer/ApiCodeBlock/Line/index.d.ts +1 -1
  7. package/lib/theme/ApiExplorer/ApiCodeBlock/index.d.ts +1 -1
  8. package/lib/theme/ApiExplorer/Authorization/index.js +9 -10
  9. package/lib/theme/ApiExplorer/Body/index.js +4 -5
  10. package/lib/theme/ApiExplorer/CodeSnippets/index.js +96 -61
  11. package/lib/theme/ApiExplorer/CodeSnippets/languages.js +12 -1
  12. package/lib/theme/ApiExplorer/CodeSnippets/languages.test.d.ts +1 -0
  13. package/lib/theme/ApiExplorer/CodeSnippets/languages.test.js +102 -0
  14. package/lib/theme/ApiExplorer/CodeTabs/index.d.ts +1 -1
  15. package/lib/theme/ApiExplorer/CodeTabs/index.js +6 -5
  16. package/lib/theme/ApiExplorer/Export/index.js +9 -2
  17. package/lib/theme/ApiExplorer/FormFileUpload/index.js +1 -2
  18. package/lib/theme/ApiExplorer/FormLabel/index.js +1 -2
  19. package/lib/theme/ApiExplorer/FormTextInput/index.js +1 -2
  20. package/lib/theme/ApiExplorer/LiveEditor/index.js +1 -2
  21. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.js +5 -3
  22. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.js +75 -4
  23. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.js +1 -2
  24. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.js +67 -4
  25. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.js +65 -1
  26. package/lib/theme/ApiExplorer/ParamOptions/index.js +2 -3
  27. package/lib/theme/ApiExplorer/Request/index.js +17 -18
  28. package/lib/theme/ApiExplorer/Response/index.js +54 -12
  29. package/lib/theme/ApiExplorer/SecuritySchemes/index.js +57 -50
  30. package/lib/theme/ApiExplorer/Server/index.js +2 -3
  31. package/lib/theme/ApiItem/index.js +59 -33
  32. package/lib/theme/ApiTabs/index.d.ts +1 -1
  33. package/lib/theme/ApiTabs/index.js +7 -7
  34. package/lib/theme/DiscriminatorTabs/index.d.ts +1 -1
  35. package/lib/theme/DiscriminatorTabs/index.js +6 -5
  36. package/lib/theme/Example/index.js +3 -4
  37. package/lib/theme/MimeTabs/index.d.ts +1 -1
  38. package/lib/theme/MimeTabs/index.js +6 -5
  39. package/lib/theme/OperationTabs/index.d.ts +1 -1
  40. package/lib/theme/OperationTabs/index.js +6 -5
  41. package/lib/theme/ParamsDetails/index.js +1 -2
  42. package/lib/theme/ParamsItem/index.js +7 -8
  43. package/lib/theme/RequestSchema/index.js +57 -57
  44. package/lib/theme/ResponseExamples/index.js +3 -4
  45. package/lib/theme/ResponseSchema/index.js +26 -24
  46. package/lib/theme/Schema/index.js +148 -110
  47. package/lib/theme/Schema/normalize.d.ts +41 -0
  48. package/lib/theme/Schema/normalize.js +210 -0
  49. package/lib/theme/Schema/normalize.test.d.ts +1 -0
  50. package/lib/theme/Schema/normalize.test.js +271 -0
  51. package/lib/theme/SchemaExpansion/_SchemaExpansion.scss +113 -0
  52. package/lib/theme/SchemaExpansion/context.d.ts +24 -0
  53. package/lib/theme/SchemaExpansion/context.js +187 -0
  54. package/lib/theme/SchemaExpansion/index.d.ts +4 -0
  55. package/lib/theme/SchemaExpansion/index.js +314 -0
  56. package/lib/theme/SchemaItem/index.js +9 -10
  57. package/lib/theme/SchemaTabs/index.d.ts +1 -1
  58. package/lib/theme/SchemaTabs/index.js +6 -5
  59. package/lib/theme/StatusCodes/index.js +2 -4
  60. package/lib/theme/TabItem/index.d.ts +5 -0
  61. package/lib/theme/TabItem/index.js +51 -0
  62. package/lib/theme/TabItem/styles.module.css +3 -0
  63. package/lib/theme/Tabs/index.d.ts +5 -0
  64. package/lib/theme/Tabs/index.js +148 -0
  65. package/lib/theme/Tabs/styles.module.css +7 -0
  66. package/lib/theme/styles.scss +1 -0
  67. package/lib/theme/translationIds.d.ts +1 -93
  68. package/lib/theme/translationIds.js +0 -109
  69. package/lib/theme/utils/codeBlockUtils.d.ts +28 -0
  70. package/lib/theme/utils/codeBlockUtils.js +223 -0
  71. package/lib/theme/utils/reactUtils.d.ts +1 -0
  72. package/lib/theme/utils/reactUtils.js +23 -0
  73. package/lib/theme/utils/scrollUtils.d.ts +7 -0
  74. package/lib/theme/utils/scrollUtils.js +175 -0
  75. package/lib/theme/utils/tabsUtils.d.ts +47 -0
  76. package/lib/theme/utils/tabsUtils.js +299 -0
  77. package/lib/theme/utils/useCodeWordWrap.d.ts +8 -0
  78. package/lib/theme/utils/useCodeWordWrap.js +84 -0
  79. package/lib/theme/utils/useMutationObserver.d.ts +3 -0
  80. package/lib/theme/utils/useMutationObserver.js +34 -0
  81. package/package.json +4 -4
  82. package/src/markdown/schema.test.ts +102 -0
  83. package/src/markdown/schema.ts +42 -15
  84. package/src/theme/ApiExplorer/ApiCodeBlock/Container/index.tsx +2 -1
  85. package/src/theme/ApiExplorer/ApiCodeBlock/Content/String.tsx +8 -7
  86. package/src/theme/ApiExplorer/ApiCodeBlock/Line/index.tsx +1 -1
  87. package/src/theme/ApiExplorer/ApiCodeBlock/index.tsx +1 -1
  88. package/src/theme/ApiExplorer/Authorization/index.tsx +9 -10
  89. package/src/theme/ApiExplorer/Body/index.tsx +7 -5
  90. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +103 -59
  91. package/src/theme/ApiExplorer/CodeSnippets/languages.test.ts +109 -0
  92. package/src/theme/ApiExplorer/CodeSnippets/languages.ts +13 -1
  93. package/src/theme/ApiExplorer/CodeTabs/index.tsx +5 -5
  94. package/src/theme/ApiExplorer/Export/index.tsx +6 -2
  95. package/src/theme/ApiExplorer/FormFileUpload/index.tsx +1 -2
  96. package/src/theme/ApiExplorer/FormLabel/index.tsx +1 -2
  97. package/src/theme/ApiExplorer/FormTextInput/index.tsx +1 -2
  98. package/src/theme/ApiExplorer/LiveEditor/index.tsx +1 -2
  99. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx +5 -3
  100. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx +20 -4
  101. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx +1 -2
  102. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx +15 -4
  103. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx +11 -1
  104. package/src/theme/ApiExplorer/ParamOptions/index.tsx +2 -3
  105. package/src/theme/ApiExplorer/Request/index.tsx +23 -18
  106. package/src/theme/ApiExplorer/Response/index.tsx +63 -9
  107. package/src/theme/ApiExplorer/SecuritySchemes/index.tsx +60 -52
  108. package/src/theme/ApiExplorer/Server/index.tsx +8 -3
  109. package/src/theme/ApiItem/index.tsx +43 -21
  110. package/src/theme/ApiTabs/index.tsx +8 -8
  111. package/src/theme/DiscriminatorTabs/index.tsx +6 -5
  112. package/src/theme/Example/index.tsx +3 -4
  113. package/src/theme/MimeTabs/index.tsx +9 -8
  114. package/src/theme/OperationTabs/index.tsx +5 -4
  115. package/src/theme/ParamsDetails/index.tsx +1 -2
  116. package/src/theme/ParamsItem/index.tsx +13 -8
  117. package/src/theme/RequestSchema/index.tsx +38 -40
  118. package/src/theme/ResponseExamples/index.tsx +3 -4
  119. package/src/theme/ResponseSchema/index.tsx +16 -17
  120. package/src/theme/Schema/index.tsx +154 -121
  121. package/src/theme/Schema/normalize.test.ts +288 -0
  122. package/src/theme/Schema/normalize.ts +224 -0
  123. package/src/theme/SchemaExpansion/_SchemaExpansion.scss +113 -0
  124. package/src/theme/SchemaExpansion/context.tsx +154 -0
  125. package/src/theme/SchemaExpansion/index.tsx +236 -0
  126. package/src/theme/SchemaItem/index.tsx +18 -10
  127. package/src/theme/SchemaTabs/index.tsx +6 -5
  128. package/src/theme/StatusCodes/index.tsx +2 -3
  129. package/src/theme/TabItem/index.tsx +61 -0
  130. package/src/theme/TabItem/styles.module.css +3 -0
  131. package/src/theme/Tabs/index.tsx +164 -0
  132. package/src/theme/Tabs/styles.module.css +7 -0
  133. package/src/theme/styles.scss +1 -0
  134. package/src/theme/translationIds.ts +37 -106
  135. package/src/theme/utils/codeBlockUtils.ts +296 -0
  136. package/src/theme/utils/reactUtils.ts +22 -0
  137. package/src/theme/utils/scrollUtils.tsx +153 -0
  138. package/src/theme/utils/tabsUtils.tsx +329 -0
  139. package/src/theme/utils/useCodeWordWrap.ts +110 -0
  140. package/src/theme/utils/useMutationObserver.ts +43 -0
  141. package/src/theme-classic.d.ts +0 -96
  142. package/src/types.d.ts +27 -0
  143. package/tsconfig.tsbuildinfo +1 -1
@@ -19,10 +19,10 @@ const Markdown_1 = __importDefault(require("@theme/Markdown"));
19
19
  const MimeTabs_1 = __importDefault(require("@theme/MimeTabs")); // Assume these components exist
20
20
  const ResponseExamples_1 = require("@theme/ResponseExamples");
21
21
  const Schema_1 = __importDefault(require("@theme/Schema"));
22
+ const SchemaExpansion_1 = __importDefault(require("@theme/SchemaExpansion"));
22
23
  const SchemaTabs_1 = __importDefault(require("@theme/SchemaTabs"));
23
24
  const SkeletonLoader_1 = __importDefault(require("@theme/SkeletonLoader"));
24
25
  const TabItem_1 = __importDefault(require("@theme/TabItem"));
25
- const translationIds_1 = require("@theme/translationIds");
26
26
  const ResponseSchemaComponent = ({ title, body, style }) => {
27
27
  if (
28
28
  body === undefined ||
@@ -57,7 +57,7 @@ const ResponseSchemaComponent = ({ title, body, style }) => {
57
57
  "div",
58
58
  null,
59
59
  (0, Translate_1.translate)({
60
- id: translationIds_1.OPENAPI_SCHEMA.NO_SCHEMA,
60
+ id: "theme.openapi.schema.noSchema",
61
61
  message: "No schema",
62
62
  })
63
63
  )
@@ -83,29 +83,31 @@ const ResponseSchemaComponent = ({ title, body, style }) => {
83
83
  open: true,
84
84
  style: style,
85
85
  summary: react_1.default.createElement(
86
- react_1.default.Fragment,
87
- null,
86
+ "summary",
87
+ {
88
+ className:
89
+ "openapi-markdown__details-summary--with-control",
90
+ },
88
91
  react_1.default.createElement(
89
- "summary",
90
- null,
91
- react_1.default.createElement(
92
- "strong",
93
- {
94
- className:
95
- "openapi-markdown__details-summary-response",
96
- },
97
- title,
98
- body.required === true &&
99
- react_1.default.createElement(
100
- "span",
101
- { className: "openapi-schema__required" },
102
- (0, Translate_1.translate)({
103
- id: translationIds_1.OPENAPI_SCHEMA_ITEM
104
- .REQUIRED,
105
- message: "required",
106
- })
107
- )
108
- )
92
+ "strong",
93
+ {
94
+ className:
95
+ "openapi-markdown__details-summary-response",
96
+ },
97
+ title,
98
+ body.required === true &&
99
+ react_1.default.createElement(
100
+ "span",
101
+ { className: "openapi-schema__required" },
102
+ (0, Translate_1.translate)({
103
+ id: "theme.openapi.schemaItem.required",
104
+ message: "required",
105
+ })
106
+ )
107
+ ),
108
+ react_1.default.createElement(
109
+ SchemaExpansion_1.default,
110
+ null
109
111
  )
110
112
  ),
111
113
  },
@@ -77,85 +77,14 @@ const DiscriminatorTabs_1 = __importDefault(
77
77
  require("@theme/DiscriminatorTabs")
78
78
  );
79
79
  const Markdown_1 = __importDefault(require("@theme/Markdown"));
80
+ const normalize_1 = require("@theme/Schema/normalize");
81
+ const SchemaExpansion_1 = require("@theme/SchemaExpansion");
80
82
  const SchemaItem_1 = __importDefault(require("@theme/SchemaItem"));
81
83
  const SchemaTabs_1 = __importDefault(require("@theme/SchemaTabs"));
82
84
  const TabItem_1 = __importDefault(require("@theme/TabItem"));
83
- const translationIds_1 = require("@theme/translationIds");
84
- // eslint-disable-next-line import/no-extraneous-dependencies
85
- const allof_merge_1 = require("allof-merge");
86
85
  const clsx_1 = __importDefault(require("clsx"));
87
86
  const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
88
87
  const schema_1 = require("../../markdown/schema");
89
- // eslint-disable-next-line import/no-extraneous-dependencies
90
- // const jsonSchemaMergeAllOf = require("json-schema-merge-allof");
91
- const mergeAllOf = (allOf) => {
92
- const onMergeError = (msg) => {
93
- console.warn(msg);
94
- };
95
- const mergedSchemas = (0, allof_merge_1.merge)(allOf, { onMergeError });
96
- return mergedSchemas ?? {};
97
- };
98
- /**
99
- * Recursively searches for a property in a schema, including nested
100
- * oneOf, anyOf, and allOf structures. This is needed for discriminators
101
- * where the property definition may be in a nested schema.
102
- */
103
- const findProperty = (schema, propertyName) => {
104
- // Check direct properties first
105
- if (schema.properties?.[propertyName]) {
106
- return schema.properties[propertyName];
107
- }
108
- // Search in oneOf schemas
109
- if (schema.oneOf) {
110
- for (const subschema of schema.oneOf) {
111
- const found = findProperty(subschema, propertyName);
112
- if (found) return found;
113
- }
114
- }
115
- // Search in anyOf schemas
116
- if (schema.anyOf) {
117
- for (const subschema of schema.anyOf) {
118
- const found = findProperty(subschema, propertyName);
119
- if (found) return found;
120
- }
121
- }
122
- // Search in allOf schemas
123
- if (schema.allOf) {
124
- for (const subschema of schema.allOf) {
125
- const found = findProperty(subschema, propertyName);
126
- if (found) return found;
127
- }
128
- }
129
- return undefined;
130
- };
131
- /**
132
- * Recursively searches for a discriminator in a schema, including nested
133
- * oneOf, anyOf, and allOf structures.
134
- */
135
- const findDiscriminator = (schema) => {
136
- if (schema.discriminator) {
137
- return schema.discriminator;
138
- }
139
- if (schema.oneOf) {
140
- for (const subschema of schema.oneOf) {
141
- const found = findDiscriminator(subschema);
142
- if (found) return found;
143
- }
144
- }
145
- if (schema.anyOf) {
146
- for (const subschema of schema.anyOf) {
147
- const found = findDiscriminator(subschema);
148
- if (found) return found;
149
- }
150
- }
151
- if (schema.allOf) {
152
- for (const subschema of schema.allOf) {
153
- const found = findDiscriminator(subschema);
154
- if (found) return found;
155
- }
156
- }
157
- return undefined;
158
- };
159
88
  // Renders string as markdown, useful for descriptions and qualifiers
160
89
  const MarkdownWrapper = ({ text }) => {
161
90
  return react_1.default.createElement(
@@ -199,7 +128,7 @@ const Summary = ({ name, schemaName, schema, required }) => {
199
128
  "span",
200
129
  { className: "openapi-schema__nullable" },
201
130
  (0, Translate_1.translate)({
202
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.NULLABLE,
131
+ id: "theme.openapi.schemaItem.nullable",
203
132
  message: "nullable",
204
133
  })
205
134
  ),
@@ -208,7 +137,7 @@ const Summary = ({ name, schemaName, schema, required }) => {
208
137
  "span",
209
138
  { className: "openapi-schema__required" },
210
139
  (0, Translate_1.translate)({
211
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.REQUIRED,
140
+ id: "theme.openapi.schemaItem.required",
212
141
  message: "required",
213
142
  })
214
143
  ),
@@ -217,7 +146,7 @@ const Summary = ({ name, schemaName, schema, required }) => {
217
146
  "span",
218
147
  { className: "openapi-schema__deprecated" },
219
148
  (0, Translate_1.translate)({
220
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.DEPRECATED,
149
+ id: "theme.openapi.schemaItem.deprecated",
221
150
  message: "deprecated",
222
151
  })
223
152
  )
@@ -234,11 +163,11 @@ const AnyOneOf = ({ schema, schemaType, schemaPath }) => {
234
163
  }
235
164
  const type = schema.oneOf
236
165
  ? (0, Translate_1.translate)({
237
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.ONE_OF,
166
+ id: "theme.openapi.schemaItem.oneOf",
238
167
  message: "oneOf",
239
168
  })
240
169
  : (0, Translate_1.translate)({
241
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.ANY_OF,
170
+ id: "theme.openapi.schemaItem.anyOf",
242
171
  message: "anyOf",
243
172
  });
244
173
  // Generate a unique ID for this anyOf/oneOf to prevent tab value collisions
@@ -285,12 +214,12 @@ const AnyOneOf = ({ schema, schemaType, schemaPath }) => {
285
214
  if (!label) {
286
215
  if (anyOneSchema.oneOf) {
287
216
  label = (0, Translate_1.translate)({
288
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.ONE_OF,
217
+ id: "theme.openapi.schemaItem.oneOf",
289
218
  message: "oneOf",
290
219
  });
291
220
  } else if (anyOneSchema.anyOf) {
292
221
  label = (0, Translate_1.translate)({
293
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.ANY_OF,
222
+ id: "theme.openapi.schemaItem.anyOf",
294
223
  message: "anyOf",
295
224
  });
296
225
  } else {
@@ -306,6 +235,14 @@ const AnyOneOf = ({ schema, schemaType, schemaPath }) => {
306
235
  react_1.default.createElement(
307
236
  TabItem_1.default,
308
237
  { key: index, label: label, value: `${uniqueId}-${index}-item` },
238
+ anyOneSchema.description &&
239
+ react_1.default.createElement(
240
+ "div",
241
+ { style: { marginLeft: "1rem" } },
242
+ react_1.default.createElement(MarkdownWrapper, {
243
+ text: anyOneSchema.description,
244
+ })
245
+ ),
309
246
  (isPrimitive(anyOneSchema) || anyOneSchema.const) &&
310
247
  react_1.default.createElement(SchemaItem_1.default, {
311
248
  collapsible: false,
@@ -459,7 +396,7 @@ const PropertyDiscriminator = ({
459
396
  "span",
460
397
  { className: "openapi-schema__required" },
461
398
  (0, Translate_1.translate)({
462
- id: translationIds_1.OPENAPI_SCHEMA_ITEM.REQUIRED,
399
+ id: "theme.openapi.schemaItem.required",
463
400
  message: "required",
464
401
  })
465
402
  )
@@ -484,6 +421,14 @@ const PropertyDiscriminator = ({
484
421
  react_1.default.createElement(
485
422
  TabItem_1.default,
486
423
  { key: index, label: key, value: `${index}-item-discriminator` },
424
+ discriminator.mapping[key]?.description &&
425
+ react_1.default.createElement(
426
+ "div",
427
+ { style: { marginLeft: "1rem" } },
428
+ react_1.default.createElement(MarkdownWrapper, {
429
+ text: discriminator.mapping[key].description,
430
+ })
431
+ ),
487
432
  react_1.default.createElement(SchemaNode, {
488
433
  schema: discriminator.mapping[key],
489
434
  schemaType: schemaType,
@@ -513,11 +458,13 @@ const PropertyDiscriminator = ({
513
458
  const DiscriminatorNode = ({ discriminator, schema, schemaType }) => {
514
459
  let discriminatedSchemas = {};
515
460
  let inferredMapping = {};
516
- // Search for the discriminator property in the schema, including nested structures
461
+ // Search for the discriminator property in the schema, including nested
462
+ // structures. Cached recursive lookup replaces the O(subtree) findProperty
463
+ // walk that caused #1525's O(N^2) render cost.
517
464
  const discriminatorProperty =
518
- findProperty(schema, discriminator.propertyName) ?? {};
465
+ (0, normalize_1.findPropertyDeep)(schema, discriminator.propertyName) ?? {};
519
466
  if (schema.allOf) {
520
- const mergedSchemas = mergeAllOf(schema);
467
+ const mergedSchemas = (0, normalize_1.mergeAllOf)(schema);
521
468
  if (mergedSchemas.oneOf || mergedSchemas.anyOf) {
522
469
  discriminatedSchemas = mergedSchemas.oneOf || mergedSchemas.anyOf;
523
470
  }
@@ -537,7 +484,7 @@ const DiscriminatorNode = ({ discriminator, schema, schemaType }) => {
537
484
  // Handle discriminated schema with allOf
538
485
  let mergedSubSchema = {};
539
486
  if (subSchema.allOf) {
540
- mergedSubSchema = mergeAllOf(subSchema);
487
+ mergedSubSchema = (0, normalize_1.mergeAllOf)(subSchema);
541
488
  }
542
489
  const subProperties = subSchema.properties || mergedSubSchema.properties;
543
490
  // Add a safeguard check to avoid referencing subProperties if it's undefined
@@ -578,6 +525,17 @@ const DiscriminatorNode = ({ discriminator, schema, schemaType }) => {
578
525
  const AdditionalProperties = ({ schema, schemaType }) => {
579
526
  const additionalProperties = schema.additionalProperties;
580
527
  if (!additionalProperties) return null;
528
+ if ((0, normalize_1.isCircularMarker)(additionalProperties)) {
529
+ return react_1.default.createElement(SchemaItem_1.default, {
530
+ name: "property name*",
531
+ required: false,
532
+ schemaName: additionalProperties,
533
+ schema: additionalProperties,
534
+ collapsible: false,
535
+ discriminator: false,
536
+ children: null,
537
+ });
538
+ }
581
539
  // Handle free-form objects
582
540
  if (
583
541
  additionalProperties === true ||
@@ -644,13 +602,18 @@ const SchemaNodeDetails = ({
644
602
  schemaType,
645
603
  schemaPath,
646
604
  }) => {
605
+ const depth = (0, SchemaExpansion_1.useSchemaDepth)();
606
+ const { level } = (0, SchemaExpansion_1.useSchemaExpansion)();
607
+ const defaultOpen = depth < level;
647
608
  return react_1.default.createElement(
648
609
  SchemaItem_1.default,
649
610
  { collapsible: true },
650
611
  react_1.default.createElement(
651
612
  Details_1.default,
652
613
  {
614
+ key: `level-${level}`,
653
615
  className: "openapi-markdown__details",
616
+ open: defaultOpen,
654
617
  summary: react_1.default.createElement(Summary, {
655
618
  name: name,
656
619
  schemaName: schemaName,
@@ -669,20 +632,40 @@ const SchemaNodeDetails = ({
669
632
  react_1.default.createElement(MarkdownWrapper, {
670
633
  text: (0, schema_1.getQualifierMessage)(schema),
671
634
  }),
672
- react_1.default.createElement(SchemaNode, {
673
- schema: schema,
674
- schemaType: schemaType,
675
- schemaPath: schemaPath,
676
- })
635
+ react_1.default.createElement(
636
+ SchemaExpansion_1.SchemaDepthProvider,
637
+ { depth: depth + 1 },
638
+ react_1.default.createElement(SchemaNode, {
639
+ schema: schema,
640
+ schemaType: schemaType,
641
+ schemaPath: schemaPath,
642
+ })
643
+ )
677
644
  )
678
645
  )
679
646
  );
680
647
  };
681
648
  const Items = ({ schema, schemaType, schemaPath }) => {
649
+ if ((0, normalize_1.isCircularMarker)(schema.items)) {
650
+ return react_1.default.createElement(
651
+ "div",
652
+ { style: { marginLeft: ".5rem" } },
653
+ react_1.default.createElement(ArrayBrackets_1.OpeningArrayBracket, null),
654
+ react_1.default.createElement(SchemaItem_1.default, {
655
+ collapsible: false,
656
+ name: "",
657
+ schemaName: schema.items,
658
+ schema: schema.items,
659
+ discriminator: false,
660
+ children: null,
661
+ }),
662
+ react_1.default.createElement(ArrayBrackets_1.ClosingArrayBracket, null)
663
+ );
664
+ }
682
665
  // Process schema.items to handle allOf merging
683
666
  let itemsSchema = schema.items;
684
667
  if (schema.items?.allOf) {
685
- itemsSchema = mergeAllOf(schema.items);
668
+ itemsSchema = (0, normalize_1.mergeAllOf)(schema.items);
686
669
  }
687
670
  // Handle complex schemas with multiple schema types
688
671
  const hasOneOfAnyOf = itemsSchema?.oneOf || itemsSchema?.anyOf;
@@ -691,17 +674,25 @@ const Items = ({ schema, schemaType, schemaPath }) => {
691
674
  // Build the items schema path
692
675
  const itemsSchemaPath = schemaPath ? `${schemaPath}.items` : undefined;
693
676
  if (hasOneOfAnyOf || hasProperties || hasAdditionalProperties) {
677
+ // Fold sibling properties into each oneOf/anyOf branch to avoid duplicate
678
+ // property rendering. See issue #1218.
679
+ const renderSchema =
680
+ hasOneOfAnyOf && hasProperties
681
+ ? (0, normalize_1.foldSiblingsIntoBranches)(itemsSchema)
682
+ : itemsSchema;
683
+ const renderHasProperties =
684
+ hasOneOfAnyOf && hasProperties ? false : hasProperties;
694
685
  return react_1.default.createElement(
695
686
  react_1.default.Fragment,
696
687
  null,
697
688
  react_1.default.createElement(ArrayBrackets_1.OpeningArrayBracket, null),
698
689
  hasOneOfAnyOf &&
699
690
  react_1.default.createElement(AnyOneOf, {
700
- schema: itemsSchema,
691
+ schema: renderSchema,
701
692
  schemaType: schemaType,
702
693
  schemaPath: itemsSchemaPath,
703
694
  }),
704
- hasProperties &&
695
+ renderHasProperties &&
705
696
  react_1.default.createElement(Properties, {
706
697
  schema: itemsSchema,
707
698
  schemaType: schemaType,
@@ -771,6 +762,17 @@ const SchemaEdge = ({
771
762
  ) {
772
763
  return null;
773
764
  }
765
+ if ((0, normalize_1.isCircularMarker)(schema)) {
766
+ return react_1.default.createElement(SchemaItem_1.default, {
767
+ collapsible: false,
768
+ name: name,
769
+ required: Array.isArray(required) ? required.includes(name) : required,
770
+ schemaName: schema,
771
+ schema: schema,
772
+ discriminator: false,
773
+ children: null,
774
+ });
775
+ }
774
776
  const schemaName = (0, schema_1.getSchemaName)(schema);
775
777
  if (discriminator && discriminator.propertyName === name) {
776
778
  return react_1.default.createElement(PropertyDiscriminator, {
@@ -838,6 +840,17 @@ const SchemaEdge = ({
838
840
  schemaPath: schemaPath,
839
841
  });
840
842
  }
843
+ if ((0, normalize_1.isCircularMarker)(schema.items)) {
844
+ return react_1.default.createElement(SchemaNodeDetails, {
845
+ name: name,
846
+ schemaName: schemaName,
847
+ required: required,
848
+ nullable: schema.nullable,
849
+ schema: schema,
850
+ schemaType: schemaType,
851
+ schemaPath: schemaPath,
852
+ });
853
+ }
841
854
  if (schema.allOf) {
842
855
  // handle circular properties
843
856
  if (
@@ -856,7 +869,7 @@ const SchemaEdge = ({
856
869
  children: null,
857
870
  });
858
871
  }
859
- const mergedSchemas = mergeAllOf(schema);
872
+ const mergedSchemas = (0, normalize_1.mergeAllOf)(schema);
860
873
  if (
861
874
  (schemaType === "request" && mergedSchemas.readOnly) ||
862
875
  (schemaType === "response" && mergedSchemas.writeOnly)
@@ -918,22 +931,32 @@ const SchemaEdge = ({
918
931
  });
919
932
  };
920
933
  function renderChildren(schema, schemaType, schemaPath) {
934
+ // Fold sibling properties into each oneOf/anyOf branch to avoid duplicate
935
+ // property rendering. See issue #1218.
936
+ const hasOneOfAnyOf = !!(schema.oneOf || schema.anyOf);
937
+ const hasProperties = !!schema.properties;
938
+ const folded =
939
+ hasOneOfAnyOf && hasProperties
940
+ ? (0, normalize_1.foldSiblingsIntoBranches)(schema)
941
+ : schema;
942
+ const renderProperties =
943
+ hasOneOfAnyOf && hasProperties ? false : hasProperties;
921
944
  return react_1.default.createElement(
922
945
  react_1.default.Fragment,
923
946
  null,
924
- schema.oneOf &&
947
+ folded.oneOf &&
925
948
  react_1.default.createElement(AnyOneOf, {
926
- schema: schema,
949
+ schema: folded,
927
950
  schemaType: schemaType,
928
951
  schemaPath: schemaPath,
929
952
  }),
930
- schema.anyOf &&
953
+ folded.anyOf &&
931
954
  react_1.default.createElement(AnyOneOf, {
932
- schema: schema,
955
+ schema: folded,
933
956
  schemaType: schemaType,
934
957
  schemaPath: schemaPath,
935
958
  }),
936
- schema.properties &&
959
+ renderProperties &&
937
960
  react_1.default.createElement(Properties, {
938
961
  schema: schema,
939
962
  schemaType: schemaType,
@@ -952,7 +975,11 @@ function renderChildren(schema, schemaType, schemaPath) {
952
975
  })
953
976
  );
954
977
  }
955
- const SchemaNode = ({ schema, schemaType, schemaPath }) => {
978
+ const SchemaNode = ({ schema: rawSchema, schemaType, schemaPath }) => {
979
+ const schema = (0, react_1.useMemo)(
980
+ () => (0, normalize_1.normalizeSchema)(rawSchema),
981
+ [rawSchema]
982
+ );
956
983
  if (
957
984
  (schemaType === "request" && schema.readOnly) ||
958
985
  (schemaType === "response" && schema.writeOnly)
@@ -960,12 +987,13 @@ const SchemaNode = ({ schema, schemaType, schemaPath }) => {
960
987
  return null;
961
988
  }
962
989
  // Resolve discriminator recursively so nested oneOf/anyOf/allOf compositions
963
- // can still render discriminator tabs.
990
+ // can still render discriminator tabs. Cached via getDiscriminator so per-
991
+ // render cost is O(1) amortized (see #1525).
964
992
  let workingSchema = schema;
965
993
  const resolvedDiscriminator =
966
- schema.discriminator ?? findDiscriminator(schema);
994
+ schema.discriminator ?? (0, normalize_1.getDiscriminator)(schema);
967
995
  if (schema.allOf && !schema.discriminator && resolvedDiscriminator) {
968
- workingSchema = mergeAllOf(schema);
996
+ workingSchema = (0, normalize_1.mergeAllOf)(schema);
969
997
  }
970
998
  if (!workingSchema.discriminator && resolvedDiscriminator) {
971
999
  workingSchema.discriminator = resolvedDiscriminator;
@@ -985,7 +1013,7 @@ const SchemaNode = ({ schema, schemaType, schemaPath }) => {
985
1013
  const hasMultipleChoices = oneOfItems.length > 1;
986
1014
  if (hasMultipleChoices) {
987
1015
  // Render each oneOf/anyOf constraint first, then shared properties
988
- const mergedSchemas = mergeAllOf(schema);
1016
+ const mergedSchemas = (0, normalize_1.mergeAllOf)(schema);
989
1017
  if (
990
1018
  (schemaType === "request" && mergedSchemas.readOnly) ||
991
1019
  (schemaType === "response" && mergedSchemas.writeOnly)
@@ -1027,29 +1055,39 @@ const SchemaNode = ({ schema, schemaType, schemaPath }) => {
1027
1055
  );
1028
1056
  }
1029
1057
  // For other allOf cases, use standard merge behavior
1030
- const mergedSchemas = mergeAllOf(schema);
1058
+ const mergedSchemas = (0, normalize_1.mergeAllOf)(schema);
1031
1059
  if (
1032
1060
  (schemaType === "request" && mergedSchemas.readOnly) ||
1033
1061
  (schemaType === "response" && mergedSchemas.writeOnly)
1034
1062
  ) {
1035
1063
  return null;
1036
1064
  }
1065
+ // Fold sibling properties into each oneOf/anyOf branch to avoid duplicate
1066
+ // property rendering. See issue #1218.
1067
+ const hasOneOfAnyOf = !!(mergedSchemas.oneOf || mergedSchemas.anyOf);
1068
+ const hasProperties = !!mergedSchemas.properties;
1069
+ const folded =
1070
+ hasOneOfAnyOf && hasProperties
1071
+ ? (0, normalize_1.foldSiblingsIntoBranches)(mergedSchemas)
1072
+ : mergedSchemas;
1073
+ const renderProperties =
1074
+ hasOneOfAnyOf && hasProperties ? false : hasProperties;
1037
1075
  return react_1.default.createElement(
1038
1076
  "div",
1039
1077
  null,
1040
- mergedSchemas.oneOf &&
1078
+ folded.oneOf &&
1041
1079
  react_1.default.createElement(AnyOneOf, {
1042
- schema: mergedSchemas,
1080
+ schema: folded,
1043
1081
  schemaType: schemaType,
1044
1082
  schemaPath: schemaPath,
1045
1083
  }),
1046
- mergedSchemas.anyOf &&
1084
+ folded.anyOf &&
1047
1085
  react_1.default.createElement(AnyOneOf, {
1048
- schema: mergedSchemas,
1086
+ schema: folded,
1049
1087
  schemaType: schemaType,
1050
1088
  schemaPath: schemaPath,
1051
1089
  }),
1052
- mergedSchemas.properties &&
1090
+ renderProperties &&
1053
1091
  react_1.default.createElement(Properties, {
1054
1092
  schema: mergedSchemas,
1055
1093
  schemaType: schemaType,
@@ -0,0 +1,41 @@
1
+ export declare function isCircularMarker(value: unknown): value is string;
2
+ export declare function mergeAllOf(schema: any): any;
3
+ /**
4
+ * Fold sibling fields into each oneOf/anyOf branch via allOf-merge so each
5
+ * branch is self-contained. See #1218.
6
+ *
7
+ * Skipped when a discriminator is present: DiscriminatorNode already renders
8
+ * sibling properties at the top level, and folding would duplicate the
9
+ * discriminator metadata into each branch (causing nested DiscriminatorNode
10
+ * renders inside tabs — see #1525 follow-up).
11
+ *
12
+ * Called by normalizeSchema after allOf resolution. Uses mergeAllOf internally
13
+ * to compose `{ allOf: [siblings, branch] }` per branch — the WeakMap caches
14
+ * in stripConflictingAdditionalProps prevent redundant work on shared subtrees.
15
+ */
16
+ export declare function foldSiblingsIntoBranches(schema: any): any;
17
+ export declare function getDiscriminator(schema: any): any | undefined;
18
+ /**
19
+ * Cached recursive property lookup. Searches schema.properties first, then
20
+ * into oneOf/anyOf/allOf branches. Returns O(1) on repeated calls with the
21
+ * same (schema, propertyName) pair — replaces the O(subtree) findProperty
22
+ * walk that caused #1525's O(N^2) render cost.
23
+ */
24
+ export declare function findPropertyDeep(schema: any, propertyName: string): any | undefined;
25
+ /**
26
+ * Identity-stable schema pass-through. Returns the input as-is so SchemaNode
27
+ * useMemo dependencies stay stable across renders.
28
+ *
29
+ * Earlier iterations of this function eagerly merged allOf and folded
30
+ * siblings into oneOf/anyOf branches, but those operations need to stay
31
+ * conditional inside SchemaNode and DiscriminatorNode — eager versions
32
+ * caused cartesian product expansion (allOf with multiple oneOfs) and
33
+ * double-folding regressions. The render-time helpers below
34
+ * (mergeAllOf, foldSiblingsIntoBranches) are still cached via the WeakMap
35
+ * inside stripConflictingAdditionalProps, so per-render cost stays bounded.
36
+ *
37
+ * The perf win for #1525 comes from getDiscriminator's WeakMap cache
38
+ * replacing the O(subtree) findDiscriminator walk, not from eager
39
+ * normalization here.
40
+ */
41
+ export declare function normalizeSchema(schema: any): any;