api-core-lib 12.0.49 → 12.0.51

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 +167 -139
  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.
35
+ var import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"), 1);
73
36
 
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}
117
-
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,13 +69,37 @@ function parseSpecToModules(spec) {
197
69
  }
198
70
  return modules;
199
71
  }
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.$ref) {
79
+ outputType = refToTypeName(responseSchema.$ref);
80
+ } else if (responseSchema.type === "array" && responseSchema.items?.$ref) {
81
+ outputType = `${refToTypeName(responseSchema.items.$ref)}[]`;
82
+ }
83
+ }
84
+ let inputType = "undefined";
85
+ if (requestBody) {
86
+ if (requestBody.$ref) {
87
+ inputType = refToTypeName(requestBody.$ref);
88
+ } else if (requestBody.type === "object") {
89
+ inputType = "any";
90
+ }
91
+ } else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
92
+ inputType = "QueryOptions";
93
+ }
94
+ return { inputType, outputType };
95
+ }
200
96
  function getAllReferencedTypes(initialTypes, allSchemas) {
201
97
  const finalTypes = new Set(initialTypes);
202
98
  const queue = [...initialTypes];
203
99
  while (queue.length > 0) {
204
100
  const typeName = queue.shift();
205
- if (!typeName) continue;
206
- const schemaString = JSON.stringify(allSchemas[typeName] || {});
101
+ if (!typeName || !allSchemas[typeName]) continue;
102
+ const schemaString = JSON.stringify(allSchemas[typeName]);
207
103
  const references = schemaString.match(/"\$ref":\s*"#\/components\/schemas\/([^"]+)"/g) || [];
208
104
  for (const ref of references) {
209
105
  const referencedTypeName = ref.split("/").pop()?.replace(/"/g, "") || "";
@@ -215,6 +111,9 @@ function getAllReferencedTypes(initialTypes, allSchemas) {
215
111
  }
216
112
  return finalTypes;
217
113
  }
114
+ function sanitizeTagName(tagName) {
115
+ return tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "");
116
+ }
218
117
  function sanitizeActionName(operationId) {
219
118
  if (!operationId) return `unnamedAction_${Date.now()}`;
220
119
  const name = operationId.split("_").slice(1).join("_");
@@ -225,10 +124,139 @@ function refToTypeName(ref) {
225
124
  return ref.split("/").pop() || "unknown";
226
125
  }
227
126
 
127
+ // src/generator/file-generator.ts
128
+ var import_fs = __toESM(require("fs"), 1);
129
+ var import_path = __toESM(require("path"), 1);
130
+ var import_json_schema_to_typescript = require("json-schema-to-typescript");
131
+ async function generateModuleFiles(moduleName, moduleData, spec, outputDir) {
132
+ const moduleFolderPath = import_path.default.join(outputDir, moduleName);
133
+ if (!import_fs.default.existsSync(moduleFolderPath)) {
134
+ import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
135
+ }
136
+ const allTypeNames = getAllReferencedTypes(moduleData.types, spec.components.schemas);
137
+ await generateTypesFile(moduleFolderPath, allTypeNames, spec);
138
+ await generateConfigFile(moduleFolderPath, moduleName, moduleData, allTypeNames);
139
+ }
140
+ async function generateTypesFile(moduleFolderPath, allTypeNames, spec) {
141
+ let typesContent = `// This file is auto-generated by the API generator. Do not edit.
142
+
143
+ `;
144
+ for (const typeName of allTypeNames) {
145
+ const schema = spec.components?.schemas?.[typeName];
146
+ if (schema) {
147
+ const tsType = await (0, import_json_schema_to_typescript.compile)(schema, typeName, {
148
+ bannerComment: "",
149
+ additionalProperties: false,
150
+ style: {
151
+ bracketSpacing: true,
152
+ printWidth: 120,
153
+ semi: true,
154
+ singleQuote: true,
155
+ tabWidth: 2,
156
+ trailingComma: "es5",
157
+ useTabs: false
158
+ }
159
+ });
160
+ typesContent += tsType + "\n";
161
+ }
162
+ }
163
+ const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
164
+ import_fs.default.writeFileSync(typesFilePath, typesContent);
165
+ }
166
+ async function generateConfigFile(moduleFolderPath, moduleName, moduleData, allTypeNames) {
167
+ const typeNamesArray = [...allTypeNames];
168
+ const typesImportStatement = typeNamesArray.length > 0 ? `import type { ${typeNamesArray.join(", ")} } from './types';` : ``;
169
+ const actionsTypeParts = Object.entries(moduleData.actions).map(
170
+ ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
171
+ );
172
+ const actionsTypeDefinition = `{
173
+ ${actionsTypeParts.join("\n")}
174
+ }`;
175
+ const actionsValueParts = Object.entries(moduleData.actions).map(
176
+ ([actionName, actionData]) => {
177
+ const { _inputType, _outputType, ...config } = actionData;
178
+ return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
179
+ }
180
+ );
181
+ const actionsValueDefinition = `{
182
+ ${actionsValueParts.join(",\n")}
183
+ }`;
184
+ const configContent = `
185
+ import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
186
+ ${typesImportStatement}
187
+
188
+ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
189
+ baseEndpoint: '${moduleData.baseEndpoint}',
190
+ actions: ${actionsValueDefinition},
191
+ };
192
+ `;
193
+ const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
194
+ import_fs.default.writeFileSync(configFilePath, configContent);
195
+ }
196
+
197
+ // src/generator/index.ts
198
+ async function runGenerator(options) {
199
+ console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Core Lib Code Generator..."));
200
+ console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
201
+ console.log(import_chalk.default.gray(`.env path: ${options.envPath}`));
202
+ console.log("\n" + import_chalk.default.blue("Step 1: Loading environment variables..."));
203
+ import_dotenv.default.config({ path: options.envPath });
204
+ const specUrl = getSpecUrl();
205
+ console.log(import_chalk.default.green("\u2713 Environment variables loaded."));
206
+ try {
207
+ console.log("\n" + import_chalk.default.blue(`Step 2: Fetching OpenAPI spec from ${specUrl}...`));
208
+ const response = await import_axios.default.get(specUrl, { timeout: 15e3 });
209
+ let spec = response.data;
210
+ if (!spec.openapi || !spec.paths) {
211
+ throw new Error('Invalid OpenAPI specification file. "openapi" or "paths" property is missing.');
212
+ }
213
+ console.log(import_chalk.default.green("\u2713 OpenAPI spec fetched successfully."));
214
+ console.log(import_chalk.default.blue("Step 2.5: Dereferencing all $refs in the spec..."));
215
+ spec = await import_json_schema_ref_parser.default.dereference(spec);
216
+ console.log(import_chalk.default.green("\u2713 Spec dereferenced successfully."));
217
+ console.log("\n" + import_chalk.default.blue("Step 3: Parsing spec and generating API modules..."));
218
+ const modules = parseSpecToModules(spec);
219
+ const modulesOutputPath = import_path2.default.join(options.output, "modules");
220
+ for (const moduleName in modules) {
221
+ const moduleData = modules[moduleName];
222
+ await generateModuleFiles(moduleName, moduleData, spec, modulesOutputPath);
223
+ console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/ (config.ts, types.ts)`));
224
+ }
225
+ console.log(import_chalk.default.green("\u2713 All custom modules generated."));
226
+ console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
227
+ console.log(import_chalk.default.bold.cyan(options.output));
228
+ } catch (error) {
229
+ handleGenerationError(error);
230
+ }
231
+ }
232
+ function getSpecUrl() {
233
+ 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);
234
+ if (!specUrl) {
235
+ console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
236
+ console.error(import_chalk.default.red("Please define either OPENAPI_SPEC_URL or API_URL/NEXT_PUBLIC_API_URL in your .env file."));
237
+ process.exit(1);
238
+ }
239
+ return specUrl;
240
+ }
241
+ function handleGenerationError(error) {
242
+ console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
243
+ if (error.message.includes("$ref")) {
244
+ console.error(import_chalk.default.red(error.message));
245
+ } else if (error.response) {
246
+ console.error(import_chalk.default.red(`Network Error: ${error.message} (Status: ${error.response.status})`));
247
+ } else if (error.stderr) {
248
+ console.error(import_chalk.default.red(`Command Execution Error: ${error.message}`));
249
+ console.error(import_chalk.default.gray(error.stderr.toString()));
250
+ } else {
251
+ console.error(import_chalk.default.red(error.message));
252
+ }
253
+ process.exit(1);
254
+ }
255
+
228
256
  // src/cli.ts
229
257
  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);
258
+ 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) => {
259
+ const absoluteOutputPath = import_path3.default.resolve(process.cwd(), options.output);
232
260
  runGenerator({ ...options, output: absoluteOutputPath });
233
261
  });
234
262
  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.49",
3
+ "version": "12.0.51",
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",