@kubb/adapter-oas 5.17.0 → 5.18.0
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/dist/index.cjs +514 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -6
- package/dist/index.d.ts +22 -6
- package/dist/index.js +515 -100
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
let _kubb_core = require("@kubb/core");
|
|
24
24
|
let _kubb_kit = require("@kubb/kit");
|
|
25
25
|
let node_path = require("node:path");
|
|
26
|
+
let node_url = require("node:url");
|
|
26
27
|
let node_fs_promises = require("node:fs/promises");
|
|
27
28
|
let _scalar_openapi_upgrader = require("@scalar/openapi-upgrader");
|
|
28
29
|
let api_ref_bundler = require("api-ref-bundler");
|
|
@@ -35,7 +36,7 @@ var compatibility = {
|
|
|
35
36
|
"node": ">=22.22"
|
|
36
37
|
};
|
|
37
38
|
// package.json
|
|
38
|
-
var version = "5.
|
|
39
|
+
var version = "5.18.0";
|
|
39
40
|
// src/constants.ts
|
|
40
41
|
/**
|
|
41
42
|
* Default parser options applied when no explicit options are provided.
|
|
@@ -417,6 +418,21 @@ function isOpenApi32(document) {
|
|
|
417
418
|
const [major = 0, minor = 0] = String(document.openapi).split(".").map(Number);
|
|
418
419
|
return major > 3 || major === 3 && minor >= 2;
|
|
419
420
|
}
|
|
421
|
+
function pickStrings(source, keys) {
|
|
422
|
+
if (!source || typeof source !== "object") return void 0;
|
|
423
|
+
const entries = keys.flatMap((key) => {
|
|
424
|
+
const value = source[key];
|
|
425
|
+
return typeof value === "string" ? [[key, value]] : [];
|
|
426
|
+
});
|
|
427
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
428
|
+
}
|
|
429
|
+
function getExternalDocs(source) {
|
|
430
|
+
const docs = pickStrings(source, ["url", "description"]);
|
|
431
|
+
return docs?.url ? {
|
|
432
|
+
url: docs.url,
|
|
433
|
+
...docs.description !== void 0 && { description: docs.description }
|
|
434
|
+
} : void 0;
|
|
435
|
+
}
|
|
420
436
|
/**
|
|
421
437
|
* Returns the `x-*` keys of `source` as-is, or `undefined` when there are none.
|
|
422
438
|
*/
|
|
@@ -431,12 +447,12 @@ function isReference(obj) {
|
|
|
431
447
|
return !!obj && typeof obj === "object" && "$ref" in obj;
|
|
432
448
|
}
|
|
433
449
|
/**
|
|
434
|
-
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object
|
|
435
|
-
* excluding the Swagger 2 string form.
|
|
450
|
+
* Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object that names a
|
|
451
|
+
* `propertyName`, excluding the Swagger 2 string form and a 3.2 discriminator that only sets `defaultMapping`.
|
|
436
452
|
*/
|
|
437
453
|
function isDiscriminator(obj) {
|
|
438
|
-
const
|
|
439
|
-
return !!
|
|
454
|
+
const discriminator = obj?.["discriminator"];
|
|
455
|
+
return !!discriminator && typeof discriminator === "object" && typeof discriminator.propertyName === "string";
|
|
440
456
|
}
|
|
441
457
|
/**
|
|
442
458
|
* Returns `true` when a schema is a raw binary payload.
|
|
@@ -445,17 +461,32 @@ function isBinary(schema) {
|
|
|
445
461
|
return schema.contentMediaType === "application/octet-stream" && schema.contentEncoding === void 0 && (schema.type === void 0 || schema.type === "string");
|
|
446
462
|
}
|
|
447
463
|
/**
|
|
464
|
+
* Keywords that only annotate a schema. A schema with nothing else does not constrain the value.
|
|
465
|
+
*/
|
|
466
|
+
const annotationKeywords = /* @__PURE__ */ new Set([
|
|
467
|
+
"title",
|
|
468
|
+
"description",
|
|
469
|
+
"default",
|
|
470
|
+
"example",
|
|
471
|
+
"examples",
|
|
472
|
+
"deprecated",
|
|
473
|
+
"readOnly",
|
|
474
|
+
"writeOnly",
|
|
475
|
+
"externalDocs"
|
|
476
|
+
]);
|
|
477
|
+
/**
|
|
448
478
|
* Returns the binary schema for a non-JSON entry the OAS 3.1 upgrade emptied out, or `undefined`
|
|
449
479
|
* when the entry carries a schema of its own. The upgrade rewrites `format: 'binary'` into
|
|
450
480
|
* `contentMediaType` and drops it whenever it only repeats the media type key, for any binary
|
|
451
481
|
* media type (`application/octet-stream`, `application/pdf`, `image/png`, ...), not just
|
|
452
482
|
* `application/octet-stream`. Without the fallback such a body resolves to the empty schema type
|
|
453
|
-
* instead of a blob.
|
|
483
|
+
* instead of a blob. Annotations such as `description` survive the upgrade and are kept.
|
|
454
484
|
*/
|
|
455
485
|
function getBinaryFallbackSchema(mediaType, schema) {
|
|
456
486
|
if (!mediaType || isJsonMimeType(mediaType)) return void 0;
|
|
457
|
-
if (schema && Object.keys(schema).
|
|
487
|
+
if (schema && Object.keys(schema).some((key) => !annotationKeywords.has(key) && !key.startsWith("x-"))) return void 0;
|
|
458
488
|
return {
|
|
489
|
+
...schema,
|
|
459
490
|
type: "string",
|
|
460
491
|
contentMediaType: "application/octet-stream"
|
|
461
492
|
};
|
|
@@ -594,6 +625,65 @@ function createRefs(document) {
|
|
|
594
625
|
derefKeepingRef
|
|
595
626
|
};
|
|
596
627
|
}
|
|
628
|
+
// src/load/schemaIds.ts
|
|
629
|
+
const DATA_KEYS = /* @__PURE__ */ new Set([
|
|
630
|
+
"example",
|
|
631
|
+
"examples",
|
|
632
|
+
"const",
|
|
633
|
+
"enum",
|
|
634
|
+
"default"
|
|
635
|
+
]);
|
|
636
|
+
const NAMED_ENTRY_KEYS = /* @__PURE__ */ new Set([
|
|
637
|
+
"properties",
|
|
638
|
+
"patternProperties",
|
|
639
|
+
"$defs",
|
|
640
|
+
"definitions",
|
|
641
|
+
"schemas",
|
|
642
|
+
"dependentSchemas"
|
|
643
|
+
]);
|
|
644
|
+
function walk(node, pointer, visit, parentKey) {
|
|
645
|
+
if (!node || typeof node !== "object") return;
|
|
646
|
+
if (!Array.isArray(node)) visit(node, pointer);
|
|
647
|
+
const named = parentKey !== void 0 && NAMED_ENTRY_KEYS.has(parentKey);
|
|
648
|
+
for (const [key, value] of Object.entries(node)) {
|
|
649
|
+
if (!named && (DATA_KEYS.has(key) || key.startsWith("x-"))) continue;
|
|
650
|
+
walk(value, `${pointer}/${escapePointerToken(key)}`, visit, Array.isArray(node) ? parentKey : key);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* The declaration in the nearest enclosing schema, else the only one, else `undefined`.
|
|
655
|
+
*/
|
|
656
|
+
function pick(declarations, pointer) {
|
|
657
|
+
if (!declarations) return void 0;
|
|
658
|
+
const enclosing = declarations.filter((target) => pointer === target || pointer.startsWith(`${target}/`));
|
|
659
|
+
if (enclosing.length) return enclosing.reduce((a, b) => b.length > a.length ? b : a);
|
|
660
|
+
return declarations.length === 1 ? declarations[0] : void 0;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Rewrites, in place, each `$ref` to an `$id`, `$anchor` or `$dynamicAnchor`, and each `$dynamicRef`, into a
|
|
664
|
+
* `#/...` pointer, so it is not fetched. Instance data and `x-` extensions are left as written.
|
|
665
|
+
*
|
|
666
|
+
* shortcut: `$id` matches the `$ref` string exactly and `$dynamicRef` resolves statically to the nearest
|
|
667
|
+
* enclosing anchor, so a relative `$id` base or a recursively extended schema is not followed, upgrade when a spec needs that.
|
|
668
|
+
*/
|
|
669
|
+
function resolveSchemaIds(document) {
|
|
670
|
+
const targets = /* @__PURE__ */ new Map();
|
|
671
|
+
const declare = (key, pointer) => targets.set(key, [...targets.get(key) ?? [], pointer]);
|
|
672
|
+
walk(document, "#", (node, pointer) => {
|
|
673
|
+
if (typeof node["$id"] === "string") declare(node["$id"], pointer);
|
|
674
|
+
for (const key of ["$anchor", "$dynamicAnchor"]) if (typeof node[key] === "string") declare(`#${node[key]}`, pointer);
|
|
675
|
+
});
|
|
676
|
+
if (!targets.size) return;
|
|
677
|
+
walk(document, "#", (node, pointer) => {
|
|
678
|
+
const ref = typeof node["$ref"] === "string" ? pick(targets.get(node["$ref"]), pointer) : void 0;
|
|
679
|
+
if (ref) node["$ref"] = ref;
|
|
680
|
+
const dynamicRef = typeof node["$dynamicRef"] === "string" ? pick(targets.get(node["$dynamicRef"]), pointer) : void 0;
|
|
681
|
+
if (dynamicRef && node["$ref"] === void 0) {
|
|
682
|
+
node["$ref"] = dynamicRef;
|
|
683
|
+
delete node["$dynamicRef"];
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
}
|
|
597
687
|
// src/load/source.ts
|
|
598
688
|
const urlRegExp = /^https?:\/+/i;
|
|
599
689
|
/**
|
|
@@ -763,6 +853,21 @@ function isExtensionPath(path) {
|
|
|
763
853
|
return path.some((token, index) => String(token).startsWith("x-") && !namedEntryKeys.has(String(path[index - 1])));
|
|
764
854
|
}
|
|
765
855
|
/**
|
|
856
|
+
* Returns the base for relative `$ref`s: the 3.2 `$self` resolved against `pathOrUrl`, else `pathOrUrl`.
|
|
857
|
+
*/
|
|
858
|
+
function getBaseUri(pathOrUrl, root) {
|
|
859
|
+
const self = root && typeof root === "object" ? root.$self : void 0;
|
|
860
|
+
if (typeof self !== "string" || !self) return pathOrUrl;
|
|
861
|
+
try {
|
|
862
|
+
if (urlRegExp.test(self)) return new URL(self).href;
|
|
863
|
+
if (/^file:/i.test(self)) return (0, node_url.fileURLToPath)(self);
|
|
864
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(self)) return pathOrUrl;
|
|
865
|
+
return urlRegExp.test(pathOrUrl) ? new URL(self, pathOrUrl).href : (0, node_path.resolve)((0, node_path.dirname)(pathOrUrl), self);
|
|
866
|
+
} catch {
|
|
867
|
+
return pathOrUrl;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
766
871
|
* Bundles a multi-file OpenAPI document into a single document via `api-ref-bundler`.
|
|
767
872
|
*
|
|
768
873
|
* External file schemas are hoisted into named `components.schemas` entries, so a property
|
|
@@ -775,8 +880,9 @@ function isExtensionPath(path) {
|
|
|
775
880
|
* all-internal document it is a no-op that still walks the whole tree to confirm that, which
|
|
776
881
|
* costs real time on a large spec.
|
|
777
882
|
*
|
|
778
|
-
* `document` stands in for the file at `pathOrUrl`, such as inline input.
|
|
779
|
-
*
|
|
883
|
+
* `document` stands in for the file at `pathOrUrl`, such as inline input. Relative `$ref`s resolve
|
|
884
|
+
* against the OpenAPI 3.2 `$self` when the document sets one. Every other file or URL read,
|
|
885
|
+
* including one that fails, lands in `sources`. A `$ref` that cannot be read reports
|
|
780
886
|
* `KUBB_REF_NOT_FOUND` at the key that holds it, as a warning when it sits inside an `x-` extension.
|
|
781
887
|
*
|
|
782
888
|
* @example Local file
|
|
@@ -788,8 +894,10 @@ function isExtensionPath(path) {
|
|
|
788
894
|
async function bundleDocument(pathOrUrl, { document, sources = [] } = {}) {
|
|
789
895
|
const cache = /* @__PURE__ */ new Map();
|
|
790
896
|
const toKey = (sourcePath) => urlRegExp.test(sourcePath) ? new URL(sourcePath).href : sourcePath;
|
|
897
|
+
let entry = pathOrUrl;
|
|
898
|
+
let entryDocument = document;
|
|
791
899
|
const resolver = (sourcePath) => {
|
|
792
|
-
if (
|
|
900
|
+
if (entryDocument !== void 0 && toKey(sourcePath) === toKey(entry)) return Promise.resolve(entryDocument);
|
|
793
901
|
const key = toKey(sourcePath);
|
|
794
902
|
const cached = cache.get(key);
|
|
795
903
|
if (cached) return cached;
|
|
@@ -799,9 +907,12 @@ async function bundleDocument(pathOrUrl, { document, sources = [] } = {}) {
|
|
|
799
907
|
return result;
|
|
800
908
|
};
|
|
801
909
|
const root = await resolver(pathOrUrl);
|
|
910
|
+
resolveSchemaIds(root);
|
|
802
911
|
if (typeof root === "object" && root !== null && !hasExternalRef(root)) return root;
|
|
803
|
-
|
|
804
|
-
|
|
912
|
+
entry = getBaseUri(pathOrUrl, root);
|
|
913
|
+
entryDocument = root;
|
|
914
|
+
return await (0, api_ref_bundler.bundle)(entry, resolver, { hooks: { onError: (message, { path, state }) => {
|
|
915
|
+
const pointer = toKey(state.baseFile) === toKey(entry) ? `#/${path.map((token) => escapePointerToken(String(token))).join("/")}` : void 0;
|
|
805
916
|
const diagnostic = {
|
|
806
917
|
code: _kubb_kit.Diagnostics.code.refNotFound,
|
|
807
918
|
severity: isExtensionPath(path) ? "warning" : "error",
|
|
@@ -840,10 +951,12 @@ async function parseFromConfig(source, sources = []) {
|
|
|
840
951
|
origin: "the inline `input` string",
|
|
841
952
|
help: "Fix the syntax at the reported line. Kubb reads `input` as an inline spec, not a file path, because it starts with `{`, `[`, `openapi:`, or `swagger:`, or spans several lines."
|
|
842
953
|
}) : structuredClone(source.data);
|
|
843
|
-
|
|
954
|
+
if (source.root && hasExternalRef(data)) return bundleDocument((0, node_path.join)(source.root, inlineInputName), {
|
|
844
955
|
document: data,
|
|
845
956
|
sources
|
|
846
|
-
})
|
|
957
|
+
});
|
|
958
|
+
resolveSchemaIds(data);
|
|
959
|
+
return data;
|
|
847
960
|
}
|
|
848
961
|
await assertInputExists(source.path);
|
|
849
962
|
return bundleDocument(source.path, { sources });
|
|
@@ -1168,6 +1281,80 @@ function getSchemas(document, { contentType }, refs) {
|
|
|
1168
1281
|
pointers
|
|
1169
1282
|
};
|
|
1170
1283
|
}
|
|
1284
|
+
// src/model/security.ts
|
|
1285
|
+
const SCHEME_POINTER = "#/components/securitySchemes/";
|
|
1286
|
+
const API_KEY_LOCATIONS = /* @__PURE__ */ new Set([
|
|
1287
|
+
"header",
|
|
1288
|
+
"query",
|
|
1289
|
+
"cookie"
|
|
1290
|
+
]);
|
|
1291
|
+
function getCredential(scheme) {
|
|
1292
|
+
if (scheme.type === "apiKey") return scheme.name && scheme.in && API_KEY_LOCATIONS.has(scheme.in) ? {
|
|
1293
|
+
type: "apiKey",
|
|
1294
|
+
in: scheme.in,
|
|
1295
|
+
name: scheme.name
|
|
1296
|
+
} : void 0;
|
|
1297
|
+
if (scheme.type === "http") return scheme.scheme ? {
|
|
1298
|
+
type: "http",
|
|
1299
|
+
scheme: scheme.scheme.toLowerCase(),
|
|
1300
|
+
...scheme.bearerFormat && { bearerFormat: scheme.bearerFormat }
|
|
1301
|
+
} : void 0;
|
|
1302
|
+
if (scheme.type === "openIdConnect") return {
|
|
1303
|
+
type: "openIdConnect",
|
|
1304
|
+
...scheme.openIdConnectUrl && { url: scheme.openIdConnectUrl }
|
|
1305
|
+
};
|
|
1306
|
+
if (scheme.type === "oauth2") {
|
|
1307
|
+
const flows = Object.entries(scheme.flows ?? {}).map(([name, flow]) => {
|
|
1308
|
+
const scopes = flow && typeof flow === "object" && flow.scopes && typeof flow.scopes === "object" ? { scopes: flow.scopes } : {};
|
|
1309
|
+
return [name, {
|
|
1310
|
+
...pickStrings(flow, [
|
|
1311
|
+
"authorizationUrl",
|
|
1312
|
+
"deviceAuthorizationUrl",
|
|
1313
|
+
"tokenUrl",
|
|
1314
|
+
"refreshUrl"
|
|
1315
|
+
]),
|
|
1316
|
+
...scopes
|
|
1317
|
+
}];
|
|
1318
|
+
});
|
|
1319
|
+
return {
|
|
1320
|
+
type: "oauth2",
|
|
1321
|
+
flows: Object.fromEntries(flows),
|
|
1322
|
+
...scheme.oauth2MetadataUrl && { metadataUrl: scheme.oauth2MetadataUrl }
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
return scheme.type === "mutualTLS" ? { type: "mutualTLS" } : void 0;
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Maps `components.securitySchemes` to `AuthScheme` entries, leaving out a scheme a client cannot place.
|
|
1329
|
+
*/
|
|
1330
|
+
function toAuthSchemes(schemes, refs) {
|
|
1331
|
+
return Object.entries(schemes ?? {}).flatMap(([name, value]) => {
|
|
1332
|
+
const scheme = refs.deref(value);
|
|
1333
|
+
const credential = scheme && getCredential(scheme);
|
|
1334
|
+
if (!scheme || !credential) return [];
|
|
1335
|
+
return [{
|
|
1336
|
+
name,
|
|
1337
|
+
...scheme.description && { description: scheme.description },
|
|
1338
|
+
...scheme.deprecated && { deprecated: true },
|
|
1339
|
+
credential
|
|
1340
|
+
}];
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Maps security requirements to `AuthRequirement` entries. A 3.2 URI key becomes the scheme name.
|
|
1345
|
+
*/
|
|
1346
|
+
function toAuthRequirements(requirements, document) {
|
|
1347
|
+
if (!Array.isArray(requirements)) return void 0;
|
|
1348
|
+
const schemes = document.components?.securitySchemes ?? {};
|
|
1349
|
+
const toName = (key) => {
|
|
1350
|
+
const index = key.indexOf(SCHEME_POINTER);
|
|
1351
|
+
return Object.hasOwn(schemes, key) || index === -1 ? key : unescapePointerToken(key.slice(index + 29));
|
|
1352
|
+
};
|
|
1353
|
+
return requirements.filter((requirement) => !!requirement && typeof requirement === "object").map((requirement) => Object.entries(requirement).map(([key, scopes]) => ({
|
|
1354
|
+
scheme: toName(key),
|
|
1355
|
+
...Array.isArray(scopes) && scopes.length && { scopes: scopes.map(String) }
|
|
1356
|
+
})));
|
|
1357
|
+
}
|
|
1171
1358
|
// src/model/server.ts
|
|
1172
1359
|
/**
|
|
1173
1360
|
* Reads the server URL from the document's `servers` array at `server.index`,
|
|
@@ -1224,8 +1411,11 @@ function resolveServerUrl(server, overrides) {
|
|
|
1224
1411
|
* Maps OpenAPI servers to `InputServer` entries: drops entries without a `url` and fills `{variable}` defaults.
|
|
1225
1412
|
*/
|
|
1226
1413
|
function toInputServers(servers) {
|
|
1227
|
-
return servers.filter((entry) => entry.url).map(({ url, description, variables }) => {
|
|
1228
|
-
const declared =
|
|
1414
|
+
return servers.filter((entry) => entry.url).map(({ url, name, description, variables }) => {
|
|
1415
|
+
const declared = {
|
|
1416
|
+
...name !== void 0 && { name },
|
|
1417
|
+
...variables && Object.keys(variables).length > 0 && { variables }
|
|
1418
|
+
};
|
|
1229
1419
|
try {
|
|
1230
1420
|
return {
|
|
1231
1421
|
url: resolveServerUrl({
|
|
@@ -1244,6 +1434,20 @@ function toInputServers(servers) {
|
|
|
1244
1434
|
}
|
|
1245
1435
|
});
|
|
1246
1436
|
}
|
|
1437
|
+
// src/model/tag.ts
|
|
1438
|
+
function toInputTags(tags) {
|
|
1439
|
+
return tags.filter((tag) => typeof tag.name === "string").map(({ name, summary, description, parent, kind, externalDocs }) => {
|
|
1440
|
+
const docs = getExternalDocs(externalDocs);
|
|
1441
|
+
return {
|
|
1442
|
+
name,
|
|
1443
|
+
...typeof summary === "string" && { summary },
|
|
1444
|
+
...typeof description === "string" && { description },
|
|
1445
|
+
...typeof parent === "string" && { parent },
|
|
1446
|
+
...typeof kind === "string" && { kind },
|
|
1447
|
+
...docs && { externalDocs: docs }
|
|
1448
|
+
};
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1247
1451
|
// src/model/mediaType.ts
|
|
1248
1452
|
/**
|
|
1249
1453
|
* The media type without its parameters, so `text/event-stream; charset=utf-8` matches the bare name.
|
|
@@ -1425,6 +1629,39 @@ function flattenSchema(schema) {
|
|
|
1425
1629
|
function scanSchema(node, pointer = node.pointer) {
|
|
1426
1630
|
if (pointer) visit(node, pointer);
|
|
1427
1631
|
}
|
|
1632
|
+
let formatTally;
|
|
1633
|
+
/**
|
|
1634
|
+
* Runs `fn` and reports `KUBB_UNSUPPORTED_FORMAT` once per format with the number of schemas that use it,
|
|
1635
|
+
* instead of once per schema. `format` is an open value in OpenAPI, so a large spec would otherwise print
|
|
1636
|
+
* thousands of warnings that say the same thing. `fn` must be synchronous.
|
|
1637
|
+
*/
|
|
1638
|
+
function groupUnsupportedFormats(fn) {
|
|
1639
|
+
formatTally = /* @__PURE__ */ new Map();
|
|
1640
|
+
try {
|
|
1641
|
+
return fn();
|
|
1642
|
+
} finally {
|
|
1643
|
+
const tally = formatTally;
|
|
1644
|
+
formatTally = void 0;
|
|
1645
|
+
for (const [format, pointers] of tally) reportUnsupportedFormat(format, [...pointers]);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
function tallyFormat(format, pointer) {
|
|
1649
|
+
if (!formatTally) return reportUnsupportedFormat(format, [pointer]);
|
|
1650
|
+
formatTally.set(format, (formatTally.get(format) ?? /* @__PURE__ */ new Set()).add(pointer));
|
|
1651
|
+
}
|
|
1652
|
+
function reportUnsupportedFormat(format, pointers) {
|
|
1653
|
+
const count = pointers.length;
|
|
1654
|
+
_kubb_kit.Diagnostics.report({
|
|
1655
|
+
code: _kubb_kit.Diagnostics.code.unsupportedFormat,
|
|
1656
|
+
severity: "warning",
|
|
1657
|
+
message: count === 1 ? `Kubb does not map the format "${format}" to a specific type, so it falls back to the base type.` : `Kubb does not map the format "${format}" to a specific type, so ${count} schemas fall back to the base type.`,
|
|
1658
|
+
help: `Use a format Kubb supports, or handle "${format}" with a custom parser or plugin.`,
|
|
1659
|
+
location: {
|
|
1660
|
+
kind: "schema",
|
|
1661
|
+
pointer: pointers[0]
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1428
1665
|
function visit(node, pointer) {
|
|
1429
1666
|
node.pointer ??= pointer;
|
|
1430
1667
|
if (node.deprecated) _kubb_kit.Diagnostics.report({
|
|
@@ -1436,16 +1673,7 @@ function visit(node, pointer) {
|
|
|
1436
1673
|
pointer
|
|
1437
1674
|
}
|
|
1438
1675
|
});
|
|
1439
|
-
if (typeof node.format === "string" && !isHandledFormat(node.format))
|
|
1440
|
-
code: _kubb_kit.Diagnostics.code.unsupportedFormat,
|
|
1441
|
-
severity: "warning",
|
|
1442
|
-
message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
|
|
1443
|
-
help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
|
|
1444
|
-
location: {
|
|
1445
|
-
kind: "schema",
|
|
1446
|
-
pointer
|
|
1447
|
-
}
|
|
1448
|
-
});
|
|
1676
|
+
if (typeof node.format === "string" && !isHandledFormat(node.format)) tallyFormat(node.format, pointer);
|
|
1449
1677
|
if (node.type === "object") {
|
|
1450
1678
|
for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
|
|
1451
1679
|
if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
|
|
@@ -1554,18 +1782,6 @@ function getResponseByStatusCode({ operation, refs, statusCode }) {
|
|
|
1554
1782
|
function getRequestBody({ operation, refs }) {
|
|
1555
1783
|
return refs.deref(operation.schema.requestBody);
|
|
1556
1784
|
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Returns the primary request content type. Prefers a JSON-like media type (the last one wins
|
|
1559
|
-
* when several are declared), then the first declared one, defaulting to `'application/json'`.
|
|
1560
|
-
*/
|
|
1561
|
-
function getRequestContentType({ operation, refs }) {
|
|
1562
|
-
const content = getRequestBody({
|
|
1563
|
-
operation,
|
|
1564
|
-
refs
|
|
1565
|
-
})?.content;
|
|
1566
|
-
const mediaTypes = content ? Object.keys(content) : [];
|
|
1567
|
-
return mediaTypes.findLast(isJsonMimeType) ?? mediaTypes[0] ?? "application/json";
|
|
1568
|
-
}
|
|
1569
1785
|
/** Merges path-level and operation-level parameters; operation-level wins per `in:name`. */
|
|
1570
1786
|
function getParameters({ operation, refs }) {
|
|
1571
1787
|
const resolveParams = (params, pointer) => params.flatMap((entry, index) => {
|
|
@@ -1589,7 +1805,8 @@ const PARAMETER_STYLES = /* @__PURE__ */ new Set([
|
|
|
1589
1805
|
"simple",
|
|
1590
1806
|
"spaceDelimited",
|
|
1591
1807
|
"pipeDelimited",
|
|
1592
|
-
"deepObject"
|
|
1808
|
+
"deepObject",
|
|
1809
|
+
"cookie"
|
|
1593
1810
|
]);
|
|
1594
1811
|
function pickStyleFields({ style, explode, allowReserved }) {
|
|
1595
1812
|
return {
|
|
@@ -1598,16 +1815,45 @@ function pickStyleFields({ style, explode, allowReserved }) {
|
|
|
1598
1815
|
...allowReserved !== void 0 ? { allowReserved } : {}
|
|
1599
1816
|
};
|
|
1600
1817
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
const node = {
|
|
1605
|
-
...contentType !== void 0 ? { contentType } : {},
|
|
1606
|
-
...pickStyleFields(rest)
|
|
1607
|
-
};
|
|
1818
|
+
function getEncodingFields({ encoding, prefixEncoding, itemEncoding }) {
|
|
1819
|
+
const entries = Object.entries(encoding ?? {}).flatMap(([property, entry]) => {
|
|
1820
|
+
const node = toEncodingNode(entry);
|
|
1608
1821
|
return Object.keys(node).length ? [[property, node]] : [];
|
|
1609
1822
|
});
|
|
1610
|
-
|
|
1823
|
+
const item = itemEncoding && toEncodingNode(itemEncoding);
|
|
1824
|
+
return {
|
|
1825
|
+
...entries.length && { encoding: Object.fromEntries(entries) },
|
|
1826
|
+
...prefixEncoding?.length && { prefixEncoding: prefixEncoding.map(toEncodingNode) },
|
|
1827
|
+
...item && Object.keys(item).length && { itemEncoding: item }
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
function toEncodingNode({ contentType, encoding, prefixEncoding, itemEncoding, ...rest }) {
|
|
1831
|
+
return {
|
|
1832
|
+
...contentType !== void 0 && { contentType },
|
|
1833
|
+
...pickStyleFields(rest),
|
|
1834
|
+
...getEncodingFields({
|
|
1835
|
+
encoding,
|
|
1836
|
+
prefixEncoding,
|
|
1837
|
+
itemEncoding
|
|
1838
|
+
})
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
function getExamples({ example, examples }, refs) {
|
|
1842
|
+
const named = Object.entries(examples ?? {}).flatMap(([name, entry]) => {
|
|
1843
|
+
const resolved = refs.deref(entry);
|
|
1844
|
+
if (!resolved) return [];
|
|
1845
|
+
const { summary, description, dataValue, value, serializedValue } = resolved;
|
|
1846
|
+
const data = dataValue !== void 0 ? dataValue : value;
|
|
1847
|
+
return [{
|
|
1848
|
+
name,
|
|
1849
|
+
...typeof summary === "string" && { summary },
|
|
1850
|
+
...typeof description === "string" && { description },
|
|
1851
|
+
...data !== void 0 && { value: data },
|
|
1852
|
+
...typeof serializedValue === "string" && { serializedValue }
|
|
1853
|
+
}];
|
|
1854
|
+
});
|
|
1855
|
+
if (named.length) return { examples: named };
|
|
1856
|
+
return example !== void 0 ? { examples: [{ value: example }] } : {};
|
|
1611
1857
|
}
|
|
1612
1858
|
/**
|
|
1613
1859
|
* Returns the media type entry for `contentType`, resolving a `$ref` into `components.mediaTypes`. `null` when absent.
|
|
@@ -1636,6 +1882,24 @@ function getBodySchema({ content, contentType, refs, document }) {
|
|
|
1636
1882
|
if (binary) return binary;
|
|
1637
1883
|
return schema ? refs.derefKeepingRef(schema) : null;
|
|
1638
1884
|
}
|
|
1885
|
+
function getLinks(links, refs) {
|
|
1886
|
+
const entries = Object.entries(links ?? {}).flatMap(([name, value]) => {
|
|
1887
|
+
const link = refs.deref(value);
|
|
1888
|
+
if (!link) return [];
|
|
1889
|
+
const server = link.server ? toInputServers([link.server])[0] : void 0;
|
|
1890
|
+
return [[name, {
|
|
1891
|
+
...pickStrings(link, [
|
|
1892
|
+
"operationId",
|
|
1893
|
+
"operationRef",
|
|
1894
|
+
"description"
|
|
1895
|
+
]),
|
|
1896
|
+
...link.parameters && { parameters: link.parameters },
|
|
1897
|
+
..."requestBody" in link && { requestBody: link.requestBody },
|
|
1898
|
+
...server && { server }
|
|
1899
|
+
}]];
|
|
1900
|
+
});
|
|
1901
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
1902
|
+
}
|
|
1639
1903
|
/** The `stream` field of a content node, set when the media type is a stream. `node` is the parsed item schema. */
|
|
1640
1904
|
function getStreamField({ contentType, mediaType, refs, document, node }) {
|
|
1641
1905
|
if (!isStream(contentType, mediaType)) return {};
|
|
@@ -1706,11 +1970,12 @@ function collectPropertyKeysByFlag(schema, flag) {
|
|
|
1706
1970
|
function parseParameter({ param, pointer, parentName, options, parseSchema, refs }) {
|
|
1707
1971
|
const schemaName = parentName && param.name ? pascalCase(`${parentName} ${param.name}`) : void 0;
|
|
1708
1972
|
const contentType = param.schema ? void 0 : Object.keys(param.content ?? {})[0];
|
|
1709
|
-
const
|
|
1973
|
+
const mediaType = getMediaType({
|
|
1710
1974
|
content: param.content,
|
|
1711
1975
|
contentType,
|
|
1712
1976
|
refs
|
|
1713
|
-
})
|
|
1977
|
+
});
|
|
1978
|
+
const rawSchema = param.schema ?? mediaType?.schema;
|
|
1714
1979
|
const schema = rawSchema ? parseSchema({
|
|
1715
1980
|
schema: rawSchema,
|
|
1716
1981
|
name: schemaName
|
|
@@ -1726,7 +1991,10 @@ function parseParameter({ param, pointer, parentName, options, parseSchema, refs
|
|
|
1726
1991
|
},
|
|
1727
1992
|
required: param.required ?? false,
|
|
1728
1993
|
...pickStyleFields(param),
|
|
1729
|
-
...contentType !== void 0 ? { contentType } : {}
|
|
1994
|
+
...contentType !== void 0 ? { contentType } : {},
|
|
1995
|
+
...getExamples(mediaType ?? param, refs),
|
|
1996
|
+
...param.deprecated && { deprecated: true },
|
|
1997
|
+
...param.allowEmptyValue && { allowEmptyValue: true }
|
|
1730
1998
|
});
|
|
1731
1999
|
}
|
|
1732
2000
|
/**
|
|
@@ -1771,12 +2039,12 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1771
2039
|
name: requestBodyName
|
|
1772
2040
|
}), bodyRequired);
|
|
1773
2041
|
scanSchema(node, `${mediaTypePointer(body?.content, ct, `${pointer}/requestBody/content/${escapePointerToken(ct)}`)}/${schemaKeyword(mediaType)}`);
|
|
1774
|
-
const encoding = getEncoding(mediaType?.encoding);
|
|
1775
2042
|
return [_kubb_kit.ast.factory.createContent({
|
|
1776
2043
|
contentType: ct,
|
|
1777
2044
|
schema: node,
|
|
1778
2045
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly"),
|
|
1779
|
-
...
|
|
2046
|
+
...mediaType && getEncodingFields(mediaType),
|
|
2047
|
+
...mediaType && getExamples(mediaType, refs),
|
|
1780
2048
|
...getStreamField({
|
|
1781
2049
|
contentType: ct,
|
|
1782
2050
|
mediaType,
|
|
@@ -1819,6 +2087,8 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1819
2087
|
return {
|
|
1820
2088
|
schema: node,
|
|
1821
2089
|
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly"),
|
|
2090
|
+
...mediaType && getEncodingFields(mediaType),
|
|
2091
|
+
...mediaType && getExamples(mediaType, refs),
|
|
1822
2092
|
...ct ? getStreamField({
|
|
1823
2093
|
contentType: ct,
|
|
1824
2094
|
mediaType,
|
|
@@ -1828,22 +2098,41 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1828
2098
|
}) : {}
|
|
1829
2099
|
};
|
|
1830
2100
|
};
|
|
1831
|
-
const
|
|
2101
|
+
const declaredContentTypes = Object.keys(response?.content ?? {});
|
|
2102
|
+
const responseContent = (contentType && declaredContentTypes.length ? [contentType] : declaredContentTypes).map((ct) => _kubb_kit.ast.factory.createContent({
|
|
1832
2103
|
contentType: ct,
|
|
1833
2104
|
...parseEntrySchema(ct)
|
|
1834
2105
|
}));
|
|
1835
2106
|
if (responseContent.length === 0) responseContent.push(_kubb_kit.ast.factory.createContent({
|
|
1836
|
-
contentType:
|
|
1837
|
-
operation,
|
|
1838
|
-
refs
|
|
1839
|
-
}),
|
|
2107
|
+
contentType: "",
|
|
1840
2108
|
...parseEntrySchema(contentType)
|
|
1841
2109
|
}));
|
|
2110
|
+
const responsePointer = `${pointer}/responses/${statusCode}`;
|
|
2111
|
+
const headers = Object.entries(response?.headers ?? {}).flatMap(([name, value]) => {
|
|
2112
|
+
const header = name.toLowerCase() === "content-type" ? null : refs.deref(value);
|
|
2113
|
+
if (!header) return [];
|
|
2114
|
+
return [parseParameter({
|
|
2115
|
+
param: {
|
|
2116
|
+
...header,
|
|
2117
|
+
name,
|
|
2118
|
+
in: "header"
|
|
2119
|
+
},
|
|
2120
|
+
pointer: `${responsePointer}/headers/${escapePointerToken(name)}`,
|
|
2121
|
+
parentName: responseName,
|
|
2122
|
+
options,
|
|
2123
|
+
parseSchema,
|
|
2124
|
+
refs
|
|
2125
|
+
})];
|
|
2126
|
+
});
|
|
2127
|
+
const links = getLinks(response?.links, refs);
|
|
1842
2128
|
return _kubb_kit.ast.factory.createResponse({
|
|
1843
|
-
pointer:
|
|
2129
|
+
pointer: responsePointer,
|
|
1844
2130
|
statusCode,
|
|
2131
|
+
summary: response?.summary,
|
|
1845
2132
|
description: response?.description,
|
|
1846
|
-
content: responseContent
|
|
2133
|
+
content: responseContent,
|
|
2134
|
+
...headers.length && { headers },
|
|
2135
|
+
...links && { links }
|
|
1847
2136
|
});
|
|
1848
2137
|
});
|
|
1849
2138
|
const pickDoc = (key) => {
|
|
@@ -1853,6 +2142,7 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1853
2142
|
return typeof fallback === "string" ? fallback : void 0;
|
|
1854
2143
|
};
|
|
1855
2144
|
const servers = operation.schema.servers ?? operation.pathItem.servers;
|
|
2145
|
+
const auth = toAuthRequirements(operation.schema.security ?? document.security, document);
|
|
1856
2146
|
return _kubb_kit.ast.factory.createOperation({
|
|
1857
2147
|
pointer,
|
|
1858
2148
|
operationId,
|
|
@@ -1863,11 +2153,13 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1863
2153
|
summary: pickDoc("summary") || void 0,
|
|
1864
2154
|
description: pickDoc("description") || void 0,
|
|
1865
2155
|
deprecated: operation.schema.deprecated || void 0,
|
|
2156
|
+
externalDocs: getExternalDocs(operation.schema.externalDocs),
|
|
1866
2157
|
parameters,
|
|
1867
2158
|
requestBody,
|
|
1868
2159
|
responses,
|
|
1869
2160
|
extensions: pickExtensions(operation.schema),
|
|
1870
|
-
...servers && { servers: toInputServers(servers) }
|
|
2161
|
+
...servers && { servers: toInputServers(servers) },
|
|
2162
|
+
...auth && { auth }
|
|
1871
2163
|
});
|
|
1872
2164
|
}
|
|
1873
2165
|
// src/emit/createNode.ts
|
|
@@ -1888,6 +2180,7 @@ function createNode({ schema, name, nullable, defaultValue }, extras) {
|
|
|
1888
2180
|
default: defaultValue,
|
|
1889
2181
|
examples: extractExamples(schema),
|
|
1890
2182
|
format: schema.format,
|
|
2183
|
+
externalDocs: getExternalDocs(schema.externalDocs),
|
|
1891
2184
|
...extras
|
|
1892
2185
|
});
|
|
1893
2186
|
}
|
|
@@ -2016,6 +2309,15 @@ function removeInheritedDiscriminator({ members, refs }) {
|
|
|
2016
2309
|
});
|
|
2017
2310
|
}
|
|
2018
2311
|
/**
|
|
2312
|
+
* Whether a member can carry the discriminator property. A discriminator is only a hint, so an array
|
|
2313
|
+
* or primitive member keeps its own type instead of an intersection no value can satisfy.
|
|
2314
|
+
*/
|
|
2315
|
+
function isObjectLike(member, refs) {
|
|
2316
|
+
const type = (isReference(member) ? refs.resolve(member.$ref, { report: false }) : member)?.type;
|
|
2317
|
+
if (type === void 0) return true;
|
|
2318
|
+
return Array.isArray(type) ? type.includes("object") : type === "object";
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2019
2321
|
* Narrows each `oneOf`/`anyOf` member with its discriminant value, intersecting the member's own
|
|
2020
2322
|
* node with either the shared-properties slice carrying that value, or a synthetic discriminant
|
|
2021
2323
|
* literal. The referenced child schema's own definition is left untouched — narrowing happens only
|
|
@@ -2078,7 +2380,7 @@ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode,
|
|
|
2078
2380
|
schema: s,
|
|
2079
2381
|
name
|
|
2080
2382
|
});
|
|
2081
|
-
if (!discriminatorValues.length || !discriminator) return memberNode;
|
|
2383
|
+
if (!discriminatorValues.length || !discriminator || !isObjectLike(s, refs)) return memberNode;
|
|
2082
2384
|
const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(_kubb_kit.ast.applyMacros(sharedPropertiesNode, [(0, _kubb_kit.macroDiscriminatorEnum)({
|
|
2083
2385
|
propertyName: discriminator.propertyName,
|
|
2084
2386
|
values: discriminatorValues
|
|
@@ -2343,13 +2645,15 @@ const annotationKeys = /* @__PURE__ */ new Set([
|
|
|
2343
2645
|
]);
|
|
2344
2646
|
/**
|
|
2345
2647
|
* Moves `allOf` members that hold only annotations or `if`/`then`/`else` onto the parent, so they do
|
|
2346
|
-
* not become `unknown`. The parent's own values win
|
|
2648
|
+
* not become `unknown`. The parent's own values win. The parser reads their `if`/`then`/`else` into `conditionals`.
|
|
2347
2649
|
*/
|
|
2650
|
+
function isAnnotationMember(member) {
|
|
2651
|
+
return !!member && typeof member === "object" && !isReference(member) && Object.keys(member).every((key) => annotationKeys.has(key));
|
|
2652
|
+
}
|
|
2348
2653
|
function mergeAnnotations(schema) {
|
|
2349
2654
|
const members = schema.allOf;
|
|
2350
|
-
const
|
|
2351
|
-
const
|
|
2352
|
-
const rest = members.filter((member) => !isAnnotation(member));
|
|
2655
|
+
const annotations = members.filter(isAnnotationMember);
|
|
2656
|
+
const rest = members.filter((member) => !isAnnotationMember(member));
|
|
2353
2657
|
return annotations.length && rest.length ? {
|
|
2354
2658
|
...Object.assign({}, ...annotations),
|
|
2355
2659
|
...schema,
|
|
@@ -2382,7 +2686,8 @@ function convertAllOf({ schema: original, name, nullable, defaultValue, parse, r
|
|
|
2382
2686
|
default: mergedDefault,
|
|
2383
2687
|
examples: extractExamples(schema) ?? memberNode.examples,
|
|
2384
2688
|
pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
|
|
2385
|
-
format: schema.format ?? memberNode.format
|
|
2689
|
+
format: schema.format ?? memberNode.format,
|
|
2690
|
+
externalDocs: getExternalDocs(schema.externalDocs) ?? memberNode.externalDocs
|
|
2386
2691
|
});
|
|
2387
2692
|
}
|
|
2388
2693
|
const { members: discriminatedAllOf, discriminantValues } = extractDiscriminatedAllOfMembers({
|
|
@@ -2457,7 +2762,7 @@ function convertUnion(context) {
|
|
|
2457
2762
|
};
|
|
2458
2763
|
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
2459
2764
|
const declaredMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
2460
|
-
const defaultRef = getDefaultMappingRef(discriminator);
|
|
2765
|
+
const defaultRef = getDefaultMappingRef(typeof schema.discriminator === "object" ? schema.discriminator : void 0);
|
|
2461
2766
|
const unionMembers = resolveUnionMembers(defaultRef && !declaredMembers.some((member) => isReference(member) && member.$ref === defaultRef) ? [...declaredMembers, { $ref: defaultRef }] : declaredMembers, schema.properties ?? {});
|
|
2462
2767
|
const strategy = schema.oneOf ? "one" : "any";
|
|
2463
2768
|
const explicitDiscriminatorPropertyName = discriminator?.propertyName;
|
|
@@ -2569,11 +2874,18 @@ function getPrimitiveType(type) {
|
|
|
2569
2874
|
}
|
|
2570
2875
|
/** Shared by number, integer and bigint nodes; the exclusive bounds keep only the OAS 3.1 numeric form. */
|
|
2571
2876
|
function getNumericConstraints(schema) {
|
|
2877
|
+
const exclusiveMinimum = typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0;
|
|
2878
|
+
const exclusiveMaximum = typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0;
|
|
2879
|
+
const hasExclusive = exclusiveMinimum !== void 0 || exclusiveMaximum !== void 0;
|
|
2572
2880
|
return {
|
|
2573
2881
|
min: schema.minimum,
|
|
2574
2882
|
max: schema.maximum,
|
|
2575
|
-
|
|
2576
|
-
|
|
2883
|
+
...hasExclusive && { exclusive: {
|
|
2884
|
+
...exclusiveMinimum !== void 0 && { min: exclusiveMinimum },
|
|
2885
|
+
...exclusiveMaximum !== void 0 && { max: exclusiveMaximum }
|
|
2886
|
+
} },
|
|
2887
|
+
exclusiveMinimum,
|
|
2888
|
+
exclusiveMaximum,
|
|
2577
2889
|
multipleOf: schema.multipleOf
|
|
2578
2890
|
};
|
|
2579
2891
|
}
|
|
@@ -2582,11 +2894,11 @@ function getNumericConstraints(schema) {
|
|
|
2582
2894
|
*/
|
|
2583
2895
|
function isWithinBounds({ value, schema }) {
|
|
2584
2896
|
if (typeof value !== "number") return true;
|
|
2585
|
-
const { min, max,
|
|
2897
|
+
const { min, max, exclusive, multipleOf } = getNumericConstraints(schema);
|
|
2586
2898
|
if (min !== void 0 && value < min) return false;
|
|
2587
2899
|
if (max !== void 0 && value > max) return false;
|
|
2588
|
-
if (
|
|
2589
|
-
if (
|
|
2900
|
+
if (exclusive?.min !== void 0 && value <= exclusive.min) return false;
|
|
2901
|
+
if (exclusive?.max !== void 0 && value >= exclusive.max) return false;
|
|
2590
2902
|
if (multipleOf !== void 0 && Math.abs(value / multipleOf - Math.round(value / multipleOf)) > 1e-9) return false;
|
|
2591
2903
|
return true;
|
|
2592
2904
|
}
|
|
@@ -2744,6 +3056,15 @@ function convertFormattedEnum(context) {
|
|
|
2744
3056
|
}))
|
|
2745
3057
|
});
|
|
2746
3058
|
}
|
|
3059
|
+
function getStringContent({ schema, parse }) {
|
|
3060
|
+
const { contentMediaType: mediaType, contentEncoding: encoding, contentSchema } = schema;
|
|
3061
|
+
if (mediaType === void 0 && encoding === void 0 && !contentSchema) return {};
|
|
3062
|
+
return { content: {
|
|
3063
|
+
...mediaType !== void 0 && { mediaType },
|
|
3064
|
+
...encoding !== void 0 && { encoding },
|
|
3065
|
+
...contentSchema && { schema: parse({ schema: contentSchema }) }
|
|
3066
|
+
} };
|
|
3067
|
+
}
|
|
2747
3068
|
/**
|
|
2748
3069
|
* Converts a `type: 'string'` schema into a `StringSchemaNode`.
|
|
2749
3070
|
*/
|
|
@@ -2754,7 +3075,8 @@ function convertString(context) {
|
|
|
2754
3075
|
primitive: "string",
|
|
2755
3076
|
min: schema.minLength,
|
|
2756
3077
|
max: schema.maxLength,
|
|
2757
|
-
pattern: schema.pattern
|
|
3078
|
+
pattern: schema.pattern,
|
|
3079
|
+
...getStringContent(context)
|
|
2758
3080
|
});
|
|
2759
3081
|
}
|
|
2760
3082
|
/**
|
|
@@ -2768,6 +3090,15 @@ function convertNumeric(context, type) {
|
|
|
2768
3090
|
});
|
|
2769
3091
|
}
|
|
2770
3092
|
// src/emit/converters/structural.ts
|
|
3093
|
+
function getContains(schema, parse) {
|
|
3094
|
+
const { contains, minContains, maxContains } = schema;
|
|
3095
|
+
if (!contains || typeof contains !== "object") return {};
|
|
3096
|
+
return { contains: {
|
|
3097
|
+
schema: parse({ schema: contains }),
|
|
3098
|
+
...typeof minContains === "number" && { min: minContains },
|
|
3099
|
+
...typeof maxContains === "number" && { max: maxContains }
|
|
3100
|
+
} };
|
|
3101
|
+
}
|
|
2771
3102
|
/**
|
|
2772
3103
|
* Resolves a `true` or empty-object map schema (`additionalProperties`/`patternProperties`) to
|
|
2773
3104
|
* `options.unknownType`, otherwise parses it as a regular schema.
|
|
@@ -2820,7 +3151,7 @@ function convertObject({ schema, name, nullable, defaultValue, options, parse, r
|
|
|
2820
3151
|
required
|
|
2821
3152
|
});
|
|
2822
3153
|
}) : [];
|
|
2823
|
-
const additionalProperties = schema.additionalProperties;
|
|
3154
|
+
const additionalProperties = schema.additionalProperties ?? schema.unevaluatedProperties;
|
|
2824
3155
|
const additionalPropertiesNode = (() => {
|
|
2825
3156
|
if (additionalProperties === true) return true;
|
|
2826
3157
|
if (additionalProperties) return resolveMapSchema(additionalProperties, {
|
|
@@ -2850,6 +3181,8 @@ function convertObject({ schema, name, nullable, defaultValue, options, parse, r
|
|
|
2850
3181
|
additionalProperties: additionalPropertiesNode,
|
|
2851
3182
|
patternProperties,
|
|
2852
3183
|
propertyNames,
|
|
3184
|
+
min: schema.minProperties,
|
|
3185
|
+
max: schema.maxProperties,
|
|
2853
3186
|
minProperties: schema.minProperties,
|
|
2854
3187
|
maxProperties: schema.maxProperties
|
|
2855
3188
|
});
|
|
@@ -2880,7 +3213,8 @@ function convertObject({ schema, name, nullable, defaultValue, options, parse, r
|
|
|
2880
3213
|
*/
|
|
2881
3214
|
function convertTuple({ schema, name, nullable, defaultValue, options, parse }) {
|
|
2882
3215
|
const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }));
|
|
2883
|
-
const
|
|
3216
|
+
const tail = schema.items ?? schema.unevaluatedItems;
|
|
3217
|
+
const rest = tail === false ? void 0 : !tail || tail === true ? _kubb_kit.ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: tail });
|
|
2884
3218
|
return createNode({
|
|
2885
3219
|
schema,
|
|
2886
3220
|
name,
|
|
@@ -2892,7 +3226,8 @@ function convertTuple({ schema, name, nullable, defaultValue, options, parse })
|
|
|
2892
3226
|
items: tupleItems,
|
|
2893
3227
|
rest,
|
|
2894
3228
|
min: schema.minItems,
|
|
2895
|
-
max: schema.maxItems
|
|
3229
|
+
max: schema.maxItems,
|
|
3230
|
+
...getContains(schema, parse)
|
|
2896
3231
|
});
|
|
2897
3232
|
}
|
|
2898
3233
|
/**
|
|
@@ -2916,7 +3251,8 @@ function convertArray({ schema, name, nullable, defaultValue, options, parse })
|
|
|
2916
3251
|
items,
|
|
2917
3252
|
min: schema.minItems,
|
|
2918
3253
|
max: schema.maxItems,
|
|
2919
|
-
unique: schema.uniqueItems ?? void 0
|
|
3254
|
+
unique: schema.uniqueItems ?? void 0,
|
|
3255
|
+
...getContains(schema, parse)
|
|
2920
3256
|
});
|
|
2921
3257
|
}
|
|
2922
3258
|
// src/emit/parseSchema.ts
|
|
@@ -3044,6 +3380,20 @@ const schemaRules = [
|
|
|
3044
3380
|
];
|
|
3045
3381
|
// src/parser.ts
|
|
3046
3382
|
/**
|
|
3383
|
+
* `unevaluated*` also sees what composition siblings evaluate, so with such a sibling it is dropped to keep the schema open.
|
|
3384
|
+
*/
|
|
3385
|
+
function hasUnevaluatedSiblings(schema) {
|
|
3386
|
+
if (schema.unevaluatedProperties === void 0 && schema.unevaluatedItems === void 0) return false;
|
|
3387
|
+
return [
|
|
3388
|
+
schema.allOf,
|
|
3389
|
+
schema.anyOf,
|
|
3390
|
+
schema.oneOf,
|
|
3391
|
+
schema.if,
|
|
3392
|
+
schema.dependentSchemas,
|
|
3393
|
+
schema.$ref
|
|
3394
|
+
].some((value) => value !== void 0);
|
|
3395
|
+
}
|
|
3396
|
+
/**
|
|
3047
3397
|
* Creates the schema converter bound to one OpenAPI document: `parseSchema` dispatches each
|
|
3048
3398
|
* schema through the ordered `schemaRules` table, with the parser options merged once.
|
|
3049
3399
|
*
|
|
@@ -3061,6 +3411,13 @@ function createSchemaParser(ctx) {
|
|
|
3061
3411
|
schema: flattenedSchema,
|
|
3062
3412
|
name
|
|
3063
3413
|
});
|
|
3414
|
+
if (hasUnevaluatedSiblings(schema)) {
|
|
3415
|
+
const { unevaluatedProperties: _properties, unevaluatedItems: _items, ...open } = schema;
|
|
3416
|
+
return parseSchema({
|
|
3417
|
+
schema: open,
|
|
3418
|
+
name
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3064
3421
|
const nullable = isNullable(schema) || void 0;
|
|
3065
3422
|
const context = {
|
|
3066
3423
|
schema,
|
|
@@ -3074,8 +3431,62 @@ function createSchemaParser(ctx) {
|
|
|
3074
3431
|
refs,
|
|
3075
3432
|
renames
|
|
3076
3433
|
};
|
|
3077
|
-
|
|
3078
|
-
return createNode(context, { type: options.emptySchemaType });
|
|
3434
|
+
const rule = schemaRules.find((entry) => entry.match(context));
|
|
3435
|
+
return withConditionals(rule ? rule.convert(context) : createNode(context, { type: options.emptySchemaType }), schema);
|
|
3436
|
+
}
|
|
3437
|
+
function requireKeys(keys) {
|
|
3438
|
+
const unknown = _kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schemaTypes.unknown });
|
|
3439
|
+
return _kubb_kit.ast.factory.createSchema({
|
|
3440
|
+
type: _kubb_kit.ast.schemaTypes.object,
|
|
3441
|
+
primitive: "object",
|
|
3442
|
+
properties: keys.map((name) => _kubb_kit.ast.factory.createProperty({
|
|
3443
|
+
name,
|
|
3444
|
+
required: true,
|
|
3445
|
+
schema: unknown
|
|
3446
|
+
}))
|
|
3447
|
+
});
|
|
3448
|
+
}
|
|
3449
|
+
/**
|
|
3450
|
+
* Adds `if`/`then`/`else`, `dependentRequired` and `dependentSchemas` to the node as `conditionals`.
|
|
3451
|
+
*/
|
|
3452
|
+
function withConditionals(node, schema) {
|
|
3453
|
+
const conditionals = [schema, ...Array.isArray(schema.allOf) ? schema.allOf.filter(isAnnotationMember) : []].flatMap(toConditionals);
|
|
3454
|
+
return conditionals.length ? {
|
|
3455
|
+
...node,
|
|
3456
|
+
conditionals
|
|
3457
|
+
} : node;
|
|
3458
|
+
}
|
|
3459
|
+
function toConditionals(schema) {
|
|
3460
|
+
const branch = (value) => {
|
|
3461
|
+
if (!value || typeof value !== "object") return void 0;
|
|
3462
|
+
const { required, type, ...rest } = value;
|
|
3463
|
+
if (Array.isArray(required) && required.length && (type === void 0 || type === "object") && !Object.keys(rest).length) return requireKeys(required);
|
|
3464
|
+
return parseSchema({ schema: value });
|
|
3465
|
+
};
|
|
3466
|
+
const entries = (value) => value && typeof value === "object" ? Object.entries(value) : [];
|
|
3467
|
+
const conditionals = [];
|
|
3468
|
+
const condition = branch(schema.if);
|
|
3469
|
+
if (condition) {
|
|
3470
|
+
const thenNode = branch(schema.then);
|
|
3471
|
+
const elseNode = branch(schema.else);
|
|
3472
|
+
conditionals.push({
|
|
3473
|
+
if: condition,
|
|
3474
|
+
...thenNode && { then: thenNode },
|
|
3475
|
+
...elseNode && { else: elseNode }
|
|
3476
|
+
});
|
|
3477
|
+
}
|
|
3478
|
+
for (const [key, required] of entries(schema.dependentRequired)) if (Array.isArray(required) && required.length && required.every((name) => typeof name === "string")) conditionals.push({
|
|
3479
|
+
if: requireKeys([key]),
|
|
3480
|
+
then: requireKeys(required)
|
|
3481
|
+
});
|
|
3482
|
+
for (const [key, dependent] of entries(schema.dependentSchemas)) {
|
|
3483
|
+
const thenNode = branch(dependent);
|
|
3484
|
+
if (thenNode) conditionals.push({
|
|
3485
|
+
if: requireKeys([key]),
|
|
3486
|
+
then: thenNode
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
return conditionals;
|
|
3079
3490
|
}
|
|
3080
3491
|
return parseSchema;
|
|
3081
3492
|
}
|
|
@@ -3414,6 +3825,7 @@ const adapterOas = (0, _kubb_core.defineExtension)({
|
|
|
3414
3825
|
schemaNodes.push(promotedEnums ? refPromotedEnums(node, promotedEnums) : node);
|
|
3415
3826
|
}
|
|
3416
3827
|
const operations = promotedEnums ? operationNodes.map((node) => refPromotedEnums(node, promotedEnums)) : operationNodes;
|
|
3828
|
+
const authSchemes = toAuthSchemes(document.components?.securitySchemes, refs);
|
|
3417
3829
|
return _kubb_kit.ast.factory.createInput({
|
|
3418
3830
|
schemas: schemaNodes,
|
|
3419
3831
|
operations,
|
|
@@ -3421,23 +3833,25 @@ const adapterOas = (0, _kubb_core.defineExtension)({
|
|
|
3421
3833
|
title: document.info?.title,
|
|
3422
3834
|
description: document.info?.description,
|
|
3423
3835
|
version: document.info?.version,
|
|
3836
|
+
summary: document.info?.summary,
|
|
3837
|
+
termsOfService: document.info?.termsOfService,
|
|
3838
|
+
contact: pickStrings(document.info?.contact, [
|
|
3839
|
+
"name",
|
|
3840
|
+
"url",
|
|
3841
|
+
"email"
|
|
3842
|
+
]),
|
|
3843
|
+
license: typeof document.info?.license?.name === "string" ? {
|
|
3844
|
+
...pickStrings(document.info.license, ["identifier", "url"]),
|
|
3845
|
+
name: document.info.license.name
|
|
3846
|
+
} : void 0,
|
|
3847
|
+
externalDocs: getExternalDocs(document.externalDocs),
|
|
3424
3848
|
baseURL: resolveBaseUrl({
|
|
3425
3849
|
document,
|
|
3426
3850
|
server
|
|
3427
3851
|
}),
|
|
3428
3852
|
servers: toInputServers(document.servers ?? []),
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
return scheme?.type ? [{
|
|
3432
|
-
name,
|
|
3433
|
-
type: scheme.type,
|
|
3434
|
-
description: scheme.description,
|
|
3435
|
-
scheme: scheme.scheme,
|
|
3436
|
-
bearerFormat: scheme.bearerFormat,
|
|
3437
|
-
in: scheme.in,
|
|
3438
|
-
parameterName: scheme.name
|
|
3439
|
-
}] : [];
|
|
3440
|
-
}),
|
|
3853
|
+
...document.tags?.length && { tags: toInputTags(document.tags) },
|
|
3854
|
+
...authSchemes.length && { auth: authSchemes },
|
|
3441
3855
|
extensions: pickExtensions(document)
|
|
3442
3856
|
}
|
|
3443
3857
|
});
|
|
@@ -3478,18 +3892,19 @@ const adapterOas = (0, _kubb_core.defineExtension)({
|
|
|
3478
3892
|
parsedDocument = document;
|
|
3479
3893
|
const refs = createRefs(document);
|
|
3480
3894
|
const { schemas, renames, pointers } = getSchemas(document, { contentType }, refs);
|
|
3481
|
-
|
|
3895
|
+
const parseSchema = createSchemaParser({
|
|
3896
|
+
document,
|
|
3897
|
+
refs,
|
|
3898
|
+
renames,
|
|
3899
|
+
options: parserOptions
|
|
3900
|
+
});
|
|
3901
|
+
return groupUnsupportedFormats(() => parseInput({
|
|
3482
3902
|
document,
|
|
3483
3903
|
refs,
|
|
3484
3904
|
schemas,
|
|
3485
3905
|
pointers,
|
|
3486
|
-
parseSchema
|
|
3487
|
-
|
|
3488
|
-
refs,
|
|
3489
|
-
renames,
|
|
3490
|
-
options: parserOptions
|
|
3491
|
-
})
|
|
3492
|
-
});
|
|
3906
|
+
parseSchema
|
|
3907
|
+
}));
|
|
3493
3908
|
})();
|
|
3494
3909
|
inputCache.set(source, {
|
|
3495
3910
|
promise,
|