@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 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 OpenAPI 3.1 specifications and transforms them into a `@kubb/ast` `RootNode` for downstream code generation plugins.
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.15.4";
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
- return jsonMimeFragments.some((fragment) => mimeType.includes(fragment));
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 || [], `${pathPointer}/${operation.method.toLowerCase()}`);
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
- /** Schema for one media type of a `content` map, with the binary fallback for an emptied non-JSON entry. */
1389
- function getBodySchema({ content, contentType, refs }) {
1390
- const entry = contentType ? content?.[contentType] : void 0;
1391
- if (!entry) return null;
1392
- const binary = getBinaryFallbackSchema(contentType, entry.schema);
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 entry.schema ? refs.derefKeepingRef(entry.schema) : null;
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 on every path, in document order.
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
- function parseParameter({ param, pointer, parentName, options, parseSchema }) {
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 ?? (contentType ? param.content?.[contentType]?.schema : void 0);
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 = `#/paths/${escapePointerToken(operation.path)}/${operation.method.toLowerCase()}`;
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 node = parseSchema({
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)}/schema`);
1490
- const encoding = getEncoding(body?.content?.[ct]?.encoding);
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: _kubb_kit.ast.optionality(node, bodyRequired),
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)}/schema`);
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 mappedValues = findDiscriminators({
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 values = findDiscriminators({
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.macroSimplifyUnion], { depth: "shallow" });
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.1 specifications. Reads the
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,