@sdk-it/spec 0.20.0 → 0.22.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 (55) hide show
  1. package/dist/index.d.ts +10 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2629 -2
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/find-polymorphic-varients.d.ts +15 -0
  6. package/dist/lib/find-polymorphic-varients.d.ts.map +1 -0
  7. package/dist/lib/find-polymorphic-varients.test.d.ts +2 -0
  8. package/dist/lib/find-polymorphic-varients.test.d.ts.map +1 -0
  9. package/dist/lib/find-unique-schema-name.d.ts +3 -0
  10. package/dist/lib/find-unique-schema-name.d.ts.map +1 -0
  11. package/dist/lib/format-name.d.ts +2 -0
  12. package/dist/lib/format-name.d.ts.map +1 -0
  13. package/dist/lib/get-ref-usage.d.ts +3 -0
  14. package/dist/lib/get-ref-usage.d.ts.map +1 -0
  15. package/dist/lib/is-primitive-schema.d.ts +3 -0
  16. package/dist/lib/is-primitive-schema.d.ts.map +1 -0
  17. package/dist/lib/loaders/postman/postman-converter.d.ts.map +1 -1
  18. package/dist/lib/loaders/remote-loader.d.ts.map +1 -1
  19. package/dist/lib/metadata.d.ts +22 -0
  20. package/dist/lib/metadata.d.ts.map +1 -0
  21. package/dist/lib/operation.d.ts +66 -4
  22. package/dist/lib/operation.d.ts.map +1 -1
  23. package/dist/lib/pagination/pagination-result.d.ts +20 -0
  24. package/dist/lib/pagination/pagination-result.d.ts.map +1 -0
  25. package/dist/lib/pagination/pagination-result.test.d.ts +2 -0
  26. package/dist/lib/pagination/pagination-result.test.d.ts.map +1 -0
  27. package/dist/lib/pagination/pagination.d.ts +50 -0
  28. package/dist/lib/pagination/pagination.d.ts.map +1 -0
  29. package/dist/lib/pagination/pagination.test.d.ts +2 -0
  30. package/dist/lib/pagination/pagination.test.d.ts.map +1 -0
  31. package/dist/lib/security.d.ts +10 -0
  32. package/dist/lib/security.d.ts.map +1 -0
  33. package/dist/lib/sidebar.d.ts +4 -7
  34. package/dist/lib/sidebar.d.ts.map +1 -1
  35. package/dist/lib/tune.d.ts +11 -0
  36. package/dist/lib/tune.d.ts.map +1 -0
  37. package/dist/lib/tune.test.d.ts +2 -0
  38. package/dist/lib/tune.test.d.ts.map +1 -0
  39. package/package.json +8 -3
  40. package/dist/lib/loaders/load-spec.js +0 -25
  41. package/dist/lib/loaders/load-spec.js.map +0 -7
  42. package/dist/lib/loaders/local-loader.js +0 -20
  43. package/dist/lib/loaders/local-loader.js.map +0 -7
  44. package/dist/lib/loaders/postman/postman-converter.js +0 -486
  45. package/dist/lib/loaders/postman/postman-converter.js.map +0 -7
  46. package/dist/lib/loaders/postman/spec-types.js +0 -1
  47. package/dist/lib/loaders/postman/spec-types.js.map +0 -7
  48. package/dist/lib/loaders/remote-loader.js +0 -26
  49. package/dist/lib/loaders/remote-loader.js.map +0 -7
  50. package/dist/lib/operation.js +0 -286
  51. package/dist/lib/operation.js.map +0 -7
  52. package/dist/lib/operation.test.js +0 -261
  53. package/dist/lib/operation.test.js.map +0 -7
  54. package/dist/lib/sidebar.js +0 -33
  55. package/dist/lib/sidebar.js.map +0 -7
package/dist/index.js CHANGED
@@ -1,3 +1,2630 @@
1
- export * from "./lib/loaders/load-spec.js";
2
- export * from "./lib/operation.js";
1
+ // packages/spec/src/lib/find-polymorphic-varients.ts
2
+ import { groupBy, uniqBy } from "lodash-es";
3
+ import { camelcase, isEmpty as isEmpty2, isRef as isRef2, resolveRef as resolveRef2 } from "@sdk-it/core";
4
+
5
+ // packages/spec/src/lib/tune.ts
6
+ import { merge, uniq } from "lodash-es";
7
+ import assert from "node:assert";
8
+ import {
9
+ isEmpty,
10
+ isRef,
11
+ joinSkipDigits as joinSkipDigits2,
12
+ notRef,
13
+ pascalcase as pascalcase2,
14
+ resolveRef,
15
+ snakecase
16
+ } from "@sdk-it/core";
17
+
18
+ // packages/spec/src/lib/find-unique-schema-name.ts
19
+ import { joinSkipDigits, pascalcase } from "@sdk-it/core";
20
+ var reservedNames = /* @__PURE__ */ new Set(["Function", "Error"]);
21
+ function findUniqueSchemaName(spec, initialName, potentialSuffixList, fallback) {
22
+ spec.components ??= {};
23
+ spec.components.schemas ??= {};
24
+ let name = pascalcase(initialName);
25
+ while (spec.components.schemas[name] || reservedNames.has(name)) {
26
+ const suffix = potentialSuffixList.shift();
27
+ name = pascalcase(joinSkipDigits([name, suffix || ""], " "));
28
+ }
29
+ return initialName === name ? fallback ?? name : name;
30
+ }
31
+
32
+ // packages/spec/src/lib/format-name.ts
33
+ var reservedWords = /* @__PURE__ */ new Set([
34
+ "abstract",
35
+ "as",
36
+ "assert",
37
+ "async",
38
+ "await",
39
+ "break",
40
+ "case",
41
+ "catch",
42
+ "class",
43
+ "const",
44
+ "continue",
45
+ "default",
46
+ "deferred",
47
+ "do",
48
+ "dynamic",
49
+ "else",
50
+ "enum",
51
+ "export",
52
+ "extends",
53
+ "extension",
54
+ "external",
55
+ "factory",
56
+ "final",
57
+ "finally",
58
+ "for",
59
+ "Function",
60
+ "get",
61
+ "set",
62
+ "hide",
63
+ "if",
64
+ "default",
65
+ "new",
66
+ "implements",
67
+ "import",
68
+ "in",
69
+ "interface",
70
+ "is",
71
+ "library",
72
+ "mixin",
73
+ "new",
74
+ "null",
75
+ "on",
76
+ "operator",
77
+ "part",
78
+ "required",
79
+ "rethrow",
80
+ "return",
81
+ "hide",
82
+ "show"
83
+ ]);
84
+ var STARTS_WITH_DIGITS_PATTERN = /^-?\d/;
85
+ var FIRST_DASH = /^_/;
86
+ var LAST_DASH = /_$/;
87
+ var ONLY_ENGLISH = /(^\$)|(\+)|(-)|[^A-Za-z0-9]+/g;
88
+ var formatName = (it) => {
89
+ if (reservedWords.has(it)) {
90
+ return `$${it}`;
91
+ }
92
+ if (typeof it === "number") {
93
+ if (Math.sign(it) === -1) {
94
+ return `$_${Math.abs(it)}`;
95
+ }
96
+ return `$${it}`;
97
+ }
98
+ if (typeof it === "string") {
99
+ if (STARTS_WITH_DIGITS_PATTERN.test(it)) {
100
+ if (Math.sign(parseInt(it, 10)) === -1) {
101
+ return `$_${Math.abs(parseInt(it, 10))}`;
102
+ }
103
+ return `$${it}`;
104
+ }
105
+ return it.replace(ONLY_ENGLISH, (match) => {
106
+ if (match === "-" && match === it[0])
107
+ return "desc_";
108
+ if (match === "+")
109
+ return "_plus_";
110
+ if (match === "$" && match === it[0])
111
+ return "$";
112
+ return "_";
113
+ }).replace(FIRST_DASH, "").replace(LAST_DASH, "");
114
+ }
115
+ return String(it);
116
+ };
117
+
118
+ // packages/spec/src/lib/tune.ts
119
+ function fixSpec(spec, schemas, visited = /* @__PURE__ */ new Set()) {
120
+ for (const schema of schemas) {
121
+ if (isRef(schema))
122
+ continue;
123
+ if (!isEmpty(schema.properties)) {
124
+ schema.type = "object";
125
+ delete schema.oneOf;
126
+ delete schema.anyOf;
127
+ fixSpec(spec, Object.values(schema.properties), visited);
128
+ }
129
+ if (!isEmpty(schema["x-properties"])) {
130
+ fixSpec(spec, Object.values(schema["x-properties"]), visited);
131
+ }
132
+ if (!isEmpty(schema.items)) {
133
+ delete schema.oneOf;
134
+ delete schema.anyOf;
135
+ schema.type = "array";
136
+ fixSpec(spec, [schema.items], visited);
137
+ const items = resolveRef(spec, schema.items);
138
+ if (Array.isArray(items.default)) {
139
+ schema.default ??= structuredClone(items.default);
140
+ }
141
+ delete items.default;
142
+ }
143
+ if (!isEmpty(schema.anyOf) && !isEmpty(schema.oneOf)) {
144
+ delete schema.anyOf;
145
+ }
146
+ if (isEmpty(schema.enum)) {
147
+ delete schema.enum;
148
+ }
149
+ if (!isEmpty(schema.enum)) {
150
+ if (schema.enum.length === 1) {
151
+ schema.const = schema.enum[0];
152
+ delete schema.enum;
153
+ } else {
154
+ const valuesSet = /* @__PURE__ */ new Set();
155
+ const valuesList = [];
156
+ for (const it of schema.enum) {
157
+ const formattedValue = formatName(snakecase(formatName(it)));
158
+ if (!valuesSet.has(formattedValue)) {
159
+ valuesSet.add(formattedValue);
160
+ valuesList.push(it);
161
+ }
162
+ }
163
+ schema.enum = valuesList;
164
+ }
165
+ delete schema.allOf;
166
+ }
167
+ if (schema.const !== void 0) {
168
+ schema.default = schema.const;
169
+ }
170
+ if (!isEmpty(schema.allOf)) {
171
+ const schemas2 = schema.allOf;
172
+ const resolved = schemas2.map((it) => resolveRef(spec, it));
173
+ const hasObjects = resolved.some((it) => it.type === "object");
174
+ const hasOtherTypes = resolved.some(
175
+ (it) => it.type && it.type !== "object"
176
+ );
177
+ if (hasObjects && hasOtherTypes) {
178
+ assert(false, `allOf must be an object`);
179
+ }
180
+ merge(
181
+ schema,
182
+ ...resolved.map((it) => {
183
+ fixSpec(spec, [it], visited);
184
+ return it;
185
+ })
186
+ );
187
+ delete schema.allOf;
188
+ } else {
189
+ delete schema.allOf;
190
+ }
191
+ if (schema.type === "object" && isEmpty(schema.properties) && typeof schema.additionalProperties === "object" && !isEmpty(schema.additionalProperties) && notRef(schema.additionalProperties) && !isEmpty(schema.additionalProperties.properties)) {
192
+ fixSpec(
193
+ spec,
194
+ Object.values(schema.additionalProperties.properties),
195
+ visited
196
+ );
197
+ Object.assign(schema, schema.additionalProperties);
198
+ delete schema.additionalProperties;
199
+ }
200
+ for (const kind of ["oneOf", "anyOf"]) {
201
+ if (!isEmpty(schema[kind])) {
202
+ delete schema.type;
203
+ fixSpec(spec, schema[kind], visited);
204
+ if (isEmpty(schema[kind])) {
205
+ continue;
206
+ }
207
+ let enumSchemaIndex = -1;
208
+ const enumValues = [];
209
+ for (let idx = 0; idx < schema[kind].length; idx++) {
210
+ const item = schema[kind][idx];
211
+ if (notRef(item) && item.type === "string") {
212
+ if (item.enum && item.enum.length > 1) {
213
+ enumValues.push(...item.enum);
214
+ if (enumSchemaIndex === -1) {
215
+ enumSchemaIndex = idx;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ if (enumSchemaIndex !== -1) {
221
+ const enumSchema = schema[kind][enumSchemaIndex];
222
+ if (notRef(enumSchema)) {
223
+ enumSchema.enum = uniq(enumValues);
224
+ }
225
+ schema[kind] = schema[kind].filter(
226
+ (it, idx) => idx === enumSchemaIndex || isRef(it)
227
+ );
228
+ }
229
+ const otherTypes = schema[kind].filter(
230
+ (it) => resolveRef(spec, it).type !== "null"
231
+ );
232
+ if (otherTypes.length === 1) {
233
+ Object.assign(schema, otherTypes[0]);
234
+ delete schema[kind];
235
+ continue;
236
+ }
237
+ schema["x-varients"] = findVarients(spec, schema[kind]);
238
+ } else {
239
+ delete schema[kind];
240
+ }
241
+ }
242
+ }
243
+ }
244
+ function expandSpec(spec, schemas, refs) {
245
+ for (const [name, schema] of Object.entries(schemas)) {
246
+ if (isRef(schema))
247
+ continue;
248
+ if (!isEmpty(schema.properties)) {
249
+ if (!isEmpty(schema.oneOf)) {
250
+ for (const oneOfIdx in schema.oneOf) {
251
+ const oneOf = schema.oneOf[oneOfIdx];
252
+ if (isRef(oneOf))
253
+ continue;
254
+ for (const key of ["properties", "x-properties"]) {
255
+ if (!isEmpty(oneOf.required) && schema[key]) {
256
+ schema.oneOf[oneOfIdx] = schema[key][oneOf.required[0]];
257
+ }
258
+ }
259
+ }
260
+ delete schema.type;
261
+ expandSpec(spec, schemas, refs);
262
+ continue;
263
+ }
264
+ if (schema.additionalProperties) {
265
+ continue;
266
+ }
267
+ spec.components.schemas[name] = schema;
268
+ const properties = schema.properties;
269
+ for (const [propName, value] of Object.entries(properties)) {
270
+ if (isRef(value))
271
+ continue;
272
+ const fixedPropName = propName.replace("[]", "");
273
+ const refName = pascalcase2(joinSkipDigits2([name, fixedPropName], " "));
274
+ if (!isEmpty(value.properties)) {
275
+ spec.components.schemas[refName] = value;
276
+ properties[propName] = { $ref: `#/components/schemas/${refName}` };
277
+ expandSpec(spec, { [refName]: value }, refs);
278
+ } else if (!isEmpty(value.oneOf)) {
279
+ expandOneOf(spec, name, value, refs, "oneOf");
280
+ spec.components.schemas[refName] = value;
281
+ properties[propName] = { $ref: `#/components/schemas/${refName}` };
282
+ expandSpec(spec, { [refName]: value }, refs);
283
+ } else if (!isEmpty(value.anyOf)) {
284
+ expandOneOf(spec, name, value, refs, "anyOf");
285
+ spec.components.schemas[refName] = value;
286
+ properties[propName] = { $ref: `#/components/schemas/${refName}` };
287
+ expandSpec(spec, { [refName]: value }, refs);
288
+ } else {
289
+ expandSpec(spec, { [refName]: value }, refs);
290
+ }
291
+ }
292
+ continue;
293
+ }
294
+ if (!isEmpty(schema["x-properties"])) {
295
+ spec.components.schemas[name] = schema;
296
+ const properties = schema["x-properties"];
297
+ for (const [propName, value] of Object.entries(properties)) {
298
+ if (isRef(value))
299
+ continue;
300
+ const fixedPropName = propName.replace("[]", "");
301
+ const refName = pascalcase2(joinSkipDigits2([name, fixedPropName], " "));
302
+ if (!isEmpty(value.properties)) {
303
+ spec.components.schemas[refName] = value;
304
+ properties[propName] = { $ref: `#/components/schemas/${refName}` };
305
+ expandSpec(spec, { [refName]: value }, refs);
306
+ } else if (!isEmpty(value.oneOf)) {
307
+ expandOneOf(spec, name, value, refs, "oneOf");
308
+ spec.components.schemas[refName] = value;
309
+ properties[propName] = { $ref: `#/components/schemas/${refName}` };
310
+ expandSpec(spec, { [refName]: value }, refs);
311
+ } else if (!isEmpty(value.anyOf)) {
312
+ expandOneOf(spec, name, value, refs, "anyOf");
313
+ spec.components.schemas[refName] = value;
314
+ properties[propName] = { $ref: `#/components/schemas/${refName}` };
315
+ expandSpec(spec, { [refName]: value }, refs);
316
+ } else {
317
+ expandSpec(spec, { [refName]: value }, refs);
318
+ }
319
+ }
320
+ continue;
321
+ }
322
+ if (schema.type === "array") {
323
+ if (isRef(schema.items))
324
+ continue;
325
+ if (isEmpty(schema.items))
326
+ continue;
327
+ const refName = findUniqueSchemaName(spec, name, ["Item", "Entry"]);
328
+ if (schema.items.type === "object") {
329
+ spec.components.schemas[refName] = schema.items;
330
+ expandSpec(spec, { [refName]: schema.items }, refs);
331
+ schema.items = { $ref: `#/components/schemas/${refName}` };
332
+ continue;
333
+ }
334
+ if (schema.items.type === "array") {
335
+ expandSpec(spec, { [refName]: schema.items }, refs);
336
+ continue;
337
+ }
338
+ if (!isEmpty(schema.items.oneOf)) {
339
+ expandOneOf(spec, refName, schema.items, refs, "oneOf");
340
+ continue;
341
+ }
342
+ if (!isEmpty(schema.items.anyOf)) {
343
+ expandOneOf(spec, refName, schema.items, refs, "anyOf");
344
+ continue;
345
+ }
346
+ }
347
+ if (!isEmpty(schema.oneOf)) {
348
+ expandOneOf(spec, name, schema, refs, "oneOf");
349
+ continue;
350
+ }
351
+ if (!isEmpty(schema.anyOf)) {
352
+ expandOneOf(spec, name, schema, refs, "anyOf");
353
+ continue;
354
+ }
355
+ }
356
+ }
357
+ function expandOneOf(spec, name, schema, refs, kind) {
358
+ const varients = schema["x-varients"];
359
+ if (!varients || varients.length === 0) {
360
+ console.warn(
361
+ `No varients found for ${name}. This might be an error in the OpenAPI spec.`
362
+ );
363
+ }
364
+ varients.forEach((varient) => {
365
+ const varientSchema = schema[kind][varient.position];
366
+ if (isRef(varientSchema))
367
+ return;
368
+ const refName = findUniqueSchemaName(
369
+ spec,
370
+ pascalcase2(`${name} ${varient.name}`),
371
+ ["Varient"]
372
+ );
373
+ if (varientSchema.type === "object") {
374
+ expandSpec(spec, { [refName]: varientSchema }, refs);
375
+ schema[kind][varient.position] = {
376
+ $ref: `#/components/schemas/${refName}`
377
+ };
378
+ } else {
379
+ expandSpec(spec, { [refName]: varientSchema }, refs);
380
+ }
381
+ });
382
+ }
383
+ function coerceTypes(schema, excludeNull = true) {
384
+ const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
385
+ if (excludeNull) {
386
+ return types.filter((type) => type !== "null");
387
+ }
388
+ return types;
389
+ }
390
+
391
+ // packages/spec/src/lib/find-polymorphic-varients.ts
392
+ var groupSchemasByType = (spec, schemas) => {
393
+ const groups = schemas.reduce((acc, schema, index) => {
394
+ if (isRef2(schema)) {
395
+ const referenced = resolveRef2(spec, schema);
396
+ const [type2] = coerceTypes(referenced, false);
397
+ acc[type2] ??= [];
398
+ acc[type2].push({ schema: referenced, position: index });
399
+ return acc;
400
+ }
401
+ if (isRef2(schema.items)) {
402
+ const referenced = resolveRef2(spec, schema.items);
403
+ acc.array ??= [];
404
+ acc.array.push({
405
+ schema: { ...schema, items: referenced },
406
+ position: index
407
+ });
408
+ return acc;
409
+ }
410
+ if (schema.oneOf) {
411
+ acc["oneOf"] ??= [];
412
+ acc["oneOf"].push({ schema: schema.oneOf, position: index });
413
+ return acc;
414
+ }
415
+ if (schema.anyOf) {
416
+ acc["oneOf"] ??= [];
417
+ acc["oneOf"].push({ schema: schema.anyOf, position: index });
418
+ return acc;
419
+ }
420
+ if (schema.const) {
421
+ switch (typeof schema.const) {
422
+ case "string":
423
+ acc.string ??= [];
424
+ acc.string.push({ schema, position: index });
425
+ return acc;
426
+ case "number":
427
+ acc.number ??= [];
428
+ acc.number.push({ schema, position: index });
429
+ return acc;
430
+ case "boolean":
431
+ acc.boolean ??= [];
432
+ acc.boolean.push({ schema, position: index });
433
+ return acc;
434
+ default:
435
+ throw new Error(
436
+ `Unsupported const type: ${typeof schema.const} for ${schema.const}`
437
+ );
438
+ }
439
+ }
440
+ const [type] = coerceTypes(schema, false);
441
+ acc[type] ??= [];
442
+ acc[type].push({ schema, position: index });
443
+ return acc;
444
+ }, {});
445
+ return groups;
446
+ };
447
+ function findVarients(spec, schemas) {
448
+ let varients = [];
449
+ const schemasByType = groupSchemasByType(spec, schemas);
450
+ if (!isEmpty2(schemasByType.string)) {
451
+ for (const { schema, position } of schemasByType.string) {
452
+ if (schema.const !== void 0) {
453
+ varients.push({
454
+ name: schema.const || "empty",
455
+ type: "string",
456
+ position,
457
+ priority: 100 - varients.length
458
+ });
459
+ continue;
460
+ }
461
+ if (schema.format) {
462
+ varients.push({
463
+ name: camelcase(schema.format),
464
+ type: "string",
465
+ position,
466
+ priority: 90 - varients.length
467
+ });
468
+ continue;
469
+ }
470
+ varients.push({ name: "textContent", type: "string", position });
471
+ }
472
+ varients = uniqBy(varients, (it) => it.name);
473
+ }
474
+ if (!isEmpty2(schemasByType.number) || !isEmpty2(schemasByType.integer)) {
475
+ const schemas2 = [
476
+ ...schemasByType.number ?? [],
477
+ ...schemasByType.integer ?? []
478
+ ];
479
+ for (const { schema, position } of schemas2) {
480
+ if (schema.format === "int64") {
481
+ varients.push({
482
+ name: "integer",
483
+ type: "number",
484
+ position,
485
+ priority: 90 - varients.length
486
+ });
487
+ continue;
488
+ }
489
+ if (schema.format === "float") {
490
+ varients.push({
491
+ name: "float",
492
+ type: "number",
493
+ position,
494
+ priority: 90 - varients.length
495
+ });
496
+ continue;
497
+ }
498
+ if (schema.format === "double") {
499
+ varients.push({
500
+ name: "double",
501
+ type: "number",
502
+ position,
503
+ priority: 90 - varients.length
504
+ });
505
+ continue;
506
+ }
507
+ varients.push({ name: "number", type: "number", position });
508
+ }
509
+ }
510
+ if (!isEmpty2(schemasByType.array)) {
511
+ for (const { schema, position } of schemasByType.array) {
512
+ const items = schema.items;
513
+ if (!items) {
514
+ varients.push({ name: "any", type: "array", position });
515
+ continue;
516
+ }
517
+ const [type] = coerceTypes(items);
518
+ if (type === "string") {
519
+ varients.push({
520
+ name: "textList",
521
+ type: "array",
522
+ subtype: "string",
523
+ position
524
+ });
525
+ continue;
526
+ }
527
+ if (type === "number") {
528
+ varients.push({
529
+ name: "numList",
530
+ type: "array",
531
+ subtype: "number",
532
+ position
533
+ });
534
+ continue;
535
+ }
536
+ if (type === "integer") {
537
+ varients.push({
538
+ name: "intList",
539
+ type: "array",
540
+ subtype: "integer",
541
+ position
542
+ });
543
+ continue;
544
+ }
545
+ if (type === "object") {
546
+ const subvarients = findVarients(spec, [items]);
547
+ for (const subvarient of subvarients) {
548
+ varients.push({
549
+ ...subvarient,
550
+ type: "array",
551
+ position
552
+ });
553
+ }
554
+ continue;
555
+ }
556
+ if (type === "array") {
557
+ const subvarients = findVarients(spec, [items]);
558
+ for (const subvarient of subvarients) {
559
+ varients.push({
560
+ ...subvarient,
561
+ name: `${subvarient.name}Matrix`,
562
+ type: "array",
563
+ position
564
+ });
565
+ }
566
+ continue;
567
+ }
568
+ varients.push({ name: "list", type: "array", position });
569
+ }
570
+ }
571
+ if (!isEmpty2(schemasByType.$ref)) {
572
+ const subvarients = findVarients(
573
+ spec,
574
+ schemasByType.$ref.map((it) => resolveRef2(spec, it.schema))
575
+ );
576
+ varients.push(
577
+ ...subvarients.map((it) => ({
578
+ ...it
579
+ }))
580
+ );
581
+ }
582
+ if (!isEmpty2(schemasByType.oneOf)) {
583
+ for (const { schema, position } of schemasByType.oneOf) {
584
+ const subvarients = findVarients(spec, schema);
585
+ varients.push(
586
+ ...subvarients.map((it) => ({
587
+ ...it,
588
+ position
589
+ }))
590
+ );
591
+ }
592
+ }
593
+ const matrix = [];
594
+ for (const { schema, position } of schemasByType.object ?? []) {
595
+ if (schema.additionalProperties || isEmpty2({ ...schema.properties, ...schema["x-properties"] })) {
596
+ continue;
597
+ }
598
+ for (const key of ["properties", "x-properties"]) {
599
+ if (!schema[key])
600
+ continue;
601
+ const list = Object.entries(schema[key]).map(([name, schemaOrRef]) => {
602
+ const schema2 = resolveRef2(spec, schemaOrRef);
603
+ name = schema2.const ?? schema2.enum?.[0] ?? name;
604
+ if (schema2.type === "string") {
605
+ return {
606
+ priority: schema2.const !== void 0 || schema2.enum?.[0] !== void 0 ? 100 - matrix.length : void 0,
607
+ static: true,
608
+ subtype: "string",
609
+ source: name,
610
+ name,
611
+ type: "object",
612
+ position
613
+ };
614
+ }
615
+ return {
616
+ subtype: "string",
617
+ source: name,
618
+ name,
619
+ type: "object",
620
+ position
621
+ };
622
+ });
623
+ matrix.push([...new Set(list)].sort((a) => a.static ? -1 : 1));
624
+ }
625
+ if (matrix.length === 0) {
626
+ throw new Error(
627
+ "No valid objects found. Please check your OpenAPI spec."
628
+ );
629
+ }
630
+ let discriminatorProp;
631
+ const firstRow = matrix[0];
632
+ for (const prop of firstRow) {
633
+ const existsCrossAllRows = matrix.every((row) => row.includes(prop));
634
+ if (existsCrossAllRows) {
635
+ discriminatorProp = prop;
636
+ break;
637
+ }
638
+ }
639
+ }
640
+ for (const row of matrix) {
641
+ for (const prop of row) {
642
+ const isUnique = matrix.every(
643
+ (it) => it === row ? true : !it.some((p) => p.name === prop.name)
644
+ );
645
+ if (isUnique) {
646
+ varients.push(prop);
647
+ break;
648
+ }
649
+ }
650
+ }
651
+ return varients.sort((a, b) => {
652
+ const aHasPriority = a.priority !== void 0;
653
+ const bHasPriority = b.priority !== void 0;
654
+ if (aHasPriority && bHasPriority) {
655
+ if (a.priority !== b.priority) {
656
+ return b.priority - a.priority;
657
+ }
658
+ return a.position - b.position;
659
+ } else if (aHasPriority) {
660
+ return -1;
661
+ } else if (bHasPriority) {
662
+ return 1;
663
+ } else {
664
+ return 0;
665
+ }
666
+ });
667
+ }
668
+ function findPolymorphicVarients(spec, schemas) {
669
+ const varients = findVarients(spec, schemas);
670
+ return Object.values(groupBy(varients, (it) => "-" + it.position)).map(
671
+ (group) => {
672
+ return (group ?? [])[0];
673
+ }
674
+ );
675
+ }
676
+
677
+ // packages/spec/src/lib/get-ref-usage.ts
678
+ import { isEmpty as isEmpty5, isRef as isRef5, parseRef as parseRef2, pascalcase as pascalcase4 } from "@sdk-it/core";
679
+
680
+ // packages/spec/src/lib/operation.ts
681
+ import { camelcase as camelcase2 } from "stringcase";
682
+ import { methods as methods2 } from "@sdk-it/core/paths.js";
683
+ import { followRef, isRef as isRef4, parseRef, resolveRef as resolveRef4 } from "@sdk-it/core/ref.js";
684
+ import { isEmpty as isEmpty4, pascalcase as pascalcase3, snakecase as snakecase2 } from "@sdk-it/core/utils.js";
685
+
686
+ // packages/spec/src/lib/pagination/pagination.ts
687
+ import { isRef as isRef3 } from "@sdk-it/core/ref.js";
688
+ import { isEmpty as isEmpty3 } from "@sdk-it/core/utils.js";
689
+
690
+ // packages/spec/src/lib/pagination/pagination-result.ts
691
+ import pluralize from "pluralize";
692
+ var PRIMARY_TOP_TIER_KEYWORDS = [
693
+ "data",
694
+ "items",
695
+ "results",
696
+ "value"
697
+ ];
698
+ var PRIMARY_OTHER_KEYWORDS = [
699
+ "records",
700
+ "content",
701
+ "list",
702
+ "payload",
703
+ "entities",
704
+ "collection",
705
+ "users",
706
+ "products",
707
+ "orders",
708
+ "bookings",
709
+ "articles",
710
+ "posts",
711
+ "documents",
712
+ "events"
713
+ ];
714
+ var SECONDARY_KEYWORDS = ["entries", "rows", "elements"];
715
+ var PLURAL_DEPRIORITIZE_LIST = [
716
+ "status",
717
+ "success",
718
+ "address",
719
+ "details",
720
+ "properties",
721
+ "params",
722
+ "headers",
723
+ "cookies",
724
+ "series",
725
+ "links",
726
+ "meta",
727
+ "metadata",
728
+ "statistics",
729
+ "settings",
730
+ "options",
731
+ "permissions",
732
+ "credentials",
733
+ "diagnostics",
734
+ "warnings",
735
+ "errors",
736
+ "actions",
737
+ "attributes",
738
+ "categories",
739
+ "features",
740
+ "includes",
741
+ "tags"
742
+ ];
743
+ var HAS_MORE_PRIMARY_POSITIVE_EXACT = [
744
+ "hasmore",
745
+ "hasnext",
746
+ "hasnextpage",
747
+ "moreitems",
748
+ "moreitemsavailable",
749
+ "nextpage",
750
+ "nextpageexists",
751
+ "nextpageavailable",
752
+ "hasadditionalresults",
753
+ "moreresultsavailable",
754
+ "canloadmore",
755
+ "hasadditional",
756
+ "additionalitems",
757
+ "fetchmore"
758
+ ];
759
+ var HAS_MORE_SECONDARY_POSITIVE_EXACT = ["more", "next"];
760
+ var HAS_MORE_PRIMARY_INVERTED_EXACT = [
761
+ "islast",
762
+ "lastpage",
763
+ "endofresults",
764
+ "endoflist",
765
+ "nomoreitems",
766
+ "nomoredata",
767
+ "allitemsloaded",
768
+ "iscomplete",
769
+ "completed"
770
+ ];
771
+ var HAS_MORE_POSITIVE_REGEX_PATTERNS = [
772
+ "\\bhas_?more\\b",
773
+ "\\bhas_?next\\b",
774
+ // e.g., itemsHasNext, items_has_next
775
+ "\\bmore_?items\\b",
776
+ "\\bnext_?page\\b",
777
+ // e.g., userNextPageFlag
778
+ "\\badditional\\b",
779
+ // e.g., hasAdditionalData, additional_results_exist
780
+ "\\bcontinuation\\b",
781
+ // e.g., continuationAvailable, has_continuation_token
782
+ "\\bmore_?results\\b",
783
+ "\\bpage_?available\\b",
784
+ "\\bnext(?:_?(page|marker))?\\b"
785
+ ];
786
+ var COMPILED_HAS_MORE_POSITIVE_REGEXES = HAS_MORE_POSITIVE_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
787
+ var HAS_MORE_INVERTED_REGEX_PATTERNS = [
788
+ "\\bis_?last\\b",
789
+ // e.g., pageIsLast
790
+ "\\blast_?page\\b",
791
+ // e.g., resultsAreLastPage
792
+ "\\bend_?of_?(data|results|list|items|stream)\\b",
793
+ "\\bno_?more_?(items|data|results)?\\b",
794
+ "\\ball_?(items_?)?loaded\\b",
795
+ "\\bis_?complete\\b"
796
+ ];
797
+ var COMPILED_HAS_MORE_INVERTED_REGEXES = HAS_MORE_INVERTED_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
798
+ function getItemsName(properties) {
799
+ const arrayPropertyNames = [];
800
+ for (const propName in properties) {
801
+ if (propName in properties) {
802
+ const propSchema = properties[propName];
803
+ if (propSchema && propSchema.type === "array") {
804
+ arrayPropertyNames.push(propName);
805
+ }
806
+ }
807
+ }
808
+ if (arrayPropertyNames.length === 0) {
809
+ return null;
810
+ }
811
+ if (arrayPropertyNames.length === 1) {
812
+ return arrayPropertyNames[0];
813
+ }
814
+ let bestCandidate = null;
815
+ let candidateRank = Infinity;
816
+ const updateCandidate = (propName, rank) => {
817
+ if (rank < candidateRank) {
818
+ bestCandidate = propName;
819
+ candidateRank = rank;
820
+ }
821
+ };
822
+ for (const propName of arrayPropertyNames) {
823
+ const lowerPropName = propName.toLowerCase();
824
+ if (PRIMARY_TOP_TIER_KEYWORDS.includes(lowerPropName)) {
825
+ updateCandidate(propName, 2);
826
+ continue;
827
+ }
828
+ if (candidateRank > 3 && PRIMARY_OTHER_KEYWORDS.includes(lowerPropName)) {
829
+ updateCandidate(propName, 3);
830
+ continue;
831
+ }
832
+ if (candidateRank > 4 && SECONDARY_KEYWORDS.includes(lowerPropName)) {
833
+ updateCandidate(propName, 4);
834
+ continue;
835
+ }
836
+ if (candidateRank > 5 && pluralize.isPlural(propName) && !PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
837
+ updateCandidate(propName, 5);
838
+ continue;
839
+ }
840
+ if (candidateRank > 6 && pluralize.isPlural(propName) && PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
841
+ updateCandidate(propName, 6);
842
+ continue;
843
+ }
844
+ }
845
+ if (bestCandidate) {
846
+ return bestCandidate;
847
+ }
848
+ return arrayPropertyNames[0];
849
+ }
850
+ function guess(properties) {
851
+ const booleanPropertyNames = [];
852
+ for (const propName in properties) {
853
+ const propSchema = properties[propName];
854
+ if (propSchema.type === "boolean" || propSchema.type === "integer" || propSchema.type === "string") {
855
+ booleanPropertyNames.push(propName);
856
+ }
857
+ }
858
+ if (booleanPropertyNames.length === 0) {
859
+ return null;
860
+ }
861
+ if (booleanPropertyNames.length === 1) {
862
+ return booleanPropertyNames[0];
863
+ }
864
+ let bestCandidate = null;
865
+ let candidateRank = Infinity;
866
+ const updateCandidate = (propName, rank) => {
867
+ if (rank < candidateRank) {
868
+ bestCandidate = propName;
869
+ candidateRank = rank;
870
+ }
871
+ };
872
+ for (const propName of booleanPropertyNames) {
873
+ const normalizedForExactMatch = propName.toLowerCase().replace(/[-_]/g, "");
874
+ let currentPropRank = Infinity;
875
+ if (HAS_MORE_PRIMARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
876
+ currentPropRank = 1;
877
+ } else if (HAS_MORE_SECONDARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
878
+ currentPropRank = 2;
879
+ } else {
880
+ let foundPositiveRegex = false;
881
+ for (const regex of COMPILED_HAS_MORE_POSITIVE_REGEXES) {
882
+ if (regex.test(propName)) {
883
+ currentPropRank = 3;
884
+ foundPositiveRegex = true;
885
+ break;
886
+ }
887
+ }
888
+ if (!foundPositiveRegex) {
889
+ if (HAS_MORE_PRIMARY_INVERTED_EXACT.includes(normalizedForExactMatch)) {
890
+ currentPropRank = 4;
891
+ } else {
892
+ for (const regex of COMPILED_HAS_MORE_INVERTED_REGEXES) {
893
+ if (regex.test(propName)) {
894
+ currentPropRank = 5;
895
+ break;
896
+ }
897
+ }
898
+ }
899
+ }
900
+ }
901
+ updateCandidate(propName, currentPropRank);
902
+ }
903
+ return bestCandidate;
904
+ }
905
+ function getHasMoreName(properties) {
906
+ const rootGuess = guess(properties);
907
+ if (rootGuess) {
908
+ return rootGuess;
909
+ }
910
+ for (const propName in properties) {
911
+ const propSchema = properties[propName];
912
+ if (propSchema.type === "object" && propSchema.properties) {
913
+ const nested = getHasMoreName(propSchema.properties);
914
+ if (nested) {
915
+ return propName + "." + nested;
916
+ }
917
+ }
918
+ }
919
+ return null;
920
+ }
921
+
922
+ // packages/spec/src/lib/pagination/pagination.ts
923
+ var OFFSET_PARAM_REGEXES = [
924
+ /\boffset\b/i,
925
+ /\bskip\b/i,
926
+ /\bstart(?:ing_at|_index)?\b/i,
927
+ // e.g., start, starting_at, start_index
928
+ /\bfrom\b/i
929
+ ];
930
+ var GENERIC_LIMIT_PARAM_REGEXES = [
931
+ /\blimit\b/i,
932
+ /\bcount\b/i,
933
+ /\b(?:page_?)?size\b/i,
934
+ // e.g., size, page_size, pagesize
935
+ /\bmax_results\b/i,
936
+ /\bnum_results\b/i,
937
+ /\bshow\b/i,
938
+ // Can sometimes mean limit
939
+ /\bper_?page\b/i,
940
+ // e.g., per_page, perpage
941
+ /\bper-page\b/i,
942
+ /\btake\b/i
943
+ ];
944
+ var PAGE_NUMBER_REGEXES = [
945
+ // /^(currentPage)?page$/i, // Exact match for "page"
946
+ // /^currentPage$/i, // Exact match for "currentPage"
947
+ /^p$/i,
948
+ // Exact match for "p" (common shorthand)
949
+ // /\bpage_?(?:number|no|num|idx|index)\b/i, // e.g., page_number, pageNumber, page_num, page_idx
950
+ /^(current)?_?page(_?)(?:number|no|num|idx|index)?\b/i
951
+ ];
952
+ var PAGE_SIZE_REGEXES = [
953
+ /\bpage_?size\b/i,
954
+ // e.g., page_size, pagesize
955
+ /^size$/i,
956
+ // Exact "size"
957
+ // /\bsize\b/i, // Broader "size" - can be ambiguous, prefer more specific ones first
958
+ /\blimit\b/i,
959
+ // Limit is often used for page size
960
+ /\bcount\b/i,
961
+ // Count can also be used for page size
962
+ /\bper_?page\b/i,
963
+ // e.g., per_page, perpage
964
+ /\bper-page\b/i,
965
+ /\bnum_?(?:items|records|results)\b/i,
966
+ // e.g., num_items, numitems
967
+ /\bresults_?per_?page\b/i
968
+ ];
969
+ var CURSOR_REGEXES = [
970
+ /\bmarker\b/i,
971
+ /\bcursor\b/i,
972
+ /\bafter(?:_?cursor)?\b/i,
973
+ // e.g., after, after_cursor
974
+ /\bbefore(?:_?cursor)?\b/i,
975
+ // e.g., before, before_cursor
976
+ /\b(next|prev|previous)_?(?:page_?)?token\b/i,
977
+ // e.g., next_page_token, nextPageToken, prev_token
978
+ /\b(next|prev|previous)_?cursor\b/i,
979
+ // e.g., next_cursor, previousCursor
980
+ /\bcontinuation(?:_?token)?\b/i,
981
+ // e.g., continuation, continuation_token
982
+ /\bpage(?:_?(token|id))?\b/i,
983
+ // e.g., after, after_cursor
984
+ /\bstart_?(?:key|cursor|token|after)\b/i
985
+ // e.g., start_key, startCursor, startToken, startAfter
986
+ ];
987
+ var CURSOR_LIMIT_REGEXES = [
988
+ /\blimit\b/i,
989
+ /\bcount\b/i,
990
+ /\bsize\b/i,
991
+ // General size
992
+ /\bfirst\b/i,
993
+ // Common in Relay-style cursor pagination (forward pagination)
994
+ /\blast\b/i,
995
+ // Common in Relay-style cursor pagination (backward pagination)
996
+ /\bpage_?size\b/i,
997
+ // Sometimes page_size is used with cursors
998
+ /\bnum_?(?:items|records|results)\b/i,
999
+ // e.g., num_items
1000
+ /\bmax_?items\b/i,
1001
+ /\btake\b/i
1002
+ ];
1003
+ function findParamAndKeyword(queryParams, regexes, excludeParamName) {
1004
+ for (const param of queryParams) {
1005
+ if (param.name === excludeParamName) {
1006
+ continue;
1007
+ }
1008
+ for (const regex of regexes) {
1009
+ const match = param.name.match(regex);
1010
+ if (match) {
1011
+ return { param, keyword: match[0] };
1012
+ }
1013
+ }
1014
+ }
1015
+ return null;
1016
+ }
1017
+ function coearceParameters(parameters) {
1018
+ const cleanedParameters = [];
1019
+ for (const param of parameters) {
1020
+ if (!param.schema) {
1021
+ continue;
1022
+ }
1023
+ if (isRef3(param.schema)) {
1024
+ continue;
1025
+ }
1026
+ if (!param.schema.type) {
1027
+ continue;
1028
+ }
1029
+ if (param.schema) {
1030
+ cleanedParameters.push({
1031
+ ...param,
1032
+ schema: {
1033
+ ...param.schema,
1034
+ type: Array.isArray(param.schema.type) ? param.schema.type : [param.schema.type]
1035
+ }
1036
+ });
1037
+ }
1038
+ }
1039
+ return cleanedParameters;
1040
+ }
1041
+ function isOffsetPagination(operation, parameters) {
1042
+ const params = coearceParameters(parameters).filter(
1043
+ (it) => it.schema.type.includes("integer") || it.schema.type.includes("number")
1044
+ );
1045
+ const offsetMatch = findParamAndKeyword(params, OFFSET_PARAM_REGEXES);
1046
+ if (!offsetMatch)
1047
+ return null;
1048
+ const limitMatch = findParamAndKeyword(
1049
+ params,
1050
+ GENERIC_LIMIT_PARAM_REGEXES,
1051
+ offsetMatch.param.name
1052
+ );
1053
+ if (!limitMatch)
1054
+ return null;
1055
+ return {
1056
+ type: "offset",
1057
+ offsetParamName: offsetMatch.param.name,
1058
+ offsetKeyword: offsetMatch.keyword,
1059
+ limitParamName: limitMatch.param.name,
1060
+ limitKeyword: limitMatch.keyword
1061
+ };
1062
+ }
1063
+ function isPagePagination(operation, parameters) {
1064
+ const params = coearceParameters(parameters).filter(
1065
+ (it) => it.schema.type.includes("integer") || it.schema.type.includes("number")
1066
+ );
1067
+ if (params.length < 2)
1068
+ return null;
1069
+ const pageNoMatch = findParamAndKeyword(params, PAGE_NUMBER_REGEXES);
1070
+ if (!pageNoMatch)
1071
+ return null;
1072
+ const pageSizeMatch = findParamAndKeyword(
1073
+ params,
1074
+ PAGE_SIZE_REGEXES,
1075
+ pageNoMatch.param.name
1076
+ );
1077
+ if (!pageSizeMatch)
1078
+ return null;
1079
+ return {
1080
+ type: "page",
1081
+ pageNumberParamName: pageNoMatch.param.name,
1082
+ pageNumberKeyword: pageNoMatch.keyword,
1083
+ pageSizeParamName: pageSizeMatch.param.name,
1084
+ pageSizeKeyword: pageSizeMatch.keyword
1085
+ };
1086
+ }
1087
+ function isCursorPagination(operation, parameters = []) {
1088
+ const queryParams = parameters;
1089
+ if (queryParams.length < 2)
1090
+ return null;
1091
+ const cursorMatch = findParamAndKeyword(queryParams, CURSOR_REGEXES);
1092
+ if (!cursorMatch)
1093
+ return null;
1094
+ const limitMatch = findParamAndKeyword(
1095
+ queryParams,
1096
+ CURSOR_LIMIT_REGEXES,
1097
+ cursorMatch.param.name
1098
+ );
1099
+ if (!limitMatch)
1100
+ return null;
1101
+ return {
1102
+ type: "cursor",
1103
+ cursorParamName: cursorMatch.param.name,
1104
+ cursorKeyword: cursorMatch.keyword,
1105
+ limitParamName: limitMatch.param.name,
1106
+ limitKeyword: limitMatch.keyword
1107
+ };
1108
+ }
1109
+ function guessPagination(operation, body, response) {
1110
+ const bodyParameters = body && body.properties ? Object.entries(body.properties).map(([it, schema]) => ({
1111
+ name: it,
1112
+ schema
1113
+ })) : [];
1114
+ const parameters = operation.parameters;
1115
+ if (isEmpty3(operation.parameters) && isEmpty3(bodyParameters)) {
1116
+ return { type: "none", reason: "no parameters" };
1117
+ }
1118
+ if (!response) {
1119
+ return { type: "none", reason: "no response" };
1120
+ }
1121
+ if (!response.properties) {
1122
+ return { type: "none", reason: "empty response" };
1123
+ }
1124
+ const properties = response.properties;
1125
+ const itemsKey = getItemsName(properties);
1126
+ if (!itemsKey) {
1127
+ return { type: "none", reason: "no items key" };
1128
+ }
1129
+ const hasMoreKey = getHasMoreName(excludeKey(properties, itemsKey));
1130
+ if (!hasMoreKey) {
1131
+ return { type: "none", reason: "no hasMore key" };
1132
+ }
1133
+ const pagination = isOffsetPagination(operation, [...parameters, ...bodyParameters]) || isPagePagination(operation, [...parameters, ...bodyParameters]) || isCursorPagination(operation, [...parameters, ...bodyParameters]);
1134
+ return pagination ? { ...pagination, items: itemsKey, hasMore: hasMoreKey } : { type: "none", reason: "no pagination" };
1135
+ }
1136
+ function excludeKey(obj, key) {
1137
+ const { [key]: _, ...rest } = obj;
1138
+ return rest;
1139
+ }
1140
+
1141
+ // packages/spec/src/lib/security.ts
1142
+ import { methods } from "@sdk-it/core/paths.js";
1143
+ import { resolveRef as resolveRef3 } from "@sdk-it/core/ref.js";
1144
+ function securityToOptions(spec, security2, securitySchemes, staticIn) {
1145
+ const parameters = [];
1146
+ for (const it of security2) {
1147
+ const [name] = Object.keys(it);
1148
+ if (!name) {
1149
+ continue;
1150
+ }
1151
+ const schema = resolveRef3(
1152
+ spec,
1153
+ securitySchemes[name]
1154
+ );
1155
+ if (schema.type === "http") {
1156
+ parameters.push({
1157
+ in: staticIn ?? "header",
1158
+ name: "authorization",
1159
+ schema: { type: "string" },
1160
+ example: schema.scheme === "bearer" ? '"<token>"' : `<${schema.scheme}> <token>`
1161
+ });
1162
+ continue;
1163
+ }
1164
+ if (schema.type === "apiKey") {
1165
+ if (!schema.in) {
1166
+ throw new Error(`apiKey security schema must have an "in" field`);
1167
+ }
1168
+ if (!schema.name) {
1169
+ throw new Error(`apiKey security schema must have a "name" field`);
1170
+ }
1171
+ parameters.push({
1172
+ in: staticIn ?? schema.in,
1173
+ name: schema.name,
1174
+ schema: { type: "string" },
1175
+ example: `"proj-${crypto.randomUUID()}"`
1176
+ });
1177
+ continue;
1178
+ }
1179
+ }
1180
+ return parameters;
1181
+ }
1182
+ function security(spec) {
1183
+ const security2 = spec.security || [];
1184
+ const paths = Object.values(spec.paths ?? {});
1185
+ const options = securityToOptions(
1186
+ spec,
1187
+ security2,
1188
+ spec.components.securitySchemes
1189
+ );
1190
+ for (const it of paths) {
1191
+ for (const method of methods) {
1192
+ const operation = it[method];
1193
+ if (!operation) {
1194
+ continue;
1195
+ }
1196
+ Object.assign(
1197
+ options,
1198
+ securityToOptions(
1199
+ spec,
1200
+ operation.security || [],
1201
+ spec.components.securitySchemes,
1202
+ "input"
1203
+ )
1204
+ );
1205
+ }
1206
+ }
1207
+ return options;
1208
+ }
1209
+
1210
+ // packages/spec/src/lib/operation.ts
1211
+ function findUniqueOperationId(usedOperationIds, initialId, choices, formatter) {
1212
+ let counter = 1;
1213
+ let uniqueOperationId = formatter(initialId);
1214
+ while (usedOperationIds.has(uniqueOperationId)) {
1215
+ const prependIndex = Math.min(counter - 1, choices.length - 1);
1216
+ const prefix = choices[prependIndex];
1217
+ if (prependIndex < choices.length - 1) {
1218
+ uniqueOperationId = formatter(
1219
+ `${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}`
1220
+ );
1221
+ } else {
1222
+ uniqueOperationId = formatter(
1223
+ `${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}${counter - choices.length + 1}`
1224
+ );
1225
+ }
1226
+ counter++;
1227
+ }
1228
+ return uniqueOperationId;
1229
+ }
1230
+ function augmentSpec(config, verbose = false) {
1231
+ if ("x-sdk-augmented" in config.spec) {
1232
+ return config.spec;
1233
+ }
1234
+ const spec = {
1235
+ ...config.spec,
1236
+ components: {
1237
+ ...config.spec.components,
1238
+ schemas: config.spec.components?.schemas ?? {},
1239
+ securitySchemes: config.spec.components?.securitySchemes ?? {}
1240
+ },
1241
+ paths: config.spec.paths ?? {}
1242
+ };
1243
+ const paths = {};
1244
+ const usedOperationIds = /* @__PURE__ */ new Set();
1245
+ for (const [path, pathItem] of Object.entries(spec.paths)) {
1246
+ const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
1247
+ for (const [method, operation] of Object.entries(pathItem)) {
1248
+ if (!methods2.includes(method)) {
1249
+ continue;
1250
+ }
1251
+ const formatOperationId = config.operationId ?? defaults.operationId;
1252
+ const formatTag = config.tag ?? defaults.tag;
1253
+ const operationTag = formatTag(operation, fixedPath);
1254
+ const operationId = findUniqueOperationId(
1255
+ usedOperationIds,
1256
+ formatOperationId(operation, fixedPath, method),
1257
+ [operationTag, method, fixedPath.split("/").filter(Boolean).join("")],
1258
+ (id) => formatOperationId(
1259
+ { ...operation, operationId: id },
1260
+ fixedPath,
1261
+ method
1262
+ )
1263
+ );
1264
+ usedOperationIds.add(operationId);
1265
+ const parameters = [
1266
+ ...pathItem.parameters ?? [],
1267
+ ...operation.parameters ?? []
1268
+ ].map((it) => resolveRef4(spec, it));
1269
+ const tunedOperation = {
1270
+ ...operation,
1271
+ parameters,
1272
+ tags: [snakecase2(operationTag)],
1273
+ operationId,
1274
+ responses: resolveResponses(
1275
+ spec,
1276
+ operationId,
1277
+ operation,
1278
+ config.responses
1279
+ ),
1280
+ requestBody: tuneRequestBody(
1281
+ spec,
1282
+ operationId,
1283
+ operation,
1284
+ parameters,
1285
+ operation.security ?? []
1286
+ )
1287
+ };
1288
+ tunedOperation["x-pagination"] = toPagination(spec, tunedOperation);
1289
+ Object.assign(paths, {
1290
+ [fixedPath]: {
1291
+ ...paths[fixedPath],
1292
+ [method]: tunedOperation
1293
+ }
1294
+ });
1295
+ }
1296
+ }
1297
+ fixSpec(spec, Object.values(spec.components.schemas));
1298
+ if (verbose) {
1299
+ const newRefs = [];
1300
+ expandSpec(spec, spec.components.schemas, newRefs);
1301
+ }
1302
+ return {
1303
+ ...spec,
1304
+ paths,
1305
+ "x-sdk-augmented": true
1306
+ };
1307
+ }
1308
+ function toPagination(spec, tunedOperation) {
1309
+ if (tunedOperation["x-pagination"]) {
1310
+ return tunedOperation["x-pagination"];
1311
+ }
1312
+ const schema = getResponseContentSchema(
1313
+ spec,
1314
+ tunedOperation.responses["200"],
1315
+ "application/json"
1316
+ );
1317
+ const pagination = guessPagination(
1318
+ tunedOperation,
1319
+ tunedOperation.requestBody ? getRequestContentSchema(
1320
+ spec,
1321
+ tunedOperation.requestBody,
1322
+ "application/json"
1323
+ ) : void 0,
1324
+ schema
1325
+ );
1326
+ if (pagination && pagination.type !== "none" && schema) {
1327
+ return pagination;
1328
+ }
1329
+ return void 0;
1330
+ }
1331
+ function getResponseContentSchema(spec, response, type) {
1332
+ if (!response) {
1333
+ return void 0;
1334
+ }
1335
+ const content = response.content;
1336
+ if (!content) {
1337
+ return void 0;
1338
+ }
1339
+ for (const contentType in content) {
1340
+ if (contentType.toLowerCase() === type.toLowerCase()) {
1341
+ return isRef4(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
1342
+ }
1343
+ }
1344
+ return void 0;
1345
+ }
1346
+ function getRequestContentSchema(spec, requestBody, type) {
1347
+ const content = requestBody.content;
1348
+ if (!content) {
1349
+ return void 0;
1350
+ }
1351
+ for (const contentType in content) {
1352
+ if (contentType.toLowerCase() === type.toLowerCase()) {
1353
+ return isRef4(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
1354
+ }
1355
+ }
1356
+ return void 0;
1357
+ }
1358
+ var defaults = {
1359
+ operationId: (operation, path, method) => {
1360
+ if (operation.operationId) {
1361
+ return camelcase2(
1362
+ operation.operationId.split("#").pop().replace(/-(?=\d)/g, "")
1363
+ );
1364
+ }
1365
+ const metadata = operation["x-oaiMeta"];
1366
+ if (metadata && metadata.name) {
1367
+ return camelcase2(metadata.name);
1368
+ }
1369
+ return camelcase2(
1370
+ [method, ...path.replace(/[\\/\\{\\}]/g, " ").split(" ")].filter(Boolean).join(" ").trim()
1371
+ );
1372
+ },
1373
+ tag: (operation, path) => {
1374
+ return operation.tags?.[0] ? sanitizeTag(operation.tags?.[0]) : determineGenericTag(path, operation);
1375
+ }
1376
+ };
1377
+ function resolveResponses(spec, operationId, operation, responsesConfig) {
1378
+ const responses = operation.responses ?? {};
1379
+ operation.responses ??= {};
1380
+ let foundSuccessResponse = false;
1381
+ for (const status in responses) {
1382
+ if (status === "default") {
1383
+ delete responses[status];
1384
+ continue;
1385
+ }
1386
+ operation.responses[status] = structuredClone(
1387
+ resolveRef4(spec, responses[status])
1388
+ );
1389
+ if (isSuccessStatusCode(status)) {
1390
+ foundSuccessResponse = true;
1391
+ }
1392
+ }
1393
+ if (!foundSuccessResponse) {
1394
+ operation.responses["200"] = {
1395
+ description: "OK",
1396
+ content: {
1397
+ "application/json": {
1398
+ schema: {}
1399
+ }
1400
+ }
1401
+ };
1402
+ }
1403
+ for (const status in operation.responses) {
1404
+ const response = operation.responses[status];
1405
+ const statusCode = +status;
1406
+ const outputName = statusCode !== 200 ? pascalcase3(operationId) + status : findUniqueSchemaName(spec, operationId, [
1407
+ "output",
1408
+ "payload",
1409
+ "result"
1410
+ ]);
1411
+ if (!responsesConfig?.flattenErrorResponses) {
1412
+ if (!isSuccessStatusCode(status)) {
1413
+ continue;
1414
+ }
1415
+ }
1416
+ if (isEmpty4(response.content)) {
1417
+ response.content = {
1418
+ "application/octet-stream": {}
1419
+ };
1420
+ }
1421
+ response["x-response-name"] = outputName;
1422
+ for (const [contentType, mediaType] of Object.entries(
1423
+ response.content
1424
+ )) {
1425
+ if (isRef4(mediaType.schema)) {
1426
+ const { model } = parseRef(mediaType.schema.$ref);
1427
+ Object.assign(spec.components.schemas[model], {
1428
+ "x-responsebody": true
1429
+ // do not assign response ref to a group
1430
+ // because they are supposed to be in a separate file only inlined
1431
+ // schemas are grouped by operationId
1432
+ });
1433
+ response["x-response-name"] = model;
1434
+ continue;
1435
+ }
1436
+ if (isSseContentType(contentType)) {
1437
+ continue;
1438
+ }
1439
+ if (parseJsonContentType(contentType)) {
1440
+ if (isEmpty4(mediaType.schema)) {
1441
+ spec.components.schemas[outputName] = {
1442
+ type: "object",
1443
+ additionalProperties: true
1444
+ };
1445
+ }
1446
+ } else {
1447
+ spec.components.schemas[outputName] = {
1448
+ ...spec.components.schemas[outputName],
1449
+ "x-stream": !isTextContentType(contentType)
1450
+ };
1451
+ }
1452
+ spec.components.schemas[outputName] = {
1453
+ ...spec.components.schemas[outputName],
1454
+ ...mediaType.schema,
1455
+ "x-responsebody": true,
1456
+ "x-response-group": operationId
1457
+ };
1458
+ operation.responses[status].content[contentType].schema = {
1459
+ $ref: `#/components/schemas/${outputName}`
1460
+ };
1461
+ }
1462
+ }
1463
+ return operation.responses;
1464
+ }
1465
+ function forEachOperation(spec, callback) {
1466
+ const result = [];
1467
+ for (const [path, pathItem] of Object.entries(spec.paths)) {
1468
+ for (const [method, operation] of Object.entries(pathItem)) {
1469
+ if (!methods2.includes(method)) {
1470
+ continue;
1471
+ }
1472
+ const metadata = operation["x-oaiMeta"] ?? {};
1473
+ const operationTag = operation.tags?.[0];
1474
+ result.push(
1475
+ callback(
1476
+ {
1477
+ name: metadata.name,
1478
+ method,
1479
+ path,
1480
+ groupName: operationTag,
1481
+ tag: operationTag
1482
+ },
1483
+ operation
1484
+ )
1485
+ );
1486
+ }
1487
+ }
1488
+ return result;
1489
+ }
1490
+ var reservedKeywords = /* @__PURE__ */ new Set([
1491
+ "await",
1492
+ // Reserved in async functions
1493
+ "break",
1494
+ "case",
1495
+ "catch",
1496
+ "class",
1497
+ "const",
1498
+ "continue",
1499
+ "debugger",
1500
+ "default",
1501
+ "delete",
1502
+ "do",
1503
+ "else",
1504
+ "enum",
1505
+ "export",
1506
+ "extends",
1507
+ "false",
1508
+ "finally",
1509
+ "for",
1510
+ "function",
1511
+ "if",
1512
+ "implements",
1513
+ // Strict mode
1514
+ "import",
1515
+ "in",
1516
+ "instanceof",
1517
+ "interface",
1518
+ // Strict mode
1519
+ "let",
1520
+ // Strict mode
1521
+ "new",
1522
+ "null",
1523
+ "package",
1524
+ // Strict mode
1525
+ "private",
1526
+ // Strict mode
1527
+ "protected",
1528
+ // Strict mode
1529
+ "public",
1530
+ // Strict mode
1531
+ "return",
1532
+ "static",
1533
+ // Strict mode
1534
+ "super",
1535
+ "switch",
1536
+ "this",
1537
+ "throw",
1538
+ "true",
1539
+ "try",
1540
+ "typeof",
1541
+ "var",
1542
+ "void",
1543
+ "while",
1544
+ "with",
1545
+ "yield",
1546
+ // Strict mode / Generator functions
1547
+ // 'arguments' is not technically a reserved word, but it's a special identifier within functions
1548
+ // and assigning to it or declaring it can cause issues or unexpected behavior.
1549
+ "arguments"
1550
+ ]);
1551
+ var reservedSdkKeywords = /* @__PURE__ */ new Set(["ClientError", "Error", "ConflictError"]);
1552
+ function sanitizeTag(tag) {
1553
+ if (/^\d/.test(tag)) {
1554
+ return `_${tag}`;
1555
+ }
1556
+ if (reservedKeywords.has(tag)) {
1557
+ return `$${tag}`;
1558
+ }
1559
+ if (reservedSdkKeywords.has(tag)) {
1560
+ return `$${tag}`;
1561
+ }
1562
+ return tag.replace(/[()]/g, "").replace(/--/g, "").split(/\s+/).filter(Boolean).join(" ");
1563
+ }
1564
+ function determineGenericTag(pathString, operation) {
1565
+ const operationId = operation.operationId || "";
1566
+ const VERSION_REGEX = /^[vV]\d+$/;
1567
+ const commonVerbs = /* @__PURE__ */ new Set([
1568
+ // Verbs to potentially strip from operationId prefix
1569
+ "get",
1570
+ "list",
1571
+ "create",
1572
+ "update",
1573
+ "delete",
1574
+ "post",
1575
+ "put",
1576
+ "patch",
1577
+ "do",
1578
+ "send",
1579
+ "add",
1580
+ "remove",
1581
+ "set",
1582
+ "find",
1583
+ "search",
1584
+ "check",
1585
+ "make"
1586
+ ]);
1587
+ const segments = pathString.split("/").filter(Boolean);
1588
+ const potentialCandidates = segments.filter(
1589
+ (segment) => segment && !segment.startsWith("{") && !segment.endsWith("}") && !VERSION_REGEX.test(segment)
1590
+ );
1591
+ for (let i = potentialCandidates.length - 1; i >= 0; i--) {
1592
+ const segment = potentialCandidates[i];
1593
+ if (!segment.startsWith("@")) {
1594
+ return sanitizeTag(camelcase2(segment));
1595
+ }
1596
+ }
1597
+ const canFallbackToPathSegment = potentialCandidates.length > 0;
1598
+ if (operationId) {
1599
+ const lowerOpId = operationId.toLowerCase();
1600
+ const parts = operationId.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").replace(/([a-zA-Z])(\d)/g, "$1_$2").replace(/(\d)([a-zA-Z])/g, "$1_$2").toLowerCase().split(/[_-\s]+/);
1601
+ const validParts = parts.filter(Boolean);
1602
+ if (commonVerbs.has(lowerOpId) && validParts.length === 1 && canFallbackToPathSegment) {
1603
+ } else if (validParts.length > 0) {
1604
+ const firstPart = validParts[0];
1605
+ const isFirstPartVerb = commonVerbs.has(firstPart);
1606
+ if (isFirstPartVerb && validParts.length > 1) {
1607
+ const verbPrefixLength = firstPart.length;
1608
+ let nextPartStartIndex = -1;
1609
+ if (operationId.length > verbPrefixLength) {
1610
+ const charAfterPrefix = operationId[verbPrefixLength];
1611
+ if (charAfterPrefix >= "A" && charAfterPrefix <= "Z") {
1612
+ nextPartStartIndex = verbPrefixLength;
1613
+ } else if (charAfterPrefix >= "0" && charAfterPrefix <= "9") {
1614
+ nextPartStartIndex = verbPrefixLength;
1615
+ } else if (["_", "-"].includes(charAfterPrefix)) {
1616
+ nextPartStartIndex = verbPrefixLength + 1;
1617
+ } else {
1618
+ const match = operationId.substring(verbPrefixLength).match(/[A-Z0-9]/);
1619
+ if (match && match.index !== void 0) {
1620
+ nextPartStartIndex = verbPrefixLength + match.index;
1621
+ }
1622
+ if (nextPartStartIndex === -1 && operationId.length > verbPrefixLength) {
1623
+ nextPartStartIndex = verbPrefixLength;
1624
+ }
1625
+ }
1626
+ }
1627
+ if (nextPartStartIndex !== -1 && nextPartStartIndex < operationId.length) {
1628
+ const remainingOriginalSubstring = operationId.substring(nextPartStartIndex);
1629
+ const potentialTag = camelcase2(remainingOriginalSubstring);
1630
+ if (potentialTag) {
1631
+ return sanitizeTag(potentialTag);
1632
+ }
1633
+ }
1634
+ const potentialTagJoined = camelcase2(validParts.slice(1).join("_"));
1635
+ if (potentialTagJoined) {
1636
+ return sanitizeTag(potentialTagJoined);
1637
+ }
1638
+ }
1639
+ const potentialTagFull = camelcase2(operationId);
1640
+ if (potentialTagFull) {
1641
+ const isResultSingleVerb = validParts.length === 1 && isFirstPartVerb;
1642
+ if (!(isResultSingleVerb && canFallbackToPathSegment)) {
1643
+ if (potentialTagFull.length > 0) {
1644
+ return sanitizeTag(potentialTagFull);
1645
+ }
1646
+ }
1647
+ }
1648
+ const firstPartCamel = camelcase2(firstPart);
1649
+ if (firstPartCamel) {
1650
+ const isFirstPartCamelVerb = commonVerbs.has(firstPartCamel);
1651
+ if (!isFirstPartCamelVerb || validParts.length === 1 || !canFallbackToPathSegment) {
1652
+ return sanitizeTag(firstPartCamel);
1653
+ }
1654
+ }
1655
+ if (isFirstPartVerb && validParts.length > 1 && validParts[1] && canFallbackToPathSegment) {
1656
+ const secondPartCamel = camelcase2(validParts[1]);
1657
+ if (secondPartCamel) {
1658
+ return sanitizeTag(secondPartCamel);
1659
+ }
1660
+ }
1661
+ }
1662
+ }
1663
+ if (potentialCandidates.length > 0) {
1664
+ let firstCandidate = potentialCandidates[0];
1665
+ if (firstCandidate.startsWith("@")) {
1666
+ firstCandidate = firstCandidate.substring(1);
1667
+ }
1668
+ if (firstCandidate) {
1669
+ return sanitizeTag(camelcase2(firstCandidate));
1670
+ }
1671
+ }
1672
+ console.warn(
1673
+ `Could not determine a suitable tag for path: ${pathString}, operationId: ${operationId}. Using 'unknown'.`
1674
+ );
1675
+ return "unknown";
1676
+ }
1677
+ function parseJsonContentType(contentType) {
1678
+ if (!contentType) {
1679
+ return null;
1680
+ }
1681
+ let mainType = contentType.trim();
1682
+ const semicolonIndex = mainType.indexOf(";");
1683
+ if (semicolonIndex !== -1) {
1684
+ mainType = mainType.substring(0, semicolonIndex).trim();
1685
+ }
1686
+ mainType = mainType.toLowerCase();
1687
+ if (mainType.endsWith("/json")) {
1688
+ return mainType.split("/")[1];
1689
+ } else if (mainType.endsWith("+json")) {
1690
+ return mainType.split("+")[1];
1691
+ }
1692
+ return null;
1693
+ }
1694
+ function isTextContentType(contentType) {
1695
+ if (!contentType) {
1696
+ return false;
1697
+ }
1698
+ let mainType = contentType.trim();
1699
+ const semicolonIndex = mainType.indexOf(";");
1700
+ if (semicolonIndex !== -1) {
1701
+ mainType = mainType.substring(0, semicolonIndex).trim();
1702
+ }
1703
+ mainType = mainType.toLowerCase();
1704
+ return mainType.startsWith("text/");
1705
+ }
1706
+ function isSseContentType(contentType) {
1707
+ if (!contentType) {
1708
+ return false;
1709
+ }
1710
+ let mainType = contentType.trim();
1711
+ const semicolonIndex = mainType.indexOf(";");
1712
+ if (semicolonIndex !== -1) {
1713
+ mainType = mainType.substring(0, semicolonIndex).trim();
1714
+ }
1715
+ mainType = mainType.toLowerCase();
1716
+ return mainType === "text/event-stream";
1717
+ }
1718
+ function isStreamingContentType(contentType) {
1719
+ return contentType === "application/octet-stream";
1720
+ }
1721
+ function isSuccessStatusCode(statusCode) {
1722
+ if (typeof statusCode === "string") {
1723
+ const statusGroup = +statusCode.slice(0, 1);
1724
+ const status = Number(statusCode);
1725
+ return status >= 200 && status < 300 || status >= 2 && statusGroup <= 3;
1726
+ }
1727
+ statusCode = Number(statusCode);
1728
+ return statusCode >= 200 && statusCode < 300;
1729
+ }
1730
+ function isErrorStatusCode(statusCode) {
1731
+ if (typeof statusCode === "string") {
1732
+ const statusGroup = +statusCode.slice(0, 1);
1733
+ const status = Number(statusCode);
1734
+ return status < 200 || status >= 300 || statusGroup >= 4 || statusGroup === 0 || statusGroup === 1;
1735
+ }
1736
+ statusCode = Number(statusCode);
1737
+ return statusCode < 200 || statusCode >= 300;
1738
+ }
1739
+ function patchParameters(spec, schema, parameters, security2) {
1740
+ const securitySchemes = spec.components?.securitySchemes ?? {};
1741
+ const securityOptions = securityToOptions(spec, security2, securitySchemes);
1742
+ let required = Array.isArray(schema.required) ? schema.required : [];
1743
+ schema["x-properties"] ??= {};
1744
+ for (const param of parameters) {
1745
+ if (param.required) {
1746
+ required.push(param.name);
1747
+ }
1748
+ schema["x-properties"][param.name] = {
1749
+ "x-in": param.in,
1750
+ ...isRef4(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
1751
+ };
1752
+ }
1753
+ for (const param of securityOptions) {
1754
+ required = required.filter((name) => name !== param.name);
1755
+ schema["x-properties"][param.name] = {
1756
+ "x-in": "header",
1757
+ ...isRef4(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
1758
+ };
1759
+ }
1760
+ schema["x-required"] = required;
1761
+ }
1762
+ function createOperation(options) {
1763
+ const parameters = [];
1764
+ if (!isEmpty4(options.parameters)) {
1765
+ const locations = ["query", "path", "header", "cookie"];
1766
+ for (const location of locations) {
1767
+ const locationParams = options.parameters[location];
1768
+ if (locationParams) {
1769
+ for (const [name, param] of Object.entries(locationParams)) {
1770
+ parameters.push({
1771
+ name,
1772
+ in: location,
1773
+ required: param.required ?? false,
1774
+ schema: param.schema
1775
+ });
1776
+ }
1777
+ }
1778
+ }
1779
+ }
1780
+ const responses = {};
1781
+ for (const [key, schema] of Object.entries(options.response)) {
1782
+ const [statusCode, contentType] = key.split(/-(.*)/);
1783
+ if (!contentType) {
1784
+ throw new Error(
1785
+ `Response key "${key}" must be in the format "statusCode-contentType"`
1786
+ );
1787
+ }
1788
+ responses[statusCode] ??= {
1789
+ description: `Response for ${statusCode}`,
1790
+ content: {}
1791
+ };
1792
+ if (contentType === "headers") {
1793
+ responses[statusCode].headers = schema;
1794
+ } else {
1795
+ responses[statusCode].content[contentType] = {
1796
+ schema
1797
+ };
1798
+ }
1799
+ }
1800
+ let requestBody = void 0;
1801
+ if (options.request) {
1802
+ requestBody = { description: "Request body", content: {} };
1803
+ for (const [contentType, schema] of Object.entries(options.request)) {
1804
+ requestBody.content[contentType] = { schema };
1805
+ }
1806
+ }
1807
+ return {
1808
+ security: (options.security ?? []).map((name) => ({
1809
+ [name]: []
1810
+ })),
1811
+ operationId: options.name,
1812
+ tags: [options.group],
1813
+ parameters,
1814
+ responses,
1815
+ requestBody
1816
+ };
1817
+ }
1818
+ function tuneRequestBody(spec, operationId, operation, parameters, security2) {
1819
+ const inputName = findUniqueSchemaName(spec, operationId, [
1820
+ "input",
1821
+ "payload",
1822
+ "request"
1823
+ ]);
1824
+ const requestBody = isRef4(operation.requestBody) ? followRef(spec, operation.requestBody.$ref) : operation.requestBody ?? {
1825
+ content: {},
1826
+ required: false
1827
+ };
1828
+ if (isEmpty4(requestBody.content)) {
1829
+ const schema = {
1830
+ "x-inputname": inputName,
1831
+ "x-requestbody": true
1832
+ };
1833
+ patchParameters(spec, schema, parameters, security2);
1834
+ const tuned = {
1835
+ ...requestBody,
1836
+ content: {
1837
+ "application/empty": {
1838
+ schema: { $ref: `#/components/schemas/${inputName}` }
1839
+ }
1840
+ }
1841
+ };
1842
+ spec.components.schemas[inputName] = schema;
1843
+ return tuned;
1844
+ }
1845
+ for (const contentType in requestBody.content) {
1846
+ const mediaType = requestBody.content[contentType];
1847
+ let schema;
1848
+ switch (true) {
1849
+ case isRef4(mediaType.schema):
1850
+ schema = followRef(spec, mediaType.schema.$ref);
1851
+ break;
1852
+ case isEmpty4(mediaType.schema):
1853
+ schema ??= {};
1854
+ console.warn(
1855
+ `Request body schema for content type "${contentType}" is empty.`
1856
+ );
1857
+ break;
1858
+ default:
1859
+ schema = mediaType.schema;
1860
+ break;
1861
+ }
1862
+ patchParameters(spec, schema, parameters, security2);
1863
+ spec.components.schemas[inputName] = {
1864
+ ...schema,
1865
+ "x-requestbody": true,
1866
+ "x-inputname": inputName
1867
+ };
1868
+ requestBody.content[contentType].schema = {
1869
+ $ref: `#/components/schemas/${inputName}`
1870
+ };
1871
+ }
1872
+ return requestBody;
1873
+ }
1874
+
1875
+ // packages/spec/src/lib/get-ref-usage.ts
1876
+ function getRefUsage(spec, schemaName, list = []) {
1877
+ const checkSchema = (schema, withRefCheck = false) => {
1878
+ if (isRef5(schema)) {
1879
+ if (!withRefCheck)
1880
+ return false;
1881
+ const { model } = parseRef2(schema.$ref);
1882
+ return model === schemaName;
1883
+ }
1884
+ if (!isEmpty5(schema.oneOf)) {
1885
+ return schema.oneOf.some((it) => checkSchema(it, true));
1886
+ }
1887
+ if (schema.type === "array" && schema.items) {
1888
+ if (isRef5(schema.items)) {
1889
+ return checkSchema(schema.items, withRefCheck);
1890
+ }
1891
+ if (schema.items.oneOf) {
1892
+ return schema.items.oneOf.some((it) => checkSchema(it, true));
1893
+ }
1894
+ return checkSchema(schema.items, withRefCheck);
1895
+ }
1896
+ if (schema.type === "object") {
1897
+ const properties = schema.properties;
1898
+ if (!isEmpty5(properties)) {
1899
+ let found = false;
1900
+ let propertyName = "";
1901
+ for (const [key, it] of Object.entries(properties)) {
1902
+ found = checkSchema(it, false);
1903
+ if (found) {
1904
+ propertyName = key;
1905
+ }
1906
+ }
1907
+ return propertyName;
1908
+ }
1909
+ }
1910
+ return false;
1911
+ };
1912
+ for (const [key, value] of Object.entries(spec.components.schemas)) {
1913
+ const thisWouldBeTheObjectPropertyKeyName = checkSchema(value);
1914
+ if (thisWouldBeTheObjectPropertyKeyName) {
1915
+ if (typeof thisWouldBeTheObjectPropertyKeyName === "string") {
1916
+ list.push(pascalcase4(`${key} ${thisWouldBeTheObjectPropertyKeyName}`));
1917
+ } else {
1918
+ list.push(pascalcase4(key));
1919
+ }
1920
+ }
1921
+ }
1922
+ return list;
1923
+ }
1924
+
1925
+ // packages/spec/src/lib/is-primitive-schema.ts
1926
+ function isPrimitiveSchema(schema) {
1927
+ const types = coerceTypes(schema, false);
1928
+ if (!types || types.length === 0) {
1929
+ return false;
1930
+ }
1931
+ return types.includes("object") === false;
1932
+ }
1933
+
1934
+ // packages/spec/src/lib/loaders/local-loader.ts
1935
+ import { readFile } from "node:fs/promises";
1936
+ import { extname } from "node:path";
1937
+ import { parse } from "yaml";
1938
+ async function loadLocal(location) {
1939
+ const extName = extname(location);
1940
+ const text = await readFile(location, "utf-8");
1941
+ switch (extName) {
1942
+ case ".json":
1943
+ return JSON.parse(text);
1944
+ case ".yaml":
1945
+ case ".yml":
1946
+ return parse(text);
1947
+ default:
1948
+ throw new Error(`Unsupported file extension: ${extName}`);
1949
+ }
1950
+ }
1951
+
1952
+ // packages/spec/src/lib/loaders/postman/postman-converter.ts
1953
+ import { parse as parse2 } from "fast-content-type-parse";
1954
+ function descriptionToText(description) {
1955
+ if (!description) {
1956
+ return void 0;
1957
+ }
1958
+ if (typeof description === "string") {
1959
+ return description;
1960
+ }
1961
+ if (description.content) {
1962
+ return description.content;
1963
+ }
1964
+ return void 0;
1965
+ }
1966
+ function isFolder(item) {
1967
+ return "item" in item;
1968
+ }
1969
+ function isRequest(item) {
1970
+ return !!item.request;
1971
+ }
1972
+ function processItems(items, parentTags, globalTags, paths, securitySchemes) {
1973
+ for (const item of items) {
1974
+ if (!isFolder(item) && !isRequest(item)) {
1975
+ console.warn(
1976
+ `Skipping item ${item.name} because it is not a folder or request`
1977
+ );
1978
+ continue;
1979
+ }
1980
+ if (isFolder(item)) {
1981
+ if (item.auth) {
1982
+ processAuthScheme(item.auth, securitySchemes);
1983
+ }
1984
+ if (!globalTags.some((tag) => tag.name === item.name)) {
1985
+ globalTags.push({
1986
+ name: item.name,
1987
+ description: descriptionToText(item.description)
1988
+ });
1989
+ }
1990
+ const currentTags = [...parentTags, item.name];
1991
+ processItems(item.item, currentTags, globalTags, paths, securitySchemes);
1992
+ } else if (isRequest(item)) {
1993
+ let operation;
1994
+ if (typeof item.request === "string") {
1995
+ const url = new URL(item.request);
1996
+ operation = requestToOperation(
1997
+ {
1998
+ name: url.pathname,
1999
+ response: [{ code: 200 }],
2000
+ request: {
2001
+ method: "get",
2002
+ url: {
2003
+ path: url.pathname.split("/").slice(1)
2004
+ }
2005
+ }
2006
+ },
2007
+ securitySchemes
2008
+ );
2009
+ } else {
2010
+ const auth = item.request.auth;
2011
+ operation = requestToOperation(
2012
+ {
2013
+ name: item.name,
2014
+ request: { ...item.request, auth },
2015
+ response: item.response
2016
+ },
2017
+ securitySchemes
2018
+ );
2019
+ }
2020
+ paths[operation.path] ??= {};
2021
+ Object.assign(paths[operation.path], {
2022
+ [operation.method]: {
2023
+ tags: parentTags.length ? parentTags : void 0,
2024
+ ...operation.operation
2025
+ }
2026
+ });
2027
+ }
2028
+ }
2029
+ }
2030
+ function coerceVariable(query) {
2031
+ if (!query.key) {
2032
+ throw new Error("Invalid query parameter format");
2033
+ }
2034
+ return {
2035
+ key: query.key,
2036
+ description: descriptionToText(query.description)
2037
+ };
2038
+ }
2039
+ function coerceQuery(query) {
2040
+ if (!query.key) {
2041
+ throw new Error("Invalid query parameter format");
2042
+ }
2043
+ return {
2044
+ key: query.key,
2045
+ description: descriptionToText(query.description)
2046
+ };
2047
+ }
2048
+ function coerceUrl(url) {
2049
+ if (!url) {
2050
+ throw new Error("Invalid URL format");
2051
+ }
2052
+ if (typeof url === "string") {
2053
+ return {
2054
+ path: url.split("/").slice(1),
2055
+ query: [],
2056
+ variable: []
2057
+ };
2058
+ }
2059
+ if (typeof url.path === "string") {
2060
+ return {
2061
+ path: url.path.split("/").slice(1),
2062
+ query: (url.query ?? []).map(coerceQuery),
2063
+ variable: (url.variable ?? []).map(coerceVariable)
2064
+ };
2065
+ }
2066
+ return {
2067
+ path: (url.path ?? []).map((p) => {
2068
+ if (typeof p === "string") {
2069
+ return p;
2070
+ }
2071
+ throw new Error("Invalid URL path format");
2072
+ }),
2073
+ query: (url.query ?? []).map(coerceQuery),
2074
+ variable: (url.variable ?? []).map(coerceVariable)
2075
+ };
2076
+ }
2077
+ function coerceResponseHeader(header) {
2078
+ if (!header) {
2079
+ return [];
2080
+ }
2081
+ if (typeof header === "string") {
2082
+ throw new Error(`Invalid header format: ${header}`);
2083
+ }
2084
+ return header.map((h) => {
2085
+ if (typeof h === "string") {
2086
+ return {
2087
+ key: h,
2088
+ value: null
2089
+ };
2090
+ }
2091
+ return h;
2092
+ });
2093
+ }
2094
+ function processAuthScheme(auth, securitySchemes) {
2095
+ if (!auth || auth.type === "noauth")
2096
+ return null;
2097
+ const getAuthAttr = (key) => {
2098
+ if (!auth || auth.type === "noauth")
2099
+ return void 0;
2100
+ const authType = auth[auth.type];
2101
+ if (!authType)
2102
+ return void 0;
2103
+ const attr = authType.find((a) => a.key === key);
2104
+ return attr ? String(attr.value) : void 0;
2105
+ };
2106
+ const schemeId = `${auth.type}Auth`;
2107
+ switch (auth.type) {
2108
+ case "apikey": {
2109
+ const key = getAuthAttr("key") || "api_key";
2110
+ const in_ = getAuthAttr("in") || "header";
2111
+ securitySchemes[schemeId] = {
2112
+ type: "apiKey",
2113
+ name: key,
2114
+ in: in_,
2115
+ description: "API key authentication"
2116
+ };
2117
+ break;
2118
+ }
2119
+ case "basic":
2120
+ securitySchemes[schemeId] = {
2121
+ type: "http",
2122
+ scheme: "basic",
2123
+ description: "Basic HTTP authentication"
2124
+ };
2125
+ break;
2126
+ case "bearer": {
2127
+ const token = getAuthAttr("token");
2128
+ securitySchemes[schemeId] = {
2129
+ type: "http",
2130
+ scheme: "bearer",
2131
+ description: "Bearer token authentication",
2132
+ ...token ? { bearerFormat: "JWT" } : {}
2133
+ };
2134
+ break;
2135
+ }
2136
+ case "oauth2": {
2137
+ const flowType = getAuthAttr("grant_type") || "implicit";
2138
+ securitySchemes[schemeId] = {
2139
+ type: "oauth2",
2140
+ description: "OAuth 2.0 authentication",
2141
+ flows: {
2142
+ [flowType === "authorization_code" ? "authorizationCode" : flowType]: {
2143
+ authorizationUrl: getAuthAttr("authUrl") || "https://example.com/oauth/authorize",
2144
+ tokenUrl: getAuthAttr("tokenUrl") || "https://example.com/oauth/token",
2145
+ scopes: {}
2146
+ }
2147
+ }
2148
+ };
2149
+ break;
2150
+ }
2151
+ case "digest":
2152
+ securitySchemes[schemeId] = {
2153
+ type: "http",
2154
+ scheme: "digest",
2155
+ description: "Digest authentication"
2156
+ };
2157
+ break;
2158
+ case "awsv4":
2159
+ securitySchemes[schemeId] = {
2160
+ type: "apiKey",
2161
+ name: "Authorization",
2162
+ in: "header",
2163
+ description: "AWS Signature v4 authentication"
2164
+ };
2165
+ break;
2166
+ default:
2167
+ securitySchemes[schemeId] = {
2168
+ type: "apiKey",
2169
+ name: "Authorization",
2170
+ in: "header",
2171
+ description: `${auth.type} authentication`
2172
+ };
2173
+ }
2174
+ return schemeId;
2175
+ }
2176
+ function generateSecurityRequirement(auth, securitySchemes) {
2177
+ if (!auth || auth.type === "noauth")
2178
+ return [];
2179
+ const schemeId = `${auth.type}Auth`;
2180
+ if (securitySchemes[schemeId]) {
2181
+ return [{ [schemeId]: [] }];
2182
+ }
2183
+ return [];
2184
+ }
2185
+ function requestToOperation(item, securitySchemes) {
2186
+ const url = coerceUrl(item.request.url);
2187
+ const headers = Array.isArray(item.request.header) ? item.request.header : [];
2188
+ const parameters = [
2189
+ ...url.query.filter((param) => !param.disabled).map((param) => {
2190
+ return {
2191
+ in: "query",
2192
+ name: param.key,
2193
+ required: false,
2194
+ description: param.description,
2195
+ schema: {
2196
+ ...param.value && !isNaN(Number(param.value)) ? { type: "number" } : param.value === "true" || param.value === "false" ? { type: "boolean" } : { type: "string" }
2197
+ }
2198
+ };
2199
+ }),
2200
+ ...url.variable.map((param) => {
2201
+ return {
2202
+ in: "path",
2203
+ name: param.key,
2204
+ required: true,
2205
+ description: param.description,
2206
+ schema: {
2207
+ ...param.value && !isNaN(Number(param.value)) ? { type: "number" } : param.value === "true" || param.value === "false" ? { type: "boolean" } : { type: "string" }
2208
+ }
2209
+ };
2210
+ }),
2211
+ ...headers.filter(
2212
+ (h) => !h.disabled && h.key.toLowerCase() !== "accept" && h.key.toLowerCase() !== "content-type"
2213
+ ).map((header) => {
2214
+ return {
2215
+ in: "header",
2216
+ name: header.key,
2217
+ required: false,
2218
+ description: descriptionToText(header.description),
2219
+ schema: {
2220
+ type: "string"
2221
+ }
2222
+ };
2223
+ })
2224
+ ];
2225
+ const acceptHeaderIdx = headers.findIndex(
2226
+ (h) => h.key.toLowerCase() === "accept"
2227
+ );
2228
+ const contentTypeIdx = headers.findIndex(
2229
+ (h) => h.key.toLowerCase() === "content-type"
2230
+ );
2231
+ const [acceptHeaderValue] = acceptHeaderIdx !== -1 ? headers.splice(acceptHeaderIdx, 1).map((h) => h.value) : [];
2232
+ const [contentTypeValue] = contentTypeIdx !== -1 ? headers.splice(contentTypeIdx, 1).map((h) => h.value) : [];
2233
+ let security2;
2234
+ if (item.request.auth) {
2235
+ const schemeId = processAuthScheme(item.request.auth, securitySchemes);
2236
+ if (schemeId) {
2237
+ security2 = [{ [schemeId]: [] }];
2238
+ }
2239
+ }
2240
+ return {
2241
+ path: `/${url.path.join("/")}`.replace(/:([^/]+)/g, "{$1}"),
2242
+ method: (item.request.method ?? "get").toLowerCase(),
2243
+ operation: {
2244
+ summary: item.name,
2245
+ description: descriptionToText(item.request.description),
2246
+ parameters,
2247
+ security: security2,
2248
+ responses: !item.response || item.response.length === 0 ? {
2249
+ 200: {
2250
+ description: "Successful response"
2251
+ }
2252
+ } : item.response.reduce((acc, response) => {
2253
+ const headers2 = coerceResponseHeader(response.header);
2254
+ const contentTypeIdx2 = headers2.findIndex(
2255
+ (h) => h.key.toLowerCase() === "content-type"
2256
+ );
2257
+ const [contentTypeValue2] = contentTypeIdx2 !== -1 ? headers2.splice(contentTypeIdx2).map((h) => h.value) : [];
2258
+ let contentType = response.body ? parse2(contentTypeValue2 || "application/json")?.type : null;
2259
+ contentType ??= "application/octet-stream";
2260
+ return {
2261
+ ...acc,
2262
+ [response.code ?? 200]: {
2263
+ description: response.name ? response.name : `Response for ${response.code}`,
2264
+ content: {
2265
+ [contentType]: {
2266
+ schema: bodyToSchema(response.body)
2267
+ }
2268
+ }
2269
+ }
2270
+ };
2271
+ }, {}),
2272
+ requestBody: item.request.body ? requestBodyToOperationBody(
2273
+ contentTypeValue || "application/json",
2274
+ item.request.body
2275
+ ) : void 0
2276
+ }
2277
+ };
2278
+ }
2279
+ function requestBodyToOperationBody(contentType, body) {
2280
+ if (body.mode === "raw") {
2281
+ return {
2282
+ content: {
2283
+ [contentType]: {
2284
+ schema: bodyToSchema(body.raw)
2285
+ }
2286
+ }
2287
+ };
2288
+ } else if (body.mode === "urlencoded") {
2289
+ const properties = {};
2290
+ (body.urlencoded || []).filter((param) => !param.disabled).forEach((param) => {
2291
+ properties[param.key] = {
2292
+ type: "string",
2293
+ description: descriptionToText(param.description)
2294
+ };
2295
+ });
2296
+ return {
2297
+ content: {
2298
+ "application/x-www-form-urlencoded": {
2299
+ schema: {
2300
+ type: "object",
2301
+ properties
2302
+ }
2303
+ }
2304
+ }
2305
+ };
2306
+ } else if (body.mode === "formdata") {
2307
+ const properties = {};
2308
+ (body.formdata || []).filter((param) => !param.disabled).forEach((param) => {
2309
+ if (param.type === "text") {
2310
+ properties[param.key] = {
2311
+ type: "string",
2312
+ description: descriptionToText(param.description)
2313
+ };
2314
+ } else if (param.type === "file") {
2315
+ properties[param.key] = {
2316
+ type: "string",
2317
+ format: "binary",
2318
+ description: descriptionToText(param.description)
2319
+ };
2320
+ }
2321
+ });
2322
+ return {
2323
+ content: {
2324
+ "multipart/form-data": {
2325
+ schema: {
2326
+ type: "object",
2327
+ properties
2328
+ }
2329
+ }
2330
+ }
2331
+ };
2332
+ }
2333
+ throw new Error(
2334
+ `Unsupported request body mode: ${body.mode}. Supported modes are: raw, urlencoded, formdata`
2335
+ );
2336
+ }
2337
+ function bodyToSchema(bodyString) {
2338
+ if (!bodyString) {
2339
+ return void 0;
2340
+ }
2341
+ let body;
2342
+ try {
2343
+ body = JSON.parse(bodyString.normalize("NFKD"));
2344
+ } catch (error) {
2345
+ console.warn(
2346
+ `Failed to parse JSON body: ${bodyString}. Treating as plain string.`,
2347
+ error
2348
+ );
2349
+ return { type: "string", example: bodyString };
2350
+ }
2351
+ return toSchema(body);
2352
+ }
2353
+ function toSchema(body) {
2354
+ const typeMap = {
2355
+ "<number>": "number",
2356
+ "<string>": "string",
2357
+ "<boolean>": "boolean",
2358
+ false: "boolean",
2359
+ true: "boolean"
2360
+ };
2361
+ if (Array.isArray(body)) {
2362
+ return {
2363
+ type: "array",
2364
+ items: toSchema(body[0])
2365
+ };
2366
+ }
2367
+ if (typeof body === "object" && body !== null) {
2368
+ const properties = {};
2369
+ for (const [key, value] of Object.entries(body)) {
2370
+ properties[key] = toSchema(value);
2371
+ }
2372
+ return {
2373
+ type: "object",
2374
+ properties
2375
+ };
2376
+ }
2377
+ if (typeof body === "string") {
2378
+ return {
2379
+ type: typeMap[body] ?? "string"
2380
+ };
2381
+ }
2382
+ if (typeof body === "number") {
2383
+ return {
2384
+ type: "number"
2385
+ };
2386
+ }
2387
+ if (typeof body === "boolean") {
2388
+ return {
2389
+ type: "boolean"
2390
+ };
2391
+ }
2392
+ if (body === null) {
2393
+ return {
2394
+ type: "null"
2395
+ };
2396
+ }
2397
+ console.warn(`Unknown type for body: ${body}. Defaulting to string.`, body);
2398
+ return {
2399
+ type: "string"
2400
+ };
2401
+ }
2402
+ function convertPostmanToOpenAPI(collection) {
2403
+ const tags = [];
2404
+ const paths = {};
2405
+ const securitySchemes = {};
2406
+ if (collection.auth) {
2407
+ processAuthScheme(collection.auth, securitySchemes);
2408
+ }
2409
+ processItems(collection.item, [], tags, paths, securitySchemes);
2410
+ return {
2411
+ openapi: "3.1.0",
2412
+ info: {
2413
+ title: collection.info.name,
2414
+ version: "1.0.0",
2415
+ description: descriptionToText(collection.info.description)
2416
+ },
2417
+ tags,
2418
+ paths,
2419
+ // Only add security at top level if there's collection auth
2420
+ security: collection.auth ? generateSecurityRequirement(collection.auth, securitySchemes) : void 0,
2421
+ components: Object.keys(securitySchemes).length > 0 ? {
2422
+ securitySchemes
2423
+ } : void 0
2424
+ };
2425
+ }
2426
+
2427
+ // packages/spec/src/lib/loaders/remote-loader.ts
2428
+ import { extname as extname2 } from "node:path";
2429
+ import { parse as parse3 } from "yaml";
2430
+ async function loadRemote(location) {
2431
+ const extName = extname2(location);
2432
+ const response = await fetch(location);
2433
+ if (!response.ok) {
2434
+ throw new Error(`Failed to fetch ${location}: ${response.statusText}`);
2435
+ }
2436
+ switch (extName) {
2437
+ case ".json":
2438
+ return response.json();
2439
+ case ".yaml":
2440
+ case ".yml": {
2441
+ const text = await response.text();
2442
+ return parse3(text);
2443
+ }
2444
+ default:
2445
+ try {
2446
+ return response.json();
2447
+ } catch {
2448
+ const text = await response.text();
2449
+ return parse3(text);
2450
+ }
2451
+ }
2452
+ }
2453
+
2454
+ // packages/spec/src/lib/loaders/load-spec.ts
2455
+ function isPostman(content) {
2456
+ return typeof content === "object" && content !== null && "info" in content && typeof content.info === "object" && content.info !== null && "item" in content && Array.isArray(content.item) && "schema" in content.info && typeof content.info.schema === "string" && content.info.schema.includes("//schema.getpostman.com/");
2457
+ }
2458
+ async function loadSpec(location) {
2459
+ let content = await loadFile(location);
2460
+ if (isPostman(content)) {
2461
+ content = convertPostmanToOpenAPI(content);
2462
+ }
2463
+ return content;
2464
+ }
2465
+ function loadFile(location) {
2466
+ const [protocol] = location.split(":");
2467
+ if (protocol === "http" || protocol === "https") {
2468
+ return loadRemote(location);
2469
+ }
2470
+ return loadLocal(location);
2471
+ }
2472
+
2473
+ // packages/spec/src/lib/metadata.ts
2474
+ import deubg from "debug";
2475
+ import { readFile as readFile2, unlink, writeFile } from "node:fs/promises";
2476
+ import { availableParallelism } from "node:os";
2477
+ import { join } from "node:path";
2478
+ import pLimit from "p-limit";
2479
+ import {
2480
+ addLeadingSlash,
2481
+ exist,
2482
+ readFolder
2483
+ } from "@sdk-it/core/file-system.js";
2484
+ var log = deubg("sdk-it:metdata");
2485
+ async function readWriteJson(path) {
2486
+ const content = await exist(path) ? JSON.parse(await readFile2(path, "utf-8")) : {};
2487
+ return {
2488
+ content,
2489
+ write: (value = content) => writeFile(path, JSON.stringify(value, null, 2), "utf-8")
2490
+ };
2491
+ }
2492
+ async function readWriteMetadata(output, files) {
2493
+ const metadata = await readWriteJson(join(output, "metadata.json"));
2494
+ metadata.content.generatedFiles = files;
2495
+ metadata.content.userFiles ??= ["/dist/**", "/build/**", "/readme.md"];
2496
+ await metadata.write(metadata.content);
2497
+ return metadata;
2498
+ }
2499
+ async function cleanFiles(metadata, output, alwaysAvailableFiles = []) {
2500
+ const { default: micromatch } = await import("micromatch");
2501
+ const generated = metadata.generatedFiles ?? [];
2502
+ const user = metadata.userFiles ?? [];
2503
+ const keep = [...generated, ...user, ...alwaysAvailableFiles];
2504
+ const actualFiles = (await readFolder(output, true)).map(addLeadingSlash);
2505
+ const filesToDelete = actualFiles.filter(
2506
+ (file) => !micromatch.isMatch(file, keep, { cwd: join(process.cwd(), output) })
2507
+ );
2508
+ const limit = pLimit(availableParallelism());
2509
+ await Promise.all(
2510
+ filesToDelete.map(
2511
+ (file) => limit(async () => {
2512
+ const filePath = join(output, file);
2513
+ await unlink(filePath);
2514
+ log(`Deleted file: ${filePath}`);
2515
+ })
2516
+ )
2517
+ );
2518
+ }
2519
+
2520
+ // packages/spec/src/lib/sidebar.ts
2521
+ import { camelcase as camelcase3 } from "stringcase";
2522
+ function createOAIMeta(spec) {
2523
+ spec.paths ??= {};
2524
+ const navigationGroups = {
2525
+ default: { id: "default", title: "General" }
2526
+ };
2527
+ const groups = {};
2528
+ forEachOperation(spec, (entry, operation) => {
2529
+ const tag = entry.tag;
2530
+ groups[tag] ??= {
2531
+ id: tag,
2532
+ title: tag,
2533
+ description: spec.tags?.find((t) => t.name === tag)?.description,
2534
+ navigationGroup: "default",
2535
+ sections: []
2536
+ };
2537
+ groups[tag].sections.push({
2538
+ type: "endpoint",
2539
+ key: operation.operationId,
2540
+ path: entry.path
2541
+ });
2542
+ });
2543
+ return {
2544
+ groups: Object.values(groups),
2545
+ navigationGroups: Object.values(navigationGroups)
2546
+ };
2547
+ }
2548
+ function getOperationById(spec, operationId) {
2549
+ let operation;
2550
+ forEachOperation(spec, (entry, op) => {
2551
+ if (op.operationId === operationId) {
2552
+ operation = op;
2553
+ }
2554
+ });
2555
+ return operation;
2556
+ }
2557
+ function toSidebar(spec) {
2558
+ const openapi = spec;
2559
+ const sidebar = [];
2560
+ const oaiMeta = openapi["x-oaiMeta"] ?? createOAIMeta(spec);
2561
+ for (const navGroup of oaiMeta.navigationGroups) {
2562
+ const group = oaiMeta.groups.filter(
2563
+ (group2) => group2.navigationGroup === navGroup.id
2564
+ );
2565
+ if (group.length === 0)
2566
+ continue;
2567
+ const groupItems = [];
2568
+ for (const item of group) {
2569
+ const subitems = (item.sections ?? []).filter((it) => it.type === "endpoint").map((section) => {
2570
+ const operation = getOperationById(spec, section.key);
2571
+ const title = operation?.["x-oaiMeta"]?.name || operation?.summary || section.key;
2572
+ return {
2573
+ id: section.key,
2574
+ title,
2575
+ url: `${item.id}/${camelcase3(section.key)}`
2576
+ };
2577
+ });
2578
+ if (subitems.length === 0)
2579
+ continue;
2580
+ groupItems.push({
2581
+ id: item.id,
2582
+ title: item.title,
2583
+ url: `/${item.id}`,
2584
+ description: item.description,
2585
+ items: subitems
2586
+ });
2587
+ }
2588
+ if (groupItems.length === 0)
2589
+ continue;
2590
+ sidebar.push({
2591
+ category: navGroup.title,
2592
+ items: groupItems
2593
+ });
2594
+ }
2595
+ return sidebar;
2596
+ }
2597
+ export {
2598
+ augmentSpec,
2599
+ cleanFiles,
2600
+ coerceTypes,
2601
+ createOperation,
2602
+ defaults,
2603
+ determineGenericTag,
2604
+ expandSpec,
2605
+ findPolymorphicVarients,
2606
+ findVarients,
2607
+ fixSpec,
2608
+ forEachOperation,
2609
+ formatName,
2610
+ getRefUsage,
2611
+ isErrorStatusCode,
2612
+ isPrimitiveSchema,
2613
+ isSseContentType,
2614
+ isStreamingContentType,
2615
+ isSuccessStatusCode,
2616
+ isTextContentType,
2617
+ loadFile,
2618
+ loadLocal,
2619
+ loadRemote,
2620
+ loadSpec,
2621
+ parseJsonContentType,
2622
+ patchParameters,
2623
+ readWriteJson,
2624
+ readWriteMetadata,
2625
+ sanitizeTag,
2626
+ security,
2627
+ securityToOptions,
2628
+ toSidebar
2629
+ };
3
2630
  //# sourceMappingURL=index.js.map