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