@kubb/adapter-oas 5.15.4 → 5.16.0
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/README.md +1 -1
- package/dist/index.cjs +288 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -8
- package/dist/index.d.ts +20 -8
- package/dist/index.js +289 -35
- package/dist/index.js.map +1 -1
- package/docs/options.md +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1104,6 +1104,10 @@ type SchemaObject$1 = {
|
|
|
1104
1104
|
* OAS 3.1: encoding applied to string content, such as `base64`.
|
|
1105
1105
|
*/
|
|
1106
1106
|
contentEncoding?: string;
|
|
1107
|
+
/**
|
|
1108
|
+
* OAS 3.1: schema of the decoded value a string with `contentMediaType` holds.
|
|
1109
|
+
*/
|
|
1110
|
+
contentSchema?: SchemaObject$1 | ReferenceObject$1;
|
|
1107
1111
|
$ref?: string;
|
|
1108
1112
|
/**
|
|
1109
1113
|
* OAS 3.1: positional tuple items, replacing the multi-item `items` array from OAS 3.0.
|
|
@@ -1134,13 +1138,18 @@ type Document = OpenAPIV3_1.Document & Record<string, unknown>;
|
|
|
1134
1138
|
*/
|
|
1135
1139
|
type OperationObject$1 = OpenAPIV3_1.OperationObject & Record<string, unknown>;
|
|
1136
1140
|
/**
|
|
1137
|
-
* Path item object holding the operations and shared parameters for a single URL path
|
|
1141
|
+
* Path item object holding the operations and shared parameters for a single URL path, plus the
|
|
1142
|
+
* OpenAPI 3.2 `additionalOperations` map for HTTP methods outside the fixed fields.
|
|
1138
1143
|
*/
|
|
1139
|
-
type PathItemObject$1 = OpenAPIV3_1.PathItemObject
|
|
1144
|
+
type PathItemObject$1 = OpenAPIV3_1.PathItemObject & {
|
|
1145
|
+
additionalOperations?: Record<string, OperationObject$1>;
|
|
1146
|
+
};
|
|
1140
1147
|
/**
|
|
1141
|
-
* Discriminator object for `oneOf`/`anyOf` schemas
|
|
1148
|
+
* Discriminator object for `oneOf`/`anyOf` schemas, with the OpenAPI 3.2 `defaultMapping` fallback schema.
|
|
1142
1149
|
*/
|
|
1143
|
-
type DiscriminatorObject$1 = OpenAPIV3_1.DiscriminatorObject
|
|
1150
|
+
type DiscriminatorObject$1 = OpenAPIV3_1.DiscriminatorObject & {
|
|
1151
|
+
defaultMapping?: string;
|
|
1152
|
+
};
|
|
1144
1153
|
/**
|
|
1145
1154
|
* OpenAPI reference object pointing to a schema definition via `$ref`.
|
|
1146
1155
|
*/
|
|
@@ -1150,9 +1159,11 @@ type ReferenceObject$1 = OpenAPIV3_1.ReferenceObject;
|
|
|
1150
1159
|
*/
|
|
1151
1160
|
type ResponseObject$1 = OpenAPIV3_1.ResponseObject;
|
|
1152
1161
|
/**
|
|
1153
|
-
* OpenAPI media type object
|
|
1162
|
+
* OpenAPI media type object, with the OpenAPI 3.2 `itemSchema` of one item of a stream.
|
|
1154
1163
|
*/
|
|
1155
|
-
type MediaTypeObject$1 = OpenAPIV3_1.MediaTypeObject
|
|
1164
|
+
type MediaTypeObject$1 = OpenAPIV3_1.MediaTypeObject & {
|
|
1165
|
+
itemSchema?: SchemaObject$1 | ReferenceObject$1;
|
|
1166
|
+
};
|
|
1156
1167
|
/**
|
|
1157
1168
|
* Selects which entry in the spec's `servers` array becomes the base URL and
|
|
1158
1169
|
* supplies values for its `{variable}` placeholders.
|
|
@@ -1186,7 +1197,8 @@ type AdapterOasOptions = {
|
|
|
1186
1197
|
/**
|
|
1187
1198
|
* Validate the OpenAPI spec with `@readme/openapi-parser` before parsing. Each problem is
|
|
1188
1199
|
* reported as a `KUBB_INVALID_SPEC` warning and generation continues. Set to `false` to skip the
|
|
1189
|
-
* check, which also makes generation faster on a large spec.
|
|
1200
|
+
* check, which also makes generation faster on a large spec. An OpenAPI 3.2 document is not
|
|
1201
|
+
* validated yet and gets one warning that says so.
|
|
1190
1202
|
*
|
|
1191
1203
|
* @default true
|
|
1192
1204
|
*/
|
|
@@ -1254,7 +1266,7 @@ type AdapterOas = AdapterFactoryOptions<'oas', AdapterOasOptions, AdapterOasReso
|
|
|
1254
1266
|
*/
|
|
1255
1267
|
export declare const adapterOasName = "oas";
|
|
1256
1268
|
/**
|
|
1257
|
-
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.
|
|
1269
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, 3.1, and 3.2 specifications. Reads the
|
|
1258
1270
|
* spec from `input` (a file path, URL, inline content, or parsed object), validates
|
|
1259
1271
|
* it, resolves the base URL, and converts every schema and operation into the
|
|
1260
1272
|
* universal AST that every downstream plugin consumes.
|
package/dist/index.d.ts
CHANGED
|
@@ -1104,6 +1104,10 @@ type SchemaObject$1 = {
|
|
|
1104
1104
|
* OAS 3.1: encoding applied to string content, such as `base64`.
|
|
1105
1105
|
*/
|
|
1106
1106
|
contentEncoding?: string;
|
|
1107
|
+
/**
|
|
1108
|
+
* OAS 3.1: schema of the decoded value a string with `contentMediaType` holds.
|
|
1109
|
+
*/
|
|
1110
|
+
contentSchema?: SchemaObject$1 | ReferenceObject$1;
|
|
1107
1111
|
$ref?: string;
|
|
1108
1112
|
/**
|
|
1109
1113
|
* OAS 3.1: positional tuple items, replacing the multi-item `items` array from OAS 3.0.
|
|
@@ -1134,13 +1138,18 @@ type Document = OpenAPIV3_1.Document & Record<string, unknown>;
|
|
|
1134
1138
|
*/
|
|
1135
1139
|
type OperationObject$1 = OpenAPIV3_1.OperationObject & Record<string, unknown>;
|
|
1136
1140
|
/**
|
|
1137
|
-
* Path item object holding the operations and shared parameters for a single URL path
|
|
1141
|
+
* Path item object holding the operations and shared parameters for a single URL path, plus the
|
|
1142
|
+
* OpenAPI 3.2 `additionalOperations` map for HTTP methods outside the fixed fields.
|
|
1138
1143
|
*/
|
|
1139
|
-
type PathItemObject$1 = OpenAPIV3_1.PathItemObject
|
|
1144
|
+
type PathItemObject$1 = OpenAPIV3_1.PathItemObject & {
|
|
1145
|
+
additionalOperations?: Record<string, OperationObject$1>;
|
|
1146
|
+
};
|
|
1140
1147
|
/**
|
|
1141
|
-
* Discriminator object for `oneOf`/`anyOf` schemas
|
|
1148
|
+
* Discriminator object for `oneOf`/`anyOf` schemas, with the OpenAPI 3.2 `defaultMapping` fallback schema.
|
|
1142
1149
|
*/
|
|
1143
|
-
type DiscriminatorObject$1 = OpenAPIV3_1.DiscriminatorObject
|
|
1150
|
+
type DiscriminatorObject$1 = OpenAPIV3_1.DiscriminatorObject & {
|
|
1151
|
+
defaultMapping?: string;
|
|
1152
|
+
};
|
|
1144
1153
|
/**
|
|
1145
1154
|
* OpenAPI reference object pointing to a schema definition via `$ref`.
|
|
1146
1155
|
*/
|
|
@@ -1150,9 +1159,11 @@ type ReferenceObject$1 = OpenAPIV3_1.ReferenceObject;
|
|
|
1150
1159
|
*/
|
|
1151
1160
|
type ResponseObject$1 = OpenAPIV3_1.ResponseObject;
|
|
1152
1161
|
/**
|
|
1153
|
-
* OpenAPI media type object
|
|
1162
|
+
* OpenAPI media type object, with the OpenAPI 3.2 `itemSchema` of one item of a stream.
|
|
1154
1163
|
*/
|
|
1155
|
-
type MediaTypeObject$1 = OpenAPIV3_1.MediaTypeObject
|
|
1164
|
+
type MediaTypeObject$1 = OpenAPIV3_1.MediaTypeObject & {
|
|
1165
|
+
itemSchema?: SchemaObject$1 | ReferenceObject$1;
|
|
1166
|
+
};
|
|
1156
1167
|
/**
|
|
1157
1168
|
* Selects which entry in the spec's `servers` array becomes the base URL and
|
|
1158
1169
|
* supplies values for its `{variable}` placeholders.
|
|
@@ -1186,7 +1197,8 @@ type AdapterOasOptions = {
|
|
|
1186
1197
|
/**
|
|
1187
1198
|
* Validate the OpenAPI spec with `@readme/openapi-parser` before parsing. Each problem is
|
|
1188
1199
|
* reported as a `KUBB_INVALID_SPEC` warning and generation continues. Set to `false` to skip the
|
|
1189
|
-
* check, which also makes generation faster on a large spec.
|
|
1200
|
+
* check, which also makes generation faster on a large spec. An OpenAPI 3.2 document is not
|
|
1201
|
+
* validated yet and gets one warning that says so.
|
|
1190
1202
|
*
|
|
1191
1203
|
* @default true
|
|
1192
1204
|
*/
|
|
@@ -1254,7 +1266,7 @@ type AdapterOas = AdapterFactoryOptions<'oas', AdapterOasOptions, AdapterOasReso
|
|
|
1254
1266
|
*/
|
|
1255
1267
|
export declare const adapterOasName = "oas";
|
|
1256
1268
|
/**
|
|
1257
|
-
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.
|
|
1269
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, 3.1, and 3.2 specifications. Reads the
|
|
1258
1270
|
* spec from `input` (a file path, URL, inline content, or parsed object), validates
|
|
1259
1271
|
* it, resolves the base URL, and converts every schema and operation into the
|
|
1260
1272
|
* universal AST that every downstream plugin consumes.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineExtension } from "@kubb/core";
|
|
2
|
-
import { Diagnostics, ast, childName, enumPrimitive, enumPropName, extractRefName, macroDiscriminatorEnum, macroEnumName,
|
|
2
|
+
import { Diagnostics, ast, childName, enumPrimitive, enumPropName, extractRefName, macroDedupeUnion, macroDiscriminatorEnum, macroEnumName, mergeAdjacentObjectsLazy } from "@kubb/kit";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { access, readFile } from "node:fs/promises";
|
|
5
5
|
import { upgrade } from "@scalar/openapi-upgrader";
|
|
@@ -19,7 +19,7 @@ var compatibility = {
|
|
|
19
19
|
"node": ">=22.22"
|
|
20
20
|
};
|
|
21
21
|
// package.json
|
|
22
|
-
var version = "5.
|
|
22
|
+
var version = "5.16.0";
|
|
23
23
|
// src/constants.ts
|
|
24
24
|
/**
|
|
25
25
|
* Default parser options applied when no explicit options are provided.
|
|
@@ -59,6 +59,17 @@ const SUPPORTED_METHODS = /* @__PURE__ */ new Set([
|
|
|
59
59
|
"query"
|
|
60
60
|
]);
|
|
61
61
|
/**
|
|
62
|
+
* Media types OpenAPI 3.2 reads as a stream of items (its "sequential media types"), each described by `itemSchema`.
|
|
63
|
+
*/
|
|
64
|
+
const STREAM_MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
65
|
+
"application/jsonl",
|
|
66
|
+
"application/x-ndjson",
|
|
67
|
+
"application/json-seq",
|
|
68
|
+
"application/geo+json-seq",
|
|
69
|
+
"text/event-stream",
|
|
70
|
+
"multipart/mixed"
|
|
71
|
+
]);
|
|
72
|
+
/**
|
|
62
73
|
* Set of JSON Schema keywords that prevent a schema fragment from being inlined during `allOf` flattening.
|
|
63
74
|
*
|
|
64
75
|
* A fragment that contains any of these keys carries structural meaning of its own and must stay as a separate
|
|
@@ -383,6 +394,14 @@ function isNullable(schema) {
|
|
|
383
394
|
return false;
|
|
384
395
|
}
|
|
385
396
|
/**
|
|
397
|
+
* Returns `true` for an OpenAPI 3.2 or later document. Swagger 2.0 and OpenAPI 3.0 documents are
|
|
398
|
+
* upgraded to 3.1 before parsing, so `openapi` is always set here.
|
|
399
|
+
*/
|
|
400
|
+
function isOpenApi32(document) {
|
|
401
|
+
const [major = 0, minor = 0] = String(document.openapi).split(".").map(Number);
|
|
402
|
+
return major > 3 || major === 3 && minor >= 2;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
386
405
|
* Returns the `x-*` keys of `source` as-is, or `undefined` when there are none.
|
|
387
406
|
*/
|
|
388
407
|
function pickExtensions(source) {
|
|
@@ -449,7 +468,8 @@ const jsonMimeFragments = [
|
|
|
449
468
|
* ```
|
|
450
469
|
*/
|
|
451
470
|
function isJsonMimeType(mimeType) {
|
|
452
|
-
|
|
471
|
+
const lower = mimeType.toLowerCase();
|
|
472
|
+
return jsonMimeFragments.some((fragment) => lower.includes(fragment));
|
|
453
473
|
}
|
|
454
474
|
// src/refs.ts
|
|
455
475
|
/**
|
|
@@ -873,6 +893,17 @@ function reportInvalidSpec(message) {
|
|
|
873
893
|
* ```
|
|
874
894
|
*/
|
|
875
895
|
async function validateDocument(document, { throwOnError = false } = {}) {
|
|
896
|
+
if (isOpenApi32(document)) {
|
|
897
|
+
const diagnostic = {
|
|
898
|
+
code: Diagnostics.code.invalidSpec,
|
|
899
|
+
severity: "warning",
|
|
900
|
+
message: "The document was not validated: `@readme/openapi-parser` does not support OpenAPI 3.2 yet.",
|
|
901
|
+
help: "Set `validate: false` on the adapter to skip this check until the validator supports OpenAPI 3.2."
|
|
902
|
+
};
|
|
903
|
+
if (throwOnError) throw new Diagnostics.Error(diagnostic);
|
|
904
|
+
Diagnostics.report(diagnostic);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
876
907
|
const { compileErrors, validate } = await import("@readme/openapi-parser");
|
|
877
908
|
let result;
|
|
878
909
|
try {
|
|
@@ -902,6 +933,7 @@ function reportValidationErrors({ errors, additionalErrors }) {
|
|
|
902
933
|
* validating in place where worker threads are missing, as in edge runtimes.
|
|
903
934
|
*/
|
|
904
935
|
async function validateDocumentInWorker(document) {
|
|
936
|
+
if (isOpenApi32(document)) return validateDocument(document);
|
|
905
937
|
let message;
|
|
906
938
|
try {
|
|
907
939
|
const { Worker } = await import("node:worker_threads");
|
|
@@ -1172,6 +1204,123 @@ function resolveServerUrl(server, overrides) {
|
|
|
1172
1204
|
}
|
|
1173
1205
|
return url;
|
|
1174
1206
|
}
|
|
1207
|
+
// src/model/mediaType.ts
|
|
1208
|
+
/**
|
|
1209
|
+
* The media type without its parameters, so `text/event-stream; charset=utf-8` matches the bare name.
|
|
1210
|
+
*/
|
|
1211
|
+
function baseMediaType(contentType) {
|
|
1212
|
+
return contentType.split(";")[0].trim().toLowerCase();
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Returns `true` for a media type read as a stream of items: one that OpenAPI 3.2 lists, or any that declares `itemSchema`.
|
|
1216
|
+
*/
|
|
1217
|
+
function isStream(contentType, mediaType) {
|
|
1218
|
+
return STREAM_MEDIA_TYPES.has(baseMediaType(contentType)) || mediaType?.itemSchema !== void 0;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Whether the media type is `text/event-stream`, with or without parameters.
|
|
1222
|
+
*/
|
|
1223
|
+
function isEventStream(contentType) {
|
|
1224
|
+
return baseMediaType(contentType) === "text/event-stream";
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Replaces a `data` string that holds JSON with its `contentSchema`, so the event reads as parsed.
|
|
1228
|
+
*/
|
|
1229
|
+
function withParsedData(schema, refs) {
|
|
1230
|
+
const data = schema.properties?.data;
|
|
1231
|
+
const resolved = data && isReference(data) ? refs.resolve(data.$ref, { report: false }) : data;
|
|
1232
|
+
if (!resolved?.contentSchema || !resolved.contentMediaType || !isJsonMimeType(resolved.contentMediaType)) return schema;
|
|
1233
|
+
return {
|
|
1234
|
+
...schema,
|
|
1235
|
+
properties: {
|
|
1236
|
+
...schema.properties,
|
|
1237
|
+
data: resolved.contentSchema
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Keywords next to `$ref` that only annotate it, so a reference that carries nothing else stays a reference.
|
|
1243
|
+
*/
|
|
1244
|
+
const REF_ANNOTATIONS = /* @__PURE__ */ new Set([
|
|
1245
|
+
"$ref",
|
|
1246
|
+
"summary",
|
|
1247
|
+
"description",
|
|
1248
|
+
"$comment"
|
|
1249
|
+
]);
|
|
1250
|
+
/**
|
|
1251
|
+
* Applies `extra` on top of `base` the way JSON Schema does: `properties` and `required` add up, other keywords of `extra` win.
|
|
1252
|
+
*/
|
|
1253
|
+
function mergeSchemas(base, extra) {
|
|
1254
|
+
const required = [.../* @__PURE__ */ new Set([...Array.isArray(base.required) ? base.required : [], ...Array.isArray(extra.required) ? extra.required : []])];
|
|
1255
|
+
return {
|
|
1256
|
+
type: "object",
|
|
1257
|
+
...base,
|
|
1258
|
+
...extra,
|
|
1259
|
+
properties: {
|
|
1260
|
+
...base.properties,
|
|
1261
|
+
...extra.properties
|
|
1262
|
+
},
|
|
1263
|
+
...required.length ? { required } : {}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Normalizes the OpenAPI 3.2 `text/event-stream` item schema into one schema per event.
|
|
1268
|
+
* - Keywords next to a `$ref` apply to the referenced schema.
|
|
1269
|
+
* - Shared `properties` merge into each inline `oneOf`/`anyOf` member.
|
|
1270
|
+
* - A JSON `data` string becomes its `contentSchema`.
|
|
1271
|
+
*/
|
|
1272
|
+
function toEventSchema(schema, refs) {
|
|
1273
|
+
if (isReference(schema)) {
|
|
1274
|
+
const siblings = Object.fromEntries(Object.entries(schema).filter(([key]) => !REF_ANNOTATIONS.has(key)));
|
|
1275
|
+
const resolved = Object.keys(siblings).length ? refs.resolve(schema.$ref, { report: false }) : null;
|
|
1276
|
+
return resolved ? toEventSchema(mergeSchemas(resolved, siblings), refs) : schema;
|
|
1277
|
+
}
|
|
1278
|
+
const key = schema.oneOf?.length ? "oneOf" : schema.anyOf?.length ? "anyOf" : void 0;
|
|
1279
|
+
if (!key) return withParsedData(schema, refs);
|
|
1280
|
+
const members = schema[key];
|
|
1281
|
+
const { properties, required, ...rest } = schema;
|
|
1282
|
+
const merge = !!properties && !members.some(isReference);
|
|
1283
|
+
const shared = {
|
|
1284
|
+
properties,
|
|
1285
|
+
required
|
|
1286
|
+
};
|
|
1287
|
+
return {
|
|
1288
|
+
...merge ? rest : schema,
|
|
1289
|
+
[key]: members.map((member) => isReference(member) ? member : withParsedData(merge ? mergeSchemas(shared, member) : member, refs))
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Whether the schema, following `$ref`s, or a composition member of it declares an `event` or `data` property.
|
|
1294
|
+
*/
|
|
1295
|
+
function declaresEventFields(schema, refs, seen) {
|
|
1296
|
+
if (isReference(schema)) {
|
|
1297
|
+
if (seen.has(schema.$ref)) return false;
|
|
1298
|
+
seen.add(schema.$ref);
|
|
1299
|
+
const resolved = refs.resolve(schema.$ref, { report: false });
|
|
1300
|
+
return !!resolved && declaresEventFields(resolved, refs, seen);
|
|
1301
|
+
}
|
|
1302
|
+
if (schema.properties && (Object.hasOwn(schema.properties, "event") || Object.hasOwn(schema.properties, "data"))) return true;
|
|
1303
|
+
return [
|
|
1304
|
+
...schema.oneOf ?? [],
|
|
1305
|
+
...schema.anyOf ?? [],
|
|
1306
|
+
...schema.allOf ?? []
|
|
1307
|
+
].some((member) => declaresEventFields(member, refs, seen));
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Returns `'event'` when the item declares `event` or `data`, directly or in a composition member, else `'data'`.
|
|
1311
|
+
*/
|
|
1312
|
+
function getItemShape(schema, refs) {
|
|
1313
|
+
return declaresEventFields(schema, refs, /* @__PURE__ */ new Set()) ? "event" : "data";
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Returns the raw item schema: `itemSchema`, or `schema` on `text/event-stream` as 3.0 and 3.1 documents write one event.
|
|
1317
|
+
*/
|
|
1318
|
+
function getItemSchema({ contentType, mediaType, refs, document }) {
|
|
1319
|
+
if (!mediaType || !isStream(contentType, mediaType)) return null;
|
|
1320
|
+
const raw = mediaType.itemSchema ?? (isEventStream(contentType) && !isOpenApi32(document) ? mediaType.schema : void 0);
|
|
1321
|
+
if (!raw) return null;
|
|
1322
|
+
return isEventStream(contentType) ? toEventSchema(raw, refs) : raw;
|
|
1323
|
+
}
|
|
1175
1324
|
// src/emit/schemaShape.ts
|
|
1176
1325
|
/**
|
|
1177
1326
|
* Whether `format` maps to a dedicated type; `false` is what `KUBB_UNSUPPORTED_FORMAT` flags.
|
|
@@ -1284,6 +1433,13 @@ function slugify(value) {
|
|
|
1284
1433
|
return value.replace(/[^a-zA-Z0-9]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
1285
1434
|
}
|
|
1286
1435
|
/**
|
|
1436
|
+
* Returns the operation's JSON pointer, under `additionalOperations` for an OpenAPI 3.2 method outside the fixed fields.
|
|
1437
|
+
*/
|
|
1438
|
+
function getOperationPointer({ path, method }) {
|
|
1439
|
+
const pathPointer = `#/paths/${escapePointerToken(path)}`;
|
|
1440
|
+
return SUPPORTED_METHODS.has(method.toLowerCase()) ? `${pathPointer}/${method.toLowerCase()}` : `${pathPointer}/additionalOperations/${escapePointerToken(method)}`;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1287
1443
|
* Returns the operation's `operationId`, falling back to `<method>_<slugified-path>` when absent.
|
|
1288
1444
|
*/
|
|
1289
1445
|
function getOperationId({ path, method, schema }) {
|
|
@@ -1336,7 +1492,7 @@ function getParameters({ operation, refs }) {
|
|
|
1336
1492
|
}] : [];
|
|
1337
1493
|
});
|
|
1338
1494
|
const pathPointer = `#/paths/${escapePointerToken(operation.path)}`;
|
|
1339
|
-
const operationParams = resolveParams(operation.schema?.parameters || [],
|
|
1495
|
+
const operationParams = resolveParams(operation.schema?.parameters || [], getOperationPointer(operation));
|
|
1340
1496
|
const pathLevelParams = resolveParams(operation.pathItem.parameters ?? [], pathPointer);
|
|
1341
1497
|
const paramMap = /* @__PURE__ */ new Map();
|
|
1342
1498
|
for (const entry of [...pathLevelParams, ...operationParams]) if (entry.param.name && entry.param.in) paramMap.set(`${entry.param.in}:${entry.param.name}`, entry);
|
|
@@ -1369,17 +1525,59 @@ function getEncoding(encoding) {
|
|
|
1369
1525
|
});
|
|
1370
1526
|
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
1371
1527
|
}
|
|
1372
|
-
/**
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1528
|
+
/**
|
|
1529
|
+
* Returns the media type entry for `contentType`, resolving a `$ref` into `components.mediaTypes`. `null` when absent.
|
|
1530
|
+
*/
|
|
1531
|
+
function getMediaType({ content, contentType, refs }) {
|
|
1532
|
+
return contentType ? refs.deref(content?.[contentType]) : null;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Returns the schema for one media type of a `content` map, with the binary fallback for an emptied non-JSON
|
|
1536
|
+
* entry. For a stream this is the item schema, so `schema: string` next to `itemSchema` types one item.
|
|
1537
|
+
*/
|
|
1538
|
+
function getBodySchema({ content, contentType, refs, document }) {
|
|
1539
|
+
const entry = getMediaType({
|
|
1540
|
+
content,
|
|
1541
|
+
contentType,
|
|
1542
|
+
refs
|
|
1543
|
+
});
|
|
1544
|
+
if (!contentType || !entry) return null;
|
|
1545
|
+
const schema = getItemSchema({
|
|
1546
|
+
contentType,
|
|
1547
|
+
mediaType: entry,
|
|
1548
|
+
refs,
|
|
1549
|
+
document
|
|
1550
|
+
}) ?? entry.schema;
|
|
1551
|
+
const binary = entry.itemSchema ? void 0 : getBinaryFallbackSchema(contentType, schema);
|
|
1377
1552
|
if (binary) return binary;
|
|
1378
|
-
return
|
|
1553
|
+
return schema ? refs.derefKeepingRef(schema) : null;
|
|
1554
|
+
}
|
|
1555
|
+
/** The `stream` field of a content node, set when the media type is a stream. `node` is the parsed item schema. */
|
|
1556
|
+
function getStreamField({ contentType, mediaType, refs, document, node }) {
|
|
1557
|
+
if (!isStream(contentType, mediaType)) return {};
|
|
1558
|
+
const raw = getItemSchema({
|
|
1559
|
+
contentType,
|
|
1560
|
+
mediaType,
|
|
1561
|
+
refs,
|
|
1562
|
+
document
|
|
1563
|
+
});
|
|
1564
|
+
return { stream: {
|
|
1565
|
+
...raw ? { schema: node } : {},
|
|
1566
|
+
...raw && isEventStream(contentType) ? { shape: getItemShape(raw, refs) } : {}
|
|
1567
|
+
} };
|
|
1568
|
+
}
|
|
1569
|
+
/** The media type keyword the body schema was read from, for the diagnostic pointer. */
|
|
1570
|
+
function schemaKeyword(mediaType) {
|
|
1571
|
+
return mediaType?.itemSchema ? "itemSchema" : "schema";
|
|
1572
|
+
}
|
|
1573
|
+
/** Pointer of a media type entry, following a `$ref` into `components.mediaTypes` so a diagnostic lands on the source. */
|
|
1574
|
+
function mediaTypePointer(content, contentType, fallback) {
|
|
1575
|
+
const entry = content?.[contentType];
|
|
1576
|
+
return isReference(entry) ? entry.$ref : fallback;
|
|
1379
1577
|
}
|
|
1380
1578
|
/**
|
|
1381
|
-
* Builds an `Operation` for every supported HTTP method
|
|
1382
|
-
* `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
1579
|
+
* Builds an `Operation` for every supported HTTP method and every OpenAPI 3.2 `additionalOperations` entry on
|
|
1580
|
+
* every path, in document order. `x-` path keys and unresolvable path-item `$ref`s are skipped.
|
|
1383
1581
|
*/
|
|
1384
1582
|
function getOperations(document, refs) {
|
|
1385
1583
|
const operations = [];
|
|
@@ -1401,6 +1599,12 @@ function getOperations(document, refs) {
|
|
|
1401
1599
|
pathItem
|
|
1402
1600
|
});
|
|
1403
1601
|
}
|
|
1602
|
+
for (const [method, schema] of Object.entries(pathItem.additionalOperations ?? {})) if (schema && typeof schema === "object" && !SUPPORTED_METHODS.has(method.toLowerCase())) operations.push({
|
|
1603
|
+
path,
|
|
1604
|
+
method,
|
|
1605
|
+
schema,
|
|
1606
|
+
pathItem
|
|
1607
|
+
});
|
|
1404
1608
|
}
|
|
1405
1609
|
return operations;
|
|
1406
1610
|
}
|
|
@@ -1414,10 +1618,15 @@ function collectPropertyKeysByFlag(schema, flag) {
|
|
|
1414
1618
|
}
|
|
1415
1619
|
return keys.length ? keys : null;
|
|
1416
1620
|
}
|
|
1417
|
-
|
|
1621
|
+
/** Converts one parameter into a `ParameterNode`, naming an inline schema after the operation and the parameter. */
|
|
1622
|
+
function parseParameter({ param, pointer, parentName, options, parseSchema, refs }) {
|
|
1418
1623
|
const schemaName = parentName && param.name ? pascalCase(`${parentName} ${param.name}`) : void 0;
|
|
1419
1624
|
const contentType = param.schema ? void 0 : Object.keys(param.content ?? {})[0];
|
|
1420
|
-
const rawSchema = param.schema ?? (
|
|
1625
|
+
const rawSchema = param.schema ?? getMediaType({
|
|
1626
|
+
content: param.content,
|
|
1627
|
+
contentType,
|
|
1628
|
+
refs
|
|
1629
|
+
})?.schema;
|
|
1421
1630
|
const schema = rawSchema ? parseSchema({
|
|
1422
1631
|
schema: rawSchema,
|
|
1423
1632
|
name: schemaName
|
|
@@ -1440,16 +1649,17 @@ function parseParameter({ param, pointer, parentName, options, parseSchema }) {
|
|
|
1440
1649
|
* Converts an OAS `Operation` into an `OperationNode`.
|
|
1441
1650
|
*/
|
|
1442
1651
|
function parseOperation({ operation, ...ctx }) {
|
|
1443
|
-
const { refs, contentType, options, parseSchema } = ctx;
|
|
1652
|
+
const { document, refs, contentType, options, parseSchema } = ctx;
|
|
1444
1653
|
const operationId = getOperationId(operation);
|
|
1445
1654
|
const operationName = operationId ? pascalCase(operationId) : void 0;
|
|
1446
|
-
const pointer =
|
|
1655
|
+
const pointer = getOperationPointer(operation);
|
|
1447
1656
|
const parameters = getParameters({
|
|
1448
1657
|
operation,
|
|
1449
1658
|
refs
|
|
1450
1659
|
}).map((entry) => parseParameter({
|
|
1451
1660
|
options,
|
|
1452
1661
|
parseSchema,
|
|
1662
|
+
refs,
|
|
1453
1663
|
...entry,
|
|
1454
1664
|
parentName: operationName
|
|
1455
1665
|
}));
|
|
@@ -1463,20 +1673,33 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1463
1673
|
const schema = getBodySchema({
|
|
1464
1674
|
content: body?.content,
|
|
1465
1675
|
contentType: ct,
|
|
1466
|
-
refs
|
|
1676
|
+
refs,
|
|
1677
|
+
document
|
|
1467
1678
|
});
|
|
1468
1679
|
if (!schema) return [];
|
|
1469
|
-
const
|
|
1680
|
+
const mediaType = getMediaType({
|
|
1681
|
+
content: body?.content,
|
|
1682
|
+
contentType: ct,
|
|
1683
|
+
refs
|
|
1684
|
+
});
|
|
1685
|
+
const node = ast.optionality(parseSchema({
|
|
1470
1686
|
schema,
|
|
1471
1687
|
name: requestBodyName
|
|
1472
|
-
});
|
|
1473
|
-
scanSchema(node, `${pointer}/requestBody/content/${escapePointerToken(ct)}
|
|
1474
|
-
const encoding = getEncoding(
|
|
1688
|
+
}), bodyRequired);
|
|
1689
|
+
scanSchema(node, `${mediaTypePointer(body?.content, ct, `${pointer}/requestBody/content/${escapePointerToken(ct)}`)}/${schemaKeyword(mediaType)}`);
|
|
1690
|
+
const encoding = getEncoding(mediaType?.encoding);
|
|
1475
1691
|
return [ast.factory.createContent({
|
|
1476
1692
|
contentType: ct,
|
|
1477
|
-
schema:
|
|
1693
|
+
schema: node,
|
|
1478
1694
|
keysToOmit: collectPropertyKeysByFlag(schema, "readOnly"),
|
|
1479
|
-
...encoding && { encoding }
|
|
1695
|
+
...encoding && { encoding },
|
|
1696
|
+
...getStreamField({
|
|
1697
|
+
contentType: ct,
|
|
1698
|
+
mediaType,
|
|
1699
|
+
refs,
|
|
1700
|
+
document,
|
|
1701
|
+
node
|
|
1702
|
+
})
|
|
1480
1703
|
})];
|
|
1481
1704
|
});
|
|
1482
1705
|
const requestBody = content.length > 0 || body?.description ? {
|
|
@@ -1494,6 +1717,12 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1494
1717
|
const responseName = operationName ? `${operationName}Status${statusCode}` : void 0;
|
|
1495
1718
|
const parseEntrySchema = (ct) => {
|
|
1496
1719
|
const raw = getBodySchema({
|
|
1720
|
+
content: response?.content,
|
|
1721
|
+
contentType: ct,
|
|
1722
|
+
refs,
|
|
1723
|
+
document
|
|
1724
|
+
});
|
|
1725
|
+
const mediaType = getMediaType({
|
|
1497
1726
|
content: response?.content,
|
|
1498
1727
|
contentType: ct,
|
|
1499
1728
|
refs
|
|
@@ -1502,10 +1731,17 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1502
1731
|
schema: raw,
|
|
1503
1732
|
name: responseName
|
|
1504
1733
|
}) : ast.factory.createSchema({ type: options.emptySchemaType });
|
|
1505
|
-
if (raw && ct) scanSchema(node, `${pointer}/responses/${statusCode}/content/${escapePointerToken(ct)}
|
|
1734
|
+
if (raw && ct) scanSchema(node, `${mediaTypePointer(response?.content, ct, `${pointer}/responses/${statusCode}/content/${escapePointerToken(ct)}`)}/${schemaKeyword(mediaType)}`);
|
|
1506
1735
|
return {
|
|
1507
1736
|
schema: node,
|
|
1508
|
-
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly")
|
|
1737
|
+
keysToOmit: collectPropertyKeysByFlag(raw, "writeOnly"),
|
|
1738
|
+
...ct ? getStreamField({
|
|
1739
|
+
contentType: ct,
|
|
1740
|
+
mediaType,
|
|
1741
|
+
refs,
|
|
1742
|
+
document,
|
|
1743
|
+
node
|
|
1744
|
+
}) : {}
|
|
1509
1745
|
};
|
|
1510
1746
|
};
|
|
1511
1747
|
const responseContent = (contentType ? [contentType] : Object.keys(response?.content ?? {})).map((ct) => ast.factory.createContent({
|
|
@@ -1536,7 +1772,7 @@ function parseOperation({ operation, ...ctx }) {
|
|
|
1536
1772
|
pointer,
|
|
1537
1773
|
operationId,
|
|
1538
1774
|
protocol: "http",
|
|
1539
|
-
method: operation.method.toUpperCase(),
|
|
1775
|
+
method: SUPPORTED_METHODS.has(operation.method) ? operation.method.toUpperCase() : operation.method,
|
|
1540
1776
|
path: operation.path,
|
|
1541
1777
|
tags: Array.isArray(operation.schema.tags) ? operation.schema.tags.map(String) : [],
|
|
1542
1778
|
summary: pickDoc("summary") || void 0,
|
|
@@ -1631,6 +1867,14 @@ function createDiscriminantNode({ propertyName, values }) {
|
|
|
1631
1867
|
});
|
|
1632
1868
|
}
|
|
1633
1869
|
/**
|
|
1870
|
+
* Returns the `$ref` of an OpenAPI 3.2 `defaultMapping`, written as a schema name or a `$ref`; `undefined` when none.
|
|
1871
|
+
*/
|
|
1872
|
+
function getDefaultMappingRef(discriminator) {
|
|
1873
|
+
const value = discriminator?.defaultMapping;
|
|
1874
|
+
if (!value) return void 0;
|
|
1875
|
+
return value.includes("/") ? value : `${SCHEMA_REF_PREFIX}${value}`;
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1634
1878
|
* Returns every discriminator key whose mapping value matches `ref`, in mapping order.
|
|
1635
1879
|
*
|
|
1636
1880
|
* A mapping may point several keys at the same schema, in which case the member has to be
|
|
@@ -1726,13 +1970,15 @@ function narrowUnionMembers({ unionMembers, discriminator, sharedPropertiesNode,
|
|
|
1726
1970
|
}
|
|
1727
1971
|
return constrains(variant) ? null : value;
|
|
1728
1972
|
}
|
|
1973
|
+
const defaultRef = getDefaultMappingRef(discriminator);
|
|
1729
1974
|
return unionMembers.map((s) => {
|
|
1730
1975
|
const ref = isReference(s) ? s.$ref : void 0;
|
|
1731
|
-
const
|
|
1976
|
+
const isDefault = ref !== void 0 && ref === defaultRef;
|
|
1977
|
+
const mappedValues = isDefault ? [] : findDiscriminators({
|
|
1732
1978
|
mapping: discriminator?.mapping,
|
|
1733
1979
|
ref
|
|
1734
1980
|
});
|
|
1735
|
-
const implicitValue = mappedValues.length ? null : implicitDiscriminantValue(s);
|
|
1981
|
+
const implicitValue = mappedValues.length || isDefault ? null : implicitDiscriminantValue(s);
|
|
1736
1982
|
const propertyType = propertyTypeOf({
|
|
1737
1983
|
schema: s,
|
|
1738
1984
|
propertyName: discriminator?.propertyName ?? "",
|
|
@@ -1777,7 +2023,8 @@ function extractDiscriminatedAllOfMembers({ allOfMembers, name, refs }) {
|
|
|
1777
2023
|
const parentUnion = deref?.oneOf ?? deref?.anyOf;
|
|
1778
2024
|
if (!deref || !discriminator || isDiscriminator(deref) && !parentUnion) return true;
|
|
1779
2025
|
const childRef = `${SCHEMA_REF_PREFIX}${name}`;
|
|
1780
|
-
const
|
|
2026
|
+
const isDefault = childRef === getDefaultMappingRef(discriminator);
|
|
2027
|
+
const values = isDefault ? [] : findDiscriminators({
|
|
1781
2028
|
mapping: discriminator.mapping,
|
|
1782
2029
|
ref: childRef
|
|
1783
2030
|
});
|
|
@@ -1800,7 +2047,7 @@ function extractDiscriminatedAllOfMembers({ allOfMembers, name, refs }) {
|
|
|
1800
2047
|
})
|
|
1801
2048
|
});
|
|
1802
2049
|
}
|
|
1803
|
-
return !(parentUnion && (values.length > 0 || parentUnion.some((member) => isReference(member) && member.$ref === childRef)));
|
|
2050
|
+
return !(parentUnion && (isDefault || values.length > 0 || parentUnion.some((member) => isReference(member) && member.$ref === childRef)));
|
|
1804
2051
|
}),
|
|
1805
2052
|
discriminantValues
|
|
1806
2053
|
};
|
|
@@ -2122,10 +2369,12 @@ function convertUnion(context) {
|
|
|
2122
2369
|
nullable,
|
|
2123
2370
|
defaultValue
|
|
2124
2371
|
};
|
|
2125
|
-
const unionMembers = resolveUnionMembers([...schema.oneOf ?? [], ...schema.anyOf ?? []], schema.properties ?? {});
|
|
2126
|
-
const strategy = schema.oneOf ? "one" : "any";
|
|
2127
|
-
const explicitDiscriminatorPropertyName = isDiscriminator(schema) ? schema.discriminator.propertyName : void 0;
|
|
2128
2372
|
const discriminator = isDiscriminator(schema) ? schema.discriminator : void 0;
|
|
2373
|
+
const declaredMembers = [...schema.oneOf ?? [], ...schema.anyOf ?? []];
|
|
2374
|
+
const defaultRef = getDefaultMappingRef(discriminator);
|
|
2375
|
+
const unionMembers = resolveUnionMembers(defaultRef && !declaredMembers.some((member) => isReference(member) && member.$ref === defaultRef) ? [...declaredMembers, { $ref: defaultRef }] : declaredMembers, schema.properties ?? {});
|
|
2376
|
+
const strategy = schema.oneOf ? "one" : "any";
|
|
2377
|
+
const explicitDiscriminatorPropertyName = discriminator?.propertyName;
|
|
2129
2378
|
const { oneOf: _o, anyOf: _a, discriminator: _d, ...memberBaseSchema } = schema;
|
|
2130
2379
|
const sharedPropertiesNode = schema.properties ? parse({
|
|
2131
2380
|
schema: memberBaseSchema,
|
|
@@ -2178,7 +2427,7 @@ function convertUnion(context) {
|
|
|
2178
2427
|
members,
|
|
2179
2428
|
discriminatorPropertyName: explicitDiscriminatorPropertyName ?? ast.inferDiscriminatorPropertyName(members)
|
|
2180
2429
|
});
|
|
2181
|
-
return ast.applyMacros(unionNode, [
|
|
2430
|
+
return ast.applyMacros(unionNode, [macroDedupeUnion], { depth: "shallow" });
|
|
2182
2431
|
}
|
|
2183
2432
|
/**
|
|
2184
2433
|
* Converts an OAS 3.1 multi-type array (e.g. `type: ['string', 'number']`) into a `UnionSchemaNode`.
|
|
@@ -2655,6 +2904,10 @@ const schemaRules = [
|
|
|
2655
2904
|
primitive: "string"
|
|
2656
2905
|
})
|
|
2657
2906
|
},
|
|
2907
|
+
{
|
|
2908
|
+
match: ({ schema, type }) => !type && !schema.enum?.length && (schema.contentMediaType !== void 0 || schema.contentSchema !== void 0),
|
|
2909
|
+
convert: convertString
|
|
2910
|
+
},
|
|
2658
2911
|
{
|
|
2659
2912
|
match: ({ schema, type }) => !type && (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0),
|
|
2660
2913
|
convert: convertString
|
|
@@ -2949,7 +3202,7 @@ const locate = (source, location) => {
|
|
|
2949
3202
|
*/
|
|
2950
3203
|
const adapterOasName = "oas";
|
|
2951
3204
|
/**
|
|
2952
|
-
* Default Kubb adapter for OpenAPI 2.0, 3.0, and 3.
|
|
3205
|
+
* Default Kubb adapter for OpenAPI 2.0, 3.0, 3.1, and 3.2 specifications. Reads the
|
|
2953
3206
|
* spec from `input` (a file path, URL, inline content, or parsed object), validates
|
|
2954
3207
|
* it, resolves the base URL, and converts every schema and operation into the
|
|
2955
3208
|
* universal AST that every downstream plugin consumes.
|
|
@@ -3023,6 +3276,7 @@ const adapterOas = defineExtension({
|
|
|
3023
3276
|
const discriminatorChildMap = discriminatorParentNodes.length > 0 ? buildDiscriminatorChildMap(discriminatorParentNodes) : null;
|
|
3024
3277
|
const operationNodes = getOperations(document, refs).map((operation) => parseOperation({
|
|
3025
3278
|
operation,
|
|
3279
|
+
document,
|
|
3026
3280
|
refs,
|
|
3027
3281
|
contentType,
|
|
3028
3282
|
options: parserOptions,
|