@typia/utils 12.0.0-dev.20260311 → 12.0.0-dev.20260312

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 (51) hide show
  1. package/lib/converters/OpenApiConverter.d.ts +29 -16
  2. package/lib/converters/OpenApiConverter.js +25 -15
  3. package/lib/converters/OpenApiConverter.js.map +1 -1
  4. package/lib/converters/OpenApiConverter.mjs +25 -15
  5. package/lib/converters/OpenApiConverter.mjs.map +1 -1
  6. package/lib/converters/internal/OpenApiV3Downgrader.js +38 -18
  7. package/lib/converters/internal/OpenApiV3Downgrader.js.map +1 -1
  8. package/lib/converters/internal/OpenApiV3Downgrader.mjs +50 -27
  9. package/lib/converters/internal/OpenApiV3Downgrader.mjs.map +1 -1
  10. package/lib/converters/internal/OpenApiV3Upgrader.js +36 -18
  11. package/lib/converters/internal/OpenApiV3Upgrader.js.map +1 -1
  12. package/lib/converters/internal/OpenApiV3Upgrader.mjs +50 -29
  13. package/lib/converters/internal/OpenApiV3Upgrader.mjs.map +1 -1
  14. package/lib/converters/internal/OpenApiV3_1Upgrader.js +38 -20
  15. package/lib/converters/internal/OpenApiV3_1Upgrader.js.map +1 -1
  16. package/lib/converters/internal/OpenApiV3_1Upgrader.mjs +51 -29
  17. package/lib/converters/internal/OpenApiV3_1Upgrader.mjs.map +1 -1
  18. package/lib/converters/internal/OpenApiV3_2Upgrader.d.ts +16 -0
  19. package/lib/converters/internal/OpenApiV3_2Upgrader.js +204 -0
  20. package/lib/converters/internal/OpenApiV3_2Upgrader.js.map +1 -0
  21. package/lib/converters/internal/OpenApiV3_2Upgrader.mjs +243 -0
  22. package/lib/converters/internal/OpenApiV3_2Upgrader.mjs.map +1 -0
  23. package/lib/converters/internal/SwaggerV2Downgrader.js +38 -18
  24. package/lib/converters/internal/SwaggerV2Downgrader.js.map +1 -1
  25. package/lib/converters/internal/SwaggerV2Downgrader.mjs +50 -27
  26. package/lib/converters/internal/SwaggerV2Downgrader.mjs.map +1 -1
  27. package/lib/converters/internal/SwaggerV2Upgrader.js +37 -19
  28. package/lib/converters/internal/SwaggerV2Upgrader.js.map +1 -1
  29. package/lib/converters/internal/SwaggerV2Upgrader.mjs +50 -29
  30. package/lib/converters/internal/SwaggerV2Upgrader.mjs.map +1 -1
  31. package/lib/http/internal/HttpMigrateApplicationComposer.js +1 -1
  32. package/lib/http/internal/HttpMigrateApplicationComposer.js.map +1 -1
  33. package/lib/http/internal/HttpMigrateApplicationComposer.mjs +1 -1
  34. package/lib/http/internal/HttpMigrateApplicationComposer.mjs.map +1 -1
  35. package/lib/http/internal/HttpMigrateRouteComposer.d.ts +1 -1
  36. package/lib/utils/LlmJson.d.ts +19 -1
  37. package/lib/utils/LlmJson.js +27 -0
  38. package/lib/utils/LlmJson.js.map +1 -1
  39. package/lib/utils/LlmJson.mjs +27 -0
  40. package/lib/utils/LlmJson.mjs.map +1 -1
  41. package/package.json +2 -2
  42. package/src/converters/OpenApiConverter.ts +322 -285
  43. package/src/converters/internal/OpenApiV3Downgrader.ts +381 -355
  44. package/src/converters/internal/OpenApiV3Upgrader.ts +494 -470
  45. package/src/converters/internal/OpenApiV3_1Upgrader.ts +710 -685
  46. package/src/converters/internal/OpenApiV3_2Upgrader.ts +342 -0
  47. package/src/converters/internal/SwaggerV2Downgrader.ts +450 -424
  48. package/src/converters/internal/SwaggerV2Upgrader.ts +547 -523
  49. package/src/http/internal/HttpMigrateApplicationComposer.ts +56 -56
  50. package/src/http/internal/HttpMigrateRouteComposer.ts +505 -505
  51. package/src/utils/LlmJson.ts +173 -141
@@ -1,523 +1,547 @@
1
- import { IJsonSchemaAttribute, OpenApi, SwaggerV2 } from "@typia/interface";
2
-
3
- import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
- import { SwaggerV2TypeChecker } from "../../validators/SwaggerV2TypeChecker";
5
- import { OpenApiExclusiveEmender } from "./OpenApiExclusiveEmender";
6
-
7
- export namespace SwaggerV2Upgrader {
8
- export const convert = (input: SwaggerV2.IDocument): OpenApi.IDocument => ({
9
- openapi: "3.1.0",
10
- info: input.info,
11
- components: convertComponents(input),
12
- paths: input.paths
13
- ? Object.fromEntries(
14
- Object.entries(input.paths)
15
- .filter(([_, v]) => v !== undefined)
16
- .map(
17
- ([key, value]) => [key, convertPathItem(input)(value)] as const,
18
- ),
19
- )
20
- : undefined,
21
- servers: input.host
22
- ? [
23
- {
24
- url: input.host,
25
- },
26
- ]
27
- : undefined,
28
- security: input.security,
29
- tags: input.tags,
30
- "x-samchon-emended-v4": true,
31
- });
32
-
33
- /* -----------------------------------------------------------
34
- OPERATORS
35
- ----------------------------------------------------------- */
36
- const convertPathItem =
37
- (doc: SwaggerV2.IDocument) =>
38
- (pathItem: SwaggerV2.IPath): OpenApi.IPath => ({
39
- ...(pathItem as any),
40
- ...(pathItem.get
41
- ? { get: convertOperation(doc)(pathItem)(pathItem.get) }
42
- : undefined),
43
- ...(pathItem.put
44
- ? { put: convertOperation(doc)(pathItem)(pathItem.put) }
45
- : undefined),
46
- ...(pathItem.post
47
- ? { post: convertOperation(doc)(pathItem)(pathItem.post) }
48
- : undefined),
49
- ...(pathItem.delete
50
- ? { delete: convertOperation(doc)(pathItem)(pathItem.delete) }
51
- : undefined),
52
- ...(pathItem.options
53
- ? { options: convertOperation(doc)(pathItem)(pathItem.options) }
54
- : undefined),
55
- ...(pathItem.head
56
- ? { head: convertOperation(doc)(pathItem)(pathItem.head) }
57
- : undefined),
58
- ...(pathItem.patch
59
- ? { patch: convertOperation(doc)(pathItem)(pathItem.patch) }
60
- : undefined),
61
- ...(pathItem.trace
62
- ? { trace: convertOperation(doc)(pathItem)(pathItem.trace) }
63
- : undefined),
64
- });
65
-
66
- const convertOperation =
67
- (doc: SwaggerV2.IDocument) =>
68
- (pathItem: SwaggerV2.IPath) =>
69
- (input: SwaggerV2.IOperation): OpenApi.IOperation => ({
70
- ...input,
71
- parameters:
72
- pathItem.parameters !== undefined || input.parameters !== undefined
73
- ? (
74
- [...(pathItem.parameters ?? []), ...(input.parameters ?? [])]
75
- .map((p) =>
76
- SwaggerV2TypeChecker.isReference(p)
77
- ? doc.parameters?.[p.$ref.split("/").pop() ?? ""]!
78
- : p,
79
- )
80
- .filter(
81
- (p) =>
82
- p !== undefined &&
83
- p.in !== "body" &&
84
- (p as SwaggerV2.IOperation.IBodyParameter).schema ===
85
- undefined,
86
- ) as SwaggerV2.IOperation.IGeneralParameter[]
87
- ).map(convertParameter(doc.definitions ?? {}))
88
- : undefined,
89
- requestBody: (() => {
90
- const found: SwaggerV2.IOperation.IBodyParameter | undefined =
91
- input.parameters?.find((p) => {
92
- if (SwaggerV2TypeChecker.isReference(p))
93
- p = doc.parameters?.[p.$ref.split("/").pop() ?? ""]!;
94
- return (
95
- (p as SwaggerV2.IOperation.IBodyParameter)?.schema !== undefined
96
- );
97
- }) as SwaggerV2.IOperation.IBodyParameter | undefined;
98
- return found
99
- ? convertRequestBody(doc.definitions ?? {})(found)
100
- : undefined;
101
- })(),
102
- responses: input.responses
103
- ? Object.fromEntries(
104
- Object.entries(input.responses)
105
- .filter(([_, v]) => v !== undefined)
106
- .map(
107
- ([key, value]) => [key, convertResponse(doc)(value)!] as const,
108
- )
109
- .filter(([_, v]) => v !== undefined),
110
- )
111
- : undefined,
112
- });
113
-
114
- const convertParameter =
115
- (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
116
- (
117
- input: SwaggerV2.IOperation.IGeneralParameter,
118
- ): OpenApi.IOperation.IParameter => ({
119
- name: input.name,
120
- in: input.in as any,
121
- description: input.description,
122
- schema: convertSchema(definitions)(input),
123
- required: true,
124
- });
125
- const convertRequestBody =
126
- (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
127
- (
128
- input: SwaggerV2.IOperation.IBodyParameter,
129
- ): OpenApi.IOperation.IRequestBody => ({
130
- description: input.description,
131
- content: {
132
- "application/json": {
133
- schema: convertSchema(definitions)(input.schema),
134
- },
135
- },
136
- });
137
-
138
- const convertResponse =
139
- (doc: SwaggerV2.IDocument) =>
140
- (
141
- input:
142
- | SwaggerV2.IOperation.IResponse
143
- | SwaggerV2.IJsonSchema.IReference<`#/definitions/responses/${string}`>,
144
- ): OpenApi.IOperation.IResponse | undefined => {
145
- if (SwaggerV2TypeChecker.isReference(input)) {
146
- const found: SwaggerV2.IOperation.IResponse | undefined =
147
- doc.responses?.[input.$ref.split("/").pop() ?? ""]!;
148
- if (found === undefined) return undefined;
149
- input = found;
150
- }
151
- return {
152
- description: input.description,
153
- content: input.schema
154
- ? {
155
- "application/json": {
156
- schema: convertSchema(doc.definitions ?? {})(input.schema),
157
- example: input.example,
158
- },
159
- }
160
- : undefined,
161
- headers: input.headers
162
- ? Object.fromEntries(
163
- Object.entries(input.headers)
164
- .filter(([_, v]) => v !== undefined)
165
- .map(
166
- ([key, value]) =>
167
- [
168
- key,
169
- {
170
- schema: convertSchema(doc.definitions ?? {})(value),
171
- in: "header",
172
- },
173
- ] as const,
174
- ),
175
- )
176
- : undefined,
177
- };
178
- };
179
-
180
- /* -----------------------------------------------------------
181
- DEFINITIONS
182
- ----------------------------------------------------------- */
183
- export const convertComponents = (
184
- input: SwaggerV2.IDocument,
185
- ): OpenApi.IComponents => ({
186
- schemas: Object.fromEntries(
187
- Object.entries(input.definitions ?? {})
188
- .filter(([_, v]) => v !== undefined)
189
- .map(([key, value]) => [
190
- key,
191
- convertSchema(input.definitions ?? {})(value),
192
- ]),
193
- ),
194
- securitySchemes: input.securityDefinitions
195
- ? Object.fromEntries(
196
- Object.entries(input.securityDefinitions)
197
- .filter(([_, v]) => v !== undefined)
198
- .map(([key, value]) => [key, convertSecurityScheme(value)])
199
- .filter(([_, v]) => v !== undefined),
200
- )
201
- : undefined,
202
- });
203
-
204
- const convertSecurityScheme = (
205
- input: SwaggerV2.ISecurityDefinition,
206
- ): OpenApi.ISecurityScheme => {
207
- if (input.type === "apiKey") return input;
208
- else if (input.type === "basic")
209
- return {
210
- type: "http",
211
- scheme: "basic",
212
- description: input.description,
213
- };
214
- else if (input.type === "oauth2")
215
- if (input.flow === "implicit")
216
- return {
217
- type: "oauth2",
218
- description: input.description,
219
- flows: {
220
- implicit: {
221
- authorizationUrl: input.authorizationUrl,
222
- scopes: input.scopes,
223
- },
224
- },
225
- };
226
- else if (input.flow === "accessCode")
227
- return {
228
- type: "oauth2",
229
- description: input.description,
230
- flows: {
231
- authorizationCode: {
232
- authorizationUrl: input.authorizationUrl,
233
- tokenUrl: input.tokenUrl,
234
- scopes: input.scopes,
235
- },
236
- },
237
- };
238
- else if (input.flow === "password")
239
- return {
240
- type: "oauth2",
241
- description: input.description,
242
- flows: {
243
- password: {
244
- tokenUrl: input.tokenUrl,
245
- scopes: input.scopes,
246
- },
247
- },
248
- };
249
- else if (input.flow === "application")
250
- return {
251
- type: "oauth2",
252
- description: input.description,
253
- flows: {
254
- clientCredentials: {
255
- tokenUrl: input.tokenUrl,
256
- scopes: input.scopes,
257
- },
258
- },
259
- };
260
- else return undefined!;
261
- return undefined!;
262
- };
263
-
264
- export const convertSchema =
265
- (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
266
- (input: SwaggerV2.IJsonSchema): OpenApi.IJsonSchema => {
267
- const nullable: { value: boolean; default?: null } = {
268
- value: false,
269
- default: undefined,
270
- };
271
- const union: OpenApi.IJsonSchema[] = [];
272
- const attribute: IJsonSchemaAttribute = {
273
- title: input.title,
274
- description: input.description,
275
- deprecated: input.deprecated,
276
- readOnly: input.readOnly,
277
- example: input.example,
278
- examples: input.examples
279
- ? Object.fromEntries(input.examples.map((v, i) => [`v${i}`, v]))
280
- : undefined,
281
- ...Object.fromEntries(
282
- Object.entries(input).filter(
283
- ([key, value]) => key.startsWith("x-") && value !== undefined,
284
- ),
285
- ),
286
- };
287
- const visit = (schema: SwaggerV2.IJsonSchema): void => {
288
- // NULLABLE PROPERTY
289
- if (
290
- (schema as SwaggerV2.IJsonSchema.__ISignificant<any>)[
291
- "x-nullable"
292
- ] === true
293
- ) {
294
- nullable.value ||= true;
295
- if ((schema as SwaggerV2.IJsonSchema.INumber).default === null)
296
- nullable.default = null;
297
- }
298
- if (
299
- Array.isArray((schema as SwaggerV2.IJsonSchema.INumber).enum) &&
300
- (schema as SwaggerV2.IJsonSchema.INumber).enum?.length &&
301
- (schema as SwaggerV2.IJsonSchema.INumber).enum?.some(
302
- (e) => e === null,
303
- )
304
- )
305
- nullable.value ||= true;
306
- // UNION TYPE CASE
307
- if (SwaggerV2TypeChecker.isAnyOf(schema))
308
- schema["x-anyOf"].forEach(visit);
309
- else if (SwaggerV2TypeChecker.isOneOf(schema))
310
- schema["x-oneOf"].forEach(visit);
311
- else if (SwaggerV2TypeChecker.isAllOf(schema))
312
- if (schema.allOf.length === 1) visit(schema.allOf[0]!);
313
- else union.push(convertAllOfSchema(definitions)(schema));
314
- // ATOMIC TYPE CASE (CONSIDER ENUM VALUES)
315
- else if (
316
- SwaggerV2TypeChecker.isBoolean(schema) ||
317
- SwaggerV2TypeChecker.isInteger(schema) ||
318
- SwaggerV2TypeChecker.isNumber(schema) ||
319
- SwaggerV2TypeChecker.isString(schema)
320
- )
321
- if (
322
- schema.enum?.length &&
323
- schema.enum.filter((e) => e !== null).length
324
- )
325
- union.push(
326
- ...schema.enum
327
- .filter((v) => v !== null)
328
- .map((value) => ({ const: value })),
329
- );
330
- else if (
331
- SwaggerV2TypeChecker.isInteger(schema) ||
332
- SwaggerV2TypeChecker.isNumber(schema)
333
- )
334
- union.push(
335
- OpenApiExclusiveEmender.emend({
336
- ...schema,
337
- default: (schema.default ?? undefined) satisfies
338
- | boolean
339
- | number
340
- | string
341
- | undefined as any,
342
- examples: schema.examples
343
- ? Object.fromEntries(
344
- schema.examples.map((v, i) => [`v${i}`, v]),
345
- )
346
- : undefined,
347
- exclusiveMinimum:
348
- typeof schema.exclusiveMinimum === "boolean"
349
- ? schema.exclusiveMinimum === true
350
- ? schema.minimum
351
- : undefined
352
- : schema.exclusiveMinimum,
353
- exclusiveMaximum:
354
- typeof schema.exclusiveMaximum === "boolean"
355
- ? schema.exclusiveMaximum === true
356
- ? schema.maximum
357
- : undefined
358
- : schema.exclusiveMaximum,
359
- minimum:
360
- schema.exclusiveMinimum === true ? undefined : schema.minimum,
361
- maximum:
362
- schema.exclusiveMaximum === true ? undefined : schema.maximum,
363
- ...{ enum: undefined },
364
- }),
365
- );
366
- else
367
- union.push({
368
- ...schema,
369
- default: (schema.default ?? undefined) satisfies
370
- | boolean
371
- | number
372
- | string
373
- | undefined as any,
374
- examples: schema.examples
375
- ? Object.fromEntries(
376
- schema.examples.map((v, i) => [`v${i}`, v]),
377
- )
378
- : undefined,
379
- ...{ enum: undefined },
380
- });
381
- // INSTANCE TYPE CASE
382
- else if (SwaggerV2TypeChecker.isArray(schema))
383
- union.push({
384
- ...schema,
385
- items: convertSchema(definitions)(schema.items),
386
- examples: schema.examples
387
- ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
388
- : undefined,
389
- });
390
- else if (SwaggerV2TypeChecker.isObject(schema))
391
- union.push({
392
- ...schema,
393
- ...{
394
- properties: schema.properties
395
- ? Object.fromEntries(
396
- Object.entries(schema.properties)
397
- .filter(([_, v]) => v !== undefined)
398
- .map(([key, value]) => [
399
- key,
400
- convertSchema(definitions)(value),
401
- ]),
402
- )
403
- : {},
404
- additionalProperties: schema.additionalProperties
405
- ? typeof schema.additionalProperties === "object" &&
406
- schema.additionalProperties !== null
407
- ? convertSchema(definitions)(schema.additionalProperties)
408
- : schema.additionalProperties
409
- : undefined,
410
- },
411
- examples: schema.examples
412
- ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
413
- : undefined,
414
- required: schema.required ?? [],
415
- });
416
- else if (SwaggerV2TypeChecker.isReference(schema))
417
- union.push({
418
- ...schema,
419
- $ref: schema.$ref.replace(
420
- "#/definitions/",
421
- "#/components/schemas/",
422
- ),
423
- examples: schema.examples
424
- ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
425
- : undefined,
426
- });
427
- else
428
- union.push({
429
- ...schema,
430
- examples: schema.examples
431
- ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
432
- : undefined,
433
- });
434
- };
435
-
436
- visit(input);
437
- if (
438
- nullable.value === true &&
439
- !union.some((e) => (e as OpenApi.IJsonSchema.INull).type === "null")
440
- )
441
- union.push({
442
- type: "null",
443
- default: nullable.default,
444
- });
445
- if (
446
- union.length === 2 &&
447
- union.filter((x) => OpenApiTypeChecker.isNull(x)).length === 1
448
- ) {
449
- const type: OpenApi.IJsonSchema = union.filter(
450
- (x) => OpenApiTypeChecker.isNull(x) === false,
451
- )[0]!;
452
- for (const key of [
453
- "title",
454
- "description",
455
- "deprecated",
456
- "example",
457
- "examples",
458
- ] as const)
459
- if (type[key] !== undefined) delete type[key];
460
- }
461
- return {
462
- ...(union.length === 0
463
- ? { type: undefined }
464
- : union.length === 1
465
- ? { ...union[0] }
466
- : { oneOf: union.map((u) => ({ ...u, "x-nullable": undefined })) }),
467
- ...attribute,
468
- ...{ "x-nullable": undefined },
469
- };
470
- };
471
-
472
- const convertAllOfSchema =
473
- (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
474
- (input: SwaggerV2.IJsonSchema.IAllOf): OpenApi.IJsonSchema => {
475
- const objects: Array<SwaggerV2.IJsonSchema.IObject | null> =
476
- input.allOf.map((schema) => retrieveObject(definitions)(schema));
477
- if (objects.some((obj) => obj === null))
478
- return {
479
- type: undefined,
480
- ...{
481
- allOf: undefined,
482
- },
483
- };
484
- return {
485
- ...input,
486
- type: "object",
487
- properties: Object.fromEntries(
488
- objects
489
- .map((o) => Object.entries(o?.properties ?? {}))
490
- .flat()
491
- .map(
492
- ([key, value]) =>
493
- [key, convertSchema(definitions)(value)] as const,
494
- ),
495
- ),
496
- ...{
497
- allOf: undefined,
498
- required: [...new Set(objects.map((o) => o?.required ?? []).flat())],
499
- },
500
- };
501
- };
502
-
503
- const retrieveObject =
504
- (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
505
- (
506
- input: SwaggerV2.IJsonSchema,
507
- visited: Set<SwaggerV2.IJsonSchema> = new Set(),
508
- ): SwaggerV2.IJsonSchema.IObject | null => {
509
- if (SwaggerV2TypeChecker.isObject(input))
510
- return input.properties !== undefined && !input.additionalProperties
511
- ? input
512
- : null;
513
- else if (visited.has(input)) return null;
514
- else visited.add(input);
515
-
516
- if (SwaggerV2TypeChecker.isReference(input))
517
- return retrieveObject(definitions)(
518
- definitions?.[input.$ref.split("/").pop() ?? ""] ?? {},
519
- visited,
520
- );
521
- return null;
522
- };
523
- }
1
+ import { IJsonSchemaAttribute, OpenApi, SwaggerV2 } from "@typia/interface";
2
+
3
+ import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
+ import { SwaggerV2TypeChecker } from "../../validators/SwaggerV2TypeChecker";
5
+ import { OpenApiExclusiveEmender } from "./OpenApiExclusiveEmender";
6
+
7
+ export namespace SwaggerV2Upgrader {
8
+ export const convert = (input: SwaggerV2.IDocument): OpenApi.IDocument => ({
9
+ openapi: "3.2.0",
10
+ info: input.info,
11
+ components: convertComponents(input),
12
+ paths: input.paths
13
+ ? Object.fromEntries(
14
+ Object.entries(input.paths)
15
+ .filter(([_, v]) => v !== undefined)
16
+ .map(
17
+ ([key, value]) => [key, convertPathItem(input)(value)] as const,
18
+ ),
19
+ )
20
+ : undefined,
21
+ servers: input.host
22
+ ? [
23
+ {
24
+ url: input.host,
25
+ },
26
+ ]
27
+ : undefined,
28
+ security: input.security,
29
+ tags: input.tags,
30
+ "x-typia-emended-v12": true,
31
+ });
32
+
33
+ /* -----------------------------------------------------------
34
+ OPERATORS
35
+ ----------------------------------------------------------- */
36
+ const convertPathItem =
37
+ (doc: SwaggerV2.IDocument) =>
38
+ (pathItem: SwaggerV2.IPath): OpenApi.IPath => {
39
+ // Convert x-additionalOperations to additionalOperations
40
+ // Promote "query" to standard method (it's a v3.2 standard method)
41
+ const xAdditional = pathItem["x-additionalOperations"];
42
+ const queryOp = xAdditional?.["query"];
43
+ const additionalOperations = xAdditional
44
+ ? Object.fromEntries(
45
+ Object.entries(xAdditional)
46
+ .filter(([key, v]) => key !== "query" && v !== undefined)
47
+ .map(([key, value]) => [
48
+ key,
49
+ convertOperation(doc)(pathItem)(value),
50
+ ]),
51
+ )
52
+ : undefined;
53
+
54
+ return {
55
+ ...(pathItem as any),
56
+ ...(pathItem.get
57
+ ? { get: convertOperation(doc)(pathItem)(pathItem.get) }
58
+ : undefined),
59
+ ...(pathItem.put
60
+ ? { put: convertOperation(doc)(pathItem)(pathItem.put) }
61
+ : undefined),
62
+ ...(pathItem.post
63
+ ? { post: convertOperation(doc)(pathItem)(pathItem.post) }
64
+ : undefined),
65
+ ...(pathItem.delete
66
+ ? { delete: convertOperation(doc)(pathItem)(pathItem.delete) }
67
+ : undefined),
68
+ ...(pathItem.options
69
+ ? { options: convertOperation(doc)(pathItem)(pathItem.options) }
70
+ : undefined),
71
+ ...(pathItem.head
72
+ ? { head: convertOperation(doc)(pathItem)(pathItem.head) }
73
+ : undefined),
74
+ ...(pathItem.patch
75
+ ? { patch: convertOperation(doc)(pathItem)(pathItem.patch) }
76
+ : undefined),
77
+ ...(pathItem.trace
78
+ ? { trace: convertOperation(doc)(pathItem)(pathItem.trace) }
79
+ : undefined),
80
+ ...(queryOp
81
+ ? { query: convertOperation(doc)(pathItem)(queryOp) }
82
+ : undefined),
83
+ ...(additionalOperations && Object.keys(additionalOperations).length > 0
84
+ ? { additionalOperations }
85
+ : undefined),
86
+ "x-additionalOperations": undefined,
87
+ };
88
+ };
89
+
90
+ const convertOperation =
91
+ (doc: SwaggerV2.IDocument) =>
92
+ (pathItem: SwaggerV2.IPath) =>
93
+ (input: SwaggerV2.IOperation): OpenApi.IOperation => ({
94
+ ...input,
95
+ parameters:
96
+ pathItem.parameters !== undefined || input.parameters !== undefined
97
+ ? (
98
+ [...(pathItem.parameters ?? []), ...(input.parameters ?? [])]
99
+ .map((p) =>
100
+ SwaggerV2TypeChecker.isReference(p)
101
+ ? doc.parameters?.[p.$ref.split("/").pop() ?? ""]!
102
+ : p,
103
+ )
104
+ .filter(
105
+ (p) =>
106
+ p !== undefined &&
107
+ p.in !== "body" &&
108
+ (p as SwaggerV2.IOperation.IBodyParameter).schema ===
109
+ undefined,
110
+ ) as SwaggerV2.IOperation.IGeneralParameter[]
111
+ ).map(convertParameter(doc.definitions ?? {}))
112
+ : undefined,
113
+ requestBody: (() => {
114
+ const found: SwaggerV2.IOperation.IBodyParameter | undefined =
115
+ input.parameters?.find((p) => {
116
+ if (SwaggerV2TypeChecker.isReference(p))
117
+ p = doc.parameters?.[p.$ref.split("/").pop() ?? ""]!;
118
+ return (
119
+ (p as SwaggerV2.IOperation.IBodyParameter)?.schema !== undefined
120
+ );
121
+ }) as SwaggerV2.IOperation.IBodyParameter | undefined;
122
+ return found
123
+ ? convertRequestBody(doc.definitions ?? {})(found)
124
+ : undefined;
125
+ })(),
126
+ responses: input.responses
127
+ ? Object.fromEntries(
128
+ Object.entries(input.responses)
129
+ .filter(([_, v]) => v !== undefined)
130
+ .map(
131
+ ([key, value]) => [key, convertResponse(doc)(value)!] as const,
132
+ )
133
+ .filter(([_, v]) => v !== undefined),
134
+ )
135
+ : undefined,
136
+ });
137
+
138
+ const convertParameter =
139
+ (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
140
+ (
141
+ input: SwaggerV2.IOperation.IGeneralParameter,
142
+ ): OpenApi.IOperation.IParameter => ({
143
+ name: input.name,
144
+ in: input.in as any,
145
+ description: input.description,
146
+ schema: convertSchema(definitions)(input),
147
+ required: true,
148
+ });
149
+ const convertRequestBody =
150
+ (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
151
+ (
152
+ input: SwaggerV2.IOperation.IBodyParameter,
153
+ ): OpenApi.IOperation.IRequestBody => ({
154
+ description: input.description,
155
+ content: {
156
+ "application/json": {
157
+ schema: convertSchema(definitions)(input.schema),
158
+ },
159
+ },
160
+ });
161
+
162
+ const convertResponse =
163
+ (doc: SwaggerV2.IDocument) =>
164
+ (
165
+ input:
166
+ | SwaggerV2.IOperation.IResponse
167
+ | SwaggerV2.IJsonSchema.IReference<`#/definitions/responses/${string}`>,
168
+ ): OpenApi.IOperation.IResponse | undefined => {
169
+ if (SwaggerV2TypeChecker.isReference(input)) {
170
+ const found: SwaggerV2.IOperation.IResponse | undefined =
171
+ doc.responses?.[input.$ref.split("/").pop() ?? ""]!;
172
+ if (found === undefined) return undefined;
173
+ input = found;
174
+ }
175
+ return {
176
+ description: input.description,
177
+ content: input.schema
178
+ ? {
179
+ "application/json": {
180
+ schema: convertSchema(doc.definitions ?? {})(input.schema),
181
+ example: input.example,
182
+ },
183
+ }
184
+ : undefined,
185
+ headers: input.headers
186
+ ? Object.fromEntries(
187
+ Object.entries(input.headers)
188
+ .filter(([_, v]) => v !== undefined)
189
+ .map(
190
+ ([key, value]) =>
191
+ [
192
+ key,
193
+ {
194
+ schema: convertSchema(doc.definitions ?? {})(value),
195
+ in: "header",
196
+ },
197
+ ] as const,
198
+ ),
199
+ )
200
+ : undefined,
201
+ };
202
+ };
203
+
204
+ /* -----------------------------------------------------------
205
+ DEFINITIONS
206
+ ----------------------------------------------------------- */
207
+ export const convertComponents = (
208
+ input: SwaggerV2.IDocument,
209
+ ): OpenApi.IComponents => ({
210
+ schemas: Object.fromEntries(
211
+ Object.entries(input.definitions ?? {})
212
+ .filter(([_, v]) => v !== undefined)
213
+ .map(([key, value]) => [
214
+ key,
215
+ convertSchema(input.definitions ?? {})(value),
216
+ ]),
217
+ ),
218
+ securitySchemes: input.securityDefinitions
219
+ ? Object.fromEntries(
220
+ Object.entries(input.securityDefinitions)
221
+ .filter(([_, v]) => v !== undefined)
222
+ .map(([key, value]) => [key, convertSecurityScheme(value)])
223
+ .filter(([_, v]) => v !== undefined),
224
+ )
225
+ : undefined,
226
+ });
227
+
228
+ const convertSecurityScheme = (
229
+ input: SwaggerV2.ISecurityDefinition,
230
+ ): OpenApi.ISecurityScheme => {
231
+ if (input.type === "apiKey") return input;
232
+ else if (input.type === "basic")
233
+ return {
234
+ type: "http",
235
+ scheme: "basic",
236
+ description: input.description,
237
+ };
238
+ else if (input.type === "oauth2")
239
+ if (input.flow === "implicit")
240
+ return {
241
+ type: "oauth2",
242
+ description: input.description,
243
+ flows: {
244
+ implicit: {
245
+ authorizationUrl: input.authorizationUrl,
246
+ scopes: input.scopes,
247
+ },
248
+ },
249
+ };
250
+ else if (input.flow === "accessCode")
251
+ return {
252
+ type: "oauth2",
253
+ description: input.description,
254
+ flows: {
255
+ authorizationCode: {
256
+ authorizationUrl: input.authorizationUrl,
257
+ tokenUrl: input.tokenUrl,
258
+ scopes: input.scopes,
259
+ },
260
+ },
261
+ };
262
+ else if (input.flow === "password")
263
+ return {
264
+ type: "oauth2",
265
+ description: input.description,
266
+ flows: {
267
+ password: {
268
+ tokenUrl: input.tokenUrl,
269
+ scopes: input.scopes,
270
+ },
271
+ },
272
+ };
273
+ else if (input.flow === "application")
274
+ return {
275
+ type: "oauth2",
276
+ description: input.description,
277
+ flows: {
278
+ clientCredentials: {
279
+ tokenUrl: input.tokenUrl,
280
+ scopes: input.scopes,
281
+ },
282
+ },
283
+ };
284
+ else return undefined!;
285
+ return undefined!;
286
+ };
287
+
288
+ export const convertSchema =
289
+ (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
290
+ (input: SwaggerV2.IJsonSchema): OpenApi.IJsonSchema => {
291
+ const nullable: { value: boolean; default?: null } = {
292
+ value: false,
293
+ default: undefined,
294
+ };
295
+ const union: OpenApi.IJsonSchema[] = [];
296
+ const attribute: IJsonSchemaAttribute = {
297
+ title: input.title,
298
+ description: input.description,
299
+ deprecated: input.deprecated,
300
+ readOnly: input.readOnly,
301
+ example: input.example,
302
+ examples: input.examples
303
+ ? Object.fromEntries(input.examples.map((v, i) => [`v${i}`, v]))
304
+ : undefined,
305
+ ...Object.fromEntries(
306
+ Object.entries(input).filter(
307
+ ([key, value]) => key.startsWith("x-") && value !== undefined,
308
+ ),
309
+ ),
310
+ };
311
+ const visit = (schema: SwaggerV2.IJsonSchema): void => {
312
+ // NULLABLE PROPERTY
313
+ if (
314
+ (schema as SwaggerV2.IJsonSchema.__ISignificant<any>)[
315
+ "x-nullable"
316
+ ] === true
317
+ ) {
318
+ nullable.value ||= true;
319
+ if ((schema as SwaggerV2.IJsonSchema.INumber).default === null)
320
+ nullable.default = null;
321
+ }
322
+ if (
323
+ Array.isArray((schema as SwaggerV2.IJsonSchema.INumber).enum) &&
324
+ (schema as SwaggerV2.IJsonSchema.INumber).enum?.length &&
325
+ (schema as SwaggerV2.IJsonSchema.INumber).enum?.some(
326
+ (e) => e === null,
327
+ )
328
+ )
329
+ nullable.value ||= true;
330
+ // UNION TYPE CASE
331
+ if (SwaggerV2TypeChecker.isAnyOf(schema))
332
+ schema["x-anyOf"].forEach(visit);
333
+ else if (SwaggerV2TypeChecker.isOneOf(schema))
334
+ schema["x-oneOf"].forEach(visit);
335
+ else if (SwaggerV2TypeChecker.isAllOf(schema))
336
+ if (schema.allOf.length === 1) visit(schema.allOf[0]!);
337
+ else union.push(convertAllOfSchema(definitions)(schema));
338
+ // ATOMIC TYPE CASE (CONSIDER ENUM VALUES)
339
+ else if (
340
+ SwaggerV2TypeChecker.isBoolean(schema) ||
341
+ SwaggerV2TypeChecker.isInteger(schema) ||
342
+ SwaggerV2TypeChecker.isNumber(schema) ||
343
+ SwaggerV2TypeChecker.isString(schema)
344
+ )
345
+ if (
346
+ schema.enum?.length &&
347
+ schema.enum.filter((e) => e !== null).length
348
+ )
349
+ union.push(
350
+ ...schema.enum
351
+ .filter((v) => v !== null)
352
+ .map((value) => ({ const: value })),
353
+ );
354
+ else if (
355
+ SwaggerV2TypeChecker.isInteger(schema) ||
356
+ SwaggerV2TypeChecker.isNumber(schema)
357
+ )
358
+ union.push(
359
+ OpenApiExclusiveEmender.emend({
360
+ ...schema,
361
+ default: (schema.default ?? undefined) satisfies
362
+ | boolean
363
+ | number
364
+ | string
365
+ | undefined as any,
366
+ examples: schema.examples
367
+ ? Object.fromEntries(
368
+ schema.examples.map((v, i) => [`v${i}`, v]),
369
+ )
370
+ : undefined,
371
+ exclusiveMinimum:
372
+ typeof schema.exclusiveMinimum === "boolean"
373
+ ? schema.exclusiveMinimum === true
374
+ ? schema.minimum
375
+ : undefined
376
+ : schema.exclusiveMinimum,
377
+ exclusiveMaximum:
378
+ typeof schema.exclusiveMaximum === "boolean"
379
+ ? schema.exclusiveMaximum === true
380
+ ? schema.maximum
381
+ : undefined
382
+ : schema.exclusiveMaximum,
383
+ minimum:
384
+ schema.exclusiveMinimum === true ? undefined : schema.minimum,
385
+ maximum:
386
+ schema.exclusiveMaximum === true ? undefined : schema.maximum,
387
+ ...{ enum: undefined },
388
+ }),
389
+ );
390
+ else
391
+ union.push({
392
+ ...schema,
393
+ default: (schema.default ?? undefined) satisfies
394
+ | boolean
395
+ | number
396
+ | string
397
+ | undefined as any,
398
+ examples: schema.examples
399
+ ? Object.fromEntries(
400
+ schema.examples.map((v, i) => [`v${i}`, v]),
401
+ )
402
+ : undefined,
403
+ ...{ enum: undefined },
404
+ });
405
+ // INSTANCE TYPE CASE
406
+ else if (SwaggerV2TypeChecker.isArray(schema))
407
+ union.push({
408
+ ...schema,
409
+ items: convertSchema(definitions)(schema.items),
410
+ examples: schema.examples
411
+ ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
412
+ : undefined,
413
+ });
414
+ else if (SwaggerV2TypeChecker.isObject(schema))
415
+ union.push({
416
+ ...schema,
417
+ ...{
418
+ properties: schema.properties
419
+ ? Object.fromEntries(
420
+ Object.entries(schema.properties)
421
+ .filter(([_, v]) => v !== undefined)
422
+ .map(([key, value]) => [
423
+ key,
424
+ convertSchema(definitions)(value),
425
+ ]),
426
+ )
427
+ : {},
428
+ additionalProperties: schema.additionalProperties
429
+ ? typeof schema.additionalProperties === "object" &&
430
+ schema.additionalProperties !== null
431
+ ? convertSchema(definitions)(schema.additionalProperties)
432
+ : schema.additionalProperties
433
+ : undefined,
434
+ },
435
+ examples: schema.examples
436
+ ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
437
+ : undefined,
438
+ required: schema.required ?? [],
439
+ });
440
+ else if (SwaggerV2TypeChecker.isReference(schema))
441
+ union.push({
442
+ ...schema,
443
+ $ref: schema.$ref.replace(
444
+ "#/definitions/",
445
+ "#/components/schemas/",
446
+ ),
447
+ examples: schema.examples
448
+ ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
449
+ : undefined,
450
+ });
451
+ else
452
+ union.push({
453
+ ...schema,
454
+ examples: schema.examples
455
+ ? Object.fromEntries(schema.examples.map((v, i) => [`v${i}`, v]))
456
+ : undefined,
457
+ });
458
+ };
459
+
460
+ visit(input);
461
+ if (
462
+ nullable.value === true &&
463
+ !union.some((e) => (e as OpenApi.IJsonSchema.INull).type === "null")
464
+ )
465
+ union.push({
466
+ type: "null",
467
+ default: nullable.default,
468
+ });
469
+ if (
470
+ union.length === 2 &&
471
+ union.filter((x) => OpenApiTypeChecker.isNull(x)).length === 1
472
+ ) {
473
+ const type: OpenApi.IJsonSchema = union.filter(
474
+ (x) => OpenApiTypeChecker.isNull(x) === false,
475
+ )[0]!;
476
+ for (const key of [
477
+ "title",
478
+ "description",
479
+ "deprecated",
480
+ "example",
481
+ "examples",
482
+ ] as const)
483
+ if (type[key] !== undefined) delete type[key];
484
+ }
485
+ return {
486
+ ...(union.length === 0
487
+ ? { type: undefined }
488
+ : union.length === 1
489
+ ? { ...union[0] }
490
+ : { oneOf: union.map((u) => ({ ...u, "x-nullable": undefined })) }),
491
+ ...attribute,
492
+ ...{ "x-nullable": undefined },
493
+ };
494
+ };
495
+
496
+ const convertAllOfSchema =
497
+ (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
498
+ (input: SwaggerV2.IJsonSchema.IAllOf): OpenApi.IJsonSchema => {
499
+ const objects: Array<SwaggerV2.IJsonSchema.IObject | null> =
500
+ input.allOf.map((schema) => retrieveObject(definitions)(schema));
501
+ if (objects.some((obj) => obj === null))
502
+ return {
503
+ type: undefined,
504
+ ...{
505
+ allOf: undefined,
506
+ },
507
+ };
508
+ return {
509
+ ...input,
510
+ type: "object",
511
+ properties: Object.fromEntries(
512
+ objects
513
+ .map((o) => Object.entries(o?.properties ?? {}))
514
+ .flat()
515
+ .map(
516
+ ([key, value]) =>
517
+ [key, convertSchema(definitions)(value)] as const,
518
+ ),
519
+ ),
520
+ ...{
521
+ allOf: undefined,
522
+ required: [...new Set(objects.map((o) => o?.required ?? []).flat())],
523
+ },
524
+ };
525
+ };
526
+
527
+ const retrieveObject =
528
+ (definitions: Record<string, SwaggerV2.IJsonSchema>) =>
529
+ (
530
+ input: SwaggerV2.IJsonSchema,
531
+ visited: Set<SwaggerV2.IJsonSchema> = new Set(),
532
+ ): SwaggerV2.IJsonSchema.IObject | null => {
533
+ if (SwaggerV2TypeChecker.isObject(input))
534
+ return input.properties !== undefined && !input.additionalProperties
535
+ ? input
536
+ : null;
537
+ else if (visited.has(input)) return null;
538
+ else visited.add(input);
539
+
540
+ if (SwaggerV2TypeChecker.isReference(input))
541
+ return retrieveObject(definitions)(
542
+ definitions?.[input.$ref.split("/").pop() ?? ""] ?? {},
543
+ visited,
544
+ );
545
+ return null;
546
+ };
547
+ }