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