@kubb/adapter-oas 5.0.0-beta.3 → 5.0.0-beta.31
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/README.md +98 -0
- package/dist/index.cjs +856 -418
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +50 -66
- package/dist/index.js +856 -415
- package/dist/index.js.map +1 -1
- package/extension.yaml +431 -0
- package/package.json +6 -4
- package/src/adapter.bench.ts +64 -0
- package/src/adapter.ts +99 -48
- package/src/dialect.ts +38 -0
- package/src/discriminator.ts +57 -40
- package/src/factory.ts +1 -4
- package/src/index.ts +2 -2
- package/src/parser.ts +235 -134
- package/src/refs.ts +16 -4
- package/src/resolvers.ts +50 -21
- package/src/stream.ts +276 -0
- package/src/types.ts +34 -11
package/dist/index.cjs
CHANGED
|
@@ -21,219 +21,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
let _kubb_core = require("@kubb/core");
|
|
25
24
|
let node_path = require("node:path");
|
|
26
25
|
node_path = __toESM(node_path, 1);
|
|
26
|
+
let _kubb_core = require("@kubb/core");
|
|
27
|
+
let oas = require("oas");
|
|
28
|
+
oas = __toESM(oas, 1);
|
|
27
29
|
let _redocly_openapi_core = require("@redocly/openapi-core");
|
|
28
30
|
let oas_normalize = require("oas-normalize");
|
|
29
31
|
oas_normalize = __toESM(oas_normalize, 1);
|
|
30
32
|
let swagger2openapi = require("swagger2openapi");
|
|
31
33
|
swagger2openapi = __toESM(swagger2openapi, 1);
|
|
32
|
-
let oas = require("oas");
|
|
33
|
-
oas = __toESM(oas, 1);
|
|
34
34
|
let oas_types = require("oas/types");
|
|
35
35
|
let oas_utils = require("oas/utils");
|
|
36
|
-
//#region src/constants.ts
|
|
37
|
-
/**
|
|
38
|
-
* Default parser options applied when no explicit options are provided.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
43
|
-
*
|
|
44
|
-
* const parser = createOasParser(oas)
|
|
45
|
-
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
const DEFAULT_PARSER_OPTIONS = {
|
|
49
|
-
dateType: "string",
|
|
50
|
-
integerType: "bigint",
|
|
51
|
-
unknownType: "any",
|
|
52
|
-
emptySchemaType: "any",
|
|
53
|
-
enumSuffix: "enum"
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
|
|
57
|
-
*
|
|
58
|
-
* Used when building or parsing `$ref` strings.
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```ts
|
|
62
|
-
* `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
66
|
-
/**
|
|
67
|
-
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
68
|
-
*/
|
|
69
|
-
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
70
|
-
/**
|
|
71
|
-
* Fallback `info.title` placed in the stub document when merging multiple API files.
|
|
72
|
-
*/
|
|
73
|
-
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
74
|
-
/**
|
|
75
|
-
* Fallback `info.version` placed in the stub document when merging multiple API files.
|
|
76
|
-
*/
|
|
77
|
-
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
78
|
-
/**
|
|
79
|
-
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
80
|
-
*
|
|
81
|
-
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
82
|
-
* intersection member rather than being merged into the parent.
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```ts
|
|
86
|
-
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
87
|
-
*
|
|
88
|
-
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
89
|
-
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
|
-
const structuralKeys = new Set([
|
|
93
|
-
"properties",
|
|
94
|
-
"items",
|
|
95
|
-
"additionalProperties",
|
|
96
|
-
"oneOf",
|
|
97
|
-
"anyOf",
|
|
98
|
-
"allOf",
|
|
99
|
-
"not"
|
|
100
|
-
]);
|
|
101
|
-
/**
|
|
102
|
-
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
103
|
-
*
|
|
104
|
-
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
105
|
-
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
106
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
|
|
107
|
-
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```ts
|
|
111
|
-
* import { formatMap } from '@kubb/adapter-oas'
|
|
112
|
-
*
|
|
113
|
-
* formatMap['uuid'] // 'uuid'
|
|
114
|
-
* formatMap['binary'] // 'blob'
|
|
115
|
-
* formatMap['float'] // 'number'
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
const formatMap = {
|
|
119
|
-
uuid: "uuid",
|
|
120
|
-
email: "email",
|
|
121
|
-
"idn-email": "email",
|
|
122
|
-
uri: "url",
|
|
123
|
-
"uri-reference": "url",
|
|
124
|
-
url: "url",
|
|
125
|
-
ipv4: "ipv4",
|
|
126
|
-
ipv6: "ipv6",
|
|
127
|
-
hostname: "url",
|
|
128
|
-
"idn-hostname": "url",
|
|
129
|
-
binary: "blob",
|
|
130
|
-
byte: "blob",
|
|
131
|
-
int32: "integer",
|
|
132
|
-
float: "number",
|
|
133
|
-
double: "number"
|
|
134
|
-
};
|
|
135
|
-
/**
|
|
136
|
-
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
137
|
-
*
|
|
138
|
-
* @example
|
|
139
|
-
* ```ts
|
|
140
|
-
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
141
|
-
*
|
|
142
|
-
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
143
|
-
* ```
|
|
144
|
-
*/
|
|
145
|
-
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
146
|
-
/**
|
|
147
|
-
* Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
|
|
148
|
-
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
149
|
-
*/
|
|
150
|
-
const typeOptionMap = new Map([
|
|
151
|
-
["any", _kubb_core.ast.schemaTypes.any],
|
|
152
|
-
["unknown", _kubb_core.ast.schemaTypes.unknown],
|
|
153
|
-
["void", _kubb_core.ast.schemaTypes.void]
|
|
154
|
-
]);
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/discriminator.ts
|
|
157
|
-
/**
|
|
158
|
-
* Injects discriminator enum values into child schemas so they know which value identifies them.
|
|
159
|
-
*
|
|
160
|
-
* Finds every union schema in `input.schemas` that has a `discriminatorPropertyName`, collects the
|
|
161
|
-
* enum value each union member is mapped to, then adds (or replaces) that property on the matching
|
|
162
|
-
* child object schema.
|
|
163
|
-
*
|
|
164
|
-
* Returns a new `InputNode` — the original is never mutated.
|
|
165
|
-
*
|
|
166
|
-
* @example
|
|
167
|
-
* ```ts
|
|
168
|
-
* const { root } = parseOas(document, options)
|
|
169
|
-
* const next = applyDiscriminatorInheritance(root)
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
function applyDiscriminatorInheritance(root) {
|
|
173
|
-
const childMap = /* @__PURE__ */ new Map();
|
|
174
|
-
for (const schema of root.schemas) {
|
|
175
|
-
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
176
|
-
if (!unionNode) {
|
|
177
|
-
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
178
|
-
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
179
|
-
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
180
|
-
if (u) {
|
|
181
|
-
unionNode = u;
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
187
|
-
const { discriminatorPropertyName, members } = unionNode;
|
|
188
|
-
for (const member of members) {
|
|
189
|
-
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
190
|
-
if (!intersectionNode?.members) continue;
|
|
191
|
-
let refNode;
|
|
192
|
-
let objNode;
|
|
193
|
-
for (const m of intersectionNode.members) {
|
|
194
|
-
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
195
|
-
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
196
|
-
}
|
|
197
|
-
if (!refNode?.name || !objNode) continue;
|
|
198
|
-
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
199
|
-
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : void 0;
|
|
200
|
-
if (!enumNode?.enumValues?.length) continue;
|
|
201
|
-
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
202
|
-
if (!enumValues.length) continue;
|
|
203
|
-
const existing = childMap.get(refNode.name);
|
|
204
|
-
if (existing) existing.enumValues.push(...enumValues);
|
|
205
|
-
else childMap.set(refNode.name, {
|
|
206
|
-
propertyName: discriminatorPropertyName,
|
|
207
|
-
enumValues: [...enumValues]
|
|
208
|
-
});
|
|
209
|
-
}
|
|
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
|
-
}
|
|
236
|
-
//#endregion
|
|
237
36
|
//#region ../../internals/utils/src/casing.ts
|
|
238
37
|
/**
|
|
239
38
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -332,6 +131,30 @@ function mergeDeep(target, source) {
|
|
|
332
131
|
return result;
|
|
333
132
|
}
|
|
334
133
|
//#endregion
|
|
134
|
+
//#region ../../internals/utils/src/promise.ts
|
|
135
|
+
/**
|
|
136
|
+
* Returns a wrapper that caches the result of the first invocation and replays
|
|
137
|
+
* it for every subsequent call, ignoring later arguments.
|
|
138
|
+
*
|
|
139
|
+
* Works for sync and async factories — for async, the cached value is the
|
|
140
|
+
* promise itself, so concurrent callers share one in-flight execution and
|
|
141
|
+
* cannot race each other.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const loadDocument = once(async (path: string) => parse(await readFile(path)))
|
|
146
|
+
* const a = loadDocument('./a.yaml') // parses
|
|
147
|
+
* const b = loadDocument('./b.yaml') // returns the cached promise from the first call
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
function once(factory) {
|
|
151
|
+
let cache;
|
|
152
|
+
return (...args) => {
|
|
153
|
+
if (!cache) cache = { value: factory(...args) };
|
|
154
|
+
return cache.value;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
335
158
|
//#region ../../internals/utils/src/reserved.ts
|
|
336
159
|
/**
|
|
337
160
|
* JavaScript and Java reserved words.
|
|
@@ -543,12 +366,13 @@ var URLPath = class {
|
|
|
543
366
|
* @example
|
|
544
367
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
545
368
|
*/
|
|
546
|
-
toTemplateString({ prefix
|
|
547
|
-
|
|
369
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
370
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
548
371
|
if (i % 2 === 0) return part;
|
|
549
372
|
const param = this.#transformParam(part);
|
|
550
373
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
551
|
-
}).join("")
|
|
374
|
+
}).join("");
|
|
375
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
552
376
|
}
|
|
553
377
|
/**
|
|
554
378
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -581,6 +405,126 @@ var URLPath = class {
|
|
|
581
405
|
}
|
|
582
406
|
};
|
|
583
407
|
//#endregion
|
|
408
|
+
//#region src/constants.ts
|
|
409
|
+
/**
|
|
410
|
+
* Default parser options applied when no explicit options are provided.
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```ts
|
|
414
|
+
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
415
|
+
*
|
|
416
|
+
* const parser = createOasParser(oas)
|
|
417
|
+
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
const DEFAULT_PARSER_OPTIONS = {
|
|
421
|
+
dateType: "string",
|
|
422
|
+
integerType: "bigint",
|
|
423
|
+
unknownType: "any",
|
|
424
|
+
emptySchemaType: "any",
|
|
425
|
+
enumSuffix: "enum"
|
|
426
|
+
};
|
|
427
|
+
/**
|
|
428
|
+
* JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
|
|
429
|
+
*
|
|
430
|
+
* Used when building or parsing `$ref` strings.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
438
|
+
/**
|
|
439
|
+
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
440
|
+
*/
|
|
441
|
+
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
442
|
+
/**
|
|
443
|
+
* Fallback `info.title` placed in the stub document when merging multiple API files.
|
|
444
|
+
*/
|
|
445
|
+
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
446
|
+
/**
|
|
447
|
+
* Fallback `info.version` placed in the stub document when merging multiple API files.
|
|
448
|
+
*/
|
|
449
|
+
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
450
|
+
/**
|
|
451
|
+
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
452
|
+
*
|
|
453
|
+
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
454
|
+
* intersection member rather than being merged into the parent.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
459
|
+
*
|
|
460
|
+
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
461
|
+
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
const structuralKeys = new Set([
|
|
465
|
+
"properties",
|
|
466
|
+
"items",
|
|
467
|
+
"additionalProperties",
|
|
468
|
+
"oneOf",
|
|
469
|
+
"anyOf",
|
|
470
|
+
"allOf",
|
|
471
|
+
"not"
|
|
472
|
+
]);
|
|
473
|
+
/**
|
|
474
|
+
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
475
|
+
*
|
|
476
|
+
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
477
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
478
|
+
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
|
|
479
|
+
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```ts
|
|
483
|
+
* import { formatMap } from '@kubb/adapter-oas'
|
|
484
|
+
*
|
|
485
|
+
* formatMap['uuid'] // 'uuid'
|
|
486
|
+
* formatMap['binary'] // 'blob'
|
|
487
|
+
* formatMap['float'] // 'number'
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
const formatMap = {
|
|
491
|
+
uuid: "uuid",
|
|
492
|
+
email: "email",
|
|
493
|
+
"idn-email": "email",
|
|
494
|
+
uri: "url",
|
|
495
|
+
"uri-reference": "url",
|
|
496
|
+
url: "url",
|
|
497
|
+
ipv4: "ipv4",
|
|
498
|
+
ipv6: "ipv6",
|
|
499
|
+
hostname: "url",
|
|
500
|
+
"idn-hostname": "url",
|
|
501
|
+
binary: "blob",
|
|
502
|
+
byte: "blob",
|
|
503
|
+
int32: "integer",
|
|
504
|
+
float: "number",
|
|
505
|
+
double: "number"
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```ts
|
|
512
|
+
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
513
|
+
*
|
|
514
|
+
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
518
|
+
/**
|
|
519
|
+
* Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
|
|
520
|
+
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
521
|
+
*/
|
|
522
|
+
const typeOptionMap = new Map([
|
|
523
|
+
["any", _kubb_core.ast.schemaTypes.any],
|
|
524
|
+
["unknown", _kubb_core.ast.schemaTypes.unknown],
|
|
525
|
+
["void", _kubb_core.ast.schemaTypes.void]
|
|
526
|
+
]);
|
|
527
|
+
//#endregion
|
|
584
528
|
//#region src/guards.ts
|
|
585
529
|
/**
|
|
586
530
|
* Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
|
|
@@ -686,11 +630,10 @@ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true }
|
|
|
686
630
|
* ```
|
|
687
631
|
*/
|
|
688
632
|
async function mergeDocuments(pathOrApi) {
|
|
689
|
-
const documents =
|
|
690
|
-
for (const p of pathOrApi) documents.push(await parseDocument(p, {
|
|
633
|
+
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
|
|
691
634
|
enablePaths: false,
|
|
692
635
|
canBundle: false
|
|
693
|
-
}));
|
|
636
|
+
})));
|
|
694
637
|
if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
|
|
695
638
|
const seed = {
|
|
696
639
|
openapi: MERGE_OPENAPI_VERSION,
|
|
@@ -746,6 +689,7 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
746
689
|
}
|
|
747
690
|
//#endregion
|
|
748
691
|
//#region src/refs.ts
|
|
692
|
+
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
749
693
|
/**
|
|
750
694
|
* Resolves a local JSON pointer reference from a document.
|
|
751
695
|
*
|
|
@@ -761,10 +705,17 @@ function resolveRef(document, $ref) {
|
|
|
761
705
|
const origRef = $ref;
|
|
762
706
|
$ref = $ref.trim();
|
|
763
707
|
if ($ref === "") return null;
|
|
764
|
-
if (
|
|
765
|
-
|
|
708
|
+
if (!$ref.startsWith("#")) return null;
|
|
709
|
+
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
710
|
+
let docCache = _refCache.get(document);
|
|
711
|
+
if (!docCache) {
|
|
712
|
+
docCache = /* @__PURE__ */ new Map();
|
|
713
|
+
_refCache.set(document, docCache);
|
|
714
|
+
}
|
|
715
|
+
if (docCache.has($ref)) return docCache.get($ref);
|
|
766
716
|
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
767
717
|
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
718
|
+
docCache.set($ref, current);
|
|
768
719
|
return current;
|
|
769
720
|
}
|
|
770
721
|
/**
|
|
@@ -788,6 +739,35 @@ function dereferenceWithRef(document, schema) {
|
|
|
788
739
|
return schema;
|
|
789
740
|
}
|
|
790
741
|
//#endregion
|
|
742
|
+
//#region src/dialect.ts
|
|
743
|
+
/**
|
|
744
|
+
* The OpenAPI / Swagger dialect — the default used by `@kubb/adapter-oas`.
|
|
745
|
+
*
|
|
746
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
747
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
748
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
749
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect — `type: ['null', …]`
|
|
750
|
+
* nullability, no discriminator object, binary via `contentEncoding` — and reuses
|
|
751
|
+
* the rest unchanged.
|
|
752
|
+
*
|
|
753
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
754
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```ts
|
|
758
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
759
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
const oasDialect = _kubb_core.ast.defineSchemaDialect({
|
|
763
|
+
name: "oas",
|
|
764
|
+
isNullable,
|
|
765
|
+
isReference,
|
|
766
|
+
isDiscriminator,
|
|
767
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
768
|
+
resolveRef
|
|
769
|
+
});
|
|
770
|
+
//#endregion
|
|
791
771
|
//#region src/resolvers.ts
|
|
792
772
|
/**
|
|
793
773
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -831,12 +811,6 @@ function getPrimitiveType(type) {
|
|
|
831
811
|
return "string";
|
|
832
812
|
}
|
|
833
813
|
/**
|
|
834
|
-
* Narrows a content-type string to the `MediaType` union Kubb recognizes, or returns `null`.
|
|
835
|
-
*/
|
|
836
|
-
function getMediaType(contentType) {
|
|
837
|
-
return Object.values(_kubb_core.ast.mediaTypes).includes(contentType) ? contentType : null;
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
814
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
841
815
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
842
816
|
* `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
|
|
@@ -979,21 +953,22 @@ function extractSchemaFromContent(content, preferredContentType) {
|
|
|
979
953
|
/**
|
|
980
954
|
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
981
955
|
*/
|
|
982
|
-
function collectRefs(schema
|
|
956
|
+
function* collectRefs(schema) {
|
|
983
957
|
if (Array.isArray(schema)) {
|
|
984
|
-
for (const item of schema) collectRefs(item
|
|
985
|
-
return
|
|
958
|
+
for (const item of schema) yield* collectRefs(item);
|
|
959
|
+
return;
|
|
986
960
|
}
|
|
987
961
|
if (schema && typeof schema === "object") for (const key in schema) {
|
|
988
962
|
const value = schema[key];
|
|
989
|
-
if (key === "$ref" && typeof value === "string") {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
963
|
+
if (!(key === "$ref" && typeof value === "string")) {
|
|
964
|
+
yield* collectRefs(value);
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
if (value.startsWith("#/components/schemas/")) {
|
|
968
|
+
const name = value.slice(21);
|
|
969
|
+
if (name) yield name;
|
|
970
|
+
}
|
|
995
971
|
}
|
|
996
|
-
return refs;
|
|
997
972
|
}
|
|
998
973
|
/**
|
|
999
974
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
@@ -1009,7 +984,7 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
|
1009
984
|
*/
|
|
1010
985
|
function sortSchemas(schemas) {
|
|
1011
986
|
const deps = /* @__PURE__ */ new Map();
|
|
1012
|
-
for (const [name, schema] of Object.entries(schemas)) deps.set(name,
|
|
987
|
+
for (const [name, schema] of Object.entries(schemas)) deps.set(name, [...new Set(collectRefs(schema))]);
|
|
1013
988
|
const sorted = [];
|
|
1014
989
|
const visited = /* @__PURE__ */ new Set();
|
|
1015
990
|
function visit(name, stack) {
|
|
@@ -1144,7 +1119,8 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
|
1144
1119
|
readOnly: schema.readOnly,
|
|
1145
1120
|
writeOnly: schema.writeOnly,
|
|
1146
1121
|
default: defaultValue,
|
|
1147
|
-
example: schema.example
|
|
1122
|
+
example: schema.example,
|
|
1123
|
+
format: schema.format
|
|
1148
1124
|
};
|
|
1149
1125
|
}
|
|
1150
1126
|
/**
|
|
@@ -1166,6 +1142,31 @@ function getRequestBodyContentTypes(document, operation) {
|
|
|
1166
1142
|
if (!body) return [];
|
|
1167
1143
|
return body.content ? Object.keys(body.content) : [];
|
|
1168
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Returns all response content type keys for an operation at a given status code.
|
|
1147
|
+
*
|
|
1148
|
+
* Response `$ref`s are resolved in-place first — the same mutation `getResponseSchema` performs —
|
|
1149
|
+
* so the returned list reflects the available content types even for referenced responses.
|
|
1150
|
+
*
|
|
1151
|
+
* @example
|
|
1152
|
+
* ```ts
|
|
1153
|
+
* getResponseBodyContentTypes(document, operation, 200)
|
|
1154
|
+
* // ['application/json', 'application/xml']
|
|
1155
|
+
* ```
|
|
1156
|
+
*/
|
|
1157
|
+
function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
1158
|
+
if (operation.schema.responses) {
|
|
1159
|
+
const responses = operation.schema.responses;
|
|
1160
|
+
for (const key in responses) {
|
|
1161
|
+
const schema = responses[key];
|
|
1162
|
+
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1166
|
+
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
1167
|
+
const body = responseObj;
|
|
1168
|
+
return body.content ? Object.keys(body.content) : [];
|
|
1169
|
+
}
|
|
1169
1170
|
//#endregion
|
|
1170
1171
|
//#region src/parser.ts
|
|
1171
1172
|
/**
|
|
@@ -1194,9 +1195,9 @@ function normalizeArrayEnum(schema) {
|
|
|
1194
1195
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1195
1196
|
* made possible by hoisting of function declarations.
|
|
1196
1197
|
*
|
|
1197
|
-
* @
|
|
1198
|
+
* @internal
|
|
1198
1199
|
*/
|
|
1199
|
-
function createSchemaParser(ctx) {
|
|
1200
|
+
function createSchemaParser(ctx, dialect = oasDialect) {
|
|
1200
1201
|
const document = ctx.document;
|
|
1201
1202
|
/**
|
|
1202
1203
|
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
@@ -1204,6 +1205,19 @@ function createSchemaParser(ctx) {
|
|
|
1204
1205
|
*/
|
|
1205
1206
|
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1206
1207
|
/**
|
|
1208
|
+
* Cache of already-resolved `$ref` schemas within this parser instance.
|
|
1209
|
+
*
|
|
1210
|
+
* Without this, the same referenced schema (e.g. `customer`) is fully re-expanded
|
|
1211
|
+
* every time it appears as a `$ref` in a different parent schema. In heavily
|
|
1212
|
+
* cross-referenced specs like Stripe (~1 400 schemas), this causes exponential
|
|
1213
|
+
* blowup — `customer` alone may be referenced from dozens of top-level schemas,
|
|
1214
|
+
* each triggering a fresh recursive expansion of its entire sub-tree.
|
|
1215
|
+
*
|
|
1216
|
+
* Memoizing by `$ref` path reduces the overall work from O(2^depth) to O(N)
|
|
1217
|
+
* where N is the number of unique schema names.
|
|
1218
|
+
*/
|
|
1219
|
+
const resolvedRefCache = /* @__PURE__ */ new Map();
|
|
1220
|
+
/**
|
|
1207
1221
|
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1208
1222
|
*
|
|
1209
1223
|
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
@@ -1212,16 +1226,22 @@ function createSchemaParser(ctx) {
|
|
|
1212
1226
|
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1213
1227
|
*/
|
|
1214
1228
|
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1215
|
-
let resolvedSchema;
|
|
1229
|
+
let resolvedSchema = null;
|
|
1216
1230
|
const refPath = schema.$ref;
|
|
1217
|
-
if (refPath && !resolvingRefs.has(refPath))
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1231
|
+
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1232
|
+
if (!resolvedRefCache.has(refPath)) {
|
|
1233
|
+
try {
|
|
1234
|
+
const referenced = dialect.resolveRef(document, refPath);
|
|
1235
|
+
if (referenced) {
|
|
1236
|
+
resolvingRefs.add(refPath);
|
|
1237
|
+
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1238
|
+
resolvingRefs.delete(refPath);
|
|
1239
|
+
}
|
|
1240
|
+
} catch {}
|
|
1241
|
+
resolvedRefCache.set(refPath, resolvedSchema);
|
|
1223
1242
|
}
|
|
1224
|
-
|
|
1243
|
+
resolvedSchema = resolvedRefCache.get(refPath) ?? null;
|
|
1244
|
+
}
|
|
1225
1245
|
return _kubb_core.ast.createSchema({
|
|
1226
1246
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1227
1247
|
type: "ref",
|
|
@@ -1238,7 +1258,7 @@ function createSchemaParser(ctx) {
|
|
|
1238
1258
|
const [memberSchema] = schema.allOf;
|
|
1239
1259
|
const memberNode = parseSchema({
|
|
1240
1260
|
schema: memberSchema,
|
|
1241
|
-
name
|
|
1261
|
+
name
|
|
1242
1262
|
}, rawOptions);
|
|
1243
1263
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1244
1264
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
@@ -1254,18 +1274,19 @@ function createSchemaParser(ctx) {
|
|
|
1254
1274
|
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1255
1275
|
default: mergedDefault,
|
|
1256
1276
|
example: schema.example ?? memberNode.example,
|
|
1257
|
-
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
|
|
1277
|
+
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1278
|
+
format: schema.format ?? memberNode.format
|
|
1258
1279
|
});
|
|
1259
1280
|
}
|
|
1260
1281
|
const filteredDiscriminantValues = [];
|
|
1261
1282
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1262
|
-
if (!isReference(item) || !name) return true;
|
|
1263
|
-
const deref = resolveRef(document, item.$ref);
|
|
1264
|
-
if (!deref || !isDiscriminator(deref)) return true;
|
|
1283
|
+
if (!dialect.isReference(item) || !name) return true;
|
|
1284
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1285
|
+
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1265
1286
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1266
1287
|
if (!parentUnion) return true;
|
|
1267
1288
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1268
|
-
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1289
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1269
1290
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1270
1291
|
if (inOneOf || inMapping) {
|
|
1271
1292
|
const discriminatorValue = _kubb_core.ast.findDiscriminator(deref.discriminator.mapping, childRef);
|
|
@@ -1276,22 +1297,28 @@ function createSchemaParser(ctx) {
|
|
|
1276
1297
|
return false;
|
|
1277
1298
|
}
|
|
1278
1299
|
return true;
|
|
1279
|
-
}).map((s) => parseSchema({
|
|
1300
|
+
}).map((s) => parseSchema({
|
|
1301
|
+
schema: s,
|
|
1302
|
+
name
|
|
1303
|
+
}, rawOptions));
|
|
1280
1304
|
const syntheticStart = allOfMembers.length;
|
|
1281
1305
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1282
1306
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1283
1307
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1284
1308
|
if (missingRequired.length) {
|
|
1285
1309
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1286
|
-
if (!isReference(item)) return [item];
|
|
1287
|
-
const deref = resolveRef(document, item.$ref);
|
|
1288
|
-
return deref && !isReference(deref) ? [deref] : [];
|
|
1310
|
+
if (!dialect.isReference(item)) return [item];
|
|
1311
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1312
|
+
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1289
1313
|
});
|
|
1290
1314
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1291
|
-
allOfMembers.push(parseSchema({
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1315
|
+
allOfMembers.push(parseSchema({
|
|
1316
|
+
schema: {
|
|
1317
|
+
properties: { [key]: resolved.properties[key] },
|
|
1318
|
+
required: [key]
|
|
1319
|
+
},
|
|
1320
|
+
name
|
|
1321
|
+
}, rawOptions));
|
|
1295
1322
|
break;
|
|
1296
1323
|
}
|
|
1297
1324
|
}
|
|
@@ -1306,7 +1333,7 @@ function createSchemaParser(ctx) {
|
|
|
1306
1333
|
}));
|
|
1307
1334
|
return _kubb_core.ast.createSchema({
|
|
1308
1335
|
type: "intersection",
|
|
1309
|
-
members: [..._kubb_core.ast.
|
|
1336
|
+
members: [..._kubb_core.ast.mergeAdjacentObjectsLazy(allOfMembers.slice(0, syntheticStart)), ..._kubb_core.ast.mergeAdjacentObjectsLazy(allOfMembers.slice(syntheticStart))],
|
|
1310
1337
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1311
1338
|
});
|
|
1312
1339
|
}
|
|
@@ -1327,10 +1354,10 @@ function createSchemaParser(ctx) {
|
|
|
1327
1354
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1328
1355
|
const unionBase = {
|
|
1329
1356
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1330
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1357
|
+
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1331
1358
|
strategy
|
|
1332
1359
|
};
|
|
1333
|
-
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1360
|
+
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1334
1361
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1335
1362
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1336
1363
|
return parseSchema({
|
|
@@ -1340,9 +1367,12 @@ function createSchemaParser(ctx) {
|
|
|
1340
1367
|
})() : void 0;
|
|
1341
1368
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1342
1369
|
const members = unionMembers.map((s) => {
|
|
1343
|
-
const ref = isReference(s) ? s.$ref : void 0;
|
|
1370
|
+
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1344
1371
|
const discriminatorValue = _kubb_core.ast.findDiscriminator(discriminator?.mapping, ref);
|
|
1345
|
-
const memberNode = parseSchema({
|
|
1372
|
+
const memberNode = parseSchema({
|
|
1373
|
+
schema: s,
|
|
1374
|
+
name
|
|
1375
|
+
}, rawOptions);
|
|
1346
1376
|
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1347
1377
|
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.setDiscriminatorEnum({
|
|
1348
1378
|
node: sharedPropertiesNode,
|
|
@@ -1372,7 +1402,10 @@ function createSchemaParser(ctx) {
|
|
|
1372
1402
|
return _kubb_core.ast.createSchema({
|
|
1373
1403
|
type: "union",
|
|
1374
1404
|
...unionBase,
|
|
1375
|
-
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1405
|
+
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1406
|
+
schema: s,
|
|
1407
|
+
name
|
|
1408
|
+
}, rawOptions)))
|
|
1376
1409
|
});
|
|
1377
1410
|
}
|
|
1378
1411
|
/**
|
|
@@ -1386,7 +1419,8 @@ function createSchemaParser(ctx) {
|
|
|
1386
1419
|
name,
|
|
1387
1420
|
title: schema.title,
|
|
1388
1421
|
description: schema.description,
|
|
1389
|
-
deprecated: schema.deprecated
|
|
1422
|
+
deprecated: schema.deprecated,
|
|
1423
|
+
format: schema.format
|
|
1390
1424
|
});
|
|
1391
1425
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1392
1426
|
return _kubb_core.ast.createSchema({
|
|
@@ -1476,6 +1510,15 @@ function createSchemaParser(ctx) {
|
|
|
1476
1510
|
}, rawOptions);
|
|
1477
1511
|
const nullInEnum = schema.enum.includes(null);
|
|
1478
1512
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1513
|
+
if (nullInEnum && filteredValues.length === 0) return _kubb_core.ast.createSchema({
|
|
1514
|
+
type: "null",
|
|
1515
|
+
primitive: "null",
|
|
1516
|
+
name,
|
|
1517
|
+
title: schema.title,
|
|
1518
|
+
description: schema.description,
|
|
1519
|
+
deprecated: schema.deprecated,
|
|
1520
|
+
format: schema.format
|
|
1521
|
+
});
|
|
1479
1522
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1480
1523
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1481
1524
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1490,7 +1533,8 @@ function createSchemaParser(ctx) {
|
|
|
1490
1533
|
readOnly: schema.readOnly,
|
|
1491
1534
|
writeOnly: schema.writeOnly,
|
|
1492
1535
|
default: enumDefault,
|
|
1493
|
-
example: schema.example
|
|
1536
|
+
example: schema.example,
|
|
1537
|
+
format: schema.format
|
|
1494
1538
|
};
|
|
1495
1539
|
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1496
1540
|
if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
@@ -1524,20 +1568,23 @@ function createSchemaParser(ctx) {
|
|
|
1524
1568
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1525
1569
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1526
1570
|
const resolvedPropSchema = propSchema;
|
|
1527
|
-
const propNullable = isNullable(resolvedPropSchema);
|
|
1571
|
+
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
1528
1572
|
const propNode = parseSchema({
|
|
1529
1573
|
schema: resolvedPropSchema,
|
|
1530
1574
|
name: _kubb_core.ast.childName(name, propName)
|
|
1531
1575
|
}, rawOptions);
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1576
|
+
const schemaNode = (() => {
|
|
1577
|
+
const node = _kubb_core.ast.setEnumName(propNode, name, propName, options.enumSuffix);
|
|
1578
|
+
const tupleNode = _kubb_core.ast.narrowSchema(node, "tuple");
|
|
1579
|
+
if (tupleNode?.items) {
|
|
1580
|
+
const namedItems = tupleNode.items.map((item) => _kubb_core.ast.setEnumName(item, name, propName, options.enumSuffix));
|
|
1581
|
+
if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
|
|
1582
|
+
...tupleNode,
|
|
1583
|
+
items: namedItems
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
return node;
|
|
1587
|
+
})();
|
|
1541
1588
|
return _kubb_core.ast.createProperty({
|
|
1542
1589
|
name: propName,
|
|
1543
1590
|
schema: {
|
|
@@ -1548,11 +1595,12 @@ function createSchemaParser(ctx) {
|
|
|
1548
1595
|
});
|
|
1549
1596
|
}) : [];
|
|
1550
1597
|
const additionalProperties = schema.additionalProperties;
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1598
|
+
const additionalPropertiesNode = (() => {
|
|
1599
|
+
if (additionalProperties === true) return true;
|
|
1600
|
+
if (additionalProperties === false) return false;
|
|
1601
|
+
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1602
|
+
if (additionalProperties) return _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1603
|
+
})();
|
|
1556
1604
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1557
1605
|
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1558
1606
|
const objectNode = _kubb_core.ast.createSchema({
|
|
@@ -1565,7 +1613,7 @@ function createSchemaParser(ctx) {
|
|
|
1565
1613
|
maxProperties: schema.maxProperties,
|
|
1566
1614
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1567
1615
|
});
|
|
1568
|
-
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1616
|
+
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1569
1617
|
const discPropName = schema.discriminator.propertyName;
|
|
1570
1618
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1571
1619
|
const enumName = name ? _kubb_core.ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
@@ -1599,7 +1647,7 @@ function createSchemaParser(ctx) {
|
|
|
1599
1647
|
*/
|
|
1600
1648
|
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1601
1649
|
const rawItems = schema.items;
|
|
1602
|
-
const itemName = rawItems?.enum?.length && name ? _kubb_core.ast.enumPropName(
|
|
1650
|
+
const itemName = rawItems?.enum?.length && name ? _kubb_core.ast.enumPropName(null, name, options.enumSuffix) : name;
|
|
1603
1651
|
const items = rawItems ? [parseSchema({
|
|
1604
1652
|
schema: rawItems,
|
|
1605
1653
|
name: itemName
|
|
@@ -1663,15 +1711,148 @@ function createSchemaParser(ctx) {
|
|
|
1663
1711
|
title: schema.title,
|
|
1664
1712
|
description: schema.description,
|
|
1665
1713
|
deprecated: schema.deprecated,
|
|
1666
|
-
nullable
|
|
1714
|
+
nullable,
|
|
1715
|
+
format: schema.format
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1720
|
+
* into a `blob` node.
|
|
1721
|
+
*/
|
|
1722
|
+
function convertBlob({ schema, name, nullable, defaultValue }) {
|
|
1723
|
+
return _kubb_core.ast.createSchema({
|
|
1724
|
+
type: "blob",
|
|
1725
|
+
primitive: "string",
|
|
1726
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1731
|
+
*
|
|
1732
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
|
|
1733
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1734
|
+
*/
|
|
1735
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1736
|
+
const types = schema.type;
|
|
1737
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1738
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1739
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1740
|
+
return _kubb_core.ast.createSchema({
|
|
1741
|
+
type: "union",
|
|
1742
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1743
|
+
schema: {
|
|
1744
|
+
...schema,
|
|
1745
|
+
type: t
|
|
1746
|
+
},
|
|
1747
|
+
name
|
|
1748
|
+
}, rawOptions)),
|
|
1749
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1667
1750
|
});
|
|
1668
1751
|
}
|
|
1669
1752
|
/**
|
|
1753
|
+
* Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1754
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1755
|
+
* `type`. The first matching rule that produces a node wins; see {@link SchemaRule} for the
|
|
1756
|
+
* match/convert/fall-through contract.
|
|
1757
|
+
*/
|
|
1758
|
+
const schemaRules = [
|
|
1759
|
+
{
|
|
1760
|
+
name: "ref",
|
|
1761
|
+
match: ({ schema }) => dialect.isReference(schema),
|
|
1762
|
+
convert: convertRef
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
name: "allOf",
|
|
1766
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1767
|
+
convert: convertAllOf
|
|
1768
|
+
},
|
|
1769
|
+
{
|
|
1770
|
+
name: "union",
|
|
1771
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1772
|
+
convert: convertUnion
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
name: "const",
|
|
1776
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1777
|
+
convert: convertConst
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
name: "format",
|
|
1781
|
+
match: ({ schema }) => !!schema.format,
|
|
1782
|
+
convert: convertFormat
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
name: "blob",
|
|
1786
|
+
match: ({ schema }) => dialect.isBinary(schema),
|
|
1787
|
+
convert: convertBlob
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
name: "multi-type",
|
|
1791
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1792
|
+
convert: convertMultiType
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
name: "constrained-string",
|
|
1796
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1797
|
+
convert: convertString
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
name: "constrained-number",
|
|
1801
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1802
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
name: "enum",
|
|
1806
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1807
|
+
convert: convertEnum
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
name: "object",
|
|
1811
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1812
|
+
convert: convertObject
|
|
1813
|
+
},
|
|
1814
|
+
{
|
|
1815
|
+
name: "tuple",
|
|
1816
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1817
|
+
convert: convertTuple
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
name: "array",
|
|
1821
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1822
|
+
convert: convertArray
|
|
1823
|
+
},
|
|
1824
|
+
{
|
|
1825
|
+
name: "string",
|
|
1826
|
+
match: ({ type }) => type === "string",
|
|
1827
|
+
convert: convertString
|
|
1828
|
+
},
|
|
1829
|
+
{
|
|
1830
|
+
name: "number",
|
|
1831
|
+
match: ({ type }) => type === "number",
|
|
1832
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
name: "integer",
|
|
1836
|
+
match: ({ type }) => type === "integer",
|
|
1837
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1838
|
+
},
|
|
1839
|
+
{
|
|
1840
|
+
name: "boolean",
|
|
1841
|
+
match: ({ type }) => type === "boolean",
|
|
1842
|
+
convert: convertBoolean
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
name: "null",
|
|
1846
|
+
match: ({ type }) => type === "null",
|
|
1847
|
+
convert: convertNull
|
|
1848
|
+
}
|
|
1849
|
+
];
|
|
1850
|
+
/**
|
|
1670
1851
|
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1671
1852
|
*
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
1674
|
-
*
|
|
1853
|
+
* Builds the per-schema context, then runs it through the ordered {@link schemaRules} table
|
|
1854
|
+
* via {@link ast.dispatch}. When no rule produces a node, falls back to the configured
|
|
1855
|
+
* `emptySchemaType`.
|
|
1675
1856
|
*/
|
|
1676
1857
|
function parseSchema({ schema, name }, rawOptions) {
|
|
1677
1858
|
const options = {
|
|
@@ -1683,75 +1864,40 @@ function createSchemaParser(ctx) {
|
|
|
1683
1864
|
schema: flattenedSchema,
|
|
1684
1865
|
name
|
|
1685
1866
|
}, rawOptions);
|
|
1686
|
-
const nullable = isNullable(schema) || void 0;
|
|
1687
|
-
const
|
|
1688
|
-
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
1689
|
-
const ctx = {
|
|
1867
|
+
const nullable = dialect.isNullable(schema) || void 0;
|
|
1868
|
+
const schemaCtx = {
|
|
1690
1869
|
schema,
|
|
1691
1870
|
name,
|
|
1692
1871
|
nullable,
|
|
1693
|
-
defaultValue,
|
|
1694
|
-
type,
|
|
1872
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1873
|
+
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1695
1874
|
rawOptions,
|
|
1696
1875
|
options
|
|
1697
1876
|
};
|
|
1698
|
-
|
|
1699
|
-
if (
|
|
1700
|
-
if ([...schema.oneOf ?? [], ...schema.anyOf ?? []].length) return convertUnion(ctx);
|
|
1701
|
-
if ("const" in schema && schema.const !== void 0) return convertConst(ctx);
|
|
1702
|
-
if (schema.format) {
|
|
1703
|
-
const formatResult = convertFormat(ctx);
|
|
1704
|
-
if (formatResult) return formatResult;
|
|
1705
|
-
}
|
|
1706
|
-
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return _kubb_core.ast.createSchema({
|
|
1707
|
-
type: "blob",
|
|
1708
|
-
primitive: "string",
|
|
1709
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1710
|
-
});
|
|
1711
|
-
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1712
|
-
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1713
|
-
const arrayNullable = schema.type.includes("null") || nullable || void 0;
|
|
1714
|
-
if (nonNullTypes.length > 1) return _kubb_core.ast.createSchema({
|
|
1715
|
-
type: "union",
|
|
1716
|
-
members: nonNullTypes.map((t) => parseSchema({
|
|
1717
|
-
schema: {
|
|
1718
|
-
...schema,
|
|
1719
|
-
type: t
|
|
1720
|
-
},
|
|
1721
|
-
name
|
|
1722
|
-
}, rawOptions)),
|
|
1723
|
-
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1724
|
-
});
|
|
1725
|
-
}
|
|
1726
|
-
if (!type) {
|
|
1727
|
-
if (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0) return convertString(ctx);
|
|
1728
|
-
if (schema.minimum !== void 0 || schema.maximum !== void 0) return convertNumeric(ctx, "number");
|
|
1729
|
-
}
|
|
1730
|
-
if (schema.enum?.length) return convertEnum(ctx);
|
|
1731
|
-
if (type === "object" || schema.properties || schema.additionalProperties || "patternProperties" in schema) return convertObject(ctx);
|
|
1732
|
-
if ("prefixItems" in schema) return convertTuple(ctx);
|
|
1733
|
-
if (type === "array" || "items" in schema) return convertArray(ctx);
|
|
1734
|
-
if (type === "string") return convertString(ctx);
|
|
1735
|
-
if (type === "number") return convertNumeric(ctx, "number");
|
|
1736
|
-
if (type === "integer") return convertNumeric(ctx, "integer");
|
|
1737
|
-
if (type === "boolean") return convertBoolean(ctx);
|
|
1738
|
-
if (type === "null") return convertNull(ctx);
|
|
1877
|
+
const node = _kubb_core.ast.dispatch(schemaRules, schemaCtx);
|
|
1878
|
+
if (node) return node;
|
|
1739
1879
|
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1740
1880
|
return _kubb_core.ast.createSchema({
|
|
1741
1881
|
type: emptyType,
|
|
1742
1882
|
name,
|
|
1743
1883
|
title: schema.title,
|
|
1744
|
-
description: schema.description
|
|
1884
|
+
description: schema.description,
|
|
1885
|
+
format: schema.format
|
|
1745
1886
|
});
|
|
1746
1887
|
}
|
|
1747
1888
|
/**
|
|
1748
1889
|
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1749
1890
|
*/
|
|
1750
|
-
function parseParameter(options, param) {
|
|
1891
|
+
function parseParameter(options, param, parentName) {
|
|
1751
1892
|
const required = param["required"] ?? false;
|
|
1752
|
-
const
|
|
1893
|
+
const paramName = param["name"];
|
|
1894
|
+
const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
|
|
1895
|
+
const schema = param["schema"] ? parseSchema({
|
|
1896
|
+
schema: param["schema"],
|
|
1897
|
+
name: schemaName
|
|
1898
|
+
}, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1753
1899
|
return _kubb_core.ast.createParameter({
|
|
1754
|
-
name:
|
|
1900
|
+
name: paramName,
|
|
1755
1901
|
in: param["in"],
|
|
1756
1902
|
schema: {
|
|
1757
1903
|
...schema,
|
|
@@ -1788,27 +1934,33 @@ function createSchemaParser(ctx) {
|
|
|
1788
1934
|
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
1789
1935
|
*/
|
|
1790
1936
|
function collectPropertyKeysByFlag(schema, flag) {
|
|
1791
|
-
if (!schema?.properties) return
|
|
1937
|
+
if (!schema?.properties) return null;
|
|
1792
1938
|
const keys = [];
|
|
1793
1939
|
for (const key in schema.properties) {
|
|
1794
1940
|
const prop = schema.properties[key];
|
|
1795
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
1941
|
+
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
1796
1942
|
}
|
|
1797
|
-
return keys.length ? keys :
|
|
1943
|
+
return keys.length ? keys : null;
|
|
1798
1944
|
}
|
|
1799
1945
|
/**
|
|
1800
1946
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1801
1947
|
*/
|
|
1802
1948
|
function parseOperation(options, operation) {
|
|
1803
|
-
const
|
|
1949
|
+
const operationId = operation.getOperationId();
|
|
1950
|
+
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
1951
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
|
|
1804
1952
|
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
1805
1953
|
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
1954
|
+
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
1806
1955
|
const content = allContentTypes.flatMap((ct) => {
|
|
1807
1956
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
1808
1957
|
if (!schema) return [];
|
|
1809
1958
|
return [{
|
|
1810
1959
|
contentType: ct,
|
|
1811
|
-
schema: _kubb_core.ast.syncOptionality(parseSchema({
|
|
1960
|
+
schema: _kubb_core.ast.syncOptionality(parseSchema({
|
|
1961
|
+
schema,
|
|
1962
|
+
name: requestBodyName
|
|
1963
|
+
}, options), requestBodyMeta.required),
|
|
1812
1964
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1813
1965
|
}];
|
|
1814
1966
|
});
|
|
@@ -1819,21 +1971,36 @@ function createSchemaParser(ctx) {
|
|
|
1819
1971
|
} : void 0;
|
|
1820
1972
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1821
1973
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1824
|
-
const
|
|
1825
|
-
|
|
1974
|
+
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
1975
|
+
const { description } = getResponseMeta(responseObj);
|
|
1976
|
+
const parseEntrySchema = (contentType) => {
|
|
1977
|
+
const raw = getResponseSchema(document, operation, statusCode, { contentType });
|
|
1978
|
+
return {
|
|
1979
|
+
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
1980
|
+
schema: raw,
|
|
1981
|
+
name: responseName
|
|
1982
|
+
}, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType) }),
|
|
1983
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
1984
|
+
};
|
|
1985
|
+
};
|
|
1986
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
|
|
1987
|
+
contentType,
|
|
1988
|
+
...parseEntrySchema(contentType)
|
|
1989
|
+
}));
|
|
1990
|
+
if (content.length === 0) content.push({
|
|
1991
|
+
contentType: operation.contentType || "application/json",
|
|
1992
|
+
...parseEntrySchema(ctx.contentType)
|
|
1993
|
+
});
|
|
1826
1994
|
return _kubb_core.ast.createResponse({
|
|
1827
1995
|
statusCode,
|
|
1828
1996
|
description,
|
|
1829
|
-
|
|
1830
|
-
mediaType,
|
|
1831
|
-
keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
|
|
1997
|
+
content
|
|
1832
1998
|
});
|
|
1833
1999
|
});
|
|
1834
2000
|
const urlPath = new URLPath(operation.path);
|
|
1835
2001
|
return _kubb_core.ast.createOperation({
|
|
1836
|
-
operationId
|
|
2002
|
+
operationId,
|
|
2003
|
+
protocol: "http",
|
|
1837
2004
|
method: operation.method.toUpperCase(),
|
|
1838
2005
|
path: urlPath.path,
|
|
1839
2006
|
tags: operation.getTags().map((tag) => tag.name),
|
|
@@ -1851,59 +2018,278 @@ function createSchemaParser(ctx) {
|
|
|
1851
2018
|
parseParameter
|
|
1852
2019
|
};
|
|
1853
2020
|
}
|
|
2021
|
+
//#endregion
|
|
2022
|
+
//#region src/discriminator.ts
|
|
2023
|
+
/**
|
|
2024
|
+
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
2025
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
2026
|
+
*
|
|
2027
|
+
* Extracted from `applyDiscriminatorInheritance` so the streaming path can call it on a
|
|
2028
|
+
* small pre-parsed subset of schemas (only the discriminator parents) rather than on all
|
|
2029
|
+
* schemas at once.
|
|
2030
|
+
*/
|
|
2031
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
2032
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
2033
|
+
for (const schema of schemas) {
|
|
2034
|
+
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
2035
|
+
if (!unionNode) {
|
|
2036
|
+
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
2037
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
2038
|
+
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
2039
|
+
if (u) {
|
|
2040
|
+
unionNode = u;
|
|
2041
|
+
break;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
2046
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
2047
|
+
for (const member of members) {
|
|
2048
|
+
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
2049
|
+
if (!intersectionNode?.members) continue;
|
|
2050
|
+
let refNode = null;
|
|
2051
|
+
let objNode = null;
|
|
2052
|
+
for (const m of intersectionNode.members) {
|
|
2053
|
+
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
2054
|
+
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
2055
|
+
}
|
|
2056
|
+
if (!refNode?.name || !objNode) continue;
|
|
2057
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
2058
|
+
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : null;
|
|
2059
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
2060
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
2061
|
+
if (!enumValues.length) continue;
|
|
2062
|
+
const existing = childMap.get(refNode.name);
|
|
2063
|
+
if (!existing) {
|
|
2064
|
+
childMap.set(refNode.name, {
|
|
2065
|
+
propertyName: discriminatorPropertyName,
|
|
2066
|
+
enumValues: [...enumValues]
|
|
2067
|
+
});
|
|
2068
|
+
continue;
|
|
2069
|
+
}
|
|
2070
|
+
existing.enumValues.push(...enumValues);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
return childMap;
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
2077
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
2078
|
+
* without buffering all schemas.
|
|
2079
|
+
*/
|
|
2080
|
+
function patchDiscriminatorNode(node, entry) {
|
|
2081
|
+
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
2082
|
+
if (!objectNode) return node;
|
|
2083
|
+
const { propertyName, enumValues } = entry;
|
|
2084
|
+
const enumSchema = _kubb_core.ast.createSchema({
|
|
2085
|
+
type: "enum",
|
|
2086
|
+
enumValues
|
|
2087
|
+
});
|
|
2088
|
+
const newProp = _kubb_core.ast.createProperty({
|
|
2089
|
+
name: propertyName,
|
|
2090
|
+
required: true,
|
|
2091
|
+
schema: enumSchema
|
|
2092
|
+
});
|
|
2093
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
2094
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
2095
|
+
return {
|
|
2096
|
+
...objectNode,
|
|
2097
|
+
properties: newProperties
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
//#endregion
|
|
2101
|
+
//#region src/stream.ts
|
|
2102
|
+
/**
|
|
2103
|
+
* Builds the deduplication plan from the already-parsed top-level schema nodes plus a
|
|
2104
|
+
* single extra parse pass over operations (so duplicates in request/response bodies are seen).
|
|
2105
|
+
*
|
|
2106
|
+
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2107
|
+
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2108
|
+
* name (collision-resolved against existing component names); shapes without a name stay inline.
|
|
2109
|
+
*/
|
|
2110
|
+
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2111
|
+
const circularSchemas = new Set(circularNames);
|
|
2112
|
+
const usedNames = new Set(schemaNames);
|
|
2113
|
+
return _kubb_core.ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2114
|
+
isCandidate: (node) => {
|
|
2115
|
+
if (node.type === "enum") return true;
|
|
2116
|
+
if (node.type !== "object") return false;
|
|
2117
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2118
|
+
return !_kubb_core.ast.containsCircularRef(node, { circularSchemas });
|
|
2119
|
+
},
|
|
2120
|
+
nameFor: (node) => {
|
|
2121
|
+
const base = node.name;
|
|
2122
|
+
if (!base) return null;
|
|
2123
|
+
let name = base;
|
|
2124
|
+
let counter = 2;
|
|
2125
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2126
|
+
usedNames.add(name);
|
|
2127
|
+
return name;
|
|
2128
|
+
},
|
|
2129
|
+
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
1854
2132
|
/**
|
|
1855
|
-
*
|
|
2133
|
+
* Reads the server URL from the document's `servers` array at `serverIndex`,
|
|
2134
|
+
* interpolating any `serverVariables` into the URL template.
|
|
1856
2135
|
*
|
|
1857
|
-
*
|
|
1858
|
-
* that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here —
|
|
1859
|
-
* the tree is a pure data structure representing all schemas and operations.
|
|
2136
|
+
* Returns `null` when `serverIndex` is omitted or out of range.
|
|
1860
2137
|
*
|
|
1861
|
-
*
|
|
2138
|
+
* @example Resolve the first server
|
|
2139
|
+
* `resolveBaseUrl({ document, serverIndex: 0 })`
|
|
2140
|
+
*
|
|
2141
|
+
* @example Override a path variable
|
|
2142
|
+
* `resolveBaseUrl({ document, serverIndex: 0, serverVariables: { version: 'v2' } })`
|
|
2143
|
+
*/
|
|
2144
|
+
function resolveBaseUrl({ document, serverIndex, serverVariables }) {
|
|
2145
|
+
const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
|
|
2146
|
+
return server?.url ? resolveServerUrl(server, serverVariables) : null;
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Parses every schema once to build the lookup structures that streaming needs upfront.
|
|
2150
|
+
*
|
|
2151
|
+
* Three things happen in this single pass:
|
|
2152
|
+
* - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
|
|
2153
|
+
* - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
|
|
2154
|
+
* - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
|
|
2155
|
+
* The `allNodes` array is local and drops out of scope as soon as this function returns.
|
|
2156
|
+
*
|
|
2157
|
+
* After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
|
|
2158
|
+
* Both are proportional to the number of aliases or discriminator parents, not total schema count.
|
|
2159
|
+
*
|
|
2160
|
+
* Each schema is parsed again during the streaming pass — this is intentional.
|
|
2161
|
+
* Holding the parsed nodes in memory here would defeat the streaming memory benefit.
|
|
1862
2162
|
*
|
|
1863
2163
|
* @example
|
|
1864
2164
|
* ```ts
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1867
|
-
*
|
|
1868
|
-
*
|
|
2165
|
+
* const { refAliasMap, enumNames, circularNames } = preScan({
|
|
2166
|
+
* schemas,
|
|
2167
|
+
* parseSchema,
|
|
2168
|
+
* parserOptions,
|
|
2169
|
+
* discriminator: 'strict',
|
|
2170
|
+
* })
|
|
1869
2171
|
* ```
|
|
1870
2172
|
*/
|
|
1871
|
-
function
|
|
1872
|
-
const
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
}
|
|
1886
|
-
const
|
|
1887
|
-
const
|
|
2173
|
+
function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions, discriminator, dedupe }) {
|
|
2174
|
+
const allNodes = [];
|
|
2175
|
+
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2176
|
+
const enumNames = [];
|
|
2177
|
+
const discriminatorParentNodes = [];
|
|
2178
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2179
|
+
const node = parseSchema({
|
|
2180
|
+
schema,
|
|
2181
|
+
name
|
|
2182
|
+
}, parserOptions);
|
|
2183
|
+
allNodes.push(node);
|
|
2184
|
+
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2185
|
+
if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
|
|
2186
|
+
if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2187
|
+
}
|
|
2188
|
+
const circularNames = [..._kubb_core.ast.findCircularSchemas(allNodes)];
|
|
2189
|
+
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2190
|
+
let dedupePlan = null;
|
|
2191
|
+
if (dedupe) {
|
|
2192
|
+
const operationNodes = [];
|
|
2193
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2194
|
+
if (!operation) continue;
|
|
2195
|
+
const operationNode = parseOperation(parserOptions, operation);
|
|
2196
|
+
if (operationNode) operationNodes.push(operationNode);
|
|
2197
|
+
}
|
|
2198
|
+
dedupePlan = createDedupePlan({
|
|
2199
|
+
schemaNodes: allNodes,
|
|
2200
|
+
operationNodes,
|
|
2201
|
+
schemaNames: Object.keys(schemas),
|
|
2202
|
+
circularNames
|
|
2203
|
+
});
|
|
2204
|
+
for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2205
|
+
}
|
|
1888
2206
|
return {
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2207
|
+
refAliasMap,
|
|
2208
|
+
enumNames,
|
|
2209
|
+
circularNames,
|
|
2210
|
+
discriminatorChildMap,
|
|
2211
|
+
dedupePlan
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
/**
|
|
2215
|
+
* Creates a lazy `InputStreamNode` from already-resolved adapter state.
|
|
2216
|
+
*
|
|
2217
|
+
* The schema and operation iterables each start a fresh parse pass on every
|
|
2218
|
+
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
2219
|
+
* stream object independently without sharing a cursor or holding all nodes in memory.
|
|
2220
|
+
*
|
|
2221
|
+
* Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
|
|
2222
|
+
* with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
|
|
2223
|
+
*
|
|
2224
|
+
* @example
|
|
2225
|
+
* ```ts
|
|
2226
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2227
|
+
* for await (const schema of streamNode.schemas) {
|
|
2228
|
+
* // each call to for-await restarts from the first schema
|
|
2229
|
+
* }
|
|
2230
|
+
* ```
|
|
2231
|
+
*/
|
|
2232
|
+
function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2233
|
+
const rewriteTopLevelSchema = (node) => {
|
|
2234
|
+
if (!dedupePlan) return node;
|
|
2235
|
+
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.schemaSignature(node));
|
|
2236
|
+
if (canonical && canonical.name !== node.name) return _kubb_core.ast.createSchema({
|
|
2237
|
+
type: "ref",
|
|
2238
|
+
name: node.name ?? null,
|
|
2239
|
+
ref: canonical.ref,
|
|
2240
|
+
description: node.description,
|
|
2241
|
+
deprecated: node.deprecated
|
|
2242
|
+
});
|
|
2243
|
+
return _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature, true);
|
|
1894
2244
|
};
|
|
2245
|
+
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2246
|
+
return (async function* () {
|
|
2247
|
+
if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
|
|
2248
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2249
|
+
const alias = refAliasMap.get(name);
|
|
2250
|
+
if (alias?.name && schemas[alias.name]) {
|
|
2251
|
+
yield rewriteTopLevelSchema({
|
|
2252
|
+
...parseSchema({
|
|
2253
|
+
schema: schemas[alias.name],
|
|
2254
|
+
name: alias.name
|
|
2255
|
+
}, parserOptions),
|
|
2256
|
+
name
|
|
2257
|
+
});
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
const parsed = parseSchema({
|
|
2261
|
+
schema,
|
|
2262
|
+
name
|
|
2263
|
+
}, parserOptions);
|
|
2264
|
+
yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
|
|
2265
|
+
}
|
|
2266
|
+
})();
|
|
2267
|
+
} };
|
|
2268
|
+
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2269
|
+
return (async function* () {
|
|
2270
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2271
|
+
if (!operation) continue;
|
|
2272
|
+
const node = parseOperation(parserOptions, operation);
|
|
2273
|
+
if (node) yield dedupePlan ? _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node;
|
|
2274
|
+
}
|
|
2275
|
+
})();
|
|
2276
|
+
} };
|
|
2277
|
+
return _kubb_core.ast.createStreamInput(schemasIterable, operationsIterable, meta);
|
|
1895
2278
|
}
|
|
1896
2279
|
//#endregion
|
|
1897
2280
|
//#region src/adapter.ts
|
|
1898
2281
|
/**
|
|
1899
|
-
*
|
|
2282
|
+
* Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
|
|
1900
2283
|
*/
|
|
1901
2284
|
const adapterOasName = "oas";
|
|
1902
2285
|
/**
|
|
1903
|
-
*
|
|
2286
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.1 specifications. Reads the
|
|
2287
|
+
* file at `input.path`, validates it, resolves the base URL, and converts every
|
|
2288
|
+
* schema and operation into the universal AST that every downstream plugin
|
|
2289
|
+
* consumes.
|
|
1904
2290
|
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
2291
|
+
* Configure once on `defineConfig`. The adapter's choices (date representation,
|
|
2292
|
+
* integer width, server URL) apply to every plugin in the build.
|
|
1907
2293
|
*
|
|
1908
2294
|
* @example
|
|
1909
2295
|
* ```ts
|
|
@@ -1912,17 +2298,83 @@ const adapterOasName = "oas";
|
|
|
1912
2298
|
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1913
2299
|
*
|
|
1914
2300
|
* export default defineConfig({
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
2301
|
+
* input: { path: './petStore.yaml' },
|
|
2302
|
+
* output: { path: './src/gen' },
|
|
2303
|
+
* adapter: adapterOas({
|
|
2304
|
+
* serverIndex: 0,
|
|
2305
|
+
* discriminator: 'inherit',
|
|
2306
|
+
* dateType: 'date',
|
|
2307
|
+
* }),
|
|
1917
2308
|
* plugins: [pluginTs()],
|
|
1918
2309
|
* })
|
|
1919
2310
|
* ```
|
|
1920
2311
|
*/
|
|
1921
2312
|
const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
1922
|
-
const { validate = true, contentType, serverIndex, serverVariables, discriminator = "strict", dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
|
|
2313
|
+
const { validate = true, contentType, serverIndex, serverVariables, discriminator = "strict", dedupe = true, dateType = DEFAULT_PARSER_OPTIONS.dateType, integerType = DEFAULT_PARSER_OPTIONS.integerType, unknownType = DEFAULT_PARSER_OPTIONS.unknownType, enumSuffix = DEFAULT_PARSER_OPTIONS.enumSuffix, emptySchemaType = unknownType || DEFAULT_PARSER_OPTIONS.emptySchemaType } = options;
|
|
2314
|
+
const parserOptions = {
|
|
2315
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
2316
|
+
dateType,
|
|
2317
|
+
integerType,
|
|
2318
|
+
unknownType,
|
|
2319
|
+
emptySchemaType,
|
|
2320
|
+
enumSuffix
|
|
2321
|
+
};
|
|
1923
2322
|
let nameMapping = /* @__PURE__ */ new Map();
|
|
1924
|
-
let parsedDocument;
|
|
1925
|
-
|
|
2323
|
+
let parsedDocument = null;
|
|
2324
|
+
const ensureDocument = once(async (source) => {
|
|
2325
|
+
const fresh = await parseFromConfig(source);
|
|
2326
|
+
if (validate) await validateDocument(fresh);
|
|
2327
|
+
parsedDocument = fresh;
|
|
2328
|
+
return fresh;
|
|
2329
|
+
});
|
|
2330
|
+
const ensureSchemas = once(async (document) => {
|
|
2331
|
+
const result = getSchemas(document, { contentType });
|
|
2332
|
+
nameMapping = result.nameMapping;
|
|
2333
|
+
return result.schemas;
|
|
2334
|
+
});
|
|
2335
|
+
const ensureBaseOas = once((document) => new oas.default(document));
|
|
2336
|
+
const ensureSchemaParser = once((document) => createSchemaParser({
|
|
2337
|
+
document,
|
|
2338
|
+
contentType
|
|
2339
|
+
}));
|
|
2340
|
+
const ensurePreScan = once((schemas, parseSchema, parseOperation, baseOas) => preScan({
|
|
2341
|
+
schemas,
|
|
2342
|
+
parseSchema,
|
|
2343
|
+
parseOperation,
|
|
2344
|
+
baseOas,
|
|
2345
|
+
parserOptions,
|
|
2346
|
+
discriminator,
|
|
2347
|
+
dedupe
|
|
2348
|
+
}));
|
|
2349
|
+
async function createStream(source) {
|
|
2350
|
+
const document = await ensureDocument(source);
|
|
2351
|
+
const schemas = await ensureSchemas(document);
|
|
2352
|
+
const { parseSchema, parseOperation } = ensureSchemaParser(document);
|
|
2353
|
+
const baseOas = ensureBaseOas(document);
|
|
2354
|
+
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(schemas, parseSchema, parseOperation, baseOas);
|
|
2355
|
+
return createInputStream({
|
|
2356
|
+
schemas,
|
|
2357
|
+
parseSchema,
|
|
2358
|
+
parseOperation,
|
|
2359
|
+
baseOas,
|
|
2360
|
+
parserOptions,
|
|
2361
|
+
refAliasMap,
|
|
2362
|
+
discriminatorChildMap,
|
|
2363
|
+
dedupePlan,
|
|
2364
|
+
meta: {
|
|
2365
|
+
title: document.info?.title,
|
|
2366
|
+
description: document.info?.description,
|
|
2367
|
+
version: document.info?.version,
|
|
2368
|
+
baseURL: resolveBaseUrl({
|
|
2369
|
+
document,
|
|
2370
|
+
serverIndex,
|
|
2371
|
+
serverVariables
|
|
2372
|
+
}),
|
|
2373
|
+
circularNames,
|
|
2374
|
+
enumNames
|
|
2375
|
+
}
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
1926
2378
|
return {
|
|
1927
2379
|
name: "oas",
|
|
1928
2380
|
get options() {
|
|
@@ -1932,6 +2384,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1932
2384
|
serverIndex,
|
|
1933
2385
|
serverVariables,
|
|
1934
2386
|
discriminator,
|
|
2387
|
+
dedupe,
|
|
1935
2388
|
dateType,
|
|
1936
2389
|
integerType,
|
|
1937
2390
|
unknownType,
|
|
@@ -1943,8 +2396,8 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1943
2396
|
get document() {
|
|
1944
2397
|
return parsedDocument;
|
|
1945
2398
|
},
|
|
1946
|
-
|
|
1947
|
-
|
|
2399
|
+
async validate(input, options) {
|
|
2400
|
+
await validateDocument(await parseDocument(input), options);
|
|
1948
2401
|
},
|
|
1949
2402
|
getImports(node, resolve) {
|
|
1950
2403
|
return _kubb_core.ast.collectImports({
|
|
@@ -1952,7 +2405,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1952
2405
|
nameMapping,
|
|
1953
2406
|
resolve: (schemaName) => {
|
|
1954
2407
|
const result = resolve(schemaName);
|
|
1955
|
-
if (!result) return;
|
|
2408
|
+
if (!result) return null;
|
|
1956
2409
|
return _kubb_core.ast.createImport({
|
|
1957
2410
|
name: [result.name],
|
|
1958
2411
|
path: result.path
|
|
@@ -1961,32 +2414,20 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1961
2414
|
});
|
|
1962
2415
|
},
|
|
1963
2416
|
async parse(source) {
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
});
|
|
1976
|
-
const node = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
|
|
1977
|
-
nameMapping = parsedNameMapping;
|
|
1978
|
-
parsedDocument = document;
|
|
1979
|
-
inputNode = _kubb_core.ast.createInput({
|
|
1980
|
-
...node,
|
|
1981
|
-
meta: {
|
|
1982
|
-
title: document.info?.title,
|
|
1983
|
-
description: document.info?.description,
|
|
1984
|
-
version: document.info?.version,
|
|
1985
|
-
baseURL
|
|
1986
|
-
}
|
|
2417
|
+
const streamNode = await createStream(source);
|
|
2418
|
+
const collect = async (iter) => {
|
|
2419
|
+
const out = [];
|
|
2420
|
+
for await (const item of iter) out.push(item);
|
|
2421
|
+
return out;
|
|
2422
|
+
};
|
|
2423
|
+
const [schemas, operations] = await Promise.all([collect(streamNode.schemas), collect(streamNode.operations)]);
|
|
2424
|
+
return _kubb_core.ast.createInput({
|
|
2425
|
+
schemas,
|
|
2426
|
+
operations,
|
|
2427
|
+
meta: streamNode.meta
|
|
1987
2428
|
});
|
|
1988
|
-
|
|
1989
|
-
|
|
2429
|
+
},
|
|
2430
|
+
stream: createStream
|
|
1990
2431
|
};
|
|
1991
2432
|
});
|
|
1992
2433
|
//#endregion
|
|
@@ -2006,8 +2447,5 @@ exports.HttpMethods = HttpMethods;
|
|
|
2006
2447
|
exports.adapterOas = adapterOas;
|
|
2007
2448
|
exports.adapterOasName = adapterOasName;
|
|
2008
2449
|
exports.mergeDocuments = mergeDocuments;
|
|
2009
|
-
exports.parseDocument = parseDocument;
|
|
2010
|
-
exports.parseFromConfig = parseFromConfig;
|
|
2011
|
-
exports.validateDocument = validateDocument;
|
|
2012
2450
|
|
|
2013
2451
|
//# sourceMappingURL=index.cjs.map
|