api-core-lib 12.0.50 → 12.0.52

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 +166 -152
  2. package/package.json +2 -1
package/dist/cli.cjs CHANGED
@@ -25,124 +25,16 @@ 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_path2 = __toESM(require("path"), 1);
28
+ var import_path3 = __toESM(require("path"), 1);
29
29
 
30
30
  // src/generator/index.ts
31
- var import_fs = __toESM(require("fs"), 1);
32
- var import_path = __toESM(require("path"), 1);
31
+ var import_path2 = __toESM(require("path"), 1);
33
32
  var import_axios = __toESM(require("axios"), 1);
34
33
  var import_chalk = __toESM(require("chalk"), 1);
35
34
  var import_dotenv = __toESM(require("dotenv"), 1);
36
- var import_json_schema_to_typescript = require("json-schema-to-typescript");
37
- async function runGenerator(options) {
38
- console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
39
- console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
40
- console.log(import_chalk.default.gray(`.env path: ${options.envPath}`));
41
- console.log("\n" + import_chalk.default.blue("Step 1: Loading environment variables..."));
42
- import_dotenv.default.config({ path: options.envPath });
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);
44
- if (!specUrl) {
45
- console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
46
- console.error(import_chalk.default.red("Please define either OPENAPI_SPEC_URL or API_URL/NEXT_PUBLIC_API_URL in your .env file."));
47
- process.exit(1);
48
- }
49
- console.log(import_chalk.default.green("\u2713 Environment variables loaded."));
50
- let tempSpecPath = "";
51
- try {
52
- console.log("\n" + import_chalk.default.blue(`Step 2: Fetching OpenAPI spec from ${specUrl}...`));
53
- const response = await import_axios.default.get(specUrl, { timeout: 15e3 });
54
- const spec = response.data;
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.');
57
- }
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..."));
60
- const modules = parseSpecToModules(spec);
61
- const modulesOutputPath = import_path.default.join(options.output, "modules");
62
- if (!import_fs.default.existsSync(modulesOutputPath)) {
63
- import_fs.default.mkdirSync(modulesOutputPath, { recursive: true });
64
- }
65
- for (const moduleName in modules) {
66
- const moduleData = modules[moduleName];
67
- const moduleFolderPath = import_path.default.join(modulesOutputPath, moduleName);
68
- if (!import_fs.default.existsSync(moduleFolderPath)) {
69
- import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
70
- }
71
- const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
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)(spec, typeName, {
80
- bannerComment: "",
81
- additionalProperties: false,
82
- style: {
83
- bracketSpacing: true,
84
- printWidth: 120,
85
- semi: true,
86
- singleQuote: true,
87
- tabWidth: 2,
88
- trailingComma: "es5",
89
- useTabs: false
90
- }
91
- });
92
- typesContent += tsType + "\n";
93
- }
94
- }
95
- import_fs.default.writeFileSync(typesFilePath, typesContent);
96
- const allTypeNames = [...allModuleTypes];
97
- const actionsTypeParts = Object.entries(moduleData.actions).map(
98
- ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
99
- );
100
- const actionsTypeDefinition = `{
101
- ${actionsTypeParts.join("\n")}
102
- }`;
103
- const actionsValueParts = Object.entries(moduleData.actions).map(
104
- ([actionName, actionData]) => {
105
- const { _inputType, _outputType, ...config } = actionData;
106
- return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
107
- }
108
- );
109
- const actionsValueDefinition = `{
110
- ${actionsValueParts.join(",\n")}
111
- }`;
112
- const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
113
- const typesImportStatement = allTypeNames.length > 0 ? `import type { ${allTypeNames.join(", ")} } from './types';` : `// No custom types used in this module.`;
114
- const configContent = `
115
- import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
116
- ${typesImportStatement}
35
+ var import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"), 1);
117
36
 
118
- export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
119
- baseEndpoint: '${moduleData.baseEndpoint}',
120
- actions: ${actionsValueDefinition},
121
- };
122
- `;
123
- import_fs.default.writeFileSync(configFilePath, configContent);
124
- console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/ (config.ts, types.ts)`));
125
- }
126
- console.log(import_chalk.default.green("\u2713 All custom modules generated."));
127
- console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
128
- console.log(import_chalk.default.bold.cyan(options.output));
129
- } catch (error) {
130
- console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
131
- if (error.response) {
132
- console.error(import_chalk.default.red(`Network Error: ${error.message} (Status: ${error.response.status})`));
133
- } else if (error.stderr) {
134
- console.error(import_chalk.default.red(`Command Execution Error: ${error.message}`));
135
- console.error(import_chalk.default.gray(error.stderr.toString()));
136
- } else {
137
- console.error(import_chalk.default.red(error.message));
138
- }
139
- process.exit(1);
140
- } finally {
141
- if (tempSpecPath && import_fs.default.existsSync(tempSpecPath)) {
142
- console.log(import_chalk.default.gray("\nTemporary spec file cleaned up."));
143
- }
144
- }
145
- }
37
+ // src/generator/spec-parser.ts
146
38
  function parseSpecToModules(spec) {
147
39
  const modules = {};
148
40
  for (const apiPath in spec.paths) {
@@ -150,34 +42,14 @@ function parseSpecToModules(spec) {
150
42
  const endpoint = spec.paths[apiPath][method];
151
43
  if (!endpoint.tags || endpoint.tags.length === 0) continue;
152
44
  const tagName = endpoint.tags[0];
153
- const moduleName = tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "") + "Api";
45
+ const moduleName = sanitizeTagName(tagName) + "Api";
154
46
  if (!modules[moduleName]) {
155
47
  const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
156
48
  modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {}, types: /* @__PURE__ */ new Set() };
157
49
  }
158
- const requestBody = endpoint.requestBody?.content?.["application/json"]?.schema;
159
- const successResponse = endpoint.responses["200"] || endpoint.responses["201"];
160
- const responseSchema = successResponse?.content?.["application/json"]?.schema;
161
- let outputType = "unknown";
162
- if (responseSchema) {
163
- if (responseSchema.$ref) {
164
- outputType = refToTypeName(responseSchema.$ref);
165
- } else if (responseSchema.type === "array" && responseSchema.items?.$ref) {
166
- outputType = `${refToTypeName(responseSchema.items.$ref)}[]`;
167
- }
168
- }
169
- let inputType = "undefined";
170
- if (requestBody) {
171
- if (requestBody.$ref) {
172
- inputType = refToTypeName(requestBody.$ref);
173
- } else if (requestBody.type === "object") {
174
- inputType = "any";
175
- }
176
- } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
177
- inputType = "QueryOptions";
178
- }
50
+ const { inputType, outputType } = getInputOutputTypes(endpoint);
179
51
  [inputType, outputType].forEach((t) => {
180
- if (t && t !== "unknown" && t !== "undefined" && t !== "any" && t !== "QueryOptions") {
52
+ if (t && !["unknown", "undefined", "any", "QueryOptions"].includes(t)) {
181
53
  modules[moduleName].types.add(t.replace("[]", ""));
182
54
  }
183
55
  });
@@ -197,23 +69,36 @@ function parseSpecToModules(spec) {
197
69
  }
198
70
  return modules;
199
71
  }
200
- function getAllReferencedTypes(initialTypes, allSchemas) {
201
- const finalTypes = new Set(initialTypes);
202
- const queue = [...initialTypes];
203
- while (queue.length > 0) {
204
- const typeName = queue.shift();
205
- if (!typeName) continue;
206
- const schemaString = JSON.stringify(allSchemas[typeName] || {});
207
- const references = schemaString.match(/"\$ref":\s*"#\/components\/schemas\/([^"]+)"/g) || [];
208
- for (const ref of references) {
209
- const referencedTypeName = ref.split("/").pop()?.replace(/"/g, "") || "";
210
- if (referencedTypeName && !finalTypes.has(referencedTypeName)) {
211
- finalTypes.add(referencedTypeName);
212
- queue.push(referencedTypeName);
213
- }
72
+ function getInputOutputTypes(endpoint) {
73
+ const requestBody = endpoint.requestBody?.content?.["application/json"]?.schema;
74
+ const successResponse = endpoint.responses["200"] || endpoint.responses["201"];
75
+ const responseSchema = successResponse?.content?.["application/json"]?.schema;
76
+ let outputType = "unknown";
77
+ if (responseSchema) {
78
+ if (responseSchema.title) {
79
+ outputType = responseSchema.title.replace(/\[\]/g, "") + (responseSchema.type === "array" ? "[]" : "");
80
+ } else if (responseSchema.$ref) {
81
+ outputType = refToTypeName(responseSchema.$ref);
82
+ } else if (responseSchema.type === "array" && (responseSchema.items?.title || responseSchema.items?.$ref)) {
83
+ outputType = (responseSchema.items.title || refToTypeName(responseSchema.items.$ref)) + "[]";
84
+ }
85
+ }
86
+ let inputType = "undefined";
87
+ if (requestBody) {
88
+ if (requestBody.title) {
89
+ inputType = requestBody.title;
90
+ } else if (requestBody.$ref) {
91
+ inputType = refToTypeName(requestBody.$ref);
92
+ } else if (requestBody.type === "object") {
93
+ inputType = "any";
214
94
  }
95
+ } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
96
+ inputType = "QueryOptions";
215
97
  }
216
- return finalTypes;
98
+ return { inputType, outputType };
99
+ }
100
+ function sanitizeTagName(tagName) {
101
+ return tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "");
217
102
  }
218
103
  function sanitizeActionName(operationId) {
219
104
  if (!operationId) return `unnamedAction_${Date.now()}`;
@@ -225,10 +110,139 @@ function refToTypeName(ref) {
225
110
  return ref.split("/").pop() || "unknown";
226
111
  }
227
112
 
113
+ // src/generator/file-generator.ts
114
+ var import_fs = __toESM(require("fs"), 1);
115
+ var import_path = __toESM(require("path"), 1);
116
+ var import_json_schema_to_typescript = require("json-schema-to-typescript");
117
+ async function generateModuleFiles(moduleName, moduleData, spec, outputDir) {
118
+ const moduleFolderPath = import_path.default.join(outputDir, moduleName);
119
+ if (!import_fs.default.existsSync(moduleFolderPath)) {
120
+ import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
121
+ }
122
+ await generateTypesFile(moduleFolderPath, moduleData.types, spec);
123
+ await generateConfigFile(moduleFolderPath, moduleName, moduleData, moduleData.types);
124
+ }
125
+ async function generateTypesFile(moduleFolderPath, typeNames, spec) {
126
+ let typesContent = `// This file is auto-generated by the API generator. Do not edit.
127
+
128
+ `;
129
+ const allSchemas = spec.components?.schemas || {};
130
+ for (const typeName of typeNames) {
131
+ const schema = allSchemas[typeName];
132
+ if (schema) {
133
+ const tsType = await (0, import_json_schema_to_typescript.compile)(schema, typeName, {
134
+ bannerComment: "",
135
+ additionalProperties: false,
136
+ style: {
137
+ bracketSpacing: true,
138
+ printWidth: 120,
139
+ semi: true,
140
+ singleQuote: true,
141
+ tabWidth: 2,
142
+ trailingComma: "es5",
143
+ useTabs: false
144
+ }
145
+ });
146
+ typesContent += tsType + "\n";
147
+ }
148
+ }
149
+ const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
150
+ import_fs.default.writeFileSync(typesFilePath, typesContent);
151
+ }
152
+ async function generateConfigFile(moduleFolderPath, moduleName, moduleData, typeNames) {
153
+ const typeNamesArray = [...typeNames];
154
+ const typesImportStatement = typeNamesArray.length > 0 ? `import type { ${typeNamesArray.join(", ")} } from './types';` : ``;
155
+ const actionsTypeParts = Object.entries(moduleData.actions).map(
156
+ ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
157
+ );
158
+ const actionsTypeDefinition = `{
159
+ ${actionsTypeParts.join("\n")}
160
+ }`;
161
+ const actionsValueParts = Object.entries(moduleData.actions).map(
162
+ ([actionName, actionData]) => {
163
+ const { _inputType, _outputType, ...config } = actionData;
164
+ return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
165
+ }
166
+ );
167
+ const actionsValueDefinition = `{
168
+ ${actionsValueParts.join(",\n")}
169
+ }`;
170
+ const configContent = `
171
+ import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
172
+ ${typesImportStatement}
173
+
174
+ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
175
+ baseEndpoint: '${moduleData.baseEndpoint}',
176
+ actions: ${actionsValueDefinition},
177
+ };
178
+ `;
179
+ const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
180
+ import_fs.default.writeFileSync(configFilePath, configContent);
181
+ }
182
+
183
+ // src/generator/index.ts
184
+ async function runGenerator(options) {
185
+ console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
186
+ console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
187
+ console.log(import_chalk.default.gray(`.env path: ${options.envPath}`));
188
+ console.log("\n" + import_chalk.default.blue("Step 1: Loading environment variables..."));
189
+ import_dotenv.default.config({ path: options.envPath });
190
+ const specUrl = getSpecUrl();
191
+ console.log(import_chalk.default.green("\u2713 Environment variables loaded."));
192
+ try {
193
+ console.log("\n" + import_chalk.default.blue(`Step 2: Fetching OpenAPI spec from ${specUrl}...`));
194
+ const response = await import_axios.default.get(specUrl, { timeout: 15e3 });
195
+ let spec = response.data;
196
+ if (!spec.openapi || !spec.paths) {
197
+ throw new Error('Invalid OpenAPI specification file. "openapi" or "paths" property is missing.');
198
+ }
199
+ console.log(import_chalk.default.green("\u2713 OpenAPI spec fetched successfully."));
200
+ console.log(import_chalk.default.blue("Step 2.5: Dereferencing all $refs in the spec..."));
201
+ spec = await import_json_schema_ref_parser.default.dereference(spec);
202
+ console.log(import_chalk.default.green("\u2713 Spec dereferenced successfully."));
203
+ console.log("\n" + import_chalk.default.blue("Step 3: Parsing spec and generating API modules..."));
204
+ const modules = parseSpecToModules(spec);
205
+ const modulesOutputPath = import_path2.default.join(options.output, "modules");
206
+ for (const moduleName in modules) {
207
+ const moduleData = modules[moduleName];
208
+ await generateModuleFiles(moduleName, moduleData, spec, modulesOutputPath);
209
+ console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/ (config.ts, types.ts)`));
210
+ }
211
+ console.log(import_chalk.default.green("\u2713 All custom modules generated."));
212
+ console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
213
+ console.log(import_chalk.default.bold.cyan(options.output));
214
+ } catch (error) {
215
+ handleGenerationError(error);
216
+ }
217
+ }
218
+ function getSpecUrl() {
219
+ 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);
220
+ if (!specUrl) {
221
+ console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
222
+ console.error(import_chalk.default.red("Please define either OPENAPI_SPEC_URL or API_URL/NEXT_PUBLIC_API_URL in your .env file."));
223
+ process.exit(1);
224
+ }
225
+ return specUrl;
226
+ }
227
+ function handleGenerationError(error) {
228
+ console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
229
+ if (error.message.includes("$ref")) {
230
+ console.error(import_chalk.default.red(error.message));
231
+ } else if (error.response) {
232
+ console.error(import_chalk.default.red(`Network Error: ${error.message} (Status: ${error.response.status})`));
233
+ } else if (error.stderr) {
234
+ console.error(import_chalk.default.red(`Command Execution Error: ${error.message}`));
235
+ console.error(import_chalk.default.gray(error.stderr.toString()));
236
+ } else {
237
+ console.error(import_chalk.default.red(error.message));
238
+ }
239
+ process.exit(1);
240
+ }
241
+
228
242
  // src/cli.ts
229
243
  console.log("API Core Lib - Code Generator");
230
- 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) => {
231
- const absoluteOutputPath = import_path2.default.resolve(process.cwd(), options.output);
244
+ 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) => {
245
+ const absoluteOutputPath = import_path3.default.resolve(process.cwd(), options.output);
232
246
  runGenerator({ ...options, output: absoluteOutputPath });
233
247
  });
234
248
  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.50",
3
+ "version": "12.0.52",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -35,6 +35,7 @@
35
35
  "react-dom": ">=19.0.0"
36
36
  },
37
37
  "dependencies": {
38
+ "@apidevtools/json-schema-ref-parser": "^14.2.0",
38
39
  "axios": "^1.6.8",
39
40
  "axios-retry": "^4.1.0",
40
41
  "chalk": "^4.1.2",