@kubb/adapter-oas 5.0.0-beta.8 → 5.0.0-beta.80
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/LICENSE +17 -10
- package/README.md +26 -24
- package/dist/index.cjs +1416 -871
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +844 -78
- package/dist/index.js +1419 -869
- package/dist/index.js.map +1 -1
- package/package.json +13 -10
- package/extension.yaml +0 -344
- package/src/adapter.ts +0 -130
- package/src/constants.ts +0 -122
- package/src/discriminator.ts +0 -108
- package/src/factory.ts +0 -162
- package/src/guards.ts +0 -68
- package/src/index.ts +0 -18
- package/src/parser.ts +0 -1002
- package/src/refs.ts +0 -74
- package/src/resolvers.ts +0 -544
- package/src/types.ts +0 -206
- /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -24,25 +24,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
let _kubb_core = require("@kubb/core");
|
|
25
25
|
let node_path = require("node:path");
|
|
26
26
|
node_path = __toESM(node_path, 1);
|
|
27
|
-
let
|
|
28
|
-
let
|
|
29
|
-
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
let
|
|
35
|
-
let oas_utils = require("oas/utils");
|
|
27
|
+
let node_fs_promises = require("node:fs/promises");
|
|
28
|
+
let _readme_openapi_parser = require("@readme/openapi-parser");
|
|
29
|
+
let _scalar_openapi_upgrader = require("@scalar/openapi-upgrader");
|
|
30
|
+
let yaml = require("yaml");
|
|
31
|
+
let api_ref_bundler = require("api-ref-bundler");
|
|
32
|
+
let _kubb_ast_macros = require("@kubb/ast/macros");
|
|
33
|
+
let _kubb_ast_utils = require("@kubb/ast/utils");
|
|
34
|
+
let _kubb_ast = require("@kubb/ast");
|
|
36
35
|
//#region src/constants.ts
|
|
37
36
|
/**
|
|
38
37
|
* Default parser options applied when no explicit options are provided.
|
|
39
38
|
*
|
|
40
39
|
* @example
|
|
41
40
|
* ```ts
|
|
42
|
-
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
41
|
+
* import { DEFAULT_PARSER_OPTIONS, parseOas } from '@kubb/adapter-oas'
|
|
43
42
|
*
|
|
44
|
-
* const
|
|
45
|
-
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
43
|
+
* const { root } = parseOas(document, { ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
46
44
|
* ```
|
|
47
45
|
*/
|
|
48
46
|
const DEFAULT_PARSER_OPTIONS = {
|
|
@@ -64,17 +62,19 @@ const DEFAULT_PARSER_OPTIONS = {
|
|
|
64
62
|
*/
|
|
65
63
|
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
66
64
|
/**
|
|
67
|
-
*
|
|
65
|
+
* HTTP methods that count as operations on an OpenAPI path item. Other keys
|
|
66
|
+
* (`parameters`, `summary`, `$ref`, vendor extensions) are skipped when iterating operations.
|
|
68
67
|
*/
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
const SUPPORTED_METHODS = /* @__PURE__ */ new Set([
|
|
69
|
+
"get",
|
|
70
|
+
"put",
|
|
71
|
+
"post",
|
|
72
|
+
"delete",
|
|
73
|
+
"options",
|
|
74
|
+
"head",
|
|
75
|
+
"patch",
|
|
76
|
+
"trace"
|
|
77
|
+
]);
|
|
78
78
|
/**
|
|
79
79
|
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
80
80
|
*
|
|
@@ -89,7 +89,7 @@ const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
|
89
89
|
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
90
90
|
* ```
|
|
91
91
|
*/
|
|
92
|
-
const structuralKeys = new Set([
|
|
92
|
+
const structuralKeys = /* @__PURE__ */ new Set([
|
|
93
93
|
"properties",
|
|
94
94
|
"items",
|
|
95
95
|
"additionalProperties",
|
|
@@ -99,12 +99,24 @@ const structuralKeys = new Set([
|
|
|
99
99
|
"not"
|
|
100
100
|
]);
|
|
101
101
|
/**
|
|
102
|
+
* Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
|
|
103
|
+
* `int64` and the date/time family. Keep this in sync with the `convertFormat`
|
|
104
|
+
* special-cases in `parser.ts`. `isHandledFormat` reads it so the
|
|
105
|
+
* `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
|
|
106
|
+
*/
|
|
107
|
+
const specialCasedFormats = /* @__PURE__ */ new Set([
|
|
108
|
+
"int64",
|
|
109
|
+
"date-time",
|
|
110
|
+
"date",
|
|
111
|
+
"time"
|
|
112
|
+
]);
|
|
113
|
+
/**
|
|
102
114
|
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
103
115
|
*
|
|
104
116
|
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
105
|
-
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
|
|
106
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types
|
|
107
|
-
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
117
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled
|
|
118
|
+
* separately in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname`
|
|
119
|
+
* and `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
108
120
|
*
|
|
109
121
|
* @example
|
|
110
122
|
* ```ts
|
|
@@ -144,95 +156,16 @@ const formatMap = {
|
|
|
144
156
|
*/
|
|
145
157
|
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
146
158
|
/**
|
|
147
|
-
*
|
|
148
|
-
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
149
|
-
*/
|
|
150
|
-
const typeOptionMap = new Map([
|
|
151
|
-
["any", _kubb_core.ast.schemaTypes.any],
|
|
152
|
-
["unknown", _kubb_core.ast.schemaTypes.unknown],
|
|
153
|
-
["void", _kubb_core.ast.schemaTypes.void]
|
|
154
|
-
]);
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/discriminator.ts
|
|
157
|
-
/**
|
|
158
|
-
* Injects discriminator enum values into child schemas so they know which value identifies them.
|
|
159
|
-
*
|
|
160
|
-
* Finds every union schema in `input.schemas` that has a `discriminatorPropertyName`, collects the
|
|
161
|
-
* enum value each union member is mapped to, then adds (or replaces) that property on the matching
|
|
162
|
-
* child object schema.
|
|
163
|
-
*
|
|
164
|
-
* Returns a new `InputNode` — the original is never mutated.
|
|
159
|
+
* Vendor extension keys that attach human-readable descriptions to enum values, checked in priority order.
|
|
165
160
|
*
|
|
166
161
|
* @example
|
|
167
162
|
* ```ts
|
|
168
|
-
*
|
|
169
|
-
*
|
|
163
|
+
* import { enumDescriptionKeys } from '@kubb/adapter-oas'
|
|
164
|
+
*
|
|
165
|
+
* const key = enumDescriptionKeys.find((k) => k in schema) // 'x-enumDescriptions' | 'x-enum-descriptions' | undefined
|
|
170
166
|
* ```
|
|
171
167
|
*/
|
|
172
|
-
|
|
173
|
-
const childMap = /* @__PURE__ */ new Map();
|
|
174
|
-
for (const schema of root.schemas) {
|
|
175
|
-
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
176
|
-
if (!unionNode) {
|
|
177
|
-
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
178
|
-
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
179
|
-
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
180
|
-
if (u) {
|
|
181
|
-
unionNode = u;
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
187
|
-
const { discriminatorPropertyName, members } = unionNode;
|
|
188
|
-
for (const member of members) {
|
|
189
|
-
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
190
|
-
if (!intersectionNode?.members) continue;
|
|
191
|
-
let refNode;
|
|
192
|
-
let objNode;
|
|
193
|
-
for (const m of intersectionNode.members) {
|
|
194
|
-
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
195
|
-
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
196
|
-
}
|
|
197
|
-
if (!refNode?.name || !objNode) continue;
|
|
198
|
-
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
199
|
-
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : void 0;
|
|
200
|
-
if (!enumNode?.enumValues?.length) continue;
|
|
201
|
-
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
202
|
-
if (!enumValues.length) continue;
|
|
203
|
-
const existing = childMap.get(refNode.name);
|
|
204
|
-
if (existing) existing.enumValues.push(...enumValues);
|
|
205
|
-
else childMap.set(refNode.name, {
|
|
206
|
-
propertyName: discriminatorPropertyName,
|
|
207
|
-
enumValues: [...enumValues]
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (childMap.size === 0) return root;
|
|
212
|
-
return _kubb_core.ast.transform(root, { schema(node, { parent }) {
|
|
213
|
-
if (parent?.kind !== "Input" || !node.name) return;
|
|
214
|
-
const entry = childMap.get(node.name);
|
|
215
|
-
if (!entry) return;
|
|
216
|
-
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
217
|
-
if (!objectNode) return;
|
|
218
|
-
const { propertyName, enumValues } = entry;
|
|
219
|
-
const enumSchema = _kubb_core.ast.createSchema({
|
|
220
|
-
type: "enum",
|
|
221
|
-
enumValues
|
|
222
|
-
});
|
|
223
|
-
const newProp = _kubb_core.ast.createProperty({
|
|
224
|
-
name: propertyName,
|
|
225
|
-
required: true,
|
|
226
|
-
schema: enumSchema
|
|
227
|
-
});
|
|
228
|
-
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
229
|
-
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
230
|
-
return {
|
|
231
|
-
...objectNode,
|
|
232
|
-
properties: newProperties
|
|
233
|
-
};
|
|
234
|
-
} });
|
|
235
|
-
}
|
|
168
|
+
const enumDescriptionKeys = ["x-enumDescriptions", "x-enum-descriptions"];
|
|
236
169
|
//#endregion
|
|
237
170
|
//#region ../../internals/utils/src/casing.ts
|
|
238
171
|
/**
|
|
@@ -245,357 +178,238 @@ function applyDiscriminatorInheritance(root) {
|
|
|
245
178
|
function toCamelOrPascal(text, pascal) {
|
|
246
179
|
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
247
180
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
248
|
-
|
|
249
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
181
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
250
182
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
251
183
|
}
|
|
252
184
|
/**
|
|
253
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
254
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
255
|
-
* Segments are joined with `/` to form a file path.
|
|
256
|
-
*
|
|
257
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
258
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
259
|
-
*
|
|
260
|
-
* Empty segments are filtered before joining. They arise when the text starts with
|
|
261
|
-
* a dot followed immediately by a letter (e.g. `..Schema` splits into `['..', 'Schema']`
|
|
262
|
-
* and `'..'` transforms to an empty string). Without this filter the join would produce
|
|
263
|
-
* a leading `/`, which `path.resolve` would interpret as an absolute path, allowing
|
|
264
|
-
* generated files to escape the configured output directory.
|
|
265
|
-
*/
|
|
266
|
-
function applyToFileParts(text, transformPart) {
|
|
267
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
268
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).filter(Boolean).join("/");
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Converts `text` to camelCase.
|
|
272
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
273
|
-
*
|
|
274
|
-
* @example
|
|
275
|
-
* camelCase('hello-world') // 'helloWorld'
|
|
276
|
-
* camelCase('pet.petId', { isFile: true }) // 'pet/petId'
|
|
277
|
-
*/
|
|
278
|
-
function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
279
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
280
|
-
prefix,
|
|
281
|
-
suffix
|
|
282
|
-
} : {}));
|
|
283
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
185
|
* Converts `text` to PascalCase.
|
|
287
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
288
186
|
*
|
|
289
|
-
* @example
|
|
290
|
-
* pascalCase('hello-world')
|
|
291
|
-
* pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
|
|
292
|
-
*/
|
|
293
|
-
function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
294
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
295
|
-
prefix,
|
|
296
|
-
suffix
|
|
297
|
-
}) : camelCase(part));
|
|
298
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
299
|
-
}
|
|
300
|
-
//#endregion
|
|
301
|
-
//#region ../../internals/utils/src/object.ts
|
|
302
|
-
/**
|
|
303
|
-
* Returns `true` when `value` is a plain (non-null, non-array) object.
|
|
304
|
-
*
|
|
305
|
-
* @example
|
|
306
|
-
* ```ts
|
|
307
|
-
* isPlainObject({}) // true
|
|
308
|
-
* isPlainObject([]) // false
|
|
309
|
-
* isPlainObject(null) // false
|
|
310
|
-
* ```
|
|
311
|
-
*/
|
|
312
|
-
function isPlainObject(value) {
|
|
313
|
-
return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Recursively merges `source` into `target`, combining nested plain objects.
|
|
317
|
-
* Arrays and non-object values from `source` override the corresponding values in `target`.
|
|
318
|
-
*
|
|
319
|
-
* @example
|
|
320
|
-
* ```ts
|
|
321
|
-
* mergeDeep({ a: { x: 1 } }, { a: { y: 2 } })
|
|
322
|
-
* // { a: { x: 1, y: 2 } }
|
|
323
|
-
* ```
|
|
324
|
-
*/
|
|
325
|
-
function mergeDeep(target, source) {
|
|
326
|
-
const result = { ...target };
|
|
327
|
-
for (const key of Object.keys(source)) {
|
|
328
|
-
const sv = source[key];
|
|
329
|
-
const tv = result[key];
|
|
330
|
-
result[key] = sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv) ? mergeDeep(tv, sv) : sv;
|
|
331
|
-
}
|
|
332
|
-
return result;
|
|
333
|
-
}
|
|
334
|
-
//#endregion
|
|
335
|
-
//#region ../../internals/utils/src/reserved.ts
|
|
336
|
-
/**
|
|
337
|
-
* JavaScript and Java reserved words.
|
|
338
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
339
|
-
*/
|
|
340
|
-
const reservedWords = new Set([
|
|
341
|
-
"abstract",
|
|
342
|
-
"arguments",
|
|
343
|
-
"boolean",
|
|
344
|
-
"break",
|
|
345
|
-
"byte",
|
|
346
|
-
"case",
|
|
347
|
-
"catch",
|
|
348
|
-
"char",
|
|
349
|
-
"class",
|
|
350
|
-
"const",
|
|
351
|
-
"continue",
|
|
352
|
-
"debugger",
|
|
353
|
-
"default",
|
|
354
|
-
"delete",
|
|
355
|
-
"do",
|
|
356
|
-
"double",
|
|
357
|
-
"else",
|
|
358
|
-
"enum",
|
|
359
|
-
"eval",
|
|
360
|
-
"export",
|
|
361
|
-
"extends",
|
|
362
|
-
"false",
|
|
363
|
-
"final",
|
|
364
|
-
"finally",
|
|
365
|
-
"float",
|
|
366
|
-
"for",
|
|
367
|
-
"function",
|
|
368
|
-
"goto",
|
|
369
|
-
"if",
|
|
370
|
-
"implements",
|
|
371
|
-
"import",
|
|
372
|
-
"in",
|
|
373
|
-
"instanceof",
|
|
374
|
-
"int",
|
|
375
|
-
"interface",
|
|
376
|
-
"let",
|
|
377
|
-
"long",
|
|
378
|
-
"native",
|
|
379
|
-
"new",
|
|
380
|
-
"null",
|
|
381
|
-
"package",
|
|
382
|
-
"private",
|
|
383
|
-
"protected",
|
|
384
|
-
"public",
|
|
385
|
-
"return",
|
|
386
|
-
"short",
|
|
387
|
-
"static",
|
|
388
|
-
"super",
|
|
389
|
-
"switch",
|
|
390
|
-
"synchronized",
|
|
391
|
-
"this",
|
|
392
|
-
"throw",
|
|
393
|
-
"throws",
|
|
394
|
-
"transient",
|
|
395
|
-
"true",
|
|
396
|
-
"try",
|
|
397
|
-
"typeof",
|
|
398
|
-
"var",
|
|
399
|
-
"void",
|
|
400
|
-
"volatile",
|
|
401
|
-
"while",
|
|
402
|
-
"with",
|
|
403
|
-
"yield",
|
|
404
|
-
"Array",
|
|
405
|
-
"Date",
|
|
406
|
-
"hasOwnProperty",
|
|
407
|
-
"Infinity",
|
|
408
|
-
"isFinite",
|
|
409
|
-
"isNaN",
|
|
410
|
-
"isPrototypeOf",
|
|
411
|
-
"length",
|
|
412
|
-
"Math",
|
|
413
|
-
"name",
|
|
414
|
-
"NaN",
|
|
415
|
-
"Number",
|
|
416
|
-
"Object",
|
|
417
|
-
"prototype",
|
|
418
|
-
"String",
|
|
419
|
-
"toString",
|
|
420
|
-
"undefined",
|
|
421
|
-
"valueOf"
|
|
422
|
-
]);
|
|
423
|
-
/**
|
|
424
|
-
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
187
|
+
* @example Word boundaries
|
|
188
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
425
189
|
*
|
|
426
|
-
* @example
|
|
427
|
-
*
|
|
428
|
-
* isValidVarName('status') // true
|
|
429
|
-
* isValidVarName('class') // false (reserved word)
|
|
430
|
-
* isValidVarName('42foo') // false (starts with digit)
|
|
431
|
-
* ```
|
|
190
|
+
* @example With a suffix
|
|
191
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
432
192
|
*/
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
193
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
194
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
436
195
|
}
|
|
437
196
|
//#endregion
|
|
438
|
-
//#region ../../internals/utils/src/
|
|
197
|
+
//#region ../../internals/utils/src/runtime.ts
|
|
439
198
|
/**
|
|
440
|
-
*
|
|
199
|
+
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
441
200
|
*
|
|
442
|
-
* @
|
|
443
|
-
* const p = new URLPath('/pet/{petId}')
|
|
444
|
-
* p.URL // '/pet/:petId'
|
|
445
|
-
* p.template // '`/pet/${petId}`'
|
|
201
|
+
* Prefer the shared {@link runtime} instance over constructing your own.
|
|
446
202
|
*/
|
|
447
|
-
var
|
|
448
|
-
/**
|
|
449
|
-
* The raw OpenAPI/Swagger path string, e.g. `/pet/{petId}`.
|
|
450
|
-
*/
|
|
451
|
-
path;
|
|
452
|
-
#options;
|
|
453
|
-
constructor(path, options = {}) {
|
|
454
|
-
this.path = path;
|
|
455
|
-
this.#options = options;
|
|
456
|
-
}
|
|
457
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
458
|
-
*
|
|
459
|
-
* @example
|
|
460
|
-
* ```ts
|
|
461
|
-
* new URLPath('/pet/{petId}').URL // '/pet/:petId'
|
|
462
|
-
* ```
|
|
463
|
-
*/
|
|
464
|
-
get URL() {
|
|
465
|
-
return this.toURLPath();
|
|
466
|
-
}
|
|
467
|
-
/** Returns `true` when `path` is a fully-qualified URL (e.g. starts with `https://`).
|
|
468
|
-
*
|
|
469
|
-
* @example
|
|
470
|
-
* ```ts
|
|
471
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
472
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
473
|
-
* ```
|
|
474
|
-
*/
|
|
475
|
-
get isURL() {
|
|
476
|
-
try {
|
|
477
|
-
return !!new URL(this.path).href;
|
|
478
|
-
} catch {
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
203
|
+
var Runtime = class {
|
|
482
204
|
/**
|
|
483
|
-
*
|
|
205
|
+
* `true` when the current process is running under Bun.
|
|
484
206
|
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*/
|
|
489
|
-
get template() {
|
|
490
|
-
return this.toTemplateString();
|
|
491
|
-
}
|
|
492
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
207
|
+
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
208
|
+
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
209
|
+
* otherwise look like Node.
|
|
493
210
|
*
|
|
494
211
|
* @example
|
|
495
212
|
* ```ts
|
|
496
|
-
*
|
|
497
|
-
*
|
|
213
|
+
* if (runtime.isBun) {
|
|
214
|
+
* await Bun.write(path, data)
|
|
215
|
+
* }
|
|
498
216
|
* ```
|
|
499
217
|
*/
|
|
500
|
-
get
|
|
501
|
-
return
|
|
502
|
-
}
|
|
503
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
504
|
-
*
|
|
505
|
-
* @example
|
|
506
|
-
* ```ts
|
|
507
|
-
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
508
|
-
* new URLPath('/pet').params // undefined
|
|
509
|
-
* ```
|
|
510
|
-
*/
|
|
511
|
-
get params() {
|
|
512
|
-
return this.getParams();
|
|
513
|
-
}
|
|
514
|
-
#transformParam(raw) {
|
|
515
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
516
|
-
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
218
|
+
get isBun() {
|
|
219
|
+
return typeof Bun !== "undefined";
|
|
517
220
|
}
|
|
518
221
|
/**
|
|
519
|
-
*
|
|
222
|
+
* `true` when the current process is running under Deno.
|
|
520
223
|
*/
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const raw = match[1];
|
|
524
|
-
fn(raw, this.#transformParam(raw));
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
528
|
-
const object = {
|
|
529
|
-
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
530
|
-
params: this.getParams()
|
|
531
|
-
};
|
|
532
|
-
if (stringify) {
|
|
533
|
-
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
534
|
-
if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
|
|
535
|
-
return `{ url: '${object.url}' }`;
|
|
536
|
-
}
|
|
537
|
-
return object;
|
|
224
|
+
get isDeno() {
|
|
225
|
+
return typeof globalThis.Deno !== "undefined";
|
|
538
226
|
}
|
|
539
227
|
/**
|
|
540
|
-
*
|
|
541
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
228
|
+
* `true` when the current process is running under Node.
|
|
542
229
|
*
|
|
543
|
-
*
|
|
544
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
230
|
+
* Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
|
|
545
231
|
*/
|
|
546
|
-
|
|
547
|
-
return
|
|
548
|
-
if (i % 2 === 0) return part;
|
|
549
|
-
const param = this.#transformParam(part);
|
|
550
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
551
|
-
}).join("")}\``;
|
|
232
|
+
get isNode() {
|
|
233
|
+
return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
|
|
552
234
|
}
|
|
553
235
|
/**
|
|
554
|
-
*
|
|
555
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
556
|
-
* Returns `undefined` when no path parameters are found.
|
|
236
|
+
* Name of the runtime executing the current process.
|
|
557
237
|
*
|
|
558
238
|
* @example
|
|
559
239
|
* ```ts
|
|
560
|
-
*
|
|
561
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
240
|
+
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
562
241
|
* ```
|
|
563
242
|
*/
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
this
|
|
567
|
-
|
|
568
|
-
params[key] = key;
|
|
569
|
-
});
|
|
570
|
-
return Object.keys(params).length > 0 ? params : void 0;
|
|
243
|
+
get name() {
|
|
244
|
+
if (this.isBun) return "bun";
|
|
245
|
+
if (this.isDeno) return "deno";
|
|
246
|
+
return "node";
|
|
571
247
|
}
|
|
572
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* Version of the active runtime, or an empty string when it cannot be read.
|
|
573
250
|
*
|
|
574
251
|
* @example
|
|
575
252
|
* ```ts
|
|
576
|
-
*
|
|
253
|
+
* runtime.version // '1.3.11' under Bun, '22.22.2' under Node
|
|
577
254
|
* ```
|
|
578
255
|
*/
|
|
579
|
-
|
|
580
|
-
|
|
256
|
+
get version() {
|
|
257
|
+
if (this.isBun) return process.versions.bun ?? "";
|
|
258
|
+
if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
|
|
259
|
+
return process.versions?.node ?? "";
|
|
581
260
|
}
|
|
582
261
|
};
|
|
262
|
+
/**
|
|
263
|
+
* Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
|
|
264
|
+
*/
|
|
265
|
+
const runtime = new Runtime();
|
|
583
266
|
//#endregion
|
|
584
|
-
//#region src/
|
|
267
|
+
//#region ../../internals/utils/src/fs.ts
|
|
585
268
|
/**
|
|
586
|
-
*
|
|
269
|
+
* Resolves to `true` when the file or directory at `path` exists.
|
|
270
|
+
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
587
271
|
*
|
|
588
272
|
* @example
|
|
589
273
|
* ```ts
|
|
590
|
-
* if (
|
|
591
|
-
*
|
|
274
|
+
* if (await exists('./kubb.config.ts')) {
|
|
275
|
+
* const content = await read('./kubb.config.ts')
|
|
592
276
|
* }
|
|
593
277
|
* ```
|
|
594
278
|
*/
|
|
595
|
-
function
|
|
596
|
-
|
|
279
|
+
async function exists(path) {
|
|
280
|
+
if (runtime.isBun) return Bun.file(path).exists();
|
|
281
|
+
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Reads the file at `path` as a UTF-8 string.
|
|
285
|
+
* Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* const source = await read('./src/Pet.ts')
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
async function read(path) {
|
|
293
|
+
if (runtime.isBun) return Bun.file(path).text();
|
|
294
|
+
return (0, node_fs_promises.readFile)(path, { encoding: "utf8" });
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/bundler.ts
|
|
298
|
+
const urlRegExp = /^https?:\/+/i;
|
|
299
|
+
async function readSource(sourcePath) {
|
|
300
|
+
if (urlRegExp.test(sourcePath)) {
|
|
301
|
+
const url = new URL(sourcePath);
|
|
302
|
+
const response = await fetch(url);
|
|
303
|
+
if (!response.ok) throw new Error(`Cannot fetch the OAS document at ${url.href} (HTTP ${response.status})`);
|
|
304
|
+
return response.text();
|
|
305
|
+
}
|
|
306
|
+
return read(sourcePath);
|
|
307
|
+
}
|
|
308
|
+
async function resolveSource(sourcePath) {
|
|
309
|
+
const data = await readSource(sourcePath);
|
|
310
|
+
if (sourcePath.toLowerCase().endsWith(".md")) return data;
|
|
311
|
+
return (0, yaml.parse)(data);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Bundles a multi-file OpenAPI document into a single document via `api-ref-bundler`.
|
|
315
|
+
*
|
|
316
|
+
* External file schemas are hoisted into named `components.schemas` entries, so a property
|
|
317
|
+
* pointing at `./schemas/User.yaml` ends up referencing `#/components/schemas/User`. Generators
|
|
318
|
+
* can then emit a named type with an import instead of inlining the shape. Sources are read with
|
|
319
|
+
* the Bun-aware `read` util for local YAML and JSON files, and with `fetch` for HTTP(S) URLs.
|
|
320
|
+
*
|
|
321
|
+
* @example Local file
|
|
322
|
+
* `const document = await bundleDocument('./openapi.yaml')`
|
|
323
|
+
*
|
|
324
|
+
* @example Remote URL
|
|
325
|
+
* `const document = await bundleDocument('https://example.com/openapi.yaml')`
|
|
326
|
+
*/
|
|
327
|
+
async function bundleDocument(pathOrUrl) {
|
|
328
|
+
const cache = /* @__PURE__ */ new Map();
|
|
329
|
+
const resolver = (sourcePath) => {
|
|
330
|
+
const key = urlRegExp.test(sourcePath) ? new URL(sourcePath).href : sourcePath;
|
|
331
|
+
const cached = cache.get(key);
|
|
332
|
+
if (cached) return cached;
|
|
333
|
+
const result = resolveSource(sourcePath);
|
|
334
|
+
cache.set(key, result);
|
|
335
|
+
return result;
|
|
336
|
+
};
|
|
337
|
+
await resolver(pathOrUrl);
|
|
338
|
+
return await (0, api_ref_bundler.bundle)(pathOrUrl, resolver);
|
|
339
|
+
}
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/factory.ts
|
|
342
|
+
/**
|
|
343
|
+
* Loads and bundles an OpenAPI document, returning the raw `Document`.
|
|
344
|
+
*
|
|
345
|
+
* A string is a file path or URL: it is bundled via `api-ref-bundler`, hoisting external file
|
|
346
|
+
* schemas into named `components.schemas` entries so generators can emit named types and imports.
|
|
347
|
+
* An object is treated as an already-parsed document. Swagger 2.0 and OpenAPI 3.0 documents are
|
|
348
|
+
* up-converted to OpenAPI 3.1 via `@scalar/openapi-upgrader`.
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```ts
|
|
352
|
+
* const document = await parseDocument('./openapi.yaml')
|
|
353
|
+
* const document = await parseDocument(rawDocumentObject)
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
async function parseDocument(pathOrApi) {
|
|
357
|
+
if (typeof pathOrApi === "string") return parseDocument(await bundleDocument(pathOrApi));
|
|
358
|
+
return (0, _scalar_openapi_upgrader.upgrade)(pathOrApi, "3.1");
|
|
597
359
|
}
|
|
598
360
|
/**
|
|
361
|
+
* Creates a `Document` from an `AdapterSource`.
|
|
362
|
+
*
|
|
363
|
+
* - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
|
|
364
|
+
* - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```ts
|
|
368
|
+
* const document = await parseFromConfig({ type: 'path', path: './openapi.yaml' })
|
|
369
|
+
* const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
async function parseFromConfig(source) {
|
|
373
|
+
if (source.type === "data") return parseDocument(typeof source.data === "string" ? (0, yaml.parse)(source.data) : structuredClone(source.data));
|
|
374
|
+
if (URL.canParse(source.path)) return parseDocument(source.path);
|
|
375
|
+
const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
|
|
376
|
+
await assertInputExists(resolved);
|
|
377
|
+
return parseDocument(resolved);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Throws a coded `KUBB_INPUT_NOT_FOUND` diagnostic when a local input path does not exist.
|
|
381
|
+
* URLs are skipped, and a malformed but readable file is left for `parseDocument` to surface
|
|
382
|
+
* its parse error instead.
|
|
383
|
+
*/
|
|
384
|
+
async function assertInputExists(input) {
|
|
385
|
+
if (URL.canParse(input)) return;
|
|
386
|
+
if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
|
|
387
|
+
code: _kubb_core.Diagnostics.code.inputNotFound,
|
|
388
|
+
severity: "error",
|
|
389
|
+
message: `Cannot read the file set in \`input.path\` (or via \`kubb generate PATH\`): ${input}`,
|
|
390
|
+
help: "Check that the path exists and is readable, then set it in `input.path` or pass it as `kubb generate PATH`.",
|
|
391
|
+
location: { kind: "config" }
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Validates an OpenAPI document using `@readme/openapi-parser` with colorized error output.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```ts
|
|
399
|
+
* await validateDocument(document)
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
async function validateDocument(document, { throwOnError = false } = {}) {
|
|
403
|
+
try {
|
|
404
|
+
const result = await (0, _readme_openapi_parser.validate)(structuredClone(document), { validate: { errors: { colorize: true } } });
|
|
405
|
+
if (!result.valid) throw new Error((0, _readme_openapi_parser.compileErrors)(result));
|
|
406
|
+
} catch (error) {
|
|
407
|
+
if (throwOnError) throw error;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/guards.ts
|
|
412
|
+
/**
|
|
599
413
|
* Returns `true` when a schema should be treated as nullable.
|
|
600
414
|
*
|
|
601
415
|
* Recognizes all nullable signals across OAS versions: `nullable: true` (OAS 3.0),
|
|
@@ -641,158 +455,369 @@ function isDiscriminator(obj) {
|
|
|
641
455
|
return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
|
|
642
456
|
}
|
|
643
457
|
//#endregion
|
|
644
|
-
//#region src/
|
|
458
|
+
//#region src/refs.ts
|
|
459
|
+
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
645
460
|
/**
|
|
646
|
-
*
|
|
461
|
+
* Resolves a local JSON pointer reference from a document.
|
|
647
462
|
*
|
|
648
|
-
* Accepts
|
|
649
|
-
*
|
|
650
|
-
* to
|
|
463
|
+
* Accepts `#/...` refs. Returns `null` for an empty or non-local ref. When the pointer cannot be
|
|
464
|
+
* resolved, reports a `refNotFound` diagnostic into the active build and returns `null`. Outside a
|
|
465
|
+
* build there is no sink to collect it, so it throws instead.
|
|
651
466
|
*
|
|
652
467
|
* @example
|
|
653
468
|
* ```ts
|
|
654
|
-
*
|
|
655
|
-
* const document = await parse(rawDocumentObject, { canBundle: false })
|
|
469
|
+
* resolveRef<SchemaObject>(document, '#/components/schemas/Pet')
|
|
656
470
|
* ```
|
|
657
471
|
*/
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (
|
|
672
|
-
const
|
|
673
|
-
|
|
472
|
+
function resolveRef(document, $ref) {
|
|
473
|
+
const origRef = $ref;
|
|
474
|
+
$ref = $ref.trim();
|
|
475
|
+
if ($ref === "") return null;
|
|
476
|
+
if (!$ref.startsWith("#")) return null;
|
|
477
|
+
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
478
|
+
let docCache = _refCache.get(document);
|
|
479
|
+
if (!docCache) {
|
|
480
|
+
docCache = /* @__PURE__ */ new Map();
|
|
481
|
+
_refCache.set(document, docCache);
|
|
482
|
+
}
|
|
483
|
+
if (docCache.has($ref)) return docCache.get($ref);
|
|
484
|
+
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
485
|
+
if (!current) {
|
|
486
|
+
const diagnostic = {
|
|
487
|
+
code: _kubb_core.Diagnostics.code.refNotFound,
|
|
488
|
+
severity: "error",
|
|
489
|
+
message: `Could not find a definition for ${origRef}.`,
|
|
490
|
+
help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
|
|
491
|
+
location: {
|
|
492
|
+
kind: "schema",
|
|
493
|
+
pointer: origRef,
|
|
494
|
+
ref: origRef
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
if (!_kubb_core.Diagnostics.report(diagnostic)) throw new _kubb_core.Diagnostics.Error(diagnostic);
|
|
498
|
+
return null;
|
|
674
499
|
}
|
|
675
|
-
|
|
500
|
+
docCache.set($ref, current);
|
|
501
|
+
return current;
|
|
676
502
|
}
|
|
677
503
|
/**
|
|
678
|
-
*
|
|
504
|
+
* Resolves a `$ref` object while preserving the original `$ref` field on the result.
|
|
679
505
|
*
|
|
680
|
-
*
|
|
681
|
-
*
|
|
506
|
+
* Useful for parser flows that need both dereferenced fields and pointer
|
|
507
|
+
* identity (for naming/import purposes). Non-reference values are returned as-is.
|
|
682
508
|
*
|
|
683
509
|
* @example
|
|
684
510
|
* ```ts
|
|
685
|
-
*
|
|
511
|
+
* dereferenceWithRef(document, { $ref: '#/components/schemas/Pet' })
|
|
512
|
+
* // { $ref: '#/components/schemas/Pet', type: 'object', properties: { ... } }
|
|
686
513
|
* ```
|
|
687
514
|
*/
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
|
|
694
|
-
const seed = {
|
|
695
|
-
openapi: MERGE_OPENAPI_VERSION,
|
|
696
|
-
info: {
|
|
697
|
-
title: MERGE_DEFAULT_TITLE,
|
|
698
|
-
version: MERGE_DEFAULT_VERSION
|
|
699
|
-
},
|
|
700
|
-
paths: {},
|
|
701
|
-
components: { schemas: {} }
|
|
515
|
+
function dereferenceWithRef(document, schema) {
|
|
516
|
+
if (isReference(schema)) return {
|
|
517
|
+
...schema,
|
|
518
|
+
...resolveRef(document, schema.$ref),
|
|
519
|
+
$ref: schema.$ref
|
|
702
520
|
};
|
|
703
|
-
return
|
|
521
|
+
return schema;
|
|
704
522
|
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/dialect.ts
|
|
705
525
|
/**
|
|
706
|
-
*
|
|
526
|
+
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
527
|
+
*
|
|
528
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
529
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
530
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
531
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
|
|
532
|
+
* nullability, no discriminator object, binary via `contentEncoding` and reuses
|
|
533
|
+
* the rest unchanged.
|
|
707
534
|
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
710
|
-
* - `{ type: 'paths' }` — merges multiple file paths into a single document.
|
|
711
|
-
* - `{ type: 'data' }` — parses an inline string (YAML/JSON) or raw object.
|
|
535
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
536
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
712
537
|
*
|
|
713
538
|
* @example
|
|
714
539
|
* ```ts
|
|
715
|
-
* const
|
|
716
|
-
* const
|
|
540
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
541
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
717
542
|
* ```
|
|
718
543
|
*/
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
544
|
+
const oasDialect = _kubb_core.ast.defineDialect({
|
|
545
|
+
name: "oas",
|
|
546
|
+
schema: {
|
|
547
|
+
isNullable,
|
|
548
|
+
isReference,
|
|
549
|
+
isDiscriminator,
|
|
550
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
551
|
+
resolveRef
|
|
723
552
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
553
|
+
});
|
|
554
|
+
//#endregion
|
|
555
|
+
//#region src/discriminator.ts
|
|
556
|
+
/**
|
|
557
|
+
* Maps each child schema name to its discriminator patch data by scanning the given
|
|
558
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
559
|
+
*
|
|
560
|
+
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
561
|
+
* discriminator parents) rather than on all schemas at once.
|
|
562
|
+
*/
|
|
563
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
564
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
565
|
+
for (const schema of schemas) {
|
|
566
|
+
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
567
|
+
if (!unionNode) {
|
|
568
|
+
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
569
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
570
|
+
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
571
|
+
if (u) {
|
|
572
|
+
unionNode = u;
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
578
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
579
|
+
for (const member of members) {
|
|
580
|
+
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
581
|
+
if (!intersectionNode?.members) continue;
|
|
582
|
+
let refNode = null;
|
|
583
|
+
let objNode = null;
|
|
584
|
+
for (const m of intersectionNode.members) {
|
|
585
|
+
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
586
|
+
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
587
|
+
}
|
|
588
|
+
if (!refNode?.name || !objNode) continue;
|
|
589
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
590
|
+
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : null;
|
|
591
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
592
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
593
|
+
if (!enumValues.length) continue;
|
|
594
|
+
const existing = childMap.get(refNode.name);
|
|
595
|
+
if (!existing) {
|
|
596
|
+
childMap.set(refNode.name, {
|
|
597
|
+
propertyName: discriminatorPropertyName,
|
|
598
|
+
enumValues: [...enumValues]
|
|
599
|
+
});
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
existing.enumValues.push(...enumValues);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return childMap;
|
|
727
606
|
}
|
|
728
607
|
/**
|
|
729
|
-
*
|
|
608
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
609
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
610
|
+
* without buffering all schemas.
|
|
611
|
+
*/
|
|
612
|
+
function patchDiscriminatorNode(node, entry) {
|
|
613
|
+
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
614
|
+
if (!objectNode) return node;
|
|
615
|
+
const { propertyName, enumValues } = entry;
|
|
616
|
+
const enumSchema = _kubb_core.ast.factory.createSchema({
|
|
617
|
+
type: "enum",
|
|
618
|
+
enumValues
|
|
619
|
+
});
|
|
620
|
+
const newProp = _kubb_core.ast.factory.createProperty({
|
|
621
|
+
name: propertyName,
|
|
622
|
+
required: true,
|
|
623
|
+
schema: enumSchema
|
|
624
|
+
});
|
|
625
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
626
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
627
|
+
return {
|
|
628
|
+
...objectNode,
|
|
629
|
+
properties: newProperties
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
730
634
|
*
|
|
731
635
|
* @example
|
|
732
636
|
* ```ts
|
|
733
|
-
*
|
|
637
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
638
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
734
639
|
* ```
|
|
735
640
|
*/
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
641
|
+
function createDiscriminantNode({ propertyName, value }) {
|
|
642
|
+
return _kubb_core.ast.factory.createSchema({
|
|
643
|
+
type: "object",
|
|
644
|
+
primitive: "object",
|
|
645
|
+
properties: [_kubb_core.ast.factory.createProperty({
|
|
646
|
+
name: propertyName,
|
|
647
|
+
schema: _kubb_core.ast.factory.createSchema({
|
|
648
|
+
type: "enum",
|
|
649
|
+
primitive: "string",
|
|
650
|
+
enumValues: [value]
|
|
651
|
+
}),
|
|
652
|
+
required: true
|
|
653
|
+
})]
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```ts
|
|
661
|
+
* findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
|
|
662
|
+
* ```
|
|
663
|
+
*/
|
|
664
|
+
function findDiscriminator(mapping, ref) {
|
|
665
|
+
if (!mapping || !ref) return null;
|
|
666
|
+
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
|
|
745
667
|
}
|
|
746
668
|
//#endregion
|
|
747
|
-
//#region src/
|
|
748
|
-
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
669
|
+
//#region src/mime.ts
|
|
749
670
|
/**
|
|
750
|
-
*
|
|
671
|
+
* MIME type fragments that mark a media type as JSON-like.
|
|
751
672
|
*
|
|
752
|
-
*
|
|
753
|
-
*
|
|
673
|
+
* A content type is JSON when it contains any of these substrings. The `+json` entry catches
|
|
674
|
+
* structured-syntax suffixes such as `application/vnd.api+json`.
|
|
675
|
+
*/
|
|
676
|
+
const jsonMimeFragments = [
|
|
677
|
+
"application/json",
|
|
678
|
+
"application/x-json",
|
|
679
|
+
"text/json",
|
|
680
|
+
"text/x-json",
|
|
681
|
+
"+json"
|
|
682
|
+
];
|
|
683
|
+
/**
|
|
684
|
+
* Returns `true` when a media type string is JSON-like.
|
|
754
685
|
*
|
|
755
686
|
* @example
|
|
756
687
|
* ```ts
|
|
757
|
-
*
|
|
688
|
+
* isJsonMimeType('application/json') // true
|
|
689
|
+
* isJsonMimeType('application/vnd.api+json') // true
|
|
690
|
+
* isJsonMimeType('multipart/form-data') // false
|
|
758
691
|
* ```
|
|
759
692
|
*/
|
|
760
|
-
function
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
693
|
+
function isJsonMimeType(mimeType) {
|
|
694
|
+
return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
|
|
695
|
+
}
|
|
696
|
+
//#endregion
|
|
697
|
+
//#region src/operation.ts
|
|
698
|
+
/**
|
|
699
|
+
* Slugifies a path for the `operationId` fallback: non-alphanumerics collapse to single dashes,
|
|
700
|
+
* with no leading or trailing dash.
|
|
701
|
+
*/
|
|
702
|
+
function slugify(value) {
|
|
703
|
+
return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Returns the operation's `operationId`, falling back to `<method>_<slugified-path>` when absent.
|
|
707
|
+
*/
|
|
708
|
+
function getOperationId({ path, method, schema }) {
|
|
709
|
+
const { operationId } = schema;
|
|
710
|
+
if (typeof operationId === "string" && operationId.length > 0) return operationId;
|
|
711
|
+
return `${method}_${slugify(path).toLowerCase()}`;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Returns the declared response status codes, skipping `x-` extensions and non-object entries.
|
|
715
|
+
*/
|
|
716
|
+
function getResponseStatusCodes({ schema }) {
|
|
717
|
+
const responses = schema.responses;
|
|
718
|
+
if (!responses || isReference(responses)) return [];
|
|
719
|
+
return Object.keys(responses).filter((key) => !key.startsWith("x-") && !!responses[key] && typeof responses[key] === "object");
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Returns the response object for a status code, resolving a `$ref` in place. `false` when absent.
|
|
723
|
+
*/
|
|
724
|
+
function getResponseByStatusCode({ document, operation, statusCode }) {
|
|
725
|
+
const responses = operation.schema.responses;
|
|
726
|
+
if (!responses || isReference(responses)) return false;
|
|
727
|
+
const response = responses[statusCode];
|
|
728
|
+
if (!response) return false;
|
|
729
|
+
if (isReference(response)) {
|
|
730
|
+
const resolved = resolveRef(document, response.$ref);
|
|
731
|
+
responses[statusCode] = resolved;
|
|
732
|
+
if (!resolved || isReference(resolved)) return false;
|
|
733
|
+
return resolved;
|
|
770
734
|
}
|
|
771
|
-
|
|
772
|
-
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
773
|
-
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
774
|
-
docCache.set($ref, current);
|
|
775
|
-
return current;
|
|
735
|
+
return response;
|
|
776
736
|
}
|
|
777
737
|
/**
|
|
778
|
-
* Resolves a `$ref`
|
|
779
|
-
*
|
|
780
|
-
|
|
781
|
-
|
|
738
|
+
* Resolves the request body (dereferencing a `$ref` in place) and returns its content map, or
|
|
739
|
+
* `undefined` when the operation has no request body.
|
|
740
|
+
*/
|
|
741
|
+
function getRequestBodyContent({ document, operation }) {
|
|
742
|
+
const { schema } = operation;
|
|
743
|
+
let requestBody = schema.requestBody;
|
|
744
|
+
if (!requestBody) return;
|
|
745
|
+
if (isReference(requestBody)) {
|
|
746
|
+
const resolved = resolveRef(document, requestBody.$ref);
|
|
747
|
+
schema.requestBody = resolved;
|
|
748
|
+
if (!resolved || isReference(resolved)) return;
|
|
749
|
+
requestBody = resolved;
|
|
750
|
+
}
|
|
751
|
+
return requestBody.content;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Returns the request body media type. With `mediaType` set, returns that entry or `false`.
|
|
755
|
+
* Otherwise picks the first JSON-like media type, then the first declared one, as a
|
|
756
|
+
* `[mediaType, object]` tuple.
|
|
757
|
+
*/
|
|
758
|
+
function getRequestContent({ document, operation, mediaType }) {
|
|
759
|
+
const content = getRequestBodyContent({
|
|
760
|
+
document,
|
|
761
|
+
operation
|
|
762
|
+
});
|
|
763
|
+
if (!content) return false;
|
|
764
|
+
if (mediaType) return mediaType in content ? content[mediaType] : false;
|
|
765
|
+
const mediaTypes = Object.keys(content);
|
|
766
|
+
const available = mediaTypes.find((mt) => isJsonMimeType(mt)) ?? mediaTypes[0];
|
|
767
|
+
return available ? [available, content[available]] : false;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
771
|
+
* when several are declared), then the first declared one, defaulting to `'application/json'`.
|
|
772
|
+
*/
|
|
773
|
+
function getRequestContentType({ document, operation }) {
|
|
774
|
+
const content = getRequestBodyContent({
|
|
775
|
+
document,
|
|
776
|
+
operation
|
|
777
|
+
});
|
|
778
|
+
const mediaTypes = content ? Object.keys(content) : [];
|
|
779
|
+
let result = mediaTypes[0] ?? "application/json";
|
|
780
|
+
for (const mt of mediaTypes) if (isJsonMimeType(mt)) result = mt;
|
|
781
|
+
return result;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Builds an `Operation` for every supported HTTP method on every path, in document order.
|
|
785
|
+
* `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
782
786
|
*
|
|
783
787
|
* @example
|
|
784
788
|
* ```ts
|
|
785
|
-
*
|
|
786
|
-
*
|
|
789
|
+
* for (const operation of getOperations(document)) {
|
|
790
|
+
* parseOperation(options, operation)
|
|
791
|
+
* }
|
|
787
792
|
* ```
|
|
788
793
|
*/
|
|
789
|
-
function
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
794
|
+
function getOperations(document) {
|
|
795
|
+
const operations = [];
|
|
796
|
+
const paths = document.paths;
|
|
797
|
+
if (!paths) return operations;
|
|
798
|
+
for (const path of Object.keys(paths)) {
|
|
799
|
+
if (path.startsWith("x-")) continue;
|
|
800
|
+
let pathItem = paths[path];
|
|
801
|
+
if (!pathItem) continue;
|
|
802
|
+
if (isReference(pathItem)) {
|
|
803
|
+
const resolved = resolveRef(document, pathItem.$ref);
|
|
804
|
+
paths[path] = resolved;
|
|
805
|
+
if (!resolved || isReference(resolved)) continue;
|
|
806
|
+
pathItem = resolved;
|
|
807
|
+
}
|
|
808
|
+
const item = pathItem;
|
|
809
|
+
for (const method of Object.keys(item)) {
|
|
810
|
+
if (!SUPPORTED_METHODS.has(method)) continue;
|
|
811
|
+
const schema = item[method];
|
|
812
|
+
if (!schema || typeof schema !== "object") continue;
|
|
813
|
+
operations.push({
|
|
814
|
+
path,
|
|
815
|
+
method,
|
|
816
|
+
schema
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return operations;
|
|
796
821
|
}
|
|
797
822
|
//#endregion
|
|
798
823
|
//#region src/resolvers.ts
|
|
@@ -816,7 +841,16 @@ function resolveServerUrl(server, overrides) {
|
|
|
816
841
|
for (const [key, variable] of Object.entries(server.variables)) {
|
|
817
842
|
const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
|
|
818
843
|
if (value === void 0) continue;
|
|
819
|
-
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new Error(
|
|
844
|
+
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.Diagnostics.Error({
|
|
845
|
+
code: _kubb_core.Diagnostics.code.invalidServerVariable,
|
|
846
|
+
severity: "error",
|
|
847
|
+
message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`,
|
|
848
|
+
help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
|
|
849
|
+
location: {
|
|
850
|
+
kind: "document",
|
|
851
|
+
pointer: "#/servers"
|
|
852
|
+
}
|
|
853
|
+
});
|
|
820
854
|
url = url.replaceAll(`{${key}}`, value);
|
|
821
855
|
}
|
|
822
856
|
return url;
|
|
@@ -829,6 +863,15 @@ function getSchemaType(format) {
|
|
|
829
863
|
return formatMap[format] ?? null;
|
|
830
864
|
}
|
|
831
865
|
/**
|
|
866
|
+
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry, plus the
|
|
867
|
+
* `specialCasedFormats` that `convertFormat` handles directly. False means the format falls back to
|
|
868
|
+
* the base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
869
|
+
* diagnostic in step with the parser as `formatMap` grows.
|
|
870
|
+
*/
|
|
871
|
+
function isHandledFormat(format) {
|
|
872
|
+
return getSchemaType(format) !== null || specialCasedFormats.has(format);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
832
875
|
* Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
|
|
833
876
|
* Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
|
|
834
877
|
*/
|
|
@@ -838,15 +881,9 @@ function getPrimitiveType(type) {
|
|
|
838
881
|
return "string";
|
|
839
882
|
}
|
|
840
883
|
/**
|
|
841
|
-
* Narrows a content-type string to the `MediaType` union Kubb recognizes, or returns `null`.
|
|
842
|
-
*/
|
|
843
|
-
function getMediaType(contentType) {
|
|
844
|
-
return Object.values(_kubb_core.ast.mediaTypes).includes(contentType) ? contentType : null;
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
884
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
848
885
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
849
|
-
* `$ref`
|
|
886
|
+
* Each `$ref` parameter is dereferenced via `dereferenceWithRef` before merging.
|
|
850
887
|
*
|
|
851
888
|
* @example
|
|
852
889
|
* ```ts
|
|
@@ -875,7 +912,7 @@ function getResponseBody(responseBody, contentType) {
|
|
|
875
912
|
}
|
|
876
913
|
let availableContentType;
|
|
877
914
|
const contentTypes = Object.keys(body.content);
|
|
878
|
-
for (const mt of contentTypes) if (
|
|
915
|
+
for (const mt of contentTypes) if (isJsonMimeType(mt)) {
|
|
879
916
|
availableContentType = mt;
|
|
880
917
|
break;
|
|
881
918
|
}
|
|
@@ -898,15 +935,21 @@ function getResponseBody(responseBody, contentType) {
|
|
|
898
935
|
* getResponseSchema(document, operation, '4XX') // {}
|
|
899
936
|
* ```
|
|
900
937
|
*/
|
|
901
|
-
function
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
938
|
+
function resolveResponseRefs(document, operation) {
|
|
939
|
+
const responses = operation.schema.responses;
|
|
940
|
+
if (!responses) return;
|
|
941
|
+
for (const key in responses) {
|
|
942
|
+
const schema = responses[key];
|
|
943
|
+
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
908
944
|
}
|
|
909
|
-
|
|
945
|
+
}
|
|
946
|
+
function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
947
|
+
resolveResponseRefs(document, operation);
|
|
948
|
+
const responseBody = getResponseBody(getResponseByStatusCode({
|
|
949
|
+
document,
|
|
950
|
+
operation,
|
|
951
|
+
statusCode
|
|
952
|
+
}), options.contentType);
|
|
910
953
|
if (responseBody === false) return {};
|
|
911
954
|
const schema = Array.isArray(responseBody) ? responseBody[1].schema : responseBody.schema;
|
|
912
955
|
if (!schema) return {};
|
|
@@ -922,16 +965,25 @@ function getResponseSchema(document, operation, statusCode, options = {}) {
|
|
|
922
965
|
*/
|
|
923
966
|
function getRequestSchema(document, operation, options = {}) {
|
|
924
967
|
if (operation.schema.requestBody) operation.schema.requestBody = dereferenceWithRef(document, operation.schema.requestBody);
|
|
925
|
-
const requestBody =
|
|
968
|
+
const requestBody = getRequestContent({
|
|
969
|
+
document,
|
|
970
|
+
operation,
|
|
971
|
+
mediaType: options.contentType
|
|
972
|
+
});
|
|
926
973
|
if (requestBody === false) return null;
|
|
974
|
+
const mediaType = Array.isArray(requestBody) ? requestBody[0] : options.contentType;
|
|
927
975
|
const schema = Array.isArray(requestBody) ? requestBody[1].schema : requestBody.schema;
|
|
976
|
+
if (mediaType === "application/octet-stream" && (!schema || Object.keys(schema).length === 0)) return {
|
|
977
|
+
type: "string",
|
|
978
|
+
contentMediaType: "application/octet-stream"
|
|
979
|
+
};
|
|
928
980
|
if (!schema) return null;
|
|
929
981
|
return dereferenceWithRef(document, schema);
|
|
930
982
|
}
|
|
931
983
|
/**
|
|
932
984
|
* Flattens a keyword-only `allOf` into its parent schema.
|
|
933
985
|
*
|
|
934
|
-
* Only flattens when every member is a plain fragment
|
|
986
|
+
* Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
|
|
935
987
|
* (see `structuralKeys`). Outer schema values take precedence over fragment values.
|
|
936
988
|
* Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
|
|
937
989
|
*
|
|
@@ -939,9 +991,12 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
939
991
|
* ```ts
|
|
940
992
|
* flattenSchema({ allOf: [{ description: 'A pet' }], type: 'object', properties: {} })
|
|
941
993
|
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
994
|
+
* ```
|
|
942
995
|
*
|
|
996
|
+
* @example
|
|
997
|
+
* ```ts
|
|
943
998
|
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
944
|
-
* // returned unchanged
|
|
999
|
+
* // returned unchanged, contains a $ref
|
|
945
1000
|
* ```
|
|
946
1001
|
*/
|
|
947
1002
|
/**
|
|
@@ -957,17 +1012,17 @@ function hasStructuralKeywords(fragment) {
|
|
|
957
1012
|
function flattenSchema(schema) {
|
|
958
1013
|
if (!schema?.allOf || schema.allOf.length === 0) return schema ?? null;
|
|
959
1014
|
const allOfFragments = schema.allOf;
|
|
960
|
-
if (allOfFragments.some((item) => (
|
|
1015
|
+
if (allOfFragments.some((item) => isReference(item))) return schema;
|
|
961
1016
|
if (allOfFragments.some(hasStructuralKeywords)) return schema;
|
|
962
|
-
const
|
|
963
|
-
|
|
1017
|
+
const { allOf: _allOf, ...rest } = schema;
|
|
1018
|
+
const merged = rest;
|
|
964
1019
|
for (const fragment of allOfFragments) for (const [key, value] of Object.entries(fragment)) if (merged[key] === void 0) merged[key] = value;
|
|
965
1020
|
return merged;
|
|
966
1021
|
}
|
|
967
1022
|
/**
|
|
968
1023
|
* Extracts the inline schema from a media-type `content` map.
|
|
969
1024
|
*
|
|
970
|
-
* Prefers `preferredContentType` when given
|
|
1025
|
+
* Prefers `preferredContentType` when given, otherwise uses the first key in the map.
|
|
971
1026
|
* Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
|
|
972
1027
|
*
|
|
973
1028
|
* @example
|
|
@@ -986,21 +1041,22 @@ function extractSchemaFromContent(content, preferredContentType) {
|
|
|
986
1041
|
/**
|
|
987
1042
|
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
988
1043
|
*/
|
|
989
|
-
function collectRefs(schema
|
|
1044
|
+
function* collectRefs(schema) {
|
|
990
1045
|
if (Array.isArray(schema)) {
|
|
991
|
-
for (const item of schema) collectRefs(item
|
|
992
|
-
return
|
|
1046
|
+
for (const item of schema) yield* collectRefs(item);
|
|
1047
|
+
return;
|
|
993
1048
|
}
|
|
994
1049
|
if (schema && typeof schema === "object") for (const key in schema) {
|
|
995
1050
|
const value = schema[key];
|
|
996
|
-
if (key === "$ref" && typeof value === "string") {
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1051
|
+
if (!(key === "$ref" && typeof value === "string")) {
|
|
1052
|
+
yield* collectRefs(value);
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
if (value.startsWith("#/components/schemas/")) {
|
|
1056
|
+
const name = value.slice(21);
|
|
1057
|
+
if (name) yield name;
|
|
1058
|
+
}
|
|
1002
1059
|
}
|
|
1003
|
-
return refs;
|
|
1004
1060
|
}
|
|
1005
1061
|
/**
|
|
1006
1062
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
@@ -1016,7 +1072,7 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
|
1016
1072
|
*/
|
|
1017
1073
|
function sortSchemas(schemas) {
|
|
1018
1074
|
const deps = /* @__PURE__ */ new Map();
|
|
1019
|
-
for (const [name, schema] of Object.entries(schemas)) deps.set(name,
|
|
1075
|
+
for (const [name, schema] of Object.entries(schemas)) deps.set(name, [...new Set(collectRefs(schema))]);
|
|
1020
1076
|
const sorted = [];
|
|
1021
1077
|
const visited = /* @__PURE__ */ new Set();
|
|
1022
1078
|
function visit(name, stack) {
|
|
@@ -1037,9 +1093,6 @@ const semanticSuffixes = {
|
|
|
1037
1093
|
responses: "Response",
|
|
1038
1094
|
requestBodies: "Request"
|
|
1039
1095
|
};
|
|
1040
|
-
function getSemanticSuffix(source) {
|
|
1041
|
-
return semanticSuffixes[source];
|
|
1042
|
-
}
|
|
1043
1096
|
function resolveSchemaRef(document, schema) {
|
|
1044
1097
|
if (!isReference(schema)) return schema;
|
|
1045
1098
|
const resolved = resolveRef(document, schema.$ref);
|
|
@@ -1088,13 +1141,13 @@ function getSchemas(document, { contentType }) {
|
|
|
1088
1141
|
let hasMultipleSources = false;
|
|
1089
1142
|
if (!isSingle) {
|
|
1090
1143
|
const firstSource = items[0].source;
|
|
1091
|
-
for (
|
|
1144
|
+
for (const item of items) if (item.source !== firstSource) {
|
|
1092
1145
|
hasMultipleSources = true;
|
|
1093
1146
|
break;
|
|
1094
1147
|
}
|
|
1095
1148
|
}
|
|
1096
1149
|
items.forEach((item, index) => {
|
|
1097
|
-
const suffix = isSingle ? "" : hasMultipleSources ?
|
|
1150
|
+
const suffix = isSingle ? "" : hasMultipleSources ? semanticSuffixes[item.source] : index === 0 ? "" : String(index + 1);
|
|
1098
1151
|
const uniqueName = item.originalName + suffix;
|
|
1099
1152
|
schemas[uniqueName] = item.schema;
|
|
1100
1153
|
nameMapping.set(`#/components/${item.source}/${item.originalName}`, uniqueName);
|
|
@@ -1107,7 +1160,7 @@ function getSchemas(document, { contentType }) {
|
|
|
1107
1160
|
}
|
|
1108
1161
|
/**
|
|
1109
1162
|
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
1110
|
-
* Returns `null` when `dateType: false`,
|
|
1163
|
+
* Returns `null` when `dateType: false`, so the format falls through to `string`.
|
|
1111
1164
|
*/
|
|
1112
1165
|
function getDateType(options, format) {
|
|
1113
1166
|
if (!options.dateType) return null;
|
|
@@ -1141,6 +1194,15 @@ function getDateType(options, format) {
|
|
|
1141
1194
|
/**
|
|
1142
1195
|
* Collects the shared metadata fields passed to every `createSchema` call.
|
|
1143
1196
|
*/
|
|
1197
|
+
/**
|
|
1198
|
+
* Reads schema examples as an array. OAS 3.1 uses an `examples` array, but specs (including ones
|
|
1199
|
+
* labeled 3.1) still use the singular OAS 3.0 `example`, which the upgrader only converts on the
|
|
1200
|
+
* 3.0 -> 3.1 hop. Normalize both into one array so the AST node exposes only `examples`.
|
|
1201
|
+
*/
|
|
1202
|
+
function extractExamples(schema) {
|
|
1203
|
+
if (Array.isArray(schema.examples)) return schema.examples;
|
|
1204
|
+
return schema.example !== void 0 ? [schema.example] : void 0;
|
|
1205
|
+
}
|
|
1144
1206
|
function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
1145
1207
|
return {
|
|
1146
1208
|
name,
|
|
@@ -1151,15 +1213,16 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
|
1151
1213
|
readOnly: schema.readOnly,
|
|
1152
1214
|
writeOnly: schema.writeOnly,
|
|
1153
1215
|
default: defaultValue,
|
|
1154
|
-
|
|
1216
|
+
examples: extractExamples(schema),
|
|
1217
|
+
format: schema.format
|
|
1155
1218
|
};
|
|
1156
1219
|
}
|
|
1157
1220
|
/**
|
|
1158
1221
|
* Returns all request body content type keys for an operation.
|
|
1159
1222
|
*
|
|
1160
|
-
* The requestBody is dereferenced
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1223
|
+
* The requestBody is dereferenced in place when it is a `$ref` (the same mutation that
|
|
1224
|
+
* `getRequestSchema` already performs), so the returned list accurately reflects the
|
|
1225
|
+
* available content types even for referenced bodies.
|
|
1163
1226
|
*
|
|
1164
1227
|
* @example
|
|
1165
1228
|
* ```ts
|
|
@@ -1173,15 +1236,38 @@ function getRequestBodyContentTypes(document, operation) {
|
|
|
1173
1236
|
if (!body) return [];
|
|
1174
1237
|
return body.content ? Object.keys(body.content) : [];
|
|
1175
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Returns all response content type keys for an operation at a given status code.
|
|
1241
|
+
*
|
|
1242
|
+
* Response `$ref`s are resolved in place first (the same mutation `getResponseSchema` performs),
|
|
1243
|
+
* so the returned list reflects the available content types even for referenced responses.
|
|
1244
|
+
*
|
|
1245
|
+
* @example
|
|
1246
|
+
* ```ts
|
|
1247
|
+
* getResponseBodyContentTypes(document, operation, 200)
|
|
1248
|
+
* // ['application/json', 'application/xml']
|
|
1249
|
+
* ```
|
|
1250
|
+
*/
|
|
1251
|
+
function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
1252
|
+
resolveResponseRefs(document, operation);
|
|
1253
|
+
const responseObj = getResponseByStatusCode({
|
|
1254
|
+
document,
|
|
1255
|
+
operation,
|
|
1256
|
+
statusCode
|
|
1257
|
+
});
|
|
1258
|
+
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
1259
|
+
const body = responseObj;
|
|
1260
|
+
return body.content ? Object.keys(body.content) : [];
|
|
1261
|
+
}
|
|
1176
1262
|
//#endregion
|
|
1177
1263
|
//#region src/parser.ts
|
|
1178
1264
|
/**
|
|
1179
1265
|
* Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
|
|
1180
1266
|
*
|
|
1181
1267
|
* This pattern violates the OpenAPI spec but appears in real specs. The fix moves enum values
|
|
1182
|
-
* from the array to its items sub-schema,
|
|
1268
|
+
* from the array to its items sub-schema, so they are valid for downstream processing.
|
|
1183
1269
|
*
|
|
1184
|
-
* @note
|
|
1270
|
+
* @note A defensive measure for non-compliant specs.
|
|
1185
1271
|
*/
|
|
1186
1272
|
function normalizeArrayEnum(schema) {
|
|
1187
1273
|
const normalizedItems = {
|
|
@@ -1195,15 +1281,48 @@ function normalizeArrayEnum(schema) {
|
|
|
1195
1281
|
};
|
|
1196
1282
|
}
|
|
1197
1283
|
/**
|
|
1284
|
+
* Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
|
|
1285
|
+
* and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
|
|
1286
|
+
*/
|
|
1287
|
+
function createNullSchema(schema, name, nullable) {
|
|
1288
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1289
|
+
type: "null",
|
|
1290
|
+
primitive: "null",
|
|
1291
|
+
name,
|
|
1292
|
+
title: schema.title,
|
|
1293
|
+
description: schema.description,
|
|
1294
|
+
deprecated: schema.deprecated,
|
|
1295
|
+
nullable,
|
|
1296
|
+
format: schema.format
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Names the inline enums on a property's schema, and on each item when the property is a tuple, from
|
|
1301
|
+
* the parent and property name. Wraps `macroEnumName` at the property construction site.
|
|
1302
|
+
*/
|
|
1303
|
+
function nameEnums(node, options) {
|
|
1304
|
+
const macro = (0, _kubb_ast_macros.macroEnumName)(options);
|
|
1305
|
+
const named = _kubb_core.ast.applyMacros(node, [macro], { depth: "shallow" });
|
|
1306
|
+
const tupleNode = _kubb_core.ast.narrowSchema(named, "tuple");
|
|
1307
|
+
if (tupleNode?.items) {
|
|
1308
|
+
const namedItems = tupleNode.items.map((item) => _kubb_core.ast.applyMacros(item, [macro], { depth: "shallow" }));
|
|
1309
|
+
if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
|
|
1310
|
+
...tupleNode,
|
|
1311
|
+
items: namedItems
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
return named;
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1198
1317
|
* Factory function that creates schema and operation converters for a given OpenAPI context.
|
|
1199
1318
|
*
|
|
1200
1319
|
* Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
|
|
1201
1320
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1202
|
-
*
|
|
1321
|
+
* which works because function declarations hoist.
|
|
1203
1322
|
*
|
|
1204
|
-
* @
|
|
1323
|
+
* @internal
|
|
1205
1324
|
*/
|
|
1206
|
-
function createSchemaParser(ctx) {
|
|
1325
|
+
function createSchemaParser(ctx, dialect = oasDialect) {
|
|
1207
1326
|
const document = ctx.document;
|
|
1208
1327
|
/**
|
|
1209
1328
|
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
@@ -1211,6 +1330,33 @@ function createSchemaParser(ctx) {
|
|
|
1211
1330
|
*/
|
|
1212
1331
|
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1213
1332
|
/**
|
|
1333
|
+
* Cache of `$ref` schemas already resolved in this parser instance, keyed by ref path.
|
|
1334
|
+
*
|
|
1335
|
+
* Without it, a shared schema (e.g. `customer`) is re-expanded for every `$ref` that points at
|
|
1336
|
+
* it. In cross-referenced specs like Stripe (~1400 schemas) that becomes exponential blowup,
|
|
1337
|
+
* since one schema can be referenced from dozens of parents, each re-walking its whole subtree.
|
|
1338
|
+
* Memoizing by ref path drops the work from O(2^depth) to O(N) unique schema names.
|
|
1339
|
+
*/
|
|
1340
|
+
const resolvedRefCache = /* @__PURE__ */ new Map();
|
|
1341
|
+
/**
|
|
1342
|
+
* Memoized record of whether a `$ref` path resolves to a node the document actually defines.
|
|
1343
|
+
* A circular ref still resolves to an existing target, so this stays `true` for cycles and only
|
|
1344
|
+
* goes `false` for a `$ref` that points at a component the spec never declares.
|
|
1345
|
+
*/
|
|
1346
|
+
const refExistence = /* @__PURE__ */ new Map();
|
|
1347
|
+
function refExists(refPath) {
|
|
1348
|
+
if (!refExistence.has(refPath)) {
|
|
1349
|
+
let exists = false;
|
|
1350
|
+
try {
|
|
1351
|
+
exists = !!dialect.schema.resolveRef(document, refPath);
|
|
1352
|
+
} catch {
|
|
1353
|
+
exists = false;
|
|
1354
|
+
}
|
|
1355
|
+
refExistence.set(refPath, exists);
|
|
1356
|
+
}
|
|
1357
|
+
return refExistence.get(refPath) ?? false;
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1214
1360
|
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1215
1361
|
*
|
|
1216
1362
|
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
@@ -1219,20 +1365,30 @@ function createSchemaParser(ctx) {
|
|
|
1219
1365
|
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1220
1366
|
*/
|
|
1221
1367
|
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1222
|
-
let resolvedSchema;
|
|
1368
|
+
let resolvedSchema = null;
|
|
1223
1369
|
const refPath = schema.$ref;
|
|
1224
|
-
if (refPath && !resolvingRefs.has(refPath))
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1370
|
+
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1371
|
+
if (!resolvedRefCache.has(refPath)) {
|
|
1372
|
+
try {
|
|
1373
|
+
const referenced = dialect.schema.resolveRef(document, refPath);
|
|
1374
|
+
if (referenced) {
|
|
1375
|
+
resolvingRefs.add(refPath);
|
|
1376
|
+
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1377
|
+
resolvingRefs.delete(refPath);
|
|
1378
|
+
}
|
|
1379
|
+
} catch {}
|
|
1380
|
+
resolvedRefCache.set(refPath, resolvedSchema);
|
|
1230
1381
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1382
|
+
resolvedSchema = resolvedRefCache.get(refPath) ?? null;
|
|
1383
|
+
}
|
|
1384
|
+
if (refPath && document.components && !refExists(refPath)) return _kubb_core.ast.factory.createSchema({
|
|
1385
|
+
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1386
|
+
type: "unknown"
|
|
1387
|
+
});
|
|
1388
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1233
1389
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1234
1390
|
type: "ref",
|
|
1235
|
-
name:
|
|
1391
|
+
name: (0, _kubb_ast_utils.extractRefName)(schema.$ref),
|
|
1236
1392
|
ref: schema.$ref,
|
|
1237
1393
|
schema: resolvedSchema
|
|
1238
1394
|
});
|
|
@@ -1245,12 +1401,12 @@ function createSchemaParser(ctx) {
|
|
|
1245
1401
|
const [memberSchema] = schema.allOf;
|
|
1246
1402
|
const memberNode = parseSchema({
|
|
1247
1403
|
schema: memberSchema,
|
|
1248
|
-
name
|
|
1404
|
+
name
|
|
1249
1405
|
}, rawOptions);
|
|
1250
1406
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1251
1407
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
1252
1408
|
const mergedDefault = schema.default === null && mergedNullable ? void 0 : schema.default ?? memberNode.default;
|
|
1253
|
-
return _kubb_core.ast.createSchema({
|
|
1409
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1254
1410
|
...memberNodeProps,
|
|
1255
1411
|
name,
|
|
1256
1412
|
title: schema.title ?? memberNode.title,
|
|
@@ -1260,22 +1416,23 @@ function createSchemaParser(ctx) {
|
|
|
1260
1416
|
readOnly: schema.readOnly ?? memberNode.readOnly,
|
|
1261
1417
|
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1262
1418
|
default: mergedDefault,
|
|
1263
|
-
|
|
1264
|
-
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
|
|
1419
|
+
examples: extractExamples(schema) ?? memberNode.examples,
|
|
1420
|
+
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1421
|
+
format: schema.format ?? memberNode.format
|
|
1265
1422
|
});
|
|
1266
1423
|
}
|
|
1267
1424
|
const filteredDiscriminantValues = [];
|
|
1268
1425
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1269
|
-
if (!isReference(item) || !name) return true;
|
|
1270
|
-
const deref = resolveRef(document, item.$ref);
|
|
1271
|
-
if (!deref || !isDiscriminator(deref)) return true;
|
|
1426
|
+
if (!dialect.schema.isReference(item) || !name) return true;
|
|
1427
|
+
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1428
|
+
if (!deref || !dialect.schema.isDiscriminator(deref)) return true;
|
|
1272
1429
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1273
1430
|
if (!parentUnion) return true;
|
|
1274
1431
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1275
|
-
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1432
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.schema.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1276
1433
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1277
1434
|
if (inOneOf || inMapping) {
|
|
1278
|
-
const discriminatorValue =
|
|
1435
|
+
const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
|
|
1279
1436
|
if (discriminatorValue) filteredDiscriminantValues.push({
|
|
1280
1437
|
propertyName: deref.discriminator.propertyName,
|
|
1281
1438
|
value: discriminatorValue
|
|
@@ -1283,22 +1440,28 @@ function createSchemaParser(ctx) {
|
|
|
1283
1440
|
return false;
|
|
1284
1441
|
}
|
|
1285
1442
|
return true;
|
|
1286
|
-
}).map((s) => parseSchema({
|
|
1443
|
+
}).map((s) => parseSchema({
|
|
1444
|
+
schema: s,
|
|
1445
|
+
name
|
|
1446
|
+
}, rawOptions));
|
|
1287
1447
|
const syntheticStart = allOfMembers.length;
|
|
1288
1448
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1289
1449
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1290
1450
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1291
1451
|
if (missingRequired.length) {
|
|
1292
1452
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1293
|
-
if (!isReference(item)) return [item];
|
|
1294
|
-
const deref = resolveRef(document, item.$ref);
|
|
1295
|
-
return deref && !isReference(deref) ? [deref] : [];
|
|
1453
|
+
if (!dialect.schema.isReference(item)) return [item];
|
|
1454
|
+
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1455
|
+
return deref && !dialect.schema.isReference(deref) ? [deref] : [];
|
|
1296
1456
|
});
|
|
1297
1457
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1298
|
-
allOfMembers.push(parseSchema({
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1458
|
+
allOfMembers.push(parseSchema({
|
|
1459
|
+
schema: {
|
|
1460
|
+
properties: { [key]: resolved.properties[key] },
|
|
1461
|
+
required: [key]
|
|
1462
|
+
},
|
|
1463
|
+
name
|
|
1464
|
+
}, rawOptions));
|
|
1302
1465
|
break;
|
|
1303
1466
|
}
|
|
1304
1467
|
}
|
|
@@ -1307,13 +1470,13 @@ function createSchemaParser(ctx) {
|
|
|
1307
1470
|
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1308
1471
|
allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
|
|
1309
1472
|
}
|
|
1310
|
-
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(
|
|
1473
|
+
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(createDiscriminantNode({
|
|
1311
1474
|
propertyName,
|
|
1312
1475
|
value
|
|
1313
1476
|
}));
|
|
1314
|
-
return _kubb_core.ast.createSchema({
|
|
1477
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1315
1478
|
type: "intersection",
|
|
1316
|
-
members: [...
|
|
1479
|
+
members: [...(0, _kubb_ast_utils.mergeAdjacentObjectsLazy)(allOfMembers.slice(0, syntheticStart)), ...(0, _kubb_ast_utils.mergeAdjacentObjectsLazy)(allOfMembers.slice(syntheticStart))],
|
|
1317
1480
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1318
1481
|
});
|
|
1319
1482
|
}
|
|
@@ -1324,79 +1487,104 @@ function createSchemaParser(ctx) {
|
|
|
1324
1487
|
function pickDiscriminatorPropertyNode(node, propertyName) {
|
|
1325
1488
|
const discriminatorProperty = _kubb_core.ast.narrowSchema(node, "object")?.properties?.find((property) => property.name === propertyName);
|
|
1326
1489
|
if (!discriminatorProperty) return null;
|
|
1327
|
-
return _kubb_core.ast.createSchema({
|
|
1490
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1328
1491
|
type: "object",
|
|
1329
1492
|
primitive: "object",
|
|
1330
1493
|
properties: [discriminatorProperty]
|
|
1331
1494
|
});
|
|
1332
1495
|
}
|
|
1496
|
+
function resolveRefSilent($ref) {
|
|
1497
|
+
if (!$ref.startsWith("#")) return null;
|
|
1498
|
+
return decodeURIComponent($ref.substring(1)).split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document) ?? null;
|
|
1499
|
+
}
|
|
1500
|
+
function implicitDiscriminantValue(member) {
|
|
1501
|
+
if (!discriminator || discriminator.mapping || !dialect.schema.isReference(member)) return null;
|
|
1502
|
+
const value = (0, _kubb_ast_utils.extractRefName)(member.$ref);
|
|
1503
|
+
if (!value) return null;
|
|
1504
|
+
const variant = resolveRefSilent(member.$ref);
|
|
1505
|
+
if (!variant) return null;
|
|
1506
|
+
const propertyName = discriminator.propertyName;
|
|
1507
|
+
const seen = /* @__PURE__ */ new Set([member.$ref]);
|
|
1508
|
+
function constrains(v) {
|
|
1509
|
+
const prop = v.properties?.[propertyName];
|
|
1510
|
+
const resolved = prop && dialect.schema.isReference(prop) ? resolveRefSilent(prop.$ref) : prop;
|
|
1511
|
+
if (resolved && (Array.isArray(resolved.enum) || resolved.const !== void 0)) return true;
|
|
1512
|
+
const composition = v.allOf ?? v.oneOf ?? v.anyOf;
|
|
1513
|
+
if (!composition) return false;
|
|
1514
|
+
return composition.some((m) => {
|
|
1515
|
+
if (!dialect.schema.isReference(m)) return constrains(m);
|
|
1516
|
+
if (seen.has(m.$ref)) return false;
|
|
1517
|
+
seen.add(m.$ref);
|
|
1518
|
+
const r = resolveRefSilent(m.$ref);
|
|
1519
|
+
return r ? constrains(r) : false;
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
return constrains(variant) ? null : value;
|
|
1523
|
+
}
|
|
1333
1524
|
const unionMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
1334
1525
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1335
1526
|
const unionBase = {
|
|
1336
1527
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1337
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1528
|
+
discriminatorPropertyName: dialect.schema.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1338
1529
|
strategy
|
|
1339
1530
|
};
|
|
1340
|
-
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
})() : void 0;
|
|
1348
|
-
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1531
|
+
const discriminator = dialect.schema.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1532
|
+
const { oneOf: _o, anyOf: _a, discriminator: _d, ...memberBaseSchema } = schema;
|
|
1533
|
+
const sharedPropertiesNode = schema.properties ? parseSchema({
|
|
1534
|
+
schema: memberBaseSchema,
|
|
1535
|
+
name
|
|
1536
|
+
}, rawOptions) : void 0;
|
|
1537
|
+
if (sharedPropertiesNode || discriminator) {
|
|
1349
1538
|
const members = unionMembers.map((s) => {
|
|
1350
|
-
const ref = isReference(s) ? s.$ref : void 0;
|
|
1351
|
-
const discriminatorValue =
|
|
1352
|
-
const memberNode = parseSchema({
|
|
1539
|
+
const ref = dialect.schema.isReference(s) ? s.$ref : void 0;
|
|
1540
|
+
const discriminatorValue = findDiscriminator(discriminator?.mapping, ref) ?? implicitDiscriminantValue(s);
|
|
1541
|
+
const memberNode = parseSchema({
|
|
1542
|
+
schema: s,
|
|
1543
|
+
name
|
|
1544
|
+
}, rawOptions);
|
|
1353
1545
|
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1354
|
-
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.
|
|
1355
|
-
node: sharedPropertiesNode,
|
|
1546
|
+
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.applyMacros(sharedPropertiesNode, [(0, _kubb_ast_macros.macroDiscriminatorEnum)({
|
|
1356
1547
|
propertyName: discriminator.propertyName,
|
|
1357
1548
|
values: [discriminatorValue]
|
|
1358
|
-
}), discriminator.propertyName) : void 0;
|
|
1359
|
-
return _kubb_core.ast.createSchema({
|
|
1549
|
+
})], { depth: "shallow" }), discriminator.propertyName) : void 0;
|
|
1550
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1360
1551
|
type: "intersection",
|
|
1361
|
-
members: [memberNode, narrowedDiscriminatorNode ??
|
|
1552
|
+
members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
|
|
1362
1553
|
propertyName: discriminator.propertyName,
|
|
1363
1554
|
value: discriminatorValue
|
|
1364
1555
|
})]
|
|
1365
1556
|
});
|
|
1366
1557
|
});
|
|
1367
|
-
const unionNode = _kubb_core.ast.createSchema({
|
|
1558
|
+
const unionNode = _kubb_core.ast.factory.createSchema({
|
|
1368
1559
|
type: "union",
|
|
1369
1560
|
...unionBase,
|
|
1370
1561
|
members
|
|
1371
1562
|
});
|
|
1372
1563
|
if (!sharedPropertiesNode) return unionNode;
|
|
1373
|
-
return _kubb_core.ast.createSchema({
|
|
1564
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1374
1565
|
type: "intersection",
|
|
1375
1566
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1376
1567
|
members: [unionNode, sharedPropertiesNode]
|
|
1377
1568
|
});
|
|
1378
1569
|
}
|
|
1379
|
-
|
|
1570
|
+
const unionNode = _kubb_core.ast.factory.createSchema({
|
|
1380
1571
|
type: "union",
|
|
1381
1572
|
...unionBase,
|
|
1382
|
-
members:
|
|
1573
|
+
members: unionMembers.map((s) => parseSchema({
|
|
1574
|
+
schema: s,
|
|
1575
|
+
name
|
|
1576
|
+
}, rawOptions))
|
|
1383
1577
|
});
|
|
1578
|
+
return _kubb_core.ast.applyMacros(unionNode, [_kubb_ast_macros.macroSimplifyUnion], { depth: "shallow" });
|
|
1384
1579
|
}
|
|
1385
1580
|
/**
|
|
1386
1581
|
* Converts an OAS 3.1 `const` schema into a null scalar or a single-value `EnumSchemaNode`.
|
|
1387
1582
|
*/
|
|
1388
1583
|
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1389
1584
|
const constValue = schema.const;
|
|
1390
|
-
if (constValue === null) return
|
|
1391
|
-
type: "null",
|
|
1392
|
-
primitive: "null",
|
|
1393
|
-
name,
|
|
1394
|
-
title: schema.title,
|
|
1395
|
-
description: schema.description,
|
|
1396
|
-
deprecated: schema.deprecated
|
|
1397
|
-
});
|
|
1585
|
+
if (constValue === null) return createNullSchema(schema, name);
|
|
1398
1586
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1399
|
-
return _kubb_core.ast.createSchema({
|
|
1587
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1400
1588
|
type: "enum",
|
|
1401
1589
|
primitive: constPrimitive,
|
|
1402
1590
|
enumValues: [constValue],
|
|
@@ -1409,7 +1597,7 @@ function createSchemaParser(ctx) {
|
|
|
1409
1597
|
*/
|
|
1410
1598
|
function convertFormat({ schema, name, nullable, defaultValue, options }) {
|
|
1411
1599
|
const base = buildSchemaNode(schema, name, nullable, defaultValue);
|
|
1412
|
-
if (schema.format === "int64") return _kubb_core.ast.createSchema({
|
|
1600
|
+
if (schema.format === "int64") return _kubb_core.ast.factory.createSchema({
|
|
1413
1601
|
type: options.integerType === "bigint" ? "bigint" : "integer",
|
|
1414
1602
|
primitive: "integer",
|
|
1415
1603
|
...base,
|
|
@@ -1421,14 +1609,14 @@ function createSchemaParser(ctx) {
|
|
|
1421
1609
|
if (schema.format === "date-time" || schema.format === "date" || schema.format === "time") {
|
|
1422
1610
|
const dateType = getDateType(options, schema.format);
|
|
1423
1611
|
if (!dateType) return null;
|
|
1424
|
-
if (dateType.type === "datetime") return _kubb_core.ast.createSchema({
|
|
1612
|
+
if (dateType.type === "datetime") return _kubb_core.ast.factory.createSchema({
|
|
1425
1613
|
...base,
|
|
1426
1614
|
primitive: "string",
|
|
1427
1615
|
type: "datetime",
|
|
1428
1616
|
offset: dateType.offset,
|
|
1429
1617
|
local: dateType.local
|
|
1430
1618
|
});
|
|
1431
|
-
return _kubb_core.ast.createSchema({
|
|
1619
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1432
1620
|
...base,
|
|
1433
1621
|
primitive: "string",
|
|
1434
1622
|
type: dateType.type,
|
|
@@ -1438,36 +1626,36 @@ function createSchemaParser(ctx) {
|
|
|
1438
1626
|
const specialType = getSchemaType(schema.format);
|
|
1439
1627
|
if (!specialType) return null;
|
|
1440
1628
|
const specialPrimitive = specialType === "number" || specialType === "integer" || specialType === "bigint" ? specialType : "string";
|
|
1441
|
-
if (specialType === "number" || specialType === "integer" || specialType === "bigint") return _kubb_core.ast.createSchema({
|
|
1629
|
+
if (specialType === "number" || specialType === "integer" || specialType === "bigint") return _kubb_core.ast.factory.createSchema({
|
|
1442
1630
|
...base,
|
|
1443
1631
|
primitive: specialPrimitive,
|
|
1444
1632
|
type: specialType
|
|
1445
1633
|
});
|
|
1446
|
-
if (specialType === "url") return _kubb_core.ast.createSchema({
|
|
1634
|
+
if (specialType === "url") return _kubb_core.ast.factory.createSchema({
|
|
1447
1635
|
...base,
|
|
1448
1636
|
primitive: "string",
|
|
1449
1637
|
type: "url",
|
|
1450
1638
|
min: schema.minLength,
|
|
1451
1639
|
max: schema.maxLength
|
|
1452
1640
|
});
|
|
1453
|
-
if (specialType === "ipv4") return _kubb_core.ast.createSchema({
|
|
1641
|
+
if (specialType === "ipv4") return _kubb_core.ast.factory.createSchema({
|
|
1454
1642
|
...base,
|
|
1455
1643
|
primitive: "string",
|
|
1456
1644
|
type: "ipv4"
|
|
1457
1645
|
});
|
|
1458
|
-
if (specialType === "ipv6") return _kubb_core.ast.createSchema({
|
|
1646
|
+
if (specialType === "ipv6") return _kubb_core.ast.factory.createSchema({
|
|
1459
1647
|
...base,
|
|
1460
1648
|
primitive: "string",
|
|
1461
1649
|
type: "ipv6"
|
|
1462
1650
|
});
|
|
1463
|
-
if (specialType === "uuid" || specialType === "email") return _kubb_core.ast.createSchema({
|
|
1651
|
+
if (specialType === "uuid" || specialType === "email") return _kubb_core.ast.factory.createSchema({
|
|
1464
1652
|
...base,
|
|
1465
1653
|
primitive: "string",
|
|
1466
1654
|
type: specialType,
|
|
1467
1655
|
min: schema.minLength,
|
|
1468
1656
|
max: schema.maxLength
|
|
1469
1657
|
});
|
|
1470
|
-
return _kubb_core.ast.createSchema({
|
|
1658
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1471
1659
|
...base,
|
|
1472
1660
|
primitive: specialPrimitive,
|
|
1473
1661
|
type: specialType
|
|
@@ -1483,6 +1671,7 @@ function createSchemaParser(ctx) {
|
|
|
1483
1671
|
}, rawOptions);
|
|
1484
1672
|
const nullInEnum = schema.enum.includes(null);
|
|
1485
1673
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1674
|
+
if (nullInEnum && filteredValues.length === 0) return createNullSchema(schema, name);
|
|
1486
1675
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1487
1676
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1488
1677
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1497,21 +1686,25 @@ function createSchemaParser(ctx) {
|
|
|
1497
1686
|
readOnly: schema.readOnly,
|
|
1498
1687
|
writeOnly: schema.writeOnly,
|
|
1499
1688
|
default: enumDefault,
|
|
1500
|
-
|
|
1689
|
+
examples: extractExamples(schema),
|
|
1690
|
+
format: schema.format
|
|
1501
1691
|
};
|
|
1502
1692
|
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1503
|
-
|
|
1693
|
+
const descriptionKey = enumDescriptionKeys.find((key) => key in schema);
|
|
1694
|
+
if (extensionKey || descriptionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
1504
1695
|
const enumPrimitiveType = enumPrimitive === "number" || enumPrimitive === "integer" ? "number" : enumPrimitive === "boolean" ? "boolean" : "string";
|
|
1505
1696
|
const rawEnumNames = extensionKey ? schema[extensionKey] : void 0;
|
|
1697
|
+
const rawEnumDescriptions = descriptionKey ? schema[descriptionKey] : void 0;
|
|
1506
1698
|
const uniqueValues = [...new Set(filteredValues)];
|
|
1507
1699
|
const seenNames = /* @__PURE__ */ new Set();
|
|
1508
|
-
return _kubb_core.ast.createSchema({
|
|
1700
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1509
1701
|
...enumBase,
|
|
1510
1702
|
primitive: enumPrimitiveType,
|
|
1511
1703
|
namedEnumValues: uniqueValues.map((value, index) => ({
|
|
1512
1704
|
name: String(rawEnumNames?.[index] ?? value),
|
|
1513
1705
|
value,
|
|
1514
|
-
primitive: enumPrimitiveType
|
|
1706
|
+
primitive: enumPrimitiveType,
|
|
1707
|
+
description: rawEnumDescriptions?.[index]
|
|
1515
1708
|
})).filter((entry) => {
|
|
1516
1709
|
if (seenNames.has(entry.name)) return false;
|
|
1517
1710
|
seenNames.add(entry.name);
|
|
@@ -1519,7 +1712,7 @@ function createSchemaParser(ctx) {
|
|
|
1519
1712
|
})
|
|
1520
1713
|
});
|
|
1521
1714
|
}
|
|
1522
|
-
return _kubb_core.ast.createSchema({
|
|
1715
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1523
1716
|
...enumBase,
|
|
1524
1717
|
enumValues: [...new Set(filteredValues)]
|
|
1525
1718
|
});
|
|
@@ -1531,21 +1724,16 @@ function createSchemaParser(ctx) {
|
|
|
1531
1724
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1532
1725
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1533
1726
|
const resolvedPropSchema = propSchema;
|
|
1534
|
-
const propNullable = isNullable(resolvedPropSchema);
|
|
1535
|
-
const
|
|
1727
|
+
const propNullable = dialect.schema.isNullable(resolvedPropSchema);
|
|
1728
|
+
const schemaNode = nameEnums(parseSchema({
|
|
1536
1729
|
schema: resolvedPropSchema,
|
|
1537
|
-
name:
|
|
1538
|
-
}, rawOptions)
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
...tupleNode,
|
|
1545
|
-
items: namedItems
|
|
1546
|
-
};
|
|
1547
|
-
}
|
|
1548
|
-
return _kubb_core.ast.createProperty({
|
|
1730
|
+
name: (0, _kubb_ast_utils.childName)(name, propName)
|
|
1731
|
+
}, rawOptions), {
|
|
1732
|
+
parentName: name,
|
|
1733
|
+
propName,
|
|
1734
|
+
enumSuffix: options.enumSuffix
|
|
1735
|
+
});
|
|
1736
|
+
return _kubb_core.ast.factory.createProperty({
|
|
1549
1737
|
name: propName,
|
|
1550
1738
|
schema: {
|
|
1551
1739
|
...schemaNode,
|
|
@@ -1555,14 +1743,15 @@ function createSchemaParser(ctx) {
|
|
|
1555
1743
|
});
|
|
1556
1744
|
}) : [];
|
|
1557
1745
|
const additionalProperties = schema.additionalProperties;
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1746
|
+
const additionalPropertiesNode = (() => {
|
|
1747
|
+
if (additionalProperties === true) return true;
|
|
1748
|
+
if (additionalProperties === false) return false;
|
|
1749
|
+
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1750
|
+
if (additionalProperties) return _kubb_core.ast.factory.createSchema({ type: options.unknownType });
|
|
1751
|
+
})();
|
|
1563
1752
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1564
|
-
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.createSchema({ type:
|
|
1565
|
-
const objectNode = _kubb_core.ast.createSchema({
|
|
1753
|
+
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.factory.createSchema({ type: options.unknownType }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1754
|
+
const objectNode = _kubb_core.ast.factory.createSchema({
|
|
1566
1755
|
type: "object",
|
|
1567
1756
|
primitive: "object",
|
|
1568
1757
|
properties,
|
|
@@ -1572,16 +1761,15 @@ function createSchemaParser(ctx) {
|
|
|
1572
1761
|
maxProperties: schema.maxProperties,
|
|
1573
1762
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1574
1763
|
});
|
|
1575
|
-
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1764
|
+
if (dialect.schema.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1576
1765
|
const discPropName = schema.discriminator.propertyName;
|
|
1577
1766
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1578
|
-
const enumName = name ?
|
|
1579
|
-
return _kubb_core.ast.
|
|
1580
|
-
node: objectNode,
|
|
1767
|
+
const enumName = name ? (0, _kubb_ast_utils.enumPropName)(name, discPropName, options.enumSuffix) : void 0;
|
|
1768
|
+
return _kubb_core.ast.applyMacros(objectNode, [(0, _kubb_ast_macros.macroDiscriminatorEnum)({
|
|
1581
1769
|
propertyName: discPropName,
|
|
1582
1770
|
values,
|
|
1583
1771
|
enumName
|
|
1584
|
-
});
|
|
1772
|
+
})], { depth: "shallow" });
|
|
1585
1773
|
}
|
|
1586
1774
|
return objectNode;
|
|
1587
1775
|
}
|
|
@@ -1590,8 +1778,8 @@ function createSchemaParser(ctx) {
|
|
|
1590
1778
|
*/
|
|
1591
1779
|
function convertTuple({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1592
1780
|
const tupleItems = (schema.prefixItems ?? []).map((item) => parseSchema({ schema: item }, rawOptions));
|
|
1593
|
-
const rest = schema.items ?
|
|
1594
|
-
return _kubb_core.ast.createSchema({
|
|
1781
|
+
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? _kubb_core.ast.factory.createSchema({ type: "any" }) : parseSchema({ schema: schema.items }, rawOptions);
|
|
1782
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1595
1783
|
type: "tuple",
|
|
1596
1784
|
primitive: "array",
|
|
1597
1785
|
items: tupleItems,
|
|
@@ -1606,12 +1794,12 @@ function createSchemaParser(ctx) {
|
|
|
1606
1794
|
*/
|
|
1607
1795
|
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1608
1796
|
const rawItems = schema.items;
|
|
1609
|
-
const itemName = rawItems?.enum?.length && name ?
|
|
1797
|
+
const itemName = rawItems?.enum?.length && name ? (0, _kubb_ast_utils.enumPropName)(null, name, options.enumSuffix) : name;
|
|
1610
1798
|
const items = rawItems ? [parseSchema({
|
|
1611
1799
|
schema: rawItems,
|
|
1612
1800
|
name: itemName
|
|
1613
1801
|
}, rawOptions)] : [];
|
|
1614
|
-
return _kubb_core.ast.createSchema({
|
|
1802
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1615
1803
|
type: "array",
|
|
1616
1804
|
primitive: "array",
|
|
1617
1805
|
items,
|
|
@@ -1625,7 +1813,7 @@ function createSchemaParser(ctx) {
|
|
|
1625
1813
|
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
1626
1814
|
*/
|
|
1627
1815
|
function convertString({ schema, name, nullable, defaultValue }) {
|
|
1628
|
-
return _kubb_core.ast.createSchema({
|
|
1816
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1629
1817
|
type: "string",
|
|
1630
1818
|
primitive: "string",
|
|
1631
1819
|
min: schema.minLength,
|
|
@@ -1638,7 +1826,7 @@ function createSchemaParser(ctx) {
|
|
|
1638
1826
|
* Converts a `type: 'number'` or `type: 'integer'` schema.
|
|
1639
1827
|
*/
|
|
1640
1828
|
function convertNumeric({ schema, name, nullable, defaultValue }, type) {
|
|
1641
|
-
return _kubb_core.ast.createSchema({
|
|
1829
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1642
1830
|
type,
|
|
1643
1831
|
primitive: type,
|
|
1644
1832
|
min: schema.minimum,
|
|
@@ -1653,32 +1841,132 @@ function createSchemaParser(ctx) {
|
|
|
1653
1841
|
* Converts a `type: 'boolean'` schema.
|
|
1654
1842
|
*/
|
|
1655
1843
|
function convertBoolean({ schema, name, nullable, defaultValue }) {
|
|
1656
|
-
return _kubb_core.ast.createSchema({
|
|
1844
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1657
1845
|
type: "boolean",
|
|
1658
1846
|
primitive: "boolean",
|
|
1659
1847
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1660
1848
|
});
|
|
1661
1849
|
}
|
|
1662
1850
|
/**
|
|
1663
|
-
* Converts
|
|
1851
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1852
|
+
* into a `blob` node.
|
|
1664
1853
|
*/
|
|
1665
|
-
function
|
|
1666
|
-
return _kubb_core.ast.createSchema({
|
|
1667
|
-
type: "
|
|
1668
|
-
primitive: "
|
|
1669
|
-
name,
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1854
|
+
function convertBlob({ schema, name, nullable, defaultValue }) {
|
|
1855
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1856
|
+
type: "blob",
|
|
1857
|
+
primitive: "string",
|
|
1858
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1863
|
+
*
|
|
1864
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
|
|
1865
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1866
|
+
*/
|
|
1867
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1868
|
+
const types = schema.type;
|
|
1869
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1870
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1871
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1872
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1873
|
+
type: "union",
|
|
1874
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1875
|
+
schema: {
|
|
1876
|
+
...schema,
|
|
1877
|
+
type: t
|
|
1878
|
+
},
|
|
1879
|
+
name
|
|
1880
|
+
}, rawOptions)),
|
|
1881
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1674
1882
|
});
|
|
1675
1883
|
}
|
|
1676
1884
|
/**
|
|
1677
|
-
*
|
|
1885
|
+
* Ordered schema rule table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1886
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1887
|
+
* `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
|
|
1888
|
+
* match/convert/fall-through contract.
|
|
1889
|
+
*/
|
|
1890
|
+
const schemaRules = [
|
|
1891
|
+
{
|
|
1892
|
+
match: ({ schema }) => dialect.schema.isReference(schema),
|
|
1893
|
+
convert: convertRef
|
|
1894
|
+
},
|
|
1895
|
+
{
|
|
1896
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1897
|
+
convert: convertAllOf
|
|
1898
|
+
},
|
|
1899
|
+
{
|
|
1900
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1901
|
+
convert: convertUnion
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1905
|
+
convert: convertConst
|
|
1906
|
+
},
|
|
1907
|
+
{
|
|
1908
|
+
match: ({ schema }) => !!schema.format,
|
|
1909
|
+
convert: convertFormat
|
|
1910
|
+
},
|
|
1911
|
+
{
|
|
1912
|
+
match: ({ schema }) => dialect.schema.isBinary(schema),
|
|
1913
|
+
convert: convertBlob
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1917
|
+
convert: convertMultiType
|
|
1918
|
+
},
|
|
1919
|
+
{
|
|
1920
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1921
|
+
convert: convertString
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1925
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1929
|
+
convert: convertEnum
|
|
1930
|
+
},
|
|
1931
|
+
{
|
|
1932
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1933
|
+
convert: convertObject
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1937
|
+
convert: convertTuple
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1941
|
+
convert: convertArray
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
match: ({ type }) => type === "string",
|
|
1945
|
+
convert: convertString
|
|
1946
|
+
},
|
|
1947
|
+
{
|
|
1948
|
+
match: ({ type }) => type === "number",
|
|
1949
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
match: ({ type }) => type === "integer",
|
|
1953
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
match: ({ type }) => type === "boolean",
|
|
1957
|
+
convert: convertBoolean
|
|
1958
|
+
},
|
|
1959
|
+
{
|
|
1960
|
+
match: ({ type }) => type === "null",
|
|
1961
|
+
convert: ({ schema, name, nullable }) => createNullSchema(schema, name, nullable)
|
|
1962
|
+
}
|
|
1963
|
+
];
|
|
1964
|
+
/**
|
|
1965
|
+
* Converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1678
1966
|
*
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
*
|
|
1967
|
+
* Builds the per-schema context, then walks the ordered {@link schemaRules} table and returns
|
|
1968
|
+
* the first converter that produces a node. When none match, falls back to the configured
|
|
1969
|
+
* `emptySchemaType`.
|
|
1682
1970
|
*/
|
|
1683
1971
|
function parseSchema({ schema, name }, rawOptions) {
|
|
1684
1972
|
const options = {
|
|
@@ -1690,81 +1978,53 @@ function createSchemaParser(ctx) {
|
|
|
1690
1978
|
schema: flattenedSchema,
|
|
1691
1979
|
name
|
|
1692
1980
|
}, rawOptions);
|
|
1693
|
-
const nullable = isNullable(schema) || void 0;
|
|
1694
|
-
const
|
|
1695
|
-
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
1696
|
-
const ctx = {
|
|
1981
|
+
const nullable = dialect.schema.isNullable(schema) || void 0;
|
|
1982
|
+
const schemaCtx = {
|
|
1697
1983
|
schema,
|
|
1698
1984
|
name,
|
|
1699
1985
|
nullable,
|
|
1700
|
-
defaultValue,
|
|
1701
|
-
type,
|
|
1986
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1987
|
+
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1702
1988
|
rawOptions,
|
|
1703
1989
|
options
|
|
1704
1990
|
};
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
if (schema.format) {
|
|
1710
|
-
const formatResult = convertFormat(ctx);
|
|
1711
|
-
if (formatResult) return formatResult;
|
|
1712
|
-
}
|
|
1713
|
-
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return _kubb_core.ast.createSchema({
|
|
1714
|
-
type: "blob",
|
|
1715
|
-
primitive: "string",
|
|
1716
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1717
|
-
});
|
|
1718
|
-
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1719
|
-
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1720
|
-
const arrayNullable = schema.type.includes("null") || nullable || void 0;
|
|
1721
|
-
if (nonNullTypes.length > 1) return _kubb_core.ast.createSchema({
|
|
1722
|
-
type: "union",
|
|
1723
|
-
members: nonNullTypes.map((t) => parseSchema({
|
|
1724
|
-
schema: {
|
|
1725
|
-
...schema,
|
|
1726
|
-
type: t
|
|
1727
|
-
},
|
|
1728
|
-
name
|
|
1729
|
-
}, rawOptions)),
|
|
1730
|
-
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1731
|
-
});
|
|
1991
|
+
for (const rule of schemaRules) {
|
|
1992
|
+
if (!rule.match(schemaCtx)) continue;
|
|
1993
|
+
const node = rule.convert(schemaCtx);
|
|
1994
|
+
if (node) return node;
|
|
1732
1995
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
if (schema.minimum !== void 0 || schema.maximum !== void 0) return convertNumeric(ctx, "number");
|
|
1736
|
-
}
|
|
1737
|
-
if (schema.enum?.length) return convertEnum(ctx);
|
|
1738
|
-
if (type === "object" || schema.properties || schema.additionalProperties || "patternProperties" in schema) return convertObject(ctx);
|
|
1739
|
-
if ("prefixItems" in schema) return convertTuple(ctx);
|
|
1740
|
-
if (type === "array" || "items" in schema) return convertArray(ctx);
|
|
1741
|
-
if (type === "string") return convertString(ctx);
|
|
1742
|
-
if (type === "number") return convertNumeric(ctx, "number");
|
|
1743
|
-
if (type === "integer") return convertNumeric(ctx, "integer");
|
|
1744
|
-
if (type === "boolean") return convertBoolean(ctx);
|
|
1745
|
-
if (type === "null") return convertNull(ctx);
|
|
1746
|
-
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1747
|
-
return _kubb_core.ast.createSchema({
|
|
1996
|
+
const emptyType = options.emptySchemaType;
|
|
1997
|
+
return _kubb_core.ast.factory.createSchema({
|
|
1748
1998
|
type: emptyType,
|
|
1749
1999
|
name,
|
|
1750
2000
|
title: schema.title,
|
|
1751
|
-
description: schema.description
|
|
2001
|
+
description: schema.description,
|
|
2002
|
+
format: schema.format
|
|
1752
2003
|
});
|
|
1753
2004
|
}
|
|
1754
2005
|
/**
|
|
1755
2006
|
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1756
2007
|
*/
|
|
1757
|
-
function parseParameter(options, param) {
|
|
2008
|
+
function parseParameter(options, param, parentName) {
|
|
1758
2009
|
const required = param["required"] ?? false;
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
2010
|
+
const paramName = param["name"];
|
|
2011
|
+
const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
|
|
2012
|
+
const schema = param["schema"] ? parseSchema({
|
|
2013
|
+
schema: param["schema"],
|
|
2014
|
+
name: schemaName
|
|
2015
|
+
}, options) : _kubb_core.ast.factory.createSchema({ type: options.unknownType });
|
|
2016
|
+
const style = param["style"];
|
|
2017
|
+
const explode = param["explode"];
|
|
2018
|
+
return _kubb_core.ast.factory.createParameter({
|
|
2019
|
+
name: paramName,
|
|
1762
2020
|
in: param["in"],
|
|
1763
2021
|
schema: {
|
|
1764
2022
|
...schema,
|
|
1765
2023
|
description: param["description"] ?? schema.description
|
|
1766
2024
|
},
|
|
1767
|
-
required
|
|
2025
|
+
required,
|
|
2026
|
+
...style !== void 0 ? { style } : {},
|
|
2027
|
+
...explode !== void 0 ? { explode } : {}
|
|
1768
2028
|
});
|
|
1769
2029
|
}
|
|
1770
2030
|
/**
|
|
@@ -1780,73 +2040,97 @@ function createSchemaParser(ctx) {
|
|
|
1780
2040
|
};
|
|
1781
2041
|
}
|
|
1782
2042
|
/**
|
|
1783
|
-
* Reads the inline response object (not a `$ref`) and returns its description plus its `content` map.
|
|
1784
|
-
*/
|
|
1785
|
-
function getResponseMeta(responseObj) {
|
|
1786
|
-
if (typeof responseObj !== "object" || responseObj === null || Array.isArray(responseObj)) return {};
|
|
1787
|
-
const inline = responseObj;
|
|
1788
|
-
return {
|
|
1789
|
-
description: inline.description,
|
|
1790
|
-
content: inline.content
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
/**
|
|
1794
2043
|
* Collects property names whose schema has a truthy boolean flag (`readOnly` or `writeOnly`).
|
|
1795
2044
|
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
1796
2045
|
*/
|
|
1797
2046
|
function collectPropertyKeysByFlag(schema, flag) {
|
|
1798
|
-
if (!schema?.properties) return
|
|
2047
|
+
if (!schema?.properties) return null;
|
|
1799
2048
|
const keys = [];
|
|
1800
2049
|
for (const key in schema.properties) {
|
|
1801
2050
|
const prop = schema.properties[key];
|
|
1802
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
2051
|
+
if (prop && !dialect.schema.isReference(prop) && prop[flag]) keys.push(key);
|
|
1803
2052
|
}
|
|
1804
|
-
return keys.length ? keys :
|
|
2053
|
+
return keys.length ? keys : null;
|
|
1805
2054
|
}
|
|
1806
2055
|
/**
|
|
1807
2056
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1808
2057
|
*/
|
|
1809
2058
|
function parseOperation(options, operation) {
|
|
1810
|
-
const
|
|
2059
|
+
const operationId = getOperationId(operation);
|
|
2060
|
+
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
2061
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
|
|
1811
2062
|
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
1812
2063
|
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
2064
|
+
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
1813
2065
|
const content = allContentTypes.flatMap((ct) => {
|
|
1814
2066
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
1815
2067
|
if (!schema) return [];
|
|
1816
|
-
return [{
|
|
2068
|
+
return [_kubb_core.ast.factory.createContent({
|
|
1817
2069
|
contentType: ct,
|
|
1818
|
-
schema: _kubb_core.ast.
|
|
2070
|
+
schema: _kubb_core.ast.optionality(parseSchema({
|
|
2071
|
+
schema,
|
|
2072
|
+
name: requestBodyName
|
|
2073
|
+
}, options), requestBodyMeta.required),
|
|
1819
2074
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1820
|
-
}];
|
|
2075
|
+
})];
|
|
1821
2076
|
});
|
|
1822
2077
|
const requestBody = content.length > 0 || requestBodyMeta.description ? {
|
|
1823
2078
|
description: requestBodyMeta.description,
|
|
1824
2079
|
required: requestBodyMeta.required || void 0,
|
|
1825
2080
|
content: content.length > 0 ? content : void 0
|
|
1826
2081
|
} : void 0;
|
|
1827
|
-
const responses =
|
|
1828
|
-
const responseObj =
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
2082
|
+
const responses = getResponseStatusCodes(operation).map((statusCode) => {
|
|
2083
|
+
const responseObj = getResponseByStatusCode({
|
|
2084
|
+
document,
|
|
2085
|
+
operation,
|
|
2086
|
+
statusCode
|
|
2087
|
+
});
|
|
2088
|
+
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
2089
|
+
const description = typeof responseObj === "object" && responseObj !== null ? responseObj.description : void 0;
|
|
2090
|
+
const parseEntrySchema = (contentType) => {
|
|
2091
|
+
const raw = getResponseSchema(document, operation, statusCode, { contentType });
|
|
2092
|
+
return {
|
|
2093
|
+
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
2094
|
+
schema: raw,
|
|
2095
|
+
name: responseName
|
|
2096
|
+
}, options) : _kubb_core.ast.factory.createSchema({ type: options.emptySchemaType }),
|
|
2097
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
2098
|
+
};
|
|
2099
|
+
};
|
|
2100
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => _kubb_core.ast.factory.createContent({
|
|
2101
|
+
contentType,
|
|
2102
|
+
...parseEntrySchema(contentType)
|
|
2103
|
+
}));
|
|
2104
|
+
if (content.length === 0) content.push(_kubb_core.ast.factory.createContent({
|
|
2105
|
+
contentType: getRequestContentType({
|
|
2106
|
+
document,
|
|
2107
|
+
operation
|
|
2108
|
+
}) || "application/json",
|
|
2109
|
+
...parseEntrySchema(ctx.contentType)
|
|
2110
|
+
}));
|
|
2111
|
+
return _kubb_core.ast.factory.createResponse({
|
|
1834
2112
|
statusCode,
|
|
1835
2113
|
description,
|
|
1836
|
-
|
|
1837
|
-
mediaType,
|
|
1838
|
-
keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
|
|
2114
|
+
content
|
|
1839
2115
|
});
|
|
1840
2116
|
});
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1843
|
-
|
|
2117
|
+
const pathItem = document.paths?.[operation.path];
|
|
2118
|
+
const pathItemDoc = pathItem && !dialect.schema.isReference(pathItem) ? pathItem : void 0;
|
|
2119
|
+
const pickDoc = (key) => {
|
|
2120
|
+
const own = operation.schema[key];
|
|
2121
|
+
if (typeof own === "string") return own;
|
|
2122
|
+
const fallback = pathItemDoc?.[key];
|
|
2123
|
+
return typeof fallback === "string" ? fallback : void 0;
|
|
2124
|
+
};
|
|
2125
|
+
return _kubb_core.ast.factory.createOperation({
|
|
2126
|
+
operationId,
|
|
2127
|
+
protocol: "http",
|
|
1844
2128
|
method: operation.method.toUpperCase(),
|
|
1845
|
-
path:
|
|
1846
|
-
tags: operation.
|
|
1847
|
-
summary:
|
|
1848
|
-
description:
|
|
1849
|
-
deprecated: operation.
|
|
2129
|
+
path: operation.path,
|
|
2130
|
+
tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
|
|
2131
|
+
summary: pickDoc("summary") || void 0,
|
|
2132
|
+
description: pickDoc("description") || void 0,
|
|
2133
|
+
deprecated: operation.schema.deprecated || void 0,
|
|
1850
2134
|
parameters,
|
|
1851
2135
|
requestBody,
|
|
1852
2136
|
responses
|
|
@@ -1858,59 +2142,264 @@ function createSchemaParser(ctx) {
|
|
|
1858
2142
|
parseParameter
|
|
1859
2143
|
};
|
|
1860
2144
|
}
|
|
2145
|
+
//#endregion
|
|
2146
|
+
//#region src/promoteEnums.ts
|
|
2147
|
+
/**
|
|
2148
|
+
* Collects inline enums to lift to the top level, keyed by the name the parser derived for them
|
|
2149
|
+
* (e.g. `PetStatusEnum`). An enum already defined as a top-level component is left as-is, and a
|
|
2150
|
+
* name that recurs maps to the first definition so each name yields one shared type.
|
|
2151
|
+
*/
|
|
2152
|
+
function collectInlineEnums(roots, topLevelNames) {
|
|
2153
|
+
const promoted = /* @__PURE__ */ new Map();
|
|
2154
|
+
for (const root of roots) {
|
|
2155
|
+
const isSchemaRoot = root.kind === "Schema";
|
|
2156
|
+
for (const node of _kubb_core.ast.collect(root, { schema: (schemaNode) => schemaNode })) {
|
|
2157
|
+
if (node.type !== "enum" || !node.name) continue;
|
|
2158
|
+
if (isSchemaRoot && node === root) continue;
|
|
2159
|
+
if (topLevelNames.has(node.name)) continue;
|
|
2160
|
+
if (!promoted.has(node.name)) promoted.set(node.name, {
|
|
2161
|
+
...node,
|
|
2162
|
+
optional: void 0,
|
|
2163
|
+
nullish: void 0
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return promoted;
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Replaces every promoted inline enum in `node` with a `ref` to its lifted definition, keeping the
|
|
2171
|
+
* occurrence's usage-slot and documentation fields.
|
|
2172
|
+
*/
|
|
2173
|
+
function refPromotedEnums(node, promoted) {
|
|
2174
|
+
if (promoted.size === 0) return node;
|
|
2175
|
+
return _kubb_core.ast.transform(node, { schema(schemaNode) {
|
|
2176
|
+
if (schemaNode.type !== "enum" || !schemaNode.name || !promoted.has(schemaNode.name)) return void 0;
|
|
2177
|
+
return _kubb_core.ast.factory.createSchema({
|
|
2178
|
+
type: "ref",
|
|
2179
|
+
name: schemaNode.name,
|
|
2180
|
+
ref: `${SCHEMA_REF_PREFIX}${schemaNode.name}`,
|
|
2181
|
+
optional: schemaNode.optional,
|
|
2182
|
+
nullish: schemaNode.nullish,
|
|
2183
|
+
readOnly: schemaNode.readOnly,
|
|
2184
|
+
writeOnly: schemaNode.writeOnly,
|
|
2185
|
+
deprecated: schemaNode.deprecated,
|
|
2186
|
+
description: schemaNode.description,
|
|
2187
|
+
default: schemaNode.default,
|
|
2188
|
+
examples: schemaNode.examples
|
|
2189
|
+
});
|
|
2190
|
+
} });
|
|
2191
|
+
}
|
|
2192
|
+
//#endregion
|
|
2193
|
+
//#region src/schemaDiagnostics.ts
|
|
2194
|
+
/**
|
|
2195
|
+
* Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
|
|
2196
|
+
* top-level schema. Walks the node the parser produced during `preScan`, threading the RFC 6901
|
|
2197
|
+
* pointer as it descends so a nested field reports against its full path
|
|
2198
|
+
* (`#/components/schemas/Pet/properties/owner/properties/name`). Refs are not followed, so the
|
|
2199
|
+
* resolved schema is reported under its own walk. Reports land in the active build run, are a
|
|
2200
|
+
* no-op outside one, and repeats are deduped by the build.
|
|
2201
|
+
*/
|
|
2202
|
+
function reportSchemaDiagnostics({ node, name }) {
|
|
2203
|
+
visit(node, `#/components/schemas/${escapePointerToken(name)}`);
|
|
2204
|
+
}
|
|
2205
|
+
/**
|
|
2206
|
+
* Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
|
|
2207
|
+
* property name with those characters maps to a distinct pointer instead of colliding in the dedupe.
|
|
2208
|
+
*/
|
|
2209
|
+
function escapePointerToken(token) {
|
|
2210
|
+
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2211
|
+
}
|
|
2212
|
+
function visit(node, pointer) {
|
|
2213
|
+
if (node.deprecated) _kubb_core.Diagnostics.report({
|
|
2214
|
+
code: _kubb_core.Diagnostics.code.deprecated,
|
|
2215
|
+
severity: "info",
|
|
2216
|
+
message: "This schema is marked as deprecated.",
|
|
2217
|
+
location: {
|
|
2218
|
+
kind: "schema",
|
|
2219
|
+
pointer
|
|
2220
|
+
}
|
|
2221
|
+
});
|
|
2222
|
+
if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_core.Diagnostics.report({
|
|
2223
|
+
code: _kubb_core.Diagnostics.code.unsupportedFormat,
|
|
2224
|
+
severity: "warning",
|
|
2225
|
+
message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
|
|
2226
|
+
help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
|
|
2227
|
+
location: {
|
|
2228
|
+
kind: "schema",
|
|
2229
|
+
pointer
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
if (node.type === "object") {
|
|
2233
|
+
for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
|
|
2234
|
+
if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
if (node.type === "array") {
|
|
2238
|
+
for (const item of node.items ?? []) visit(item, `${pointer}/items`);
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2241
|
+
if (node.type === "tuple") {
|
|
2242
|
+
for (const [index, item] of (node.items ?? []).entries()) visit(item, `${pointer}/items/${index}`);
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
if (node.type === "union" || node.type === "intersection") for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/members/${index}`);
|
|
2246
|
+
}
|
|
2247
|
+
//#endregion
|
|
2248
|
+
//#region src/stream.ts
|
|
2249
|
+
/**
|
|
2250
|
+
* Reads the server URL from the document's `servers` array at `server.index`,
|
|
2251
|
+
* interpolating any `server.variables` into the URL template.
|
|
2252
|
+
*
|
|
2253
|
+
* Returns `null` when `server.index` is omitted or out of range.
|
|
2254
|
+
*
|
|
2255
|
+
* @example Resolve the first server
|
|
2256
|
+
* `resolveBaseUrl({ document, server: { index: 0 } })`
|
|
2257
|
+
*
|
|
2258
|
+
* @example Override a path variable
|
|
2259
|
+
* `resolveBaseUrl({ document, server: { index: 0, variables: { version: 'v2' } } })`
|
|
2260
|
+
*/
|
|
2261
|
+
function resolveBaseUrl({ document, server }) {
|
|
2262
|
+
const index = server?.index;
|
|
2263
|
+
const entry = index !== void 0 ? document.servers?.at(index) : void 0;
|
|
2264
|
+
return entry?.url ? resolveServerUrl(entry, server?.variables) : null;
|
|
2265
|
+
}
|
|
1861
2266
|
/**
|
|
1862
|
-
* Parses
|
|
2267
|
+
* Parses every schema once to build the lookup structures that streaming needs upfront.
|
|
1863
2268
|
*
|
|
1864
|
-
*
|
|
1865
|
-
*
|
|
1866
|
-
* the
|
|
2269
|
+
* Three things happen in this single pass:
|
|
2270
|
+
* - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
|
|
2271
|
+
* - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
|
|
2272
|
+
* - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
|
|
2273
|
+
* The `allNodes` array is local and drops out of scope as soon as this function returns.
|
|
1867
2274
|
*
|
|
1868
|
-
*
|
|
2275
|
+
* After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
|
|
2276
|
+
* Both are proportional to the number of aliases or discriminator parents, not total schema count.
|
|
2277
|
+
*
|
|
2278
|
+
* Each schema is parsed again during the streaming pass. This is intentional.
|
|
2279
|
+
* Holding the parsed nodes in memory here would defeat the streaming memory benefit.
|
|
1869
2280
|
*
|
|
1870
2281
|
* @example
|
|
1871
2282
|
* ```ts
|
|
1872
|
-
*
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
2283
|
+
* const { refAliasMap, enumNames, circularNames } = preScan({
|
|
2284
|
+
* schemas,
|
|
2285
|
+
* parseSchema,
|
|
2286
|
+
* parserOptions,
|
|
2287
|
+
* discriminator: 'preserve',
|
|
2288
|
+
* })
|
|
1876
2289
|
* ```
|
|
1877
2290
|
*/
|
|
1878
|
-
function
|
|
1879
|
-
const
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2291
|
+
function preScan({ schemas, parseSchema, parseOperation, document, parserOptions, discriminator, enums = "inline" }) {
|
|
2292
|
+
const allNodes = [];
|
|
2293
|
+
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2294
|
+
const enumNames = [];
|
|
2295
|
+
const discriminatorParentNodes = [];
|
|
2296
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2297
|
+
const node = parseSchema({
|
|
2298
|
+
schema,
|
|
2299
|
+
name
|
|
2300
|
+
}, parserOptions);
|
|
2301
|
+
allNodes.push(node);
|
|
2302
|
+
reportSchemaDiagnostics({
|
|
2303
|
+
node,
|
|
2304
|
+
name
|
|
2305
|
+
});
|
|
2306
|
+
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2307
|
+
if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
|
|
2308
|
+
if (discriminator === "propagate" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2309
|
+
}
|
|
2310
|
+
const circularNames = [...(0, _kubb_ast_utils.findCircularSchemas)(allNodes)];
|
|
2311
|
+
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2312
|
+
let promotedEnums = null;
|
|
2313
|
+
if (enums === "root" && document && parseOperation) {
|
|
2314
|
+
const operationNodes = [];
|
|
2315
|
+
for (const operation of getOperations(document)) {
|
|
2316
|
+
const operationNode = parseOperation(parserOptions, operation);
|
|
2317
|
+
if (operationNode) operationNodes.push(operationNode);
|
|
2318
|
+
}
|
|
2319
|
+
promotedEnums = collectInlineEnums([...allNodes, ...operationNodes], new Set(Object.keys(schemas)));
|
|
2320
|
+
for (const name of promotedEnums.keys()) enumNames.push(name);
|
|
2321
|
+
}
|
|
1895
2322
|
return {
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2323
|
+
refAliasMap,
|
|
2324
|
+
enumNames,
|
|
2325
|
+
circularNames,
|
|
2326
|
+
discriminatorChildMap,
|
|
2327
|
+
promotedEnums
|
|
1901
2328
|
};
|
|
1902
2329
|
}
|
|
2330
|
+
/**
|
|
2331
|
+
* Creates a lazy `InputNode<true>` from already-resolved adapter state.
|
|
2332
|
+
*
|
|
2333
|
+
* The schema and operation iterables each start a fresh parse pass on every
|
|
2334
|
+
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
2335
|
+
* stream object independently without sharing a cursor or holding all nodes in memory.
|
|
2336
|
+
*
|
|
2337
|
+
* Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
|
|
2338
|
+
* with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
|
|
2339
|
+
*
|
|
2340
|
+
* @example
|
|
2341
|
+
* ```ts
|
|
2342
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2343
|
+
* for await (const schema of streamNode.schemas) {
|
|
2344
|
+
* // each call to for-await restarts from the first schema
|
|
2345
|
+
* }
|
|
2346
|
+
* ```
|
|
2347
|
+
*/
|
|
2348
|
+
function createInputStream({ schemas, parseSchema, parseOperation, document, parserOptions, refAliasMap, discriminatorChildMap, promotedEnums, meta }) {
|
|
2349
|
+
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2350
|
+
return (async function* () {
|
|
2351
|
+
if (promotedEnums) for (const definition of promotedEnums.values()) yield definition;
|
|
2352
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2353
|
+
const alias = refAliasMap.get(name);
|
|
2354
|
+
if (alias?.name && schemas[alias.name]) {
|
|
2355
|
+
const aliasNode = {
|
|
2356
|
+
...parseSchema({
|
|
2357
|
+
schema: schemas[alias.name],
|
|
2358
|
+
name: alias.name
|
|
2359
|
+
}, parserOptions),
|
|
2360
|
+
name
|
|
2361
|
+
};
|
|
2362
|
+
yield promotedEnums ? refPromotedEnums(aliasNode, promotedEnums) : aliasNode;
|
|
2363
|
+
continue;
|
|
2364
|
+
}
|
|
2365
|
+
const parsed = parseSchema({
|
|
2366
|
+
schema,
|
|
2367
|
+
name
|
|
2368
|
+
}, parserOptions);
|
|
2369
|
+
const node = discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed;
|
|
2370
|
+
yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
|
|
2371
|
+
}
|
|
2372
|
+
})();
|
|
2373
|
+
} };
|
|
2374
|
+
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2375
|
+
return (async function* () {
|
|
2376
|
+
for (const operation of getOperations(document)) {
|
|
2377
|
+
const node = parseOperation(parserOptions, operation);
|
|
2378
|
+
if (node) yield promotedEnums ? refPromotedEnums(node, promotedEnums) : node;
|
|
2379
|
+
}
|
|
2380
|
+
})();
|
|
2381
|
+
} };
|
|
2382
|
+
return _kubb_core.ast.factory.createInput({
|
|
2383
|
+
stream: true,
|
|
2384
|
+
schemas: schemasIterable,
|
|
2385
|
+
operations: operationsIterable,
|
|
2386
|
+
meta
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
1903
2389
|
//#endregion
|
|
1904
2390
|
//#region src/adapter.ts
|
|
1905
2391
|
/**
|
|
1906
|
-
*
|
|
2392
|
+
* The `name` of `@kubb/adapter-oas`, used to identify this adapter in a Kubb config.
|
|
1907
2393
|
*/
|
|
1908
2394
|
const adapterOasName = "oas";
|
|
1909
2395
|
/**
|
|
1910
|
-
*
|
|
2396
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.1 specifications. Reads the
|
|
2397
|
+
* file at `input.path`, validates it, resolves the base URL, and converts every
|
|
2398
|
+
* schema and operation into the universal AST that every downstream plugin
|
|
2399
|
+
* consumes.
|
|
1911
2400
|
*
|
|
1912
|
-
*
|
|
1913
|
-
*
|
|
2401
|
+
* Configure once on `defineConfig`. The adapter's choices (date representation,
|
|
2402
|
+
* integer width, server URL) apply to every plugin in the build.
|
|
1914
2403
|
*
|
|
1915
2404
|
* @example
|
|
1916
2405
|
* ```ts
|
|
@@ -1919,26 +2408,117 @@ const adapterOasName = "oas";
|
|
|
1919
2408
|
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1920
2409
|
*
|
|
1921
2410
|
* export default defineConfig({
|
|
1922
|
-
*
|
|
1923
|
-
*
|
|
2411
|
+
* input: { path: './petStore.yaml' },
|
|
2412
|
+
* output: { path: './src/gen' },
|
|
2413
|
+
* adapter: adapterOas({
|
|
2414
|
+
* server: { index: 0 },
|
|
2415
|
+
* discriminator: 'propagate',
|
|
2416
|
+
* dateType: 'date',
|
|
2417
|
+
* }),
|
|
1924
2418
|
* plugins: [pluginTs()],
|
|
1925
2419
|
* })
|
|
1926
2420
|
* ```
|
|
1927
2421
|
*/
|
|
1928
2422
|
const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
1929
|
-
const { validate = true, contentType,
|
|
2423
|
+
const { validate = true, contentType, server, discriminator = "preserve", enums = "inline", 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;
|
|
2424
|
+
const parserOptions = {
|
|
2425
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
2426
|
+
dateType,
|
|
2427
|
+
integerType,
|
|
2428
|
+
unknownType,
|
|
2429
|
+
emptySchemaType,
|
|
2430
|
+
enumSuffix
|
|
2431
|
+
};
|
|
1930
2432
|
let nameMapping = /* @__PURE__ */ new Map();
|
|
1931
|
-
let parsedDocument;
|
|
1932
|
-
|
|
2433
|
+
let parsedDocument = null;
|
|
2434
|
+
const documentCache = /* @__PURE__ */ new WeakMap();
|
|
2435
|
+
const schemasCache = /* @__PURE__ */ new WeakMap();
|
|
2436
|
+
const schemaParserCache = /* @__PURE__ */ new WeakMap();
|
|
2437
|
+
const preScanCache = /* @__PURE__ */ new WeakMap();
|
|
2438
|
+
function ensureDocument(source) {
|
|
2439
|
+
const cached = documentCache.get(source);
|
|
2440
|
+
if (cached) return cached;
|
|
2441
|
+
const promise = (async () => {
|
|
2442
|
+
const fresh = await parseFromConfig(source);
|
|
2443
|
+
if (validate) await validateDocument(fresh);
|
|
2444
|
+
parsedDocument = fresh;
|
|
2445
|
+
return fresh;
|
|
2446
|
+
})();
|
|
2447
|
+
documentCache.set(source, promise);
|
|
2448
|
+
return promise;
|
|
2449
|
+
}
|
|
2450
|
+
function ensureSchemas(document) {
|
|
2451
|
+
const cached = schemasCache.get(document);
|
|
2452
|
+
if (cached) return cached;
|
|
2453
|
+
const promise = Promise.resolve().then(() => {
|
|
2454
|
+
const result = getSchemas(document, { contentType });
|
|
2455
|
+
nameMapping = result.nameMapping;
|
|
2456
|
+
return result.schemas;
|
|
2457
|
+
});
|
|
2458
|
+
schemasCache.set(document, promise);
|
|
2459
|
+
return promise;
|
|
2460
|
+
}
|
|
2461
|
+
function ensureSchemaParser(document) {
|
|
2462
|
+
const cached = schemaParserCache.get(document);
|
|
2463
|
+
if (cached) return cached;
|
|
2464
|
+
const parser = createSchemaParser({
|
|
2465
|
+
document,
|
|
2466
|
+
contentType
|
|
2467
|
+
});
|
|
2468
|
+
schemaParserCache.set(document, parser);
|
|
2469
|
+
return parser;
|
|
2470
|
+
}
|
|
2471
|
+
function ensurePreScan(document, schemas, parseSchema, parseOperation) {
|
|
2472
|
+
const cached = preScanCache.get(document);
|
|
2473
|
+
if (cached) return cached;
|
|
2474
|
+
const result = preScan({
|
|
2475
|
+
schemas,
|
|
2476
|
+
parseSchema,
|
|
2477
|
+
parseOperation,
|
|
2478
|
+
document,
|
|
2479
|
+
parserOptions,
|
|
2480
|
+
discriminator,
|
|
2481
|
+
enums
|
|
2482
|
+
});
|
|
2483
|
+
preScanCache.set(document, result);
|
|
2484
|
+
return result;
|
|
2485
|
+
}
|
|
2486
|
+
async function createStream(source) {
|
|
2487
|
+
const document = await ensureDocument(source);
|
|
2488
|
+
const schemas = await ensureSchemas(document);
|
|
2489
|
+
const { parseSchema, parseOperation } = ensureSchemaParser(document);
|
|
2490
|
+
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, promotedEnums } = ensurePreScan(document, schemas, parseSchema, parseOperation);
|
|
2491
|
+
return createInputStream({
|
|
2492
|
+
schemas,
|
|
2493
|
+
parseSchema,
|
|
2494
|
+
parseOperation,
|
|
2495
|
+
document,
|
|
2496
|
+
parserOptions,
|
|
2497
|
+
refAliasMap,
|
|
2498
|
+
discriminatorChildMap,
|
|
2499
|
+
promotedEnums,
|
|
2500
|
+
meta: {
|
|
2501
|
+
title: document.info?.title,
|
|
2502
|
+
description: document.info?.description,
|
|
2503
|
+
version: document.info?.version,
|
|
2504
|
+
baseURL: resolveBaseUrl({
|
|
2505
|
+
document,
|
|
2506
|
+
server
|
|
2507
|
+
}),
|
|
2508
|
+
circularNames,
|
|
2509
|
+
enumNames
|
|
2510
|
+
}
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
1933
2513
|
return {
|
|
1934
2514
|
name: "oas",
|
|
1935
2515
|
get options() {
|
|
1936
2516
|
return {
|
|
1937
2517
|
validate,
|
|
1938
2518
|
contentType,
|
|
1939
|
-
|
|
1940
|
-
serverVariables,
|
|
2519
|
+
server,
|
|
1941
2520
|
discriminator,
|
|
2521
|
+
enums,
|
|
1942
2522
|
dateType,
|
|
1943
2523
|
integerType,
|
|
1944
2524
|
unknownType,
|
|
@@ -1950,71 +2530,36 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1950
2530
|
get document() {
|
|
1951
2531
|
return parsedDocument;
|
|
1952
2532
|
},
|
|
1953
|
-
get inputNode() {
|
|
1954
|
-
return inputNode;
|
|
1955
|
-
},
|
|
1956
2533
|
async validate(input, options) {
|
|
2534
|
+
await assertInputExists(input);
|
|
1957
2535
|
await validateDocument(await parseDocument(input), options);
|
|
1958
2536
|
},
|
|
1959
2537
|
getImports(node, resolve) {
|
|
1960
|
-
return
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
resolve
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
}
|
|
1971
|
-
});
|
|
2538
|
+
return (0, _kubb_ast.collect)(node, { schema(schemaNode) {
|
|
2539
|
+
const schemaRef = (0, _kubb_ast.narrowSchema)(schemaNode, "ref");
|
|
2540
|
+
if (!schemaRef?.ref) return null;
|
|
2541
|
+
const result = resolve(nameMapping.get(schemaRef.ref) ?? (0, _kubb_ast_utils.extractRefName)(schemaRef.ref));
|
|
2542
|
+
if (!result) return null;
|
|
2543
|
+
return _kubb_core.ast.factory.createImport({
|
|
2544
|
+
name: [result.name],
|
|
2545
|
+
path: result.path
|
|
2546
|
+
});
|
|
2547
|
+
} });
|
|
1972
2548
|
},
|
|
1973
2549
|
async parse(source) {
|
|
1974
|
-
const
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
dateType,
|
|
1981
|
-
integerType,
|
|
1982
|
-
unknownType,
|
|
1983
|
-
emptySchemaType,
|
|
1984
|
-
enumSuffix
|
|
2550
|
+
const streamNode = await createStream(source);
|
|
2551
|
+
const [schemas, operations] = await Promise.all([Array.fromAsync(streamNode.schemas), Array.fromAsync(streamNode.operations)]);
|
|
2552
|
+
return _kubb_core.ast.factory.createInput({
|
|
2553
|
+
schemas,
|
|
2554
|
+
operations,
|
|
2555
|
+
meta: streamNode.meta
|
|
1985
2556
|
});
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
parsedDocument = document;
|
|
1989
|
-
inputNode = _kubb_core.ast.createInput({
|
|
1990
|
-
...node,
|
|
1991
|
-
meta: {
|
|
1992
|
-
title: document.info?.title,
|
|
1993
|
-
description: document.info?.description,
|
|
1994
|
-
version: document.info?.version,
|
|
1995
|
-
baseURL
|
|
1996
|
-
}
|
|
1997
|
-
});
|
|
1998
|
-
return inputNode;
|
|
1999
|
-
}
|
|
2557
|
+
},
|
|
2558
|
+
stream: createStream
|
|
2000
2559
|
};
|
|
2001
2560
|
});
|
|
2002
2561
|
//#endregion
|
|
2003
|
-
//#region src/types.ts
|
|
2004
|
-
/**
|
|
2005
|
-
* Maps uppercase HTTP method names to lowercase for backwards compatibility.
|
|
2006
|
-
*
|
|
2007
|
-
* @example
|
|
2008
|
-
* ```ts
|
|
2009
|
-
* HttpMethods['GET'] // 'get'
|
|
2010
|
-
* HttpMethods['POST'] // 'post'
|
|
2011
|
-
* ```
|
|
2012
|
-
*/
|
|
2013
|
-
const HttpMethods = Object.fromEntries(Object.entries(_kubb_core.ast.httpMethods).map(([lower, upper]) => [upper, lower]));
|
|
2014
|
-
//#endregion
|
|
2015
|
-
exports.HttpMethods = HttpMethods;
|
|
2016
2562
|
exports.adapterOas = adapterOas;
|
|
2017
2563
|
exports.adapterOasName = adapterOasName;
|
|
2018
|
-
exports.mergeDocuments = mergeDocuments;
|
|
2019
2564
|
|
|
2020
2565
|
//# sourceMappingURL=index.cjs.map
|