@kubb/adapter-oas 5.0.0-beta.61 → 5.0.0-beta.62
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 +88 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +88 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/dialect.ts +28 -6
- package/src/parser.ts +69 -57
- package/src/stream.ts +28 -47
package/dist/index.js
CHANGED
|
@@ -814,6 +814,23 @@ function dereferenceWithRef(document, schema) {
|
|
|
814
814
|
//#endregion
|
|
815
815
|
//#region src/dialect.ts
|
|
816
816
|
/**
|
|
817
|
+
* Derives a schema's `optional`/`nullish` flags from a parent's `required` value and the
|
|
818
|
+
* schema's own `nullable`. How "required" and "nullable" combine is OpenAPI-specific, so it
|
|
819
|
+
* lives in the dialect.
|
|
820
|
+
*
|
|
821
|
+
* - Non-required + non-nullable → `optional: true`.
|
|
822
|
+
* - Non-required + nullable → `nullish: true`.
|
|
823
|
+
* - Required → both flags cleared.
|
|
824
|
+
*/
|
|
825
|
+
function optionality(schema, required) {
|
|
826
|
+
const nullable = schema.nullable ?? false;
|
|
827
|
+
return {
|
|
828
|
+
...schema,
|
|
829
|
+
optional: !required && !nullable ? true : void 0,
|
|
830
|
+
nullish: !required && nullable ? true : void 0
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
817
834
|
* The OpenAPI / Swagger dialect, the default used by `@kubb/adapter-oas`.
|
|
818
835
|
*
|
|
819
836
|
* Implements the spec-agnostic {@link ast.SchemaDialect} contract: it isolates the
|
|
@@ -832,13 +849,16 @@ function dereferenceWithRef(document, schema) {
|
|
|
832
849
|
* const parser = createSchemaParser(context, oasDialect) // explicit
|
|
833
850
|
* ```
|
|
834
851
|
*/
|
|
835
|
-
const oasDialect = ast.
|
|
852
|
+
const oasDialect = ast.defineDialect({
|
|
836
853
|
name: "oas",
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
854
|
+
schema: {
|
|
855
|
+
isNullable,
|
|
856
|
+
isReference,
|
|
857
|
+
isDiscriminator,
|
|
858
|
+
isBinary: (schema) => schema.type === "string" && schema.contentMediaType === "application/octet-stream",
|
|
859
|
+
resolveRef,
|
|
860
|
+
optionality
|
|
861
|
+
}
|
|
842
862
|
});
|
|
843
863
|
//#endregion
|
|
844
864
|
//#region src/discriminator.ts
|
|
@@ -1561,6 +1581,21 @@ function normalizeArrayEnum(schema) {
|
|
|
1561
1581
|
};
|
|
1562
1582
|
}
|
|
1563
1583
|
/**
|
|
1584
|
+
* Builds a `null` scalar node carrying the schema's documentation. Shared by the `const: null`
|
|
1585
|
+
* and the drf-spectacular `NullEnum` (`{ enum: [null] }`) branches, which render identically.
|
|
1586
|
+
*/
|
|
1587
|
+
function createNullSchema(schema, name) {
|
|
1588
|
+
return ast.factory.createSchema({
|
|
1589
|
+
type: "null",
|
|
1590
|
+
primitive: "null",
|
|
1591
|
+
name,
|
|
1592
|
+
title: schema.title,
|
|
1593
|
+
description: schema.description,
|
|
1594
|
+
deprecated: schema.deprecated,
|
|
1595
|
+
format: schema.format
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1564
1599
|
* Names the inline enums on a property's schema, and on each item when the property is a tuple, from
|
|
1565
1600
|
* the parent and property name. Wraps `macroEnumName` at the property construction site.
|
|
1566
1601
|
*/
|
|
@@ -1616,7 +1651,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1616
1651
|
if (refPath && !resolvingRefs.has(refPath)) {
|
|
1617
1652
|
if (!resolvedRefCache.has(refPath)) {
|
|
1618
1653
|
try {
|
|
1619
|
-
const referenced = dialect.resolveRef(document, refPath);
|
|
1654
|
+
const referenced = dialect.schema.resolveRef(document, refPath);
|
|
1620
1655
|
if (referenced) {
|
|
1621
1656
|
resolvingRefs.add(refPath);
|
|
1622
1657
|
resolvedSchema = parseSchema({ schema: referenced }, rawOptions);
|
|
@@ -1665,13 +1700,13 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1665
1700
|
}
|
|
1666
1701
|
const filteredDiscriminantValues = [];
|
|
1667
1702
|
const allOfMembers = schema.allOf.filter((item) => {
|
|
1668
|
-
if (!dialect.isReference(item) || !name) return true;
|
|
1669
|
-
const deref = dialect.resolveRef(document, item.$ref);
|
|
1670
|
-
if (!deref || !dialect.isDiscriminator(deref)) return true;
|
|
1703
|
+
if (!dialect.schema.isReference(item) || !name) return true;
|
|
1704
|
+
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1705
|
+
if (!deref || !dialect.schema.isDiscriminator(deref)) return true;
|
|
1671
1706
|
const parentUnion = deref.oneOf ?? deref.anyOf;
|
|
1672
1707
|
if (!parentUnion) return true;
|
|
1673
1708
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1674
|
-
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1709
|
+
const inOneOf = parentUnion.some((oneOfItem) => dialect.schema.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1675
1710
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1676
1711
|
if (inOneOf || inMapping) {
|
|
1677
1712
|
const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
|
|
@@ -1692,9 +1727,9 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1692
1727
|
const missingRequired = schema.required.filter((key) => !outerKeys.has(key));
|
|
1693
1728
|
if (missingRequired.length) {
|
|
1694
1729
|
const resolvedMembers = schema.allOf.flatMap((item) => {
|
|
1695
|
-
if (!dialect.isReference(item)) return [item];
|
|
1696
|
-
const deref = dialect.resolveRef(document, item.$ref);
|
|
1697
|
-
return deref && !dialect.isReference(deref) ? [deref] : [];
|
|
1730
|
+
if (!dialect.schema.isReference(item)) return [item];
|
|
1731
|
+
const deref = dialect.schema.resolveRef(document, item.$ref);
|
|
1732
|
+
return deref && !dialect.schema.isReference(deref) ? [deref] : [];
|
|
1698
1733
|
});
|
|
1699
1734
|
for (const key of missingRequired) for (const resolved of resolvedMembers) if (resolved.properties?.[key]) {
|
|
1700
1735
|
allOfMembers.push(parseSchema({
|
|
@@ -1739,10 +1774,10 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1739
1774
|
const strategy = schema.oneOf ? "one" : "any";
|
|
1740
1775
|
const unionBase = {
|
|
1741
1776
|
...buildSchemaNode(schema, name, nullable, defaultValue),
|
|
1742
|
-
discriminatorPropertyName: dialect.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1777
|
+
discriminatorPropertyName: dialect.schema.isDiscriminator(schema) ? schema.discriminator.propertyName : void 0,
|
|
1743
1778
|
strategy
|
|
1744
1779
|
};
|
|
1745
|
-
const discriminator = dialect.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1780
|
+
const discriminator = dialect.schema.isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
1746
1781
|
const sharedPropertiesNode = schema.properties ? (() => {
|
|
1747
1782
|
const { oneOf: _oneOf, anyOf: _anyOf, ...schemaWithoutUnion } = schema;
|
|
1748
1783
|
return parseSchema({
|
|
@@ -1752,7 +1787,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1752
1787
|
})() : void 0;
|
|
1753
1788
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1754
1789
|
const members = unionMembers.map((s) => {
|
|
1755
|
-
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1790
|
+
const ref = dialect.schema.isReference(s) ? s.$ref : void 0;
|
|
1756
1791
|
const discriminatorValue = findDiscriminator(discriminator?.mapping, ref);
|
|
1757
1792
|
const memberNode = parseSchema({
|
|
1758
1793
|
schema: s,
|
|
@@ -1798,15 +1833,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1798
1833
|
*/
|
|
1799
1834
|
function convertConst({ schema, name, nullable, defaultValue }) {
|
|
1800
1835
|
const constValue = schema.const;
|
|
1801
|
-
if (constValue === null) return
|
|
1802
|
-
type: "null",
|
|
1803
|
-
primitive: "null",
|
|
1804
|
-
name,
|
|
1805
|
-
title: schema.title,
|
|
1806
|
-
description: schema.description,
|
|
1807
|
-
deprecated: schema.deprecated,
|
|
1808
|
-
format: schema.format
|
|
1809
|
-
});
|
|
1836
|
+
if (constValue === null) return createNullSchema(schema, name);
|
|
1810
1837
|
const constPrimitive = getPrimitiveType(typeof constValue === "number" ? "number" : typeof constValue === "boolean" ? "boolean" : "string");
|
|
1811
1838
|
return ast.factory.createSchema({
|
|
1812
1839
|
type: "enum",
|
|
@@ -1895,15 +1922,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1895
1922
|
}, rawOptions);
|
|
1896
1923
|
const nullInEnum = schema.enum.includes(null);
|
|
1897
1924
|
const filteredValues = nullInEnum ? schema.enum.filter((v) => v !== null) : schema.enum;
|
|
1898
|
-
if (nullInEnum && filteredValues.length === 0) return
|
|
1899
|
-
type: "null",
|
|
1900
|
-
primitive: "null",
|
|
1901
|
-
name,
|
|
1902
|
-
title: schema.title,
|
|
1903
|
-
description: schema.description,
|
|
1904
|
-
deprecated: schema.deprecated,
|
|
1905
|
-
format: schema.format
|
|
1906
|
-
});
|
|
1925
|
+
if (nullInEnum && filteredValues.length === 0) return createNullSchema(schema, name);
|
|
1907
1926
|
const enumNullable = nullable || nullInEnum || void 0;
|
|
1908
1927
|
const enumDefault = schema.default === null && enumNullable ? void 0 : schema.default;
|
|
1909
1928
|
const enumPrimitive = getPrimitiveType(type);
|
|
@@ -1953,7 +1972,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1953
1972
|
const properties = schema.properties ? Object.entries(schema.properties).map(([propName, propSchema]) => {
|
|
1954
1973
|
const required = Array.isArray(schema.required) ? schema.required.includes(propName) : !!schema.required;
|
|
1955
1974
|
const resolvedPropSchema = propSchema;
|
|
1956
|
-
const propNullable = dialect.isNullable(resolvedPropSchema);
|
|
1975
|
+
const propNullable = dialect.schema.isNullable(resolvedPropSchema);
|
|
1957
1976
|
const schemaNode = nameEnums(parseSchema({
|
|
1958
1977
|
schema: resolvedPropSchema,
|
|
1959
1978
|
name: childName(name, propName)
|
|
@@ -1969,7 +1988,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1969
1988
|
nullable: schemaNode.type === "null" ? void 0 : propNullable || void 0
|
|
1970
1989
|
},
|
|
1971
1990
|
required
|
|
1972
|
-
});
|
|
1991
|
+
}, dialect);
|
|
1973
1992
|
}) : [];
|
|
1974
1993
|
const additionalProperties = schema.additionalProperties;
|
|
1975
1994
|
const additionalPropertiesNode = (() => {
|
|
@@ -1990,7 +2009,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1990
2009
|
maxProperties: schema.maxProperties,
|
|
1991
2010
|
...buildSchemaNode(schema, name, nullable, defaultValue)
|
|
1992
2011
|
});
|
|
1993
|
-
if (dialect.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
2012
|
+
if (dialect.schema.isDiscriminator(schema) && schema.discriminator.mapping) {
|
|
1994
2013
|
const discPropName = schema.discriminator.propertyName;
|
|
1995
2014
|
const values = Object.keys(schema.discriminator.mapping);
|
|
1996
2015
|
const enumName = name ? enumPropName(name, discPropName, options.enumSuffix) : void 0;
|
|
@@ -2134,7 +2153,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2134
2153
|
const schemaRules = [
|
|
2135
2154
|
{
|
|
2136
2155
|
name: "ref",
|
|
2137
|
-
match: ({ schema }) => dialect.isReference(schema),
|
|
2156
|
+
match: ({ schema }) => dialect.schema.isReference(schema),
|
|
2138
2157
|
convert: convertRef
|
|
2139
2158
|
},
|
|
2140
2159
|
{
|
|
@@ -2159,7 +2178,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2159
2178
|
},
|
|
2160
2179
|
{
|
|
2161
2180
|
name: "blob",
|
|
2162
|
-
match: ({ schema }) => dialect.isBinary(schema),
|
|
2181
|
+
match: ({ schema }) => dialect.schema.isBinary(schema),
|
|
2163
2182
|
convert: convertBlob
|
|
2164
2183
|
},
|
|
2165
2184
|
{
|
|
@@ -2240,7 +2259,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2240
2259
|
schema: flattenedSchema,
|
|
2241
2260
|
name
|
|
2242
2261
|
}, rawOptions);
|
|
2243
|
-
const nullable = dialect.isNullable(schema) || void 0;
|
|
2262
|
+
const nullable = dialect.schema.isNullable(schema) || void 0;
|
|
2244
2263
|
const schemaCtx = {
|
|
2245
2264
|
schema,
|
|
2246
2265
|
name,
|
|
@@ -2283,7 +2302,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2283
2302
|
description: param["description"] ?? schema.description
|
|
2284
2303
|
},
|
|
2285
2304
|
required
|
|
2286
|
-
});
|
|
2305
|
+
}, dialect);
|
|
2287
2306
|
}
|
|
2288
2307
|
/**
|
|
2289
2308
|
* Reads the inline `requestBody` metadata (description / required) that OAS exposes
|
|
@@ -2317,7 +2336,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2317
2336
|
const keys = [];
|
|
2318
2337
|
for (const key in schema.properties) {
|
|
2319
2338
|
const prop = schema.properties[key];
|
|
2320
|
-
if (prop && !dialect.isReference(prop) && prop[flag]) keys.push(key);
|
|
2339
|
+
if (prop && !dialect.schema.isReference(prop) && prop[flag]) keys.push(key);
|
|
2321
2340
|
}
|
|
2322
2341
|
return keys.length ? keys : null;
|
|
2323
2342
|
}
|
|
@@ -2334,14 +2353,14 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2334
2353
|
const content = allContentTypes.flatMap((ct) => {
|
|
2335
2354
|
const schema = getRequestSchema(document, operation, { contentType: ct });
|
|
2336
2355
|
if (!schema) return [];
|
|
2337
|
-
return [{
|
|
2356
|
+
return [ast.factory.createContent({
|
|
2338
2357
|
contentType: ct,
|
|
2339
|
-
schema:
|
|
2358
|
+
schema: dialect.schema.optionality(parseSchema({
|
|
2340
2359
|
schema,
|
|
2341
2360
|
name: requestBodyName
|
|
2342
2361
|
}, options), requestBodyMeta.required),
|
|
2343
2362
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly")
|
|
2344
|
-
}];
|
|
2363
|
+
})];
|
|
2345
2364
|
});
|
|
2346
2365
|
const requestBody = content.length > 0 || requestBodyMeta.description ? {
|
|
2347
2366
|
description: requestBodyMeta.description,
|
|
@@ -2366,17 +2385,17 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2366
2385
|
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
2367
2386
|
};
|
|
2368
2387
|
};
|
|
2369
|
-
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ({
|
|
2388
|
+
const content = (ctx.contentType ? [ctx.contentType] : getResponseBodyContentTypes(document, operation, statusCode)).map((contentType) => ast.factory.createContent({
|
|
2370
2389
|
contentType,
|
|
2371
2390
|
...parseEntrySchema(contentType)
|
|
2372
2391
|
}));
|
|
2373
|
-
if (content.length === 0) content.push({
|
|
2392
|
+
if (content.length === 0) content.push(ast.factory.createContent({
|
|
2374
2393
|
contentType: getRequestContentType({
|
|
2375
2394
|
document,
|
|
2376
2395
|
operation
|
|
2377
2396
|
}) || "application/json",
|
|
2378
2397
|
...parseEntrySchema(ctx.contentType)
|
|
2379
|
-
});
|
|
2398
|
+
}));
|
|
2380
2399
|
return ast.factory.createResponse({
|
|
2381
2400
|
statusCode,
|
|
2382
2401
|
description,
|
|
@@ -2384,7 +2403,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2384
2403
|
});
|
|
2385
2404
|
});
|
|
2386
2405
|
const pathItem = document.paths?.[operation.path];
|
|
2387
|
-
const pathItemDoc = pathItem && !dialect.isReference(pathItem) ? pathItem : void 0;
|
|
2406
|
+
const pathItemDoc = pathItem && !dialect.schema.isReference(pathItem) ? pathItem : void 0;
|
|
2388
2407
|
const pickDoc = (key) => {
|
|
2389
2408
|
const own = operation.schema[key];
|
|
2390
2409
|
if (typeof own === "string") return own;
|
|
@@ -2469,36 +2488,6 @@ function visit(node, pointer) {
|
|
|
2469
2488
|
//#endregion
|
|
2470
2489
|
//#region src/stream.ts
|
|
2471
2490
|
/**
|
|
2472
|
-
* Builds the deduplication plan from the already-parsed top-level schema nodes plus a
|
|
2473
|
-
* single extra parse pass over operations (so duplicates in request/response bodies are seen).
|
|
2474
|
-
*
|
|
2475
|
-
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2476
|
-
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2477
|
-
* name (collision-resolved against existing component names). Shapes without a name stay inline.
|
|
2478
|
-
*/
|
|
2479
|
-
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2480
|
-
const circularSchemas = new Set(circularNames);
|
|
2481
|
-
const usedNames = new Set(schemaNames);
|
|
2482
|
-
return ast.buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2483
|
-
isCandidate: (node) => {
|
|
2484
|
-
if (node.type === "enum") return true;
|
|
2485
|
-
if (node.type !== "object") return false;
|
|
2486
|
-
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2487
|
-
return !containsCircularRef(node, { circularSchemas });
|
|
2488
|
-
},
|
|
2489
|
-
nameFor: (node) => {
|
|
2490
|
-
const base = node.name;
|
|
2491
|
-
if (!base) return null;
|
|
2492
|
-
let name = base;
|
|
2493
|
-
let counter = 2;
|
|
2494
|
-
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2495
|
-
usedNames.add(name);
|
|
2496
|
-
return name;
|
|
2497
|
-
},
|
|
2498
|
-
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2499
|
-
});
|
|
2500
|
-
}
|
|
2501
|
-
/**
|
|
2502
2491
|
* Reads the server URL from the document's `servers` array at `serverIndex`,
|
|
2503
2492
|
* interpolating any `serverVariables` into the URL template.
|
|
2504
2493
|
*
|
|
@@ -2567,11 +2556,25 @@ function preScan({ schemas, parseSchema, parseOperation, document, parserOptions
|
|
|
2567
2556
|
const operationNode = parseOperation(parserOptions, operation);
|
|
2568
2557
|
if (operationNode) operationNodes.push(operationNode);
|
|
2569
2558
|
}
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2559
|
+
const circularSchemas = new Set(circularNames);
|
|
2560
|
+
const usedNames = new Set(Object.keys(schemas));
|
|
2561
|
+
dedupePlan = ast.buildDedupePlan([...allNodes, ...operationNodes], {
|
|
2562
|
+
isCandidate: (node) => {
|
|
2563
|
+
if (node.type === "enum") return true;
|
|
2564
|
+
if (node.type !== "object") return false;
|
|
2565
|
+
if (node.name && circularSchemas.has(node.name)) return false;
|
|
2566
|
+
return !containsCircularRef(node, { circularSchemas });
|
|
2567
|
+
},
|
|
2568
|
+
nameFor: (node) => {
|
|
2569
|
+
const base = node.name;
|
|
2570
|
+
if (!base) return null;
|
|
2571
|
+
let name = base;
|
|
2572
|
+
let counter = 2;
|
|
2573
|
+
while (usedNames.has(name)) name = `${base}${counter++}`;
|
|
2574
|
+
usedNames.add(name);
|
|
2575
|
+
return name;
|
|
2576
|
+
},
|
|
2577
|
+
refFor: (name) => `${SCHEMA_REF_PREFIX}${name}`
|
|
2575
2578
|
});
|
|
2576
2579
|
for (const definition of dedupePlan.hoisted) if (definition.type === "enum" && definition.name) enumNames.push(definition.name);
|
|
2577
2580
|
}
|