@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.d.ts
CHANGED
|
@@ -1071,8 +1071,7 @@ type Operation = {
|
|
|
1071
1071
|
//#endregion
|
|
1072
1072
|
//#region src/types.d.ts
|
|
1073
1073
|
/**
|
|
1074
|
-
*
|
|
1075
|
-
* Supports `'application/json'` or any other media type.
|
|
1074
|
+
* Media type used to pick a schema from an operation's request or response.
|
|
1076
1075
|
*
|
|
1077
1076
|
* @example
|
|
1078
1077
|
* ```ts
|
|
@@ -1131,12 +1130,12 @@ type SchemaObject$1 = {
|
|
|
1131
1130
|
*/
|
|
1132
1131
|
items?: SchemaObject$1 | ReferenceObject$1;
|
|
1133
1132
|
/**
|
|
1134
|
-
*
|
|
1133
|
+
* Allowed values for this schema.
|
|
1135
1134
|
*/
|
|
1136
1135
|
enum?: Array<string | number | boolean | null>;
|
|
1137
1136
|
} & (OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject | JSONSchema4 | JSONSchema6 | JSONSchema7);
|
|
1138
1137
|
/**
|
|
1139
|
-
* HTTP method
|
|
1138
|
+
* HTTP method in the lowercase form an OpenAPI path item uses for its keys.
|
|
1140
1139
|
*/
|
|
1141
1140
|
type HttpMethod = Lowercase<ast.HttpMethod>;
|
|
1142
1141
|
/**
|
|
@@ -1156,7 +1155,7 @@ type DiscriminatorObject$1 = OpenAPIV3.DiscriminatorObject | OpenAPIV3_1.Discrim
|
|
|
1156
1155
|
*/
|
|
1157
1156
|
type ReferenceObject$1 = OpenAPIV3.ReferenceObject;
|
|
1158
1157
|
/**
|
|
1159
|
-
* OpenAPI response object
|
|
1158
|
+
* OpenAPI response object holding the content and headers for one status code.
|
|
1160
1159
|
*/
|
|
1161
1160
|
type ResponseObject$1 = OpenAPIV3.ResponseObject | OpenAPIV3_1.ResponseObject;
|
|
1162
1161
|
/**
|
|
@@ -1164,9 +1163,8 @@ type ResponseObject$1 = OpenAPIV3.ResponseObject | OpenAPIV3_1.ResponseObject;
|
|
|
1164
1163
|
*/
|
|
1165
1164
|
type MediaTypeObject$1 = OpenAPIV3.MediaTypeObject | OpenAPIV3_1.MediaTypeObject;
|
|
1166
1165
|
/**
|
|
1167
|
-
* Configuration
|
|
1168
|
-
*
|
|
1169
|
-
* from the spec.
|
|
1166
|
+
* Configuration for the OpenAPI adapter: spec validation, content-type selection,
|
|
1167
|
+
* server URL resolution, and how schema types are derived.
|
|
1170
1168
|
*
|
|
1171
1169
|
* @example
|
|
1172
1170
|
* ```ts
|
|
@@ -1227,9 +1225,9 @@ type AdapterOasOptions = {
|
|
|
1227
1225
|
*
|
|
1228
1226
|
* Duplicated inline shapes (especially enums repeated across many properties) are hoisted
|
|
1229
1227
|
* into one named schema. Every other occurrence, and any structurally identical top-level
|
|
1230
|
-
* component, becomes a `ref` to it. Equality is shape-only
|
|
1231
|
-
* `description` and `example` is ignored.
|
|
1232
|
-
*
|
|
1228
|
+
* component, becomes a `ref` to it. Equality is shape-only, so documentation such as
|
|
1229
|
+
* `description` and `example` is ignored. Set to `false` to keep every occurrence inline
|
|
1230
|
+
* and produce byte-for-byte identical output to earlier versions.
|
|
1233
1231
|
*
|
|
1234
1232
|
* @default true
|
|
1235
1233
|
*/
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { access, readFile } from "node:fs/promises";
|
|
|
5
5
|
import { compileErrors, validate } from "@readme/openapi-parser";
|
|
6
6
|
import { parse } from "yaml";
|
|
7
7
|
import { bundle } from "api-ref-bundler";
|
|
8
|
-
import { childName, containsCircularRef, enumPropName, extractRefName, findCircularSchemas
|
|
8
|
+
import { childName, containsCircularRef, enumPropName, extractRefName, findCircularSchemas } from "@kubb/ast/utils";
|
|
9
9
|
import { collect, narrowSchema } from "@kubb/ast";
|
|
10
10
|
//#region src/constants.ts
|
|
11
11
|
/**
|
|
@@ -91,7 +91,7 @@ const structuralKeys = new Set([
|
|
|
91
91
|
*
|
|
92
92
|
* Only formats whose AST type differs from the OAS `type` field appear here.
|
|
93
93
|
* Formats that depend on runtime options (`int64`, `date-time`, `date`, `time`) are handled separately
|
|
94
|
-
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types
|
|
94
|
+
* in the parser. `ipv4` and `ipv6` map to their own dedicated schema types. `hostname` and
|
|
95
95
|
* `idn-hostname` map to `'url'` as the closest generic string-format type.
|
|
96
96
|
*
|
|
97
97
|
* @example
|
|
@@ -144,8 +144,8 @@ const formatMap = {
|
|
|
144
144
|
*/
|
|
145
145
|
const enumExtensionKeys = ["x-enumNames", "x-enum-varnames"];
|
|
146
146
|
/**
|
|
147
|
-
* Maps
|
|
148
|
-
*
|
|
147
|
+
* Maps the `unknownType`/`emptySchemaType` option string to its `ScalarSchemaType` constant.
|
|
148
|
+
* A `Map` (over a plain object) lets callers test key membership with `.has()`.
|
|
149
149
|
*/
|
|
150
150
|
const typeOptionMap = new Map([
|
|
151
151
|
["any", ast.schemaTypes.any],
|
|
@@ -840,6 +840,120 @@ const oasDialect = ast.defineSchemaDialect({
|
|
|
840
840
|
resolveRef
|
|
841
841
|
});
|
|
842
842
|
//#endregion
|
|
843
|
+
//#region src/discriminator.ts
|
|
844
|
+
/**
|
|
845
|
+
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
846
|
+
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
847
|
+
*
|
|
848
|
+
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
849
|
+
* discriminator parents) rather than on all schemas at once.
|
|
850
|
+
*/
|
|
851
|
+
function buildDiscriminatorChildMap(schemas) {
|
|
852
|
+
const childMap = /* @__PURE__ */ new Map();
|
|
853
|
+
for (const schema of schemas) {
|
|
854
|
+
let unionNode = ast.narrowSchema(schema, "union");
|
|
855
|
+
if (!unionNode) {
|
|
856
|
+
const intersectionMembers = ast.narrowSchema(schema, "intersection")?.members;
|
|
857
|
+
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
858
|
+
const u = ast.narrowSchema(m, "union");
|
|
859
|
+
if (u) {
|
|
860
|
+
unionNode = u;
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
866
|
+
const { discriminatorPropertyName, members } = unionNode;
|
|
867
|
+
for (const member of members) {
|
|
868
|
+
const intersectionNode = ast.narrowSchema(member, "intersection");
|
|
869
|
+
if (!intersectionNode?.members) continue;
|
|
870
|
+
let refNode = null;
|
|
871
|
+
let objNode = null;
|
|
872
|
+
for (const m of intersectionNode.members) {
|
|
873
|
+
refNode ??= ast.narrowSchema(m, "ref");
|
|
874
|
+
objNode ??= ast.narrowSchema(m, "object");
|
|
875
|
+
}
|
|
876
|
+
if (!refNode?.name || !objNode) continue;
|
|
877
|
+
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
878
|
+
const enumNode = prop ? ast.narrowSchema(prop.schema, "enum") : null;
|
|
879
|
+
if (!enumNode?.enumValues?.length) continue;
|
|
880
|
+
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
881
|
+
if (!enumValues.length) continue;
|
|
882
|
+
const existing = childMap.get(refNode.name);
|
|
883
|
+
if (!existing) {
|
|
884
|
+
childMap.set(refNode.name, {
|
|
885
|
+
propertyName: discriminatorPropertyName,
|
|
886
|
+
enumValues: [...enumValues]
|
|
887
|
+
});
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
existing.enumValues.push(...enumValues);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return childMap;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
897
|
+
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
898
|
+
* without buffering all schemas.
|
|
899
|
+
*/
|
|
900
|
+
function patchDiscriminatorNode(node, entry) {
|
|
901
|
+
const objectNode = ast.narrowSchema(node, "object");
|
|
902
|
+
if (!objectNode) return node;
|
|
903
|
+
const { propertyName, enumValues } = entry;
|
|
904
|
+
const enumSchema = ast.factory.createSchema({
|
|
905
|
+
type: "enum",
|
|
906
|
+
enumValues
|
|
907
|
+
});
|
|
908
|
+
const newProp = ast.factory.createProperty({
|
|
909
|
+
name: propertyName,
|
|
910
|
+
required: true,
|
|
911
|
+
schema: enumSchema
|
|
912
|
+
});
|
|
913
|
+
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
914
|
+
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
915
|
+
return {
|
|
916
|
+
...objectNode,
|
|
917
|
+
properties: newProperties
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* ```ts
|
|
925
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
926
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
927
|
+
* ```
|
|
928
|
+
*/
|
|
929
|
+
function createDiscriminantNode({ propertyName, value }) {
|
|
930
|
+
return ast.factory.createSchema({
|
|
931
|
+
type: "object",
|
|
932
|
+
primitive: "object",
|
|
933
|
+
properties: [ast.factory.createProperty({
|
|
934
|
+
name: propertyName,
|
|
935
|
+
schema: ast.factory.createSchema({
|
|
936
|
+
type: "enum",
|
|
937
|
+
primitive: "string",
|
|
938
|
+
enumValues: [value]
|
|
939
|
+
}),
|
|
940
|
+
required: true
|
|
941
|
+
})]
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Returns the discriminator key whose mapping value matches `ref`, or `null` when there is no match.
|
|
946
|
+
*
|
|
947
|
+
* @example
|
|
948
|
+
* ```ts
|
|
949
|
+
* findDiscriminator({ dog: '#/components/schemas/Dog' }, '#/components/schemas/Dog') // 'dog'
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
function findDiscriminator(mapping, ref) {
|
|
953
|
+
if (!mapping || !ref) return null;
|
|
954
|
+
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
|
|
955
|
+
}
|
|
956
|
+
//#endregion
|
|
843
957
|
//#region src/mime.ts
|
|
844
958
|
/**
|
|
845
959
|
* MIME type fragments that mark a media type as JSON-like.
|
|
@@ -1328,7 +1442,7 @@ function getSchemas(document, { contentType }) {
|
|
|
1328
1442
|
}
|
|
1329
1443
|
/**
|
|
1330
1444
|
* Resolves the AST type descriptor for a date/time format, honoring the `dateType` option.
|
|
1331
|
-
* Returns `null` when `dateType: false`,
|
|
1445
|
+
* Returns `null` when `dateType: false`, so the format falls through to `string`.
|
|
1332
1446
|
*/
|
|
1333
1447
|
function getDateType(options, format) {
|
|
1334
1448
|
if (!options.dateType) return null;
|
|
@@ -1430,9 +1544,9 @@ function getResponseBodyContentTypes(document, operation, statusCode) {
|
|
|
1430
1544
|
* Normalizes malformed `{ type: 'array', enum: [...] }` schemas by moving enum values into items.
|
|
1431
1545
|
*
|
|
1432
1546
|
* This pattern violates the OpenAPI spec but appears in real specs. The fix moves enum values
|
|
1433
|
-
* from the array to its items sub-schema,
|
|
1547
|
+
* from the array to its items sub-schema, so they are valid for downstream processing.
|
|
1434
1548
|
*
|
|
1435
|
-
* @note
|
|
1549
|
+
* @note A defensive measure for non-compliant specs.
|
|
1436
1550
|
*/
|
|
1437
1551
|
function normalizeArrayEnum(schema) {
|
|
1438
1552
|
const normalizedItems = {
|
|
@@ -1450,7 +1564,7 @@ function normalizeArrayEnum(schema) {
|
|
|
1450
1564
|
*
|
|
1451
1565
|
* Returns closures that share mutable state (`resolvingRefs` set for cycle detection).
|
|
1452
1566
|
* Each converter branch (`convertRef`, `convertAllOf`, etc.) mutually recursively calls `parseSchema`,
|
|
1453
|
-
*
|
|
1567
|
+
* which works because function declarations hoist.
|
|
1454
1568
|
*
|
|
1455
1569
|
* @internal
|
|
1456
1570
|
*/
|
|
@@ -1580,7 +1694,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1580
1694
|
const { allOf: _allOf, ...schemaWithoutAllOf } = schema;
|
|
1581
1695
|
allOfMembers.push(parseSchema({ schema: schemaWithoutAllOf }, rawOptions));
|
|
1582
1696
|
}
|
|
1583
|
-
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(
|
|
1697
|
+
for (const { propertyName, value } of filteredDiscriminantValues) allOfMembers.push(createDiscriminantNode({
|
|
1584
1698
|
propertyName,
|
|
1585
1699
|
value
|
|
1586
1700
|
}));
|
|
@@ -1634,7 +1748,7 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
1634
1748
|
}), discriminator.propertyName) : void 0;
|
|
1635
1749
|
return ast.factory.createSchema({
|
|
1636
1750
|
type: "intersection",
|
|
1637
|
-
members: [memberNode, narrowedDiscriminatorNode ??
|
|
1751
|
+
members: [memberNode, narrowedDiscriminatorNode ?? createDiscriminantNode({
|
|
1638
1752
|
propertyName: discriminator.propertyName,
|
|
1639
1753
|
value: discriminatorValue
|
|
1640
1754
|
})]
|
|
@@ -2289,84 +2403,6 @@ function createSchemaParser(ctx, dialect = oasDialect) {
|
|
|
2289
2403
|
};
|
|
2290
2404
|
}
|
|
2291
2405
|
//#endregion
|
|
2292
|
-
//#region src/discriminator.ts
|
|
2293
|
-
/**
|
|
2294
|
-
* Builds a map of child schema names → discriminator patch data by scanning the given
|
|
2295
|
-
* top-level AST schema nodes for union schemas that carry a `discriminatorPropertyName`.
|
|
2296
|
-
*
|
|
2297
|
-
* The streaming path calls this on a small pre-parsed subset of schemas (only the
|
|
2298
|
-
* discriminator parents) rather than on all schemas at once.
|
|
2299
|
-
*/
|
|
2300
|
-
function buildDiscriminatorChildMap(schemas) {
|
|
2301
|
-
const childMap = /* @__PURE__ */ new Map();
|
|
2302
|
-
for (const schema of schemas) {
|
|
2303
|
-
let unionNode = ast.narrowSchema(schema, "union");
|
|
2304
|
-
if (!unionNode) {
|
|
2305
|
-
const intersectionMembers = ast.narrowSchema(schema, "intersection")?.members;
|
|
2306
|
-
if (intersectionMembers) for (const m of intersectionMembers) {
|
|
2307
|
-
const u = ast.narrowSchema(m, "union");
|
|
2308
|
-
if (u) {
|
|
2309
|
-
unionNode = u;
|
|
2310
|
-
break;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
if (!unionNode?.discriminatorPropertyName || !unionNode.members) continue;
|
|
2315
|
-
const { discriminatorPropertyName, members } = unionNode;
|
|
2316
|
-
for (const member of members) {
|
|
2317
|
-
const intersectionNode = ast.narrowSchema(member, "intersection");
|
|
2318
|
-
if (!intersectionNode?.members) continue;
|
|
2319
|
-
let refNode = null;
|
|
2320
|
-
let objNode = null;
|
|
2321
|
-
for (const m of intersectionNode.members) {
|
|
2322
|
-
refNode ??= ast.narrowSchema(m, "ref");
|
|
2323
|
-
objNode ??= ast.narrowSchema(m, "object");
|
|
2324
|
-
}
|
|
2325
|
-
if (!refNode?.name || !objNode) continue;
|
|
2326
|
-
const prop = objNode.properties.find((p) => p.name === discriminatorPropertyName);
|
|
2327
|
-
const enumNode = prop ? ast.narrowSchema(prop.schema, "enum") : null;
|
|
2328
|
-
if (!enumNode?.enumValues?.length) continue;
|
|
2329
|
-
const enumValues = enumNode.enumValues.filter((v) => v !== null);
|
|
2330
|
-
if (!enumValues.length) continue;
|
|
2331
|
-
const existing = childMap.get(refNode.name);
|
|
2332
|
-
if (!existing) {
|
|
2333
|
-
childMap.set(refNode.name, {
|
|
2334
|
-
propertyName: discriminatorPropertyName,
|
|
2335
|
-
enumValues: [...enumValues]
|
|
2336
|
-
});
|
|
2337
|
-
continue;
|
|
2338
|
-
}
|
|
2339
|
-
existing.enumValues.push(...enumValues);
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2342
|
-
return childMap;
|
|
2343
|
-
}
|
|
2344
|
-
/**
|
|
2345
|
-
* Patches a single top-level `SchemaNode` with its discriminator entry (adds or replaces
|
|
2346
|
-
* the discriminant property). Used by the streaming path to apply patches inline per yield
|
|
2347
|
-
* without buffering all schemas.
|
|
2348
|
-
*/
|
|
2349
|
-
function patchDiscriminatorNode(node, entry) {
|
|
2350
|
-
const objectNode = ast.narrowSchema(node, "object");
|
|
2351
|
-
if (!objectNode) return node;
|
|
2352
|
-
const { propertyName, enumValues } = entry;
|
|
2353
|
-
const enumSchema = ast.factory.createSchema({
|
|
2354
|
-
type: "enum",
|
|
2355
|
-
enumValues
|
|
2356
|
-
});
|
|
2357
|
-
const newProp = ast.factory.createProperty({
|
|
2358
|
-
name: propertyName,
|
|
2359
|
-
required: true,
|
|
2360
|
-
schema: enumSchema
|
|
2361
|
-
});
|
|
2362
|
-
const existingIdx = objectNode.properties.findIndex((p) => p.name === propertyName);
|
|
2363
|
-
const newProperties = existingIdx >= 0 ? objectNode.properties.map((p, i) => i === existingIdx ? newProp : p) : [...objectNode.properties, newProp];
|
|
2364
|
-
return {
|
|
2365
|
-
...objectNode,
|
|
2366
|
-
properties: newProperties
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
|
-
//#endregion
|
|
2370
2406
|
//#region src/schemaDiagnostics.ts
|
|
2371
2407
|
/**
|
|
2372
2408
|
* Reports the advisory diagnostics (`KUBB_UNSUPPORTED_FORMAT`, `KUBB_DEPRECATED`) for one
|
|
@@ -2429,7 +2465,7 @@ function visit(node, pointer) {
|
|
|
2429
2465
|
*
|
|
2430
2466
|
* Only enums and objects are candidates, and object shapes that reference a circular schema are
|
|
2431
2467
|
* rejected to avoid hoisting recursive structures. Inline shapes reuse their context-derived
|
|
2432
|
-
* name (collision-resolved against existing component names)
|
|
2468
|
+
* name (collision-resolved against existing component names). Shapes without a name stay inline.
|
|
2433
2469
|
*/
|
|
2434
2470
|
function createDedupePlan({ schemaNodes, operationNodes, schemaNames, circularNames }) {
|
|
2435
2471
|
const circularSchemas = new Set(circularNames);
|