api-core-lib 12.0.72 → 12.0.73

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 +88 -94
  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
- var getModuleName = (opId) => `${opId.split("_")[0].replace(/Controller$/, "")}Api`;
86
- var getActionName = (opId) => toCamelCase(opId.split("_")[1] || opId);
85
+ var sanitizeForModuleName = (tagName) => `${toPascalCase(tagName.replace(/[^a-zA-Z0-9]/g, " "))}Api`;
86
+ var getActionName = (opId) => toCamelCase(opId.replace(/^(central|tenant)/i, "").replace(/_v\d+$/, ""));
87
87
  var findCommonPath = (paths) => {
88
88
  if (!paths || paths.length === 0) return "/";
89
89
  const sorted = [...paths].sort();
@@ -94,14 +94,21 @@ var findCommonPath = (paths) => {
94
94
  const prefix = first.substring(0, i);
95
95
  return prefix.substring(0, prefix.lastIndexOf("/") + 1) || "/";
96
96
  };
97
- function parseSchema(name, schema) {
97
+ function parseSchema(name, schema, allEnums) {
98
98
  const properties = [];
99
+ const enums = {};
99
100
  if (schema.properties) {
100
101
  for (const propName in schema.properties) {
101
102
  const propSchema = schema.properties[propName];
102
- let items = void 0;
103
+ let itemSchema;
103
104
  if (propSchema.type === "array" && propSchema.items) {
104
- items = parseSchema("item", propSchema.items).properties[0];
105
+ const itemTypeName = `${toPascalCase(name)}${toPascalCase(propName)}Item`;
106
+ itemSchema = parseSchema(itemTypeName, propSchema.items, allEnums).properties[0];
107
+ }
108
+ if (propSchema.enum) {
109
+ const enumName = `${toPascalCase(name)}${toPascalCase(propName)}Enum`;
110
+ enums[propName] = propSchema.enum;
111
+ if (!allEnums.has(enumName)) allEnums.set(enumName, { name: enumName, values: propSchema.enum });
105
112
  }
106
113
  properties.push({
107
114
  name: propName,
@@ -112,24 +119,29 @@ function parseSchema(name, schema) {
112
119
  example: propSchema.example,
113
120
  enum: propSchema.enum,
114
121
  format: propSchema.format,
115
- items,
116
- // نستخدم المتغير الذي تم حسابه بأمان
117
- properties: propSchema.properties ? parseSchema("sub-object", propSchema).properties : void 0
122
+ items: itemSchema,
123
+ properties: propSchema.properties ? parseSchema(`${name}${toPascalCase(propName)}`, propSchema, allEnums).properties : void 0
118
124
  });
119
125
  }
120
126
  }
121
- return { name, description: schema.description, properties };
127
+ return { name, description: schema.description, properties, enums };
122
128
  }
123
129
  function parseSpecToModules(spec) {
124
130
  const modules = /* @__PURE__ */ new Map();
125
131
  const allSchemas = /* @__PURE__ */ new Map();
132
+ const allEnums = /* @__PURE__ */ new Map();
126
133
  const modulePaths = /* @__PURE__ */ new Map();
127
134
  const registerSchema = (schema, baseName) => {
128
135
  if (!schema) return "unknown";
129
- if (schema.type === "array" && schema.items) return `${registerSchema(schema.items, `${baseName}Item`)}[]`;
136
+ if (schema.type === "array" && schema.items) {
137
+ const itemSchema = schema.items;
138
+ const itemBaseName = `${baseName}Item`;
139
+ if (itemSchema.properties || itemSchema.type === "object") return `${registerSchema(itemSchema, itemBaseName)}[]`;
140
+ return `${itemSchema.type || "unknown"}[]`;
141
+ }
130
142
  if (schema.type === "object" || schema.properties || schema.allOf || !schema.type) {
131
143
  const typeName = toPascalCase(baseName.replace(/_v\d+(Request|Response)$/, "$1"));
132
- if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema));
144
+ if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema, allEnums));
133
145
  return typeName;
134
146
  }
135
147
  return schema.type === "integer" ? "number" : schema.type || "unknown";
@@ -140,10 +152,11 @@ function parseSpecToModules(spec) {
140
152
  for (const method in pathItem) {
141
153
  if (!Object.values(import_openapi_types.OpenAPIV3.HttpMethods).includes(method)) continue;
142
154
  const endpoint = pathItem[method];
143
- if (!endpoint.operationId) continue;
144
- const moduleName = getModuleName(endpoint.operationId);
155
+ if (!endpoint.tags || endpoint.tags.length === 0 || !endpoint.operationId) continue;
156
+ const tagName = endpoint.tags[0];
157
+ const moduleName = sanitizeForModuleName(tagName);
145
158
  if (!modules.has(moduleName)) {
146
- modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set() });
159
+ modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set(), enums: /* @__PURE__ */ new Set() });
147
160
  modulePaths.set(moduleName, []);
148
161
  }
149
162
  const currentModule = modules.get(moduleName);
@@ -159,6 +172,8 @@ function parseSpecToModules(spec) {
159
172
  const cleanType = t.replace("[]", "");
160
173
  if (cleanType && !["unknown", "undefined", "void", "any", "QueryOptions", "Promise"].includes(cleanType)) {
161
174
  currentModule.schemas.add(cleanType);
175
+ const schemaDef = allSchemas.get(cleanType);
176
+ if (schemaDef) Object.keys(schemaDef.enums).forEach((propName) => currentModule.enums.add(`${toPascalCase(cleanType)}${toPascalCase(propName)}Enum`));
162
177
  }
163
178
  });
164
179
  const actionName = getActionName(endpoint.operationId);
@@ -166,28 +181,28 @@ function parseSpecToModules(spec) {
166
181
  }
167
182
  }
168
183
  modules.forEach((mod, name) => {
169
- const basePath = findCommonPath(modulePaths.get(name));
170
- mod.baseEndpoint = basePath;
171
- Object.values(mod.actions).forEach((action) => {
172
- const relativePath = action.path.replace(basePath, "").replace(/^\//, "");
173
- action.path = relativePath === "" ? "/" : relativePath;
174
- });
184
+ mod.baseEndpoint = findCommonPath(modulePaths.get(name));
185
+ Object.values(mod.actions).forEach((action) => action.path = action.path.replace(mod.baseEndpoint, "").replace(/^\//, "") || "/");
175
186
  });
176
187
  debugLog("Final Parsed Modules", Object.fromEntries(modules));
177
- return { modules, allSchemas };
188
+ return { modules, allSchemas, allEnums };
178
189
  }
179
- async function generateModuleFiles(module2, allSchemas, outputDir) {
190
+ async function generateModuleFiles(module2, allSchemas, allEnums, outputDir) {
180
191
  const moduleOutputPath = import_path.default.join(outputDir, module2.moduleName);
181
192
  if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
182
193
  console.log(import_chalk.default.cyan(`
183
194
  Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
184
- const typesToImport = [...module2.schemas].sort();
195
+ const schemasToImport = [...module2.schemas].sort();
196
+ const enumsToImport = [...module2.enums].sort();
197
+ const indexContent = [`// This file is auto-generated.
198
+
199
+ export * from './config';`];
185
200
  let configContent = `/* eslint-disable */
186
201
  // This file is auto-generated.
187
202
 
188
203
  import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
189
204
  `;
190
- if (typesToImport.length > 0) configContent += `import type { ${typesToImport.join(", ")} } from './types';
205
+ if (schemasToImport.length > 0) configContent += `import type { ${schemasToImport.join(", ")} } from './types';
191
206
  `;
192
207
  const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
193
208
  const actionsValue = Object.values(module2.actions).map((a) => {
@@ -206,14 +221,32 @@ ${actionsValue}
206
221
  `;
207
222
  import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
208
223
  console.log(import_chalk.default.gray(` \u2713 config.ts`));
209
- const indexContent = [`// This file is auto-generated.
224
+ if (schemasToImport.length > 0) {
225
+ if (enumsToImport.length > 0) {
226
+ let enumsContent = `// This file is auto-generated.
210
227
 
211
- export * from './config';`];
212
- if (typesToImport.length > 0) {
228
+ `;
229
+ for (const enumName of enumsToImport) {
230
+ const enumDef = allEnums.get(enumName);
231
+ if (enumDef) {
232
+ enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
233
+ `;
234
+ enumsContent += `export type ${enumName} = typeof ${enumName}[number];
235
+
236
+ `;
237
+ }
238
+ }
239
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "enums.ts"), enumsContent);
240
+ console.log(import_chalk.default.gray(` \u2713 enums.ts`));
241
+ indexContent.push(`export * from './enums';`);
242
+ }
213
243
  let typesContent = `// This file is auto-generated.
214
244
 
215
245
  `;
216
- for (const typeName of typesToImport) {
246
+ if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
247
+
248
+ `;
249
+ for (const typeName of schemasToImport) {
217
250
  const parsedSchema = allSchemas.get(typeName);
218
251
  if (parsedSchema) {
219
252
  if (parsedSchema.description) typesContent += `/**
@@ -228,7 +261,10 @@ export * from './config';`];
228
261
  ${prop.example ? ` * @example ${JSON.stringify(prop.example)}
229
262
  ` : ""} */
230
263
  `;
231
- const propType = prop.enum ? prop.enum.map((e) => typeof e === "string" ? `'${e}'` : e).join(" | ") : prop.items ? `${toPascalCase(prop.items.name)}[]` : prop.type;
264
+ let propType = prop.type;
265
+ if (prop.enum) propType = `${toPascalCase(typeName)}${toPascalCase(prop.name)}Enum`;
266
+ else if (prop.items) propType = prop.items.name ? `${toPascalCase(prop.items.name)}[]` : `${prop.items.type || "unknown"}[]`;
267
+ else if (prop.type === "object") propType = prop.properties && prop.properties.length > 0 ? `Record<string, unknown>` : `Record<string, unknown>`;
232
268
  typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
233
269
  `;
234
270
  }
@@ -242,9 +278,11 @@ ${prop.example ? ` * @example ${JSON.stringify(prop.example)}
242
278
  indexContent.push(`export * from './types';`);
243
279
  let validationContent = `// This file is auto-generated.
244
280
  import { z } from 'zod';
281
+ `;
282
+ if (enumsToImport.length > 0) validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
245
283
 
246
284
  `;
247
- for (const typeName of typesToImport) {
285
+ for (const typeName of schemasToImport) {
248
286
  const parsedSchema = allSchemas.get(typeName);
249
287
  if (parsedSchema) {
250
288
  let zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
@@ -259,10 +297,12 @@ ${zodShape}
259
297
  console.log(import_chalk.default.gray(` \u2713 validation.ts`));
260
298
  indexContent.push(`export * from './validation';`);
261
299
  let mocksContent = `// This file is auto-generated.
262
- import type { ${typesToImport.join(", ")} } from './types';
300
+ import type { ${schemasToImport.join(", ")} } from './types';
301
+ `;
302
+ if (enumsToImport.length > 0) mocksContent += `import { ${enumsToImport.join(", ")} } from './enums';
263
303
 
264
304
  `;
265
- for (const typeName of typesToImport) {
305
+ for (const typeName of schemasToImport) {
266
306
  const parsedSchema = allSchemas.get(typeName);
267
307
  if (parsedSchema) {
268
308
  let mockObject = {};
@@ -346,79 +386,34 @@ ${shape}
346
386
  }
347
387
  return chain;
348
388
  }
349
- function _generateUUID() {
350
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
351
- const r = Math.random() * 16 | 0;
352
- const v = c === "x" ? r : r & 3 | 8;
353
- return v.toString(16);
354
- });
355
- }
356
- function _getRandomInt(min, max) {
357
- min = Math.ceil(min);
358
- max = Math.floor(max);
359
- return Math.floor(Math.random() * (max - min + 1)) + min;
360
- }
361
- var _firstNames = ["Ahmed", "Fatima", "Mohammed", "Zainab", "Ali", "Nour"];
362
- var _lastNames = ["Al-Masri", "Khan", "Hassan", "Abbas", "Said"];
363
389
  function _propToMock(prop) {
364
- if (prop.example !== void 0) {
365
- return prop.example;
366
- }
367
- if (prop.enum && prop.enum.length > 0) {
368
- return prop.enum[_getRandomInt(0, prop.enum.length - 1)];
369
- }
390
+ if (prop.example) return prop.example;
391
+ if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
392
+ if (prop.enum) return prop.enum[0];
370
393
  switch (prop.type) {
371
394
  case "string":
372
- if (prop.format === "email") return `user${_getRandomInt(1, 1e3)}@example.com`;
373
- if (prop.format === "uuid") return _generateUUID();
374
- if (prop.format === "url") return "https://www.example.com";
375
- if (prop.format === "datetime") return (/* @__PURE__ */ new Date()).toISOString();
376
- const name = prop.name.toLowerCase();
377
- if (name.includes("image") || name.includes("avatar") || name.includes("logo") || name.includes("picture")) return `https://via.placeholder.com/${_getRandomInt(150, 400)}`;
378
- if (name.includes("firstname")) return _firstNames[_getRandomInt(0, _firstNames.length - 1)];
379
- if (name.includes("lastname")) return _lastNames[_getRandomInt(0, _lastNames.length - 1)];
380
- if (name.includes("name")) return `${_firstNames[_getRandomInt(0, _firstNames.length - 1)]} ${_lastNames[_getRandomInt(0, _lastNames.length - 1)]}`;
381
- if (name.includes("city")) return "Riyadh";
382
- if (name.includes("country")) return "Saudi Arabia";
383
- if (name.includes("phone")) return `+1-${_getRandomInt(100, 999)}-${_getRandomInt(100, 999)}-${_getRandomInt(1e3, 9999)}`;
384
- if (name.includes("description") || name.includes("comment")) return "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
385
- const minLen = prop.minLength || 8;
386
- const maxLen = prop.maxLength || 16;
387
- return `Mock${toPascalCase(prop.name)}`.padEnd(_getRandomInt(minLen, maxLen), "x");
395
+ if (prop.format === "email") return "test@example.com";
396
+ if (prop.format === "uuid") return "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
397
+ return `Mock ${toPascalCase(prop.name)}`;
388
398
  case "integer":
389
399
  case "number":
390
- const min = prop.minimum === void 0 ? 1 : prop.minimum;
391
- const max = prop.maximum === void 0 ? 1e3 : prop.maximum;
392
- const randomNum = _getRandomInt(min, max);
393
- return prop.type === "integer" ? Math.floor(randomNum) : randomNum + Math.random();
400
+ return 1;
394
401
  case "boolean":
395
- return Math.random() > 0.5;
402
+ return true;
396
403
  case "array":
397
- const minItems = prop.minItems || 1;
398
- const maxItems = prop.maxItems || 3;
399
- const count = _getRandomInt(minItems, maxItems);
400
- if (!prop.items) return [];
401
- const items = [];
402
- for (let i = 0; i < count; i++) {
403
- items.push(_propToMock(prop.items));
404
- }
405
- return items;
404
+ return prop.items ? [_propToMock(prop.items)] : [];
406
405
  case "object":
407
406
  const mock = {};
408
- if (prop.properties) {
409
- prop.properties.forEach((p) => {
410
- if (p.isRequired || Math.random() > 0.3) {
411
- mock[p.name] = _propToMock(p);
412
- }
413
- });
414
- }
407
+ if (prop.properties) prop.properties.forEach((p) => {
408
+ mock[p.name] = _propToMock(p);
409
+ });
415
410
  return mock;
416
411
  default:
417
412
  return null;
418
413
  }
419
414
  }
420
415
  async function runGenerator(options) {
421
- console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Phoenix Edition)..."));
416
+ console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Sapphire Edition)..."));
422
417
  import_dotenv.default.config({ path: options.envPath });
423
418
  const specUrl = process.env.OPENAPI_SPEC_URL || "./swagger.json";
424
419
  try {
@@ -426,13 +421,12 @@ async function runGenerator(options) {
426
421
  \u23F3 Step 1: Dereferencing spec from ${specUrl}...`));
427
422
  const spec = await import_swagger_parser.default.dereference(specUrl);
428
423
  console.log(import_chalk.default.green("\u2713 Spec fully dereferenced."));
429
- debugLog("Dereferenced Spec", spec);
430
424
  console.log(import_chalk.default.blue("\n\u23F3 Step 2: Parsing spec with intelligent grouping..."));
431
- const { modules, allSchemas } = parseSpecToModules(spec);
425
+ const { modules, allSchemas, allEnums } = parseSpecToModules(spec);
432
426
  console.log(import_chalk.default.green(`\u2713 Found and grouped ${modules.size} logical modules.`));
433
427
  console.log(import_chalk.default.blue("\n\u23F3 Step 3: Generating all module files..."));
434
428
  for (const module2 of modules.values()) {
435
- await generateModuleFiles(module2, allSchemas, options.output);
429
+ await generateModuleFiles(module2, allSchemas, allEnums, options.output);
436
430
  }
437
431
  console.log(import_chalk.default.green("\n\u2713 All module files generated successfully."));
438
432
  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.72",
3
+ "version": "12.0.73",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {