@cushin/api-codegen 5.0.8 → 6.0.1

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/cli.js CHANGED
@@ -28,6 +28,7 @@ async function loadConfig(configPath) {
28
28
  const rootDir = path.dirname(result.filepath);
29
29
  const endpointsPath = path.resolve(rootDir, userConfig.endpoints);
30
30
  const outputDir = path.resolve(rootDir, userConfig.output);
31
+ const swaggerSourcePath = userConfig.swaggerSource ? path.resolve(rootDir, userConfig.swaggerSource) : void 0;
31
32
  const generateHooks = userConfig.generateHooks ?? true;
32
33
  const generateServerActions = userConfig.generateServerActions ?? userConfig.provider === "nextjs";
33
34
  const generateServerQueries = userConfig.generateServerQueries ?? userConfig.provider === "nextjs";
@@ -38,6 +39,7 @@ async function loadConfig(configPath) {
38
39
  rootDir,
39
40
  endpointsPath,
40
41
  outputDir,
42
+ swaggerSourcePath,
41
43
  generateHooks,
42
44
  generateServerActions,
43
45
  generateServerQueries,
@@ -1011,11 +1013,576 @@ var CodeGenerator = class {
1011
1013
 
1012
1014
  // src/core/codegen.ts
1013
1015
  import { fileURLToPath } from "url";
1016
+ import fs11 from "fs/promises";
1017
+ import path12 from "path";
1018
+
1019
+ // src/swagger/parser.ts
1020
+ import fs10 from "fs";
1021
+ import path11 from "path";
1022
+ async function parseOpenAPISpec(filePath) {
1023
+ const content = await fs10.promises.readFile(filePath, "utf-8");
1024
+ const ext = path11.extname(filePath).toLowerCase();
1025
+ let spec;
1026
+ if (ext === ".json") {
1027
+ spec = JSON.parse(content);
1028
+ } else if (ext === ".yaml" || ext === ".yml") {
1029
+ const yaml = await import("yaml");
1030
+ spec = yaml.parse(content);
1031
+ } else {
1032
+ throw new Error(
1033
+ `Unsupported file format: ${ext}. Only .json, .yaml, and .yml are supported.`
1034
+ );
1035
+ }
1036
+ if (!spec.openapi || !spec.openapi.startsWith("3.")) {
1037
+ throw new Error(
1038
+ `Unsupported OpenAPI version: ${spec.openapi}. Only OpenAPI 3.0 and 3.1 are supported.`
1039
+ );
1040
+ }
1041
+ return spec;
1042
+ }
1043
+ function extractEndpoints(spec) {
1044
+ const endpoints = [];
1045
+ for (const [path14, pathItem] of Object.entries(spec.paths)) {
1046
+ const methods = [
1047
+ "get",
1048
+ "post",
1049
+ "put",
1050
+ "delete",
1051
+ "patch",
1052
+ "head",
1053
+ "options"
1054
+ ];
1055
+ for (const method of methods) {
1056
+ const operation = pathItem[method];
1057
+ if (!operation) continue;
1058
+ const endpoint = parseOperation(
1059
+ path14,
1060
+ method,
1061
+ operation,
1062
+ pathItem,
1063
+ spec
1064
+ );
1065
+ endpoints.push(endpoint);
1066
+ }
1067
+ }
1068
+ return endpoints;
1069
+ }
1070
+ function parseOperation(path14, method, operation, pathItem, spec) {
1071
+ const allParameters = [
1072
+ ...pathItem.parameters || [],
1073
+ ...operation.parameters || []
1074
+ ];
1075
+ const pathParams = [];
1076
+ const queryParams = [];
1077
+ for (const param of allParameters) {
1078
+ const resolved = resolveParameter(param, spec);
1079
+ const parsed = {
1080
+ name: resolved.name,
1081
+ type: schemaToTypeString(resolved.schema),
1082
+ required: resolved.required ?? resolved.in === "path",
1083
+ description: resolved.description,
1084
+ schema: resolved.schema
1085
+ // Keep original schema
1086
+ };
1087
+ if (resolved.in === "path") {
1088
+ pathParams.push(parsed);
1089
+ } else if (resolved.in === "query") {
1090
+ queryParams.push(parsed);
1091
+ }
1092
+ }
1093
+ let requestBody;
1094
+ if (operation.requestBody) {
1095
+ const resolved = resolveRequestBody(operation.requestBody, spec);
1096
+ const content = resolved.content?.["application/json"];
1097
+ if (content) {
1098
+ requestBody = {
1099
+ type: schemaToTypeString(content.schema),
1100
+ required: resolved.required ?? false,
1101
+ description: resolved.description,
1102
+ schema: content.schema
1103
+ };
1104
+ }
1105
+ }
1106
+ let response;
1107
+ const responses = operation.responses;
1108
+ const successStatus = responses["200"] || responses["201"] || responses["204"];
1109
+ if (successStatus) {
1110
+ const statusCode = responses["200"] ? "200" : responses["201"] ? "201" : "204";
1111
+ const resolved = resolveResponse(successStatus, spec);
1112
+ const content = resolved.content?.["application/json"];
1113
+ if (content || statusCode === "204") {
1114
+ response = {
1115
+ statusCode,
1116
+ type: content ? schemaToTypeString(content.schema) : "void",
1117
+ description: resolved.description,
1118
+ schema: content?.schema || { type: "null" }
1119
+ };
1120
+ }
1121
+ }
1122
+ if (!response) {
1123
+ for (const [statusCode, res] of Object.entries(responses)) {
1124
+ if (statusCode.startsWith("2")) {
1125
+ const resolved = resolveResponse(res, spec);
1126
+ const content = resolved.content?.["application/json"];
1127
+ response = {
1128
+ statusCode,
1129
+ type: content ? schemaToTypeString(content.schema) : "void",
1130
+ description: resolved.description,
1131
+ schema: content?.schema || { type: "null" }
1132
+ };
1133
+ break;
1134
+ }
1135
+ }
1136
+ }
1137
+ return {
1138
+ path: path14,
1139
+ method: method.toUpperCase(),
1140
+ operationId: operation.operationId,
1141
+ summary: operation.summary,
1142
+ description: operation.description,
1143
+ tags: operation.tags,
1144
+ pathParams: pathParams.length > 0 ? pathParams : void 0,
1145
+ queryParams: queryParams.length > 0 ? queryParams : void 0,
1146
+ requestBody,
1147
+ response
1148
+ };
1149
+ }
1150
+ function resolveParameter(param, spec) {
1151
+ if ("$ref" in param && param.$ref) {
1152
+ const refPath = param.$ref.replace("#/components/parameters/", "");
1153
+ const resolved = spec.components?.parameters?.[refPath];
1154
+ if (!resolved) {
1155
+ throw new Error(`Cannot resolve parameter reference: ${param.$ref}`);
1156
+ }
1157
+ return resolved;
1158
+ }
1159
+ return param;
1160
+ }
1161
+ function resolveRequestBody(requestBody, spec) {
1162
+ if ("$ref" in requestBody && requestBody.$ref) {
1163
+ const refPath = requestBody.$ref.replace("#/components/requestBodies/", "");
1164
+ const resolved = spec.components?.requestBodies?.[refPath];
1165
+ if (!resolved) {
1166
+ throw new Error(`Cannot resolve request body reference: ${requestBody.$ref}`);
1167
+ }
1168
+ return resolved;
1169
+ }
1170
+ return requestBody;
1171
+ }
1172
+ function resolveResponse(response, spec) {
1173
+ if ("$ref" in response && response.$ref) {
1174
+ const refPath = response.$ref.replace("#/components/responses/", "");
1175
+ const resolved = spec.components?.responses?.[refPath];
1176
+ if (!resolved) {
1177
+ throw new Error(`Cannot resolve response reference: ${response.$ref}`);
1178
+ }
1179
+ return resolved;
1180
+ }
1181
+ return response;
1182
+ }
1183
+ function schemaToTypeString(schema) {
1184
+ if (!schema) return "any";
1185
+ if (schema.$ref) {
1186
+ const parts = schema.$ref.split("/");
1187
+ return parts[parts.length - 1];
1188
+ }
1189
+ if (schema.type) {
1190
+ switch (schema.type) {
1191
+ case "string":
1192
+ return "string";
1193
+ case "number":
1194
+ case "integer":
1195
+ return "number";
1196
+ case "boolean":
1197
+ return "boolean";
1198
+ case "array":
1199
+ if (schema.items) {
1200
+ return `${schemaToTypeString(schema.items)}[]`;
1201
+ }
1202
+ return "any[]";
1203
+ case "object":
1204
+ return "object";
1205
+ default:
1206
+ return "any";
1207
+ }
1208
+ }
1209
+ if (schema.allOf) {
1210
+ return schema.allOf.map(schemaToTypeString).join(" & ");
1211
+ }
1212
+ if (schema.oneOf || schema.anyOf) {
1213
+ const schemas = schema.oneOf || schema.anyOf;
1214
+ return schemas.map(schemaToTypeString).join(" | ");
1215
+ }
1216
+ return "any";
1217
+ }
1218
+
1219
+ // src/swagger/config-generator.ts
1220
+ function groupEndpointsByTags(endpoints) {
1221
+ const grouped = /* @__PURE__ */ new Map();
1222
+ for (const endpoint of endpoints) {
1223
+ const tag = endpoint.tags && endpoint.tags.length > 0 ? endpoint.tags[0] : "default";
1224
+ if (!grouped.has(tag)) {
1225
+ grouped.set(tag, []);
1226
+ }
1227
+ grouped.get(tag).push(endpoint);
1228
+ }
1229
+ return grouped;
1230
+ }
1231
+ function generateConfigFile(endpoints, spec, baseUrl) {
1232
+ const lines = [];
1233
+ lines.push('import { defineConfig, defineEndpoint } from "@cushin/api-runtime";');
1234
+ lines.push('import { z } from "zod";');
1235
+ lines.push("");
1236
+ if (spec.components?.schemas) {
1237
+ lines.push("// Schema definitions from OpenAPI components");
1238
+ for (const [name, schema] of Object.entries(spec.components.schemas)) {
1239
+ const zodSchema = convertSchemaToZod(schema, spec);
1240
+ lines.push(`const ${name}Schema = ${zodSchema};`);
1241
+ }
1242
+ lines.push("");
1243
+ }
1244
+ lines.push("// Endpoint definitions");
1245
+ const endpointNames = [];
1246
+ for (const endpoint of endpoints) {
1247
+ const name = generateEndpointName(endpoint);
1248
+ endpointNames.push(name);
1249
+ const path14 = convertPathFormat(endpoint.path);
1250
+ lines.push(`const ${name} = defineEndpoint({`);
1251
+ lines.push(` path: "${path14}",`);
1252
+ lines.push(` method: "${endpoint.method}",`);
1253
+ if (endpoint.pathParams && endpoint.pathParams.length > 0) {
1254
+ const paramsSchema = generateParamsSchema(endpoint.pathParams, spec);
1255
+ lines.push(` params: ${paramsSchema},`);
1256
+ }
1257
+ if (endpoint.queryParams && endpoint.queryParams.length > 0) {
1258
+ const querySchema = generateQuerySchema(endpoint.queryParams, spec);
1259
+ lines.push(` query: ${querySchema},`);
1260
+ }
1261
+ if (endpoint.requestBody) {
1262
+ const bodySchema = convertSchemaToZod(endpoint.requestBody.schema, spec);
1263
+ lines.push(` body: ${bodySchema},`);
1264
+ }
1265
+ if (endpoint.response) {
1266
+ const responseSchema = convertSchemaToZod(endpoint.response.schema, spec);
1267
+ lines.push(` response: ${responseSchema},`);
1268
+ } else {
1269
+ lines.push(` response: z.any(),`);
1270
+ }
1271
+ if (endpoint.tags && endpoint.tags.length > 0) {
1272
+ const tagsStr = endpoint.tags.map((t) => `"${t}"`).join(", ");
1273
+ lines.push(` tags: [${tagsStr}],`);
1274
+ }
1275
+ if (endpoint.description || endpoint.summary) {
1276
+ const desc = endpoint.description || endpoint.summary;
1277
+ lines.push(` description: "${escapeString(desc)}",`);
1278
+ }
1279
+ lines.push("});");
1280
+ lines.push("");
1281
+ }
1282
+ lines.push("// API Configuration");
1283
+ lines.push("export const apiConfig = defineConfig({");
1284
+ if (baseUrl) {
1285
+ lines.push(` baseUrl: "${baseUrl}",`);
1286
+ } else if (spec.servers && spec.servers.length > 0) {
1287
+ lines.push(` baseUrl: "${spec.servers[0].url}",`);
1288
+ }
1289
+ lines.push(" endpoints: {");
1290
+ for (const name of endpointNames) {
1291
+ lines.push(` ${name},`);
1292
+ }
1293
+ lines.push(" },");
1294
+ lines.push("});");
1295
+ lines.push("");
1296
+ return lines.join("\n");
1297
+ }
1298
+ function generateEndpointName(endpoint) {
1299
+ if (endpoint.operationId) {
1300
+ return toCamelCase(endpoint.operationId);
1301
+ }
1302
+ const method = endpoint.method.toLowerCase();
1303
+ const pathParts = endpoint.path.split("/").filter((p) => p && !p.startsWith(":")).map((p) => p.replace(/[^a-zA-Z0-9]/g, ""));
1304
+ const parts = [method, ...pathParts];
1305
+ return toCamelCase(parts.join("_"));
1306
+ }
1307
+ function toCamelCase(str) {
1308
+ return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toLowerCase());
1309
+ }
1310
+ function generateParamsSchema(params, spec) {
1311
+ const props = [];
1312
+ for (const param of params) {
1313
+ const zodType = convertSchemaToZod(param.schema, spec);
1314
+ props.push(` ${param.name}: ${zodType}`);
1315
+ }
1316
+ return `z.object({
1317
+ ${props.join(",\n")}
1318
+ })`;
1319
+ }
1320
+ function generateQuerySchema(params, spec) {
1321
+ const props = [];
1322
+ for (const param of params) {
1323
+ let zodType = convertSchemaToZod(param.schema, spec);
1324
+ if (!param.required) {
1325
+ zodType += ".optional()";
1326
+ }
1327
+ props.push(` ${param.name}: ${zodType}`);
1328
+ }
1329
+ return `z.object({
1330
+ ${props.join(",\n")}
1331
+ })`;
1332
+ }
1333
+ function convertSchemaToZod(schema, spec) {
1334
+ if (!schema) return "z.any()";
1335
+ if (schema.$ref) {
1336
+ const refName = schema.$ref.split("/").pop();
1337
+ if (refName && spec.components?.schemas?.[refName]) {
1338
+ return `${refName}Schema`;
1339
+ }
1340
+ return "z.any()";
1341
+ }
1342
+ if (schema.type) {
1343
+ switch (schema.type) {
1344
+ case "string":
1345
+ if (schema.enum) {
1346
+ const values = schema.enum.map((v) => `"${v}"`).join(", ");
1347
+ return `z.enum([${values}])`;
1348
+ }
1349
+ return "z.string()";
1350
+ case "number":
1351
+ case "integer":
1352
+ return "z.number()";
1353
+ case "boolean":
1354
+ return "z.boolean()";
1355
+ case "array":
1356
+ if (schema.items) {
1357
+ const itemSchema = convertSchemaToZod(schema.items, spec);
1358
+ return `z.array(${itemSchema})`;
1359
+ }
1360
+ return "z.array(z.any())";
1361
+ case "object":
1362
+ if (schema.properties) {
1363
+ const props = [];
1364
+ const required = schema.required || [];
1365
+ for (const [propName, propSchema] of Object.entries(
1366
+ schema.properties
1367
+ )) {
1368
+ let zodType = convertSchemaToZod(propSchema, spec);
1369
+ if (!required.includes(propName)) {
1370
+ zodType += ".optional()";
1371
+ }
1372
+ props.push(` ${propName}: ${zodType}`);
1373
+ }
1374
+ return `z.object({
1375
+ ${props.join(",\n")}
1376
+ })`;
1377
+ }
1378
+ return "z.object({})";
1379
+ case "null":
1380
+ return "z.null()";
1381
+ default:
1382
+ return "z.any()";
1383
+ }
1384
+ }
1385
+ if (schema.allOf) {
1386
+ const schemas = schema.allOf.map((s) => convertSchemaToZod(s, spec));
1387
+ return schemas.join(".and(");
1388
+ }
1389
+ if (schema.oneOf || schema.anyOf) {
1390
+ const schemas = (schema.oneOf || schema.anyOf).map(
1391
+ (s) => convertSchemaToZod(s, spec)
1392
+ );
1393
+ return `z.union([${schemas.join(", ")}])`;
1394
+ }
1395
+ return "z.any()";
1396
+ }
1397
+ function escapeString(str) {
1398
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
1399
+ }
1400
+ function convertPathFormat(path14) {
1401
+ return path14.replace(/\{([^}]+)\}/g, ":$1");
1402
+ }
1403
+ function generateModuleFile(tag, endpoints, spec) {
1404
+ const lines = [];
1405
+ lines.push('import { defineEndpoint } from "@cushin/api-runtime";');
1406
+ lines.push('import { z } from "zod";');
1407
+ lines.push("");
1408
+ const usedSchemas = /* @__PURE__ */ new Set();
1409
+ for (const endpoint of endpoints) {
1410
+ collectUsedSchemas(endpoint, usedSchemas, spec);
1411
+ }
1412
+ if (usedSchemas.size > 0 && spec.components?.schemas) {
1413
+ lines.push("// Schema definitions");
1414
+ const sortedSchemas = sortSchemasByDependencies(Array.from(usedSchemas), spec);
1415
+ for (const schemaName of sortedSchemas) {
1416
+ const schema = spec.components.schemas[schemaName];
1417
+ if (schema) {
1418
+ const zodSchema = convertSchemaToZod(schema, spec);
1419
+ lines.push(`const ${schemaName}Schema = ${zodSchema};`);
1420
+ }
1421
+ }
1422
+ lines.push("");
1423
+ }
1424
+ lines.push("// Endpoint definitions");
1425
+ const endpointNames = [];
1426
+ for (const endpoint of endpoints) {
1427
+ const name = generateEndpointName(endpoint);
1428
+ endpointNames.push(name);
1429
+ const path14 = convertPathFormat(endpoint.path);
1430
+ lines.push(`export const ${name} = defineEndpoint({`);
1431
+ lines.push(` path: "${path14}",`);
1432
+ lines.push(` method: "${endpoint.method}",`);
1433
+ if (endpoint.pathParams && endpoint.pathParams.length > 0) {
1434
+ const paramsSchema = generateParamsSchema(endpoint.pathParams, spec);
1435
+ lines.push(` params: ${paramsSchema},`);
1436
+ }
1437
+ if (endpoint.queryParams && endpoint.queryParams.length > 0) {
1438
+ const querySchema = generateQuerySchema(endpoint.queryParams, spec);
1439
+ lines.push(` query: ${querySchema},`);
1440
+ }
1441
+ if (endpoint.requestBody) {
1442
+ const bodySchema = convertSchemaToZod(endpoint.requestBody.schema, spec);
1443
+ lines.push(` body: ${bodySchema},`);
1444
+ }
1445
+ if (endpoint.response) {
1446
+ const responseSchema = convertSchemaToZod(endpoint.response.schema, spec);
1447
+ lines.push(` response: ${responseSchema},`);
1448
+ } else {
1449
+ lines.push(` response: z.any(),`);
1450
+ }
1451
+ if (endpoint.tags && endpoint.tags.length > 0) {
1452
+ const tagsStr = endpoint.tags.map((t) => `"${t}"`).join(", ");
1453
+ lines.push(` tags: [${tagsStr}],`);
1454
+ }
1455
+ if (endpoint.description || endpoint.summary) {
1456
+ const desc = endpoint.description || endpoint.summary;
1457
+ lines.push(` description: "${escapeString(desc)}",`);
1458
+ }
1459
+ lines.push("});");
1460
+ lines.push("");
1461
+ }
1462
+ return lines.join("\n");
1463
+ }
1464
+ function sortSchemasByDependencies(schemaNames, spec) {
1465
+ const sorted = [];
1466
+ const visited = /* @__PURE__ */ new Set();
1467
+ const visiting = /* @__PURE__ */ new Set();
1468
+ function visit(name) {
1469
+ if (visited.has(name)) return;
1470
+ if (visiting.has(name)) {
1471
+ return;
1472
+ }
1473
+ visiting.add(name);
1474
+ const schema = spec.components?.schemas?.[name];
1475
+ if (schema) {
1476
+ const deps = /* @__PURE__ */ new Set();
1477
+ extractSchemaNames(schema, deps, spec);
1478
+ for (const dep of deps) {
1479
+ if (dep !== name && schemaNames.includes(dep)) {
1480
+ visit(dep);
1481
+ }
1482
+ }
1483
+ }
1484
+ visiting.delete(name);
1485
+ if (!visited.has(name)) {
1486
+ visited.add(name);
1487
+ sorted.push(name);
1488
+ }
1489
+ }
1490
+ for (const name of schemaNames) {
1491
+ visit(name);
1492
+ }
1493
+ return sorted;
1494
+ }
1495
+ function collectUsedSchemas(endpoint, usedSchemas, spec) {
1496
+ if (endpoint.requestBody?.schema) {
1497
+ extractSchemaNames(endpoint.requestBody.schema, usedSchemas, spec);
1498
+ }
1499
+ if (endpoint.response?.schema) {
1500
+ extractSchemaNames(endpoint.response.schema, usedSchemas, spec);
1501
+ }
1502
+ if (endpoint.pathParams) {
1503
+ for (const param of endpoint.pathParams) {
1504
+ extractSchemaNames(param.schema, usedSchemas, spec);
1505
+ }
1506
+ }
1507
+ if (endpoint.queryParams) {
1508
+ for (const param of endpoint.queryParams) {
1509
+ extractSchemaNames(param.schema, usedSchemas, spec);
1510
+ }
1511
+ }
1512
+ }
1513
+ function extractSchemaNames(schema, names, spec) {
1514
+ if (!schema) return;
1515
+ if (schema.$ref) {
1516
+ const schemaName = schema.$ref.split("/").pop();
1517
+ if (schemaName) {
1518
+ names.add(schemaName);
1519
+ const referencedSchema = spec.components?.schemas?.[schemaName];
1520
+ if (referencedSchema) {
1521
+ extractSchemaNames(referencedSchema, names, spec);
1522
+ }
1523
+ }
1524
+ return;
1525
+ }
1526
+ if (schema.properties) {
1527
+ for (const prop of Object.values(schema.properties)) {
1528
+ extractSchemaNames(prop, names, spec);
1529
+ }
1530
+ }
1531
+ if (schema.items) {
1532
+ extractSchemaNames(schema.items, names, spec);
1533
+ }
1534
+ if (schema.allOf) {
1535
+ for (const s of schema.allOf) {
1536
+ extractSchemaNames(s, names, spec);
1537
+ }
1538
+ }
1539
+ if (schema.oneOf) {
1540
+ for (const s of schema.oneOf) {
1541
+ extractSchemaNames(s, names, spec);
1542
+ }
1543
+ }
1544
+ if (schema.anyOf) {
1545
+ for (const s of schema.anyOf) {
1546
+ extractSchemaNames(s, names, spec);
1547
+ }
1548
+ }
1549
+ }
1550
+ function generateIndexFile(tagModules, spec, baseUrl) {
1551
+ const lines = [];
1552
+ lines.push('import { defineConfig } from "@cushin/api-runtime";');
1553
+ lines.push("");
1554
+ for (const [tag] of tagModules) {
1555
+ const moduleFileName = tag.toLowerCase().replace(/[^a-z0-9]/g, "-");
1556
+ lines.push(`import * as ${toCamelCase(tag)}Module from "./${moduleFileName}.js";`);
1557
+ }
1558
+ lines.push("");
1559
+ lines.push("export const apiConfig = defineConfig({");
1560
+ const url = baseUrl || (spec.servers && spec.servers.length > 0 ? spec.servers[0].url : void 0);
1561
+ if (url) {
1562
+ lines.push(` baseUrl: "${url}",`);
1563
+ }
1564
+ lines.push(" endpoints: {");
1565
+ for (const [tag] of tagModules) {
1566
+ lines.push(` ...${toCamelCase(tag)}Module,`);
1567
+ }
1568
+ lines.push(" },");
1569
+ lines.push("});");
1570
+ lines.push("");
1571
+ for (const [tag] of tagModules) {
1572
+ lines.push(`export * from "./${tag.toLowerCase().replace(/[^a-z0-9]/g, "-")}.js";`);
1573
+ }
1574
+ return lines.join("\n");
1575
+ }
1576
+
1577
+ // src/core/codegen.ts
1014
1578
  var CodegenCore = class {
1015
1579
  constructor(config) {
1016
1580
  this.config = config;
1017
1581
  }
1018
1582
  async execute() {
1583
+ if (this.config.swaggerSourcePath) {
1584
+ await this.generateConfigFromSwagger();
1585
+ }
1019
1586
  const apiConfig = await this.loadAPIConfig();
1020
1587
  this.config.apiConfig = apiConfig;
1021
1588
  const generator = new CodeGenerator({
@@ -1024,6 +1591,67 @@ var CodegenCore = class {
1024
1591
  });
1025
1592
  await generator.generate();
1026
1593
  }
1594
+ /**
1595
+ * Generate single config file (no split)
1596
+ */
1597
+ async generateSingleConfigFile(endpoints, spec) {
1598
+ const configContent = generateConfigFile(endpoints, spec, this.config.baseUrl);
1599
+ const endpointsDir = path12.dirname(this.config.endpointsPath);
1600
+ await fs11.mkdir(endpointsDir, { recursive: true });
1601
+ await fs11.writeFile(this.config.endpointsPath, configContent, "utf-8");
1602
+ console.log(`\u2713 Generated endpoint config at ${this.config.endpointsPath}`);
1603
+ }
1604
+ /**
1605
+ * Generate multiple module files split by tags
1606
+ */
1607
+ async generateMultipleModuleFiles(endpoints, spec) {
1608
+ const grouped = groupEndpointsByTags(endpoints);
1609
+ console.log(`\u2713 Grouped into ${grouped.size} modules by tags`);
1610
+ const endpointsDir = path12.dirname(this.config.endpointsPath);
1611
+ const modulesDir = path12.join(endpointsDir, "modules");
1612
+ await fs11.mkdir(modulesDir, { recursive: true });
1613
+ for (const [tag, tagEndpoints] of grouped.entries()) {
1614
+ const moduleFileName = tag.toLowerCase().replace(/[^a-z0-9]/g, "-");
1615
+ const moduleFilePath = path12.join(modulesDir, `${moduleFileName}.ts`);
1616
+ const moduleContent = generateModuleFile(tag, tagEndpoints, spec);
1617
+ await fs11.writeFile(moduleFilePath, moduleContent, "utf-8");
1618
+ console.log(` \u2713 ${tag}: ${tagEndpoints.length} endpoints \u2192 ${moduleFileName}.ts`);
1619
+ }
1620
+ const indexContent = generateIndexFile(grouped, spec, this.config.baseUrl);
1621
+ const indexPath = path12.join(modulesDir, "index.ts");
1622
+ await fs11.writeFile(indexPath, indexContent, "utf-8");
1623
+ console.log(`\u2713 Generated index.ts at ${modulesDir}/index.ts`);
1624
+ const mainExportContent = `export * from "./modules/index.js";
1625
+ `;
1626
+ await fs11.mkdir(endpointsDir, { recursive: true });
1627
+ await fs11.writeFile(this.config.endpointsPath, mainExportContent, "utf-8");
1628
+ console.log(`\u2713 Generated main export at ${this.config.endpointsPath}`);
1629
+ }
1630
+ /**
1631
+ * Generate endpoint config file from Swagger/OpenAPI spec
1632
+ */
1633
+ async generateConfigFromSwagger() {
1634
+ if (!this.config.swaggerSourcePath) {
1635
+ return;
1636
+ }
1637
+ try {
1638
+ console.log(`\u{1F4C4} Parsing Swagger spec from ${this.config.swaggerSourcePath}...`);
1639
+ const spec = await parseOpenAPISpec(this.config.swaggerSourcePath);
1640
+ console.log(`\u2713 Found OpenAPI ${spec.openapi} specification`);
1641
+ console.log(` Title: ${spec.info.title} (v${spec.info.version})`);
1642
+ const endpoints = extractEndpoints(spec);
1643
+ console.log(`\u2713 Extracted ${endpoints.length} endpoints`);
1644
+ if (this.config.splitByTags) {
1645
+ await this.generateMultipleModuleFiles(endpoints, spec);
1646
+ } else {
1647
+ await this.generateSingleConfigFile(endpoints, spec);
1648
+ }
1649
+ } catch (error) {
1650
+ throw new Error(
1651
+ `Failed to generate config from Swagger: ${error instanceof Error ? error.message : String(error)}`
1652
+ );
1653
+ }
1654
+ }
1027
1655
  async loadAPIConfig() {
1028
1656
  try {
1029
1657
  const jiti = createJiti(fileURLToPath(import.meta.url), {
@@ -1049,14 +1677,14 @@ var CodegenCore = class {
1049
1677
  };
1050
1678
 
1051
1679
  // src/cli.ts
1052
- import path11 from "path";
1680
+ import path13 from "path";
1053
1681
  var program = new Command();
1054
1682
  setupCLIProgram(program);
1055
1683
  var context = {
1056
1684
  loadConfig,
1057
1685
  validateConfig,
1058
1686
  CodegenCore,
1059
- pathToFileURL: (filePath) => new URL(`file://${path11.resolve(filePath)}`)
1687
+ pathToFileURL: (filePath) => new URL(`file://${path13.resolve(filePath)}`)
1060
1688
  };
1061
1689
  setupGenerateCommand(program, context);
1062
1690
  setupInitCommand(program);