api-core-lib 12.0.58 → 12.0.60

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 +329 -30
  2. package/package.json +2 -1
package/dist/cli.cjs CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
9
12
  var __copyProps = (to, from, except, desc) => {
10
13
  if (from && typeof from === "object" || typeof from === "function") {
11
14
  for (let key of __getOwnPropNames(from))
@@ -23,43 +26,340 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
26
  mod
24
27
  ));
25
28
 
29
+ // node_modules/openapi-types/dist/index.js
30
+ var require_dist = __commonJS({
31
+ "node_modules/openapi-types/dist/index.js"(exports2) {
32
+ "use strict";
33
+ exports2.__esModule = true;
34
+ exports2.OpenAPIV2 = exports2.OpenAPIV3 = void 0;
35
+ var OpenAPIV32;
36
+ (function(OpenAPIV33) {
37
+ var HttpMethods;
38
+ (function(HttpMethods2) {
39
+ HttpMethods2["GET"] = "get";
40
+ HttpMethods2["PUT"] = "put";
41
+ HttpMethods2["POST"] = "post";
42
+ HttpMethods2["DELETE"] = "delete";
43
+ HttpMethods2["OPTIONS"] = "options";
44
+ HttpMethods2["HEAD"] = "head";
45
+ HttpMethods2["PATCH"] = "patch";
46
+ HttpMethods2["TRACE"] = "trace";
47
+ })(HttpMethods = OpenAPIV33.HttpMethods || (OpenAPIV33.HttpMethods = {}));
48
+ })(OpenAPIV32 = exports2.OpenAPIV3 || (exports2.OpenAPIV3 = {}));
49
+ var OpenAPIV2;
50
+ (function(OpenAPIV22) {
51
+ var HttpMethods;
52
+ (function(HttpMethods2) {
53
+ HttpMethods2["GET"] = "get";
54
+ HttpMethods2["PUT"] = "put";
55
+ HttpMethods2["POST"] = "post";
56
+ HttpMethods2["DELETE"] = "delete";
57
+ HttpMethods2["OPTIONS"] = "options";
58
+ HttpMethods2["HEAD"] = "head";
59
+ HttpMethods2["PATCH"] = "patch";
60
+ })(HttpMethods = OpenAPIV22.HttpMethods || (OpenAPIV22.HttpMethods = {}));
61
+ })(OpenAPIV2 = exports2.OpenAPIV2 || (exports2.OpenAPIV2 = {}));
62
+ }
63
+ });
64
+
26
65
  // src/cli.ts
27
66
  var import_commander = require("commander");
28
- var import_path = __toESM(require("path"), 1);
67
+ var import_path2 = __toESM(require("path"), 1);
29
68
 
30
69
  // src/generator/index.ts
70
+ var import_fs = __toESM(require("fs"), 1);
71
+ var import_path = __toESM(require("path"), 1);
31
72
  var import_chalk = __toESM(require("chalk"), 1);
32
73
  var import_dotenv = __toESM(require("dotenv"), 1);
33
- var import_openapi_typescript_codegen = require("openapi-typescript-codegen");
74
+ var import_swagger_parser = __toESM(require("@apidevtools/swagger-parser"), 1);
75
+ var import_openapi_types = __toESM(require_dist(), 1);
76
+ var import_json_schema_to_typescript = require("json-schema-to-typescript");
77
+ function preprocessSpec(spec) {
78
+ if (!spec.components) spec.components = {};
79
+ if (!spec.components.schemas) spec.components.schemas = {};
80
+ const schemas = spec.components.schemas;
81
+ for (const apiPath in spec.paths) {
82
+ const pathItem = spec.paths[apiPath];
83
+ if (!pathItem) continue;
84
+ for (const method in pathItem) {
85
+ if (!Object.values(import_openapi_types.OpenAPIV3.HttpMethods).includes(method)) continue;
86
+ const endpoint = pathItem[method];
87
+ const baseName = _generateBaseNameForSchema(method, apiPath, endpoint.operationId);
88
+ const requestBody = endpoint.requestBody;
89
+ if (requestBody?.content?.["application/json"]?.schema) {
90
+ requestBody.content["application/json"].schema = _normalizeSchema(
91
+ requestBody.content["application/json"].schema,
92
+ schemas,
93
+ baseName,
94
+ "Request"
95
+ );
96
+ }
97
+ if (endpoint.responses) {
98
+ for (const statusCode in endpoint.responses) {
99
+ const response = endpoint.responses[statusCode];
100
+ const mediaTypeObject = response.content?.["application/json"];
101
+ if (mediaTypeObject?.schema) {
102
+ mediaTypeObject.schema = _normalizeSchema(
103
+ mediaTypeObject.schema,
104
+ // مرر المخطط الأصلي
105
+ schemas,
106
+ baseName,
107
+ `Response${statusCode}`
108
+ );
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return spec;
115
+ }
116
+ function _normalizeSchema(schema, schemas, baseName, suffix) {
117
+ if (!schema || "$ref" in schema) {
118
+ return schema;
119
+ }
120
+ if (schema.type === "array" && schema.items) {
121
+ schema.items = _normalizeSchema(schema.items, schemas, baseName, suffix.replace("[]", "Item"));
122
+ return schema;
123
+ }
124
+ if (schema.type === "object" && schema.properties) {
125
+ const typeName = `${_toPascalCase(baseName)}${_toPascalCase(suffix)}Dto`;
126
+ if (!schemas[typeName]) {
127
+ schemas[typeName] = schema;
128
+ for (const propName in schema.properties) {
129
+ schema.properties[propName] = _normalizeSchema(
130
+ schema.properties[propName],
131
+ schemas,
132
+ baseName,
133
+ `${_toPascalCase(propName)}Property`
134
+ );
135
+ }
136
+ }
137
+ return { $ref: `#/components/schemas/${typeName}` };
138
+ }
139
+ return schema;
140
+ }
141
+ function _generateBaseNameForSchema(method, path3, operationId) {
142
+ if (operationId) return _toPascalCase(operationId.replace(/_v\d+$/, ""));
143
+ const pathPart = path3.replace(/[\/{}]/g, " ").trim();
144
+ return `${_toPascalCase(method)} ${_toPascalCase(pathPart)}`;
145
+ }
146
+ function parseSpecToModules(spec) {
147
+ const modules = {};
148
+ for (const apiPath in spec.paths) {
149
+ const pathItem = spec.paths[apiPath];
150
+ if (!pathItem) continue;
151
+ for (const method in pathItem) {
152
+ if (!Object.values(import_openapi_types.OpenAPIV3.HttpMethods).includes(method)) continue;
153
+ const endpoint = pathItem[method];
154
+ if (!endpoint.tags || endpoint.tags.length === 0) continue;
155
+ const tagName = endpoint.tags[0];
156
+ const moduleName = _sanitizeTagName(tagName) + "Api";
157
+ if (!modules[moduleName]) {
158
+ modules[moduleName] = { baseEndpoint: _findCommonBasePath(spec, tagName), actions: {}, types: /* @__PURE__ */ new Set() };
159
+ }
160
+ const currentModule = modules[moduleName];
161
+ const successResponseKey = Object.keys(endpoint.responses).find((code) => code.startsWith("2"));
162
+ const successResponse = successResponseKey ? endpoint.responses[successResponseKey] : void 0;
163
+ const responseSchema = successResponse?.content?.["application/json"]?.schema;
164
+ const outputType = successResponseKey === "204" ? "void" : _extractTypeNameFromSchema(responseSchema);
165
+ const requestBody = endpoint.requestBody;
166
+ const requestSchema = requestBody?.content?.["application/json"]?.schema;
167
+ let inputType = "undefined";
168
+ if (requestSchema) {
169
+ inputType = _extractTypeNameFromSchema(requestSchema);
170
+ } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
171
+ inputType = "QueryOptions";
172
+ }
173
+ [inputType, outputType].forEach((t) => {
174
+ if (t && !["unknown", "undefined", "void", "any", "QueryOptions", "Promise"].includes(t)) {
175
+ currentModule.types.add(t.replace("[]", ""));
176
+ }
177
+ });
178
+ const actionName = _sanitizeActionName(endpoint.operationId, method, apiPath);
179
+ const relativePath = apiPath.replace(currentModule.baseEndpoint, "").replace(/^\//, "");
180
+ currentModule.actions[actionName] = {
181
+ method: method.toUpperCase(),
182
+ path: relativePath === "" ? "/" : relativePath,
183
+ description: endpoint.summary || "",
184
+ hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
185
+ autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
186
+ invalidates: [],
187
+ _inputType: inputType,
188
+ _outputType: outputType
189
+ };
190
+ }
191
+ }
192
+ return modules;
193
+ }
194
+ function _extractTypeNameFromSchema(schema) {
195
+ if (!schema) return "unknown";
196
+ if ("$ref" in schema && schema.$ref) return _refToTypeName(schema.$ref);
197
+ if (!("$ref" in schema)) {
198
+ if (schema.type === "array" && schema.items) {
199
+ const itemType = _extractTypeNameFromSchema(schema.items);
200
+ return itemType !== "unknown" ? `${itemType}[]` : "unknown[]";
201
+ }
202
+ if (schema.type && ["string", "number", "boolean", "integer"].includes(schema.type)) {
203
+ return schema.type === "integer" ? "number" : schema.type;
204
+ }
205
+ }
206
+ return "unknown";
207
+ }
208
+ async function generateModuleFiles(moduleName, moduleData, spec, outputDir) {
209
+ console.log(import_chalk.default.gray(` Generating files for module: ${import_chalk.default.bold(moduleName)}...`));
210
+ const moduleFolderPath = import_path.default.join(outputDir, moduleName);
211
+ if (!import_fs.default.existsSync(moduleFolderPath)) {
212
+ import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
213
+ }
214
+ await _generateTypesFile(moduleFolderPath, moduleName, moduleData.types, spec);
215
+ await _generateConfigFile(moduleFolderPath, moduleName, moduleData);
216
+ }
217
+ async function _generateTypesFile(moduleFolderPath, moduleName, typeNames, spec) {
218
+ if (typeNames.size === 0) {
219
+ console.log(import_chalk.default.yellow(` - No types found for module "${moduleName}". Skipping types.ts generation.`));
220
+ const typesFilePath2 = import_path.default.join(moduleFolderPath, "types.ts");
221
+ import_fs.default.writeFileSync(typesFilePath2, `// No types found for this module.
222
+ `);
223
+ return;
224
+ }
225
+ console.log(import_chalk.default.gray(` - Generating types.ts with ${typeNames.size} types...`));
226
+ let typesContent = `// This file is auto-generated by the API generator. Do not edit.
227
+
228
+ `;
229
+ const allSchemas = spec.components?.schemas || {};
230
+ const compilationOptions = {
231
+ bannerComment: "",
232
+ style: { bracketSpacing: true, printWidth: 120, semi: true, singleQuote: true, tabWidth: 2, trailingComma: "es5", useTabs: false },
233
+ additionalProperties: false,
234
+ declareExternallyReferenced: true
235
+ };
236
+ const rootSchemaForCompilation = {
237
+ $id: "allSchemas",
238
+ // بدلاً من "definitions", قم ببناء المسار الصحيح
239
+ components: {
240
+ schemas: allSchemas
241
+ },
242
+ type: "object",
243
+ properties: {}
244
+ };
245
+ for (const typeName of Array.from(typeNames).sort()) {
246
+ if (allSchemas[typeName]) {
247
+ try {
248
+ const tsType = await (0, import_json_schema_to_typescript.compile)(rootSchemaForCompilation, typeName, compilationOptions);
249
+ typesContent += tsType + "\n";
250
+ } catch (compileError) {
251
+ console.error(import_chalk.default.red(` - Error compiling type "${typeName}": ${compileError.message}`));
252
+ }
253
+ } else {
254
+ console.log(import_chalk.default.yellow(` - Warning: Schema for type "${typeName}" not found, skipping.`));
255
+ }
256
+ }
257
+ const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
258
+ import_fs.default.writeFileSync(typesFilePath, typesContent, { encoding: "utf-8" });
259
+ }
260
+ async function _generateConfigFile(moduleFolderPath, moduleName, moduleData) {
261
+ const actionsCount = Object.keys(moduleData.actions).length;
262
+ console.log(import_chalk.default.gray(` - Generating config.ts with ${actionsCount} actions...`));
263
+ const typeNamesArray = [...moduleData.types].sort();
264
+ const typesImportStatement = typeNamesArray.length > 0 ? `import type { ${typeNamesArray.join(", ")} } from './types';` : `// No types to import for this module`;
265
+ const actionsTypeParts = Object.entries(moduleData.actions).map(
266
+ ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType || "undefined"}, ${actionData._outputType || "unknown"}>;`
267
+ );
268
+ const actionsTypeDefinition = `{
269
+ ${actionsTypeParts.join("\n")}
270
+ }`;
271
+ const actionsValueParts = Object.entries(moduleData.actions).map(
272
+ ([actionName, actionData]) => {
273
+ const { _inputType, _outputType, ...config } = actionData;
274
+ return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
275
+ }
276
+ );
277
+ const actionsValueDefinition = `{
278
+ ${actionsValueParts.join(",\n")}
279
+ }`;
280
+ const configContent = `/* eslint-disable */
281
+ // This file is auto-generated by the API generator. Do not edit.
282
+
283
+ import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib'; // \u0627\u0641\u062A\u0631\u0636 \u0623\u0646 \u0647\u0630\u0627 \u0627\u0644\u0645\u0633\u0627\u0631 \u0635\u062D\u064A\u062D
284
+ ${typesImportStatement}
285
+
286
+ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
287
+ baseEndpoint: '${moduleData.baseEndpoint}',
288
+ actions: ${actionsValueDefinition},
289
+ };
290
+ `;
291
+ const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
292
+ import_fs.default.writeFileSync(configFilePath, configContent.trim(), { encoding: "utf-8" });
293
+ }
294
+ function _toPascalCase(str) {
295
+ return str.replace(/[^a-zA-Z0-9]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase()).replace(/\s+/g, "");
296
+ }
297
+ function _refToTypeName(ref) {
298
+ if (!ref) return "unknown";
299
+ return ref.split("/").pop() || "unknown";
300
+ }
301
+ function _sanitizeTagName(tagName) {
302
+ return tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "");
303
+ }
304
+ function _sanitizeActionName(operationId, method, path3) {
305
+ if (operationId) {
306
+ const name = operationId.split("_").slice(1).join("_");
307
+ const sanitized = name.charAt(0).toLowerCase() + name.slice(1).replace(/_v\d+$/, "");
308
+ return sanitized || "action";
309
+ }
310
+ const pathPart = path3.replace(/[\/{}]/g, "_").replace(/^_|_$/g, "");
311
+ return `${method.toLowerCase()}_${pathPart}`;
312
+ }
313
+ function _findCommonBasePath(spec, tagName) {
314
+ const paths = Object.keys(spec.paths).filter((p) => {
315
+ const pathItem = spec.paths[p];
316
+ if (!pathItem) return false;
317
+ for (const method in pathItem) {
318
+ const endpoint = pathItem[method];
319
+ if (endpoint.tags?.includes(tagName)) return true;
320
+ }
321
+ return false;
322
+ });
323
+ if (paths.length === 0) return "/";
324
+ let commonPrefix = paths[0];
325
+ for (let i = 1; i < paths.length; i++) {
326
+ while (paths[i].indexOf(commonPrefix) !== 0) {
327
+ commonPrefix = commonPrefix.substring(0, commonPrefix.length - 1);
328
+ }
329
+ }
330
+ return commonPrefix.substring(0, commonPrefix.lastIndexOf("/") + 1) || "/";
331
+ }
34
332
  async function runGenerator(options) {
35
- console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Client Generation..."));
36
- console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
37
- console.log(import_chalk.default.gray(`.env path: ${options.envPath}`));
38
- console.log("\n" + import_chalk.default.blue("Step 1: Loading environment variables..."));
333
+ console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
39
334
  import_dotenv.default.config({ path: options.envPath });
40
335
  const specUrl = getSpecUrl();
41
- console.log(import_chalk.default.green("\u2713 Environment variables loaded successfully."));
42
- console.log(import_chalk.default.gray(`Fetching spec from: ${specUrl}`));
336
+ console.log(import_chalk.default.blue("\u2713 Step 1: Environment variables loaded."));
43
337
  try {
44
- console.log("\n" + import_chalk.default.blue("Step 2: Generating API client from OpenAPI spec..."));
45
- await (0, import_openapi_typescript_codegen.generate)({
46
- input: specUrl,
47
- // مسار ملف المواصفات أو الرابط المباشر
48
- output: options.output,
49
- // مجلد المخرجات الرئيسي
50
- clientName: "ApiClient",
51
- // يمكنك تسمية العميل الرئيسي (اختياري)
52
- useOptions: true,
53
- // يضيف خيارات إضافية للدوال (مثل الفلاتر والترتيب)
54
- useUnionTypes: false,
55
- // استخدم الواجهات (interfaces) بدلاً من أنواع الاتحاد (union types)
56
- exportSchemas: true,
57
- // يقوم بتصدير جميع الـ schemas في ملف models.ts
58
- exportServices: true
59
- // يقوم بتوليد services بناءً على الـ tags
338
+ console.log(import_chalk.default.blue(`
339
+ \u23F3 Step 2: Fetching and validating spec from ${specUrl}...`));
340
+ const initialSpec = await import_swagger_parser.default.validate(specUrl, {
341
+ dereference: { circular: "ignore" }
60
342
  });
61
- console.log(import_chalk.default.bold.green("\n\u{1F389} API client generation complete!"));
62
- console.log(import_chalk.default.bold.cyan(`All files are located in: ${options.output}`));
343
+ console.log(import_chalk.default.green("\u2713 Spec fetched and validated successfully."));
344
+ console.log(import_chalk.default.blue("\n\u23F3 Step 3: Preprocessing spec to normalize inline schemas..."));
345
+ const normalizedSpec = preprocessSpec(initialSpec);
346
+ console.log(import_chalk.default.green("\u2713 Spec has been normalized successfully."));
347
+ console.log(import_chalk.default.blue("\n\u23F3 Step 4: Parsing spec and generating API modules structure..."));
348
+ const modules = parseSpecToModules(normalizedSpec);
349
+ const modulesCount = Object.keys(modules).length;
350
+ console.log(import_chalk.default.green(`\u2713 Found ${modulesCount} modules to generate.`));
351
+ if (modulesCount === 0) {
352
+ console.log(import_chalk.default.yellow("Warning: No modules were found in the spec."));
353
+ }
354
+ console.log(import_chalk.default.blue("\n\u23F3 Step 5: Generating module files..."));
355
+ const modulesOutputPath = import_path.default.join(options.output, "modules");
356
+ for (const moduleName in modules) {
357
+ const moduleData = modules[moduleName];
358
+ await generateModuleFiles(moduleName, moduleData, normalizedSpec, modulesOutputPath);
359
+ }
360
+ console.log(import_chalk.default.green("\u2713 All module files generated."));
361
+ console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete!"));
362
+ console.log(import_chalk.default.bold.cyan(` Output directory: ${options.output}`));
63
363
  } catch (error) {
64
364
  handleGenerationError(error);
65
365
  }
@@ -68,14 +368,13 @@ function getSpecUrl() {
68
368
  const specUrl = process.env.OPENAPI_SPEC_URL || (process.env.API_URL ? `${process.env.API_URL}/docs-json` : null) || (process.env.NEXT_PUBLIC_API_URL ? `${process.env.NEXT_PUBLIC_API_URL}/docs-json` : null);
69
369
  if (!specUrl) {
70
370
  console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
71
- console.log(import_chalk.default.yellow("Please set OPENAPI_SPEC_URL, API_URL, or NEXT_PUBLIC_API_URL in your .env file."));
72
371
  process.exit(1);
73
372
  }
74
373
  return specUrl;
75
374
  }
76
375
  function handleGenerationError(error) {
77
376
  console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
78
- console.error(import_chalk.default.red(error.message || "An unknown error occurred."));
377
+ console.error(import_chalk.default.red(`Error Message: ${error.message}`));
79
378
  if (error.stack) {
80
379
  console.error(import_chalk.default.gray(error.stack));
81
380
  }
@@ -84,8 +383,8 @@ function handleGenerationError(error) {
84
383
 
85
384
  // src/cli.ts
86
385
  console.log("API Core Lib - Code Generator");
87
- import_commander.program.option("-o, --output <path>", "Output directory for generated files", "src/lib/api-generated").option("--env-path <path>", ".env file path", import_path.default.resolve(process.cwd(), ".env")).action((options) => {
88
- const absoluteOutputPath = import_path.default.resolve(process.cwd(), options.output);
386
+ import_commander.program.option("-o, --output <path>", "Output directory for generated files", "src/lib/api-generated").option("--env-path <path>", ".env file path", import_path2.default.resolve(process.cwd(), ".env")).action((options) => {
387
+ const absoluteOutputPath = import_path2.default.resolve(process.cwd(), options.output);
89
388
  runGenerator({ ...options, output: absoluteOutputPath });
90
389
  });
91
390
  import_commander.program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.58",
3
+ "version": "12.0.60",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -36,6 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@apidevtools/json-schema-ref-parser": "^14.2.0",
39
+ "@apidevtools/swagger-parser": "^12.0.0",
39
40
  "axios": "^1.6.8",
40
41
  "axios-retry": "^4.1.0",
41
42
  "chalk": "^4.1.2",