@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,355 +1,355 @@
1
- import { OpenApi, OpenApiV3 } from "@typia/interface";
2
-
3
- import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
-
5
- export namespace OpenApiV3Downgrader {
6
- export interface IComponentsCollection {
7
- original: OpenApi.IComponents;
8
- downgraded: OpenApiV3.IComponents;
9
- }
10
-
11
- export const downgrade = (input: OpenApi.IDocument): OpenApiV3.IDocument => {
12
- const collection: IComponentsCollection = downgradeComponents(
13
- input.components,
14
- );
15
- return {
16
- openapi: "3.0.0",
17
- servers: input.servers,
18
- info: input.info,
19
- components: collection.downgraded,
20
- paths: input.paths
21
- ? Object.fromEntries(
22
- Object.entries(input.paths)
23
- .filter(([_, v]) => v !== undefined)
24
- .map(
25
- ([key, value]) =>
26
- [key, downgradePathItem(collection)(value)] as const,
27
- ),
28
- )
29
- : undefined,
30
- security: input.security,
31
- tags: input.tags,
32
- };
33
- };
34
-
35
- /* -----------------------------------------------------------
36
- OPERATORS
37
- ----------------------------------------------------------- */
38
- const downgradePathItem =
39
- (collection: IComponentsCollection) =>
40
- (pathItem: OpenApi.IPath): OpenApiV3.IPath => ({
41
- ...(pathItem as any),
42
- ...(pathItem.get
43
- ? { get: downgradeOperation(collection)(pathItem.get) }
44
- : undefined),
45
- ...(pathItem.put
46
- ? { put: downgradeOperation(collection)(pathItem.put) }
47
- : undefined),
48
- ...(pathItem.post
49
- ? { post: downgradeOperation(collection)(pathItem.post) }
50
- : undefined),
51
- ...(pathItem.delete
52
- ? { delete: downgradeOperation(collection)(pathItem.delete) }
53
- : undefined),
54
- ...(pathItem.options
55
- ? { options: downgradeOperation(collection)(pathItem.options) }
56
- : undefined),
57
- ...(pathItem.head
58
- ? { head: downgradeOperation(collection)(pathItem.head) }
59
- : undefined),
60
- ...(pathItem.patch
61
- ? { patch: downgradeOperation(collection)(pathItem.patch) }
62
- : undefined),
63
- ...(pathItem.trace
64
- ? { trace: downgradeOperation(collection)(pathItem.trace) }
65
- : undefined),
66
- });
67
-
68
- const downgradeOperation =
69
- (collection: IComponentsCollection) =>
70
- (input: OpenApi.IOperation): OpenApiV3.IOperation => ({
71
- ...input,
72
- parameters: input.parameters
73
- ? input.parameters.map(downgradeParameter(collection))
74
- : undefined,
75
- requestBody: input.requestBody
76
- ? downgradeRequestBody(collection)(input.requestBody)
77
- : undefined,
78
- responses: input.responses
79
- ? Object.fromEntries(
80
- Object.entries(input.responses)
81
- .filter(([_, v]) => v !== undefined)
82
- .map(([key, value]) => [
83
- key,
84
- downgradeResponse(collection)(value),
85
- ]),
86
- )
87
- : undefined,
88
- });
89
-
90
- const downgradeParameter =
91
- (collection: IComponentsCollection) =>
92
- (
93
- input: OpenApi.IOperation.IParameter,
94
- ): OpenApiV3.IOperation.IParameter => ({
95
- ...input,
96
- schema: downgradeSchema(collection)(input.schema),
97
- });
98
-
99
- const downgradeRequestBody =
100
- (collection: IComponentsCollection) =>
101
- (
102
- input: OpenApi.IOperation.IRequestBody,
103
- ): OpenApiV3.IOperation.IRequestBody => ({
104
- ...input,
105
- content: input.content
106
- ? downgradeContent(collection)(input.content)
107
- : undefined,
108
- });
109
-
110
- const downgradeResponse =
111
- (collection: IComponentsCollection) =>
112
- (input: OpenApi.IOperation.IResponse): OpenApiV3.IOperation.IResponse => ({
113
- ...input,
114
- content: input.content
115
- ? downgradeContent(collection)(input.content)
116
- : undefined,
117
- headers: input.headers
118
- ? Object.fromEntries(
119
- Object.entries(input.headers)
120
- .filter(([_, v]) => v !== undefined)
121
- .map(([key, value]) => [
122
- key,
123
- {
124
- ...value,
125
- schema: downgradeSchema(collection)(value.schema),
126
- },
127
- ]),
128
- )
129
- : undefined,
130
- });
131
-
132
- const downgradeContent =
133
- (collection: IComponentsCollection) =>
134
- (
135
- record: OpenApi.IOperation.IContent,
136
- ): Record<string, OpenApiV3.IOperation.IMediaType> =>
137
- Object.fromEntries(
138
- Object.entries(record)
139
- .filter(([_, v]) => v !== undefined)
140
- .map(
141
- ([key, value]) =>
142
- [
143
- key,
144
- {
145
- ...value,
146
- schema: value?.schema
147
- ? downgradeSchema(collection)(value.schema)
148
- : undefined,
149
- },
150
- ] as const,
151
- ),
152
- );
153
-
154
- /* -----------------------------------------------------------
155
- DEFINITIONS
156
- ----------------------------------------------------------- */
157
- export const downgradeComponents = (
158
- input: OpenApi.IComponents,
159
- ): IComponentsCollection => {
160
- const collection: IComponentsCollection = {
161
- original: input,
162
- downgraded: {
163
- securitySchemes: input.securitySchemes,
164
- },
165
- };
166
- if (input.schemas) {
167
- collection.downgraded.schemas = {};
168
- for (const [key, value] of Object.entries(input.schemas))
169
- if (value !== undefined)
170
- collection.downgraded.schemas[key] =
171
- downgradeSchema(collection)(value);
172
- }
173
- return collection;
174
- };
175
-
176
- export const downgradeSchema =
177
- (collection: IComponentsCollection) =>
178
- (input: OpenApi.IJsonSchema): OpenApiV3.IJsonSchema => {
179
- const nullable: boolean = isNullable(new Set())(collection.original)(
180
- input,
181
- );
182
- const union: OpenApiV3.IJsonSchema[] = [];
183
- const attribute: OpenApiV3.IJsonSchema.__IAttribute = {
184
- title: input.title,
185
- description: input.description,
186
- deprecated: input.deprecated,
187
- readOnly: input.readOnly,
188
- writeOnly: input.writeOnly,
189
- example: input.example,
190
- examples: input.examples,
191
- ...Object.fromEntries(
192
- Object.entries(input).filter(
193
- ([key, value]) => key.startsWith("x-") && value !== undefined,
194
- ),
195
- ),
196
- };
197
- const visit = (schema: OpenApi.IJsonSchema): void => {
198
- if (OpenApiTypeChecker.isBoolean(schema))
199
- union.push({ type: "boolean" });
200
- else if (
201
- OpenApiTypeChecker.isBoolean(schema) ||
202
- OpenApiTypeChecker.isInteger(schema) ||
203
- OpenApiTypeChecker.isNumber(schema) ||
204
- OpenApiTypeChecker.isString(schema) ||
205
- OpenApiTypeChecker.isReference(schema)
206
- )
207
- union.push({ ...schema });
208
- else if (OpenApiTypeChecker.isArray(schema))
209
- union.push({
210
- ...schema,
211
- items: downgradeSchema(collection)(schema.items),
212
- });
213
- else if (OpenApiTypeChecker.isTuple(schema))
214
- union.push({
215
- ...schema,
216
- items: ((): OpenApiV3.IJsonSchema => {
217
- if (schema.additionalItems === true) return {};
218
- const elements = [
219
- ...schema.prefixItems,
220
- ...(typeof schema.additionalItems === "object"
221
- ? [downgradeSchema(collection)(schema.additionalItems)]
222
- : []),
223
- ];
224
- if (elements.length === 0) return {};
225
- return {
226
- oneOf: elements.map(downgradeSchema(collection) as any),
227
- };
228
- })(),
229
- minItems: schema.prefixItems.length,
230
- maxItems:
231
- !!schema.additionalItems === true
232
- ? undefined
233
- : schema.prefixItems.length,
234
- ...{
235
- prefixItems: undefined,
236
- additionalItems: undefined,
237
- },
238
- });
239
- else if (OpenApiTypeChecker.isObject(schema))
240
- union.push({
241
- ...schema,
242
- properties: schema.properties
243
- ? Object.fromEntries(
244
- Object.entries(schema.properties)
245
- .filter(([_, v]) => v !== undefined)
246
- .map(([key, value]) => [
247
- key,
248
- downgradeSchema(collection)(value),
249
- ]),
250
- )
251
- : undefined,
252
- additionalProperties:
253
- typeof schema.additionalProperties === "object"
254
- ? downgradeSchema(collection)(schema.additionalProperties)
255
- : schema.additionalProperties,
256
- required: schema.required,
257
- });
258
- else if (OpenApiTypeChecker.isOneOf(schema))
259
- schema.oneOf.forEach(visit);
260
- };
261
- const visitConstant = (schema: OpenApi.IJsonSchema): void => {
262
- const insert = (value: any): void => {
263
- const matched: OpenApiV3.IJsonSchema.INumber | undefined = union.find(
264
- (u) => (u as OpenApiV3.IJsonSchema.INumber).type === typeof value,
265
- ) as OpenApiV3.IJsonSchema.INumber | undefined;
266
- if (matched !== undefined) {
267
- matched.enum ??= [];
268
- matched.enum.push(value);
269
- } else union.push({ type: typeof value as "number", enum: [value] });
270
- };
271
- if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const);
272
- else if (OpenApiTypeChecker.isOneOf(schema))
273
- for (const u of schema.oneOf)
274
- if (OpenApiTypeChecker.isConstant(u)) insert(u.const);
275
- };
276
-
277
- visit(input);
278
- visitConstant(input);
279
- if (nullable === true)
280
- for (const u of union)
281
- if (OpenApiTypeChecker.isReference(u as any))
282
- downgradeNullableReference(new Set())(collection)(u as any);
283
- else (u as OpenApiV3.IJsonSchema.IArray).nullable = true;
284
- if (nullable === true && union.length === 0)
285
- return { type: "null", ...attribute };
286
- return {
287
- ...(union.length === 0
288
- ? { type: undefined }
289
- : union.length === 1
290
- ? { ...union[0] }
291
- : { oneOf: union }),
292
- ...attribute,
293
- };
294
- };
295
-
296
- const downgradeNullableReference =
297
- (visited: Set<string>) =>
298
- (collection: IComponentsCollection) =>
299
- (schema: OpenApiV3.IJsonSchema.IReference): void => {
300
- const key: string = schema.$ref.split("/").pop()!;
301
- if (key.endsWith(".Nullable")) return;
302
-
303
- const found: OpenApi.IJsonSchema | undefined =
304
- collection.original.schemas?.[key];
305
- if (found === undefined) return;
306
- else if (isNullable(visited)(collection.original)(found) === true) return;
307
- else if (
308
- collection.downgraded.schemas?.[`${key}.Nullable`] === undefined
309
- ) {
310
- collection.downgraded.schemas ??= {};
311
- collection.downgraded.schemas[`${key}.Nullable`] = {};
312
- collection.downgraded.schemas[`${key}.Nullable`] = downgradeSchema(
313
- collection,
314
- )(
315
- OpenApiTypeChecker.isOneOf(found)
316
- ? {
317
- ...found,
318
- oneOf: [...found.oneOf, { type: "null" }],
319
- }
320
- : {
321
- oneOf: [found, { type: "null" }],
322
- title: found.title,
323
- description: found.description,
324
- example: found.example,
325
- examples: found.examples,
326
- ...Object.fromEntries(
327
- Object.entries(found).filter(
328
- ([key, value]) =>
329
- key.startsWith("x-") && value !== undefined,
330
- ),
331
- ),
332
- },
333
- );
334
- }
335
- schema.$ref += ".Nullable";
336
- };
337
-
338
- const isNullable =
339
- (visited: Set<string>) =>
340
- (components: OpenApi.IComponents) =>
341
- (schema: OpenApi.IJsonSchema): boolean => {
342
- if (OpenApiTypeChecker.isNull(schema)) return true;
343
- else if (OpenApiTypeChecker.isReference(schema)) {
344
- if (visited.has(schema.$ref)) return false;
345
- visited.add(schema.$ref);
346
- const key: string = schema.$ref.split("/").pop()!;
347
- const next: OpenApi.IJsonSchema | undefined = components.schemas?.[key];
348
- return next ? isNullable(visited)(components)(next) : false;
349
- }
350
- return (
351
- OpenApiTypeChecker.isOneOf(schema) &&
352
- schema.oneOf.some(isNullable(visited)(components))
353
- );
354
- };
355
- }
1
+ import { OpenApi, OpenApiV3 } from "@typia/interface";
2
+
3
+ import { OpenApiTypeChecker } from "../../validators/OpenApiTypeChecker";
4
+
5
+ export namespace OpenApiV3Downgrader {
6
+ export interface IComponentsCollection {
7
+ original: OpenApi.IComponents;
8
+ downgraded: OpenApiV3.IComponents;
9
+ }
10
+
11
+ export const downgrade = (input: OpenApi.IDocument): OpenApiV3.IDocument => {
12
+ const collection: IComponentsCollection = downgradeComponents(
13
+ input.components,
14
+ );
15
+ return {
16
+ openapi: "3.0.0",
17
+ servers: input.servers,
18
+ info: input.info,
19
+ components: collection.downgraded,
20
+ paths: input.paths
21
+ ? Object.fromEntries(
22
+ Object.entries(input.paths)
23
+ .filter(([_, v]) => v !== undefined)
24
+ .map(
25
+ ([key, value]) =>
26
+ [key, downgradePathItem(collection)(value)] as const,
27
+ ),
28
+ )
29
+ : undefined,
30
+ security: input.security,
31
+ tags: input.tags,
32
+ };
33
+ };
34
+
35
+ /* -----------------------------------------------------------
36
+ OPERATORS
37
+ ----------------------------------------------------------- */
38
+ const downgradePathItem =
39
+ (collection: IComponentsCollection) =>
40
+ (pathItem: OpenApi.IPath): OpenApiV3.IPath => ({
41
+ ...(pathItem as any),
42
+ ...(pathItem.get
43
+ ? { get: downgradeOperation(collection)(pathItem.get) }
44
+ : undefined),
45
+ ...(pathItem.put
46
+ ? { put: downgradeOperation(collection)(pathItem.put) }
47
+ : undefined),
48
+ ...(pathItem.post
49
+ ? { post: downgradeOperation(collection)(pathItem.post) }
50
+ : undefined),
51
+ ...(pathItem.delete
52
+ ? { delete: downgradeOperation(collection)(pathItem.delete) }
53
+ : undefined),
54
+ ...(pathItem.options
55
+ ? { options: downgradeOperation(collection)(pathItem.options) }
56
+ : undefined),
57
+ ...(pathItem.head
58
+ ? { head: downgradeOperation(collection)(pathItem.head) }
59
+ : undefined),
60
+ ...(pathItem.patch
61
+ ? { patch: downgradeOperation(collection)(pathItem.patch) }
62
+ : undefined),
63
+ ...(pathItem.trace
64
+ ? { trace: downgradeOperation(collection)(pathItem.trace) }
65
+ : undefined),
66
+ });
67
+
68
+ const downgradeOperation =
69
+ (collection: IComponentsCollection) =>
70
+ (input: OpenApi.IOperation): OpenApiV3.IOperation => ({
71
+ ...input,
72
+ parameters: input.parameters
73
+ ? input.parameters.map(downgradeParameter(collection))
74
+ : undefined,
75
+ requestBody: input.requestBody
76
+ ? downgradeRequestBody(collection)(input.requestBody)
77
+ : undefined,
78
+ responses: input.responses
79
+ ? Object.fromEntries(
80
+ Object.entries(input.responses)
81
+ .filter(([_, v]) => v !== undefined)
82
+ .map(([key, value]) => [
83
+ key,
84
+ downgradeResponse(collection)(value),
85
+ ]),
86
+ )
87
+ : undefined,
88
+ });
89
+
90
+ const downgradeParameter =
91
+ (collection: IComponentsCollection) =>
92
+ (
93
+ input: OpenApi.IOperation.IParameter,
94
+ ): OpenApiV3.IOperation.IParameter => ({
95
+ ...input,
96
+ schema: downgradeSchema(collection)(input.schema),
97
+ });
98
+
99
+ const downgradeRequestBody =
100
+ (collection: IComponentsCollection) =>
101
+ (
102
+ input: OpenApi.IOperation.IRequestBody,
103
+ ): OpenApiV3.IOperation.IRequestBody => ({
104
+ ...input,
105
+ content: input.content
106
+ ? downgradeContent(collection)(input.content)
107
+ : undefined,
108
+ });
109
+
110
+ const downgradeResponse =
111
+ (collection: IComponentsCollection) =>
112
+ (input: OpenApi.IOperation.IResponse): OpenApiV3.IOperation.IResponse => ({
113
+ ...input,
114
+ content: input.content
115
+ ? downgradeContent(collection)(input.content)
116
+ : undefined,
117
+ headers: input.headers
118
+ ? Object.fromEntries(
119
+ Object.entries(input.headers)
120
+ .filter(([_, v]) => v !== undefined)
121
+ .map(([key, value]) => [
122
+ key,
123
+ {
124
+ ...value,
125
+ schema: downgradeSchema(collection)(value.schema),
126
+ },
127
+ ]),
128
+ )
129
+ : undefined,
130
+ });
131
+
132
+ const downgradeContent =
133
+ (collection: IComponentsCollection) =>
134
+ (
135
+ record: OpenApi.IOperation.IContent,
136
+ ): Record<string, OpenApiV3.IOperation.IMediaType> =>
137
+ Object.fromEntries(
138
+ Object.entries(record)
139
+ .filter(([_, v]) => v !== undefined)
140
+ .map(
141
+ ([key, value]) =>
142
+ [
143
+ key,
144
+ {
145
+ ...value,
146
+ schema: value?.schema
147
+ ? downgradeSchema(collection)(value.schema)
148
+ : undefined,
149
+ },
150
+ ] as const,
151
+ ),
152
+ );
153
+
154
+ /* -----------------------------------------------------------
155
+ DEFINITIONS
156
+ ----------------------------------------------------------- */
157
+ export const downgradeComponents = (
158
+ input: OpenApi.IComponents,
159
+ ): IComponentsCollection => {
160
+ const collection: IComponentsCollection = {
161
+ original: input,
162
+ downgraded: {
163
+ securitySchemes: input.securitySchemes,
164
+ },
165
+ };
166
+ if (input.schemas) {
167
+ collection.downgraded.schemas = {};
168
+ for (const [key, value] of Object.entries(input.schemas))
169
+ if (value !== undefined)
170
+ collection.downgraded.schemas[key] =
171
+ downgradeSchema(collection)(value);
172
+ }
173
+ return collection;
174
+ };
175
+
176
+ export const downgradeSchema =
177
+ (collection: IComponentsCollection) =>
178
+ (input: OpenApi.IJsonSchema): OpenApiV3.IJsonSchema => {
179
+ const nullable: boolean = isNullable(new Set())(collection.original)(
180
+ input,
181
+ );
182
+ const union: OpenApiV3.IJsonSchema[] = [];
183
+ const attribute: OpenApiV3.IJsonSchema.__IAttribute = {
184
+ title: input.title,
185
+ description: input.description,
186
+ deprecated: input.deprecated,
187
+ readOnly: input.readOnly,
188
+ writeOnly: input.writeOnly,
189
+ example: input.example,
190
+ examples: input.examples,
191
+ ...Object.fromEntries(
192
+ Object.entries(input).filter(
193
+ ([key, value]) => key.startsWith("x-") && value !== undefined,
194
+ ),
195
+ ),
196
+ };
197
+ const visit = (schema: OpenApi.IJsonSchema): void => {
198
+ if (OpenApiTypeChecker.isBoolean(schema))
199
+ union.push({ type: "boolean" });
200
+ else if (
201
+ OpenApiTypeChecker.isBoolean(schema) ||
202
+ OpenApiTypeChecker.isInteger(schema) ||
203
+ OpenApiTypeChecker.isNumber(schema) ||
204
+ OpenApiTypeChecker.isString(schema) ||
205
+ OpenApiTypeChecker.isReference(schema)
206
+ )
207
+ union.push({ ...schema });
208
+ else if (OpenApiTypeChecker.isArray(schema))
209
+ union.push({
210
+ ...schema,
211
+ items: downgradeSchema(collection)(schema.items),
212
+ });
213
+ else if (OpenApiTypeChecker.isTuple(schema))
214
+ union.push({
215
+ ...schema,
216
+ items: ((): OpenApiV3.IJsonSchema => {
217
+ if (schema.additionalItems === true) return {};
218
+ const elements = [
219
+ ...schema.prefixItems,
220
+ ...(typeof schema.additionalItems === "object"
221
+ ? [downgradeSchema(collection)(schema.additionalItems)]
222
+ : []),
223
+ ];
224
+ if (elements.length === 0) return {};
225
+ return {
226
+ oneOf: elements.map(downgradeSchema(collection) as any),
227
+ };
228
+ })(),
229
+ minItems: schema.prefixItems.length,
230
+ maxItems:
231
+ !!schema.additionalItems === true
232
+ ? undefined
233
+ : schema.prefixItems.length,
234
+ ...{
235
+ prefixItems: undefined,
236
+ additionalItems: undefined,
237
+ },
238
+ });
239
+ else if (OpenApiTypeChecker.isObject(schema))
240
+ union.push({
241
+ ...schema,
242
+ properties: schema.properties
243
+ ? Object.fromEntries(
244
+ Object.entries(schema.properties)
245
+ .filter(([_, v]) => v !== undefined)
246
+ .map(([key, value]) => [
247
+ key,
248
+ downgradeSchema(collection)(value),
249
+ ]),
250
+ )
251
+ : undefined,
252
+ additionalProperties:
253
+ typeof schema.additionalProperties === "object"
254
+ ? downgradeSchema(collection)(schema.additionalProperties)
255
+ : schema.additionalProperties,
256
+ required: schema.required,
257
+ });
258
+ else if (OpenApiTypeChecker.isOneOf(schema))
259
+ schema.oneOf.forEach(visit);
260
+ };
261
+ const visitConstant = (schema: OpenApi.IJsonSchema): void => {
262
+ const insert = (value: any): void => {
263
+ const matched: OpenApiV3.IJsonSchema.INumber | undefined = union.find(
264
+ (u) => (u as OpenApiV3.IJsonSchema.INumber).type === typeof value,
265
+ ) as OpenApiV3.IJsonSchema.INumber | undefined;
266
+ if (matched !== undefined) {
267
+ matched.enum ??= [];
268
+ matched.enum.push(value);
269
+ } else union.push({ type: typeof value as "number", enum: [value] });
270
+ };
271
+ if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const);
272
+ else if (OpenApiTypeChecker.isOneOf(schema))
273
+ for (const u of schema.oneOf)
274
+ if (OpenApiTypeChecker.isConstant(u)) insert(u.const);
275
+ };
276
+
277
+ visit(input);
278
+ visitConstant(input);
279
+ if (nullable === true)
280
+ for (const u of union)
281
+ if (OpenApiTypeChecker.isReference(u as any))
282
+ downgradeNullableReference(new Set())(collection)(u as any);
283
+ else (u as OpenApiV3.IJsonSchema.IArray).nullable = true;
284
+ if (nullable === true && union.length === 0)
285
+ return { type: "null", ...attribute };
286
+ return {
287
+ ...(union.length === 0
288
+ ? { type: undefined }
289
+ : union.length === 1
290
+ ? { ...union[0] }
291
+ : { oneOf: union }),
292
+ ...attribute,
293
+ };
294
+ };
295
+
296
+ const downgradeNullableReference =
297
+ (visited: Set<string>) =>
298
+ (collection: IComponentsCollection) =>
299
+ (schema: OpenApiV3.IJsonSchema.IReference): void => {
300
+ const key: string = schema.$ref.split("/").pop()!;
301
+ if (key.endsWith(".Nullable")) return;
302
+
303
+ const found: OpenApi.IJsonSchema | undefined =
304
+ collection.original.schemas?.[key];
305
+ if (found === undefined) return;
306
+ else if (isNullable(visited)(collection.original)(found) === true) return;
307
+ else if (
308
+ collection.downgraded.schemas?.[`${key}.Nullable`] === undefined
309
+ ) {
310
+ collection.downgraded.schemas ??= {};
311
+ collection.downgraded.schemas[`${key}.Nullable`] = {};
312
+ collection.downgraded.schemas[`${key}.Nullable`] = downgradeSchema(
313
+ collection,
314
+ )(
315
+ OpenApiTypeChecker.isOneOf(found)
316
+ ? {
317
+ ...found,
318
+ oneOf: [...found.oneOf, { type: "null" }],
319
+ }
320
+ : {
321
+ oneOf: [found, { type: "null" }],
322
+ title: found.title,
323
+ description: found.description,
324
+ example: found.example,
325
+ examples: found.examples,
326
+ ...Object.fromEntries(
327
+ Object.entries(found).filter(
328
+ ([key, value]) =>
329
+ key.startsWith("x-") && value !== undefined,
330
+ ),
331
+ ),
332
+ },
333
+ );
334
+ }
335
+ schema.$ref += ".Nullable";
336
+ };
337
+
338
+ const isNullable =
339
+ (visited: Set<string>) =>
340
+ (components: OpenApi.IComponents) =>
341
+ (schema: OpenApi.IJsonSchema): boolean => {
342
+ if (OpenApiTypeChecker.isNull(schema)) return true;
343
+ else if (OpenApiTypeChecker.isReference(schema)) {
344
+ if (visited.has(schema.$ref)) return false;
345
+ visited.add(schema.$ref);
346
+ const key: string = schema.$ref.split("/").pop()!;
347
+ const next: OpenApi.IJsonSchema | undefined = components.schemas?.[key];
348
+ return next ? isNullable(visited)(components)(next) : false;
349
+ }
350
+ return (
351
+ OpenApiTypeChecker.isOneOf(schema) &&
352
+ schema.oneOf.some(isNullable(visited)(components))
353
+ );
354
+ };
355
+ }