@kubb/adapter-oas 5.0.0-alpha.8 → 5.0.0-beta.75
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 +1299 -1224
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +522 -135
- package/dist/index.js +1290 -1218
- package/dist/index.js.map +1 -1
- package/package.json +32 -35
- package/src/adapter.ts +82 -78
- package/src/constants.ts +78 -66
- package/src/discriminator.ts +108 -0
- package/src/factory.ts +165 -0
- package/src/guards.ts +68 -0
- package/src/index.ts +17 -0
- package/src/parser.ts +566 -656
- package/src/refs.ts +59 -0
- package/src/resolvers.ts +544 -0
- package/src/types.ts +172 -59
- package/src/oas/Oas.ts +0 -616
- package/src/oas/resolveServerUrl.ts +0 -47
- package/src/oas/types.ts +0 -71
- package/src/oas/utils.ts +0 -402
- package/src/utils.ts +0 -161
package/dist/index.cjs
CHANGED
|
@@ -21,45 +21,217 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
let node_path = require("node:path");
|
|
25
|
-
node_path = __toESM(node_path);
|
|
26
|
-
let _kubb_ast = require("@kubb/ast");
|
|
27
24
|
let _kubb_core = require("@kubb/core");
|
|
25
|
+
let node_path = require("node:path");
|
|
26
|
+
node_path = __toESM(node_path, 1);
|
|
28
27
|
let _redocly_openapi_core = require("@redocly/openapi-core");
|
|
29
|
-
let _stoplight_yaml = require("@stoplight/yaml");
|
|
30
|
-
_stoplight_yaml = __toESM(_stoplight_yaml);
|
|
31
|
-
let oas_types = require("oas/types");
|
|
32
28
|
let oas_normalize = require("oas-normalize");
|
|
33
|
-
oas_normalize = __toESM(oas_normalize);
|
|
34
|
-
let remeda = require("remeda");
|
|
29
|
+
oas_normalize = __toESM(oas_normalize, 1);
|
|
35
30
|
let swagger2openapi = require("swagger2openapi");
|
|
36
|
-
swagger2openapi = __toESM(swagger2openapi);
|
|
37
|
-
let jsonpointer = require("jsonpointer");
|
|
38
|
-
jsonpointer = __toESM(jsonpointer);
|
|
31
|
+
swagger2openapi = __toESM(swagger2openapi, 1);
|
|
39
32
|
let oas = require("oas");
|
|
40
|
-
oas = __toESM(oas);
|
|
33
|
+
oas = __toESM(oas, 1);
|
|
34
|
+
let oas_types = require("oas/types");
|
|
41
35
|
let oas_utils = require("oas/utils");
|
|
42
|
-
//#region src/
|
|
36
|
+
//#region src/constants.ts
|
|
43
37
|
/**
|
|
44
|
-
*
|
|
38
|
+
* Default parser options applied when no explicit options are provided.
|
|
45
39
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* 3. Variable is left unreplaced when neither is available.
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
50
43
|
*
|
|
51
|
-
*
|
|
44
|
+
* const parser = createOasParser(oas)
|
|
45
|
+
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
46
|
+
* ```
|
|
52
47
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
const DEFAULT_PARSER_OPTIONS = {
|
|
49
|
+
dateType: "string",
|
|
50
|
+
integerType: "number",
|
|
51
|
+
unknownType: "any",
|
|
52
|
+
emptySchemaType: "any",
|
|
53
|
+
enumSuffix: "enum"
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
|
|
57
|
+
*
|
|
58
|
+
* Used when building or parsing `$ref` strings.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
66
|
+
/**
|
|
67
|
+
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
68
|
+
*/
|
|
69
|
+
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
70
|
+
/**
|
|
71
|
+
* Fallback `info.title` placed in the stub document when merging multiple API files.
|
|
72
|
+
*/
|
|
73
|
+
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
74
|
+
/**
|
|
75
|
+
* Fallback `info.version` placed in the stub document when merging multiple API files.
|
|
76
|
+
*/
|
|
77
|
+
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
78
|
+
/**
|
|
79
|
+
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
80
|
+
*
|
|
81
|
+
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
82
|
+
* intersection member rather than being merged into the parent.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
87
|
+
*
|
|
88
|
+
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
89
|
+
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
const structuralKeys = new Set([
|
|
93
|
+
"properties",
|
|
94
|
+
"items",
|
|
95
|
+
"additionalProperties",
|
|
96
|
+
"oneOf",
|
|
97
|
+
"anyOf",
|
|
98
|
+
"allOf",
|
|
99
|
+
"not"
|
|
100
|
+
]);
|
|
101
|
+
/**
|
|
102
|
+
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
103
|
+
*
|
|
104
|
+
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
105
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
106
|
+
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
|
|
107
|
+
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* import { formatMap } from '@kubb/adapter-oas'
|
|
112
|
+
*
|
|
113
|
+
* formatMap['uuid'] // 'uuid'
|
|
114
|
+
* formatMap['binary'] // 'blob'
|
|
115
|
+
* formatMap['float'] // 'number'
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
const formatMap = {
|
|
119
|
+
uuid: "uuid",
|
|
120
|
+
email: "email",
|
|
121
|
+
"idn-email": "email",
|
|
122
|
+
uri: "url",
|
|
123
|
+
"uri-reference": "url",
|
|
124
|
+
url: "url",
|
|
125
|
+
ipv4: "ipv4",
|
|
126
|
+
ipv6: "ipv6",
|
|
127
|
+
hostname: "url",
|
|
128
|
+
"idn-hostname": "url",
|
|
129
|
+
binary: "blob",
|
|
130
|
+
byte: "blob",
|
|
131
|
+
int32: "integer",
|
|
132
|
+
float: "number",
|
|
133
|
+
double: "number"
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
141
|
+
*
|
|
142
|
+
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
146
|
+
/**
|
|
147
|
+
* Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
|
|
148
|
+
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
149
|
+
*/
|
|
150
|
+
const typeOptionMap = new Map([
|
|
151
|
+
["any", _kubb_core.ast.schemaTypes.any],
|
|
152
|
+
["unknown", _kubb_core.ast.schemaTypes.unknown],
|
|
153
|
+
["void", _kubb_core.ast.schemaTypes.void]
|
|
154
|
+
]);
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/discriminator.ts
|
|
157
|
+
/**
|
|
158
|
+
* Injects discriminator enum values into child schemas so they know which value identifies them.
|
|
159
|
+
*
|
|
160
|
+
* Finds every union schema in `input.schemas` that has a `discriminatorPropertyName`, collects the
|
|
161
|
+
* enum value each union member is mapped to, then adds (or replaces) that property on the matching
|
|
162
|
+
* child object schema.
|
|
163
|
+
*
|
|
164
|
+
* Returns a new `InputNode` — the original is never mutated.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* const { root } = parseOas(document, options)
|
|
169
|
+
* const next = applyDiscriminatorInheritance(root)
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
function applyDiscriminatorInheritance(root) {
|
|
173
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
174
|
+
for (const schema of root.schemas) {
|
|
175
|
+
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
176
|
+
if (!unionNode) {
|
|
177
|
+
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
178
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
179
|
+
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
180
|
+
if (u) {
|
|
181
|
+
unionNode = u;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
187
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
188
|
+
for (const member of members) {
|
|
189
|
+
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
190
|
+
if (!intersectionNode?.members) continue;
|
|
191
|
+
let refNode;
|
|
192
|
+
let objNode;
|
|
193
|
+
for (const m of intersectionNode.members) {
|
|
194
|
+
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
195
|
+
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
196
|
+
}
|
|
197
|
+
if (!refNode?.name || !objNode) continue;
|
|
198
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
199
|
+
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : void 0;
|
|
200
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
201
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
202
|
+
if (!enumValues.length) continue;
|
|
203
|
+
const existing = childMap.get(refNode.name);
|
|
204
|
+
if (existing) existing.enumValues.push(...enumValues);
|
|
205
|
+
else childMap.set(refNode.name, {
|
|
206
|
+
propertyName: discriminatorPropertyName,
|
|
207
|
+
enumValues: [...enumValues]
|
|
208
|
+
});
|
|
209
|
+
}
|
|
61
210
|
}
|
|
62
|
-
return
|
|
211
|
+
if (childMap.size === 0) return root;
|
|
212
|
+
return _kubb_core.ast.transform(root, { schema(node, { parent }) {
|
|
213
|
+
if (parent?.kind !== "Input" || !node.name) return;
|
|
214
|
+
const entry = childMap.get(node.name);
|
|
215
|
+
if (!entry) return;
|
|
216
|
+
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
217
|
+
if (!objectNode) return;
|
|
218
|
+
const { propertyName, enumValues } = entry;
|
|
219
|
+
const enumSchema = _kubb_core.ast.createSchema({
|
|
220
|
+
type: "enum",
|
|
221
|
+
enumValues
|
|
222
|
+
});
|
|
223
|
+
const newProp = _kubb_core.ast.createProperty({
|
|
224
|
+
name: propertyName,
|
|
225
|
+
required: true,
|
|
226
|
+
schema: enumSchema
|
|
227
|
+
});
|
|
228
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
229
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
230
|
+
return {
|
|
231
|
+
...objectNode,
|
|
232
|
+
properties: newProperties
|
|
233
|
+
};
|
|
234
|
+
} });
|
|
63
235
|
}
|
|
64
236
|
//#endregion
|
|
65
237
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -81,10 +253,19 @@ function toCamelOrPascal(text, pascal) {
|
|
|
81
253
|
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
82
254
|
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
83
255
|
* Segments are joined with `/` to form a file path.
|
|
256
|
+
*
|
|
257
|
+
* Only splits on dots followed by a letter so that version numbers
|
|
258
|
+
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
259
|
+
*
|
|
260
|
+
* Empty segments are filtered before joining. They arise when the text starts with
|
|
261
|
+
* a dot followed immediately by a letter (e.g. `..Schema` splits into `['..', 'Schema']`
|
|
262
|
+
* and `'..'` transforms to an empty string). Without this filter the join would produce
|
|
263
|
+
* a leading `/`, which `path.resolve` would interpret as an absolute path, allowing
|
|
264
|
+
* generated files to escape the configured output directory.
|
|
84
265
|
*/
|
|
85
266
|
function applyToFileParts(text, transformPart) {
|
|
86
|
-
const parts = text.split(
|
|
87
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
267
|
+
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
268
|
+
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).filter(Boolean).join("/");
|
|
88
269
|
}
|
|
89
270
|
/**
|
|
90
271
|
* Converts `text` to camelCase.
|
|
@@ -117,17 +298,141 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
117
298
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
118
299
|
}
|
|
119
300
|
//#endregion
|
|
301
|
+
//#region ../../internals/utils/src/object.ts
|
|
302
|
+
/**
|
|
303
|
+
* Returns `true` when `value` is a plain (non-null, non-array) object.
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* isPlainObject({}) // true
|
|
308
|
+
* isPlainObject([]) // false
|
|
309
|
+
* isPlainObject(null) // false
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
function isPlainObject(value) {
|
|
313
|
+
return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Recursively merges `source` into `target`, combining nested plain objects.
|
|
317
|
+
* Arrays and non-object values from `source` override the corresponding values in `target`.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* mergeDeep({ a: { x: 1 } }, { a: { y: 2 } })
|
|
322
|
+
* // { a: { x: 1, y: 2 } }
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
function mergeDeep(target, source) {
|
|
326
|
+
const result = { ...target };
|
|
327
|
+
for (const key of Object.keys(source)) {
|
|
328
|
+
const sv = source[key];
|
|
329
|
+
const tv = result[key];
|
|
330
|
+
result[key] = sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv) ? mergeDeep(tv, sv) : sv;
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
120
335
|
//#region ../../internals/utils/src/reserved.ts
|
|
121
336
|
/**
|
|
337
|
+
* JavaScript and Java reserved words.
|
|
338
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
339
|
+
*/
|
|
340
|
+
const reservedWords = new Set([
|
|
341
|
+
"abstract",
|
|
342
|
+
"arguments",
|
|
343
|
+
"boolean",
|
|
344
|
+
"break",
|
|
345
|
+
"byte",
|
|
346
|
+
"case",
|
|
347
|
+
"catch",
|
|
348
|
+
"char",
|
|
349
|
+
"class",
|
|
350
|
+
"const",
|
|
351
|
+
"continue",
|
|
352
|
+
"debugger",
|
|
353
|
+
"default",
|
|
354
|
+
"delete",
|
|
355
|
+
"do",
|
|
356
|
+
"double",
|
|
357
|
+
"else",
|
|
358
|
+
"enum",
|
|
359
|
+
"eval",
|
|
360
|
+
"export",
|
|
361
|
+
"extends",
|
|
362
|
+
"false",
|
|
363
|
+
"final",
|
|
364
|
+
"finally",
|
|
365
|
+
"float",
|
|
366
|
+
"for",
|
|
367
|
+
"function",
|
|
368
|
+
"goto",
|
|
369
|
+
"if",
|
|
370
|
+
"implements",
|
|
371
|
+
"import",
|
|
372
|
+
"in",
|
|
373
|
+
"instanceof",
|
|
374
|
+
"int",
|
|
375
|
+
"interface",
|
|
376
|
+
"let",
|
|
377
|
+
"long",
|
|
378
|
+
"native",
|
|
379
|
+
"new",
|
|
380
|
+
"null",
|
|
381
|
+
"package",
|
|
382
|
+
"private",
|
|
383
|
+
"protected",
|
|
384
|
+
"public",
|
|
385
|
+
"return",
|
|
386
|
+
"short",
|
|
387
|
+
"static",
|
|
388
|
+
"super",
|
|
389
|
+
"switch",
|
|
390
|
+
"synchronized",
|
|
391
|
+
"this",
|
|
392
|
+
"throw",
|
|
393
|
+
"throws",
|
|
394
|
+
"transient",
|
|
395
|
+
"true",
|
|
396
|
+
"try",
|
|
397
|
+
"typeof",
|
|
398
|
+
"var",
|
|
399
|
+
"void",
|
|
400
|
+
"volatile",
|
|
401
|
+
"while",
|
|
402
|
+
"with",
|
|
403
|
+
"yield",
|
|
404
|
+
"Array",
|
|
405
|
+
"Date",
|
|
406
|
+
"hasOwnProperty",
|
|
407
|
+
"Infinity",
|
|
408
|
+
"isFinite",
|
|
409
|
+
"isNaN",
|
|
410
|
+
"isPrototypeOf",
|
|
411
|
+
"length",
|
|
412
|
+
"Math",
|
|
413
|
+
"name",
|
|
414
|
+
"NaN",
|
|
415
|
+
"Number",
|
|
416
|
+
"Object",
|
|
417
|
+
"prototype",
|
|
418
|
+
"String",
|
|
419
|
+
"toString",
|
|
420
|
+
"undefined",
|
|
421
|
+
"valueOf"
|
|
422
|
+
]);
|
|
423
|
+
/**
|
|
122
424
|
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* isValidVarName('status') // true
|
|
429
|
+
* isValidVarName('class') // false (reserved word)
|
|
430
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
431
|
+
* ```
|
|
123
432
|
*/
|
|
124
433
|
function isValidVarName(name) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
} catch {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return true;
|
|
434
|
+
if (!name || reservedWords.has(name)) return false;
|
|
435
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
131
436
|
}
|
|
132
437
|
//#endregion
|
|
133
438
|
//#region ../../internals/utils/src/urlPath.ts
|
|
@@ -140,18 +445,33 @@ function isValidVarName(name) {
|
|
|
140
445
|
* p.template // '`/pet/${petId}`'
|
|
141
446
|
*/
|
|
142
447
|
var URLPath = class {
|
|
143
|
-
/**
|
|
448
|
+
/**
|
|
449
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
450
|
+
*/
|
|
144
451
|
path;
|
|
145
452
|
#options;
|
|
146
453
|
constructor(path, options = {}) {
|
|
147
454
|
this.path = path;
|
|
148
455
|
this.#options = options;
|
|
149
456
|
}
|
|
150
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
457
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* ```ts
|
|
461
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
151
464
|
get URL() {
|
|
152
465
|
return this.toURLPath();
|
|
153
466
|
}
|
|
154
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
467
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* ```ts
|
|
471
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
472
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
155
475
|
get isURL() {
|
|
156
476
|
try {
|
|
157
477
|
return !!new URL(this.path).href;
|
|
@@ -169,11 +489,25 @@ var URLPath = class {
|
|
|
169
489
|
get template() {
|
|
170
490
|
return this.toTemplateString();
|
|
171
491
|
}
|
|
172
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
492
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```ts
|
|
496
|
+
* new URLPath('/pet/{petId}').object
|
|
497
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
173
500
|
get object() {
|
|
174
501
|
return this.toObject();
|
|
175
502
|
}
|
|
176
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
503
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
504
|
+
*
|
|
505
|
+
* @example
|
|
506
|
+
* ```ts
|
|
507
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
508
|
+
* new URLPath('/pet').params // undefined
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
177
511
|
get params() {
|
|
178
512
|
return this.getParams();
|
|
179
513
|
}
|
|
@@ -181,7 +515,9 @@ var URLPath = class {
|
|
|
181
515
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
182
516
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
183
517
|
}
|
|
184
|
-
/**
|
|
518
|
+
/**
|
|
519
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
520
|
+
*/
|
|
185
521
|
#eachParam(fn) {
|
|
186
522
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
187
523
|
const raw = match[1];
|
|
@@ -218,6 +554,12 @@ var URLPath = class {
|
|
|
218
554
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
219
555
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
220
556
|
* Returns `undefined` when no path parameters are found.
|
|
557
|
+
*
|
|
558
|
+
* @example
|
|
559
|
+
* ```ts
|
|
560
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
561
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
562
|
+
* ```
|
|
221
563
|
*/
|
|
222
564
|
getParams(replacer) {
|
|
223
565
|
const params = {};
|
|
@@ -227,449 +569,44 @@ var URLPath = class {
|
|
|
227
569
|
});
|
|
228
570
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
229
571
|
}
|
|
230
|
-
/** Converts the OpenAPI path to Express-style colon syntax
|
|
572
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```ts
|
|
576
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
231
579
|
toURLPath() {
|
|
232
580
|
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
233
581
|
}
|
|
234
582
|
};
|
|
235
583
|
//#endregion
|
|
236
|
-
//#region src/
|
|
584
|
+
//#region src/guards.ts
|
|
237
585
|
/**
|
|
238
|
-
*
|
|
239
|
-
*/
|
|
240
|
-
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
241
|
-
/**
|
|
242
|
-
* Fallback `info.title` used when merging multiple API documents.
|
|
243
|
-
*/
|
|
244
|
-
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
245
|
-
/**
|
|
246
|
-
* Fallback `info.version` used when merging multiple API documents.
|
|
247
|
-
*/
|
|
248
|
-
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
249
|
-
/**
|
|
250
|
-
* JSON Schema keywords that indicate structural composition.
|
|
251
|
-
* A schema fragment containing any of these keys must not be inlined into its
|
|
252
|
-
* parent during `allOf` flattening — it carries semantic meaning of its own.
|
|
253
|
-
*/
|
|
254
|
-
const structuralKeys = new Set([
|
|
255
|
-
"properties",
|
|
256
|
-
"items",
|
|
257
|
-
"additionalProperties",
|
|
258
|
-
"oneOf",
|
|
259
|
-
"anyOf",
|
|
260
|
-
"allOf",
|
|
261
|
-
"not"
|
|
262
|
-
]);
|
|
263
|
-
/**
|
|
264
|
-
* Maps OAS/JSON Schema `format` strings to their Kubb `SchemaType` equivalents.
|
|
586
|
+
* Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
|
|
265
587
|
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*/
|
|
273
|
-
const formatMap = {
|
|
274
|
-
uuid: "uuid",
|
|
275
|
-
email: "email",
|
|
276
|
-
"idn-email": "email",
|
|
277
|
-
uri: "url",
|
|
278
|
-
"uri-reference": "url",
|
|
279
|
-
url: "url",
|
|
280
|
-
ipv4: "url",
|
|
281
|
-
ipv6: "url",
|
|
282
|
-
hostname: "url",
|
|
283
|
-
"idn-hostname": "url",
|
|
284
|
-
binary: "blob",
|
|
285
|
-
byte: "blob",
|
|
286
|
-
int32: "integer",
|
|
287
|
-
float: "number",
|
|
288
|
-
double: "number"
|
|
289
|
-
};
|
|
290
|
-
/**
|
|
291
|
-
* Exhaustive list of media types that Kubb recognizes.
|
|
292
|
-
* Kept as a module-level constant to avoid re-allocating the array on every call.
|
|
293
|
-
*/
|
|
294
|
-
const knownMediaTypes = [
|
|
295
|
-
"application/json",
|
|
296
|
-
"application/xml",
|
|
297
|
-
"application/x-www-form-urlencoded",
|
|
298
|
-
"application/octet-stream",
|
|
299
|
-
"application/pdf",
|
|
300
|
-
"application/zip",
|
|
301
|
-
"application/graphql",
|
|
302
|
-
"multipart/form-data",
|
|
303
|
-
"text/plain",
|
|
304
|
-
"text/html",
|
|
305
|
-
"text/csv",
|
|
306
|
-
"text/xml",
|
|
307
|
-
"image/png",
|
|
308
|
-
"image/jpeg",
|
|
309
|
-
"image/gif",
|
|
310
|
-
"image/webp",
|
|
311
|
-
"image/svg+xml",
|
|
312
|
-
"audio/mpeg",
|
|
313
|
-
"video/mp4"
|
|
314
|
-
];
|
|
315
|
-
/**
|
|
316
|
-
* Vendor extension keys used to attach human-readable labels to enum values.
|
|
317
|
-
* Checked in priority order: the first key found wins.
|
|
318
|
-
*/
|
|
319
|
-
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
320
|
-
//#endregion
|
|
321
|
-
//#region src/oas/Oas.ts
|
|
322
|
-
/**
|
|
323
|
-
* Prefix used to create synthetic `$ref` values for anonymous (inline) discriminator schemas.
|
|
324
|
-
* The suffix is the schema index within the discriminator's `oneOf`/`anyOf` array.
|
|
325
|
-
* @example `#kubb-inline-0`
|
|
326
|
-
*/
|
|
327
|
-
const KUBB_INLINE_REF_PREFIX = "#kubb-inline-";
|
|
328
|
-
var Oas = class extends oas.default {
|
|
329
|
-
#options = { discriminator: "strict" };
|
|
330
|
-
document;
|
|
331
|
-
constructor(document) {
|
|
332
|
-
super(document, void 0);
|
|
333
|
-
this.document = document;
|
|
334
|
-
}
|
|
335
|
-
setOptions(options) {
|
|
336
|
-
this.#options = {
|
|
337
|
-
...this.#options,
|
|
338
|
-
...options
|
|
339
|
-
};
|
|
340
|
-
if (this.#options.discriminator === "inherit") this.#applyDiscriminatorInheritance();
|
|
341
|
-
}
|
|
342
|
-
get options() {
|
|
343
|
-
return this.#options;
|
|
344
|
-
}
|
|
345
|
-
get($ref) {
|
|
346
|
-
const origRef = $ref;
|
|
347
|
-
$ref = $ref.trim();
|
|
348
|
-
if ($ref === "") return null;
|
|
349
|
-
if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
350
|
-
else return null;
|
|
351
|
-
const current = jsonpointer.default.get(this.api, $ref);
|
|
352
|
-
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
353
|
-
return current;
|
|
354
|
-
}
|
|
355
|
-
getKey($ref) {
|
|
356
|
-
const key = $ref.split("/").pop();
|
|
357
|
-
return key === "" ? void 0 : key;
|
|
358
|
-
}
|
|
359
|
-
set($ref, value) {
|
|
360
|
-
$ref = $ref.trim();
|
|
361
|
-
if ($ref === "") return false;
|
|
362
|
-
if ($ref.startsWith("#")) {
|
|
363
|
-
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
364
|
-
jsonpointer.default.set(this.api, $ref, value);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
#setDiscriminator(schema) {
|
|
368
|
-
const { mapping = {}, propertyName } = schema.discriminator;
|
|
369
|
-
if (this.#options.discriminator === "inherit") Object.entries(mapping).forEach(([mappingKey, mappingValue]) => {
|
|
370
|
-
if (mappingValue) {
|
|
371
|
-
const childSchema = this.get(mappingValue);
|
|
372
|
-
if (!childSchema) return;
|
|
373
|
-
if (!childSchema.properties) childSchema.properties = {};
|
|
374
|
-
const property = childSchema.properties[propertyName];
|
|
375
|
-
if (childSchema.properties) {
|
|
376
|
-
childSchema.properties[propertyName] = {
|
|
377
|
-
...childSchema.properties ? childSchema.properties[propertyName] : {},
|
|
378
|
-
enum: [...property?.enum?.filter((value) => value !== mappingKey) ?? [], mappingKey]
|
|
379
|
-
};
|
|
380
|
-
childSchema.required = typeof childSchema.required === "boolean" ? childSchema.required : [...new Set([...childSchema.required ?? [], propertyName])];
|
|
381
|
-
this.set(mappingValue, childSchema);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
getDiscriminator(schema) {
|
|
387
|
-
if (!isDiscriminator(schema) || !schema) return null;
|
|
388
|
-
const { mapping = {}, propertyName } = schema.discriminator;
|
|
389
|
-
/**
|
|
390
|
-
* Helper to extract discriminator value from a schema.
|
|
391
|
-
* Checks in order:
|
|
392
|
-
* 1. Extension property matching propertyName (e.g., x-linode-ref-name)
|
|
393
|
-
* 2. Property with const value
|
|
394
|
-
* 3. Property with single enum value
|
|
395
|
-
* 4. Title as fallback
|
|
396
|
-
*/
|
|
397
|
-
const getDiscriminatorValue = (schema) => {
|
|
398
|
-
if (!schema) return null;
|
|
399
|
-
if (propertyName.startsWith("x-")) {
|
|
400
|
-
const extensionValue = schema[propertyName];
|
|
401
|
-
if (extensionValue && typeof extensionValue === "string") return extensionValue;
|
|
402
|
-
}
|
|
403
|
-
const propertySchema = schema.properties?.[propertyName];
|
|
404
|
-
if (propertySchema && "const" in propertySchema && propertySchema.const !== void 0) return String(propertySchema.const);
|
|
405
|
-
if (propertySchema && propertySchema.enum?.length === 1) return String(propertySchema.enum[0]);
|
|
406
|
-
return schema.title || null;
|
|
407
|
-
};
|
|
408
|
-
/**
|
|
409
|
-
* Process oneOf/anyOf items to build mapping.
|
|
410
|
-
* Handles both $ref and inline schemas.
|
|
411
|
-
*/
|
|
412
|
-
const processSchemas = (schemas, existingMapping) => {
|
|
413
|
-
schemas.forEach((schemaItem, index) => {
|
|
414
|
-
if (isReference(schemaItem)) {
|
|
415
|
-
const key = this.getKey(schemaItem.$ref);
|
|
416
|
-
try {
|
|
417
|
-
const discriminatorValue = getDiscriminatorValue(this.get(schemaItem.$ref));
|
|
418
|
-
const canAdd = key && !Object.values(existingMapping).includes(schemaItem.$ref);
|
|
419
|
-
if (canAdd && discriminatorValue) existingMapping[discriminatorValue] = schemaItem.$ref;
|
|
420
|
-
else if (canAdd) existingMapping[key] = schemaItem.$ref;
|
|
421
|
-
} catch (_error) {
|
|
422
|
-
if (key && !Object.values(existingMapping).includes(schemaItem.$ref)) existingMapping[key] = schemaItem.$ref;
|
|
423
|
-
}
|
|
424
|
-
} else {
|
|
425
|
-
const discriminatorValue = getDiscriminatorValue(schemaItem);
|
|
426
|
-
if (discriminatorValue) existingMapping[discriminatorValue] = `${KUBB_INLINE_REF_PREFIX}${index}`;
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
};
|
|
430
|
-
if (schema.oneOf) processSchemas(schema.oneOf, mapping);
|
|
431
|
-
if (schema.anyOf) processSchemas(schema.anyOf, mapping);
|
|
432
|
-
return {
|
|
433
|
-
...schema.discriminator,
|
|
434
|
-
mapping
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
dereferenceWithRef(schema) {
|
|
438
|
-
if (isReference(schema)) return {
|
|
439
|
-
...schema,
|
|
440
|
-
...this.get(schema.$ref),
|
|
441
|
-
$ref: schema.$ref
|
|
442
|
-
};
|
|
443
|
-
return schema;
|
|
444
|
-
}
|
|
445
|
-
#applyDiscriminatorInheritance() {
|
|
446
|
-
const components = this.api.components;
|
|
447
|
-
if (!components?.schemas) return;
|
|
448
|
-
const visited = /* @__PURE__ */ new WeakSet();
|
|
449
|
-
const enqueue = (value) => {
|
|
450
|
-
if (!value) return;
|
|
451
|
-
if (Array.isArray(value)) {
|
|
452
|
-
for (const item of value) enqueue(item);
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (typeof value === "object") visit(value);
|
|
456
|
-
};
|
|
457
|
-
const visit = (schema) => {
|
|
458
|
-
if (!schema || typeof schema !== "object") return;
|
|
459
|
-
if (isReference(schema)) {
|
|
460
|
-
visit(this.get(schema.$ref));
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
const schemaObject = schema;
|
|
464
|
-
if (visited.has(schemaObject)) return;
|
|
465
|
-
visited.add(schemaObject);
|
|
466
|
-
if (isDiscriminator(schemaObject)) this.#setDiscriminator(schemaObject);
|
|
467
|
-
if ("allOf" in schemaObject) enqueue(schemaObject.allOf);
|
|
468
|
-
if ("oneOf" in schemaObject) enqueue(schemaObject.oneOf);
|
|
469
|
-
if ("anyOf" in schemaObject) enqueue(schemaObject.anyOf);
|
|
470
|
-
if ("not" in schemaObject) enqueue(schemaObject.not);
|
|
471
|
-
if ("items" in schemaObject) enqueue(schemaObject.items);
|
|
472
|
-
if ("prefixItems" in schemaObject) enqueue(schemaObject.prefixItems);
|
|
473
|
-
if (schemaObject.properties) enqueue(Object.values(schemaObject.properties));
|
|
474
|
-
if (schemaObject.additionalProperties && typeof schemaObject.additionalProperties === "object") enqueue(schemaObject.additionalProperties);
|
|
475
|
-
};
|
|
476
|
-
for (const schema of Object.values(components.schemas)) visit(schema);
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Oas does not have a getResponseBody(contentType)
|
|
480
|
-
*/
|
|
481
|
-
#getResponseBodyFactory(responseBody) {
|
|
482
|
-
function hasResponseBody(res = responseBody) {
|
|
483
|
-
return !!res;
|
|
484
|
-
}
|
|
485
|
-
return (contentType) => {
|
|
486
|
-
if (!hasResponseBody(responseBody)) return false;
|
|
487
|
-
if (isReference(responseBody)) return false;
|
|
488
|
-
if (!responseBody.content) return false;
|
|
489
|
-
if (contentType) {
|
|
490
|
-
if (!(contentType in responseBody.content)) return false;
|
|
491
|
-
return responseBody.content[contentType];
|
|
492
|
-
}
|
|
493
|
-
let availableContentType;
|
|
494
|
-
const contentTypes = Object.keys(responseBody.content);
|
|
495
|
-
contentTypes.forEach((mt) => {
|
|
496
|
-
if (!availableContentType && oas_utils.matchesMimeType.json(mt)) availableContentType = mt;
|
|
497
|
-
});
|
|
498
|
-
if (!availableContentType) contentTypes.forEach((mt) => {
|
|
499
|
-
if (!availableContentType) availableContentType = mt;
|
|
500
|
-
});
|
|
501
|
-
if (availableContentType) return [
|
|
502
|
-
availableContentType,
|
|
503
|
-
responseBody.content[availableContentType],
|
|
504
|
-
...responseBody.description ? [responseBody.description] : []
|
|
505
|
-
];
|
|
506
|
-
return false;
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
getResponseSchema(operation, statusCode) {
|
|
510
|
-
if (operation.schema.responses) Object.keys(operation.schema.responses).forEach((key) => {
|
|
511
|
-
const schema = operation.schema.responses[key];
|
|
512
|
-
const $ref = isReference(schema) ? schema.$ref : void 0;
|
|
513
|
-
if (schema && $ref) operation.schema.responses[key] = this.get($ref);
|
|
514
|
-
});
|
|
515
|
-
const getResponseBody = this.#getResponseBodyFactory(operation.getResponseByStatusCode(statusCode));
|
|
516
|
-
const { contentType } = this.#options;
|
|
517
|
-
const responseBody = getResponseBody(contentType);
|
|
518
|
-
if (responseBody === false) return {};
|
|
519
|
-
const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
|
|
520
|
-
if (!schema) return {};
|
|
521
|
-
return this.dereferenceWithRef(schema);
|
|
522
|
-
}
|
|
523
|
-
getRequestSchema(operation) {
|
|
524
|
-
const { contentType } = this.#options;
|
|
525
|
-
if (operation.schema.requestBody) operation.schema.requestBody = this.dereferenceWithRef(operation.schema.requestBody);
|
|
526
|
-
const requestBody = operation.getRequestBody(contentType);
|
|
527
|
-
if (requestBody === false) return;
|
|
528
|
-
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
529
|
-
if (!schema) return;
|
|
530
|
-
return this.dereferenceWithRef(schema);
|
|
531
|
-
}
|
|
532
|
-
getParametersSchema(operation, inKey) {
|
|
533
|
-
const { contentType = operation.getContentType() } = this.#options;
|
|
534
|
-
const resolveParams = (params) => params.map((p) => this.dereferenceWithRef(p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
|
|
535
|
-
const operationParams = resolveParams(operation.schema?.parameters || []);
|
|
536
|
-
const pathItem = this.api?.paths?.[operation.path];
|
|
537
|
-
const pathLevelParams = resolveParams(pathItem && !isReference(pathItem) && pathItem.parameters ? pathItem.parameters : []);
|
|
538
|
-
const paramMap = /* @__PURE__ */ new Map();
|
|
539
|
-
for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
540
|
-
for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
541
|
-
const params = Array.from(paramMap.values()).filter((v) => v.in === inKey);
|
|
542
|
-
if (!params.length) return null;
|
|
543
|
-
return params.reduce((schema, pathParameters) => {
|
|
544
|
-
const property = pathParameters.content?.[contentType]?.schema ?? pathParameters.schema;
|
|
545
|
-
const required = typeof schema.required === "boolean" ? schema.required : [...schema.required || [], pathParameters.required ? pathParameters.name : void 0].filter(Boolean);
|
|
546
|
-
const getDefaultStyle = (location) => {
|
|
547
|
-
if (location === "query") return "form";
|
|
548
|
-
if (location === "path") return "simple";
|
|
549
|
-
return "simple";
|
|
550
|
-
};
|
|
551
|
-
const style = pathParameters.style || getDefaultStyle(inKey);
|
|
552
|
-
const explode = pathParameters.explode !== void 0 ? pathParameters.explode : style === "form";
|
|
553
|
-
if (inKey === "query" && style === "form" && explode === true && property?.type === "object" && property?.additionalProperties && !property?.properties) return {
|
|
554
|
-
...schema,
|
|
555
|
-
description: pathParameters.description || schema.description,
|
|
556
|
-
deprecated: schema.deprecated,
|
|
557
|
-
example: property.example || schema.example,
|
|
558
|
-
additionalProperties: property.additionalProperties
|
|
559
|
-
};
|
|
560
|
-
return {
|
|
561
|
-
...schema,
|
|
562
|
-
description: schema.description,
|
|
563
|
-
deprecated: schema.deprecated,
|
|
564
|
-
example: schema.example,
|
|
565
|
-
required,
|
|
566
|
-
properties: {
|
|
567
|
-
...schema.properties,
|
|
568
|
-
[pathParameters.name]: {
|
|
569
|
-
description: pathParameters.description,
|
|
570
|
-
...property
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
}, {
|
|
575
|
-
type: "object",
|
|
576
|
-
required: [],
|
|
577
|
-
properties: {}
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
async validate() {
|
|
581
|
-
return validate(this.api);
|
|
582
|
-
}
|
|
583
|
-
flattenSchema(schema) {
|
|
584
|
-
return flattenSchema(schema);
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* Get schemas from OpenAPI components (schemas, responses, requestBodies).
|
|
588
|
-
* Returns schemas in dependency order along with name mapping for collision resolution.
|
|
589
|
-
*/
|
|
590
|
-
getSchemas(options = {}) {
|
|
591
|
-
const contentType = options.contentType ?? this.#options.contentType;
|
|
592
|
-
const includes = options.includes ?? [
|
|
593
|
-
"schemas",
|
|
594
|
-
"requestBodies",
|
|
595
|
-
"responses"
|
|
596
|
-
];
|
|
597
|
-
const shouldResolveCollisions = options.collisionDetection ?? this.#options.collisionDetection ?? false;
|
|
598
|
-
const components = this.getDefinition().components;
|
|
599
|
-
const schemasWithMeta = [];
|
|
600
|
-
if (includes.includes("schemas")) {
|
|
601
|
-
const componentSchemas = components?.schemas || {};
|
|
602
|
-
for (const [name, schemaObject] of Object.entries(componentSchemas)) {
|
|
603
|
-
let schema = schemaObject;
|
|
604
|
-
if (isReference(schemaObject)) {
|
|
605
|
-
const resolved = this.get(schemaObject.$ref);
|
|
606
|
-
if (resolved && !isReference(resolved)) schema = resolved;
|
|
607
|
-
}
|
|
608
|
-
schemasWithMeta.push({
|
|
609
|
-
schema,
|
|
610
|
-
source: "schemas",
|
|
611
|
-
originalName: name
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
if (includes.includes("responses")) {
|
|
616
|
-
const responses = components?.responses || {};
|
|
617
|
-
for (const [name, response] of Object.entries(responses)) {
|
|
618
|
-
const schema = extractSchemaFromContent(response.content, contentType);
|
|
619
|
-
if (schema) {
|
|
620
|
-
let resolvedSchema = schema;
|
|
621
|
-
if (isReference(schema)) {
|
|
622
|
-
const resolved = this.get(schema.$ref);
|
|
623
|
-
if (resolved && !isReference(resolved)) resolvedSchema = resolved;
|
|
624
|
-
}
|
|
625
|
-
schemasWithMeta.push({
|
|
626
|
-
schema: resolvedSchema,
|
|
627
|
-
source: "responses",
|
|
628
|
-
originalName: name
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
if (includes.includes("requestBodies")) {
|
|
634
|
-
const requestBodies = components?.requestBodies || {};
|
|
635
|
-
for (const [name, request] of Object.entries(requestBodies)) {
|
|
636
|
-
const schema = extractSchemaFromContent(request.content, contentType);
|
|
637
|
-
if (schema) {
|
|
638
|
-
let resolvedSchema = schema;
|
|
639
|
-
if (isReference(schema)) {
|
|
640
|
-
const resolved = this.get(schema.$ref);
|
|
641
|
-
if (resolved && !isReference(resolved)) resolvedSchema = resolved;
|
|
642
|
-
}
|
|
643
|
-
schemasWithMeta.push({
|
|
644
|
-
schema: resolvedSchema,
|
|
645
|
-
source: "requestBodies",
|
|
646
|
-
originalName: name
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
const { schemas, nameMapping } = shouldResolveCollisions ? resolveCollisions(schemasWithMeta) : legacyResolve(schemasWithMeta);
|
|
652
|
-
return {
|
|
653
|
-
schemas: sortSchemas(schemas),
|
|
654
|
-
nameMapping
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
//#endregion
|
|
659
|
-
//#region src/oas/utils.ts
|
|
660
|
-
/**
|
|
661
|
-
* Narrows `doc` to a Swagger 2.0 document.
|
|
662
|
-
* Swagger 2.0 documents do not have an `openapi` version key.
|
|
588
|
+
* @example
|
|
589
|
+
* ```ts
|
|
590
|
+
* if (isOpenApiV2Document(doc)) {
|
|
591
|
+
* // doc is OpenAPIV2.Document
|
|
592
|
+
* }
|
|
593
|
+
* ```
|
|
663
594
|
*/
|
|
664
595
|
function isOpenApiV2Document(doc) {
|
|
665
|
-
return !!doc &&
|
|
596
|
+
return !!doc && isPlainObject(doc) && !("openapi" in doc);
|
|
666
597
|
}
|
|
667
598
|
/**
|
|
668
599
|
* Returns `true` when a schema should be treated as nullable.
|
|
669
600
|
*
|
|
670
|
-
*
|
|
671
|
-
* -
|
|
672
|
-
*
|
|
601
|
+
* Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
|
|
602
|
+
* `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
|
|
603
|
+
*
|
|
604
|
+
* @example
|
|
605
|
+
* ```ts
|
|
606
|
+
* isNullable({ type: 'string', nullable: true }) // true
|
|
607
|
+
* isNullable({ type: ['string', 'null'] }) // true
|
|
608
|
+
* isNullable({ type: 'string' }) // false
|
|
609
|
+
* ```
|
|
673
610
|
*/
|
|
674
611
|
function isNullable(schema) {
|
|
675
612
|
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
@@ -679,34 +616,51 @@ function isNullable(schema) {
|
|
|
679
616
|
return false;
|
|
680
617
|
}
|
|
681
618
|
/**
|
|
682
|
-
*
|
|
683
|
-
*
|
|
619
|
+
* Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
|
|
620
|
+
*
|
|
621
|
+
* @example
|
|
622
|
+
* ```ts
|
|
623
|
+
* isReference({ $ref: '#/components/schemas/Pet' }) // true
|
|
624
|
+
* isReference({ type: 'string' }) // false
|
|
625
|
+
* ```
|
|
684
626
|
*/
|
|
685
627
|
function isReference(obj) {
|
|
686
|
-
return !!obj &&
|
|
628
|
+
return !!obj && typeof obj === "object" && "$ref" in obj;
|
|
687
629
|
}
|
|
688
630
|
/**
|
|
689
|
-
* Returns `true` when `obj` is a schema
|
|
690
|
-
*
|
|
631
|
+
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```ts
|
|
635
|
+
* isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
|
|
636
|
+
* isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
|
|
637
|
+
* ```
|
|
691
638
|
*/
|
|
692
639
|
function isDiscriminator(obj) {
|
|
693
640
|
const record = obj;
|
|
694
641
|
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
695
642
|
}
|
|
643
|
+
//#endregion
|
|
644
|
+
//#region src/factory.ts
|
|
696
645
|
/**
|
|
697
|
-
* Loads
|
|
646
|
+
* Loads and dereferences an OpenAPI document, returning the raw `Document`.
|
|
698
647
|
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
*
|
|
648
|
+
* Accepts a file path string or an already-parsed document object. File paths are bundled via
|
|
649
|
+
* Redocly to resolve external `$ref`s. Swagger 2.0 documents are automatically up-converted
|
|
650
|
+
* to OpenAPI 3.0 via `swagger2openapi`.
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* ```ts
|
|
654
|
+
* const document = await parseDocument('./openapi.yaml')
|
|
655
|
+
* const document = await parse(rawDocumentObject, { canBundle: false })
|
|
656
|
+
* ```
|
|
702
657
|
*/
|
|
703
|
-
async function
|
|
704
|
-
if (typeof pathOrApi === "string" && canBundle) return
|
|
658
|
+
async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true } = {}) {
|
|
659
|
+
if (typeof pathOrApi === "string" && canBundle) return parseDocument((await (0, _redocly_openapi_core.bundle)({
|
|
705
660
|
ref: pathOrApi,
|
|
706
661
|
config: await (0, _redocly_openapi_core.loadConfig)(),
|
|
707
662
|
base: pathOrApi
|
|
708
663
|
})).bundle.parsed, {
|
|
709
|
-
oasClass,
|
|
710
664
|
canBundle,
|
|
711
665
|
enablePaths
|
|
712
666
|
});
|
|
@@ -716,26 +670,28 @@ async function parse(pathOrApi, { oasClass = Oas, canBundle = true, enablePaths
|
|
|
716
670
|
}).load();
|
|
717
671
|
if (isOpenApiV2Document(document)) {
|
|
718
672
|
const { openapi } = await swagger2openapi.default.convertObj(document, { anchors: true });
|
|
719
|
-
return
|
|
673
|
+
return openapi;
|
|
720
674
|
}
|
|
721
|
-
return
|
|
675
|
+
return document;
|
|
722
676
|
}
|
|
723
677
|
/**
|
|
724
|
-
* Deep-merges multiple OpenAPI documents into a single `
|
|
678
|
+
* Deep-merges multiple OpenAPI documents into a single `Document`.
|
|
725
679
|
*
|
|
726
|
-
* Each document is parsed independently
|
|
727
|
-
*
|
|
728
|
-
* the returned `Oas` instance is fully initialized.
|
|
680
|
+
* Each document is parsed independently then recursively merged via `mergeDeep` from `@internals/utils`.
|
|
681
|
+
* Throws when the input array is empty.
|
|
729
682
|
*
|
|
730
|
-
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```ts
|
|
685
|
+
* const document = await mergeDocuments(['./pets.yaml', './orders.yaml'])
|
|
686
|
+
* ```
|
|
731
687
|
*/
|
|
732
|
-
async function
|
|
733
|
-
const
|
|
734
|
-
|
|
688
|
+
async function mergeDocuments(pathOrApi) {
|
|
689
|
+
const documents = [];
|
|
690
|
+
for (const p of pathOrApi) documents.push(await parseDocument(p, {
|
|
735
691
|
enablePaths: false,
|
|
736
692
|
canBundle: false
|
|
737
|
-
}))
|
|
738
|
-
if (
|
|
693
|
+
}));
|
|
694
|
+
if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
|
|
739
695
|
const seed = {
|
|
740
696
|
openapi: MERGE_OPENAPI_VERSION,
|
|
741
697
|
info: {
|
|
@@ -745,82 +701,311 @@ async function merge(pathOrApi, { oasClass = Oas } = {}) {
|
|
|
745
701
|
paths: {},
|
|
746
702
|
components: { schemas: {} }
|
|
747
703
|
};
|
|
748
|
-
return
|
|
704
|
+
return parseDocument(documents.reduce((acc, current) => mergeDeep(acc, current), seed));
|
|
749
705
|
}
|
|
750
706
|
/**
|
|
751
|
-
*
|
|
707
|
+
* Creates a `Document` from an `AdapterSource`.
|
|
708
|
+
*
|
|
709
|
+
* Handles all three source types:
|
|
710
|
+
* - `{ type: 'path' }` — resolves and bundles a local file path or remote URL.
|
|
711
|
+
* - `{ type: 'paths' }` — merges multiple file paths into a single document.
|
|
712
|
+
* - `{ type: 'data' }` — parses an inline string (YAML/JSON) or raw object.
|
|
752
713
|
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
*
|
|
714
|
+
* @example
|
|
715
|
+
* ```ts
|
|
716
|
+
* const document = await parseFromConfig({ type: 'path', path: './openapi.yaml' })
|
|
717
|
+
* const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
|
|
718
|
+
* ```
|
|
757
719
|
*/
|
|
758
|
-
function parseFromConfig(
|
|
759
|
-
if ("data"
|
|
760
|
-
if (typeof
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
720
|
+
function parseFromConfig(source) {
|
|
721
|
+
if (source.type === "data") {
|
|
722
|
+
if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
|
|
723
|
+
return parseDocument(source.data, { canBundle: false });
|
|
724
|
+
}
|
|
725
|
+
if (source.type === "paths") return mergeDocuments(source.paths);
|
|
726
|
+
if (new URLPath(source.path).isURL) return parseDocument(source.path);
|
|
727
|
+
return parseDocument(node_path.default.resolve(node_path.default.dirname(source.path), source.path));
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Validates an OpenAPI document using `oas-normalize` with colorized error output.
|
|
731
|
+
*
|
|
732
|
+
* @example
|
|
733
|
+
* ```ts
|
|
734
|
+
* await validateDocument(document)
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
async function validateDocument(document, { throwOnError = false } = {}) {
|
|
738
|
+
try {
|
|
739
|
+
await new oas_normalize.default(document, {
|
|
740
|
+
enablePaths: true,
|
|
741
|
+
colorizeErrors: true
|
|
742
|
+
}).validate({ parser: { validate: { errors: { colorize: true } } } });
|
|
743
|
+
} catch (error) {
|
|
744
|
+
if (throwOnError) throw error;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region src/refs.ts
|
|
749
|
+
/**
|
|
750
|
+
* Resolves a local JSON pointer reference from a document.
|
|
751
|
+
*
|
|
752
|
+
* Accepts `#/...` refs. Returns `null` for empty or non-local refs.
|
|
753
|
+
* Throws when the pointer cannot be resolved.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```ts
|
|
757
|
+
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet') // SchemaObject | null
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
function resolveRef(document, $ref) {
|
|
761
|
+
const origRef = $ref;
|
|
762
|
+
$ref = $ref.trim();
|
|
763
|
+
if ($ref === "") return null;
|
|
764
|
+
if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
765
|
+
else return null;
|
|
766
|
+
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
767
|
+
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
768
|
+
return current;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Resolves a `$ref` object while preserving the original `$ref` field on the result.
|
|
772
|
+
*
|
|
773
|
+
* Useful for parser flows that need both dereferenced fields and pointer
|
|
774
|
+
* identity (for naming/import purposes). Non-reference values are returned as-is.
|
|
775
|
+
*
|
|
776
|
+
* @example
|
|
777
|
+
* ```ts
|
|
778
|
+
* dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
|
|
779
|
+
* // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
|
|
780
|
+
* ```
|
|
781
|
+
*/
|
|
782
|
+
function dereferenceWithRef(document, schema) {
|
|
783
|
+
if (isReference(schema)) return {
|
|
784
|
+
...schema,
|
|
785
|
+
...resolveRef(document, schema.$ref),
|
|
786
|
+
$ref: schema.$ref
|
|
787
|
+
};
|
|
788
|
+
return schema;
|
|
789
|
+
}
|
|
790
|
+
//#endregion
|
|
791
|
+
//#region src/resolvers.ts
|
|
792
|
+
/**
|
|
793
|
+
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
794
|
+
* Resolution order: `overrides[key]` → `variable.default` → left unreplaced.
|
|
795
|
+
* Throws if an override value is not in the variable's `enum` list.
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* ```ts
|
|
799
|
+
* resolveServerUrl(
|
|
800
|
+
* { url: 'https://{env}.api.example.com', variables: { env: { default: 'dev', enum: ['dev', 'prod'] } } },
|
|
801
|
+
* { env: 'prod' },
|
|
802
|
+
* )
|
|
803
|
+
* // 'https://prod.api.example.com'
|
|
804
|
+
* ```
|
|
805
|
+
*/
|
|
806
|
+
function resolveServerUrl(server, overrides) {
|
|
807
|
+
if (!server.variables) return server.url;
|
|
808
|
+
let url = server.url;
|
|
809
|
+
for (const [key, variable] of Object.entries(server.variables)) {
|
|
810
|
+
const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
|
|
811
|
+
if (value === void 0) continue;
|
|
812
|
+
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new Error(`Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`);
|
|
813
|
+
url = url.replaceAll(`{${key}}`, value);
|
|
814
|
+
}
|
|
815
|
+
return url;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Returns the Kubb `SchemaType` for a given OAS `format` string, or `null` if not found.
|
|
819
|
+
* Formats not in `formatMap` (e.g., `int64`, `date-time`) are handled separately by parser options.
|
|
820
|
+
*/
|
|
821
|
+
function getSchemaType(format) {
|
|
822
|
+
return formatMap[format] ?? null;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
|
|
826
|
+
* Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
|
|
827
|
+
*/
|
|
828
|
+
function getPrimitiveType(type) {
|
|
829
|
+
if (type === "number" || type === "integer" || type === "bigint") return type;
|
|
830
|
+
if (type === "boolean") return "boolean";
|
|
831
|
+
return "string";
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Narrows a content-type string to the `MediaType` union Kubb recognizes, or returns `null`.
|
|
835
|
+
*/
|
|
836
|
+
function getMediaType(contentType) {
|
|
837
|
+
return Object.values(_kubb_core.ast.mediaTypes).includes(contentType) ? contentType : null;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
841
|
+
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
842
|
+
* `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```ts
|
|
846
|
+
* getParameters(document, operation)
|
|
847
|
+
* // [{ name: 'petId', in: 'path', required: true, schema: { type: 'integer' } }]
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
function getParameters(document, operation) {
|
|
851
|
+
const resolveParams = (params) => params.map((p) => dereferenceWithRef(document, p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
|
|
852
|
+
const operationParams = resolveParams(operation.schema?.parameters || []);
|
|
853
|
+
const pathItem = document.paths?.[operation.path];
|
|
854
|
+
const pathLevelParams = resolveParams(pathItem && !isReference(pathItem) && pathItem.parameters ? pathItem.parameters : []);
|
|
855
|
+
const paramMap = /* @__PURE__ */ new Map();
|
|
856
|
+
for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
857
|
+
for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
858
|
+
return Array.from(paramMap.values());
|
|
859
|
+
}
|
|
860
|
+
function getResponseBody(responseBody, contentType) {
|
|
861
|
+
if (!responseBody) return false;
|
|
862
|
+
if (isReference(responseBody)) return false;
|
|
863
|
+
const body = responseBody;
|
|
864
|
+
if (!body.content) return false;
|
|
865
|
+
if (contentType) {
|
|
866
|
+
if (!(contentType in body.content)) return false;
|
|
867
|
+
return body.content[contentType];
|
|
868
|
+
}
|
|
869
|
+
let availableContentType;
|
|
870
|
+
const contentTypes = Object.keys(body.content);
|
|
871
|
+
for (const mt of contentTypes) if (oas_utils.matchesMimeType.json(mt)) {
|
|
872
|
+
availableContentType = mt;
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
if (!availableContentType) availableContentType = contentTypes[0];
|
|
876
|
+
if (availableContentType) return [
|
|
877
|
+
availableContentType,
|
|
878
|
+
body.content[availableContentType],
|
|
879
|
+
...body.description ? [body.description] : []
|
|
880
|
+
];
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Returns the response schema for a given operation and HTTP status code.
|
|
885
|
+
*
|
|
886
|
+
* Returns an empty object `{}` when no response body schema is available.
|
|
887
|
+
*
|
|
888
|
+
* @example
|
|
889
|
+
* ```ts
|
|
890
|
+
* getResponseSchema(document, operation, 200) // SchemaObject
|
|
891
|
+
* getResponseSchema(document, operation, '4XX') // {}
|
|
892
|
+
* ```
|
|
893
|
+
*/
|
|
894
|
+
function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
895
|
+
if (operation.schema.responses) {
|
|
896
|
+
const responses = operation.schema.responses;
|
|
897
|
+
for (const key in responses) {
|
|
898
|
+
const schema = responses[key];
|
|
899
|
+
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
765
900
|
}
|
|
766
901
|
}
|
|
767
|
-
|
|
768
|
-
if (
|
|
769
|
-
|
|
902
|
+
const responseBody = getResponseBody(operation.getResponseByStatusCode(statusCode), options.contentType);
|
|
903
|
+
if (responseBody === false) return {};
|
|
904
|
+
const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
|
|
905
|
+
if (!schema) return {};
|
|
906
|
+
return dereferenceWithRef(document, schema);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Returns the request body schema for an operation, or `null` when absent.
|
|
910
|
+
*
|
|
911
|
+
* @example
|
|
912
|
+
* ```ts
|
|
913
|
+
* getRequestSchema(document, operation) // SchemaObject | null
|
|
914
|
+
* ```
|
|
915
|
+
*/
|
|
916
|
+
function getRequestSchema(document, operation, options = {}) {
|
|
917
|
+
if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
|
|
918
|
+
const requestBody = operation.getRequestBody(options.contentType);
|
|
919
|
+
if (requestBody === false) return null;
|
|
920
|
+
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
921
|
+
if (!schema) return null;
|
|
922
|
+
return dereferenceWithRef(document, schema);
|
|
770
923
|
}
|
|
771
924
|
/**
|
|
772
|
-
* Flattens a
|
|
925
|
+
* Flattens a keyword-only `allOf` into its parent schema.
|
|
773
926
|
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
*
|
|
927
|
+
* Only flattens when every member is a plain fragment — no `$ref` and no structural keywords
|
|
928
|
+
* (see `structuralKeys`). Outer schema values take precedence over fragment values.
|
|
929
|
+
* Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
|
|
777
930
|
*
|
|
778
|
-
*
|
|
779
|
-
*
|
|
931
|
+
* @example
|
|
932
|
+
* ```ts
|
|
933
|
+
* flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
|
|
934
|
+
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
935
|
+
*
|
|
936
|
+
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
937
|
+
* // returned unchanged — contains a $ref
|
|
938
|
+
* ```
|
|
780
939
|
*/
|
|
940
|
+
/**
|
|
941
|
+
* Returns `true` when `fragment` carries any JSON Schema keyword that makes it
|
|
942
|
+
* structurally significant on its own (see `structuralKeys`).
|
|
943
|
+
*
|
|
944
|
+
* A fragment with a structural keyword can't be safely merged into a parent schema.
|
|
945
|
+
*/
|
|
946
|
+
function hasStructuralKeywords(fragment) {
|
|
947
|
+
for (const key in fragment) if (structuralKeys.has(key)) return true;
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
781
950
|
function flattenSchema(schema) {
|
|
782
951
|
if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (
|
|
952
|
+
const allOfFragments = schema.allOf;
|
|
953
|
+
if (allOfFragments.some((item) => (0, oas_types.isRef)(item))) return schema;
|
|
954
|
+
if (allOfFragments.some(hasStructuralKeywords)) return schema;
|
|
786
955
|
const merged = { ...schema };
|
|
787
956
|
delete merged.allOf;
|
|
788
|
-
for (const fragment of
|
|
957
|
+
for (const fragment of allOfFragments) for (const [key, value] of Object.entries(fragment)) if (merged[key] === void 0) merged[key] = value;
|
|
789
958
|
return merged;
|
|
790
959
|
}
|
|
791
960
|
/**
|
|
792
|
-
*
|
|
793
|
-
*
|
|
961
|
+
* Extracts the inline schema from a media-type `content` map.
|
|
962
|
+
*
|
|
963
|
+
* Prefers `preferredContentType` when given; otherwise uses the first key in the map.
|
|
964
|
+
* Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
|
|
965
|
+
*
|
|
966
|
+
* @example
|
|
967
|
+
* ```ts
|
|
968
|
+
* extractSchemaFromContent(operation.content, 'application/json')
|
|
969
|
+
* // SchemaObject | null
|
|
970
|
+
* ```
|
|
794
971
|
*/
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
972
|
+
function extractSchemaFromContent(content, preferredContentType) {
|
|
973
|
+
if (!content) return null;
|
|
974
|
+
const firstContentType = Object.keys(content)[0] ?? "application/json";
|
|
975
|
+
const schema = content[preferredContentType ?? firstContentType]?.schema;
|
|
976
|
+
if (schema && "$ref" in schema) return null;
|
|
977
|
+
return schema ?? null;
|
|
800
978
|
}
|
|
801
979
|
/**
|
|
802
|
-
* Walks a schema tree and collects the names of all `#/components/schemas/<name>`
|
|
803
|
-
* Used by `sortSchemas` to build the dependency graph.
|
|
980
|
+
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
804
981
|
*/
|
|
805
982
|
function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
806
983
|
if (Array.isArray(schema)) {
|
|
807
984
|
for (const item of schema) collectRefs(item, refs);
|
|
808
985
|
return refs;
|
|
809
986
|
}
|
|
810
|
-
if (schema && typeof schema === "object") for (const
|
|
811
|
-
const
|
|
812
|
-
if (
|
|
813
|
-
|
|
987
|
+
if (schema && typeof schema === "object") for (const key in schema) {
|
|
988
|
+
const value = schema[key];
|
|
989
|
+
if (key === "$ref" && typeof value === "string") {
|
|
990
|
+
if (value.startsWith("#/components/schemas/")) {
|
|
991
|
+
const name = value.slice(21);
|
|
992
|
+
if (name) refs.add(name);
|
|
993
|
+
}
|
|
994
|
+
} else collectRefs(value, refs);
|
|
995
|
+
}
|
|
814
996
|
return refs;
|
|
815
997
|
}
|
|
816
998
|
/**
|
|
817
999
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
818
1000
|
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
* referenced types before the types that depend on them.
|
|
1001
|
+
* Referenced schemas appear before the schemas that depend on them, so code generators
|
|
1002
|
+
* can emit types in the correct order. Cycles are silently skipped.
|
|
822
1003
|
*
|
|
823
|
-
*
|
|
1004
|
+
* @example
|
|
1005
|
+
* ```ts
|
|
1006
|
+
* const sorted = sortSchemas({ Order: orderSchema, Pet: petSchema })
|
|
1007
|
+
* // Pet appears before Order when Order.$ref points at Pet
|
|
1008
|
+
* ```
|
|
824
1009
|
*/
|
|
825
1010
|
function sortSchemas(schemas) {
|
|
826
1011
|
const deps = /* @__PURE__ */ new Map();
|
|
@@ -833,396 +1018,232 @@ function sortSchemas(schemas) {
|
|
|
833
1018
|
for (const child of deps.get(name) ?? []) if (deps.has(child)) visit(child, stack);
|
|
834
1019
|
stack.delete(name);
|
|
835
1020
|
visited.add(name);
|
|
836
|
-
sorted.push(name);
|
|
837
|
-
}
|
|
838
|
-
for (const name of Object.keys(schemas)) visit(name, /* @__PURE__ */ new Set());
|
|
839
|
-
const result = {};
|
|
840
|
-
for (const name of sorted) result[name] = schemas[name];
|
|
841
|
-
return result;
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Extracts the inline schema from a media-type `content` map.
|
|
845
|
-
*
|
|
846
|
-
* Prefers `preferredContentType` when provided; otherwise falls back to the
|
|
847
|
-
* first key in the map. Returns `null` when:
|
|
848
|
-
* - `content` is absent.
|
|
849
|
-
* - The target content-type has no `schema` entry.
|
|
850
|
-
* - The schema is a `$ref` (callers resolve refs separately).
|
|
851
|
-
*/
|
|
852
|
-
function extractSchemaFromContent(content, preferredContentType) {
|
|
853
|
-
if (!content) return null;
|
|
854
|
-
const firstContentType = Object.keys(content)[0] ?? "application/json";
|
|
855
|
-
const schema = content[preferredContentType ?? firstContentType]?.schema;
|
|
856
|
-
if (schema && "$ref" in schema) return null;
|
|
857
|
-
return schema ?? null;
|
|
1021
|
+
sorted.push(name);
|
|
1022
|
+
}
|
|
1023
|
+
for (const name of Object.keys(schemas)) visit(name, /* @__PURE__ */ new Set());
|
|
1024
|
+
const result = {};
|
|
1025
|
+
for (const name of sorted) result[name] = schemas[name];
|
|
1026
|
+
return result;
|
|
858
1027
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1028
|
+
const semanticSuffixes = {
|
|
1029
|
+
schemas: "Schema",
|
|
1030
|
+
responses: "Response",
|
|
1031
|
+
requestBodies: "Request"
|
|
1032
|
+
};
|
|
863
1033
|
function getSemanticSuffix(source) {
|
|
864
|
-
|
|
865
|
-
case "schemas": return "Schema";
|
|
866
|
-
case "responses": return "Response";
|
|
867
|
-
case "requestBodies": return "Request";
|
|
868
|
-
}
|
|
1034
|
+
return semanticSuffixes[source];
|
|
869
1035
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
*/
|
|
875
|
-
function legacyResolve(schemasWithMeta) {
|
|
876
|
-
const schemas = {};
|
|
877
|
-
const nameMapping = /* @__PURE__ */ new Map();
|
|
878
|
-
for (const item of schemasWithMeta) {
|
|
879
|
-
schemas[item.originalName] = item.schema;
|
|
880
|
-
nameMapping.set(`#/components/${item.source}/${item.originalName}`, item.originalName);
|
|
881
|
-
}
|
|
882
|
-
return {
|
|
883
|
-
schemas,
|
|
884
|
-
nameMapping
|
|
885
|
-
};
|
|
1036
|
+
function resolveSchemaRef(document, schema) {
|
|
1037
|
+
if (!isReference(schema)) return schema;
|
|
1038
|
+
const resolved = resolveRef(document, schema.$ref);
|
|
1039
|
+
return resolved && !isReference(resolved) ? resolved : schema;
|
|
886
1040
|
}
|
|
887
1041
|
/**
|
|
888
|
-
*
|
|
1042
|
+
* Collects component schemas from one or more sources and resolves name collisions.
|
|
1043
|
+
*
|
|
1044
|
+
* Sources default to `['schemas', 'requestBodies', 'responses']`. Returned schemas are
|
|
1045
|
+
* topologically sorted by `$ref` dependency so generators emit types in the correct order.
|
|
889
1046
|
*
|
|
890
1047
|
* When two or more schemas normalize to the same PascalCase name:
|
|
891
|
-
* -
|
|
892
|
-
* -
|
|
893
|
-
* a semantic suffix (`Schema`, `Response`, `Request`) is appended.
|
|
1048
|
+
* - Same source → numeric suffix (`2`, `3`, …).
|
|
1049
|
+
* - Different sources → semantic suffix (`Schema`, `Response`, `Request`).
|
|
894
1050
|
*
|
|
895
|
-
*
|
|
1051
|
+
* @example
|
|
1052
|
+
* ```ts
|
|
1053
|
+
* const { schemas, nameMapping } = getSchemas(document, { contentType: 'application/json' })
|
|
1054
|
+
* ```
|
|
896
1055
|
*/
|
|
897
|
-
function
|
|
898
|
-
const
|
|
899
|
-
const
|
|
1056
|
+
function getSchemas(document, { contentType }) {
|
|
1057
|
+
const components = document.components;
|
|
1058
|
+
const candidates = [...Object.entries(components?.schemas ?? {}).map(([name, schema]) => ({
|
|
1059
|
+
schema: resolveSchemaRef(document, schema),
|
|
1060
|
+
source: "schemas",
|
|
1061
|
+
originalName: name
|
|
1062
|
+
})), ...["responses", "requestBodies"].flatMap((source) => Object.entries(components?.[source] ?? {}).flatMap(([name, item]) => {
|
|
1063
|
+
const schema = extractSchemaFromContent(item.content, contentType);
|
|
1064
|
+
return schema ? [{
|
|
1065
|
+
schema: resolveSchemaRef(document, schema),
|
|
1066
|
+
source,
|
|
1067
|
+
originalName: name
|
|
1068
|
+
}] : [];
|
|
1069
|
+
}))];
|
|
900
1070
|
const normalizedNames = /* @__PURE__ */ new Map();
|
|
901
|
-
for (const item of
|
|
902
|
-
const
|
|
903
|
-
const bucket = normalizedNames.get(
|
|
1071
|
+
for (const item of candidates) {
|
|
1072
|
+
const key = pascalCase(item.originalName);
|
|
1073
|
+
const bucket = normalizedNames.get(key) ?? [];
|
|
904
1074
|
bucket.push(item);
|
|
905
|
-
normalizedNames.set(
|
|
1075
|
+
normalizedNames.set(key, bucket);
|
|
906
1076
|
}
|
|
1077
|
+
const schemas = {};
|
|
1078
|
+
const nameMapping = /* @__PURE__ */ new Map();
|
|
907
1079
|
for (const [, items] of normalizedNames) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1080
|
+
const isSingle = items.length === 1;
|
|
1081
|
+
let hasMultipleSources = false;
|
|
1082
|
+
if (!isSingle) {
|
|
1083
|
+
const firstSource = items[0].source;
|
|
1084
|
+
for (let i = 1; i < items.length; i++) if (items[i].source !== firstSource) {
|
|
1085
|
+
hasMultipleSources = true;
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
913
1088
|
}
|
|
914
|
-
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
|
|
918
|
-
});
|
|
919
|
-
else items.forEach((item) => {
|
|
920
|
-
const uniqueName = item.originalName + getSemanticSuffix(item.source);
|
|
1089
|
+
items.forEach((item, index) => {
|
|
1090
|
+
const suffix = isSingle ? "" : hasMultipleSources ? getSemanticSuffix(item.source) : index === 0 ? "" : String(index + 1);
|
|
1091
|
+
const uniqueName = item.originalName + suffix;
|
|
921
1092
|
schemas[uniqueName] = item.schema;
|
|
922
1093
|
nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
|
|
923
1094
|
});
|
|
924
1095
|
}
|
|
925
1096
|
return {
|
|
926
|
-
schemas,
|
|
1097
|
+
schemas: sortSchemas(schemas),
|
|
927
1098
|
nameMapping
|
|
928
1099
|
};
|
|
929
1100
|
}
|
|
930
|
-
//#endregion
|
|
931
|
-
//#region src/utils.ts
|
|
932
|
-
/**
|
|
933
|
-
* Extracts the schema name from a `$ref` string.
|
|
934
|
-
* For `#/components/schemas/Order` this returns `'Order'`.
|
|
935
|
-
* Falls back to the full ref string when no slash is present.
|
|
936
|
-
*/
|
|
937
|
-
function extractRefName($ref) {
|
|
938
|
-
return $ref.split("/").at(-1) ?? $ref;
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Replaces the discriminator property's schema inside an `ObjectSchemaNode`
|
|
942
|
-
* with an enum of the given `values`.
|
|
943
|
-
*
|
|
944
|
-
* - When `enumName` is provided the enum is named, which lets printers emit a
|
|
945
|
-
* standalone enum declaration + type reference (e.g. `PetTypeEnum`).
|
|
946
|
-
* - When `enumName` is omitted the enum stays anonymous, so printers inline it
|
|
947
|
-
* as a literal union (e.g. `'dog'`).
|
|
948
|
-
*
|
|
949
|
-
* Returns the node unchanged when it is not an object or lacks the target property.
|
|
950
|
-
*/
|
|
951
|
-
function applyDiscriminatorEnum({ node, propertyName, values, enumName }) {
|
|
952
|
-
if (node.type !== "object" || !node.properties?.length) return node;
|
|
953
|
-
if (!node.properties.some((prop) => prop.name === propertyName)) return node;
|
|
954
|
-
return (0, _kubb_ast.createSchema)({
|
|
955
|
-
...node,
|
|
956
|
-
properties: node.properties.map((prop) => {
|
|
957
|
-
if (prop.name !== propertyName) return prop;
|
|
958
|
-
const enumSchema = (0, _kubb_ast.createSchema)({
|
|
959
|
-
type: "enum",
|
|
960
|
-
primitive: "string",
|
|
961
|
-
enumValues: values,
|
|
962
|
-
name: enumName,
|
|
963
|
-
readOnly: prop.schema.readOnly,
|
|
964
|
-
writeOnly: prop.schema.writeOnly
|
|
965
|
-
});
|
|
966
|
-
return (0, _kubb_ast.createProperty)({
|
|
967
|
-
...prop,
|
|
968
|
-
schema: enumSchema
|
|
969
|
-
});
|
|
970
|
-
})
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
1101
|
/**
|
|
974
|
-
*
|
|
975
|
-
*
|
|
976
|
-
*
|
|
977
|
-
* Only adjacent pairs are merged — non-object or named nodes act as boundaries.
|
|
978
|
-
* This collapses patterns like `Address & { streetNumber } & { streetName }` into
|
|
979
|
-
* `Address & { streetNumber; streetName }`.
|
|
1102
|
+
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
1103
|
+
* Returns `null` when `dateType: false`, signalling the format should fall through to `string`.
|
|
980
1104
|
*/
|
|
981
|
-
function
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
if (
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
return
|
|
997
|
-
|
|
1105
|
+
function getDateType(options, format) {
|
|
1106
|
+
if (!options.dateType) return null;
|
|
1107
|
+
if (format === "date-time") {
|
|
1108
|
+
if (options.dateType === "date") return {
|
|
1109
|
+
type: "date",
|
|
1110
|
+
representation: "date"
|
|
1111
|
+
};
|
|
1112
|
+
if (options.dateType === "stringOffset") return {
|
|
1113
|
+
type: "datetime",
|
|
1114
|
+
offset: true
|
|
1115
|
+
};
|
|
1116
|
+
if (options.dateType === "stringLocal") return {
|
|
1117
|
+
type: "datetime",
|
|
1118
|
+
local: true
|
|
1119
|
+
};
|
|
1120
|
+
return {
|
|
1121
|
+
type: "datetime",
|
|
1122
|
+
offset: false
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
if (format === "date") return {
|
|
1126
|
+
type: "date",
|
|
1127
|
+
representation: options.dateType === "date" ? "date" : "string"
|
|
1128
|
+
};
|
|
1129
|
+
return {
|
|
1130
|
+
type: "time",
|
|
1131
|
+
representation: options.dateType === "date" ? "date" : "string"
|
|
1132
|
+
};
|
|
998
1133
|
}
|
|
999
1134
|
/**
|
|
1000
|
-
*
|
|
1001
|
-
* already represented by a broader scalar node in the same union.
|
|
1002
|
-
*
|
|
1003
|
-
* For example `['placed', 'approved'] | string` collapses to `string` because
|
|
1004
|
-
* `string` subsumes all string literals. `'' | string` similarly becomes `string`.
|
|
1005
|
-
*
|
|
1006
|
-
* Only scalar primitives (`string`, `number`, `integer`, `bigint`, `boolean`) are
|
|
1007
|
-
* considered — object, array, and ref members are left untouched.
|
|
1135
|
+
* Collects the shared metadata fields passed to every `createSchema` call.
|
|
1008
1136
|
*/
|
|
1009
|
-
function
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
if (!prim) return true;
|
|
1022
|
-
if (scalarPrimitives.has(prim)) return false;
|
|
1023
|
-
if ((prim === "integer" || prim === "number") && (scalarPrimitives.has("integer") || scalarPrimitives.has("number"))) return false;
|
|
1024
|
-
return true;
|
|
1025
|
-
});
|
|
1137
|
+
function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
1138
|
+
return {
|
|
1139
|
+
name,
|
|
1140
|
+
nullable,
|
|
1141
|
+
title: schema.title,
|
|
1142
|
+
description: schema.description,
|
|
1143
|
+
deprecated: schema.deprecated,
|
|
1144
|
+
readOnly: schema.readOnly,
|
|
1145
|
+
writeOnly: schema.writeOnly,
|
|
1146
|
+
default: defaultValue,
|
|
1147
|
+
example: schema.example
|
|
1148
|
+
};
|
|
1026
1149
|
}
|
|
1027
1150
|
/**
|
|
1028
|
-
*
|
|
1029
|
-
* each import. When `oas` is supplied, only `$ref`s that are resolvable in the
|
|
1030
|
-
* spec are included; omit it to skip the existence check.
|
|
1151
|
+
* Returns all request body content type keys for an operation.
|
|
1031
1152
|
*
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1153
|
+
* The requestBody is dereferenced **in-place** when it is a `$ref` — the same mutation
|
|
1154
|
+
* that `getRequestSchema` already performs — so that the returned list accurately reflects
|
|
1155
|
+
* the available content types even for referenced bodies.
|
|
1035
1156
|
*
|
|
1036
1157
|
* @example
|
|
1037
1158
|
* ```ts
|
|
1038
|
-
*
|
|
1039
|
-
*
|
|
1040
|
-
* node: schemaNode,
|
|
1041
|
-
* nameMapping: adapter.options.nameMapping,
|
|
1042
|
-
* resolve: (schemaName) => ({
|
|
1043
|
-
* name: schemaManager.getName(schemaName, { type: 'type' }),
|
|
1044
|
-
* path: schemaManager.getFile(schemaName).path,
|
|
1045
|
-
* }),
|
|
1046
|
-
* })
|
|
1159
|
+
* getRequestBodyContentTypes(document, operation)
|
|
1160
|
+
* // ['application/json', 'multipart/form-data']
|
|
1047
1161
|
* ```
|
|
1048
1162
|
*/
|
|
1049
|
-
function
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (!result) return;
|
|
1055
|
-
return {
|
|
1056
|
-
name: [result.name],
|
|
1057
|
-
path: result.path
|
|
1058
|
-
};
|
|
1059
|
-
} });
|
|
1163
|
+
function getRequestBodyContentTypes(document, operation) {
|
|
1164
|
+
if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
|
|
1165
|
+
const body = operation.schema.requestBody;
|
|
1166
|
+
if (!body) return [];
|
|
1167
|
+
return body.content ? Object.keys(body.content) : [];
|
|
1060
1168
|
}
|
|
1061
1169
|
//#endregion
|
|
1062
1170
|
//#region src/parser.ts
|
|
1063
1171
|
/**
|
|
1064
|
-
*
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
emptySchemaType: "any",
|
|
1071
|
-
enumSuffix: "enum"
|
|
1072
|
-
};
|
|
1073
|
-
/**
|
|
1074
|
-
* Looks up the Kubb `SchemaType` for a given OAS `format` string.
|
|
1075
|
-
* Returns `undefined` for formats not in `formatMap` (e.g. `int64`, `date-time`),
|
|
1076
|
-
* which are handled separately because their output depends on parser options.
|
|
1077
|
-
*/
|
|
1078
|
-
function formatToSchemaType(format) {
|
|
1079
|
-
return formatMap[format];
|
|
1080
|
-
}
|
|
1081
|
-
/**
|
|
1082
|
-
* Maps an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
|
|
1083
|
-
* Numeric types (`number`, `integer`, `bigint`) are returned unchanged;
|
|
1084
|
-
* `boolean` maps to `'boolean'`; everything else defaults to `'string'`.
|
|
1085
|
-
*/
|
|
1086
|
-
function getPrimitiveType(type) {
|
|
1087
|
-
if (type === "number" || type === "integer" || type === "bigint") return type;
|
|
1088
|
-
if (type === "boolean") return "boolean";
|
|
1089
|
-
return "string";
|
|
1090
|
-
}
|
|
1091
|
-
/**
|
|
1092
|
-
* Narrows a raw content-type string to the `MediaType` union recognized by Kubb.
|
|
1093
|
-
* Returns `undefined` for content types not present in `KNOWN_MEDIA_TYPES`.
|
|
1172
|
+
* Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
|
|
1173
|
+
*
|
|
1174
|
+
* This pattern violates the OpenAPI spec but appears in real specs. The fix moves enum values
|
|
1175
|
+
* from the array to its items sub-schema, making them valid for downstream processing.
|
|
1176
|
+
*
|
|
1177
|
+
* @note This is a defensive measure for robustness with non-compliant specs.
|
|
1094
1178
|
*/
|
|
1095
|
-
function
|
|
1096
|
-
|
|
1179
|
+
function normalizeArrayEnum(schema) {
|
|
1180
|
+
const normalizedItems = {
|
|
1181
|
+
...typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : {},
|
|
1182
|
+
enum: schema.enum
|
|
1183
|
+
};
|
|
1184
|
+
const { enum: _enum, ...schemaWithoutEnum } = schema;
|
|
1185
|
+
return {
|
|
1186
|
+
...schemaWithoutEnum,
|
|
1187
|
+
items: normalizedItems
|
|
1188
|
+
};
|
|
1097
1189
|
}
|
|
1098
1190
|
/**
|
|
1099
|
-
*
|
|
1100
|
-
* the `@kubb/ast` tree.
|
|
1101
|
-
*
|
|
1102
|
-
* Options are passed per-call to `parse` or `convertSchema` rather than
|
|
1103
|
-
* at construction time, keeping the factory lightweight.
|
|
1191
|
+
* Factory function that creates schema and operation converters for a given OpenAPI context.
|
|
1104
1192
|
*
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1193
|
+
* Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
|
|
1194
|
+
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1195
|
+
* made possible by hoisting of function declarations.
|
|
1107
1196
|
*
|
|
1108
|
-
*
|
|
1109
|
-
* be consumed by any downstream plugin (plugin-ts, plugin-zod, …).
|
|
1110
|
-
*
|
|
1111
|
-
* @example
|
|
1112
|
-
* ```ts
|
|
1113
|
-
* const parser = createOasParser(oas)
|
|
1114
|
-
* const root = parser.parse({ emptySchemaType: 'unknown' })
|
|
1115
|
-
* ```
|
|
1197
|
+
* @note Not exported; called internally by `parseOas()` and `parseSchema()`.
|
|
1116
1198
|
*/
|
|
1117
|
-
function
|
|
1118
|
-
const
|
|
1119
|
-
contentType,
|
|
1120
|
-
collisionDetection
|
|
1121
|
-
});
|
|
1199
|
+
function createSchemaParser(ctx) {
|
|
1200
|
+
const document = ctx.document;
|
|
1122
1201
|
/**
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1202
|
+
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
1203
|
+
* recursion when schemas contain circular references (e.g. `Pet → parent → Pet`).
|
|
1125
1204
|
*/
|
|
1126
|
-
|
|
1127
|
-
if (value === "any") return _kubb_ast.schemaTypes.any;
|
|
1128
|
-
if (value === "void") return _kubb_ast.schemaTypes.void;
|
|
1129
|
-
return _kubb_ast.schemaTypes.unknown;
|
|
1130
|
-
}
|
|
1131
|
-
/**
|
|
1132
|
-
* Resolves the AST type and datetime modifiers for a date/time format, honoring the `dateType` option.
|
|
1133
|
-
* Returns `undefined` when `dateType` is `false`, meaning the format should fall through to `string`.
|
|
1134
|
-
*/
|
|
1135
|
-
function getDateType(options, format) {
|
|
1136
|
-
if (!options.dateType) return;
|
|
1137
|
-
if (format === "date-time") {
|
|
1138
|
-
if (options.dateType === "date") return {
|
|
1139
|
-
type: "date",
|
|
1140
|
-
representation: "date"
|
|
1141
|
-
};
|
|
1142
|
-
if (options.dateType === "stringOffset") return {
|
|
1143
|
-
type: "datetime",
|
|
1144
|
-
offset: true
|
|
1145
|
-
};
|
|
1146
|
-
if (options.dateType === "stringLocal") return {
|
|
1147
|
-
type: "datetime",
|
|
1148
|
-
local: true
|
|
1149
|
-
};
|
|
1150
|
-
return {
|
|
1151
|
-
type: "datetime",
|
|
1152
|
-
offset: false
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
if (format === "date") return {
|
|
1156
|
-
type: "date",
|
|
1157
|
-
representation: options.dateType === "date" ? "date" : "string"
|
|
1158
|
-
};
|
|
1159
|
-
return {
|
|
1160
|
-
type: "time",
|
|
1161
|
-
representation: options.dateType === "date" ? "date" : "string"
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
/**
|
|
1165
|
-
* Shared metadata fields included in every `createSchema` call.
|
|
1166
|
-
* Centralizes the common properties so sub-handlers don't repeat them.
|
|
1167
|
-
*/
|
|
1168
|
-
function buildSchemaBase(schema, name, nullable, defaultValue) {
|
|
1169
|
-
return {
|
|
1170
|
-
name,
|
|
1171
|
-
nullable,
|
|
1172
|
-
title: schema.title,
|
|
1173
|
-
description: schema.description,
|
|
1174
|
-
deprecated: schema.deprecated,
|
|
1175
|
-
readOnly: schema.readOnly,
|
|
1176
|
-
writeOnly: schema.writeOnly,
|
|
1177
|
-
default: defaultValue,
|
|
1178
|
-
example: schema.example
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1205
|
+
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1181
1206
|
/**
|
|
1182
|
-
* Converts a `$ref` schema
|
|
1207
|
+
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1183
1208
|
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
*
|
|
1209
|
+
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
1210
|
+
* (description, readOnly, nullable, etc.) are stored directly on the ref node.
|
|
1211
|
+
* Use `syncSchemaRef(node)` in printers to get a merged view of both.
|
|
1212
|
+
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1187
1213
|
*/
|
|
1188
|
-
function convertRef({ schema, nullable, defaultValue }) {
|
|
1189
|
-
|
|
1214
|
+
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1215
|
+
let resolvedSchema;
|
|
1216
|
+
const refPath = schema.$ref;
|
|
1217
|
+
if (refPath && !resolvingRefs.has(refPath)) try {
|
|
1218
|
+
const referenced = resolveRef(document, refPath);
|
|
1219
|
+
if (referenced) {
|
|
1220
|
+
resolvingRefs.add(refPath);
|
|
1221
|
+
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1222
|
+
resolvingRefs.delete(refPath);
|
|
1223
|
+
}
|
|
1224
|
+
} catch {}
|
|
1225
|
+
return _kubb_core.ast.createSchema({
|
|
1226
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1190
1227
|
type: "ref",
|
|
1191
|
-
name: extractRefName(schema.$ref),
|
|
1228
|
+
name: _kubb_core.ast.extractRefName(schema.$ref),
|
|
1192
1229
|
ref: schema.$ref,
|
|
1193
|
-
|
|
1194
|
-
description: schema.description,
|
|
1195
|
-
deprecated: schema.deprecated,
|
|
1196
|
-
readOnly: schema.readOnly,
|
|
1197
|
-
writeOnly: schema.writeOnly,
|
|
1198
|
-
pattern: schema.type === "string" ? schema.pattern : void 0,
|
|
1199
|
-
example: schema.example,
|
|
1200
|
-
default: defaultValue
|
|
1230
|
+
schema: resolvedSchema
|
|
1201
1231
|
});
|
|
1202
1232
|
}
|
|
1203
1233
|
/**
|
|
1204
|
-
* Converts
|
|
1205
|
-
* or an `IntersectionSchemaNode` (multi-member `allOf`).
|
|
1206
|
-
*
|
|
1207
|
-
* Single-member `allOf` without sibling structural keys is the common OAS 3.0 pattern for
|
|
1208
|
-
* annotating a `$ref` or primitive with extra constraints; it is flattened to avoid
|
|
1209
|
-
* producing needless intersection wrappers.
|
|
1210
|
-
*
|
|
1211
|
-
* The flatten path is skipped when the outer schema carries structural keys that cannot be
|
|
1212
|
-
* merged into annotation fields: `properties`, `required`, or `additionalProperties`.
|
|
1213
|
-
* Those cases must become an intersection so the constraints are preserved.
|
|
1214
|
-
*
|
|
1215
|
-
* Circular references through discriminator parents are detected and skipped to prevent
|
|
1216
|
-
* infinite recursion during code generation.
|
|
1234
|
+
* Converts an `allOf` schema into a flattened node or an `IntersectionSchemaNode`.
|
|
1217
1235
|
*/
|
|
1218
|
-
function convertAllOf({ schema, name, nullable, defaultValue,
|
|
1236
|
+
function convertAllOf({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1219
1237
|
if (schema.allOf.length === 1 && !schema.properties && !(Array.isArray(schema.required) && schema.required.length) && schema.additionalProperties === void 0) {
|
|
1220
1238
|
const [memberSchema] = schema.allOf;
|
|
1221
|
-
const memberNode =
|
|
1239
|
+
const memberNode = parseSchema({
|
|
1240
|
+
schema: memberSchema,
|
|
1241
|
+
name: null
|
|
1242
|
+
}, rawOptions);
|
|
1222
1243
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1223
1244
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
1224
1245
|
const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
|
|
1225
|
-
return
|
|
1246
|
+
return _kubb_core.ast.createSchema({
|
|
1226
1247
|
...memberNodeProps,
|
|
1227
1248
|
name,
|
|
1228
1249
|
title: schema.title ?? memberNode.title,
|
|
@@ -1236,17 +1257,26 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1236
1257
|
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
|
|
1237
1258
|
});
|
|
1238
1259
|
}
|
|
1260
|
+
const filteredDiscriminantValues = [];
|
|
1239
1261
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1240
1262
|
if (!isReference(item) || !name) return true;
|
|
1241
|
-
const deref =
|
|
1263
|
+
const deref = resolveRef(document, item.$ref);
|
|
1242
1264
|
if (!deref || !isDiscriminator(deref)) return true;
|
|
1243
1265
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1244
1266
|
if (!parentUnion) return true;
|
|
1245
|
-
const childRef =
|
|
1267
|
+
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1246
1268
|
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1247
1269
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1248
|
-
|
|
1249
|
-
|
|
1270
|
+
if (inOneOf || inMapping) {
|
|
1271
|
+
const discriminatorValue = _kubb_core.ast.findDiscriminator(deref.discriminator.mapping, childRef);
|
|
1272
|
+
if (discriminatorValue) filteredDiscriminantValues.push({
|
|
1273
|
+
propertyName: deref.discriminator.propertyName,
|
|
1274
|
+
value: discriminatorValue
|
|
1275
|
+
});
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
return true;
|
|
1279
|
+
}).map((s) => parseSchema({ schema: s }, rawOptions));
|
|
1250
1280
|
const syntheticStart = allOfMembers.length;
|
|
1251
1281
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1252
1282
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
@@ -1254,106 +1284,126 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1254
1284
|
if (missingRequired.length) {
|
|
1255
1285
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1256
1286
|
if (!isReference(item)) return [item];
|
|
1257
|
-
const deref =
|
|
1287
|
+
const deref = resolveRef(document, item.$ref);
|
|
1258
1288
|
return deref && !isReference(deref) ? [deref] : [];
|
|
1259
1289
|
});
|
|
1260
1290
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1261
|
-
allOfMembers.push(
|
|
1291
|
+
allOfMembers.push(parseSchema({ schema: {
|
|
1262
1292
|
properties: { [key]: resolved.properties[key] },
|
|
1263
1293
|
required: [key]
|
|
1264
|
-
} },
|
|
1294
|
+
} }, rawOptions));
|
|
1265
1295
|
break;
|
|
1266
1296
|
}
|
|
1267
1297
|
}
|
|
1268
1298
|
}
|
|
1269
1299
|
if (schema.properties) {
|
|
1270
1300
|
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1271
|
-
allOfMembers.push(
|
|
1301
|
+
allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
|
|
1272
1302
|
}
|
|
1273
|
-
|
|
1303
|
+
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(_kubb_core.ast.createDiscriminantNode({
|
|
1304
|
+
propertyName,
|
|
1305
|
+
value
|
|
1306
|
+
}));
|
|
1307
|
+
return _kubb_core.ast.createSchema({
|
|
1274
1308
|
type: "intersection",
|
|
1275
|
-
members: [...allOfMembers.slice(0, syntheticStart), ...
|
|
1276
|
-
...
|
|
1309
|
+
members: [..._kubb_core.ast.mergeAdjacentObjects(allOfMembers.slice(0, syntheticStart)), ..._kubb_core.ast.mergeAdjacentObjects(allOfMembers.slice(syntheticStart))],
|
|
1310
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1277
1311
|
});
|
|
1278
1312
|
}
|
|
1279
1313
|
/**
|
|
1280
1314
|
* Converts a `oneOf` / `anyOf` schema into a `UnionSchemaNode`.
|
|
1281
|
-
*
|
|
1282
|
-
* Both keywords are treated identically — their members are concatenated into a single union.
|
|
1283
|
-
* When sibling `properties` are present alongside `oneOf`/`anyOf`, each union member is
|
|
1284
|
-
* individually intersected with the shared properties node to match the OAS pattern of
|
|
1285
|
-
* adding common fields next to a discriminated union.
|
|
1286
1315
|
*/
|
|
1287
|
-
function convertUnion({ schema, name, nullable, defaultValue,
|
|
1316
|
+
function convertUnion({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1317
|
+
function pickDiscriminatorPropertyNode(node, propertyName) {
|
|
1318
|
+
const discriminatorProperty = _kubb_core.ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
|
|
1319
|
+
if (!discriminatorProperty) return null;
|
|
1320
|
+
return _kubb_core.ast.createSchema({
|
|
1321
|
+
type: "object",
|
|
1322
|
+
primitive: "object",
|
|
1323
|
+
properties: [discriminatorProperty]
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1288
1326
|
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1327
|
+
const strategy = schema.oneOf ? "one" : "any";
|
|
1289
1328
|
const unionBase = {
|
|
1290
|
-
...
|
|
1291
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0
|
|
1329
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1330
|
+
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1331
|
+
strategy
|
|
1292
1332
|
};
|
|
1293
|
-
|
|
1333
|
+
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1334
|
+
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1294
1335
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1336
|
+
return parseSchema({
|
|
1337
|
+
schema: discriminator ? Object.fromEntries(Object.entries(schemaWithoutUnion).filter(([key]) => key !== "discriminator")) : schemaWithoutUnion,
|
|
1338
|
+
name
|
|
1339
|
+
}, rawOptions);
|
|
1340
|
+
})() : void 0;
|
|
1341
|
+
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1342
|
+
const members = unionMembers.map((s) => {
|
|
1343
|
+
const ref = isReference(s) ? s.$ref : void 0;
|
|
1344
|
+
const discriminatorValue = _kubb_core.ast.findDiscriminator(discriminator?.mapping, ref);
|
|
1345
|
+
const memberNode = parseSchema({ schema: s }, rawOptions);
|
|
1346
|
+
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1347
|
+
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.setDiscriminatorEnum({
|
|
1348
|
+
node: sharedPropertiesNode,
|
|
1349
|
+
propertyName: discriminator.propertyName,
|
|
1350
|
+
values: [discriminatorValue]
|
|
1351
|
+
}), discriminator.propertyName) : void 0;
|
|
1352
|
+
return _kubb_core.ast.createSchema({
|
|
1353
|
+
type: "intersection",
|
|
1354
|
+
members: [memberNode, narrowedDiscriminatorNode ?? _kubb_core.ast.createDiscriminantNode({
|
|
1355
|
+
propertyName: discriminator.propertyName,
|
|
1356
|
+
value: discriminatorValue
|
|
1357
|
+
})]
|
|
1358
|
+
});
|
|
1359
|
+
});
|
|
1360
|
+
const unionNode = _kubb_core.ast.createSchema({
|
|
1298
1361
|
type: "union",
|
|
1299
1362
|
...unionBase,
|
|
1300
|
-
members
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
if (discriminatorValue && discriminator) propertiesNode = applyDiscriminatorEnum({
|
|
1308
|
-
node: propertiesNode,
|
|
1309
|
-
propertyName: discriminator.propertyName,
|
|
1310
|
-
values: [discriminatorValue]
|
|
1311
|
-
});
|
|
1312
|
-
return (0, _kubb_ast.createSchema)({
|
|
1313
|
-
type: "intersection",
|
|
1314
|
-
members: [convertSchema({ schema: s }, options), propertiesNode]
|
|
1315
|
-
});
|
|
1316
|
-
})
|
|
1363
|
+
members
|
|
1364
|
+
});
|
|
1365
|
+
if (!sharedPropertiesNode) return unionNode;
|
|
1366
|
+
return _kubb_core.ast.createSchema({
|
|
1367
|
+
type: "intersection",
|
|
1368
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1369
|
+
members: [unionNode, sharedPropertiesNode]
|
|
1317
1370
|
});
|
|
1318
1371
|
}
|
|
1319
|
-
return
|
|
1372
|
+
return _kubb_core.ast.createSchema({
|
|
1320
1373
|
type: "union",
|
|
1321
1374
|
...unionBase,
|
|
1322
|
-
members:
|
|
1375
|
+
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({ schema: s }, rawOptions)))
|
|
1323
1376
|
});
|
|
1324
1377
|
}
|
|
1325
1378
|
/**
|
|
1326
|
-
* Converts an OAS 3.1 `const` schema into
|
|
1327
|
-
* `const: null` maps to a null scalar; any other value becomes a one-item enum so that generators
|
|
1328
|
-
* can produce a precise literal type.
|
|
1379
|
+
* Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
|
|
1329
1380
|
*/
|
|
1330
1381
|
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1331
1382
|
const constValue = schema.const;
|
|
1332
|
-
if (constValue === null) return
|
|
1383
|
+
if (constValue === null) return _kubb_core.ast.createSchema({
|
|
1333
1384
|
type: "null",
|
|
1334
1385
|
primitive: "null",
|
|
1335
1386
|
name,
|
|
1336
1387
|
title: schema.title,
|
|
1337
1388
|
description: schema.description,
|
|
1338
|
-
deprecated: schema.deprecated
|
|
1339
|
-
nullable
|
|
1389
|
+
deprecated: schema.deprecated
|
|
1340
1390
|
});
|
|
1341
|
-
|
|
1391
|
+
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1392
|
+
return _kubb_core.ast.createSchema({
|
|
1342
1393
|
type: "enum",
|
|
1343
|
-
primitive:
|
|
1394
|
+
primitive: constPrimitive,
|
|
1344
1395
|
enumValues: [constValue],
|
|
1345
|
-
...
|
|
1396
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1346
1397
|
});
|
|
1347
1398
|
}
|
|
1348
1399
|
/**
|
|
1349
|
-
*
|
|
1350
|
-
* Returns `
|
|
1351
|
-
* (i.e. `format: 'date-time'` with `dateType: false`).
|
|
1400
|
+
* Converts a format-annotated schema into a special-type `SchemaNode`.
|
|
1401
|
+
* Returns `null` when the format should fall through to string handling (`dateType: false`).
|
|
1352
1402
|
*/
|
|
1353
|
-
function convertFormat({ schema, name, nullable, defaultValue,
|
|
1354
|
-
const base =
|
|
1355
|
-
if (schema.format === "int64") return
|
|
1356
|
-
type:
|
|
1403
|
+
function convertFormat({ schema, name, nullable, defaultValue, options }) {
|
|
1404
|
+
const base = buildSchemaNode(schema, name, nullable, defaultValue);
|
|
1405
|
+
if (schema.format === "int64") return _kubb_core.ast.createSchema({
|
|
1406
|
+
type: options.integerType === "bigint" ? "bigint" : "integer",
|
|
1357
1407
|
primitive: "integer",
|
|
1358
1408
|
...base,
|
|
1359
1409
|
min: schema.minimum,
|
|
@@ -1362,36 +1412,55 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1362
1412
|
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0
|
|
1363
1413
|
});
|
|
1364
1414
|
if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
|
|
1365
|
-
const dateType = getDateType(
|
|
1366
|
-
if (!dateType) return
|
|
1367
|
-
if (dateType.type === "datetime") return
|
|
1415
|
+
const dateType = getDateType(options, schema.format);
|
|
1416
|
+
if (!dateType) return null;
|
|
1417
|
+
if (dateType.type === "datetime") return _kubb_core.ast.createSchema({
|
|
1368
1418
|
...base,
|
|
1369
1419
|
primitive: "string",
|
|
1370
1420
|
type: "datetime",
|
|
1371
1421
|
offset: dateType.offset,
|
|
1372
1422
|
local: dateType.local
|
|
1373
1423
|
});
|
|
1374
|
-
return
|
|
1424
|
+
return _kubb_core.ast.createSchema({
|
|
1375
1425
|
...base,
|
|
1376
1426
|
primitive: "string",
|
|
1377
1427
|
type: dateType.type,
|
|
1378
1428
|
representation: dateType.representation
|
|
1379
1429
|
});
|
|
1380
1430
|
}
|
|
1381
|
-
const specialType =
|
|
1382
|
-
if (!specialType) return
|
|
1431
|
+
const specialType = getSchemaType(schema.format);
|
|
1432
|
+
if (!specialType) return null;
|
|
1383
1433
|
const specialPrimitive = specialType === "number" || specialType === "integer" || specialType === "bigint" ? specialType : "string";
|
|
1384
|
-
if (specialType === "number" || specialType === "integer" || specialType === "bigint") return
|
|
1434
|
+
if (specialType === "number" || specialType === "integer" || specialType === "bigint") return _kubb_core.ast.createSchema({
|
|
1385
1435
|
...base,
|
|
1386
1436
|
primitive: specialPrimitive,
|
|
1387
1437
|
type: specialType
|
|
1388
1438
|
});
|
|
1389
|
-
if (specialType === "url") return
|
|
1439
|
+
if (specialType === "url") return _kubb_core.ast.createSchema({
|
|
1440
|
+
...base,
|
|
1441
|
+
primitive: "string",
|
|
1442
|
+
type: "url",
|
|
1443
|
+
min: schema.minLength,
|
|
1444
|
+
max: schema.maxLength
|
|
1445
|
+
});
|
|
1446
|
+
if (specialType === "ipv4") return _kubb_core.ast.createSchema({
|
|
1447
|
+
...base,
|
|
1448
|
+
primitive: "string",
|
|
1449
|
+
type: "ipv4"
|
|
1450
|
+
});
|
|
1451
|
+
if (specialType === "ipv6") return _kubb_core.ast.createSchema({
|
|
1452
|
+
...base,
|
|
1453
|
+
primitive: "string",
|
|
1454
|
+
type: "ipv6"
|
|
1455
|
+
});
|
|
1456
|
+
if (specialType === "uuid" || specialType === "email") return _kubb_core.ast.createSchema({
|
|
1390
1457
|
...base,
|
|
1391
1458
|
primitive: "string",
|
|
1392
|
-
type:
|
|
1459
|
+
type: specialType,
|
|
1460
|
+
min: schema.minLength,
|
|
1461
|
+
max: schema.maxLength
|
|
1393
1462
|
});
|
|
1394
|
-
return
|
|
1463
|
+
return _kubb_core.ast.createSchema({
|
|
1395
1464
|
...base,
|
|
1396
1465
|
primitive: specialPrimitive,
|
|
1397
1466
|
type: specialType
|
|
@@ -1399,36 +1468,20 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1399
1468
|
}
|
|
1400
1469
|
/**
|
|
1401
1470
|
* Converts an `enum` schema into an `EnumSchemaNode`.
|
|
1402
|
-
*
|
|
1403
|
-
* Handles several edge cases:
|
|
1404
|
-
* - `{ type: 'array', enum }` (technically invalid OAS) — the enum is normalized into `items`.
|
|
1405
|
-
* - `null` in enum values (OAS 3.0 nullable enum convention) — stripped and reflected as `nullable`.
|
|
1406
|
-
* - `x-enumNames` / `x-enum-varnames` vendor extensions — produce named enum variants with explicit labels.
|
|
1407
|
-
* - Numeric and boolean enums require a const-map representation because most generators cannot
|
|
1408
|
-
* use string-enum syntax for non-string values.
|
|
1409
1471
|
*/
|
|
1410
|
-
function convertEnum({ schema, name, nullable, type,
|
|
1411
|
-
if (type === "array") {
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
};
|
|
1416
|
-
const { enum: _enum, ...schemaWithoutEnum } = schema;
|
|
1417
|
-
return convertSchema({
|
|
1418
|
-
schema: {
|
|
1419
|
-
...schemaWithoutEnum,
|
|
1420
|
-
items: normalizedItems
|
|
1421
|
-
},
|
|
1422
|
-
name
|
|
1423
|
-
}, options);
|
|
1424
|
-
}
|
|
1472
|
+
function convertEnum({ schema, name, nullable, type, rawOptions }) {
|
|
1473
|
+
if (type === "array") return parseSchema({
|
|
1474
|
+
schema: normalizeArrayEnum(schema),
|
|
1475
|
+
name
|
|
1476
|
+
}, rawOptions);
|
|
1425
1477
|
const nullInEnum = schema.enum.includes(null);
|
|
1426
1478
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1427
1479
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1428
1480
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1481
|
+
const enumPrimitive = getPrimitiveType(type);
|
|
1429
1482
|
const enumBase = {
|
|
1430
1483
|
type: "enum",
|
|
1431
|
-
primitive:
|
|
1484
|
+
primitive: enumPrimitive,
|
|
1432
1485
|
name,
|
|
1433
1486
|
title: schema.title,
|
|
1434
1487
|
description: schema.description,
|
|
@@ -1440,81 +1493,56 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1440
1493
|
example: schema.example
|
|
1441
1494
|
};
|
|
1442
1495
|
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1443
|
-
if (extensionKey) {
|
|
1444
|
-
const
|
|
1445
|
-
const
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1496
|
+
if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
1497
|
+
const enumPrimitiveType = enumPrimitive === "number" || enumPrimitive === "integer" ? "number" : enumPrimitive === "boolean" ? "boolean" : "string";
|
|
1498
|
+
const rawEnumNames = extensionKey ? schema[extensionKey] : void 0;
|
|
1499
|
+
const uniqueValues = [...new Set(filteredValues)];
|
|
1500
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1501
|
+
return _kubb_core.ast.createSchema({
|
|
1448
1502
|
...enumBase,
|
|
1449
|
-
|
|
1450
|
-
namedEnumValues:
|
|
1451
|
-
name: String(
|
|
1452
|
-
value
|
|
1453
|
-
|
|
1454
|
-
}))
|
|
1503
|
+
primitive: enumPrimitiveType,
|
|
1504
|
+
namedEnumValues: uniqueValues.map((value, index) => ({
|
|
1505
|
+
name: String(rawEnumNames?.[index] ?? value),
|
|
1506
|
+
value,
|
|
1507
|
+
primitive: enumPrimitiveType
|
|
1508
|
+
})).filter((entry) => {
|
|
1509
|
+
if (seenNames.has(entry.name)) return false;
|
|
1510
|
+
seenNames.add(entry.name);
|
|
1511
|
+
return true;
|
|
1512
|
+
})
|
|
1455
1513
|
});
|
|
1456
1514
|
}
|
|
1457
|
-
|
|
1458
|
-
...enumBase,
|
|
1459
|
-
enumType: "number",
|
|
1460
|
-
namedEnumValues: [...new Set(filteredValues)].map((value) => ({
|
|
1461
|
-
name: String(value),
|
|
1462
|
-
value,
|
|
1463
|
-
format: "number"
|
|
1464
|
-
}))
|
|
1465
|
-
});
|
|
1466
|
-
if (type === "boolean") return (0, _kubb_ast.createSchema)({
|
|
1467
|
-
...enumBase,
|
|
1468
|
-
enumType: "boolean",
|
|
1469
|
-
namedEnumValues: [...new Set(filteredValues)].map((value) => ({
|
|
1470
|
-
name: String(value),
|
|
1471
|
-
value,
|
|
1472
|
-
format: "boolean"
|
|
1473
|
-
}))
|
|
1474
|
-
});
|
|
1475
|
-
return (0, _kubb_ast.createSchema)({
|
|
1515
|
+
return _kubb_core.ast.createSchema({
|
|
1476
1516
|
...enumBase,
|
|
1477
1517
|
enumValues: [...new Set(filteredValues)]
|
|
1478
1518
|
});
|
|
1479
1519
|
}
|
|
1480
1520
|
/**
|
|
1481
|
-
* Converts an object-like schema
|
|
1482
|
-
* or `patternProperties`) into an `ObjectSchemaNode`.
|
|
1483
|
-
*
|
|
1484
|
-
* When a `discriminator` is present, the discriminator property's schema is replaced with an
|
|
1485
|
-
* enum of the mapping keys so generators can produce a precise literal-union type for it.
|
|
1486
|
-
*
|
|
1487
|
-
* Property optionality follows OAS semantics:
|
|
1488
|
-
* - required + not nullable → `required: true`
|
|
1489
|
-
* - not required + not nullable → `optional: true`
|
|
1490
|
-
* - not required + nullable → `nullish: true`
|
|
1521
|
+
* Converts an object-like schema into an `ObjectSchemaNode`.
|
|
1491
1522
|
*/
|
|
1492
|
-
function convertObject({ schema, name, nullable, defaultValue,
|
|
1523
|
+
function convertObject({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1493
1524
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1494
1525
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1495
1526
|
const resolvedPropSchema = propSchema;
|
|
1496
1527
|
const propNullable = isNullable(resolvedPropSchema);
|
|
1497
|
-
const
|
|
1498
|
-
const propNode = convertSchema({
|
|
1528
|
+
const propNode = parseSchema({
|
|
1499
1529
|
schema: resolvedPropSchema,
|
|
1500
|
-
name:
|
|
1501
|
-
},
|
|
1502
|
-
|
|
1503
|
-
const
|
|
1504
|
-
|
|
1505
|
-
propName,
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1530
|
+
name: _kubb_core.ast.childName(name, propName)
|
|
1531
|
+
}, rawOptions);
|
|
1532
|
+
let schemaNode = _kubb_core.ast.setEnumName(propNode, name, propName, options.enumSuffix);
|
|
1533
|
+
const tupleNode = _kubb_core.ast.narrowSchema(schemaNode, "tuple");
|
|
1534
|
+
if (tupleNode?.items) {
|
|
1535
|
+
const namedItems = tupleNode.items.map((item) => _kubb_core.ast.setEnumName(item, name, propName, options.enumSuffix));
|
|
1536
|
+
if (namedItems.some((item, i) => item !== tupleNode.items[i])) schemaNode = {
|
|
1537
|
+
...tupleNode,
|
|
1538
|
+
items: namedItems
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
return _kubb_core.ast.createProperty({
|
|
1509
1542
|
name: propName,
|
|
1510
1543
|
schema: {
|
|
1511
|
-
...
|
|
1512
|
-
|
|
1513
|
-
name: derivedPropName
|
|
1514
|
-
} : propNode,
|
|
1515
|
-
nullable: propNullable || void 0,
|
|
1516
|
-
optional: !required && !propNullable ? true : void 0,
|
|
1517
|
-
nullish: !required && propNullable ? true : void 0
|
|
1544
|
+
...schemaNode,
|
|
1545
|
+
nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
|
|
1518
1546
|
},
|
|
1519
1547
|
required
|
|
1520
1548
|
});
|
|
@@ -1522,115 +1550,113 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1522
1550
|
const additionalProperties = schema.additionalProperties;
|
|
1523
1551
|
let additionalPropertiesNode;
|
|
1524
1552
|
if (additionalProperties === true) additionalPropertiesNode = true;
|
|
1525
|
-
else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode =
|
|
1526
|
-
else if (additionalProperties === false) additionalPropertiesNode =
|
|
1527
|
-
else if (additionalProperties) additionalPropertiesNode =
|
|
1553
|
+
else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode = parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1554
|
+
else if (additionalProperties === false) additionalPropertiesNode = false;
|
|
1555
|
+
else if (additionalProperties) additionalPropertiesNode = _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1528
1556
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1529
|
-
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ?
|
|
1530
|
-
const objectNode =
|
|
1557
|
+
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1558
|
+
const objectNode = _kubb_core.ast.createSchema({
|
|
1531
1559
|
type: "object",
|
|
1532
1560
|
primitive: "object",
|
|
1533
1561
|
properties,
|
|
1534
1562
|
additionalProperties: additionalPropertiesNode,
|
|
1535
1563
|
patternProperties,
|
|
1536
|
-
|
|
1564
|
+
minProperties: schema.minProperties,
|
|
1565
|
+
maxProperties: schema.maxProperties,
|
|
1566
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1537
1567
|
});
|
|
1538
1568
|
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1539
1569
|
const discPropName = schema.discriminator.propertyName;
|
|
1540
|
-
|
|
1570
|
+
const values = Object.keys(schema.discriminator.mapping);
|
|
1571
|
+
const enumName = name ? _kubb_core.ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
1572
|
+
return _kubb_core.ast.setDiscriminatorEnum({
|
|
1541
1573
|
node: objectNode,
|
|
1542
1574
|
propertyName: discPropName,
|
|
1543
|
-
values
|
|
1544
|
-
enumName
|
|
1545
|
-
name,
|
|
1546
|
-
discPropName,
|
|
1547
|
-
mergedOptions.enumSuffix
|
|
1548
|
-
].filter(Boolean).join(" ")) : void 0
|
|
1575
|
+
values,
|
|
1576
|
+
enumName
|
|
1549
1577
|
});
|
|
1550
1578
|
}
|
|
1551
1579
|
return objectNode;
|
|
1552
1580
|
}
|
|
1553
1581
|
/**
|
|
1554
1582
|
* Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
|
|
1555
|
-
*
|
|
1556
|
-
* Each `prefixItems` element maps to a positional tuple slot. An optional `items` schema
|
|
1557
|
-
* after the prefix items is mapped to the rest parameter of the tuple.
|
|
1558
1583
|
*/
|
|
1559
|
-
function convertTuple({ schema, name, nullable, defaultValue,
|
|
1560
|
-
|
|
1584
|
+
function convertTuple({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1585
|
+
const tupleItems = (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item }, rawOptions));
|
|
1586
|
+
const rest = schema.items ? parseSchema({ schema: schema.items }, rawOptions) : _kubb_core.ast.createSchema({ type: "any" });
|
|
1587
|
+
return _kubb_core.ast.createSchema({
|
|
1561
1588
|
type: "tuple",
|
|
1562
1589
|
primitive: "array",
|
|
1563
|
-
items:
|
|
1564
|
-
rest
|
|
1590
|
+
items: tupleItems,
|
|
1591
|
+
rest,
|
|
1565
1592
|
min: schema.minItems,
|
|
1566
1593
|
max: schema.maxItems,
|
|
1567
|
-
...
|
|
1594
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1568
1595
|
});
|
|
1569
1596
|
}
|
|
1570
1597
|
/**
|
|
1571
1598
|
* Converts a `type: 'array'` schema into an `ArraySchemaNode`.
|
|
1572
|
-
*
|
|
1573
|
-
* When the items schema is an inline enum, a name derived from the parent array's name and
|
|
1574
|
-
* `enumSuffix` is forwarded so generators can emit a named enum declaration.
|
|
1575
1599
|
*/
|
|
1576
|
-
function convertArray({ schema, name, nullable, defaultValue,
|
|
1600
|
+
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1577
1601
|
const rawItems = schema.items;
|
|
1578
|
-
const itemName = rawItems?.enum?.length && name ?
|
|
1579
|
-
|
|
1602
|
+
const itemName = rawItems?.enum?.length && name ? _kubb_core.ast.enumPropName(void 0, name, options.enumSuffix) : void 0;
|
|
1603
|
+
const items = rawItems ? [parseSchema({
|
|
1604
|
+
schema: rawItems,
|
|
1605
|
+
name: itemName
|
|
1606
|
+
}, rawOptions)] : [];
|
|
1607
|
+
return _kubb_core.ast.createSchema({
|
|
1580
1608
|
type: "array",
|
|
1581
1609
|
primitive: "array",
|
|
1582
|
-
items
|
|
1583
|
-
schema: rawItems,
|
|
1584
|
-
name: itemName
|
|
1585
|
-
}, options)] : [],
|
|
1610
|
+
items,
|
|
1586
1611
|
min: schema.minItems,
|
|
1587
1612
|
max: schema.maxItems,
|
|
1588
1613
|
unique: schema.uniqueItems ?? void 0,
|
|
1589
|
-
...
|
|
1614
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1590
1615
|
});
|
|
1591
1616
|
}
|
|
1592
1617
|
/**
|
|
1593
|
-
* Converts a `type: 'string'` schema
|
|
1618
|
+
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
1594
1619
|
*/
|
|
1595
1620
|
function convertString({ schema, name, nullable, defaultValue }) {
|
|
1596
|
-
return
|
|
1621
|
+
return _kubb_core.ast.createSchema({
|
|
1597
1622
|
type: "string",
|
|
1598
1623
|
primitive: "string",
|
|
1599
1624
|
min: schema.minLength,
|
|
1600
1625
|
max: schema.maxLength,
|
|
1601
1626
|
pattern: schema.pattern,
|
|
1602
|
-
...
|
|
1627
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1603
1628
|
});
|
|
1604
1629
|
}
|
|
1605
1630
|
/**
|
|
1606
|
-
* Converts a `type: 'number'` or `type: 'integer'` schema
|
|
1631
|
+
* Converts a `type: 'number'` or `type: 'integer'` schema.
|
|
1607
1632
|
*/
|
|
1608
1633
|
function convertNumeric({ schema, name, nullable, defaultValue }, type) {
|
|
1609
|
-
return
|
|
1634
|
+
return _kubb_core.ast.createSchema({
|
|
1610
1635
|
type,
|
|
1611
1636
|
primitive: type,
|
|
1612
1637
|
min: schema.minimum,
|
|
1613
1638
|
max: schema.maximum,
|
|
1614
1639
|
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1615
1640
|
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
|
|
1616
|
-
|
|
1641
|
+
multipleOf: schema.multipleOf,
|
|
1642
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1617
1643
|
});
|
|
1618
1644
|
}
|
|
1619
1645
|
/**
|
|
1620
|
-
* Converts a `type: 'boolean'` schema
|
|
1646
|
+
* Converts a `type: 'boolean'` schema.
|
|
1621
1647
|
*/
|
|
1622
1648
|
function convertBoolean({ schema, name, nullable, defaultValue }) {
|
|
1623
|
-
return
|
|
1649
|
+
return _kubb_core.ast.createSchema({
|
|
1624
1650
|
type: "boolean",
|
|
1625
1651
|
primitive: "boolean",
|
|
1626
|
-
...
|
|
1652
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1627
1653
|
});
|
|
1628
1654
|
}
|
|
1629
1655
|
/**
|
|
1630
|
-
* Converts an explicit `type: 'null'`
|
|
1656
|
+
* Converts an explicit `type: 'null'` schema.
|
|
1631
1657
|
*/
|
|
1632
1658
|
function convertNull({ schema, name, nullable }) {
|
|
1633
|
-
return
|
|
1659
|
+
return _kubb_core.ast.createSchema({
|
|
1634
1660
|
type: "null",
|
|
1635
1661
|
primitive: "null",
|
|
1636
1662
|
name,
|
|
@@ -1641,31 +1667,22 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1641
1667
|
});
|
|
1642
1668
|
}
|
|
1643
1669
|
/**
|
|
1644
|
-
* Central dispatcher
|
|
1670
|
+
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1645
1671
|
*
|
|
1646
|
-
* Dispatch order (first match wins):
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
* 3. `oneOf` / `anyOf` union
|
|
1650
|
-
* 4. `const` literal (OAS 3.1)
|
|
1651
|
-
* 5. `format`-based special type (date/time, uuid, blob, …)
|
|
1652
|
-
* 6. OAS 3.1 `contentMediaType: 'application/octet-stream'` blob
|
|
1653
|
-
* 7. OAS 3.1 multi-type array → union or fallthrough
|
|
1654
|
-
* 8. Constraint-inferred type (minLength/maxLength → string; minimum/maximum → number)
|
|
1655
|
-
* 9. `enum` values
|
|
1656
|
-
* 10. Object / array / tuple / scalar by `type`
|
|
1657
|
-
* 11. Empty schema fallback (`emptySchemaType` option)
|
|
1672
|
+
* Dispatch order (first match wins): `$ref` → `allOf` → `oneOf`/`anyOf` → `const` → `format`
|
|
1673
|
+
* → octet-stream blob → multi-type array → constraint-inferred type → `enum` → object/array/tuple/scalar
|
|
1674
|
+
* → empty-schema fallback (`emptySchemaType` option).
|
|
1658
1675
|
*/
|
|
1659
|
-
function
|
|
1660
|
-
const
|
|
1661
|
-
...
|
|
1662
|
-
...
|
|
1676
|
+
function parseSchema({ schema, name }, rawOptions) {
|
|
1677
|
+
const options = {
|
|
1678
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
1679
|
+
...rawOptions
|
|
1663
1680
|
};
|
|
1664
1681
|
const flattenedSchema = flattenSchema(schema);
|
|
1665
|
-
if (flattenedSchema && flattenedSchema !== schema) return
|
|
1682
|
+
if (flattenedSchema && flattenedSchema !== schema) return parseSchema({
|
|
1666
1683
|
schema: flattenedSchema,
|
|
1667
1684
|
name
|
|
1668
|
-
},
|
|
1685
|
+
}, rawOptions);
|
|
1669
1686
|
const nullable = isNullable(schema) || void 0;
|
|
1670
1687
|
const defaultValue = schema.default === null && nullable ? void 0 : schema.default;
|
|
1671
1688
|
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
@@ -1675,8 +1692,8 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1675
1692
|
nullable,
|
|
1676
1693
|
defaultValue,
|
|
1677
1694
|
type,
|
|
1678
|
-
|
|
1679
|
-
|
|
1695
|
+
rawOptions,
|
|
1696
|
+
options
|
|
1680
1697
|
};
|
|
1681
1698
|
if (isReference(schema)) return convertRef(ctx);
|
|
1682
1699
|
if (schema.allOf?.length) return convertAllOf(ctx);
|
|
@@ -1686,24 +1703,24 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1686
1703
|
const formatResult = convertFormat(ctx);
|
|
1687
1704
|
if (formatResult) return formatResult;
|
|
1688
1705
|
}
|
|
1689
|
-
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return
|
|
1706
|
+
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return _kubb_core.ast.createSchema({
|
|
1690
1707
|
type: "blob",
|
|
1691
1708
|
primitive: "string",
|
|
1692
|
-
...
|
|
1709
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1693
1710
|
});
|
|
1694
1711
|
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1695
1712
|
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1696
1713
|
const arrayNullable = schema.type.includes("null") || nullable || void 0;
|
|
1697
|
-
if (nonNullTypes.length > 1) return
|
|
1714
|
+
if (nonNullTypes.length > 1) return _kubb_core.ast.createSchema({
|
|
1698
1715
|
type: "union",
|
|
1699
|
-
members: nonNullTypes.map((t) =>
|
|
1716
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1700
1717
|
schema: {
|
|
1701
1718
|
...schema,
|
|
1702
1719
|
type: t
|
|
1703
1720
|
},
|
|
1704
1721
|
name
|
|
1705
|
-
},
|
|
1706
|
-
...
|
|
1722
|
+
}, rawOptions)),
|
|
1723
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1707
1724
|
});
|
|
1708
1725
|
}
|
|
1709
1726
|
if (!type) {
|
|
@@ -1719,57 +1736,106 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1719
1736
|
if (type === "integer") return convertNumeric(ctx, "integer");
|
|
1720
1737
|
if (type === "boolean") return convertBoolean(ctx);
|
|
1721
1738
|
if (type === "null") return convertNull(ctx);
|
|
1722
|
-
|
|
1723
|
-
|
|
1739
|
+
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1740
|
+
return _kubb_core.ast.createSchema({
|
|
1741
|
+
type: emptyType,
|
|
1724
1742
|
name,
|
|
1725
1743
|
title: schema.title,
|
|
1726
1744
|
description: schema.description
|
|
1727
1745
|
});
|
|
1728
1746
|
}
|
|
1729
1747
|
/**
|
|
1730
|
-
* Converts a
|
|
1731
|
-
* When the parameter has no `schema` or its schema is a `$ref`, falls back to `unknownType`.
|
|
1748
|
+
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1732
1749
|
*/
|
|
1733
1750
|
function parseParameter(options, param) {
|
|
1734
1751
|
const required = param["required"] ?? false;
|
|
1735
|
-
const schema = param["schema"]
|
|
1736
|
-
return
|
|
1752
|
+
const schema = param["schema"] ? parseSchema({ schema: param["schema"] }, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1753
|
+
return _kubb_core.ast.createParameter({
|
|
1737
1754
|
name: param["name"],
|
|
1738
1755
|
in: param["in"],
|
|
1739
1756
|
schema: {
|
|
1740
1757
|
...schema,
|
|
1741
|
-
|
|
1758
|
+
description: param["description"] ?? schema.description
|
|
1742
1759
|
},
|
|
1743
1760
|
required
|
|
1744
1761
|
});
|
|
1745
1762
|
}
|
|
1746
1763
|
/**
|
|
1747
|
-
*
|
|
1748
|
-
*
|
|
1764
|
+
* Reads the inline `requestBody` metadata (description / required) that OAS exposes
|
|
1765
|
+
* outside the schema itself. Returns an empty object when the request body is missing or a `$ref`.
|
|
1766
|
+
*/
|
|
1767
|
+
function getRequestBodyMeta(operation) {
|
|
1768
|
+
const body = operation.schema.requestBody;
|
|
1769
|
+
if (!body) return { required: false };
|
|
1770
|
+
return {
|
|
1771
|
+
description: body.description,
|
|
1772
|
+
required: body.required === true
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Reads the inline response object (not a `$ref`) and returns its description plus its `content` map.
|
|
1777
|
+
*/
|
|
1778
|
+
function getResponseMeta(responseObj) {
|
|
1779
|
+
if (typeof responseObj !== "object" || responseObj === null || Array.isArray(responseObj)) return {};
|
|
1780
|
+
const inline = responseObj;
|
|
1781
|
+
return {
|
|
1782
|
+
description: inline.description,
|
|
1783
|
+
content: inline.content
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* Collects property names whose schema has a truthy boolean flag (`readOnly` or `writeOnly`).
|
|
1788
|
+
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
1789
|
+
*/
|
|
1790
|
+
function collectPropertyKeysByFlag(schema, flag) {
|
|
1791
|
+
if (!schema?.properties) return void 0;
|
|
1792
|
+
const keys = [];
|
|
1793
|
+
for (const key in schema.properties) {
|
|
1794
|
+
const prop = schema.properties[key];
|
|
1795
|
+
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
1796
|
+
}
|
|
1797
|
+
return keys.length ? keys : void 0;
|
|
1798
|
+
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1749
1801
|
*/
|
|
1750
|
-
function parseOperation(options,
|
|
1751
|
-
const parameters =
|
|
1752
|
-
|
|
1802
|
+
function parseOperation(options, operation) {
|
|
1803
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param));
|
|
1804
|
+
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
1805
|
+
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
1806
|
+
const content = allContentTypes.flatMap((ct) => {
|
|
1807
|
+
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
1808
|
+
if (!schema) return [];
|
|
1809
|
+
return [{
|
|
1810
|
+
contentType: ct,
|
|
1811
|
+
schema: _kubb_core.ast.syncOptionality(parseSchema({ schema }, options), requestBodyMeta.required),
|
|
1812
|
+
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1813
|
+
}];
|
|
1753
1814
|
});
|
|
1754
|
-
const
|
|
1755
|
-
|
|
1815
|
+
const requestBody = content.length > 0 || requestBodyMeta.description ? {
|
|
1816
|
+
description: requestBodyMeta.description,
|
|
1817
|
+
required: requestBodyMeta.required || void 0,
|
|
1818
|
+
content: content.length > 0 ? content : void 0
|
|
1819
|
+
} : void 0;
|
|
1756
1820
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1757
1821
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1758
|
-
const responseSchema =
|
|
1759
|
-
const schema = responseSchema && Object.keys(responseSchema).length > 0 ?
|
|
1760
|
-
const description
|
|
1761
|
-
const
|
|
1762
|
-
return
|
|
1822
|
+
const responseSchema = getResponseSchema(document, operation, statusCode, { contentType: ctx.contentType });
|
|
1823
|
+
const schema = responseSchema && Object.keys(responseSchema).length > 0 ? parseSchema({ schema: responseSchema }, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType) });
|
|
1824
|
+
const { description, content } = getResponseMeta(responseObj);
|
|
1825
|
+
const mediaType = content ? getMediaType(Object.keys(content)[0] ?? "") : getMediaType(operation.contentType ?? "");
|
|
1826
|
+
return _kubb_core.ast.createResponse({
|
|
1763
1827
|
statusCode,
|
|
1764
1828
|
description,
|
|
1765
1829
|
schema,
|
|
1766
|
-
mediaType
|
|
1830
|
+
mediaType,
|
|
1831
|
+
keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
|
|
1767
1832
|
});
|
|
1768
1833
|
});
|
|
1769
|
-
|
|
1834
|
+
const urlPath = new URLPath(operation.path);
|
|
1835
|
+
return _kubb_core.ast.createOperation({
|
|
1770
1836
|
operationId: operation.getOperationId(),
|
|
1771
1837
|
method: operation.method.toUpperCase(),
|
|
1772
|
-
path:
|
|
1838
|
+
path: urlPath.path,
|
|
1773
1839
|
tags: operation.getTags().map((tag) => tag.name),
|
|
1774
1840
|
summary: operation.getSummary() || void 0,
|
|
1775
1841
|
description: operation.getDescription() || void 0,
|
|
@@ -1779,160 +1845,169 @@ function createOasParser(oas, { contentType, collisionDetection } = {}) {
|
|
|
1779
1845
|
responses
|
|
1780
1846
|
});
|
|
1781
1847
|
}
|
|
1782
|
-
/**
|
|
1783
|
-
* Converts an OpenAPI/Swagger spec (wrapped in a Kubb `Oas` instance) into
|
|
1784
|
-
* a `RootNode` — the top-level node of the `@kubb/ast` tree.
|
|
1785
|
-
*/
|
|
1786
|
-
function parse(options) {
|
|
1787
|
-
const mergedOptions = {
|
|
1788
|
-
...DEFAULT_OPTIONS,
|
|
1789
|
-
...options
|
|
1790
|
-
};
|
|
1791
|
-
const schemas = Object.entries(schemaObjects).map(([name, schemaObject]) => convertSchema({
|
|
1792
|
-
schema: schemaObject,
|
|
1793
|
-
name
|
|
1794
|
-
}, mergedOptions));
|
|
1795
|
-
const paths = oas.getPaths();
|
|
1796
|
-
return (0, _kubb_ast.createRoot)({
|
|
1797
|
-
schemas,
|
|
1798
|
-
operations: Object.entries(paths).flatMap(([_path, methods]) => Object.entries(methods).map(([, operation]) => operation ? parseOperation(mergedOptions, oas, operation) : null).filter((op) => op !== null))
|
|
1799
|
-
});
|
|
1800
|
-
}
|
|
1801
|
-
/**
|
|
1802
|
-
* Walks a `SchemaNode` tree and resolves all `ref` node names through the provided callbacks.
|
|
1803
|
-
*
|
|
1804
|
-
* `resolveName` handles all schema types; `resolveEnumName` (when provided) takes precedence
|
|
1805
|
-
* for `enum` nodes, enabling a separate naming strategy for enums (e.g. different suffix).
|
|
1806
|
-
*
|
|
1807
|
-
* Collision-resolved names (from `nameMapping`) are applied before user-supplied resolvers.
|
|
1808
|
-
*/
|
|
1809
|
-
function resolveRefs(node, resolveName, resolveEnumName) {
|
|
1810
|
-
return (0, _kubb_ast.transform)(node, { schema(schemaNode) {
|
|
1811
|
-
const schemaRef = (0, _kubb_ast.narrowSchema)(schemaNode, _kubb_ast.schemaTypes.ref);
|
|
1812
|
-
if (schemaRef && (schemaRef.ref || schemaRef.name)) {
|
|
1813
|
-
const rawRef = schemaRef.ref ?? schemaRef.name;
|
|
1814
|
-
const resolved = resolveName(nameMapping.get(rawRef) ?? rawRef);
|
|
1815
|
-
if (resolved) return {
|
|
1816
|
-
...schemaNode,
|
|
1817
|
-
name: resolved
|
|
1818
|
-
};
|
|
1819
|
-
}
|
|
1820
|
-
if (schemaNode.type === "enum" && schemaNode.name) {
|
|
1821
|
-
const resolved = (resolveEnumName ?? resolveName)(schemaNode.name);
|
|
1822
|
-
if (resolved) return {
|
|
1823
|
-
...schemaNode,
|
|
1824
|
-
name: resolved
|
|
1825
|
-
};
|
|
1826
|
-
}
|
|
1827
|
-
} });
|
|
1828
|
-
}
|
|
1829
1848
|
return {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1849
|
+
parseSchema,
|
|
1850
|
+
parseOperation,
|
|
1851
|
+
parseParameter
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* Parses an OpenAPI specification into Kubb's universal `InputNode` AST.
|
|
1856
|
+
*
|
|
1857
|
+
* This is the main entry point for `@kubb/adapter-oas`. It converts OpenAPI/Swagger specs into a spec-agnostic tree
|
|
1858
|
+
* that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here —
|
|
1859
|
+
* the tree is a pure data structure representing all schemas and operations.
|
|
1860
|
+
*
|
|
1861
|
+
* Returns the AST root and a `nameMapping` for resolving schema references.
|
|
1862
|
+
*
|
|
1863
|
+
* @example
|
|
1864
|
+
* ```ts
|
|
1865
|
+
* import { parseOas } from '@kubb/adapter-oas'
|
|
1866
|
+
*
|
|
1867
|
+
* const document = await parseFromConfig(config)
|
|
1868
|
+
* const { root, nameMapping } = parseOas(document, { dateType: 'date', contentType: 'application/json' })
|
|
1869
|
+
* ```
|
|
1870
|
+
*/
|
|
1871
|
+
function parseOas(document, options = {}) {
|
|
1872
|
+
const { contentType, ...parserOptions } = options;
|
|
1873
|
+
const mergedOptions = {
|
|
1874
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
1875
|
+
...parserOptions
|
|
1876
|
+
};
|
|
1877
|
+
const { schemas: schemaObjects, nameMapping } = getSchemas(document, { contentType });
|
|
1878
|
+
const { parseSchema: _parseSchema, parseOperation: _parseOperation } = createSchemaParser({
|
|
1879
|
+
document,
|
|
1880
|
+
contentType
|
|
1881
|
+
});
|
|
1882
|
+
const schemas = Object.entries(schemaObjects).map(([name, schema]) => _parseSchema({
|
|
1883
|
+
schema,
|
|
1884
|
+
name
|
|
1885
|
+
}, mergedOptions));
|
|
1886
|
+
const paths = new oas.default(document).getPaths();
|
|
1887
|
+
const operations = Object.entries(paths).flatMap(([_path, methods]) => Object.entries(methods).map(([, operation]) => operation ? _parseOperation(mergedOptions, operation) : null).filter((op) => op !== null));
|
|
1888
|
+
return {
|
|
1889
|
+
root: _kubb_core.ast.createInput({
|
|
1890
|
+
schemas,
|
|
1891
|
+
operations
|
|
1892
|
+
}),
|
|
1833
1893
|
nameMapping
|
|
1834
1894
|
};
|
|
1835
1895
|
}
|
|
1836
1896
|
//#endregion
|
|
1837
1897
|
//#region src/adapter.ts
|
|
1898
|
+
/**
|
|
1899
|
+
* Stable string identifier for the OAS adapter used in Kubb's adapter registry.
|
|
1900
|
+
*/
|
|
1838
1901
|
const adapterOasName = "oas";
|
|
1839
1902
|
/**
|
|
1840
|
-
* Creates
|
|
1903
|
+
* Creates the default OpenAPI / Swagger adapter for Kubb.
|
|
1841
1904
|
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1905
|
+
* Parses the spec, optionally validates it, resolves the base URL, and converts
|
|
1906
|
+
* everything into an `InputNode` that downstream plugins consume.
|
|
1844
1907
|
*
|
|
1845
1908
|
* @example
|
|
1846
1909
|
* ```ts
|
|
1847
|
-
* import { defineConfig } from '
|
|
1910
|
+
* import { defineConfig } from 'kubb'
|
|
1848
1911
|
* import { adapterOas } from '@kubb/adapter-oas'
|
|
1912
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1849
1913
|
*
|
|
1850
1914
|
* export default defineConfig({
|
|
1851
|
-
* adapter: adapterOas({
|
|
1852
|
-
* input:
|
|
1853
|
-
* plugins: [pluginTs()
|
|
1915
|
+
* adapter: adapterOas({ dateType: 'date', serverIndex: 0 }),
|
|
1916
|
+
* input: { path: './openapi.yaml' },
|
|
1917
|
+
* plugins: [pluginTs()],
|
|
1854
1918
|
* })
|
|
1855
1919
|
* ```
|
|
1856
1920
|
*/
|
|
1857
1921
|
const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
1858
|
-
const { validate = true,
|
|
1859
|
-
|
|
1922
|
+
const { validate = true, contentType, serverIndex, serverVariables, discriminator = "strict", 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;
|
|
1923
|
+
let nameMapping = /* @__PURE__ */ new Map();
|
|
1924
|
+
let parsedDocument;
|
|
1925
|
+
let inputNode;
|
|
1860
1926
|
return {
|
|
1861
1927
|
name: "oas",
|
|
1862
|
-
options
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1928
|
+
get options() {
|
|
1929
|
+
return {
|
|
1930
|
+
validate,
|
|
1931
|
+
contentType,
|
|
1932
|
+
serverIndex,
|
|
1933
|
+
serverVariables,
|
|
1934
|
+
discriminator,
|
|
1935
|
+
dateType,
|
|
1936
|
+
integerType,
|
|
1937
|
+
unknownType,
|
|
1938
|
+
emptySchemaType,
|
|
1939
|
+
enumSuffix,
|
|
1940
|
+
nameMapping
|
|
1941
|
+
};
|
|
1942
|
+
},
|
|
1943
|
+
get document() {
|
|
1944
|
+
return parsedDocument;
|
|
1945
|
+
},
|
|
1946
|
+
get inputNode() {
|
|
1947
|
+
return inputNode;
|
|
1875
1948
|
},
|
|
1876
1949
|
getImports(node, resolve) {
|
|
1877
|
-
return
|
|
1950
|
+
return _kubb_core.ast.collectImports({
|
|
1878
1951
|
node,
|
|
1879
1952
|
nameMapping,
|
|
1880
|
-
resolve
|
|
1953
|
+
resolve: (schemaName) => {
|
|
1954
|
+
const result = resolve(schemaName);
|
|
1955
|
+
if (!result) return;
|
|
1956
|
+
return _kubb_core.ast.createImport({
|
|
1957
|
+
name: [result.name],
|
|
1958
|
+
path: result.path
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1881
1961
|
});
|
|
1882
1962
|
},
|
|
1883
1963
|
async parse(source) {
|
|
1884
|
-
const
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
discriminator,
|
|
1888
|
-
collisionDetection
|
|
1889
|
-
});
|
|
1890
|
-
if (validate) try {
|
|
1891
|
-
await oas.validate();
|
|
1892
|
-
} catch (_err) {}
|
|
1893
|
-
const server = serverIndex !== void 0 ? oas.api.servers?.at(serverIndex) : void 0;
|
|
1964
|
+
const document = await parseFromConfig(source);
|
|
1965
|
+
if (validate) await validateDocument(document);
|
|
1966
|
+
const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
|
|
1894
1967
|
const baseURL = server?.url ? resolveServerUrl(server, serverVariables) : void 0;
|
|
1895
|
-
const
|
|
1968
|
+
const { root: parsedRoot, nameMapping: parsedNameMapping } = parseOas(document, {
|
|
1896
1969
|
contentType,
|
|
1897
|
-
|
|
1970
|
+
dateType,
|
|
1971
|
+
integerType,
|
|
1972
|
+
unknownType,
|
|
1973
|
+
emptySchemaType,
|
|
1974
|
+
enumSuffix
|
|
1898
1975
|
});
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
integerType,
|
|
1905
|
-
unknownType,
|
|
1906
|
-
emptySchemaType
|
|
1907
|
-
}),
|
|
1976
|
+
const node = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
|
|
1977
|
+
nameMapping = parsedNameMapping;
|
|
1978
|
+
parsedDocument = document;
|
|
1979
|
+
inputNode = _kubb_core.ast.createInput({
|
|
1980
|
+
...node,
|
|
1908
1981
|
meta: {
|
|
1909
|
-
title:
|
|
1910
|
-
description:
|
|
1911
|
-
version:
|
|
1982
|
+
title: document.info?.title,
|
|
1983
|
+
description: document.info?.description,
|
|
1984
|
+
version: document.info?.version,
|
|
1912
1985
|
baseURL
|
|
1913
1986
|
}
|
|
1914
1987
|
});
|
|
1988
|
+
return inputNode;
|
|
1915
1989
|
}
|
|
1916
1990
|
};
|
|
1917
1991
|
});
|
|
1918
|
-
function sourceToFakeConfig(source) {
|
|
1919
|
-
switch (source.type) {
|
|
1920
|
-
case "path": return {
|
|
1921
|
-
root: node_path.default.dirname(source.path),
|
|
1922
|
-
input: { path: source.path }
|
|
1923
|
-
};
|
|
1924
|
-
case "data": return {
|
|
1925
|
-
root: process.cwd(),
|
|
1926
|
-
input: { data: source.data }
|
|
1927
|
-
};
|
|
1928
|
-
case "paths": return {
|
|
1929
|
-
root: source.paths[0] ? node_path.default.dirname(source.paths[0]) : process.cwd(),
|
|
1930
|
-
input: source.paths.map((p) => ({ path: p }))
|
|
1931
|
-
};
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
1992
|
//#endregion
|
|
1993
|
+
//#region src/types.ts
|
|
1994
|
+
/**
|
|
1995
|
+
* Maps uppercase HTTP method names to lowercase for backwards compatibility.
|
|
1996
|
+
*
|
|
1997
|
+
* @example
|
|
1998
|
+
* ```ts
|
|
1999
|
+
* HttpMethods['GET'] // 'get'
|
|
2000
|
+
* HttpMethods['POST'] // 'post'
|
|
2001
|
+
* ```
|
|
2002
|
+
*/
|
|
2003
|
+
const HttpMethods = Object.fromEntries(Object.entries(_kubb_core.ast.httpMethods).map(([lower, upper]) => [upper, lower]));
|
|
2004
|
+
//#endregion
|
|
2005
|
+
exports.HttpMethods = HttpMethods;
|
|
1935
2006
|
exports.adapterOas = adapterOas;
|
|
1936
2007
|
exports.adapterOasName = adapterOasName;
|
|
2008
|
+
exports.mergeDocuments = mergeDocuments;
|
|
2009
|
+
exports.parseDocument = parseDocument;
|
|
2010
|
+
exports.parseFromConfig = parseFromConfig;
|
|
2011
|
+
exports.validateDocument = validateDocument;
|
|
1937
2012
|
|
|
1938
2013
|
//# sourceMappingURL=index.cjs.map
|