@kubb/adapter-oas 5.0.0-beta.5 → 5.0.0-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -10
- package/README.md +100 -0
- package/dist/index.cjs +936 -316
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +52 -69
- package/dist/index.js +938 -315
- package/dist/index.js.map +1 -1
- package/extension.yaml +145 -58
- package/package.json +9 -6
- package/src/adapter.bench.ts +60 -0
- package/src/adapter.ts +139 -50
- 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 +239 -142
- 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,17 @@ 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
|
|
33
|
-
oas = __toESM(oas, 1);
|
|
35
|
+
let _kubb_ast_utils = require("@kubb/ast/utils");
|
|
34
36
|
let oas_types = require("oas/types");
|
|
35
37
|
let oas_utils = require("oas/utils");
|
|
36
38
|
//#region src/constants.ts
|
|
@@ -115,6 +117,18 @@ const structuralKeys = new Set([
|
|
|
115
117
|
* formatMap['float'] // 'number'
|
|
116
118
|
* ```
|
|
117
119
|
*/
|
|
120
|
+
/**
|
|
121
|
+
* Formats `convertFormat` maps to a dedicated type without going through `formatMap`:
|
|
122
|
+
* `int64` and the date/time family. Keep this in sync with the `convertFormat`
|
|
123
|
+
* special-cases in `parser.ts`. `isHandledFormat` reads it so the
|
|
124
|
+
* `KUBB_UNSUPPORTED_FORMAT` diagnostic and the parser agree on what is handled.
|
|
125
|
+
*/
|
|
126
|
+
const specialCasedFormats = new Set([
|
|
127
|
+
"int64",
|
|
128
|
+
"date-time",
|
|
129
|
+
"date",
|
|
130
|
+
"time"
|
|
131
|
+
]);
|
|
118
132
|
const formatMap = {
|
|
119
133
|
uuid: "uuid",
|
|
120
134
|
email: "email",
|
|
@@ -153,87 +167,6 @@ const typeOptionMap = new Map([
|
|
|
153
167
|
["void", _kubb_core.ast.schemaTypes.void]
|
|
154
168
|
]);
|
|
155
169
|
//#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
170
|
//#region ../../internals/utils/src/casing.ts
|
|
238
171
|
/**
|
|
239
172
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -298,6 +231,42 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
298
231
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
299
232
|
}
|
|
300
233
|
//#endregion
|
|
234
|
+
//#region ../../internals/utils/src/runtime.ts
|
|
235
|
+
/**
|
|
236
|
+
* Returns `true` when the current process is running under Bun.
|
|
237
|
+
*
|
|
238
|
+
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
239
|
+
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
240
|
+
* otherwise look like Node.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* if (isBun()) {
|
|
245
|
+
* await Bun.write(path, data)
|
|
246
|
+
* }
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
function isBun() {
|
|
250
|
+
return typeof Bun !== "undefined";
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region ../../internals/utils/src/fs.ts
|
|
254
|
+
/**
|
|
255
|
+
* Resolves to `true` when the file or directory at `path` exists.
|
|
256
|
+
* Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* if (await exists('./kubb.config.ts')) {
|
|
261
|
+
* const content = await read('./kubb.config.ts')
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
async function exists(path) {
|
|
266
|
+
if (isBun()) return Bun.file(path).exists();
|
|
267
|
+
return (0, node_fs_promises.access)(path).then(() => true, () => false);
|
|
268
|
+
}
|
|
269
|
+
//#endregion
|
|
301
270
|
//#region ../../internals/utils/src/object.ts
|
|
302
271
|
/**
|
|
303
272
|
* Returns `true` when `value` is a plain (non-null, non-array) object.
|
|
@@ -432,6 +401,22 @@ const reservedWords = new Set([
|
|
|
432
401
|
*/
|
|
433
402
|
function isValidVarName(name) {
|
|
434
403
|
if (!name || reservedWords.has(name)) return false;
|
|
404
|
+
return isIdentifier(name);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
408
|
+
*
|
|
409
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
410
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
411
|
+
* deciding whether an object key needs quoting.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```ts
|
|
415
|
+
* isIdentifier('name') // true
|
|
416
|
+
* isIdentifier('x-total')// false
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
function isIdentifier(name) {
|
|
435
420
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
436
421
|
}
|
|
437
422
|
//#endregion
|
|
@@ -543,12 +528,13 @@ var URLPath = class {
|
|
|
543
528
|
* @example
|
|
544
529
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
545
530
|
*/
|
|
546
|
-
toTemplateString({ prefix
|
|
547
|
-
|
|
531
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
532
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
548
533
|
if (i % 2 === 0) return part;
|
|
549
534
|
const param = this.#transformParam(part);
|
|
550
535
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
551
|
-
}).join("")
|
|
536
|
+
}).join("");
|
|
537
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
552
538
|
}
|
|
553
539
|
/**
|
|
554
540
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -686,12 +672,17 @@ async function parseDocument(pathOrApi, { canBundle = true, enablePaths = true }
|
|
|
686
672
|
* ```
|
|
687
673
|
*/
|
|
688
674
|
async function mergeDocuments(pathOrApi) {
|
|
689
|
-
const documents =
|
|
690
|
-
for (const p of pathOrApi) documents.push(await parseDocument(p, {
|
|
675
|
+
const documents = await Promise.all(pathOrApi.map((p) => parseDocument(p, {
|
|
691
676
|
enablePaths: false,
|
|
692
677
|
canBundle: false
|
|
693
|
-
}));
|
|
694
|
-
if (documents.length === 0) throw new Error(
|
|
678
|
+
})));
|
|
679
|
+
if (documents.length === 0) throw new _kubb_core.Diagnostics.Error({
|
|
680
|
+
code: _kubb_core.Diagnostics.code.inputRequired,
|
|
681
|
+
severity: "error",
|
|
682
|
+
message: "No OAS documents were provided for merging.",
|
|
683
|
+
help: "Pass at least one path or document to `input.path`.",
|
|
684
|
+
location: { kind: "config" }
|
|
685
|
+
});
|
|
695
686
|
const seed = {
|
|
696
687
|
openapi: MERGE_OPENAPI_VERSION,
|
|
697
688
|
info: {
|
|
@@ -707,9 +698,9 @@ async function mergeDocuments(pathOrApi) {
|
|
|
707
698
|
* Creates a `Document` from an `AdapterSource`.
|
|
708
699
|
*
|
|
709
700
|
* Handles all three source types:
|
|
710
|
-
* - `{ type: 'path' }`
|
|
711
|
-
* - `{ type: 'paths' }`
|
|
712
|
-
* - `{ type: 'data' }`
|
|
701
|
+
* - `{ type: 'path' }` resolves and bundles a local file path or remote URL.
|
|
702
|
+
* - `{ type: 'paths' }` merges multiple file paths into a single document.
|
|
703
|
+
* - `{ type: 'data' }` parses an inline string (YAML/JSON) or raw object.
|
|
713
704
|
*
|
|
714
705
|
* @example
|
|
715
706
|
* ```ts
|
|
@@ -717,14 +708,31 @@ async function mergeDocuments(pathOrApi) {
|
|
|
717
708
|
* const document = await parseFromConfig({ type: 'data', data: '{"openapi":"3.0.0",...}' })
|
|
718
709
|
* ```
|
|
719
710
|
*/
|
|
720
|
-
function parseFromConfig(source) {
|
|
711
|
+
async function parseFromConfig(source) {
|
|
721
712
|
if (source.type === "data") {
|
|
722
713
|
if (typeof source.data === "object") return parseDocument(structuredClone(source.data));
|
|
723
714
|
return parseDocument(source.data, { canBundle: false });
|
|
724
715
|
}
|
|
725
716
|
if (source.type === "paths") return mergeDocuments(source.paths);
|
|
726
717
|
if (new URLPath(source.path).isURL) return parseDocument(source.path);
|
|
727
|
-
|
|
718
|
+
const resolved = node_path.default.resolve(node_path.default.dirname(source.path), source.path);
|
|
719
|
+
await assertInputExists(resolved);
|
|
720
|
+
return parseDocument(resolved);
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Throws a coded `KUBB_INPUT_NOT_FOUND` diagnostic when a local input path does not exist.
|
|
724
|
+
* URLs are skipped, and a malformed but readable file is left for `parseDocument` to surface
|
|
725
|
+
* its parse error instead.
|
|
726
|
+
*/
|
|
727
|
+
async function assertInputExists(input) {
|
|
728
|
+
if (new URLPath(input).isURL) return;
|
|
729
|
+
if (!await exists(input)) throw new _kubb_core.Diagnostics.Error({
|
|
730
|
+
code: _kubb_core.Diagnostics.code.inputNotFound,
|
|
731
|
+
severity: "error",
|
|
732
|
+
message: `Cannot read the file set in \`input.path\` (or via \`kubb generate PATH\`): ${input}`,
|
|
733
|
+
help: "Check that the path exists and is readable, then set it in `input.path` or pass it as `kubb generate PATH`.",
|
|
734
|
+
location: { kind: "config" }
|
|
735
|
+
});
|
|
728
736
|
}
|
|
729
737
|
/**
|
|
730
738
|
* Validates an OpenAPI document using `oas-normalize` with colorized error output.
|
|
@@ -746,6 +754,7 @@ async function validateDocument(document, { throwOnError = false } = {}) {
|
|
|
746
754
|
}
|
|
747
755
|
//#endregion
|
|
748
756
|
//#region src/refs.ts
|
|
757
|
+
const _refCache = /* @__PURE__ */ new WeakMap();
|
|
749
758
|
/**
|
|
750
759
|
* Resolves a local JSON pointer reference from a document.
|
|
751
760
|
*
|
|
@@ -761,10 +770,31 @@ function resolveRef(document, $ref) {
|
|
|
761
770
|
const origRef = $ref;
|
|
762
771
|
$ref = $ref.trim();
|
|
763
772
|
if ($ref === "") return null;
|
|
764
|
-
if (
|
|
765
|
-
|
|
773
|
+
if (!$ref.startsWith("#")) return null;
|
|
774
|
+
$ref = globalThis.decodeURIComponent($ref.substring(1));
|
|
775
|
+
let docCache = _refCache.get(document);
|
|
776
|
+
if (!docCache) {
|
|
777
|
+
docCache = /* @__PURE__ */ new Map();
|
|
778
|
+
_refCache.set(document, docCache);
|
|
779
|
+
}
|
|
780
|
+
if (docCache.has($ref)) return docCache.get($ref);
|
|
766
781
|
const current = $ref.split("/").filter(Boolean).reduce((obj, key) => obj?.[key], document);
|
|
767
|
-
if (!current)
|
|
782
|
+
if (!current) {
|
|
783
|
+
const diagnostic = {
|
|
784
|
+
code: _kubb_core.Diagnostics.code.refNotFound,
|
|
785
|
+
severity: "error",
|
|
786
|
+
message: `Could not find a definition for ${origRef}.`,
|
|
787
|
+
help: "Add the schema under `components.schemas`, or fix the `$ref`. Run `kubb validate` to check the spec.",
|
|
788
|
+
location: {
|
|
789
|
+
kind: "schema",
|
|
790
|
+
pointer: origRef,
|
|
791
|
+
ref: origRef
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
if (!_kubb_core.Diagnostics.report(diagnostic)) throw new _kubb_core.Diagnostics.Error(diagnostic);
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
docCache.set($ref, current);
|
|
768
798
|
return current;
|
|
769
799
|
}
|
|
770
800
|
/**
|
|
@@ -788,6 +818,35 @@ function dereferenceWithRef(document, schema) {
|
|
|
788
818
|
return schema;
|
|
789
819
|
}
|
|
790
820
|
//#endregion
|
|
821
|
+
//#region src/dialect.ts
|
|
822
|
+
/**
|
|
823
|
+
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
824
|
+
*
|
|
825
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
826
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
827
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
828
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect, `type: ['null', …]`
|
|
829
|
+
* nullability, no discriminator object, binary via `contentEncoding` and reuses
|
|
830
|
+
* the rest unchanged.
|
|
831
|
+
*
|
|
832
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
833
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* ```ts
|
|
837
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
838
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
839
|
+
* ```
|
|
840
|
+
*/
|
|
841
|
+
const oasDialect = _kubb_core.ast.defineSchemaDialect({
|
|
842
|
+
name: "oas",
|
|
843
|
+
isNullable,
|
|
844
|
+
isReference,
|
|
845
|
+
isDiscriminator,
|
|
846
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
847
|
+
resolveRef
|
|
848
|
+
});
|
|
849
|
+
//#endregion
|
|
791
850
|
//#region src/resolvers.ts
|
|
792
851
|
/**
|
|
793
852
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -809,7 +868,16 @@ function resolveServerUrl(server, overrides) {
|
|
|
809
868
|
for (const [key, variable] of Object.entries(server.variables)) {
|
|
810
869
|
const value = overrides?.[key] ?? (variable.default != null ? String(variable.default) : void 0);
|
|
811
870
|
if (value === void 0) continue;
|
|
812
|
-
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new Error(
|
|
871
|
+
if (variable.enum?.length && !variable.enum.some((e) => String(e) === value)) throw new _kubb_core.Diagnostics.Error({
|
|
872
|
+
code: _kubb_core.Diagnostics.code.invalidServerVariable,
|
|
873
|
+
severity: "error",
|
|
874
|
+
message: `Invalid server variable value '${value}' for '${key}' when resolving ${server.url}. Valid values are: ${variable.enum.join(", ")}.`,
|
|
875
|
+
help: `Use one of the allowed enum values, or drop the enum on the '${key}' server variable.`,
|
|
876
|
+
location: {
|
|
877
|
+
kind: "document",
|
|
878
|
+
pointer: "#/servers"
|
|
879
|
+
}
|
|
880
|
+
});
|
|
813
881
|
url = url.replaceAll(`{${key}}`, value);
|
|
814
882
|
}
|
|
815
883
|
return url;
|
|
@@ -822,6 +890,15 @@ function getSchemaType(format) {
|
|
|
822
890
|
return formatMap[format] ?? null;
|
|
823
891
|
}
|
|
824
892
|
/**
|
|
893
|
+
* Whether the parser maps `format` to a dedicated type. True for any `formatMap` entry and the
|
|
894
|
+
* `specialCasedFormats` `convertFormat` handles directly. False means the format falls back to the
|
|
895
|
+
* base type, which is what `KUBB_UNSUPPORTED_FORMAT` flags. Reading both sources keeps the
|
|
896
|
+
* diagnostic in step with the parser as `formatMap` grows.
|
|
897
|
+
*/
|
|
898
|
+
function isHandledFormat(format) {
|
|
899
|
+
return getSchemaType(format) !== null || specialCasedFormats.has(format);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
825
902
|
* Converts an OAS primitive type string to its `PrimitiveSchemaType` equivalent.
|
|
826
903
|
* Numeric types (`number`, `integer`, `bigint`) pass through unchanged. `boolean` maps to `'boolean'`. Everything else becomes `'string'`.
|
|
827
904
|
*/
|
|
@@ -831,12 +908,6 @@ function getPrimitiveType(type) {
|
|
|
831
908
|
return "string";
|
|
832
909
|
}
|
|
833
910
|
/**
|
|
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
911
|
* Returns all parameters for an operation, merging path-level and operation-level entries.
|
|
841
912
|
* Operation-level parameters override path-level ones with the same `in:name` key.
|
|
842
913
|
* `$ref` parameters resolve via `dereferenceWithRef` for backward compatibility.
|
|
@@ -924,7 +995,7 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
924
995
|
/**
|
|
925
996
|
* Flattens a keyword-only `allOf` into its parent schema.
|
|
926
997
|
*
|
|
927
|
-
* Only flattens when every member is a plain fragment
|
|
998
|
+
* Only flattens when every member is a plain fragment, with no `$ref` and no structural keywords
|
|
928
999
|
* (see `structuralKeys`). Outer schema values take precedence over fragment values.
|
|
929
1000
|
* Returns `null` for a `null` input, and the original schema unchanged when flattening is unsafe.
|
|
930
1001
|
*
|
|
@@ -934,7 +1005,7 @@ function getRequestSchema(document, operation, options = {}) {
|
|
|
934
1005
|
* // { type: 'object', properties: {}, description: 'A pet' }
|
|
935
1006
|
*
|
|
936
1007
|
* flattenSchema({ allOf: [{ $ref: '#/components/schemas/Pet' }] })
|
|
937
|
-
* // returned unchanged
|
|
1008
|
+
* // returned unchanged, contains a $ref
|
|
938
1009
|
* ```
|
|
939
1010
|
*/
|
|
940
1011
|
/**
|
|
@@ -960,7 +1031,7 @@ function flattenSchema(schema) {
|
|
|
960
1031
|
/**
|
|
961
1032
|
* Extracts the inline schema from a media-type `content` map.
|
|
962
1033
|
*
|
|
963
|
-
* Prefers `preferredContentType` when given
|
|
1034
|
+
* Prefers `preferredContentType` when given, otherwise uses the first key in the map.
|
|
964
1035
|
* Returns `null` when `content` is absent, the schema is missing, or the schema is a `$ref`.
|
|
965
1036
|
*
|
|
966
1037
|
* @example
|
|
@@ -979,21 +1050,22 @@ function extractSchemaFromContent(content, preferredContentType) {
|
|
|
979
1050
|
/**
|
|
980
1051
|
* Walks a schema tree and collects the names of all `#/components/schemas/<name>` `$ref`s.
|
|
981
1052
|
*/
|
|
982
|
-
function collectRefs(schema
|
|
1053
|
+
function* collectRefs(schema) {
|
|
983
1054
|
if (Array.isArray(schema)) {
|
|
984
|
-
for (const item of schema) collectRefs(item
|
|
985
|
-
return
|
|
1055
|
+
for (const item of schema) yield* collectRefs(item);
|
|
1056
|
+
return;
|
|
986
1057
|
}
|
|
987
1058
|
if (schema && typeof schema === "object") for (const key in schema) {
|
|
988
1059
|
const value = schema[key];
|
|
989
|
-
if (key === "$ref" && typeof value === "string") {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1060
|
+
if (!(key === "$ref" && typeof value === "string")) {
|
|
1061
|
+
yield* collectRefs(value);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
if (value.startsWith("#/components/schemas/")) {
|
|
1065
|
+
const name = value.slice(21);
|
|
1066
|
+
if (name) yield name;
|
|
1067
|
+
}
|
|
995
1068
|
}
|
|
996
|
-
return refs;
|
|
997
1069
|
}
|
|
998
1070
|
/**
|
|
999
1071
|
* Returns a copy of `schemas` topologically sorted by `$ref` dependency.
|
|
@@ -1009,7 +1081,7 @@ function collectRefs(schema, refs = /* @__PURE__ */ new Set()) {
|
|
|
1009
1081
|
*/
|
|
1010
1082
|
function sortSchemas(schemas) {
|
|
1011
1083
|
const deps = /* @__PURE__ */ new Map();
|
|
1012
|
-
for (const [name, schema] of Object.entries(schemas)) deps.set(name,
|
|
1084
|
+
for (const [name, schema] of Object.entries(schemas)) deps.set(name, [...new Set(collectRefs(schema))]);
|
|
1013
1085
|
const sorted = [];
|
|
1014
1086
|
const visited = /* @__PURE__ */ new Set();
|
|
1015
1087
|
function visit(name, stack) {
|
|
@@ -1144,15 +1216,16 @@ function buildSchemaNode(schema, name, nullable, defaultValue) {
|
|
|
1144
1216
|
readOnly: schema.readOnly,
|
|
1145
1217
|
writeOnly: schema.writeOnly,
|
|
1146
1218
|
default: defaultValue,
|
|
1147
|
-
example: schema.example
|
|
1219
|
+
example: schema.example,
|
|
1220
|
+
format: schema.format
|
|
1148
1221
|
};
|
|
1149
1222
|
}
|
|
1150
1223
|
/**
|
|
1151
1224
|
* Returns all request body content type keys for an operation.
|
|
1152
1225
|
*
|
|
1153
|
-
* The requestBody is dereferenced
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1226
|
+
* The requestBody is dereferenced in place when it is a `$ref` (the same mutation that
|
|
1227
|
+
* `getRequestSchema` already performs), so the returned list accurately reflects the
|
|
1228
|
+
* available content types even for referenced bodies.
|
|
1156
1229
|
*
|
|
1157
1230
|
* @example
|
|
1158
1231
|
* ```ts
|
|
@@ -1166,6 +1239,31 @@ function getRequestBodyContentTypes(document, operation) {
|
|
|
1166
1239
|
if (!body) return [];
|
|
1167
1240
|
return body.content ? Object.keys(body.content) : [];
|
|
1168
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Returns all response content type keys for an operation at a given status code.
|
|
1244
|
+
*
|
|
1245
|
+
* Response `$ref`s are resolved in place first (the same mutation `getResponseSchema` performs),
|
|
1246
|
+
* so the returned list reflects the available content types even for referenced responses.
|
|
1247
|
+
*
|
|
1248
|
+
* @example
|
|
1249
|
+
* ```ts
|
|
1250
|
+
* getResponseBodyContentTypes(document, operation, 200)
|
|
1251
|
+
* // ['application/json', 'application/xml']
|
|
1252
|
+
* ```
|
|
1253
|
+
*/
|
|
1254
|
+
function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
1255
|
+
if (operation.schema.responses) {
|
|
1256
|
+
const responses = operation.schema.responses;
|
|
1257
|
+
for (const key in responses) {
|
|
1258
|
+
const schema = responses[key];
|
|
1259
|
+
if (schema && isReference(schema)) responses[key] = resolveRef(document, schema.$ref);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1263
|
+
if (!responseObj || typeof responseObj !== "object" || isReference(responseObj)) return [];
|
|
1264
|
+
const body = responseObj;
|
|
1265
|
+
return body.content ? Object.keys(body.content) : [];
|
|
1266
|
+
}
|
|
1169
1267
|
//#endregion
|
|
1170
1268
|
//#region src/parser.ts
|
|
1171
1269
|
/**
|
|
@@ -1194,9 +1292,9 @@ function normalizeArrayEnum(schema) {
|
|
|
1194
1292
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1195
1293
|
* made possible by hoisting of function declarations.
|
|
1196
1294
|
*
|
|
1197
|
-
* @
|
|
1295
|
+
* @internal
|
|
1198
1296
|
*/
|
|
1199
|
-
function createSchemaParser(ctx) {
|
|
1297
|
+
function createSchemaParser(ctx, dialect = oasDialect) {
|
|
1200
1298
|
const document = ctx.document;
|
|
1201
1299
|
/**
|
|
1202
1300
|
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
@@ -1204,6 +1302,15 @@ function createSchemaParser(ctx) {
|
|
|
1204
1302
|
*/
|
|
1205
1303
|
const resolvingRefs = /* @__PURE__ */ new Set();
|
|
1206
1304
|
/**
|
|
1305
|
+
* Cache of `$ref` schemas already resolved in this parser instance, keyed by ref path.
|
|
1306
|
+
*
|
|
1307
|
+
* Without it, a shared schema (e.g. `customer`) is re-expanded for every `$ref` that points at
|
|
1308
|
+
* it. In cross-referenced specs like Stripe (~1400 schemas) that becomes exponential blowup,
|
|
1309
|
+
* since one schema can be referenced from dozens of parents, each re-walking its whole subtree.
|
|
1310
|
+
* Memoizing by ref path drops the work from O(2^depth) to O(N) unique schema names.
|
|
1311
|
+
*/
|
|
1312
|
+
const resolvedRefCache = /* @__PURE__ */ new Map();
|
|
1313
|
+
/**
|
|
1207
1314
|
* Converts a `$ref` schema into a `RefSchemaNode`.
|
|
1208
1315
|
*
|
|
1209
1316
|
* The resolved schema is stored in `node.schema`. Usage-site sibling fields
|
|
@@ -1212,20 +1319,26 @@ function createSchemaParser(ctx) {
|
|
|
1212
1319
|
* Circular refs are detected via `resolvingRefs` and leave `schema` as `undefined`.
|
|
1213
1320
|
*/
|
|
1214
1321
|
function convertRef({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1215
|
-
let resolvedSchema;
|
|
1322
|
+
let resolvedSchema = null;
|
|
1216
1323
|
const refPath = schema.$ref;
|
|
1217
|
-
if (refPath && !resolvingRefs.has(refPath))
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1324
|
+
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1325
|
+
if (!resolvedRefCache.has(refPath)) {
|
|
1326
|
+
try {
|
|
1327
|
+
const referenced = dialect.resolveRef(document, refPath);
|
|
1328
|
+
if (referenced) {
|
|
1329
|
+
resolvingRefs.add(refPath);
|
|
1330
|
+
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
1331
|
+
resolvingRefs.delete(refPath);
|
|
1332
|
+
}
|
|
1333
|
+
} catch {}
|
|
1334
|
+
resolvedRefCache.set(refPath, resolvedSchema);
|
|
1223
1335
|
}
|
|
1224
|
-
|
|
1336
|
+
resolvedSchema = resolvedRefCache.get(refPath) ?? null;
|
|
1337
|
+
}
|
|
1225
1338
|
return _kubb_core.ast.createSchema({
|
|
1226
1339
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1227
1340
|
type: "ref",
|
|
1228
|
-
name:
|
|
1341
|
+
name: (0, _kubb_ast_utils.extractRefName)(schema.$ref),
|
|
1229
1342
|
ref: schema.$ref,
|
|
1230
1343
|
schema: resolvedSchema
|
|
1231
1344
|
});
|
|
@@ -1238,7 +1351,7 @@ function createSchemaParser(ctx) {
|
|
|
1238
1351
|
const [memberSchema] = schema.allOf;
|
|
1239
1352
|
const memberNode = parseSchema({
|
|
1240
1353
|
schema: memberSchema,
|
|
1241
|
-
name
|
|
1354
|
+
name
|
|
1242
1355
|
}, rawOptions);
|
|
1243
1356
|
const { kind: _kind, ...memberNodeProps } = memberNode;
|
|
1244
1357
|
const mergedNullable = nullable || memberNode.nullable || void 0;
|
|
@@ -1254,21 +1367,22 @@ function createSchemaParser(ctx) {
|
|
|
1254
1367
|
writeOnly: schema.writeOnly ?? memberNode.writeOnly,
|
|
1255
1368
|
default: mergedDefault,
|
|
1256
1369
|
example: schema.example ?? memberNode.example,
|
|
1257
|
-
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0)
|
|
1370
|
+
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
1371
|
+
format: schema.format ?? memberNode.format
|
|
1258
1372
|
});
|
|
1259
1373
|
}
|
|
1260
1374
|
const filteredDiscriminantValues = [];
|
|
1261
1375
|
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;
|
|
1376
|
+
if (!dialect.isReference(item) || !name) return true;
|
|
1377
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1378
|
+
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1265
1379
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1266
1380
|
if (!parentUnion) return true;
|
|
1267
1381
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1268
|
-
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1382
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1269
1383
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1270
1384
|
if (inOneOf || inMapping) {
|
|
1271
|
-
const discriminatorValue =
|
|
1385
|
+
const discriminatorValue = (0, _kubb_ast_utils.findDiscriminator)(deref.discriminator.mapping, childRef);
|
|
1272
1386
|
if (discriminatorValue) filteredDiscriminantValues.push({
|
|
1273
1387
|
propertyName: deref.discriminator.propertyName,
|
|
1274
1388
|
value: discriminatorValue
|
|
@@ -1276,22 +1390,28 @@ function createSchemaParser(ctx) {
|
|
|
1276
1390
|
return false;
|
|
1277
1391
|
}
|
|
1278
1392
|
return true;
|
|
1279
|
-
}).map((s) => parseSchema({
|
|
1393
|
+
}).map((s) => parseSchema({
|
|
1394
|
+
schema: s,
|
|
1395
|
+
name
|
|
1396
|
+
}, rawOptions));
|
|
1280
1397
|
const syntheticStart = allOfMembers.length;
|
|
1281
1398
|
if (Array.isArray(schema.required) && schema.required.length) {
|
|
1282
1399
|
const outerKeys = schema.properties ? new Set(Object.keys(schema.properties)) : /* @__PURE__ */ new Set();
|
|
1283
1400
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1284
1401
|
if (missingRequired.length) {
|
|
1285
1402
|
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] : [];
|
|
1403
|
+
if (!dialect.isReference(item)) return [item];
|
|
1404
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1405
|
+
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1289
1406
|
});
|
|
1290
1407
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1291
|
-
allOfMembers.push(parseSchema({
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1408
|
+
allOfMembers.push(parseSchema({
|
|
1409
|
+
schema: {
|
|
1410
|
+
properties: { [key]: resolved.properties[key] },
|
|
1411
|
+
required: [key]
|
|
1412
|
+
},
|
|
1413
|
+
name
|
|
1414
|
+
}, rawOptions));
|
|
1295
1415
|
break;
|
|
1296
1416
|
}
|
|
1297
1417
|
}
|
|
@@ -1306,7 +1426,7 @@ function createSchemaParser(ctx) {
|
|
|
1306
1426
|
}));
|
|
1307
1427
|
return _kubb_core.ast.createSchema({
|
|
1308
1428
|
type: "intersection",
|
|
1309
|
-
members: [..._kubb_core.ast.
|
|
1429
|
+
members: [..._kubb_core.ast.mergeAdjacentObjectsLazy(allOfMembers.slice(0, syntheticStart)), ..._kubb_core.ast.mergeAdjacentObjectsLazy(allOfMembers.slice(syntheticStart))],
|
|
1310
1430
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1311
1431
|
});
|
|
1312
1432
|
}
|
|
@@ -1327,10 +1447,10 @@ function createSchemaParser(ctx) {
|
|
|
1327
1447
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1328
1448
|
const unionBase = {
|
|
1329
1449
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1330
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1450
|
+
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1331
1451
|
strategy
|
|
1332
1452
|
};
|
|
1333
|
-
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1453
|
+
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1334
1454
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1335
1455
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1336
1456
|
return parseSchema({
|
|
@@ -1340,9 +1460,12 @@ function createSchemaParser(ctx) {
|
|
|
1340
1460
|
})() : void 0;
|
|
1341
1461
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1342
1462
|
const members = unionMembers.map((s) => {
|
|
1343
|
-
const ref = isReference(s) ? s.$ref : void 0;
|
|
1344
|
-
const discriminatorValue =
|
|
1345
|
-
const memberNode = parseSchema({
|
|
1463
|
+
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1464
|
+
const discriminatorValue = (0, _kubb_ast_utils.findDiscriminator)(discriminator?.mapping, ref);
|
|
1465
|
+
const memberNode = parseSchema({
|
|
1466
|
+
schema: s,
|
|
1467
|
+
name
|
|
1468
|
+
}, rawOptions);
|
|
1346
1469
|
if (!discriminatorValue || !discriminator) return memberNode;
|
|
1347
1470
|
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_core.ast.setDiscriminatorEnum({
|
|
1348
1471
|
node: sharedPropertiesNode,
|
|
@@ -1372,7 +1495,10 @@ function createSchemaParser(ctx) {
|
|
|
1372
1495
|
return _kubb_core.ast.createSchema({
|
|
1373
1496
|
type: "union",
|
|
1374
1497
|
...unionBase,
|
|
1375
|
-
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1498
|
+
members: _kubb_core.ast.simplifyUnion(unionMembers.map((s) => parseSchema({
|
|
1499
|
+
schema: s,
|
|
1500
|
+
name
|
|
1501
|
+
}, rawOptions)))
|
|
1376
1502
|
});
|
|
1377
1503
|
}
|
|
1378
1504
|
/**
|
|
@@ -1386,7 +1512,8 @@ function createSchemaParser(ctx) {
|
|
|
1386
1512
|
name,
|
|
1387
1513
|
title: schema.title,
|
|
1388
1514
|
description: schema.description,
|
|
1389
|
-
deprecated: schema.deprecated
|
|
1515
|
+
deprecated: schema.deprecated,
|
|
1516
|
+
format: schema.format
|
|
1390
1517
|
});
|
|
1391
1518
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1392
1519
|
return _kubb_core.ast.createSchema({
|
|
@@ -1476,6 +1603,15 @@ function createSchemaParser(ctx) {
|
|
|
1476
1603
|
}, rawOptions);
|
|
1477
1604
|
const nullInEnum = schema.enum.includes(null);
|
|
1478
1605
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1606
|
+
if (nullInEnum && filteredValues.length === 0) return _kubb_core.ast.createSchema({
|
|
1607
|
+
type: "null",
|
|
1608
|
+
primitive: "null",
|
|
1609
|
+
name,
|
|
1610
|
+
title: schema.title,
|
|
1611
|
+
description: schema.description,
|
|
1612
|
+
deprecated: schema.deprecated,
|
|
1613
|
+
format: schema.format
|
|
1614
|
+
});
|
|
1479
1615
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1480
1616
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1481
1617
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1490,7 +1626,8 @@ function createSchemaParser(ctx) {
|
|
|
1490
1626
|
readOnly: schema.readOnly,
|
|
1491
1627
|
writeOnly: schema.writeOnly,
|
|
1492
1628
|
default: enumDefault,
|
|
1493
|
-
example: schema.example
|
|
1629
|
+
example: schema.example,
|
|
1630
|
+
format: schema.format
|
|
1494
1631
|
};
|
|
1495
1632
|
const extensionKey = enumExtensionKeys.find((key) => key in schema);
|
|
1496
1633
|
if (extensionKey || enumPrimitive === "number" || enumPrimitive === "integer" || enumPrimitive === "boolean") {
|
|
@@ -1524,20 +1661,23 @@ function createSchemaParser(ctx) {
|
|
|
1524
1661
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1525
1662
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1526
1663
|
const resolvedPropSchema = propSchema;
|
|
1527
|
-
const propNullable = isNullable(resolvedPropSchema);
|
|
1664
|
+
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
1528
1665
|
const propNode = parseSchema({
|
|
1529
1666
|
schema: resolvedPropSchema,
|
|
1530
|
-
name:
|
|
1667
|
+
name: (0, _kubb_ast_utils.childName)(name, propName)
|
|
1531
1668
|
}, rawOptions);
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1669
|
+
const schemaNode = (() => {
|
|
1670
|
+
const node = _kubb_core.ast.setEnumName(propNode, name, propName, options.enumSuffix);
|
|
1671
|
+
const tupleNode = _kubb_core.ast.narrowSchema(node, "tuple");
|
|
1672
|
+
if (tupleNode?.items) {
|
|
1673
|
+
const namedItems = tupleNode.items.map((item) => _kubb_core.ast.setEnumName(item, name, propName, options.enumSuffix));
|
|
1674
|
+
if (namedItems.some((item, i) => item !== tupleNode.items[i])) return {
|
|
1675
|
+
...tupleNode,
|
|
1676
|
+
items: namedItems
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
return node;
|
|
1680
|
+
})();
|
|
1541
1681
|
return _kubb_core.ast.createProperty({
|
|
1542
1682
|
name: propName,
|
|
1543
1683
|
schema: {
|
|
@@ -1548,11 +1688,12 @@ function createSchemaParser(ctx) {
|
|
|
1548
1688
|
});
|
|
1549
1689
|
}) : [];
|
|
1550
1690
|
const additionalProperties = schema.additionalProperties;
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1691
|
+
const additionalPropertiesNode = (() => {
|
|
1692
|
+
if (additionalProperties === true) return true;
|
|
1693
|
+
if (additionalProperties === false) return false;
|
|
1694
|
+
if (additionalProperties && Object.keys(additionalProperties).length > 0) return parseSchema({ schema: additionalProperties }, rawOptions);
|
|
1695
|
+
if (additionalProperties) return _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1696
|
+
})();
|
|
1556
1697
|
const rawPatternProperties = "patternProperties" in schema ? schema.patternProperties : void 0;
|
|
1557
1698
|
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
1699
|
const objectNode = _kubb_core.ast.createSchema({
|
|
@@ -1565,10 +1706,10 @@ function createSchemaParser(ctx) {
|
|
|
1565
1706
|
maxProperties: schema.maxProperties,
|
|
1566
1707
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1567
1708
|
});
|
|
1568
|
-
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1709
|
+
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1569
1710
|
const discPropName = schema.discriminator.propertyName;
|
|
1570
1711
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1571
|
-
const enumName = name ?
|
|
1712
|
+
const enumName = name ? (0, _kubb_ast_utils.enumPropName)(name, discPropName, options.enumSuffix) : void 0;
|
|
1572
1713
|
return _kubb_core.ast.setDiscriminatorEnum({
|
|
1573
1714
|
node: objectNode,
|
|
1574
1715
|
propertyName: discPropName,
|
|
@@ -1599,7 +1740,7 @@ function createSchemaParser(ctx) {
|
|
|
1599
1740
|
*/
|
|
1600
1741
|
function convertArray({ schema, name, nullable, defaultValue, rawOptions, options }) {
|
|
1601
1742
|
const rawItems = schema.items;
|
|
1602
|
-
const itemName = rawItems?.enum?.length && name ?
|
|
1743
|
+
const itemName = rawItems?.enum?.length && name ? (0, _kubb_ast_utils.enumPropName)(null, name, options.enumSuffix) : name;
|
|
1603
1744
|
const items = rawItems ? [parseSchema({
|
|
1604
1745
|
schema: rawItems,
|
|
1605
1746
|
name: itemName
|
|
@@ -1663,15 +1804,148 @@ function createSchemaParser(ctx) {
|
|
|
1663
1804
|
title: schema.title,
|
|
1664
1805
|
description: schema.description,
|
|
1665
1806
|
deprecated: schema.deprecated,
|
|
1666
|
-
nullable
|
|
1807
|
+
nullable,
|
|
1808
|
+
format: schema.format
|
|
1667
1809
|
});
|
|
1668
1810
|
}
|
|
1669
1811
|
/**
|
|
1812
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1813
|
+
* into a `blob` node.
|
|
1814
|
+
*/
|
|
1815
|
+
function convertBlob({ schema, name, nullable, defaultValue }) {
|
|
1816
|
+
return _kubb_core.ast.createSchema({
|
|
1817
|
+
type: "blob",
|
|
1818
|
+
primitive: "string",
|
|
1819
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1824
|
+
*
|
|
1825
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
|
|
1826
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1827
|
+
*/
|
|
1828
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1829
|
+
const types = schema.type;
|
|
1830
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1831
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1832
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1833
|
+
return _kubb_core.ast.createSchema({
|
|
1834
|
+
type: "union",
|
|
1835
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1836
|
+
schema: {
|
|
1837
|
+
...schema,
|
|
1838
|
+
type: t
|
|
1839
|
+
},
|
|
1840
|
+
name
|
|
1841
|
+
}, rawOptions)),
|
|
1842
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1847
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1848
|
+
* `type`. The first matching rule that produces a node wins. See {@link SchemaRule} for the
|
|
1849
|
+
* match/convert/fall-through contract.
|
|
1850
|
+
*/
|
|
1851
|
+
const schemaRules = [
|
|
1852
|
+
{
|
|
1853
|
+
name: "ref",
|
|
1854
|
+
match: ({ schema }) => dialect.isReference(schema),
|
|
1855
|
+
convert: convertRef
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
name: "allOf",
|
|
1859
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1860
|
+
convert: convertAllOf
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
name: "union",
|
|
1864
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1865
|
+
convert: convertUnion
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
name: "const",
|
|
1869
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1870
|
+
convert: convertConst
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
name: "format",
|
|
1874
|
+
match: ({ schema }) => !!schema.format,
|
|
1875
|
+
convert: convertFormat
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
name: "blob",
|
|
1879
|
+
match: ({ schema }) => dialect.isBinary(schema),
|
|
1880
|
+
convert: convertBlob
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
name: "multi-type",
|
|
1884
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1885
|
+
convert: convertMultiType
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
name: "constrained-string",
|
|
1889
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1890
|
+
convert: convertString
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
name: "constrained-number",
|
|
1894
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1895
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
name: "enum",
|
|
1899
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1900
|
+
convert: convertEnum
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
name: "object",
|
|
1904
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1905
|
+
convert: convertObject
|
|
1906
|
+
},
|
|
1907
|
+
{
|
|
1908
|
+
name: "tuple",
|
|
1909
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1910
|
+
convert: convertTuple
|
|
1911
|
+
},
|
|
1912
|
+
{
|
|
1913
|
+
name: "array",
|
|
1914
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1915
|
+
convert: convertArray
|
|
1916
|
+
},
|
|
1917
|
+
{
|
|
1918
|
+
name: "string",
|
|
1919
|
+
match: ({ type }) => type === "string",
|
|
1920
|
+
convert: convertString
|
|
1921
|
+
},
|
|
1922
|
+
{
|
|
1923
|
+
name: "number",
|
|
1924
|
+
match: ({ type }) => type === "number",
|
|
1925
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
name: "integer",
|
|
1929
|
+
match: ({ type }) => type === "integer",
|
|
1930
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1931
|
+
},
|
|
1932
|
+
{
|
|
1933
|
+
name: "boolean",
|
|
1934
|
+
match: ({ type }) => type === "boolean",
|
|
1935
|
+
convert: convertBoolean
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
name: "null",
|
|
1939
|
+
match: ({ type }) => type === "null",
|
|
1940
|
+
convert: convertNull
|
|
1941
|
+
}
|
|
1942
|
+
];
|
|
1943
|
+
/**
|
|
1670
1944
|
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1671
1945
|
*
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
1674
|
-
*
|
|
1946
|
+
* Builds the per-schema context, then runs it through the ordered {@link schemaRules} table
|
|
1947
|
+
* via {@link ast.dispatch}. When no rule produces a node, falls back to the configured
|
|
1948
|
+
* `emptySchemaType`.
|
|
1675
1949
|
*/
|
|
1676
1950
|
function parseSchema({ schema, name }, rawOptions) {
|
|
1677
1951
|
const options = {
|
|
@@ -1683,75 +1957,40 @@ function createSchemaParser(ctx) {
|
|
|
1683
1957
|
schema: flattenedSchema,
|
|
1684
1958
|
name
|
|
1685
1959
|
}, 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 = {
|
|
1960
|
+
const nullable = dialect.isNullable(schema) || void 0;
|
|
1961
|
+
const schemaCtx = {
|
|
1690
1962
|
schema,
|
|
1691
1963
|
name,
|
|
1692
1964
|
nullable,
|
|
1693
|
-
defaultValue,
|
|
1694
|
-
type,
|
|
1965
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1966
|
+
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1695
1967
|
rawOptions,
|
|
1696
1968
|
options
|
|
1697
1969
|
};
|
|
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);
|
|
1970
|
+
const node = _kubb_core.ast.dispatch(schemaRules, schemaCtx);
|
|
1971
|
+
if (node) return node;
|
|
1739
1972
|
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1740
1973
|
return _kubb_core.ast.createSchema({
|
|
1741
1974
|
type: emptyType,
|
|
1742
1975
|
name,
|
|
1743
1976
|
title: schema.title,
|
|
1744
|
-
description: schema.description
|
|
1977
|
+
description: schema.description,
|
|
1978
|
+
format: schema.format
|
|
1745
1979
|
});
|
|
1746
1980
|
}
|
|
1747
1981
|
/**
|
|
1748
1982
|
* Converts a dereferenced OAS parameter object into a `ParameterNode`.
|
|
1749
1983
|
*/
|
|
1750
|
-
function parseParameter(options, param) {
|
|
1984
|
+
function parseParameter(options, param, parentName) {
|
|
1751
1985
|
const required = param["required"] ?? false;
|
|
1752
|
-
const
|
|
1986
|
+
const paramName = param["name"];
|
|
1987
|
+
const schemaName = parentName && paramName ? pascalCase(`${parentName} ${paramName}`) : void 0;
|
|
1988
|
+
const schema = param["schema"] ? parseSchema({
|
|
1989
|
+
schema: param["schema"],
|
|
1990
|
+
name: schemaName
|
|
1991
|
+
}, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.unknownType) });
|
|
1753
1992
|
return _kubb_core.ast.createParameter({
|
|
1754
|
-
name:
|
|
1993
|
+
name: paramName,
|
|
1755
1994
|
in: param["in"],
|
|
1756
1995
|
schema: {
|
|
1757
1996
|
...schema,
|
|
@@ -1788,27 +2027,33 @@ function createSchemaParser(ctx) {
|
|
|
1788
2027
|
* `$ref` entries are skipped since their flags live on the dereferenced target.
|
|
1789
2028
|
*/
|
|
1790
2029
|
function collectPropertyKeysByFlag(schema, flag) {
|
|
1791
|
-
if (!schema?.properties) return
|
|
2030
|
+
if (!schema?.properties) return null;
|
|
1792
2031
|
const keys = [];
|
|
1793
2032
|
for (const key in schema.properties) {
|
|
1794
2033
|
const prop = schema.properties[key];
|
|
1795
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
2034
|
+
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
1796
2035
|
}
|
|
1797
|
-
return keys.length ? keys :
|
|
2036
|
+
return keys.length ? keys : null;
|
|
1798
2037
|
}
|
|
1799
2038
|
/**
|
|
1800
2039
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1801
2040
|
*/
|
|
1802
2041
|
function parseOperation(options, operation) {
|
|
1803
|
-
const
|
|
2042
|
+
const operationId = operation.getOperationId();
|
|
2043
|
+
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
2044
|
+
const parameters = getParameters(document, operation).map((param) => parseParameter(options, param, operationName));
|
|
1804
2045
|
const allContentTypes = ctx.contentType ? [ctx.contentType] : getRequestBodyContentTypes(document, operation);
|
|
1805
2046
|
const requestBodyMeta = getRequestBodyMeta(operation);
|
|
2047
|
+
const requestBodyName = operationName ? `${operationName}Request` : void 0;
|
|
1806
2048
|
const content = allContentTypes.flatMap((ct) => {
|
|
1807
2049
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
1808
2050
|
if (!schema) return [];
|
|
1809
2051
|
return [{
|
|
1810
2052
|
contentType: ct,
|
|
1811
|
-
schema: _kubb_core.ast.syncOptionality(parseSchema({
|
|
2053
|
+
schema: _kubb_core.ast.syncOptionality(parseSchema({
|
|
2054
|
+
schema,
|
|
2055
|
+
name: requestBodyName
|
|
2056
|
+
}, options), requestBodyMeta.required),
|
|
1812
2057
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
1813
2058
|
}];
|
|
1814
2059
|
});
|
|
@@ -1819,21 +2064,36 @@ function createSchemaParser(ctx) {
|
|
|
1819
2064
|
} : void 0;
|
|
1820
2065
|
const responses = operation.getResponseStatusCodes().map((statusCode) => {
|
|
1821
2066
|
const responseObj = operation.getResponseByStatusCode(statusCode);
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1824
|
-
const
|
|
1825
|
-
|
|
2067
|
+
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
2068
|
+
const { description } = getResponseMeta(responseObj);
|
|
2069
|
+
const parseEntrySchema = (contentType) => {
|
|
2070
|
+
const raw = getResponseSchema(document, operation, statusCode, { contentType });
|
|
2071
|
+
return {
|
|
2072
|
+
schema: raw && Object.keys(raw).length > 0 ? parseSchema({
|
|
2073
|
+
schema: raw,
|
|
2074
|
+
name: responseName
|
|
2075
|
+
}, options) : _kubb_core.ast.createSchema({ type: typeOptionMap.get(options.emptySchemaType) }),
|
|
2076
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
2077
|
+
};
|
|
2078
|
+
};
|
|
2079
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
|
|
2080
|
+
contentType,
|
|
2081
|
+
...parseEntrySchema(contentType)
|
|
2082
|
+
}));
|
|
2083
|
+
if (content.length === 0) content.push({
|
|
2084
|
+
contentType: operation.contentType || "application/json",
|
|
2085
|
+
...parseEntrySchema(ctx.contentType)
|
|
2086
|
+
});
|
|
1826
2087
|
return _kubb_core.ast.createResponse({
|
|
1827
2088
|
statusCode,
|
|
1828
2089
|
description,
|
|
1829
|
-
|
|
1830
|
-
mediaType,
|
|
1831
|
-
keysToOmit: collectPropertyKeysByFlag(responseSchema, "writeOnly")
|
|
2090
|
+
content
|
|
1832
2091
|
});
|
|
1833
2092
|
});
|
|
1834
2093
|
const urlPath = new URLPath(operation.path);
|
|
1835
2094
|
return _kubb_core.ast.createOperation({
|
|
1836
|
-
operationId
|
|
2095
|
+
operationId,
|
|
2096
|
+
protocol: "http",
|
|
1837
2097
|
method: operation.method.toUpperCase(),
|
|
1838
2098
|
path: urlPath.path,
|
|
1839
2099
|
tags: operation.getTags().map((tag) => tag.name),
|
|
@@ -1851,59 +2111,336 @@ function createSchemaParser(ctx) {
|
|
|
1851
2111
|
parseParameter
|
|
1852
2112
|
};
|
|
1853
2113
|
}
|
|
2114
|
+
//#endregion
|
|
2115
|
+
//#region src/discriminator.ts
|
|
2116
|
+
/**
|
|
2117
|
+
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
2118
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
2119
|
+
*
|
|
2120
|
+
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
2121
|
+
* discriminator parents) rather than on all schemas at once.
|
|
2122
|
+
*/
|
|
2123
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
2124
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
2125
|
+
for (const schema of schemas) {
|
|
2126
|
+
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
2127
|
+
if (!unionNode) {
|
|
2128
|
+
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
2129
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
2130
|
+
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
2131
|
+
if (u) {
|
|
2132
|
+
unionNode = u;
|
|
2133
|
+
break;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
2138
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
2139
|
+
for (const member of members) {
|
|
2140
|
+
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
2141
|
+
if (!intersectionNode?.members) continue;
|
|
2142
|
+
let refNode = null;
|
|
2143
|
+
let objNode = null;
|
|
2144
|
+
for (const m of intersectionNode.members) {
|
|
2145
|
+
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
2146
|
+
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
2147
|
+
}
|
|
2148
|
+
if (!refNode?.name || !objNode) continue;
|
|
2149
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
2150
|
+
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : null;
|
|
2151
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
2152
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
2153
|
+
if (!enumValues.length) continue;
|
|
2154
|
+
const existing = childMap.get(refNode.name);
|
|
2155
|
+
if (!existing) {
|
|
2156
|
+
childMap.set(refNode.name, {
|
|
2157
|
+
propertyName: discriminatorPropertyName,
|
|
2158
|
+
enumValues: [...enumValues]
|
|
2159
|
+
});
|
|
2160
|
+
continue;
|
|
2161
|
+
}
|
|
2162
|
+
existing.enumValues.push(...enumValues);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return childMap;
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
2169
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
2170
|
+
* without buffering all schemas.
|
|
2171
|
+
*/
|
|
2172
|
+
function patchDiscriminatorNode(node, entry) {
|
|
2173
|
+
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
2174
|
+
if (!objectNode) return node;
|
|
2175
|
+
const { propertyName, enumValues } = entry;
|
|
2176
|
+
const enumSchema = _kubb_core.ast.createSchema({
|
|
2177
|
+
type: "enum",
|
|
2178
|
+
enumValues
|
|
2179
|
+
});
|
|
2180
|
+
const newProp = _kubb_core.ast.createProperty({
|
|
2181
|
+
name: propertyName,
|
|
2182
|
+
required: true,
|
|
2183
|
+
schema: enumSchema
|
|
2184
|
+
});
|
|
2185
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
2186
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
2187
|
+
return {
|
|
2188
|
+
...objectNode,
|
|
2189
|
+
properties: newProperties
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
//#endregion
|
|
2193
|
+
//#region src/schemaDiagnostics.ts
|
|
2194
|
+
/**
|
|
2195
|
+
* Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
|
|
2196
|
+
* top-level schema. Walks the node the parser produced during `preScan`, threading the RFC 6901
|
|
2197
|
+
* pointer as it descends so a nested field reports against its full path
|
|
2198
|
+
* (`#/components/schemas/Pet/properties/owner/properties/name`). Refs are not followed, so the
|
|
2199
|
+
* resolved schema is reported under its own walk. Reports land in the active build run, are a
|
|
2200
|
+
* no-op outside one, and repeats are deduped by the build.
|
|
2201
|
+
*/
|
|
2202
|
+
function reportSchemaDiagnostics({ node, name }) {
|
|
2203
|
+
visit(node, `#/components/schemas/${escapePointerToken(name)}`);
|
|
2204
|
+
}
|
|
1854
2205
|
/**
|
|
1855
|
-
*
|
|
2206
|
+
* Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
|
|
2207
|
+
* property name with those characters maps to a distinct pointer instead of colliding in the dedupe.
|
|
2208
|
+
*/
|
|
2209
|
+
function escapePointerToken(token) {
|
|
2210
|
+
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2211
|
+
}
|
|
2212
|
+
function visit(node, pointer) {
|
|
2213
|
+
if (node.deprecated) _kubb_core.Diagnostics.report({
|
|
2214
|
+
code: _kubb_core.Diagnostics.code.deprecated,
|
|
2215
|
+
severity: "info",
|
|
2216
|
+
message: "This schema is marked as deprecated.",
|
|
2217
|
+
location: {
|
|
2218
|
+
kind: "schema",
|
|
2219
|
+
pointer
|
|
2220
|
+
}
|
|
2221
|
+
});
|
|
2222
|
+
if (typeof node.format === "string" && !isHandledFormat(node.format)) _kubb_core.Diagnostics.report({
|
|
2223
|
+
code: _kubb_core.Diagnostics.code.unsupportedFormat,
|
|
2224
|
+
severity: "warning",
|
|
2225
|
+
message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
|
|
2226
|
+
help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
|
|
2227
|
+
location: {
|
|
2228
|
+
kind: "schema",
|
|
2229
|
+
pointer
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
if (node.type === "object") {
|
|
2233
|
+
for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
|
|
2234
|
+
if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
if (node.type === "array") {
|
|
2238
|
+
for (const item of node.items ?? []) visit(item, `${pointer}/items`);
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2241
|
+
if (node.type === "tuple") {
|
|
2242
|
+
for (const [index, item] of (node.items ?? []).entries()) visit(item, `${pointer}/items/${index}`);
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
if (node.type === "union" || node.type === "intersection") for (const [index, member] of (node.members ?? []).entries()) visit(member, `${pointer}/members/${index}`);
|
|
2246
|
+
}
|
|
2247
|
+
//#endregion
|
|
2248
|
+
//#region src/stream.ts
|
|
2249
|
+
/**
|
|
2250
|
+
* Builds the deduplication plan from the already-parsed top-level schema nodes plus a
|
|
2251
|
+
* single extra parse pass over operations (so duplicates in request/response bodies are seen).
|
|
2252
|
+
*
|
|
2253
|
+
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2254
|
+
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2255
|
+
* name (collision-resolved against existing component names); shapes without a name stay inline.
|
|
2256
|
+
*/
|
|
2257
|
+
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2258
|
+
const circularSchemas = new Set(circularNames);
|
|
2259
|
+
const usedNames = new Set(schemaNames);
|
|
2260
|
+
return _kubb_core.ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2261
|
+
isCandidate: (node) => {
|
|
2262
|
+
if (node.type === "enum") return true;
|
|
2263
|
+
if (node.type !== "object") return false;
|
|
2264
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2265
|
+
return !_kubb_core.ast.containsCircularRef(node, { circularSchemas });
|
|
2266
|
+
},
|
|
2267
|
+
nameFor: (node) => {
|
|
2268
|
+
const base = node.name;
|
|
2269
|
+
if (!base) return null;
|
|
2270
|
+
let name = base;
|
|
2271
|
+
let counter = 2;
|
|
2272
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2273
|
+
usedNames.add(name);
|
|
2274
|
+
return name;
|
|
2275
|
+
},
|
|
2276
|
+
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
* Reads the server URL from the document's `servers` array at `serverIndex`,
|
|
2281
|
+
* interpolating any `serverVariables` into the URL template.
|
|
2282
|
+
*
|
|
2283
|
+
* Returns `null` when `serverIndex` is omitted or out of range.
|
|
2284
|
+
*
|
|
2285
|
+
* @example Resolve the first server
|
|
2286
|
+
* `resolveBaseUrl({ document, serverIndex: 0 })`
|
|
1856
2287
|
*
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
|
|
2288
|
+
* @example Override a path variable
|
|
2289
|
+
* `resolveBaseUrl({ document, serverIndex: 0, serverVariables: { version: 'v2' } })`
|
|
2290
|
+
*/
|
|
2291
|
+
function resolveBaseUrl({ document, serverIndex, serverVariables }) {
|
|
2292
|
+
const server = serverIndex !== void 0 ? document.servers?.at(serverIndex) : void 0;
|
|
2293
|
+
return server?.url ? resolveServerUrl(server, serverVariables) : null;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Parses every schema once to build the lookup structures that streaming needs upfront.
|
|
1860
2297
|
*
|
|
1861
|
-
*
|
|
2298
|
+
* Three things happen in this single pass:
|
|
2299
|
+
* - `refAliasMap` records schemas that are pure `$ref` aliases so the streaming pass can inline them.
|
|
2300
|
+
* - `enumNames` collects the names of every enum schema so plugins skip re-scanning the stream.
|
|
2301
|
+
* - `circularNames` runs cycle detection, which requires all nodes in memory simultaneously.
|
|
2302
|
+
* The `allNodes` array is local and drops out of scope as soon as this function returns.
|
|
2303
|
+
*
|
|
2304
|
+
* After this call, only `refAliasMap` and `discriminatorChildMap` stay alive in the adapter closure.
|
|
2305
|
+
* Both are proportional to the number of aliases or discriminator parents, not total schema count.
|
|
2306
|
+
*
|
|
2307
|
+
* Each schema is parsed again during the streaming pass. This is intentional.
|
|
2308
|
+
* Holding the parsed nodes in memory here would defeat the streaming memory benefit.
|
|
1862
2309
|
*
|
|
1863
2310
|
* @example
|
|
1864
2311
|
* ```ts
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1867
|
-
*
|
|
1868
|
-
*
|
|
2312
|
+
* const { refAliasMap, enumNames, circularNames } = preScan({
|
|
2313
|
+
* schemas,
|
|
2314
|
+
* parseSchema,
|
|
2315
|
+
* parserOptions,
|
|
2316
|
+
* discriminator: 'strict',
|
|
2317
|
+
* })
|
|
1869
2318
|
* ```
|
|
1870
2319
|
*/
|
|
1871
|
-
function
|
|
1872
|
-
const
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2320
|
+
function preScan({ schemas, parseSchema, parseOperation, baseOas, parserOptions, discriminator, dedupe }) {
|
|
2321
|
+
const allNodes = [];
|
|
2322
|
+
const refAliasMap = /* @__PURE__ */ new Map();
|
|
2323
|
+
const enumNames = [];
|
|
2324
|
+
const discriminatorParentNodes = [];
|
|
2325
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2326
|
+
const node = parseSchema({
|
|
2327
|
+
schema,
|
|
2328
|
+
name
|
|
2329
|
+
}, parserOptions);
|
|
2330
|
+
allNodes.push(node);
|
|
2331
|
+
reportSchemaDiagnostics({
|
|
2332
|
+
node,
|
|
2333
|
+
name
|
|
2334
|
+
});
|
|
2335
|
+
if (node.type === "ref" && node.name && node.name !== name) refAliasMap.set(name, node);
|
|
2336
|
+
if (_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum) && node.name) enumNames.push(node.name);
|
|
2337
|
+
if (discriminator === "inherit" && (schema.oneOf ?? schema.anyOf) && schema.discriminator?.propertyName) discriminatorParentNodes.push(node);
|
|
2338
|
+
}
|
|
2339
|
+
const circularNames = [..._kubb_core.ast.findCircularSchemas(allNodes)];
|
|
2340
|
+
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
2341
|
+
let dedupePlan = null;
|
|
2342
|
+
if (dedupe) {
|
|
2343
|
+
const operationNodes = [];
|
|
2344
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2345
|
+
if (!operation) continue;
|
|
2346
|
+
const operationNode = parseOperation(parserOptions, operation);
|
|
2347
|
+
if (operationNode) operationNodes.push(operationNode);
|
|
2348
|
+
}
|
|
2349
|
+
dedupePlan = createDedupePlan({
|
|
2350
|
+
schemaNodes: allNodes,
|
|
2351
|
+
operationNodes,
|
|
2352
|
+
schemaNames: Object.keys(schemas),
|
|
2353
|
+
circularNames
|
|
2354
|
+
});
|
|
2355
|
+
for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2356
|
+
}
|
|
1888
2357
|
return {
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2358
|
+
refAliasMap,
|
|
2359
|
+
enumNames,
|
|
2360
|
+
circularNames,
|
|
2361
|
+
discriminatorChildMap,
|
|
2362
|
+
dedupePlan
|
|
1894
2363
|
};
|
|
1895
2364
|
}
|
|
2365
|
+
/**
|
|
2366
|
+
* Creates a lazy `InputStreamNode` from already-resolved adapter state.
|
|
2367
|
+
*
|
|
2368
|
+
* The schema and operation iterables each start a fresh parse pass on every
|
|
2369
|
+
* `[Symbol.asyncIterator]()` call. This lets multiple plugins consume the same
|
|
2370
|
+
* stream object independently without sharing a cursor or holding all nodes in memory.
|
|
2371
|
+
*
|
|
2372
|
+
* Ref aliases in `refAliasMap` are inlined during iteration: an alias entry is replaced
|
|
2373
|
+
* with its target's parsed node (but keeps the alias name) so plugins never receive bare `ref` nodes.
|
|
2374
|
+
*
|
|
2375
|
+
* @example
|
|
2376
|
+
* ```ts
|
|
2377
|
+
* const streamNode = createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, meta })
|
|
2378
|
+
* for await (const schema of streamNode.schemas) {
|
|
2379
|
+
* // each call to for-await restarts from the first schema
|
|
2380
|
+
* }
|
|
2381
|
+
* ```
|
|
2382
|
+
*/
|
|
2383
|
+
function createInputStream({ schemas, parseSchema, parseOperation, baseOas, parserOptions, refAliasMap, discriminatorChildMap, dedupePlan, meta }) {
|
|
2384
|
+
const rewriteTopLevelSchema = (node) => {
|
|
2385
|
+
if (!dedupePlan) return node;
|
|
2386
|
+
const canonical = dedupePlan.canonicalBySignature.get(_kubb_core.ast.schemaSignature(node));
|
|
2387
|
+
if (canonical && canonical.name !== node.name) return _kubb_core.ast.createSchema({
|
|
2388
|
+
type: "ref",
|
|
2389
|
+
name: node.name ?? null,
|
|
2390
|
+
ref: canonical.ref,
|
|
2391
|
+
description: node.description,
|
|
2392
|
+
deprecated: node.deprecated
|
|
2393
|
+
});
|
|
2394
|
+
return _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature, true);
|
|
2395
|
+
};
|
|
2396
|
+
const schemasIterable = { [Symbol.asyncIterator]() {
|
|
2397
|
+
return (async function* () {
|
|
2398
|
+
if (dedupePlan) for (const definition of dedupePlan.hoisted) yield definition;
|
|
2399
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
2400
|
+
const alias = refAliasMap.get(name);
|
|
2401
|
+
if (alias?.name && schemas[alias.name]) {
|
|
2402
|
+
yield rewriteTopLevelSchema({
|
|
2403
|
+
...parseSchema({
|
|
2404
|
+
schema: schemas[alias.name],
|
|
2405
|
+
name: alias.name
|
|
2406
|
+
}, parserOptions),
|
|
2407
|
+
name
|
|
2408
|
+
});
|
|
2409
|
+
continue;
|
|
2410
|
+
}
|
|
2411
|
+
const parsed = parseSchema({
|
|
2412
|
+
schema,
|
|
2413
|
+
name
|
|
2414
|
+
}, parserOptions);
|
|
2415
|
+
yield rewriteTopLevelSchema(discriminatorChildMap?.get(name) ? patchDiscriminatorNode(parsed, discriminatorChildMap.get(name)) : parsed);
|
|
2416
|
+
}
|
|
2417
|
+
})();
|
|
2418
|
+
} };
|
|
2419
|
+
const operationsIterable = { [Symbol.asyncIterator]() {
|
|
2420
|
+
return (async function* () {
|
|
2421
|
+
for (const methods of Object.values(baseOas.getPaths())) for (const operation of Object.values(methods)) {
|
|
2422
|
+
if (!operation) continue;
|
|
2423
|
+
const node = parseOperation(parserOptions, operation);
|
|
2424
|
+
if (node) yield dedupePlan ? _kubb_core.ast.applyDedupe(node, dedupePlan.canonicalBySignature) : node;
|
|
2425
|
+
}
|
|
2426
|
+
})();
|
|
2427
|
+
} };
|
|
2428
|
+
return _kubb_core.ast.createStreamInput(schemasIterable, operationsIterable, meta);
|
|
2429
|
+
}
|
|
1896
2430
|
//#endregion
|
|
1897
2431
|
//#region src/adapter.ts
|
|
1898
2432
|
/**
|
|
1899
|
-
*
|
|
2433
|
+
* Canonical adapter name for `@kubb/adapter-oas`. Used for driver lookups.
|
|
1900
2434
|
*/
|
|
1901
2435
|
const adapterOasName = "oas";
|
|
1902
2436
|
/**
|
|
1903
|
-
*
|
|
2437
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.1 specifications. Reads the
|
|
2438
|
+
* file at `input.path`, validates it, resolves the base URL, and converts every
|
|
2439
|
+
* schema and operation into the universal AST that every downstream plugin
|
|
2440
|
+
* consumes.
|
|
1904
2441
|
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
2442
|
+
* Configure once on `defineConfig`. The adapter's choices (date representation,
|
|
2443
|
+
* integer width, server URL) apply to every plugin in the build.
|
|
1907
2444
|
*
|
|
1908
2445
|
* @example
|
|
1909
2446
|
* ```ts
|
|
@@ -1912,17 +2449,118 @@ const adapterOasName = "oas";
|
|
|
1912
2449
|
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1913
2450
|
*
|
|
1914
2451
|
* export default defineConfig({
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
2452
|
+
* input: { path: './petStore.yaml' },
|
|
2453
|
+
* output: { path: './src/gen' },
|
|
2454
|
+
* adapter: adapterOas({
|
|
2455
|
+
* serverIndex: 0,
|
|
2456
|
+
* discriminator: 'inherit',
|
|
2457
|
+
* dateType: 'date',
|
|
2458
|
+
* }),
|
|
1917
2459
|
* plugins: [pluginTs()],
|
|
1918
2460
|
* })
|
|
1919
2461
|
* ```
|
|
1920
2462
|
*/
|
|
1921
2463
|
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;
|
|
2464
|
+
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;
|
|
2465
|
+
const parserOptions = {
|
|
2466
|
+
...DEFAULT_PARSER_OPTIONS,
|
|
2467
|
+
dateType,
|
|
2468
|
+
integerType,
|
|
2469
|
+
unknownType,
|
|
2470
|
+
emptySchemaType,
|
|
2471
|
+
enumSuffix
|
|
2472
|
+
};
|
|
1923
2473
|
let nameMapping = /* @__PURE__ */ new Map();
|
|
1924
|
-
let parsedDocument;
|
|
1925
|
-
|
|
2474
|
+
let parsedDocument = null;
|
|
2475
|
+
const documentCache = /* @__PURE__ */ new WeakMap();
|
|
2476
|
+
const schemasCache = /* @__PURE__ */ new WeakMap();
|
|
2477
|
+
const baseOasCache = /* @__PURE__ */ new WeakMap();
|
|
2478
|
+
const schemaParserCache = /* @__PURE__ */ new WeakMap();
|
|
2479
|
+
const preScanCache = /* @__PURE__ */ new WeakMap();
|
|
2480
|
+
function ensureDocument(source) {
|
|
2481
|
+
const cached = documentCache.get(source);
|
|
2482
|
+
if (cached) return cached;
|
|
2483
|
+
const promise = (async () => {
|
|
2484
|
+
const fresh = await parseFromConfig(source);
|
|
2485
|
+
if (validate) await validateDocument(fresh);
|
|
2486
|
+
parsedDocument = fresh;
|
|
2487
|
+
return fresh;
|
|
2488
|
+
})();
|
|
2489
|
+
documentCache.set(source, promise);
|
|
2490
|
+
return promise;
|
|
2491
|
+
}
|
|
2492
|
+
function ensureSchemas(document) {
|
|
2493
|
+
const cached = schemasCache.get(document);
|
|
2494
|
+
if (cached) return cached;
|
|
2495
|
+
const promise = Promise.resolve().then(() => {
|
|
2496
|
+
const result = getSchemas(document, { contentType });
|
|
2497
|
+
nameMapping = result.nameMapping;
|
|
2498
|
+
return result.schemas;
|
|
2499
|
+
});
|
|
2500
|
+
schemasCache.set(document, promise);
|
|
2501
|
+
return promise;
|
|
2502
|
+
}
|
|
2503
|
+
function ensureBaseOas(document) {
|
|
2504
|
+
const cached = baseOasCache.get(document);
|
|
2505
|
+
if (cached) return cached;
|
|
2506
|
+
const baseOas = new oas.default(document);
|
|
2507
|
+
baseOasCache.set(document, baseOas);
|
|
2508
|
+
return baseOas;
|
|
2509
|
+
}
|
|
2510
|
+
function ensureSchemaParser(document) {
|
|
2511
|
+
const cached = schemaParserCache.get(document);
|
|
2512
|
+
if (cached) return cached;
|
|
2513
|
+
const parser = createSchemaParser({
|
|
2514
|
+
document,
|
|
2515
|
+
contentType
|
|
2516
|
+
});
|
|
2517
|
+
schemaParserCache.set(document, parser);
|
|
2518
|
+
return parser;
|
|
2519
|
+
}
|
|
2520
|
+
function ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas) {
|
|
2521
|
+
const cached = preScanCache.get(document);
|
|
2522
|
+
if (cached) return cached;
|
|
2523
|
+
const result = preScan({
|
|
2524
|
+
schemas,
|
|
2525
|
+
parseSchema,
|
|
2526
|
+
parseOperation,
|
|
2527
|
+
baseOas,
|
|
2528
|
+
parserOptions,
|
|
2529
|
+
discriminator,
|
|
2530
|
+
dedupe
|
|
2531
|
+
});
|
|
2532
|
+
preScanCache.set(document, result);
|
|
2533
|
+
return result;
|
|
2534
|
+
}
|
|
2535
|
+
async function createStream(source) {
|
|
2536
|
+
const document = await ensureDocument(source);
|
|
2537
|
+
const schemas = await ensureSchemas(document);
|
|
2538
|
+
const { parseSchema, parseOperation } = ensureSchemaParser(document);
|
|
2539
|
+
const baseOas = ensureBaseOas(document);
|
|
2540
|
+
const { refAliasMap, enumNames, circularNames, discriminatorChildMap, dedupePlan } = ensurePreScan(document, schemas, parseSchema, parseOperation, baseOas);
|
|
2541
|
+
return createInputStream({
|
|
2542
|
+
schemas,
|
|
2543
|
+
parseSchema,
|
|
2544
|
+
parseOperation,
|
|
2545
|
+
baseOas,
|
|
2546
|
+
parserOptions,
|
|
2547
|
+
refAliasMap,
|
|
2548
|
+
discriminatorChildMap,
|
|
2549
|
+
dedupePlan,
|
|
2550
|
+
meta: {
|
|
2551
|
+
title: document.info?.title,
|
|
2552
|
+
description: document.info?.description,
|
|
2553
|
+
version: document.info?.version,
|
|
2554
|
+
baseURL: resolveBaseUrl({
|
|
2555
|
+
document,
|
|
2556
|
+
serverIndex,
|
|
2557
|
+
serverVariables
|
|
2558
|
+
}),
|
|
2559
|
+
circularNames,
|
|
2560
|
+
enumNames
|
|
2561
|
+
}
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
1926
2564
|
return {
|
|
1927
2565
|
name: "oas",
|
|
1928
2566
|
get options() {
|
|
@@ -1932,6 +2570,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1932
2570
|
serverIndex,
|
|
1933
2571
|
serverVariables,
|
|
1934
2572
|
discriminator,
|
|
2573
|
+
dedupe,
|
|
1935
2574
|
dateType,
|
|
1936
2575
|
integerType,
|
|
1937
2576
|
unknownType,
|
|
@@ -1943,8 +2582,9 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1943
2582
|
get document() {
|
|
1944
2583
|
return parsedDocument;
|
|
1945
2584
|
},
|
|
1946
|
-
|
|
1947
|
-
|
|
2585
|
+
async validate(input, options) {
|
|
2586
|
+
await assertInputExists(input);
|
|
2587
|
+
await validateDocument(await parseDocument(input), options);
|
|
1948
2588
|
},
|
|
1949
2589
|
getImports(node, resolve) {
|
|
1950
2590
|
return _kubb_core.ast.collectImports({
|
|
@@ -1952,7 +2592,7 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1952
2592
|
nameMapping,
|
|
1953
2593
|
resolve: (schemaName) => {
|
|
1954
2594
|
const result = resolve(schemaName);
|
|
1955
|
-
if (!result) return;
|
|
2595
|
+
if (!result) return null;
|
|
1956
2596
|
return _kubb_core.ast.createImport({
|
|
1957
2597
|
name: [result.name],
|
|
1958
2598
|
path: result.path
|
|
@@ -1961,32 +2601,15 @@ const adapterOas = (0, _kubb_core.createAdapter)((options) => {
|
|
|
1961
2601
|
});
|
|
1962
2602
|
},
|
|
1963
2603
|
async parse(source) {
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
dateType,
|
|
1971
|
-
integerType,
|
|
1972
|
-
unknownType,
|
|
1973
|
-
emptySchemaType,
|
|
1974
|
-
enumSuffix
|
|
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
|
-
}
|
|
2604
|
+
const streamNode = await createStream(source);
|
|
2605
|
+
const [schemas, operations] = await Promise.all([Array.fromAsync(streamNode.schemas), Array.fromAsync(streamNode.operations)]);
|
|
2606
|
+
return _kubb_core.ast.createInput({
|
|
2607
|
+
schemas,
|
|
2608
|
+
operations,
|
|
2609
|
+
meta: streamNode.meta
|
|
1987
2610
|
});
|
|
1988
|
-
|
|
1989
|
-
|
|
2611
|
+
},
|
|
2612
|
+
stream: createStream
|
|
1990
2613
|
};
|
|
1991
2614
|
});
|
|
1992
2615
|
//#endregion
|
|
@@ -2006,8 +2629,5 @@ exports.HttpMethods = HttpMethods;
|
|
|
2006
2629
|
exports.adapterOas = adapterOas;
|
|
2007
2630
|
exports.adapterOasName = adapterOasName;
|
|
2008
2631
|
exports.mergeDocuments = mergeDocuments;
|
|
2009
|
-
exports.parseDocument = parseDocument;
|
|
2010
|
-
exports.parseFromConfig = parseFromConfig;
|
|
2011
|
-
exports.validateDocument = validateDocument;
|
|
2012
2632
|
|
|
2013
2633
|
//# sourceMappingURL=index.cjs.map
|