api-core-lib 12.0.36 → 12.0.38

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 +131 -29
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -33,8 +33,18 @@ var import_path = __toESM(require("path"), 1);
33
33
  var import_axios = __toESM(require("axios"), 1);
34
34
  var import_chalk = __toESM(require("chalk"), 1);
35
35
  var import_dotenv = __toESM(require("dotenv"), 1);
36
+ var import_child_process = require("child_process");
36
37
 
37
38
  // src/generator/module-parser.ts
39
+ function sanitizeActionName(operationId) {
40
+ if (!operationId) return `unnamedAction_${Date.now()}`;
41
+ const name = operationId.split("_").slice(1).join("_");
42
+ return name.charAt(0).toLowerCase() + name.slice(1).replace(/_v\d+$/, "");
43
+ }
44
+ function refToTypeName(ref) {
45
+ if (!ref) return "unknown";
46
+ return ref.split("/").pop() || "unknown";
47
+ }
38
48
  function parseSpecToModules(spec) {
39
49
  const modules = {};
40
50
  for (const apiPath in spec.paths) {
@@ -44,22 +54,37 @@ function parseSpecToModules(spec) {
44
54
  const tagName = endpoint.tags[0];
45
55
  const moduleName = tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "") + "Api";
46
56
  if (!modules[moduleName]) {
47
- modules[moduleName] = {
48
- baseEndpoint: "",
49
- // سيتم ملء المسار الكامل في path
50
- actions: {}
51
- };
57
+ const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
58
+ modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {} };
59
+ }
60
+ const requestBody = endpoint.requestBody?.content?.["application/json"]?.schema;
61
+ const successResponse = endpoint.responses["200"] || endpoint.responses["201"];
62
+ const responseSchema = successResponse?.content?.["application/json"]?.schema;
63
+ let outputType = "unknown";
64
+ if (responseSchema) {
65
+ if (responseSchema.$ref) {
66
+ outputType = refToTypeName(responseSchema.$ref);
67
+ } else if (responseSchema.type === "array" && responseSchema.items?.$ref) {
68
+ outputType = `${refToTypeName(responseSchema.items.$ref)}[]`;
69
+ }
52
70
  }
53
- const actionName = endpoint.operationId || method + apiPath.replace(/[\/{}]+/g, "_");
71
+ let inputType = "undefined";
72
+ if (requestBody) {
73
+ inputType = refToTypeName(requestBody.$ref);
74
+ } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
75
+ inputType = "QueryOptions";
76
+ }
77
+ const actionName = sanitizeActionName(endpoint.operationId);
78
+ const relativePath = apiPath.replace(modules[moduleName].baseEndpoint, "") || "/";
54
79
  modules[moduleName].actions[actionName] = {
55
80
  method: method.toUpperCase(),
56
- path: apiPath,
81
+ path: relativePath,
57
82
  description: endpoint.summary || "No description available.",
58
83
  hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
59
84
  autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
60
- // افتراض ذكي
61
- invalidates: []
62
- // يمكن تركه فارغًا
85
+ invalidates: [],
86
+ _inputType: inputType,
87
+ _outputType: outputType
63
88
  };
64
89
  }
65
90
  }
@@ -67,44 +92,121 @@ function parseSpecToModules(spec) {
67
92
  }
68
93
 
69
94
  // src/generator/index.ts
70
- var import_child_process = require("child_process");
71
95
  async function runGenerator(options) {
72
- console.log(import_chalk.default.cyan(`Starting API module generation...`));
96
+ console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
73
97
  console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
74
98
  console.log(import_chalk.default.gray(`.env path: ${options.envPath}`));
99
+ console.log("\n" + import_chalk.default.blue("Step 1: Loading environment variables..."));
75
100
  import_dotenv.default.config({ path: options.envPath });
76
- const apiUrl = process.env.API_URL_CONFIG;
77
- if (!apiUrl) {
78
- console.error(import_chalk.default.red("Error: API_URL or NEXT_PUBLIC_API_URL not found in .env file."));
101
+ 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);
102
+ if (!specUrl) {
103
+ console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
104
+ console.error(import_chalk.default.red("Please define either OPENAPI_SPEC_URL (recommended) or API_URL/NEXT_PUBLIC_API_URL in your .env file."));
79
105
  process.exit(1);
80
106
  }
107
+ console.log(import_chalk.default.green("\u2713 Environment variables loaded."));
108
+ const tempSpecPath = import_path.default.join(process.cwd(), `swagger-${Date.now()}.json`);
81
109
  try {
82
- const specUrl = `${apiUrl}/docs-json`;
83
- console.log(`Fetching OpenAPI spec from ${specUrl}...`);
84
- const response = await import_axios.default.get(specUrl);
110
+ console.log("\n" + import_chalk.default.blue(`Step 2: Fetching OpenAPI spec from ${specUrl}...`));
111
+ const response = await import_axios.default.get(specUrl, { timeout: 15e3 });
85
112
  const spec = response.data;
86
- const specPath = import_path.default.join(process.cwd(), "swagger.json");
87
- import_fs.default.writeFileSync(specPath, JSON.stringify(spec, null, 2));
88
- console.log(import_chalk.default.green(`\u2713 OpenAPI spec saved to ${specPath}`));
89
- console.log("Generating organized TypeScript types and services...");
113
+ if (!spec.openapi || !spec.paths) {
114
+ throw new Error('Invalid OpenAPI specification file. "openapi" or "paths" property is missing.');
115
+ }
116
+ import_fs.default.writeFileSync(tempSpecPath, JSON.stringify(spec, null, 2));
117
+ console.log(import_chalk.default.green(`\u2713 OpenAPI spec saved temporarily to ${tempSpecPath}`));
118
+ console.log("\n" + import_chalk.default.blue("Step 3: Generating organized TypeScript types..."));
90
119
  const typesOutputPath = import_path.default.join(options.output, "types");
91
- const generatorCommand = `npx @openapitools/openapi-generator-cli generate -i ${specPath} -g typescript-axios -o ${typesOutputPath} --additional-properties=supportsES6=true,useSingleRequestParameter=true,withInterfaces=true`;
92
- (0, import_child_process.execSync)(generatorCommand, { stdio: "inherit" });
120
+ const ignoreFilePath = import_path.default.join(typesOutputPath, ".openapi-generator-ignore");
121
+ const ignoreContent = `
122
+ # Ignore all documentation files
123
+ docs/
124
+ # Ignore git and helper files
125
+ .openapi-generator-ignore
126
+ .npmignore
127
+ git_push.sh
128
+ # Ignore the main generator metadata
129
+ .openapi-generator/
130
+ # We only want the generated models and apis
131
+ `;
132
+ if (!import_fs.default.existsSync(typesOutputPath)) {
133
+ import_fs.default.mkdirSync(typesOutputPath, { recursive: true });
134
+ }
135
+ import_fs.default.writeFileSync(ignoreFilePath, ignoreContent);
136
+ console.log(import_chalk.default.gray("\u2713 Created .openapi-generator-ignore to ensure clean output."));
137
+ console.log(import_chalk.default.gray(`Executing: ${ignoreContent}`));
138
+ (0, import_child_process.execSync)(ignoreContent, { stdio: "inherit" });
93
139
  console.log(import_chalk.default.green(`\u2713 Types generated successfully at ${typesOutputPath}`));
94
- console.log("Generating API modules for api-core-lib...");
140
+ console.log("\n" + import_chalk.default.blue("Step 4: Generating custom API modules..."));
95
141
  const modules = parseSpecToModules(spec);
96
142
  const modulesOutputPath = import_path.default.join(options.output, "modules");
97
143
  if (!import_fs.default.existsSync(modulesOutputPath)) {
98
144
  import_fs.default.mkdirSync(modulesOutputPath, { recursive: true });
99
145
  }
146
+ const allModelTypes = /* @__PURE__ */ new Set();
147
+ Object.values(modules).forEach((mod) => {
148
+ Object.values(mod.actions).forEach((action) => {
149
+ if (action._inputType && !["unknown", "undefined", "QueryOptions"].includes(action._inputType)) allModelTypes.add(action._inputType.replace("[]", ""));
150
+ if (action._outputType && !["unknown"].includes(action._outputType)) allModelTypes.add(action._outputType.replace("[]", ""));
151
+ });
152
+ });
100
153
  for (const moduleName in modules) {
154
+ const moduleData = modules[moduleName];
155
+ const fileName = `${moduleName}.module.ts`;
156
+ const filePath = import_path.default.join(modulesOutputPath, fileName);
157
+ const actionsTypeParts = Object.entries(moduleData.actions).map(([actionName, actionData]) => {
158
+ return ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`;
159
+ });
160
+ const actionsTypeDefinition = `{
161
+ ${actionsTypeParts.join("\n")}
162
+ }`;
163
+ const actionsValueParts = Object.entries(moduleData.actions).map(([actionName, actionData]) => {
164
+ const { _inputType, _outputType, ...config } = actionData;
165
+ return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
166
+ });
167
+ const actionsValueDefinition = `{
168
+ ${actionsValueParts.join(",\n")}
169
+ }`;
170
+ const fileContent = `/**
171
+ * This file is auto-generated by api-core-lib.
172
+ * Do not edit this file directly.
173
+ * @module ${moduleName}
174
+ */
175
+
176
+ import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
177
+ // [\u0625\u0635\u0644\u0627\u062D] \u0627\u0633\u062A\u064A\u0631\u0627\u062F \u0627\u0644\u0623\u0646\u0648\u0627\u0639 \u0645\u0646 \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0631\u0626\u064A\u0633\u064A \u0627\u0644\u0645\u0646\u0638\u0645
178
+ import type { ${[...allModelTypes].join(", ")} } from '../types';
179
+
180
+ /**
181
+ * Defines the configuration for the ${moduleName} API module.
182
+ */
183
+ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
184
+ baseEndpoint: '${moduleData.baseEndpoint}',
185
+ actions: ${actionsValueDefinition},
186
+ };
187
+ `;
188
+ import_fs.default.writeFileSync(filePath, fileContent);
189
+ console.log(import_chalk.default.green(`\u2713 Module generated: ${fileName}`));
101
190
  }
102
- import_fs.default.unlinkSync(specPath);
103
- console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete!"));
191
+ console.log(import_chalk.default.green("\u2713 All custom modules generated."));
192
+ console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
193
+ console.log(import_chalk.default.bold.cyan(options.output));
104
194
  } catch (error) {
105
- console.error(import_chalk.default.red("\nAn error occurred during generation:"));
106
- console.error(error.message);
195
+ console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
196
+ if (error.response) {
197
+ console.error(import_chalk.default.red(`Network Error: ${error.message} (Status: ${error.response.status})`));
198
+ } else if (error.stderr) {
199
+ console.error(import_chalk.default.red(`Command Execution Error: ${error.message}`));
200
+ console.error(import_chalk.default.gray(error.stderr.toString()));
201
+ } else {
202
+ console.error(import_chalk.default.red(error.message));
203
+ }
107
204
  process.exit(1);
205
+ } finally {
206
+ if (import_fs.default.existsSync(tempSpecPath)) {
207
+ import_fs.default.unlinkSync(tempSpecPath);
208
+ console.log(import_chalk.default.gray("\nTemporary spec file cleaned up."));
209
+ }
108
210
  }
109
211
  }
110
212
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.36",
3
+ "version": "12.0.38",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {