@kubb/adapter-oas 5.0.0-beta.4 → 5.0.0-beta.40
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 +100 -0
- package/dist/index.cjs +900 -311
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +52 -69
- package/dist/index.js +902 -310
- package/dist/index.js.map +1 -1
- package/extension.yaml +144 -57
- package/package.json +6 -4
- package/src/adapter.bench.ts +64 -0
- package/src/adapter.ts +143 -48
- package/src/constants.ts +8 -0
- package/src/dialect.ts +38 -0
- package/src/discriminator.ts +31 -47
- package/src/factory.ts +38 -12
- package/src/index.ts +2 -2
- package/src/parser.ts +233 -137
- package/src/refs.ts +31 -5
- package/src/resolvers.ts +77 -38
- package/src/schemaDiagnostics.ts +76 -0
- package/src/stream.ts +278 -0
- package/src/types.ts +34 -11
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -22,15 +22,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
24
|
let _kubb_core = require("@kubb/core");
|
|
25
|
+
let oas = require("oas");
|
|
26
|
+
oas = __toESM(oas, 1);
|
|
25
27
|
let node_path = require("node:path");
|
|
26
28
|
node_path = __toESM(node_path, 1);
|
|
29
|
+
let node_fs_promises = require("node:fs/promises");
|
|
27
30
|
let _redocly_openapi_core = require("@redocly/openapi-core");
|
|
28
31
|
let oas_normalize = require("oas-normalize");
|
|
29
32
|
oas_normalize = __toESM(oas_normalize, 1);
|
|
30
33
|
let swagger2openapi = require("swagger2openapi");
|
|
31
34
|
swagger2openapi = __toESM(swagger2openapi, 1);
|
|
32
|
-
let oas = require("oas");
|
|
33
|
-
oas = __toESM(oas, 1);
|
|
34
35
|
let oas_types = require("oas/types");
|
|
35
36
|
let oas_utils = require("oas/utils");
|
|
36
37
|
//#region src/constants.ts
|
|
@@ -115,6 +116,18 @@ const structuralKeys = new Set([
|
|
|
115
116
|
* formatMap['float'] // 'number'
|
|
116
117
|
* ```
|
|
117
118
|
*/
|
|
119
|
+
/**
|
|
120
|
+
* Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
|
|
121
|
+
* `int64` and the date/time family. Keep this in sync with the `convertFormat`
|
|
122
|
+
* special-cases in `parser.ts`. `isHandledFormat` reads it so the
|
|
123
|
+
* `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
|
|
124
|
+
*/
|
|
125
|
+
const specialCasedFormats = new Set([
|
|
126
|
+
"int64",
|
|
127
|
+
"date-time",
|
|
128
|
+
"date",
|
|
129
|
+
"time"
|
|
130
|
+
]);
|
|
118
131
|
const formatMap = {
|
|
119
132
|
uuid: "uuid",
|
|
120
133
|
email: "email",
|
|
@@ -153,87 +166,6 @@ const typeOptionMap = new Map([
|
|
|
153
166
|
["void", _kubb_core.ast.schemaTypes.void]
|
|
154
167
|
]);
|
|
155
168
|
//#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
169
|
//#region ../../internals/utils/src/casing.ts
|
|
238
170
|
/**
|
|
239
171
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -298,6 +230,23 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
298
230
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
299
231
|
}
|
|
300
232
|
//#endregion
|
|
233
|
+
//#region ../../internals/utils/src/fs.ts
|
|
234
|
+
/**
|
|
235
|
+
* Resolves to `true` when the file or directory at `path` exists.
|
|
236
|
+
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* if (await exists('./kubb.config.ts')) {
|
|
241
|
+
* const content = await read('./kubb.config.ts')
|
|
242
|
+
* }
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
async function exists(path) {
|
|
246
|
+
if (typeof Bun !== "undefined") return Bun.file(path).exists();
|
|
247
|
+
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
248
|
+
}
|
|
249
|
+
//#endregion
|
|
301
250
|
//#region ../../internals/utils/src/object.ts
|
|
302
251
|
/**
|
|
303
252
|
* Returns `true` when `value` is a plain (non-null, non-array) object.
|
|
@@ -543,12 +492,13 @@ var URLPath = class {
|
|
|
543
492
|
* @example
|
|
544
493
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
545
494
|
*/
|
|
546
|
-
toTemplateString({ prefix
|
|
547
|
-
|
|
495
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
496
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
548
497
|
if (i % 2 === 0) return part;
|
|
549
498
|
const param = this.#transformParam(part);
|
|
550
499
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
551
|
-
}).join("")
|
|
500
|
+
}).join("");
|
|
501
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
552
502
|
}
|
|
553
503
|
/**
|
|
554
504
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -686,12 +636,17 @@ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true }
|
|
|
686
636
|
* ```
|
|
687
637
|
*/
|
|
688
638
|
async function mergeDocuments(pathOrApi) {
|
|
689
|
-
const documents =
|
|
690
|
-
for (const p of pathOrApi) documents.push(await parseDocument(p, {
|
|
639
|
+
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
|
|
691
640
|
enablePaths: false,
|
|
692
641
|
canBundle: false
|
|
693
|
-
}));
|
|
694
|
-
if (documents.length === 0) throw new Error(
|
|
642
|
+
})));
|
|
643
|
+
if (documents.length === 0) throw new _kubb_core.Diagnostics.Error({
|
|
644
|
+
code: _kubb_core.Diagnostics.code.inputRequired,
|
|
645
|
+
severity: "error",
|
|
646
|
+
message: "No OAS documents were provided for merging.",
|
|
647
|
+
help: "Pass at least one path or document to `input.path`.",
|
|
648
|
+
location: { kind: "config" }
|
|
649
|
+
});
|
|
695
650
|
const seed = {
|
|
696
651
|
openapi: MERGE_OPENAPI_VERSION,
|
|
697
652
|
info: {
|
|
@@ -707,9 +662,9 @@ async function mergeDocuments(pathOrApi) {
|
|
|
707
662
|
* Creates a `Document` from an `AdapterSource`.
|
|
708
663
|
*
|
|
709
664
|
* Handles all three source types:
|
|
710
|
-
* - `{ type: 'path' }`
|
|
711
|
-
* - `{ type: 'paths' }`
|
|
712
|
-
* - `{ type: 'data' }`
|
|
665
|
+
* - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
|
|
666
|
+
* - `{ type: 'paths' }` merges multiple file paths into a single document.
|
|
667
|
+
* - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
|
|
713
668
|
*
|
|
714
669
|
* @example
|
|
715
670
|
* ```ts
|
|
@@ -717,14 +672,31 @@ async function mergeDocuments(pathOrApi) {
|
|
|
717
672
|
* const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
|
|
718
673
|
* ```
|
|
719
674
|
*/
|
|
720
|
-
function parseFromConfig(source) {
|
|
675
|
+
async function parseFromConfig(source) {
|
|
721
676
|
if (source.type === "data") {
|
|
722
677
|
if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
|
|
723
678
|
return parseDocument(source.data, { canBundle: false });
|
|
724
679
|
}
|
|
725
680
|
if (source.type === "paths") return mergeDocuments(source.paths);
|
|
726
681
|
if (new URLPath(source.path).isURL) return parseDocument(source.path);
|
|
727
|
-
|
|
682
|
+
const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
|
|
683
|
+
await assertInputExists(resolved);
|
|
684
|
+
return parseDocument(resolved);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Throws a coded `KUBB_INPUT_NOT_FOUND` diagnostic when a local input path does not exist.
|
|
688
|
+
* URLs are skipped, and a malformed but readable file is left for `parseDocument` to surface
|
|
689
|
+
* its parse error instead.
|
|
690
|
+
*/
|
|
691
|
+
async function assertInputExists(input) {
|
|
692
|
+
if (new URLPath(input).isURL) return;
|
|
693
|
+
if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
|
|
694
|
+
code: _kubb_core.Diagnostics.code.inputNotFound,
|
|
695
|
+
severity: "error",
|
|
696
|
+
message: `Cannot read the file set in \`input.path\` (or via \`kubb generate PATH\`): ${input}`,
|
|
697
|
+
help: "Check that the path exists and is readable, then set it in `input.path` or pass it as `kubb generate PATH`.",
|
|
698
|
+
location: { kind: "config" }
|
|
699
|
+
});
|
|
728
700
|
}
|
|
729
701
|
/**
|
|
730
702
|
* Validates an OpenAPI document using `oas-normalize` with colorized error output.
|
|
@@ -746,6 +718,7 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
746
718
|
}
|
|
747
719
|
//#endregion
|
|
748
720
|
//#region src/refs.ts
|
|
721
|
+
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
749
722
|
/**
|
|
750
723
|
* Resolves a local JSON pointer reference from a document.
|
|
751
724
|
*
|
|
@@ -761,10 +734,31 @@ function resolveRef(document, $ref) {
|
|
|
761
734
|
const origRef = $ref;
|
|
762
735
|
$ref = $ref.trim();
|
|
763
736
|
if ($ref === "") return null;
|
|
764
|
-
if (
|
|
765
|
-
|
|
737
|
+
if (!$ref.startsWith("#")) return null;
|
|
738
|
+
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
739
|
+
let docCache = _refCache.get(document);
|
|
740
|
+
if (!docCache) {
|
|
741
|
+
docCache = /* @__PURE__ */ new Map();
|
|
742
|
+
_refCache.set(document, docCache);
|
|
743
|
+
}
|
|
744
|
+
if (docCache.has($ref)) return docCache.get($ref);
|
|
766
745
|
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
767
|
-
if (!current)
|
|
746
|
+
if (!current) {
|
|
747
|
+
const diagnostic = {
|
|
748
|
+
code: _kubb_core.Diagnostics.code.refNotFound,
|
|
749
|
+
severity: "error",
|
|
750
|
+
message: `Could not find a definition for ${origRef}.`,
|
|
751
|
+
help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
|
|
752
|
+
location: {
|
|
753
|
+
kind: "schema",
|
|
754
|
+
pointer: origRef,
|
|
755
|
+
ref: origRef
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
if (!_kubb_core.Diagnostics.report(diagnostic)) throw new _kubb_core.Diagnostics.Error(diagnostic);
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
docCache.set($ref, current);
|
|
768
762
|
return current;
|
|
769
763
|
}
|
|
770
764
|
/**
|
|
@@ -788,6 +782,35 @@ function dereferenceWithRef(document, schema) {
|
|
|
788
782
|
return schema;
|
|
789
783
|
}
|
|
790
784
|
//#endregion
|
|
785
|
+
//#region src/dialect.ts
|
|
786
|
+
/**
|
|
787
|
+
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
788
|
+
*
|
|
789
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
790
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
791
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
792
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
|
|
793
|
+
* nullability, no discriminator object, binary via `contentEncoding` and reuses
|
|
794
|
+
* the rest unchanged.
|
|
795
|
+
*
|
|
796
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
797
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
798
|
+
*
|
|
799
|
+
* @example
|
|
800
|
+
* ```ts
|
|
801
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
802
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
const oasDialect = _kubb_core.ast.defineSchemaDialect({
|
|
806
|
+
name: "oas",
|
|
807
|
+
isNullable,
|
|
808
|
+
isReference,
|
|
809
|
+
isDiscriminator,
|
|
810
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
811
|
+
resolveRef
|
|
812
|
+
});
|
|
813
|
+
//#endregion
|
|
791
814
|
//#region src/resolvers.ts
|
|
792
815
|
/**
|
|
793
816
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -809,7 +832,16 @@ function resolveServerUrl(server, overrides) {
|
|
|
809
832
|
for (const [key, variable] of Object.entries(server.variables)) {
|
|
810
833
|
const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
|
|
811
834
|
if (value === void 0) continue;
|
|
812
|
-
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new Error(
|
|
835
|
+
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.Diagnostics.Error({
|
|
836
|
+
code: _kubb_core.Diagnostics.code.invalidServerVariable,
|
|
837
|
+
severity: "error",
|
|
838
|
+
message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`,
|
|
839
|
+
help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
|
|
840
|
+
location: {
|
|
841
|
+
kind: "document",
|
|
842
|
+
pointer: "#/servers"
|
|
843
|
+
}
|
|
844
|
+
});
|
|
813
845
|
url = url.replaceAll(`{${key}}`, value);
|
|
814
846
|
}
|
|
815
847
|
return url;
|
|
@@ -822,6 +854,15 @@ function getSchemaType(format) {
|
|
|
822
854
|
return formatMap[format] ?? null;
|
|
823
855
|
}
|
|
824
856
|
/**
|
|
857
|
+
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry and the
|
|
858
|
+
* `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to the
|
|
859
|
+
* base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
860
|
+
* diagnostic in step with the parser as `formatMap` grows.
|
|
861
|
+
*/
|
|
862
|
+
function isHandledFormat(format) {
|
|
863
|
+
return getSchemaType(format) !== null || specialCasedFormats.has(format);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
825
866
|
* Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
|
|
826
867
|
* Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
|
|
827
868
|
*/
|
|
@@ -831,12 +872,6 @@ function getPrimitiveType(type) {
|
|
|
831
872
|
return "string";
|
|
832
873
|
}
|
|
833
874
|
/**
|
|
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
875
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
841
876
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
842
877
|
* `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
|
|
@@ -924,7 +959,7 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
924
959
|
/**
|
|
925
960
|
* Flattens a keyword-only `allOf` into its parent schema.
|
|
926
961
|
*
|
|
927
|
-
* Only flattens when every member is a plain fragment
|
|
962
|
+
* Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
|
|
928
963
|
* (see `structuralKeys`). Outer schema values take precedence over fragment values.
|
|
929
964
|
* Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
|
|
930
965
|
*
|
|
@@ -934,7 +969,7 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
934
969
|
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
935
970
|
*
|
|
936
971
|
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
937
|
-
* // returned unchanged
|
|
972
|
+
* // returned unchanged, contains a $ref
|
|
938
973
|
* ```
|
|
939
974
|
*/
|
|
940
975
|
/**
|
|
@@ -960,7 +995,7 @@ function flattenSchema(schema) {
|
|
|
960
995
|
/**
|
|
961
996
|
* Extracts the inline schema from a media-type `content` map.
|
|
962
997
|
*
|
|
963
|
-
* Prefers `preferredContentType` when given
|
|
998
|
+
* Prefers `preferredContentType` when given, otherwise uses the first key in the map.
|
|
964
999
|
* Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
|
|
965
1000
|
*
|
|
966
1001
|
* @example
|
|
@@ -979,21 +1014,22 @@ function extractSchemaFromContent(content, preferredContentType) {
|
|
|
979
1014
|
/**
|
|
980
1015
|
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
981
1016
|
*/
|
|
982
|
-
function collectRefs(schema
|
|
1017
|
+
function* collectRefs(schema) {
|
|
983
1018
|
if (Array.isArray(schema)) {
|
|
984
|
-
for (const item of schema) collectRefs(item
|
|
985
|
-
return
|
|
1019
|
+
for (const item of schema) yield* collectRefs(item);
|
|
1020
|
+
return;
|
|
986
1021
|
}
|
|
987
1022
|
if (schema && typeof schema === "object") for (const key in schema) {
|
|
988
1023
|
const value = schema[key];
|
|
989
|
-
if (key === "$ref" && typeof value === "string") {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1024
|
+
if (!(key === "$ref" && typeof value === "string")) {
|
|
1025
|
+
yield* collectRefs(value);
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
if (value.startsWith("#/components/schemas/")) {
|
|
1029
|
+
const name = value.slice(21);
|
|
1030
|
+
if (name) yield name;
|
|
1031
|
+
}
|
|
995
1032
|
}
|
|
996
|
-
return refs;
|
|
997
1033
|
}
|
|
998
1034
|
/**
|
|
999
1035
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
@@ -1009,7 +1045,7 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
|
1009
1045
|
*/
|
|
1010
1046
|
function sortSchemas(schemas) {
|
|
1011
1047
|
const deps = /* @__PURE__ */ new Map();
|
|
1012
|
-
for (const [name, schema] of Object.entries(schemas)) deps.set(name,
|
|
1048
|
+
for (const [name, schema] of Object.entries(schemas)) deps.set(name, [...new Set(collectRefs(schema))]);
|
|
1013
1049
|
const sorted = [];
|
|
1014
1050
|
const visited = /* @__PURE__ */ new Set();
|
|
1015
1051
|
function visit(name, stack) {
|
|
@@ -1144,15 +1180,16 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
|
1144
1180
|
readOnly: schema.readOnly,
|
|
1145
1181
|
writeOnly: schema.writeOnly,
|
|
1146
1182
|
default: defaultValue,
|
|
1147
|
-
example: schema.example
|
|
1183
|
+
example: schema.example,
|
|
1184
|
+
format: schema.format
|
|
1148
1185
|
};
|
|
1149
1186
|
}
|
|
1150
1187
|
/**
|
|
1151
1188
|
* Returns all request body content type keys for an operation.
|
|
1152
1189
|
*
|
|
1153
|
-
* The requestBody is dereferenced
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1190
|
+
* The requestBody is dereferenced in place when it is a `$ref` (the same mutation that
|
|
1191
|
+
* `getRequestSchema` already performs), so the returned list accurately reflects the
|
|
1192
|
+
* available content types even for referenced bodies.
|
|
1156
1193
|
*
|
|
1157
1194
|
* @example
|
|
1158
1195
|
* ```ts
|
|
@@ -1166,6 +1203,31 @@ function getRequestBodyContentTypes(document, operation) {
|
|
|
1166
1203
|
if (!body) return [];
|
|
1167
1204
|
return body.content ? Object.keys(body.content) : [];
|
|
1168
1205
|
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Returns all response content type keys for an operation at a given status code.
|
|
1208
|
+
*
|
|
1209
|
+
* Response `$ref`s are resolved in place first (the same mutation `getResponseSchema` performs),
|
|
1210
|
+
* so the returned list reflects the available content types even for referenced responses.
|
|
1211
|
+
*
|
|
1212
|
+
* @example
|
|
1213
|
+
* ```ts
|
|
1214
|
+
* getResponseBodyContentTypes(document, operation, 200)
|
|
1215
|
+
* // ['application/json', 'application/xml']
|
|
1216
|
+
* ```
|
|
1217
|
+
*/
|
|
1218
|
+
function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
1219
|
+
if (operation.schema.responses) {
|
|
1220
|
+
const responses = operation.schema.responses;
|
|
1221
|
+
for (const key in responses) {
|
|
1222
|
+
const schema = responses[key];
|
|
1223
|
+
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1227
|
+
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
1228
|
+
const body = responseObj;
|
|
1229
|
+
return body.content ? Object.keys(body.content) : [];
|
|
1230
|
+
}
|
|
1169
1231
|
//#endregion
|
|
1170
1232
|
//#region src/parser.ts
|
|
1171
1233
|
/**
|
|
@@ -1194,9 +1256,9 @@ function normalizeArrayEnum(schema) {
|
|
|
1194
1256
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1195
1257
|
* made possible by hoisting of function declarations.
|
|
1196
1258
|
*
|
|
1197
|
-
* @
|
|
1259
|
+
* @internal
|
|
1198
1260
|
*/
|
|
1199
|
-
function createSchemaParser(ctx) {
|
|
1261
|
+
function createSchemaParser(ctx, dialect = oasDialect) {
|
|
1200
1262
|
const document = ctx.document;
|
|
1201
1263
|
/**
|
|
1202
1264
|
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
@@ -1204,6 +1266,15 @@ function createSchemaParser(ctx) {
|
|
|
1204
1266
|
*/
|
|
1205
1267
|
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1206
1268
|
/**
|
|
1269
|
+
* Cache of `$ref` schemas already resolved in this parser instance, keyed by ref path.
|
|
1270
|
+
*
|
|
1271
|
+
* Without it, a shared schema (e.g. `customer`) is re-expanded for every `$ref` that points at
|
|
1272
|
+
* it. In cross-referenced specs like Stripe (~1400 schemas) that becomes exponential blowup,
|
|
1273
|
+
* since one schema can be referenced from dozens of parents, each re-walking its whole subtree.
|
|
1274
|
+
* Memoizing by ref path drops the work from O(2^depth) to O(N) unique schema names.
|
|
1275
|
+
*/
|
|
1276
|
+
const resolvedRefCache = /* @__PURE__ */ new Map();
|
|
1277
|
+
/**
|
|
1207
1278
|
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1208
1279
|
*
|
|
1209
1280
|
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
@@ -1212,16 +1283,22 @@ function createSchemaParser(ctx) {
|
|
|
1212
1283
|
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1213
1284
|
*/
|
|
1214
1285
|
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1215
|
-
let resolvedSchema;
|
|
1286
|
+
let resolvedSchema = null;
|
|
1216
1287
|
const refPath = schema.$ref;
|
|
1217
|
-
if (refPath && !resolvingRefs.has(refPath))
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1288
|
+
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1289
|
+
if (!resolvedRefCache.has(refPath)) {
|
|
1290
|
+
try {
|
|
1291
|
+
const referenced = dialect.resolveRef(document, refPath);
|
|
1292
|
+
if (referenced) {
|
|
1293
|
+
resolvingRefs.add(refPath);
|
|
1294
|
+
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1295
|
+
resolvingRefs.delete(refPath);
|
|
1296
|
+
}
|
|
1297
|
+
} catch {}
|
|
1298
|
+
resolvedRefCache.set(refPath, resolvedSchema);
|
|
1223
1299
|
}
|
|
1224
|
-
|
|
1300
|
+
resolvedSchema = resolvedRefCache.get(refPath) ?? null;
|
|
1301
|
+
}
|
|
1225
1302
|
return _kubb_core.ast.createSchema({
|
|
1226
1303
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1227
1304
|
type: "ref",
|
|
@@ -1238,7 +1315,7 @@ function createSchemaParser(ctx) {
|
|
|
1238
1315
|
const [memberSchema] = schema.allOf;
|
|
1239
1316
|
const memberNode = parseSchema({
|
|
1240
1317
|
schema: memberSchema,
|
|
1241
|
-
name
|
|
1318
|
+
name
|
|
1242
1319
|
}, rawOptions);
|
|
1243
1320
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1244
1321
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
@@ -1254,18 +1331,19 @@ function createSchemaParser(ctx) {
|
|
|
1254
1331
|
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1255
1332
|
default: mergedDefault,
|
|
1256
1333
|
example: schema.example ?? memberNode.example,
|
|
1257
|
-
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
|
|
1334
|
+
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1335
|
+
format: schema.format ?? memberNode.format
|
|
1258
1336
|
});
|
|
1259
1337
|
}
|
|
1260
1338
|
const filteredDiscriminantValues = [];
|
|
1261
1339
|
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;
|
|
1340
|
+
if (!dialect.isReference(item) || !name) return true;
|
|
1341
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1342
|
+
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1265
1343
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1266
1344
|
if (!parentUnion) return true;
|
|
1267
1345
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1268
|
-
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1346
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1269
1347
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1270
1348
|
if (inOneOf || inMapping) {
|
|
1271
1349
|
const discriminatorValue = _kubb_core.ast.findDiscriminator(deref.discriminator.mapping, childRef);
|
|
@@ -1276,22 +1354,28 @@ function createSchemaParser(ctx) {
|
|
|
1276
1354
|
return false;
|
|
1277
1355
|
}
|
|
1278
1356
|
return true;
|
|
1279
|
-
}).map((s) => parseSchema({
|
|
1357
|
+
}).map((s) => parseSchema({
|
|
1358
|
+
schema: s,
|
|
1359
|
+
name
|
|
1360
|
+
}, rawOptions));
|
|
1280
1361
|
const syntheticStart = allOfMembers.length;
|
|
1281
1362
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1282
1363
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1283
1364
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1284
1365
|
if (missingRequired.length) {
|
|
1285
1366
|
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] : [];
|
|
1367
|
+
if (!dialect.isReference(item)) return [item];
|
|
1368
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1369
|
+
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1289
1370
|
});
|
|
1290
1371
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1291
|
-
allOfMembers.push(parseSchema({
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1372
|
+
allOfMembers.push(parseSchema({
|
|
1373
|
+
schema: {
|
|
1374
|
+
properties: { [key]: resolved.properties[key] },
|
|
1375
|
+
required: [key]
|
|
1376
|
+
},
|
|
1377
|
+
name
|
|
1378
|
+
}, rawOptions));
|
|
1295
1379
|
break;
|
|
1296
1380
|
}
|
|
1297
1381
|
}
|
|
@@ -1306,7 +1390,7 @@ function createSchemaParser(ctx) {
|
|
|
1306
1390
|
}));
|
|
1307
1391
|
return _kubb_core.ast.createSchema({
|
|
1308
1392
|
type: "intersection",
|
|
1309
|
-
members: [..._kubb_core.ast.
|
|
1393
|
+
members: [..._kubb_core.ast.mergeAdjacentObjectsLazy(allOfMembers.slice(0, syntheticStart)), ..._kubb_core.ast.mergeAdjacentObjectsLazy(allOfMembers.slice(syntheticStart))],
|
|
1310
1394
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1311
1395
|
});
|
|
1312
1396
|
}
|
|
@@ -1327,10 +1411,10 @@ function createSchemaParser(ctx) {
|
|
|
1327
1411
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1328
1412
|
const unionBase = {
|
|
1329
1413
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1330
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1414
|
+
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1331
1415
|
strategy
|
|
1332
1416
|
};
|
|
1333
|
-
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1417
|
+
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1334
1418
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1335
1419
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1336
1420
|
return parseSchema({
|
|
@@ -1340,9 +1424,12 @@ function createSchemaParser(ctx) {
|
|
|
1340
1424
|
})() : void 0;
|
|
1341
1425
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1342
1426
|
const members = unionMembers.map((s) => {
|
|
1343
|
-
const ref = isReference(s) ? s.$ref : void 0;
|
|
1427
|
+
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1344
1428
|
const discriminatorValue = _kubb_core.ast.findDiscriminator(discriminator?.mapping, ref);
|
|
1345
|
-
const memberNode = parseSchema({
|
|
1429
|
+
const memberNode = parseSchema({
|
|
1430
|
+
schema: s,
|
|
1431
|
+
name
|
|
1432
|
+
}, rawOptions);
|
|
1346
1433
|
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1347
1434
|
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.setDiscriminatorEnum({
|
|
1348
1435
|
node: sharedPropertiesNode,
|
|
@@ -1372,7 +1459,10 @@ function createSchemaParser(ctx) {
|
|
|
1372
1459
|
return _kubb_core.ast.createSchema({
|
|
1373
1460
|
type: "union",
|
|
1374
1461
|
...unionBase,
|
|
1375
|
-
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1462
|
+
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1463
|
+
schema: s,
|
|
1464
|
+
name
|
|
1465
|
+
}, rawOptions)))
|
|
1376
1466
|
});
|
|
1377
1467
|
}
|
|
1378
1468
|
/**
|
|
@@ -1386,7 +1476,8 @@ function createSchemaParser(ctx) {
|
|
|
1386
1476
|
name,
|
|
1387
1477
|
title: schema.title,
|
|
1388
1478
|
description: schema.description,
|
|
1389
|
-
deprecated: schema.deprecated
|
|
1479
|
+
deprecated: schema.deprecated,
|
|
1480
|
+
format: schema.format
|
|
1390
1481
|
});
|
|
1391
1482
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1392
1483
|
return _kubb_core.ast.createSchema({
|
|
@@ -1476,6 +1567,15 @@ function createSchemaParser(ctx) {
|
|
|
1476
1567
|
}, rawOptions);
|
|
1477
1568
|
const nullInEnum = schema.enum.includes(null);
|
|
1478
1569
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1570
|
+
if (nullInEnum && filteredValues.length === 0) return _kubb_core.ast.createSchema({
|
|
1571
|
+
type: "null",
|
|
1572
|
+
primitive: "null",
|
|
1573
|
+
name,
|
|
1574
|
+
title: schema.title,
|
|
1575
|
+
description: schema.description,
|
|
1576
|
+
deprecated: schema.deprecated,
|
|
1577
|
+
format: schema.format
|
|
1578
|
+
});
|
|
1479
1579
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1480
1580
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1481
1581
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1490,7 +1590,8 @@ function createSchemaParser(ctx) {
|
|
|
1490
1590
|
readOnly: schema.readOnly,
|
|
1491
1591
|
writeOnly: schema.writeOnly,
|
|
1492
1592
|
default: enumDefault,
|
|
1493
|
-
example: schema.example
|
|
1593
|
+
example: schema.example,
|
|
1594
|
+
format: schema.format
|
|
1494
1595
|
};
|
|
1495
1596
|
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1496
1597
|
if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
@@ -1524,20 +1625,23 @@ function createSchemaParser(ctx) {
|
|
|
1524
1625
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1525
1626
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1526
1627
|
const resolvedPropSchema = propSchema;
|
|
1527
|
-
const propNullable = isNullable(resolvedPropSchema);
|
|
1628
|
+
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
1528
1629
|
const propNode = parseSchema({
|
|
1529
1630
|
schema: resolvedPropSchema,
|
|
1530
1631
|
name: _kubb_core.ast.childName(name, propName)
|
|
1531
1632
|
}, rawOptions);
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1633
|
+
const schemaNode = (() => {
|
|
1634
|
+
const node = _kubb_core.ast.setEnumName(propNode, name, propName, options.enumSuffix);
|
|
1635
|
+
const tupleNode = _kubb_core.ast.narrowSchema(node, "tuple");
|
|
1636
|
+
if (tupleNode?.items) {
|
|
1637
|
+
const namedItems = tupleNode.items.map((item) => _kubb_core.ast.setEnumName(item, name, propName, options.enumSuffix));
|
|
1638
|
+
if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
|
|
1639
|
+
...tupleNode,
|
|
1640
|
+
items: namedItems
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
return node;
|
|
1644
|
+
})();
|
|
1541
1645
|
return _kubb_core.ast.createProperty({
|
|
1542
1646
|
name: propName,
|
|
1543
1647
|
schema: {
|
|
@@ -1548,11 +1652,12 @@ function createSchemaParser(ctx) {
|
|
|
1548
1652
|
});
|
|
1549
1653
|
}) : [];
|
|
1550
1654
|
const additionalProperties = schema.additionalProperties;
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1655
|
+
const additionalPropertiesNode = (() => {
|
|
1656
|
+
if (additionalProperties === true) return true;
|
|
1657
|
+
if (additionalProperties === false) return false;
|
|
1658
|
+
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1659
|
+
if (additionalProperties) return _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1660
|
+
})();
|
|
1556
1661
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1557
1662
|
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
1663
|
const objectNode = _kubb_core.ast.createSchema({
|
|
@@ -1565,7 +1670,7 @@ function createSchemaParser(ctx) {
|
|
|
1565
1670
|
maxProperties: schema.maxProperties,
|
|
1566
1671
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1567
1672
|
});
|
|
1568
|
-
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1673
|
+
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1569
1674
|
const discPropName = schema.discriminator.propertyName;
|
|
1570
1675
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1571
1676
|
const enumName = name ? _kubb_core.ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
@@ -1599,7 +1704,7 @@ function createSchemaParser(ctx) {
|
|
|
1599
1704
|
*/
|
|
1600
1705
|
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1601
1706
|
const rawItems = schema.items;
|
|
1602
|
-
const itemName = rawItems?.enum?.length && name ? _kubb_core.ast.enumPropName(
|
|
1707
|
+
const itemName = rawItems?.enum?.length && name ? _kubb_core.ast.enumPropName(null, name, options.enumSuffix) : name;
|
|
1603
1708
|
const items = rawItems ? [parseSchema({
|
|
1604
1709
|
schema: rawItems,
|
|
1605
1710
|
name: itemName
|
|
@@ -1663,15 +1768,148 @@ function createSchemaParser(ctx) {
|
|
|
1663
1768
|
title: schema.title,
|
|
1664
1769
|
description: schema.description,
|
|
1665
1770
|
deprecated: schema.deprecated,
|
|
1666
|
-
nullable
|
|
1771
|
+
nullable,
|
|
1772
|
+
format: schema.format
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1777
|
+
* into a `blob` node.
|
|
1778
|
+
*/
|
|
1779
|
+
function convertBlob({ schema, name, nullable, defaultValue }) {
|
|
1780
|
+
return _kubb_core.ast.createSchema({
|
|
1781
|
+
type: "blob",
|
|
1782
|
+
primitive: "string",
|
|
1783
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1667
1784
|
});
|
|
1668
1785
|
}
|
|
1669
1786
|
/**
|
|
1787
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1788
|
+
*
|
|
1789
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
|
|
1790
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1791
|
+
*/
|
|
1792
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1793
|
+
const types = schema.type;
|
|
1794
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1795
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1796
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1797
|
+
return _kubb_core.ast.createSchema({
|
|
1798
|
+
type: "union",
|
|
1799
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1800
|
+
schema: {
|
|
1801
|
+
...schema,
|
|
1802
|
+
type: t
|
|
1803
|
+
},
|
|
1804
|
+
name
|
|
1805
|
+
}, rawOptions)),
|
|
1806
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1811
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1812
|
+
* `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
|
|
1813
|
+
* match/convert/fall-through contract.
|
|
1814
|
+
*/
|
|
1815
|
+
const schemaRules = [
|
|
1816
|
+
{
|
|
1817
|
+
name: "ref",
|
|
1818
|
+
match: ({ schema }) => dialect.isReference(schema),
|
|
1819
|
+
convert: convertRef
|
|
1820
|
+
},
|
|
1821
|
+
{
|
|
1822
|
+
name: "allOf",
|
|
1823
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1824
|
+
convert: convertAllOf
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
name: "union",
|
|
1828
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1829
|
+
convert: convertUnion
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
name: "const",
|
|
1833
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1834
|
+
convert: convertConst
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
name: "format",
|
|
1838
|
+
match: ({ schema }) => !!schema.format,
|
|
1839
|
+
convert: convertFormat
|
|
1840
|
+
},
|
|
1841
|
+
{
|
|
1842
|
+
name: "blob",
|
|
1843
|
+
match: ({ schema }) => dialect.isBinary(schema),
|
|
1844
|
+
convert: convertBlob
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
name: "multi-type",
|
|
1848
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1849
|
+
convert: convertMultiType
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
name: "constrained-string",
|
|
1853
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1854
|
+
convert: convertString
|
|
1855
|
+
},
|
|
1856
|
+
{
|
|
1857
|
+
name: "constrained-number",
|
|
1858
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1859
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1860
|
+
},
|
|
1861
|
+
{
|
|
1862
|
+
name: "enum",
|
|
1863
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1864
|
+
convert: convertEnum
|
|
1865
|
+
},
|
|
1866
|
+
{
|
|
1867
|
+
name: "object",
|
|
1868
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1869
|
+
convert: convertObject
|
|
1870
|
+
},
|
|
1871
|
+
{
|
|
1872
|
+
name: "tuple",
|
|
1873
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1874
|
+
convert: convertTuple
|
|
1875
|
+
},
|
|
1876
|
+
{
|
|
1877
|
+
name: "array",
|
|
1878
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1879
|
+
convert: convertArray
|
|
1880
|
+
},
|
|
1881
|
+
{
|
|
1882
|
+
name: "string",
|
|
1883
|
+
match: ({ type }) => type === "string",
|
|
1884
|
+
convert: convertString
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
name: "number",
|
|
1888
|
+
match: ({ type }) => type === "number",
|
|
1889
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1890
|
+
},
|
|
1891
|
+
{
|
|
1892
|
+
name: "integer",
|
|
1893
|
+
match: ({ type }) => type === "integer",
|
|
1894
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
name: "boolean",
|
|
1898
|
+
match: ({ type }) => type === "boolean",
|
|
1899
|
+
convert: convertBoolean
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
name: "null",
|
|
1903
|
+
match: ({ type }) => type === "null",
|
|
1904
|
+
convert: convertNull
|
|
1905
|
+
}
|
|
1906
|
+
];
|
|
1907
|
+
/**
|
|
1670
1908
|
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1671
1909
|
*
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
1674
|
-
*
|
|
1910
|
+
* Builds the per-schema context, then runs it through the ordered {@link schemaRules} table
|
|
1911
|
+
* via {@link ast.dispatch}. When no rule produces a node, falls back to the configured
|
|
1912
|
+
* `emptySchemaType`.
|
|
1675
1913
|
*/
|
|
1676
1914
|
function parseSchema({ schema, name }, rawOptions) {
|
|
1677
1915
|
const options = {
|
|
@@ -1683,75 +1921,40 @@ function createSchemaParser(ctx) {
|
|
|
1683
1921
|
schema: flattenedSchema,
|
|
1684
1922
|
name
|
|
1685
1923
|
}, 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 = {
|
|
1924
|
+
const nullable = dialect.isNullable(schema) || void 0;
|
|
1925
|
+
const schemaCtx = {
|
|
1690
1926
|
schema,
|
|
1691
1927
|
name,
|
|
1692
1928
|
nullable,
|
|
1693
|
-
defaultValue,
|
|
1694
|
-
type,
|
|
1929
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1930
|
+
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1695
1931
|
rawOptions,
|
|
1696
1932
|
options
|
|
1697
1933
|
};
|
|
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);
|
|
1934
|
+
const node = _kubb_core.ast.dispatch(schemaRules, schemaCtx);
|
|
1935
|
+
if (node) return node;
|
|
1739
1936
|
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1740
1937
|
return _kubb_core.ast.createSchema({
|
|
1741
1938
|
type: emptyType,
|
|
1742
1939
|
name,
|
|
1743
1940
|
title: schema.title,
|
|
1744
|
-
description: schema.description
|
|
1941
|
+
description: schema.description,
|
|
1942
|
+
format: schema.format
|
|
1745
1943
|
});
|
|
1746
1944
|
}
|
|
1747
1945
|
/**
|
|
1748
1946
|
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1749
1947
|
*/
|
|
1750
|
-
function parseParameter(options, param) {
|
|
1948
|
+
function parseParameter(options, param, parentName) {
|
|
1751
1949
|
const required = param["required"] ?? false;
|
|
1752
|
-
const
|
|
1950
|
+
const paramName = param["name"];
|
|
1951
|
+
const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
|
|
1952
|
+
const schema = param["schema"] ? parseSchema({
|
|
1953
|
+
schema: param["schema"],
|
|
1954
|
+
name: schemaName
|
|
1955
|
+
}, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1753
1956
|
return _kubb_core.ast.createParameter({
|
|
1754
|
-
name:
|
|
1957
|
+
name: paramName,
|
|
1755
1958
|
in: param["in"],
|
|
1756
1959
|
schema: {
|
|
1757
1960
|
...schema,
|
|
@@ -1788,27 +1991,33 @@ function createSchemaParser(ctx) {
|
|
|
1788
1991
|
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
1789
1992
|
*/
|
|
1790
1993
|
function collectPropertyKeysByFlag(schema, flag) {
|
|
1791
|
-
if (!schema?.properties) return
|
|
1994
|
+
if (!schema?.properties) return null;
|
|
1792
1995
|
const keys = [];
|
|
1793
1996
|
for (const key in schema.properties) {
|
|
1794
1997
|
const prop = schema.properties[key];
|
|
1795
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
1998
|
+
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
1796
1999
|
}
|
|
1797
|
-
return keys.length ? keys :
|
|
2000
|
+
return keys.length ? keys : null;
|
|
1798
2001
|
}
|
|
1799
2002
|
/**
|
|
1800
2003
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1801
2004
|
*/
|
|
1802
2005
|
function parseOperation(options, operation) {
|
|
1803
|
-
const
|
|
2006
|
+
const operationId = operation.getOperationId();
|
|
2007
|
+
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
2008
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
|
|
1804
2009
|
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
1805
2010
|
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
2011
|
+
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
1806
2012
|
const content = allContentTypes.flatMap((ct) => {
|
|
1807
2013
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
1808
2014
|
if (!schema) return [];
|
|
1809
2015
|
return [{
|
|
1810
2016
|
contentType: ct,
|
|
1811
|
-
schema: _kubb_core.ast.syncOptionality(parseSchema({
|
|
2017
|
+
schema: _kubb_core.ast.syncOptionality(parseSchema({
|
|
2018
|
+
schema,
|
|
2019
|
+
name: requestBodyName
|
|
2020
|
+
}, options), requestBodyMeta.required),
|
|
1812
2021
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1813
2022
|
}];
|
|
1814
2023
|
});
|
|
@@ -1819,21 +2028,36 @@ function createSchemaParser(ctx) {
|
|
|
1819
2028
|
} : void 0;
|
|
1820
2029
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1821
2030
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1824
|
-
const
|
|
1825
|
-
|
|
2031
|
+
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
2032
|
+
const { description } = getResponseMeta(responseObj);
|
|
2033
|
+
const parseEntrySchema = (contentType) => {
|
|
2034
|
+
const raw = getResponseSchema(document, operation, statusCode, { contentType });
|
|
2035
|
+
return {
|
|
2036
|
+
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
2037
|
+
schema: raw,
|
|
2038
|
+
name: responseName
|
|
2039
|
+
}, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType) }),
|
|
2040
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
2041
|
+
};
|
|
2042
|
+
};
|
|
2043
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
|
|
2044
|
+
contentType,
|
|
2045
|
+
...parseEntrySchema(contentType)
|
|
2046
|
+
}));
|
|
2047
|
+
if (content.length === 0) content.push({
|
|
2048
|
+
contentType: operation.contentType || "application/json",
|
|
2049
|
+
...parseEntrySchema(ctx.contentType)
|
|
2050
|
+
});
|
|
1826
2051
|
return _kubb_core.ast.createResponse({
|
|
1827
2052
|
statusCode,
|
|
1828
2053
|
description,
|
|
1829
|
-
|
|
1830
|
-
mediaType,
|
|
1831
|
-
keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
|
|
2054
|
+
content
|
|
1832
2055
|
});
|
|
1833
2056
|
});
|
|
1834
2057
|
const urlPath = new URLPath(operation.path);
|
|
1835
2058
|
return _kubb_core.ast.createOperation({
|
|
1836
|
-
operationId
|
|
2059
|
+
operationId,
|
|
2060
|
+
protocol: "http",
|
|
1837
2061
|
method: operation.method.toUpperCase(),
|
|
1838
2062
|
path: urlPath.path,
|
|
1839
2063
|
tags: operation.getTags().map((tag) => tag.name),
|
|
@@ -1851,59 +2075,336 @@ function createSchemaParser(ctx) {
|
|
|
1851
2075
|
parseParameter
|
|
1852
2076
|
};
|
|
1853
2077
|
}
|
|
2078
|
+
//#endregion
|
|
2079
|
+
//#region src/discriminator.ts
|
|
1854
2080
|
/**
|
|
1855
|
-
*
|
|
2081
|
+
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
2082
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
1856
2083
|
*
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
|
|
2084
|
+
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
2085
|
+
* discriminator parents) rather than on all schemas at once.
|
|
2086
|
+
*/
|
|
2087
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
2088
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
2089
|
+
for (const schema of schemas) {
|
|
2090
|
+
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
2091
|
+
if (!unionNode) {
|
|
2092
|
+
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
2093
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
2094
|
+
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
2095
|
+
if (u) {
|
|
2096
|
+
unionNode = u;
|
|
2097
|
+
break;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
2102
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
2103
|
+
for (const member of members) {
|
|
2104
|
+
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
2105
|
+
if (!intersectionNode?.members) continue;
|
|
2106
|
+
let refNode = null;
|
|
2107
|
+
let objNode = null;
|
|
2108
|
+
for (const m of intersectionNode.members) {
|
|
2109
|
+
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
2110
|
+
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
2111
|
+
}
|
|
2112
|
+
if (!refNode?.name || !objNode) continue;
|
|
2113
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
2114
|
+
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : null;
|
|
2115
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
2116
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
2117
|
+
if (!enumValues.length) continue;
|
|
2118
|
+
const existing = childMap.get(refNode.name);
|
|
2119
|
+
if (!existing) {
|
|
2120
|
+
childMap.set(refNode.name, {
|
|
2121
|
+
propertyName: discriminatorPropertyName,
|
|
2122
|
+
enumValues: [...enumValues]
|
|
2123
|
+
});
|
|
2124
|
+
continue;
|
|
2125
|
+
}
|
|
2126
|
+
existing.enumValues.push(...enumValues);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
return childMap;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
2133
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
2134
|
+
* without buffering all schemas.
|
|
2135
|
+
*/
|
|
2136
|
+
function patchDiscriminatorNode(node, entry) {
|
|
2137
|
+
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
2138
|
+
if (!objectNode) return node;
|
|
2139
|
+
const { propertyName, enumValues } = entry;
|
|
2140
|
+
const enumSchema = _kubb_core.ast.createSchema({
|
|
2141
|
+
type: "enum",
|
|
2142
|
+
enumValues
|
|
2143
|
+
});
|
|
2144
|
+
const newProp = _kubb_core.ast.createProperty({
|
|
2145
|
+
name: propertyName,
|
|
2146
|
+
required: true,
|
|
2147
|
+
schema: enumSchema
|
|
2148
|
+
});
|
|
2149
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
2150
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
2151
|
+
return {
|
|
2152
|
+
...objectNode,
|
|
2153
|
+
properties: newProperties
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
//#endregion
|
|
2157
|
+
//#region src/schemaDiagnostics.ts
|
|
2158
|
+
/**
|
|
2159
|
+
* Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
|
|
2160
|
+
* top-level schema. Walks the node the parser produced during `preScan`, threading the RFC 6901
|
|
2161
|
+
* pointer as it descends so a nested field reports against its full path
|
|
2162
|
+
* (`#/components/schemas/Pet/properties/owner/properties/name`). Refs are not followed, so the
|
|
2163
|
+
* resolved schema is reported under its own walk. Reports land in the active build run, are a
|
|
2164
|
+
* no-op outside one, and repeats are deduped by the build.
|
|
2165
|
+
*/
|
|
2166
|
+
function reportSchemaDiagnostics({ node, name }) {
|
|
2167
|
+
visit(node, `#/components/schemas/${escapePointerToken(name)}`);
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
|
|
2171
|
+
* property name with those characters maps to a distinct pointer instead of colliding in the dedupe.
|
|
2172
|
+
*/
|
|
2173
|
+
function escapePointerToken(token) {
|
|
2174
|
+
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2175
|
+
}
|
|
2176
|
+
function visit(node, pointer) {
|
|
2177
|
+
if (node.deprecated) _kubb_core.Diagnostics.report({
|
|
2178
|
+
code: _kubb_core.Diagnostics.code.deprecated,
|
|
2179
|
+
severity: "info",
|
|
2180
|
+
message: "This schema is marked as deprecated.",
|
|
2181
|
+
location: {
|
|
2182
|
+
kind: "schema",
|
|
2183
|
+
pointer
|
|
2184
|
+
}
|
|
2185
|
+
});
|
|
2186
|
+
if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_core.Diagnostics.report({
|
|
2187
|
+
code: _kubb_core.Diagnostics.code.unsupportedFormat,
|
|
2188
|
+
severity: "warning",
|
|
2189
|
+
message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
|
|
2190
|
+
help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
|
|
2191
|
+
location: {
|
|
2192
|
+
kind: "schema",
|
|
2193
|
+
pointer
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2196
|
+
if (node.type === "object") {
|
|
2197
|
+
for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
|
|
2198
|
+
if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
if (node.type === "array") {
|
|
2202
|
+
for (const item of node.items ?? []) visit(item, `${pointer}/items`);
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
if (node.type === "tuple") {
|
|
2206
|
+
for (const [index, item] of (node.items ?? []).entries()) visit(item, `${pointer}/items/${index}`);
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
if (node.type === "union" || node.type === "intersection") for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/members/${index}`);
|
|
2210
|
+
}
|
|
2211
|
+
//#endregion
|
|
2212
|
+
//#region src/stream.ts
|
|
2213
|
+
/**
|
|
2214
|
+
* Builds the deduplication plan from the already-parsed top-level schema nodes plus a
|
|
2215
|
+
* single extra parse pass over operations (so duplicates in request/response bodies are seen).
|
|
2216
|
+
*
|
|
2217
|
+
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2218
|
+
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2219
|
+
* name (collision-resolved against existing component names); shapes without a name stay inline.
|
|
2220
|
+
*/
|
|
2221
|
+
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2222
|
+
const circularSchemas = new Set(circularNames);
|
|
2223
|
+
const usedNames = new Set(schemaNames);
|
|
2224
|
+
return _kubb_core.ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2225
|
+
isCandidate: (node) => {
|
|
2226
|
+
if (node.type === "enum") return true;
|
|
2227
|
+
if (node.type !== "object") return false;
|
|
2228
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2229
|
+
return !_kubb_core.ast.containsCircularRef(node, { circularSchemas });
|
|
2230
|
+
},
|
|
2231
|
+
nameFor: (node) => {
|
|
2232
|
+
const base = node.name;
|
|
2233
|
+
if (!base) return null;
|
|
2234
|
+
let name = base;
|
|
2235
|
+
let counter = 2;
|
|
2236
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2237
|
+
usedNames.add(name);
|
|
2238
|
+
return name;
|
|
2239
|
+
},
|
|
2240
|
+
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Reads the server URL from the document's `servers` array at `serverIndex`,
|
|
2245
|
+
* interpolating any `serverVariables` into the URL template.
|
|
2246
|
+
*
|
|
2247
|
+
* Returns `null` when `serverIndex` is omitted or out of range.
|
|
2248
|
+
*
|
|
2249
|
+
* @example Resolve the first server
|
|
2250
|
+
* `resolveBaseUrl({ document, serverIndex: 0 })`
|
|
2251
|
+
*
|
|
2252
|
+
* @example Override a path variable
|
|
2253
|
+
* `resolveBaseUrl({ document, serverIndex: 0, serverVariables: { version: 'v2' } })`
|
|
2254
|
+
*/
|
|
2255
|
+
function resolveBaseUrl({ document, serverIndex, serverVariables }) {
|
|
2256
|
+
const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
|
|
2257
|
+
return server?.url ? resolveServerUrl(server, serverVariables) : null;
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Parses every schema once to build the lookup structures that streaming needs upfront.
|
|
2261
|
+
*
|
|
2262
|
+
* Three things happen in this single pass:
|
|
2263
|
+
* - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
|
|
2264
|
+
* - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
|
|
2265
|
+
* - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
|
|
2266
|
+
* The `allNodes` array is local and drops out of scope as soon as this function returns.
|
|
2267
|
+
*
|
|
2268
|
+
* After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
|
|
2269
|
+
* Both are proportional to the number of aliases or discriminator parents, not total schema count.
|
|
1860
2270
|
*
|
|
1861
|
-
*
|
|
2271
|
+
* Each schema is parsed again during the streaming pass. This is intentional.
|
|
2272
|
+
* Holding the parsed nodes in memory here would defeat the streaming memory benefit.
|
|
1862
2273
|
*
|
|
1863
2274
|
* @example
|
|
1864
2275
|
* ```ts
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1867
|
-
*
|
|
1868
|
-
*
|
|
2276
|
+
* const { refAliasMap, enumNames, circularNames } = preScan({
|
|
2277
|
+
* schemas,
|
|
2278
|
+
* parseSchema,
|
|
2279
|
+
* parserOptions,
|
|
2280
|
+
* discriminator: 'strict',
|
|
2281
|
+
* })
|
|
1869
2282
|
* ```
|
|
1870
2283
|
*/
|
|
1871
|
-
function
|
|
1872
|
-
const
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2284
|
+
function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions, discriminator, dedupe }) {
|
|
2285
|
+
const allNodes = [];
|
|
2286
|
+
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2287
|
+
const enumNames = [];
|
|
2288
|
+
const discriminatorParentNodes = [];
|
|
2289
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2290
|
+
const node = parseSchema({
|
|
2291
|
+
schema,
|
|
2292
|
+
name
|
|
2293
|
+
}, parserOptions);
|
|
2294
|
+
allNodes.push(node);
|
|
2295
|
+
reportSchemaDiagnostics({
|
|
2296
|
+
node,
|
|
2297
|
+
name
|
|
2298
|
+
});
|
|
2299
|
+
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2300
|
+
if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
|
|
2301
|
+
if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2302
|
+
}
|
|
2303
|
+
const circularNames = [..._kubb_core.ast.findCircularSchemas(allNodes)];
|
|
2304
|
+
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2305
|
+
let dedupePlan = null;
|
|
2306
|
+
if (dedupe) {
|
|
2307
|
+
const operationNodes = [];
|
|
2308
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2309
|
+
if (!operation) continue;
|
|
2310
|
+
const operationNode = parseOperation(parserOptions, operation);
|
|
2311
|
+
if (operationNode) operationNodes.push(operationNode);
|
|
2312
|
+
}
|
|
2313
|
+
dedupePlan = createDedupePlan({
|
|
2314
|
+
schemaNodes: allNodes,
|
|
2315
|
+
operationNodes,
|
|
2316
|
+
schemaNames: Object.keys(schemas),
|
|
2317
|
+
circularNames
|
|
2318
|
+
});
|
|
2319
|
+
for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2320
|
+
}
|
|
1888
2321
|
return {
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2322
|
+
refAliasMap,
|
|
2323
|
+
enumNames,
|
|
2324
|
+
circularNames,
|
|
2325
|
+
discriminatorChildMap,
|
|
2326
|
+
dedupePlan
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Creates a lazy `InputStreamNode` from already-resolved adapter state.
|
|
2331
|
+
*
|
|
2332
|
+
* The schema and operation iterables each start a fresh parse pass on every
|
|
2333
|
+
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
2334
|
+
* stream object independently without sharing a cursor or holding all nodes in memory.
|
|
2335
|
+
*
|
|
2336
|
+
* Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
|
|
2337
|
+
* with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
|
|
2338
|
+
*
|
|
2339
|
+
* @example
|
|
2340
|
+
* ```ts
|
|
2341
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2342
|
+
* for await (const schema of streamNode.schemas) {
|
|
2343
|
+
* // each call to for-await restarts from the first schema
|
|
2344
|
+
* }
|
|
2345
|
+
* ```
|
|
2346
|
+
*/
|
|
2347
|
+
function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2348
|
+
const rewriteTopLevelSchema = (node) => {
|
|
2349
|
+
if (!dedupePlan) return node;
|
|
2350
|
+
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.schemaSignature(node));
|
|
2351
|
+
if (canonical && canonical.name !== node.name) return _kubb_core.ast.createSchema({
|
|
2352
|
+
type: "ref",
|
|
2353
|
+
name: node.name ?? null,
|
|
2354
|
+
ref: canonical.ref,
|
|
2355
|
+
description: node.description,
|
|
2356
|
+
deprecated: node.deprecated
|
|
2357
|
+
});
|
|
2358
|
+
return _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature, true);
|
|
1894
2359
|
};
|
|
2360
|
+
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2361
|
+
return (async function* () {
|
|
2362
|
+
if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
|
|
2363
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2364
|
+
const alias = refAliasMap.get(name);
|
|
2365
|
+
if (alias?.name && schemas[alias.name]) {
|
|
2366
|
+
yield rewriteTopLevelSchema({
|
|
2367
|
+
...parseSchema({
|
|
2368
|
+
schema: schemas[alias.name],
|
|
2369
|
+
name: alias.name
|
|
2370
|
+
}, parserOptions),
|
|
2371
|
+
name
|
|
2372
|
+
});
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2375
|
+
const parsed = parseSchema({
|
|
2376
|
+
schema,
|
|
2377
|
+
name
|
|
2378
|
+
}, parserOptions);
|
|
2379
|
+
yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
|
|
2380
|
+
}
|
|
2381
|
+
})();
|
|
2382
|
+
} };
|
|
2383
|
+
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2384
|
+
return (async function* () {
|
|
2385
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2386
|
+
if (!operation) continue;
|
|
2387
|
+
const node = parseOperation(parserOptions, operation);
|
|
2388
|
+
if (node) yield dedupePlan ? _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node;
|
|
2389
|
+
}
|
|
2390
|
+
})();
|
|
2391
|
+
} };
|
|
2392
|
+
return _kubb_core.ast.createStreamInput(schemasIterable, operationsIterable, meta);
|
|
1895
2393
|
}
|
|
1896
2394
|
//#endregion
|
|
1897
2395
|
//#region src/adapter.ts
|
|
1898
2396
|
/**
|
|
1899
|
-
*
|
|
2397
|
+
* Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
|
|
1900
2398
|
*/
|
|
1901
2399
|
const adapterOasName = "oas";
|
|
1902
2400
|
/**
|
|
1903
|
-
*
|
|
2401
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.1 specifications. Reads the
|
|
2402
|
+
* file at `input.path`, validates it, resolves the base URL, and converts every
|
|
2403
|
+
* schema and operation into the universal AST that every downstream plugin
|
|
2404
|
+
* consumes.
|
|
1904
2405
|
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
2406
|
+
* Configure once on `defineConfig`. The adapter's choices (date representation,
|
|
2407
|
+
* integer width, server URL) apply to every plugin in the build.
|
|
1907
2408
|
*
|
|
1908
2409
|
* @example
|
|
1909
2410
|
* ```ts
|
|
@@ -1912,17 +2413,118 @@ const adapterOasName = "oas";
|
|
|
1912
2413
|
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1913
2414
|
*
|
|
1914
2415
|
* export default defineConfig({
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
2416
|
+
* input: { path: './petStore.yaml' },
|
|
2417
|
+
* output: { path: './src/gen' },
|
|
2418
|
+
* adapter: adapterOas({
|
|
2419
|
+
* serverIndex: 0,
|
|
2420
|
+
* discriminator: 'inherit',
|
|
2421
|
+
* dateType: 'date',
|
|
2422
|
+
* }),
|
|
1917
2423
|
* plugins: [pluginTs()],
|
|
1918
2424
|
* })
|
|
1919
2425
|
* ```
|
|
1920
2426
|
*/
|
|
1921
2427
|
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;
|
|
2428
|
+
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;
|
|
2429
|
+
const parserOptions = {
|
|
2430
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
2431
|
+
dateType,
|
|
2432
|
+
integerType,
|
|
2433
|
+
unknownType,
|
|
2434
|
+
emptySchemaType,
|
|
2435
|
+
enumSuffix
|
|
2436
|
+
};
|
|
1923
2437
|
let nameMapping = /* @__PURE__ */ new Map();
|
|
1924
|
-
let parsedDocument;
|
|
1925
|
-
|
|
2438
|
+
let parsedDocument = null;
|
|
2439
|
+
const documentCache = /* @__PURE__ */ new WeakMap();
|
|
2440
|
+
const schemasCache = /* @__PURE__ */ new WeakMap();
|
|
2441
|
+
const baseOasCache = /* @__PURE__ */ new WeakMap();
|
|
2442
|
+
const schemaParserCache = /* @__PURE__ */ new WeakMap();
|
|
2443
|
+
const preScanCache = /* @__PURE__ */ new WeakMap();
|
|
2444
|
+
function ensureDocument(source) {
|
|
2445
|
+
const cached = documentCache.get(source);
|
|
2446
|
+
if (cached) return cached;
|
|
2447
|
+
const promise = (async () => {
|
|
2448
|
+
const fresh = await parseFromConfig(source);
|
|
2449
|
+
if (validate) await validateDocument(fresh);
|
|
2450
|
+
parsedDocument = fresh;
|
|
2451
|
+
return fresh;
|
|
2452
|
+
})();
|
|
2453
|
+
documentCache.set(source, promise);
|
|
2454
|
+
return promise;
|
|
2455
|
+
}
|
|
2456
|
+
function ensureSchemas(document) {
|
|
2457
|
+
const cached = schemasCache.get(document);
|
|
2458
|
+
if (cached) return cached;
|
|
2459
|
+
const promise = Promise.resolve().then(() => {
|
|
2460
|
+
const result = getSchemas(document, { contentType });
|
|
2461
|
+
nameMapping = result.nameMapping;
|
|
2462
|
+
return result.schemas;
|
|
2463
|
+
});
|
|
2464
|
+
schemasCache.set(document, promise);
|
|
2465
|
+
return promise;
|
|
2466
|
+
}
|
|
2467
|
+
function ensureBaseOas(document) {
|
|
2468
|
+
const cached = baseOasCache.get(document);
|
|
2469
|
+
if (cached) return cached;
|
|
2470
|
+
const baseOas = new oas.default(document);
|
|
2471
|
+
baseOasCache.set(document, baseOas);
|
|
2472
|
+
return baseOas;
|
|
2473
|
+
}
|
|
2474
|
+
function ensureSchemaParser(document) {
|
|
2475
|
+
const cached = schemaParserCache.get(document);
|
|
2476
|
+
if (cached) return cached;
|
|
2477
|
+
const parser = createSchemaParser({
|
|
2478
|
+
document,
|
|
2479
|
+
contentType
|
|
2480
|
+
});
|
|
2481
|
+
schemaParserCache.set(document, parser);
|
|
2482
|
+
return parser;
|
|
2483
|
+
}
|
|
2484
|
+
function ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas) {
|
|
2485
|
+
const cached = preScanCache.get(document);
|
|
2486
|
+
if (cached) return cached;
|
|
2487
|
+
const result = preScan({
|
|
2488
|
+
schemas,
|
|
2489
|
+
parseSchema,
|
|
2490
|
+
parseOperation,
|
|
2491
|
+
baseOas,
|
|
2492
|
+
parserOptions,
|
|
2493
|
+
discriminator,
|
|
2494
|
+
dedupe
|
|
2495
|
+
});
|
|
2496
|
+
preScanCache.set(document, result);
|
|
2497
|
+
return result;
|
|
2498
|
+
}
|
|
2499
|
+
async function createStream(source) {
|
|
2500
|
+
const document = await ensureDocument(source);
|
|
2501
|
+
const schemas = await ensureSchemas(document);
|
|
2502
|
+
const { parseSchema, parseOperation } = ensureSchemaParser(document);
|
|
2503
|
+
const baseOas = ensureBaseOas(document);
|
|
2504
|
+
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas);
|
|
2505
|
+
return createInputStream({
|
|
2506
|
+
schemas,
|
|
2507
|
+
parseSchema,
|
|
2508
|
+
parseOperation,
|
|
2509
|
+
baseOas,
|
|
2510
|
+
parserOptions,
|
|
2511
|
+
refAliasMap,
|
|
2512
|
+
discriminatorChildMap,
|
|
2513
|
+
dedupePlan,
|
|
2514
|
+
meta: {
|
|
2515
|
+
title: document.info?.title,
|
|
2516
|
+
description: document.info?.description,
|
|
2517
|
+
version: document.info?.version,
|
|
2518
|
+
baseURL: resolveBaseUrl({
|
|
2519
|
+
document,
|
|
2520
|
+
serverIndex,
|
|
2521
|
+
serverVariables
|
|
2522
|
+
}),
|
|
2523
|
+
circularNames,
|
|
2524
|
+
enumNames
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
1926
2528
|
return {
|
|
1927
2529
|
name: "oas",
|
|
1928
2530
|
get options() {
|
|
@@ -1932,6 +2534,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1932
2534
|
serverIndex,
|
|
1933
2535
|
serverVariables,
|
|
1934
2536
|
discriminator,
|
|
2537
|
+
dedupe,
|
|
1935
2538
|
dateType,
|
|
1936
2539
|
integerType,
|
|
1937
2540
|
unknownType,
|
|
@@ -1943,8 +2546,9 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1943
2546
|
get document() {
|
|
1944
2547
|
return parsedDocument;
|
|
1945
2548
|
},
|
|
1946
|
-
|
|
1947
|
-
|
|
2549
|
+
async validate(input, options) {
|
|
2550
|
+
await assertInputExists(input);
|
|
2551
|
+
await validateDocument(await parseDocument(input), options);
|
|
1948
2552
|
},
|
|
1949
2553
|
getImports(node, resolve) {
|
|
1950
2554
|
return _kubb_core.ast.collectImports({
|
|
@@ -1952,7 +2556,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1952
2556
|
nameMapping,
|
|
1953
2557
|
resolve: (schemaName) => {
|
|
1954
2558
|
const result = resolve(schemaName);
|
|
1955
|
-
if (!result) return;
|
|
2559
|
+
if (!result) return null;
|
|
1956
2560
|
return _kubb_core.ast.createImport({
|
|
1957
2561
|
name: [result.name],
|
|
1958
2562
|
path: result.path
|
|
@@ -1961,32 +2565,20 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1961
2565
|
});
|
|
1962
2566
|
},
|
|
1963
2567
|
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
|
-
}
|
|
2568
|
+
const streamNode = await createStream(source);
|
|
2569
|
+
const collect = async (iter) => {
|
|
2570
|
+
const out = [];
|
|
2571
|
+
for await (const item of iter) out.push(item);
|
|
2572
|
+
return out;
|
|
2573
|
+
};
|
|
2574
|
+
const [schemas, operations] = await Promise.all([collect(streamNode.schemas), collect(streamNode.operations)]);
|
|
2575
|
+
return _kubb_core.ast.createInput({
|
|
2576
|
+
schemas,
|
|
2577
|
+
operations,
|
|
2578
|
+
meta: streamNode.meta
|
|
1987
2579
|
});
|
|
1988
|
-
|
|
1989
|
-
|
|
2580
|
+
},
|
|
2581
|
+
stream: createStream
|
|
1990
2582
|
};
|
|
1991
2583
|
});
|
|
1992
2584
|
//#endregion
|
|
@@ -2006,8 +2598,5 @@ exports.HttpMethods = HttpMethods;
|
|
|
2006
2598
|
exports.adapterOas = adapterOas;
|
|
2007
2599
|
exports.adapterOasName = adapterOasName;
|
|
2008
2600
|
exports.mergeDocuments = mergeDocuments;
|
|
2009
|
-
exports.parseDocument = parseDocument;
|
|
2010
|
-
exports.parseFromConfig = parseFromConfig;
|
|
2011
|
-
exports.validateDocument = validateDocument;
|
|
2012
2601
|
|
|
2013
2602
|
//# sourceMappingURL=index.cjs.map
|