@typia/utils 12.0.0-dev.20260309 → 12.0.0-dev.20260310

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 (92) hide show
  1. package/lib/http/internal/HttpLlmApplicationComposer.mjs +5 -1
  2. package/lib/http/internal/HttpLlmApplicationComposer.mjs.map +1 -1
  3. package/lib/index.mjs +9 -9
  4. package/lib/utils/LlmJson.mjs +9 -2
  5. package/lib/utils/LlmJson.mjs.map +1 -1
  6. package/lib/utils/internal/stringifyValidationFailure.js +17 -15
  7. package/lib/utils/internal/stringifyValidationFailure.js.map +1 -1
  8. package/lib/utils/internal/stringifyValidationFailure.mjs +17 -15
  9. package/lib/utils/internal/stringifyValidationFailure.mjs.map +1 -1
  10. package/lib/validators/internal/OpenApiOneOfValidator.mjs +5 -1
  11. package/lib/validators/internal/OpenApiOneOfValidator.mjs.map +1 -1
  12. package/package.json +2 -2
  13. package/src/converters/LlmSchemaConverter.ts +647 -647
  14. package/src/converters/OpenApiConverter.ts +285 -285
  15. package/src/converters/index.ts +5 -5
  16. package/src/converters/internal/LlmDescriptionInverter.ts +178 -178
  17. package/src/converters/internal/LlmParametersComposer.ts +52 -52
  18. package/src/converters/internal/OpenApiConstraintShifter.ts +154 -154
  19. package/src/converters/internal/OpenApiExclusiveEmender.ts +46 -46
  20. package/src/converters/internal/OpenApiV3Downgrader.ts +355 -355
  21. package/src/converters/internal/OpenApiV3Upgrader.ts +470 -470
  22. package/src/converters/internal/OpenApiV3_1Upgrader.ts +685 -685
  23. package/src/converters/internal/SwaggerV2Downgrader.ts +424 -424
  24. package/src/converters/internal/SwaggerV2Upgrader.ts +523 -523
  25. package/src/http/HttpError.ts +107 -107
  26. package/src/http/HttpLlm.ts +167 -167
  27. package/src/http/HttpMigration.ts +92 -92
  28. package/src/http/index.ts +3 -3
  29. package/src/http/internal/HttpLlmApplicationComposer.ts +361 -361
  30. package/src/http/internal/HttpLlmFunctionFetcher.ts +37 -37
  31. package/src/http/internal/HttpMigrateApplicationComposer.ts +56 -56
  32. package/src/http/internal/HttpMigrateRouteAccessor.ts +135 -135
  33. package/src/http/internal/HttpMigrateRouteComposer.ts +505 -505
  34. package/src/http/internal/HttpMigrateRouteFetcher.ts +203 -203
  35. package/src/index.ts +4 -4
  36. package/src/utils/ArrayUtil.ts +42 -42
  37. package/src/utils/LlmJson.ts +141 -141
  38. package/src/utils/MapUtil.ts +15 -15
  39. package/src/utils/NamingConvention.ts +205 -205
  40. package/src/utils/Singleton.ts +17 -17
  41. package/src/utils/StringUtil.ts +14 -14
  42. package/src/utils/dedent.ts +57 -57
  43. package/src/utils/index.ts +8 -8
  44. package/src/utils/internal/EndpointUtil.ts +44 -44
  45. package/src/utils/internal/JsonDescriptor.ts +70 -70
  46. package/src/utils/internal/OpenApiTypeCheckerBase.ts +822 -822
  47. package/src/utils/internal/coerceLlmArguments.ts +314 -314
  48. package/src/utils/internal/parseLenientJson.ts +894 -894
  49. package/src/utils/internal/stringifyValidationFailure.ts +415 -411
  50. package/src/validators/LlmTypeChecker.ts +402 -402
  51. package/src/validators/OpenApiTypeChecker.ts +297 -297
  52. package/src/validators/OpenApiV3TypeChecker.ts +70 -70
  53. package/src/validators/OpenApiV3_1TypeChecker.ts +86 -86
  54. package/src/validators/OpenApiValidator.ts +94 -94
  55. package/src/validators/SwaggerV2TypeChecker.ts +71 -71
  56. package/src/validators/functional/_isBigintString.ts +8 -8
  57. package/src/validators/functional/_isFormatByte.ts +7 -7
  58. package/src/validators/functional/_isFormatDate.ts +3 -3
  59. package/src/validators/functional/_isFormatDateTime.ts +4 -4
  60. package/src/validators/functional/_isFormatDuration.ts +4 -4
  61. package/src/validators/functional/_isFormatEmail.ts +4 -4
  62. package/src/validators/functional/_isFormatHostname.ts +4 -4
  63. package/src/validators/functional/_isFormatIdnEmail.ts +4 -4
  64. package/src/validators/functional/_isFormatIdnHostname.ts +4 -4
  65. package/src/validators/functional/_isFormatIpv4.ts +4 -4
  66. package/src/validators/functional/_isFormatIpv6.ts +4 -4
  67. package/src/validators/functional/_isFormatIri.ts +3 -3
  68. package/src/validators/functional/_isFormatIriReference.ts +4 -4
  69. package/src/validators/functional/_isFormatJsonPointer.ts +3 -3
  70. package/src/validators/functional/_isFormatPassword.ts +1 -1
  71. package/src/validators/functional/_isFormatRegex.ts +8 -8
  72. package/src/validators/functional/_isFormatRelativeJsonPointer.ts +4 -4
  73. package/src/validators/functional/_isFormatTime.ts +4 -4
  74. package/src/validators/functional/_isFormatUri.ts +6 -6
  75. package/src/validators/functional/_isFormatUriReference.ts +5 -5
  76. package/src/validators/functional/_isFormatUriTemplate.ts +4 -4
  77. package/src/validators/functional/_isFormatUrl.ts +4 -4
  78. package/src/validators/functional/_isFormatUuid.ts +3 -3
  79. package/src/validators/functional/_isUniqueItems.ts +159 -159
  80. package/src/validators/index.ts +14 -14
  81. package/src/validators/internal/IOpenApiValidatorContext.ts +17 -17
  82. package/src/validators/internal/OpenApiArrayValidator.ts +49 -49
  83. package/src/validators/internal/OpenApiBooleanValidator.ts +11 -11
  84. package/src/validators/internal/OpenApiConstantValidator.ts +11 -11
  85. package/src/validators/internal/OpenApiIntegerValidator.ts +49 -49
  86. package/src/validators/internal/OpenApiNumberValidator.ts +48 -48
  87. package/src/validators/internal/OpenApiObjectValidator.ts +83 -83
  88. package/src/validators/internal/OpenApiOneOfValidator.ts +309 -309
  89. package/src/validators/internal/OpenApiSchemaNamingRule.ts +124 -124
  90. package/src/validators/internal/OpenApiStationValidator.ts +115 -115
  91. package/src/validators/internal/OpenApiStringValidator.ts +88 -88
  92. package/src/validators/internal/OpenApiTupleValidator.ts +55 -55
@@ -1,424 +1,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
- ...(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
+ ...(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
+ }