@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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { defineExtension } from "@kubb/core";
2
2
  import { Diagnostics, ast, childName, enumPrimitive, enumPropName, extractRefName, macroDedupeUnion, macroDiscriminatorEnum, macroEnumName, mergeAdjacentObjectsLazy } from "@kubb/kit";
3
- import { join } from "node:path";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
4
5
  import { access, readFile } from "node:fs/promises";
5
6
  import { upgrade } from "@scalar/openapi-upgrader";
6
7
  import { bundle } from "api-ref-bundler";
@@ -19,7 +20,7 @@ var compatibility = {
19
20
  "node": ">=22.22"
20
21
  };
21
22
  // package.json
22
- var version = "5.17.0";
23
+ var version = "5.18.0";
23
24
  // src/constants.ts
24
25
  /**
25
26
  * Default parser options applied when no explicit options are provided.
@@ -401,6 +402,21 @@ function isOpenApi32(document) {
401
402
  const [major = 0, minor = 0] = String(document.openapi).split(".").map(Number);
402
403
  return major > 3 || major === 3 && minor >= 2;
403
404
  }
405
+ function pickStrings(source, keys) {
406
+ if (!source || typeof source !== "object") return void 0;
407
+ const entries = keys.flatMap((key) => {
408
+ const value = source[key];
409
+ return typeof value === "string" ? [[key, value]] : [];
410
+ });
411
+ return entries.length ? Object.fromEntries(entries) : void 0;
412
+ }
413
+ function getExternalDocs(source) {
414
+ const docs = pickStrings(source, ["url", "description"]);
415
+ return docs?.url ? {
416
+ url: docs.url,
417
+ ...docs.description !== void 0 && { description: docs.description }
418
+ } : void 0;
419
+ }
404
420
  /**
405
421
  * Returns the `x-*` keys of `source` as-is, or `undefined` when there are none.
406
422
  */
@@ -415,12 +431,12 @@ function isReference(obj) {
415
431
  return !!obj && typeof obj === "object" && "$ref" in obj;
416
432
  }
417
433
  /**
418
- * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object,
419
- * excluding the Swagger 2 string form.
434
+ * Returns `true` when `obj` is a schema with a structured OAS 3.x `discriminator` object that names a
435
+ * `propertyName`, excluding the Swagger 2 string form and a 3.2 discriminator that only sets `defaultMapping`.
420
436
  */
421
437
  function isDiscriminator(obj) {
422
- const record = obj;
423
- return !!obj && !!record["discriminator"] && typeof record["discriminator"] !== "string";
438
+ const discriminator = obj?.["discriminator"];
439
+ return !!discriminator && typeof discriminator === "object" && typeof discriminator.propertyName === "string";
424
440
  }
425
441
  /**
426
442
  * Returns `true` when a schema is a raw binary payload.
@@ -429,17 +445,32 @@ function isBinary(schema) {
429
445
  return schema.contentMediaType === "application/octet-stream" && schema.contentEncoding === void 0 && (schema.type === void 0 || schema.type === "string");
430
446
  }
431
447
  /**
448
+ * Keywords that only annotate a schema. A schema with nothing else does not constrain the value.
449
+ */
450
+ const annotationKeywords = /* @__PURE__ */ new Set([
451
+ "title",
452
+ "description",
453
+ "default",
454
+ "example",
455
+ "examples",
456
+ "deprecated",
457
+ "readOnly",
458
+ "writeOnly",
459
+ "externalDocs"
460
+ ]);
461
+ /**
432
462
  * Returns the binary schema for a non-JSON entry the OAS 3.1 upgrade emptied out, or `undefined`
433
463
  * when the entry carries a schema of its own. The upgrade rewrites `format: 'binary'` into
434
464
  * `contentMediaType` and drops it whenever it only repeats the media type key, for any binary
435
465
  * media type (`application/octet-stream`, `application/pdf`, `image/png`, ...), not just
436
466
  * `application/octet-stream`. Without the fallback such a body resolves to the empty schema type
437
- * instead of a blob.
467
+ * instead of a blob. Annotations such as `description` survive the upgrade and are kept.
438
468
  */
439
469
  function getBinaryFallbackSchema(mediaType, schema) {
440
470
  if (!mediaType || isJsonMimeType(mediaType)) return void 0;
441
- if (schema && Object.keys(schema).length > 0) return void 0;
471
+ if (schema && Object.keys(schema).some((key) => !annotationKeywords.has(key) && !key.startsWith("x-"))) return void 0;
442
472
  return {
473
+ ...schema,
443
474
  type: "string",
444
475
  contentMediaType: "application/octet-stream"
445
476
  };
@@ -578,6 +609,65 @@ function createRefs(document) {
578
609
  derefKeepingRef
579
610
  };
580
611
  }
612
+ // src/load/schemaIds.ts
613
+ const DATA_KEYS = /* @__PURE__ */ new Set([
614
+ "example",
615
+ "examples",
616
+ "const",
617
+ "enum",
618
+ "default"
619
+ ]);
620
+ const NAMED_ENTRY_KEYS = /* @__PURE__ */ new Set([
621
+ "properties",
622
+ "patternProperties",
623
+ "$defs",
624
+ "definitions",
625
+ "schemas",
626
+ "dependentSchemas"
627
+ ]);
628
+ function walk(node, pointer, visit, parentKey) {
629
+ if (!node || typeof node !== "object") return;
630
+ if (!Array.isArray(node)) visit(node, pointer);
631
+ const named = parentKey !== void 0 && NAMED_ENTRY_KEYS.has(parentKey);
632
+ for (const [key, value] of Object.entries(node)) {
633
+ if (!named && (DATA_KEYS.has(key) || key.startsWith("x-"))) continue;
634
+ walk(value, `${pointer}/${escapePointerToken(key)}`, visit, Array.isArray(node) ? parentKey : key);
635
+ }
636
+ }
637
+ /**
638
+ * The declaration in the nearest enclosing schema, else the only one, else `undefined`.
639
+ */
640
+ function pick(declarations, pointer) {
641
+ if (!declarations) return void 0;
642
+ const enclosing = declarations.filter((target) => pointer === target || pointer.startsWith(`${target}/`));
643
+ if (enclosing.length) return enclosing.reduce((a, b) => b.length > a.length ? b : a);
644
+ return declarations.length === 1 ? declarations[0] : void 0;
645
+ }
646
+ /**
647
+ * Rewrites, in place, each `$ref` to an `$id`, `$anchor` or `$dynamicAnchor`, and each `$dynamicRef`, into a
648
+ * `#/...` pointer, so it is not fetched. Instance data and `x-` extensions are left as written.
649
+ *
650
+ * shortcut: `$id` matches the `$ref` string exactly and `$dynamicRef` resolves statically to the nearest
651
+ * enclosing anchor, so a relative `$id` base or a recursively extended schema is not followed, upgrade when a spec needs that.
652
+ */
653
+ function resolveSchemaIds(document) {
654
+ const targets = /* @__PURE__ */ new Map();
655
+ const declare = (key, pointer) => targets.set(key, [...targets.get(key) ?? [], pointer]);
656
+ walk(document, "#", (node, pointer) => {
657
+ if (typeof node["$id"] === "string") declare(node["$id"], pointer);
658
+ for (const key of ["$anchor", "$dynamicAnchor"]) if (typeof node[key] === "string") declare(`#${node[key]}`, pointer);
659
+ });
660
+ if (!targets.size) return;
661
+ walk(document, "#", (node, pointer) => {
662
+ const ref = typeof node["$ref"] === "string" ? pick(targets.get(node["$ref"]), pointer) : void 0;
663
+ if (ref) node["$ref"] = ref;
664
+ const dynamicRef = typeof node["$dynamicRef"] === "string" ? pick(targets.get(node["$dynamicRef"]), pointer) : void 0;
665
+ if (dynamicRef && node["$ref"] === void 0) {
666
+ node["$ref"] = dynamicRef;
667
+ delete node["$dynamicRef"];
668
+ }
669
+ });
670
+ }
581
671
  // src/load/source.ts
582
672
  const urlRegExp = /^https?:\/+/i;
583
673
  /**
@@ -747,6 +837,21 @@ function isExtensionPath(path) {
747
837
  return path.some((token, index) => String(token).startsWith("x-") && !namedEntryKeys.has(String(path[index - 1])));
748
838
  }
749
839
  /**
840
+ * Returns the base for relative `$ref`s: the 3.2 `$self` resolved against `pathOrUrl`, else `pathOrUrl`.
841
+ */
842
+ function getBaseUri(pathOrUrl, root) {
843
+ const self = root && typeof root === "object" ? root.$self : void 0;
844
+ if (typeof self !== "string" || !self) return pathOrUrl;
845
+ try {
846
+ if (urlRegExp.test(self)) return new URL(self).href;
847
+ if (/^file:/i.test(self)) return fileURLToPath(self);
848
+ if (/^[a-z][a-z\d+.-]*:/i.test(self)) return pathOrUrl;
849
+ return urlRegExp.test(pathOrUrl) ? new URL(self, pathOrUrl).href : resolve(dirname(pathOrUrl), self);
850
+ } catch {
851
+ return pathOrUrl;
852
+ }
853
+ }
854
+ /**
750
855
  * Bundles a multi-file OpenAPI document into a single document via `api-ref-bundler`.
751
856
  *
752
857
  * External file schemas are hoisted into named `components.schemas` entries, so a property
@@ -759,8 +864,9 @@ function isExtensionPath(path) {
759
864
  * all-internal document it is a no-op that still walks the whole tree to confirm that, which
760
865
  * costs real time on a large spec.
761
866
  *
762
- * `document` stands in for the file at `pathOrUrl`, such as inline input. Every other file or URL
763
- * read, including one that fails, lands in `sources`. A `$ref` that cannot be read reports
867
+ * `document` stands in for the file at `pathOrUrl`, such as inline input. Relative `$ref`s resolve
868
+ * against the OpenAPI 3.2 `$self` when the document sets one. Every other file or URL read,
869
+ * including one that fails, lands in `sources`. A `$ref` that cannot be read reports
764
870
  * `KUBB_REF_NOT_FOUND` at the key that holds it, as a warning when it sits inside an `x-` extension.
765
871
  *
766
872
  * @example Local file
@@ -772,8 +878,10 @@ function isExtensionPath(path) {
772
878
  async function bundleDocument(pathOrUrl, { document, sources = [] } = {}) {
773
879
  const cache = /* @__PURE__ */ new Map();
774
880
  const toKey = (sourcePath) => urlRegExp.test(sourcePath) ? new URL(sourcePath).href : sourcePath;
881
+ let entry = pathOrUrl;
882
+ let entryDocument = document;
775
883
  const resolver = (sourcePath) => {
776
- if (document && sourcePath === pathOrUrl) return Promise.resolve(document);
884
+ if (entryDocument !== void 0 && toKey(sourcePath) === toKey(entry)) return Promise.resolve(entryDocument);
777
885
  const key = toKey(sourcePath);
778
886
  const cached = cache.get(key);
779
887
  if (cached) return cached;
@@ -783,9 +891,12 @@ async function bundleDocument(pathOrUrl, { document, sources = [] } = {}) {
783
891
  return result;
784
892
  };
785
893
  const root = await resolver(pathOrUrl);
894
+ resolveSchemaIds(root);
786
895
  if (typeof root === "object" && root !== null && !hasExternalRef(root)) return root;
787
- return await bundle(pathOrUrl, resolver, { hooks: { onError: (message, { path, state }) => {
788
- const pointer = toKey(state.baseFile) === toKey(pathOrUrl) ? `#/${path.map((token) => escapePointerToken(String(token))).join("/")}` : void 0;
896
+ entry = getBaseUri(pathOrUrl, root);
897
+ entryDocument = root;
898
+ return await bundle(entry, resolver, { hooks: { onError: (message, { path, state }) => {
899
+ const pointer = toKey(state.baseFile) === toKey(entry) ? `#/${path.map((token) => escapePointerToken(String(token))).join("/")}` : void 0;
789
900
  const diagnostic = {
790
901
  code: Diagnostics.code.refNotFound,
791
902
  severity: isExtensionPath(path) ? "warning" : "error",
@@ -824,10 +935,12 @@ async function parseFromConfig(source, sources = []) {
824
935
  origin: "the inline `input` string",
825
936
  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."
826
937
  }) : structuredClone(source.data);
827
- return source.root && hasExternalRef(data) ? bundleDocument(join(source.root, inlineInputName), {
938
+ if (source.root && hasExternalRef(data)) return bundleDocument(join(source.root, inlineInputName), {
828
939
  document: data,
829
940
  sources
830
- }) : data;
941
+ });
942
+ resolveSchemaIds(data);
943
+ return data;
831
944
  }
832
945
  await assertInputExists(source.path);
833
946
  return bundleDocument(source.path, { sources });
@@ -1152,6 +1265,80 @@ function getSchemas(document, { contentType }, refs) {
1152
1265
  pointers
1153
1266
  };
1154
1267
  }
1268
+ // src/model/security.ts
1269
+ const SCHEME_POINTER = "#/components/securitySchemes/";
1270
+ const API_KEY_LOCATIONS = /* @__PURE__ */ new Set([
1271
+ "header",
1272
+ "query",
1273
+ "cookie"
1274
+ ]);
1275
+ function getCredential(scheme) {
1276
+ if (scheme.type === "apiKey") return scheme.name && scheme.in && API_KEY_LOCATIONS.has(scheme.in) ? {
1277
+ type: "apiKey",
1278
+ in: scheme.in,
1279
+ name: scheme.name
1280
+ } : void 0;
1281
+ if (scheme.type === "http") return scheme.scheme ? {
1282
+ type: "http",
1283
+ scheme: scheme.scheme.toLowerCase(),
1284
+ ...scheme.bearerFormat && { bearerFormat: scheme.bearerFormat }
1285
+ } : void 0;
1286
+ if (scheme.type === "openIdConnect") return {
1287
+ type: "openIdConnect",
1288
+ ...scheme.openIdConnectUrl && { url: scheme.openIdConnectUrl }
1289
+ };
1290
+ if (scheme.type === "oauth2") {
1291
+ const flows = Object.entries(scheme.flows ?? {}).map(([name, flow]) => {
1292
+ const scopes = flow && typeof flow === "object" && flow.scopes && typeof flow.scopes === "object" ? { scopes: flow.scopes } : {};
1293
+ return [name, {
1294
+ ...pickStrings(flow, [
1295
+ "authorizationUrl",
1296
+ "deviceAuthorizationUrl",
1297
+ "tokenUrl",
1298
+ "refreshUrl"
1299
+ ]),
1300
+ ...scopes
1301
+ }];
1302
+ });
1303
+ return {
1304
+ type: "oauth2",
1305
+ flows: Object.fromEntries(flows),
1306
+ ...scheme.oauth2MetadataUrl && { metadataUrl: scheme.oauth2MetadataUrl }
1307
+ };
1308
+ }
1309
+ return scheme.type === "mutualTLS" ? { type: "mutualTLS" } : void 0;
1310
+ }
1311
+ /**
1312
+ * Maps `components.securitySchemes` to `AuthScheme` entries, leaving out a scheme a client cannot place.
1313
+ */
1314
+ function toAuthSchemes(schemes, refs) {
1315
+ return Object.entries(schemes ?? {}).flatMap(([name, value]) => {
1316
+ const scheme = refs.deref(value);
1317
+ const credential = scheme && getCredential(scheme);
1318
+ if (!scheme || !credential) return [];
1319
+ return [{
1320
+ name,
1321
+ ...scheme.description && { description: scheme.description },
1322
+ ...scheme.deprecated && { deprecated: true },
1323
+ credential
1324
+ }];
1325
+ });
1326
+ }
1327
+ /**
1328
+ * Maps security requirements to `AuthRequirement` entries. A 3.2 URI key becomes the scheme name.
1329
+ */
1330
+ function toAuthRequirements(requirements, document) {
1331
+ if (!Array.isArray(requirements)) return void 0;
1332
+ const schemes = document.components?.securitySchemes ?? {};
1333
+ const toName = (key) => {
1334
+ const index = key.indexOf(SCHEME_POINTER);
1335
+ return Object.hasOwn(schemes, key) || index === -1 ? key : unescapePointerToken(key.slice(index + 29));
1336
+ };
1337
+ return requirements.filter((requirement) => !!requirement && typeof requirement === "object").map((requirement) => Object.entries(requirement).map(([key, scopes]) => ({
1338
+ scheme: toName(key),
1339
+ ...Array.isArray(scopes) && scopes.length && { scopes: scopes.map(String) }
1340
+ })));
1341
+ }
1155
1342
  // src/model/server.ts
1156
1343
  /**
1157
1344
  * Reads the server URL from the document's `servers` array at `server.index`,
@@ -1208,8 +1395,11 @@ function resolveServerUrl(server, overrides) {
1208
1395
  * Maps OpenAPI servers to `InputServer` entries: drops entries without a `url` and fills `{variable}` defaults.
1209
1396
  */
1210
1397
  function toInputServers(servers) {
1211
- return servers.filter((entry) => entry.url).map(({ url, description, variables }) => {
1212
- const declared = variables && Object.keys(variables).length > 0 ? { variables } : {};
1398
+ return servers.filter((entry) => entry.url).map(({ url, name, description, variables }) => {
1399
+ const declared = {
1400
+ ...name !== void 0 && { name },
1401
+ ...variables && Object.keys(variables).length > 0 && { variables }
1402
+ };
1213
1403
  try {
1214
1404
  return {
1215
1405
  url: resolveServerUrl({
@@ -1228,6 +1418,20 @@ function toInputServers(servers) {
1228
1418
  }
1229
1419
  });
1230
1420
  }
1421
+ // src/model/tag.ts
1422
+ function toInputTags(tags) {
1423
+ return tags.filter((tag) => typeof tag.name === "string").map(({ name, summary, description, parent, kind, externalDocs }) => {
1424
+ const docs = getExternalDocs(externalDocs);
1425
+ return {
1426
+ name,
1427
+ ...typeof summary === "string" && { summary },
1428
+ ...typeof description === "string" && { description },
1429
+ ...typeof parent === "string" && { parent },
1430
+ ...typeof kind === "string" && { kind },
1431
+ ...docs && { externalDocs: docs }
1432
+ };
1433
+ });
1434
+ }
1231
1435
  // src/model/mediaType.ts
1232
1436
  /**
1233
1437
  * The media type without its parameters, so `text/event-stream; charset=utf-8` matches the bare name.
@@ -1409,6 +1613,39 @@ function flattenSchema(schema) {
1409
1613
  function scanSchema(node, pointer = node.pointer) {
1410
1614
  if (pointer) visit(node, pointer);
1411
1615
  }
1616
+ let formatTally;
1617
+ /**
1618
+ * Runs `fn` and reports `KUBB_UNSUPPORTED_FORMAT` once per format with the number of schemas that use it,
1619
+ * instead of once per schema. `format` is an open value in OpenAPI, so a large spec would otherwise print
1620
+ * thousands of warnings that say the same thing. `fn` must be synchronous.
1621
+ */
1622
+ function groupUnsupportedFormats(fn) {
1623
+ formatTally = /* @__PURE__ */ new Map();
1624
+ try {
1625
+ return fn();
1626
+ } finally {
1627
+ const tally = formatTally;
1628
+ formatTally = void 0;
1629
+ for (const [format, pointers] of tally) reportUnsupportedFormat(format, [...pointers]);
1630
+ }
1631
+ }
1632
+ function tallyFormat(format, pointer) {
1633
+ if (!formatTally) return reportUnsupportedFormat(format, [pointer]);
1634
+ formatTally.set(format, (formatTally.get(format) ?? /* @__PURE__ */ new Set()).add(pointer));
1635
+ }
1636
+ function reportUnsupportedFormat(format, pointers) {
1637
+ const count = pointers.length;
1638
+ Diagnostics.report({
1639
+ code: Diagnostics.code.unsupportedFormat,
1640
+ severity: "warning",
1641
+ 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.`,
1642
+ help: `Use a format Kubb supports, or handle "${format}" with a custom parser or plugin.`,
1643
+ location: {
1644
+ kind: "schema",
1645
+ pointer: pointers[0]
1646
+ }
1647
+ });
1648
+ }
1412
1649
  function visit(node, pointer) {
1413
1650
  node.pointer ??= pointer;
1414
1651
  if (node.deprecated) Diagnostics.report({
@@ -1420,16 +1657,7 @@ function visit(node, pointer) {
1420
1657
  pointer
1421
1658
  }
1422
1659
  });
1423
- if (typeof node.format === "string" && !isHandledFormat(node.format)) Diagnostics.report({
1424
- code: Diagnostics.code.unsupportedFormat,
1425
- severity: "warning",
1426
- message: `Kubb does not map the format "${node.format}" to a specific type, so it falls back to the base type.`,
1427
- help: `Use a format Kubb supports, or handle "${node.format}" with a custom parser or plugin.`,
1428
- location: {
1429
- kind: "schema",
1430
- pointer
1431
- }
1432
- });
1660
+ if (typeof node.format === "string" && !isHandledFormat(node.format)) tallyFormat(node.format, pointer);
1433
1661
  if (node.type === "object") {
1434
1662
  for (const property of node.properties) visit(property.schema, `${pointer}/properties/${escapePointerToken(property.name)}`);
1435
1663
  if (node.additionalProperties && typeof node.additionalProperties === "object") visit(node.additionalProperties, `${pointer}/additionalProperties`);
@@ -1538,18 +1766,6 @@ function getResponseByStatusCode({ operation, refs, statusCode }) {
1538
1766
  function getRequestBody({ operation, refs }) {
1539
1767
  return refs.deref(operation.schema.requestBody);
1540
1768
  }
1541
- /**
1542
- * Returns the primary request content type. Prefers a JSON-like media type (the last one wins
1543
- * when several are declared), then the first declared one, defaulting to `'application/json'`.
1544
- */
1545
- function getRequestContentType({ operation, refs }) {
1546
- const content = getRequestBody({
1547
- operation,
1548
- refs
1549
- })?.content;
1550
- const mediaTypes = content ? Object.keys(content) : [];
1551
- return mediaTypes.findLast(isJsonMimeType) ?? mediaTypes[0] ?? "application/json";
1552
- }
1553
1769
  /** Merges path-level and operation-level parameters; operation-level wins per `in:name`. */
1554
1770
  function getParameters({ operation, refs }) {
1555
1771
  const resolveParams = (params, pointer) => params.flatMap((entry, index) => {
@@ -1573,7 +1789,8 @@ const PARAMETER_STYLES = /* @__PURE__ */ new Set([
1573
1789
  "simple",
1574
1790
  "spaceDelimited",
1575
1791
  "pipeDelimited",
1576
- "deepObject"
1792
+ "deepObject",
1793
+ "cookie"
1577
1794
  ]);
1578
1795
  function pickStyleFields({ style, explode, allowReserved }) {
1579
1796
  return {
@@ -1582,16 +1799,45 @@ function pickStyleFields({ style, explode, allowReserved }) {
1582
1799
  ...allowReserved !== void 0 ? { allowReserved } : {}
1583
1800
  };
1584
1801
  }
1585
- /** The `encoding` of a media type, reduced to the entries that set a field Kubb carries. A `style` outside the OpenAPI set is dropped. */
1586
- function getEncoding(encoding) {
1587
- const entries = Object.entries(encoding ?? {}).flatMap(([property, { contentType, ...rest }]) => {
1588
- const node = {
1589
- ...contentType !== void 0 ? { contentType } : {},
1590
- ...pickStyleFields(rest)
1591
- };
1802
+ function getEncodingFields({ encoding, prefixEncoding, itemEncoding }) {
1803
+ const entries = Object.entries(encoding ?? {}).flatMap(([property, entry]) => {
1804
+ const node = toEncodingNode(entry);
1592
1805
  return Object.keys(node).length ? [[property, node]] : [];
1593
1806
  });
1594
- return entries.length ? Object.fromEntries(entries) : void 0;
1807
+ const item = itemEncoding && toEncodingNode(itemEncoding);
1808
+ return {
1809
+ ...entries.length && { encoding: Object.fromEntries(entries) },
1810
+ ...prefixEncoding?.length && { prefixEncoding: prefixEncoding.map(toEncodingNode) },
1811
+ ...item && Object.keys(item).length && { itemEncoding: item }
1812
+ };
1813
+ }
1814
+ function toEncodingNode({ contentType, encoding, prefixEncoding, itemEncoding, ...rest }) {
1815
+ return {
1816
+ ...contentType !== void 0 && { contentType },
1817
+ ...pickStyleFields(rest),
1818
+ ...getEncodingFields({
1819
+ encoding,
1820
+ prefixEncoding,
1821
+ itemEncoding
1822
+ })
1823
+ };
1824
+ }
1825
+ function getExamples({ example, examples }, refs) {
1826
+ const named = Object.entries(examples ?? {}).flatMap(([name, entry]) => {
1827
+ const resolved = refs.deref(entry);
1828
+ if (!resolved) return [];
1829
+ const { summary, description, dataValue, value, serializedValue } = resolved;
1830
+ const data = dataValue !== void 0 ? dataValue : value;
1831
+ return [{
1832
+ name,
1833
+ ...typeof summary === "string" && { summary },
1834
+ ...typeof description === "string" && { description },
1835
+ ...data !== void 0 && { value: data },
1836
+ ...typeof serializedValue === "string" && { serializedValue }
1837
+ }];
1838
+ });
1839
+ if (named.length) return { examples: named };
1840
+ return example !== void 0 ? { examples: [{ value: example }] } : {};
1595
1841
  }
1596
1842
  /**
1597
1843
  * Returns the media type entry for `contentType`, resolving a `$ref` into `components.mediaTypes`. `null` when absent.
@@ -1620,6 +1866,24 @@ function getBodySchema({ content, contentType, refs, document }) {
1620
1866
  if (binary) return binary;
1621
1867
  return schema ? refs.derefKeepingRef(schema) : null;
1622
1868
  }
1869
+ function getLinks(links, refs) {
1870
+ const entries = Object.entries(links ?? {}).flatMap(([name, value]) => {
1871
+ const link = refs.deref(value);
1872
+ if (!link) return [];
1873
+ const server = link.server ? toInputServers([link.server])[0] : void 0;
1874
+ return [[name, {
1875
+ ...pickStrings(link, [
1876
+ "operationId",
1877
+ "operationRef",
1878
+ "description"
1879
+ ]),
1880
+ ...link.parameters && { parameters: link.parameters },
1881
+ ..."requestBody" in link && { requestBody: link.requestBody },
1882
+ ...server && { server }
1883
+ }]];
1884
+ });
1885
+ return entries.length ? Object.fromEntries(entries) : void 0;
1886
+ }
1623
1887
  /** The `stream` field of a content node, set when the media type is a stream. `node` is the parsed item schema. */
1624
1888
  function getStreamField({ contentType, mediaType, refs, document, node }) {
1625
1889
  if (!isStream(contentType, mediaType)) return {};
@@ -1690,11 +1954,12 @@ function collectPropertyKeysByFlag(schema, flag) {
1690
1954
  function parseParameter({ param, pointer, parentName, options, parseSchema, refs }) {
1691
1955
  const schemaName = parentName && param.name ? pascalCase(`${parentName} ${param.name}`) : void 0;
1692
1956
  const contentType = param.schema ? void 0 : Object.keys(param.content ?? {})[0];
1693
- const rawSchema = param.schema ?? getMediaType({
1957
+ const mediaType = getMediaType({
1694
1958
  content: param.content,
1695
1959
  contentType,
1696
1960
  refs
1697
- })?.schema;
1961
+ });
1962
+ const rawSchema = param.schema ?? mediaType?.schema;
1698
1963
  const schema = rawSchema ? parseSchema({
1699
1964
  schema: rawSchema,
1700
1965
  name: schemaName
@@ -1710,7 +1975,10 @@ function parseParameter({ param, pointer, parentName, options, parseSchema, refs
1710
1975
  },
1711
1976
  required: param.required ?? false,
1712
1977
  ...pickStyleFields(param),
1713
- ...contentType !== void 0 ? { contentType } : {}
1978
+ ...contentType !== void 0 ? { contentType } : {},
1979
+ ...getExamples(mediaType ?? param, refs),
1980
+ ...param.deprecated && { deprecated: true },
1981
+ ...param.allowEmptyValue && { allowEmptyValue: true }
1714
1982
  });
1715
1983
  }
1716
1984
  /**
@@ -1755,12 +2023,12 @@ function parseOperation({ operation, ...ctx }) {
1755
2023
  name: requestBodyName
1756
2024
  }), bodyRequired);
1757
2025
  scanSchema(node, `${mediaTypePointer(body?.content, ct, `${pointer}/requestBody/content/${escapePointerToken(ct)}`)}/${schemaKeyword(mediaType)}`);
1758
- const encoding = getEncoding(mediaType?.encoding);
1759
2026
  return [ast.factory.createContent({
1760
2027
  contentType: ct,
1761
2028
  schema: node,
1762
2029
  keysToOmit: collectPropertyKeysByFlag(schema, "readOnly"),
1763
- ...encoding && { encoding },
2030
+ ...mediaType && getEncodingFields(mediaType),
2031
+ ...mediaType && getExamples(mediaType, refs),
1764
2032
  ...getStreamField({
1765
2033
  contentType: ct,
1766
2034
  mediaType,
@@ -1803,6 +2071,8 @@ function parseOperation({ operation, ...ctx }) {
1803
2071
  return {
1804
2072
  schema: node,
1805
2073
  keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly"),
2074
+ ...mediaType && getEncodingFields(mediaType),
2075
+ ...mediaType && getExamples(mediaType, refs),
1806
2076
  ...ct ? getStreamField({
1807
2077
  contentType: ct,
1808
2078
  mediaType,
@@ -1812,22 +2082,41 @@ function parseOperation({ operation, ...ctx }) {
1812
2082
  }) : {}
1813
2083
  };
1814
2084
  };
1815
- const responseContent = (contentType ? [contentType] : Object.keys(response?.content ?? {})).map((ct) => ast.factory.createContent({
2085
+ const declaredContentTypes = Object.keys(response?.content ?? {});
2086
+ const responseContent = (contentType && declaredContentTypes.length ? [contentType] : declaredContentTypes).map((ct) => ast.factory.createContent({
1816
2087
  contentType: ct,
1817
2088
  ...parseEntrySchema(ct)
1818
2089
  }));
1819
2090
  if (responseContent.length === 0) responseContent.push(ast.factory.createContent({
1820
- contentType: getRequestContentType({
1821
- operation,
1822
- refs
1823
- }),
2091
+ contentType: "",
1824
2092
  ...parseEntrySchema(contentType)
1825
2093
  }));
2094
+ const responsePointer = `${pointer}/responses/${statusCode}`;
2095
+ const headers = Object.entries(response?.headers ?? {}).flatMap(([name, value]) => {
2096
+ const header = name.toLowerCase() === "content-type" ? null : refs.deref(value);
2097
+ if (!header) return [];
2098
+ return [parseParameter({
2099
+ param: {
2100
+ ...header,
2101
+ name,
2102
+ in: "header"
2103
+ },
2104
+ pointer: `${responsePointer}/headers/${escapePointerToken(name)}`,
2105
+ parentName: responseName,
2106
+ options,
2107
+ parseSchema,
2108
+ refs
2109
+ })];
2110
+ });
2111
+ const links = getLinks(response?.links, refs);
1826
2112
  return ast.factory.createResponse({
1827
- pointer: `${pointer}/responses/${statusCode}`,
2113
+ pointer: responsePointer,
1828
2114
  statusCode,
2115
+ summary: response?.summary,
1829
2116
  description: response?.description,
1830
- content: responseContent
2117
+ content: responseContent,
2118
+ ...headers.length && { headers },
2119
+ ...links && { links }
1831
2120
  });
1832
2121
  });
1833
2122
  const pickDoc = (key) => {
@@ -1837,6 +2126,7 @@ function parseOperation({ operation, ...ctx }) {
1837
2126
  return typeof fallback === "string" ? fallback : void 0;
1838
2127
  };
1839
2128
  const servers = operation.schema.servers ?? operation.pathItem.servers;
2129
+ const auth = toAuthRequirements(operation.schema.security ?? document.security, document);
1840
2130
  return ast.factory.createOperation({
1841
2131
  pointer,
1842
2132
  operationId,
@@ -1847,11 +2137,13 @@ function parseOperation({ operation, ...ctx }) {
1847
2137
  summary: pickDoc("summary") || void 0,
1848
2138
  description: pickDoc("description") || void 0,
1849
2139
  deprecated: operation.schema.deprecated || void 0,
2140
+ externalDocs: getExternalDocs(operation.schema.externalDocs),
1850
2141
  parameters,
1851
2142
  requestBody,
1852
2143
  responses,
1853
2144
  extensions: pickExtensions(operation.schema),
1854
- ...servers && { servers: toInputServers(servers) }
2145
+ ...servers && { servers: toInputServers(servers) },
2146
+ ...auth && { auth }
1855
2147
  });
1856
2148
  }
1857
2149
  // src/emit/createNode.ts
@@ -1872,6 +2164,7 @@ function createNode({ schema, name, nullable, defaultValue }, extras) {
1872
2164
  default: defaultValue,
1873
2165
  examples: extractExamples(schema),
1874
2166
  format: schema.format,
2167
+ externalDocs: getExternalDocs(schema.externalDocs),
1875
2168
  ...extras
1876
2169
  });
1877
2170
  }
@@ -2000,6 +2293,15 @@ function removeInheritedDiscriminator({ members, refs }) {
2000
2293
  });
2001
2294
  }
2002
2295
  /**
2296
+ * Whether a member can carry the discriminator property. A discriminator is only a hint, so an array
2297
+ * or primitive member keeps its own type instead of an intersection no value can satisfy.
2298
+ */
2299
+ function isObjectLike(member, refs) {
2300
+ const type = (isReference(member) ? refs.resolve(member.$ref, { report: false }) : member)?.type;
2301
+ if (type === void 0) return true;
2302
+ return Array.isArray(type) ? type.includes("object") : type === "object";
2303
+ }
2304
+ /**
2003
2305
  * Narrows each `oneOf`/`anyOf` member with its discriminant value, intersecting the member's own
2004
2306
  * node with either the shared-properties slice carrying that value, or a synthetic discriminant
2005
2307
  * literal. The referenced child schema's own definition is left untouched — narrowing happens only
@@ -2062,7 +2364,7 @@ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode,
2062
2364
  schema: s,
2063
2365
  name
2064
2366
  });
2065
- if (!discriminatorValues.length || !discriminator) return memberNode;
2367
+ if (!discriminatorValues.length || !discriminator || !isObjectLike(s, refs)) return memberNode;
2066
2368
  const narrowedDiscriminatorNode = sharedPropertiesNode ? pickDiscriminatorPropertyNode(ast.applyMacros(sharedPropertiesNode, [macroDiscriminatorEnum({
2067
2369
  propertyName: discriminator.propertyName,
2068
2370
  values: discriminatorValues
@@ -2327,13 +2629,15 @@ const annotationKeys = /* @__PURE__ */ new Set([
2327
2629
  ]);
2328
2630
  /**
2329
2631
  * Moves `allOf` members that hold only annotations or `if`/`then`/`else` onto the parent, so they do
2330
- * not become `unknown`. The parent's own values win, and the conditional keywords are not enforced.
2632
+ * not become `unknown`. The parent's own values win. The parser reads their `if`/`then`/`else` into `conditionals`.
2331
2633
  */
2634
+ function isAnnotationMember(member) {
2635
+ return !!member && typeof member === "object" && !isReference(member) && Object.keys(member).every((key) => annotationKeys.has(key));
2636
+ }
2332
2637
  function mergeAnnotations(schema) {
2333
2638
  const members = schema.allOf;
2334
- const isAnnotation = (member) => !isReference(member) && Object.keys(member).every((key) => annotationKeys.has(key));
2335
- const annotations = members.filter(isAnnotation);
2336
- const rest = members.filter((member) => !isAnnotation(member));
2639
+ const annotations = members.filter(isAnnotationMember);
2640
+ const rest = members.filter((member) => !isAnnotationMember(member));
2337
2641
  return annotations.length && rest.length ? {
2338
2642
  ...Object.assign({}, ...annotations),
2339
2643
  ...schema,
@@ -2366,7 +2670,8 @@ function convertAllOf({ schema: original, name, nullable, defaultValue, parse, r
2366
2670
  default: mergedDefault,
2367
2671
  examples: extractExamples(schema) ?? memberNode.examples,
2368
2672
  pattern: schema.pattern ?? ("pattern" in memberNode ? memberNode.pattern : void 0),
2369
- format: schema.format ?? memberNode.format
2673
+ format: schema.format ?? memberNode.format,
2674
+ externalDocs: getExternalDocs(schema.externalDocs) ?? memberNode.externalDocs
2370
2675
  });
2371
2676
  }
2372
2677
  const { members: discriminatedAllOf, discriminantValues } = extractDiscriminatedAllOfMembers({
@@ -2441,7 +2746,7 @@ function convertUnion(context) {
2441
2746
  };
2442
2747
  const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
2443
2748
  const declaredMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
2444
- const defaultRef = getDefaultMappingRef(discriminator);
2749
+ const defaultRef = getDefaultMappingRef(typeof schema.discriminator === "object" ? schema.discriminator : void 0);
2445
2750
  const unionMembers = resolveUnionMembers(defaultRef && !declaredMembers.some((member) => isReference(member) && member.$ref === defaultRef) ? [...declaredMembers, { $ref: defaultRef }] : declaredMembers, schema.properties ?? {});
2446
2751
  const strategy = schema.oneOf ? "one" : "any";
2447
2752
  const explicitDiscriminatorPropertyName = discriminator?.propertyName;
@@ -2553,11 +2858,18 @@ function getPrimitiveType(type) {
2553
2858
  }
2554
2859
  /** Shared by number, integer and bigint nodes; the exclusive bounds keep only the OAS 3.1 numeric form. */
2555
2860
  function getNumericConstraints(schema) {
2861
+ const exclusiveMinimum = typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0;
2862
+ const exclusiveMaximum = typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0;
2863
+ const hasExclusive = exclusiveMinimum !== void 0 || exclusiveMaximum !== void 0;
2556
2864
  return {
2557
2865
  min: schema.minimum,
2558
2866
  max: schema.maximum,
2559
- exclusiveMinimum: typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : void 0,
2560
- exclusiveMaximum: typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0,
2867
+ ...hasExclusive && { exclusive: {
2868
+ ...exclusiveMinimum !== void 0 && { min: exclusiveMinimum },
2869
+ ...exclusiveMaximum !== void 0 && { max: exclusiveMaximum }
2870
+ } },
2871
+ exclusiveMinimum,
2872
+ exclusiveMaximum,
2561
2873
  multipleOf: schema.multipleOf
2562
2874
  };
2563
2875
  }
@@ -2566,11 +2878,11 @@ function getNumericConstraints(schema) {
2566
2878
  */
2567
2879
  function isWithinBounds({ value, schema }) {
2568
2880
  if (typeof value !== "number") return true;
2569
- const { min, max, exclusiveMinimum, exclusiveMaximum, multipleOf } = getNumericConstraints(schema);
2881
+ const { min, max, exclusive, multipleOf } = getNumericConstraints(schema);
2570
2882
  if (min !== void 0 && value < min) return false;
2571
2883
  if (max !== void 0 && value > max) return false;
2572
- if (exclusiveMinimum !== void 0 && value <= exclusiveMinimum) return false;
2573
- if (exclusiveMaximum !== void 0 && value >= exclusiveMaximum) return false;
2884
+ if (exclusive?.min !== void 0 && value <= exclusive.min) return false;
2885
+ if (exclusive?.max !== void 0 && value >= exclusive.max) return false;
2574
2886
  if (multipleOf !== void 0 && Math.abs(value / multipleOf - Math.round(value / multipleOf)) > 1e-9) return false;
2575
2887
  return true;
2576
2888
  }
@@ -2728,6 +3040,15 @@ function convertFormattedEnum(context) {
2728
3040
  }))
2729
3041
  });
2730
3042
  }
3043
+ function getStringContent({ schema, parse }) {
3044
+ const { contentMediaType: mediaType, contentEncoding: encoding, contentSchema } = schema;
3045
+ if (mediaType === void 0 && encoding === void 0 && !contentSchema) return {};
3046
+ return { content: {
3047
+ ...mediaType !== void 0 && { mediaType },
3048
+ ...encoding !== void 0 && { encoding },
3049
+ ...contentSchema && { schema: parse({ schema: contentSchema }) }
3050
+ } };
3051
+ }
2731
3052
  /**
2732
3053
  * Converts a `type: 'string'` schema into a `StringSchemaNode`.
2733
3054
  */
@@ -2738,7 +3059,8 @@ function convertString(context) {
2738
3059
  primitive: "string",
2739
3060
  min: schema.minLength,
2740
3061
  max: schema.maxLength,
2741
- pattern: schema.pattern
3062
+ pattern: schema.pattern,
3063
+ ...getStringContent(context)
2742
3064
  });
2743
3065
  }
2744
3066
  /**
@@ -2752,6 +3074,15 @@ function convertNumeric(context, type) {
2752
3074
  });
2753
3075
  }
2754
3076
  // src/emit/converters/structural.ts
3077
+ function getContains(schema, parse) {
3078
+ const { contains, minContains, maxContains } = schema;
3079
+ if (!contains || typeof contains !== "object") return {};
3080
+ return { contains: {
3081
+ schema: parse({ schema: contains }),
3082
+ ...typeof minContains === "number" && { min: minContains },
3083
+ ...typeof maxContains === "number" && { max: maxContains }
3084
+ } };
3085
+ }
2755
3086
  /**
2756
3087
  * Resolves a `true` or empty-object map schema (`additionalProperties`/`patternProperties`) to
2757
3088
  * `options.unknownType`, otherwise parses it as a regular schema.
@@ -2804,7 +3135,7 @@ function convertObject({ schema, name, nullable, defaultValue, options, parse, r
2804
3135
  required
2805
3136
  });
2806
3137
  }) : [];
2807
- const additionalProperties = schema.additionalProperties;
3138
+ const additionalProperties = schema.additionalProperties ?? schema.unevaluatedProperties;
2808
3139
  const additionalPropertiesNode = (() => {
2809
3140
  if (additionalProperties === true) return true;
2810
3141
  if (additionalProperties) return resolveMapSchema(additionalProperties, {
@@ -2834,6 +3165,8 @@ function convertObject({ schema, name, nullable, defaultValue, options, parse, r
2834
3165
  additionalProperties: additionalPropertiesNode,
2835
3166
  patternProperties,
2836
3167
  propertyNames,
3168
+ min: schema.minProperties,
3169
+ max: schema.maxProperties,
2837
3170
  minProperties: schema.minProperties,
2838
3171
  maxProperties: schema.maxProperties
2839
3172
  });
@@ -2864,7 +3197,8 @@ function convertObject({ schema, name, nullable, defaultValue, options, parse, r
2864
3197
  */
2865
3198
  function convertTuple({ schema, name, nullable, defaultValue, options, parse }) {
2866
3199
  const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }));
2867
- const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: schema.items });
3200
+ const tail = schema.items ?? schema.unevaluatedItems;
3201
+ const rest = tail === false ? void 0 : !tail || tail === true ? ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: tail });
2868
3202
  return createNode({
2869
3203
  schema,
2870
3204
  name,
@@ -2876,7 +3210,8 @@ function convertTuple({ schema, name, nullable, defaultValue, options, parse })
2876
3210
  items: tupleItems,
2877
3211
  rest,
2878
3212
  min: schema.minItems,
2879
- max: schema.maxItems
3213
+ max: schema.maxItems,
3214
+ ...getContains(schema, parse)
2880
3215
  });
2881
3216
  }
2882
3217
  /**
@@ -2900,7 +3235,8 @@ function convertArray({ schema, name, nullable, defaultValue, options, parse })
2900
3235
  items,
2901
3236
  min: schema.minItems,
2902
3237
  max: schema.maxItems,
2903
- unique: schema.uniqueItems ?? void 0
3238
+ unique: schema.uniqueItems ?? void 0,
3239
+ ...getContains(schema, parse)
2904
3240
  });
2905
3241
  }
2906
3242
  // src/emit/parseSchema.ts
@@ -3028,6 +3364,20 @@ const schemaRules = [
3028
3364
  ];
3029
3365
  // src/parser.ts
3030
3366
  /**
3367
+ * `unevaluated*` also sees what composition siblings evaluate, so with such a sibling it is dropped to keep the schema open.
3368
+ */
3369
+ function hasUnevaluatedSiblings(schema) {
3370
+ if (schema.unevaluatedProperties === void 0 && schema.unevaluatedItems === void 0) return false;
3371
+ return [
3372
+ schema.allOf,
3373
+ schema.anyOf,
3374
+ schema.oneOf,
3375
+ schema.if,
3376
+ schema.dependentSchemas,
3377
+ schema.$ref
3378
+ ].some((value) => value !== void 0);
3379
+ }
3380
+ /**
3031
3381
  * Creates the schema converter bound to one OpenAPI document: `parseSchema` dispatches each
3032
3382
  * schema through the ordered `schemaRules` table, with the parser options merged once.
3033
3383
  *
@@ -3045,6 +3395,13 @@ function createSchemaParser(ctx) {
3045
3395
  schema: flattenedSchema,
3046
3396
  name
3047
3397
  });
3398
+ if (hasUnevaluatedSiblings(schema)) {
3399
+ const { unevaluatedProperties: _properties, unevaluatedItems: _items, ...open } = schema;
3400
+ return parseSchema({
3401
+ schema: open,
3402
+ name
3403
+ });
3404
+ }
3048
3405
  const nullable = isNullable(schema) || void 0;
3049
3406
  const context = {
3050
3407
  schema,
@@ -3058,8 +3415,62 @@ function createSchemaParser(ctx) {
3058
3415
  refs,
3059
3416
  renames
3060
3417
  };
3061
- for (const rule of schemaRules) if (rule.match(context)) return rule.convert(context);
3062
- return createNode(context, { type: options.emptySchemaType });
3418
+ const rule = schemaRules.find((entry) => entry.match(context));
3419
+ return withConditionals(rule ? rule.convert(context) : createNode(context, { type: options.emptySchemaType }), schema);
3420
+ }
3421
+ function requireKeys(keys) {
3422
+ const unknown = ast.factory.createSchema({ type: ast.schemaTypes.unknown });
3423
+ return ast.factory.createSchema({
3424
+ type: ast.schemaTypes.object,
3425
+ primitive: "object",
3426
+ properties: keys.map((name) => ast.factory.createProperty({
3427
+ name,
3428
+ required: true,
3429
+ schema: unknown
3430
+ }))
3431
+ });
3432
+ }
3433
+ /**
3434
+ * Adds `if`/`then`/`else`, `dependentRequired` and `dependentSchemas` to the node as `conditionals`.
3435
+ */
3436
+ function withConditionals(node, schema) {
3437
+ const conditionals = [schema, ...Array.isArray(schema.allOf) ? schema.allOf.filter(isAnnotationMember) : []].flatMap(toConditionals);
3438
+ return conditionals.length ? {
3439
+ ...node,
3440
+ conditionals
3441
+ } : node;
3442
+ }
3443
+ function toConditionals(schema) {
3444
+ const branch = (value) => {
3445
+ if (!value || typeof value !== "object") return void 0;
3446
+ const { required, type, ...rest } = value;
3447
+ if (Array.isArray(required) && required.length && (type === void 0 || type === "object") && !Object.keys(rest).length) return requireKeys(required);
3448
+ return parseSchema({ schema: value });
3449
+ };
3450
+ const entries = (value) => value && typeof value === "object" ? Object.entries(value) : [];
3451
+ const conditionals = [];
3452
+ const condition = branch(schema.if);
3453
+ if (condition) {
3454
+ const thenNode = branch(schema.then);
3455
+ const elseNode = branch(schema.else);
3456
+ conditionals.push({
3457
+ if: condition,
3458
+ ...thenNode && { then: thenNode },
3459
+ ...elseNode && { else: elseNode }
3460
+ });
3461
+ }
3462
+ for (const [key, required] of entries(schema.dependentRequired)) if (Array.isArray(required) && required.length && required.every((name) => typeof name === "string")) conditionals.push({
3463
+ if: requireKeys([key]),
3464
+ then: requireKeys(required)
3465
+ });
3466
+ for (const [key, dependent] of entries(schema.dependentSchemas)) {
3467
+ const thenNode = branch(dependent);
3468
+ if (thenNode) conditionals.push({
3469
+ if: requireKeys([key]),
3470
+ then: thenNode
3471
+ });
3472
+ }
3473
+ return conditionals;
3063
3474
  }
3064
3475
  return parseSchema;
3065
3476
  }
@@ -3399,6 +3810,7 @@ const adapterOas = defineExtension({
3399
3810
  schemaNodes.push(promotedEnums ? refPromotedEnums(node, promotedEnums) : node);
3400
3811
  }
3401
3812
  const operations = promotedEnums ? operationNodes.map((node) => refPromotedEnums(node, promotedEnums)) : operationNodes;
3813
+ const authSchemes = toAuthSchemes(document.components?.securitySchemes, refs);
3402
3814
  return ast.factory.createInput({
3403
3815
  schemas: schemaNodes,
3404
3816
  operations,
@@ -3406,23 +3818,25 @@ const adapterOas = defineExtension({
3406
3818
  title: document.info?.title,
3407
3819
  description: document.info?.description,
3408
3820
  version: document.info?.version,
3821
+ summary: document.info?.summary,
3822
+ termsOfService: document.info?.termsOfService,
3823
+ contact: pickStrings(document.info?.contact, [
3824
+ "name",
3825
+ "url",
3826
+ "email"
3827
+ ]),
3828
+ license: typeof document.info?.license?.name === "string" ? {
3829
+ ...pickStrings(document.info.license, ["identifier", "url"]),
3830
+ name: document.info.license.name
3831
+ } : void 0,
3832
+ externalDocs: getExternalDocs(document.externalDocs),
3409
3833
  baseURL: resolveBaseUrl({
3410
3834
  document,
3411
3835
  server
3412
3836
  }),
3413
3837
  servers: toInputServers(document.servers ?? []),
3414
- securitySchemes: Object.entries(document.components?.securitySchemes ?? {}).flatMap(([name, value]) => {
3415
- const scheme = refs.deref(value);
3416
- return scheme?.type ? [{
3417
- name,
3418
- type: scheme.type,
3419
- description: scheme.description,
3420
- scheme: scheme.scheme,
3421
- bearerFormat: scheme.bearerFormat,
3422
- in: scheme.in,
3423
- parameterName: scheme.name
3424
- }] : [];
3425
- }),
3838
+ ...document.tags?.length && { tags: toInputTags(document.tags) },
3839
+ ...authSchemes.length && { auth: authSchemes },
3426
3840
  extensions: pickExtensions(document)
3427
3841
  }
3428
3842
  });
@@ -3463,18 +3877,19 @@ const adapterOas = defineExtension({
3463
3877
  parsedDocument = document;
3464
3878
  const refs = createRefs(document);
3465
3879
  const { schemas, renames, pointers } = getSchemas(document, { contentType }, refs);
3466
- return parseInput({
3880
+ const parseSchema = createSchemaParser({
3881
+ document,
3882
+ refs,
3883
+ renames,
3884
+ options: parserOptions
3885
+ });
3886
+ return groupUnsupportedFormats(() => parseInput({
3467
3887
  document,
3468
3888
  refs,
3469
3889
  schemas,
3470
3890
  pointers,
3471
- parseSchema: createSchemaParser({
3472
- document,
3473
- refs,
3474
- renames,
3475
- options: parserOptions
3476
- })
3477
- });
3891
+ parseSchema
3892
+ }));
3478
3893
  })();
3479
3894
  inputCache.set(source, {
3480
3895
  promise,