@kubb/adapter-oas 5.0.0-beta.58 → 5.0.0-beta.59
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 +126 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +9 -11
- package/dist/index.js +125 -89
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/constants.ts +3 -3
- package/src/discriminator.ts +40 -0
- package/src/parser.ts +22 -13
- package/src/resolvers.ts +1 -1
- package/src/stream.ts +2 -2
- package/src/types.ts +9 -11
package/dist/index.cjs
CHANGED
|
@@ -114,7 +114,7 @@ const structuralKeys = new Set([
|
|
|
114
114
|
*
|
|
115
115
|
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
116
116
|
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
117
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types
|
|
117
|
+
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname` and
|
|
118
118
|
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
119
119
|
*
|
|
120
120
|
* @example
|
|
@@ -167,8 +167,8 @@ const formatMap = {
|
|
|
167
167
|
*/
|
|
168
168
|
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
169
169
|
/**
|
|
170
|
-
* Maps
|
|
171
|
-
*
|
|
170
|
+
* Maps the `unknownType`/`emptySchemaType` option string to its `ScalarSchemaType` constant.
|
|
171
|
+
* A `Map` (over a plain object) lets callers test key membership with `.has()`.
|
|
172
172
|
*/
|
|
173
173
|
const typeOptionMap = new Map([
|
|
174
174
|
["any", _kubb_core.ast.schemaTypes.any],
|
|
@@ -863,6 +863,120 @@ const oasDialect = _kubb_core.ast.defineSchemaDialect({
|
|
|
863
863
|
resolveRef
|
|
864
864
|
});
|
|
865
865
|
//#endregion
|
|
866
|
+
//#region src/discriminator.ts
|
|
867
|
+
/**
|
|
868
|
+
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
869
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
870
|
+
*
|
|
871
|
+
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
872
|
+
* discriminator parents) rather than on all schemas at once.
|
|
873
|
+
*/
|
|
874
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
875
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
876
|
+
for (const schema of schemas) {
|
|
877
|
+
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
878
|
+
if (!unionNode) {
|
|
879
|
+
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
880
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
881
|
+
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
882
|
+
if (u) {
|
|
883
|
+
unionNode = u;
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
889
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
890
|
+
for (const member of members) {
|
|
891
|
+
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
892
|
+
if (!intersectionNode?.members) continue;
|
|
893
|
+
let refNode = null;
|
|
894
|
+
let objNode = null;
|
|
895
|
+
for (const m of intersectionNode.members) {
|
|
896
|
+
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
897
|
+
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
898
|
+
}
|
|
899
|
+
if (!refNode?.name || !objNode) continue;
|
|
900
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
901
|
+
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : null;
|
|
902
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
903
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
904
|
+
if (!enumValues.length) continue;
|
|
905
|
+
const existing = childMap.get(refNode.name);
|
|
906
|
+
if (!existing) {
|
|
907
|
+
childMap.set(refNode.name, {
|
|
908
|
+
propertyName: discriminatorPropertyName,
|
|
909
|
+
enumValues: [...enumValues]
|
|
910
|
+
});
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
existing.enumValues.push(...enumValues);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return childMap;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
920
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
921
|
+
* without buffering all schemas.
|
|
922
|
+
*/
|
|
923
|
+
function patchDiscriminatorNode(node, entry) {
|
|
924
|
+
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
925
|
+
if (!objectNode) return node;
|
|
926
|
+
const { propertyName, enumValues } = entry;
|
|
927
|
+
const enumSchema = _kubb_core.ast.factory.createSchema({
|
|
928
|
+
type: "enum",
|
|
929
|
+
enumValues
|
|
930
|
+
});
|
|
931
|
+
const newProp = _kubb_core.ast.factory.createProperty({
|
|
932
|
+
name: propertyName,
|
|
933
|
+
required: true,
|
|
934
|
+
schema: enumSchema
|
|
935
|
+
});
|
|
936
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
937
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
938
|
+
return {
|
|
939
|
+
...objectNode,
|
|
940
|
+
properties: newProperties
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
945
|
+
*
|
|
946
|
+
* @example
|
|
947
|
+
* ```ts
|
|
948
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
949
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
function createDiscriminantNode({ propertyName, value }) {
|
|
953
|
+
return _kubb_core.ast.factory.createSchema({
|
|
954
|
+
type: "object",
|
|
955
|
+
primitive: "object",
|
|
956
|
+
properties: [_kubb_core.ast.factory.createProperty({
|
|
957
|
+
name: propertyName,
|
|
958
|
+
schema: _kubb_core.ast.factory.createSchema({
|
|
959
|
+
type: "enum",
|
|
960
|
+
primitive: "string",
|
|
961
|
+
enumValues: [value]
|
|
962
|
+
}),
|
|
963
|
+
required: true
|
|
964
|
+
})]
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* ```ts
|
|
972
|
+
* findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
|
|
973
|
+
* ```
|
|
974
|
+
*/
|
|
975
|
+
function findDiscriminator(mapping, ref) {
|
|
976
|
+
if (!mapping || !ref) return null;
|
|
977
|
+
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
|
|
978
|
+
}
|
|
979
|
+
//#endregion
|
|
866
980
|
//#region src/mime.ts
|
|
867
981
|
/**
|
|
868
982
|
* MIME type fragments that mark a media type as JSON-like.
|
|
@@ -1351,7 +1465,7 @@ function getSchemas(document, { contentType }) {
|
|
|
1351
1465
|
}
|
|
1352
1466
|
/**
|
|
1353
1467
|
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
1354
|
-
* Returns `null` when `dateType: false`,
|
|
1468
|
+
* Returns `null` when `dateType: false`, so the format falls through to `string`.
|
|
1355
1469
|
*/
|
|
1356
1470
|
function getDateType(options, format) {
|
|
1357
1471
|
if (!options.dateType) return null;
|
|
@@ -1453,9 +1567,9 @@ function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
|
1453
1567
|
* Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
|
|
1454
1568
|
*
|
|
1455
1569
|
* This pattern violates the OpenAPI spec but appears in real specs. The fix moves enum values
|
|
1456
|
-
* from the array to its items sub-schema,
|
|
1570
|
+
* from the array to its items sub-schema, so they are valid for downstream processing.
|
|
1457
1571
|
*
|
|
1458
|
-
* @note
|
|
1572
|
+
* @note A defensive measure for non-compliant specs.
|
|
1459
1573
|
*/
|
|
1460
1574
|
function normalizeArrayEnum(schema) {
|
|
1461
1575
|
const normalizedItems = {
|
|
@@ -1473,7 +1587,7 @@ function normalizeArrayEnum(schema) {
|
|
|
1473
1587
|
*
|
|
1474
1588
|
* Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
|
|
1475
1589
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1476
|
-
*
|
|
1590
|
+
* which works because function declarations hoist.
|
|
1477
1591
|
*
|
|
1478
1592
|
* @internal
|
|
1479
1593
|
*/
|
|
@@ -1565,7 +1679,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1565
1679
|
const inOneOf = parentUnion.some((oneOfItem) => dialect.isReference(oneOfItem) && oneOfItem.$ref === childRef);
|
|
1566
1680
|
const inMapping = Object.values(deref.discriminator.mapping ?? {}).some((v) => v === childRef);
|
|
1567
1681
|
if (inOneOf || inMapping) {
|
|
1568
|
-
const discriminatorValue =
|
|
1682
|
+
const discriminatorValue = findDiscriminator(deref.discriminator.mapping, childRef);
|
|
1569
1683
|
if (discriminatorValue) filteredDiscriminantValues.push({
|
|
1570
1684
|
propertyName: deref.discriminator.propertyName,
|
|
1571
1685
|
value: discriminatorValue
|
|
@@ -1603,7 +1717,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1603
1717
|
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1604
1718
|
allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
|
|
1605
1719
|
}
|
|
1606
|
-
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(
|
|
1720
|
+
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(createDiscriminantNode({
|
|
1607
1721
|
propertyName,
|
|
1608
1722
|
value
|
|
1609
1723
|
}));
|
|
@@ -1644,7 +1758,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1644
1758
|
if (sharedPropertiesNode || discriminator?.mapping) {
|
|
1645
1759
|
const members = unionMembers.map((s) => {
|
|
1646
1760
|
const ref = dialect.isReference(s) ? s.$ref : void 0;
|
|
1647
|
-
const discriminatorValue =
|
|
1761
|
+
const discriminatorValue = findDiscriminator(discriminator?.mapping, ref);
|
|
1648
1762
|
const memberNode = parseSchema({
|
|
1649
1763
|
schema: s,
|
|
1650
1764
|
name
|
|
@@ -1657,7 +1771,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1657
1771
|
}), discriminator.propertyName) : void 0;
|
|
1658
1772
|
return _kubb_core.ast.factory.createSchema({
|
|
1659
1773
|
type: "intersection",
|
|
1660
|
-
members: [memberNode, narrowedDiscriminatorNode ??
|
|
1774
|
+
members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
|
|
1661
1775
|
propertyName: discriminator.propertyName,
|
|
1662
1776
|
value: discriminatorValue
|
|
1663
1777
|
})]
|
|
@@ -2312,84 +2426,6 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2312
2426
|
};
|
|
2313
2427
|
}
|
|
2314
2428
|
//#endregion
|
|
2315
|
-
//#region src/discriminator.ts
|
|
2316
|
-
/**
|
|
2317
|
-
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
2318
|
-
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
2319
|
-
*
|
|
2320
|
-
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
2321
|
-
* discriminator parents) rather than on all schemas at once.
|
|
2322
|
-
*/
|
|
2323
|
-
function buildDiscriminatorChildMap(schemas) {
|
|
2324
|
-
const childMap = /* @__PURE__ */ new Map();
|
|
2325
|
-
for (const schema of schemas) {
|
|
2326
|
-
let unionNode = _kubb_core.ast.narrowSchema(schema, "union");
|
|
2327
|
-
if (!unionNode) {
|
|
2328
|
-
const intersectionMembers = _kubb_core.ast.narrowSchema(schema, "intersection")?.members;
|
|
2329
|
-
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
2330
|
-
const u = _kubb_core.ast.narrowSchema(m, "union");
|
|
2331
|
-
if (u) {
|
|
2332
|
-
unionNode = u;
|
|
2333
|
-
break;
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
2338
|
-
const { discriminatorPropertyName, members } = unionNode;
|
|
2339
|
-
for (const member of members) {
|
|
2340
|
-
const intersectionNode = _kubb_core.ast.narrowSchema(member, "intersection");
|
|
2341
|
-
if (!intersectionNode?.members) continue;
|
|
2342
|
-
let refNode = null;
|
|
2343
|
-
let objNode = null;
|
|
2344
|
-
for (const m of intersectionNode.members) {
|
|
2345
|
-
refNode ??= _kubb_core.ast.narrowSchema(m, "ref");
|
|
2346
|
-
objNode ??= _kubb_core.ast.narrowSchema(m, "object");
|
|
2347
|
-
}
|
|
2348
|
-
if (!refNode?.name || !objNode) continue;
|
|
2349
|
-
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
2350
|
-
const enumNode = prop ? _kubb_core.ast.narrowSchema(prop.schema, "enum") : null;
|
|
2351
|
-
if (!enumNode?.enumValues?.length) continue;
|
|
2352
|
-
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
2353
|
-
if (!enumValues.length) continue;
|
|
2354
|
-
const existing = childMap.get(refNode.name);
|
|
2355
|
-
if (!existing) {
|
|
2356
|
-
childMap.set(refNode.name, {
|
|
2357
|
-
propertyName: discriminatorPropertyName,
|
|
2358
|
-
enumValues: [...enumValues]
|
|
2359
|
-
});
|
|
2360
|
-
continue;
|
|
2361
|
-
}
|
|
2362
|
-
existing.enumValues.push(...enumValues);
|
|
2363
|
-
}
|
|
2364
|
-
}
|
|
2365
|
-
return childMap;
|
|
2366
|
-
}
|
|
2367
|
-
/**
|
|
2368
|
-
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
2369
|
-
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
2370
|
-
* without buffering all schemas.
|
|
2371
|
-
*/
|
|
2372
|
-
function patchDiscriminatorNode(node, entry) {
|
|
2373
|
-
const objectNode = _kubb_core.ast.narrowSchema(node, "object");
|
|
2374
|
-
if (!objectNode) return node;
|
|
2375
|
-
const { propertyName, enumValues } = entry;
|
|
2376
|
-
const enumSchema = _kubb_core.ast.factory.createSchema({
|
|
2377
|
-
type: "enum",
|
|
2378
|
-
enumValues
|
|
2379
|
-
});
|
|
2380
|
-
const newProp = _kubb_core.ast.factory.createProperty({
|
|
2381
|
-
name: propertyName,
|
|
2382
|
-
required: true,
|
|
2383
|
-
schema: enumSchema
|
|
2384
|
-
});
|
|
2385
|
-
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
2386
|
-
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
2387
|
-
return {
|
|
2388
|
-
...objectNode,
|
|
2389
|
-
properties: newProperties
|
|
2390
|
-
};
|
|
2391
|
-
}
|
|
2392
|
-
//#endregion
|
|
2393
2429
|
//#region src/schemaDiagnostics.ts
|
|
2394
2430
|
/**
|
|
2395
2431
|
* Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
|
|
@@ -2452,7 +2488,7 @@ function visit(node, pointer) {
|
|
|
2452
2488
|
*
|
|
2453
2489
|
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2454
2490
|
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2455
|
-
* name (collision-resolved against existing component names)
|
|
2491
|
+
* name (collision-resolved against existing component names). Shapes without a name stay inline.
|
|
2456
2492
|
*/
|
|
2457
2493
|
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2458
2494
|
const circularSchemas = new Set(circularNames);
|