@trapi/swagger 1.2.3 → 2.0.0-beta.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 (118) hide show
  1. package/README.MD +146 -6
  2. package/dist/index.d.mts +812 -0
  3. package/dist/index.d.mts.map +1 -0
  4. package/dist/index.mjs +1306 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +22 -16
  7. package/dist/config/index.d.ts +0 -3
  8. package/dist/config/index.d.ts.map +0 -1
  9. package/dist/config/index.js +0 -25
  10. package/dist/config/index.js.map +0 -1
  11. package/dist/config/type.d.ts +0 -83
  12. package/dist/config/type.d.ts.map +0 -1
  13. package/dist/config/type.js +0 -9
  14. package/dist/config/type.js.map +0 -1
  15. package/dist/config/utils.d.ts +0 -3
  16. package/dist/config/utils.d.ts.map +0 -1
  17. package/dist/config/utils.js +0 -54
  18. package/dist/config/utils.js.map +0 -1
  19. package/dist/constants.d.ts +0 -15
  20. package/dist/constants.d.ts.map +0 -1
  21. package/dist/constants.js +0 -27
  22. package/dist/constants.js.map +0 -1
  23. package/dist/generator/abstract.d.ts +0 -35
  24. package/dist/generator/abstract.d.ts.map +0 -1
  25. package/dist/generator/abstract.js +0 -252
  26. package/dist/generator/abstract.js.map +0 -1
  27. package/dist/generator/index.d.ts +0 -5
  28. package/dist/generator/index.d.ts.map +0 -1
  29. package/dist/generator/index.js +0 -27
  30. package/dist/generator/index.js.map +0 -1
  31. package/dist/generator/module.d.ts +0 -14
  32. package/dist/generator/module.d.ts.map +0 -1
  33. package/dist/generator/module.js +0 -35
  34. package/dist/generator/module.js.map +0 -1
  35. package/dist/generator/v2/index.d.ts +0 -2
  36. package/dist/generator/v2/index.d.ts.map +0 -1
  37. package/dist/generator/v2/index.js +0 -24
  38. package/dist/generator/v2/index.js.map +0 -1
  39. package/dist/generator/v2/module.d.ts +0 -25
  40. package/dist/generator/v2/module.d.ts.map +0 -1
  41. package/dist/generator/v2/module.js +0 -517
  42. package/dist/generator/v2/module.js.map +0 -1
  43. package/dist/generator/v3/index.d.ts +0 -2
  44. package/dist/generator/v3/index.d.ts.map +0 -1
  45. package/dist/generator/v3/index.js +0 -24
  46. package/dist/generator/v3/index.js.map +0 -1
  47. package/dist/generator/v3/module.d.ts +0 -30
  48. package/dist/generator/v3/module.d.ts.map +0 -1
  49. package/dist/generator/v3/module.js +0 -500
  50. package/dist/generator/v3/module.js.map +0 -1
  51. package/dist/index.d.ts +0 -8
  52. package/dist/index.d.ts.map +0 -1
  53. package/dist/index.js +0 -30
  54. package/dist/index.js.map +0 -1
  55. package/dist/metadata.d.ts +0 -4
  56. package/dist/metadata.d.ts.map +0 -1
  57. package/dist/metadata.js +0 -14
  58. package/dist/metadata.js.map +0 -1
  59. package/dist/schema/constants.d.ts +0 -28
  60. package/dist/schema/constants.d.ts.map +0 -1
  61. package/dist/schema/constants.js +0 -40
  62. package/dist/schema/constants.js.map +0 -1
  63. package/dist/schema/index.d.ts +0 -5
  64. package/dist/schema/index.d.ts.map +0 -1
  65. package/dist/schema/index.js +0 -27
  66. package/dist/schema/index.js.map +0 -1
  67. package/dist/schema/type.d.ts +0 -139
  68. package/dist/schema/type.d.ts.map +0 -1
  69. package/dist/schema/type.js +0 -9
  70. package/dist/schema/type.js.map +0 -1
  71. package/dist/schema/v2/constants.d.ts +0 -8
  72. package/dist/schema/v2/constants.d.ts.map +0 -1
  73. package/dist/schema/v2/constants.js +0 -18
  74. package/dist/schema/v2/constants.js.map +0 -1
  75. package/dist/schema/v2/index.d.ts +0 -3
  76. package/dist/schema/v2/index.d.ts.map +0 -1
  77. package/dist/schema/v2/index.js +0 -25
  78. package/dist/schema/v2/index.js.map +0 -1
  79. package/dist/schema/v2/type.d.ts +0 -116
  80. package/dist/schema/v2/type.d.ts.map +0 -1
  81. package/dist/schema/v2/type.js +0 -9
  82. package/dist/schema/v2/type.js.map +0 -1
  83. package/dist/schema/v3/constants.d.ts +0 -7
  84. package/dist/schema/v3/constants.d.ts.map +0 -1
  85. package/dist/schema/v3/constants.js +0 -17
  86. package/dist/schema/v3/constants.js.map +0 -1
  87. package/dist/schema/v3/index.d.ts +0 -3
  88. package/dist/schema/v3/index.d.ts.map +0 -1
  89. package/dist/schema/v3/index.js +0 -25
  90. package/dist/schema/v3/index.js.map +0 -1
  91. package/dist/schema/v3/type.d.ts +0 -159
  92. package/dist/schema/v3/type.d.ts.map +0 -1
  93. package/dist/schema/v3/type.js +0 -9
  94. package/dist/schema/v3/type.js.map +0 -1
  95. package/dist/type.d.ts +0 -49
  96. package/dist/type.d.ts.map +0 -1
  97. package/dist/type.js +0 -9
  98. package/dist/type.js.map +0 -1
  99. package/dist/utils/character.d.ts +0 -3
  100. package/dist/utils/character.d.ts.map +0 -1
  101. package/dist/utils/character.js +0 -21
  102. package/dist/utils/character.js.map +0 -1
  103. package/dist/utils/index.d.ts +0 -5
  104. package/dist/utils/index.d.ts.map +0 -1
  105. package/dist/utils/index.js +0 -27
  106. package/dist/utils/index.js.map +0 -1
  107. package/dist/utils/object.d.ts +0 -2
  108. package/dist/utils/object.d.ts.map +0 -1
  109. package/dist/utils/object.js +0 -15
  110. package/dist/utils/object.js.map +0 -1
  111. package/dist/utils/path.d.ts +0 -2
  112. package/dist/utils/path.d.ts.map +0 -1
  113. package/dist/utils/path.js +0 -21
  114. package/dist/utils/path.js.map +0 -1
  115. package/dist/utils/value.d.ts +0 -2
  116. package/dist/utils/value.d.ts.map +0 -1
  117. package/dist/utils/value.js +0 -26
  118. package/dist/utils/value.js.map +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,1306 @@
1
+ import { BaseError } from "@ebec/core";
2
+ import { ParameterSource, TypeName, generateMetadata, isAnyType, isArrayType, isBinaryType, isEnumType, isIntersectionType, isMetadata, isNestedObjectLiteralType, isNeverType, isPrimitiveType, isRefAliasType, isRefEnumType, isRefObjectType, isReferenceType, isTupleType, isUndefinedType, isUnionType, isVoidType } from "@trapi/metadata";
3
+ import path from "node:path";
4
+ import { URL } from "node:url";
5
+ import { isObject, merge } from "smob";
6
+ import fs from "node:fs";
7
+ import process from "node:process";
8
+ import YAML from "yamljs";
9
+ //#region src/core/config/utils.ts
10
+ function buildSpecGeneratorOptions(input) {
11
+ const servers = [];
12
+ if (input.servers) if (Array.isArray(input.servers)) for (let i = 0; i < input.servers.length; i++) {
13
+ const server = input.servers[i];
14
+ if (typeof server === "string") servers.push({ url: server });
15
+ else servers.push(server);
16
+ }
17
+ else if (typeof input.servers === "string") servers.push({ url: input.servers });
18
+ else servers.push(input.servers);
19
+ return {
20
+ ...input,
21
+ servers
22
+ };
23
+ }
24
+ //#endregion
25
+ //#region src/core/constants.ts
26
+ const Version = {
27
+ V2: "v2",
28
+ V3: "v3",
29
+ V3_1: "v3.1",
30
+ V3_2: "v3.2"
31
+ };
32
+ const DocumentFormat = {
33
+ YAML: "yaml",
34
+ JSON: "json"
35
+ };
36
+ const SecurityType = {
37
+ API_KEY: "apiKey",
38
+ BASIC: "basic",
39
+ HTTP: "http",
40
+ OAUTH2: "oauth2"
41
+ };
42
+ //#endregion
43
+ //#region src/core/error/codes.ts
44
+ const SwaggerErrorCode = {
45
+ SPEC_NOT_BUILT: "SWAGGER_SPEC_NOT_BUILT",
46
+ ENUM_UNSUPPORTED_TYPE: "SWAGGER_ENUM_UNSUPPORTED_TYPE",
47
+ BODY_PARAMETER_DUPLICATE: "SWAGGER_BODY_PARAMETER_DUPLICATE",
48
+ BODY_FORM_CONFLICT: "SWAGGER_BODY_FORM_CONFLICT",
49
+ PARAMETER_SOURCE_UNSUPPORTED: "SWAGGER_PARAMETER_SOURCE_UNSUPPORTED"
50
+ };
51
+ //#endregion
52
+ //#region src/core/error/module.ts
53
+ var SwaggerError = class extends BaseError {};
54
+ //#endregion
55
+ //#region src/core/schema/v2/constants.ts
56
+ const ParameterSourceV2 = {
57
+ BODY: "body",
58
+ FORM_DATA: "formData",
59
+ HEADER: "header",
60
+ PATH: "path",
61
+ QUERY: "query"
62
+ };
63
+ //#endregion
64
+ //#region src/core/schema/v3/constants.ts
65
+ const ParameterSourceV3 = {
66
+ COOKIE: "cookie",
67
+ HEADER: "header",
68
+ PATH: "path",
69
+ QUERY: "query"
70
+ };
71
+ //#endregion
72
+ //#region src/core/schema/constants.ts
73
+ const TransferProtocol = {
74
+ HTTP: "http",
75
+ HTTPS: "https",
76
+ WS: "ws",
77
+ WSS: "wss"
78
+ };
79
+ const DataFormatName = {
80
+ INT_32: "int32",
81
+ INT_64: "int64",
82
+ FLOAT: "float",
83
+ DOUBLE: "double",
84
+ BYTE: "byte",
85
+ BINARY: "binary",
86
+ DATE: "date",
87
+ DATE_TIME: "date-time",
88
+ PASSWORD: "password"
89
+ };
90
+ const DataTypeName = {
91
+ VOID: "void",
92
+ INTEGER: "integer",
93
+ NUMBER: "number",
94
+ BOOLEAN: "boolean",
95
+ STRING: "string",
96
+ ARRAY: "array",
97
+ OBJECT: "object",
98
+ FILE: "file"
99
+ };
100
+ //#endregion
101
+ //#region src/core/utils/character.ts
102
+ function removeDuplicateSlashes(str) {
103
+ return str.replace("/([^:]$)/+/g", "$1");
104
+ }
105
+ function removeFinalCharacter(str, character) {
106
+ while (str.charAt(str.length - 1) === character && str.length > 0) str = str.slice(0, -1);
107
+ return str;
108
+ }
109
+ //#endregion
110
+ //#region src/core/utils/path.ts
111
+ function normalizePathParameters(str) {
112
+ str = str.replace(/<:([^/]+)>/g, "{$1}");
113
+ str = str.replace(/:([^/]+)/g, "{$1}");
114
+ str = str.replace(/<([^/]+)>/g, "{$1}");
115
+ return str;
116
+ }
117
+ //#endregion
118
+ //#region src/core/utils/object.ts
119
+ function hasOwnProperty(obj, prop) {
120
+ return Object.prototype.hasOwnProperty.call(obj, prop);
121
+ }
122
+ //#endregion
123
+ //#region src/core/utils/value.ts
124
+ function transformValueTo(type, value) {
125
+ if (value === null) return null;
126
+ switch (type) {
127
+ case "integer":
128
+ case "number": return Number(value);
129
+ case "boolean": return !!value;
130
+ default: return String(value);
131
+ }
132
+ }
133
+ //#endregion
134
+ //#region src/adapters/generator/abstract.ts
135
+ var AbstractSpecGenerator = class {
136
+ spec;
137
+ metadata;
138
+ config;
139
+ constructor(metadata, config) {
140
+ this.metadata = metadata;
141
+ this.config = buildSpecGeneratorOptions(config);
142
+ }
143
+ buildInfo() {
144
+ const info = {
145
+ title: this.config.name || "Documentation",
146
+ version: this.config.version || "1.0.0"
147
+ };
148
+ if (this.config.description) info.description = this.config.description;
149
+ if (this.config.license) info.license = { name: this.config.license };
150
+ return info;
151
+ }
152
+ buildTags() {
153
+ const tagMap = /* @__PURE__ */ new Map();
154
+ for (const controller of this.metadata.controllers) {
155
+ if (controller.hidden) continue;
156
+ const extensions = controller.extensions ?? [];
157
+ if (extensions.length === 0) continue;
158
+ const tagNames = controller.tags.length > 0 ? controller.tags : [controller.name];
159
+ const extensionFields = this.transformExtensions(extensions);
160
+ for (const tagName of tagNames) {
161
+ let entry = tagMap.get(tagName);
162
+ if (!entry) {
163
+ entry = { name: tagName };
164
+ tagMap.set(tagName, entry);
165
+ }
166
+ Object.assign(entry, extensionFields);
167
+ }
168
+ }
169
+ return Array.from(tagMap.values());
170
+ }
171
+ getSchemaForType(type) {
172
+ if (isVoidType(type) || isUndefinedType(type) || isNeverType(type)) return {};
173
+ if (isReferenceType(type)) return this.getSchemaForReferenceType(type);
174
+ if (isPrimitiveType(type)) return this.getSchemaForPrimitiveType(type);
175
+ if (isArrayType(type)) return this.getSchemaForArrayType(type);
176
+ if (isTupleType(type)) return this.getSchemaForTupleType(type);
177
+ if (isEnumType(type)) return this.getSchemaForEnumType(type);
178
+ if (isUnionType(type)) return this.getSchemaForUnionType(type);
179
+ if (isIntersectionType(type)) return this.getSchemaForIntersectionType(type);
180
+ if (isNestedObjectLiteralType(type)) return this.getSchemaForObjectLiteralType(type);
181
+ return {};
182
+ }
183
+ getSchemaForEnumType(enumType) {
184
+ const type = this.decideEnumType(enumType.members);
185
+ const nullable = !!enumType.members.includes(null);
186
+ const schema = {
187
+ type,
188
+ enum: enumType.members.map((member) => transformValueTo(type, member))
189
+ };
190
+ this.applyNullable(schema, nullable);
191
+ return schema;
192
+ }
193
+ getSchemaForPrimitiveType(type) {
194
+ return {
195
+ [TypeName.ANY]: { additionalProperties: true },
196
+ [TypeName.BINARY]: {
197
+ type: DataTypeName.STRING,
198
+ format: DataFormatName.BINARY
199
+ },
200
+ [TypeName.BOOLEAN]: { type: DataTypeName.BOOLEAN },
201
+ [TypeName.BUFFER]: {
202
+ type: DataTypeName.STRING,
203
+ format: DataFormatName.BYTE
204
+ },
205
+ [TypeName.BYTE]: {
206
+ type: DataTypeName.STRING,
207
+ format: DataFormatName.BYTE
208
+ },
209
+ [TypeName.DATE]: {
210
+ type: DataTypeName.STRING,
211
+ format: DataFormatName.DATE
212
+ },
213
+ [TypeName.DATETIME]: {
214
+ type: DataTypeName.STRING,
215
+ format: DataFormatName.DATE_TIME
216
+ },
217
+ [TypeName.DOUBLE]: {
218
+ type: DataTypeName.NUMBER,
219
+ format: DataFormatName.DOUBLE
220
+ },
221
+ [TypeName.FILE]: {
222
+ type: DataTypeName.STRING,
223
+ format: DataFormatName.BINARY
224
+ },
225
+ [TypeName.FLOAT]: {
226
+ type: DataTypeName.NUMBER,
227
+ format: DataFormatName.FLOAT
228
+ },
229
+ [TypeName.BIGINT]: { type: DataTypeName.INTEGER },
230
+ [TypeName.INTEGER]: {
231
+ type: DataTypeName.INTEGER,
232
+ format: DataFormatName.INT_32
233
+ },
234
+ [TypeName.LONG]: {
235
+ type: DataTypeName.INTEGER,
236
+ format: DataFormatName.INT_64
237
+ },
238
+ [TypeName.OBJECT]: {
239
+ type: DataTypeName.OBJECT,
240
+ additionalProperties: true
241
+ },
242
+ [TypeName.STRING]: { type: DataTypeName.STRING },
243
+ [TypeName.UNDEFINED]: {}
244
+ }[type.typeName] || { type: DataTypeName.OBJECT };
245
+ }
246
+ getSchemaForArrayType(arrayType) {
247
+ return {
248
+ type: DataTypeName.ARRAY,
249
+ items: this.getSchemaForType(arrayType.elementType)
250
+ };
251
+ }
252
+ getSchemaForTupleType(tupleType) {
253
+ if (tupleType.elements.length === 0) return {
254
+ type: DataTypeName.ARRAY,
255
+ items: {}
256
+ };
257
+ const elementSchemas = tupleType.elements.map((el) => this.getSchemaForType(el.type));
258
+ if (elementSchemas.length === 1) return {
259
+ type: DataTypeName.ARRAY,
260
+ items: elementSchemas[0]
261
+ };
262
+ return {
263
+ type: DataTypeName.ARRAY,
264
+ items: { anyOf: elementSchemas }
265
+ };
266
+ }
267
+ getSchemaForObjectLiteralType(objectLiteral) {
268
+ const properties = this.buildProperties(objectLiteral.properties);
269
+ const additionalProperties = objectLiteral.additionalProperties && this.getSchemaForType(objectLiteral.additionalProperties);
270
+ const required = objectLiteral.properties.filter((prop) => prop.required && !this.isUndefinedProperty(prop)).map((prop) => prop.name);
271
+ return {
272
+ properties,
273
+ ...additionalProperties && { additionalProperties },
274
+ ...required && required.length && { required },
275
+ type: DataTypeName.OBJECT
276
+ };
277
+ }
278
+ getSchemaForReferenceType(referenceType) {
279
+ return { $ref: `${this.getRefPrefix()}${referenceType.refName}` };
280
+ }
281
+ buildSchemaForRefAlias(referenceType) {
282
+ const swaggerType = this.getSchemaForType(referenceType.type);
283
+ const format = referenceType.format;
284
+ return {
285
+ ...swaggerType,
286
+ default: referenceType.default ?? swaggerType.default,
287
+ example: referenceType.example ?? swaggerType.example,
288
+ format: format ?? swaggerType.format,
289
+ description: referenceType.description ?? swaggerType.description,
290
+ ...this.transformValidators(referenceType.validators)
291
+ };
292
+ }
293
+ buildSchemaForRefEnum(referenceType) {
294
+ const output = {
295
+ ...this.getSchemaForEnumType({
296
+ typeName: TypeName.ENUM,
297
+ members: referenceType.members
298
+ }),
299
+ description: referenceType.description
300
+ };
301
+ if (typeof referenceType.memberNames !== "undefined" && referenceType.members.length === referenceType.memberNames.length) output["x-enum-varnames"] = referenceType.memberNames;
302
+ return output;
303
+ }
304
+ buildSchemasForReferenceTypes(extendFn) {
305
+ const output = {};
306
+ const keys = Object.keys(this.metadata.referenceTypes);
307
+ for (const key of keys) {
308
+ const referenceType = this.metadata.referenceTypes[key];
309
+ switch (referenceType.typeName) {
310
+ case TypeName.REF_ALIAS:
311
+ output[referenceType.refName] = this.buildSchemaForRefAlias(referenceType);
312
+ break;
313
+ case TypeName.REF_ENUM:
314
+ output[referenceType.refName] = this.buildSchemaForRefEnum(referenceType);
315
+ break;
316
+ case TypeName.REF_OBJECT:
317
+ output[referenceType.refName] = this.buildSchemaForRefObject(referenceType);
318
+ break;
319
+ }
320
+ if (typeof extendFn === "function") extendFn(output[referenceType.refName], referenceType);
321
+ }
322
+ return output;
323
+ }
324
+ isUndefinedProperty(input) {
325
+ return isUndefinedType(input.type) || isUnionType(input.type) && input.type.members.some((el) => isUndefinedType(el));
326
+ }
327
+ buildProperties(properties) {
328
+ const output = {};
329
+ properties.forEach((property) => {
330
+ const swaggerType = this.getSchemaForType(property.type);
331
+ if (swaggerType.$ref && this.shouldStripRefSiblings()) {
332
+ output[property.name] = { $ref: swaggerType.$ref };
333
+ return;
334
+ }
335
+ swaggerType.description = property.description;
336
+ swaggerType.example = property.example;
337
+ swaggerType.format = property.format || swaggerType.format;
338
+ this.assignPropertyDefaults(swaggerType, property);
339
+ if (property.deprecated) this.markPropertyDeprecated(swaggerType);
340
+ const extensions = this.transformExtensions(property.extensions);
341
+ const validators = this.transformValidators(property.validators);
342
+ output[property.name] = {
343
+ ...swaggerType,
344
+ ...validators,
345
+ ...extensions
346
+ };
347
+ });
348
+ return output;
349
+ }
350
+ shouldStripRefSiblings() {
351
+ return true;
352
+ }
353
+ assignPropertyDefaults(schema, property) {}
354
+ buildSchemaForRefObject(referenceType) {
355
+ const required = referenceType.properties.filter((p) => p.required && !this.isUndefinedProperty(p)).map((p) => p.name);
356
+ const output = {
357
+ description: referenceType.description,
358
+ properties: this.buildProperties(referenceType.properties),
359
+ required: required && required.length > 0 ? Array.from(new Set(required)) : void 0,
360
+ type: DataTypeName.OBJECT
361
+ };
362
+ if (referenceType.additionalProperties) output.additionalProperties = this.resolveAdditionalProperties(referenceType.additionalProperties);
363
+ if (referenceType.example !== void 0) output.example = referenceType.example;
364
+ return output;
365
+ }
366
+ determineTypesUsedInEnum(anEnum) {
367
+ const set = /* @__PURE__ */ new Set();
368
+ for (const element of anEnum) {
369
+ if (element === null) continue;
370
+ set.add(typeof element);
371
+ }
372
+ return Array.from(set);
373
+ }
374
+ decideEnumType(input) {
375
+ const types = this.determineTypesUsedInEnum(input);
376
+ if (types.length === 1) {
377
+ const value = types[0];
378
+ if (value === "string" || value === "number" || value === "boolean") return value;
379
+ throw new SwaggerError({
380
+ message: `Enum contains unsupported type '${types[0] || "unknown"}'. Only string, number, and boolean values are allowed.`,
381
+ code: SwaggerErrorCode.ENUM_UNSUPPORTED_TYPE
382
+ });
383
+ }
384
+ const unsupportedTypes = types.filter((type) => type !== "string" && type !== "number" && type !== "boolean");
385
+ if (unsupportedTypes.length > 0) throw new SwaggerError({
386
+ message: `Enum contains unsupported types: ${unsupportedTypes.join(", ")}. Only string, number, and boolean values are allowed.`,
387
+ code: SwaggerErrorCode.ENUM_UNSUPPORTED_TYPE
388
+ });
389
+ return "string";
390
+ }
391
+ getOperationId(name) {
392
+ return name.charAt(0).toUpperCase() + name.substring(1);
393
+ }
394
+ groupParameters(items) {
395
+ const output = {};
396
+ for (const item of items) {
397
+ if (typeof output[item.in] === "undefined") output[item.in] = [];
398
+ output[item.in].push(item);
399
+ }
400
+ return output;
401
+ }
402
+ transformExtensions(input) {
403
+ if (!input) return {};
404
+ const output = {};
405
+ for (const extension of input) {
406
+ const key = extension.key.startsWith("x-") ? extension.key : `x-${extension.key}`;
407
+ output[key] = extension.value;
408
+ }
409
+ return output;
410
+ }
411
+ transformValidators(input) {
412
+ if (!isObject(input)) return {};
413
+ const output = {};
414
+ for (const [name, validator] of Object.entries(input)) {
415
+ const mapping = validator.meta?.openApi ?? DEFAULT_VALIDATOR_OPENAPI_MAPPINGS[name];
416
+ if (!mapping || mapping.kind === "ignore") continue;
417
+ if (mapping.kind === "keyword") output[mapping.key] = validator.value;
418
+ else if (mapping.kind === "format") output.format = mapping.format;
419
+ }
420
+ return output;
421
+ }
422
+ };
423
+ const DEFAULT_VALIDATOR_OPENAPI_MAPPINGS = {
424
+ maxLength: {
425
+ kind: "keyword",
426
+ key: "maxLength"
427
+ },
428
+ minLength: {
429
+ kind: "keyword",
430
+ key: "minLength"
431
+ },
432
+ maximum: {
433
+ kind: "keyword",
434
+ key: "maximum"
435
+ },
436
+ minimum: {
437
+ kind: "keyword",
438
+ key: "minimum"
439
+ },
440
+ pattern: {
441
+ kind: "keyword",
442
+ key: "pattern"
443
+ },
444
+ maxItems: {
445
+ kind: "keyword",
446
+ key: "maxItems"
447
+ },
448
+ minItems: {
449
+ kind: "keyword",
450
+ key: "minItems"
451
+ },
452
+ uniqueItems: {
453
+ kind: "keyword",
454
+ key: "uniqueItems"
455
+ }
456
+ };
457
+ //#endregion
458
+ //#region src/adapters/generator/v2/module.ts
459
+ function uniqueOperationId$1(base, used) {
460
+ if (!used.has(base)) {
461
+ used.add(base);
462
+ return base;
463
+ }
464
+ let counter = 2;
465
+ while (used.has(`${base}_${counter}`)) counter += 1;
466
+ const candidate = `${base}_${counter}`;
467
+ used.add(candidate);
468
+ return candidate;
469
+ }
470
+ var V2Generator = class V2Generator extends AbstractSpecGenerator {
471
+ async build() {
472
+ if (typeof this.spec !== "undefined") return this.spec;
473
+ let spec = {
474
+ definitions: this.buildSchemasForReferenceTypes(),
475
+ info: this.buildInfo(),
476
+ paths: this.buildPaths(),
477
+ swagger: "2.0"
478
+ };
479
+ spec.securityDefinitions = this.config.securityDefinitions ? V2Generator.translateSecurityDefinitions(this.config.securityDefinitions) : {};
480
+ if (this.config.consumes) spec.consumes = this.config.consumes;
481
+ if (this.config.produces) spec.produces = this.config.produces;
482
+ if (this.config.servers && this.config.servers.length > 0) {
483
+ const url = new URL(this.config.servers[0].url, "http://localhost:3000/");
484
+ spec.host = url.host;
485
+ if (url.pathname) spec.basePath = url.pathname;
486
+ }
487
+ const tags = this.buildTags();
488
+ if (tags.length > 0) spec.tags = tags;
489
+ if (this.config.specificationExtra) spec = merge(spec, this.config.specificationExtra);
490
+ this.spec = spec;
491
+ return spec;
492
+ }
493
+ static translateSecurityDefinitions(securityDefinitions) {
494
+ const definitions = {};
495
+ const keys = Object.keys(securityDefinitions);
496
+ for (const key of keys) {
497
+ const securityDefinition = securityDefinitions[key];
498
+ switch (securityDefinition.type) {
499
+ case "http":
500
+ if (securityDefinition.scheme === "basic") definitions[key] = { type: "basic" };
501
+ break;
502
+ case "apiKey":
503
+ definitions[key] = securityDefinition;
504
+ break;
505
+ case "oauth2":
506
+ if (securityDefinition.flows.implicit) definitions[`${key}Implicit`] = {
507
+ type: "oauth2",
508
+ flow: "implicit",
509
+ authorizationUrl: securityDefinition.flows.implicit.authorizationUrl,
510
+ scopes: securityDefinition.flows.implicit.scopes
511
+ };
512
+ if (securityDefinition.flows.password) definitions[`${key}Password`] = {
513
+ type: "oauth2",
514
+ flow: "password",
515
+ tokenUrl: securityDefinition.flows.password.tokenUrl,
516
+ scopes: securityDefinition.flows.password.scopes
517
+ };
518
+ if (securityDefinition.flows.authorizationCode) definitions[`${key}AccessCode`] = {
519
+ type: "oauth2",
520
+ flow: "accessCode",
521
+ tokenUrl: securityDefinition.flows.authorizationCode.tokenUrl,
522
+ authorizationUrl: securityDefinition.flows.authorizationCode.authorizationUrl,
523
+ scopes: securityDefinition.flows.authorizationCode.scopes
524
+ };
525
+ if (securityDefinition.flows.clientCredentials) definitions[`${key}Application`] = {
526
+ type: "oauth2",
527
+ flow: "application",
528
+ tokenUrl: securityDefinition.flows.clientCredentials.tokenUrl,
529
+ scopes: securityDefinition.flows.clientCredentials.scopes
530
+ };
531
+ break;
532
+ }
533
+ }
534
+ return definitions;
535
+ }
536
+ resolveAdditionalProperties(type) {
537
+ return true;
538
+ }
539
+ markPropertyDeprecated(schema) {
540
+ schema["x-deprecated"] = true;
541
+ }
542
+ buildPaths() {
543
+ const output = {};
544
+ const usedOperationIds = /* @__PURE__ */ new Set();
545
+ const unique = (input) => [...new Set(input)];
546
+ this.metadata.controllers.forEach((controller) => {
547
+ if (controller.hidden) return;
548
+ const controllerPaths = controller.paths.length === 0 ? [""] : controller.paths;
549
+ controller.methods.forEach((method) => {
550
+ if (method.hidden) return;
551
+ method.consumes = unique([...controller.consumes, ...method.consumes]);
552
+ method.produces = unique([...controller.produces, ...method.produces]);
553
+ method.tags = unique([...controller.tags, ...method.tags]);
554
+ method.security = method.security || controller.security;
555
+ method.responses = unique([...controller.responses, ...method.responses]);
556
+ for (const controllerPath of controllerPaths) {
557
+ let fullPath = path.posix.join("/", controllerPath, method.path);
558
+ fullPath = normalizePathParameters(fullPath);
559
+ output[fullPath] = output[fullPath] || {};
560
+ output[fullPath][method.method] = this.buildMethod(method, fullPath, usedOperationIds);
561
+ }
562
+ });
563
+ });
564
+ return output;
565
+ }
566
+ buildMethod(method, emittedPath, usedOperationIds) {
567
+ const output = this.buildOperation(method);
568
+ output.consumes = this.buildMethodConsumes(method);
569
+ output.operationId = uniqueOperationId$1(method.operationId || output.operationId, usedOperationIds);
570
+ output.description = method.description;
571
+ if (method.summary) output.summary = method.summary;
572
+ if (method.deprecated) output.deprecated = method.deprecated;
573
+ if (method.tags.length) output.tags = method.tags;
574
+ if (method.security) output.security = method.security;
575
+ const parameters = this.groupParameters(method.parameters);
576
+ output.parameters = [
577
+ ...(parameters[ParameterSource.PATH] || []).filter((p) => emittedPath.includes(`{${p.name}}`)),
578
+ ...parameters[ParameterSource.QUERY_PROP] || [],
579
+ ...parameters[ParameterSource.HEADER] || [],
580
+ ...parameters[ParameterSource.FORM_DATA] || []
581
+ ].map((p) => this.buildParameter(p));
582
+ const bodyParameters = parameters[ParameterSource.BODY] || [];
583
+ if (bodyParameters.length > 1) throw new SwaggerError({
584
+ message: `Only one body parameter allowed per method, but ${bodyParameters.length} found in '${method.name}'.`,
585
+ code: SwaggerErrorCode.BODY_PARAMETER_DUPLICATE
586
+ });
587
+ const bodyParameter = bodyParameters.length > 0 ? this.buildParameter(bodyParameters[0]) : void 0;
588
+ const bodyPropParams = parameters[ParameterSource.BODY_PROP] || [];
589
+ if (bodyPropParams.length > 0) {
590
+ const schema = {
591
+ type: DataTypeName.OBJECT,
592
+ title: "Body",
593
+ properties: {}
594
+ };
595
+ const required = [];
596
+ for (const bodyPropParam of bodyPropParams) {
597
+ const bodyProp = this.getSchemaForType(bodyPropParam.type);
598
+ bodyProp.default = bodyPropParam.default;
599
+ bodyProp.description = bodyPropParam.description;
600
+ bodyProp.example = bodyPropParam.examples;
601
+ if (bodyProp.required) required.push(bodyPropParam.name);
602
+ schema.properties[bodyPropParam.name] = bodyProp;
603
+ }
604
+ if (bodyParameter && bodyParameter.in === ParameterSourceV2.BODY) {
605
+ if (bodyParameter.schema.type === DataTypeName.OBJECT) {
606
+ bodyParameter.schema.properties = {
607
+ ...bodyParameter.schema.properties || {},
608
+ ...schema.properties
609
+ };
610
+ bodyParameter.schema.required = [...bodyParameter.schema.required || [], ...required];
611
+ } else bodyParameter.schema = schema;
612
+ output.parameters.push(bodyParameter);
613
+ } else {
614
+ const parameter = {
615
+ in: ParameterSourceV2.BODY,
616
+ name: "body",
617
+ schema
618
+ };
619
+ if (required.length) parameter.schema.required = required;
620
+ output.parameters.push(parameter);
621
+ }
622
+ } else if (bodyParameter) output.parameters.push(bodyParameter);
623
+ Object.assign(output, this.transformExtensions(method.extensions));
624
+ return output;
625
+ }
626
+ transformParameterSource(source) {
627
+ if (source === ParameterSource.BODY) return ParameterSourceV2.BODY;
628
+ if (source === ParameterSource.FORM_DATA) return ParameterSourceV2.FORM_DATA;
629
+ if (source === ParameterSource.HEADER) return ParameterSourceV2.HEADER;
630
+ if (source === ParameterSource.PATH) return ParameterSourceV2.PATH;
631
+ if (source === ParameterSource.QUERY || source === ParameterSource.QUERY_PROP) return ParameterSourceV2.QUERY;
632
+ }
633
+ buildParameter(input) {
634
+ const sourceIn = this.transformParameterSource(input.in);
635
+ if (!sourceIn) throw new SwaggerError({
636
+ message: `The parameter source '${input.in}' for parameter '${input.name}' is not supported in OpenAPI 2.0.`,
637
+ code: SwaggerErrorCode.PARAMETER_SOURCE_UNSUPPORTED
638
+ });
639
+ const parameter = {
640
+ description: input.description,
641
+ in: sourceIn,
642
+ name: input.name,
643
+ required: input.required
644
+ };
645
+ Object.assign(parameter, this.transformExtensions(input.extensions));
646
+ if (input.in !== ParameterSource.BODY && isRefEnumType(input.type)) input.type = {
647
+ typeName: TypeName.ENUM,
648
+ members: input.type.members
649
+ };
650
+ if (parameter.in === ParameterSourceV2.FORM_DATA && input.type.typeName === TypeName.FILE) {
651
+ parameter.type = "file";
652
+ Object.assign(parameter, this.transformValidators(input.validators));
653
+ return parameter;
654
+ }
655
+ const parameterType = this.getSchemaForType(input.type);
656
+ if (parameter.in !== ParameterSourceV2.BODY && parameterType.format) parameter.format = parameterType.format;
657
+ if ((parameter.in === ParameterSourceV2.FORM_DATA || parameter.in === ParameterSourceV2.QUERY) && (input.type.typeName === TypeName.ARRAY || parameterType.type === DataTypeName.ARRAY)) parameter.collectionFormat = input.collectionFormat || this.config.collectionFormat || "multi";
658
+ if (parameter.in === ParameterSourceV2.BODY) {
659
+ if (input.type.typeName === TypeName.ARRAY || parameterType.type === DataTypeName.ARRAY) parameter.schema = {
660
+ items: parameterType.items,
661
+ type: DataTypeName.ARRAY
662
+ };
663
+ else if (input.type.typeName === TypeName.ANY) parameter.schema = { type: DataTypeName.OBJECT };
664
+ else parameter.schema = parameterType;
665
+ parameter.schema = {
666
+ ...parameter.schema,
667
+ ...this.transformValidators(input.validators)
668
+ };
669
+ return parameter;
670
+ }
671
+ Object.assign(parameter, this.transformValidators(input.validators));
672
+ if (input.type.typeName === TypeName.ANY) parameter.type = DataTypeName.STRING;
673
+ else if (parameterType.type && !Array.isArray(parameterType.type)) parameter.type = parameterType.type;
674
+ if (parameterType.items) parameter.items = parameterType.items;
675
+ if (parameterType.enum) parameter.enum = parameterType.enum;
676
+ if (typeof input.default !== "undefined") parameter.default = input.default;
677
+ return parameter;
678
+ }
679
+ buildMethodConsumes(method) {
680
+ if (method.consumes && method.consumes.length > 0) return method.consumes;
681
+ if (this.hasFileParams(method)) return ["multipart/form-data"];
682
+ if (this.hasFormParams(method)) return ["application/x-www-form-urlencoded"];
683
+ if (this.supportsBodyParameters(method.method)) return ["application/json"];
684
+ return [];
685
+ }
686
+ hasFileParams(method) {
687
+ return method.parameters.some((p) => p.in === ParameterSource.FORM_DATA && p.type.typeName === "file");
688
+ }
689
+ hasFormParams(method) {
690
+ return method.parameters.some((p) => p.in === ParameterSource.FORM_DATA);
691
+ }
692
+ supportsBodyParameters(method) {
693
+ return [
694
+ "post",
695
+ "put",
696
+ "patch"
697
+ ].includes(method);
698
+ }
699
+ applyNullable(schema, nullable) {
700
+ schema["x-nullable"] = nullable;
701
+ }
702
+ getRefPrefix() {
703
+ return "#/definitions/";
704
+ }
705
+ getSchemaForIntersectionType(type) {
706
+ const properties = type.members.reduce((acc, type) => {
707
+ if (isRefObjectType(type)) {
708
+ const refType = this.metadata.referenceTypes[type.refName];
709
+ const props = refType && refType.properties && refType.properties.reduce((pAcc, prop) => ({
710
+ ...pAcc,
711
+ [prop.name]: this.getSchemaForType(prop.type)
712
+ }), {});
713
+ return {
714
+ ...acc,
715
+ ...props
716
+ };
717
+ }
718
+ return { ...acc };
719
+ }, {});
720
+ return {
721
+ type: DataTypeName.OBJECT,
722
+ properties
723
+ };
724
+ }
725
+ getSchemaForUnionType(type) {
726
+ const members = [];
727
+ const enumTypeMember = {
728
+ typeName: TypeName.ENUM,
729
+ members: []
730
+ };
731
+ for (let i = 0; i < type.members.length; i++) {
732
+ const member = type.members[i];
733
+ if (isEnumType(member)) enumTypeMember.members.push(...member.members);
734
+ if (!isAnyType(member) && !isUndefinedType(member) && !isNeverType(member) && !isEnumType(member)) members.push(member);
735
+ }
736
+ if (members.length === 0 && enumTypeMember.members.length > 0) return this.getSchemaForEnumType(enumTypeMember);
737
+ const isNullEnum = enumTypeMember.members.every((member) => member === null);
738
+ if (members.length === 1) {
739
+ if (isNullEnum) {
740
+ const memberType = this.getSchemaForType(members[0]);
741
+ if (memberType.$ref) return memberType;
742
+ memberType["x-nullable"] = true;
743
+ return memberType;
744
+ }
745
+ if (enumTypeMember.members.length === 0) return this.getSchemaForType(members[0]);
746
+ }
747
+ return {
748
+ type: DataTypeName.OBJECT,
749
+ ...isNullEnum ? { "x-nullable": true } : {}
750
+ };
751
+ }
752
+ buildOperation(method) {
753
+ const operation = {
754
+ operationId: this.getOperationId(method.name),
755
+ consumes: method.consumes || [],
756
+ produces: method.produces || [],
757
+ responses: {},
758
+ security: method.security || []
759
+ };
760
+ const produces = [];
761
+ method.responses.forEach((res) => {
762
+ operation.responses[res.status] = { description: res.description };
763
+ if (res.schema && !isVoidType(res.schema) && !isNeverType(res.schema)) {
764
+ if (res.produces) produces.push(...res.produces);
765
+ else if (isBinaryType(res.schema)) produces.push("application/octet-stream");
766
+ operation.responses[res.status].schema = this.getSchemaForType(res.schema);
767
+ }
768
+ if (res.examples && res.examples.length > 0) {
769
+ const example = res.examples[0];
770
+ if (example.value) operation.responses[res.status].examples = { "application/json": example.value };
771
+ }
772
+ });
773
+ if (operation.consumes.length === 0) {
774
+ if (method.parameters.some((parameter) => parameter.in === ParameterSource.BODY || parameter.in === ParameterSource.BODY_PROP)) operation.consumes.push("application/json");
775
+ if (method.parameters.some((parameter) => parameter.in === ParameterSource.FORM_DATA)) operation.consumes.push("multipart/form-data");
776
+ }
777
+ if (operation.produces.length === 0 && produces.length > 0) operation.produces = [...new Set(produces)];
778
+ if (operation.produces.length === 0) operation.produces = ["application/json"];
779
+ return operation;
780
+ }
781
+ };
782
+ //#endregion
783
+ //#region src/adapters/generator/v3/module.ts
784
+ const OPENAPI_VERSION_MAP = {
785
+ v3: "3.0.0",
786
+ "v3.1": "3.1.0",
787
+ "v3.2": "3.2.0"
788
+ };
789
+ function uniqueOperationId(base, used) {
790
+ if (!used.has(base)) {
791
+ used.add(base);
792
+ return base;
793
+ }
794
+ let counter = 2;
795
+ while (used.has(`${base}_${counter}`)) counter += 1;
796
+ const candidate = `${base}_${counter}`;
797
+ used.add(candidate);
798
+ return candidate;
799
+ }
800
+ var V3Generator = class V3Generator extends AbstractSpecGenerator {
801
+ openApiVersion;
802
+ constructor(metadata, config, version = "v3.2") {
803
+ super(metadata, config);
804
+ this.openApiVersion = OPENAPI_VERSION_MAP[version] || "3.2.0";
805
+ }
806
+ async build() {
807
+ if (typeof this.spec !== "undefined") return this.spec;
808
+ let spec = {
809
+ components: this.buildComponents(),
810
+ info: this.buildInfo(),
811
+ openapi: this.openApiVersion,
812
+ paths: this.buildPaths(),
813
+ servers: this.buildServers(),
814
+ tags: this.buildTags()
815
+ };
816
+ if (this.config.specificationExtra) spec = merge(spec, this.config.specificationExtra);
817
+ this.spec = spec;
818
+ return spec;
819
+ }
820
+ buildComponents() {
821
+ const components = {
822
+ examples: {},
823
+ headers: {},
824
+ parameters: {},
825
+ requestBodies: {},
826
+ responses: {},
827
+ schemas: this.buildSchemasForReferenceTypes((output, referenceType) => {
828
+ if (referenceType.deprecated) output.deprecated = true;
829
+ }),
830
+ securitySchemes: {}
831
+ };
832
+ if (this.config.securityDefinitions) components.securitySchemes = V3Generator.translateSecurityDefinitions(this.config.securityDefinitions);
833
+ return components;
834
+ }
835
+ static translateSecurityDefinitions(securityDefinitions) {
836
+ const output = {};
837
+ const keys = Object.keys(securityDefinitions);
838
+ for (const key of keys) {
839
+ const securityDefinition = securityDefinitions[key];
840
+ switch (securityDefinition.type) {
841
+ case "http":
842
+ output[key] = securityDefinition;
843
+ break;
844
+ case "oauth2":
845
+ output[key] = securityDefinition;
846
+ break;
847
+ case "apiKey":
848
+ output[key] = securityDefinition;
849
+ break;
850
+ }
851
+ }
852
+ return output;
853
+ }
854
+ buildPaths() {
855
+ const output = {};
856
+ const usedOperationIds = /* @__PURE__ */ new Set();
857
+ for (let i = 0; i < this.metadata.controllers.length; i++) {
858
+ const controller = this.metadata.controllers[i];
859
+ if (controller.hidden) continue;
860
+ const controllerPaths = controller.paths.length === 0 ? [""] : controller.paths;
861
+ for (let j = 0; j < controller.methods.length; j++) {
862
+ const method = controller.methods[j];
863
+ if (method.hidden) continue;
864
+ for (const controllerPath of controllerPaths) {
865
+ let path = removeFinalCharacter(removeDuplicateSlashes(`/${controllerPath}/${method.path}`), "/");
866
+ path = normalizePathParameters(path);
867
+ output[path] = output[path] || {};
868
+ output[path][method.method] = this.buildMethod(controller.name, method, path, usedOperationIds);
869
+ }
870
+ }
871
+ }
872
+ return output;
873
+ }
874
+ buildMethod(controllerName, method, emittedPath, usedOperationIds) {
875
+ const output = this.buildOperation(controllerName, method);
876
+ output.description = method.description;
877
+ output.summary = method.summary;
878
+ output.tags = method.tags;
879
+ output.operationId = uniqueOperationId(method.operationId || output.operationId, usedOperationIds);
880
+ if (method.deprecated) output.deprecated = method.deprecated;
881
+ if (method.security) output.security = method.security;
882
+ const parameters = this.groupParameters(method.parameters);
883
+ const pathParams = (parameters[ParameterSource.PATH] || []).filter((p) => emittedPath.includes(`{${p.name}}`));
884
+ output.parameters = [
885
+ ...parameters[ParameterSource.QUERY_PROP] || [],
886
+ ...parameters[ParameterSource.HEADER] || [],
887
+ ...pathParams,
888
+ ...parameters[ParameterSource.COOKIE] || []
889
+ ].map((p) => this.buildParameter(p));
890
+ const bodyParams = parameters[ParameterSource.BODY] || [];
891
+ const formParams = parameters[ParameterSource.FORM_DATA] || [];
892
+ if (bodyParams.length > 1) throw new SwaggerError({
893
+ message: `Only one body parameter allowed per method, but ${bodyParams.length} found in '${method.name}'.`,
894
+ code: SwaggerErrorCode.BODY_PARAMETER_DUPLICATE
895
+ });
896
+ if (bodyParams.length > 0 && formParams.length > 0) throw new SwaggerError({
897
+ message: `Cannot mix body and form parameters in method '${method.name}'.`,
898
+ code: SwaggerErrorCode.BODY_FORM_CONFLICT
899
+ });
900
+ const bodyPropParams = parameters[ParameterSource.BODY_PROP] || [];
901
+ if (bodyPropParams.length > 0) {
902
+ if (bodyParams.length === 0) bodyParams.push({
903
+ in: ParameterSource.BODY,
904
+ name: "body",
905
+ description: "",
906
+ parameterName: bodyPropParams[0].parameterName || "body",
907
+ required: true,
908
+ type: {
909
+ typeName: TypeName.NESTED_OBJECT_LITERAL,
910
+ properties: []
911
+ },
912
+ validators: {},
913
+ deprecated: false,
914
+ extensions: []
915
+ });
916
+ if (isNestedObjectLiteralType(bodyParams[0].type)) for (const bodyPropParam of bodyPropParams) bodyParams[0].type.properties.push({
917
+ default: bodyPropParam.default,
918
+ validators: bodyPropParam.validators,
919
+ description: bodyPropParam.description,
920
+ name: bodyPropParam.name,
921
+ type: bodyPropParam.type,
922
+ required: bodyPropParam.required,
923
+ deprecated: bodyPropParam.deprecated
924
+ });
925
+ }
926
+ if (bodyParams.length > 0) output.requestBody = this.buildRequestBody(bodyParams[0]);
927
+ else if (formParams.length > 0) output.requestBody = this.buildRequestBodyWithFormData(formParams);
928
+ Object.assign(output, this.transformExtensions(method.extensions));
929
+ return output;
930
+ }
931
+ buildRequestBodyWithFormData(parameters) {
932
+ const required = [];
933
+ const properties = {};
934
+ const keys = Object.keys(parameters);
935
+ for (let i = 0; i < parameters.length; i++) {
936
+ const { schema } = this.buildMediaType(parameters[keys[i]]);
937
+ properties[parameters[keys[i]].name] = schema;
938
+ if (parameters[keys[i]].required) required.push(parameters[keys[i]].name);
939
+ }
940
+ return {
941
+ required: required.length > 0,
942
+ content: { "multipart/form-data": { schema: {
943
+ type: DataTypeName.OBJECT,
944
+ properties,
945
+ ...required && required.length && { required }
946
+ } } }
947
+ };
948
+ }
949
+ buildRequestBody(parameter) {
950
+ const mediaType = this.buildMediaType(parameter);
951
+ return {
952
+ description: parameter.description,
953
+ required: parameter.required,
954
+ content: { "application/json": mediaType }
955
+ };
956
+ }
957
+ buildMediaType(parameter) {
958
+ const examples = this.transformParameterExamples(parameter);
959
+ return {
960
+ schema: this.getSchemaForType(parameter.type),
961
+ ...Object.keys(examples).length > 0 && { examples }
962
+ };
963
+ }
964
+ buildResponses(input) {
965
+ const output = {};
966
+ for (const res of input) {
967
+ const name = res.status || "default";
968
+ output[name] = { description: res.description };
969
+ if (res.schema && !isVoidType(res.schema) && !isNeverType(res.schema)) {
970
+ const examples = {};
971
+ if (res.examples && res.examples.length > 0) for (let i = 0; i < res.examples.length; i++) {
972
+ const label = res.examples[i].label || `example${i + 1}`;
973
+ examples[label] = { value: res.examples[i].value };
974
+ }
975
+ output[name].content = output[name].content || {};
976
+ const contentTypes = res.produces || ["application/json"];
977
+ for (const contentType of contentTypes) output[name].content[contentType] = {
978
+ schema: this.getSchemaForType(res.schema),
979
+ ...Object.keys(examples).length > 0 && { examples }
980
+ };
981
+ }
982
+ if (res.headers) {
983
+ const headers = {};
984
+ if (isRefObjectType(res.headers)) headers[res.headers.refName] = {
985
+ schema: this.getSchemaForReferenceType(res.headers),
986
+ description: res.headers.description
987
+ };
988
+ else if (isNestedObjectLiteralType(res.headers)) res.headers.properties.forEach((each) => {
989
+ headers[each.name] = {
990
+ schema: this.getSchemaForType(each.type),
991
+ description: each.description,
992
+ required: each.required
993
+ };
994
+ });
995
+ output[res.name].headers = headers;
996
+ }
997
+ }
998
+ return output;
999
+ }
1000
+ buildOperation(controllerName, method) {
1001
+ const operation = {
1002
+ operationId: this.getOperationId(method.name),
1003
+ responses: this.buildResponses(method.responses)
1004
+ };
1005
+ if (method.description) operation.description = method.description;
1006
+ if (method.security) operation.security = method.security;
1007
+ if (method.deprecated) operation.deprecated = method.deprecated;
1008
+ return operation;
1009
+ }
1010
+ transformParameterSource(source) {
1011
+ if (source === ParameterSource.COOKIE) return ParameterSourceV3.COOKIE;
1012
+ if (source === ParameterSource.HEADER) return ParameterSourceV3.HEADER;
1013
+ if (source === ParameterSource.PATH) return ParameterSourceV3.PATH;
1014
+ if (source === ParameterSource.QUERY_PROP || source === ParameterSource.QUERY) return ParameterSourceV3.QUERY;
1015
+ }
1016
+ buildParameter(input) {
1017
+ const sourceIn = this.transformParameterSource(input.in);
1018
+ if (!sourceIn) throw new SwaggerError({
1019
+ message: `The parameter source '${input.in}' for parameter '${input.name}' is not supported in OpenAPI 3.x.`,
1020
+ code: SwaggerErrorCode.PARAMETER_SOURCE_UNSUPPORTED
1021
+ });
1022
+ const parameter = {
1023
+ allowEmptyValue: false,
1024
+ deprecated: false,
1025
+ description: input.description,
1026
+ in: sourceIn,
1027
+ name: input.name,
1028
+ required: input.required,
1029
+ schema: {
1030
+ default: input.default,
1031
+ format: void 0,
1032
+ ...this.transformValidators(input.validators)
1033
+ }
1034
+ };
1035
+ Object.assign(parameter, this.transformExtensions(input.extensions));
1036
+ if (input.deprecated) parameter.deprecated = true;
1037
+ const parameterType = this.getSchemaForType(input.type);
1038
+ if (parameterType.format) parameter.schema.format = parameterType.format;
1039
+ if (parameterType.$ref) {
1040
+ parameter.schema = parameterType;
1041
+ return parameter;
1042
+ }
1043
+ if (isAnyType(input.type)) parameter.schema.type = DataTypeName.STRING;
1044
+ else {
1045
+ if (parameterType.type) parameter.schema.type = parameterType.type;
1046
+ parameter.schema.items = parameterType.items;
1047
+ parameter.schema.enum = parameterType.enum;
1048
+ }
1049
+ parameter.examples = this.transformParameterExamples(input);
1050
+ return parameter;
1051
+ }
1052
+ transformParameterExamples(parameter) {
1053
+ const output = {};
1054
+ if (parameter.examples && parameter.examples.length > 0) for (let i = 0; i < parameter.examples.length; i++) {
1055
+ const label = parameter.examples[i].label || `example${i + 1}`;
1056
+ output[label] = { value: parameter.examples[i].value };
1057
+ }
1058
+ return output;
1059
+ }
1060
+ buildServers() {
1061
+ const servers = [];
1062
+ for (let i = 0; i < this.config.servers.length; i++) {
1063
+ const url = new URL(this.config.servers[i].url, "http://localhost:3000/");
1064
+ servers.push({
1065
+ url: `${url.protocol}//${url.host}${url.pathname || ""}`,
1066
+ ...this.config.servers[i].description ? { description: this.config.servers[i].description } : {}
1067
+ });
1068
+ }
1069
+ return servers;
1070
+ }
1071
+ resolveAdditionalProperties(type) {
1072
+ return this.getSchemaForType(type);
1073
+ }
1074
+ markPropertyDeprecated(schema) {
1075
+ schema.deprecated = true;
1076
+ }
1077
+ assignPropertyDefaults(schema, property) {
1078
+ schema.default = property.default;
1079
+ }
1080
+ buildSchemaForRefEnum(referenceType) {
1081
+ const typesUsed = this.determineTypesUsedInEnum(referenceType.members);
1082
+ if (typesUsed.length === 1) return super.buildSchemaForRefEnum(referenceType);
1083
+ const schema = {
1084
+ description: referenceType.description,
1085
+ anyOf: []
1086
+ };
1087
+ for (const element of typesUsed) schema.anyOf.push({
1088
+ type: element,
1089
+ enum: referenceType.members.filter((e) => typeof e === element)
1090
+ });
1091
+ return schema;
1092
+ }
1093
+ isV31OrLater() {
1094
+ return !this.openApiVersion.startsWith("3.0");
1095
+ }
1096
+ shouldStripRefSiblings() {
1097
+ return !this.isV31OrLater();
1098
+ }
1099
+ getSchemaForIntersectionType(type) {
1100
+ return { allOf: type.members.map((x) => this.getSchemaForType(x)) };
1101
+ }
1102
+ applyNullable(schema, nullable) {
1103
+ if (!nullable) return;
1104
+ if (this.isV31OrLater()) {
1105
+ if (schema.type && !Array.isArray(schema.type)) schema.type = [schema.type, "null"];
1106
+ else if (Array.isArray(schema.type) && !schema.type.includes("null")) schema.type = [...schema.type, "null"];
1107
+ } else schema.nullable = true;
1108
+ }
1109
+ getRefPrefix() {
1110
+ return "#/components/schemas/";
1111
+ }
1112
+ getSchemaForUnionType(type) {
1113
+ const members = [];
1114
+ let nullable = false;
1115
+ const enumMembers = {};
1116
+ for (let i = 0; i < type.members.length; i++) {
1117
+ const member = type.members[i];
1118
+ if (isEnumType(member)) for (let j = 0; j < member.members.length; j++) {
1119
+ const memberChild = member.members[j];
1120
+ if (memberChild === null || memberChild === void 0) {
1121
+ nullable = true;
1122
+ continue;
1123
+ }
1124
+ const typeOf = typeof memberChild;
1125
+ if (typeOf === "string" || typeOf === "number" || typeOf === "boolean") {
1126
+ enumMembers[typeOf] = enumMembers[typeOf] || [];
1127
+ enumMembers[typeOf].push(memberChild);
1128
+ }
1129
+ }
1130
+ if (!isAnyType(member) && !isUndefinedType(member) && !isNeverType(member) && !isEnumType(member)) members.push(member);
1131
+ }
1132
+ const schemas = [];
1133
+ for (const member of members) schemas.push(this.getSchemaForType(member));
1134
+ const enumMembersKeys = Object.keys(enumMembers);
1135
+ for (const enumMembersKey of enumMembersKeys) {
1136
+ const enumType = {
1137
+ typeName: "enum",
1138
+ members: enumMembers[enumMembersKey]
1139
+ };
1140
+ schemas.push(this.getSchemaForEnumType(enumType));
1141
+ }
1142
+ const useOneOf = members.length > 0 && enumMembersKeys.length === 0 && members.every((m) => V3Generator.isObjectLikeType(m));
1143
+ const compositionKey = useOneOf ? "oneOf" : "anyOf";
1144
+ if (this.isV31OrLater()) {
1145
+ if (nullable) schemas.push({ type: "null" });
1146
+ if (schemas.length === 1) return schemas[0];
1147
+ const schema = { [compositionKey]: schemas };
1148
+ if (useOneOf) this.applyDiscriminator(schema, members);
1149
+ return schema;
1150
+ }
1151
+ if (schemas.length === 1) {
1152
+ const schema = schemas[0];
1153
+ if (schema.$ref) return {
1154
+ allOf: [schema],
1155
+ nullable
1156
+ };
1157
+ return {
1158
+ ...schema,
1159
+ nullable
1160
+ };
1161
+ }
1162
+ const schema = {
1163
+ [compositionKey]: schemas,
1164
+ ...nullable ? { nullable } : {}
1165
+ };
1166
+ if (useOneOf) this.applyDiscriminator(schema, members);
1167
+ return schema;
1168
+ }
1169
+ static isObjectLikeType(type) {
1170
+ if (isRefObjectType(type) || isNestedObjectLiteralType(type) || isIntersectionType(type)) return true;
1171
+ if (isRefAliasType(type)) return V3Generator.isObjectLikeType(type.type);
1172
+ return false;
1173
+ }
1174
+ applyDiscriminator(schema, members) {
1175
+ const discriminator = this.detectDiscriminator(members);
1176
+ if (discriminator) schema.discriminator = discriminator;
1177
+ }
1178
+ detectDiscriminator(members) {
1179
+ const resolvedMembers = members.map((m) => this.resolveDiscriminatorMember(m));
1180
+ if (resolvedMembers.some((m) => !m)) return;
1181
+ const firstProps = resolvedMembers[0].properties;
1182
+ for (const prop of firstProps) {
1183
+ if (prop.type.typeName !== "enum") continue;
1184
+ if (prop.type.members.length !== 1) continue;
1185
+ const propName = prop.name;
1186
+ const mapping = {};
1187
+ let isDiscriminator = true;
1188
+ for (const member of resolvedMembers) {
1189
+ const memberProp = member.properties.find((p) => p.name === propName);
1190
+ if (!memberProp || !memberProp.required || memberProp.type.typeName !== "enum" || memberProp.type.members.length !== 1) {
1191
+ isDiscriminator = false;
1192
+ break;
1193
+ }
1194
+ const value = String(memberProp.type.members[0]);
1195
+ if (mapping[value]) {
1196
+ isDiscriminator = false;
1197
+ break;
1198
+ }
1199
+ mapping[value] = `${this.getRefPrefix()}${member.refName}`;
1200
+ }
1201
+ if (isDiscriminator && Object.keys(mapping).length === members.length) return {
1202
+ propertyName: propName,
1203
+ mapping
1204
+ };
1205
+ }
1206
+ }
1207
+ /**
1208
+ * Resolve a union member to its refName and properties for discriminator
1209
+ * detection. Accepts both refObject and refAlias members, unwrapping
1210
+ * aliases to find the underlying properties while preserving the
1211
+ * original refName for $ref mapping.
1212
+ */
1213
+ resolveDiscriminatorMember(member) {
1214
+ if (!isRefObjectType(member) && !isRefAliasType(member)) return;
1215
+ const { refName } = member;
1216
+ const referenceType = this.metadata.referenceTypes[refName];
1217
+ if (!referenceType) return;
1218
+ if (referenceType.typeName === "refObject") return {
1219
+ refName,
1220
+ properties: referenceType.properties
1221
+ };
1222
+ if (referenceType.typeName === "refAlias") {
1223
+ let inner = referenceType.type;
1224
+ for (let depth = 0; depth < 10; depth++) {
1225
+ if (isRefObjectType(inner)) {
1226
+ const resolved = this.metadata.referenceTypes[inner.refName];
1227
+ if (resolved?.typeName === "refObject") return {
1228
+ refName,
1229
+ properties: resolved.properties
1230
+ };
1231
+ return;
1232
+ }
1233
+ if (isNestedObjectLiteralType(inner)) return {
1234
+ refName,
1235
+ properties: inner.properties
1236
+ };
1237
+ if (isRefAliasType(inner)) {
1238
+ inner = inner.type;
1239
+ continue;
1240
+ }
1241
+ return;
1242
+ }
1243
+ }
1244
+ }
1245
+ };
1246
+ //#endregion
1247
+ //#region src/app/module.ts
1248
+ function toSpecGeneratorOptionsInput(options) {
1249
+ const { data } = options;
1250
+ if (!data) return {};
1251
+ return {
1252
+ name: data.name,
1253
+ version: data.version,
1254
+ description: data.description,
1255
+ license: data.license,
1256
+ servers: data.servers,
1257
+ securityDefinitions: data.securityDefinitions,
1258
+ consumes: data.consumes,
1259
+ produces: data.produces,
1260
+ collectionFormat: data.collectionFormat,
1261
+ specificationExtra: data.extra
1262
+ };
1263
+ }
1264
+ async function resolveMetadata(options) {
1265
+ if (isMetadata(options.metadata)) return options.metadata;
1266
+ return generateMetadata(options.metadata);
1267
+ }
1268
+ async function generateSwagger(options) {
1269
+ const metadata = await resolveMetadata(options);
1270
+ const specGeneratorOptionsInput = toSpecGeneratorOptionsInput(options);
1271
+ switch (options.version) {
1272
+ case Version.V3:
1273
+ case Version.V3_1:
1274
+ case Version.V3_2: return await new V3Generator(metadata, specGeneratorOptionsInput, options.version).build();
1275
+ default: return await new V2Generator(metadata, specGeneratorOptionsInput).build();
1276
+ }
1277
+ }
1278
+ //#endregion
1279
+ //#region src/app/save.ts
1280
+ const EXTENSION_PATTERN = /\.(json|ya?ml)$/i;
1281
+ function resolveFileName(name, format) {
1282
+ return `${(name ?? "swagger").replace(EXTENSION_PATTERN, "")}.${format}`;
1283
+ }
1284
+ function serialise(spec, format) {
1285
+ if (format === DocumentFormat.YAML) return YAML.stringify(spec, 1e3);
1286
+ return JSON.stringify(spec, null, 4);
1287
+ }
1288
+ async function saveSwagger(spec, options = {}) {
1289
+ const format = options.format ?? DocumentFormat.JSON;
1290
+ let cwd = process.cwd();
1291
+ if (options.cwd) cwd = path.isAbsolute(options.cwd) ? options.cwd : path.join(process.cwd(), options.cwd);
1292
+ const name = resolveFileName(options.name, format);
1293
+ const filePath = path.join(cwd, name);
1294
+ await fs.promises.mkdir(cwd, { recursive: true });
1295
+ const content = serialise(spec, format);
1296
+ await fs.promises.writeFile(filePath, content, { encoding: "utf-8" });
1297
+ return {
1298
+ path: filePath,
1299
+ name,
1300
+ content
1301
+ };
1302
+ }
1303
+ //#endregion
1304
+ export { AbstractSpecGenerator, DataFormatName, DataTypeName, DocumentFormat, ParameterSourceV2, ParameterSourceV3, SecurityType, SwaggerError, SwaggerErrorCode, TransferProtocol, V2Generator, V3Generator, Version, buildSpecGeneratorOptions, generateSwagger, hasOwnProperty, normalizePathParameters, removeDuplicateSlashes, removeFinalCharacter, saveSwagger, transformValueTo };
1305
+
1306
+ //# sourceMappingURL=index.mjs.map