@kubb/adapter-oas 5.0.0-beta.29 → 5.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -739,6 +739,35 @@ function dereferenceWithRef(document, schema) {
739
739
  return schema;
740
740
  }
741
741
  //#endregion
742
+ //#region src/dialect.ts
743
+ /**
744
+ * The OpenAPI / Swagger dialect — the default used by `@kubb/adapter-oas`.
745
+ *
746
+ * Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
747
+ * decisions that differ between specs (nullability, `$ref`, discriminator, binary,
748
+ * ref resolution) so the converter pipeline and dispatch rules stay shared. A
749
+ * future adapter (e.g. AsyncAPI) ships its own dialect — `type: ['null', …]`
750
+ * nullability, no discriminator object, binary via `contentEncoding` — and reuses
751
+ * the rest unchanged.
752
+ *
753
+ * Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
754
+ * JSON Schema vocabulary, so the converters keep that common case.
755
+ *
756
+ * @example
757
+ * ```ts
758
+ * const parser = createSchemaParser(context) // uses oasDialect
759
+ * const parser = createSchemaParser(context, oasDialect) // explicit
760
+ * ```
761
+ */
762
+ const oasDialect = _kubb_core.ast.defineSchemaDialect({
763
+ name: "oas",
764
+ isNullable,
765
+ isReference,
766
+ isDiscriminator,
767
+ isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
768
+ resolveRef
769
+ });
770
+ //#endregion
742
771
  //#region src/resolvers.ts
743
772
  /**
744
773
  * Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
@@ -1168,7 +1197,7 @@ function normalizeArrayEnum(schema) {
1168
1197
  *
1169
1198
  * @internal
1170
1199
  */
1171
- function createSchemaParser(ctx) {
1200
+ function createSchemaParser(ctx, dialect = oasDialect) {
1172
1201
  const document = ctx.document;
1173
1202
  /**
1174
1203
  * Tracks `$ref` paths that are currently being resolved to prevent infinite
@@ -1202,7 +1231,7 @@ function createSchemaParser(ctx) {
1202
1231
  if (refPath && !resolvingRefs.has(refPath)) {
1203
1232
  if (!resolvedRefCache.has(refPath)) {
1204
1233
  try {
1205
- const referenced = resolveRef(document, refPath);
1234
+ const referenced = dialect.resolveRef(document, refPath);
1206
1235
  if (referenced) {
1207
1236
  resolvingRefs.add(refPath);
1208
1237
  resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
@@ -1251,13 +1280,13 @@ function createSchemaParser(ctx) {
1251
1280
  }
1252
1281
  const filteredDiscriminantValues = [];
1253
1282
  const allOfMembers = schema.allOf.filter((item) => {
1254
- if (!isReference(item) || !name) return true;
1255
- const deref = resolveRef(document, item.$ref);
1256
- if (!deref || !isDiscriminator(deref)) return true;
1283
+ if (!dialect.isReference(item) || !name) return true;
1284
+ const deref = dialect.resolveRef(document, item.$ref);
1285
+ if (!deref || !dialect.isDiscriminator(deref)) return true;
1257
1286
  const parentUnion = deref.oneOf ?? deref.anyOf;
1258
1287
  if (!parentUnion) return true;
1259
1288
  const childRef = `${SCHEMA_REF_PREFIX}${name}`;
1260
- const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
1289
+ const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
1261
1290
  const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
1262
1291
  if (inOneOf || inMapping) {
1263
1292
  const discriminatorValue = _kubb_core.ast.findDiscriminator(deref.discriminator.mapping, childRef);
@@ -1278,9 +1307,9 @@ function createSchemaParser(ctx) {
1278
1307
  const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
1279
1308
  if (missingRequired.length) {
1280
1309
  const resolvedMembers = schema.allOf.flatMap((item) => {
1281
- if (!isReference(item)) return [item];
1282
- const deref = resolveRef(document, item.$ref);
1283
- return deref && !isReference(deref) ? [deref] : [];
1310
+ if (!dialect.isReference(item)) return [item];
1311
+ const deref = dialect.resolveRef(document, item.$ref);
1312
+ return deref && !dialect.isReference(deref) ? [deref] : [];
1284
1313
  });
1285
1314
  for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
1286
1315
  allOfMembers.push(parseSchema({
@@ -1325,10 +1354,10 @@ function createSchemaParser(ctx) {
1325
1354
  const strategy = schema.oneOf ? "one" : "any";
1326
1355
  const unionBase = {
1327
1356
  ...buildSchemaNode(schema, name, nullable, defaultValue),
1328
- discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
1357
+ discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
1329
1358
  strategy
1330
1359
  };
1331
- const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
1360
+ const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
1332
1361
  const sharedPropertiesNode = schema.properties ? (() => {
1333
1362
  const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
1334
1363
  return parseSchema({
@@ -1338,7 +1367,7 @@ function createSchemaParser(ctx) {
1338
1367
  })() : void 0;
1339
1368
  if (sharedPropertiesNode || discriminator?.mapping) {
1340
1369
  const members = unionMembers.map((s) => {
1341
- const ref = isReference(s) ? s.$ref : void 0;
1370
+ const ref = dialect.isReference(s) ? s.$ref : void 0;
1342
1371
  const discriminatorValue = _kubb_core.ast.findDiscriminator(discriminator?.mapping, ref);
1343
1372
  const memberNode = parseSchema({
1344
1373
  schema: s,
@@ -1530,7 +1559,7 @@ function createSchemaParser(ctx) {
1530
1559
  const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
1531
1560
  const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
1532
1561
  const resolvedPropSchema = propSchema;
1533
- const propNullable = isNullable(resolvedPropSchema);
1562
+ const propNullable = dialect.isNullable(resolvedPropSchema);
1534
1563
  const propNode = parseSchema({
1535
1564
  schema: resolvedPropSchema,
1536
1565
  name: _kubb_core.ast.childName(name, propName)
@@ -1575,7 +1604,7 @@ function createSchemaParser(ctx) {
1575
1604
  maxProperties: schema.maxProperties,
1576
1605
  ...buildSchemaNode(schema, name, nullable, defaultValue)
1577
1606
  });
1578
- if (isDiscriminator(schema) && schema.discriminator.mapping) {
1607
+ if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
1579
1608
  const discPropName = schema.discriminator.propertyName;
1580
1609
  const values = Object.keys(schema.discriminator.mapping);
1581
1610
  const enumName = name ? _kubb_core.ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
@@ -1678,11 +1707,143 @@ function createSchemaParser(ctx) {
1678
1707
  });
1679
1708
  }
1680
1709
  /**
1710
+ * Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
1711
+ * into a `blob` node.
1712
+ */
1713
+ function convertBlob({ schema, name, nullable, defaultValue }) {
1714
+ return _kubb_core.ast.createSchema({
1715
+ type: "blob",
1716
+ primitive: "string",
1717
+ ...buildSchemaNode(schema, name, nullable, defaultValue)
1718
+ });
1719
+ }
1720
+ /**
1721
+ * Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
1722
+ *
1723
+ * Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
1724
+ * falls through and handles it as that single type with nullability already folded in.
1725
+ */
1726
+ function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
1727
+ const types = schema.type;
1728
+ const nonNullTypes = types.filter((t) => t !== "null");
1729
+ if (nonNullTypes.length <= 1) return null;
1730
+ const arrayNullable = types.includes("null") || nullable || void 0;
1731
+ return _kubb_core.ast.createSchema({
1732
+ type: "union",
1733
+ members: nonNullTypes.map((t) => parseSchema({
1734
+ schema: {
1735
+ ...schema,
1736
+ type: t
1737
+ },
1738
+ name
1739
+ }, rawOptions)),
1740
+ ...buildSchemaNode(schema, name, arrayNullable, defaultValue)
1741
+ });
1742
+ }
1743
+ /**
1744
+ * Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
1745
+ * `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
1746
+ * `type`. The first matching rule that produces a node wins; see {@link SchemaRule} for the
1747
+ * match/convert/fall-through contract.
1748
+ */
1749
+ const schemaRules = [
1750
+ {
1751
+ name: "ref",
1752
+ match: ({ schema }) => dialect.isReference(schema),
1753
+ convert: convertRef
1754
+ },
1755
+ {
1756
+ name: "allOf",
1757
+ match: ({ schema }) => !!schema.allOf?.length,
1758
+ convert: convertAllOf
1759
+ },
1760
+ {
1761
+ name: "union",
1762
+ match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
1763
+ convert: convertUnion
1764
+ },
1765
+ {
1766
+ name: "const",
1767
+ match: ({ schema }) => "const" in schema && schema.const !== void 0,
1768
+ convert: convertConst
1769
+ },
1770
+ {
1771
+ name: "format",
1772
+ match: ({ schema }) => !!schema.format,
1773
+ convert: convertFormat
1774
+ },
1775
+ {
1776
+ name: "blob",
1777
+ match: ({ schema }) => dialect.isBinary(schema),
1778
+ convert: convertBlob
1779
+ },
1780
+ {
1781
+ name: "multi-type",
1782
+ match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
1783
+ convert: convertMultiType
1784
+ },
1785
+ {
1786
+ name: "constrained-string",
1787
+ match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
1788
+ convert: convertString
1789
+ },
1790
+ {
1791
+ name: "constrained-number",
1792
+ match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
1793
+ convert: (ctx) => convertNumeric(ctx, "number")
1794
+ },
1795
+ {
1796
+ name: "enum",
1797
+ match: ({ schema }) => !!schema.enum?.length,
1798
+ convert: convertEnum
1799
+ },
1800
+ {
1801
+ name: "object",
1802
+ match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
1803
+ convert: convertObject
1804
+ },
1805
+ {
1806
+ name: "tuple",
1807
+ match: ({ schema }) => "prefixItems" in schema,
1808
+ convert: convertTuple
1809
+ },
1810
+ {
1811
+ name: "array",
1812
+ match: ({ schema, type }) => type === "array" || "items" in schema,
1813
+ convert: convertArray
1814
+ },
1815
+ {
1816
+ name: "string",
1817
+ match: ({ type }) => type === "string",
1818
+ convert: convertString
1819
+ },
1820
+ {
1821
+ name: "number",
1822
+ match: ({ type }) => type === "number",
1823
+ convert: (ctx) => convertNumeric(ctx, "number")
1824
+ },
1825
+ {
1826
+ name: "integer",
1827
+ match: ({ type }) => type === "integer",
1828
+ convert: (ctx) => convertNumeric(ctx, "integer")
1829
+ },
1830
+ {
1831
+ name: "boolean",
1832
+ match: ({ type }) => type === "boolean",
1833
+ convert: convertBoolean
1834
+ },
1835
+ {
1836
+ name: "null",
1837
+ match: ({ type }) => type === "null",
1838
+ convert: convertNull
1839
+ }
1840
+ ];
1841
+ /**
1681
1842
  * Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
1682
1843
  *
1683
- * Dispatch order (first match wins): `$ref` `allOf` `oneOf`/`anyOf` `const` → `format`
1684
- * octet-stream blob multi-type array constraint-inferred type `enum` object/array/tuple/scalar
1685
- * → empty-schema fallback (`emptySchemaType` option).
1844
+ * Builds the per-schema context, then runs it through the ordered {@link schemaRules} table
1845
+ * via {@link ast.dispatch}. When no rule produces a node, falls back to the configured
1846
+ * `emptySchemaType`.
1686
1847
  */
1687
1848
  function parseSchema({ schema, name }, rawOptions) {
1688
1849
  const options = {
@@ -1694,59 +1855,18 @@ function createSchemaParser(ctx) {
1694
1855
  schema: flattenedSchema,
1695
1856
  name
1696
1857
  }, rawOptions);
1697
- const nullable = isNullable(schema) || void 0;
1698
- const defaultValue = schema.default === null && nullable ? void 0 : schema.default;
1699
- const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
1700
- const ctx = {
1858
+ const nullable = dialect.isNullable(schema) || void 0;
1859
+ const schemaCtx = {
1701
1860
  schema,
1702
1861
  name,
1703
1862
  nullable,
1704
- defaultValue,
1705
- type,
1863
+ defaultValue: schema.default === null && nullable ? void 0 : schema.default,
1864
+ type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
1706
1865
  rawOptions,
1707
1866
  options
1708
1867
  };
1709
- if (isReference(schema)) return convertRef(ctx);
1710
- if (schema.allOf?.length) return convertAllOf(ctx);
1711
- if ([...schema.oneOf ?? [], ...schema.anyOf ?? []].length) return convertUnion(ctx);
1712
- if ("const" in schema && schema.const !== void 0) return convertConst(ctx);
1713
- if (schema.format) {
1714
- const formatResult = convertFormat(ctx);
1715
- if (formatResult) return formatResult;
1716
- }
1717
- if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return _kubb_core.ast.createSchema({
1718
- type: "blob",
1719
- primitive: "string",
1720
- ...buildSchemaNode(schema, name, nullable, defaultValue)
1721
- });
1722
- if (Array.isArray(schema.type) && schema.type.length > 1) {
1723
- const nonNullTypes = schema.type.filter((t) => t !== "null");
1724
- const arrayNullable = schema.type.includes("null") || nullable || void 0;
1725
- if (nonNullTypes.length > 1) return _kubb_core.ast.createSchema({
1726
- type: "union",
1727
- members: nonNullTypes.map((t) => parseSchema({
1728
- schema: {
1729
- ...schema,
1730
- type: t
1731
- },
1732
- name
1733
- }, rawOptions)),
1734
- ...buildSchemaNode(schema, name, arrayNullable, defaultValue)
1735
- });
1736
- }
1737
- if (!type) {
1738
- if (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0) return convertString(ctx);
1739
- if (schema.minimum !== void 0 || schema.maximum !== void 0) return convertNumeric(ctx, "number");
1740
- }
1741
- if (schema.enum?.length) return convertEnum(ctx);
1742
- if (type === "object" || schema.properties || schema.additionalProperties || "patternProperties" in schema) return convertObject(ctx);
1743
- if ("prefixItems" in schema) return convertTuple(ctx);
1744
- if (type === "array" || "items" in schema) return convertArray(ctx);
1745
- if (type === "string") return convertString(ctx);
1746
- if (type === "number") return convertNumeric(ctx, "number");
1747
- if (type === "integer") return convertNumeric(ctx, "integer");
1748
- if (type === "boolean") return convertBoolean(ctx);
1749
- if (type === "null") return convertNull(ctx);
1868
+ const node = _kubb_core.ast.dispatch(schemaRules, schemaCtx);
1869
+ if (node) return node;
1750
1870
  const emptyType = typeOptionMap.get(options.emptySchemaType);
1751
1871
  return _kubb_core.ast.createSchema({
1752
1872
  type: emptyType,
@@ -1809,7 +1929,7 @@ function createSchemaParser(ctx) {
1809
1929
  const keys = [];
1810
1930
  for (const key in schema.properties) {
1811
1931
  const prop = schema.properties[key];
1812
- if (prop && !isReference(prop) && prop[flag]) keys.push(key);
1932
+ if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
1813
1933
  }
1814
1934
  return keys.length ? keys : null;
1815
1935
  }
@@ -1871,6 +1991,7 @@ function createSchemaParser(ctx) {
1871
1991
  const urlPath = new URLPath(operation.path);
1872
1992
  return _kubb_core.ast.createOperation({
1873
1993
  operationId,
1994
+ protocol: "http",
1874
1995
  method: operation.method.toUpperCase(),
1875
1996
  path: urlPath.path,
1876
1997
  tags: operation.getTags().map((tag) => tag.name),