@typia/utils 12.0.0-dev.20260307-2 → 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 (103) hide show
  1. package/README.md +2 -2
  2. package/lib/http/internal/HttpLlmApplicationComposer.js +1 -0
  3. package/lib/http/internal/HttpLlmApplicationComposer.js.map +1 -1
  4. package/lib/http/internal/HttpLlmApplicationComposer.mjs +1 -0
  5. package/lib/http/internal/HttpLlmApplicationComposer.mjs.map +1 -1
  6. package/lib/utils/LlmJson.d.ts +3 -3
  7. package/lib/utils/LlmJson.js +2 -2
  8. package/lib/utils/LlmJson.js.map +1 -1
  9. package/lib/utils/LlmJson.mjs +2 -2
  10. package/lib/utils/LlmJson.mjs.map +1 -1
  11. package/lib/utils/internal/coerceLlmArguments.js +17 -1
  12. package/lib/utils/internal/coerceLlmArguments.js.map +1 -1
  13. package/lib/utils/internal/coerceLlmArguments.mjs +17 -1
  14. package/lib/utils/internal/coerceLlmArguments.mjs.map +1 -1
  15. package/lib/utils/internal/parseLenientJson.js +236 -96
  16. package/lib/utils/internal/parseLenientJson.js.map +1 -1
  17. package/lib/utils/internal/parseLenientJson.mjs +236 -96
  18. package/lib/utils/internal/parseLenientJson.mjs.map +1 -1
  19. package/lib/utils/internal/stringifyValidationFailure.js +17 -15
  20. package/lib/utils/internal/stringifyValidationFailure.js.map +1 -1
  21. package/lib/utils/internal/stringifyValidationFailure.mjs +17 -15
  22. package/lib/utils/internal/stringifyValidationFailure.mjs.map +1 -1
  23. package/package.json +2 -2
  24. package/src/converters/LlmSchemaConverter.ts +647 -647
  25. package/src/converters/OpenApiConverter.ts +285 -285
  26. package/src/converters/index.ts +5 -5
  27. package/src/converters/internal/LlmDescriptionInverter.ts +178 -178
  28. package/src/converters/internal/LlmParametersComposer.ts +52 -52
  29. package/src/converters/internal/OpenApiConstraintShifter.ts +154 -154
  30. package/src/converters/internal/OpenApiExclusiveEmender.ts +46 -46
  31. package/src/converters/internal/OpenApiV3Downgrader.ts +355 -355
  32. package/src/converters/internal/OpenApiV3Upgrader.ts +470 -470
  33. package/src/converters/internal/OpenApiV3_1Upgrader.ts +685 -685
  34. package/src/converters/internal/SwaggerV2Downgrader.ts +424 -424
  35. package/src/converters/internal/SwaggerV2Upgrader.ts +523 -523
  36. package/src/http/HttpError.ts +107 -107
  37. package/src/http/HttpLlm.ts +167 -167
  38. package/src/http/HttpMigration.ts +92 -92
  39. package/src/http/index.ts +3 -3
  40. package/src/http/internal/HttpLlmApplicationComposer.ts +361 -360
  41. package/src/http/internal/HttpLlmFunctionFetcher.ts +37 -37
  42. package/src/http/internal/HttpMigrateApplicationComposer.ts +56 -56
  43. package/src/http/internal/HttpMigrateRouteAccessor.ts +135 -135
  44. package/src/http/internal/HttpMigrateRouteComposer.ts +505 -505
  45. package/src/http/internal/HttpMigrateRouteFetcher.ts +203 -203
  46. package/src/index.ts +4 -4
  47. package/src/utils/ArrayUtil.ts +42 -42
  48. package/src/utils/LlmJson.ts +141 -141
  49. package/src/utils/MapUtil.ts +15 -15
  50. package/src/utils/NamingConvention.ts +205 -205
  51. package/src/utils/Singleton.ts +17 -17
  52. package/src/utils/StringUtil.ts +14 -14
  53. package/src/utils/dedent.ts +57 -57
  54. package/src/utils/index.ts +8 -8
  55. package/src/utils/internal/EndpointUtil.ts +44 -44
  56. package/src/utils/internal/JsonDescriptor.ts +70 -70
  57. package/src/utils/internal/OpenApiTypeCheckerBase.ts +822 -822
  58. package/src/utils/internal/coerceLlmArguments.ts +314 -297
  59. package/src/utils/internal/parseLenientJson.ts +894 -731
  60. package/src/utils/internal/stringifyValidationFailure.ts +415 -411
  61. package/src/validators/LlmTypeChecker.ts +402 -402
  62. package/src/validators/OpenApiTypeChecker.ts +297 -297
  63. package/src/validators/OpenApiV3TypeChecker.ts +70 -70
  64. package/src/validators/OpenApiV3_1TypeChecker.ts +86 -86
  65. package/src/validators/OpenApiValidator.ts +94 -94
  66. package/src/validators/SwaggerV2TypeChecker.ts +71 -71
  67. package/src/validators/functional/_isBigintString.ts +8 -8
  68. package/src/validators/functional/_isFormatByte.ts +7 -7
  69. package/src/validators/functional/_isFormatDate.ts +3 -3
  70. package/src/validators/functional/_isFormatDateTime.ts +4 -4
  71. package/src/validators/functional/_isFormatDuration.ts +4 -4
  72. package/src/validators/functional/_isFormatEmail.ts +4 -4
  73. package/src/validators/functional/_isFormatHostname.ts +4 -4
  74. package/src/validators/functional/_isFormatIdnEmail.ts +4 -4
  75. package/src/validators/functional/_isFormatIdnHostname.ts +4 -4
  76. package/src/validators/functional/_isFormatIpv4.ts +4 -4
  77. package/src/validators/functional/_isFormatIpv6.ts +4 -4
  78. package/src/validators/functional/_isFormatIri.ts +3 -3
  79. package/src/validators/functional/_isFormatIriReference.ts +4 -4
  80. package/src/validators/functional/_isFormatJsonPointer.ts +3 -3
  81. package/src/validators/functional/_isFormatPassword.ts +1 -1
  82. package/src/validators/functional/_isFormatRegex.ts +8 -8
  83. package/src/validators/functional/_isFormatRelativeJsonPointer.ts +4 -4
  84. package/src/validators/functional/_isFormatTime.ts +4 -4
  85. package/src/validators/functional/_isFormatUri.ts +6 -6
  86. package/src/validators/functional/_isFormatUriReference.ts +5 -5
  87. package/src/validators/functional/_isFormatUriTemplate.ts +4 -4
  88. package/src/validators/functional/_isFormatUrl.ts +4 -4
  89. package/src/validators/functional/_isFormatUuid.ts +3 -3
  90. package/src/validators/functional/_isUniqueItems.ts +159 -159
  91. package/src/validators/index.ts +14 -14
  92. package/src/validators/internal/IOpenApiValidatorContext.ts +17 -17
  93. package/src/validators/internal/OpenApiArrayValidator.ts +49 -49
  94. package/src/validators/internal/OpenApiBooleanValidator.ts +11 -11
  95. package/src/validators/internal/OpenApiConstantValidator.ts +11 -11
  96. package/src/validators/internal/OpenApiIntegerValidator.ts +49 -49
  97. package/src/validators/internal/OpenApiNumberValidator.ts +48 -48
  98. package/src/validators/internal/OpenApiObjectValidator.ts +83 -83
  99. package/src/validators/internal/OpenApiOneOfValidator.ts +309 -309
  100. package/src/validators/internal/OpenApiSchemaNamingRule.ts +124 -124
  101. package/src/validators/internal/OpenApiStationValidator.ts +115 -115
  102. package/src/validators/internal/OpenApiStringValidator.ts +88 -88
  103. package/src/validators/internal/OpenApiTupleValidator.ts +55 -55
@@ -1,685 +1,685 @@
1
- import { IJsonSchemaAttribute, OpenApi, OpenApiV3_1 } from "@typia/interface";
2
-
3
- import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
- import { OpenApiV3_1TypeChecker } from "../../validators/OpenApiV3_1TypeChecker";
5
- import { OpenApiExclusiveEmender } from "./OpenApiExclusiveEmender";
6
-
7
- export namespace OpenApiV3_1Upgrader {
8
- export const convert = (input: OpenApiV3_1.IDocument): OpenApi.IDocument => {
9
- if ((input as OpenApi.IDocument)["x-samchon-emended-v4"] === true)
10
- return input as OpenApi.IDocument;
11
- return {
12
- ...input,
13
- components: convertComponents(input.components ?? {}),
14
- paths: input.paths
15
- ? Object.fromEntries(
16
- Object.entries(input.paths)
17
- .filter(([_, v]) => v !== undefined)
18
- .map(
19
- ([key, value]) => [key, convertPathItem(input)(value)] as const,
20
- ),
21
- )
22
- : undefined,
23
- webhooks: input.webhooks
24
- ? Object.fromEntries(
25
- Object.entries(input.webhooks)
26
- .filter(([_, v]) => v !== undefined)
27
- .map(
28
- ([key, value]) =>
29
- [key, convertWebhooks(input)(value)!] as const,
30
- )
31
- .filter(([_, value]) => value !== undefined),
32
- )
33
- : undefined,
34
- "x-samchon-emended-v4": true,
35
- };
36
- };
37
-
38
- /* -----------------------------------------------------------
39
- OPERATORS
40
- ----------------------------------------------------------- */
41
- const convertWebhooks =
42
- (doc: OpenApiV3_1.IDocument) =>
43
- (
44
- webhook:
45
- | OpenApiV3_1.IPath
46
- | OpenApiV3_1.IJsonSchema.IReference<`#/components/pathItems/${string}`>,
47
- ): OpenApi.IPath | undefined => {
48
- if (!OpenApiV3_1TypeChecker.isReference(webhook))
49
- return convertPathItem(doc)(webhook);
50
- const found: OpenApiV3_1.IPath | undefined =
51
- doc.components?.pathItems?.[webhook.$ref.split("/").pop() ?? ""];
52
- return found ? convertPathItem(doc)(found) : undefined;
53
- };
54
-
55
- const convertPathItem =
56
- (doc: OpenApiV3_1.IDocument) =>
57
- (pathItem: OpenApiV3_1.IPath): OpenApi.IPath => ({
58
- ...(pathItem as any),
59
- ...(pathItem.get
60
- ? { get: convertOperation(doc)(pathItem)(pathItem.get) }
61
- : undefined),
62
- ...(pathItem.put
63
- ? { put: convertOperation(doc)(pathItem)(pathItem.put) }
64
- : undefined),
65
- ...(pathItem.post
66
- ? { post: convertOperation(doc)(pathItem)(pathItem.post) }
67
- : undefined),
68
- ...(pathItem.delete
69
- ? { delete: convertOperation(doc)(pathItem)(pathItem.delete) }
70
- : undefined),
71
- ...(pathItem.options
72
- ? { options: convertOperation(doc)(pathItem)(pathItem.options) }
73
- : undefined),
74
- ...(pathItem.head
75
- ? { head: convertOperation(doc)(pathItem)(pathItem.head) }
76
- : undefined),
77
- ...(pathItem.patch
78
- ? { patch: convertOperation(doc)(pathItem)(pathItem.patch) }
79
- : undefined),
80
- ...(pathItem.trace
81
- ? { trace: convertOperation(doc)(pathItem)(pathItem.trace) }
82
- : undefined),
83
- });
84
-
85
- const convertOperation =
86
- (doc: OpenApiV3_1.IDocument) =>
87
- (pathItem: OpenApiV3_1.IPath) =>
88
- (input: OpenApiV3_1.IOperation): OpenApi.IOperation => ({
89
- ...input,
90
- parameters:
91
- pathItem.parameters !== undefined || input.parameters !== undefined
92
- ? [...(pathItem.parameters ?? []), ...(input.parameters ?? [])]
93
- .map((p) => {
94
- if (!OpenApiV3_1TypeChecker.isReference(p))
95
- return convertParameter(doc.components ?? {})(p);
96
- const found:
97
- | Omit<OpenApiV3_1.IOperation.IParameter, "in">
98
- | undefined = p.$ref.startsWith("#/components/headers/")
99
- ? doc.components?.headers?.[p.$ref.split("/").pop() ?? ""]
100
- : doc.components?.parameters?.[p.$ref.split("/").pop() ?? ""];
101
- return found !== undefined
102
- ? convertParameter(doc.components ?? {})({
103
- ...found,
104
- in: "header",
105
- })
106
- : undefined!;
107
- })
108
- .filter((_, v) => v !== undefined)
109
- : undefined,
110
- requestBody: input.requestBody
111
- ? convertRequestBody(doc)(input.requestBody)
112
- : undefined,
113
- responses: input.responses
114
- ? Object.fromEntries(
115
- Object.entries(input.responses)
116
- .filter(([_, v]) => v !== undefined)
117
- .map(
118
- ([key, value]) => [key, convertResponse(doc)(value)!] as const,
119
- )
120
- .filter(([_, value]) => value !== undefined),
121
- )
122
- : undefined,
123
- });
124
-
125
- const convertParameter =
126
- (components: OpenApiV3_1.IComponents) =>
127
- (
128
- input: OpenApiV3_1.IOperation.IParameter,
129
- ): OpenApi.IOperation.IParameter => ({
130
- ...input,
131
- schema: convertSchema(components)(input.schema),
132
- examples: input.examples
133
- ? Object.fromEntries(
134
- Object.entries(input.examples)
135
- .map(([key, value]) => [
136
- key,
137
- OpenApiV3_1TypeChecker.isReference(value)
138
- ? components.examples?.[value.$ref.split("/").pop() ?? ""]
139
- : value,
140
- ])
141
- .filter(([_, v]) => v !== undefined),
142
- )
143
- : undefined,
144
- });
145
-
146
- const convertRequestBody =
147
- (doc: OpenApiV3_1.IDocument) =>
148
- (
149
- input:
150
- | OpenApiV3_1.IOperation.IRequestBody
151
- | OpenApiV3_1.IJsonSchema.IReference<`#/components/requestBodies/${string}`>,
152
- ): OpenApi.IOperation.IRequestBody | undefined => {
153
- if (OpenApiV3_1TypeChecker.isReference(input)) {
154
- const found: OpenApiV3_1.IOperation.IRequestBody | undefined =
155
- doc.components?.requestBodies?.[input.$ref.split("/").pop() ?? ""];
156
- if (found === undefined) return undefined;
157
- input = found;
158
- }
159
- return {
160
- ...input,
161
- content: input.content
162
- ? convertContent(doc.components ?? {})(input.content)
163
- : undefined,
164
- };
165
- };
166
-
167
- const convertResponse =
168
- (doc: OpenApiV3_1.IDocument) =>
169
- (
170
- input:
171
- | OpenApiV3_1.IOperation.IResponse
172
- | OpenApiV3_1.IJsonSchema.IReference<`#/components/responses/${string}`>,
173
- ): OpenApi.IOperation.IResponse | undefined => {
174
- if (OpenApiV3_1TypeChecker.isReference(input)) {
175
- const found: OpenApiV3_1.IOperation.IResponse | undefined =
176
- doc.components?.responses?.[input.$ref.split("/").pop() ?? ""];
177
- if (found === undefined) return undefined;
178
- input = found;
179
- }
180
- return {
181
- ...input,
182
- content: input.content
183
- ? convertContent(doc.components ?? {})(input.content)
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
- if (OpenApiV3_1TypeChecker.isReference(value) === false)
195
- return convertParameter(doc.components ?? {})({
196
- ...value,
197
- in: "header",
198
- });
199
- const found:
200
- | Omit<OpenApiV3_1.IOperation.IParameter, "in">
201
- | undefined = value.$ref.startsWith(
202
- "#/components/headers/",
203
- )
204
- ? doc.components?.headers?.[
205
- value.$ref.split("/").pop() ?? ""
206
- ]
207
- : undefined;
208
- return found !== undefined
209
- ? convertParameter(doc.components ?? {})({
210
- ...found,
211
- in: "header",
212
- })
213
- : undefined!;
214
- })(),
215
- ] as const,
216
- )
217
- .filter(([_, v]) => v !== undefined),
218
- )
219
- : undefined,
220
- };
221
- };
222
-
223
- const convertContent =
224
- (components: OpenApiV3_1.IComponents) =>
225
- (
226
- record: Record<string, OpenApiV3_1.IOperation.IMediaType>,
227
- ): Record<string, OpenApi.IOperation.IMediaType> =>
228
- Object.fromEntries(
229
- Object.entries(record)
230
- .filter(([_, v]) => v !== undefined)
231
- .map(
232
- ([key, value]) =>
233
- [
234
- key,
235
- {
236
- ...value,
237
- schema: value.schema
238
- ? convertSchema(components)(value.schema)
239
- : undefined,
240
- examples: value.examples
241
- ? Object.fromEntries(
242
- Object.entries(value.examples)
243
- .map(([key, value]) => [
244
- key,
245
- OpenApiV3_1TypeChecker.isReference(value)
246
- ? components.examples?.[
247
- value.$ref.split("/").pop() ?? ""
248
- ]
249
- : value,
250
- ])
251
- .filter(([_, v]) => v !== undefined),
252
- )
253
- : undefined,
254
- },
255
- ] as const,
256
- ),
257
- );
258
-
259
- /* -----------------------------------------------------------
260
- DEFINITIONS
261
- ----------------------------------------------------------- */
262
- export const convertComponents = (
263
- input: OpenApiV3_1.IComponents,
264
- ): OpenApi.IComponents => ({
265
- schemas: Object.fromEntries(
266
- Object.entries(input.schemas ?? {})
267
- .filter(([_, v]) => v !== undefined)
268
- .map(([key, value]) => [key, convertSchema(input)(value)] as const),
269
- ),
270
- securitySchemes: input.securitySchemes,
271
- });
272
-
273
- export const convertSchema =
274
- (components: OpenApiV3_1.IComponents) =>
275
- (input: OpenApiV3_1.IJsonSchema): OpenApi.IJsonSchema => {
276
- const union: OpenApi.IJsonSchema[] = [];
277
- const attribute: IJsonSchemaAttribute = {
278
- title: input.title,
279
- description: input.description,
280
- deprecated: input.deprecated,
281
- readOnly: input.readOnly,
282
- writeOnly: input.writeOnly,
283
- example: input.example,
284
- examples: Array.isArray(input.examples)
285
- ? Object.fromEntries(input.examples.map((v, i) => [`v${i}`, v]))
286
- : input.examples,
287
- ...Object.fromEntries(
288
- Object.entries(input).filter(
289
- ([key, value]) => key.startsWith("x-") && value !== undefined,
290
- ),
291
- ),
292
- };
293
- const nullable: { value: boolean; default?: null } = {
294
- value: false,
295
- default: undefined,
296
- };
297
-
298
- const visit = (schema: OpenApiV3_1.IJsonSchema): void => {
299
- // NULLABLE PROPERTY
300
- if ((schema as OpenApiV3_1.IJsonSchema.INumber).nullable === true) {
301
- nullable.value ||= true;
302
- if ((schema as OpenApiV3_1.IJsonSchema.INumber).default === null)
303
- nullable.default = null;
304
- }
305
- if (
306
- Array.isArray((schema as OpenApiV3_1.IJsonSchema.INumber).enum) &&
307
- (schema as OpenApiV3_1.IJsonSchema.INumber).enum?.length &&
308
- (schema as OpenApiV3_1.IJsonSchema.INumber).enum?.some(
309
- (e) => e === null,
310
- )
311
- )
312
- nullable.value ||= true;
313
-
314
- // MIXED TYPE CASE
315
- if (OpenApiV3_1TypeChecker.isMixed(schema)) {
316
- if (schema.const !== undefined)
317
- visit({
318
- ...schema,
319
- ...{
320
- type: undefined,
321
- oneOf: undefined,
322
- anyOf: undefined,
323
- allOf: undefined,
324
- $ref: undefined,
325
- },
326
- });
327
- if (schema.oneOf !== undefined)
328
- visit({
329
- ...schema,
330
- ...{
331
- type: undefined,
332
- anyOf: undefined,
333
- allOf: undefined,
334
- $ref: undefined,
335
- },
336
- });
337
- if (schema.anyOf !== undefined)
338
- visit({
339
- ...schema,
340
- ...{
341
- type: undefined,
342
- oneOf: undefined,
343
- allOf: undefined,
344
- $ref: undefined,
345
- },
346
- });
347
- if (schema.allOf !== undefined)
348
- visit({
349
- ...schema,
350
- ...{
351
- type: undefined,
352
- oneOf: undefined,
353
- anyOf: undefined,
354
- $ref: undefined,
355
- },
356
- });
357
- for (const type of schema.type)
358
- if (type === "boolean" || type === "number" || type === "string")
359
- visit({
360
- ...schema,
361
- ...{
362
- enum:
363
- schema.enum?.length && schema.enum.filter((e) => e !== null)
364
- ? schema.enum.filter((x) => typeof x === type)
365
- : undefined,
366
- },
367
- type: type as any,
368
- });
369
- else if (type === "integer")
370
- visit({
371
- ...schema,
372
- ...{
373
- enum:
374
- schema.enum?.length && schema.enum.filter((e) => e !== null)
375
- ? schema.enum.filter(
376
- (x) =>
377
- x !== null &&
378
- typeof x === "number" &&
379
- Number.isInteger(x),
380
- )
381
- : undefined,
382
- },
383
- type: type as any,
384
- });
385
- else visit({ ...schema, type: type as any });
386
- }
387
- // UNION TYPE CASE
388
- else if (OpenApiV3_1TypeChecker.isOneOf(schema))
389
- schema.oneOf.forEach(visit);
390
- else if (OpenApiV3_1TypeChecker.isAnyOf(schema))
391
- schema.anyOf.forEach(visit);
392
- else if (OpenApiV3_1TypeChecker.isAllOf(schema))
393
- if (schema.allOf.length === 1) visit(schema.allOf[0]!);
394
- else union.push(convertAllOfSchema(components)(schema));
395
- // ATOMIC TYPE CASE (CONSIDER ENUM VALUES)
396
- else if (OpenApiV3_1TypeChecker.isBoolean(schema))
397
- if (
398
- schema.enum?.length &&
399
- schema.enum.filter((e) => e !== null).length
400
- )
401
- for (const value of schema.enum.filter((e) => e !== null))
402
- union.push({
403
- const: value,
404
- ...({
405
- ...schema,
406
- type: undefined as any,
407
- enum: undefined,
408
- default: undefined,
409
- } satisfies OpenApiV3_1.IJsonSchema.IBoolean as any),
410
- } satisfies OpenApi.IJsonSchema.IConstant);
411
- else
412
- union.push({
413
- ...schema,
414
- default: schema.default ?? undefined,
415
- ...{
416
- enum: undefined,
417
- },
418
- });
419
- else if (
420
- OpenApiV3_1TypeChecker.isInteger(schema) ||
421
- OpenApiV3_1TypeChecker.isNumber(schema)
422
- )
423
- if (schema.enum?.length && schema.enum.filter((e) => e !== null))
424
- for (const value of schema.enum.filter((e) => e !== null))
425
- union.push({
426
- const: value,
427
- ...({
428
- ...schema,
429
- type: undefined as any,
430
- enum: undefined,
431
- default: undefined,
432
- minimum: undefined,
433
- maximum: undefined,
434
- exclusiveMinimum: undefined,
435
- exclusiveMaximum: undefined,
436
- multipleOf: undefined,
437
- } satisfies OpenApiV3_1.IJsonSchema.IInteger as any),
438
- } satisfies OpenApi.IJsonSchema.IConstant);
439
- else
440
- union.push(
441
- OpenApiExclusiveEmender.emend({
442
- ...schema,
443
- default: schema.default ?? undefined,
444
- ...{
445
- enum: undefined,
446
- },
447
- exclusiveMinimum:
448
- typeof schema.exclusiveMinimum === "boolean"
449
- ? schema.exclusiveMinimum === true
450
- ? schema.minimum
451
- : undefined
452
- : schema.exclusiveMinimum,
453
- exclusiveMaximum:
454
- typeof schema.exclusiveMaximum === "boolean"
455
- ? schema.exclusiveMaximum === true
456
- ? schema.maximum
457
- : undefined
458
- : schema.exclusiveMaximum,
459
- minimum:
460
- schema.exclusiveMinimum === true ? undefined : schema.minimum,
461
- maximum:
462
- schema.exclusiveMaximum === true ? undefined : schema.maximum,
463
- }),
464
- );
465
- else if (OpenApiV3_1TypeChecker.isString(schema))
466
- if (
467
- schema.enum?.length &&
468
- schema.enum.filter((e) => e !== null).length
469
- )
470
- for (const value of schema.enum.filter((e) => e !== null))
471
- union.push({
472
- const: value,
473
- ...({
474
- ...schema,
475
- type: undefined as any,
476
- enum: undefined,
477
- default: undefined,
478
- } satisfies OpenApiV3_1.IJsonSchema.IString as any),
479
- } satisfies OpenApi.IJsonSchema.IConstant);
480
- else
481
- union.push({
482
- ...schema,
483
- default: schema.default ?? undefined,
484
- ...{
485
- enum: undefined,
486
- },
487
- });
488
- // ARRAY TYPE CASE (CONSIDER TUPLE)
489
- else if (OpenApiV3_1TypeChecker.isArray(schema)) {
490
- if (Array.isArray(schema.items))
491
- union.push({
492
- ...schema,
493
- ...{
494
- items: undefined!,
495
- prefixItems: schema.items.map(convertSchema(components)),
496
- additionalItems:
497
- typeof schema.additionalItems === "object" &&
498
- schema.additionalItems !== null
499
- ? convertSchema(components)(schema.additionalItems)
500
- : schema.additionalItems,
501
- },
502
- } satisfies OpenApi.IJsonSchema.ITuple);
503
- else if (Array.isArray(schema.prefixItems))
504
- union.push({
505
- ...schema,
506
- ...{
507
- items: undefined!,
508
- prefixItems: schema.prefixItems.map(convertSchema(components)),
509
- additionalItems:
510
- typeof schema.additionalItems === "object" &&
511
- schema.additionalItems !== null
512
- ? convertSchema(components)(schema.additionalItems)
513
- : schema.additionalItems,
514
- },
515
- });
516
- else if (schema.items === undefined)
517
- union.push({
518
- ...schema,
519
- ...{
520
- items: undefined!,
521
- prefixItems: [],
522
- },
523
- });
524
- else
525
- union.push({
526
- ...schema,
527
- ...{
528
- items: convertSchema(components)(schema.items),
529
- prefixItems: undefined,
530
- additionalItems: undefined,
531
- },
532
- });
533
- }
534
- // OBJECT TYPE CASE
535
- else if (OpenApiV3_1TypeChecker.isObject(schema))
536
- union.push({
537
- ...schema,
538
- ...{
539
- properties: schema.properties
540
- ? Object.fromEntries(
541
- Object.entries(schema.properties)
542
- .filter(([_, v]) => v !== undefined)
543
- .map(
544
- ([key, value]) =>
545
- [key, convertSchema(components)(value)] as const,
546
- ),
547
- )
548
- : {},
549
- additionalProperties: schema.additionalProperties
550
- ? typeof schema.additionalProperties === "object" &&
551
- schema.additionalProperties !== null
552
- ? convertSchema(components)(schema.additionalProperties)
553
- : schema.additionalProperties
554
- : undefined,
555
- required: schema.required ?? [],
556
- },
557
- });
558
- else if (OpenApiV3_1TypeChecker.isReference(schema))
559
- union.push({
560
- ...schema,
561
- ...{
562
- $ref: `#/components/schemas/${schema.$ref.split("/").pop()}`,
563
- },
564
- });
565
- else if (OpenApiV3_1TypeChecker.isRecursiveReference(schema))
566
- union.push({
567
- ...schema,
568
- ...{
569
- $ref: `#/components/schemas/${schema.$recursiveRef.split("/").pop()}`,
570
- $recursiveRef: undefined,
571
- },
572
- });
573
- // THE OTHERS
574
- else union.push(schema);
575
- };
576
-
577
- visit(input);
578
- if (
579
- nullable.value === true &&
580
- !union.some((e) => (e as OpenApi.IJsonSchema.INull).type === "null")
581
- )
582
- union.push({
583
- type: "null",
584
- default: nullable.default,
585
- });
586
- if (
587
- union.length === 2 &&
588
- union.filter((x) => OpenApiTypeChecker.isNull(x)).length === 1
589
- ) {
590
- const type: OpenApi.IJsonSchema = union.filter(
591
- (x) => OpenApiTypeChecker.isNull(x) === false,
592
- )[0]!;
593
- for (const key of [
594
- "title",
595
- "description",
596
- "deprecated",
597
- "readOnly",
598
- "writeOnly",
599
- "example",
600
- "examples",
601
- ] as const)
602
- if (type[key] !== undefined) delete type[key];
603
- }
604
- return {
605
- ...(union.length === 0
606
- ? {
607
- type: undefined,
608
- }
609
- : union.length === 1
610
- ? {
611
- ...union[0],
612
- }
613
- : {
614
- oneOf: union.map((u) => ({
615
- ...u,
616
- nullable: undefined,
617
- $defs: undefined,
618
- })),
619
- }),
620
- ...attribute,
621
- ...{
622
- nullable: undefined,
623
- $defs: undefined,
624
- },
625
- };
626
- };
627
-
628
- const convertAllOfSchema =
629
- (components: OpenApiV3_1.IComponents) =>
630
- (input: OpenApiV3_1.IJsonSchema.IAllOf): OpenApi.IJsonSchema => {
631
- const objects: Array<OpenApiV3_1.IJsonSchema.IObject | null> =
632
- input.allOf.map((schema) => retrieveObject(components)(schema));
633
- if (objects.some((obj) => obj === null))
634
- return {
635
- type: undefined,
636
- ...{
637
- allOf: undefined,
638
- },
639
- };
640
- return {
641
- ...input,
642
- type: "object",
643
- properties: Object.fromEntries(
644
- objects
645
- .map((o) => Object.entries(o?.properties ?? {}))
646
- .flat()
647
- .map(
648
- ([key, value]) =>
649
- [key, convertSchema(components)(value)] as const,
650
- ),
651
- ),
652
- ...{
653
- allOf: undefined,
654
- required: [...new Set(objects.map((o) => o?.required ?? []).flat())],
655
- },
656
- };
657
- };
658
-
659
- const retrieveObject =
660
- (components: OpenApiV3_1.IComponents) =>
661
- (
662
- input: OpenApiV3_1.IJsonSchema,
663
- visited: Set<OpenApiV3_1.IJsonSchema> = new Set(),
664
- ): OpenApiV3_1.IJsonSchema.IObject | null => {
665
- if (OpenApiV3_1TypeChecker.isObject(input))
666
- return input.properties !== undefined && !input.additionalProperties
667
- ? input
668
- : null;
669
- else if (visited.has(input)) return null;
670
- else visited.add(input);
671
-
672
- if (OpenApiV3_1TypeChecker.isReference(input))
673
- return retrieveObject(components)(
674
- components.schemas?.[input.$ref.split("/").pop() ?? ""] ?? {},
675
- visited,
676
- );
677
- else if (OpenApiV3_1TypeChecker.isRecursiveReference(input))
678
- return retrieveObject(components)(
679
- components.schemas?.[input.$recursiveRef.split("/").pop() ?? ""] ??
680
- {},
681
- visited,
682
- );
683
- return null;
684
- };
685
- }
1
+ import { IJsonSchemaAttribute, OpenApi, OpenApiV3_1 } from "@typia/interface";
2
+
3
+ import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
+ import { OpenApiV3_1TypeChecker } from "../../validators/OpenApiV3_1TypeChecker";
5
+ import { OpenApiExclusiveEmender } from "./OpenApiExclusiveEmender";
6
+
7
+ export namespace OpenApiV3_1Upgrader {
8
+ export const convert = (input: OpenApiV3_1.IDocument): OpenApi.IDocument => {
9
+ if ((input as OpenApi.IDocument)["x-samchon-emended-v4"] === true)
10
+ return input as OpenApi.IDocument;
11
+ return {
12
+ ...input,
13
+ components: convertComponents(input.components ?? {}),
14
+ paths: input.paths
15
+ ? Object.fromEntries(
16
+ Object.entries(input.paths)
17
+ .filter(([_, v]) => v !== undefined)
18
+ .map(
19
+ ([key, value]) => [key, convertPathItem(input)(value)] as const,
20
+ ),
21
+ )
22
+ : undefined,
23
+ webhooks: input.webhooks
24
+ ? Object.fromEntries(
25
+ Object.entries(input.webhooks)
26
+ .filter(([_, v]) => v !== undefined)
27
+ .map(
28
+ ([key, value]) =>
29
+ [key, convertWebhooks(input)(value)!] as const,
30
+ )
31
+ .filter(([_, value]) => value !== undefined),
32
+ )
33
+ : undefined,
34
+ "x-samchon-emended-v4": true,
35
+ };
36
+ };
37
+
38
+ /* -----------------------------------------------------------
39
+ OPERATORS
40
+ ----------------------------------------------------------- */
41
+ const convertWebhooks =
42
+ (doc: OpenApiV3_1.IDocument) =>
43
+ (
44
+ webhook:
45
+ | OpenApiV3_1.IPath
46
+ | OpenApiV3_1.IJsonSchema.IReference<`#/components/pathItems/${string}`>,
47
+ ): OpenApi.IPath | undefined => {
48
+ if (!OpenApiV3_1TypeChecker.isReference(webhook))
49
+ return convertPathItem(doc)(webhook);
50
+ const found: OpenApiV3_1.IPath | undefined =
51
+ doc.components?.pathItems?.[webhook.$ref.split("/").pop() ?? ""];
52
+ return found ? convertPathItem(doc)(found) : undefined;
53
+ };
54
+
55
+ const convertPathItem =
56
+ (doc: OpenApiV3_1.IDocument) =>
57
+ (pathItem: OpenApiV3_1.IPath): OpenApi.IPath => ({
58
+ ...(pathItem as any),
59
+ ...(pathItem.get
60
+ ? { get: convertOperation(doc)(pathItem)(pathItem.get) }
61
+ : undefined),
62
+ ...(pathItem.put
63
+ ? { put: convertOperation(doc)(pathItem)(pathItem.put) }
64
+ : undefined),
65
+ ...(pathItem.post
66
+ ? { post: convertOperation(doc)(pathItem)(pathItem.post) }
67
+ : undefined),
68
+ ...(pathItem.delete
69
+ ? { delete: convertOperation(doc)(pathItem)(pathItem.delete) }
70
+ : undefined),
71
+ ...(pathItem.options
72
+ ? { options: convertOperation(doc)(pathItem)(pathItem.options) }
73
+ : undefined),
74
+ ...(pathItem.head
75
+ ? { head: convertOperation(doc)(pathItem)(pathItem.head) }
76
+ : undefined),
77
+ ...(pathItem.patch
78
+ ? { patch: convertOperation(doc)(pathItem)(pathItem.patch) }
79
+ : undefined),
80
+ ...(pathItem.trace
81
+ ? { trace: convertOperation(doc)(pathItem)(pathItem.trace) }
82
+ : undefined),
83
+ });
84
+
85
+ const convertOperation =
86
+ (doc: OpenApiV3_1.IDocument) =>
87
+ (pathItem: OpenApiV3_1.IPath) =>
88
+ (input: OpenApiV3_1.IOperation): OpenApi.IOperation => ({
89
+ ...input,
90
+ parameters:
91
+ pathItem.parameters !== undefined || input.parameters !== undefined
92
+ ? [...(pathItem.parameters ?? []), ...(input.parameters ?? [])]
93
+ .map((p) => {
94
+ if (!OpenApiV3_1TypeChecker.isReference(p))
95
+ return convertParameter(doc.components ?? {})(p);
96
+ const found:
97
+ | Omit<OpenApiV3_1.IOperation.IParameter, "in">
98
+ | undefined = p.$ref.startsWith("#/components/headers/")
99
+ ? doc.components?.headers?.[p.$ref.split("/").pop() ?? ""]
100
+ : doc.components?.parameters?.[p.$ref.split("/").pop() ?? ""];
101
+ return found !== undefined
102
+ ? convertParameter(doc.components ?? {})({
103
+ ...found,
104
+ in: "header",
105
+ })
106
+ : undefined!;
107
+ })
108
+ .filter((_, v) => v !== undefined)
109
+ : undefined,
110
+ requestBody: input.requestBody
111
+ ? convertRequestBody(doc)(input.requestBody)
112
+ : undefined,
113
+ responses: input.responses
114
+ ? Object.fromEntries(
115
+ Object.entries(input.responses)
116
+ .filter(([_, v]) => v !== undefined)
117
+ .map(
118
+ ([key, value]) => [key, convertResponse(doc)(value)!] as const,
119
+ )
120
+ .filter(([_, value]) => value !== undefined),
121
+ )
122
+ : undefined,
123
+ });
124
+
125
+ const convertParameter =
126
+ (components: OpenApiV3_1.IComponents) =>
127
+ (
128
+ input: OpenApiV3_1.IOperation.IParameter,
129
+ ): OpenApi.IOperation.IParameter => ({
130
+ ...input,
131
+ schema: convertSchema(components)(input.schema),
132
+ examples: input.examples
133
+ ? Object.fromEntries(
134
+ Object.entries(input.examples)
135
+ .map(([key, value]) => [
136
+ key,
137
+ OpenApiV3_1TypeChecker.isReference(value)
138
+ ? components.examples?.[value.$ref.split("/").pop() ?? ""]
139
+ : value,
140
+ ])
141
+ .filter(([_, v]) => v !== undefined),
142
+ )
143
+ : undefined,
144
+ });
145
+
146
+ const convertRequestBody =
147
+ (doc: OpenApiV3_1.IDocument) =>
148
+ (
149
+ input:
150
+ | OpenApiV3_1.IOperation.IRequestBody
151
+ | OpenApiV3_1.IJsonSchema.IReference<`#/components/requestBodies/${string}`>,
152
+ ): OpenApi.IOperation.IRequestBody | undefined => {
153
+ if (OpenApiV3_1TypeChecker.isReference(input)) {
154
+ const found: OpenApiV3_1.IOperation.IRequestBody | undefined =
155
+ doc.components?.requestBodies?.[input.$ref.split("/").pop() ?? ""];
156
+ if (found === undefined) return undefined;
157
+ input = found;
158
+ }
159
+ return {
160
+ ...input,
161
+ content: input.content
162
+ ? convertContent(doc.components ?? {})(input.content)
163
+ : undefined,
164
+ };
165
+ };
166
+
167
+ const convertResponse =
168
+ (doc: OpenApiV3_1.IDocument) =>
169
+ (
170
+ input:
171
+ | OpenApiV3_1.IOperation.IResponse
172
+ | OpenApiV3_1.IJsonSchema.IReference<`#/components/responses/${string}`>,
173
+ ): OpenApi.IOperation.IResponse | undefined => {
174
+ if (OpenApiV3_1TypeChecker.isReference(input)) {
175
+ const found: OpenApiV3_1.IOperation.IResponse | undefined =
176
+ doc.components?.responses?.[input.$ref.split("/").pop() ?? ""];
177
+ if (found === undefined) return undefined;
178
+ input = found;
179
+ }
180
+ return {
181
+ ...input,
182
+ content: input.content
183
+ ? convertContent(doc.components ?? {})(input.content)
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
+ if (OpenApiV3_1TypeChecker.isReference(value) === false)
195
+ return convertParameter(doc.components ?? {})({
196
+ ...value,
197
+ in: "header",
198
+ });
199
+ const found:
200
+ | Omit<OpenApiV3_1.IOperation.IParameter, "in">
201
+ | undefined = value.$ref.startsWith(
202
+ "#/components/headers/",
203
+ )
204
+ ? doc.components?.headers?.[
205
+ value.$ref.split("/").pop() ?? ""
206
+ ]
207
+ : undefined;
208
+ return found !== undefined
209
+ ? convertParameter(doc.components ?? {})({
210
+ ...found,
211
+ in: "header",
212
+ })
213
+ : undefined!;
214
+ })(),
215
+ ] as const,
216
+ )
217
+ .filter(([_, v]) => v !== undefined),
218
+ )
219
+ : undefined,
220
+ };
221
+ };
222
+
223
+ const convertContent =
224
+ (components: OpenApiV3_1.IComponents) =>
225
+ (
226
+ record: Record<string, OpenApiV3_1.IOperation.IMediaType>,
227
+ ): Record<string, OpenApi.IOperation.IMediaType> =>
228
+ Object.fromEntries(
229
+ Object.entries(record)
230
+ .filter(([_, v]) => v !== undefined)
231
+ .map(
232
+ ([key, value]) =>
233
+ [
234
+ key,
235
+ {
236
+ ...value,
237
+ schema: value.schema
238
+ ? convertSchema(components)(value.schema)
239
+ : undefined,
240
+ examples: value.examples
241
+ ? Object.fromEntries(
242
+ Object.entries(value.examples)
243
+ .map(([key, value]) => [
244
+ key,
245
+ OpenApiV3_1TypeChecker.isReference(value)
246
+ ? components.examples?.[
247
+ value.$ref.split("/").pop() ?? ""
248
+ ]
249
+ : value,
250
+ ])
251
+ .filter(([_, v]) => v !== undefined),
252
+ )
253
+ : undefined,
254
+ },
255
+ ] as const,
256
+ ),
257
+ );
258
+
259
+ /* -----------------------------------------------------------
260
+ DEFINITIONS
261
+ ----------------------------------------------------------- */
262
+ export const convertComponents = (
263
+ input: OpenApiV3_1.IComponents,
264
+ ): OpenApi.IComponents => ({
265
+ schemas: Object.fromEntries(
266
+ Object.entries(input.schemas ?? {})
267
+ .filter(([_, v]) => v !== undefined)
268
+ .map(([key, value]) => [key, convertSchema(input)(value)] as const),
269
+ ),
270
+ securitySchemes: input.securitySchemes,
271
+ });
272
+
273
+ export const convertSchema =
274
+ (components: OpenApiV3_1.IComponents) =>
275
+ (input: OpenApiV3_1.IJsonSchema): OpenApi.IJsonSchema => {
276
+ const union: OpenApi.IJsonSchema[] = [];
277
+ const attribute: IJsonSchemaAttribute = {
278
+ title: input.title,
279
+ description: input.description,
280
+ deprecated: input.deprecated,
281
+ readOnly: input.readOnly,
282
+ writeOnly: input.writeOnly,
283
+ example: input.example,
284
+ examples: Array.isArray(input.examples)
285
+ ? Object.fromEntries(input.examples.map((v, i) => [`v${i}`, v]))
286
+ : input.examples,
287
+ ...Object.fromEntries(
288
+ Object.entries(input).filter(
289
+ ([key, value]) => key.startsWith("x-") && value !== undefined,
290
+ ),
291
+ ),
292
+ };
293
+ const nullable: { value: boolean; default?: null } = {
294
+ value: false,
295
+ default: undefined,
296
+ };
297
+
298
+ const visit = (schema: OpenApiV3_1.IJsonSchema): void => {
299
+ // NULLABLE PROPERTY
300
+ if ((schema as OpenApiV3_1.IJsonSchema.INumber).nullable === true) {
301
+ nullable.value ||= true;
302
+ if ((schema as OpenApiV3_1.IJsonSchema.INumber).default === null)
303
+ nullable.default = null;
304
+ }
305
+ if (
306
+ Array.isArray((schema as OpenApiV3_1.IJsonSchema.INumber).enum) &&
307
+ (schema as OpenApiV3_1.IJsonSchema.INumber).enum?.length &&
308
+ (schema as OpenApiV3_1.IJsonSchema.INumber).enum?.some(
309
+ (e) => e === null,
310
+ )
311
+ )
312
+ nullable.value ||= true;
313
+
314
+ // MIXED TYPE CASE
315
+ if (OpenApiV3_1TypeChecker.isMixed(schema)) {
316
+ if (schema.const !== undefined)
317
+ visit({
318
+ ...schema,
319
+ ...{
320
+ type: undefined,
321
+ oneOf: undefined,
322
+ anyOf: undefined,
323
+ allOf: undefined,
324
+ $ref: undefined,
325
+ },
326
+ });
327
+ if (schema.oneOf !== undefined)
328
+ visit({
329
+ ...schema,
330
+ ...{
331
+ type: undefined,
332
+ anyOf: undefined,
333
+ allOf: undefined,
334
+ $ref: undefined,
335
+ },
336
+ });
337
+ if (schema.anyOf !== undefined)
338
+ visit({
339
+ ...schema,
340
+ ...{
341
+ type: undefined,
342
+ oneOf: undefined,
343
+ allOf: undefined,
344
+ $ref: undefined,
345
+ },
346
+ });
347
+ if (schema.allOf !== undefined)
348
+ visit({
349
+ ...schema,
350
+ ...{
351
+ type: undefined,
352
+ oneOf: undefined,
353
+ anyOf: undefined,
354
+ $ref: undefined,
355
+ },
356
+ });
357
+ for (const type of schema.type)
358
+ if (type === "boolean" || type === "number" || type === "string")
359
+ visit({
360
+ ...schema,
361
+ ...{
362
+ enum:
363
+ schema.enum?.length && schema.enum.filter((e) => e !== null)
364
+ ? schema.enum.filter((x) => typeof x === type)
365
+ : undefined,
366
+ },
367
+ type: type as any,
368
+ });
369
+ else if (type === "integer")
370
+ visit({
371
+ ...schema,
372
+ ...{
373
+ enum:
374
+ schema.enum?.length && schema.enum.filter((e) => e !== null)
375
+ ? schema.enum.filter(
376
+ (x) =>
377
+ x !== null &&
378
+ typeof x === "number" &&
379
+ Number.isInteger(x),
380
+ )
381
+ : undefined,
382
+ },
383
+ type: type as any,
384
+ });
385
+ else visit({ ...schema, type: type as any });
386
+ }
387
+ // UNION TYPE CASE
388
+ else if (OpenApiV3_1TypeChecker.isOneOf(schema))
389
+ schema.oneOf.forEach(visit);
390
+ else if (OpenApiV3_1TypeChecker.isAnyOf(schema))
391
+ schema.anyOf.forEach(visit);
392
+ else if (OpenApiV3_1TypeChecker.isAllOf(schema))
393
+ if (schema.allOf.length === 1) visit(schema.allOf[0]!);
394
+ else union.push(convertAllOfSchema(components)(schema));
395
+ // ATOMIC TYPE CASE (CONSIDER ENUM VALUES)
396
+ else if (OpenApiV3_1TypeChecker.isBoolean(schema))
397
+ if (
398
+ schema.enum?.length &&
399
+ schema.enum.filter((e) => e !== null).length
400
+ )
401
+ for (const value of schema.enum.filter((e) => e !== null))
402
+ union.push({
403
+ const: value,
404
+ ...({
405
+ ...schema,
406
+ type: undefined as any,
407
+ enum: undefined,
408
+ default: undefined,
409
+ } satisfies OpenApiV3_1.IJsonSchema.IBoolean as any),
410
+ } satisfies OpenApi.IJsonSchema.IConstant);
411
+ else
412
+ union.push({
413
+ ...schema,
414
+ default: schema.default ?? undefined,
415
+ ...{
416
+ enum: undefined,
417
+ },
418
+ });
419
+ else if (
420
+ OpenApiV3_1TypeChecker.isInteger(schema) ||
421
+ OpenApiV3_1TypeChecker.isNumber(schema)
422
+ )
423
+ if (schema.enum?.length && schema.enum.filter((e) => e !== null))
424
+ for (const value of schema.enum.filter((e) => e !== null))
425
+ union.push({
426
+ const: value,
427
+ ...({
428
+ ...schema,
429
+ type: undefined as any,
430
+ enum: undefined,
431
+ default: undefined,
432
+ minimum: undefined,
433
+ maximum: undefined,
434
+ exclusiveMinimum: undefined,
435
+ exclusiveMaximum: undefined,
436
+ multipleOf: undefined,
437
+ } satisfies OpenApiV3_1.IJsonSchema.IInteger as any),
438
+ } satisfies OpenApi.IJsonSchema.IConstant);
439
+ else
440
+ union.push(
441
+ OpenApiExclusiveEmender.emend({
442
+ ...schema,
443
+ default: schema.default ?? undefined,
444
+ ...{
445
+ enum: undefined,
446
+ },
447
+ exclusiveMinimum:
448
+ typeof schema.exclusiveMinimum === "boolean"
449
+ ? schema.exclusiveMinimum === true
450
+ ? schema.minimum
451
+ : undefined
452
+ : schema.exclusiveMinimum,
453
+ exclusiveMaximum:
454
+ typeof schema.exclusiveMaximum === "boolean"
455
+ ? schema.exclusiveMaximum === true
456
+ ? schema.maximum
457
+ : undefined
458
+ : schema.exclusiveMaximum,
459
+ minimum:
460
+ schema.exclusiveMinimum === true ? undefined : schema.minimum,
461
+ maximum:
462
+ schema.exclusiveMaximum === true ? undefined : schema.maximum,
463
+ }),
464
+ );
465
+ else if (OpenApiV3_1TypeChecker.isString(schema))
466
+ if (
467
+ schema.enum?.length &&
468
+ schema.enum.filter((e) => e !== null).length
469
+ )
470
+ for (const value of schema.enum.filter((e) => e !== null))
471
+ union.push({
472
+ const: value,
473
+ ...({
474
+ ...schema,
475
+ type: undefined as any,
476
+ enum: undefined,
477
+ default: undefined,
478
+ } satisfies OpenApiV3_1.IJsonSchema.IString as any),
479
+ } satisfies OpenApi.IJsonSchema.IConstant);
480
+ else
481
+ union.push({
482
+ ...schema,
483
+ default: schema.default ?? undefined,
484
+ ...{
485
+ enum: undefined,
486
+ },
487
+ });
488
+ // ARRAY TYPE CASE (CONSIDER TUPLE)
489
+ else if (OpenApiV3_1TypeChecker.isArray(schema)) {
490
+ if (Array.isArray(schema.items))
491
+ union.push({
492
+ ...schema,
493
+ ...{
494
+ items: undefined!,
495
+ prefixItems: schema.items.map(convertSchema(components)),
496
+ additionalItems:
497
+ typeof schema.additionalItems === "object" &&
498
+ schema.additionalItems !== null
499
+ ? convertSchema(components)(schema.additionalItems)
500
+ : schema.additionalItems,
501
+ },
502
+ } satisfies OpenApi.IJsonSchema.ITuple);
503
+ else if (Array.isArray(schema.prefixItems))
504
+ union.push({
505
+ ...schema,
506
+ ...{
507
+ items: undefined!,
508
+ prefixItems: schema.prefixItems.map(convertSchema(components)),
509
+ additionalItems:
510
+ typeof schema.additionalItems === "object" &&
511
+ schema.additionalItems !== null
512
+ ? convertSchema(components)(schema.additionalItems)
513
+ : schema.additionalItems,
514
+ },
515
+ });
516
+ else if (schema.items === undefined)
517
+ union.push({
518
+ ...schema,
519
+ ...{
520
+ items: undefined!,
521
+ prefixItems: [],
522
+ },
523
+ });
524
+ else
525
+ union.push({
526
+ ...schema,
527
+ ...{
528
+ items: convertSchema(components)(schema.items),
529
+ prefixItems: undefined,
530
+ additionalItems: undefined,
531
+ },
532
+ });
533
+ }
534
+ // OBJECT TYPE CASE
535
+ else if (OpenApiV3_1TypeChecker.isObject(schema))
536
+ union.push({
537
+ ...schema,
538
+ ...{
539
+ properties: schema.properties
540
+ ? Object.fromEntries(
541
+ Object.entries(schema.properties)
542
+ .filter(([_, v]) => v !== undefined)
543
+ .map(
544
+ ([key, value]) =>
545
+ [key, convertSchema(components)(value)] as const,
546
+ ),
547
+ )
548
+ : {},
549
+ additionalProperties: schema.additionalProperties
550
+ ? typeof schema.additionalProperties === "object" &&
551
+ schema.additionalProperties !== null
552
+ ? convertSchema(components)(schema.additionalProperties)
553
+ : schema.additionalProperties
554
+ : undefined,
555
+ required: schema.required ?? [],
556
+ },
557
+ });
558
+ else if (OpenApiV3_1TypeChecker.isReference(schema))
559
+ union.push({
560
+ ...schema,
561
+ ...{
562
+ $ref: `#/components/schemas/${schema.$ref.split("/").pop()}`,
563
+ },
564
+ });
565
+ else if (OpenApiV3_1TypeChecker.isRecursiveReference(schema))
566
+ union.push({
567
+ ...schema,
568
+ ...{
569
+ $ref: `#/components/schemas/${schema.$recursiveRef.split("/").pop()}`,
570
+ $recursiveRef: undefined,
571
+ },
572
+ });
573
+ // THE OTHERS
574
+ else union.push(schema);
575
+ };
576
+
577
+ visit(input);
578
+ if (
579
+ nullable.value === true &&
580
+ !union.some((e) => (e as OpenApi.IJsonSchema.INull).type === "null")
581
+ )
582
+ union.push({
583
+ type: "null",
584
+ default: nullable.default,
585
+ });
586
+ if (
587
+ union.length === 2 &&
588
+ union.filter((x) => OpenApiTypeChecker.isNull(x)).length === 1
589
+ ) {
590
+ const type: OpenApi.IJsonSchema = union.filter(
591
+ (x) => OpenApiTypeChecker.isNull(x) === false,
592
+ )[0]!;
593
+ for (const key of [
594
+ "title",
595
+ "description",
596
+ "deprecated",
597
+ "readOnly",
598
+ "writeOnly",
599
+ "example",
600
+ "examples",
601
+ ] as const)
602
+ if (type[key] !== undefined) delete type[key];
603
+ }
604
+ return {
605
+ ...(union.length === 0
606
+ ? {
607
+ type: undefined,
608
+ }
609
+ : union.length === 1
610
+ ? {
611
+ ...union[0],
612
+ }
613
+ : {
614
+ oneOf: union.map((u) => ({
615
+ ...u,
616
+ nullable: undefined,
617
+ $defs: undefined,
618
+ })),
619
+ }),
620
+ ...attribute,
621
+ ...{
622
+ nullable: undefined,
623
+ $defs: undefined,
624
+ },
625
+ };
626
+ };
627
+
628
+ const convertAllOfSchema =
629
+ (components: OpenApiV3_1.IComponents) =>
630
+ (input: OpenApiV3_1.IJsonSchema.IAllOf): OpenApi.IJsonSchema => {
631
+ const objects: Array<OpenApiV3_1.IJsonSchema.IObject | null> =
632
+ input.allOf.map((schema) => retrieveObject(components)(schema));
633
+ if (objects.some((obj) => obj === null))
634
+ return {
635
+ type: undefined,
636
+ ...{
637
+ allOf: undefined,
638
+ },
639
+ };
640
+ return {
641
+ ...input,
642
+ type: "object",
643
+ properties: Object.fromEntries(
644
+ objects
645
+ .map((o) => Object.entries(o?.properties ?? {}))
646
+ .flat()
647
+ .map(
648
+ ([key, value]) =>
649
+ [key, convertSchema(components)(value)] as const,
650
+ ),
651
+ ),
652
+ ...{
653
+ allOf: undefined,
654
+ required: [...new Set(objects.map((o) => o?.required ?? []).flat())],
655
+ },
656
+ };
657
+ };
658
+
659
+ const retrieveObject =
660
+ (components: OpenApiV3_1.IComponents) =>
661
+ (
662
+ input: OpenApiV3_1.IJsonSchema,
663
+ visited: Set<OpenApiV3_1.IJsonSchema> = new Set(),
664
+ ): OpenApiV3_1.IJsonSchema.IObject | null => {
665
+ if (OpenApiV3_1TypeChecker.isObject(input))
666
+ return input.properties !== undefined && !input.additionalProperties
667
+ ? input
668
+ : null;
669
+ else if (visited.has(input)) return null;
670
+ else visited.add(input);
671
+
672
+ if (OpenApiV3_1TypeChecker.isReference(input))
673
+ return retrieveObject(components)(
674
+ components.schemas?.[input.$ref.split("/").pop() ?? ""] ?? {},
675
+ visited,
676
+ );
677
+ else if (OpenApiV3_1TypeChecker.isRecursiveReference(input))
678
+ return retrieveObject(components)(
679
+ components.schemas?.[input.$recursiveRef.split("/").pop() ?? ""] ??
680
+ {},
681
+ visited,
682
+ );
683
+ return null;
684
+ };
685
+ }