@moccona/apicodegen 0.0.9 → 0.0.10
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/bin/cli.cjs +12 -12
- package/npm/index.cjs +609 -784
- package/npm/index.cjs.map +1 -1
- package/npm/index.d.cts +24 -5
- package/npm/index.d.cts.map +1 -1
- package/npm/index.d.mts +24 -5
- package/npm/index.d.mts.map +1 -1
- package/npm/index.mjs +609 -784
- package/npm/index.mjs.map +1 -1
- package/npm/vite/index.d.mts.map +1 -1
- package/npm/vite/index.mjs +371 -555
- package/npm/vite/index.mjs.map +1 -1
- package/package.json +1 -1
package/npm/vite/index.mjs
CHANGED
|
@@ -240,29 +240,6 @@ var ApicodegenError = class ApicodegenError extends Error {
|
|
|
240
240
|
}
|
|
241
241
|
};
|
|
242
242
|
/**
|
|
243
|
-
* ANSI color codes for terminal output
|
|
244
|
-
*/
|
|
245
|
-
const Colors = {
|
|
246
|
-
reset: "\x1B[0m",
|
|
247
|
-
bold: "\x1B[1m",
|
|
248
|
-
red: "\x1B[31m",
|
|
249
|
-
green: "\x1B[32m",
|
|
250
|
-
yellow: "\x1B[33m",
|
|
251
|
-
blue: "\x1B[34m",
|
|
252
|
-
cyan: "\x1B[36m",
|
|
253
|
-
gray: "\x1B[90m",
|
|
254
|
-
brightRed: "\x1B[91m",
|
|
255
|
-
brightGreen: "\x1B[92m"
|
|
256
|
-
};
|
|
257
|
-
/**
|
|
258
|
-
* Format error for CLI output
|
|
259
|
-
*/
|
|
260
|
-
function formatError(error, verbose = false) {
|
|
261
|
-
if (error instanceof ApicodegenError) return error.toString(verbose);
|
|
262
|
-
if (error instanceof Error) return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${error.message}${verbose && error.stack ? `\n\n${Colors.gray}${error.stack}${Colors.reset}` : ""}`;
|
|
263
|
-
return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${String(error)}`;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
243
|
* Create error with common patterns
|
|
267
244
|
*/
|
|
268
245
|
const createErrors = {
|
|
@@ -399,6 +376,68 @@ function isApicodegenError(error) {
|
|
|
399
376
|
return error instanceof ApicodegenError;
|
|
400
377
|
}
|
|
401
378
|
//#endregion
|
|
379
|
+
//#region src/core/logger.ts
|
|
380
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
381
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
382
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
383
|
+
const blue = (s) => `\x1b[34m${s}\x1b[0m`;
|
|
384
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
385
|
+
const magenta = (s) => `\x1b[35m${s}\x1b[0m`;
|
|
386
|
+
const gray = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
387
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
388
|
+
const logger$1 = {
|
|
389
|
+
success(msg) {
|
|
390
|
+
console.log(`${green("✓")} ${msg}`);
|
|
391
|
+
},
|
|
392
|
+
error(err, verbose = false) {
|
|
393
|
+
if (isApicodegenError(err)) console.error(`${red("✗")} ${err.toString(verbose)}`);
|
|
394
|
+
else if (err instanceof Error) {
|
|
395
|
+
const msg = `Error: ${err.message}`;
|
|
396
|
+
console.error(`${red("✗")} ${msg}${verbose && err.stack ? `\n${gray(err.stack)}` : ""}`);
|
|
397
|
+
} else console.error(`${red("✗")} ${String(err)}`);
|
|
398
|
+
},
|
|
399
|
+
info(msg) {
|
|
400
|
+
console.log(`${blue("ℹ")} ${msg}`);
|
|
401
|
+
},
|
|
402
|
+
warn(msg) {
|
|
403
|
+
console.log(`${yellow("⚠")} ${msg}`);
|
|
404
|
+
},
|
|
405
|
+
loading(msg) {
|
|
406
|
+
console.log(`${yellow("🔄")} ${msg}`);
|
|
407
|
+
},
|
|
408
|
+
watching(msg) {
|
|
409
|
+
console.log(`${magenta("⟳")} ${msg}`);
|
|
410
|
+
},
|
|
411
|
+
fileChange(filePath) {
|
|
412
|
+
console.log(`${yellow("↓")} ${filePath}`);
|
|
413
|
+
},
|
|
414
|
+
fileAdd(filePath) {
|
|
415
|
+
console.log(`${green("+")} ${filePath}`);
|
|
416
|
+
},
|
|
417
|
+
shutdown() {
|
|
418
|
+
console.log(`\n${gray("👋 Shutting down...")}`);
|
|
419
|
+
},
|
|
420
|
+
divider(width = 50) {
|
|
421
|
+
console.log(`${bold(cyan("─".repeat(width)))}`);
|
|
422
|
+
},
|
|
423
|
+
heading(text, mode, width = 50) {
|
|
424
|
+
console.log(`${bold(cyan("─".repeat(width)))}`);
|
|
425
|
+
console.log(`${bold(cyan(text))}`);
|
|
426
|
+
console.log(`${gray("Mode:")} ${mode || "unknown"}`);
|
|
427
|
+
console.log(`${bold(cyan("─".repeat(width)))}`);
|
|
428
|
+
},
|
|
429
|
+
item(label, color = "green") {
|
|
430
|
+
const icon = color === "green" ? "✓" : color === "red" ? "✗" : "⚠";
|
|
431
|
+
console.log(`${color === "green" ? green(icon) : color === "red" ? red(icon) : yellow(icon)} ${label}`);
|
|
432
|
+
},
|
|
433
|
+
summary(stats) {
|
|
434
|
+
const { succeeded, failed, endpoints, schemas, duration } = stats;
|
|
435
|
+
const label = `API Code Gen - Complete (${succeeded} succeeded${failed > 0 ? `, ${failed} failed` : ""}, ${endpoints} endpoints, ${schemas} schemas, ${duration}ms)`;
|
|
436
|
+
if (failed === 0) console.log(`${green("✓")} ${label}`);
|
|
437
|
+
else console.log(`${yellow("⚠")} ${label}`);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
//#endregion
|
|
402
441
|
//#region src/core/base/Adaptor.ts
|
|
403
442
|
/**
|
|
404
443
|
* Base adapter for tool
|
|
@@ -764,10 +803,19 @@ var Generator = class Generator {
|
|
|
764
803
|
return createPrinter().printFile(sourceFile);
|
|
765
804
|
}
|
|
766
805
|
static async write(code, filepath) {
|
|
806
|
+
const { mkdir } = await import("node:fs/promises");
|
|
807
|
+
const { dirname } = await import("node:path");
|
|
767
808
|
try {
|
|
809
|
+
await mkdir(dirname(filepath), { recursive: true });
|
|
768
810
|
await writeFile(filepath, code);
|
|
769
811
|
} catch (error) {
|
|
770
|
-
|
|
812
|
+
throw new ApicodegenError({
|
|
813
|
+
code: ErrorCodes.OUTPUT_DIR_MISSING,
|
|
814
|
+
message: "Failed to write generated code to output file",
|
|
815
|
+
location: filepath,
|
|
816
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
817
|
+
suggestions: ["Verify the output directory path is writable", "Check that the parent directory exists or can be created"]
|
|
818
|
+
});
|
|
771
819
|
}
|
|
772
820
|
}
|
|
773
821
|
/**
|
|
@@ -1104,12 +1152,12 @@ var FetchAdapter = class extends Adapter {
|
|
|
1104
1152
|
}
|
|
1105
1153
|
};
|
|
1106
1154
|
//#endregion
|
|
1107
|
-
//#region src/openapi/
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1155
|
+
//#region src/openapi/VersionedProvider.ts
|
|
1156
|
+
/**
|
|
1157
|
+
* Abstract base class. Subclasses implement the four container accessors
|
|
1158
|
+
* and a version tag. The shared logic lives here.
|
|
1159
|
+
*/
|
|
1160
|
+
var VersionedProvider = class {
|
|
1113
1161
|
/**
|
|
1114
1162
|
* Resolves a path $ref to the actual path object.
|
|
1115
1163
|
*/
|
|
@@ -1121,281 +1169,37 @@ var V2 = class {
|
|
|
1121
1169
|
* Is array schema.
|
|
1122
1170
|
*/
|
|
1123
1171
|
isOpenAPIArraySchema(schema) {
|
|
1124
|
-
return typeof schema === "object" && schema.type === "array";
|
|
1172
|
+
return typeof schema === "object" && schema !== null && schema.type === "array";
|
|
1125
1173
|
}
|
|
1126
1174
|
/**
|
|
1127
|
-
* OpenAPI schema to base schema.
|
|
1175
|
+
* OpenAPI schema to base schema. Override for version-specific quirks
|
|
1176
|
+
* (V3_1 throws on missing ref; V3 returns `{ type: 'unknown' }`).
|
|
1128
1177
|
*/
|
|
1129
1178
|
getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
|
|
1130
1179
|
let refName = "";
|
|
1131
1180
|
if (Base.isRef(schema)) {
|
|
1132
|
-
refName =
|
|
1181
|
+
refName = this.formatRefName(Base.ref2name(schema.$ref));
|
|
1133
1182
|
if (reserveRef) return { type: upLevelSchemaKey + refName };
|
|
1134
|
-
|
|
1135
|
-
|
|
1183
|
+
const resolvedSchema = this.getSchemaContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1184
|
+
if (!resolvedSchema) return { type: "unknown" };
|
|
1185
|
+
schema = resolvedSchema;
|
|
1136
1186
|
}
|
|
1137
1187
|
return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
|
|
1138
1188
|
}
|
|
1139
1189
|
/**
|
|
1140
|
-
*
|
|
1141
|
-
|
|
1142
|
-
toBaseSchema(schema, enums = [], schemaKey = "", upLevelSchemaKey = "") {
|
|
1143
|
-
if (!schema) return { type: "unknown" };
|
|
1144
|
-
if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
|
|
1145
|
-
if (this.isOpenAPIArraySchema(schema)) {
|
|
1146
|
-
const { type, description, items, required } = schema;
|
|
1147
|
-
return {
|
|
1148
|
-
type,
|
|
1149
|
-
required: !!required,
|
|
1150
|
-
description,
|
|
1151
|
-
items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
|
|
1152
|
-
};
|
|
1153
|
-
} else {
|
|
1154
|
-
const { required = [], allOf, anyOf, description, enum: enum_, format, oneOf, properties = {} } = schema;
|
|
1155
|
-
let { type } = schema;
|
|
1156
|
-
if (enum_ && type !== "boolean") {
|
|
1157
|
-
const enumObject = {
|
|
1158
|
-
name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
|
|
1159
|
-
enum: [...new Set(enum_)]
|
|
1160
|
-
};
|
|
1161
|
-
const sameObject = Base.findSameSchema(enumObject, enums);
|
|
1162
|
-
if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
|
|
1163
|
-
return {
|
|
1164
|
-
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1165
|
-
required,
|
|
1166
|
-
description
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
|
-
if (type === void 0 && Object.keys(properties).length > 0) type = "object";
|
|
1170
|
-
return {
|
|
1171
|
-
type,
|
|
1172
|
-
required,
|
|
1173
|
-
description,
|
|
1174
|
-
enum: enum_,
|
|
1175
|
-
format,
|
|
1176
|
-
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1177
|
-
...s,
|
|
1178
|
-
ref: s.$ref,
|
|
1179
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1180
|
-
} : this.toBaseSchema(s, enums)),
|
|
1181
|
-
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1182
|
-
...s,
|
|
1183
|
-
ref: s.$ref,
|
|
1184
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1185
|
-
} : this.toBaseSchema(s, enums)),
|
|
1186
|
-
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1187
|
-
...s,
|
|
1188
|
-
ref: s.$ref,
|
|
1189
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1190
|
-
} : this.toBaseSchema(s, enums)),
|
|
1191
|
-
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1192
|
-
const propSchema = properties[p];
|
|
1193
|
-
return {
|
|
1194
|
-
...acc,
|
|
1195
|
-
[p]: Base.isRef(propSchema) ? { type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)) } : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1196
|
-
};
|
|
1197
|
-
}, {})
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
/**
|
|
1202
|
-
* OpenAPI parameter to base parameter.
|
|
1203
|
-
*/
|
|
1204
|
-
getParameterByRef(parameter, enums = [], upLevelSchemaKey = "") {
|
|
1205
|
-
if (Base.isRef(parameter)) {
|
|
1206
|
-
const refName = Base.ref2name(parameter.$ref, this.doc);
|
|
1207
|
-
const resolved = this.doc.parameters?.[refName];
|
|
1208
|
-
if (!resolved) throw new Error(`Parameter reference not found: ${parameter.$ref}`);
|
|
1209
|
-
parameter = resolved;
|
|
1210
|
-
}
|
|
1211
|
-
const p = parameter;
|
|
1212
|
-
const { name, required, description, type, items, enum: enum_, properties, schema } = p;
|
|
1213
|
-
if (enum_) {
|
|
1214
|
-
const type = Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(name));
|
|
1215
|
-
const enumSchema = {
|
|
1216
|
-
name: type,
|
|
1217
|
-
enum: [...new Set(enum_)]
|
|
1218
|
-
};
|
|
1219
|
-
const sameEnum = Base.findSameSchema(enumSchema, enums);
|
|
1220
|
-
if (!sameEnum && Base.isValidEnumType({
|
|
1221
|
-
type,
|
|
1222
|
-
enum: enum_
|
|
1223
|
-
})) enums.push(enumSchema);
|
|
1224
|
-
return {
|
|
1225
|
-
name,
|
|
1226
|
-
required,
|
|
1227
|
-
description,
|
|
1228
|
-
in: p.in,
|
|
1229
|
-
schema: { type: sameEnum?.name ?? type }
|
|
1230
|
-
};
|
|
1231
|
-
}
|
|
1232
|
-
if (items) return {
|
|
1233
|
-
name,
|
|
1234
|
-
required,
|
|
1235
|
-
description,
|
|
1236
|
-
in: p.in,
|
|
1237
|
-
schema: {
|
|
1238
|
-
type,
|
|
1239
|
-
items
|
|
1240
|
-
}
|
|
1241
|
-
};
|
|
1242
|
-
if (schema && Base.isRef(schema)) return {
|
|
1243
|
-
name,
|
|
1244
|
-
required,
|
|
1245
|
-
description,
|
|
1246
|
-
in: p.in,
|
|
1247
|
-
schema: { type: Base.capitalize(Base.ref2name(schema.$ref)) }
|
|
1248
|
-
};
|
|
1249
|
-
return {
|
|
1250
|
-
name,
|
|
1251
|
-
required,
|
|
1252
|
-
description,
|
|
1253
|
-
in: p.in,
|
|
1254
|
-
schema: {
|
|
1255
|
-
type,
|
|
1256
|
-
properties
|
|
1257
|
-
}
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
/**
|
|
1261
|
-
* OpenAPI schema to base response
|
|
1190
|
+
* Format a ref name for the schema's `type` field.
|
|
1191
|
+
* V3 uses `capitalize`; V3_1 uses `upperCamelCase` (preserved for compat).
|
|
1262
1192
|
*/
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
const { schema: responseSchema } = schema;
|
|
1266
|
-
return [{
|
|
1267
|
-
type: "application/json",
|
|
1268
|
-
schema: responseSchema && this.getSchemaByRef(responseSchema, true)
|
|
1269
|
-
}];
|
|
1270
|
-
}
|
|
1271
|
-
init() {
|
|
1272
|
-
const { definitions = {}, responses = {}, paths = {} } = this.doc;
|
|
1273
|
-
const enums = [];
|
|
1274
|
-
const definitions_ = Object.keys(definitions).reduce((acc, key) => {
|
|
1275
|
-
const schema = definitions[key];
|
|
1276
|
-
return {
|
|
1277
|
-
...acc,
|
|
1278
|
-
[key]: this.getSchemaByRef(schema, false, enums, key)
|
|
1279
|
-
};
|
|
1280
|
-
}, {});
|
|
1281
|
-
const responses_ = Object.keys(responses).reduce((acc, key) => {
|
|
1282
|
-
const response = responses[key];
|
|
1283
|
-
return {
|
|
1284
|
-
...acc,
|
|
1285
|
-
[key]: this.getResponseByRef(response)
|
|
1286
|
-
};
|
|
1287
|
-
}, {});
|
|
1288
|
-
const apis = Object.keys(paths).reduce((acc, path) => {
|
|
1289
|
-
let pathObject = paths[path] ?? {};
|
|
1290
|
-
if (pathObject.$ref) {
|
|
1291
|
-
const resolved = this.resolvePathRef(pathObject.$ref);
|
|
1292
|
-
if (resolved) pathObject = resolved;
|
|
1293
|
-
}
|
|
1294
|
-
const { parameters = [] } = pathObject;
|
|
1295
|
-
const methodApis = [];
|
|
1296
|
-
Object.values(HttpMethods).forEach((method) => {
|
|
1297
|
-
const methodObject = pathObject[method];
|
|
1298
|
-
if (methodObject) {
|
|
1299
|
-
const { deprecated, operationId, summary: summary_, description: description_, responses = {} } = methodObject;
|
|
1300
|
-
const { parameters: parameters_ = [] } = methodObject;
|
|
1301
|
-
const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
|
|
1302
|
-
const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
|
|
1303
|
-
if (Object.keys(responses).length === 0) Object.assign(responses, { 200: { description: "Successful response" } });
|
|
1304
|
-
const inBody = baseParameters.filter((p) => p.in === "body" || p.in === "formData");
|
|
1305
|
-
const notInBody = baseParameters.filter((p) => p.in !== "body" && p.in !== "formData");
|
|
1306
|
-
const httpCodes = Object.keys(responses);
|
|
1307
|
-
for (const code of httpCodes) if (code in responses) {
|
|
1308
|
-
const response = responses[code];
|
|
1309
|
-
const responseSchema = this.getResponseByRef(response);
|
|
1310
|
-
const inBodyOnlyHasBody = inBody && inBody.length === 1 && inBody[0].in === "body" && inBody[0].name === "body";
|
|
1311
|
-
methodApis.push({
|
|
1312
|
-
method,
|
|
1313
|
-
operationId,
|
|
1314
|
-
summary: summary_,
|
|
1315
|
-
deprecated,
|
|
1316
|
-
description: description_,
|
|
1317
|
-
parameters: uniqueParameterName.map((name) => notInBody.find((p) => p.name === name)).filter(Boolean),
|
|
1318
|
-
responses: responseSchema,
|
|
1319
|
-
requestBody: inBody.length > 0 ? inBodyOnlyHasBody ? [{
|
|
1320
|
-
type: "application/json",
|
|
1321
|
-
schema: inBody[0].schema
|
|
1322
|
-
}] : [{
|
|
1323
|
-
type: "application/json",
|
|
1324
|
-
schema: {
|
|
1325
|
-
type: "object",
|
|
1326
|
-
properties: inBody.reduce((a, p) => {
|
|
1327
|
-
return {
|
|
1328
|
-
...a,
|
|
1329
|
-
[p.name]: {
|
|
1330
|
-
type: p.schema?.type ?? "unknown",
|
|
1331
|
-
required: p.schema?.required,
|
|
1332
|
-
items: p.schema?.items,
|
|
1333
|
-
description: p.schema?.description
|
|
1334
|
-
}
|
|
1335
|
-
};
|
|
1336
|
-
}, {})
|
|
1337
|
-
}
|
|
1338
|
-
}] : void 0
|
|
1339
|
-
});
|
|
1340
|
-
break;
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
});
|
|
1344
|
-
return {
|
|
1345
|
-
...acc,
|
|
1346
|
-
[path]: methodApis
|
|
1347
|
-
};
|
|
1348
|
-
}, {});
|
|
1349
|
-
return {
|
|
1350
|
-
enums: Base.uniqueEnums(enums),
|
|
1351
|
-
schemas: definitions_,
|
|
1352
|
-
responses: responses_,
|
|
1353
|
-
parameters: {},
|
|
1354
|
-
requestBodies: {},
|
|
1355
|
-
apis
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
};
|
|
1359
|
-
//#endregion
|
|
1360
|
-
//#region src/openapi/V3.ts
|
|
1361
|
-
var V3 = class {
|
|
1362
|
-
doc;
|
|
1363
|
-
constructor(doc) {
|
|
1364
|
-
this.doc = doc;
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Resolves a path $ref to the actual path object.
|
|
1368
|
-
*/
|
|
1369
|
-
resolvePathRef($ref) {
|
|
1370
|
-
const refName = Base.ref2name($ref, this.doc);
|
|
1371
|
-
return this.doc.paths?.[refName];
|
|
1372
|
-
}
|
|
1373
|
-
/**
|
|
1374
|
-
* Is array schema.
|
|
1375
|
-
*/
|
|
1376
|
-
isOpenAPIArraySchema(schema) {
|
|
1377
|
-
return typeof schema === "object" && schema.type === "array";
|
|
1378
|
-
}
|
|
1379
|
-
/**
|
|
1380
|
-
* OpenAPI schema to base schema.
|
|
1381
|
-
*/
|
|
1382
|
-
getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
|
|
1383
|
-
let refName = "";
|
|
1384
|
-
if (Base.isRef(schema)) {
|
|
1385
|
-
refName = Base.capitalize(Base.ref2name(schema.$ref));
|
|
1386
|
-
if (reserveRef) return { type: upLevelSchemaKey + refName };
|
|
1387
|
-
const resolvedSchema = this.doc.components?.schemas?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1388
|
-
if (!resolvedSchema) return { type: "unknown" };
|
|
1389
|
-
schema = resolvedSchema;
|
|
1390
|
-
}
|
|
1391
|
-
return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
|
|
1193
|
+
formatRefName(name) {
|
|
1194
|
+
return Base.capitalize(name);
|
|
1392
1195
|
}
|
|
1393
1196
|
/**
|
|
1394
|
-
* OpenAPI parameter to base parameter.
|
|
1197
|
+
* OpenAPI parameter to base parameter. Override in V2 — its parameter
|
|
1198
|
+
* shape is structurally different (top-level `items`/`properties`/`enum`).
|
|
1395
1199
|
*/
|
|
1396
1200
|
getParameterByRef(schema, enums = [], upLevelSchemaKey = "") {
|
|
1397
1201
|
if (Base.isRef(schema)) {
|
|
1398
|
-
const resolvedSchema = this.
|
|
1202
|
+
const resolvedSchema = this.getParameterContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1399
1203
|
if (!resolvedSchema) return {
|
|
1400
1204
|
name: "unknown",
|
|
1401
1205
|
in: "query"
|
|
@@ -1426,37 +1230,38 @@ var V3 = class {
|
|
|
1426
1230
|
description,
|
|
1427
1231
|
deprecated,
|
|
1428
1232
|
in: schema.in,
|
|
1429
|
-
schema: schema.schema
|
|
1233
|
+
schema: schema.schema ? this.getSchemaByRef(schema.schema, false, enums, upLevelSchemaKey + Base.capitalize(name)) : void 0
|
|
1430
1234
|
};
|
|
1431
1235
|
}
|
|
1432
1236
|
/**
|
|
1433
|
-
* OpenAPI schema to base response
|
|
1237
|
+
* OpenAPI schema to base response. Override in V2 (its response shape
|
|
1238
|
+
* lacks the `content` wrapper).
|
|
1434
1239
|
*/
|
|
1435
1240
|
getResponseByRef(schema) {
|
|
1436
1241
|
if (Base.isRef(schema)) {
|
|
1437
|
-
const resolvedSchema = this.
|
|
1242
|
+
const resolvedSchema = this.getResponseContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1438
1243
|
if (!resolvedSchema) return [];
|
|
1439
1244
|
schema = resolvedSchema;
|
|
1440
1245
|
}
|
|
1441
1246
|
const { content = {} } = schema;
|
|
1442
1247
|
return Object.keys(content).map((c) => ({
|
|
1443
1248
|
type: c,
|
|
1444
|
-
schema: content[c].schema
|
|
1249
|
+
schema: content[c].schema ? this.getSchemaByRef(content[c].schema, true) : void 0
|
|
1445
1250
|
}));
|
|
1446
1251
|
}
|
|
1447
1252
|
/**
|
|
1448
|
-
* OpenAPI schema to requestBody.
|
|
1253
|
+
* OpenAPI schema to requestBody. V2 has no requestBody concept.
|
|
1449
1254
|
*/
|
|
1450
1255
|
getRequestBodyByRef(schema, enums = []) {
|
|
1451
1256
|
if (Base.isRef(schema)) {
|
|
1452
|
-
const resolvedSchema = this.
|
|
1257
|
+
const resolvedSchema = this.getRequestBodyContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1453
1258
|
if (!resolvedSchema) return [];
|
|
1454
1259
|
schema = resolvedSchema;
|
|
1455
1260
|
}
|
|
1456
1261
|
const { content = {} } = schema;
|
|
1457
1262
|
return Object.keys(content).map((c) => ({
|
|
1458
1263
|
type: c,
|
|
1459
|
-
schema: content[c].schema
|
|
1264
|
+
schema: content[c].schema ? this.getSchemaByRef(content[c].schema, false, enums) : void 0
|
|
1460
1265
|
}));
|
|
1461
1266
|
}
|
|
1462
1267
|
/**
|
|
@@ -1467,92 +1272,98 @@ var V3 = class {
|
|
|
1467
1272
|
if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
|
|
1468
1273
|
if (this.isOpenAPIArraySchema(schema)) {
|
|
1469
1274
|
const { type, description, items, required } = schema;
|
|
1275
|
+
const itemsSchema = items ? this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey) : { type: "unknown" };
|
|
1470
1276
|
return {
|
|
1471
1277
|
type,
|
|
1472
1278
|
required: !!required,
|
|
1473
1279
|
description,
|
|
1474
|
-
items:
|
|
1280
|
+
items: itemsSchema
|
|
1475
1281
|
};
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
return {
|
|
1487
|
-
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1488
|
-
required,
|
|
1489
|
-
description,
|
|
1490
|
-
deprecated
|
|
1491
|
-
};
|
|
1492
|
-
}
|
|
1493
|
-
if (type === void 0 && Object.keys(properties).length > 0) type = "object";
|
|
1282
|
+
}
|
|
1283
|
+
const { required = [], allOf, anyOf, description, deprecated, enum: enum_, format, oneOf, properties = {} } = schema;
|
|
1284
|
+
let { type } = schema;
|
|
1285
|
+
if (enum_ && type !== "boolean") {
|
|
1286
|
+
const enumObject = {
|
|
1287
|
+
name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
|
|
1288
|
+
enum: [...new Set(enum_)]
|
|
1289
|
+
};
|
|
1290
|
+
const sameObject = Base.findSameSchema(enumObject, enums);
|
|
1291
|
+
if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
|
|
1494
1292
|
return {
|
|
1495
|
-
type,
|
|
1293
|
+
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1496
1294
|
required,
|
|
1497
1295
|
description,
|
|
1498
|
-
deprecated
|
|
1499
|
-
enum: enum_,
|
|
1500
|
-
format,
|
|
1501
|
-
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1502
|
-
...s,
|
|
1503
|
-
ref: s.$ref,
|
|
1504
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1505
|
-
} : this.toBaseSchema(s, enums)),
|
|
1506
|
-
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1507
|
-
...s,
|
|
1508
|
-
ref: s.$ref,
|
|
1509
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1510
|
-
} : this.toBaseSchema(s, enums)),
|
|
1511
|
-
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1512
|
-
...s,
|
|
1513
|
-
ref: s.$ref,
|
|
1514
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1515
|
-
} : this.toBaseSchema(s, enums)),
|
|
1516
|
-
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1517
|
-
const propSchema = properties[p];
|
|
1518
|
-
return {
|
|
1519
|
-
...acc,
|
|
1520
|
-
[p]: Base.isRef(propSchema) ? {
|
|
1521
|
-
type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)),
|
|
1522
|
-
isRef: true
|
|
1523
|
-
} : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1524
|
-
};
|
|
1525
|
-
}, {})
|
|
1296
|
+
deprecated
|
|
1526
1297
|
};
|
|
1527
1298
|
}
|
|
1299
|
+
if (type === void 0 && Object.keys(properties).length > 0) type = "object";
|
|
1300
|
+
return {
|
|
1301
|
+
type,
|
|
1302
|
+
required,
|
|
1303
|
+
description,
|
|
1304
|
+
deprecated,
|
|
1305
|
+
enum: enum_,
|
|
1306
|
+
format,
|
|
1307
|
+
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1308
|
+
...s,
|
|
1309
|
+
ref: s.$ref,
|
|
1310
|
+
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1311
|
+
} : this.toBaseSchema(s, enums)),
|
|
1312
|
+
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1313
|
+
...s,
|
|
1314
|
+
ref: s.$ref,
|
|
1315
|
+
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1316
|
+
} : this.toBaseSchema(s, enums)),
|
|
1317
|
+
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1318
|
+
...s,
|
|
1319
|
+
ref: s.$ref,
|
|
1320
|
+
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1321
|
+
} : this.toBaseSchema(s, enums)),
|
|
1322
|
+
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1323
|
+
const propSchema = properties[p];
|
|
1324
|
+
return {
|
|
1325
|
+
...acc,
|
|
1326
|
+
[p]: Base.isRef(propSchema) ? {
|
|
1327
|
+
type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)),
|
|
1328
|
+
isRef: true
|
|
1329
|
+
} : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1330
|
+
};
|
|
1331
|
+
}, {})
|
|
1332
|
+
};
|
|
1528
1333
|
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Run the parsing pipeline and return a ProviderInitResult.
|
|
1336
|
+
*/
|
|
1529
1337
|
init() {
|
|
1530
|
-
const {
|
|
1338
|
+
const { paths = {} } = this.doc;
|
|
1531
1339
|
const enums = [];
|
|
1532
|
-
const
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1340
|
+
const schemaContainer = this.getSchemaContainer() ?? {};
|
|
1341
|
+
const parameterContainer = this.getParameterContainer() ?? {};
|
|
1342
|
+
const responseContainer = this.getResponseContainer() ?? {};
|
|
1343
|
+
const requestBodyContainer = this.getRequestBodyContainer() ?? {};
|
|
1344
|
+
const schemas_ = Object.keys(schemaContainer).reduce((acc, key) => {
|
|
1345
|
+
const schema = schemaContainer[key];
|
|
1535
1346
|
return {
|
|
1536
1347
|
...acc,
|
|
1537
1348
|
[key]: this.getSchemaByRef(schema, false, enums, key)
|
|
1538
1349
|
};
|
|
1539
1350
|
}, {});
|
|
1540
|
-
const parameters_ = Object.keys(
|
|
1541
|
-
const parameter =
|
|
1351
|
+
const parameters_ = Object.keys(parameterContainer).reduce((acc, key) => {
|
|
1352
|
+
const parameter = parameterContainer[key];
|
|
1542
1353
|
return {
|
|
1543
1354
|
...acc,
|
|
1544
1355
|
[key]: this.getParameterByRef(parameter, enums, key)
|
|
1545
1356
|
};
|
|
1546
1357
|
}, {});
|
|
1547
|
-
const responses_ = Object.keys(
|
|
1548
|
-
const response =
|
|
1358
|
+
const responses_ = Object.keys(responseContainer).reduce((acc, key) => {
|
|
1359
|
+
const response = responseContainer[key];
|
|
1549
1360
|
return {
|
|
1550
1361
|
...acc,
|
|
1551
1362
|
[key]: this.getResponseByRef(response)
|
|
1552
1363
|
};
|
|
1553
1364
|
}, {});
|
|
1554
|
-
const requestBodies_ = Object.keys(
|
|
1555
|
-
const requestBody =
|
|
1365
|
+
const requestBodies_ = Object.keys(requestBodyContainer).reduce((acc, key) => {
|
|
1366
|
+
const requestBody = requestBodyContainer[key];
|
|
1556
1367
|
return {
|
|
1557
1368
|
...acc,
|
|
1558
1369
|
[key]: this.getRequestBodyByRef(requestBody, enums)
|
|
@@ -1569,15 +1380,16 @@ var V3 = class {
|
|
|
1569
1380
|
Object.values(HttpMethods).forEach((method) => {
|
|
1570
1381
|
const methodObject = pathObject[method];
|
|
1571
1382
|
if (methodObject) {
|
|
1572
|
-
const { deprecated, operationId, responses
|
|
1383
|
+
const { deprecated, operationId, responses, summary: summary_, description: description_, requestBody = { content: {} } } = methodObject;
|
|
1384
|
+
const responsesClone = responses ? { ...responses } : {};
|
|
1573
1385
|
const { parameters: parameters_ = [] } = methodObject;
|
|
1574
1386
|
const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
|
|
1575
1387
|
const baseRequestBody = this.getRequestBodyByRef(requestBody, enums);
|
|
1576
1388
|
const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
|
|
1577
|
-
if (Object.keys(
|
|
1578
|
-
const httpCodes = Object.keys(
|
|
1579
|
-
for (const code of httpCodes) if (code in
|
|
1580
|
-
const response =
|
|
1389
|
+
if (Object.keys(responsesClone).length === 0) responsesClone[200] = { description: "Successful response" };
|
|
1390
|
+
const httpCodes = Object.keys(responsesClone);
|
|
1391
|
+
for (const code of httpCodes) if (code in responsesClone) {
|
|
1392
|
+
const response = responsesClone[code];
|
|
1581
1393
|
const responseSchema = this.getResponseByRef(response);
|
|
1582
1394
|
methodApis.push({
|
|
1583
1395
|
method,
|
|
@@ -1609,246 +1421,248 @@ var V3 = class {
|
|
|
1609
1421
|
}
|
|
1610
1422
|
};
|
|
1611
1423
|
//#endregion
|
|
1612
|
-
//#region src/openapi/
|
|
1613
|
-
var
|
|
1424
|
+
//#region src/openapi/V2.ts
|
|
1425
|
+
var V2 = class extends VersionedProvider {
|
|
1614
1426
|
doc;
|
|
1427
|
+
version = "v2";
|
|
1615
1428
|
constructor(doc) {
|
|
1429
|
+
super();
|
|
1616
1430
|
this.doc = doc;
|
|
1617
1431
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
*/
|
|
1621
|
-
resolvePathRef($ref) {
|
|
1622
|
-
const refName = Base.ref2name($ref, this.doc);
|
|
1623
|
-
return this.doc.paths?.[refName];
|
|
1432
|
+
getSchemaContainer() {
|
|
1433
|
+
return this.doc.definitions;
|
|
1624
1434
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
*/
|
|
1628
|
-
isOpenAPIArraySchema(schema) {
|
|
1629
|
-
return typeof schema === "object" && schema.type === "array";
|
|
1435
|
+
getParameterContainer() {
|
|
1436
|
+
return this.doc.parameters;
|
|
1630
1437
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
*/
|
|
1634
|
-
getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
|
|
1635
|
-
let refName = "";
|
|
1636
|
-
if (Base.isRef(schema)) {
|
|
1637
|
-
refName = Base.upperCamelCase(Base.ref2name(schema.$ref));
|
|
1638
|
-
if (reserveRef) return { type: upLevelSchemaKey + refName };
|
|
1639
|
-
if (!this.doc.components) this.doc.components = { schemas: {} };
|
|
1640
|
-
const resolvedSchema = this.doc.components.schemas?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1641
|
-
if (!resolvedSchema) throw new Error(`Schema reference not found: ${refName}`);
|
|
1642
|
-
schema = resolvedSchema;
|
|
1643
|
-
}
|
|
1644
|
-
return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
|
|
1438
|
+
getResponseContainer() {
|
|
1439
|
+
return this.doc.responses;
|
|
1645
1440
|
}
|
|
1441
|
+
getRequestBodyContainer() {}
|
|
1646
1442
|
/**
|
|
1647
|
-
*
|
|
1443
|
+
* V2 parameter shape differs from V3: top-level `items`/`properties`/`enum`
|
|
1444
|
+
* rather than nested under `schema`. Override accordingly.
|
|
1648
1445
|
*/
|
|
1649
|
-
getParameterByRef(
|
|
1650
|
-
if (Base.isRef(
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1446
|
+
getParameterByRef(parameter, enums = [], upLevelSchemaKey = "") {
|
|
1447
|
+
if (Base.isRef(parameter)) {
|
|
1448
|
+
const refName = Base.ref2name(parameter.$ref, this.doc);
|
|
1449
|
+
const resolved = this.doc.parameters?.[refName];
|
|
1450
|
+
if (!resolved) throw new Error(`Parameter reference not found: ${parameter.$ref}`);
|
|
1451
|
+
parameter = resolved;
|
|
1452
|
+
}
|
|
1453
|
+
const p = parameter;
|
|
1454
|
+
const { name, required, description, type, items, enum: enum_, properties, schema } = p;
|
|
1455
|
+
if (enum_) {
|
|
1456
|
+
const enumType = Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(name));
|
|
1654
1457
|
const enumSchema = {
|
|
1655
|
-
name:
|
|
1656
|
-
enum: [...new Set(
|
|
1458
|
+
name: enumType,
|
|
1459
|
+
enum: [...new Set(enum_)]
|
|
1657
1460
|
};
|
|
1658
1461
|
const sameEnum = Base.findSameSchema(enumSchema, enums);
|
|
1659
|
-
if (!sameEnum && Base.isValidEnumType(
|
|
1462
|
+
if (!sameEnum && Base.isValidEnumType({
|
|
1463
|
+
type: enumType,
|
|
1464
|
+
enum: enum_
|
|
1465
|
+
})) enums.push(enumSchema);
|
|
1660
1466
|
return {
|
|
1661
1467
|
name,
|
|
1662
1468
|
required,
|
|
1663
1469
|
description,
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
schema: { type: sameEnum?.name ?? type }
|
|
1470
|
+
in: p.in,
|
|
1471
|
+
schema: { type: sameEnum?.name ?? enumType }
|
|
1667
1472
|
};
|
|
1668
1473
|
}
|
|
1474
|
+
if (items) return {
|
|
1475
|
+
name,
|
|
1476
|
+
required,
|
|
1477
|
+
description,
|
|
1478
|
+
in: p.in,
|
|
1479
|
+
schema: {
|
|
1480
|
+
type,
|
|
1481
|
+
items
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
if (schema && Base.isRef(schema)) return {
|
|
1485
|
+
name,
|
|
1486
|
+
required,
|
|
1487
|
+
description,
|
|
1488
|
+
in: p.in,
|
|
1489
|
+
schema: { type: Base.capitalize(Base.ref2name(schema.$ref)) }
|
|
1490
|
+
};
|
|
1669
1491
|
return {
|
|
1670
1492
|
name,
|
|
1671
1493
|
required,
|
|
1672
1494
|
description,
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1495
|
+
in: p.in,
|
|
1496
|
+
schema: {
|
|
1497
|
+
type,
|
|
1498
|
+
properties
|
|
1499
|
+
}
|
|
1676
1500
|
};
|
|
1677
1501
|
}
|
|
1678
1502
|
/**
|
|
1679
|
-
*
|
|
1503
|
+
* V2 response shape: a `schema` field directly, not `content`. V2 always
|
|
1504
|
+
* emits JSON.
|
|
1680
1505
|
*/
|
|
1681
1506
|
getResponseByRef(schema) {
|
|
1682
|
-
if (Base.isRef(schema)) schema = this.doc.
|
|
1683
|
-
const {
|
|
1684
|
-
return
|
|
1685
|
-
type:
|
|
1686
|
-
schema:
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
/**
|
|
1690
|
-
* OpenAPI schema to requestBody.
|
|
1691
|
-
*/
|
|
1692
|
-
getRequestBodyByRef(schema, enums = []) {
|
|
1693
|
-
if (Base.isRef(schema)) schema = this.doc.components?.requestBodies?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1694
|
-
const { content = {} } = schema;
|
|
1695
|
-
return Object.keys(content).map((c) => ({
|
|
1696
|
-
type: c,
|
|
1697
|
-
schema: content[c].schema && this.getSchemaByRef(content[c].schema, true, enums)
|
|
1698
|
-
}));
|
|
1507
|
+
if (Base.isRef(schema)) schema = this.doc.responses[Base.ref2name(schema.$ref, this.doc)];
|
|
1508
|
+
const { schema: responseSchema } = schema;
|
|
1509
|
+
return [{
|
|
1510
|
+
type: "application/json",
|
|
1511
|
+
schema: responseSchema ? this.getSchemaByRef(responseSchema, true) : void 0
|
|
1512
|
+
}];
|
|
1699
1513
|
}
|
|
1700
1514
|
/**
|
|
1701
|
-
*
|
|
1515
|
+
* V2 has no `requestBody` concept; parameters with `in: body` or
|
|
1516
|
+
* `in: formData` are split out and turned into a synthetic requestBody
|
|
1517
|
+
* here. A single body param named `body` is used directly; otherwise
|
|
1518
|
+
* the body / formData params are wrapped in a synthetic object.
|
|
1702
1519
|
*/
|
|
1703
|
-
toBaseSchema(schema, enums = [], schemaKey = "", upLevelSchemaKey = "") {
|
|
1704
|
-
if (!schema) return { type: "unknown" };
|
|
1705
|
-
if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
|
|
1706
|
-
if (this.isOpenAPIArraySchema(schema)) {
|
|
1707
|
-
const { type, description, items, required } = schema;
|
|
1708
|
-
return {
|
|
1709
|
-
type,
|
|
1710
|
-
required: !!required,
|
|
1711
|
-
description,
|
|
1712
|
-
items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
|
|
1713
|
-
};
|
|
1714
|
-
} else {
|
|
1715
|
-
const { required = [], allOf, anyOf, description, deprecated, enum: enum_, format, oneOf, properties = {} } = schema;
|
|
1716
|
-
let { type } = schema;
|
|
1717
|
-
if (enum_ && type !== "boolean") {
|
|
1718
|
-
const enumObject = {
|
|
1719
|
-
name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
|
|
1720
|
-
enum: [...new Set(enum_)]
|
|
1721
|
-
};
|
|
1722
|
-
const sameObject = Base.findSameSchema(enumObject, enums);
|
|
1723
|
-
if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
|
|
1724
|
-
return {
|
|
1725
|
-
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1726
|
-
required,
|
|
1727
|
-
description,
|
|
1728
|
-
deprecated
|
|
1729
|
-
};
|
|
1730
|
-
}
|
|
1731
|
-
if (type === void 0 && Object.keys(properties).length > 0) type = "object";
|
|
1732
|
-
return {
|
|
1733
|
-
type,
|
|
1734
|
-
required,
|
|
1735
|
-
description,
|
|
1736
|
-
deprecated,
|
|
1737
|
-
enum: enum_,
|
|
1738
|
-
format,
|
|
1739
|
-
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1740
|
-
...s,
|
|
1741
|
-
ref: s.$ref,
|
|
1742
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1743
|
-
} : this.toBaseSchema(s, enums)),
|
|
1744
|
-
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1745
|
-
...s,
|
|
1746
|
-
ref: s.$ref,
|
|
1747
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1748
|
-
} : this.toBaseSchema(s, enums)),
|
|
1749
|
-
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1750
|
-
...s,
|
|
1751
|
-
ref: s.$ref,
|
|
1752
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1753
|
-
} : this.toBaseSchema(s, enums)),
|
|
1754
|
-
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1755
|
-
const propSchema = properties[p];
|
|
1756
|
-
return {
|
|
1757
|
-
...acc,
|
|
1758
|
-
[p]: Base.isRef(propSchema) ? {
|
|
1759
|
-
type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)),
|
|
1760
|
-
isRef: true
|
|
1761
|
-
} : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1762
|
-
};
|
|
1763
|
-
}, {})
|
|
1764
|
-
};
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
1520
|
init() {
|
|
1768
|
-
const {
|
|
1521
|
+
const { paths = {} } = this.doc;
|
|
1769
1522
|
const enums = [];
|
|
1770
|
-
const
|
|
1771
|
-
const
|
|
1772
|
-
|
|
1523
|
+
const schemaContainer = this.getSchemaContainer() ?? {};
|
|
1524
|
+
const parameterContainer = this.getParameterContainer() ?? {};
|
|
1525
|
+
const responseContainer = this.getResponseContainer() ?? {};
|
|
1526
|
+
const schemas_ = Object.keys(schemaContainer).reduce((acc, key) => {
|
|
1527
|
+
const schema = schemaContainer[key];
|
|
1773
1528
|
return {
|
|
1774
1529
|
...acc,
|
|
1775
1530
|
[key]: this.getSchemaByRef(schema, false, enums, key)
|
|
1776
1531
|
};
|
|
1777
1532
|
}, {});
|
|
1778
|
-
const parameters_ = Object.keys(
|
|
1779
|
-
const parameter =
|
|
1533
|
+
const parameters_ = Object.keys(parameterContainer).reduce((acc, key) => {
|
|
1534
|
+
const parameter = parameterContainer[key];
|
|
1780
1535
|
return {
|
|
1781
1536
|
...acc,
|
|
1782
1537
|
[key]: this.getParameterByRef(parameter, enums, key)
|
|
1783
1538
|
};
|
|
1784
1539
|
}, {});
|
|
1785
|
-
const responses_ = Object.keys(
|
|
1786
|
-
const response =
|
|
1540
|
+
const responses_ = Object.keys(responseContainer).reduce((acc, key) => {
|
|
1541
|
+
const response = responseContainer[key];
|
|
1787
1542
|
return {
|
|
1788
1543
|
...acc,
|
|
1789
1544
|
[key]: this.getResponseByRef(response)
|
|
1790
1545
|
};
|
|
1791
1546
|
}, {});
|
|
1792
|
-
const
|
|
1793
|
-
|
|
1794
|
-
return {
|
|
1795
|
-
...acc,
|
|
1796
|
-
[key]: this.getRequestBodyByRef(requestBody, enums)
|
|
1797
|
-
};
|
|
1798
|
-
}, {});
|
|
1799
|
-
const apis = Object.keys(paths).reduce((acc, path) => {
|
|
1547
|
+
const apis = {};
|
|
1548
|
+
for (const path of Object.keys(paths)) {
|
|
1800
1549
|
let pathObject = paths[path] ?? {};
|
|
1801
1550
|
if (pathObject.$ref) {
|
|
1802
1551
|
const resolved = this.resolvePathRef(pathObject.$ref);
|
|
1803
1552
|
if (resolved) pathObject = resolved;
|
|
1804
1553
|
}
|
|
1805
|
-
const { parameters = []
|
|
1554
|
+
const { parameters = [] } = pathObject;
|
|
1806
1555
|
const methodApis = [];
|
|
1807
1556
|
Object.values(HttpMethods).forEach((method) => {
|
|
1808
1557
|
const methodObject = pathObject[method];
|
|
1809
|
-
if (methodObject)
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1558
|
+
if (!methodObject) return;
|
|
1559
|
+
const { deprecated, operationId, summary: summary_, description: description_, responses } = methodObject;
|
|
1560
|
+
const { parameters: parameters_ = [] } = methodObject;
|
|
1561
|
+
const baseParameters = [...parameters, ...parameters_].map((p) => this.getParameterByRef(p, enums));
|
|
1562
|
+
const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
|
|
1563
|
+
const responsesClone = responses ? { ...responses } : {};
|
|
1564
|
+
if (Object.keys(responsesClone).length === 0) responsesClone[200] = { description: "Successful response" };
|
|
1565
|
+
const inBody = baseParameters.filter((p) => p.in === "body" || p.in === "formData");
|
|
1566
|
+
const notInBody = baseParameters.filter((p) => p.in !== "body" && p.in !== "formData");
|
|
1567
|
+
const httpCodes = Object.keys(responsesClone);
|
|
1568
|
+
for (const code of httpCodes) if (code in responsesClone) {
|
|
1569
|
+
const response = responsesClone[code];
|
|
1570
|
+
const responseSchema = this.getResponseByRef(response);
|
|
1571
|
+
const inBodyOnlyHasBody = inBody.length === 1 && inBody[0].in === "body" && inBody[0].name === "body";
|
|
1572
|
+
methodApis.push({
|
|
1573
|
+
method,
|
|
1574
|
+
operationId,
|
|
1575
|
+
summary: summary_,
|
|
1576
|
+
deprecated,
|
|
1577
|
+
description: description_,
|
|
1578
|
+
parameters: uniqueParameterName.map((name) => notInBody.find((p) => p.name === name)).filter((p) => p !== void 0),
|
|
1579
|
+
responses: responseSchema,
|
|
1580
|
+
requestBody: inBody.length > 0 ? inBodyOnlyHasBody ? [{
|
|
1581
|
+
type: "application/json",
|
|
1582
|
+
schema: inBody[0].schema
|
|
1583
|
+
}] : [{
|
|
1584
|
+
type: "application/json",
|
|
1585
|
+
schema: {
|
|
1586
|
+
type: "object",
|
|
1587
|
+
properties: inBody.reduce((a, p) => {
|
|
1588
|
+
return {
|
|
1589
|
+
...a,
|
|
1590
|
+
[p.name]: {
|
|
1591
|
+
type: p.schema?.type ?? "unknown",
|
|
1592
|
+
required: p.schema?.required,
|
|
1593
|
+
items: p.schema?.items,
|
|
1594
|
+
description: p.schema?.description
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
}, {})
|
|
1598
|
+
}
|
|
1599
|
+
}] : void 0
|
|
1600
|
+
});
|
|
1601
|
+
break;
|
|
1832
1602
|
}
|
|
1833
1603
|
});
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
[path]: methodApis
|
|
1837
|
-
};
|
|
1838
|
-
}, {});
|
|
1604
|
+
apis[path] = methodApis;
|
|
1605
|
+
}
|
|
1839
1606
|
return {
|
|
1840
1607
|
enums: Base.uniqueEnums(enums),
|
|
1841
1608
|
schemas: schemas_,
|
|
1842
1609
|
responses: responses_,
|
|
1843
1610
|
parameters: parameters_,
|
|
1844
|
-
requestBodies:
|
|
1611
|
+
requestBodies: {},
|
|
1845
1612
|
apis
|
|
1846
1613
|
};
|
|
1847
1614
|
}
|
|
1848
1615
|
};
|
|
1849
1616
|
//#endregion
|
|
1617
|
+
//#region src/openapi/V3.ts
|
|
1618
|
+
var V3 = class extends VersionedProvider {
|
|
1619
|
+
doc;
|
|
1620
|
+
version = "v3";
|
|
1621
|
+
constructor(doc) {
|
|
1622
|
+
super();
|
|
1623
|
+
this.doc = doc;
|
|
1624
|
+
}
|
|
1625
|
+
getSchemaContainer() {
|
|
1626
|
+
return this.doc.components?.schemas;
|
|
1627
|
+
}
|
|
1628
|
+
getParameterContainer() {
|
|
1629
|
+
return this.doc.components?.parameters;
|
|
1630
|
+
}
|
|
1631
|
+
getResponseContainer() {
|
|
1632
|
+
return this.doc.components?.responses;
|
|
1633
|
+
}
|
|
1634
|
+
getRequestBodyContainer() {
|
|
1635
|
+
return this.doc.components?.requestBodies;
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
//#endregion
|
|
1639
|
+
//#region src/openapi/V3_1.ts
|
|
1640
|
+
var V3_1 = class extends VersionedProvider {
|
|
1641
|
+
doc;
|
|
1642
|
+
version = "v3_1";
|
|
1643
|
+
constructor(doc) {
|
|
1644
|
+
super();
|
|
1645
|
+
this.doc = doc;
|
|
1646
|
+
}
|
|
1647
|
+
formatRefName(name) {
|
|
1648
|
+
return Base.upperCamelCase(name);
|
|
1649
|
+
}
|
|
1650
|
+
getSchemaContainer() {
|
|
1651
|
+
return this.doc.components?.schemas;
|
|
1652
|
+
}
|
|
1653
|
+
getParameterContainer() {
|
|
1654
|
+
return this.doc.components?.parameters;
|
|
1655
|
+
}
|
|
1656
|
+
getResponseContainer() {
|
|
1657
|
+
return this.doc.components?.responses;
|
|
1658
|
+
}
|
|
1659
|
+
getRequestBodyContainer() {
|
|
1660
|
+
return this.doc.components?.requestBodies;
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
//#endregion
|
|
1850
1664
|
//#region src/openapi/index.ts
|
|
1851
|
-
const logger
|
|
1665
|
+
const logger = createScopedLogger("OpenAPI");
|
|
1852
1666
|
function getDocVersion(doc) {
|
|
1853
1667
|
switch ((doc.openapi || doc.swagger).slice(0, 3)) {
|
|
1854
1668
|
case "3.1": return "v3_1";
|
|
@@ -1860,7 +1674,7 @@ function getDocVersion(doc) {
|
|
|
1860
1674
|
var OpenAPIProvider = class extends Provider {
|
|
1861
1675
|
parse(doc) {
|
|
1862
1676
|
const version = getDocVersion(doc);
|
|
1863
|
-
logger
|
|
1677
|
+
logger.debug(`openapi version ${version}`);
|
|
1864
1678
|
switch (version) {
|
|
1865
1679
|
case "v2": return new V2(doc).init();
|
|
1866
1680
|
case "v3": return new V3(doc).init();
|
|
@@ -1878,9 +1692,9 @@ function getAdaptor(type) {
|
|
|
1878
1692
|
async function codeGen(initOptions) {
|
|
1879
1693
|
const startTime = Date.now();
|
|
1880
1694
|
const { verbose } = initOptions;
|
|
1881
|
-
if (verbose) logger
|
|
1882
|
-
else logger
|
|
1883
|
-
logger
|
|
1695
|
+
if (verbose) logger.setLevel("debug");
|
|
1696
|
+
else logger.setLevel("info");
|
|
1697
|
+
logger.info(`Fetch document from ${initOptions.docURL}`);
|
|
1884
1698
|
const { enums, schemas, parameters, responses, requestBodies, apis } = new OpenAPIProvider(initOptions, await Base.fetchDoc(initOptions.docURL, initOptions.requestOptions));
|
|
1885
1699
|
const adaptor = getAdaptor(initOptions.adaptor ?? "fetch");
|
|
1886
1700
|
const code = await Generator.genCode({
|
|
@@ -1905,7 +1719,7 @@ async function codeGen(initOptions) {
|
|
|
1905
1719
|
//#endregion
|
|
1906
1720
|
//#region src/vite-plugin/index.ts
|
|
1907
1721
|
const PLUGIN_NAME = "api-code-gen";
|
|
1908
|
-
const
|
|
1722
|
+
const pluginLogger = createScopedLogger("api-code-gen");
|
|
1909
1723
|
/**
|
|
1910
1724
|
* Run TypeScript type checking on generated file
|
|
1911
1725
|
*/
|
|
@@ -1934,7 +1748,7 @@ async function validateSpecPath(specPath) {
|
|
|
1934
1748
|
async function generateForOption(option) {
|
|
1935
1749
|
const { name, typeCheck = true, verbose, ...restOptions } = option;
|
|
1936
1750
|
try {
|
|
1937
|
-
|
|
1751
|
+
logger$1.info(`Generating ${name}...`);
|
|
1938
1752
|
const config = await loadConfig({
|
|
1939
1753
|
name,
|
|
1940
1754
|
cliOptions: {
|
|
@@ -1958,8 +1772,8 @@ async function generateForOption(option) {
|
|
|
1958
1772
|
if (typeCheck && config.output) {
|
|
1959
1773
|
const typeErrors = await runTypeCheck(config.output);
|
|
1960
1774
|
if (typeErrors.length > 0) {
|
|
1961
|
-
|
|
1962
|
-
if (verbose) for (const error of typeErrors)
|
|
1775
|
+
pluginLogger.warn(`Type check failed for ${config.output}`);
|
|
1776
|
+
if (verbose) for (const error of typeErrors) pluginLogger.warn(` ${error}`);
|
|
1963
1777
|
}
|
|
1964
1778
|
}
|
|
1965
1779
|
return {
|
|
@@ -2000,45 +1814,47 @@ async function generateForOption(option) {
|
|
|
2000
1814
|
*/
|
|
2001
1815
|
function apiCodeGenPlugin(options) {
|
|
2002
1816
|
if (!Array.isArray(options) || options.length === 0) {
|
|
2003
|
-
|
|
1817
|
+
pluginLogger.warn("No API configurations provided to apiCodeGenPlugin");
|
|
2004
1818
|
return { name: PLUGIN_NAME };
|
|
2005
1819
|
}
|
|
2006
1820
|
return {
|
|
2007
1821
|
name: PLUGIN_NAME,
|
|
2008
1822
|
async config(_config, env) {
|
|
2009
|
-
|
|
2010
|
-
console.log(`\x1b[1m\x1b[36mAPI Code Gen\x1b[0m`);
|
|
2011
|
-
console.log(`\x1b[90mMode:\x1b[0m ${env?.command || "unknown"}`);
|
|
2012
|
-
console.log(`\x1b[1m\x1b[36m${"─".repeat(50)}\x1b[0m`);
|
|
1823
|
+
logger$1.heading("API Code Gen", env?.command);
|
|
2013
1824
|
const results = await Promise.all(options.map(generateForOption));
|
|
2014
1825
|
const successCount = results.filter((r) => r.success).length;
|
|
2015
1826
|
const failCount = options.length - successCount;
|
|
2016
|
-
|
|
1827
|
+
logger$1.divider();
|
|
2017
1828
|
for (const result of results) if (result.success) {
|
|
2018
1829
|
const { name, output, stats } = result;
|
|
2019
|
-
if (stats)
|
|
2020
|
-
else
|
|
1830
|
+
if (stats) logger$1.item(`${name} → ${output} (${stats.endpoints} endpoints, ${stats.schemas} schemas) ${stats.duration}ms`, "green");
|
|
1831
|
+
else logger$1.item(`${name} → ${output || "N/A"}`, "green");
|
|
2021
1832
|
} else {
|
|
2022
1833
|
const { name, error } = result;
|
|
2023
1834
|
if (isApicodegenError(error)) {
|
|
2024
|
-
|
|
2025
|
-
|
|
1835
|
+
logger$1.item(name, "red");
|
|
1836
|
+
logger$1.error(error, true);
|
|
2026
1837
|
} else {
|
|
2027
1838
|
const wrapped = wrapError(error, {
|
|
2028
|
-
code:
|
|
1839
|
+
code: ErrorCodes.GENERATION_FAILED,
|
|
2029
1840
|
message: `Failed to generate API "${name}"`
|
|
2030
1841
|
});
|
|
2031
|
-
|
|
2032
|
-
|
|
1842
|
+
logger$1.item(name, "red");
|
|
1843
|
+
logger$1.error(wrapped, true);
|
|
2033
1844
|
}
|
|
2034
1845
|
}
|
|
2035
|
-
|
|
1846
|
+
logger$1.divider();
|
|
2036
1847
|
const totalDuration = results.reduce((sum, r) => sum + (r.stats?.duration || 0), 0);
|
|
2037
1848
|
const totalEndpoints = results.reduce((sum, r) => sum + (r.stats?.endpoints || 0), 0);
|
|
2038
1849
|
const totalSchemas = results.reduce((sum, r) => sum + (r.stats?.schemas || 0), 0);
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
1850
|
+
logger$1.summary({
|
|
1851
|
+
succeeded: successCount,
|
|
1852
|
+
failed: failCount,
|
|
1853
|
+
endpoints: totalEndpoints,
|
|
1854
|
+
schemas: totalSchemas,
|
|
1855
|
+
duration: totalDuration
|
|
1856
|
+
});
|
|
1857
|
+
logger$1.divider();
|
|
2042
1858
|
return {};
|
|
2043
1859
|
}
|
|
2044
1860
|
};
|