@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,424 +1,450 @@
1
- import { OpenApi, SwaggerV2 } from "@typia/interface";
2
-
3
- import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
-
5
- export namespace SwaggerV2Downgrader {
6
- export interface IComponentsCollection {
7
- original: OpenApi.IComponents;
8
- downgraded: Record<string, SwaggerV2.IJsonSchema>;
9
- }
10
-
11
- export const downgrade = (input: OpenApi.IDocument): SwaggerV2.IDocument => {
12
- const collection: IComponentsCollection = downgradeComponents(
13
- input.components,
14
- );
15
- return {
16
- swagger: "2.0",
17
- info: input.info,
18
- host: input.servers?.[0]?.url
19
- ? input.servers[0].url.split("://").pop()!
20
- : "",
21
- definitions: collection.downgraded,
22
- securityDefinitions: input.components?.securitySchemes
23
- ? Object.fromEntries(
24
- Object.entries(input.components.securitySchemes)
25
- .filter(([_, v]) => v !== undefined)
26
- .map(([key, value]) =>
27
- downgradeSecurityScheme(value).map((v) => [key, v]),
28
- )
29
- .flat(),
30
- )
31
- : undefined,
32
- paths: input.paths
33
- ? Object.fromEntries(
34
- Object.entries(input.paths)
35
- .filter(([_, v]) => v !== undefined)
36
- .map(
37
- ([key, value]) =>
38
- [key, downgradePathItem(collection)(value)] as const,
39
- ),
40
- )
41
- : undefined,
42
- security: input.security,
43
- tags: input.tags,
44
- };
45
- };
46
-
47
- /* -----------------------------------------------------------
48
- OPERATORS
49
- ----------------------------------------------------------- */
50
- const downgradePathItem =
51
- (collection: IComponentsCollection) =>
52
- (pathItem: OpenApi.IPath): SwaggerV2.IPath => ({
53
- ...(pathItem as any),
54
- ...(pathItem.get
55
- ? { get: downgradeOperation(collection)(pathItem.get) }
56
- : undefined),
57
- ...(pathItem.put
58
- ? { put: downgradeOperation(collection)(pathItem.put) }
59
- : undefined),
60
- ...(pathItem.post
61
- ? { post: downgradeOperation(collection)(pathItem.post) }
62
- : undefined),
63
- ...(pathItem.delete
64
- ? { delete: downgradeOperation(collection)(pathItem.delete) }
65
- : undefined),
66
- ...(pathItem.options
67
- ? { options: downgradeOperation(collection)(pathItem.options) }
68
- : undefined),
69
- ...(pathItem.head
70
- ? { head: downgradeOperation(collection)(pathItem.head) }
71
- : undefined),
72
- ...(pathItem.patch
73
- ? { patch: downgradeOperation(collection)(pathItem.patch) }
74
- : undefined),
75
- ...(pathItem.trace
76
- ? { trace: downgradeOperation(collection)(pathItem.trace) }
77
- : undefined),
78
- });
79
-
80
- const downgradeOperation =
81
- (collection: IComponentsCollection) =>
82
- (input: OpenApi.IOperation): SwaggerV2.IOperation => ({
83
- ...input,
84
- parameters:
85
- input.parameters !== undefined || input.requestBody !== undefined
86
- ? [
87
- ...(input.parameters ?? []).map(downgradeParameter(collection)),
88
- ...(input.requestBody
89
- ? [downgradeRequestBody(collection)(input.requestBody)]
90
- : []),
91
- ]
92
- : undefined,
93
- responses: input.responses
94
- ? Object.fromEntries(
95
- Object.entries(input.responses)
96
- .filter(([_, v]) => v !== undefined)
97
- .map(([key, value]) => [
98
- key,
99
- downgradeResponse(collection)(value),
100
- ]),
101
- )
102
- : undefined,
103
- ...{
104
- requestBody: undefined,
105
- servers: undefined,
106
- },
107
- });
108
-
109
- const downgradeParameter =
110
- (collection: IComponentsCollection) =>
111
- (
112
- input: OpenApi.IOperation.IParameter,
113
- i: number,
114
- ): SwaggerV2.IOperation.IParameter =>
115
- ({
116
- ...downgradeSchema(collection)(input.schema),
117
- ...input,
118
- required: (input.schema as any)?.required,
119
- schema: undefined,
120
- name: input.name ?? `p${i}`,
121
- ...{
122
- example: undefined,
123
- examples: undefined,
124
- },
125
- }) as any;
126
-
127
- const downgradeRequestBody =
128
- (collection: IComponentsCollection) =>
129
- (
130
- input: OpenApi.IOperation.IRequestBody,
131
- ): SwaggerV2.IOperation.IParameter => ({
132
- name: "body",
133
- in: "body",
134
- description: input.description,
135
- required: input.required,
136
- schema: downgradeSchema(collection)(
137
- Object.values(input.content ?? {})[0]?.schema ?? {},
138
- ),
139
- });
140
-
141
- const downgradeResponse =
142
- (collection: IComponentsCollection) =>
143
- (input: OpenApi.IOperation.IResponse): SwaggerV2.IOperation.IResponse => ({
144
- description: input.description,
145
- schema: downgradeSchema(collection)(
146
- Object.values(input.content ?? {})[0]?.schema ?? {},
147
- ),
148
- headers: input.headers
149
- ? Object.fromEntries(
150
- Object.entries(input.headers)
151
- .filter(([_, v]) => v !== undefined)
152
- .map(([key, value]) => [
153
- key,
154
- {
155
- ...value,
156
- schema: downgradeSchema(collection)(value.schema),
157
- ...{
158
- example: undefined,
159
- examples: undefined,
160
- },
161
- },
162
- ]),
163
- )
164
- : undefined,
165
- });
166
-
167
- /* -----------------------------------------------------------
168
- DEFINITIONS
169
- ----------------------------------------------------------- */
170
- export const downgradeComponents = (
171
- input: OpenApi.IComponents,
172
- ): IComponentsCollection => {
173
- const collection: IComponentsCollection = {
174
- original: input,
175
- downgraded: {},
176
- };
177
- if (input.schemas) {
178
- collection.downgraded.schemas = {};
179
- for (const [key, value] of Object.entries(input.schemas))
180
- if (value !== undefined)
181
- collection.downgraded[key.split("/").pop()!] =
182
- downgradeSchema(collection)(value);
183
- }
184
- return collection;
185
- };
186
-
187
- export const downgradeSchema =
188
- (collection: IComponentsCollection) =>
189
- (input: OpenApi.IJsonSchema): SwaggerV2.IJsonSchema => {
190
- const nullable: boolean = isNullable(new Set())(collection.original)(
191
- input,
192
- );
193
- const union: SwaggerV2.IJsonSchema[] = [];
194
- const attribute: SwaggerV2.IJsonSchema.__IAttribute = {
195
- title: input.title,
196
- description: input.description,
197
- deprecated: input.deprecated,
198
- readOnly: input.readOnly,
199
- example: input.example,
200
- examples: input.examples ? Object.values(input.examples) : undefined,
201
- ...Object.fromEntries(
202
- Object.entries(input).filter(
203
- ([key, value]) => key.startsWith("x-") && value !== undefined,
204
- ),
205
- ),
206
- };
207
- const visit = (schema: OpenApi.IJsonSchema): void => {
208
- if (OpenApiTypeChecker.isBoolean(schema))
209
- union.push({ type: "boolean" });
210
- else if (
211
- OpenApiTypeChecker.isBoolean(schema) ||
212
- OpenApiTypeChecker.isInteger(schema) ||
213
- OpenApiTypeChecker.isNumber(schema) ||
214
- OpenApiTypeChecker.isString(schema)
215
- )
216
- union.push({
217
- ...schema,
218
- examples: schema.examples
219
- ? Object.values(schema.examples)
220
- : undefined,
221
- });
222
- else if (OpenApiTypeChecker.isReference(schema))
223
- union.push({ $ref: `#/definitions/${schema.$ref.split("/").pop()}` });
224
- else if (OpenApiTypeChecker.isArray(schema))
225
- union.push({
226
- ...schema,
227
- items: downgradeSchema(collection)(schema.items),
228
- examples: schema.examples
229
- ? Object.values(schema.examples)
230
- : undefined,
231
- });
232
- else if (OpenApiTypeChecker.isTuple(schema))
233
- union.push({
234
- ...schema,
235
- items: ((): SwaggerV2.IJsonSchema => {
236
- if (schema.additionalItems === true) return {};
237
- const elements = [
238
- ...schema.prefixItems,
239
- ...(typeof schema.additionalItems === "object"
240
- ? [downgradeSchema(collection)(schema.additionalItems)]
241
- : []),
242
- ];
243
- if (elements.length === 0) return {};
244
- return {
245
- "x-oneOf": elements.map(downgradeSchema(collection) as any),
246
- };
247
- })(),
248
- minItems: schema.prefixItems.length,
249
- maxItems:
250
- !!schema.additionalItems === true
251
- ? undefined
252
- : schema.prefixItems.length,
253
- ...{
254
- prefixItems: undefined,
255
- additionalItems: undefined,
256
- },
257
- examples: schema.examples
258
- ? Object.values(schema.examples)
259
- : undefined,
260
- });
261
- else if (OpenApiTypeChecker.isObject(schema))
262
- union.push({
263
- ...schema,
264
- properties: schema.properties
265
- ? Object.fromEntries(
266
- Object.entries(schema.properties)
267
- .filter(([_, v]) => v !== undefined)
268
- .map(([key, value]) => [
269
- key,
270
- downgradeSchema(collection)(value),
271
- ]),
272
- )
273
- : undefined,
274
- additionalProperties:
275
- typeof schema.additionalProperties === "object"
276
- ? downgradeSchema(collection)(schema.additionalProperties)
277
- : schema.additionalProperties,
278
- required: schema.required,
279
- examples: schema.examples
280
- ? Object.values(schema.examples)
281
- : undefined,
282
- });
283
- else if (OpenApiTypeChecker.isOneOf(schema))
284
- schema.oneOf.forEach(visit);
285
- };
286
- const visitConstant = (schema: OpenApi.IJsonSchema): void => {
287
- const insert = (value: any): void => {
288
- const matched: SwaggerV2.IJsonSchema.INumber | undefined = union.find(
289
- (u) =>
290
- (u as SwaggerV2.IJsonSchema.__ISignificant<any>).type === value,
291
- ) as SwaggerV2.IJsonSchema.INumber | undefined;
292
- if (matched !== undefined) {
293
- matched.enum ??= [];
294
- matched.enum.push(value);
295
- } else union.push({ type: typeof value as "number", enum: [value] });
296
- if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const);
297
- else if (OpenApiTypeChecker.isOneOf(schema))
298
- schema.oneOf.forEach(insert);
299
- };
300
- };
301
-
302
- visit(input);
303
- visitConstant(input);
304
- if (nullable) {
305
- for (const u of union)
306
- if (OpenApiTypeChecker.isReference(u as any))
307
- downgradeNullableReference(new Set())(collection)(u as any);
308
- else (u as SwaggerV2.IJsonSchema.IArray)["x-nullable"] = true;
309
- }
310
-
311
- if (nullable === true && union.length === 0)
312
- return { type: "null", ...attribute };
313
- return {
314
- ...(union.length === 0
315
- ? { type: undefined }
316
- : union.length === 1
317
- ? { ...union[0] }
318
- : { "x-oneOf": union }),
319
- ...attribute,
320
- ...(union.length > 1 ? { discriminator: undefined } : {}),
321
- };
322
- };
323
-
324
- const downgradeNullableReference =
325
- (visited: Set<string>) =>
326
- (collection: IComponentsCollection) =>
327
- (schema: SwaggerV2.IJsonSchema.IReference): void => {
328
- const key: string = schema.$ref.split("/").pop()!;
329
- if (key.endsWith(".Nullable")) return;
330
-
331
- const found: OpenApi.IJsonSchema | undefined =
332
- collection.original.schemas?.[key];
333
- if (found === undefined) return;
334
- else if (isNullable(visited)(collection.original)(found) === true) return;
335
- else if (collection.downgraded[`${key}.Nullable`] === undefined) {
336
- collection.downgraded[`${key}.Nullable`] = {};
337
- collection.downgraded[`${key}.Nullable`] = downgradeSchema(collection)(
338
- OpenApiTypeChecker.isOneOf(found)
339
- ? {
340
- ...found,
341
- oneOf: [...found.oneOf, { type: "null" }],
342
- }
343
- : {
344
- title: found.title,
345
- description: found.description,
346
- example: found.example,
347
- examples: found.examples
348
- ? Object.values(found.examples)
349
- : undefined,
350
- ...Object.fromEntries(
351
- Object.entries(found).filter(
352
- ([key, value]) =>
353
- key.startsWith("x-") && value !== undefined,
354
- ),
355
- ),
356
- oneOf: [found, { type: "null" }],
357
- },
358
- );
359
- }
360
- schema.$ref += ".Nullable";
361
- };
362
-
363
- const downgradeSecurityScheme = (
364
- input: OpenApi.ISecurityScheme,
365
- ): SwaggerV2.ISecurityDefinition[] => {
366
- if (input.type === "apiKey") return [input];
367
- else if (input.type === "http")
368
- if (input.scheme === "basic")
369
- return [{ type: "basic", description: input.description }];
370
- else return [];
371
- else if (input.type === "oauth2") {
372
- const output: SwaggerV2.ISecurityDefinition[] = [];
373
- if (input.flows.implicit)
374
- output.push({
375
- type: "oauth2",
376
- flow: "implicit",
377
- authorizationUrl: input.flows.implicit.authorizationUrl,
378
- scopes: input.flows.implicit.scopes,
379
- });
380
- if (input.flows.password)
381
- output.push({
382
- type: "oauth2",
383
- flow: "password",
384
- tokenUrl: input.flows.password.tokenUrl,
385
- scopes: input.flows.password.scopes,
386
- });
387
- if (input.flows.clientCredentials)
388
- output.push({
389
- type: "oauth2",
390
- flow: "application",
391
- tokenUrl: input.flows.clientCredentials.tokenUrl,
392
- scopes: input.flows.clientCredentials.scopes,
393
- });
394
- if (input.flows.authorizationCode)
395
- output.push({
396
- type: "oauth2",
397
- flow: "accessCode",
398
- authorizationUrl: input.flows.authorizationCode.authorizationUrl,
399
- tokenUrl: input.flows.authorizationCode.tokenUrl,
400
- scopes: input.flows.authorizationCode.scopes,
401
- });
402
- return output;
403
- }
404
- return [];
405
- };
406
-
407
- const isNullable =
408
- (visited: Set<string>) =>
409
- (components: OpenApi.IComponents) =>
410
- (schema: OpenApi.IJsonSchema): boolean => {
411
- if (OpenApiTypeChecker.isNull(schema)) return true;
412
- else if (OpenApiTypeChecker.isReference(schema)) {
413
- if (visited.has(schema.$ref)) return false;
414
- visited.add(schema.$ref);
415
- const key: string = schema.$ref.split("/").pop()!;
416
- const next: OpenApi.IJsonSchema | undefined = components.schemas?.[key];
417
- return next ? isNullable(visited)(components)(next) : false;
418
- }
419
- return (
420
- OpenApiTypeChecker.isOneOf(schema) &&
421
- schema.oneOf.some(isNullable(visited)(components))
422
- );
423
- };
424
- }
1
+ import { OpenApi, SwaggerV2 } from "@typia/interface";
2
+
3
+ import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
+
5
+ export namespace SwaggerV2Downgrader {
6
+ export interface IComponentsCollection {
7
+ original: OpenApi.IComponents;
8
+ downgraded: Record<string, SwaggerV2.IJsonSchema>;
9
+ }
10
+
11
+ export const downgrade = (input: OpenApi.IDocument): SwaggerV2.IDocument => {
12
+ const collection: IComponentsCollection = downgradeComponents(
13
+ input.components,
14
+ );
15
+ return {
16
+ swagger: "2.0",
17
+ info: input.info,
18
+ host: input.servers?.[0]?.url
19
+ ? input.servers[0].url.split("://").pop()!
20
+ : "",
21
+ definitions: collection.downgraded,
22
+ securityDefinitions: input.components?.securitySchemes
23
+ ? Object.fromEntries(
24
+ Object.entries(input.components.securitySchemes)
25
+ .filter(([_, v]) => v !== undefined)
26
+ .map(([key, value]) =>
27
+ downgradeSecurityScheme(value).map((v) => [key, v]),
28
+ )
29
+ .flat(),
30
+ )
31
+ : undefined,
32
+ paths: input.paths
33
+ ? Object.fromEntries(
34
+ Object.entries(input.paths)
35
+ .filter(([_, v]) => v !== undefined)
36
+ .map(
37
+ ([key, value]) =>
38
+ [key, downgradePathItem(collection)(value)] as const,
39
+ ),
40
+ )
41
+ : undefined,
42
+ security: input.security,
43
+ tags: input.tags,
44
+ };
45
+ };
46
+
47
+ /* -----------------------------------------------------------
48
+ OPERATORS
49
+ ----------------------------------------------------------- */
50
+ const downgradePathItem =
51
+ (collection: IComponentsCollection) =>
52
+ (pathItem: OpenApi.IPath): SwaggerV2.IPath => {
53
+ // Collect non-standard operations for x-additionalOperations
54
+ const xAdditionalOperations: Record<string, SwaggerV2.IOperation> = {};
55
+
56
+ // query method goes to x-additionalOperations
57
+ if (pathItem.query) {
58
+ xAdditionalOperations["query"] = downgradeOperation(collection)(pathItem.query);
59
+ }
60
+
61
+ // additionalOperations also go to x-additionalOperations
62
+ if (pathItem.additionalOperations) {
63
+ for (const [key, value] of Object.entries(pathItem.additionalOperations)) {
64
+ if (value !== undefined) {
65
+ xAdditionalOperations[key] = downgradeOperation(collection)(value);
66
+ }
67
+ }
68
+ }
69
+
70
+ return {
71
+ ...(pathItem as any),
72
+ ...(pathItem.get
73
+ ? { get: downgradeOperation(collection)(pathItem.get) }
74
+ : undefined),
75
+ ...(pathItem.put
76
+ ? { put: downgradeOperation(collection)(pathItem.put) }
77
+ : undefined),
78
+ ...(pathItem.post
79
+ ? { post: downgradeOperation(collection)(pathItem.post) }
80
+ : undefined),
81
+ ...(pathItem.delete
82
+ ? { delete: downgradeOperation(collection)(pathItem.delete) }
83
+ : undefined),
84
+ ...(pathItem.options
85
+ ? { options: downgradeOperation(collection)(pathItem.options) }
86
+ : undefined),
87
+ ...(pathItem.head
88
+ ? { head: downgradeOperation(collection)(pathItem.head) }
89
+ : undefined),
90
+ ...(pathItem.patch
91
+ ? { patch: downgradeOperation(collection)(pathItem.patch) }
92
+ : undefined),
93
+ ...(pathItem.trace
94
+ ? { trace: downgradeOperation(collection)(pathItem.trace) }
95
+ : undefined),
96
+ ...(Object.keys(xAdditionalOperations).length > 0
97
+ ? { "x-additionalOperations": xAdditionalOperations }
98
+ : undefined),
99
+ // Remove v3.2-only properties from spread
100
+ query: undefined,
101
+ additionalOperations: undefined,
102
+ };
103
+ };
104
+
105
+ const downgradeOperation =
106
+ (collection: IComponentsCollection) =>
107
+ (input: OpenApi.IOperation): SwaggerV2.IOperation => ({
108
+ ...input,
109
+ parameters:
110
+ input.parameters !== undefined || input.requestBody !== undefined
111
+ ? [
112
+ ...(input.parameters ?? []).map(downgradeParameter(collection)),
113
+ ...(input.requestBody
114
+ ? [downgradeRequestBody(collection)(input.requestBody)]
115
+ : []),
116
+ ]
117
+ : undefined,
118
+ responses: input.responses
119
+ ? Object.fromEntries(
120
+ Object.entries(input.responses)
121
+ .filter(([_, v]) => v !== undefined)
122
+ .map(([key, value]) => [
123
+ key,
124
+ downgradeResponse(collection)(value),
125
+ ]),
126
+ )
127
+ : undefined,
128
+ ...{
129
+ requestBody: undefined,
130
+ servers: undefined,
131
+ },
132
+ });
133
+
134
+ const downgradeParameter =
135
+ (collection: IComponentsCollection) =>
136
+ (
137
+ input: OpenApi.IOperation.IParameter,
138
+ i: number,
139
+ ): SwaggerV2.IOperation.IParameter =>
140
+ ({
141
+ ...downgradeSchema(collection)(input.schema),
142
+ ...input,
143
+ in: input.in === "querystring" ? "query" : input.in,
144
+ required: (input.schema as any)?.required,
145
+ schema: undefined,
146
+ name: input.name ?? `p${i}`,
147
+ ...{
148
+ example: undefined,
149
+ examples: undefined,
150
+ },
151
+ }) as any;
152
+
153
+ const downgradeRequestBody =
154
+ (collection: IComponentsCollection) =>
155
+ (
156
+ input: OpenApi.IOperation.IRequestBody,
157
+ ): SwaggerV2.IOperation.IParameter => ({
158
+ name: "body",
159
+ in: "body",
160
+ description: input.description,
161
+ required: input.required,
162
+ schema: downgradeSchema(collection)(
163
+ Object.values(input.content ?? {})[0]?.schema ?? {},
164
+ ),
165
+ });
166
+
167
+ const downgradeResponse =
168
+ (collection: IComponentsCollection) =>
169
+ (input: OpenApi.IOperation.IResponse): SwaggerV2.IOperation.IResponse => ({
170
+ description: input.description,
171
+ schema: downgradeSchema(collection)(
172
+ Object.values(input.content ?? {})[0]?.schema ?? {},
173
+ ),
174
+ headers: input.headers
175
+ ? Object.fromEntries(
176
+ Object.entries(input.headers)
177
+ .filter(([_, v]) => v !== undefined)
178
+ .map(([key, value]) => [
179
+ key,
180
+ {
181
+ ...value,
182
+ schema: downgradeSchema(collection)(value.schema),
183
+ ...{
184
+ example: undefined,
185
+ examples: undefined,
186
+ },
187
+ },
188
+ ]),
189
+ )
190
+ : undefined,
191
+ });
192
+
193
+ /* -----------------------------------------------------------
194
+ DEFINITIONS
195
+ ----------------------------------------------------------- */
196
+ export const downgradeComponents = (
197
+ input: OpenApi.IComponents,
198
+ ): IComponentsCollection => {
199
+ const collection: IComponentsCollection = {
200
+ original: input,
201
+ downgraded: {},
202
+ };
203
+ if (input.schemas) {
204
+ collection.downgraded.schemas = {};
205
+ for (const [key, value] of Object.entries(input.schemas))
206
+ if (value !== undefined)
207
+ collection.downgraded[key.split("/").pop()!] =
208
+ downgradeSchema(collection)(value);
209
+ }
210
+ return collection;
211
+ };
212
+
213
+ export const downgradeSchema =
214
+ (collection: IComponentsCollection) =>
215
+ (input: OpenApi.IJsonSchema): SwaggerV2.IJsonSchema => {
216
+ const nullable: boolean = isNullable(new Set())(collection.original)(
217
+ input,
218
+ );
219
+ const union: SwaggerV2.IJsonSchema[] = [];
220
+ const attribute: SwaggerV2.IJsonSchema.__IAttribute = {
221
+ title: input.title,
222
+ description: input.description,
223
+ deprecated: input.deprecated,
224
+ readOnly: input.readOnly,
225
+ example: input.example,
226
+ examples: input.examples ? Object.values(input.examples) : undefined,
227
+ ...Object.fromEntries(
228
+ Object.entries(input).filter(
229
+ ([key, value]) => key.startsWith("x-") && value !== undefined,
230
+ ),
231
+ ),
232
+ };
233
+ const visit = (schema: OpenApi.IJsonSchema): void => {
234
+ if (OpenApiTypeChecker.isBoolean(schema))
235
+ union.push({ type: "boolean" });
236
+ else if (
237
+ OpenApiTypeChecker.isBoolean(schema) ||
238
+ OpenApiTypeChecker.isInteger(schema) ||
239
+ OpenApiTypeChecker.isNumber(schema) ||
240
+ OpenApiTypeChecker.isString(schema)
241
+ )
242
+ union.push({
243
+ ...schema,
244
+ examples: schema.examples
245
+ ? Object.values(schema.examples)
246
+ : undefined,
247
+ });
248
+ else if (OpenApiTypeChecker.isReference(schema))
249
+ union.push({ $ref: `#/definitions/${schema.$ref.split("/").pop()}` });
250
+ else if (OpenApiTypeChecker.isArray(schema))
251
+ union.push({
252
+ ...schema,
253
+ items: downgradeSchema(collection)(schema.items),
254
+ examples: schema.examples
255
+ ? Object.values(schema.examples)
256
+ : undefined,
257
+ });
258
+ else if (OpenApiTypeChecker.isTuple(schema))
259
+ union.push({
260
+ ...schema,
261
+ items: ((): SwaggerV2.IJsonSchema => {
262
+ if (schema.additionalItems === true) return {};
263
+ const elements = [
264
+ ...schema.prefixItems,
265
+ ...(typeof schema.additionalItems === "object"
266
+ ? [downgradeSchema(collection)(schema.additionalItems)]
267
+ : []),
268
+ ];
269
+ if (elements.length === 0) return {};
270
+ return {
271
+ "x-oneOf": elements.map(downgradeSchema(collection) as any),
272
+ };
273
+ })(),
274
+ minItems: schema.prefixItems.length,
275
+ maxItems:
276
+ !!schema.additionalItems === true
277
+ ? undefined
278
+ : schema.prefixItems.length,
279
+ ...{
280
+ prefixItems: undefined,
281
+ additionalItems: undefined,
282
+ },
283
+ examples: schema.examples
284
+ ? Object.values(schema.examples)
285
+ : undefined,
286
+ });
287
+ else if (OpenApiTypeChecker.isObject(schema))
288
+ union.push({
289
+ ...schema,
290
+ properties: schema.properties
291
+ ? Object.fromEntries(
292
+ Object.entries(schema.properties)
293
+ .filter(([_, v]) => v !== undefined)
294
+ .map(([key, value]) => [
295
+ key,
296
+ downgradeSchema(collection)(value),
297
+ ]),
298
+ )
299
+ : undefined,
300
+ additionalProperties:
301
+ typeof schema.additionalProperties === "object"
302
+ ? downgradeSchema(collection)(schema.additionalProperties)
303
+ : schema.additionalProperties,
304
+ required: schema.required,
305
+ examples: schema.examples
306
+ ? Object.values(schema.examples)
307
+ : undefined,
308
+ });
309
+ else if (OpenApiTypeChecker.isOneOf(schema))
310
+ schema.oneOf.forEach(visit);
311
+ };
312
+ const visitConstant = (schema: OpenApi.IJsonSchema): void => {
313
+ const insert = (value: any): void => {
314
+ const matched: SwaggerV2.IJsonSchema.INumber | undefined = union.find(
315
+ (u) =>
316
+ (u as SwaggerV2.IJsonSchema.__ISignificant<any>).type === value,
317
+ ) as SwaggerV2.IJsonSchema.INumber | undefined;
318
+ if (matched !== undefined) {
319
+ matched.enum ??= [];
320
+ matched.enum.push(value);
321
+ } else union.push({ type: typeof value as "number", enum: [value] });
322
+ if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const);
323
+ else if (OpenApiTypeChecker.isOneOf(schema))
324
+ schema.oneOf.forEach(insert);
325
+ };
326
+ };
327
+
328
+ visit(input);
329
+ visitConstant(input);
330
+ if (nullable) {
331
+ for (const u of union)
332
+ if (OpenApiTypeChecker.isReference(u as any))
333
+ downgradeNullableReference(new Set())(collection)(u as any);
334
+ else (u as SwaggerV2.IJsonSchema.IArray)["x-nullable"] = true;
335
+ }
336
+
337
+ if (nullable === true && union.length === 0)
338
+ return { type: "null", ...attribute };
339
+ return {
340
+ ...(union.length === 0
341
+ ? { type: undefined }
342
+ : union.length === 1
343
+ ? { ...union[0] }
344
+ : { "x-oneOf": union }),
345
+ ...attribute,
346
+ ...(union.length > 1 ? { discriminator: undefined } : {}),
347
+ };
348
+ };
349
+
350
+ const downgradeNullableReference =
351
+ (visited: Set<string>) =>
352
+ (collection: IComponentsCollection) =>
353
+ (schema: SwaggerV2.IJsonSchema.IReference): void => {
354
+ const key: string = schema.$ref.split("/").pop()!;
355
+ if (key.endsWith(".Nullable")) return;
356
+
357
+ const found: OpenApi.IJsonSchema | undefined =
358
+ collection.original.schemas?.[key];
359
+ if (found === undefined) return;
360
+ else if (isNullable(visited)(collection.original)(found) === true) return;
361
+ else if (collection.downgraded[`${key}.Nullable`] === undefined) {
362
+ collection.downgraded[`${key}.Nullable`] = {};
363
+ collection.downgraded[`${key}.Nullable`] = downgradeSchema(collection)(
364
+ OpenApiTypeChecker.isOneOf(found)
365
+ ? {
366
+ ...found,
367
+ oneOf: [...found.oneOf, { type: "null" }],
368
+ }
369
+ : {
370
+ title: found.title,
371
+ description: found.description,
372
+ example: found.example,
373
+ examples: found.examples
374
+ ? Object.values(found.examples)
375
+ : undefined,
376
+ ...Object.fromEntries(
377
+ Object.entries(found).filter(
378
+ ([key, value]) =>
379
+ key.startsWith("x-") && value !== undefined,
380
+ ),
381
+ ),
382
+ oneOf: [found, { type: "null" }],
383
+ },
384
+ );
385
+ }
386
+ schema.$ref += ".Nullable";
387
+ };
388
+
389
+ const downgradeSecurityScheme = (
390
+ input: OpenApi.ISecurityScheme,
391
+ ): SwaggerV2.ISecurityDefinition[] => {
392
+ if (input.type === "apiKey") return [input];
393
+ else if (input.type === "http")
394
+ if (input.scheme === "basic")
395
+ return [{ type: "basic", description: input.description }];
396
+ else return [];
397
+ else if (input.type === "oauth2") {
398
+ const output: SwaggerV2.ISecurityDefinition[] = [];
399
+ if (input.flows.implicit)
400
+ output.push({
401
+ type: "oauth2",
402
+ flow: "implicit",
403
+ authorizationUrl: input.flows.implicit.authorizationUrl,
404
+ scopes: input.flows.implicit.scopes,
405
+ });
406
+ if (input.flows.password)
407
+ output.push({
408
+ type: "oauth2",
409
+ flow: "password",
410
+ tokenUrl: input.flows.password.tokenUrl,
411
+ scopes: input.flows.password.scopes,
412
+ });
413
+ if (input.flows.clientCredentials)
414
+ output.push({
415
+ type: "oauth2",
416
+ flow: "application",
417
+ tokenUrl: input.flows.clientCredentials.tokenUrl,
418
+ scopes: input.flows.clientCredentials.scopes,
419
+ });
420
+ if (input.flows.authorizationCode)
421
+ output.push({
422
+ type: "oauth2",
423
+ flow: "accessCode",
424
+ authorizationUrl: input.flows.authorizationCode.authorizationUrl,
425
+ tokenUrl: input.flows.authorizationCode.tokenUrl,
426
+ scopes: input.flows.authorizationCode.scopes,
427
+ });
428
+ return output;
429
+ }
430
+ return [];
431
+ };
432
+
433
+ const isNullable =
434
+ (visited: Set<string>) =>
435
+ (components: OpenApi.IComponents) =>
436
+ (schema: OpenApi.IJsonSchema): boolean => {
437
+ if (OpenApiTypeChecker.isNull(schema)) return true;
438
+ else if (OpenApiTypeChecker.isReference(schema)) {
439
+ if (visited.has(schema.$ref)) return false;
440
+ visited.add(schema.$ref);
441
+ const key: string = schema.$ref.split("/").pop()!;
442
+ const next: OpenApi.IJsonSchema | undefined = components.schemas?.[key];
443
+ return next ? isNullable(visited)(components)(next) : false;
444
+ }
445
+ return (
446
+ OpenApiTypeChecker.isOneOf(schema) &&
447
+ schema.oneOf.some(isNullable(visited)(components))
448
+ );
449
+ };
450
+ }