api-core-lib 12.0.56 → 12.0.58

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 +35 -262
  2. package/package.json +2 -1
package/dist/cli.cjs CHANGED
@@ -25,269 +25,41 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/cli.ts
27
27
  var import_commander = require("commander");
28
- var import_path3 = __toESM(require("path"), 1);
29
-
30
- // src/generator/index.ts
31
- var import_path2 = __toESM(require("path"), 1);
32
- var import_axios = __toESM(require("axios"), 1);
33
- var import_chalk2 = __toESM(require("chalk"), 1);
34
- var import_dotenv = __toESM(require("dotenv"), 1);
35
- var import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"), 1);
36
-
37
- // src/generator/spec-parser.ts
38
- function parseSpecToModules(spec) {
39
- const modules = {};
40
- for (const apiPath in spec.paths) {
41
- for (const method in spec.paths[apiPath]) {
42
- const endpoint = spec.paths[apiPath][method];
43
- if (!endpoint.tags || endpoint.tags.length === 0) continue;
44
- const tagName = endpoint.tags[0];
45
- const moduleName = sanitizeTagName(tagName) + "Api";
46
- if (!modules[moduleName]) {
47
- const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
48
- modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {}, types: /* @__PURE__ */ new Set() };
49
- }
50
- const { inputType, outputType } = getInputOutputTypes(endpoint);
51
- [inputType, outputType].forEach((t) => {
52
- if (t && !["unknown", "undefined", "any", "QueryOptions", "Promise"].includes(t)) {
53
- modules[moduleName].types.add(t.replace("[]", ""));
54
- }
55
- });
56
- const actionName = sanitizeActionName(endpoint.operationId);
57
- const relativePath = apiPath.replace(modules[moduleName].baseEndpoint, "") || "/";
58
- modules[moduleName].actions[actionName] = {
59
- method: method.toUpperCase(),
60
- path: relativePath,
61
- description: endpoint.summary || "No description available.",
62
- hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
63
- autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
64
- invalidates: [],
65
- _inputType: inputType,
66
- _outputType: outputType
67
- };
68
- }
69
- }
70
- return modules;
71
- }
72
- function getInputOutputTypes(endpoint) {
73
- const requestBodySchema = endpoint.requestBody?.content?.["application/json"]?.schema;
74
- const successResponseSchema = endpoint.responses?.["200"]?.content?.["application/json"]?.schema || endpoint.responses?.["201"]?.content?.["application/json"]?.schema;
75
- let outputType = "unknown";
76
- if (successResponseSchema) {
77
- if (successResponseSchema.$ref) {
78
- outputType = refToTypeName(successResponseSchema.$ref);
79
- } else if (successResponseSchema.type === "array" && successResponseSchema.items?.$ref) {
80
- outputType = `${refToTypeName(successResponseSchema.items.$ref)}[]`;
81
- }
82
- }
83
- let inputType = "undefined";
84
- if (requestBodySchema) {
85
- if (requestBodySchema.$ref) {
86
- inputType = refToTypeName(requestBodySchema.$ref);
87
- } else if (requestBodySchema.type === "object") {
88
- inputType = "any";
89
- }
90
- } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
91
- inputType = "QueryOptions";
92
- }
93
- return { inputType, outputType };
94
- }
95
- function sanitizeTagName(tagName) {
96
- return tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "");
97
- }
98
- function sanitizeActionName(operationId) {
99
- if (!operationId) return `unnamedAction_${Date.now()}`;
100
- const name = operationId.split("_").slice(1).join("_");
101
- return name.charAt(0).toLowerCase() + name.slice(1).replace(/_v\d+$/, "");
102
- }
103
- function refToTypeName(ref) {
104
- if (!ref) return "unknown";
105
- return ref.split("/").pop() || "unknown";
106
- }
107
-
108
- // src/generator/file-generator.ts
109
- var import_fs = __toESM(require("fs"), 1);
110
28
  var import_path = __toESM(require("path"), 1);
111
- var import_chalk = __toESM(require("chalk"), 1);
112
- var import_json_schema_to_typescript = require("json-schema-to-typescript");
113
- async function generateModuleFiles(moduleName, moduleData, spec, outputDir) {
114
- console.log(import_chalk.default.cyan(`
115
- Generating module: ${moduleName}`));
116
- const moduleFolderPath = import_path.default.join(outputDir, moduleName);
117
- if (!import_fs.default.existsSync(moduleFolderPath)) {
118
- import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
119
- }
120
- await generateTypesFile(moduleFolderPath, moduleName, moduleData.types, spec);
121
- await generateConfigFile(moduleFolderPath, moduleName, moduleData);
122
- }
123
- async function generateTypesFile(moduleFolderPath, moduleName, typeNames, spec) {
124
- const typesCount = typeNames.size;
125
- if (typesCount === 0) {
126
- console.log(import_chalk.default.yellow(` - No types found for this module. types.ts will be empty.`));
127
- } else {
128
- console.log(import_chalk.default.gray(` - Found ${typesCount} types to generate for types.ts...`));
129
- }
130
- let typesContent = `// This file is auto-generated by the API generator. Do not edit.
131
-
132
- `;
133
- const allSchemas = spec.components?.schemas || {};
134
- for (const typeName of typeNames) {
135
- const schema = allSchemas[typeName];
136
- if (schema) {
137
- try {
138
- const tsType = await (0, import_json_schema_to_typescript.compile)(schema, typeName, {
139
- bannerComment: "",
140
- additionalProperties: false,
141
- style: {
142
- bracketSpacing: true,
143
- printWidth: 120,
144
- semi: true,
145
- singleQuote: true,
146
- tabWidth: 2,
147
- trailingComma: "es5",
148
- useTabs: false
149
- }
150
- });
151
- typesContent += tsType + "\n";
152
- } catch (compileError) {
153
- console.error(import_chalk.default.red(` - Error compiling type "${typeName}" for module "${moduleName}":`));
154
- console.error(import_chalk.default.red(` ${compileError.message}`));
155
- }
156
- } else {
157
- console.log(import_chalk.default.yellow(` - Warning: Schema for type "${typeName}" not found in dereferenced spec, skipping.`));
158
- }
159
- }
160
- const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
161
- import_fs.default.writeFileSync(typesFilePath, typesContent);
162
- }
163
- async function generateConfigFile(moduleFolderPath, moduleName, moduleData) {
164
- const typeNamesArray = [...moduleData.types];
165
- const actionsCount = Object.keys(moduleData.actions).length;
166
- console.log(import_chalk.default.gray(` - Found ${actionsCount} actions to generate for config.ts...`));
167
- const typesImportStatement = typeNamesArray.length > 0 ? `import type { ${typeNamesArray.join(", ")} } from './types';` : ``;
168
- const actionsTypeParts = Object.entries(moduleData.actions).map(
169
- ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
170
- );
171
- const actionsTypeDefinition = `{
172
- ${actionsTypeParts.join("\n")}
173
- }`;
174
- const actionsValueParts = Object.entries(moduleData.actions).map(
175
- ([actionName, actionData]) => {
176
- const { _inputType, _outputType, ...config } = actionData;
177
- return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
178
- }
179
- );
180
- const actionsValueDefinition = `{
181
- ${actionsValueParts.join(",\n")}
182
- }`;
183
- const configContent = `
184
- import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
185
- ${typesImportStatement}
186
-
187
- export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
188
- baseEndpoint: '${moduleData.baseEndpoint}',
189
- actions: ${actionsValueDefinition},
190
- };
191
- `;
192
- const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
193
- import_fs.default.writeFileSync(configFilePath, configContent);
194
- }
195
-
196
- // src/generator/spec-preprocessor.ts
197
- function toPascalCase(str) {
198
- return str.replace(/_v\d+$/, "").split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
199
- }
200
- function normalizeSchema(schema, schemas, baseName, suffix) {
201
- if (!schema) return schema;
202
- if (schema.$ref) return schema;
203
- if (schema.type === "object" && schema.properties) {
204
- const typeName = `${toPascalCase(baseName)}${suffix}Dto`;
205
- if (!schemas[typeName]) {
206
- schemas[typeName] = schema;
207
- }
208
- return { $ref: `#/components/schemas/${typeName}` };
209
- }
210
- if (schema.type === "array" && schema.items) {
211
- schema.items = normalizeSchema(schema.items, schemas, baseName, suffix.replace("[]", ""));
212
- return schema;
213
- }
214
- return schema;
215
- }
216
- function preprocessSpec(spec) {
217
- if (!spec.components) spec.components = {};
218
- if (!spec.components.schemas) spec.components.schemas = {};
219
- for (const apiPath in spec.paths) {
220
- for (const method in spec.paths[apiPath]) {
221
- const endpoint = spec.paths[apiPath][method];
222
- const operationId = endpoint.operationId;
223
- if (!operationId) continue;
224
- const requestBodySchema = endpoint.requestBody?.content?.["application/json"]?.schema;
225
- if (requestBodySchema) {
226
- endpoint.requestBody.content["application/json"].schema = normalizeSchema(
227
- requestBodySchema,
228
- spec.components.schemas,
229
- operationId,
230
- "Request"
231
- );
232
- }
233
- if (endpoint.responses) {
234
- for (const statusCode in endpoint.responses) {
235
- const responseSchema = endpoint.responses[statusCode]?.content?.["application/json"]?.schema;
236
- if (responseSchema) {
237
- endpoint.responses[statusCode].content["application/json"].schema = normalizeSchema(
238
- responseSchema,
239
- spec.components.schemas,
240
- operationId,
241
- "Response"
242
- );
243
- }
244
- }
245
- }
246
- }
247
- }
248
- return spec;
249
- }
250
29
 
251
30
  // src/generator/index.ts
31
+ var import_chalk = __toESM(require("chalk"), 1);
32
+ var import_dotenv = __toESM(require("dotenv"), 1);
33
+ var import_openapi_typescript_codegen = require("openapi-typescript-codegen");
252
34
  async function runGenerator(options) {
253
- console.log(import_chalk2.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
254
- console.log(import_chalk2.default.gray(`Output directory: ${options.output}`));
255
- console.log(import_chalk2.default.gray(`.env path: ${options.envPath}`));
256
- console.log("\n" + import_chalk2.default.blue("Step 1: Loading environment variables..."));
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..."));
257
39
  import_dotenv.default.config({ path: options.envPath });
258
40
  const specUrl = getSpecUrl();
259
- console.log(import_chalk2.default.green("\u2713 Environment variables loaded."));
41
+ console.log(import_chalk.default.green("\u2713 Environment variables loaded successfully."));
42
+ console.log(import_chalk.default.gray(`Fetching spec from: ${specUrl}`));
260
43
  try {
261
- console.log("\n" + import_chalk2.default.blue(`Step 2: Fetching OpenAPI spec from ${specUrl}...`));
262
- const response = await import_axios.default.get(specUrl, { timeout: 15e3 });
263
- const originalSpec = response.data;
264
- if (!originalSpec.openapi || !originalSpec.paths) {
265
- throw new Error('Invalid OpenAPI specification file. "openapi" or "paths" property is missing.');
266
- }
267
- console.log(import_chalk2.default.green("\u2713 OpenAPI spec fetched successfully."));
268
- console.log(import_chalk2.default.blue("Step 2.2: Preprocessing spec to normalize schemas..."));
269
- const normalizedSpec = preprocessSpec(originalSpec);
270
- console.log(import_chalk2.default.green("\u2713 Spec has been normalized successfully."));
271
- console.log(import_chalk2.default.blue("Step 2.5: Dereferencing all $refs in the spec..."));
272
- const dereferencedSpec = await import_json_schema_ref_parser.default.dereference(normalizedSpec);
273
- console.log(import_chalk2.default.green("\u2713 Spec dereferenced successfully."));
274
- console.log("\n" + import_chalk2.default.blue("Step 3: Parsing spec and generating API modules..."));
275
- const modules = parseSpecToModules(normalizedSpec);
276
- const modulesCount = Object.keys(modules).length;
277
- console.log(import_chalk2.default.gray(`Found ${modulesCount} modules to generate...`));
278
- if (modulesCount === 0) {
279
- console.log(import_chalk2.default.yellow("Warning: No modules found."));
280
- }
281
- const modulesOutputPath = import_path2.default.join(options.output, "modules");
282
- for (const moduleName in modules) {
283
- const moduleData = modules[moduleName];
284
- await generateModuleFiles(moduleName, moduleData, dereferencedSpec, modulesOutputPath);
285
- }
286
- if (modulesCount > 0) {
287
- console.log(import_chalk2.default.green("\u2713 All custom modules generated."));
288
- }
289
- console.log(import_chalk2.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
290
- console.log(import_chalk2.default.bold.cyan(options.output));
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
60
+ });
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}`));
291
63
  } catch (error) {
292
64
  handleGenerationError(error);
293
65
  }
@@ -295,24 +67,25 @@ async function runGenerator(options) {
295
67
  function getSpecUrl() {
296
68
  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);
297
69
  if (!specUrl) {
298
- console.error(import_chalk2.default.red.bold("\n\u274C Error: API specification URL not found."));
70
+ 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."));
299
72
  process.exit(1);
300
73
  }
301
74
  return specUrl;
302
75
  }
303
76
  function handleGenerationError(error) {
304
- console.error(import_chalk2.default.red.bold("\n\u274C An error occurred during generation:"));
305
- console.error(import_chalk2.default.red(`Error Message: ${error.message}`));
77
+ 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."));
306
79
  if (error.stack) {
307
- console.error(import_chalk2.default.gray(error.stack));
80
+ console.error(import_chalk.default.gray(error.stack));
308
81
  }
309
82
  process.exit(1);
310
83
  }
311
84
 
312
85
  // src/cli.ts
313
86
  console.log("API Core Lib - Code Generator");
314
- import_commander.program.option("-o, --output <path>", "Output directory for generated files", "src/lib/api-generated").option("--env-path <path>", ".env file path", import_path3.default.resolve(process.cwd(), ".env")).action((options) => {
315
- const absoluteOutputPath = import_path3.default.resolve(process.cwd(), options.output);
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);
316
89
  runGenerator({ ...options, output: absoluteOutputPath });
317
90
  });
318
91
  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.56",
3
+ "version": "12.0.58",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -46,6 +46,7 @@
46
46
  "fs-extra": "^11.3.1",
47
47
  "json-schema-to-typescript": "^15.0.4",
48
48
  "openapi-typescript": "^6.2.4",
49
+ "openapi-typescript-codegen": "^0.29.0",
49
50
  "path": "^0.12.7",
50
51
  "uuid": "^9.0.1"
51
52
  },