@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.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
|
-
*
|
|
1684
|
-
*
|
|
1685
|
-
*
|
|
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
|
|
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
|
-
|
|
1710
|
-
if (
|
|
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),
|