@kubb/adapter-oas 5.0.0-alpha.17 → 5.0.0-alpha.18
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 +746 -814
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +72 -125
- package/dist/index.js +745 -813
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/adapter.ts +52 -66
- package/src/constants.ts +56 -16
- package/src/discriminator.ts +99 -0
- package/src/factory.ts +151 -0
- package/src/guards.ts +68 -0
- package/src/parser.ts +154 -362
- package/src/refs.ts +57 -0
- package/src/resolvers.ts +490 -0
- package/src/types.ts +173 -37
- package/src/oas/Oas.ts +0 -615
- package/src/oas/resolveServerUrl.ts +0 -47
- package/src/oas/types.ts +0 -77
- package/src/oas/utils.ts +0 -401
package/dist/index.cjs
CHANGED
|
@@ -21,27 +21,35 @@ 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
24
|
let _kubb_ast = require("@kubb/ast");
|
|
27
25
|
let _kubb_core = require("@kubb/core");
|
|
26
|
+
let node_path = require("node:path");
|
|
27
|
+
node_path = __toESM(node_path);
|
|
28
28
|
let _redocly_openapi_core = require("@redocly/openapi-core");
|
|
29
29
|
let _stoplight_yaml = require("@stoplight/yaml");
|
|
30
30
|
_stoplight_yaml = __toESM(_stoplight_yaml);
|
|
31
|
-
let oas_types = require("oas/types");
|
|
32
31
|
let oas_normalize = require("oas-normalize");
|
|
33
32
|
oas_normalize = __toESM(oas_normalize);
|
|
34
33
|
let remeda = require("remeda");
|
|
35
34
|
let swagger2openapi = require("swagger2openapi");
|
|
36
35
|
swagger2openapi = __toESM(swagger2openapi);
|
|
37
|
-
let jsonpointer = require("jsonpointer");
|
|
38
|
-
jsonpointer = __toESM(jsonpointer);
|
|
39
36
|
let oas = require("oas");
|
|
40
37
|
oas = __toESM(oas);
|
|
38
|
+
let jsonpointer = require("jsonpointer");
|
|
39
|
+
jsonpointer = __toESM(jsonpointer);
|
|
40
|
+
let oas_types = require("oas/types");
|
|
41
41
|
let oas_utils = require("oas/utils");
|
|
42
42
|
//#region src/constants.ts
|
|
43
43
|
/**
|
|
44
|
-
* Default
|
|
44
|
+
* Default parser options applied when no explicit options are provided.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
49
|
+
*
|
|
50
|
+
* const parser = createOasParser(oas)
|
|
51
|
+
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
52
|
+
* ```
|
|
45
53
|
*/
|
|
46
54
|
const DEFAULT_PARSER_OPTIONS = {
|
|
47
55
|
dateType: "string",
|
|
@@ -51,21 +59,30 @@ const DEFAULT_PARSER_OPTIONS = {
|
|
|
51
59
|
enumSuffix: "enum"
|
|
52
60
|
};
|
|
53
61
|
/**
|
|
54
|
-
* OpenAPI version string written into
|
|
62
|
+
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
55
63
|
*/
|
|
56
64
|
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
57
65
|
/**
|
|
58
|
-
* Fallback `info.title`
|
|
66
|
+
* Fallback `info.title` placed in the stub document when merging multiple API files.
|
|
59
67
|
*/
|
|
60
68
|
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
61
69
|
/**
|
|
62
|
-
* Fallback `info.version`
|
|
70
|
+
* Fallback `info.version` placed in the stub document when merging multiple API files.
|
|
63
71
|
*/
|
|
64
72
|
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
65
73
|
/**
|
|
66
|
-
* JSON Schema keywords that
|
|
67
|
-
*
|
|
68
|
-
*
|
|
74
|
+
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
75
|
+
*
|
|
76
|
+
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
77
|
+
* intersection member rather than being merged into the parent.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
82
|
+
*
|
|
83
|
+
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
84
|
+
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
85
|
+
* ```
|
|
69
86
|
*/
|
|
70
87
|
const structuralKeys = new Set([
|
|
71
88
|
"properties",
|
|
@@ -77,14 +94,20 @@ const structuralKeys = new Set([
|
|
|
77
94
|
"not"
|
|
78
95
|
]);
|
|
79
96
|
/**
|
|
80
|
-
*
|
|
97
|
+
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
81
98
|
*
|
|
82
|
-
* Only formats
|
|
83
|
-
* `int64`, `date-time`, `date`,
|
|
84
|
-
*
|
|
99
|
+
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
100
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
101
|
+
* in the parser. `ipv4`, `ipv6`, and `hostname` map to `'url'` as the closest supported scalar.
|
|
85
102
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* import { formatMap } from '@kubb/adapter-oas'
|
|
106
|
+
*
|
|
107
|
+
* formatMap['uuid'] // 'uuid'
|
|
108
|
+
* formatMap['binary'] // 'blob'
|
|
109
|
+
* formatMap['float'] // 'number'
|
|
110
|
+
* ```
|
|
88
111
|
*/
|
|
89
112
|
const formatMap = {
|
|
90
113
|
uuid: "uuid",
|
|
@@ -104,32 +127,104 @@ const formatMap = {
|
|
|
104
127
|
double: "number"
|
|
105
128
|
};
|
|
106
129
|
/**
|
|
107
|
-
* Vendor extension keys
|
|
108
|
-
*
|
|
130
|
+
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
135
|
+
*
|
|
136
|
+
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
137
|
+
* ```
|
|
109
138
|
*/
|
|
110
139
|
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
140
|
+
/**
|
|
141
|
+
* Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
|
|
142
|
+
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
143
|
+
*/
|
|
144
|
+
const typeOptionMap = new Map([
|
|
145
|
+
["any", _kubb_ast.schemaTypes.any],
|
|
146
|
+
["unknown", _kubb_ast.schemaTypes.unknown],
|
|
147
|
+
["void", _kubb_ast.schemaTypes.void]
|
|
148
|
+
]);
|
|
111
149
|
//#endregion
|
|
112
|
-
//#region src/
|
|
150
|
+
//#region src/discriminator.ts
|
|
113
151
|
/**
|
|
114
|
-
*
|
|
152
|
+
* Injects discriminator enum values into child schemas so they know which value identifies them.
|
|
153
|
+
*
|
|
154
|
+
* Finds every union schema in `root.schemas` that has a `discriminatorPropertyName`, collects the
|
|
155
|
+
* enum value each union member is mapped to, then adds (or replaces) that property on the matching
|
|
156
|
+
* child object schema.
|
|
115
157
|
*
|
|
116
|
-
*
|
|
117
|
-
* 1. `overrides[key]` — caller-supplied value.
|
|
118
|
-
* 2. `variable.default` — spec-defined default, coerced to `string`.
|
|
119
|
-
* 3. Variable is left unreplaced when neither is available.
|
|
158
|
+
* Returns a new `RootNode` — the original is never mutated.
|
|
120
159
|
*
|
|
121
|
-
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const { root } = parseOas(document, options)
|
|
163
|
+
* const next = applyDiscriminatorInheritance(root)
|
|
164
|
+
* ```
|
|
122
165
|
*/
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
166
|
+
function applyDiscriminatorInheritance(root) {
|
|
167
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
168
|
+
for (const schema of root.schemas) {
|
|
169
|
+
let unionNode = (0, _kubb_ast.narrowSchema)(schema, "union");
|
|
170
|
+
if (!unionNode) {
|
|
171
|
+
const intersectionMembers = (0, _kubb_ast.narrowSchema)(schema, "intersection")?.members;
|
|
172
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
173
|
+
const u = (0, _kubb_ast.narrowSchema)(m, "union");
|
|
174
|
+
if (u) {
|
|
175
|
+
unionNode = u;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
181
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
182
|
+
for (const member of members) {
|
|
183
|
+
const intersectionNode = (0, _kubb_ast.narrowSchema)(member, "intersection");
|
|
184
|
+
if (!intersectionNode?.members) continue;
|
|
185
|
+
let refNode;
|
|
186
|
+
let objNode;
|
|
187
|
+
for (const m of intersectionNode.members) {
|
|
188
|
+
refNode ??= (0, _kubb_ast.narrowSchema)(m, "ref");
|
|
189
|
+
objNode ??= (0, _kubb_ast.narrowSchema)(m, "object");
|
|
190
|
+
}
|
|
191
|
+
if (!refNode?.name || !objNode) continue;
|
|
192
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
193
|
+
const enumNode = prop ? (0, _kubb_ast.narrowSchema)(prop.schema, "enum") : void 0;
|
|
194
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
195
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
196
|
+
if (!enumValues.length) continue;
|
|
197
|
+
const existing = childMap.get(refNode.name);
|
|
198
|
+
if (existing) existing.enumValues.push(...enumValues);
|
|
199
|
+
else childMap.set(refNode.name, {
|
|
200
|
+
propertyName: discriminatorPropertyName,
|
|
201
|
+
enumValues: [...enumValues]
|
|
202
|
+
});
|
|
203
|
+
}
|
|
131
204
|
}
|
|
132
|
-
return
|
|
205
|
+
if (childMap.size === 0) return root;
|
|
206
|
+
return (0, _kubb_ast.transform)(root, { schema(node, { parent }) {
|
|
207
|
+
if (parent?.kind !== "Root" || !node.name) return;
|
|
208
|
+
const entry = childMap.get(node.name);
|
|
209
|
+
if (!entry) return;
|
|
210
|
+
const objectNode = (0, _kubb_ast.narrowSchema)(node, "object");
|
|
211
|
+
if (!objectNode) return;
|
|
212
|
+
const { propertyName, enumValues } = entry;
|
|
213
|
+
const newProp = (0, _kubb_ast.createProperty)({
|
|
214
|
+
name: propertyName,
|
|
215
|
+
required: true,
|
|
216
|
+
schema: (0, _kubb_ast.createSchema)({
|
|
217
|
+
type: "enum",
|
|
218
|
+
enumValues
|
|
219
|
+
})
|
|
220
|
+
});
|
|
221
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
222
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
223
|
+
return {
|
|
224
|
+
...objectNode,
|
|
225
|
+
properties: newProperties
|
|
226
|
+
};
|
|
227
|
+
} });
|
|
133
228
|
}
|
|
134
229
|
//#endregion
|
|
135
230
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -193,6 +288,13 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
193
288
|
//#region ../../internals/utils/src/reserved.ts
|
|
194
289
|
/**
|
|
195
290
|
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* isValidVarName('status') // true
|
|
295
|
+
* isValidVarName('class') // false (reserved word)
|
|
296
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
297
|
+
* ```
|
|
196
298
|
*/
|
|
197
299
|
function isValidVarName(name) {
|
|
198
300
|
try {
|
|
@@ -213,18 +315,33 @@ function isValidVarName(name) {
|
|
|
213
315
|
* p.template // '`/pet/${petId}`'
|
|
214
316
|
*/
|
|
215
317
|
var URLPath = class {
|
|
216
|
-
/**
|
|
318
|
+
/**
|
|
319
|
+
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
320
|
+
*/
|
|
217
321
|
path;
|
|
218
322
|
#options;
|
|
219
323
|
constructor(path, options = {}) {
|
|
220
324
|
this.path = path;
|
|
221
325
|
this.#options = options;
|
|
222
326
|
}
|
|
223
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
327
|
+
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
224
334
|
get URL() {
|
|
225
335
|
return this.toURLPath();
|
|
226
336
|
}
|
|
227
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
337
|
+
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```ts
|
|
341
|
+
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
342
|
+
* new URLPath('/pet/{petId}').isURL // false
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
228
345
|
get isURL() {
|
|
229
346
|
try {
|
|
230
347
|
return !!new URL(this.path).href;
|
|
@@ -242,11 +359,25 @@ var URLPath = class {
|
|
|
242
359
|
get template() {
|
|
243
360
|
return this.toTemplateString();
|
|
244
361
|
}
|
|
245
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
362
|
+
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* ```ts
|
|
366
|
+
* new URLPath('/pet/{petId}').object
|
|
367
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
246
370
|
get object() {
|
|
247
371
|
return this.toObject();
|
|
248
372
|
}
|
|
249
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
373
|
+
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
378
|
+
* new URLPath('/pet').params // undefined
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
250
381
|
get params() {
|
|
251
382
|
return this.getParams();
|
|
252
383
|
}
|
|
@@ -254,7 +385,9 @@ var URLPath = class {
|
|
|
254
385
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
255
386
|
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
256
387
|
}
|
|
257
|
-
/**
|
|
388
|
+
/**
|
|
389
|
+
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
390
|
+
*/
|
|
258
391
|
#eachParam(fn) {
|
|
259
392
|
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
260
393
|
const raw = match[1];
|
|
@@ -291,6 +424,12 @@ var URLPath = class {
|
|
|
291
424
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
292
425
|
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
293
426
|
* Returns `undefined` when no path parameters are found.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```ts
|
|
430
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
431
|
+
* // { petId: 'petId', tagId: 'tagId' }
|
|
432
|
+
* ```
|
|
294
433
|
*/
|
|
295
434
|
getParams(replacer) {
|
|
296
435
|
const params = {};
|
|
@@ -300,364 +439,28 @@ var URLPath = class {
|
|
|
300
439
|
});
|
|
301
440
|
return Object.keys(params).length > 0 ? params : void 0;
|
|
302
441
|
}
|
|
303
|
-
/** Converts the OpenAPI path to Express-style colon syntax
|
|
304
|
-
toURLPath() {
|
|
305
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
//#endregion
|
|
309
|
-
//#region src/oas/Oas.ts
|
|
310
|
-
/**
|
|
311
|
-
* Prefix used to create synthetic `$ref` values for anonymous (inline) discriminator schemas.
|
|
312
|
-
* The suffix is the schema index within the discriminator's `oneOf`/`anyOf` array.
|
|
313
|
-
* @example `#kubb-inline-0`
|
|
314
|
-
*/
|
|
315
|
-
const KUBB_INLINE_REF_PREFIX = "#kubb-inline-";
|
|
316
|
-
var Oas = class extends oas.default {
|
|
317
|
-
#options = { discriminator: "strict" };
|
|
318
|
-
document;
|
|
319
|
-
constructor(document) {
|
|
320
|
-
super(document, void 0);
|
|
321
|
-
this.document = document;
|
|
322
|
-
}
|
|
323
|
-
setOptions(options) {
|
|
324
|
-
this.#options = {
|
|
325
|
-
...this.#options,
|
|
326
|
-
...options
|
|
327
|
-
};
|
|
328
|
-
if (this.#options.discriminator === "inherit") this.#applyDiscriminatorInheritance();
|
|
329
|
-
}
|
|
330
|
-
get options() {
|
|
331
|
-
return this.#options;
|
|
332
|
-
}
|
|
333
|
-
get($ref) {
|
|
334
|
-
const origRef = $ref;
|
|
335
|
-
$ref = $ref.trim();
|
|
336
|
-
if ($ref === "") return null;
|
|
337
|
-
if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
338
|
-
else return null;
|
|
339
|
-
const current = jsonpointer.default.get(this.api, $ref);
|
|
340
|
-
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
341
|
-
return current;
|
|
342
|
-
}
|
|
343
|
-
getKey($ref) {
|
|
344
|
-
const key = $ref.split("/").pop();
|
|
345
|
-
return key === "" ? void 0 : key;
|
|
346
|
-
}
|
|
347
|
-
set($ref, value) {
|
|
348
|
-
$ref = $ref.trim();
|
|
349
|
-
if ($ref === "") return false;
|
|
350
|
-
if ($ref.startsWith("#")) {
|
|
351
|
-
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
352
|
-
jsonpointer.default.set(this.api, $ref, value);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
#setDiscriminator(schema) {
|
|
356
|
-
const { mapping = {}, propertyName } = schema.discriminator;
|
|
357
|
-
if (this.#options.discriminator === "inherit") Object.entries(mapping).forEach(([mappingKey, mappingValue]) => {
|
|
358
|
-
if (mappingValue) {
|
|
359
|
-
const childSchema = this.get(mappingValue);
|
|
360
|
-
if (!childSchema) return;
|
|
361
|
-
if (!childSchema.properties) childSchema.properties = {};
|
|
362
|
-
const property = childSchema.properties[propertyName];
|
|
363
|
-
if (childSchema.properties) {
|
|
364
|
-
childSchema.properties[propertyName] = {
|
|
365
|
-
...childSchema.properties ? childSchema.properties[propertyName] : {},
|
|
366
|
-
enum: [...property?.enum?.filter((value) => value !== mappingKey) ?? [], mappingKey]
|
|
367
|
-
};
|
|
368
|
-
childSchema.required = typeof childSchema.required === "boolean" ? childSchema.required : [...new Set([...childSchema.required ?? [], propertyName])];
|
|
369
|
-
this.set(mappingValue, childSchema);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
getDiscriminator(schema) {
|
|
375
|
-
if (!isDiscriminator(schema) || !schema) return null;
|
|
376
|
-
const { mapping = {}, propertyName } = schema.discriminator;
|
|
377
|
-
/**
|
|
378
|
-
* Helper to extract discriminator value from a schema.
|
|
379
|
-
* Checks in order:
|
|
380
|
-
* 1. Extension property matching propertyName (e.g., x-linode-ref-name)
|
|
381
|
-
* 2. Property with const value
|
|
382
|
-
* 3. Property with single enum value
|
|
383
|
-
* 4. Title as fallback
|
|
384
|
-
*/
|
|
385
|
-
const getDiscriminatorValue = (schema) => {
|
|
386
|
-
if (!schema) return null;
|
|
387
|
-
if (propertyName.startsWith("x-")) {
|
|
388
|
-
const extensionValue = schema[propertyName];
|
|
389
|
-
if (extensionValue && typeof extensionValue === "string") return extensionValue;
|
|
390
|
-
}
|
|
391
|
-
const propertySchema = schema.properties?.[propertyName];
|
|
392
|
-
if (propertySchema && "const" in propertySchema && propertySchema.const !== void 0) return String(propertySchema.const);
|
|
393
|
-
if (propertySchema && propertySchema.enum?.length === 1) return String(propertySchema.enum[0]);
|
|
394
|
-
return schema.title || null;
|
|
395
|
-
};
|
|
396
|
-
/**
|
|
397
|
-
* Process oneOf/anyOf items to build mapping.
|
|
398
|
-
* Handles both $ref and inline schemas.
|
|
399
|
-
*/
|
|
400
|
-
const processSchemas = (schemas, existingMapping) => {
|
|
401
|
-
schemas.forEach((schemaItem, index) => {
|
|
402
|
-
if (isReference(schemaItem)) {
|
|
403
|
-
const key = this.getKey(schemaItem.$ref);
|
|
404
|
-
try {
|
|
405
|
-
const discriminatorValue = getDiscriminatorValue(this.get(schemaItem.$ref));
|
|
406
|
-
const canAdd = key && !Object.values(existingMapping).includes(schemaItem.$ref);
|
|
407
|
-
if (canAdd && discriminatorValue) existingMapping[discriminatorValue] = schemaItem.$ref;
|
|
408
|
-
else if (canAdd) existingMapping[key] = schemaItem.$ref;
|
|
409
|
-
} catch (_error) {
|
|
410
|
-
if (key && !Object.values(existingMapping).includes(schemaItem.$ref)) existingMapping[key] = schemaItem.$ref;
|
|
411
|
-
}
|
|
412
|
-
} else {
|
|
413
|
-
const discriminatorValue = getDiscriminatorValue(schemaItem);
|
|
414
|
-
if (discriminatorValue) existingMapping[discriminatorValue] = `${KUBB_INLINE_REF_PREFIX}${index}`;
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
};
|
|
418
|
-
if (schema.oneOf) processSchemas(schema.oneOf, mapping);
|
|
419
|
-
if (schema.anyOf) processSchemas(schema.anyOf, mapping);
|
|
420
|
-
return {
|
|
421
|
-
...schema.discriminator,
|
|
422
|
-
mapping
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
dereferenceWithRef(schema) {
|
|
426
|
-
if (isReference(schema)) return {
|
|
427
|
-
...schema,
|
|
428
|
-
...this.get(schema.$ref),
|
|
429
|
-
$ref: schema.$ref
|
|
430
|
-
};
|
|
431
|
-
return schema;
|
|
432
|
-
}
|
|
433
|
-
#applyDiscriminatorInheritance() {
|
|
434
|
-
const components = this.api.components;
|
|
435
|
-
if (!components?.schemas) return;
|
|
436
|
-
const visited = /* @__PURE__ */ new WeakSet();
|
|
437
|
-
const enqueue = (value) => {
|
|
438
|
-
if (!value) return;
|
|
439
|
-
if (Array.isArray(value)) {
|
|
440
|
-
for (const item of value) enqueue(item);
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
if (typeof value === "object") visit(value);
|
|
444
|
-
};
|
|
445
|
-
const visit = (schema) => {
|
|
446
|
-
if (!schema || typeof schema !== "object") return;
|
|
447
|
-
if (isReference(schema)) {
|
|
448
|
-
visit(this.get(schema.$ref));
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
const schemaObject = schema;
|
|
452
|
-
if (visited.has(schemaObject)) return;
|
|
453
|
-
visited.add(schemaObject);
|
|
454
|
-
if (isDiscriminator(schemaObject)) this.#setDiscriminator(schemaObject);
|
|
455
|
-
if ("allOf" in schemaObject) enqueue(schemaObject.allOf);
|
|
456
|
-
if ("oneOf" in schemaObject) enqueue(schemaObject.oneOf);
|
|
457
|
-
if ("anyOf" in schemaObject) enqueue(schemaObject.anyOf);
|
|
458
|
-
if ("not" in schemaObject) enqueue(schemaObject.not);
|
|
459
|
-
if ("items" in schemaObject) enqueue(schemaObject.items);
|
|
460
|
-
if ("prefixItems" in schemaObject) enqueue(schemaObject.prefixItems);
|
|
461
|
-
if (schemaObject.properties) enqueue(Object.values(schemaObject.properties));
|
|
462
|
-
if (schemaObject.additionalProperties && typeof schemaObject.additionalProperties === "object") enqueue(schemaObject.additionalProperties);
|
|
463
|
-
};
|
|
464
|
-
for (const schema of Object.values(components.schemas)) visit(schema);
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Oas does not have a getResponseBody(contentType)
|
|
468
|
-
*/
|
|
469
|
-
#getResponseBodyFactory(responseBody) {
|
|
470
|
-
function hasResponseBody(res = responseBody) {
|
|
471
|
-
return !!res;
|
|
472
|
-
}
|
|
473
|
-
return (contentType) => {
|
|
474
|
-
if (!hasResponseBody(responseBody)) return false;
|
|
475
|
-
if (isReference(responseBody)) return false;
|
|
476
|
-
if (!responseBody.content) return false;
|
|
477
|
-
if (contentType) {
|
|
478
|
-
if (!(contentType in responseBody.content)) return false;
|
|
479
|
-
return responseBody.content[contentType];
|
|
480
|
-
}
|
|
481
|
-
let availableContentType;
|
|
482
|
-
const contentTypes = Object.keys(responseBody.content);
|
|
483
|
-
contentTypes.forEach((mt) => {
|
|
484
|
-
if (!availableContentType && oas_utils.matchesMimeType.json(mt)) availableContentType = mt;
|
|
485
|
-
});
|
|
486
|
-
if (!availableContentType) contentTypes.forEach((mt) => {
|
|
487
|
-
if (!availableContentType) availableContentType = mt;
|
|
488
|
-
});
|
|
489
|
-
if (availableContentType) return [
|
|
490
|
-
availableContentType,
|
|
491
|
-
responseBody.content[availableContentType],
|
|
492
|
-
...responseBody.description ? [responseBody.description] : []
|
|
493
|
-
];
|
|
494
|
-
return false;
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
getResponseSchema(operation, statusCode) {
|
|
498
|
-
if (operation.schema.responses) Object.keys(operation.schema.responses).forEach((key) => {
|
|
499
|
-
const schema = operation.schema.responses[key];
|
|
500
|
-
const $ref = isReference(schema) ? schema.$ref : void 0;
|
|
501
|
-
if (schema && $ref) operation.schema.responses[key] = this.get($ref);
|
|
502
|
-
});
|
|
503
|
-
const getResponseBody = this.#getResponseBodyFactory(operation.getResponseByStatusCode(statusCode));
|
|
504
|
-
const { contentType } = this.#options;
|
|
505
|
-
const responseBody = getResponseBody(contentType);
|
|
506
|
-
if (responseBody === false) return {};
|
|
507
|
-
const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
|
|
508
|
-
if (!schema) return {};
|
|
509
|
-
return this.dereferenceWithRef(schema);
|
|
510
|
-
}
|
|
511
|
-
getRequestSchema(operation) {
|
|
512
|
-
const { contentType } = this.#options;
|
|
513
|
-
if (operation.schema.requestBody) operation.schema.requestBody = this.dereferenceWithRef(operation.schema.requestBody);
|
|
514
|
-
const requestBody = operation.getRequestBody(contentType);
|
|
515
|
-
if (requestBody === false) return;
|
|
516
|
-
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
517
|
-
if (!schema) return;
|
|
518
|
-
return this.dereferenceWithRef(schema);
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Returns all resolved parameters for an operation, merging path-level and operation-level
|
|
522
|
-
* parameters and deduplicating by `in:name` (operation-level takes precedence).
|
|
442
|
+
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
523
443
|
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
|
|
528
|
-
getParameters(operation) {
|
|
529
|
-
const resolveParams = (params) => params.map((p) => this.dereferenceWithRef(p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
|
|
530
|
-
const operationParams = resolveParams(operation.schema?.parameters || []);
|
|
531
|
-
const pathItem = this.api?.paths?.[operation.path];
|
|
532
|
-
const pathLevelParams = resolveParams(pathItem && !isReference(pathItem) && pathItem.parameters ? pathItem.parameters : []);
|
|
533
|
-
const paramMap = /* @__PURE__ */ new Map();
|
|
534
|
-
for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
535
|
-
for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
536
|
-
return Array.from(paramMap.values());
|
|
537
|
-
}
|
|
538
|
-
getParametersSchema(operation, inKey) {
|
|
539
|
-
const { contentType = operation.getContentType() } = this.#options;
|
|
540
|
-
const params = this.getParameters(operation).filter((v) => v.in === inKey);
|
|
541
|
-
if (!params.length) return null;
|
|
542
|
-
return params.reduce((schema, pathParameters) => {
|
|
543
|
-
const property = pathParameters.content?.[contentType]?.schema ?? pathParameters.schema;
|
|
544
|
-
const required = typeof schema.required === "boolean" ? schema.required : [...schema.required || [], pathParameters.required ? pathParameters.name : void 0].filter(Boolean);
|
|
545
|
-
const getDefaultStyle = (location) => {
|
|
546
|
-
if (location === "query") return "form";
|
|
547
|
-
if (location === "path") return "simple";
|
|
548
|
-
return "simple";
|
|
549
|
-
};
|
|
550
|
-
const style = pathParameters.style || getDefaultStyle(inKey);
|
|
551
|
-
const explode = pathParameters.explode !== void 0 ? pathParameters.explode : style === "form";
|
|
552
|
-
if (inKey === "query" && style === "form" && explode === true && property?.type === "object" && property?.additionalProperties && !property?.properties) return {
|
|
553
|
-
...schema,
|
|
554
|
-
description: pathParameters.description || schema.description,
|
|
555
|
-
deprecated: schema.deprecated,
|
|
556
|
-
example: property.example || schema.example,
|
|
557
|
-
additionalProperties: property.additionalProperties
|
|
558
|
-
};
|
|
559
|
-
return {
|
|
560
|
-
...schema,
|
|
561
|
-
description: schema.description,
|
|
562
|
-
deprecated: schema.deprecated,
|
|
563
|
-
example: schema.example,
|
|
564
|
-
required,
|
|
565
|
-
properties: {
|
|
566
|
-
...schema.properties,
|
|
567
|
-
[pathParameters.name]: {
|
|
568
|
-
description: pathParameters.description,
|
|
569
|
-
...property
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
|
-
}, {
|
|
574
|
-
type: "object",
|
|
575
|
-
required: [],
|
|
576
|
-
properties: {}
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
async validate() {
|
|
580
|
-
return validate(this.api);
|
|
581
|
-
}
|
|
582
|
-
flattenSchema(schema) {
|
|
583
|
-
return flattenSchema(schema);
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Get schemas from OpenAPI components (schemas, responses, requestBodies).
|
|
587
|
-
* Returns schemas in dependency order along with name mapping for collision resolution.
|
|
444
|
+
* @example
|
|
445
|
+
* ```ts
|
|
446
|
+
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
447
|
+
* ```
|
|
588
448
|
*/
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const includes = options.includes ?? [
|
|
592
|
-
"schemas",
|
|
593
|
-
"requestBodies",
|
|
594
|
-
"responses"
|
|
595
|
-
];
|
|
596
|
-
const components = this.getDefinition().components;
|
|
597
|
-
const schemasWithMeta = [];
|
|
598
|
-
if (includes.includes("schemas")) {
|
|
599
|
-
const componentSchemas = components?.schemas || {};
|
|
600
|
-
for (const [name, schemaObject] of Object.entries(componentSchemas)) {
|
|
601
|
-
let schema = schemaObject;
|
|
602
|
-
if (isReference(schemaObject)) {
|
|
603
|
-
const resolved = this.get(schemaObject.$ref);
|
|
604
|
-
if (resolved && !isReference(resolved)) schema = resolved;
|
|
605
|
-
}
|
|
606
|
-
schemasWithMeta.push({
|
|
607
|
-
schema,
|
|
608
|
-
source: "schemas",
|
|
609
|
-
originalName: name
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
if (includes.includes("responses")) {
|
|
614
|
-
const responses = components?.responses || {};
|
|
615
|
-
for (const [name, response] of Object.entries(responses)) {
|
|
616
|
-
const schema = extractSchemaFromContent(response.content, contentType);
|
|
617
|
-
if (schema) {
|
|
618
|
-
let resolvedSchema = schema;
|
|
619
|
-
if (isReference(schema)) {
|
|
620
|
-
const resolved = this.get(schema.$ref);
|
|
621
|
-
if (resolved && !isReference(resolved)) resolvedSchema = resolved;
|
|
622
|
-
}
|
|
623
|
-
schemasWithMeta.push({
|
|
624
|
-
schema: resolvedSchema,
|
|
625
|
-
source: "responses",
|
|
626
|
-
originalName: name
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
if (includes.includes("requestBodies")) {
|
|
632
|
-
const requestBodies = components?.requestBodies || {};
|
|
633
|
-
for (const [name, request] of Object.entries(requestBodies)) {
|
|
634
|
-
const schema = extractSchemaFromContent(request.content, contentType);
|
|
635
|
-
if (schema) {
|
|
636
|
-
let resolvedSchema = schema;
|
|
637
|
-
if (isReference(schema)) {
|
|
638
|
-
const resolved = this.get(schema.$ref);
|
|
639
|
-
if (resolved && !isReference(resolved)) resolvedSchema = resolved;
|
|
640
|
-
}
|
|
641
|
-
schemasWithMeta.push({
|
|
642
|
-
schema: resolvedSchema,
|
|
643
|
-
source: "requestBodies",
|
|
644
|
-
originalName: name
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
const { schemas, nameMapping } = resolveCollisions(schemasWithMeta);
|
|
650
|
-
return {
|
|
651
|
-
schemas: sortSchemas(schemas),
|
|
652
|
-
nameMapping
|
|
653
|
-
};
|
|
449
|
+
toURLPath() {
|
|
450
|
+
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
654
451
|
}
|
|
655
452
|
};
|
|
656
453
|
//#endregion
|
|
657
|
-
//#region src/
|
|
454
|
+
//#region src/guards.ts
|
|
658
455
|
/**
|
|
659
|
-
*
|
|
660
|
-
*
|
|
456
|
+
* Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```ts
|
|
460
|
+
* if (isOpenApiV2Document(doc)) {
|
|
461
|
+
* // doc is OpenAPIV2.Document
|
|
462
|
+
* }
|
|
463
|
+
* ```
|
|
661
464
|
*/
|
|
662
465
|
function isOpenApiV2Document(doc) {
|
|
663
466
|
return !!doc && (0, remeda.isPlainObject)(doc) && !("openapi" in doc);
|
|
@@ -665,9 +468,15 @@ function isOpenApiV2Document(doc) {
|
|
|
665
468
|
/**
|
|
666
469
|
* Returns `true` when a schema should be treated as nullable.
|
|
667
470
|
*
|
|
668
|
-
*
|
|
669
|
-
* -
|
|
670
|
-
*
|
|
471
|
+
* Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
|
|
472
|
+
* `x-nullable: true` (vendor extension), `type: 'null'`, and `type: ['null', ...]` (OAS 3.1).
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```ts
|
|
476
|
+
* isNullable({ type: 'string', nullable: true }) // true
|
|
477
|
+
* isNullable({ type: ['string', 'null'] }) // true
|
|
478
|
+
* isNullable({ type: 'string' }) // false
|
|
479
|
+
* ```
|
|
671
480
|
*/
|
|
672
481
|
function isNullable(schema) {
|
|
673
482
|
if ((schema?.nullable ?? schema?.["x-nullable"]) === true) return true;
|
|
@@ -677,34 +486,51 @@ function isNullable(schema) {
|
|
|
677
486
|
return false;
|
|
678
487
|
}
|
|
679
488
|
/**
|
|
680
|
-
*
|
|
681
|
-
*
|
|
489
|
+
* Returns `true` when `obj` is an OpenAPI `$ref` pointer object.
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```ts
|
|
493
|
+
* isReference({ $ref: '#/components/schemas/Pet' }) // true
|
|
494
|
+
* isReference({ type: 'string' }) // false
|
|
495
|
+
* ```
|
|
682
496
|
*/
|
|
683
497
|
function isReference(obj) {
|
|
684
|
-
return !!obj &&
|
|
498
|
+
return !!obj && typeof obj === "object" && "$ref" in obj;
|
|
685
499
|
}
|
|
686
500
|
/**
|
|
687
|
-
* Returns `true` when `obj` is a schema
|
|
688
|
-
*
|
|
501
|
+
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object.
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```ts
|
|
505
|
+
* isDiscriminator({ discriminator: { propertyName: 'type', mapping: {} } }) // true
|
|
506
|
+
* isDiscriminator({ discriminator: 'type' }) // false (Swagger 2 string form)
|
|
507
|
+
* ```
|
|
689
508
|
*/
|
|
690
509
|
function isDiscriminator(obj) {
|
|
691
510
|
const record = obj;
|
|
692
511
|
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
693
512
|
}
|
|
513
|
+
//#endregion
|
|
514
|
+
//#region src/factory.ts
|
|
694
515
|
/**
|
|
695
|
-
* Loads
|
|
516
|
+
* Loads and dereferences an OpenAPI document, returning the raw `Document`.
|
|
696
517
|
*
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
518
|
+
* Accepts a file path string or an already-parsed document object. File paths are bundled via
|
|
519
|
+
* Redocly to resolve external `$ref`s. Swagger 2.0 documents are automatically up-converted
|
|
520
|
+
* to OpenAPI 3.0 via `swagger2openapi`.
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```ts
|
|
524
|
+
* const document = await parseDocument('./openapi.yaml')
|
|
525
|
+
* const document = await parse(rawDocumentObject, { canBundle: false })
|
|
526
|
+
* ```
|
|
700
527
|
*/
|
|
701
|
-
async function
|
|
702
|
-
if (typeof pathOrApi === "string" && canBundle) return
|
|
528
|
+
async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true } = {}) {
|
|
529
|
+
if (typeof pathOrApi === "string" && canBundle) return parseDocument((await (0, _redocly_openapi_core.bundle)({
|
|
703
530
|
ref: pathOrApi,
|
|
704
531
|
config: await (0, _redocly_openapi_core.loadConfig)(),
|
|
705
532
|
base: pathOrApi
|
|
706
533
|
})).bundle.parsed, {
|
|
707
|
-
oasClass,
|
|
708
534
|
canBundle,
|
|
709
535
|
enablePaths
|
|
710
536
|
});
|
|
@@ -714,26 +540,27 @@ async function parse(pathOrApi, { oasClass = Oas, canBundle = true, enablePaths
|
|
|
714
540
|
}).load();
|
|
715
541
|
if (isOpenApiV2Document(document)) {
|
|
716
542
|
const { openapi } = await swagger2openapi.default.convertObj(document, { anchors: true });
|
|
717
|
-
return
|
|
543
|
+
return openapi;
|
|
718
544
|
}
|
|
719
|
-
return
|
|
545
|
+
return document;
|
|
720
546
|
}
|
|
721
547
|
/**
|
|
722
|
-
* Deep-merges multiple OpenAPI documents into a single `
|
|
548
|
+
* Deep-merges multiple OpenAPI documents into a single `Document`.
|
|
723
549
|
*
|
|
724
|
-
* Each document is parsed independently
|
|
725
|
-
*
|
|
726
|
-
* the returned `Oas` instance is fully initialized.
|
|
550
|
+
* Each document is parsed independently then recursively merged with `remeda`'s `mergeDeep`.
|
|
551
|
+
* Throws when the input array is empty.
|
|
727
552
|
*
|
|
728
|
-
*
|
|
553
|
+
* @example
|
|
554
|
+
* ```ts
|
|
555
|
+
* const document = await mergeDocuments(['./pets.yaml', './orders.yaml'])
|
|
556
|
+
* ```
|
|
729
557
|
*/
|
|
730
|
-
async function
|
|
731
|
-
const
|
|
732
|
-
oasClass,
|
|
558
|
+
async function mergeDocuments(pathOrApi) {
|
|
559
|
+
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
|
|
733
560
|
enablePaths: false,
|
|
734
561
|
canBundle: false
|
|
735
562
|
})));
|
|
736
|
-
if (
|
|
563
|
+
if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
|
|
737
564
|
const seed = {
|
|
738
565
|
openapi: MERGE_OPENAPI_VERSION,
|
|
739
566
|
info: {
|
|
@@ -743,38 +570,248 @@ async function merge(pathOrApi, { oasClass = Oas } = {}) {
|
|
|
743
570
|
paths: {},
|
|
744
571
|
components: { schemas: {} }
|
|
745
572
|
};
|
|
746
|
-
return
|
|
573
|
+
return parseDocument(documents.reduce((acc, current) => (0, remeda.mergeDeep)(acc, current), seed));
|
|
747
574
|
}
|
|
748
575
|
/**
|
|
749
|
-
*
|
|
576
|
+
* Creates a `Document` from an `AdapterSource`.
|
|
750
577
|
*
|
|
751
|
-
* Handles all three
|
|
752
|
-
* - `
|
|
753
|
-
* -
|
|
754
|
-
* - `
|
|
578
|
+
* Handles all three source types:
|
|
579
|
+
* - `{ type: 'path' }` — resolves and bundles a local file path or remote URL.
|
|
580
|
+
* - `{ type: 'paths' }` — merges multiple file paths into a single document.
|
|
581
|
+
* - `{ type: 'data' }` — parses an inline string (YAML/JSON) or raw object.
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* const document = await parseFromConfig({ type: 'path', path: './openapi.yaml' })
|
|
586
|
+
* const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
|
|
587
|
+
* ```
|
|
755
588
|
*/
|
|
756
|
-
function parseFromConfig(
|
|
757
|
-
if ("data"
|
|
758
|
-
if (typeof
|
|
589
|
+
function parseFromConfig(source) {
|
|
590
|
+
if (source.type === "data") {
|
|
591
|
+
if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
|
|
759
592
|
try {
|
|
760
|
-
return
|
|
593
|
+
return parseDocument(_stoplight_yaml.default.parse(source.data));
|
|
761
594
|
} catch {
|
|
762
|
-
return
|
|
595
|
+
return parseDocument(source.data);
|
|
763
596
|
}
|
|
764
597
|
}
|
|
765
|
-
if (
|
|
766
|
-
if (new URLPath(
|
|
767
|
-
return
|
|
598
|
+
if (source.type === "paths") return mergeDocuments(source.paths);
|
|
599
|
+
if (new URLPath(source.path).isURL) return parseDocument(source.path);
|
|
600
|
+
return parseDocument(node_path.default.resolve(node_path.default.dirname(source.path), source.path));
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Validates an OpenAPI document using `oas-normalize` with colorized error output.
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```ts
|
|
607
|
+
* await validateDocument(document)
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
async function validateDocument(document) {
|
|
611
|
+
try {
|
|
612
|
+
await new oas_normalize.default(document, {
|
|
613
|
+
enablePaths: true,
|
|
614
|
+
colorizeErrors: true
|
|
615
|
+
}).validate({ parser: { validate: { errors: { colorize: true } } } });
|
|
616
|
+
} catch (_err) {}
|
|
617
|
+
}
|
|
618
|
+
//#endregion
|
|
619
|
+
//#region src/refs.ts
|
|
620
|
+
/**
|
|
621
|
+
* Resolves a local JSON pointer reference from a document.
|
|
622
|
+
*
|
|
623
|
+
* Accepts `#/...` refs. Returns `null` for empty or non-local refs.
|
|
624
|
+
* Throws when the pointer cannot be resolved.
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```ts
|
|
628
|
+
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet') // SchemaObject | null
|
|
629
|
+
* ```
|
|
630
|
+
*/
|
|
631
|
+
function resolveRef(document, $ref) {
|
|
632
|
+
const origRef = $ref;
|
|
633
|
+
$ref = $ref.trim();
|
|
634
|
+
if ($ref === "") return null;
|
|
635
|
+
if ($ref.startsWith("#")) $ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
636
|
+
else return null;
|
|
637
|
+
const current = jsonpointer.default.get(document, $ref);
|
|
638
|
+
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
639
|
+
return current;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Resolves a `$ref` object while preserving the original `$ref` field on the result.
|
|
643
|
+
*
|
|
644
|
+
* Useful for parser flows that need both dereferenced fields and pointer
|
|
645
|
+
* identity (for naming/import purposes). Non-reference values are returned as-is.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```ts
|
|
649
|
+
* dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
|
|
650
|
+
* // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
|
|
651
|
+
* ```
|
|
652
|
+
*/
|
|
653
|
+
function dereferenceWithRef(document, schema) {
|
|
654
|
+
if (isReference(schema)) return {
|
|
655
|
+
...schema,
|
|
656
|
+
...resolveRef(document, schema.$ref),
|
|
657
|
+
$ref: schema.$ref
|
|
658
|
+
};
|
|
659
|
+
return schema;
|
|
768
660
|
}
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region src/resolvers.ts
|
|
769
663
|
/**
|
|
770
|
-
*
|
|
664
|
+
* Resolves `{variable}` placeholders in an OpenAPI server URL.
|
|
771
665
|
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
* `$ref`. When any member carries structural meaning, the schema is returned unchanged.
|
|
666
|
+
* Resolution order per variable: `overrides[key]` → `variable.default` → left unreplaced.
|
|
667
|
+
* Throws when an override value is not in the variable's allowed `enum` list.
|
|
775
668
|
*
|
|
776
|
-
*
|
|
777
|
-
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```ts
|
|
671
|
+
* resolveServerUrl(
|
|
672
|
+
* { url: 'https://{env}.api.example.com', variables: { env: { default: 'dev', enum: ['dev', 'prod'] } } },
|
|
673
|
+
* { env: 'prod' },
|
|
674
|
+
* )
|
|
675
|
+
* // 'https://prod.api.example.com'
|
|
676
|
+
* ```
|
|
677
|
+
*/
|
|
678
|
+
function resolveServerUrl(server, overrides) {
|
|
679
|
+
if (!server.variables) return server.url;
|
|
680
|
+
let url = server.url;
|
|
681
|
+
for (const [key, variable] of Object.entries(server.variables)) {
|
|
682
|
+
const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
|
|
683
|
+
if (value === void 0) continue;
|
|
684
|
+
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(", ")}.`);
|
|
685
|
+
url = url.replaceAll(`{${key}}`, value);
|
|
686
|
+
}
|
|
687
|
+
return url;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Looks up the Kubb `SchemaType` for a given OAS `format` string.
|
|
691
|
+
* Returns `undefined` for formats not in `formatMap` (e.g. `int64`, `date-time`),
|
|
692
|
+
* which are handled separately because their output depends on parser options.
|
|
693
|
+
*/
|
|
694
|
+
function getSchemaType(format) {
|
|
695
|
+
return formatMap[format] ?? null;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Maps an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
|
|
699
|
+
* Numeric types (`number`, `integer`, `bigint`) are returned unchanged;
|
|
700
|
+
* `boolean` maps to `'boolean'`; everything else defaults to `'string'`.
|
|
701
|
+
*/
|
|
702
|
+
function getPrimitiveType(type) {
|
|
703
|
+
if (type === "number" || type === "integer" || type === "bigint") return type;
|
|
704
|
+
if (type === "boolean") return "boolean";
|
|
705
|
+
return "string";
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Narrows a raw content-type string to the `MediaType` union recognized by Kubb.
|
|
709
|
+
* Returns `undefined` for content types not present in `KNOWN_MEDIA_TYPES`.
|
|
710
|
+
*/
|
|
711
|
+
function getMediaType(contentType) {
|
|
712
|
+
return Object.values(_kubb_ast.mediaTypes).includes(contentType) ? contentType : null;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Returns all resolved parameters for an operation, merging path-level and operation-level entries.
|
|
716
|
+
*
|
|
717
|
+
* Operation-level parameters take precedence over path-level ones with the same `in:name` key.
|
|
718
|
+
* `$ref` parameters are resolved via `dereferenceWithRef` to restore backward compatibility
|
|
719
|
+
* with `oas` v31+ which otherwise filters them out.
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* ```ts
|
|
723
|
+
* getParameters(document, operation)
|
|
724
|
+
* // [{ name: 'petId', in: 'path', required: true, schema: { type: 'integer' } }]
|
|
725
|
+
* ```
|
|
726
|
+
*/
|
|
727
|
+
function getParameters(document, operation) {
|
|
728
|
+
const resolveParams = (params) => params.map((p) => dereferenceWithRef(document, p)).filter((p) => !!p && typeof p === "object" && "in" in p && "name" in p);
|
|
729
|
+
const operationParams = resolveParams(operation.schema?.parameters || []);
|
|
730
|
+
const pathItem = document.paths?.[operation.path];
|
|
731
|
+
const pathLevelParams = resolveParams(pathItem && !isReference(pathItem) && pathItem.parameters ? pathItem.parameters : []);
|
|
732
|
+
const paramMap = /* @__PURE__ */ new Map();
|
|
733
|
+
for (const p of pathLevelParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
734
|
+
for (const p of operationParams) if (p.name && p.in) paramMap.set(`${p.in}:${p.name}`, p);
|
|
735
|
+
return Array.from(paramMap.values());
|
|
736
|
+
}
|
|
737
|
+
function getResponseBody(responseBody, contentType) {
|
|
738
|
+
if (!responseBody) return false;
|
|
739
|
+
if (isReference(responseBody)) return false;
|
|
740
|
+
const body = responseBody;
|
|
741
|
+
if (!body.content) return false;
|
|
742
|
+
if (contentType) {
|
|
743
|
+
if (!(contentType in body.content)) return false;
|
|
744
|
+
return body.content[contentType];
|
|
745
|
+
}
|
|
746
|
+
let availableContentType;
|
|
747
|
+
const contentTypes = Object.keys(body.content);
|
|
748
|
+
contentTypes.forEach((mt) => {
|
|
749
|
+
if (!availableContentType && oas_utils.matchesMimeType.json(mt)) availableContentType = mt;
|
|
750
|
+
});
|
|
751
|
+
if (!availableContentType) contentTypes.forEach((mt) => {
|
|
752
|
+
if (!availableContentType) availableContentType = mt;
|
|
753
|
+
});
|
|
754
|
+
if (availableContentType) return [
|
|
755
|
+
availableContentType,
|
|
756
|
+
body.content[availableContentType],
|
|
757
|
+
...body.description ? [body.description] : []
|
|
758
|
+
];
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Returns the response schema for a given operation and HTTP status code.
|
|
763
|
+
*
|
|
764
|
+
* Returns an empty object `{}` when no response body schema is available.
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* ```ts
|
|
768
|
+
* getResponseSchema(document, operation, 200) // SchemaObject
|
|
769
|
+
* getResponseSchema(document, operation, '4XX') // {}
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
773
|
+
if (operation.schema.responses) Object.keys(operation.schema.responses).forEach((key) => {
|
|
774
|
+
const schema = operation.schema.responses[key];
|
|
775
|
+
const $ref = isReference(schema) ? schema.$ref : void 0;
|
|
776
|
+
if (schema && $ref) operation.schema.responses[key] = resolveRef(document, $ref);
|
|
777
|
+
});
|
|
778
|
+
const responseBody = getResponseBody(operation.getResponseByStatusCode(statusCode), options.contentType);
|
|
779
|
+
if (responseBody === false) return {};
|
|
780
|
+
const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
|
|
781
|
+
if (!schema) return {};
|
|
782
|
+
return dereferenceWithRef(document, schema);
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Returns the request body schema for an operation, or `undefined` when absent.
|
|
786
|
+
*
|
|
787
|
+
* @example
|
|
788
|
+
* ```ts
|
|
789
|
+
* getRequestSchema(document, operation) // SchemaObject | null
|
|
790
|
+
* ```
|
|
791
|
+
*/
|
|
792
|
+
function getRequestSchema(document, operation, options = {}) {
|
|
793
|
+
if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
|
|
794
|
+
const requestBody = operation.getRequestBody(options.contentType);
|
|
795
|
+
if (requestBody === false) return null;
|
|
796
|
+
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
797
|
+
if (!schema) return null;
|
|
798
|
+
return dereferenceWithRef(document, schema);
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Flattens a keyword-only `allOf` into its parent schema.
|
|
802
|
+
*
|
|
803
|
+
* Only flattens when every member is a plain fragment — no `$ref` and no structural keywords
|
|
804
|
+
* (see `structuralKeys`). Outer schema values take precedence over fragment values.
|
|
805
|
+
* Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
|
|
806
|
+
*
|
|
807
|
+
* @example
|
|
808
|
+
* ```ts
|
|
809
|
+
* flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
|
|
810
|
+
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
811
|
+
*
|
|
812
|
+
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
813
|
+
* // returned unchanged — contains a $ref
|
|
814
|
+
* ```
|
|
778
815
|
*/
|
|
779
816
|
function flattenSchema(schema) {
|
|
780
817
|
if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
|
|
@@ -787,18 +824,26 @@ function flattenSchema(schema) {
|
|
|
787
824
|
return merged;
|
|
788
825
|
}
|
|
789
826
|
/**
|
|
790
|
-
*
|
|
791
|
-
*
|
|
827
|
+
* Extracts the inline schema from a media-type `content` map.
|
|
828
|
+
*
|
|
829
|
+
* Prefers `preferredContentType` when given; otherwise uses the first key in the map.
|
|
830
|
+
* Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```ts
|
|
834
|
+
* extractSchemaFromContent(operation.content, 'application/json')
|
|
835
|
+
* // SchemaObject | null
|
|
836
|
+
* ```
|
|
792
837
|
*/
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
838
|
+
function extractSchemaFromContent(content, preferredContentType) {
|
|
839
|
+
if (!content) return null;
|
|
840
|
+
const firstContentType = Object.keys(content)[0] ?? "application/json";
|
|
841
|
+
const schema = content[preferredContentType ?? firstContentType]?.schema;
|
|
842
|
+
if (schema && "$ref" in schema) return null;
|
|
843
|
+
return schema ?? null;
|
|
798
844
|
}
|
|
799
845
|
/**
|
|
800
|
-
* Walks a schema tree and collects the names of all `#/components/schemas/<name>`
|
|
801
|
-
* Used by `sortSchemas` to build the dependency graph.
|
|
846
|
+
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
802
847
|
*/
|
|
803
848
|
function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
804
849
|
if (Array.isArray(schema)) {
|
|
@@ -814,11 +859,14 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
|
814
859
|
/**
|
|
815
860
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
816
861
|
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
* referenced types before the types that depend on them.
|
|
862
|
+
* Referenced schemas appear before the schemas that depend on them, so code generators
|
|
863
|
+
* can emit types in the correct order. Cycles are silently skipped.
|
|
820
864
|
*
|
|
821
|
-
*
|
|
865
|
+
* @example
|
|
866
|
+
* ```ts
|
|
867
|
+
* const sorted = sortSchemas({ Order: orderSchema, Pet: petSchema })
|
|
868
|
+
* // Pet appears before Order when Order.$ref points at Pet
|
|
869
|
+
* ```
|
|
822
870
|
*/
|
|
823
871
|
function sortSchemas(schemas) {
|
|
824
872
|
const deps = /* @__PURE__ */ new Map();
|
|
@@ -838,26 +886,6 @@ function sortSchemas(schemas) {
|
|
|
838
886
|
for (const name of sorted) result[name] = schemas[name];
|
|
839
887
|
return result;
|
|
840
888
|
}
|
|
841
|
-
/**
|
|
842
|
-
* Extracts the inline schema from a media-type `content` map.
|
|
843
|
-
*
|
|
844
|
-
* Prefers `preferredContentType` when provided; otherwise falls back to the
|
|
845
|
-
* first key in the map. Returns `null` when:
|
|
846
|
-
* - `content` is absent.
|
|
847
|
-
* - The target content-type has no `schema` entry.
|
|
848
|
-
* - The schema is a `$ref` (callers resolve refs separately).
|
|
849
|
-
*/
|
|
850
|
-
function extractSchemaFromContent(content, preferredContentType) {
|
|
851
|
-
if (!content) return null;
|
|
852
|
-
const firstContentType = Object.keys(content)[0] ?? "application/json";
|
|
853
|
-
const schema = content[preferredContentType ?? firstContentType]?.schema;
|
|
854
|
-
if (schema && "$ref" in schema) return null;
|
|
855
|
-
return schema ?? null;
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Returns the PascalCase suffix appended to a component name when resolving
|
|
859
|
-
* cross-source name collisions (schemas vs. responses vs. requestBodies).
|
|
860
|
-
*/
|
|
861
889
|
const semanticSuffixes = {
|
|
862
890
|
schemas: "Schema",
|
|
863
891
|
responses: "Response",
|
|
@@ -866,176 +894,138 @@ const semanticSuffixes = {
|
|
|
866
894
|
function getSemanticSuffix(source) {
|
|
867
895
|
return semanticSuffixes[source];
|
|
868
896
|
}
|
|
897
|
+
function resolveSchemaRef(document, schema) {
|
|
898
|
+
if (!isReference(schema)) return schema;
|
|
899
|
+
const resolved = resolveRef(document, schema.$ref);
|
|
900
|
+
return resolved && !isReference(resolved) ? resolved : schema;
|
|
901
|
+
}
|
|
869
902
|
/**
|
|
870
|
-
*
|
|
903
|
+
* Collects component schemas from one or more sources and resolves name collisions.
|
|
904
|
+
*
|
|
905
|
+
* Sources default to `['schemas', 'requestBodies', 'responses']`. Returned schemas are
|
|
906
|
+
* topologically sorted by `$ref` dependency so generators emit types in the correct order.
|
|
871
907
|
*
|
|
872
908
|
* When two or more schemas normalize to the same PascalCase name:
|
|
873
|
-
* -
|
|
874
|
-
* -
|
|
875
|
-
* a semantic suffix (`Schema`, `Response`, `Request`) is appended.
|
|
909
|
+
* - Same source → numeric suffix (`2`, `3`, …).
|
|
910
|
+
* - Different sources → semantic suffix (`Schema`, `Response`, `Request`).
|
|
876
911
|
*
|
|
877
|
-
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* const { schemas, nameMapping } = getSchemas(document, { contentType: 'application/json' })
|
|
915
|
+
* ```
|
|
878
916
|
*/
|
|
879
|
-
function
|
|
880
|
-
const
|
|
881
|
-
const
|
|
917
|
+
function getSchemas(document, { contentType }) {
|
|
918
|
+
const components = document.components;
|
|
919
|
+
const candidates = [...Object.entries(components?.schemas ?? {}).map(([name, schema]) => ({
|
|
920
|
+
schema: resolveSchemaRef(document, schema),
|
|
921
|
+
source: "schemas",
|
|
922
|
+
originalName: name
|
|
923
|
+
})), ...["responses", "requestBodies"].flatMap((source) => Object.entries(components?.[source] ?? {}).flatMap(([name, item]) => {
|
|
924
|
+
const schema = extractSchemaFromContent(item.content, contentType);
|
|
925
|
+
return schema ? [{
|
|
926
|
+
schema: resolveSchemaRef(document, schema),
|
|
927
|
+
source,
|
|
928
|
+
originalName: name
|
|
929
|
+
}] : [];
|
|
930
|
+
}))];
|
|
882
931
|
const normalizedNames = /* @__PURE__ */ new Map();
|
|
883
|
-
for (const item of
|
|
884
|
-
const
|
|
885
|
-
const bucket = normalizedNames.get(
|
|
932
|
+
for (const item of candidates) {
|
|
933
|
+
const key = pascalCase(item.originalName);
|
|
934
|
+
const bucket = normalizedNames.get(key) ?? [];
|
|
886
935
|
bucket.push(item);
|
|
887
|
-
normalizedNames.set(
|
|
888
|
-
}
|
|
889
|
-
for (const [, items] of normalizedNames) {
|
|
890
|
-
if (items.length === 1) {
|
|
891
|
-
const item = items[0];
|
|
892
|
-
schemas[item.originalName] = item.schema;
|
|
893
|
-
nameMapping.set(`#/components/${item.source}/${item.originalName}`, item.originalName);
|
|
894
|
-
continue;
|
|
895
|
-
}
|
|
896
|
-
if (new Set(items.map((item) => item.source)).size === 1) items.forEach((item, index) => {
|
|
897
|
-
const uniqueName = item.originalName + (index === 0 ? "" : (index + 1).toString());
|
|
898
|
-
schemas[uniqueName] = item.schema;
|
|
899
|
-
nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
|
|
900
|
-
});
|
|
901
|
-
else items.forEach((item) => {
|
|
902
|
-
const uniqueName = item.originalName + getSemanticSuffix(item.source);
|
|
903
|
-
schemas[uniqueName] = item.schema;
|
|
904
|
-
nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
|
|
905
|
-
});
|
|
936
|
+
normalizedNames.set(key, bucket);
|
|
906
937
|
}
|
|
938
|
+
const schemas = {};
|
|
939
|
+
const nameMapping = /* @__PURE__ */ new Map();
|
|
940
|
+
const multipleSources = (items) => new Set(items.map((i) => i.source)).size > 1;
|
|
941
|
+
for (const [, items] of normalizedNames) items.forEach((item, index) => {
|
|
942
|
+
const suffix = items.length === 1 ? "" : multipleSources(items) ? getSemanticSuffix(item.source) : index === 0 ? "" : String(index + 1);
|
|
943
|
+
const uniqueName = item.originalName + suffix;
|
|
944
|
+
schemas[uniqueName] = item.schema;
|
|
945
|
+
nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
|
|
946
|
+
});
|
|
907
947
|
return {
|
|
908
|
-
schemas,
|
|
948
|
+
schemas: sortSchemas(schemas),
|
|
909
949
|
nameMapping
|
|
910
950
|
};
|
|
911
951
|
}
|
|
912
|
-
//#endregion
|
|
913
|
-
//#region src/parser.ts
|
|
914
952
|
/**
|
|
915
|
-
*
|
|
916
|
-
* Returns `
|
|
917
|
-
* which are handled separately because their output depends on parser options.
|
|
953
|
+
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
954
|
+
* Returns `null` when `dateType: false`, signalling the format should fall through to `string`.
|
|
918
955
|
*/
|
|
919
|
-
function
|
|
920
|
-
return
|
|
956
|
+
function getDateType(options, format) {
|
|
957
|
+
if (!options.dateType) return null;
|
|
958
|
+
if (format === "date-time") {
|
|
959
|
+
if (options.dateType === "date") return {
|
|
960
|
+
type: "date",
|
|
961
|
+
representation: "date"
|
|
962
|
+
};
|
|
963
|
+
if (options.dateType === "stringOffset") return {
|
|
964
|
+
type: "datetime",
|
|
965
|
+
offset: true
|
|
966
|
+
};
|
|
967
|
+
if (options.dateType === "stringLocal") return {
|
|
968
|
+
type: "datetime",
|
|
969
|
+
local: true
|
|
970
|
+
};
|
|
971
|
+
return {
|
|
972
|
+
type: "datetime",
|
|
973
|
+
offset: false
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
if (format === "date") return {
|
|
977
|
+
type: "date",
|
|
978
|
+
representation: options.dateType === "date" ? "date" : "string"
|
|
979
|
+
};
|
|
980
|
+
return {
|
|
981
|
+
type: "time",
|
|
982
|
+
representation: options.dateType === "date" ? "date" : "string"
|
|
983
|
+
};
|
|
921
984
|
}
|
|
922
985
|
/**
|
|
923
|
-
*
|
|
924
|
-
* Numeric types (`number`, `integer`, `bigint`) are returned unchanged;
|
|
925
|
-
* `boolean` maps to `'boolean'`; everything else defaults to `'string'`.
|
|
986
|
+
* Collects the shared metadata fields passed to every `createSchema` call.
|
|
926
987
|
*/
|
|
927
|
-
function
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
988
|
+
function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
989
|
+
return {
|
|
990
|
+
name,
|
|
991
|
+
nullable,
|
|
992
|
+
title: schema.title,
|
|
993
|
+
description: schema.description,
|
|
994
|
+
deprecated: schema.deprecated,
|
|
995
|
+
readOnly: schema.readOnly,
|
|
996
|
+
writeOnly: schema.writeOnly,
|
|
997
|
+
default: defaultValue,
|
|
998
|
+
example: schema.example
|
|
999
|
+
};
|
|
931
1000
|
}
|
|
1001
|
+
//#endregion
|
|
1002
|
+
//#region src/parser.ts
|
|
932
1003
|
/**
|
|
933
|
-
*
|
|
934
|
-
*
|
|
1004
|
+
* Normalize a malformed `{ type: 'array', enum: [...] }` schema by moving the
|
|
1005
|
+
* enum values into the items sub-schema. This pattern is technically invalid OAS
|
|
1006
|
+
* but appears in the wild and must be handled gracefully.
|
|
935
1007
|
*/
|
|
936
|
-
function
|
|
937
|
-
|
|
1008
|
+
function normalizeArrayEnum(schema) {
|
|
1009
|
+
const normalizedItems = {
|
|
1010
|
+
...typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : {},
|
|
1011
|
+
enum: schema.enum
|
|
1012
|
+
};
|
|
1013
|
+
const { enum: _enum, ...schemaWithoutEnum } = schema;
|
|
1014
|
+
return {
|
|
1015
|
+
...schemaWithoutEnum,
|
|
1016
|
+
items: normalizedItems
|
|
1017
|
+
};
|
|
938
1018
|
}
|
|
939
1019
|
/**
|
|
940
|
-
*
|
|
941
|
-
* the `@kubb/ast` tree.
|
|
942
|
-
*
|
|
943
|
-
* Options are passed per-call to `parse` or `convertSchema` rather than
|
|
944
|
-
* at construction time, keeping the factory lightweight.
|
|
945
|
-
*
|
|
946
|
-
* This is the **kubb-parser** stage of the compilation lifecycle:
|
|
947
|
-
* OpenAPI / Swagger → Kubb AST
|
|
1020
|
+
* Builds the internal converter functions for a given `OasParserContext`.
|
|
948
1021
|
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
*
|
|
952
|
-
* @example
|
|
953
|
-
* ```ts
|
|
954
|
-
* const parser = createOasParser(oas)
|
|
955
|
-
* const root = parser.parse({ emptySchemaType: 'unknown' })
|
|
956
|
-
* ```
|
|
1022
|
+
* All `convert*` functions are defined as function declarations so they can freely
|
|
1023
|
+
* reference each other and `parseSchema` via JS hoisting (mutual recursion).
|
|
957
1024
|
*/
|
|
958
|
-
function
|
|
959
|
-
const
|
|
960
|
-
const TYPE_OPTION_MAP = {
|
|
961
|
-
any: _kubb_ast.schemaTypes.any,
|
|
962
|
-
unknown: _kubb_ast.schemaTypes.unknown,
|
|
963
|
-
void: _kubb_ast.schemaTypes.void
|
|
964
|
-
};
|
|
965
|
-
/**
|
|
966
|
-
* Maps an `'any' | 'unknown' | 'void'` option string to the corresponding `SchemaType` constant.
|
|
967
|
-
* Used for both `unknownType` (unannotated schemas) and `emptySchemaType` (empty `{}` schemas).
|
|
968
|
-
*/
|
|
969
|
-
function resolveTypeOption(value) {
|
|
970
|
-
return TYPE_OPTION_MAP[value];
|
|
971
|
-
}
|
|
972
|
-
function normalizeArrayEnum(schema) {
|
|
973
|
-
const normalizedItems = {
|
|
974
|
-
...typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : {},
|
|
975
|
-
enum: schema.enum
|
|
976
|
-
};
|
|
977
|
-
const { enum: _enum, ...schemaWithoutEnum } = schema;
|
|
978
|
-
return {
|
|
979
|
-
...schemaWithoutEnum,
|
|
980
|
-
items: normalizedItems
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Resolves the AST type and datetime modifiers for a date/time format, honoring the `dateType` option.
|
|
985
|
-
* Returns `undefined` when `dateType` is `false`, meaning the format should fall through to `string`.
|
|
986
|
-
*/
|
|
987
|
-
function getDateType(options, format) {
|
|
988
|
-
if (!options.dateType) return;
|
|
989
|
-
if (format === "date-time") {
|
|
990
|
-
if (options.dateType === "date") return {
|
|
991
|
-
type: "date",
|
|
992
|
-
representation: "date"
|
|
993
|
-
};
|
|
994
|
-
if (options.dateType === "stringOffset") return {
|
|
995
|
-
type: "datetime",
|
|
996
|
-
offset: true
|
|
997
|
-
};
|
|
998
|
-
if (options.dateType === "stringLocal") return {
|
|
999
|
-
type: "datetime",
|
|
1000
|
-
local: true
|
|
1001
|
-
};
|
|
1002
|
-
return {
|
|
1003
|
-
type: "datetime",
|
|
1004
|
-
offset: false
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
if (format === "date") return {
|
|
1008
|
-
type: "date",
|
|
1009
|
-
representation: options.dateType === "date" ? "date" : "string"
|
|
1010
|
-
};
|
|
1011
|
-
return {
|
|
1012
|
-
type: "time",
|
|
1013
|
-
representation: options.dateType === "date" ? "date" : "string"
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
* Shared metadata fields included in every `createSchema` call.
|
|
1018
|
-
* Centralizes the common properties so sub-handlers don't repeat them.
|
|
1019
|
-
*/
|
|
1020
|
-
function renderSchemaBase(schema, name, nullable, defaultValue) {
|
|
1021
|
-
return {
|
|
1022
|
-
name,
|
|
1023
|
-
nullable,
|
|
1024
|
-
title: schema.title,
|
|
1025
|
-
description: schema.description,
|
|
1026
|
-
deprecated: schema.deprecated,
|
|
1027
|
-
readOnly: schema.readOnly,
|
|
1028
|
-
writeOnly: schema.writeOnly,
|
|
1029
|
-
default: defaultValue,
|
|
1030
|
-
example: schema.example
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1025
|
+
function createSchemaParser(ctx) {
|
|
1026
|
+
const document = ctx.document;
|
|
1033
1027
|
/**
|
|
1034
|
-
* Converts a `$ref` schema
|
|
1035
|
-
*
|
|
1036
|
-
* In OAS 3.0 siblings of `$ref` are technically ignored by the spec, but Kubb intentionally
|
|
1037
|
-
* preserves them so that annotations like `pattern`, `description`, and `nullable` are
|
|
1038
|
-
* reflected in generated JSDoc and type modifiers.
|
|
1028
|
+
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1039
1029
|
*/
|
|
1040
1030
|
function convertRef({ schema, nullable, defaultValue }) {
|
|
1041
1031
|
return (0, _kubb_ast.createSchema)({
|
|
@@ -1053,24 +1043,15 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1053
1043
|
});
|
|
1054
1044
|
}
|
|
1055
1045
|
/**
|
|
1056
|
-
* Converts
|
|
1057
|
-
* or an `IntersectionSchemaNode` (multi-member `allOf`).
|
|
1058
|
-
*
|
|
1059
|
-
* Single-member `allOf` without sibling structural keys is the common OAS 3.0 pattern for
|
|
1060
|
-
* annotating a `$ref` or primitive with extra constraints; it is flattened to avoid
|
|
1061
|
-
* producing needless intersection wrappers.
|
|
1062
|
-
*
|
|
1063
|
-
* The flatten path is skipped when the outer schema carries structural keys that cannot be
|
|
1064
|
-
* merged into annotation fields: `properties`, `required`, or `additionalProperties`.
|
|
1065
|
-
* Those cases must become an intersection so the constraints are preserved.
|
|
1066
|
-
*
|
|
1067
|
-
* Circular references through discriminator parents are detected and skipped to prevent
|
|
1068
|
-
* infinite recursion during code generation.
|
|
1046
|
+
* Converts an `allOf` schema into a flattened node or an `IntersectionSchemaNode`.
|
|
1069
1047
|
*/
|
|
1070
1048
|
function convertAllOf({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1071
1049
|
if (schema.allOf.length === 1 && !schema.properties && !(Array.isArray(schema.required) && schema.required.length) && schema.additionalProperties === void 0) {
|
|
1072
1050
|
const [memberSchema] = schema.allOf;
|
|
1073
|
-
const memberNode =
|
|
1051
|
+
const memberNode = parseSchema({
|
|
1052
|
+
schema: memberSchema,
|
|
1053
|
+
name: null
|
|
1054
|
+
}, rawOptions);
|
|
1074
1055
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1075
1056
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
1076
1057
|
const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
|
|
@@ -1091,7 +1072,7 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1091
1072
|
const filteredDiscriminantValues = [];
|
|
1092
1073
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1093
1074
|
if (!isReference(item) || !name) return true;
|
|
1094
|
-
const deref =
|
|
1075
|
+
const deref = resolveRef(document, item.$ref);
|
|
1095
1076
|
if (!deref || !isDiscriminator(deref)) return true;
|
|
1096
1077
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1097
1078
|
if (!parentUnion) return true;
|
|
@@ -1107,7 +1088,7 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1107
1088
|
return false;
|
|
1108
1089
|
}
|
|
1109
1090
|
return true;
|
|
1110
|
-
}).map((s) =>
|
|
1091
|
+
}).map((s) => parseSchema({ schema: s }, rawOptions));
|
|
1111
1092
|
const syntheticStart = allOfMembers.length;
|
|
1112
1093
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1113
1094
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
@@ -1115,11 +1096,11 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1115
1096
|
if (missingRequired.length) {
|
|
1116
1097
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1117
1098
|
if (!isReference(item)) return [item];
|
|
1118
|
-
const deref =
|
|
1099
|
+
const deref = resolveRef(document, item.$ref);
|
|
1119
1100
|
return deref && !isReference(deref) ? [deref] : [];
|
|
1120
1101
|
});
|
|
1121
1102
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1122
|
-
allOfMembers.push(
|
|
1103
|
+
allOfMembers.push(parseSchema({ schema: {
|
|
1123
1104
|
properties: { [key]: resolved.properties[key] },
|
|
1124
1105
|
required: [key]
|
|
1125
1106
|
} }, rawOptions));
|
|
@@ -1129,7 +1110,7 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1129
1110
|
}
|
|
1130
1111
|
if (schema.properties) {
|
|
1131
1112
|
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1132
|
-
allOfMembers.push(
|
|
1113
|
+
allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
|
|
1133
1114
|
}
|
|
1134
1115
|
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push((0, _kubb_ast.createDiscriminantNode)({
|
|
1135
1116
|
propertyName,
|
|
@@ -1138,21 +1119,16 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1138
1119
|
return (0, _kubb_ast.createSchema)({
|
|
1139
1120
|
type: "intersection",
|
|
1140
1121
|
members: [...(0, _kubb_ast.mergeAdjacentObjects)(allOfMembers.slice(0, syntheticStart)), ...(0, _kubb_ast.mergeAdjacentObjects)(allOfMembers.slice(syntheticStart))],
|
|
1141
|
-
...
|
|
1122
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1142
1123
|
});
|
|
1143
1124
|
}
|
|
1144
1125
|
/**
|
|
1145
1126
|
* Converts a `oneOf` / `anyOf` schema into a `UnionSchemaNode`.
|
|
1146
|
-
*
|
|
1147
|
-
* Both keywords are treated identically — their members are concatenated into a single union.
|
|
1148
|
-
* When sibling `properties` are present alongside `oneOf`/`anyOf`, each union member is
|
|
1149
|
-
* individually intersected with the shared properties node to match the OAS pattern of
|
|
1150
|
-
* adding common fields next to a discriminated union.
|
|
1151
1127
|
*/
|
|
1152
1128
|
function convertUnion({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1153
1129
|
function pickDiscriminatorPropertyNode(node, propertyName) {
|
|
1154
1130
|
const discriminatorProperty = (0, _kubb_ast.narrowSchema)(node, "object")?.properties?.find((property) => property.name === propertyName);
|
|
1155
|
-
if (!discriminatorProperty) return;
|
|
1131
|
+
if (!discriminatorProperty) return null;
|
|
1156
1132
|
return (0, _kubb_ast.createSchema)({
|
|
1157
1133
|
type: "object",
|
|
1158
1134
|
primitive: "object",
|
|
@@ -1161,13 +1137,13 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1161
1137
|
}
|
|
1162
1138
|
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1163
1139
|
const unionBase = {
|
|
1164
|
-
...
|
|
1140
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1165
1141
|
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0
|
|
1166
1142
|
};
|
|
1167
1143
|
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1168
1144
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1169
1145
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1170
|
-
return
|
|
1146
|
+
return parseSchema({
|
|
1171
1147
|
schema: discriminator ? Object.fromEntries(Object.entries(schemaWithoutUnion).filter(([key]) => key !== "discriminator")) : schemaWithoutUnion,
|
|
1172
1148
|
name
|
|
1173
1149
|
}, rawOptions);
|
|
@@ -1176,7 +1152,7 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1176
1152
|
const members = unionMembers.map((s) => {
|
|
1177
1153
|
const ref = isReference(s) ? s.$ref : void 0;
|
|
1178
1154
|
const discriminatorValue = (0, _kubb_ast.findDiscriminator)(discriminator?.mapping, ref);
|
|
1179
|
-
const memberNode =
|
|
1155
|
+
const memberNode = parseSchema({ schema: s }, rawOptions);
|
|
1180
1156
|
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1181
1157
|
return (0, _kubb_ast.createSchema)({
|
|
1182
1158
|
type: "intersection",
|
|
@@ -1198,20 +1174,18 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1198
1174
|
if (!sharedPropertiesNode) return unionNode;
|
|
1199
1175
|
return (0, _kubb_ast.createSchema)({
|
|
1200
1176
|
type: "intersection",
|
|
1201
|
-
...
|
|
1177
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1202
1178
|
members: [unionNode, sharedPropertiesNode]
|
|
1203
1179
|
});
|
|
1204
1180
|
}
|
|
1205
1181
|
return (0, _kubb_ast.createSchema)({
|
|
1206
1182
|
type: "union",
|
|
1207
1183
|
...unionBase,
|
|
1208
|
-
members: (0, _kubb_ast.simplifyUnion)(unionMembers.map((s) =>
|
|
1184
|
+
members: (0, _kubb_ast.simplifyUnion)(unionMembers.map((s) => parseSchema({ schema: s }, rawOptions)))
|
|
1209
1185
|
});
|
|
1210
1186
|
}
|
|
1211
1187
|
/**
|
|
1212
|
-
* Converts an OAS 3.1 `const` schema into
|
|
1213
|
-
* `const: null` maps to a null scalar; any other value becomes a one-item enum so that generators
|
|
1214
|
-
* can produce a precise literal type.
|
|
1188
|
+
* Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
|
|
1215
1189
|
*/
|
|
1216
1190
|
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1217
1191
|
const constValue = schema.const;
|
|
@@ -1227,16 +1201,15 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1227
1201
|
type: "enum",
|
|
1228
1202
|
primitive: getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string"),
|
|
1229
1203
|
enumValues: [constValue],
|
|
1230
|
-
...
|
|
1204
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1231
1205
|
});
|
|
1232
1206
|
}
|
|
1233
1207
|
/**
|
|
1234
|
-
*
|
|
1235
|
-
* Returns `
|
|
1236
|
-
* (i.e. `format: 'date-time'` with `dateType: false`).
|
|
1208
|
+
* Converts a format-annotated schema into a special-type `SchemaNode`.
|
|
1209
|
+
* Returns `null` when the format should fall through to string handling (`dateType: false`).
|
|
1237
1210
|
*/
|
|
1238
1211
|
function convertFormat({ schema, name, nullable, defaultValue, options }) {
|
|
1239
|
-
const base =
|
|
1212
|
+
const base = buildSchemaNode(schema, name, nullable, defaultValue);
|
|
1240
1213
|
if (schema.format === "int64") return (0, _kubb_ast.createSchema)({
|
|
1241
1214
|
type: options.integerType === "bigint" ? "bigint" : "integer",
|
|
1242
1215
|
primitive: "integer",
|
|
@@ -1248,7 +1221,7 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1248
1221
|
});
|
|
1249
1222
|
if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
|
|
1250
1223
|
const dateType = getDateType(options, schema.format);
|
|
1251
|
-
if (!dateType) return
|
|
1224
|
+
if (!dateType) return null;
|
|
1252
1225
|
if (dateType.type === "datetime") return (0, _kubb_ast.createSchema)({
|
|
1253
1226
|
...base,
|
|
1254
1227
|
primitive: "string",
|
|
@@ -1263,8 +1236,8 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1263
1236
|
representation: dateType.representation
|
|
1264
1237
|
});
|
|
1265
1238
|
}
|
|
1266
|
-
const specialType =
|
|
1267
|
-
if (!specialType) return
|
|
1239
|
+
const specialType = getSchemaType(schema.format);
|
|
1240
|
+
if (!specialType) return null;
|
|
1268
1241
|
const specialPrimitive = specialType === "number" || specialType === "integer" || specialType === "bigint" ? specialType : "string";
|
|
1269
1242
|
if (specialType === "number" || specialType === "integer" || specialType === "bigint") return (0, _kubb_ast.createSchema)({
|
|
1270
1243
|
...base,
|
|
@@ -1284,16 +1257,9 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1284
1257
|
}
|
|
1285
1258
|
/**
|
|
1286
1259
|
* Converts an `enum` schema into an `EnumSchemaNode`.
|
|
1287
|
-
*
|
|
1288
|
-
* Handles several edge cases:
|
|
1289
|
-
* - `{ type: 'array', enum }` (technically invalid OAS) — the enum is normalized into `items`.
|
|
1290
|
-
* - `null` in enum values (OAS 3.0 nullable enum convention) — stripped and reflected as `nullable`.
|
|
1291
|
-
* - `x-enumNames` / `x-enum-varnames` vendor extensions — produce named enum variants with explicit labels.
|
|
1292
|
-
* - Numeric and boolean enums require a const-map representation because most generators cannot
|
|
1293
|
-
* use string-enum syntax for non-string values.
|
|
1294
1260
|
*/
|
|
1295
1261
|
function convertEnum({ schema, name, nullable, type, rawOptions }) {
|
|
1296
|
-
if (type === "array") return
|
|
1262
|
+
if (type === "array") return parseSchema({
|
|
1297
1263
|
schema: normalizeArrayEnum(schema),
|
|
1298
1264
|
name
|
|
1299
1265
|
}, rawOptions);
|
|
@@ -1335,23 +1301,14 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1335
1301
|
});
|
|
1336
1302
|
}
|
|
1337
1303
|
/**
|
|
1338
|
-
* Converts an object-like schema
|
|
1339
|
-
* or `patternProperties`) into an `ObjectSchemaNode`.
|
|
1340
|
-
*
|
|
1341
|
-
* When a `discriminator` is present, the discriminator property's schema is replaced with an
|
|
1342
|
-
* enum of the mapping keys so generators can produce a precise literal-union type for it.
|
|
1343
|
-
*
|
|
1344
|
-
* Property optionality follows OAS semantics:
|
|
1345
|
-
* - required + not nullable → `required: true`
|
|
1346
|
-
* - not required + not nullable → `optional: true`
|
|
1347
|
-
* - not required + nullable → `nullish: true`
|
|
1304
|
+
* Converts an object-like schema into an `ObjectSchemaNode`.
|
|
1348
1305
|
*/
|
|
1349
1306
|
function convertObject({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1350
1307
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1351
1308
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1352
1309
|
const resolvedPropSchema = propSchema;
|
|
1353
1310
|
const propNullable = isNullable(resolvedPropSchema);
|
|
1354
|
-
let schemaNode = (0, _kubb_ast.setEnumName)(
|
|
1311
|
+
let schemaNode = (0, _kubb_ast.setEnumName)(parseSchema({
|
|
1355
1312
|
schema: resolvedPropSchema,
|
|
1356
1313
|
name: (0, _kubb_ast.childName)(name, propName)
|
|
1357
1314
|
}, rawOptions), name, propName, options.enumSuffix);
|
|
@@ -1375,18 +1332,18 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1375
1332
|
const additionalProperties = schema.additionalProperties;
|
|
1376
1333
|
let additionalPropertiesNode;
|
|
1377
1334
|
if (additionalProperties === true) additionalPropertiesNode = true;
|
|
1378
|
-
else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode =
|
|
1335
|
+
else if (additionalProperties && Object.keys(additionalProperties).length > 0) additionalPropertiesNode = parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1379
1336
|
else if (additionalProperties === false) additionalPropertiesNode = void 0;
|
|
1380
|
-
else if (additionalProperties) additionalPropertiesNode = (0, _kubb_ast.createSchema)({ type:
|
|
1337
|
+
else if (additionalProperties) additionalPropertiesNode = (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.unknownType) });
|
|
1381
1338
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1382
|
-
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? (0, _kubb_ast.createSchema)({ type:
|
|
1339
|
+
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1383
1340
|
const objectNode = (0, _kubb_ast.createSchema)({
|
|
1384
1341
|
type: "object",
|
|
1385
1342
|
primitive: "object",
|
|
1386
1343
|
properties,
|
|
1387
1344
|
additionalProperties: additionalPropertiesNode,
|
|
1388
1345
|
patternProperties,
|
|
1389
|
-
...
|
|
1346
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1390
1347
|
});
|
|
1391
1348
|
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1392
1349
|
const discPropName = schema.discriminator.propertyName;
|
|
@@ -1401,28 +1358,20 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1401
1358
|
}
|
|
1402
1359
|
/**
|
|
1403
1360
|
* Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
|
|
1404
|
-
*
|
|
1405
|
-
* Each `prefixItems` element maps to a positional tuple slot. When an explicit `items` schema
|
|
1406
|
-
* is present alongside `prefixItems`, it becomes the rest element. When `items` is absent,
|
|
1407
|
-
* a rest element of type `any` is emitted to match JSON Schema semantics (absent `items`
|
|
1408
|
-
* means additional items are allowed).
|
|
1409
1361
|
*/
|
|
1410
1362
|
function convertTuple({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1411
1363
|
return (0, _kubb_ast.createSchema)({
|
|
1412
1364
|
type: "tuple",
|
|
1413
1365
|
primitive: "array",
|
|
1414
|
-
items: (schema.prefixItems ?? []).map((item) =>
|
|
1415
|
-
rest: schema.items ?
|
|
1366
|
+
items: (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item }, rawOptions)),
|
|
1367
|
+
rest: schema.items ? parseSchema({ schema: schema.items }, rawOptions) : (0, _kubb_ast.createSchema)({ type: "any" }),
|
|
1416
1368
|
min: schema.minItems,
|
|
1417
1369
|
max: schema.maxItems,
|
|
1418
|
-
...
|
|
1370
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1419
1371
|
});
|
|
1420
1372
|
}
|
|
1421
1373
|
/**
|
|
1422
1374
|
* Converts a `type: 'array'` schema into an `ArraySchemaNode`.
|
|
1423
|
-
*
|
|
1424
|
-
* When the items schema is an inline enum, a name derived from the parent array's name and
|
|
1425
|
-
* `enumSuffix` is forwarded so generators can emit a named enum declaration.
|
|
1426
1375
|
*/
|
|
1427
1376
|
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1428
1377
|
const rawItems = schema.items;
|
|
@@ -1430,18 +1379,18 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1430
1379
|
return (0, _kubb_ast.createSchema)({
|
|
1431
1380
|
type: "array",
|
|
1432
1381
|
primitive: "array",
|
|
1433
|
-
items: rawItems ? [
|
|
1382
|
+
items: rawItems ? [parseSchema({
|
|
1434
1383
|
schema: rawItems,
|
|
1435
1384
|
name: itemName
|
|
1436
1385
|
}, rawOptions)] : [],
|
|
1437
1386
|
min: schema.minItems,
|
|
1438
1387
|
max: schema.maxItems,
|
|
1439
1388
|
unique: schema.uniqueItems ?? void 0,
|
|
1440
|
-
...
|
|
1389
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1441
1390
|
});
|
|
1442
1391
|
}
|
|
1443
1392
|
/**
|
|
1444
|
-
* Converts a `type: 'string'` schema
|
|
1393
|
+
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
1445
1394
|
*/
|
|
1446
1395
|
function convertString({ schema, name, nullable, defaultValue }) {
|
|
1447
1396
|
return (0, _kubb_ast.createSchema)({
|
|
@@ -1450,11 +1399,11 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1450
1399
|
min: schema.minLength,
|
|
1451
1400
|
max: schema.maxLength,
|
|
1452
1401
|
pattern: schema.pattern,
|
|
1453
|
-
...
|
|
1402
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1454
1403
|
});
|
|
1455
1404
|
}
|
|
1456
1405
|
/**
|
|
1457
|
-
* Converts a `type: 'number'` or `type: 'integer'` schema
|
|
1406
|
+
* Converts a `type: 'number'` or `type: 'integer'` schema.
|
|
1458
1407
|
*/
|
|
1459
1408
|
function convertNumeric({ schema, name, nullable, defaultValue }, type) {
|
|
1460
1409
|
return (0, _kubb_ast.createSchema)({
|
|
@@ -1464,21 +1413,21 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1464
1413
|
max: schema.maximum,
|
|
1465
1414
|
exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
|
|
1466
1415
|
exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
|
|
1467
|
-
...
|
|
1416
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1468
1417
|
});
|
|
1469
1418
|
}
|
|
1470
1419
|
/**
|
|
1471
|
-
* Converts a `type: 'boolean'` schema
|
|
1420
|
+
* Converts a `type: 'boolean'` schema.
|
|
1472
1421
|
*/
|
|
1473
1422
|
function convertBoolean({ schema, name, nullable, defaultValue }) {
|
|
1474
1423
|
return (0, _kubb_ast.createSchema)({
|
|
1475
1424
|
type: "boolean",
|
|
1476
1425
|
primitive: "boolean",
|
|
1477
|
-
...
|
|
1426
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1478
1427
|
});
|
|
1479
1428
|
}
|
|
1480
1429
|
/**
|
|
1481
|
-
* Converts an explicit `type: 'null'`
|
|
1430
|
+
* Converts an explicit `type: 'null'` schema.
|
|
1482
1431
|
*/
|
|
1483
1432
|
function convertNull({ schema, name, nullable }) {
|
|
1484
1433
|
return (0, _kubb_ast.createSchema)({
|
|
@@ -1492,28 +1441,19 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1492
1441
|
});
|
|
1493
1442
|
}
|
|
1494
1443
|
/**
|
|
1495
|
-
* Central dispatcher
|
|
1444
|
+
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1496
1445
|
*
|
|
1497
|
-
* Dispatch order (first match wins):
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1500
|
-
* 3. `oneOf` / `anyOf` union
|
|
1501
|
-
* 4. `const` literal (OAS 3.1)
|
|
1502
|
-
* 5. `format`-based special type (date/time, uuid, blob, …)
|
|
1503
|
-
* 6. OAS 3.1 `contentMediaType: 'application/octet-stream'` blob
|
|
1504
|
-
* 7. OAS 3.1 multi-type array → union or fallthrough
|
|
1505
|
-
* 8. Constraint-inferred type (minLength/maxLength → string; minimum/maximum → number)
|
|
1506
|
-
* 9. `enum` values
|
|
1507
|
-
* 10. Object / array / tuple / scalar by `type`
|
|
1508
|
-
* 11. Empty schema fallback (`emptySchemaType` option)
|
|
1446
|
+
* Dispatch order (first match wins): `$ref` → `allOf` → `oneOf`/`anyOf` → `const` → `format`
|
|
1447
|
+
* → octet-stream blob → multi-type array → constraint-inferred type → `enum` → object/array/tuple/scalar
|
|
1448
|
+
* → empty-schema fallback (`emptySchemaType` option).
|
|
1509
1449
|
*/
|
|
1510
|
-
function
|
|
1450
|
+
function parseSchema({ schema, name }, rawOptions) {
|
|
1511
1451
|
const options = {
|
|
1512
1452
|
...DEFAULT_PARSER_OPTIONS,
|
|
1513
1453
|
...rawOptions
|
|
1514
1454
|
};
|
|
1515
1455
|
const flattenedSchema = flattenSchema(schema);
|
|
1516
|
-
if (flattenedSchema && flattenedSchema !== schema) return
|
|
1456
|
+
if (flattenedSchema && flattenedSchema !== schema) return parseSchema({
|
|
1517
1457
|
schema: flattenedSchema,
|
|
1518
1458
|
name
|
|
1519
1459
|
}, rawOptions);
|
|
@@ -1540,21 +1480,21 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1540
1480
|
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return (0, _kubb_ast.createSchema)({
|
|
1541
1481
|
type: "blob",
|
|
1542
1482
|
primitive: "string",
|
|
1543
|
-
...
|
|
1483
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1544
1484
|
});
|
|
1545
1485
|
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1546
1486
|
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1547
1487
|
const arrayNullable = schema.type.includes("null") || nullable || void 0;
|
|
1548
1488
|
if (nonNullTypes.length > 1) return (0, _kubb_ast.createSchema)({
|
|
1549
1489
|
type: "union",
|
|
1550
|
-
members: nonNullTypes.map((t) =>
|
|
1490
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1551
1491
|
schema: {
|
|
1552
1492
|
...schema,
|
|
1553
1493
|
type: t
|
|
1554
1494
|
},
|
|
1555
1495
|
name
|
|
1556
1496
|
}, rawOptions)),
|
|
1557
|
-
...
|
|
1497
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1558
1498
|
});
|
|
1559
1499
|
}
|
|
1560
1500
|
if (!type) {
|
|
@@ -1571,19 +1511,18 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1571
1511
|
if (type === "boolean") return convertBoolean(ctx);
|
|
1572
1512
|
if (type === "null") return convertNull(ctx);
|
|
1573
1513
|
return (0, _kubb_ast.createSchema)({
|
|
1574
|
-
type:
|
|
1514
|
+
type: typeOptionMap.get(options.emptySchemaType),
|
|
1575
1515
|
name,
|
|
1576
1516
|
title: schema.title,
|
|
1577
1517
|
description: schema.description
|
|
1578
1518
|
});
|
|
1579
1519
|
}
|
|
1580
1520
|
/**
|
|
1581
|
-
* Converts a
|
|
1582
|
-
* When the parameter has no `schema`, falls back to `unknownType`; `$ref` schemas are resolved through `convertSchema` to produce a proper named type reference.
|
|
1521
|
+
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1583
1522
|
*/
|
|
1584
1523
|
function parseParameter(options, param) {
|
|
1585
1524
|
const required = param["required"] ?? false;
|
|
1586
|
-
const schema = param["schema"] ?
|
|
1525
|
+
const schema = param["schema"] ? parseSchema({ schema: param["schema"] }, options) : (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.unknownType) });
|
|
1587
1526
|
return (0, _kubb_ast.createParameter)({
|
|
1588
1527
|
name: param["name"],
|
|
1589
1528
|
in: param["in"],
|
|
@@ -1595,13 +1534,12 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1595
1534
|
});
|
|
1596
1535
|
}
|
|
1597
1536
|
/**
|
|
1598
|
-
* Converts an OAS `Operation` into an `OperationNode
|
|
1599
|
-
* request body, and all response codes into their AST node equivalents.
|
|
1537
|
+
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1600
1538
|
*/
|
|
1601
|
-
function parseOperation(options,
|
|
1602
|
-
const parameters =
|
|
1603
|
-
const requestBodySchema =
|
|
1604
|
-
const requestBodySchemaNode = requestBodySchema ?
|
|
1539
|
+
function parseOperation(options, operation) {
|
|
1540
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param));
|
|
1541
|
+
const requestBodySchema = getRequestSchema(document, operation, { contentType: ctx.contentType });
|
|
1542
|
+
const requestBodySchemaNode = requestBodySchema ? parseSchema({ schema: requestBodySchema }, options) : void 0;
|
|
1605
1543
|
const requestBodyDescription = operation.schema.requestBody && !isReference(operation.schema.requestBody) ? operation.schema.requestBody.description : void 0;
|
|
1606
1544
|
const requestBodyKeysToOmit = requestBodySchema?.properties ? Object.entries(requestBodySchema.properties).filter(([, prop]) => !isReference(prop) && prop.readOnly).map(([key]) => key) : void 0;
|
|
1607
1545
|
const requestBody = requestBodySchemaNode ? {
|
|
@@ -1611,11 +1549,11 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1611
1549
|
} : void 0;
|
|
1612
1550
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1613
1551
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1614
|
-
const responseSchema =
|
|
1615
|
-
const schema = responseSchema && Object.keys(responseSchema).length > 0 ?
|
|
1552
|
+
const responseSchema = getResponseSchema(document, operation, statusCode, { contentType: ctx.contentType });
|
|
1553
|
+
const schema = responseSchema && Object.keys(responseSchema).length > 0 ? parseSchema({ schema: responseSchema }, options) : (0, _kubb_ast.createSchema)({ type: typeOptionMap.get(options.emptySchemaType) });
|
|
1616
1554
|
const description = typeof responseObj === "object" && responseObj !== null && !Array.isArray(responseObj) ? responseObj.description : void 0;
|
|
1617
1555
|
const rawContent = typeof responseObj === "object" && responseObj !== null && !Array.isArray(responseObj) ? responseObj.content : void 0;
|
|
1618
|
-
const mediaType = rawContent ?
|
|
1556
|
+
const mediaType = rawContent ? getMediaType(Object.keys(rawContent)[0] ?? "") : getMediaType(operation.contentType ?? "");
|
|
1619
1557
|
const keysToOmit = responseSchema?.properties ? Object.entries(responseSchema.properties).filter(([, prop]) => !isReference(prop) && prop.writeOnly).map(([key]) => key) : void 0;
|
|
1620
1558
|
return (0, _kubb_ast.createResponse)({
|
|
1621
1559
|
statusCode,
|
|
@@ -1638,77 +1576,93 @@ function createOasParser(oas, { contentType } = {}) {
|
|
|
1638
1576
|
responses
|
|
1639
1577
|
});
|
|
1640
1578
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1579
|
+
return {
|
|
1580
|
+
parseSchema,
|
|
1581
|
+
parseOperation,
|
|
1582
|
+
parseParameter
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* Converts the entire OpenAPI spec into a `RootNode` (the top-level `@kubb/ast` tree).
|
|
1587
|
+
*
|
|
1588
|
+
* This is the main entry point: `OpenAPI / Swagger → Kubb AST`.
|
|
1589
|
+
* No code is generated here — the resulting tree is spec-agnostic and consumed by
|
|
1590
|
+
* downstream plugins (`plugin-ts`, `plugin-zod`, …).
|
|
1591
|
+
*
|
|
1592
|
+
* @example
|
|
1593
|
+
* ```ts
|
|
1594
|
+
* const document = await parseFromConfig(config)
|
|
1595
|
+
* const root = parseOas(document, { dateType: 'date', contentType: 'application/json' })
|
|
1596
|
+
* ```
|
|
1597
|
+
*/
|
|
1598
|
+
function parseOas(document, options = {}) {
|
|
1599
|
+
const { contentType, ...parserOptions } = options;
|
|
1600
|
+
const mergedOptions = {
|
|
1601
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
1602
|
+
...parserOptions
|
|
1603
|
+
};
|
|
1604
|
+
const { schemas: schemaObjects, nameMapping } = getSchemas(document, { contentType });
|
|
1605
|
+
const { parseSchema: _parseSchema, parseOperation: _parseOperation } = createSchemaParser({
|
|
1606
|
+
document,
|
|
1607
|
+
contentType
|
|
1665
1608
|
});
|
|
1609
|
+
const schemas = Object.entries(schemaObjects).map(([name, schema]) => _parseSchema({
|
|
1610
|
+
schema,
|
|
1611
|
+
name
|
|
1612
|
+
}, mergedOptions));
|
|
1613
|
+
const paths = new oas.default(document).getPaths();
|
|
1666
1614
|
return {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1615
|
+
root: (0, _kubb_ast.createRoot)({
|
|
1616
|
+
schemas,
|
|
1617
|
+
operations: Object.entries(paths).flatMap(([_path, methods]) => Object.entries(methods).map(([, operation]) => operation ? _parseOperation(mergedOptions, operation) : null).filter((op) => op !== null))
|
|
1618
|
+
}),
|
|
1670
1619
|
nameMapping
|
|
1671
1620
|
};
|
|
1672
1621
|
}
|
|
1673
1622
|
//#endregion
|
|
1674
1623
|
//#region src/adapter.ts
|
|
1624
|
+
/**
|
|
1625
|
+
* Stable string identifier for the OAS adapter used in Kubb's adapter registry.
|
|
1626
|
+
*/
|
|
1675
1627
|
const adapterOasName = "oas";
|
|
1676
1628
|
/**
|
|
1677
|
-
* Creates
|
|
1629
|
+
* Creates the default OpenAPI / Swagger adapter for Kubb.
|
|
1678
1630
|
*
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1631
|
+
* Parses the spec, optionally validates it, resolves the base URL, and converts
|
|
1632
|
+
* everything into a `RootNode` that downstream plugins consume.
|
|
1681
1633
|
*
|
|
1682
1634
|
* @example
|
|
1683
1635
|
* ```ts
|
|
1684
1636
|
* import { defineConfig } from '@kubb/core'
|
|
1685
1637
|
* import { adapterOas } from '@kubb/adapter-oas'
|
|
1638
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1686
1639
|
*
|
|
1687
1640
|
* export default defineConfig({
|
|
1688
|
-
* adapter: adapterOas({
|
|
1689
|
-
* input:
|
|
1690
|
-
* plugins: [pluginTs()
|
|
1641
|
+
* adapter: adapterOas({ dateType: 'date', serverIndex: 0 }),
|
|
1642
|
+
* input: { path: './openapi.yaml' },
|
|
1643
|
+
* plugins: [pluginTs()],
|
|
1691
1644
|
* })
|
|
1692
1645
|
* ```
|
|
1693
1646
|
*/
|
|
1694
1647
|
const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
1695
|
-
const { validate = true,
|
|
1696
|
-
|
|
1648
|
+
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;
|
|
1649
|
+
let nameMapping = /* @__PURE__ */ new Map();
|
|
1697
1650
|
return {
|
|
1698
1651
|
name: "oas",
|
|
1699
|
-
options
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1652
|
+
get options() {
|
|
1653
|
+
return {
|
|
1654
|
+
validate,
|
|
1655
|
+
contentType,
|
|
1656
|
+
serverIndex,
|
|
1657
|
+
serverVariables,
|
|
1658
|
+
discriminator,
|
|
1659
|
+
dateType,
|
|
1660
|
+
integerType,
|
|
1661
|
+
unknownType,
|
|
1662
|
+
emptySchemaType,
|
|
1663
|
+
enumSuffix,
|
|
1664
|
+
nameMapping
|
|
1665
|
+
};
|
|
1712
1666
|
},
|
|
1713
1667
|
getImports(node, resolve) {
|
|
1714
1668
|
return (0, _kubb_ast.collectImports)({
|
|
@@ -1725,54 +1679,32 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1725
1679
|
});
|
|
1726
1680
|
},
|
|
1727
1681
|
async parse(source) {
|
|
1728
|
-
const
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
discriminator
|
|
1732
|
-
});
|
|
1733
|
-
if (validate) try {
|
|
1734
|
-
await oas.validate();
|
|
1735
|
-
} catch (_err) {}
|
|
1736
|
-
const server = serverIndex !== void 0 ? oas.api.servers?.at(serverIndex) : void 0;
|
|
1682
|
+
const document = await parseFromConfig(source);
|
|
1683
|
+
if (validate) await validateDocument(document);
|
|
1684
|
+
const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
|
|
1737
1685
|
const baseURL = server?.url ? resolveServerUrl(server, serverVariables) : void 0;
|
|
1738
|
-
const
|
|
1739
|
-
|
|
1686
|
+
const { root: parsedRoot, nameMapping: parsedNameMapping } = parseOas(document, {
|
|
1687
|
+
contentType,
|
|
1740
1688
|
dateType,
|
|
1741
1689
|
integerType,
|
|
1742
1690
|
unknownType,
|
|
1743
1691
|
emptySchemaType,
|
|
1744
1692
|
enumSuffix
|
|
1745
1693
|
});
|
|
1746
|
-
|
|
1747
|
-
|
|
1694
|
+
const root = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
|
|
1695
|
+
nameMapping = parsedNameMapping;
|
|
1748
1696
|
return (0, _kubb_ast.createRoot)({
|
|
1749
1697
|
...root,
|
|
1750
1698
|
meta: {
|
|
1751
|
-
title:
|
|
1752
|
-
description:
|
|
1753
|
-
version:
|
|
1699
|
+
title: document.info?.title,
|
|
1700
|
+
description: document.info?.description,
|
|
1701
|
+
version: document.info?.version,
|
|
1754
1702
|
baseURL
|
|
1755
1703
|
}
|
|
1756
1704
|
});
|
|
1757
1705
|
}
|
|
1758
1706
|
};
|
|
1759
1707
|
});
|
|
1760
|
-
function sourceToFakeConfig(source) {
|
|
1761
|
-
switch (source.type) {
|
|
1762
|
-
case "path": return {
|
|
1763
|
-
root: node_path.default.dirname(source.path),
|
|
1764
|
-
input: { path: source.path }
|
|
1765
|
-
};
|
|
1766
|
-
case "data": return {
|
|
1767
|
-
root: process.cwd(),
|
|
1768
|
-
input: { data: source.data }
|
|
1769
|
-
};
|
|
1770
|
-
case "paths": return {
|
|
1771
|
-
root: source.paths[0] ? node_path.default.dirname(source.paths[0]) : process.cwd(),
|
|
1772
|
-
input: source.paths.map((p) => ({ path: p }))
|
|
1773
|
-
};
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
1708
|
//#endregion
|
|
1777
1709
|
exports.adapterOas = adapterOas;
|
|
1778
1710
|
exports.adapterOasName = adapterOasName;
|