rapidoc 11.0.1 → 12.1.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 (96) hide show
  1. package/README.md +171 -54
  2. package/dist/rapidoc-flow-min.js +580 -0
  3. package/dist/rapidoc-flow-min.js.LICENSE.txt +171 -0
  4. package/dist/rapidoc-min.js +645 -202
  5. package/dist/rapidoc-min.js.LICENSE.txt +381 -0
  6. package/package.json +57 -42
  7. package/src/README.md +150 -0
  8. package/src/components/README.md +91 -0
  9. package/src/components/api-request.ts +912 -0
  10. package/src/components/api-response.ts +514 -0
  11. package/src/components/json-tree.ts +171 -0
  12. package/src/components/schema-base-element.ts +329 -0
  13. package/src/components/schema-table.ts +475 -0
  14. package/src/components/schema-tree.ts +393 -0
  15. package/src/components/tag-input.ts +180 -0
  16. package/src/flow.js +3 -0
  17. package/src/index.js +6 -0
  18. package/src/oauth-receiver.ts +29 -0
  19. package/src/rapi-flow.ts +1178 -0
  20. package/src/rapidoc-mini.ts +603 -0
  21. package/src/rapidoc.ts +1854 -0
  22. package/src/styles/README.md +96 -0
  23. package/src/styles/auth-styles.js +318 -0
  24. package/src/styles/border-styles.js +24 -0
  25. package/src/styles/css-bundle.js +129 -0
  26. package/src/styles/custom-styles.js +8 -0
  27. package/src/styles/dialog-box-styles.js +102 -0
  28. package/src/styles/endpoint-styles.js +564 -0
  29. package/src/styles/flex-styles.js +16 -0
  30. package/src/styles/flow-styles.js +248 -0
  31. package/src/styles/font-styles.js +432 -0
  32. package/src/styles/info-styles.js +36 -0
  33. package/src/styles/input-styles.js +555 -0
  34. package/src/styles/layout-styles.js +396 -0
  35. package/src/styles/microlighter-styles.js +99 -0
  36. package/src/styles/nav-styles.js +633 -0
  37. package/src/styles/param-list-styles.js +261 -0
  38. package/src/styles/schema-styles.js +296 -0
  39. package/src/styles/tab-styles.js +106 -0
  40. package/src/styles/theme-tokens.js +582 -0
  41. package/src/templates/README.md +89 -0
  42. package/src/templates/advanced-search-dialog-template.js +94 -0
  43. package/src/templates/api-response-template.js +223 -0
  44. package/src/templates/callback-template.js +86 -0
  45. package/src/templates/code-samples-template.js +52 -0
  46. package/src/templates/components-template.js +86 -0
  47. package/src/templates/downloads-template.js +70 -0
  48. package/src/templates/endpoint-template.js +377 -0
  49. package/src/templates/expanded-endpoint-template.js +270 -0
  50. package/src/templates/focused-endpoint-template.js +146 -0
  51. package/src/templates/header-template.js +92 -0
  52. package/src/templates/logo-template.js +21 -0
  53. package/src/templates/main-body-template.js +102 -0
  54. package/src/templates/navbar-template.js +498 -0
  55. package/src/templates/overview-template.js +104 -0
  56. package/src/templates/request-body-template.js +673 -0
  57. package/src/templates/request-params-template.js +485 -0
  58. package/src/templates/security-scheme-template.js +1272 -0
  59. package/src/templates/server-template.js +153 -0
  60. package/src/utils/README.md +146 -0
  61. package/src/utils/arazzo/README.md +119 -0
  62. package/src/utils/arazzo/components.js +15 -0
  63. package/src/utils/arazzo/graph.js +269 -0
  64. package/src/utils/arazzo/index.js +52 -0
  65. package/src/utils/arazzo/operations.js +106 -0
  66. package/src/utils/arazzo/parse.js +196 -0
  67. package/src/utils/arazzo/refs.js +40 -0
  68. package/src/utils/arazzo/svg.js +707 -0
  69. package/src/utils/auto-color-scheme.js +99 -0
  70. package/src/utils/bind-navbar.js +175 -0
  71. package/src/utils/color-utils.js +59 -0
  72. package/src/utils/common-utils.js +191 -0
  73. package/src/utils/curl-utils.js +126 -0
  74. package/src/utils/highlighter.js +419 -0
  75. package/src/utils/markdown-utils.js +110 -0
  76. package/src/utils/mcp/README.md +155 -0
  77. package/src/utils/mcp/common.js +166 -0
  78. package/src/utils/mcp/manifest.js +382 -0
  79. package/src/utils/mcp/project/server.ts +53 -0
  80. package/src/utils/mcp/project.js +88 -0
  81. package/src/utils/mcp/runtime.js +289 -0
  82. package/src/utils/mcp/schema.js +98 -0
  83. package/src/utils/mcp/workflow-runtime.js +374 -0
  84. package/src/utils/mcp/workflows.js +240 -0
  85. package/src/utils/mcp/zip.js +102 -0
  86. package/src/utils/mcp-generator.js +47 -0
  87. package/src/utils/mock-interceptor.js +557 -0
  88. package/src/utils/oauth-utils.js +87 -0
  89. package/src/utils/request-utils.js +264 -0
  90. package/src/utils/response-utils.js +203 -0
  91. package/src/utils/sanitize.js +57 -0
  92. package/src/utils/schema-utils.js +1954 -0
  93. package/src/utils/slugger.js +187 -0
  94. package/src/utils/spec-parser.js +941 -0
  95. package/src/utils/url-utils.js +246 -0
  96. package/src/utils/xml-utils.js +61 -0
@@ -0,0 +1,1954 @@
1
+ /**
2
+ * Generates a deterministic sample string matching common regex patterns in OpenAPI schemas.
3
+ * Falls back to the pattern string if an unexpected error occurs.
4
+ */
5
+ export function patternSampleGenerator(pattern) {
6
+ if (!pattern || typeof pattern !== "string") {
7
+ return "";
8
+ }
9
+
10
+ try {
11
+ let p = pattern.trim();
12
+ if (p.startsWith("^")) p = p.slice(1);
13
+ if (p.endsWith("$") && !p.endsWith("\\$")) p = p.slice(0, -1);
14
+
15
+ // Resolve simple group alternations: (opt1|opt2|opt3) -> opt1
16
+ p = p.replace(/\((?:\?:)?([^()|]+)(?:\|[^()]+)*\)/g, "$1");
17
+
18
+ // Tokenizer matching
19
+ const tokenRegex =
20
+ /(\[[^\]]+\]|\\[dwsDWS]|\\[^]|\.|[^\\[\]{}()+*?|])(?:\{(\d+)(?:,\d*)?\}|([+*?]))?/g;
21
+
22
+ let result = "";
23
+ let match;
24
+
25
+ while ((match = tokenRegex.exec(p)) !== null) {
26
+ const token = match[1];
27
+ const count = match[2];
28
+ const quantifier = match[3];
29
+
30
+ let rep = 1;
31
+ if (count !== undefined) {
32
+ rep = parseInt(count, 10);
33
+ } else if (quantifier === "+") {
34
+ rep = 1;
35
+ } else if (quantifier === "*" || quantifier === "?") {
36
+ rep = 0;
37
+ }
38
+
39
+ rep = Math.min(Math.max(0, rep), 50);
40
+
41
+ let sampleChar = "a";
42
+ if (token === "\\d") {
43
+ sampleChar = "0";
44
+ } else if (token === "\\D") {
45
+ sampleChar = "a";
46
+ } else if (token === "\\w") {
47
+ sampleChar = "a";
48
+ } else if (token === "\\W") {
49
+ sampleChar = "_";
50
+ } else if (token === "\\s") {
51
+ sampleChar = " ";
52
+ } else if (token === "\\S") {
53
+ sampleChar = "a";
54
+ } else if (token === ".") {
55
+ sampleChar = "a";
56
+ } else if (token.startsWith("\\")) {
57
+ sampleChar = token.slice(1);
58
+ } else if (token.startsWith("[")) {
59
+ const inner = token.slice(1, -1);
60
+ if (inner.startsWith("^")) {
61
+ sampleChar = inner.includes("a") ? "0" : "a";
62
+ } else if (/[1-9]/.test(inner) && !inner.includes("0")) {
63
+ sampleChar = "1";
64
+ } else if (/[0-9]/.test(inner) && !/[a-zA-Z]/.test(inner)) {
65
+ sampleChar = "0";
66
+ } else if (/[0-9]/.test(inner) && /[a-fA-F]/.test(inner) && !/[g-zG-Z]/.test(inner)) {
67
+ sampleChar = "0";
68
+ } else if (/[A-Z]/.test(inner) && !/[a-z]/.test(inner)) {
69
+ sampleChar = "A";
70
+ } else if (/[a-z]/.test(inner)) {
71
+ sampleChar = "a";
72
+ } else if (inner.length > 0) {
73
+ sampleChar = inner[0];
74
+ }
75
+ } else {
76
+ sampleChar = token;
77
+ }
78
+
79
+ result += sampleChar.repeat(rep);
80
+ }
81
+
82
+ return result || pattern;
83
+ } catch {
84
+ return pattern;
85
+ }
86
+ }
87
+
88
+ // Takes a value as input and provides a printable string to replresent null values, spaces, blankstring etc
89
+ export function getPrintableVal(val) {
90
+ if (val === undefined) {
91
+ return "";
92
+ }
93
+ if (val === null) {
94
+ return "null";
95
+ }
96
+ if (val === "") {
97
+ return "∅";
98
+ }
99
+ if (typeof val === "boolean" || typeof val === "number") {
100
+ return `${val}`;
101
+ }
102
+ if (Array.isArray(val)) {
103
+ return val
104
+ .map((v) =>
105
+ v === null
106
+ ? "null"
107
+ : v === ""
108
+ ? "∅"
109
+ : v.toString().replace(/^ +| +$/g, (m) => "●".repeat(m.length)) || "",
110
+ )
111
+ .join(", ");
112
+ }
113
+ if (typeof val === "object") {
114
+ const keys = Object.keys(val);
115
+ return `{ ${keys[0]}:${val[keys[0]]}${keys.length > 1 ? "," : ""} ... }`;
116
+ }
117
+ return val.toString().replace(/^ +| +$/g, (m) => "●".repeat(m.length)) || "";
118
+ }
119
+
120
+ /* Helper to detect binary file fields across OpenAPI 3.0 and 3.1 */
121
+ export function isBinaryFileField(schema) {
122
+ if (!schema) {
123
+ return false;
124
+ }
125
+ return (
126
+ schema.format === "binary" ||
127
+ schema.contentEncoding === "binary" ||
128
+ (Boolean(schema.contentMediaType) && schema.contentEncoding !== "base64")
129
+ );
130
+ }
131
+
132
+ // Keywords that only annotate a schema, without telling anything about the type of the value
133
+ const ANNOTATION_KEYWORDS = new Set([
134
+ "title",
135
+ "description",
136
+ "example",
137
+ "examples",
138
+ "default",
139
+ "deprecated",
140
+ "readOnly",
141
+ "writeOnly",
142
+ "externalDocs",
143
+ ]);
144
+
145
+ function isAnnotationOnly(schema) {
146
+ return Object.keys(schema).every((key) => ANNOTATION_KEYWORDS.has(key) || key.startsWith("x-"));
147
+ }
148
+
149
+ /* Generates an schema object containing type and constraint info */
150
+ export function getTypeInfo(schema) {
151
+ if (!schema) {
152
+ return;
153
+ }
154
+ let dataType = "";
155
+ let constrain = "";
156
+ // let examples;
157
+
158
+ if (schema.$ref) {
159
+ const n = schema.$ref.lastIndexOf("/");
160
+ const schemaNode = schema.$ref.substring(n + 1);
161
+ dataType = `{recursive: ${schemaNode}} `;
162
+ } else if (schema.type) {
163
+ dataType = Array.isArray(schema.type) ? schema.type.join("┃") : schema.type;
164
+ if (schema.format || schema.enum || schema.const !== undefined) {
165
+ dataType = dataType.replace(
166
+ "string",
167
+ schema.enum ? "enum" : schema.const !== undefined ? "const" : schema.format,
168
+ );
169
+ }
170
+ if (schema.nullable) {
171
+ dataType += "┃null";
172
+ }
173
+ } else if (schema.const !== undefined) {
174
+ dataType = "const";
175
+ } else if (schema.anyOf || schema.oneOf) {
176
+ const subSchemas = (schema.anyOf || schema.oneOf).filter(Boolean);
177
+ const subTypes = [];
178
+ subSchemas.forEach((s) => {
179
+ const sInfo = getTypeInfo(s);
180
+ if (sInfo?.type && sInfo.type !== "{missing-type-info}") {
181
+ sInfo.type.split("┃").forEach((t) => {
182
+ const trimmed = t.trim();
183
+ if (trimmed && !subTypes.includes(trimmed)) {
184
+ subTypes.push(trimmed);
185
+ }
186
+ });
187
+ }
188
+ });
189
+ dataType = subTypes.length > 0 ? subTypes.join("┃") : "{missing-type-info}";
190
+ } else if (isAnnotationOnly(schema)) {
191
+ dataType = "any";
192
+ } else {
193
+ dataType = "{missing-type-info}";
194
+ }
195
+
196
+ const effectiveSchema =
197
+ schema.anyOf || schema.oneOf
198
+ ? {
199
+ ...((schema.anyOf || schema.oneOf).find((s) => s && s.type && s.type !== "null") ||
200
+ (schema.anyOf || schema.oneOf)[0]),
201
+ ...schema,
202
+ }
203
+ : schema;
204
+
205
+ const info = {
206
+ type: dataType,
207
+ format: schema.format || effectiveSchema.format || "",
208
+ contentMediaType: schema.contentMediaType || effectiveSchema.contentMediaType || "",
209
+ contentEncoding: schema.contentEncoding || effectiveSchema.contentEncoding || "",
210
+ contentSchema: schema.contentSchema || effectiveSchema.contentSchema || null,
211
+ pattern:
212
+ (schema.pattern || effectiveSchema.pattern) && !schema.enum && !effectiveSchema.enum
213
+ ? schema.pattern || effectiveSchema.pattern
214
+ : "",
215
+ readOrWriteOnly: schema.readOnly
216
+ ? "🆁"
217
+ : schema.writeOnly
218
+ ? "🆆"
219
+ : effectiveSchema.readOnly
220
+ ? "🆁"
221
+ : effectiveSchema.writeOnly
222
+ ? "🆆"
223
+ : "",
224
+ deprecated: schema.deprecated || effectiveSchema.deprecated ? "❌" : "",
225
+ examples:
226
+ schema.examples || schema.example || effectiveSchema.examples || effectiveSchema.example,
227
+ default: getPrintableVal(
228
+ schema.default !== undefined ? schema.default : effectiveSchema.default,
229
+ ),
230
+ description: schema.description || effectiveSchema.description || "",
231
+ constrain: "",
232
+ allowedValues: "",
233
+ arrayType: "",
234
+ html: "",
235
+ };
236
+
237
+ if (info.type === "{missing-type-info}" || info.type === "any") {
238
+ info.description = info.description || "";
239
+ }
240
+ // Set Allowed Values
241
+ info.allowedValues =
242
+ schema.const !== undefined
243
+ ? getPrintableVal(schema.const)
244
+ : Array.isArray(schema.enum)
245
+ ? schema.enum.map((v) => getPrintableVal(v)).join("┃")
246
+ : effectiveSchema.const !== undefined
247
+ ? getPrintableVal(effectiveSchema.const)
248
+ : Array.isArray(effectiveSchema.enum)
249
+ ? effectiveSchema.enum.map((v) => getPrintableVal(v)).join("┃")
250
+ : "";
251
+
252
+ if (!info.allowedValues && (schema.anyOf || schema.oneOf)) {
253
+ const subValues = [];
254
+ (schema.anyOf || schema.oneOf).forEach((s) => {
255
+ const sVal =
256
+ s.const !== undefined
257
+ ? getPrintableVal(s.const)
258
+ : Array.isArray(s.enum)
259
+ ? s.enum.map((v) => getPrintableVal(v)).join("┃")
260
+ : "";
261
+ if (sVal && !subValues.includes(sVal)) {
262
+ subValues.push(sVal);
263
+ }
264
+ });
265
+ if (subValues.length > 0) {
266
+ info.allowedValues = subValues.join("┃");
267
+ }
268
+ }
269
+
270
+ const itemsSchema = schema.items || effectiveSchema.items;
271
+ if ((dataType === "array" || dataType.split("┃").includes("array")) && itemsSchema) {
272
+ const arrayItemType =
273
+ itemsSchema.format ||
274
+ itemsSchema.contentMediaType ||
275
+ (Array.isArray(itemsSchema?.type) ? itemsSchema.type.join("┃") : itemsSchema?.type) ||
276
+ "";
277
+ const arrayItemDefault = getPrintableVal(itemsSchema.default);
278
+
279
+ info.arrayType = arrayItemType ? `array of ${arrayItemType}` : `${schema.type || "array"}`;
280
+ if (!info.default) {
281
+ info.default = arrayItemDefault;
282
+ }
283
+ if (!info.allowedValues) {
284
+ info.allowedValues =
285
+ itemsSchema.const !== undefined
286
+ ? getPrintableVal(itemsSchema.const)
287
+ : Array.isArray(itemsSchema?.enum)
288
+ ? itemsSchema.enum.map((v) => getPrintableVal(v)).join("┃")
289
+ : "";
290
+ }
291
+ }
292
+ if (dataType.match(/integer|number/g)) {
293
+ const minVal = schema.minimum !== undefined ? schema.minimum : effectiveSchema.minimum;
294
+ const excMinVal =
295
+ schema.exclusiveMinimum !== undefined
296
+ ? schema.exclusiveMinimum
297
+ : effectiveSchema.exclusiveMinimum;
298
+ if (minVal !== undefined || excMinVal !== undefined) {
299
+ constrain += minVal !== undefined ? `Min ${minVal}` : `More than ${excMinVal}`;
300
+ }
301
+ const maxVal = schema.maximum !== undefined ? schema.maximum : effectiveSchema.maximum;
302
+ const excMaxVal =
303
+ schema.exclusiveMaximum !== undefined
304
+ ? schema.exclusiveMaximum
305
+ : effectiveSchema.exclusiveMaximum;
306
+ if (maxVal !== undefined || excMaxVal !== undefined) {
307
+ constrain +=
308
+ maxVal !== undefined
309
+ ? `${constrain ? "┃" : ""}Max ${maxVal}`
310
+ : `${constrain ? "┃" : ""}Less than ${excMaxVal}`;
311
+ }
312
+ const multVal =
313
+ schema.multipleOf !== undefined ? schema.multipleOf : effectiveSchema.multipleOf;
314
+ if (multVal !== undefined) {
315
+ constrain += `${constrain ? "┃" : ""} multiple of ${multVal}`;
316
+ }
317
+ }
318
+ // dataType shows the format or enum in place of 'string' (e.g. 'email', 'uri'), so check the declared type too
319
+ const declaredTypes = [].concat(effectiveSchema.type || []);
320
+ if (declaredTypes.includes("string") || dataType.match(/string/g)) {
321
+ const minLen = schema.minLength !== undefined ? schema.minLength : effectiveSchema.minLength;
322
+ const maxLen = schema.maxLength !== undefined ? schema.maxLength : effectiveSchema.maxLength;
323
+ if (minLen !== undefined && maxLen !== undefined) {
324
+ constrain += `${constrain ? "┃" : ""}${minLen} to ${maxLen} chars`;
325
+ } else if (minLen !== undefined) {
326
+ constrain += `${constrain ? "┃" : ""}Min ${minLen} chars`;
327
+ } else if (maxLen !== undefined) {
328
+ constrain += `${constrain ? "┃" : ""}Max ${maxLen} chars`;
329
+ }
330
+ }
331
+ info.constrain = constrain;
332
+ info.html = `${info.type}~|~${info.readOrWriteOnly}~|~${info.constrain}~|~${info.default}~|~${info.allowedValues}~|~${info.pattern}~|~${info.description}~|~${schema.title || ""}~|~${info.deprecated ? "deprecated" : ""}`;
333
+ return info;
334
+ }
335
+
336
+ /**
337
+ *
338
+ * @param {*} ex if the value
339
+ * - Is an Object with 'value' property like
340
+ * { 'value': 'example_val1', 'description': 'some description' }
341
+ * Returns >>>
342
+ * {
343
+ * 'Example': { 'value' : 'example_val1', 'description': 'some description' },
344
+ * }
345
+ * - Is an object where each key represents a valid example object (i,e has a value property)
346
+ * {
347
+ * 'example1': { 'value' : 'example_val1', 'description': 'some description' },
348
+ * 'example2': { 'value' : 'example_val2', 'description': 'some other description' },
349
+ * 'invalid': { 'description': 'invalid example object without any value property' }
350
+ * }
351
+ * Returns >>>
352
+ * {
353
+ * 'example1': { 'value' : 'example_val1', 'description': 'some description' },
354
+ * 'example2': { 'value' : 'example_val2', 'description': 'some other description' }
355
+ * }
356
+ * if none of the keys represents an object with 'value' property then return undefined
357
+ * - Is an array of premitive values
358
+ * ['example_val1', 'example_val2']
359
+ * Returns >>>
360
+ * {
361
+ * 'Example1': {value:'value1'}
362
+ * 'Example2': {value:'value2'}
363
+ * }
364
+ * - Is a premitive value
365
+ * 'example_val1'
366
+ * Returns >>>
367
+ * {
368
+ * 'Example': { 'value': 'example_val1' }
369
+ * }
370
+ * - Is undefined
371
+ * returns undefined
372
+ * @returns
373
+ */
374
+
375
+ export function standardizeExample(ex) {
376
+ if (typeof ex === "object" && ex !== null && !Array.isArray(ex)) {
377
+ if (ex.value !== undefined) {
378
+ // Case 1: Single object with 'value' property
379
+ return { Example: { ...ex } };
380
+ }
381
+ // Case 2: Object where each key is an object with a 'value' property
382
+ const filteredEntries = Object.entries(ex).filter(([_, obj]) => obj?.value !== undefined); // oxlint-disable-line no-unused-vars
383
+ // If no valid entries found, return JSON.stringify of the input
384
+ if (filteredEntries.length === 0) {
385
+ return undefined;
386
+ }
387
+ return Object.fromEntries(filteredEntries);
388
+ }
389
+ if (Array.isArray(ex)) {
390
+ // Case 3: Array of primitive values
391
+ return ex.reduce((acc, value, index) => {
392
+ acc[`Example${index + 1}`] = { value };
393
+ return acc;
394
+ }, {});
395
+ }
396
+ // Case 4: Single primitive value
397
+ return ex ? { Example: { value: ex } } : undefined;
398
+ }
399
+
400
+ /**
401
+ * Normalize example object in the following format (List of object which is used to render example links and fill the input boxes)
402
+ * [{
403
+ * exampleVal : 'value to be rendered on the input control (text-box)',
404
+ * exampleList : [
405
+ * value : '',
406
+ * printableValue: '',
407
+ * summary : '',
408
+ * description : ''
409
+ * ]
410
+ * }]
411
+ * */
412
+ export function normalizeExamples(examples, dataType = "string") {
413
+ if (!examples) {
414
+ return {
415
+ exampleVal: "",
416
+ exampleList: [],
417
+ };
418
+ }
419
+ if (examples.constructor === Object) {
420
+ const exampleList = Object.values(examples)
421
+ .filter((v) => v["x-example-show-value"] !== false)
422
+ .map((v) => ({
423
+ value:
424
+ typeof v.value === "boolean" || typeof v.value === "number"
425
+ ? `${v.value}`
426
+ : v.value || "",
427
+ printableValue: getPrintableVal(v.value),
428
+ summary: v.summary || "",
429
+ description: v.description || "",
430
+ }));
431
+ const exampleVal = exampleList.length > 0 ? exampleList[0].value : "";
432
+ return { exampleVal, exampleList };
433
+ }
434
+
435
+ // This is non-standard way to provide example but will support for now
436
+ if (!Array.isArray(examples)) {
437
+ examples = examples ? [examples] : [];
438
+ }
439
+
440
+ if (examples.length === 0) {
441
+ return {
442
+ exampleVal: "",
443
+ exampleList: [],
444
+ };
445
+ }
446
+
447
+ if (dataType === "array") {
448
+ const [exampleVal] = examples;
449
+ const exampleList = examples.map((v) => ({
450
+ value: v,
451
+ printableValue: getPrintableVal(v),
452
+ }));
453
+ return { exampleVal, exampleList };
454
+ }
455
+
456
+ const exampleVal = examples[0].toString();
457
+ const exampleList = examples.map((v) => ({
458
+ value: v.toString(),
459
+ printableValue: getPrintableVal(v),
460
+ }));
461
+ return { exampleVal, exampleList };
462
+ }
463
+
464
+ export function anyExampleWithSummaryOrDescription(examples) {
465
+ return examples.some((x) => x.summary?.length > 0 || x.description?.length > 0);
466
+ }
467
+
468
+ /**
469
+ * The 'example' property was deprecated in 3.1.0 in favor of the JSON Schema 'examples' keyword. However RapiDoc supports both. in the following order:
470
+ * - examples
471
+ * - example
472
+ *
473
+ * Returns the first example in the given schema.
474
+ * Returns `undefined` if `schema` is undefined or if there are no examples at the top level of the `schema`.
475
+ */
476
+ function getFirstExample(schema) {
477
+ let firstExample;
478
+ if (schema) {
479
+ if (schema.examples && schema.examples.length >= 1) {
480
+ firstExample = schema.examples[0];
481
+ } else {
482
+ firstExample = schema.example;
483
+ }
484
+ }
485
+ return firstExample;
486
+ }
487
+
488
+ export function getSampleValueByType(schemaObj) {
489
+ const example = getFirstExample(schemaObj);
490
+ if (example === "") {
491
+ return "";
492
+ }
493
+ if (example === null) {
494
+ return null;
495
+ }
496
+ if (example === 0) {
497
+ return 0;
498
+ }
499
+ if (example === false) {
500
+ return false;
501
+ }
502
+ if (example instanceof Date) {
503
+ switch (schemaObj.format.toLowerCase()) {
504
+ case "date":
505
+ return example.toISOString().split("T")[0];
506
+ case "time":
507
+ return example.toISOString().split("T")[1];
508
+ default:
509
+ return example.toISOString();
510
+ }
511
+ }
512
+ if (example) {
513
+ return example;
514
+ }
515
+
516
+ if (Object.keys(schemaObj).length === 0) {
517
+ return null;
518
+ }
519
+ if (schemaObj.$ref) {
520
+ // Indicates a Circular ref
521
+ return {};
522
+ }
523
+ if (
524
+ schemaObj.const === false ||
525
+ schemaObj.const === 0 ||
526
+ schemaObj.const === null ||
527
+ schemaObj.const === ""
528
+ ) {
529
+ return schemaObj.const;
530
+ }
531
+ if (schemaObj.const) {
532
+ return schemaObj.const;
533
+ }
534
+ if (schemaObj.default) {
535
+ return schemaObj.default;
536
+ }
537
+ const typeValue = Array.isArray(schemaObj.type) ? schemaObj.type[0] : schemaObj.type;
538
+ if (!typeValue) {
539
+ return null;
540
+ }
541
+ if (typeValue.match(/^integer|^number/g)) {
542
+ const multipleOf = Number.isNaN(Number(schemaObj.multipleOf))
543
+ ? undefined
544
+ : Number(schemaObj.multipleOf);
545
+ const maximum = Number.isNaN(Number(schemaObj.maximum)) ? undefined : Number(schemaObj.maximum);
546
+ const minimumPossibleVal = Number.isNaN(Number(schemaObj.minimum))
547
+ ? Number.isNaN(Number(schemaObj.exclusiveMinimum))
548
+ ? maximum || 0
549
+ : Number(schemaObj.exclusiveMinimum) + (typeValue.startsWith("integer") ? 1 : 0.001)
550
+ : Number(schemaObj.minimum);
551
+ const finalVal = multipleOf
552
+ ? multipleOf >= minimumPossibleVal
553
+ ? multipleOf
554
+ : minimumPossibleVal % multipleOf === 0
555
+ ? minimumPossibleVal
556
+ : Math.ceil(minimumPossibleVal / multipleOf) * multipleOf
557
+ : minimumPossibleVal;
558
+ return finalVal;
559
+ }
560
+ if (typeValue.match(/^boolean/g)) {
561
+ return false;
562
+ }
563
+ if (typeValue.match(/^null/g)) {
564
+ return null;
565
+ }
566
+ if (typeValue.match(/^string/g)) {
567
+ if (schemaObj.enum) {
568
+ return schemaObj.enum[0];
569
+ }
570
+ if (schemaObj.const) {
571
+ return schemaObj.const;
572
+ }
573
+ if (schemaObj.pattern) {
574
+ try {
575
+ return patternSampleGenerator(schemaObj.pattern);
576
+ } catch {
577
+ return schemaObj.pattern;
578
+ }
579
+ }
580
+ if (schemaObj.contentEncoding) {
581
+ switch (schemaObj.contentEncoding.toLowerCase()) {
582
+ case "base64":
583
+ return "ZXhhbXBsZQ=="; // 'example' base64 encoded
584
+ case "binary":
585
+ case "7bit":
586
+ case "8bit":
587
+ case "quoted-printable":
588
+ return "string";
589
+ }
590
+ }
591
+ if (schemaObj.contentMediaType) {
592
+ if (schemaObj.contentMediaType.toLowerCase() === "application/json") {
593
+ if (schemaObj.contentSchema) {
594
+ const sample = schemaToSampleObj(schemaObj.contentSchema);
595
+ const sampleVal = sample?.["example-0"] ?? sample;
596
+ return JSON.stringify(sampleVal);
597
+ }
598
+ return "{}";
599
+ }
600
+ if (schemaObj.contentMediaType.startsWith("text/")) {
601
+ return "text content";
602
+ }
603
+ }
604
+ if (schemaObj.format) {
605
+ switch (schemaObj.format.toLowerCase()) {
606
+ case "url":
607
+ case "uri":
608
+ return "http://example.com";
609
+ case "date":
610
+ return new Date(0).toISOString().split("T")[0];
611
+ case "time":
612
+ return new Date(0).toISOString().split("T")[1];
613
+ case "date-time":
614
+ return new Date(0).toISOString();
615
+ case "duration":
616
+ return "P3Y6M4DT12H30M5S"; // P=Period 3-Years 6-Months 4-Days 12-Hours 30-Minutes 5-Seconds
617
+ case "email":
618
+ case "idn-email":
619
+ return "user@example.com";
620
+ case "hostname":
621
+ case "idn-hostname":
622
+ return "www.example.com";
623
+ case "ipv4":
624
+ return "198.51.100.42";
625
+ case "ipv6":
626
+ return "2001:0db8:5b96:0000:0000:426f:8e17:642a";
627
+ case "uuid":
628
+ return "3fa85f64-5717-4562-b3fc-2c963f66afa6";
629
+ case "byte":
630
+ case "base64":
631
+ return "ZXhhbXBsZQ=="; // 'example' base64 encoded. See https://spec.openapis.org/oas/v3.0.0#data-types
632
+ case "binary":
633
+ return "binary";
634
+ default:
635
+ return "";
636
+ }
637
+ } else {
638
+ const minLength = Number.isNaN(schemaObj.minLength) ? undefined : Number(schemaObj.minLength);
639
+ const maxLength = Number.isNaN(schemaObj.maxLength) ? undefined : Number(schemaObj.maxLength);
640
+ const finalLength = minLength || (maxLength > 6 ? 6 : maxLength || undefined);
641
+ return finalLength ? "A".repeat(finalLength) : "string";
642
+ }
643
+ }
644
+ // If type cannot be determined
645
+ return null;
646
+ }
647
+
648
+ /*
649
+ json2xml- TestCase
650
+ {
651
+ 'prop1' : 'one',
652
+ 'prop2' : 'two',
653
+ 'prop3' : [ 'a', 'b', 'c' ],
654
+ 'prop4' : {
655
+ 'ob1' : 'val-1',
656
+ 'ob2' : 'val-2'
657
+ }
658
+ }
659
+ <root>
660
+ <prop1>simple</prop1>
661
+ <prop2>
662
+ <0>a</0>
663
+ <1>b</1>
664
+ <2>c</2>
665
+ </prop2>
666
+ <prop3>
667
+ <ob1>val-1</ob1>
668
+ <ob2>val-2</ob2>
669
+ </prop3>
670
+ </root>
671
+ */
672
+ export function json2xml(obj, level = 1) {
673
+ if (obj === null || obj === undefined) {
674
+ return "";
675
+ }
676
+ const indent = " ".repeat(level);
677
+ let xmlText = "";
678
+ if (typeof obj !== "object") {
679
+ return `\n${indent}${obj.toString()}`;
680
+ }
681
+ for (const prop in obj) {
682
+ if (prop.startsWith("::")) {
683
+ continue;
684
+ }
685
+ const val = obj[prop];
686
+ let tagName = prop;
687
+ if (Array.isArray(val)) {
688
+ tagName = val["::XML_TAG"] || val[0]?.["::XML_TAG"] || prop;
689
+ } else if (val && typeof val === "object") {
690
+ tagName = val["::XML_TAG"] || prop;
691
+ } else if (Array.isArray(obj) && obj["::XML_TAG"]) {
692
+ tagName = obj["::XML_TAG"];
693
+ }
694
+
695
+ if (val === null || val === undefined) {
696
+ xmlText = `${xmlText}\n${indent}<${tagName}></${tagName}>`;
697
+ } else if (Array.isArray(val)) {
698
+ xmlText = `${xmlText}\n${indent}<${tagName}>${json2xml(val, level + 1)}\n${indent}</${tagName}>`;
699
+ } else if (typeof val === "object") {
700
+ xmlText = `${xmlText}\n${indent}<${tagName}>${json2xml(val, level + 1)}\n${indent}</${tagName}>`;
701
+ } else {
702
+ xmlText = `${xmlText}\n${indent}<${tagName}>${val.toString()}</${tagName}>`;
703
+ }
704
+ }
705
+ return xmlText;
706
+ }
707
+
708
+ function addSchemaInfoToExample(schema, obj) {
709
+ if (typeof obj !== "object" || obj === null) {
710
+ return;
711
+ }
712
+ if (schema.title) {
713
+ obj["::TITLE"] = schema.title;
714
+ }
715
+ if (schema.description) {
716
+ obj["::DESCRIPTION"] = schema.description;
717
+ }
718
+ if (schema.xml?.name) {
719
+ obj["::XML_TAG"] = schema.xml?.name;
720
+ }
721
+ if (schema.xml?.wrapped) {
722
+ obj["::XML_WRAP"] = schema.xml?.wrapped.toString();
723
+ }
724
+ }
725
+
726
+ function removeTitlesAndDescriptions(obj) {
727
+ if (typeof obj !== "object" || obj === null) {
728
+ return;
729
+ }
730
+ delete obj["::TITLE"];
731
+ delete obj["::DESCRIPTION"];
732
+ delete obj["::XML_TAG"];
733
+ delete obj["::XML_WRAP"];
734
+ for (const k in obj) {
735
+ removeTitlesAndDescriptions(obj[k]);
736
+ }
737
+ }
738
+
739
+ function mergePropertyExamples(obj, propertyName, propExamples) {
740
+ if (!obj || typeof obj !== "object") {
741
+ return obj;
742
+ }
743
+ const propVal =
744
+ propExamples && typeof propExamples === "object" && "example-0" in propExamples
745
+ ? propExamples["example-0"]
746
+ : propExamples &&
747
+ typeof propExamples === "object" &&
748
+ Object.keys(propExamples).length > 0 &&
749
+ !Array.isArray(propExamples)
750
+ ? Object.values(propExamples)[0]
751
+ : propExamples;
752
+
753
+ for (const exampleKey in obj) {
754
+ if (obj[exampleKey] && typeof obj[exampleKey] === "object") {
755
+ obj[exampleKey][propertyName] = propVal;
756
+ }
757
+ }
758
+ return obj;
759
+ }
760
+
761
+ /* For changing JSON-Schema to a Sample Object, as per the schema (to generate examples based on schema) */
762
+ export function schemaToSampleObj(schema, config = {}, level = 0) {
763
+ let obj = {};
764
+ if (!schema || level > 8) {
765
+ return;
766
+ }
767
+ if (schema.allOf) {
768
+ const mergedObj = {};
769
+
770
+ if (schema.allOf.length === 1 && !schema.allOf[0]?.properties && !schema.allOf[0]?.items) {
771
+ // If allOf has single item and the type is not an object or array, then its a primitive
772
+ if (schema.allOf[0].$ref) {
773
+ return { "example-0": {} };
774
+ }
775
+ if (schema.allOf[0].readOnly && config.includeReadOnly) {
776
+ const tempSchema = schema.allOf[0];
777
+ return { "example-0": getSampleValueByType(tempSchema) };
778
+ }
779
+ return;
780
+ }
781
+
782
+ schema.allOf.forEach((v) => {
783
+ if (!v) return;
784
+ if (v.type === "object" || v.properties || v.allOf || v.anyOf || v.oneOf) {
785
+ const partialSamples = schemaToSampleObj(v, config, level + 1);
786
+ const partialVal = partialSamples?.["example-0"] ?? partialSamples;
787
+ if (partialVal && typeof partialVal === "object" && !Array.isArray(partialVal)) {
788
+ Object.assign(mergedObj, partialVal);
789
+ }
790
+ } else if (v.type === "array" || v.items) {
791
+ const partialSamples = schemaToSampleObj(v, config, level + 1);
792
+ const partialVal = partialSamples?.["example-0"] ?? partialSamples;
793
+ if (Array.isArray(partialVal)) {
794
+ mergedObj[Object.keys(mergedObj).length] = partialVal;
795
+ }
796
+ } else if (v.type) {
797
+ const prop = `prop${Object.keys(mergedObj).length}`;
798
+ mergedObj[prop] = getSampleValueByType(v);
799
+ }
800
+ });
801
+
802
+ obj = { "example-0": mergedObj };
803
+ addSchemaInfoToExample(schema, obj["example-0"]);
804
+ } else if (schema.oneOf) {
805
+ // 1. First create example with schema.properties
806
+ const objWithSchemaProps = {};
807
+ if (schema.properties) {
808
+ for (const propertyName in schema.properties) {
809
+ const propSchema = schema.properties[propertyName];
810
+ if (propSchema?.properties || propSchema?.items) {
811
+ const propSamples = schemaToSampleObj(propSchema, config, level + 1);
812
+ objWithSchemaProps[propertyName] = propSamples?.["example-0"] ?? propSamples;
813
+ } else {
814
+ objWithSchemaProps[propertyName] = getSampleValueByType(propSchema);
815
+ }
816
+ }
817
+ }
818
+
819
+ if (schema.oneOf.length > 0) {
820
+ // If at root (level === 0), expand all oneOf variants as distinct examples.
821
+ // If nested (level > 0), only pick the first oneOf variant to avoid combinatorial duplication.
822
+ const variantsToProcess = level === 0 ? schema.oneOf : [schema.oneOf[0]];
823
+ let i = 0;
824
+ for (let k = 0; k < variantsToProcess.length; k++) {
825
+ const variantSchema = variantsToProcess[k];
826
+ const oneOfSamples = schemaToSampleObj(variantSchema, config, level + 1);
827
+ for (const sampleKey in oneOfSamples) {
828
+ let finalExample = oneOfSamples[sampleKey];
829
+ if (
830
+ Object.keys(objWithSchemaProps).length > 0 &&
831
+ finalExample &&
832
+ typeof finalExample === "object" &&
833
+ !Array.isArray(finalExample)
834
+ ) {
835
+ finalExample = { ...objWithSchemaProps, ...finalExample };
836
+ }
837
+ obj[`example-${i}`] = finalExample;
838
+ addSchemaInfoToExample(variantSchema, obj[`example-${i}`]);
839
+ i++;
840
+ if (level > 0) {
841
+ break;
842
+ }
843
+ }
844
+ }
845
+ }
846
+ } else if (schema.anyOf) {
847
+ // First generate values for regular properties
848
+ let commonObj = { "example-0": {} };
849
+ if (schema.type === "object" || schema.properties) {
850
+ for (const propertyName in schema.properties) {
851
+ const propSchema = schema.properties[propertyName];
852
+ if (propSchema?.deprecated && !config.includeDeprecated) continue;
853
+ if (propSchema?.readOnly && !config.includeReadOnly) continue;
854
+ if (propSchema?.writeOnly && !config.includeWriteOnly) continue;
855
+ const propExample = getFirstExample(propSchema);
856
+ if (propExample !== undefined) {
857
+ commonObj["example-0"][propertyName] = propExample;
858
+ } else {
859
+ mergePropertyExamples(
860
+ commonObj,
861
+ propertyName,
862
+ schemaToSampleObj(propSchema, config, level + 1),
863
+ );
864
+ }
865
+ }
866
+ }
867
+
868
+ if (schema.anyOf.length > 0) {
869
+ const variantsToProcess = level === 0 ? schema.anyOf : [schema.anyOf[0]];
870
+ let i = 0;
871
+ for (let k = 0; k < variantsToProcess.length; k++) {
872
+ const variantSchema = variantsToProcess[k];
873
+ const anyOfSamples = schemaToSampleObj(variantSchema, config, level + 1);
874
+ for (const sampleKey in anyOfSamples) {
875
+ const sampleVal = anyOfSamples[sampleKey];
876
+ if (commonObj?.["example-0"] && Object.keys(commonObj["example-0"]).length > 0) {
877
+ obj[`example-${i}`] = {
878
+ ...commonObj["example-0"],
879
+ ...(typeof sampleVal === "object" ? sampleVal : {}),
880
+ };
881
+ } else {
882
+ obj[`example-${i}`] = sampleVal;
883
+ }
884
+ addSchemaInfoToExample(variantSchema, obj[`example-${i}`]);
885
+ i++;
886
+ if (level > 0) {
887
+ break;
888
+ }
889
+ }
890
+ }
891
+ }
892
+ } else if (schema.type === "object" || schema.properties) {
893
+ obj["example-0"] = {};
894
+ addSchemaInfoToExample(schema, obj["example-0"]);
895
+ const firstExample = getFirstExample(schema);
896
+ if (firstExample !== undefined) {
897
+ obj["example-0"] = firstExample;
898
+ } else {
899
+ for (const propertyName in schema.properties) {
900
+ const propSchema = schema.properties[propertyName];
901
+ if (!propSchema) continue;
902
+ if (propSchema.deprecated && !config.includeDeprecated) continue;
903
+ if (propSchema.readOnly && !config.includeReadOnly) continue;
904
+ if (propSchema.writeOnly && !config.includeWriteOnly) continue;
905
+
906
+ if (propSchema.type === "array" || propSchema.items) {
907
+ const propExample = getFirstExample(propSchema);
908
+ if (propExample !== undefined) {
909
+ obj["example-0"][propertyName] = propExample;
910
+ } else if (getFirstExample(propSchema.items) !== undefined) {
911
+ obj["example-0"][propertyName] = [getFirstExample(propSchema.items)];
912
+ } else {
913
+ const itemSamples = schemaToSampleObj(propSchema.items, config, level + 1);
914
+ const itemSampleVal = itemSamples?.["example-0"] ?? itemSamples;
915
+ if (config.useXmlTagForProp) {
916
+ const xmlTagName = propSchema.xml?.name || propertyName;
917
+ if (propSchema.xml?.wrapped) {
918
+ obj["example-0"][xmlTagName] = { [xmlTagName]: itemSampleVal };
919
+ } else {
920
+ obj["example-0"][xmlTagName] = [itemSampleVal];
921
+ }
922
+ } else {
923
+ obj["example-0"][propertyName] = [itemSampleVal];
924
+ }
925
+ }
926
+ continue;
927
+ }
928
+
929
+ mergePropertyExamples(obj, propertyName, schemaToSampleObj(propSchema, config, level + 1));
930
+ }
931
+
932
+ if (typeof schema.additionalProperties === "object" && schema.additionalProperties !== null) {
933
+ const propName = schema.additionalProperties["x-additionalPropertiesName"] || "property";
934
+ mergePropertyExamples(
935
+ obj,
936
+ `${propName}1`,
937
+ schemaToSampleObj(schema.additionalProperties, config, level + 1),
938
+ );
939
+ mergePropertyExamples(
940
+ obj,
941
+ `${propName}2`,
942
+ schemaToSampleObj(schema.additionalProperties, config, level + 1),
943
+ );
944
+ }
945
+ }
946
+ } else if (schema.type === "array" || schema.items) {
947
+ const firstExample = getFirstExample(schema);
948
+ if (firstExample !== undefined) {
949
+ obj["example-0"] = firstExample;
950
+ } else if (schema.items) {
951
+ const itemsExample = getFirstExample(schema.items);
952
+ if (itemsExample !== undefined) {
953
+ obj["example-0"] = [itemsExample];
954
+ } else {
955
+ const samples = schemaToSampleObj(schema.items, config, level + 1);
956
+ const itemVal = samples?.["example-0"] ?? samples;
957
+ obj["example-0"] = [itemVal];
958
+ addSchemaInfoToExample(schema.items, obj["example-0"]);
959
+ }
960
+ } else {
961
+ obj["example-0"] = [];
962
+ }
963
+ } else {
964
+ return { "example-0": getSampleValueByType(schema) };
965
+ }
966
+ return obj;
967
+ }
968
+
969
+ function generateMarkdownForArrayAndObjectDescription(schema, level = 0) {
970
+ let mainText = "";
971
+ if (schema.title) {
972
+ if (schema.description) {
973
+ mainText = `<b>${schema.title}:</b> ${schema.description}`;
974
+ } else {
975
+ mainText = schema.title;
976
+ }
977
+ } else if (schema.description) {
978
+ mainText = schema.description;
979
+ }
980
+
981
+ const limits = [];
982
+ if (schema.minItems) {
983
+ limits.push(
984
+ `<span class="descr-label">Min Items:</span> <span class="descr-value">${schema.minItems}</span>`,
985
+ );
986
+ }
987
+ if (schema.maxItems) {
988
+ limits.push(
989
+ `<span class="descr-label">Max Items:</span> <span class="descr-value">${schema.maxItems}</span>`,
990
+ );
991
+ }
992
+ if (schema.uniqueItems === true) {
993
+ limits.push(`<span class="descr-label">Must have unique items</span>`);
994
+ }
995
+
996
+ const parts = mainText ? [mainText.trim()] : [];
997
+ if (limits.length > 0) {
998
+ // Same wrapper as the constraint chips, so both sit the same distance under the description.
999
+ // Kept on one line so markdown treats it as a single block.
1000
+ parts.push(`<div class="item-details">${limits.join(" ")}</div>`);
1001
+ }
1002
+ if (level > 0 && schema.items?.description) {
1003
+ let itemsMarkdown = "";
1004
+ if (schema.items.minProperties) {
1005
+ itemsMarkdown = `<span class="descr-label">Min Properties:</span> <span class="descr-value">${schema.items.minProperties}</span>`;
1006
+ }
1007
+ if (schema.items.maxProperties) {
1008
+ itemsMarkdown = `${itemsMarkdown} <span class="descr-label">Max Properties:</span> <span class="descr-value">${schema.items.maxProperties}</span>`;
1009
+ }
1010
+ parts.push(`⮕ ${itemsMarkdown} [ ${schema.items.description} ]`);
1011
+ }
1012
+ return parts.join("\n\n");
1013
+ }
1014
+ /**
1015
+ * For changing OpenAPI-Schema to an Object Notation,
1016
+ * This Object would further be an input to UI Components to generate an Object-Tree
1017
+ * @param {object} schema - Schema object from OpenAPI spec
1018
+ * @param {object} obj - recursivly pass this object to generate object notation
1019
+ * @param {number} level - recursion level
1020
+ * @param {string} suffix - used for suffixing property names to avoid duplicate props during object composion
1021
+ */
1022
+ /**
1023
+ * Helper to populate schema properties into a target object notation.
1024
+ */
1025
+ function populateProperties(target, schema, level) {
1026
+ if (schema.properties) {
1027
+ for (const key in schema.properties) {
1028
+ const propKey = schema.required && schema.required.includes(key) ? `${key}*` : key;
1029
+ target[propKey] = schemaInObjectNotation(schema.properties[key], {}, level + 1);
1030
+ }
1031
+ }
1032
+ if (schema.patternProperties) {
1033
+ for (const key in schema.patternProperties) {
1034
+ target[`[pattern: ${key}]`] = schemaInObjectNotation(
1035
+ schema.patternProperties[key],
1036
+ {},
1037
+ level + 1,
1038
+ );
1039
+ }
1040
+ }
1041
+ if (schema.additionalProperties) {
1042
+ target["[any-key]"] = schemaInObjectNotation(schema.additionalProperties, {}, level + 1);
1043
+ }
1044
+ }
1045
+
1046
+ function handleObjectSchema(schema, baseObj = {}, level = 0) {
1047
+ const obj = Object.assign({}, baseObj);
1048
+ obj["::title"] = schema.title || "";
1049
+ obj["::description"] = generateMarkdownForArrayAndObjectDescription(schema, level);
1050
+ obj["::type"] = "object";
1051
+ if ((Array.isArray(schema.type) && schema.type.includes("null")) || schema.nullable) {
1052
+ obj["::dataTypeLabel"] = "object ┃ null";
1053
+ obj["::nullable"] = true;
1054
+ }
1055
+ obj["::deprecated"] = schema.deprecated || false;
1056
+ obj["::readwrite"] = schema.readOnly ? "readonly" : schema.writeOnly ? "writeonly" : "";
1057
+ populateProperties(obj, schema, level);
1058
+ return obj;
1059
+ }
1060
+
1061
+ function handleArraySchema(schema, baseObj = {}, level = 0) {
1062
+ const obj = Object.assign({}, baseObj);
1063
+ obj["::title"] = schema.title || "";
1064
+ obj["::description"] = generateMarkdownForArrayAndObjectDescription(schema, level);
1065
+ obj["::type"] = "array";
1066
+ if ((Array.isArray(schema.type) && schema.type.includes("null")) || schema.nullable) {
1067
+ obj["::dataTypeLabel"] = "array ┃ null";
1068
+ obj["::nullable"] = true;
1069
+ }
1070
+ obj["::deprecated"] = schema.deprecated || false;
1071
+ obj["::readwrite"] = schema.readOnly ? "readonly" : schema.writeOnly ? "writeonly" : "";
1072
+ if (schema.items?.items) {
1073
+ obj["::array-type"] = schema.items.items.type;
1074
+ }
1075
+ obj["::props"] = schema.items ? schemaInObjectNotation(schema.items, {}, level + 1) : undefined;
1076
+ return obj;
1077
+ }
1078
+
1079
+ function handleAllOfSchema(schema, baseObj = {}, level = 0) {
1080
+ if (schema.allOf.length === 1 && !schema.allOf[0].properties && !schema.allOf[0].items) {
1081
+ // If allOf has single item and the type is not an object or array, then its a primitive
1082
+ return `${getTypeInfo(schema.allOf[0]).html}`;
1083
+ }
1084
+
1085
+ const objWithAllProps = Object.assign({}, baseObj);
1086
+ schema.allOf.forEach((v, i) => {
1087
+ if (v.type === "object" || v.properties || v.allOf || v.anyOf || v.oneOf) {
1088
+ const propSuffix = (v.anyOf || v.oneOf) && i > 0 ? `${i}` : "";
1089
+ const partialObj = schemaInObjectNotation(v, {}, level + 1, propSuffix);
1090
+ Object.assign(objWithAllProps, partialObj);
1091
+ } else if (v.type === "array" || v.items) {
1092
+ const partialObj = schemaInObjectNotation(v, {}, level + 1);
1093
+ Object.assign(objWithAllProps, partialObj);
1094
+ } else if (v.type) {
1095
+ const prop = `prop${Object.keys(objWithAllProps).length}`;
1096
+ const typeObj = getTypeInfo(v);
1097
+ objWithAllProps[prop] = `${typeObj.html}`;
1098
+ }
1099
+ });
1100
+
1101
+ return objWithAllProps;
1102
+ }
1103
+
1104
+ function handleAnyOrOneOfSchema(schema, baseObj = {}, level = 0, suffix = "") {
1105
+ const obj = Object.assign({}, baseObj);
1106
+ obj["::description"] = schema.description || "";
1107
+
1108
+ // 1. First iterate the regular properties if defined
1109
+ if (schema.type === "object" || schema.properties) {
1110
+ obj["::type"] = "object";
1111
+ populateProperties(obj, schema, level);
1112
+ }
1113
+
1114
+ // 2. Then build anyOf / oneOf option entries
1115
+ const objWithAnyOfProps = {};
1116
+ const xxxOf = schema.anyOf ? "anyOf" : "oneOf";
1117
+ schema[xxxOf].forEach((v, index) => {
1118
+ const optKey = `::OPTION~${index + 1}${v.title ? `~${v.title}` : ""}`;
1119
+ if (v.type === "object" || v.properties || v.allOf || v.anyOf || v.oneOf) {
1120
+ const partialObj = schemaInObjectNotation(v, {}, level + 1);
1121
+ objWithAnyOfProps[optKey] = partialObj;
1122
+ if (typeof partialObj === "object" && partialObj !== null) {
1123
+ partialObj["::readwrite"] = ""; // xxx-options cannot be read or write only
1124
+ }
1125
+ objWithAnyOfProps["::type"] = "xxx-of-option";
1126
+ } else if (v.type === "array" || v.items) {
1127
+ const partialObj = schemaInObjectNotation(v, {}, level + 1);
1128
+ objWithAnyOfProps[optKey] = partialObj;
1129
+ if (typeof partialObj === "object" && partialObj !== null) {
1130
+ partialObj["::readwrite"] = "";
1131
+ }
1132
+ objWithAnyOfProps["::type"] = "xxx-of-array";
1133
+ } else {
1134
+ objWithAnyOfProps[optKey] = `${getTypeInfo(v).html}`;
1135
+ objWithAnyOfProps["::type"] = "xxx-of-option";
1136
+ }
1137
+ });
1138
+
1139
+ const operatorKey = schema.anyOf ? "::ANY~OF" : "::ONE~OF";
1140
+ const fullKey = suffix ? `${operatorKey} ${suffix}` : operatorKey;
1141
+ obj[fullKey] = objWithAnyOfProps;
1142
+ obj["::type"] = "object";
1143
+
1144
+ return obj;
1145
+ }
1146
+
1147
+ function handleMultiTypeSchema(schema, baseObj = {}, level = 0) {
1148
+ // Recognize OpenAPI 3.1 nullable object and array
1149
+ if (schema.type.length === 2 && schema.type.includes("null")) {
1150
+ if (schema.type.includes("object")) {
1151
+ return handleObjectSchema(schema, baseObj, level);
1152
+ }
1153
+ if (schema.type.includes("array")) {
1154
+ return handleArraySchema(schema, baseObj, level);
1155
+ }
1156
+ }
1157
+
1158
+ const subSchema =
1159
+ typeof structuredClone === "function"
1160
+ ? structuredClone(schema)
1161
+ : JSON.parse(JSON.stringify(schema));
1162
+ const primitiveType = [];
1163
+ const complexTypes = [];
1164
+
1165
+ subSchema.type.forEach((v) => {
1166
+ if (v.match(/integer|number|string|null|boolean/g)) {
1167
+ primitiveType.push(v);
1168
+ } else if (
1169
+ v === "array" &&
1170
+ typeof subSchema.items?.type === "string" &&
1171
+ subSchema.items?.type.match(/integer|number|string|null|boolean/g)
1172
+ ) {
1173
+ if (
1174
+ subSchema.items.type === "string" &&
1175
+ (subSchema.items.format || subSchema.items.contentMediaType)
1176
+ ) {
1177
+ primitiveType.push(`[${subSchema.items.format || subSchema.items.contentMediaType}]`);
1178
+ } else {
1179
+ primitiveType.push(`[${subSchema.items.type}]`);
1180
+ }
1181
+ } else {
1182
+ complexTypes.push(v);
1183
+ }
1184
+ });
1185
+
1186
+ let multiPrimitiveTypes;
1187
+ if (primitiveType.length > 0) {
1188
+ subSchema.type = primitiveType.join("┃");
1189
+ multiPrimitiveTypes = getTypeInfo(subSchema);
1190
+ if (complexTypes.length === 0) {
1191
+ return `${multiPrimitiveTypes?.html || ""}`;
1192
+ }
1193
+ }
1194
+
1195
+ if (complexTypes.length > 0) {
1196
+ const obj = Object.assign({}, baseObj);
1197
+ obj["::type"] = "object";
1198
+ const multiTypeOptions = {
1199
+ "::type": "xxx-of-option",
1200
+ };
1201
+
1202
+ let optionIndex = 1;
1203
+ complexTypes.forEach((v) => {
1204
+ if (v === "object") {
1205
+ const objTypeOption = {
1206
+ "::title": schema.title || "",
1207
+ "::description": schema.description || "",
1208
+ "::type": "object",
1209
+ "::deprecated": schema.deprecated || false,
1210
+ };
1211
+ populateProperties(objTypeOption, schema, level);
1212
+ multiTypeOptions[`::OPTION~${optionIndex++}`] = objTypeOption;
1213
+ } else if (v === "array") {
1214
+ multiTypeOptions[`::OPTION~${optionIndex++}`] = {
1215
+ "::title": schema.title || "",
1216
+ "::description": schema.description || "",
1217
+ "::type": "array",
1218
+ "::props": schemaInObjectNotation(schema.items, {}, level + 1),
1219
+ };
1220
+ }
1221
+ });
1222
+
1223
+ if (primitiveType.length > 0 && multiPrimitiveTypes?.html) {
1224
+ multiTypeOptions[`::OPTION~${optionIndex++}`] = multiPrimitiveTypes.html;
1225
+ }
1226
+
1227
+ obj["::ONE~OF"] = multiTypeOptions;
1228
+ return obj;
1229
+ }
1230
+
1231
+ return Object.assign({}, baseObj);
1232
+ }
1233
+
1234
+ function createObjectAST(schema, level, name, isRequired) {
1235
+ const isNullable =
1236
+ schema.nullable || (Array.isArray(schema.type) && schema.type.includes("null")) || false;
1237
+ const properties = [];
1238
+ if (schema.properties) {
1239
+ for (const key in schema.properties) {
1240
+ const isPropReq = Array.isArray(schema.required) && schema.required.includes(key);
1241
+ const child = schemaToAST(schema.properties[key], level + 1, key, isPropReq);
1242
+ if (child) properties.push(child);
1243
+ }
1244
+ }
1245
+
1246
+ const patternProperties = [];
1247
+ if (schema.patternProperties) {
1248
+ for (const key in schema.patternProperties) {
1249
+ const child = schemaToAST(
1250
+ schema.patternProperties[key],
1251
+ level + 1,
1252
+ `[pattern: ${key}]`,
1253
+ false,
1254
+ );
1255
+ if (child) patternProperties.push(child);
1256
+ }
1257
+ }
1258
+
1259
+ let additionalProperties = null;
1260
+ if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
1261
+ additionalProperties = schemaToAST(schema.additionalProperties, level + 1, "[any-key]", false);
1262
+ }
1263
+
1264
+ return {
1265
+ kind: "object",
1266
+ name,
1267
+ title: schema.title || "",
1268
+ description: generateMarkdownForArrayAndObjectDescription(schema, level),
1269
+ required: isRequired,
1270
+ deprecated: schema.deprecated || false,
1271
+ readOnly: schema.readOnly || false,
1272
+ writeOnly: schema.writeOnly || false,
1273
+ nullable: isNullable,
1274
+ dataTypeLabel: isNullable ? "object ┃ null" : "object",
1275
+ properties,
1276
+ patternProperties,
1277
+ additionalProperties,
1278
+ };
1279
+ }
1280
+
1281
+ function createArrayAST(schema, level, name, isRequired) {
1282
+ const isNullable =
1283
+ schema.nullable || (Array.isArray(schema.type) && schema.type.includes("null")) || false;
1284
+ let itemsNode = null;
1285
+ if (schema.items) {
1286
+ itemsNode = schemaToAST(schema.items, level + 1, "", false);
1287
+ }
1288
+
1289
+ let arrayType = "";
1290
+ if (schema.items?.items) {
1291
+ arrayType = schema.items.items.type || "";
1292
+ } else if (schema.items?.type && typeof schema.items.type === "string") {
1293
+ arrayType = schema.items.type;
1294
+ }
1295
+
1296
+ return {
1297
+ kind: "array",
1298
+ name,
1299
+ title: schema.title || "",
1300
+ description: generateMarkdownForArrayAndObjectDescription(schema, level),
1301
+ required: isRequired,
1302
+ deprecated: schema.deprecated || false,
1303
+ readOnly: schema.readOnly || false,
1304
+ writeOnly: schema.writeOnly || false,
1305
+ nullable: isNullable,
1306
+ dataTypeLabel: isNullable ? "array ┃ null" : "array",
1307
+ arrayType,
1308
+ minItems: schema.minItems,
1309
+ maxItems: schema.maxItems,
1310
+ uniqueItems: schema.uniqueItems || false,
1311
+ items: itemsNode,
1312
+ };
1313
+ }
1314
+
1315
+ function createPrimitiveAST(schema, name, isRequired) {
1316
+ const typeInfo = getTypeInfo(schema) || {};
1317
+ return {
1318
+ kind: "primitive",
1319
+ name,
1320
+ type: typeInfo.type || schema.type || "",
1321
+ format: typeInfo.format || schema.format || "",
1322
+ contentMediaType: typeInfo.contentMediaType || schema.contentMediaType || "",
1323
+ contentEncoding: typeInfo.contentEncoding || schema.contentEncoding || "",
1324
+ contentSchema: typeInfo.contentSchema || schema.contentSchema || null,
1325
+ pattern: typeInfo.pattern || schema.pattern || "",
1326
+ constraints: typeInfo.constrain || "",
1327
+ defaultValue:
1328
+ typeInfo.default !== undefined && typeInfo.default !== "" ? String(typeInfo.default) : "",
1329
+ allowedValues: typeInfo.allowedValues || "",
1330
+ description: (schema.description || typeInfo.description || "").trim(),
1331
+ title: (schema.title || "").trim(),
1332
+ required: isRequired,
1333
+ deprecated: schema.deprecated || !!typeInfo.deprecated,
1334
+ readOnly: schema.readOnly || typeInfo.readOrWriteOnly === "🆁",
1335
+ writeOnly: schema.writeOnly || typeInfo.readOrWriteOnly === "🆆",
1336
+ html: typeInfo.html || "",
1337
+ };
1338
+ }
1339
+
1340
+ // Deepest JSON nesting level expanded; anything below an object or array at this level is truncated
1341
+ const MAX_SCHEMA_DEPTH = 8;
1342
+ // Largest tree built for one schema. A bigger tree (e.g. Stripe's expandable fields) is rebuilt one level shallower,
1343
+ // down to MIN_SCHEMA_DEPTH, so the page stays responsive
1344
+ const SCHEMA_NODE_BUDGET = 8000;
1345
+ const MIN_SCHEMA_DEPTH = 4;
1346
+
1347
+ /**
1348
+ * Whether a schema opens a new JSON nesting level (an object or array), directly or through allOf.
1349
+ * Primitives and unions are not containers: primitives cost nothing to show, and union options are checked one by one.
1350
+ * @param {object} schema - OpenAPI/JSON Schema object
1351
+ * @returns {boolean}
1352
+ */
1353
+ function isContainerSchema(schema) {
1354
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
1355
+ return (
1356
+ types.includes("object") ||
1357
+ types.includes("array") ||
1358
+ !!(schema.properties || schema.patternProperties || schema.items) ||
1359
+ (!!schema.additionalProperties && typeof schema.additionalProperties === "object") ||
1360
+ !!schema.allOf?.some((sub) => sub && isContainerSchema(sub))
1361
+ );
1362
+ }
1363
+
1364
+ // State of the schemaToAST() call in progress (conversion is synchronous)
1365
+ // Schemas on the current path, to stop on cycles that only go through allOf/anyOf/oneOf
1366
+ const schemasInProgress = new Set();
1367
+ let maxDepth = MAX_SCHEMA_DEPTH;
1368
+ let nodeCount = 0;
1369
+
1370
+ /**
1371
+ * Transforms an OpenAPI Schema into a strongly-typed Abstract Syntax Tree (AST) node.
1372
+ *
1373
+ * @param {object} schema - OpenAPI/JSON Schema object
1374
+ * @param {number} [level=0] - JSON nesting depth: grows through properties, items and additionalProperties, not through allOf/anyOf/oneOf
1375
+ * @param {string} [name=''] - Property or field name
1376
+ * @param {boolean} [isRequired=false] - Whether this property is required
1377
+ * @param {string} [suffix=''] - Suffix used for union composition
1378
+ * @returns {object|null} Typed SchemaNode AST
1379
+ */
1380
+ export function schemaToAST(schema, level = 0, name = "", isRequired = false, suffix = "") {
1381
+ if (!schema) {
1382
+ return null;
1383
+ }
1384
+ if (schemasInProgress.size > 0) {
1385
+ return nodeToAST(schema, level, name, isRequired, suffix);
1386
+ }
1387
+ // Top-level call: expand as deep as allowed while the tree fits the budget
1388
+ for (maxDepth = MAX_SCHEMA_DEPTH; ; maxDepth -= 1) {
1389
+ nodeCount = 0;
1390
+ const ast = nodeToAST(schema, level, name, isRequired, suffix);
1391
+ if (nodeCount <= SCHEMA_NODE_BUDGET || maxDepth <= MIN_SCHEMA_DEPTH) {
1392
+ return ast;
1393
+ }
1394
+ }
1395
+ }
1396
+
1397
+ function nodeToAST(schema, level, name, isRequired, suffix) {
1398
+ if (!schema) {
1399
+ return null;
1400
+ }
1401
+ nodeCount += 1;
1402
+ // Once over budget this attempt is discarded, so stop expanding to get to the next one quickly
1403
+ const overBudget = nodeCount > SCHEMA_NODE_BUDGET && maxDepth > MIN_SCHEMA_DEPTH;
1404
+ if (
1405
+ schemasInProgress.has(schema) ||
1406
+ ((level > maxDepth || overBudget) && isContainerSchema(schema))
1407
+ ) {
1408
+ const isArray =
1409
+ schema.type === "array" ||
1410
+ (Array.isArray(schema.type) && schema.type.includes("array")) ||
1411
+ !!schema.items;
1412
+ return {
1413
+ kind: isArray ? "array" : "object",
1414
+ name,
1415
+ type: isArray ? "array" : "object",
1416
+ title: schema.title || "",
1417
+ description: schema.description || "",
1418
+ required: isRequired,
1419
+ truncated: true,
1420
+ };
1421
+ }
1422
+ schemasInProgress.add(schema);
1423
+ try {
1424
+ return composeSchemaAST(schema, level, name, isRequired, suffix);
1425
+ } finally {
1426
+ schemasInProgress.delete(schema);
1427
+ }
1428
+ }
1429
+
1430
+ function composeSchemaAST(schema, level, name, isRequired, suffix) {
1431
+ // 1. allOf
1432
+ if (schema.allOf) {
1433
+ if (schema.allOf.length === 1 && !schema.allOf[0].properties && !schema.allOf[0].items) {
1434
+ const mergedSchema = {
1435
+ ...schema.allOf[0],
1436
+ ...schema,
1437
+ title: schema.title || schema.allOf[0].title || "",
1438
+ description: schema.description || schema.allOf[0].description || "",
1439
+ };
1440
+ delete mergedSchema.allOf;
1441
+ return schemaToAST(mergedSchema, level, name, isRequired);
1442
+ }
1443
+
1444
+ const mergedProps = [];
1445
+ const mergedPatternProps = [];
1446
+ let mergedAdditionalProps = null;
1447
+ const unionChildren = [];
1448
+ let mergedTitle = schema.title || "";
1449
+ let mergedDescription = schema.description || "";
1450
+ let mergedDeprecated = schema.deprecated || false;
1451
+ let mergedReadOnly = schema.readOnly || false;
1452
+ let mergedWriteOnly = schema.writeOnly || false;
1453
+ let mergedNullable = schema.nullable || false;
1454
+
1455
+ schema.allOf.forEach((sub, i) => {
1456
+ if (sub.title && !mergedTitle) mergedTitle = sub.title;
1457
+ if (sub.description && !mergedDescription) mergedDescription = sub.description;
1458
+ if (sub.deprecated) mergedDeprecated = true;
1459
+ if (sub.readOnly) mergedReadOnly = true;
1460
+ if (sub.writeOnly) mergedWriteOnly = true;
1461
+ if (sub.nullable) mergedNullable = true;
1462
+
1463
+ if (sub.type === "object" || sub.properties || sub.allOf || sub.anyOf || sub.oneOf) {
1464
+ const subSuffix = (sub.anyOf || sub.oneOf) && i > 0 ? `${i}` : "";
1465
+ const subAst = schemaToAST(sub, level, "", false, subSuffix);
1466
+ if (subAst) {
1467
+ if (subAst.kind === "object") {
1468
+ if (subAst.properties) mergedProps.push(...subAst.properties);
1469
+ if (subAst.patternProperties) mergedPatternProps.push(...subAst.patternProperties);
1470
+ if (subAst.additionalProperties) mergedAdditionalProps = subAst.additionalProperties;
1471
+ } else if (subAst.kind === "union") {
1472
+ unionChildren.push(subAst);
1473
+ }
1474
+ }
1475
+ } else if (sub.type === "array" || sub.items) {
1476
+ const subAst = schemaToAST(sub, level);
1477
+ if (subAst) {
1478
+ mergedProps.push(subAst);
1479
+ }
1480
+ } else if (sub.type) {
1481
+ const propName = `prop${mergedProps.length}`;
1482
+ mergedProps.push(schemaToAST(sub, level, propName, false));
1483
+ }
1484
+ });
1485
+
1486
+ return {
1487
+ kind: "object",
1488
+ name,
1489
+ title: mergedTitle,
1490
+ description: generateMarkdownForArrayAndObjectDescription(
1491
+ { ...schema, title: mergedTitle, description: mergedDescription },
1492
+ level,
1493
+ ),
1494
+ required: isRequired,
1495
+ deprecated: mergedDeprecated,
1496
+ readOnly: mergedReadOnly,
1497
+ writeOnly: mergedWriteOnly,
1498
+ nullable: mergedNullable,
1499
+ dataTypeLabel: mergedNullable ? "object ┃ null" : "object",
1500
+ properties: mergedProps,
1501
+ patternProperties: mergedPatternProps,
1502
+ additionalProperties: mergedAdditionalProps,
1503
+ unions: unionChildren,
1504
+ };
1505
+ }
1506
+
1507
+ // 2. anyOf / oneOf
1508
+ if (schema.anyOf || schema.oneOf) {
1509
+ const operator = schema.anyOf ? "anyOf" : "oneOf";
1510
+ const rawOptions = schema.anyOf || schema.oneOf;
1511
+ const options = rawOptions
1512
+ .map((opt, idx) => {
1513
+ const optTitle = opt.title || "";
1514
+ const optAst = schemaToAST(opt, level, optTitle || `Option ${idx + 1}`, false);
1515
+ if (optAst) {
1516
+ optAst.readOnly = false;
1517
+ optAst.writeOnly = false;
1518
+ optAst.optionIndex = idx + 1;
1519
+ optAst.optionTitle = optTitle;
1520
+ if (optAst.kind === "primitive" && !optTitle) {
1521
+ // The OPT n badge already names it, a row labelled "Option n" would only repeat it
1522
+ optAst.name = "";
1523
+ }
1524
+ }
1525
+ return optAst;
1526
+ })
1527
+ .filter(Boolean);
1528
+
1529
+ let baseProps = [];
1530
+ if (schema.type === "object" || schema.properties) {
1531
+ if (schema.properties) {
1532
+ for (const key in schema.properties) {
1533
+ const isReq = Array.isArray(schema.required) && schema.required.includes(key);
1534
+ const propNode = schemaToAST(schema.properties[key], level + 1, key, isReq);
1535
+ if (propNode) baseProps.push(propNode);
1536
+ }
1537
+ }
1538
+ }
1539
+
1540
+ return {
1541
+ kind: "union",
1542
+ operator,
1543
+ name,
1544
+ suffix,
1545
+ title: schema.title || "",
1546
+ description: schema.description || "",
1547
+ required: isRequired,
1548
+ properties: baseProps,
1549
+ options,
1550
+ };
1551
+ }
1552
+
1553
+ // 3. Multi-type array (OpenAPI 3.1)
1554
+ if (Array.isArray(schema.type)) {
1555
+ if (schema.type.length === 2 && schema.type.includes("null")) {
1556
+ if (schema.type.includes("object")) {
1557
+ return createObjectAST(schema, level, name, isRequired);
1558
+ }
1559
+ if (schema.type.includes("array")) {
1560
+ return createArrayAST(schema, level, name, isRequired);
1561
+ }
1562
+ }
1563
+
1564
+ const subSchema =
1565
+ typeof structuredClone === "function"
1566
+ ? structuredClone(schema)
1567
+ : JSON.parse(JSON.stringify(schema));
1568
+ const primitiveType = [];
1569
+ const complexTypes = [];
1570
+
1571
+ subSchema.type.forEach((v) => {
1572
+ if (v.match(/integer|number|string|null|boolean/g)) {
1573
+ primitiveType.push(v);
1574
+ } else if (
1575
+ v === "array" &&
1576
+ typeof subSchema.items?.type === "string" &&
1577
+ subSchema.items?.type.match(/integer|number|string|null|boolean/g)
1578
+ ) {
1579
+ if (
1580
+ subSchema.items.type === "string" &&
1581
+ (subSchema.items.format || subSchema.items.contentMediaType)
1582
+ ) {
1583
+ primitiveType.push(`[${subSchema.items.format || subSchema.items.contentMediaType}]`);
1584
+ } else {
1585
+ primitiveType.push(`[${subSchema.items.type}]`);
1586
+ }
1587
+ } else {
1588
+ complexTypes.push(v);
1589
+ }
1590
+ });
1591
+
1592
+ if (complexTypes.length === 0 && primitiveType.length > 0) {
1593
+ subSchema.type = primitiveType.join("┃");
1594
+ return createPrimitiveAST(subSchema, name, isRequired);
1595
+ }
1596
+
1597
+ if (complexTypes.length > 0) {
1598
+ const options = [];
1599
+ let optionIndex = 1;
1600
+ complexTypes.forEach((v) => {
1601
+ if (v === "object") {
1602
+ const objNode = createObjectAST(
1603
+ schema,
1604
+ level,
1605
+ schema.title || `Option ${optionIndex}`,
1606
+ false,
1607
+ );
1608
+ objNode.optionIndex = optionIndex++;
1609
+ objNode.optionTitle = schema.title || "";
1610
+ options.push(objNode);
1611
+ } else if (v === "array") {
1612
+ const arrNode = createArrayAST(
1613
+ schema,
1614
+ level,
1615
+ schema.title || `Option ${optionIndex}`,
1616
+ false,
1617
+ );
1618
+ arrNode.optionIndex = optionIndex++;
1619
+ arrNode.optionTitle = schema.title || "";
1620
+ options.push(arrNode);
1621
+ }
1622
+ });
1623
+
1624
+ if (primitiveType.length > 0) {
1625
+ subSchema.type = primitiveType.join("┃");
1626
+ const primNode = createPrimitiveAST(subSchema, `Option ${optionIndex}`, false);
1627
+ primNode.optionIndex = optionIndex++;
1628
+ options.push(primNode);
1629
+ }
1630
+
1631
+ return {
1632
+ kind: "union",
1633
+ operator: "oneOf",
1634
+ name,
1635
+ title: schema.title || "",
1636
+ description: schema.description || "",
1637
+ required: isRequired,
1638
+ options,
1639
+ };
1640
+ }
1641
+ }
1642
+
1643
+ // 4. Object
1644
+ if (schema.type === "object" || schema.properties) {
1645
+ return createObjectAST(schema, level, name, isRequired);
1646
+ }
1647
+
1648
+ // 5. Array
1649
+ if (schema.type === "array" || schema.items) {
1650
+ return createArrayAST(schema, level, name, isRequired);
1651
+ }
1652
+
1653
+ // 6. Primitive
1654
+ return createPrimitiveAST(schema, name, isRequired);
1655
+ }
1656
+
1657
+ /**
1658
+ * For changing OpenAPI-Schema to an Object Notation,
1659
+ * This Object would further be an input to UI Components to generate an Object-Tree
1660
+ * @deprecated Use schemaToAST instead.
1661
+ * @param {object} schema - Schema object from OpenAPI spec
1662
+ * @param {object} [obj={}] - base object to populate (defaults to new object)
1663
+ * @param {number} [level=0] - recursion level
1664
+ * @param {string} [suffix=''] - used for suffixing property names to avoid duplicate props during object composition
1665
+ */
1666
+ export function schemaInObjectNotation(schema, obj = {}, level = 0, suffix = "") {
1667
+ if (!schema) {
1668
+ return;
1669
+ }
1670
+ if (level > 8) {
1671
+ return {
1672
+ ...obj,
1673
+ "::type": schema.type || "object",
1674
+ "::description": schema.description || "",
1675
+ };
1676
+ }
1677
+ if (schema.allOf) {
1678
+ return handleAllOfSchema(schema, obj, level);
1679
+ }
1680
+ if (schema.anyOf || schema.oneOf) {
1681
+ return handleAnyOrOneOfSchema(schema, obj, level, suffix);
1682
+ }
1683
+ if (Array.isArray(schema.type)) {
1684
+ return handleMultiTypeSchema(schema, obj, level);
1685
+ }
1686
+ if (schema.type === "object" || schema.properties) {
1687
+ return handleObjectSchema(schema, obj, level);
1688
+ }
1689
+ if (schema.type === "array" || schema.items) {
1690
+ return handleArraySchema(schema, obj, level);
1691
+ }
1692
+ const typeObj = getTypeInfo(schema);
1693
+ if (typeObj?.html) {
1694
+ return `${typeObj.html}`;
1695
+ }
1696
+ return "";
1697
+ }
1698
+
1699
+ /**
1700
+ * Helper to consistently format raw example values according to mimeType and outputType.
1701
+ */
1702
+ function formatExampleContent(rawVal, mimeType = "", outputType = "json") {
1703
+ let content = rawVal;
1704
+ let format = "text";
1705
+
1706
+ if (mimeType?.toLowerCase().includes("json")) {
1707
+ if (outputType === "text") {
1708
+ content = typeof rawVal === "string" ? rawVal : JSON.stringify(rawVal, undefined, 2);
1709
+ format = "text";
1710
+ } else if (typeof rawVal === "object" && rawVal !== null) {
1711
+ content = rawVal;
1712
+ format = "json";
1713
+ } else if (typeof rawVal === "string") {
1714
+ try {
1715
+ content = JSON.parse(rawVal);
1716
+ format = "json";
1717
+ } catch {
1718
+ content = rawVal;
1719
+ format = "text";
1720
+ }
1721
+ } else {
1722
+ content = rawVal;
1723
+ format = "json";
1724
+ }
1725
+ } else {
1726
+ content =
1727
+ typeof rawVal === "object" && rawVal !== null
1728
+ ? JSON.stringify(rawVal, undefined, 2)
1729
+ : String(rawVal ?? "");
1730
+ format = "text";
1731
+ }
1732
+
1733
+ return { content, format };
1734
+ }
1735
+
1736
+ /* Create Example object */
1737
+ export function generateExample(
1738
+ schema,
1739
+ mimeType = "",
1740
+ examples = null,
1741
+ example = null,
1742
+ includeReadOnly = true,
1743
+ includeWriteOnly = true,
1744
+ outputType = "json",
1745
+ includeGeneratedExample = false,
1746
+ ) {
1747
+ const finalExamples = [];
1748
+ const isJson = mimeType?.toLowerCase().includes("json");
1749
+ const isXml = mimeType?.toLowerCase().includes("xml");
1750
+
1751
+ // 1. Process multiple examples (examples map)
1752
+ if (examples && typeof examples === "object" && Object.keys(examples).length > 0) {
1753
+ for (const eg in examples) {
1754
+ const rawVal = examples[eg]?.value !== undefined ? examples[eg].value : examples[eg];
1755
+ const { content, format } = formatExampleContent(rawVal, mimeType, outputType);
1756
+
1757
+ finalExamples.push({
1758
+ exampleId: eg,
1759
+ exampleSummary: examples[eg]?.summary || eg,
1760
+ exampleDescription: examples[eg]?.description || "",
1761
+ exampleType: mimeType,
1762
+ exampleValue: content,
1763
+ exampleFormat: format,
1764
+ });
1765
+ }
1766
+ }
1767
+ // 2. Process single example (when examples is absent or empty)
1768
+ else if (
1769
+ example !== null &&
1770
+ example !== undefined &&
1771
+ (typeof example !== "object" || Object.keys(example).length > 0)
1772
+ ) {
1773
+ const rawVal = example?.value !== undefined ? example.value : example;
1774
+ const { content, format } = formatExampleContent(rawVal, mimeType, outputType);
1775
+
1776
+ finalExamples.push({
1777
+ exampleId: "Example",
1778
+ exampleSummary: example?.summary || "",
1779
+ exampleDescription: example?.description || "",
1780
+ exampleType: mimeType,
1781
+ exampleValue: content,
1782
+ exampleFormat: format,
1783
+ });
1784
+ }
1785
+
1786
+ // 3. Fallback to schema-level example or schema-generated sample
1787
+ if (finalExamples.length === 0 || includeGeneratedExample === true) {
1788
+ if (schema) {
1789
+ const firstExample = getFirstExample(schema);
1790
+ if (firstExample !== undefined && firstExample !== null) {
1791
+ const { content, format } = formatExampleContent(firstExample, mimeType, outputType);
1792
+ finalExamples.push({
1793
+ exampleId: "Example",
1794
+ exampleSummary: "",
1795
+ exampleDescription: "",
1796
+ exampleType: mimeType,
1797
+ exampleValue: content,
1798
+ exampleFormat: format,
1799
+ });
1800
+ } else if (
1801
+ isJson ||
1802
+ isXml ||
1803
+ mimeType?.toLowerCase().includes("text") ||
1804
+ mimeType?.toLowerCase().includes("yaml") ||
1805
+ mimeType?.toLowerCase().includes("*/*")
1806
+ ) {
1807
+ let xmlRootStart = "";
1808
+ let xmlRootEnd = "";
1809
+ let exampleFormat = outputType;
1810
+
1811
+ if (isXml) {
1812
+ xmlRootStart = schema.xml?.name
1813
+ ? `<${schema.xml.name}${schema.xml.namespace ? ` xmlns="${schema.xml.namespace}"` : ""}>`
1814
+ : "<root>";
1815
+ xmlRootEnd = schema.xml?.name ? `</${schema.xml.name}>` : "</root>";
1816
+ exampleFormat = "text";
1817
+ }
1818
+
1819
+ const samples = schemaToSampleObj(schema, {
1820
+ includeReadOnly,
1821
+ includeWriteOnly,
1822
+ deprecated: true,
1823
+ useXmlTagForProp: isXml,
1824
+ });
1825
+
1826
+ let i = 0;
1827
+ for (const samplesKey in samples) {
1828
+ if (!samples[samplesKey]) {
1829
+ continue;
1830
+ }
1831
+ const summary = samples[samplesKey]["::TITLE"] || `Example ${++i}`;
1832
+ const description = samples[samplesKey]["::DESCRIPTION"] || "";
1833
+ let exampleValue = "";
1834
+
1835
+ if (isXml) {
1836
+ exampleValue = `<?xml version="1.0" encoding="UTF-8"?>\n${xmlRootStart}${json2xml(samples[samplesKey], 1)}\n${xmlRootEnd}`;
1837
+ } else {
1838
+ removeTitlesAndDescriptions(samples[samplesKey]);
1839
+ exampleValue =
1840
+ outputType === "text"
1841
+ ? JSON.stringify(samples[samplesKey], null, 2)
1842
+ : samples[samplesKey];
1843
+ }
1844
+
1845
+ finalExamples.push({
1846
+ exampleId: samplesKey,
1847
+ exampleSummary: summary,
1848
+ exampleDescription: description,
1849
+ exampleType: mimeType,
1850
+ exampleFormat,
1851
+ exampleValue,
1852
+ });
1853
+ }
1854
+ } else if (mimeType?.toLowerCase().includes("jose")) {
1855
+ finalExamples.push({
1856
+ exampleId: "Example",
1857
+ exampleSummary: "Base64 Encoded",
1858
+ exampleDescription: "",
1859
+ exampleType: mimeType,
1860
+ exampleValue: schema.pattern || "bXJpbg==",
1861
+ exampleFormat: "text",
1862
+ });
1863
+ } else {
1864
+ finalExamples.push({
1865
+ exampleId: "Example",
1866
+ exampleSummary: "",
1867
+ exampleDescription: "",
1868
+ exampleType: mimeType,
1869
+ exampleValue: "",
1870
+ exampleFormat: "text",
1871
+ });
1872
+ }
1873
+ } else {
1874
+ // No Example or Schema provided
1875
+ finalExamples.push({
1876
+ exampleId: "Example",
1877
+ exampleSummary: "",
1878
+ exampleDescription: "",
1879
+ exampleType: mimeType,
1880
+ exampleValue: "",
1881
+ exampleFormat: "text",
1882
+ });
1883
+ }
1884
+ }
1885
+
1886
+ return finalExamples;
1887
+ }
1888
+
1889
+ function getSerializeStyleForContentType(contentType) {
1890
+ if (contentType === "application/json") {
1891
+ return "json";
1892
+ }
1893
+ if (contentType === "application/xml") {
1894
+ return "xml";
1895
+ }
1896
+ return null;
1897
+ }
1898
+
1899
+ /**
1900
+ * Text that names an example in an examples dropdown: its summary, or its key when it has none.
1901
+ * A long summary is shown whole; the dropdown wraps it, or cuts it where the browser's select can't wrap.
1902
+ */
1903
+ export function exampleLabel(example) {
1904
+ return example?.exampleSummary || example?.exampleId || "";
1905
+ }
1906
+
1907
+ export function getSchemaFromParam(param) {
1908
+ if (param.schema) {
1909
+ return [param.schema, null, null];
1910
+ }
1911
+ if (param.content) {
1912
+ // we gonna use the first content-encoding
1913
+ for (const contentType of Object.keys(param.content)) {
1914
+ if (param.content[contentType].schema) {
1915
+ return [
1916
+ param.content[contentType].schema,
1917
+ getSerializeStyleForContentType(contentType),
1918
+ param.content[contentType],
1919
+ ];
1920
+ }
1921
+ }
1922
+ }
1923
+ return [null, null, null];
1924
+ }
1925
+
1926
+ export const EMPTY_ARRAY = Object.freeze([]);
1927
+
1928
+ export function getMergedProperties(schema, visited = new Set()) {
1929
+ if (!schema || typeof schema !== "object" || visited.has(schema)) {
1930
+ return {};
1931
+ }
1932
+ visited.add(schema);
1933
+ const properties = { ...schema.properties };
1934
+ if (Array.isArray(schema.allOf)) {
1935
+ schema.allOf.forEach((sub) => {
1936
+ Object.assign(properties, getMergedProperties(sub, visited));
1937
+ });
1938
+ }
1939
+ return properties;
1940
+ }
1941
+
1942
+ export function getMergedRequired(schema, visited = new Set()) {
1943
+ if (!schema || typeof schema !== "object" || visited.has(schema)) {
1944
+ return [];
1945
+ }
1946
+ visited.add(schema);
1947
+ const required = Array.isArray(schema.required) ? [...schema.required] : [];
1948
+ if (Array.isArray(schema.allOf)) {
1949
+ schema.allOf.forEach((sub) => {
1950
+ required.push(...getMergedRequired(sub, visited));
1951
+ });
1952
+ }
1953
+ return [...new Set(required)];
1954
+ }