@kubb/adapter-oas 5.0.0-alpha.10 → 5.0.0-alpha.12
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.
- package/dist/index.cjs +215 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +34 -26
- package/dist/index.js +215 -150
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/adapter.ts +9 -6
- package/src/constants.ts +11 -5
- package/src/parser.ts +123 -78
- package/src/types.ts +35 -19
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,31 @@ declare class Oas extends BaseOas {
|
|
|
100
100
|
}
|
|
101
101
|
//#endregion
|
|
102
102
|
//#region src/types.d.ts
|
|
103
|
+
/**
|
|
104
|
+
* Controls how various OAS constructs are mapped to Kubb AST nodes.
|
|
105
|
+
*/
|
|
106
|
+
type ParserOptions = {
|
|
107
|
+
/**
|
|
108
|
+
* How `format: 'date-time'` schemas are represented. `false` falls through to a plain string.
|
|
109
|
+
*/
|
|
110
|
+
dateType: false | 'string' | 'stringOffset' | 'stringLocal' | 'date';
|
|
111
|
+
/**
|
|
112
|
+
* Whether `type: 'integer'` and `format: 'int64'` produce `number` or `bigint` nodes.
|
|
113
|
+
*/
|
|
114
|
+
integerType?: 'number' | 'bigint';
|
|
115
|
+
/**
|
|
116
|
+
* AST type used when no schema type can be inferred.
|
|
117
|
+
*/
|
|
118
|
+
unknownType: 'any' | 'unknown' | 'void';
|
|
119
|
+
/**
|
|
120
|
+
* AST type used for completely empty schemas (`{}`).
|
|
121
|
+
*/
|
|
122
|
+
emptySchemaType: 'any' | 'unknown' | 'void';
|
|
123
|
+
/**
|
|
124
|
+
* Suffix appended to derived enum names when building property schema names.
|
|
125
|
+
*/
|
|
126
|
+
enumSuffix: 'enum' | (string & {});
|
|
127
|
+
};
|
|
103
128
|
type OasAdapterOptions = {
|
|
104
129
|
/**
|
|
105
130
|
* Validate the OpenAPI spec before parsing.
|
|
@@ -139,34 +164,16 @@ type OasAdapterOptions = {
|
|
|
139
164
|
*/
|
|
140
165
|
discriminator?: 'strict' | 'inherit';
|
|
141
166
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
167
|
+
* Enable collision detection for inline enum and schema type names.
|
|
168
|
+
* When `true` (default), full-path names are used and name collisions
|
|
169
|
+
* across schema components are automatically resolved (e.g. `OrderParamsStatusEnum`).
|
|
170
|
+
* When `false`, enum names use only the immediate property context
|
|
171
|
+
* (e.g. `ParamsStatusEnum`) matching the v4 naming conventions, with numeric
|
|
172
|
+
* suffixes for deduplication (e.g. `ParamsStatusEnum2`).
|
|
173
|
+
* @default true
|
|
144
174
|
*/
|
|
145
175
|
collisionDetection?: boolean;
|
|
146
|
-
|
|
147
|
-
* How `format: 'date-time'` schemas are represented in the AST.
|
|
148
|
-
* - `'string'` maps to a `datetime` string node.
|
|
149
|
-
* - `'date'` maps to a JavaScript `Date` node.
|
|
150
|
-
* - `false` falls through to a plain `string` node.
|
|
151
|
-
* @default 'string'
|
|
152
|
-
*/
|
|
153
|
-
dateType?: false | 'string' | 'stringOffset' | 'stringLocal' | 'date';
|
|
154
|
-
/**
|
|
155
|
-
* Whether `type: 'integer'` / `format: 'int64'` produces `number` or `bigint` nodes.
|
|
156
|
-
* @default 'number'
|
|
157
|
-
*/
|
|
158
|
-
integerType?: 'number' | 'bigint';
|
|
159
|
-
/**
|
|
160
|
-
* AST type used when no schema type can be inferred.
|
|
161
|
-
* @default 'any'
|
|
162
|
-
*/
|
|
163
|
-
unknownType?: 'any' | 'unknown' | 'void';
|
|
164
|
-
/**
|
|
165
|
-
* AST type used for completely empty schemas (`{}`).
|
|
166
|
-
* @default `unknownType`
|
|
167
|
-
*/
|
|
168
|
-
emptySchemaType?: 'any' | 'unknown' | 'void';
|
|
169
|
-
};
|
|
176
|
+
} & Partial<ParserOptions>;
|
|
170
177
|
type OasAdapterResolvedOptions = {
|
|
171
178
|
validate: boolean;
|
|
172
179
|
oasClass: OasAdapterOptions['oasClass'];
|
|
@@ -179,6 +186,7 @@ type OasAdapterResolvedOptions = {
|
|
|
179
186
|
integerType: NonNullable<OasAdapterOptions['integerType']>;
|
|
180
187
|
unknownType: NonNullable<OasAdapterOptions['unknownType']>;
|
|
181
188
|
emptySchemaType: NonNullable<OasAdapterOptions['emptySchemaType']>;
|
|
189
|
+
enumSuffix: OasAdapterOptions['enumSuffix'];
|
|
182
190
|
/**
|
|
183
191
|
* Map from original `$ref` paths to their collision-resolved schema names.
|
|
184
192
|
* Populated by the adapter after each `parse()` call.
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,110 @@ import swagger2openapi from "swagger2openapi";
|
|
|
11
11
|
import jsonpointer from "jsonpointer";
|
|
12
12
|
import BaseOas from "oas";
|
|
13
13
|
import { matchesMimeType } from "oas/utils";
|
|
14
|
+
//#region src/constants.ts
|
|
15
|
+
/**
|
|
16
|
+
* Default values for all `Options` fields.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_PARSER_OPTIONS = {
|
|
19
|
+
dateType: "string",
|
|
20
|
+
integerType: "number",
|
|
21
|
+
unknownType: "any",
|
|
22
|
+
emptySchemaType: "any",
|
|
23
|
+
enumSuffix: "enum"
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* OpenAPI version string written into merged document stubs.
|
|
27
|
+
*/
|
|
28
|
+
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
29
|
+
/**
|
|
30
|
+
* Fallback `info.title` used when merging multiple API documents.
|
|
31
|
+
*/
|
|
32
|
+
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
33
|
+
/**
|
|
34
|
+
* Fallback `info.version` used when merging multiple API documents.
|
|
35
|
+
*/
|
|
36
|
+
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
37
|
+
/**
|
|
38
|
+
* JSON Schema keywords that indicate structural composition.
|
|
39
|
+
* A schema fragment containing any of these keys must not be inlined into its
|
|
40
|
+
* parent during `allOf` flattening — it carries semantic meaning of its own.
|
|
41
|
+
*/
|
|
42
|
+
const structuralKeys = new Set([
|
|
43
|
+
"properties",
|
|
44
|
+
"items",
|
|
45
|
+
"additionalProperties",
|
|
46
|
+
"oneOf",
|
|
47
|
+
"anyOf",
|
|
48
|
+
"allOf",
|
|
49
|
+
"not"
|
|
50
|
+
]);
|
|
51
|
+
/**
|
|
52
|
+
* Maps OAS/JSON Schema `format` strings to their Kubb `SchemaType` equivalents.
|
|
53
|
+
*
|
|
54
|
+
* Only formats that need a type different from the raw OAS `type` are listed.
|
|
55
|
+
* `int64`, `date-time`, `date`, and `time` are handled separately because their
|
|
56
|
+
* mapping depends on runtime parser options.
|
|
57
|
+
*
|
|
58
|
+
* Note: `ipv4`, `ipv6`, and `hostname` map to `'url'` — the closest supported
|
|
59
|
+
* scalar type in the Kubb AST, even though these are not strictly URLs.
|
|
60
|
+
*/
|
|
61
|
+
const formatMap = {
|
|
62
|
+
uuid: "uuid",
|
|
63
|
+
email: "email",
|
|
64
|
+
"idn-email": "email",
|
|
65
|
+
uri: "url",
|
|
66
|
+
"uri-reference": "url",
|
|
67
|
+
url: "url",
|
|
68
|
+
ipv4: "url",
|
|
69
|
+
ipv6: "url",
|
|
70
|
+
hostname: "url",
|
|
71
|
+
"idn-hostname": "url",
|
|
72
|
+
binary: "blob",
|
|
73
|
+
byte: "blob",
|
|
74
|
+
int32: "integer",
|
|
75
|
+
float: "number",
|
|
76
|
+
double: "number"
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Exhaustive list of media types that Kubb recognizes.
|
|
80
|
+
*/
|
|
81
|
+
const knownMediaTypes = new Set([
|
|
82
|
+
"application/json",
|
|
83
|
+
"application/xml",
|
|
84
|
+
"application/x-www-form-urlencoded",
|
|
85
|
+
"application/octet-stream",
|
|
86
|
+
"application/pdf",
|
|
87
|
+
"application/zip",
|
|
88
|
+
"application/graphql",
|
|
89
|
+
"multipart/form-data",
|
|
90
|
+
"text/plain",
|
|
91
|
+
"text/html",
|
|
92
|
+
"text/csv",
|
|
93
|
+
"text/xml",
|
|
94
|
+
"image/png",
|
|
95
|
+
"image/jpeg",
|
|
96
|
+
"image/gif",
|
|
97
|
+
"image/webp",
|
|
98
|
+
"image/svg+xml",
|
|
99
|
+
"audio/mpeg",
|
|
100
|
+
"video/mp4"
|
|
101
|
+
]);
|
|
102
|
+
/**
|
|
103
|
+
* Vendor extension keys used to attach human-readable labels to enum values.
|
|
104
|
+
* Checked in priority order: the first key found wins.
|
|
105
|
+
*/
|
|
106
|
+
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
107
|
+
/**
|
|
108
|
+
* Scalar primitive schema types used for union member simplification.
|
|
109
|
+
*/
|
|
110
|
+
const SCALAR_PRIMITIVE_TYPES = new Set([
|
|
111
|
+
"string",
|
|
112
|
+
"number",
|
|
113
|
+
"integer",
|
|
114
|
+
"bigint",
|
|
115
|
+
"boolean"
|
|
116
|
+
]);
|
|
117
|
+
//#endregion
|
|
14
118
|
//#region src/oas/resolveServerUrl.ts
|
|
15
119
|
/**
|
|
16
120
|
* Resolves an OpenAPI server URL by substituting `{variable}` placeholders.
|
|
@@ -53,9 +157,12 @@ function toCamelOrPascal(text, pascal) {
|
|
|
53
157
|
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
54
158
|
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
55
159
|
* Segments are joined with `/` to form a file path.
|
|
160
|
+
*
|
|
161
|
+
* Only splits on dots followed by a letter so that version numbers
|
|
162
|
+
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
56
163
|
*/
|
|
57
164
|
function applyToFileParts(text, transformPart) {
|
|
58
|
-
const parts = text.split(
|
|
165
|
+
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
59
166
|
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
60
167
|
}
|
|
61
168
|
/**
|
|
@@ -89,6 +196,27 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
89
196
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
90
197
|
}
|
|
91
198
|
//#endregion
|
|
199
|
+
//#region ../../internals/utils/src/names.ts
|
|
200
|
+
/**
|
|
201
|
+
* Returns a unique name by appending an incrementing numeric suffix when the name has already been used.
|
|
202
|
+
* Mutates `data` in-place as a usage counter so subsequent calls remain consistent.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* const seen: Record<string, number> = {}
|
|
206
|
+
* getUniqueName('Foo', seen) // 'Foo'
|
|
207
|
+
* getUniqueName('Foo', seen) // 'Foo2'
|
|
208
|
+
* getUniqueName('Foo', seen) // 'Foo3'
|
|
209
|
+
*/
|
|
210
|
+
function getUniqueName(originalName, data) {
|
|
211
|
+
let used = data[originalName] || 0;
|
|
212
|
+
if (used) {
|
|
213
|
+
data[originalName] = ++used;
|
|
214
|
+
originalName += used;
|
|
215
|
+
}
|
|
216
|
+
data[originalName] = 1;
|
|
217
|
+
return originalName;
|
|
218
|
+
}
|
|
219
|
+
//#endregion
|
|
92
220
|
//#region ../../internals/utils/src/reserved.ts
|
|
93
221
|
/**
|
|
94
222
|
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
@@ -205,100 +333,6 @@ var URLPath = class {
|
|
|
205
333
|
}
|
|
206
334
|
};
|
|
207
335
|
//#endregion
|
|
208
|
-
//#region src/constants.ts
|
|
209
|
-
/**
|
|
210
|
-
* OpenAPI version string written into merged document stubs.
|
|
211
|
-
*/
|
|
212
|
-
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
213
|
-
/**
|
|
214
|
-
* Fallback `info.title` used when merging multiple API documents.
|
|
215
|
-
*/
|
|
216
|
-
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
217
|
-
/**
|
|
218
|
-
* Fallback `info.version` used when merging multiple API documents.
|
|
219
|
-
*/
|
|
220
|
-
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
221
|
-
/**
|
|
222
|
-
* JSON Schema keywords that indicate structural composition.
|
|
223
|
-
* A schema fragment containing any of these keys must not be inlined into its
|
|
224
|
-
* parent during `allOf` flattening — it carries semantic meaning of its own.
|
|
225
|
-
*/
|
|
226
|
-
const structuralKeys = new Set([
|
|
227
|
-
"properties",
|
|
228
|
-
"items",
|
|
229
|
-
"additionalProperties",
|
|
230
|
-
"oneOf",
|
|
231
|
-
"anyOf",
|
|
232
|
-
"allOf",
|
|
233
|
-
"not"
|
|
234
|
-
]);
|
|
235
|
-
/**
|
|
236
|
-
* Maps OAS/JSON Schema `format` strings to their Kubb `SchemaType` equivalents.
|
|
237
|
-
*
|
|
238
|
-
* Only formats that need a type different from the raw OAS `type` are listed.
|
|
239
|
-
* `int64`, `date-time`, `date`, and `time` are handled separately because their
|
|
240
|
-
* mapping depends on runtime parser options.
|
|
241
|
-
*
|
|
242
|
-
* Note: `ipv4`, `ipv6`, and `hostname` map to `'url'` — the closest supported
|
|
243
|
-
* scalar type in the Kubb AST, even though these are not strictly URLs.
|
|
244
|
-
*/
|
|
245
|
-
const formatMap = {
|
|
246
|
-
uuid: "uuid",
|
|
247
|
-
email: "email",
|
|
248
|
-
"idn-email": "email",
|
|
249
|
-
uri: "url",
|
|
250
|
-
"uri-reference": "url",
|
|
251
|
-
url: "url",
|
|
252
|
-
ipv4: "url",
|
|
253
|
-
ipv6: "url",
|
|
254
|
-
hostname: "url",
|
|
255
|
-
"idn-hostname": "url",
|
|
256
|
-
binary: "blob",
|
|
257
|
-
byte: "blob",
|
|
258
|
-
int32: "integer",
|
|
259
|
-
float: "number",
|
|
260
|
-
double: "number"
|
|
261
|
-
};
|
|
262
|
-
/**
|
|
263
|
-
* Exhaustive list of media types that Kubb recognizes.
|
|
264
|
-
*/
|
|
265
|
-
const knownMediaTypes = new Set([
|
|
266
|
-
"application/json",
|
|
267
|
-
"application/xml",
|
|
268
|
-
"application/x-www-form-urlencoded",
|
|
269
|
-
"application/octet-stream",
|
|
270
|
-
"application/pdf",
|
|
271
|
-
"application/zip",
|
|
272
|
-
"application/graphql",
|
|
273
|
-
"multipart/form-data",
|
|
274
|
-
"text/plain",
|
|
275
|
-
"text/html",
|
|
276
|
-
"text/csv",
|
|
277
|
-
"text/xml",
|
|
278
|
-
"image/png",
|
|
279
|
-
"image/jpeg",
|
|
280
|
-
"image/gif",
|
|
281
|
-
"image/webp",
|
|
282
|
-
"image/svg+xml",
|
|
283
|
-
"audio/mpeg",
|
|
284
|
-
"video/mp4"
|
|
285
|
-
]);
|
|
286
|
-
/**
|
|
287
|
-
* Vendor extension keys used to attach human-readable labels to enum values.
|
|
288
|
-
* Checked in priority order: the first key found wins.
|
|
289
|
-
*/
|
|
290
|
-
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
291
|
-
/**
|
|
292
|
-
* Scalar primitive schema types used for union member simplification.
|
|
293
|
-
*/
|
|
294
|
-
const SCALAR_PRIMITIVE_TYPES = new Set([
|
|
295
|
-
"string",
|
|
296
|
-
"number",
|
|
297
|
-
"integer",
|
|
298
|
-
"bigint",
|
|
299
|
-
"boolean"
|
|
300
|
-
]);
|
|
301
|
-
//#endregion
|
|
302
336
|
//#region src/oas/Oas.ts
|
|
303
337
|
/**
|
|
304
338
|
* Prefix used to create synthetic `$ref` values for anonymous (inline) discriminator schemas.
|
|
@@ -1052,16 +1086,6 @@ function getImports({ node, nameMapping, resolve }) {
|
|
|
1052
1086
|
//#endregion
|
|
1053
1087
|
//#region src/parser.ts
|
|
1054
1088
|
/**
|
|
1055
|
-
* Default values for all `Options` fields.
|
|
1056
|
-
*/
|
|
1057
|
-
const DEFAULT_OPTIONS = {
|
|
1058
|
-
dateType: "string",
|
|
1059
|
-
integerType: "number",
|
|
1060
|
-
unknownType: "any",
|
|
1061
|
-
emptySchemaType: "any",
|
|
1062
|
-
enumSuffix: "enum"
|
|
1063
|
-
};
|
|
1064
|
-
/**
|
|
1065
1089
|
* Looks up the Kubb `SchemaType` for a given OAS `format` string.
|
|
1066
1090
|
* Returns `undefined` for formats not in `formatMap` (e.g. `int64`, `date-time`),
|
|
1067
1091
|
* which are handled separately because their output depends on parser options.
|
|
@@ -1110,6 +1134,8 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1110
1134
|
contentType,
|
|
1111
1135
|
collisionDetection
|
|
1112
1136
|
});
|
|
1137
|
+
const usedEnumNames = {};
|
|
1138
|
+
const isLegacyNaming = collisionDetection === false;
|
|
1113
1139
|
/**
|
|
1114
1140
|
* Maps an `'any' | 'unknown' | 'void'` option string to the corresponding `SchemaType` constant.
|
|
1115
1141
|
* Used for both `unknownType` (unannotated schemas) and `emptySchemaType` (empty `{}` schemas).
|
|
@@ -1156,7 +1182,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1156
1182
|
* Shared metadata fields included in every `createSchema` call.
|
|
1157
1183
|
* Centralizes the common properties so sub-handlers don't repeat them.
|
|
1158
1184
|
*/
|
|
1159
|
-
function
|
|
1185
|
+
function renderSchemaBase(schema, name, nullable, defaultValue) {
|
|
1160
1186
|
return {
|
|
1161
1187
|
name,
|
|
1162
1188
|
nullable,
|
|
@@ -1264,7 +1290,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1264
1290
|
return createSchema({
|
|
1265
1291
|
type: "intersection",
|
|
1266
1292
|
members: [...allOfMembers.slice(0, syntheticStart), ...mergeAdjacentAnonymousObjects(allOfMembers.slice(syntheticStart))],
|
|
1267
|
-
...
|
|
1293
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1268
1294
|
});
|
|
1269
1295
|
}
|
|
1270
1296
|
/**
|
|
@@ -1278,23 +1304,23 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1278
1304
|
function convertUnion({ schema, name, nullable, defaultValue, options }) {
|
|
1279
1305
|
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1280
1306
|
const unionBase = {
|
|
1281
|
-
...
|
|
1307
|
+
...renderSchemaBase(schema, name, nullable, defaultValue),
|
|
1282
1308
|
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0
|
|
1283
1309
|
};
|
|
1284
1310
|
if (schema.properties) {
|
|
1285
1311
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1286
1312
|
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1287
|
-
const
|
|
1313
|
+
const sharedPropertiesNode = convertSchema({
|
|
1314
|
+
schema: discriminator ? Object.fromEntries(Object.entries(schemaWithoutUnion).filter(([key]) => key !== "discriminator")) : schemaWithoutUnion,
|
|
1315
|
+
name: isLegacyNaming ? void 0 : name
|
|
1316
|
+
}, options);
|
|
1288
1317
|
return createSchema({
|
|
1289
1318
|
type: "union",
|
|
1290
1319
|
...unionBase,
|
|
1291
1320
|
members: unionMembers.map((s) => {
|
|
1292
1321
|
const ref = isReference(s) ? s.$ref : void 0;
|
|
1293
1322
|
const discriminatorValue = discriminator?.mapping && ref ? Object.entries(discriminator.mapping).find(([, v]) => v === ref)?.[0] : void 0;
|
|
1294
|
-
let propertiesNode =
|
|
1295
|
-
schema: memberBaseSchema,
|
|
1296
|
-
name
|
|
1297
|
-
}, options);
|
|
1323
|
+
let propertiesNode = sharedPropertiesNode;
|
|
1298
1324
|
if (discriminatorValue && discriminator) propertiesNode = applyDiscriminatorEnum({
|
|
1299
1325
|
node: propertiesNode,
|
|
1300
1326
|
propertyName: discriminator.propertyName,
|
|
@@ -1333,7 +1359,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1333
1359
|
type: "enum",
|
|
1334
1360
|
primitive: getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string"),
|
|
1335
1361
|
enumValues: [constValue],
|
|
1336
|
-
...
|
|
1362
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1337
1363
|
});
|
|
1338
1364
|
}
|
|
1339
1365
|
/**
|
|
@@ -1342,7 +1368,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1342
1368
|
* (i.e. `format: 'date-time'` with `dateType: false`).
|
|
1343
1369
|
*/
|
|
1344
1370
|
function convertFormat({ schema, name, nullable, defaultValue, mergedOptions }) {
|
|
1345
|
-
const base =
|
|
1371
|
+
const base = renderSchemaBase(schema, name, nullable, defaultValue);
|
|
1346
1372
|
if (schema.format === "int64") return createSchema({
|
|
1347
1373
|
type: mergedOptions.integerType === "bigint" ? "bigint" : "integer",
|
|
1348
1374
|
primitive: "integer",
|
|
@@ -1480,29 +1506,62 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1480
1506
|
* - not required + not nullable → `optional: true`
|
|
1481
1507
|
* - not required + nullable → `nullish: true`
|
|
1482
1508
|
*/
|
|
1509
|
+
/**
|
|
1510
|
+
* Builds the propagation name for a child property during recursive schema conversion.
|
|
1511
|
+
*
|
|
1512
|
+
* - **Legacy naming** (`isLegacyNaming`): only the immediate property key is used
|
|
1513
|
+
* (e.g. `Params` for property `params`), keeping nested names short.
|
|
1514
|
+
* - **Default naming**: the parent name is prepended so the full path is encoded
|
|
1515
|
+
* (e.g. `OrderParams` when parent is `Order`).
|
|
1516
|
+
*/
|
|
1517
|
+
function resolveChildName(parentName, propName) {
|
|
1518
|
+
if (isLegacyNaming) return pascalCase(propName);
|
|
1519
|
+
return parentName ? pascalCase([parentName, propName].join(" ")) : void 0;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Derives the final name for an enum property schema node.
|
|
1523
|
+
*
|
|
1524
|
+
* The raw name always includes the enum suffix (e.g. `StatusEnum`).
|
|
1525
|
+
* In legacy mode an additional deduplication step appends a numeric suffix
|
|
1526
|
+
* when the same name has already been used (e.g. `ParamsStatusEnum2`).
|
|
1527
|
+
*/
|
|
1528
|
+
function resolveEnumPropName(parentName, propName, enumSuffix) {
|
|
1529
|
+
const raw = pascalCase([
|
|
1530
|
+
parentName,
|
|
1531
|
+
propName,
|
|
1532
|
+
enumSuffix
|
|
1533
|
+
].filter(Boolean).join(" "));
|
|
1534
|
+
return isLegacyNaming ? getUniqueName(raw, usedEnumNames) : raw;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Given a freshly-converted property schema, returns the node with a correct
|
|
1538
|
+
* `name` attached — or stripped — depending on whether the node is a named
|
|
1539
|
+
* enum, a boolean const-enum (always inlined), or a regular schema.
|
|
1540
|
+
*/
|
|
1541
|
+
function applyEnumName(propNode, parentName, propName, enumSuffix) {
|
|
1542
|
+
const enumNode = narrowSchema(propNode, "enum");
|
|
1543
|
+
if (enumNode?.primitive === "boolean") return {
|
|
1544
|
+
...propNode,
|
|
1545
|
+
name: void 0
|
|
1546
|
+
};
|
|
1547
|
+
if (enumNode) return {
|
|
1548
|
+
...propNode,
|
|
1549
|
+
name: resolveEnumPropName(parentName, propName, enumSuffix)
|
|
1550
|
+
};
|
|
1551
|
+
return propNode;
|
|
1552
|
+
}
|
|
1483
1553
|
function convertObject({ schema, name, nullable, defaultValue, options, mergedOptions }) {
|
|
1484
1554
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1485
1555
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1486
1556
|
const resolvedPropSchema = propSchema;
|
|
1487
1557
|
const propNullable = isNullable(resolvedPropSchema);
|
|
1488
|
-
const basePropName = name ? pascalCase([name, propName].join(" ")) : void 0;
|
|
1489
|
-
const propNode = convertSchema({
|
|
1490
|
-
schema: resolvedPropSchema,
|
|
1491
|
-
name: basePropName
|
|
1492
|
-
}, options);
|
|
1493
|
-
const isEnumNode = !!narrowSchema(propNode, "enum");
|
|
1494
|
-
const derivedPropName = isEnumNode && name ? pascalCase([
|
|
1495
|
-
name,
|
|
1496
|
-
propName,
|
|
1497
|
-
mergedOptions.enumSuffix
|
|
1498
|
-
].filter(Boolean).join(" ")) : basePropName;
|
|
1499
1558
|
return createProperty({
|
|
1500
1559
|
name: propName,
|
|
1501
1560
|
schema: {
|
|
1502
|
-
...
|
|
1503
|
-
|
|
1504
|
-
name:
|
|
1505
|
-
}
|
|
1561
|
+
...applyEnumName(convertSchema({
|
|
1562
|
+
schema: resolvedPropSchema,
|
|
1563
|
+
name: resolveChildName(name, propName)
|
|
1564
|
+
}, options), name, propName, mergedOptions.enumSuffix),
|
|
1506
1565
|
nullable: propNullable || void 0,
|
|
1507
1566
|
optional: !required && !propNullable ? true : void 0,
|
|
1508
1567
|
nullish: !required && propNullable ? true : void 0
|
|
@@ -1524,7 +1583,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1524
1583
|
properties,
|
|
1525
1584
|
additionalProperties: additionalPropertiesNode,
|
|
1526
1585
|
patternProperties,
|
|
1527
|
-
...
|
|
1586
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1528
1587
|
});
|
|
1529
1588
|
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1530
1589
|
const discPropName = schema.discriminator.propertyName;
|
|
@@ -1532,11 +1591,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1532
1591
|
node: objectNode,
|
|
1533
1592
|
propertyName: discPropName,
|
|
1534
1593
|
values: Object.keys(schema.discriminator.mapping),
|
|
1535
|
-
enumName: name ?
|
|
1536
|
-
name,
|
|
1537
|
-
discPropName,
|
|
1538
|
-
mergedOptions.enumSuffix
|
|
1539
|
-
].filter(Boolean).join(" ")) : void 0
|
|
1594
|
+
enumName: name ? resolveEnumPropName(name, discPropName, mergedOptions.enumSuffix) : void 0
|
|
1540
1595
|
});
|
|
1541
1596
|
}
|
|
1542
1597
|
return objectNode;
|
|
@@ -1555,7 +1610,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1555
1610
|
rest: schema.items ? convertSchema({ schema: schema.items }, options) : void 0,
|
|
1556
1611
|
min: schema.minItems,
|
|
1557
1612
|
max: schema.maxItems,
|
|
1558
|
-
...
|
|
1613
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1559
1614
|
});
|
|
1560
1615
|
}
|
|
1561
1616
|
/**
|
|
@@ -1566,7 +1621,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1566
1621
|
*/
|
|
1567
1622
|
function convertArray({ schema, name, nullable, defaultValue, options, mergedOptions }) {
|
|
1568
1623
|
const rawItems = schema.items;
|
|
1569
|
-
const itemName = rawItems?.enum?.length && name ?
|
|
1624
|
+
const itemName = rawItems?.enum?.length && name ? resolveEnumPropName(void 0, name, mergedOptions.enumSuffix) : void 0;
|
|
1570
1625
|
return createSchema({
|
|
1571
1626
|
type: "array",
|
|
1572
1627
|
primitive: "array",
|
|
@@ -1577,7 +1632,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1577
1632
|
min: schema.minItems,
|
|
1578
1633
|
max: schema.maxItems,
|
|
1579
1634
|
unique: schema.uniqueItems ?? void 0,
|
|
1580
|
-
...
|
|
1635
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1581
1636
|
});
|
|
1582
1637
|
}
|
|
1583
1638
|
/**
|
|
@@ -1590,7 +1645,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1590
1645
|
min: schema.minLength,
|
|
1591
1646
|
max: schema.maxLength,
|
|
1592
1647
|
pattern: schema.pattern,
|
|
1593
|
-
...
|
|
1648
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1594
1649
|
});
|
|
1595
1650
|
}
|
|
1596
1651
|
/**
|
|
@@ -1604,7 +1659,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1604
1659
|
max: schema.maximum,
|
|
1605
1660
|
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1606
1661
|
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
|
|
1607
|
-
...
|
|
1662
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1608
1663
|
});
|
|
1609
1664
|
}
|
|
1610
1665
|
/**
|
|
@@ -1614,7 +1669,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1614
1669
|
return createSchema({
|
|
1615
1670
|
type: "boolean",
|
|
1616
1671
|
primitive: "boolean",
|
|
1617
|
-
...
|
|
1672
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1618
1673
|
});
|
|
1619
1674
|
}
|
|
1620
1675
|
/**
|
|
@@ -1649,7 +1704,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1649
1704
|
*/
|
|
1650
1705
|
function convertSchema({ schema, name }, options) {
|
|
1651
1706
|
const mergedOptions = {
|
|
1652
|
-
...
|
|
1707
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
1653
1708
|
...options
|
|
1654
1709
|
};
|
|
1655
1710
|
const flattenedSchema = flattenSchema(schema);
|
|
@@ -1680,7 +1735,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1680
1735
|
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return createSchema({
|
|
1681
1736
|
type: "blob",
|
|
1682
1737
|
primitive: "string",
|
|
1683
|
-
...
|
|
1738
|
+
...renderSchemaBase(schema, name, nullable, defaultValue)
|
|
1684
1739
|
});
|
|
1685
1740
|
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1686
1741
|
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
@@ -1694,7 +1749,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1694
1749
|
},
|
|
1695
1750
|
name
|
|
1696
1751
|
}, options)),
|
|
1697
|
-
...
|
|
1752
|
+
...renderSchemaBase(schema, name, arrayNullable, defaultValue)
|
|
1698
1753
|
});
|
|
1699
1754
|
}
|
|
1700
1755
|
if (!type) {
|
|
@@ -1742,18 +1797,26 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1742
1797
|
function parseOperation(options, oas, operation) {
|
|
1743
1798
|
const parameters = oas.getParameters(operation).map((param) => parseParameter(options, param));
|
|
1744
1799
|
const requestBodySchema = oas.getRequestSchema(operation);
|
|
1745
|
-
const
|
|
1800
|
+
const requestBodySchemaNode = requestBodySchema ? convertSchema({ schema: requestBodySchema }, options) : void 0;
|
|
1801
|
+
const requestBodyKeysToOmit = requestBodySchema?.properties ? Object.entries(requestBodySchema.properties).filter(([, prop]) => !isReference(prop) && prop.readOnly).map(([key]) => key) : void 0;
|
|
1802
|
+
const requestBody = requestBodySchemaNode ? {
|
|
1803
|
+
schema: requestBodySchemaNode,
|
|
1804
|
+
keysToOmit: requestBodyKeysToOmit?.length ? requestBodyKeysToOmit : void 0
|
|
1805
|
+
} : void 0;
|
|
1746
1806
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1747
1807
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1748
1808
|
const responseSchema = oas.getResponseSchema(operation, statusCode);
|
|
1749
1809
|
const schema = responseSchema && Object.keys(responseSchema).length > 0 ? convertSchema({ schema: responseSchema }, options) : createSchema({ type: resolveTypeOption(options.emptySchemaType) });
|
|
1750
1810
|
const description = typeof responseObj === "object" && responseObj !== null && !Array.isArray(responseObj) ? responseObj.description : void 0;
|
|
1751
1811
|
const rawContent = typeof responseObj === "object" && responseObj !== null && !Array.isArray(responseObj) ? responseObj.content : void 0;
|
|
1812
|
+
const mediaType = rawContent ? toMediaType(Object.keys(rawContent)[0] ?? "") : toMediaType(operation.contentType ?? "");
|
|
1813
|
+
const keysToOmit = responseSchema?.properties ? Object.entries(responseSchema.properties).filter(([, prop]) => !isReference(prop) && prop.writeOnly).map(([key]) => key) : void 0;
|
|
1752
1814
|
return createResponse({
|
|
1753
1815
|
statusCode,
|
|
1754
1816
|
description,
|
|
1755
1817
|
schema,
|
|
1756
|
-
mediaType
|
|
1818
|
+
mediaType,
|
|
1819
|
+
keysToOmit: keysToOmit?.length ? keysToOmit : void 0
|
|
1757
1820
|
});
|
|
1758
1821
|
});
|
|
1759
1822
|
return createOperation({
|
|
@@ -1775,7 +1838,7 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1775
1838
|
*/
|
|
1776
1839
|
function parse(options) {
|
|
1777
1840
|
const mergedOptions = {
|
|
1778
|
-
...
|
|
1841
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
1779
1842
|
...options
|
|
1780
1843
|
};
|
|
1781
1844
|
const schemas = Object.entries(schemaObjects).map(([name, schemaObject]) => convertSchema({
|
|
@@ -1845,7 +1908,7 @@ const adapterOasName = "oas";
|
|
|
1845
1908
|
* ```
|
|
1846
1909
|
*/
|
|
1847
1910
|
const adapterOas = createAdapter((options) => {
|
|
1848
|
-
const { validate = true, oasClass, contentType, serverIndex, serverVariables, discriminator = "strict", collisionDetection =
|
|
1911
|
+
const { validate = true, oasClass, contentType, serverIndex, serverVariables, discriminator = "strict", collisionDetection = true, dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
|
|
1849
1912
|
const nameMapping = /* @__PURE__ */ new Map();
|
|
1850
1913
|
return {
|
|
1851
1914
|
name: "oas",
|
|
@@ -1861,6 +1924,7 @@ const adapterOas = createAdapter((options) => {
|
|
|
1861
1924
|
integerType,
|
|
1862
1925
|
unknownType,
|
|
1863
1926
|
emptySchemaType,
|
|
1927
|
+
enumSuffix,
|
|
1864
1928
|
nameMapping
|
|
1865
1929
|
},
|
|
1866
1930
|
getImports(node, resolve) {
|
|
@@ -1893,7 +1957,8 @@ const adapterOas = createAdapter((options) => {
|
|
|
1893
1957
|
dateType,
|
|
1894
1958
|
integerType,
|
|
1895
1959
|
unknownType,
|
|
1896
|
-
emptySchemaType
|
|
1960
|
+
emptySchemaType,
|
|
1961
|
+
enumSuffix
|
|
1897
1962
|
}),
|
|
1898
1963
|
meta: {
|
|
1899
1964
|
title: oas.api.info?.title,
|