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.
Files changed (2) hide show
  1. package/dist/cli.cjs +89 -60
  2. 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: 4, colors: true }));
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, validation) => {
98
- const friendlyName = fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
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 items = void 0;
104
+ let itemSchema;
114
105
  if (propSchema.type === "array" && propSchema.items) {
115
- items = parseSchema("item", propSchema.items).properties[0];
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) return `${registerSchema(schema.items, `${baseName}Item`)}[]`;
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
- const actionName = getActionName(endpoint.operationId);
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
- const basePath = findCommonPath(modulePaths.get(name));
181
- mod.baseEndpoint = basePath;
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 typesToImport = [...module2.schemas].sort();
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 (typesToImport.length > 0) configContent += `import type { ${typesToImport.join(", ")} } from './types';
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
- const indexContent = [`// This file is auto-generated.
225
+ if (schemasToImport.length > 0) {
226
+ if (enumsToImport.length > 0) {
227
+ let enumsContent = `// This file is auto-generated.
221
228
 
222
- export * from './config';`];
223
- if (typesToImport.length > 0) {
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
- for (const typeName of typesToImport) {
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 = prop.enum.map((e) => typeof e === "string" ? `'${e}'` : e).join(" | ");
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 typesToImport) {
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 { ${typesToImport.join(", ")} } from './types';
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 typesToImport) {
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 requiredMsg = { required_error: prop.description || generateFriendlyMessage(prop.name, "required") };
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(requiredMsg)})`;
303
- if (prop.format === "email") zodString += `.email({ message: "${generateFriendlyMessage(prop.name, "email")}" })`;
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(${JSON.stringify(prop.enum)}, ${JSON.stringify(requiredMsg)})`;
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(requiredMsg)})`;
339
+ zodString = `z.number(${JSON.stringify(errorParams)})`;
310
340
  break;
311
341
  case "boolean":
312
- zodString = `z.boolean(${JSON.stringify(requiredMsg)})`;
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.any()";
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 += ".optional()";
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 (Phoenix Edition)..."));
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."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.68",
3
+ "version": "12.0.70",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {