api-core-lib 12.0.46 → 12.0.48

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 +115 -110
  2. package/package.json +2 -1
package/dist/cli.cjs CHANGED
@@ -33,74 +33,7 @@ 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");
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
- }
48
- function parseSpecToModules(spec) {
49
- const modules = {};
50
- for (const apiPath in spec.paths) {
51
- for (const method in spec.paths[apiPath]) {
52
- const endpoint = spec.paths[apiPath][method];
53
- if (!endpoint.tags || endpoint.tags.length === 0) continue;
54
- const tagName = endpoint.tags[0];
55
- const moduleName = tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "") + "Api";
56
- if (!modules[moduleName]) {
57
- const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
58
- modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {}, types: /* @__PURE__ */ new Set() };
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
- }
70
- }
71
- let inputType = "undefined";
72
- if (requestBody) {
73
- if (requestBody.$ref) {
74
- inputType = refToTypeName(requestBody.$ref);
75
- } else if (requestBody.type === "object") {
76
- inputType = "any";
77
- }
78
- } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
79
- inputType = "QueryOptions";
80
- }
81
- [inputType, outputType].forEach((t) => {
82
- if (t && t !== "unknown" && t !== "undefined" && t !== "any" && t !== "QueryOptions") {
83
- modules[moduleName].types.add(t.replace("[]", ""));
84
- }
85
- });
86
- const actionName = sanitizeActionName(endpoint.operationId);
87
- const relativePath = apiPath.replace(modules[moduleName].baseEndpoint, "") || "/";
88
- modules[moduleName].actions[actionName] = {
89
- method: method.toUpperCase(),
90
- path: relativePath,
91
- description: endpoint.summary || "No description available.",
92
- hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
93
- autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
94
- invalidates: [],
95
- _inputType: inputType,
96
- _outputType: outputType
97
- };
98
- }
99
- }
100
- return modules;
101
- }
102
-
103
- // src/generator/index.ts
36
+ var import_json_schema_to_typescript = require("json-schema-to-typescript");
104
37
  async function runGenerator(options) {
105
38
  console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
106
39
  console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
@@ -110,50 +43,20 @@ async function runGenerator(options) {
110
43
  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);
111
44
  if (!specUrl) {
112
45
  console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
113
- console.error(import_chalk.default.red("Please define either OPENAPI_SPEC_URL (recommended) or API_URL/NEXT_PUBLIC_API_URL in your .env file."));
46
+ console.error(import_chalk.default.red("Please define either OPENAPI_SPEC_URL or API_URL/NEXT_PUBLIC_API_URL in your .env file."));
114
47
  process.exit(1);
115
48
  }
116
49
  console.log(import_chalk.default.green("\u2713 Environment variables loaded."));
117
50
  let tempSpecPath = "";
118
- let ignoreFilePath = "";
119
51
  try {
120
52
  console.log("\n" + import_chalk.default.blue(`Step 2: Fetching OpenAPI spec from ${specUrl}...`));
121
- tempSpecPath = import_path.default.join(process.cwd(), `swagger-${Date.now()}.json`);
122
53
  const response = await import_axios.default.get(specUrl, { timeout: 15e3 });
123
54
  const spec = response.data;
124
- if (!spec.openapi || !spec.paths) {
125
- throw new Error('Invalid OpenAPI specification file. "openapi" or "paths" property is missing.');
55
+ if (!spec.openapi || !spec.paths || !spec.components?.schemas) {
56
+ throw new Error('Invalid OpenAPI specification file. "openapi", "paths", or "components.schemas" property is missing.');
126
57
  }
127
- import_fs.default.writeFileSync(tempSpecPath, JSON.stringify(spec, null, 2));
128
- console.log(import_chalk.default.green(`\u2713 OpenAPI spec saved temporarily to ${tempSpecPath}`));
129
- console.log("\n" + import_chalk.default.blue("Step 3: Generating organized TypeScript types..."));
130
- const typesOutputPath = import_path.default.join(options.output, "types");
131
- ignoreFilePath = import_path.default.join(process.cwd(), `.openapi-generator-ignore-${Date.now()}`);
132
- const ignoreContent = `# Main generator metadata
133
- .openapi-generator/
134
- # Documentation files
135
- docs/
136
- # Git helper files
137
- .gitignore
138
- git_push.sh
139
- # NPM helper files
140
- .npmignore
141
- README.md`;
142
- import_fs.default.writeFileSync(ignoreFilePath, ignoreContent);
143
- console.log(import_chalk.default.gray("\u2713 Created temporary .openapi-generator-ignore file."));
144
- const generatorCommand = [
145
- "npx --yes @openapitools/openapi-generator-cli generate",
146
- `-i "${tempSpecPath}"`,
147
- `-g typescript-axios`,
148
- `-o "${typesOutputPath}"`,
149
- `--ignore-file-override "${ignoreFilePath}"`,
150
- "--global-property models,apis=false,supportingFiles=false",
151
- `--additional-properties=supportsES6=true,useSingleRequestParameter=true,withInterfaces=true,modelPropertyNaming=original`
152
- ].join(" ");
153
- console.log(import_chalk.default.gray(`Executing command: npx @openapitools/openapi-generator-cli...`));
154
- (0, import_child_process.execSync)(generatorCommand, { stdio: "inherit" });
155
- console.log(import_chalk.default.green(`\u2713 Types generated successfully at ${typesOutputPath}`));
156
- console.log("\n" + import_chalk.default.blue("Step 4: Generating custom API modules..."));
58
+ console.log(import_chalk.default.green(`\u2713 OpenAPI spec fetched successfully.`));
59
+ console.log("\n" + import_chalk.default.blue("Step 3: Parsing spec and generating API modules..."));
157
60
  const modules = parseSpecToModules(spec);
158
61
  const modulesOutputPath = import_path.default.join(options.output, "modules");
159
62
  if (!import_fs.default.existsSync(modulesOutputPath)) {
@@ -166,8 +69,33 @@ README.md`;
166
69
  import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
167
70
  }
168
71
  const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
169
- const typesContent = moduleData.types.size ? `export type { ${[...moduleData.types].join(", ")} } from '../../types';` : `// No types used in this module`;
72
+ let typesContent = `// This file is auto-generated by the API generator. Do not edit.
73
+
74
+ `;
75
+ const allModuleTypes = getAllReferencedTypes(moduleData.types, spec.components.schemas);
76
+ for (const typeName of allModuleTypes) {
77
+ const schema = spec.components.schemas[typeName];
78
+ if (schema) {
79
+ const tsType = await (0, import_json_schema_to_typescript.compile)(schema, typeName, {
80
+ bannerComment: "",
81
+ // لا نريد تعليقات إضافية من المكتبة
82
+ additionalProperties: false,
83
+ // كن صارمًا مع الخصائص الإضافية
84
+ style: {
85
+ bracketSpacing: true,
86
+ printWidth: 120,
87
+ semi: true,
88
+ singleQuote: true,
89
+ tabWidth: 2,
90
+ trailingComma: "es5",
91
+ useTabs: false
92
+ }
93
+ });
94
+ typesContent += tsType + "\n";
95
+ }
96
+ }
170
97
  import_fs.default.writeFileSync(typesFilePath, typesContent);
98
+ const allTypeNames = [...allModuleTypes];
171
99
  const actionsTypeParts = Object.entries(moduleData.actions).map(
172
100
  ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
173
101
  );
@@ -184,9 +112,10 @@ ${actionsTypeParts.join("\n")}
184
112
  ${actionsValueParts.join(",\n")}
185
113
  }`;
186
114
  const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
115
+ const typesImportStatement = allTypeNames.length > 0 ? `import type { ${allTypeNames.join(", ")} } from './types';` : `// No custom types used in this module.`;
187
116
  const configContent = `
188
117
  import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
189
- import type { ${[...moduleData.types].join(", ")} } from './types';
118
+ ${typesImportStatement}
190
119
 
191
120
  export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
192
121
  baseEndpoint: '${moduleData.baseEndpoint}',
@@ -194,7 +123,7 @@ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
194
123
  };
195
124
  `;
196
125
  import_fs.default.writeFileSync(configFilePath, configContent);
197
- console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/`));
126
+ console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/ (config.ts, types.ts)`));
198
127
  }
199
128
  console.log(import_chalk.default.green("\u2713 All custom modules generated."));
200
129
  console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
@@ -212,14 +141,90 @@ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
212
141
  process.exit(1);
213
142
  } finally {
214
143
  if (tempSpecPath && import_fs.default.existsSync(tempSpecPath)) {
215
- import_fs.default.unlinkSync(tempSpecPath);
216
144
  console.log(import_chalk.default.gray("\nTemporary spec file cleaned up."));
217
145
  }
218
- if (ignoreFilePath && import_fs.default.existsSync(ignoreFilePath)) {
219
- import_fs.default.unlinkSync(ignoreFilePath);
220
- console.log(import_chalk.default.gray("Temporary ignore file cleaned up."));
146
+ }
147
+ }
148
+ function parseSpecToModules(spec) {
149
+ const modules = {};
150
+ for (const apiPath in spec.paths) {
151
+ for (const method in spec.paths[apiPath]) {
152
+ const endpoint = spec.paths[apiPath][method];
153
+ if (!endpoint.tags || endpoint.tags.length === 0) continue;
154
+ const tagName = endpoint.tags[0];
155
+ const moduleName = tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "") + "Api";
156
+ if (!modules[moduleName]) {
157
+ const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
158
+ modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {}, types: /* @__PURE__ */ new Set() };
159
+ }
160
+ const requestBody = endpoint.requestBody?.content?.["application/json"]?.schema;
161
+ const successResponse = endpoint.responses["200"] || endpoint.responses["201"];
162
+ const responseSchema = successResponse?.content?.["application/json"]?.schema;
163
+ let outputType = "unknown";
164
+ if (responseSchema) {
165
+ if (responseSchema.$ref) {
166
+ outputType = refToTypeName(responseSchema.$ref);
167
+ } else if (responseSchema.type === "array" && responseSchema.items?.$ref) {
168
+ outputType = `${refToTypeName(responseSchema.items.$ref)}[]`;
169
+ }
170
+ }
171
+ let inputType = "undefined";
172
+ if (requestBody) {
173
+ if (requestBody.$ref) {
174
+ inputType = refToTypeName(requestBody.$ref);
175
+ } else if (requestBody.type === "object") {
176
+ inputType = "any";
177
+ }
178
+ } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
179
+ inputType = "QueryOptions";
180
+ }
181
+ [inputType, outputType].forEach((t) => {
182
+ if (t && t !== "unknown" && t !== "undefined" && t !== "any" && t !== "QueryOptions") {
183
+ modules[moduleName].types.add(t.replace("[]", ""));
184
+ }
185
+ });
186
+ const actionName = sanitizeActionName(endpoint.operationId);
187
+ const relativePath = apiPath.replace(modules[moduleName].baseEndpoint, "") || "/";
188
+ modules[moduleName].actions[actionName] = {
189
+ method: method.toUpperCase(),
190
+ path: relativePath,
191
+ description: endpoint.summary || "No description available.",
192
+ hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
193
+ autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
194
+ invalidates: [],
195
+ _inputType: inputType,
196
+ _outputType: outputType
197
+ };
198
+ }
199
+ }
200
+ return modules;
201
+ }
202
+ function getAllReferencedTypes(initialTypes, allSchemas) {
203
+ const finalTypes = new Set(initialTypes);
204
+ const queue = [...initialTypes];
205
+ while (queue.length > 0) {
206
+ const typeName = queue.shift();
207
+ if (!typeName) continue;
208
+ const schemaString = JSON.stringify(allSchemas[typeName] || {});
209
+ const references = schemaString.match(/"\$ref":\s*"#\/components\/schemas\/([^"]+)"/g) || [];
210
+ for (const ref of references) {
211
+ const referencedTypeName = ref.split("/").pop()?.replace(/"/g, "") || "";
212
+ if (referencedTypeName && !finalTypes.has(referencedTypeName)) {
213
+ finalTypes.add(referencedTypeName);
214
+ queue.push(referencedTypeName);
215
+ }
221
216
  }
222
217
  }
218
+ return finalTypes;
219
+ }
220
+ function sanitizeActionName(operationId) {
221
+ if (!operationId) return `unnamedAction_${Date.now()}`;
222
+ const name = operationId.split("_").slice(1).join("_");
223
+ return name.charAt(0).toLowerCase() + name.slice(1).replace(/_v\d+$/, "");
224
+ }
225
+ function refToTypeName(ref) {
226
+ if (!ref) return "unknown";
227
+ return ref.split("/").pop() || "unknown";
223
228
  }
224
229
 
225
230
  // src/cli.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.46",
3
+ "version": "12.0.48",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -43,6 +43,7 @@
43
43
  "dotenv": "^16.0.3",
44
44
  "fast-deep-equal": "^3.1.3",
45
45
  "fs-extra": "^11.3.1",
46
+ "json-schema-to-typescript": "^15.0.4",
46
47
  "openapi-typescript": "^6.2.4",
47
48
  "path": "^0.12.7",
48
49
  "uuid": "^9.0.1"