@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 +186 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +186 -65
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dialect.ts +38 -0
- package/src/parser.ts +105 -74
package/dist/index.js
CHANGED
|
@@ -713,6 +713,35 @@ function dereferenceWithRef(document, schema) {
|
|
|
713
713
|
return schema;
|
|
714
714
|
}
|
|
715
715
|
//#endregion
|
|
716
|
+
//#region src/dialect.ts
|
|
717
|
+
/**
|
|
718
|
+
* The OpenAPI / Swagger dialect — the default used by `@kubb/adapter-oas`.
|
|
719
|
+
*
|
|
720
|
+
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
721
|
+
* decisions that differ between specs (nullability, `$ref`, discriminator, binary,
|
|
722
|
+
* ref resolution) so the converter pipeline and dispatch rules stay shared. A
|
|
723
|
+
* future adapter (e.g. AsyncAPI) ships its own dialect — `type: ['null', …]`
|
|
724
|
+
* nullability, no discriminator object, binary via `contentEncoding` — and reuses
|
|
725
|
+
* the rest unchanged.
|
|
726
|
+
*
|
|
727
|
+
* Formats (`uuid`, `email`, dates, …) are intentionally NOT here: they are shared
|
|
728
|
+
* JSON Schema vocabulary, so the converters keep that common case.
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```ts
|
|
732
|
+
* const parser = createSchemaParser(context) // uses oasDialect
|
|
733
|
+
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
736
|
+
const oasDialect = ast.defineSchemaDialect({
|
|
737
|
+
name: "oas",
|
|
738
|
+
isNullable,
|
|
739
|
+
isReference,
|
|
740
|
+
isDiscriminator,
|
|
741
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
742
|
+
resolveRef
|
|
743
|
+
});
|
|
744
|
+
//#endregion
|
|
716
745
|
//#region src/resolvers.ts
|
|
717
746
|
/**
|
|
718
747
|
* Replaces `{variable}` placeholders in an OpenAPI server URL with provided values.
|
|
@@ -1142,7 +1171,7 @@ function normalizeArrayEnum(schema) {
|
|
|
1142
1171
|
*
|
|
1143
1172
|
* @internal
|
|
1144
1173
|
*/
|
|
1145
|
-
function createSchemaParser(ctx) {
|
|
1174
|
+
function createSchemaParser(ctx, dialect = oasDialect) {
|
|
1146
1175
|
const document = ctx.document;
|
|
1147
1176
|
/**
|
|
1148
1177
|
* Tracks `$ref` paths that are currently being resolved to prevent infinite
|
|
@@ -1176,7 +1205,7 @@ function createSchemaParser(ctx) {
|
|
|
1176
1205
|
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1177
1206
|
if (!resolvedRefCache.has(refPath)) {
|
|
1178
1207
|
try {
|
|
1179
|
-
const referenced = resolveRef(document, refPath);
|
|
1208
|
+
const referenced = dialect.resolveRef(document, refPath);
|
|
1180
1209
|
if (referenced) {
|
|
1181
1210
|
resolvingRefs.add(refPath);
|
|
1182
1211
|
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
@@ -1225,13 +1254,13 @@ function createSchemaParser(ctx) {
|
|
|
1225
1254
|
}
|
|
1226
1255
|
const filteredDiscriminantValues = [];
|
|
1227
1256
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1228
|
-
if (!isReference(item) || !name) return true;
|
|
1229
|
-
const deref = resolveRef(document, item.$ref);
|
|
1230
|
-
if (!deref || !isDiscriminator(deref)) return true;
|
|
1257
|
+
if (!dialect.isReference(item) || !name) return true;
|
|
1258
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1259
|
+
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1231
1260
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1232
1261
|
if (!parentUnion) return true;
|
|
1233
1262
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1234
|
-
const inOneOf = parentUnion.some((oneOfItem) => isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1263
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1235
1264
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1236
1265
|
if (inOneOf || inMapping) {
|
|
1237
1266
|
const discriminatorValue = ast.findDiscriminator(deref.discriminator.mapping, childRef);
|
|
@@ -1252,9 +1281,9 @@ function createSchemaParser(ctx) {
|
|
|
1252
1281
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1253
1282
|
if (missingRequired.length) {
|
|
1254
1283
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1255
|
-
if (!isReference(item)) return [item];
|
|
1256
|
-
const deref = resolveRef(document, item.$ref);
|
|
1257
|
-
return deref && !isReference(deref) ? [deref] : [];
|
|
1284
|
+
if (!dialect.isReference(item)) return [item];
|
|
1285
|
+
const deref = dialect.resolveRef(document, item.$ref);
|
|
1286
|
+
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1258
1287
|
});
|
|
1259
1288
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1260
1289
|
allOfMembers.push(parseSchema({
|
|
@@ -1299,10 +1328,10 @@ function createSchemaParser(ctx) {
|
|
|
1299
1328
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1300
1329
|
const unionBase = {
|
|
1301
1330
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1302
|
-
discriminatorPropertyName: isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1331
|
+
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1303
1332
|
strategy
|
|
1304
1333
|
};
|
|
1305
|
-
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1334
|
+
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1306
1335
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1307
1336
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1308
1337
|
return parseSchema({
|
|
@@ -1312,7 +1341,7 @@ function createSchemaParser(ctx) {
|
|
|
1312
1341
|
})() : void 0;
|
|
1313
1342
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1314
1343
|
const members = unionMembers.map((s) => {
|
|
1315
|
-
const ref = isReference(s) ? s.$ref : void 0;
|
|
1344
|
+
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1316
1345
|
const discriminatorValue = ast.findDiscriminator(discriminator?.mapping, ref);
|
|
1317
1346
|
const memberNode = parseSchema({
|
|
1318
1347
|
schema: s,
|
|
@@ -1504,7 +1533,7 @@ function createSchemaParser(ctx) {
|
|
|
1504
1533
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1505
1534
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1506
1535
|
const resolvedPropSchema = propSchema;
|
|
1507
|
-
const propNullable = isNullable(resolvedPropSchema);
|
|
1536
|
+
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
1508
1537
|
const propNode = parseSchema({
|
|
1509
1538
|
schema: resolvedPropSchema,
|
|
1510
1539
|
name: ast.childName(name, propName)
|
|
@@ -1549,7 +1578,7 @@ function createSchemaParser(ctx) {
|
|
|
1549
1578
|
maxProperties: schema.maxProperties,
|
|
1550
1579
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1551
1580
|
});
|
|
1552
|
-
if (isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1581
|
+
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1553
1582
|
const discPropName = schema.discriminator.propertyName;
|
|
1554
1583
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1555
1584
|
const enumName = name ? ast.enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
@@ -1652,11 +1681,143 @@ function createSchemaParser(ctx) {
|
|
|
1652
1681
|
});
|
|
1653
1682
|
}
|
|
1654
1683
|
/**
|
|
1684
|
+
* Converts a binary string schema (`type: 'string'`, `contentMediaType: 'application/octet-stream'`)
|
|
1685
|
+
* into a `blob` node.
|
|
1686
|
+
*/
|
|
1687
|
+
function convertBlob({ schema, name, nullable, defaultValue }) {
|
|
1688
|
+
return ast.createSchema({
|
|
1689
|
+
type: "blob",
|
|
1690
|
+
primitive: "string",
|
|
1691
|
+
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
1696
|
+
*
|
|
1697
|
+
* Returns `null` when only one non-`null` type remains (e.g. `['string', 'null']`), so `parseSchema`
|
|
1698
|
+
* falls through and handles it as that single type with nullability already folded in.
|
|
1699
|
+
*/
|
|
1700
|
+
function convertMultiType({ schema, name, nullable, defaultValue, rawOptions }) {
|
|
1701
|
+
const types = schema.type;
|
|
1702
|
+
const nonNullTypes = types.filter((t) => t !== "null");
|
|
1703
|
+
if (nonNullTypes.length <= 1) return null;
|
|
1704
|
+
const arrayNullable = types.includes("null") || nullable || void 0;
|
|
1705
|
+
return ast.createSchema({
|
|
1706
|
+
type: "union",
|
|
1707
|
+
members: nonNullTypes.map((t) => parseSchema({
|
|
1708
|
+
schema: {
|
|
1709
|
+
...schema,
|
|
1710
|
+
type: t
|
|
1711
|
+
},
|
|
1712
|
+
name
|
|
1713
|
+
}, rawOptions)),
|
|
1714
|
+
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Ordered schema-dispatch table. Order is significant: composition keywords (`$ref`, `allOf`,
|
|
1719
|
+
* `oneOf`/`anyOf`) take precedence over `const`/`format`, which take precedence over the plain
|
|
1720
|
+
* `type`. The first matching rule that produces a node wins; see {@link SchemaRule} for the
|
|
1721
|
+
* match/convert/fall-through contract.
|
|
1722
|
+
*/
|
|
1723
|
+
const schemaRules = [
|
|
1724
|
+
{
|
|
1725
|
+
name: "ref",
|
|
1726
|
+
match: ({ schema }) => dialect.isReference(schema),
|
|
1727
|
+
convert: convertRef
|
|
1728
|
+
},
|
|
1729
|
+
{
|
|
1730
|
+
name: "allOf",
|
|
1731
|
+
match: ({ schema }) => !!schema.allOf?.length,
|
|
1732
|
+
convert: convertAllOf
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
name: "union",
|
|
1736
|
+
match: ({ schema }) => !!(schema.oneOf?.length || schema.anyOf?.length),
|
|
1737
|
+
convert: convertUnion
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
name: "const",
|
|
1741
|
+
match: ({ schema }) => "const" in schema && schema.const !== void 0,
|
|
1742
|
+
convert: convertConst
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
name: "format",
|
|
1746
|
+
match: ({ schema }) => !!schema.format,
|
|
1747
|
+
convert: convertFormat
|
|
1748
|
+
},
|
|
1749
|
+
{
|
|
1750
|
+
name: "blob",
|
|
1751
|
+
match: ({ schema }) => dialect.isBinary(schema),
|
|
1752
|
+
convert: convertBlob
|
|
1753
|
+
},
|
|
1754
|
+
{
|
|
1755
|
+
name: "multi-type",
|
|
1756
|
+
match: ({ schema }) => Array.isArray(schema.type) && schema.type.length > 1,
|
|
1757
|
+
convert: convertMultiType
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
name: "constrained-string",
|
|
1761
|
+
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
1762
|
+
convert: convertString
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
name: "constrained-number",
|
|
1766
|
+
match: ({ schema, type }) => !type && (schema.minimum !== void 0 || schema.maximum !== void 0),
|
|
1767
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1768
|
+
},
|
|
1769
|
+
{
|
|
1770
|
+
name: "enum",
|
|
1771
|
+
match: ({ schema }) => !!schema.enum?.length,
|
|
1772
|
+
convert: convertEnum
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
name: "object",
|
|
1776
|
+
match: ({ schema, type }) => type === "object" || !!schema.properties || !!schema.additionalProperties || "patternProperties" in schema,
|
|
1777
|
+
convert: convertObject
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
name: "tuple",
|
|
1781
|
+
match: ({ schema }) => "prefixItems" in schema,
|
|
1782
|
+
convert: convertTuple
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
name: "array",
|
|
1786
|
+
match: ({ schema, type }) => type === "array" || "items" in schema,
|
|
1787
|
+
convert: convertArray
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
name: "string",
|
|
1791
|
+
match: ({ type }) => type === "string",
|
|
1792
|
+
convert: convertString
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
name: "number",
|
|
1796
|
+
match: ({ type }) => type === "number",
|
|
1797
|
+
convert: (ctx) => convertNumeric(ctx, "number")
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
name: "integer",
|
|
1801
|
+
match: ({ type }) => type === "integer",
|
|
1802
|
+
convert: (ctx) => convertNumeric(ctx, "integer")
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
name: "boolean",
|
|
1806
|
+
match: ({ type }) => type === "boolean",
|
|
1807
|
+
convert: convertBoolean
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
name: "null",
|
|
1811
|
+
match: ({ type }) => type === "null",
|
|
1812
|
+
convert: convertNull
|
|
1813
|
+
}
|
|
1814
|
+
];
|
|
1815
|
+
/**
|
|
1655
1816
|
* Central dispatcher that converts an OAS `SchemaObject` into a `SchemaNode`.
|
|
1656
1817
|
*
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1818
|
+
* Builds the per-schema context, then runs it through the ordered {@link schemaRules} table
|
|
1819
|
+
* via {@link ast.dispatch}. When no rule produces a node, falls back to the configured
|
|
1820
|
+
* `emptySchemaType`.
|
|
1660
1821
|
*/
|
|
1661
1822
|
function parseSchema({ schema, name }, rawOptions) {
|
|
1662
1823
|
const options = {
|
|
@@ -1668,59 +1829,18 @@ function createSchemaParser(ctx) {
|
|
|
1668
1829
|
schema: flattenedSchema,
|
|
1669
1830
|
name
|
|
1670
1831
|
}, rawOptions);
|
|
1671
|
-
const nullable = isNullable(schema) || void 0;
|
|
1672
|
-
const
|
|
1673
|
-
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
1674
|
-
const ctx = {
|
|
1832
|
+
const nullable = dialect.isNullable(schema) || void 0;
|
|
1833
|
+
const schemaCtx = {
|
|
1675
1834
|
schema,
|
|
1676
1835
|
name,
|
|
1677
1836
|
nullable,
|
|
1678
|
-
defaultValue,
|
|
1679
|
-
type,
|
|
1837
|
+
defaultValue: schema.default === null && nullable ? void 0 : schema.default,
|
|
1838
|
+
type: Array.isArray(schema.type) ? schema.type[0] : schema.type,
|
|
1680
1839
|
rawOptions,
|
|
1681
1840
|
options
|
|
1682
1841
|
};
|
|
1683
|
-
|
|
1684
|
-
if (
|
|
1685
|
-
if ([...schema.oneOf ?? [], ...schema.anyOf ?? []].length) return convertUnion(ctx);
|
|
1686
|
-
if ("const" in schema && schema.const !== void 0) return convertConst(ctx);
|
|
1687
|
-
if (schema.format) {
|
|
1688
|
-
const formatResult = convertFormat(ctx);
|
|
1689
|
-
if (formatResult) return formatResult;
|
|
1690
|
-
}
|
|
1691
|
-
if (schema.type === "string" && schema.contentMediaType === "application/octet-stream") return ast.createSchema({
|
|
1692
|
-
type: "blob",
|
|
1693
|
-
primitive: "string",
|
|
1694
|
-
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1695
|
-
});
|
|
1696
|
-
if (Array.isArray(schema.type) && schema.type.length > 1) {
|
|
1697
|
-
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1698
|
-
const arrayNullable = schema.type.includes("null") || nullable || void 0;
|
|
1699
|
-
if (nonNullTypes.length > 1) return ast.createSchema({
|
|
1700
|
-
type: "union",
|
|
1701
|
-
members: nonNullTypes.map((t) => parseSchema({
|
|
1702
|
-
schema: {
|
|
1703
|
-
...schema,
|
|
1704
|
-
type: t
|
|
1705
|
-
},
|
|
1706
|
-
name
|
|
1707
|
-
}, rawOptions)),
|
|
1708
|
-
...buildSchemaNode(schema, name, arrayNullable, defaultValue)
|
|
1709
|
-
});
|
|
1710
|
-
}
|
|
1711
|
-
if (!type) {
|
|
1712
|
-
if (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0) return convertString(ctx);
|
|
1713
|
-
if (schema.minimum !== void 0 || schema.maximum !== void 0) return convertNumeric(ctx, "number");
|
|
1714
|
-
}
|
|
1715
|
-
if (schema.enum?.length) return convertEnum(ctx);
|
|
1716
|
-
if (type === "object" || schema.properties || schema.additionalProperties || "patternProperties" in schema) return convertObject(ctx);
|
|
1717
|
-
if ("prefixItems" in schema) return convertTuple(ctx);
|
|
1718
|
-
if (type === "array" || "items" in schema) return convertArray(ctx);
|
|
1719
|
-
if (type === "string") return convertString(ctx);
|
|
1720
|
-
if (type === "number") return convertNumeric(ctx, "number");
|
|
1721
|
-
if (type === "integer") return convertNumeric(ctx, "integer");
|
|
1722
|
-
if (type === "boolean") return convertBoolean(ctx);
|
|
1723
|
-
if (type === "null") return convertNull(ctx);
|
|
1842
|
+
const node = ast.dispatch(schemaRules, schemaCtx);
|
|
1843
|
+
if (node) return node;
|
|
1724
1844
|
const emptyType = typeOptionMap.get(options.emptySchemaType);
|
|
1725
1845
|
return ast.createSchema({
|
|
1726
1846
|
type: emptyType,
|
|
@@ -1783,7 +1903,7 @@ function createSchemaParser(ctx) {
|
|
|
1783
1903
|
const keys = [];
|
|
1784
1904
|
for (const key in schema.properties) {
|
|
1785
1905
|
const prop = schema.properties[key];
|
|
1786
|
-
if (prop && !isReference(prop) && prop[flag]) keys.push(key);
|
|
1906
|
+
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
1787
1907
|
}
|
|
1788
1908
|
return keys.length ? keys : null;
|
|
1789
1909
|
}
|
|
@@ -1845,6 +1965,7 @@ function createSchemaParser(ctx) {
|
|
|
1845
1965
|
const urlPath = new URLPath(operation.path);
|
|
1846
1966
|
return ast.createOperation({
|
|
1847
1967
|
operationId,
|
|
1968
|
+
protocol: "http",
|
|
1848
1969
|
method: operation.method.toUpperCase(),
|
|
1849
1970
|
path: urlPath.path,
|
|
1850
1971
|
tags: operation.getTags().map((tag) => tag.name),
|