@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.js
CHANGED
|
@@ -1,213 +1,12 @@
|
|
|
1
1
|
import "./chunk--u3MIqq1.js";
|
|
2
|
-
import { ast, createAdapter } from "@kubb/core";
|
|
3
2
|
import path from "node:path";
|
|
3
|
+
import { ast, createAdapter } from "@kubb/core";
|
|
4
|
+
import BaseOas from "oas";
|
|
4
5
|
import { bundle, loadConfig } from "@redocly/openapi-core";
|
|
5
6
|
import OASNormalize from "oas-normalize";
|
|
6
7
|
import swagger2openapi from "swagger2openapi";
|
|
7
|
-
import BaseOas from "oas";
|
|
8
8
|
import { isRef } from "oas/types";
|
|
9
9
|
import { matchesMimeType } from "oas/utils";
|
|
10
|
-
//#region src/constants.ts
|
|
11
|
-
/**
|
|
12
|
-
* Default parser options applied when no explicit options are provided.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
17
|
-
*
|
|
18
|
-
* const parser = createOasParser(oas)
|
|
19
|
-
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
const DEFAULT_PARSER_OPTIONS = {
|
|
23
|
-
dateType: "string",
|
|
24
|
-
integerType: "bigint",
|
|
25
|
-
unknownType: "any",
|
|
26
|
-
emptySchemaType: "any",
|
|
27
|
-
enumSuffix: "enum"
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
|
|
31
|
-
*
|
|
32
|
-
* Used when building or parsing `$ref` strings.
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* ```ts
|
|
36
|
-
* `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
40
|
-
/**
|
|
41
|
-
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
42
|
-
*/
|
|
43
|
-
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
44
|
-
/**
|
|
45
|
-
* Fallback `info.title` placed in the stub document when merging multiple API files.
|
|
46
|
-
*/
|
|
47
|
-
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
48
|
-
/**
|
|
49
|
-
* Fallback `info.version` placed in the stub document when merging multiple API files.
|
|
50
|
-
*/
|
|
51
|
-
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
52
|
-
/**
|
|
53
|
-
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
54
|
-
*
|
|
55
|
-
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
56
|
-
* intersection member rather than being merged into the parent.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
61
|
-
*
|
|
62
|
-
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
63
|
-
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
const structuralKeys = new Set([
|
|
67
|
-
"properties",
|
|
68
|
-
"items",
|
|
69
|
-
"additionalProperties",
|
|
70
|
-
"oneOf",
|
|
71
|
-
"anyOf",
|
|
72
|
-
"allOf",
|
|
73
|
-
"not"
|
|
74
|
-
]);
|
|
75
|
-
/**
|
|
76
|
-
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
77
|
-
*
|
|
78
|
-
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
79
|
-
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
80
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
|
|
81
|
-
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```ts
|
|
85
|
-
* import { formatMap } from '@kubb/adapter-oas'
|
|
86
|
-
*
|
|
87
|
-
* formatMap['uuid'] // 'uuid'
|
|
88
|
-
* formatMap['binary'] // 'blob'
|
|
89
|
-
* formatMap['float'] // 'number'
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
|
-
const formatMap = {
|
|
93
|
-
uuid: "uuid",
|
|
94
|
-
email: "email",
|
|
95
|
-
"idn-email": "email",
|
|
96
|
-
uri: "url",
|
|
97
|
-
"uri-reference": "url",
|
|
98
|
-
url: "url",
|
|
99
|
-
ipv4: "ipv4",
|
|
100
|
-
ipv6: "ipv6",
|
|
101
|
-
hostname: "url",
|
|
102
|
-
"idn-hostname": "url",
|
|
103
|
-
binary: "blob",
|
|
104
|
-
byte: "blob",
|
|
105
|
-
int32: "integer",
|
|
106
|
-
float: "number",
|
|
107
|
-
double: "number"
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```ts
|
|
114
|
-
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
115
|
-
*
|
|
116
|
-
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
120
|
-
/**
|
|
121
|
-
* Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
|
|
122
|
-
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
123
|
-
*/
|
|
124
|
-
const typeOptionMap = new Map([
|
|
125
|
-
["any", ast.schemaTypes.any],
|
|
126
|
-
["unknown", ast.schemaTypes.unknown],
|
|
127
|
-
["void", ast.schemaTypes.void]
|
|
128
|
-
]);
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region src/discriminator.ts
|
|
131
|
-
/**
|
|
132
|
-
* Injects discriminator enum values into child schemas so they know which value identifies them.
|
|
133
|
-
*
|
|
134
|
-
* Finds every union schema in `input.schemas` that has a `discriminatorPropertyName`, collects the
|
|
135
|
-
* enum value each union member is mapped to, then adds (or replaces) that property on the matching
|
|
136
|
-
* child object schema.
|
|
137
|
-
*
|
|
138
|
-
* Returns a new `InputNode` — the original is never mutated.
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* ```ts
|
|
142
|
-
* const { root } = parseOas(document, options)
|
|
143
|
-
* const next = applyDiscriminatorInheritance(root)
|
|
144
|
-
* ```
|
|
145
|
-
*/
|
|
146
|
-
function applyDiscriminatorInheritance(root) {
|
|
147
|
-
const childMap = /* @__PURE__ */ new Map();
|
|
148
|
-
for (const schema of root.schemas) {
|
|
149
|
-
let unionNode = ast.narrowSchema(schema, "union");
|
|
150
|
-
if (!unionNode) {
|
|
151
|
-
const intersectionMembers = ast.narrowSchema(schema, "intersection")?.members;
|
|
152
|
-
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
153
|
-
const u = ast.narrowSchema(m, "union");
|
|
154
|
-
if (u) {
|
|
155
|
-
unionNode = u;
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
161
|
-
const { discriminatorPropertyName, members } = unionNode;
|
|
162
|
-
for (const member of members) {
|
|
163
|
-
const intersectionNode = ast.narrowSchema(member, "intersection");
|
|
164
|
-
if (!intersectionNode?.members) continue;
|
|
165
|
-
let refNode;
|
|
166
|
-
let objNode;
|
|
167
|
-
for (const m of intersectionNode.members) {
|
|
168
|
-
refNode ??= ast.narrowSchema(m, "ref");
|
|
169
|
-
objNode ??= ast.narrowSchema(m, "object");
|
|
170
|
-
}
|
|
171
|
-
if (!refNode?.name || !objNode) continue;
|
|
172
|
-
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
173
|
-
const enumNode = prop ? ast.narrowSchema(prop.schema, "enum") : void 0;
|
|
174
|
-
if (!enumNode?.enumValues?.length) continue;
|
|
175
|
-
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
176
|
-
if (!enumValues.length) continue;
|
|
177
|
-
const existing = childMap.get(refNode.name);
|
|
178
|
-
if (existing) existing.enumValues.push(...enumValues);
|
|
179
|
-
else childMap.set(refNode.name, {
|
|
180
|
-
propertyName: discriminatorPropertyName,
|
|
181
|
-
enumValues: [...enumValues]
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (childMap.size === 0) return root;
|
|
186
|
-
return ast.transform(root, { schema(node, { parent }) {
|
|
187
|
-
if (parent?.kind !== "Input" || !node.name) return;
|
|
188
|
-
const entry = childMap.get(node.name);
|
|
189
|
-
if (!entry) return;
|
|
190
|
-
const objectNode = ast.narrowSchema(node, "object");
|
|
191
|
-
if (!objectNode) return;
|
|
192
|
-
const { propertyName, enumValues } = entry;
|
|
193
|
-
const enumSchema = ast.createSchema({
|
|
194
|
-
type: "enum",
|
|
195
|
-
enumValues
|
|
196
|
-
});
|
|
197
|
-
const newProp = ast.createProperty({
|
|
198
|
-
name: propertyName,
|
|
199
|
-
required: true,
|
|
200
|
-
schema: enumSchema
|
|
201
|
-
});
|
|
202
|
-
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
203
|
-
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
204
|
-
return {
|
|
205
|
-
...objectNode,
|
|
206
|
-
properties: newProperties
|
|
207
|
-
};
|
|
208
|
-
} });
|
|
209
|
-
}
|
|
210
|
-
//#endregion
|
|
211
10
|
//#region ../../internals/utils/src/casing.ts
|
|
212
11
|
/**
|
|
213
12
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -306,6 +105,30 @@ function mergeDeep(target, source) {
|
|
|
306
105
|
return result;
|
|
307
106
|
}
|
|
308
107
|
//#endregion
|
|
108
|
+
//#region ../../internals/utils/src/promise.ts
|
|
109
|
+
/**
|
|
110
|
+
* Returns a wrapper that caches the result of the first invocation and replays
|
|
111
|
+
* it for every subsequent call, ignoring later arguments.
|
|
112
|
+
*
|
|
113
|
+
* Works for sync and async factories — for async, the cached value is the
|
|
114
|
+
* promise itself, so concurrent callers share one in-flight execution and
|
|
115
|
+
* cannot race each other.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* const loadDocument = once(async (path: string) => parse(await readFile(path)))
|
|
120
|
+
* const a = loadDocument('./a.yaml') // parses
|
|
121
|
+
* const b = loadDocument('./b.yaml') // returns the cached promise from the first call
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
function once(factory) {
|
|
125
|
+
let cache;
|
|
126
|
+
return (...args) => {
|
|
127
|
+
if (!cache) cache = { value: factory(...args) };
|
|
128
|
+
return cache.value;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
309
132
|
//#region ../../internals/utils/src/reserved.ts
|
|
310
133
|
/**
|
|
311
134
|
* JavaScript and Java reserved words.
|
|
@@ -517,12 +340,13 @@ var URLPath = class {
|
|
|
517
340
|
* @example
|
|
518
341
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
519
342
|
*/
|
|
520
|
-
toTemplateString({ prefix
|
|
521
|
-
|
|
343
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
344
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
522
345
|
if (i % 2 === 0) return part;
|
|
523
346
|
const param = this.#transformParam(part);
|
|
524
347
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
525
|
-
}).join("")
|
|
348
|
+
}).join("");
|
|
349
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
526
350
|
}
|
|
527
351
|
/**
|
|
528
352
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -555,6 +379,126 @@ var URLPath = class {
|
|
|
555
379
|
}
|
|
556
380
|
};
|
|
557
381
|
//#endregion
|
|
382
|
+
//#region src/constants.ts
|
|
383
|
+
/**
|
|
384
|
+
* Default parser options applied when no explicit options are provided.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* import { DEFAULT_PARSER_OPTIONS } from '@kubb/adapter-oas'
|
|
389
|
+
*
|
|
390
|
+
* const parser = createOasParser(oas)
|
|
391
|
+
* const root = parser.parse({ ...DEFAULT_PARSER_OPTIONS, dateType: 'date' })
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
const DEFAULT_PARSER_OPTIONS = {
|
|
395
|
+
dateType: "string",
|
|
396
|
+
integerType: "bigint",
|
|
397
|
+
unknownType: "any",
|
|
398
|
+
emptySchemaType: "any",
|
|
399
|
+
enumSuffix: "enum"
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* JSON-Pointer prefix for schemas declared under `components.schemas` in an OpenAPI document.
|
|
403
|
+
*
|
|
404
|
+
* Used when building or parsing `$ref` strings.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* `${SCHEMA_REF_PREFIX}Pet` // '#/components/schemas/Pet'
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
const SCHEMA_REF_PREFIX = "#/components/schemas/";
|
|
412
|
+
/**
|
|
413
|
+
* OpenAPI version string written into the stub document created during multi-spec merges.
|
|
414
|
+
*/
|
|
415
|
+
const MERGE_OPENAPI_VERSION = "3.0.0";
|
|
416
|
+
/**
|
|
417
|
+
* Fallback `info.title` placed in the stub document when merging multiple API files.
|
|
418
|
+
*/
|
|
419
|
+
const MERGE_DEFAULT_TITLE = "Merged API";
|
|
420
|
+
/**
|
|
421
|
+
* Fallback `info.version` placed in the stub document when merging multiple API files.
|
|
422
|
+
*/
|
|
423
|
+
const MERGE_DEFAULT_VERSION = "1.0.0";
|
|
424
|
+
/**
|
|
425
|
+
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
426
|
+
*
|
|
427
|
+
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
428
|
+
* intersection member rather than being merged into the parent.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```ts
|
|
432
|
+
* import { structuralKeys } from '@kubb/adapter-oas'
|
|
433
|
+
*
|
|
434
|
+
* const isStructural = Object.keys(fragment).some((key) => structuralKeys.has(key))
|
|
435
|
+
* // true when fragment has e.g. 'properties' or 'oneOf'
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
const structuralKeys = new Set([
|
|
439
|
+
"properties",
|
|
440
|
+
"items",
|
|
441
|
+
"additionalProperties",
|
|
442
|
+
"oneOf",
|
|
443
|
+
"anyOf",
|
|
444
|
+
"allOf",
|
|
445
|
+
"not"
|
|
446
|
+
]);
|
|
447
|
+
/**
|
|
448
|
+
* Static map from OAS `format` strings to Kubb `SchemaType` values.
|
|
449
|
+
*
|
|
450
|
+
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
451
|
+
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
452
|
+
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types; `hostname` and
|
|
453
|
+
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```ts
|
|
457
|
+
* import { formatMap } from '@kubb/adapter-oas'
|
|
458
|
+
*
|
|
459
|
+
* formatMap['uuid'] // 'uuid'
|
|
460
|
+
* formatMap['binary'] // 'blob'
|
|
461
|
+
* formatMap['float'] // 'number'
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
const formatMap = {
|
|
465
|
+
uuid: "uuid",
|
|
466
|
+
email: "email",
|
|
467
|
+
"idn-email": "email",
|
|
468
|
+
uri: "url",
|
|
469
|
+
"uri-reference": "url",
|
|
470
|
+
url: "url",
|
|
471
|
+
ipv4: "ipv4",
|
|
472
|
+
ipv6: "ipv6",
|
|
473
|
+
hostname: "url",
|
|
474
|
+
"idn-hostname": "url",
|
|
475
|
+
binary: "blob",
|
|
476
|
+
byte: "blob",
|
|
477
|
+
int32: "integer",
|
|
478
|
+
float: "number",
|
|
479
|
+
double: "number"
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* Vendor extension keys that attach human-readable labels to enum values, checked in priority order.
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```ts
|
|
486
|
+
* import { enumExtensionKeys } from '@kubb/adapter-oas'
|
|
487
|
+
*
|
|
488
|
+
* const key = enumExtensionKeys.find((k) => k in schema) // 'x-enumNames' | 'x-enum-varnames' | undefined
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
492
|
+
/**
|
|
493
|
+
* Maps `'any' | 'unknown' | 'void'` option strings to their `ScalarSchemaType` constant.
|
|
494
|
+
* Replaces a plain object lookup with a `Map` for explicit key membership testing via `.has()`.
|
|
495
|
+
*/
|
|
496
|
+
const typeOptionMap = new Map([
|
|
497
|
+
["any", ast.schemaTypes.any],
|
|
498
|
+
["unknown", ast.schemaTypes.unknown],
|
|
499
|
+
["void", ast.schemaTypes.void]
|
|
500
|
+
]);
|
|
501
|
+
//#endregion
|
|
558
502
|
//#region src/guards.ts
|
|
559
503
|
/**
|
|
560
504
|
* Returns `true` when `doc` is a Swagger 2.0 document (no `openapi` key).
|
|
@@ -660,11 +604,10 @@ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true }
|
|
|
660
604
|
* ```
|
|
661
605
|
*/
|
|
662
606
|
async function mergeDocuments(pathOrApi) {
|
|
663
|
-
const documents =
|
|
664
|
-
for (const p of pathOrApi) documents.push(await parseDocument(p, {
|
|
607
|
+
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
|
|
665
608
|
enablePaths: false,
|
|
666
609
|
canBundle: false
|
|
667
|
-
}));
|
|
610
|
+
})));
|
|
668
611
|
if (documents.length === 0) throw new Error("No OAS documents provided for merging.");
|
|
669
612
|
const seed = {
|
|
670
613
|
openapi: MERGE_OPENAPI_VERSION,
|
|
@@ -720,6 +663,7 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
720
663
|
}
|
|
721
664
|
//#endregion
|
|
722
665
|
//#region src/refs.ts
|
|
666
|
+
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
723
667
|
/**
|
|
724
668
|
* Resolves a local JSON pointer reference from a document.
|
|
725
669
|
*
|
|
@@ -735,10 +679,17 @@ function resolveRef(document, $ref) {
|
|
|
735
679
|
const origRef = $ref;
|
|
736
680
|
$ref = $ref.trim();
|
|
737
681
|
if ($ref === "") return null;
|
|
738
|
-
if (
|
|
739
|
-
|
|
682
|
+
if (!$ref.startsWith("#")) return null;
|
|
683
|
+
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
684
|
+
let docCache = _refCache.get(document);
|
|
685
|
+
if (!docCache) {
|
|
686
|
+
docCache = /* @__PURE__ */ new Map();
|
|
687
|
+
_refCache.set(document, docCache);
|
|
688
|
+
}
|
|
689
|
+
if (docCache.has($ref)) return docCache.get($ref);
|
|
740
690
|
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
741
691
|
if (!current) throw new Error(`Could not find a definition for ${origRef}.`);
|
|
692
|
+
docCache.set($ref, current);
|
|
742
693
|
return current;
|
|
743
694
|
}
|
|
744
695
|
/**
|
|
@@ -762,6 +713,35 @@ function dereferenceWithRef(document, schema) {
|
|
|
762
713
|
return schema;
|
|
763
714
|
}
|
|
764
715
|
//#endregion
|
|
716
|
+
//#region src/dialect.ts
|
|
717
|
+
/**
|
|
718
|
+
* The OpenAPI / Swagger dialect — the default used by `@kubb/adapter-oas`.
|
|
719
|
+
*
|
|
720
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
721
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
722
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
723
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect — `type: ['null', …]`
|
|
724
|
+
* nullability, no discriminator object, binary via `contentEncoding` — and reuses
|
|
725
|
+
* the rest unchanged.
|
|
726
|
+
*
|
|
727
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
728
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```ts
|
|
732
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
733
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
736
|
+
const oasDialect = ast.defineSchemaDialect({
|
|
737
|
+
name: "oas",
|
|
738
|
+
isNullable,
|
|
739
|
+
isReference,
|
|
740
|
+
isDiscriminator,
|
|
741
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
742
|
+
resolveRef
|
|
743
|
+
});
|
|
744
|
+
//#endregion
|
|
765
745
|
//#region src/resolvers.ts
|
|
766
746
|
/**
|
|
767
747
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -805,12 +785,6 @@ function getPrimitiveType(type) {
|
|
|
805
785
|
return "string";
|
|
806
786
|
}
|
|
807
787
|
/**
|
|
808
|
-
* Narrows a content-type string to the `MediaType` union Kubb recognizes, or returns `null`.
|
|
809
|
-
*/
|
|
810
|
-
function getMediaType(contentType) {
|
|
811
|
-
return Object.values(ast.mediaTypes).includes(contentType) ? contentType : null;
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
788
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
815
789
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
816
790
|
* `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
|
|
@@ -953,21 +927,22 @@ function extractSchemaFromContent(content, preferredContentType) {
|
|
|
953
927
|
/**
|
|
954
928
|
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
955
929
|
*/
|
|
956
|
-
function collectRefs(schema
|
|
930
|
+
function* collectRefs(schema) {
|
|
957
931
|
if (Array.isArray(schema)) {
|
|
958
|
-
for (const item of schema) collectRefs(item
|
|
959
|
-
return
|
|
932
|
+
for (const item of schema) yield* collectRefs(item);
|
|
933
|
+
return;
|
|
960
934
|
}
|
|
961
935
|
if (schema && typeof schema === "object") for (const key in schema) {
|
|
962
936
|
const value = schema[key];
|
|
963
|
-
if (key === "$ref" && typeof value === "string") {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
937
|
+
if (!(key === "$ref" && typeof value === "string")) {
|
|
938
|
+
yield* collectRefs(value);
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
if (value.startsWith("#/components/schemas/")) {
|
|
942
|
+
const name = value.slice(21);
|
|
943
|
+
if (name) yield name;
|
|
944
|
+
}
|
|
969
945
|
}
|
|
970
|
-
return refs;
|
|
971
946
|
}
|
|
972
947
|
/**
|
|
973
948
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
@@ -983,7 +958,7 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
|
983
958
|
*/
|
|
984
959
|
function sortSchemas(schemas) {
|
|
985
960
|
const deps = /* @__PURE__ */ new Map();
|
|
986
|
-
for (const [name, schema] of Object.entries(schemas)) deps.set(name,
|
|
961
|
+
for (const [name, schema] of Object.entries(schemas)) deps.set(name, [...new Set(collectRefs(schema))]);
|
|
987
962
|
const sorted = [];
|
|
988
963
|
const visited = /* @__PURE__ */ new Set();
|
|
989
964
|
function visit(name, stack) {
|
|
@@ -1118,7 +1093,8 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
|
1118
1093
|
readOnly: schema.readOnly,
|
|
1119
1094
|
writeOnly: schema.writeOnly,
|
|
1120
1095
|
default: defaultValue,
|
|
1121
|
-
example: schema.example
|
|
1096
|
+
example: schema.example,
|
|
1097
|
+
format: schema.format
|
|
1122
1098
|
};
|
|
1123
1099
|
}
|
|
1124
1100
|
/**
|
|
@@ -1140,6 +1116,31 @@ function getRequestBodyContentTypes(document, operation) {
|
|
|
1140
1116
|
if (!body) return [];
|
|
1141
1117
|
return body.content ? Object.keys(body.content) : [];
|
|
1142
1118
|
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Returns all response content type keys for an operation at a given status code.
|
|
1121
|
+
*
|
|
1122
|
+
* Response `$ref`s are resolved in-place first — the same mutation `getResponseSchema` performs —
|
|
1123
|
+
* so the returned list reflects the available content types even for referenced responses.
|
|
1124
|
+
*
|
|
1125
|
+
* @example
|
|
1126
|
+
* ```ts
|
|
1127
|
+
* getResponseBodyContentTypes(document, operation, 200)
|
|
1128
|
+
* // ['application/json', 'application/xml']
|
|
1129
|
+
* ```
|
|
1130
|
+
*/
|
|
1131
|
+
function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
1132
|
+
if (operation.schema.responses) {
|
|
1133
|
+
const responses = operation.schema.responses;
|
|
1134
|
+
for (const key in responses) {
|
|
1135
|
+
const schema = responses[key];
|
|
1136
|
+
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1140
|
+
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
1141
|
+
const body = responseObj;
|
|
1142
|
+
return body.content ? Object.keys(body.content) : [];
|
|
1143
|
+
}
|
|
1143
1144
|
//#endregion
|
|
1144
1145
|
//#region src/parser.ts
|
|
1145
1146
|
/**
|
|
@@ -1168,9 +1169,9 @@ function normalizeArrayEnum(schema) {
|
|
|
1168
1169
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1169
1170
|
* made possible by hoisting of function declarations.
|
|
1170
1171
|
*
|
|
1171
|
-
* @
|
|
1172
|
+
* @internal
|
|
1172
1173
|
*/
|
|
1173
|
-
function createSchemaParser(ctx) {
|
|
1174
|
+
function createSchemaParser(ctx, dialect = oasDialect) {
|
|
1174
1175
|
const document = ctx.document;
|
|
1175
1176
|
/**
|
|
1176
1177
|
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
@@ -1178,6 +1179,19 @@ function createSchemaParser(ctx) {
|
|
|
1178
1179
|
*/
|
|
1179
1180
|
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1180
1181
|
/**
|
|
1182
|
+
* Cache of already-resolved `$ref` schemas within this parser instance.
|
|
1183
|
+
*
|
|
1184
|
+
* Without this, the same referenced schema (e.g. `customer`) is fully re-expanded
|
|
1185
|
+
* every time it appears as a `$ref` in a different parent schema. In heavily
|
|
1186
|
+
* cross-referenced specs like Stripe (~1 400 schemas), this causes exponential
|
|
1187
|
+
* blowup — `customer` alone may be referenced from dozens of top-level schemas,
|
|
1188
|
+
* each triggering a fresh recursive expansion of its entire sub-tree.
|
|
1189
|
+
*
|
|
1190
|
+
* Memoizing by `$ref` path reduces the overall work from O(2^depth) to O(N)
|
|
1191
|
+
* where N is the number of unique schema names.
|
|
1192
|
+
*/
|
|
1193
|
+
const resolvedRefCache = /* @__PURE__ */ new Map();
|
|
1194
|
+
/**
|
|
1181
1195
|
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1182
1196
|
*
|
|
1183
1197
|
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
@@ -1186,16 +1200,22 @@ function createSchemaParser(ctx) {
|
|
|
1186
1200
|
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1187
1201
|
*/
|
|
1188
1202
|
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1189
|
-
let resolvedSchema;
|
|
1203
|
+
let resolvedSchema = null;
|
|
1190
1204
|
const refPath = schema.$ref;
|
|
1191
|
-
if (refPath && !resolvingRefs.has(refPath))
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1205
|
+
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1206
|
+
if (!resolvedRefCache.has(refPath)) {
|
|
1207
|
+
try {
|
|
1208
|
+
const referenced = dialect.resolveRef(document, refPath);
|
|
1209
|
+
if (referenced) {
|
|
1210
|
+
resolvingRefs.add(refPath);
|
|
1211
|
+
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1212
|
+
resolvingRefs.delete(refPath);
|
|
1213
|
+
}
|
|
1214
|
+
} catch {}
|
|
1215
|
+
resolvedRefCache.set(refPath, resolvedSchema);
|
|
1197
1216
|
}
|
|
1198
|
-
|
|
1217
|
+
resolvedSchema = resolvedRefCache.get(refPath) ?? null;
|
|
1218
|
+
}
|
|
1199
1219
|
return ast.createSchema({
|
|
1200
1220
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1201
1221
|
type: "ref",
|
|
@@ -1212,7 +1232,7 @@ function createSchemaParser(ctx) {
|
|
|
1212
1232
|
const [memberSchema] = schema.allOf;
|
|
1213
1233
|
const memberNode = parseSchema({
|
|
1214
1234
|
schema: memberSchema,
|
|
1215
|
-
name
|
|
1235
|
+
name
|
|
1216
1236
|
}, rawOptions);
|
|
1217
1237
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1218
1238
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
@@ -1228,18 +1248,19 @@ function createSchemaParser(ctx) {
|
|
|
1228
1248
|
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1229
1249
|
default: mergedDefault,
|
|
1230
1250
|
example: schema.example ?? memberNode.example,
|
|
1231
|
-
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
|
|
1251
|
+
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1252
|
+
format: schema.format ?? memberNode.format
|
|
1232
1253
|
});
|
|
1233
1254
|
}
|
|
1234
1255
|
const filteredDiscriminantValues = [];
|
|
1235
1256
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1236
|
-
if (!isReference(item) || !name) return true;
|
|
1237
|
-
const deref = resolveRef(document, item.$ref);
|
|
1238
|
-
if (!deref || !isDiscriminator(deref)) return true;
|
|
1257
|
+
if (!dialect.isReference(item) || !name) return true;
|
|
1258
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1259
|
+
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1239
1260
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1240
1261
|
if (!parentUnion) return true;
|
|
1241
1262
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1242
|
-
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1263
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1243
1264
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1244
1265
|
if (inOneOf || inMapping) {
|
|
1245
1266
|
const discriminatorValue = ast.findDiscriminator(deref.discriminator.mapping, childRef);
|
|
@@ -1250,22 +1271,28 @@ function createSchemaParser(ctx) {
|
|
|
1250
1271
|
return false;
|
|
1251
1272
|
}
|
|
1252
1273
|
return true;
|
|
1253
|
-
}).map((s) => parseSchema({
|
|
1274
|
+
}).map((s) => parseSchema({
|
|
1275
|
+
schema: s,
|
|
1276
|
+
name
|
|
1277
|
+
}, rawOptions));
|
|
1254
1278
|
const syntheticStart = allOfMembers.length;
|
|
1255
1279
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1256
1280
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1257
1281
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1258
1282
|
if (missingRequired.length) {
|
|
1259
1283
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1260
|
-
if (!isReference(item)) return [item];
|
|
1261
|
-
const deref = resolveRef(document, item.$ref);
|
|
1262
|
-
return deref && !isReference(deref) ? [deref] : [];
|
|
1284
|
+
if (!dialect.isReference(item)) return [item];
|
|
1285
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1286
|
+
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1263
1287
|
});
|
|
1264
1288
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1265
|
-
allOfMembers.push(parseSchema({
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1289
|
+
allOfMembers.push(parseSchema({
|
|
1290
|
+
schema: {
|
|
1291
|
+
properties: { [key]: resolved.properties[key] },
|
|
1292
|
+
required: [key]
|
|
1293
|
+
},
|
|
1294
|
+
name
|
|
1295
|
+
}, rawOptions));
|
|
1269
1296
|
break;
|
|
1270
1297
|
}
|
|
1271
1298
|
}
|
|
@@ -1280,7 +1307,7 @@ function createSchemaParser(ctx) {
|
|
|
1280
1307
|
}));
|
|
1281
1308
|
return ast.createSchema({
|
|
1282
1309
|
type: "intersection",
|
|
1283
|
-
members: [...ast.
|
|
1310
|
+
members: [...ast.mergeAdjacentObjectsLazy(allOfMembers.slice(0, syntheticStart)), ...ast.mergeAdjacentObjectsLazy(allOfMembers.slice(syntheticStart))],
|
|
1284
1311
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1285
1312
|
});
|
|
1286
1313
|
}
|
|
@@ -1301,10 +1328,10 @@ function createSchemaParser(ctx) {
|
|
|
1301
1328
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1302
1329
|
const unionBase = {
|
|
1303
1330
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1304
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1331
|
+
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1305
1332
|
strategy
|
|
1306
1333
|
};
|
|
1307
|
-
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1334
|
+
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1308
1335
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1309
1336
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1310
1337
|
return parseSchema({
|
|
@@ -1314,9 +1341,12 @@ function createSchemaParser(ctx) {
|
|
|
1314
1341
|
})() : void 0;
|
|
1315
1342
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1316
1343
|
const members = unionMembers.map((s) => {
|
|
1317
|
-
const ref = isReference(s) ? s.$ref : void 0;
|
|
1344
|
+
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1318
1345
|
const discriminatorValue = ast.findDiscriminator(discriminator?.mapping, ref);
|
|
1319
|
-
const memberNode = parseSchema({
|
|
1346
|
+
const memberNode = parseSchema({
|
|
1347
|
+
schema: s,
|
|
1348
|
+
name
|
|
1349
|
+
}, rawOptions);
|
|
1320
1350
|
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1321
1351
|
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(ast.setDiscriminatorEnum({
|
|
1322
1352
|
node: sharedPropertiesNode,
|
|
@@ -1346,7 +1376,10 @@ function createSchemaParser(ctx) {
|
|
|
1346
1376
|
return ast.createSchema({
|
|
1347
1377
|
type: "union",
|
|
1348
1378
|
...unionBase,
|
|
1349
|
-
members: ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1379
|
+
members: ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1380
|
+
schema: s,
|
|
1381
|
+
name
|
|
1382
|
+
}, rawOptions)))
|
|
1350
1383
|
});
|
|
1351
1384
|
}
|
|
1352
1385
|
/**
|
|
@@ -1360,7 +1393,8 @@ function createSchemaParser(ctx) {
|
|
|
1360
1393
|
name,
|
|
1361
1394
|
title: schema.title,
|
|
1362
1395
|
description: schema.description,
|
|
1363
|
-
deprecated: schema.deprecated
|
|
1396
|
+
deprecated: schema.deprecated,
|
|
1397
|
+
format: schema.format
|
|
1364
1398
|
});
|
|
1365
1399
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1366
1400
|
return ast.createSchema({
|
|
@@ -1450,6 +1484,15 @@ function createSchemaParser(ctx) {
|
|
|
1450
1484
|
}, rawOptions);
|
|
1451
1485
|
const nullInEnum = schema.enum.includes(null);
|
|
1452
1486
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1487
|
+
if (nullInEnum && filteredValues.length === 0) return ast.createSchema({
|
|
1488
|
+
type: "null",
|
|
1489
|
+
primitive: "null",
|
|
1490
|
+
name,
|
|
1491
|
+
title: schema.title,
|
|
1492
|
+
description: schema.description,
|
|
1493
|
+
deprecated: schema.deprecated,
|
|
1494
|
+
format: schema.format
|
|
1495
|
+
});
|
|
1453
1496
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1454
1497
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1455
1498
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1464,7 +1507,8 @@ function createSchemaParser(ctx) {
|
|
|
1464
1507
|
readOnly: schema.readOnly,
|
|
1465
1508
|
writeOnly: schema.writeOnly,
|
|
1466
1509
|
default: enumDefault,
|
|
1467
|
-
example: schema.example
|
|
1510
|
+
example: schema.example,
|
|
1511
|
+
format: schema.format
|
|
1468
1512
|
};
|
|
1469
1513
|
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1470
1514
|
if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
@@ -1498,20 +1542,23 @@ function createSchemaParser(ctx) {
|
|
|
1498
1542
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1499
1543
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1500
1544
|
const resolvedPropSchema = propSchema;
|
|
1501
|
-
const propNullable = isNullable(resolvedPropSchema);
|
|
1545
|
+
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
1502
1546
|
const propNode = parseSchema({
|
|
1503
1547
|
schema: resolvedPropSchema,
|
|
1504
1548
|
name: ast.childName(name, propName)
|
|
1505
1549
|
}, rawOptions);
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1550
|
+
const schemaNode = (() => {
|
|
1551
|
+
const node = ast.setEnumName(propNode, name, propName, options.enumSuffix);
|
|
1552
|
+
const tupleNode = ast.narrowSchema(node, "tuple");
|
|
1553
|
+
if (tupleNode?.items) {
|
|
1554
|
+
const namedItems = tupleNode.items.map((item) => ast.setEnumName(item, name, propName, options.enumSuffix));
|
|
1555
|
+
if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
|
|
1556
|
+
...tupleNode,
|
|
1557
|
+
items: namedItems
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
return node;
|
|
1561
|
+
})();
|
|
1515
1562
|
return ast.createProperty({
|
|
1516
1563
|
name: propName,
|
|
1517
1564
|
schema: {
|
|
@@ -1522,11 +1569,12 @@ function createSchemaParser(ctx) {
|
|
|
1522
1569
|
});
|
|
1523
1570
|
}) : [];
|
|
1524
1571
|
const additionalProperties = schema.additionalProperties;
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1572
|
+
const additionalPropertiesNode = (() => {
|
|
1573
|
+
if (additionalProperties === true) return true;
|
|
1574
|
+
if (additionalProperties === false) return false;
|
|
1575
|
+
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1576
|
+
if (additionalProperties) return ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1577
|
+
})();
|
|
1530
1578
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1531
1579
|
const patternProperties = rawPatternProperties ? Object.fromEntries(Object.entries(rawPatternProperties).map(([pattern, patternSchema]) => [pattern, patternSchema === true || typeof patternSchema === "object" && Object.keys(patternSchema).length === 0 ? ast.createSchema({ type: typeOptionMap.get(options.unknownType) }) : parseSchema({ schema: patternSchema }, rawOptions)])) : void 0;
|
|
1532
1580
|
const objectNode = ast.createSchema({
|
|
@@ -1539,7 +1587,7 @@ function createSchemaParser(ctx) {
|
|
|
1539
1587
|
maxProperties: schema.maxProperties,
|
|
1540
1588
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1541
1589
|
});
|
|
1542
|
-
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1590
|
+
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1543
1591
|
const discPropName = schema.discriminator.propertyName;
|
|
1544
1592
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1545
1593
|
const enumName = name ? ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
@@ -1573,7 +1621,7 @@ function createSchemaParser(ctx) {
|
|
|
1573
1621
|
*/
|
|
1574
1622
|
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1575
1623
|
const rawItems = schema.items;
|
|
1576
|
-
const itemName = rawItems?.enum?.length && name ? ast.enumPropName(
|
|
1624
|
+
const itemName = rawItems?.enum?.length && name ? ast.enumPropName(null, name, options.enumSuffix) : name;
|
|
1577
1625
|
const items = rawItems ? [parseSchema({
|
|
1578
1626
|
schema: rawItems,
|
|
1579
1627
|
name: itemName
|
|
@@ -1637,15 +1685,148 @@ function createSchemaParser(ctx) {
|
|
|
1637
1685
|
title: schema.title,
|
|
1638
1686
|
description: schema.description,
|
|
1639
1687
|
deprecated: schema.deprecated,
|
|
1640
|
-
nullable
|
|
1688
|
+
nullable,
|
|
1689
|
+
format: schema.format
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1694
|
+
* into a `blob` node.
|
|
1695
|
+
*/
|
|
1696
|
+
function convertBlob({ schema, name, nullable, defaultValue }) {
|
|
1697
|
+
return ast.createSchema({
|
|
1698
|
+
type: "blob",
|
|
1699
|
+
primitive: "string",
|
|
1700
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1705
|
+
*
|
|
1706
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
|
|
1707
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1708
|
+
*/
|
|
1709
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1710
|
+
const types = schema.type;
|
|
1711
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1712
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1713
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1714
|
+
return 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)
|
|
1641
1724
|
});
|
|
1642
1725
|
}
|
|
1643
1726
|
/**
|
|
1727
|
+
* Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1728
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1729
|
+
* `type`. The first matching rule that produces a node wins; see {@link SchemaRule} for the
|
|
1730
|
+
* match/convert/fall-through contract.
|
|
1731
|
+
*/
|
|
1732
|
+
const schemaRules = [
|
|
1733
|
+
{
|
|
1734
|
+
name: "ref",
|
|
1735
|
+
match: ({ schema }) => dialect.isReference(schema),
|
|
1736
|
+
convert: convertRef
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
name: "allOf",
|
|
1740
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1741
|
+
convert: convertAllOf
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
name: "union",
|
|
1745
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1746
|
+
convert: convertUnion
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
name: "const",
|
|
1750
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1751
|
+
convert: convertConst
|
|
1752
|
+
},
|
|
1753
|
+
{
|
|
1754
|
+
name: "format",
|
|
1755
|
+
match: ({ schema }) => !!schema.format,
|
|
1756
|
+
convert: convertFormat
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
name: "blob",
|
|
1760
|
+
match: ({ schema }) => dialect.isBinary(schema),
|
|
1761
|
+
convert: convertBlob
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
name: "multi-type",
|
|
1765
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1766
|
+
convert: convertMultiType
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
name: "constrained-string",
|
|
1770
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1771
|
+
convert: convertString
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
name: "constrained-number",
|
|
1775
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1776
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
name: "enum",
|
|
1780
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1781
|
+
convert: convertEnum
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
name: "object",
|
|
1785
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1786
|
+
convert: convertObject
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
name: "tuple",
|
|
1790
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1791
|
+
convert: convertTuple
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
name: "array",
|
|
1795
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1796
|
+
convert: convertArray
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
name: "string",
|
|
1800
|
+
match: ({ type }) => type === "string",
|
|
1801
|
+
convert: convertString
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
name: "number",
|
|
1805
|
+
match: ({ type }) => type === "number",
|
|
1806
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
name: "integer",
|
|
1810
|
+
match: ({ type }) => type === "integer",
|
|
1811
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
name: "boolean",
|
|
1815
|
+
match: ({ type }) => type === "boolean",
|
|
1816
|
+
convert: convertBoolean
|
|
1817
|
+
},
|
|
1818
|
+
{
|
|
1819
|
+
name: "null",
|
|
1820
|
+
match: ({ type }) => type === "null",
|
|
1821
|
+
convert: convertNull
|
|
1822
|
+
}
|
|
1823
|
+
];
|
|
1824
|
+
/**
|
|
1644
1825
|
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1645
1826
|
*
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1827
|
+
* Builds the per-schema context, then runs it through the ordered {@link schemaRules} table
|
|
1828
|
+
* via {@link ast.dispatch}. When no rule produces a node, falls back to the configured
|
|
1829
|
+
* `emptySchemaType`.
|
|
1649
1830
|
*/
|
|
1650
1831
|
function parseSchema({ schema, name }, rawOptions) {
|
|
1651
1832
|
const options = {
|
|
@@ -1657,75 +1838,40 @@ function createSchemaParser(ctx) {
|
|
|
1657
1838
|
schema: flattenedSchema,
|
|
1658
1839
|
name
|
|
1659
1840
|
}, rawOptions);
|
|
1660
|
-
const nullable = isNullable(schema) || void 0;
|
|
1661
|
-
const
|
|
1662
|
-
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
1663
|
-
const ctx = {
|
|
1841
|
+
const nullable = dialect.isNullable(schema) || void 0;
|
|
1842
|
+
const schemaCtx = {
|
|
1664
1843
|
schema,
|
|
1665
1844
|
name,
|
|
1666
1845
|
nullable,
|
|
1667
|
-
defaultValue,
|
|
1668
|
-
type,
|
|
1846
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1847
|
+
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1669
1848
|
rawOptions,
|
|
1670
1849
|
options
|
|
1671
1850
|
};
|
|
1672
|
-
|
|
1673
|
-
if (
|
|
1674
|
-
if ([...schema.oneOf ?? [], ...schema.anyOf ?? []].length) return convertUnion(ctx);
|
|
1675
|
-
if ("const" in schema && schema.const !== void 0) return convertConst(ctx);
|
|
1676
|
-
if (schema.format) {
|
|
1677
|
-
const formatResult = convertFormat(ctx);
|
|
1678
|
-
if (formatResult) return formatResult;
|
|
1679
|
-
}
|
|
1680
|
-
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return ast.createSchema({
|
|
1681
|
-
type: "blob",
|
|
1682
|
-
primitive: "string",
|
|
1683
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1684
|
-
});
|
|
1685
|
-
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1686
|
-
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1687
|
-
const arrayNullable = schema.type.includes("null") || nullable || void 0;
|
|
1688
|
-
if (nonNullTypes.length > 1) return ast.createSchema({
|
|
1689
|
-
type: "union",
|
|
1690
|
-
members: nonNullTypes.map((t) => parseSchema({
|
|
1691
|
-
schema: {
|
|
1692
|
-
...schema,
|
|
1693
|
-
type: t
|
|
1694
|
-
},
|
|
1695
|
-
name
|
|
1696
|
-
}, rawOptions)),
|
|
1697
|
-
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1698
|
-
});
|
|
1699
|
-
}
|
|
1700
|
-
if (!type) {
|
|
1701
|
-
if (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0) return convertString(ctx);
|
|
1702
|
-
if (schema.minimum !== void 0 || schema.maximum !== void 0) return convertNumeric(ctx, "number");
|
|
1703
|
-
}
|
|
1704
|
-
if (schema.enum?.length) return convertEnum(ctx);
|
|
1705
|
-
if (type === "object" || schema.properties || schema.additionalProperties || "patternProperties" in schema) return convertObject(ctx);
|
|
1706
|
-
if ("prefixItems" in schema) return convertTuple(ctx);
|
|
1707
|
-
if (type === "array" || "items" in schema) return convertArray(ctx);
|
|
1708
|
-
if (type === "string") return convertString(ctx);
|
|
1709
|
-
if (type === "number") return convertNumeric(ctx, "number");
|
|
1710
|
-
if (type === "integer") return convertNumeric(ctx, "integer");
|
|
1711
|
-
if (type === "boolean") return convertBoolean(ctx);
|
|
1712
|
-
if (type === "null") return convertNull(ctx);
|
|
1851
|
+
const node = ast.dispatch(schemaRules, schemaCtx);
|
|
1852
|
+
if (node) return node;
|
|
1713
1853
|
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1714
1854
|
return ast.createSchema({
|
|
1715
1855
|
type: emptyType,
|
|
1716
1856
|
name,
|
|
1717
1857
|
title: schema.title,
|
|
1718
|
-
description: schema.description
|
|
1858
|
+
description: schema.description,
|
|
1859
|
+
format: schema.format
|
|
1719
1860
|
});
|
|
1720
1861
|
}
|
|
1721
1862
|
/**
|
|
1722
1863
|
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1723
1864
|
*/
|
|
1724
|
-
function parseParameter(options, param) {
|
|
1865
|
+
function parseParameter(options, param, parentName) {
|
|
1725
1866
|
const required = param["required"] ?? false;
|
|
1726
|
-
const
|
|
1867
|
+
const paramName = param["name"];
|
|
1868
|
+
const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
|
|
1869
|
+
const schema = param["schema"] ? parseSchema({
|
|
1870
|
+
schema: param["schema"],
|
|
1871
|
+
name: schemaName
|
|
1872
|
+
}, options) : ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1727
1873
|
return ast.createParameter({
|
|
1728
|
-
name:
|
|
1874
|
+
name: paramName,
|
|
1729
1875
|
in: param["in"],
|
|
1730
1876
|
schema: {
|
|
1731
1877
|
...schema,
|
|
@@ -1762,27 +1908,33 @@ function createSchemaParser(ctx) {
|
|
|
1762
1908
|
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
1763
1909
|
*/
|
|
1764
1910
|
function collectPropertyKeysByFlag(schema, flag) {
|
|
1765
|
-
if (!schema?.properties) return
|
|
1911
|
+
if (!schema?.properties) return null;
|
|
1766
1912
|
const keys = [];
|
|
1767
1913
|
for (const key in schema.properties) {
|
|
1768
1914
|
const prop = schema.properties[key];
|
|
1769
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
1915
|
+
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
1770
1916
|
}
|
|
1771
|
-
return keys.length ? keys :
|
|
1917
|
+
return keys.length ? keys : null;
|
|
1772
1918
|
}
|
|
1773
1919
|
/**
|
|
1774
1920
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1775
1921
|
*/
|
|
1776
1922
|
function parseOperation(options, operation) {
|
|
1777
|
-
const
|
|
1923
|
+
const operationId = operation.getOperationId();
|
|
1924
|
+
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
1925
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
|
|
1778
1926
|
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
1779
1927
|
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
1928
|
+
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
1780
1929
|
const content = allContentTypes.flatMap((ct) => {
|
|
1781
1930
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
1782
1931
|
if (!schema) return [];
|
|
1783
1932
|
return [{
|
|
1784
1933
|
contentType: ct,
|
|
1785
|
-
schema: ast.syncOptionality(parseSchema({
|
|
1934
|
+
schema: ast.syncOptionality(parseSchema({
|
|
1935
|
+
schema,
|
|
1936
|
+
name: requestBodyName
|
|
1937
|
+
}, options), requestBodyMeta.required),
|
|
1786
1938
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1787
1939
|
}];
|
|
1788
1940
|
});
|
|
@@ -1793,21 +1945,36 @@ function createSchemaParser(ctx) {
|
|
|
1793
1945
|
} : void 0;
|
|
1794
1946
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1795
1947
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1796
|
-
const
|
|
1797
|
-
const
|
|
1798
|
-
const
|
|
1799
|
-
|
|
1948
|
+
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
1949
|
+
const { description } = getResponseMeta(responseObj);
|
|
1950
|
+
const parseEntrySchema = (contentType) => {
|
|
1951
|
+
const raw = getResponseSchema(document, operation, statusCode, { contentType });
|
|
1952
|
+
return {
|
|
1953
|
+
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
1954
|
+
schema: raw,
|
|
1955
|
+
name: responseName
|
|
1956
|
+
}, options) : ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType) }),
|
|
1957
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
1958
|
+
};
|
|
1959
|
+
};
|
|
1960
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
|
|
1961
|
+
contentType,
|
|
1962
|
+
...parseEntrySchema(contentType)
|
|
1963
|
+
}));
|
|
1964
|
+
if (content.length === 0) content.push({
|
|
1965
|
+
contentType: operation.contentType || "application/json",
|
|
1966
|
+
...parseEntrySchema(ctx.contentType)
|
|
1967
|
+
});
|
|
1800
1968
|
return ast.createResponse({
|
|
1801
1969
|
statusCode,
|
|
1802
1970
|
description,
|
|
1803
|
-
|
|
1804
|
-
mediaType,
|
|
1805
|
-
keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
|
|
1971
|
+
content
|
|
1806
1972
|
});
|
|
1807
1973
|
});
|
|
1808
1974
|
const urlPath = new URLPath(operation.path);
|
|
1809
1975
|
return ast.createOperation({
|
|
1810
|
-
operationId
|
|
1976
|
+
operationId,
|
|
1977
|
+
protocol: "http",
|
|
1811
1978
|
method: operation.method.toUpperCase(),
|
|
1812
1979
|
path: urlPath.path,
|
|
1813
1980
|
tags: operation.getTags().map((tag) => tag.name),
|
|
@@ -1825,59 +1992,278 @@ function createSchemaParser(ctx) {
|
|
|
1825
1992
|
parseParameter
|
|
1826
1993
|
};
|
|
1827
1994
|
}
|
|
1995
|
+
//#endregion
|
|
1996
|
+
//#region src/discriminator.ts
|
|
1997
|
+
/**
|
|
1998
|
+
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
1999
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
2000
|
+
*
|
|
2001
|
+
* Extracted from `applyDiscriminatorInheritance` so the streaming path can call it on a
|
|
2002
|
+
* small pre-parsed subset of schemas (only the discriminator parents) rather than on all
|
|
2003
|
+
* schemas at once.
|
|
2004
|
+
*/
|
|
2005
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
2006
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
2007
|
+
for (const schema of schemas) {
|
|
2008
|
+
let unionNode = ast.narrowSchema(schema, "union");
|
|
2009
|
+
if (!unionNode) {
|
|
2010
|
+
const intersectionMembers = ast.narrowSchema(schema, "intersection")?.members;
|
|
2011
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
2012
|
+
const u = ast.narrowSchema(m, "union");
|
|
2013
|
+
if (u) {
|
|
2014
|
+
unionNode = u;
|
|
2015
|
+
break;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
2020
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
2021
|
+
for (const member of members) {
|
|
2022
|
+
const intersectionNode = ast.narrowSchema(member, "intersection");
|
|
2023
|
+
if (!intersectionNode?.members) continue;
|
|
2024
|
+
let refNode = null;
|
|
2025
|
+
let objNode = null;
|
|
2026
|
+
for (const m of intersectionNode.members) {
|
|
2027
|
+
refNode ??= ast.narrowSchema(m, "ref");
|
|
2028
|
+
objNode ??= ast.narrowSchema(m, "object");
|
|
2029
|
+
}
|
|
2030
|
+
if (!refNode?.name || !objNode) continue;
|
|
2031
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
2032
|
+
const enumNode = prop ? ast.narrowSchema(prop.schema, "enum") : null;
|
|
2033
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
2034
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
2035
|
+
if (!enumValues.length) continue;
|
|
2036
|
+
const existing = childMap.get(refNode.name);
|
|
2037
|
+
if (!existing) {
|
|
2038
|
+
childMap.set(refNode.name, {
|
|
2039
|
+
propertyName: discriminatorPropertyName,
|
|
2040
|
+
enumValues: [...enumValues]
|
|
2041
|
+
});
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
2044
|
+
existing.enumValues.push(...enumValues);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return childMap;
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
2051
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
2052
|
+
* without buffering all schemas.
|
|
2053
|
+
*/
|
|
2054
|
+
function patchDiscriminatorNode(node, entry) {
|
|
2055
|
+
const objectNode = ast.narrowSchema(node, "object");
|
|
2056
|
+
if (!objectNode) return node;
|
|
2057
|
+
const { propertyName, enumValues } = entry;
|
|
2058
|
+
const enumSchema = ast.createSchema({
|
|
2059
|
+
type: "enum",
|
|
2060
|
+
enumValues
|
|
2061
|
+
});
|
|
2062
|
+
const newProp = ast.createProperty({
|
|
2063
|
+
name: propertyName,
|
|
2064
|
+
required: true,
|
|
2065
|
+
schema: enumSchema
|
|
2066
|
+
});
|
|
2067
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
2068
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
2069
|
+
return {
|
|
2070
|
+
...objectNode,
|
|
2071
|
+
properties: newProperties
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
//#endregion
|
|
2075
|
+
//#region src/stream.ts
|
|
2076
|
+
/**
|
|
2077
|
+
* Builds the deduplication plan from the already-parsed top-level schema nodes plus a
|
|
2078
|
+
* single extra parse pass over operations (so duplicates in request/response bodies are seen).
|
|
2079
|
+
*
|
|
2080
|
+
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2081
|
+
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2082
|
+
* name (collision-resolved against existing component names); shapes without a name stay inline.
|
|
2083
|
+
*/
|
|
2084
|
+
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2085
|
+
const circularSchemas = new Set(circularNames);
|
|
2086
|
+
const usedNames = new Set(schemaNames);
|
|
2087
|
+
return ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2088
|
+
isCandidate: (node) => {
|
|
2089
|
+
if (node.type === "enum") return true;
|
|
2090
|
+
if (node.type !== "object") return false;
|
|
2091
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2092
|
+
return !ast.containsCircularRef(node, { circularSchemas });
|
|
2093
|
+
},
|
|
2094
|
+
nameFor: (node) => {
|
|
2095
|
+
const base = node.name;
|
|
2096
|
+
if (!base) return null;
|
|
2097
|
+
let name = base;
|
|
2098
|
+
let counter = 2;
|
|
2099
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2100
|
+
usedNames.add(name);
|
|
2101
|
+
return name;
|
|
2102
|
+
},
|
|
2103
|
+
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
1828
2106
|
/**
|
|
1829
|
-
*
|
|
2107
|
+
* Reads the server URL from the document's `servers` array at `serverIndex`,
|
|
2108
|
+
* interpolating any `serverVariables` into the URL template.
|
|
1830
2109
|
*
|
|
1831
|
-
*
|
|
1832
|
-
* that downstream plugins (`plugin-ts`, `plugin-zod`, etc.) consume for code generation. No code is generated here —
|
|
1833
|
-
* the tree is a pure data structure representing all schemas and operations.
|
|
2110
|
+
* Returns `null` when `serverIndex` is omitted or out of range.
|
|
1834
2111
|
*
|
|
1835
|
-
*
|
|
2112
|
+
* @example Resolve the first server
|
|
2113
|
+
* `resolveBaseUrl({ document, serverIndex: 0 })`
|
|
2114
|
+
*
|
|
2115
|
+
* @example Override a path variable
|
|
2116
|
+
* `resolveBaseUrl({ document, serverIndex: 0, serverVariables: { version: 'v2' } })`
|
|
2117
|
+
*/
|
|
2118
|
+
function resolveBaseUrl({ document, serverIndex, serverVariables }) {
|
|
2119
|
+
const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
|
|
2120
|
+
return server?.url ? resolveServerUrl(server, serverVariables) : null;
|
|
2121
|
+
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Parses every schema once to build the lookup structures that streaming needs upfront.
|
|
2124
|
+
*
|
|
2125
|
+
* Three things happen in this single pass:
|
|
2126
|
+
* - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
|
|
2127
|
+
* - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
|
|
2128
|
+
* - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
|
|
2129
|
+
* The `allNodes` array is local and drops out of scope as soon as this function returns.
|
|
2130
|
+
*
|
|
2131
|
+
* After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
|
|
2132
|
+
* Both are proportional to the number of aliases or discriminator parents, not total schema count.
|
|
2133
|
+
*
|
|
2134
|
+
* Each schema is parsed again during the streaming pass — this is intentional.
|
|
2135
|
+
* Holding the parsed nodes in memory here would defeat the streaming memory benefit.
|
|
1836
2136
|
*
|
|
1837
2137
|
* @example
|
|
1838
2138
|
* ```ts
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
2139
|
+
* const { refAliasMap, enumNames, circularNames } = preScan({
|
|
2140
|
+
* schemas,
|
|
2141
|
+
* parseSchema,
|
|
2142
|
+
* parserOptions,
|
|
2143
|
+
* discriminator: 'strict',
|
|
2144
|
+
* })
|
|
1843
2145
|
* ```
|
|
1844
2146
|
*/
|
|
1845
|
-
function
|
|
1846
|
-
const
|
|
1847
|
-
const
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
}
|
|
1860
|
-
const
|
|
1861
|
-
const
|
|
2147
|
+
function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions, discriminator, dedupe }) {
|
|
2148
|
+
const allNodes = [];
|
|
2149
|
+
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2150
|
+
const enumNames = [];
|
|
2151
|
+
const discriminatorParentNodes = [];
|
|
2152
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2153
|
+
const node = parseSchema({
|
|
2154
|
+
schema,
|
|
2155
|
+
name
|
|
2156
|
+
}, parserOptions);
|
|
2157
|
+
allNodes.push(node);
|
|
2158
|
+
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2159
|
+
if (ast.narrowSchema(node, ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
|
|
2160
|
+
if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2161
|
+
}
|
|
2162
|
+
const circularNames = [...ast.findCircularSchemas(allNodes)];
|
|
2163
|
+
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2164
|
+
let dedupePlan = null;
|
|
2165
|
+
if (dedupe) {
|
|
2166
|
+
const operationNodes = [];
|
|
2167
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2168
|
+
if (!operation) continue;
|
|
2169
|
+
const operationNode = parseOperation(parserOptions, operation);
|
|
2170
|
+
if (operationNode) operationNodes.push(operationNode);
|
|
2171
|
+
}
|
|
2172
|
+
dedupePlan = createDedupePlan({
|
|
2173
|
+
schemaNodes: allNodes,
|
|
2174
|
+
operationNodes,
|
|
2175
|
+
schemaNames: Object.keys(schemas),
|
|
2176
|
+
circularNames
|
|
2177
|
+
});
|
|
2178
|
+
for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2179
|
+
}
|
|
1862
2180
|
return {
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2181
|
+
refAliasMap,
|
|
2182
|
+
enumNames,
|
|
2183
|
+
circularNames,
|
|
2184
|
+
discriminatorChildMap,
|
|
2185
|
+
dedupePlan
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Creates a lazy `InputStreamNode` from already-resolved adapter state.
|
|
2190
|
+
*
|
|
2191
|
+
* The schema and operation iterables each start a fresh parse pass on every
|
|
2192
|
+
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
2193
|
+
* stream object independently without sharing a cursor or holding all nodes in memory.
|
|
2194
|
+
*
|
|
2195
|
+
* Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
|
|
2196
|
+
* with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
|
|
2197
|
+
*
|
|
2198
|
+
* @example
|
|
2199
|
+
* ```ts
|
|
2200
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2201
|
+
* for await (const schema of streamNode.schemas) {
|
|
2202
|
+
* // each call to for-await restarts from the first schema
|
|
2203
|
+
* }
|
|
2204
|
+
* ```
|
|
2205
|
+
*/
|
|
2206
|
+
function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2207
|
+
const rewriteTopLevelSchema = (node) => {
|
|
2208
|
+
if (!dedupePlan) return node;
|
|
2209
|
+
const canonical = dedupePlan.canonicalBySignature.get(ast.schemaSignature(node));
|
|
2210
|
+
if (canonical && canonical.name !== node.name) return ast.createSchema({
|
|
2211
|
+
type: "ref",
|
|
2212
|
+
name: node.name ?? null,
|
|
2213
|
+
ref: canonical.ref,
|
|
2214
|
+
description: node.description,
|
|
2215
|
+
deprecated: node.deprecated
|
|
2216
|
+
});
|
|
2217
|
+
return ast.applyDedupe(node, dedupePlan.canonicalBySignature, true);
|
|
1868
2218
|
};
|
|
2219
|
+
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2220
|
+
return (async function* () {
|
|
2221
|
+
if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
|
|
2222
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2223
|
+
const alias = refAliasMap.get(name);
|
|
2224
|
+
if (alias?.name && schemas[alias.name]) {
|
|
2225
|
+
yield rewriteTopLevelSchema({
|
|
2226
|
+
...parseSchema({
|
|
2227
|
+
schema: schemas[alias.name],
|
|
2228
|
+
name: alias.name
|
|
2229
|
+
}, parserOptions),
|
|
2230
|
+
name
|
|
2231
|
+
});
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
const parsed = parseSchema({
|
|
2235
|
+
schema,
|
|
2236
|
+
name
|
|
2237
|
+
}, parserOptions);
|
|
2238
|
+
yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
|
|
2239
|
+
}
|
|
2240
|
+
})();
|
|
2241
|
+
} };
|
|
2242
|
+
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2243
|
+
return (async function* () {
|
|
2244
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2245
|
+
if (!operation) continue;
|
|
2246
|
+
const node = parseOperation(parserOptions, operation);
|
|
2247
|
+
if (node) yield dedupePlan ? ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node;
|
|
2248
|
+
}
|
|
2249
|
+
})();
|
|
2250
|
+
} };
|
|
2251
|
+
return ast.createStreamInput(schemasIterable, operationsIterable, meta);
|
|
1869
2252
|
}
|
|
1870
2253
|
//#endregion
|
|
1871
2254
|
//#region src/adapter.ts
|
|
1872
2255
|
/**
|
|
1873
|
-
*
|
|
2256
|
+
* Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
|
|
1874
2257
|
*/
|
|
1875
2258
|
const adapterOasName = "oas";
|
|
1876
2259
|
/**
|
|
1877
|
-
*
|
|
2260
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.1 specifications. Reads the
|
|
2261
|
+
* file at `input.path`, validates it, resolves the base URL, and converts every
|
|
2262
|
+
* schema and operation into the universal AST that every downstream plugin
|
|
2263
|
+
* consumes.
|
|
1878
2264
|
*
|
|
1879
|
-
*
|
|
1880
|
-
*
|
|
2265
|
+
* Configure once on `defineConfig`. The adapter's choices (date representation,
|
|
2266
|
+
* integer width, server URL) apply to every plugin in the build.
|
|
1881
2267
|
*
|
|
1882
2268
|
* @example
|
|
1883
2269
|
* ```ts
|
|
@@ -1886,17 +2272,83 @@ const adapterOasName = "oas";
|
|
|
1886
2272
|
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1887
2273
|
*
|
|
1888
2274
|
* export default defineConfig({
|
|
1889
|
-
*
|
|
1890
|
-
*
|
|
2275
|
+
* input: { path: './petStore.yaml' },
|
|
2276
|
+
* output: { path: './src/gen' },
|
|
2277
|
+
* adapter: adapterOas({
|
|
2278
|
+
* serverIndex: 0,
|
|
2279
|
+
* discriminator: 'inherit',
|
|
2280
|
+
* dateType: 'date',
|
|
2281
|
+
* }),
|
|
1891
2282
|
* plugins: [pluginTs()],
|
|
1892
2283
|
* })
|
|
1893
2284
|
* ```
|
|
1894
2285
|
*/
|
|
1895
2286
|
const adapterOas = createAdapter((options) => {
|
|
1896
|
-
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;
|
|
2287
|
+
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;
|
|
2288
|
+
const parserOptions = {
|
|
2289
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
2290
|
+
dateType,
|
|
2291
|
+
integerType,
|
|
2292
|
+
unknownType,
|
|
2293
|
+
emptySchemaType,
|
|
2294
|
+
enumSuffix
|
|
2295
|
+
};
|
|
1897
2296
|
let nameMapping = /* @__PURE__ */ new Map();
|
|
1898
|
-
let parsedDocument;
|
|
1899
|
-
|
|
2297
|
+
let parsedDocument = null;
|
|
2298
|
+
const ensureDocument = once(async (source) => {
|
|
2299
|
+
const fresh = await parseFromConfig(source);
|
|
2300
|
+
if (validate) await validateDocument(fresh);
|
|
2301
|
+
parsedDocument = fresh;
|
|
2302
|
+
return fresh;
|
|
2303
|
+
});
|
|
2304
|
+
const ensureSchemas = once(async (document) => {
|
|
2305
|
+
const result = getSchemas(document, { contentType });
|
|
2306
|
+
nameMapping = result.nameMapping;
|
|
2307
|
+
return result.schemas;
|
|
2308
|
+
});
|
|
2309
|
+
const ensureBaseOas = once((document) => new BaseOas(document));
|
|
2310
|
+
const ensureSchemaParser = once((document) => createSchemaParser({
|
|
2311
|
+
document,
|
|
2312
|
+
contentType
|
|
2313
|
+
}));
|
|
2314
|
+
const ensurePreScan = once((schemas, parseSchema, parseOperation, baseOas) => preScan({
|
|
2315
|
+
schemas,
|
|
2316
|
+
parseSchema,
|
|
2317
|
+
parseOperation,
|
|
2318
|
+
baseOas,
|
|
2319
|
+
parserOptions,
|
|
2320
|
+
discriminator,
|
|
2321
|
+
dedupe
|
|
2322
|
+
}));
|
|
2323
|
+
async function createStream(source) {
|
|
2324
|
+
const document = await ensureDocument(source);
|
|
2325
|
+
const schemas = await ensureSchemas(document);
|
|
2326
|
+
const { parseSchema, parseOperation } = ensureSchemaParser(document);
|
|
2327
|
+
const baseOas = ensureBaseOas(document);
|
|
2328
|
+
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(schemas, parseSchema, parseOperation, baseOas);
|
|
2329
|
+
return createInputStream({
|
|
2330
|
+
schemas,
|
|
2331
|
+
parseSchema,
|
|
2332
|
+
parseOperation,
|
|
2333
|
+
baseOas,
|
|
2334
|
+
parserOptions,
|
|
2335
|
+
refAliasMap,
|
|
2336
|
+
discriminatorChildMap,
|
|
2337
|
+
dedupePlan,
|
|
2338
|
+
meta: {
|
|
2339
|
+
title: document.info?.title,
|
|
2340
|
+
description: document.info?.description,
|
|
2341
|
+
version: document.info?.version,
|
|
2342
|
+
baseURL: resolveBaseUrl({
|
|
2343
|
+
document,
|
|
2344
|
+
serverIndex,
|
|
2345
|
+
serverVariables
|
|
2346
|
+
}),
|
|
2347
|
+
circularNames,
|
|
2348
|
+
enumNames
|
|
2349
|
+
}
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
1900
2352
|
return {
|
|
1901
2353
|
name: "oas",
|
|
1902
2354
|
get options() {
|
|
@@ -1906,6 +2358,7 @@ const adapterOas = createAdapter((options) => {
|
|
|
1906
2358
|
serverIndex,
|
|
1907
2359
|
serverVariables,
|
|
1908
2360
|
discriminator,
|
|
2361
|
+
dedupe,
|
|
1909
2362
|
dateType,
|
|
1910
2363
|
integerType,
|
|
1911
2364
|
unknownType,
|
|
@@ -1917,8 +2370,8 @@ const adapterOas = createAdapter((options) => {
|
|
|
1917
2370
|
get document() {
|
|
1918
2371
|
return parsedDocument;
|
|
1919
2372
|
},
|
|
1920
|
-
|
|
1921
|
-
|
|
2373
|
+
async validate(input, options) {
|
|
2374
|
+
await validateDocument(await parseDocument(input), options);
|
|
1922
2375
|
},
|
|
1923
2376
|
getImports(node, resolve) {
|
|
1924
2377
|
return ast.collectImports({
|
|
@@ -1926,7 +2379,7 @@ const adapterOas = createAdapter((options) => {
|
|
|
1926
2379
|
nameMapping,
|
|
1927
2380
|
resolve: (schemaName) => {
|
|
1928
2381
|
const result = resolve(schemaName);
|
|
1929
|
-
if (!result) return;
|
|
2382
|
+
if (!result) return null;
|
|
1930
2383
|
return ast.createImport({
|
|
1931
2384
|
name: [result.name],
|
|
1932
2385
|
path: result.path
|
|
@@ -1935,32 +2388,20 @@ const adapterOas = createAdapter((options) => {
|
|
|
1935
2388
|
});
|
|
1936
2389
|
},
|
|
1937
2390
|
async parse(source) {
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
});
|
|
1950
|
-
const node = discriminator === "inherit" ? applyDiscriminatorInheritance(parsedRoot) : parsedRoot;
|
|
1951
|
-
nameMapping = parsedNameMapping;
|
|
1952
|
-
parsedDocument = document;
|
|
1953
|
-
inputNode = ast.createInput({
|
|
1954
|
-
...node,
|
|
1955
|
-
meta: {
|
|
1956
|
-
title: document.info?.title,
|
|
1957
|
-
description: document.info?.description,
|
|
1958
|
-
version: document.info?.version,
|
|
1959
|
-
baseURL
|
|
1960
|
-
}
|
|
2391
|
+
const streamNode = await createStream(source);
|
|
2392
|
+
const collect = async (iter) => {
|
|
2393
|
+
const out = [];
|
|
2394
|
+
for await (const item of iter) out.push(item);
|
|
2395
|
+
return out;
|
|
2396
|
+
};
|
|
2397
|
+
const [schemas, operations] = await Promise.all([collect(streamNode.schemas), collect(streamNode.operations)]);
|
|
2398
|
+
return ast.createInput({
|
|
2399
|
+
schemas,
|
|
2400
|
+
operations,
|
|
2401
|
+
meta: streamNode.meta
|
|
1961
2402
|
});
|
|
1962
|
-
|
|
1963
|
-
|
|
2403
|
+
},
|
|
2404
|
+
stream: createStream
|
|
1964
2405
|
};
|
|
1965
2406
|
});
|
|
1966
2407
|
//#endregion
|
|
@@ -1976,6 +2417,6 @@ const adapterOas = createAdapter((options) => {
|
|
|
1976
2417
|
*/
|
|
1977
2418
|
const HttpMethods = Object.fromEntries(Object.entries(ast.httpMethods).map(([lower, upper]) => [upper, lower]));
|
|
1978
2419
|
//#endregion
|
|
1979
|
-
export { HttpMethods, adapterOas, adapterOasName, mergeDocuments
|
|
2420
|
+
export { HttpMethods, adapterOas, adapterOasName, mergeDocuments };
|
|
1980
2421
|
|
|
1981
2422
|
//# sourceMappingURL=index.js.map
|