@kubb/adapter-oas 5.15.4 → 5.16.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/README.md +1 -1
- package/dist/index.cjs +288 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -8
- package/dist/index.d.ts +20 -8
- package/dist/index.js +289 -35
- package/dist/index.js.map +1 -1
- package/docs/options.md +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
### OpenAPI and Swagger adapter for Kubb
|
|
26
26
|
|
|
27
|
-
Parses and validates Swagger 2.0, OpenAPI 3.0, and
|
|
27
|
+
Parses and validates Swagger 2.0, OpenAPI 3.0, 3.1, and 3.2 specifications and transforms them into a `@kubb/ast` `RootNode` for downstream code generation plugins.
|
|
28
28
|
|
|
29
29
|
## Installation
|
|
30
30
|
|
package/dist/index.cjs
CHANGED
|
@@ -35,7 +35,7 @@ var compatibility = {
|
|
|
35
35
|
"node": ">=22.22"
|
|
36
36
|
};
|
|
37
37
|
// package.json
|
|
38
|
-
var version = "5.
|
|
38
|
+
var version = "5.16.0";
|
|
39
39
|
// src/constants.ts
|
|
40
40
|
/**
|
|
41
41
|
* Default parser options applied when no explicit options are provided.
|
|
@@ -75,6 +75,17 @@ const SUPPORTED_METHODS = /* @__PURE__ */ new Set([
|
|
|
75
75
|
"query"
|
|
76
76
|
]);
|
|
77
77
|
/**
|
|
78
|
+
* Media types OpenAPI 3.2 reads as a stream of items (its "sequential media types"), each described by `itemSchema`.
|
|
79
|
+
*/
|
|
80
|
+
const STREAM_MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
81
|
+
"application/jsonl",
|
|
82
|
+
"application/x-ndjson",
|
|
83
|
+
"application/json-seq",
|
|
84
|
+
"application/geo+json-seq",
|
|
85
|
+
"text/event-stream",
|
|
86
|
+
"multipart/mixed"
|
|
87
|
+
]);
|
|
88
|
+
/**
|
|
78
89
|
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
79
90
|
*
|
|
80
91
|
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
@@ -399,6 +410,14 @@ function isNullable(schema) {
|
|
|
399
410
|
return false;
|
|
400
411
|
}
|
|
401
412
|
/**
|
|
413
|
+
* Returns `true` for an OpenAPI 3.2 or later document. Swagger 2.0 and OpenAPI 3.0 documents are
|
|
414
|
+
* upgraded to 3.1 before parsing, so `openapi` is always set here.
|
|
415
|
+
*/
|
|
416
|
+
function isOpenApi32(document) {
|
|
417
|
+
const [major = 0, minor = 0] = String(document.openapi).split(".").map(Number);
|
|
418
|
+
return major > 3 || major === 3 && minor >= 2;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
402
421
|
* Returns the `x-*` keys of `source` as-is, or `undefined` when there are none.
|
|
403
422
|
*/
|
|
404
423
|
function pickExtensions(source) {
|
|
@@ -465,7 +484,8 @@ const jsonMimeFragments = [
|
|
|
465
484
|
* ```
|
|
466
485
|
*/
|
|
467
486
|
function isJsonMimeType(mimeType) {
|
|
468
|
-
|
|
487
|
+
const lower = mimeType.toLowerCase();
|
|
488
|
+
return jsonMimeFragments.some((fragment) => lower.includes(fragment));
|
|
469
489
|
}
|
|
470
490
|
// src/refs.ts
|
|
471
491
|
/**
|
|
@@ -889,6 +909,17 @@ function reportInvalidSpec(message) {
|
|
|
889
909
|
* ```
|
|
890
910
|
*/
|
|
891
911
|
async function validateDocument(document, { throwOnError = false } = {}) {
|
|
912
|
+
if (isOpenApi32(document)) {
|
|
913
|
+
const diagnostic = {
|
|
914
|
+
code: _kubb_kit.Diagnostics.code.invalidSpec,
|
|
915
|
+
severity: "warning",
|
|
916
|
+
message: "The document was not validated: `@readme/openapi-parser` does not support OpenAPI 3.2 yet.",
|
|
917
|
+
help: "Set `validate: false` on the adapter to skip this check until the validator supports OpenAPI 3.2."
|
|
918
|
+
};
|
|
919
|
+
if (throwOnError) throw new _kubb_kit.Diagnostics.Error(diagnostic);
|
|
920
|
+
_kubb_kit.Diagnostics.report(diagnostic);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
892
923
|
const { compileErrors, validate } = await import("@readme/openapi-parser");
|
|
893
924
|
let result;
|
|
894
925
|
try {
|
|
@@ -918,6 +949,7 @@ function reportValidationErrors({ errors, additionalErrors }) {
|
|
|
918
949
|
* validating in place where worker threads are missing, as in edge runtimes.
|
|
919
950
|
*/
|
|
920
951
|
async function validateDocumentInWorker(document) {
|
|
952
|
+
if (isOpenApi32(document)) return validateDocument(document);
|
|
921
953
|
let message;
|
|
922
954
|
try {
|
|
923
955
|
const { Worker } = await import("node:worker_threads");
|
|
@@ -1188,6 +1220,123 @@ function resolveServerUrl(server, overrides) {
|
|
|
1188
1220
|
}
|
|
1189
1221
|
return url;
|
|
1190
1222
|
}
|
|
1223
|
+
// src/model/mediaType.ts
|
|
1224
|
+
/**
|
|
1225
|
+
* The media type without its parameters, so `text/event-stream; charset=utf-8` matches the bare name.
|
|
1226
|
+
*/
|
|
1227
|
+
function baseMediaType(contentType) {
|
|
1228
|
+
return contentType.split(";")[0].trim().toLowerCase();
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Returns `true` for a media type read as a stream of items: one that OpenAPI 3.2 lists, or any that declares `itemSchema`.
|
|
1232
|
+
*/
|
|
1233
|
+
function isStream(contentType, mediaType) {
|
|
1234
|
+
return STREAM_MEDIA_TYPES.has(baseMediaType(contentType)) || mediaType?.itemSchema !== void 0;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Whether the media type is `text/event-stream`, with or without parameters.
|
|
1238
|
+
*/
|
|
1239
|
+
function isEventStream(contentType) {
|
|
1240
|
+
return baseMediaType(contentType) === "text/event-stream";
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Replaces a `data` string that holds JSON with its `contentSchema`, so the event reads as parsed.
|
|
1244
|
+
*/
|
|
1245
|
+
function withParsedData(schema, refs) {
|
|
1246
|
+
const data = schema.properties?.data;
|
|
1247
|
+
const resolved = data && isReference(data) ? refs.resolve(data.$ref, { report: false }) : data;
|
|
1248
|
+
if (!resolved?.contentSchema || !resolved.contentMediaType || !isJsonMimeType(resolved.contentMediaType)) return schema;
|
|
1249
|
+
return {
|
|
1250
|
+
...schema,
|
|
1251
|
+
properties: {
|
|
1252
|
+
...schema.properties,
|
|
1253
|
+
data: resolved.contentSchema
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Keywords next to `$ref` that only annotate it, so a reference that carries nothing else stays a reference.
|
|
1259
|
+
*/
|
|
1260
|
+
const REF_ANNOTATIONS = /* @__PURE__ */ new Set([
|
|
1261
|
+
"$ref",
|
|
1262
|
+
"summary",
|
|
1263
|
+
"description",
|
|
1264
|
+
"$comment"
|
|
1265
|
+
]);
|
|
1266
|
+
/**
|
|
1267
|
+
* Applies `extra` on top of `base` the way JSON Schema does: `properties` and `required` add up, other keywords of `extra` win.
|
|
1268
|
+
*/
|
|
1269
|
+
function mergeSchemas(base, extra) {
|
|
1270
|
+
const required = [.../* @__PURE__ */ new Set([...Array.isArray(base.required) ? base.required : [], ...Array.isArray(extra.required) ? extra.required : []])];
|
|
1271
|
+
return {
|
|
1272
|
+
type: "object",
|
|
1273
|
+
...base,
|
|
1274
|
+
...extra,
|
|
1275
|
+
properties: {
|
|
1276
|
+
...base.properties,
|
|
1277
|
+
...extra.properties
|
|
1278
|
+
},
|
|
1279
|
+
...required.length ? { required } : {}
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Normalizes the OpenAPI 3.2 `text/event-stream` item schema into one schema per event.
|
|
1284
|
+
* - Keywords next to a `$ref` apply to the referenced schema.
|
|
1285
|
+
* - Shared `properties` merge into each inline `oneOf`/`anyOf` member.
|
|
1286
|
+
* - A JSON `data` string becomes its `contentSchema`.
|
|
1287
|
+
*/
|
|
1288
|
+
function toEventSchema(schema, refs) {
|
|
1289
|
+
if (isReference(schema)) {
|
|
1290
|
+
const siblings = Object.fromEntries(Object.entries(schema).filter(([key]) => !REF_ANNOTATIONS.has(key)));
|
|
1291
|
+
const resolved = Object.keys(siblings).length ? refs.resolve(schema.$ref, { report: false }) : null;
|
|
1292
|
+
return resolved ? toEventSchema(mergeSchemas(resolved, siblings), refs) : schema;
|
|
1293
|
+
}
|
|
1294
|
+
const key = schema.oneOf?.length ? "oneOf" : schema.anyOf?.length ? "anyOf" : void 0;
|
|
1295
|
+
if (!key) return withParsedData(schema, refs);
|
|
1296
|
+
const members = schema[key];
|
|
1297
|
+
const { properties, required, ...rest } = schema;
|
|
1298
|
+
const merge = !!properties && !members.some(isReference);
|
|
1299
|
+
const shared = {
|
|
1300
|
+
properties,
|
|
1301
|
+
required
|
|
1302
|
+
};
|
|
1303
|
+
return {
|
|
1304
|
+
...merge ? rest : schema,
|
|
1305
|
+
[key]: members.map((member) => isReference(member) ? member : withParsedData(merge ? mergeSchemas(shared, member) : member, refs))
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Whether the schema, following `$ref`s, or a composition member of it declares an `event` or `data` property.
|
|
1310
|
+
*/
|
|
1311
|
+
function declaresEventFields(schema, refs, seen) {
|
|
1312
|
+
if (isReference(schema)) {
|
|
1313
|
+
if (seen.has(schema.$ref)) return false;
|
|
1314
|
+
seen.add(schema.$ref);
|
|
1315
|
+
const resolved = refs.resolve(schema.$ref, { report: false });
|
|
1316
|
+
return !!resolved && declaresEventFields(resolved, refs, seen);
|
|
1317
|
+
}
|
|
1318
|
+
if (schema.properties && (Object.hasOwn(schema.properties, "event") || Object.hasOwn(schema.properties, "data"))) return true;
|
|
1319
|
+
return [
|
|
1320
|
+
...schema.oneOf ?? [],
|
|
1321
|
+
...schema.anyOf ?? [],
|
|
1322
|
+
...schema.allOf ?? []
|
|
1323
|
+
].some((member) => declaresEventFields(member, refs, seen));
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Returns `'event'` when the item declares `event` or `data`, directly or in a composition member, else `'data'`.
|
|
1327
|
+
*/
|
|
1328
|
+
function getItemShape(schema, refs) {
|
|
1329
|
+
return declaresEventFields(schema, refs, /* @__PURE__ */ new Set()) ? "event" : "data";
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Returns the raw item schema: `itemSchema`, or `schema` on `text/event-stream` as 3.0 and 3.1 documents write one event.
|
|
1333
|
+
*/
|
|
1334
|
+
function getItemSchema({ contentType, mediaType, refs, document }) {
|
|
1335
|
+
if (!mediaType || !isStream(contentType, mediaType)) return null;
|
|
1336
|
+
const raw = mediaType.itemSchema ?? (isEventStream(contentType) && !isOpenApi32(document) ? mediaType.schema : void 0);
|
|
1337
|
+
if (!raw) return null;
|
|
1338
|
+
return isEventStream(contentType) ? toEventSchema(raw, refs) : raw;
|
|
1339
|
+
}
|
|
1191
1340
|
// src/emit/schemaShape.ts
|
|
1192
1341
|
/**
|
|
1193
1342
|
* Whether `format` maps to a dedicated type; `false` is what `KUBB_UNSUPPORTED_FORMAT` flags.
|
|
@@ -1300,6 +1449,13 @@ function slugify(value) {
|
|
|
1300
1449
|
return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
1301
1450
|
}
|
|
1302
1451
|
/**
|
|
1452
|
+
* Returns the operation's JSON pointer, under `additionalOperations` for an OpenAPI 3.2 method outside the fixed fields.
|
|
1453
|
+
*/
|
|
1454
|
+
function getOperationPointer({ path, method }) {
|
|
1455
|
+
const pathPointer = `#/paths/${escapePointerToken(path)}`;
|
|
1456
|
+
return SUPPORTED_METHODS.has(method.toLowerCase()) ? `${pathPointer}/${method.toLowerCase()}` : `${pathPointer}/additionalOperations/${escapePointerToken(method)}`;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1303
1459
|
* Returns the operation's `operationId`, falling back to `<method>_<slugified-path>` when absent.
|
|
1304
1460
|
*/
|
|
1305
1461
|
function getOperationId({ path, method, schema }) {
|
|
@@ -1352,7 +1508,7 @@ function getParameters({ operation, refs }) {
|
|
|
1352
1508
|
}] : [];
|
|
1353
1509
|
});
|
|
1354
1510
|
const pathPointer = `#/paths/${escapePointerToken(operation.path)}`;
|
|
1355
|
-
const operationParams = resolveParams(operation.schema?.parameters || [],
|
|
1511
|
+
const operationParams = resolveParams(operation.schema?.parameters || [], getOperationPointer(operation));
|
|
1356
1512
|
const pathLevelParams = resolveParams(operation.pathItem.parameters ?? [], pathPointer);
|
|
1357
1513
|
const paramMap = /* @__PURE__ */ new Map();
|
|
1358
1514
|
for (const entry of [...pathLevelParams, ...operationParams]) if (entry.param.name && entry.param.in) paramMap.set(`${entry.param.in}:${entry.param.name}`, entry);
|
|
@@ -1385,17 +1541,59 @@ function getEncoding(encoding) {
|
|
|
1385
1541
|
});
|
|
1386
1542
|
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
1387
1543
|
}
|
|
1388
|
-
/**
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1544
|
+
/**
|
|
1545
|
+
* Returns the media type entry for `contentType`, resolving a `$ref` into `components.mediaTypes`. `null` when absent.
|
|
1546
|
+
*/
|
|
1547
|
+
function getMediaType({ content, contentType, refs }) {
|
|
1548
|
+
return contentType ? refs.deref(content?.[contentType]) : null;
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Returns the schema for one media type of a `content` map, with the binary fallback for an emptied non-JSON
|
|
1552
|
+
* entry. For a stream this is the item schema, so `schema: string` next to `itemSchema` types one item.
|
|
1553
|
+
*/
|
|
1554
|
+
function getBodySchema({ content, contentType, refs, document }) {
|
|
1555
|
+
const entry = getMediaType({
|
|
1556
|
+
content,
|
|
1557
|
+
contentType,
|
|
1558
|
+
refs
|
|
1559
|
+
});
|
|
1560
|
+
if (!contentType || !entry) return null;
|
|
1561
|
+
const schema = getItemSchema({
|
|
1562
|
+
contentType,
|
|
1563
|
+
mediaType: entry,
|
|
1564
|
+
refs,
|
|
1565
|
+
document
|
|
1566
|
+
}) ?? entry.schema;
|
|
1567
|
+
const binary = entry.itemSchema ? void 0 : getBinaryFallbackSchema(contentType, schema);
|
|
1393
1568
|
if (binary) return binary;
|
|
1394
|
-
return
|
|
1569
|
+
return schema ? refs.derefKeepingRef(schema) : null;
|
|
1570
|
+
}
|
|
1571
|
+
/** The `stream` field of a content node, set when the media type is a stream. `node` is the parsed item schema. */
|
|
1572
|
+
function getStreamField({ contentType, mediaType, refs, document, node }) {
|
|
1573
|
+
if (!isStream(contentType, mediaType)) return {};
|
|
1574
|
+
const raw = getItemSchema({
|
|
1575
|
+
contentType,
|
|
1576
|
+
mediaType,
|
|
1577
|
+
refs,
|
|
1578
|
+
document
|
|
1579
|
+
});
|
|
1580
|
+
return { stream: {
|
|
1581
|
+
...raw ? { schema: node } : {},
|
|
1582
|
+
...raw && isEventStream(contentType) ? { shape: getItemShape(raw, refs) } : {}
|
|
1583
|
+
} };
|
|
1584
|
+
}
|
|
1585
|
+
/** The media type keyword the body schema was read from, for the diagnostic pointer. */
|
|
1586
|
+
function schemaKeyword(mediaType) {
|
|
1587
|
+
return mediaType?.itemSchema ? "itemSchema" : "schema";
|
|
1588
|
+
}
|
|
1589
|
+
/** Pointer of a media type entry, following a `$ref` into `components.mediaTypes` so a diagnostic lands on the source. */
|
|
1590
|
+
function mediaTypePointer(content, contentType, fallback) {
|
|
1591
|
+
const entry = content?.[contentType];
|
|
1592
|
+
return isReference(entry) ? entry.$ref : fallback;
|
|
1395
1593
|
}
|
|
1396
1594
|
/**
|
|
1397
|
-
* Builds an `Operation` for every supported HTTP method
|
|
1398
|
-
* `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
1595
|
+
* Builds an `Operation` for every supported HTTP method and every OpenAPI 3.2 `additionalOperations` entry on
|
|
1596
|
+
* every path, in document order. `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
1399
1597
|
*/
|
|
1400
1598
|
function getOperations(document, refs) {
|
|
1401
1599
|
const operations = [];
|
|
@@ -1417,6 +1615,12 @@ function getOperations(document, refs) {
|
|
|
1417
1615
|
pathItem
|
|
1418
1616
|
});
|
|
1419
1617
|
}
|
|
1618
|
+
for (const [method, schema] of Object.entries(pathItem.additionalOperations ?? {})) if (schema && typeof schema === "object" && !SUPPORTED_METHODS.has(method.toLowerCase())) operations.push({
|
|
1619
|
+
path,
|
|
1620
|
+
method,
|
|
1621
|
+
schema,
|
|
1622
|
+
pathItem
|
|
1623
|
+
});
|
|
1420
1624
|
}
|
|
1421
1625
|
return operations;
|
|
1422
1626
|
}
|
|
@@ -1430,10 +1634,15 @@ function collectPropertyKeysByFlag(schema, flag) {
|
|
|
1430
1634
|
}
|
|
1431
1635
|
return keys.length ? keys : null;
|
|
1432
1636
|
}
|
|
1433
|
-
|
|
1637
|
+
/** Converts one parameter into a `ParameterNode`, naming an inline schema after the operation and the parameter. */
|
|
1638
|
+
function parseParameter({ param, pointer, parentName, options, parseSchema, refs }) {
|
|
1434
1639
|
const schemaName = parentName && param.name ? pascalCase(`${parentName} ${param.name}`) : void 0;
|
|
1435
1640
|
const contentType = param.schema ? void 0 : Object.keys(param.content ?? {})[0];
|
|
1436
|
-
const rawSchema = param.schema ?? (
|
|
1641
|
+
const rawSchema = param.schema ?? getMediaType({
|
|
1642
|
+
content: param.content,
|
|
1643
|
+
contentType,
|
|
1644
|
+
refs
|
|
1645
|
+
})?.schema;
|
|
1437
1646
|
const schema = rawSchema ? parseSchema({
|
|
1438
1647
|
schema: rawSchema,
|
|
1439
1648
|
name: schemaName
|
|
@@ -1456,16 +1665,17 @@ function parseParameter({ param, pointer, parentName, options, parseSchema }) {
|
|
|
1456
1665
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1457
1666
|
*/
|
|
1458
1667
|
function parseOperation({ operation, ...ctx }) {
|
|
1459
|
-
const { refs, contentType, options, parseSchema } = ctx;
|
|
1668
|
+
const { document, refs, contentType, options, parseSchema } = ctx;
|
|
1460
1669
|
const operationId = getOperationId(operation);
|
|
1461
1670
|
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
1462
|
-
const pointer =
|
|
1671
|
+
const pointer = getOperationPointer(operation);
|
|
1463
1672
|
const parameters = getParameters({
|
|
1464
1673
|
operation,
|
|
1465
1674
|
refs
|
|
1466
1675
|
}).map((entry) => parseParameter({
|
|
1467
1676
|
options,
|
|
1468
1677
|
parseSchema,
|
|
1678
|
+
refs,
|
|
1469
1679
|
...entry,
|
|
1470
1680
|
parentName: operationName
|
|
1471
1681
|
}));
|
|
@@ -1479,20 +1689,33 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1479
1689
|
const schema = getBodySchema({
|
|
1480
1690
|
content: body?.content,
|
|
1481
1691
|
contentType: ct,
|
|
1482
|
-
refs
|
|
1692
|
+
refs,
|
|
1693
|
+
document
|
|
1483
1694
|
});
|
|
1484
1695
|
if (!schema) return [];
|
|
1485
|
-
const
|
|
1696
|
+
const mediaType = getMediaType({
|
|
1697
|
+
content: body?.content,
|
|
1698
|
+
contentType: ct,
|
|
1699
|
+
refs
|
|
1700
|
+
});
|
|
1701
|
+
const node = _kubb_kit.ast.optionality(parseSchema({
|
|
1486
1702
|
schema,
|
|
1487
1703
|
name: requestBodyName
|
|
1488
|
-
});
|
|
1489
|
-
scanSchema(node, `${pointer}/requestBody/content/${escapePointerToken(ct)}
|
|
1490
|
-
const encoding = getEncoding(
|
|
1704
|
+
}), bodyRequired);
|
|
1705
|
+
scanSchema(node, `${mediaTypePointer(body?.content, ct, `${pointer}/requestBody/content/${escapePointerToken(ct)}`)}/${schemaKeyword(mediaType)}`);
|
|
1706
|
+
const encoding = getEncoding(mediaType?.encoding);
|
|
1491
1707
|
return [_kubb_kit.ast.factory.createContent({
|
|
1492
1708
|
contentType: ct,
|
|
1493
|
-
schema:
|
|
1709
|
+
schema: node,
|
|
1494
1710
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly"),
|
|
1495
|
-
...encoding && { encoding }
|
|
1711
|
+
...encoding && { encoding },
|
|
1712
|
+
...getStreamField({
|
|
1713
|
+
contentType: ct,
|
|
1714
|
+
mediaType,
|
|
1715
|
+
refs,
|
|
1716
|
+
document,
|
|
1717
|
+
node
|
|
1718
|
+
})
|
|
1496
1719
|
})];
|
|
1497
1720
|
});
|
|
1498
1721
|
const requestBody = content.length > 0 || body?.description ? {
|
|
@@ -1510,6 +1733,12 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1510
1733
|
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
1511
1734
|
const parseEntrySchema = (ct) => {
|
|
1512
1735
|
const raw = getBodySchema({
|
|
1736
|
+
content: response?.content,
|
|
1737
|
+
contentType: ct,
|
|
1738
|
+
refs,
|
|
1739
|
+
document
|
|
1740
|
+
});
|
|
1741
|
+
const mediaType = getMediaType({
|
|
1513
1742
|
content: response?.content,
|
|
1514
1743
|
contentType: ct,
|
|
1515
1744
|
refs
|
|
@@ -1518,10 +1747,17 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1518
1747
|
schema: raw,
|
|
1519
1748
|
name: responseName
|
|
1520
1749
|
}) : _kubb_kit.ast.factory.createSchema({ type: options.emptySchemaType });
|
|
1521
|
-
if (raw && ct) scanSchema(node, `${pointer}/responses/${statusCode}/content/${escapePointerToken(ct)}
|
|
1750
|
+
if (raw && ct) scanSchema(node, `${mediaTypePointer(response?.content, ct, `${pointer}/responses/${statusCode}/content/${escapePointerToken(ct)}`)}/${schemaKeyword(mediaType)}`);
|
|
1522
1751
|
return {
|
|
1523
1752
|
schema: node,
|
|
1524
|
-
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
1753
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly"),
|
|
1754
|
+
...ct ? getStreamField({
|
|
1755
|
+
contentType: ct,
|
|
1756
|
+
mediaType,
|
|
1757
|
+
refs,
|
|
1758
|
+
document,
|
|
1759
|
+
node
|
|
1760
|
+
}) : {}
|
|
1525
1761
|
};
|
|
1526
1762
|
};
|
|
1527
1763
|
const responseContent = (contentType ? [contentType] : Object.keys(response?.content ?? {})).map((ct) => _kubb_kit.ast.factory.createContent({
|
|
@@ -1552,7 +1788,7 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1552
1788
|
pointer,
|
|
1553
1789
|
operationId,
|
|
1554
1790
|
protocol: "http",
|
|
1555
|
-
method: operation.method.toUpperCase(),
|
|
1791
|
+
method: SUPPORTED_METHODS.has(operation.method) ? operation.method.toUpperCase() : operation.method,
|
|
1556
1792
|
path: operation.path,
|
|
1557
1793
|
tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
|
|
1558
1794
|
summary: pickDoc("summary") || void 0,
|
|
@@ -1647,6 +1883,14 @@ function createDiscriminantNode({ propertyName, values }) {
|
|
|
1647
1883
|
});
|
|
1648
1884
|
}
|
|
1649
1885
|
/**
|
|
1886
|
+
* Returns the `$ref` of an OpenAPI 3.2 `defaultMapping`, written as a schema name or a `$ref`; `undefined` when none.
|
|
1887
|
+
*/
|
|
1888
|
+
function getDefaultMappingRef(discriminator) {
|
|
1889
|
+
const value = discriminator?.defaultMapping;
|
|
1890
|
+
if (!value) return void 0;
|
|
1891
|
+
return value.includes("/") ? value : `${SCHEMA_REF_PREFIX}${value}`;
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1650
1894
|
* Returns every discriminator key whose mapping value matches `ref`, in mapping order.
|
|
1651
1895
|
*
|
|
1652
1896
|
* A mapping may point several keys at the same schema, in which case the member has to be
|
|
@@ -1742,13 +1986,15 @@ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode,
|
|
|
1742
1986
|
}
|
|
1743
1987
|
return constrains(variant) ? null : value;
|
|
1744
1988
|
}
|
|
1989
|
+
const defaultRef = getDefaultMappingRef(discriminator);
|
|
1745
1990
|
return unionMembers.map((s) => {
|
|
1746
1991
|
const ref = isReference(s) ? s.$ref : void 0;
|
|
1747
|
-
const
|
|
1992
|
+
const isDefault = ref !== void 0 && ref === defaultRef;
|
|
1993
|
+
const mappedValues = isDefault ? [] : findDiscriminators({
|
|
1748
1994
|
mapping: discriminator?.mapping,
|
|
1749
1995
|
ref
|
|
1750
1996
|
});
|
|
1751
|
-
const implicitValue = mappedValues.length ? null : implicitDiscriminantValue(s);
|
|
1997
|
+
const implicitValue = mappedValues.length || isDefault ? null : implicitDiscriminantValue(s);
|
|
1752
1998
|
const propertyType = propertyTypeOf({
|
|
1753
1999
|
schema: s,
|
|
1754
2000
|
propertyName: discriminator?.propertyName ?? "",
|
|
@@ -1793,7 +2039,8 @@ function extractDiscriminatedAllOfMembers({ allOfMembers, name, refs }) {
|
|
|
1793
2039
|
const parentUnion = deref?.oneOf ?? deref?.anyOf;
|
|
1794
2040
|
if (!deref || !discriminator || isDiscriminator(deref) && !parentUnion) return true;
|
|
1795
2041
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1796
|
-
const
|
|
2042
|
+
const isDefault = childRef === getDefaultMappingRef(discriminator);
|
|
2043
|
+
const values = isDefault ? [] : findDiscriminators({
|
|
1797
2044
|
mapping: discriminator.mapping,
|
|
1798
2045
|
ref: childRef
|
|
1799
2046
|
});
|
|
@@ -1816,7 +2063,7 @@ function extractDiscriminatedAllOfMembers({ allOfMembers, name, refs }) {
|
|
|
1816
2063
|
})
|
|
1817
2064
|
});
|
|
1818
2065
|
}
|
|
1819
|
-
return !(parentUnion && (values.length > 0 || parentUnion.some((member) => isReference(member) && member.$ref === childRef)));
|
|
2066
|
+
return !(parentUnion && (isDefault || values.length > 0 || parentUnion.some((member) => isReference(member) && member.$ref === childRef)));
|
|
1820
2067
|
}),
|
|
1821
2068
|
discriminantValues
|
|
1822
2069
|
};
|
|
@@ -2138,10 +2385,12 @@ function convertUnion(context) {
|
|
|
2138
2385
|
nullable,
|
|
2139
2386
|
defaultValue
|
|
2140
2387
|
};
|
|
2141
|
-
const unionMembers = resolveUnionMembers([...schema.oneOf ?? [], ...schema.anyOf ?? []], schema.properties ?? {});
|
|
2142
|
-
const strategy = schema.oneOf ? "one" : "any";
|
|
2143
|
-
const explicitDiscriminatorPropertyName = isDiscriminator(schema) ? schema.discriminator.propertyName : void 0;
|
|
2144
2388
|
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
2389
|
+
const declaredMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
2390
|
+
const defaultRef = getDefaultMappingRef(discriminator);
|
|
2391
|
+
const unionMembers = resolveUnionMembers(defaultRef && !declaredMembers.some((member) => isReference(member) && member.$ref === defaultRef) ? [...declaredMembers, { $ref: defaultRef }] : declaredMembers, schema.properties ?? {});
|
|
2392
|
+
const strategy = schema.oneOf ? "one" : "any";
|
|
2393
|
+
const explicitDiscriminatorPropertyName = discriminator?.propertyName;
|
|
2145
2394
|
const { oneOf: _o, anyOf: _a, discriminator: _d, ...memberBaseSchema } = schema;
|
|
2146
2395
|
const sharedPropertiesNode = schema.properties ? parse({
|
|
2147
2396
|
schema: memberBaseSchema,
|
|
@@ -2194,7 +2443,7 @@ function convertUnion(context) {
|
|
|
2194
2443
|
members,
|
|
2195
2444
|
discriminatorPropertyName: explicitDiscriminatorPropertyName ?? _kubb_kit.ast.inferDiscriminatorPropertyName(members)
|
|
2196
2445
|
});
|
|
2197
|
-
return _kubb_kit.ast.applyMacros(unionNode, [_kubb_kit.
|
|
2446
|
+
return _kubb_kit.ast.applyMacros(unionNode, [_kubb_kit.macroDedupeUnion], { depth: "shallow" });
|
|
2198
2447
|
}
|
|
2199
2448
|
/**
|
|
2200
2449
|
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
@@ -2671,6 +2920,10 @@ const schemaRules = [
|
|
|
2671
2920
|
primitive: "string"
|
|
2672
2921
|
})
|
|
2673
2922
|
},
|
|
2923
|
+
{
|
|
2924
|
+
match: ({ schema, type }) => !type && !schema.enum?.length && (schema.contentMediaType !== void 0 || schema.contentSchema !== void 0),
|
|
2925
|
+
convert: convertString
|
|
2926
|
+
},
|
|
2674
2927
|
{
|
|
2675
2928
|
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
2676
2929
|
convert: convertString
|
|
@@ -2964,7 +3217,7 @@ const locate = (source, location) => {
|
|
|
2964
3217
|
*/
|
|
2965
3218
|
const adapterOasName = "oas";
|
|
2966
3219
|
/**
|
|
2967
|
-
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.
|
|
3220
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, 3.1, and 3.2 specifications. Reads the
|
|
2968
3221
|
* spec from `input` (a file path, URL, inline content, or parsed object), validates
|
|
2969
3222
|
* it, resolves the base URL, and converts every schema and operation into the
|
|
2970
3223
|
* universal AST that every downstream plugin consumes.
|
|
@@ -3038,6 +3291,7 @@ const adapterOas = (0, _kubb_core.defineExtension)({
|
|
|
3038
3291
|
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
3039
3292
|
const operationNodes = getOperations(document, refs).map((operation) => parseOperation({
|
|
3040
3293
|
operation,
|
|
3294
|
+
document,
|
|
3041
3295
|
refs,
|
|
3042
3296
|
contentType,
|
|
3043
3297
|
options: parserOptions,
|