api-core-lib 12.0.68 → 12.0.70
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.cjs +89 -60
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -76,14 +76,14 @@ var import_swagger_parser = __toESM(require("@apidevtools/swagger-parser"), 1);
|
|
|
76
76
|
var import_openapi_types = __toESM(require_dist(), 1);
|
|
77
77
|
var DEBUG_MODE = process.env.DEBUG === "true";
|
|
78
78
|
var debugLog = (title, data) => DEBUG_MODE && console.log(import_chalk.default.yellow(`
|
|
79
|
-
[DEBUG: ${title}]`), import_util.default.inspect(data, { depth:
|
|
79
|
+
[DEBUG: ${title}]`), import_util.default.inspect(data, { depth: 5, colors: true }));
|
|
80
80
|
var toPascalCase = (str) => str.replace(/[^a-zA-Z0-9_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (w) => w.toUpperCase()).replace(/\s+/g, "");
|
|
81
81
|
var toCamelCase = (str) => {
|
|
82
82
|
const s = toPascalCase(str);
|
|
83
83
|
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
84
84
|
};
|
|
85
85
|
var getModuleName = (opId) => `${opId.split("_")[0].replace(/Controller$/, "")}Api`;
|
|
86
|
-
var getActionName = (opId) => toCamelCase(opId.split("_")[1] || opId);
|
|
86
|
+
var getActionName = (opId) => toCamelCase(opId.split("_")[1] || opId).replace(/V\d+$/, "");
|
|
87
87
|
var findCommonPath = (paths) => {
|
|
88
88
|
if (!paths || paths.length === 0) return "/";
|
|
89
89
|
const sorted = [...paths].sort();
|
|
@@ -94,25 +94,22 @@ var findCommonPath = (paths) => {
|
|
|
94
94
|
const prefix = first.substring(0, i);
|
|
95
95
|
return prefix.substring(0, prefix.lastIndexOf("/") + 1) || "/";
|
|
96
96
|
};
|
|
97
|
-
var generateFriendlyMessage = (fieldName,
|
|
98
|
-
|
|
99
|
-
switch (validation) {
|
|
100
|
-
case "required":
|
|
101
|
-
return `${friendlyName} is required.`;
|
|
102
|
-
case "email":
|
|
103
|
-
return `Please enter a valid email for ${friendlyName}.`;
|
|
104
|
-
default:
|
|
105
|
-
return `Invalid value for ${friendlyName}.`;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
function parseSchema(name, schema) {
|
|
97
|
+
var generateFriendlyMessage = (fieldName) => `${fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase())} is required.`;
|
|
98
|
+
function parseSchema(name, schema, allEnums) {
|
|
109
99
|
const properties = [];
|
|
100
|
+
const enums = {};
|
|
110
101
|
if (schema.properties) {
|
|
111
102
|
for (const propName in schema.properties) {
|
|
112
103
|
const propSchema = schema.properties[propName];
|
|
113
|
-
let
|
|
104
|
+
let itemSchema;
|
|
114
105
|
if (propSchema.type === "array" && propSchema.items) {
|
|
115
|
-
|
|
106
|
+
const itemTypeName = `${toPascalCase(name)}${toPascalCase(propName)}Item`;
|
|
107
|
+
itemSchema = parseSchema(itemTypeName, propSchema.items, allEnums).properties[0];
|
|
108
|
+
}
|
|
109
|
+
if (propSchema.enum) {
|
|
110
|
+
const enumName = `${toPascalCase(name)}${toPascalCase(propName)}Enum`;
|
|
111
|
+
enums[propName] = propSchema.enum;
|
|
112
|
+
if (!allEnums.has(enumName)) allEnums.set(enumName, { name: enumName, values: propSchema.enum });
|
|
116
113
|
}
|
|
117
114
|
properties.push({
|
|
118
115
|
name: propName,
|
|
@@ -123,24 +120,31 @@ function parseSchema(name, schema) {
|
|
|
123
120
|
example: propSchema.example,
|
|
124
121
|
enum: propSchema.enum,
|
|
125
122
|
format: propSchema.format,
|
|
126
|
-
items,
|
|
127
|
-
|
|
128
|
-
properties: propSchema.properties ? parseSchema("sub-object", propSchema).properties : void 0
|
|
123
|
+
items: itemSchema,
|
|
124
|
+
properties: propSchema.properties ? parseSchema(`${name}${toPascalCase(propName)}`, propSchema, allEnums).properties : void 0
|
|
129
125
|
});
|
|
130
126
|
}
|
|
131
127
|
}
|
|
132
|
-
return { name, description: schema.description, properties };
|
|
128
|
+
return { name, description: schema.description, properties, enums };
|
|
133
129
|
}
|
|
134
130
|
function parseSpecToModules(spec) {
|
|
135
131
|
const modules = /* @__PURE__ */ new Map();
|
|
136
132
|
const allSchemas = /* @__PURE__ */ new Map();
|
|
133
|
+
const allEnums = /* @__PURE__ */ new Map();
|
|
137
134
|
const modulePaths = /* @__PURE__ */ new Map();
|
|
138
135
|
const registerSchema = (schema, baseName) => {
|
|
139
136
|
if (!schema) return "unknown";
|
|
140
|
-
if (schema.type === "array" && schema.items)
|
|
137
|
+
if (schema.type === "array" && schema.items) {
|
|
138
|
+
const itemSchema = schema.items;
|
|
139
|
+
const itemBaseName = `${baseName}Item`;
|
|
140
|
+
if (itemSchema.properties || itemSchema.type === "object") {
|
|
141
|
+
return `${registerSchema(itemSchema, itemBaseName)}[]`;
|
|
142
|
+
}
|
|
143
|
+
return `${itemSchema.type || "unknown"}[]`;
|
|
144
|
+
}
|
|
141
145
|
if (schema.type === "object" || schema.properties || schema.allOf || !schema.type) {
|
|
142
146
|
const typeName = toPascalCase(baseName.replace(/_v\d+(Request|Response)$/, "$1"));
|
|
143
|
-
if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema));
|
|
147
|
+
if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema, allEnums));
|
|
144
148
|
return typeName;
|
|
145
149
|
}
|
|
146
150
|
return schema.type === "integer" ? "number" : schema.type || "unknown";
|
|
@@ -154,7 +158,7 @@ function parseSpecToModules(spec) {
|
|
|
154
158
|
if (!endpoint.operationId) continue;
|
|
155
159
|
const moduleName = getModuleName(endpoint.operationId);
|
|
156
160
|
if (!modules.has(moduleName)) {
|
|
157
|
-
modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set() });
|
|
161
|
+
modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set(), enums: /* @__PURE__ */ new Set() });
|
|
158
162
|
modulePaths.set(moduleName, []);
|
|
159
163
|
}
|
|
160
164
|
const currentModule = modules.get(moduleName);
|
|
@@ -170,35 +174,36 @@ function parseSpecToModules(spec) {
|
|
|
170
174
|
const cleanType = t.replace("[]", "");
|
|
171
175
|
if (cleanType && !["unknown", "undefined", "void", "any", "QueryOptions", "Promise"].includes(cleanType)) {
|
|
172
176
|
currentModule.schemas.add(cleanType);
|
|
177
|
+
const schemaDef = allSchemas.get(cleanType);
|
|
178
|
+
if (schemaDef) Object.keys(schemaDef.enums).forEach((propName) => currentModule.enums.add(`${toPascalCase(cleanType)}${toPascalCase(propName)}Enum`));
|
|
173
179
|
}
|
|
174
180
|
});
|
|
175
|
-
|
|
176
|
-
currentModule.actions[actionName] = { name: actionName, method: method.toUpperCase(), path: apiPath, description: endpoint.summary || "", hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"), autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"), requiresAuth: !!endpoint.security && endpoint.security.length > 0, inputType, outputType };
|
|
181
|
+
currentModule.actions[getActionName(endpoint.operationId)] = { name: getActionName(endpoint.operationId), method: method.toUpperCase(), path: apiPath, description: endpoint.summary || "", hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"), autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"), requiresAuth: !!endpoint.security && endpoint.security.length > 0, inputType, outputType };
|
|
177
182
|
}
|
|
178
183
|
}
|
|
179
184
|
modules.forEach((mod, name) => {
|
|
180
|
-
|
|
181
|
-
mod.
|
|
182
|
-
Object.values(mod.actions).forEach((action) => {
|
|
183
|
-
const relativePath = action.path.replace(basePath, "").replace(/^\//, "");
|
|
184
|
-
action.path = relativePath === "" ? "/" : relativePath;
|
|
185
|
-
});
|
|
185
|
+
mod.baseEndpoint = findCommonPath(modulePaths.get(name));
|
|
186
|
+
Object.values(mod.actions).forEach((action) => action.path = action.path.replace(mod.baseEndpoint, "").replace(/^\//, "") || "/");
|
|
186
187
|
});
|
|
187
188
|
debugLog("Final Parsed Modules", Object.fromEntries(modules));
|
|
188
|
-
return { modules, allSchemas };
|
|
189
|
+
return { modules, allSchemas, allEnums };
|
|
189
190
|
}
|
|
190
|
-
async function generateModuleFiles(module2, allSchemas, outputDir) {
|
|
191
|
+
async function generateModuleFiles(module2, allSchemas, allEnums, outputDir) {
|
|
191
192
|
const moduleOutputPath = import_path.default.join(outputDir, module2.moduleName);
|
|
192
193
|
if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
|
|
193
194
|
console.log(import_chalk.default.cyan(`
|
|
194
195
|
Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
|
|
195
|
-
const
|
|
196
|
+
const schemasToImport = [...module2.schemas].sort();
|
|
197
|
+
const enumsToImport = [...module2.enums].sort();
|
|
198
|
+
const indexContent = [`// This file is auto-generated.
|
|
199
|
+
|
|
200
|
+
export * from './config';`];
|
|
196
201
|
let configContent = `/* eslint-disable */
|
|
197
202
|
// This file is auto-generated.
|
|
198
203
|
|
|
199
204
|
import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
|
|
200
205
|
`;
|
|
201
|
-
if (
|
|
206
|
+
if (schemasToImport.length > 0) configContent += `import type { ${schemasToImport.join(", ")} } from './types';
|
|
202
207
|
`;
|
|
203
208
|
const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
|
|
204
209
|
const actionsValue = Object.values(module2.actions).map((a) => {
|
|
@@ -217,14 +222,32 @@ ${actionsValue}
|
|
|
217
222
|
`;
|
|
218
223
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
|
|
219
224
|
console.log(import_chalk.default.gray(` \u2713 config.ts`));
|
|
220
|
-
|
|
225
|
+
if (schemasToImport.length > 0) {
|
|
226
|
+
if (enumsToImport.length > 0) {
|
|
227
|
+
let enumsContent = `// This file is auto-generated.
|
|
221
228
|
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
`;
|
|
230
|
+
for (const enumName of enumsToImport) {
|
|
231
|
+
const enumDef = allEnums.get(enumName);
|
|
232
|
+
if (enumDef) {
|
|
233
|
+
enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
|
|
234
|
+
`;
|
|
235
|
+
enumsContent += `export type ${enumName} = typeof ${enumName}[number];
|
|
236
|
+
|
|
237
|
+
`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "enums.ts"), enumsContent);
|
|
241
|
+
console.log(import_chalk.default.gray(` \u2713 enums.ts`));
|
|
242
|
+
indexContent.push(`export * from './enums';`);
|
|
243
|
+
}
|
|
224
244
|
let typesContent = `// This file is auto-generated.
|
|
225
245
|
|
|
226
246
|
`;
|
|
227
|
-
|
|
247
|
+
if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
|
|
248
|
+
|
|
249
|
+
`;
|
|
250
|
+
for (const typeName of schemasToImport) {
|
|
228
251
|
const parsedSchema = allSchemas.get(typeName);
|
|
229
252
|
if (parsedSchema) {
|
|
230
253
|
if (parsedSchema.description) typesContent += `/**
|
|
@@ -240,8 +263,11 @@ ${prop.example ? ` * @example ${JSON.stringify(prop.example)}
|
|
|
240
263
|
` : ""} */
|
|
241
264
|
`;
|
|
242
265
|
let propType = prop.type;
|
|
243
|
-
if (prop.enum) propType =
|
|
244
|
-
else if (prop.items) propType = `${toPascalCase(prop.items.name)}[]`;
|
|
266
|
+
if (prop.enum) propType = `${toPascalCase(typeName)}${toPascalCase(prop.name)}Enum`;
|
|
267
|
+
else if (prop.items) propType = prop.items.name ? `${toPascalCase(prop.items.name)}[]` : `${prop.items.type || "unknown"}[]`;
|
|
268
|
+
else if (prop.type === "object") propType = prop.properties && prop.properties.length > 0 ? `{
|
|
269
|
+
${prop.properties.map((p) => ` ${p.name}${p.isRequired ? "" : "?"}: ${p.type};
|
|
270
|
+
`).join("")} }` : `Record<string, unknown>`;
|
|
245
271
|
typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
|
|
246
272
|
`;
|
|
247
273
|
}
|
|
@@ -255,12 +281,14 @@ ${prop.example ? ` * @example ${JSON.stringify(prop.example)}
|
|
|
255
281
|
indexContent.push(`export * from './types';`);
|
|
256
282
|
let validationContent = `// This file is auto-generated.
|
|
257
283
|
import { z } from 'zod';
|
|
284
|
+
`;
|
|
285
|
+
if (enumsToImport.length > 0) validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
|
|
258
286
|
|
|
259
287
|
`;
|
|
260
|
-
for (const typeName of
|
|
288
|
+
for (const typeName of schemasToImport) {
|
|
261
289
|
const parsedSchema = allSchemas.get(typeName);
|
|
262
290
|
if (parsedSchema) {
|
|
263
|
-
let zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
291
|
+
let zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p, typeName)}`).join(",\n");
|
|
264
292
|
validationContent += `export const ${typeName}Schema = z.object({
|
|
265
293
|
${zodShape}
|
|
266
294
|
});
|
|
@@ -272,10 +300,12 @@ ${zodShape}
|
|
|
272
300
|
console.log(import_chalk.default.gray(` \u2713 validation.ts`));
|
|
273
301
|
indexContent.push(`export * from './validation';`);
|
|
274
302
|
let mocksContent = `// This file is auto-generated.
|
|
275
|
-
import type { ${
|
|
303
|
+
import type { ${schemasToImport.join(", ")} } from './types';
|
|
304
|
+
`;
|
|
305
|
+
if (enumsToImport.length > 0) mocksContent += `import { ${enumsToImport.join(", ")} } from './enums';
|
|
276
306
|
|
|
277
307
|
`;
|
|
278
|
-
for (const typeName of
|
|
308
|
+
for (const typeName of schemasToImport) {
|
|
279
309
|
const parsedSchema = allSchemas.get(typeName);
|
|
280
310
|
if (parsedSchema) {
|
|
281
311
|
let mockObject = {};
|
|
@@ -294,35 +324,35 @@ import type { ${typesToImport.join(", ")} } from './types';
|
|
|
294
324
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "index.ts"), indexContent.join("\n"));
|
|
295
325
|
console.log(import_chalk.default.gray(` \u2713 index.ts`));
|
|
296
326
|
}
|
|
297
|
-
function _propToZod(prop) {
|
|
327
|
+
function _propToZod(prop, parentName) {
|
|
298
328
|
let zodString = "z.any()";
|
|
299
|
-
const
|
|
329
|
+
const errorParams = { required_error: prop.description || generateFriendlyMessage(prop.name) };
|
|
300
330
|
switch (prop.type) {
|
|
301
331
|
case "string":
|
|
302
|
-
zodString = `z.string(${JSON.stringify(
|
|
303
|
-
if (prop.format === "email") zodString += `.email({ message: "
|
|
332
|
+
zodString = `z.string(${JSON.stringify(errorParams)})`;
|
|
333
|
+
if (prop.format === "email") zodString += `.email({ message: "Invalid email address" })`;
|
|
304
334
|
if (prop.format === "uuid") zodString += `.uuid()`;
|
|
305
|
-
if (prop.enum) zodString = `z.enum(${
|
|
335
|
+
if (prop.enum) zodString = `z.enum(${toPascalCase(parentName)}${toPascalCase(prop.name)}Enum, ${JSON.stringify(errorParams)})`;
|
|
306
336
|
break;
|
|
307
337
|
case "integer":
|
|
308
338
|
case "number":
|
|
309
|
-
zodString = `z.number(${JSON.stringify(
|
|
339
|
+
zodString = `z.number(${JSON.stringify(errorParams)})`;
|
|
310
340
|
break;
|
|
311
341
|
case "boolean":
|
|
312
|
-
zodString = `z.boolean(${JSON.stringify(
|
|
342
|
+
zodString = `z.boolean(${JSON.stringify(errorParams)})`;
|
|
313
343
|
break;
|
|
314
344
|
case "array":
|
|
315
|
-
zodString = `z.array(${prop.items ? _propToZod(prop.items) : "z.any()"})`;
|
|
345
|
+
zodString = `z.array(${prop.items ? _propToZod(prop.items, `${parentName}${toPascalCase(prop.name)}Item`) : "z.any()"})`;
|
|
316
346
|
break;
|
|
317
347
|
case "object":
|
|
318
|
-
let shape = "z.
|
|
348
|
+
let shape = "z.record(z.unknown())";
|
|
319
349
|
if (prop.properties) shape = `z.object({
|
|
320
|
-
${prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n")}
|
|
350
|
+
${prop.properties.map((p) => ` ${p.name}: ${_propToZod(p, `${parentName}${toPascalCase(prop.name)}`)}`).join(",\n")}
|
|
321
351
|
})`;
|
|
322
352
|
zodString = shape;
|
|
323
353
|
break;
|
|
324
354
|
}
|
|
325
|
-
if (!prop.isRequired) zodString
|
|
355
|
+
if (!prop.isRequired) zodString = zodString.replace(/\(.*\)/, "").trim() + ".optional()";
|
|
326
356
|
if (prop.isNullable) zodString += ".nullable()";
|
|
327
357
|
return zodString;
|
|
328
358
|
}
|
|
@@ -353,7 +383,7 @@ function _propToMock(prop) {
|
|
|
353
383
|
}
|
|
354
384
|
}
|
|
355
385
|
async function runGenerator(options) {
|
|
356
|
-
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (
|
|
386
|
+
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Diamond Edition)..."));
|
|
357
387
|
import_dotenv.default.config({ path: options.envPath });
|
|
358
388
|
const specUrl = process.env.OPENAPI_SPEC_URL || "./swagger.json";
|
|
359
389
|
try {
|
|
@@ -361,13 +391,12 @@ async function runGenerator(options) {
|
|
|
361
391
|
\u23F3 Step 1: Dereferencing spec from ${specUrl}...`));
|
|
362
392
|
const spec = await import_swagger_parser.default.dereference(specUrl);
|
|
363
393
|
console.log(import_chalk.default.green("\u2713 Spec fully dereferenced."));
|
|
364
|
-
debugLog("Dereferenced Spec", spec);
|
|
365
394
|
console.log(import_chalk.default.blue("\n\u23F3 Step 2: Parsing spec with intelligent grouping..."));
|
|
366
|
-
const { modules, allSchemas } = parseSpecToModules(spec);
|
|
395
|
+
const { modules, allSchemas, allEnums } = parseSpecToModules(spec);
|
|
367
396
|
console.log(import_chalk.default.green(`\u2713 Found and grouped ${modules.size} logical modules.`));
|
|
368
397
|
console.log(import_chalk.default.blue("\n\u23F3 Step 3: Generating all module files..."));
|
|
369
398
|
for (const module2 of modules.values()) {
|
|
370
|
-
await generateModuleFiles(module2, allSchemas, options.output);
|
|
399
|
+
await generateModuleFiles(module2, allSchemas, allEnums, options.output);
|
|
371
400
|
}
|
|
372
401
|
console.log(import_chalk.default.green("\n\u2713 All module files generated successfully."));
|
|
373
402
|
console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! Your development platform is ready."));
|