api-core-lib 12.0.47 → 12.0.49

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 +113 -93
  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,33 +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
- const generatorCommand = [
132
- "npx --yes openapi-typescript",
133
- `"${tempSpecPath}"`,
134
- `--output "${import_path.default.join(typesOutputPath, "schema.d.ts")}"`
135
- ].join(" ");
136
- console.log(import_chalk.default.gray(`Executing command: npx openapi-typescript...`));
137
- (0, import_child_process.execSync)(generatorCommand, { stdio: "inherit" });
138
- console.log(import_chalk.default.green(`\u2713 Types generated successfully at ${typesOutputPath}`));
139
- 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..."));
140
60
  const modules = parseSpecToModules(spec);
141
61
  const modulesOutputPath = import_path.default.join(options.output, "modules");
142
62
  if (!import_fs.default.existsSync(modulesOutputPath)) {
@@ -149,8 +69,31 @@ async function runGenerator(options) {
149
69
  import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
150
70
  }
151
71
  const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
152
- 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)(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
+ }
153
95
  import_fs.default.writeFileSync(typesFilePath, typesContent);
96
+ const allTypeNames = [...allModuleTypes];
154
97
  const actionsTypeParts = Object.entries(moduleData.actions).map(
155
98
  ([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
156
99
  );
@@ -167,9 +110,10 @@ ${actionsTypeParts.join("\n")}
167
110
  ${actionsValueParts.join(",\n")}
168
111
  }`;
169
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.`;
170
114
  const configContent = `
171
115
  import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
172
- import type { ${[...moduleData.types].join(", ")} } from './types';
116
+ ${typesImportStatement}
173
117
 
174
118
  export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
175
119
  baseEndpoint: '${moduleData.baseEndpoint}',
@@ -177,7 +121,7 @@ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
177
121
  };
178
122
  `;
179
123
  import_fs.default.writeFileSync(configFilePath, configContent);
180
- console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/`));
124
+ console.log(import_chalk.default.green(`\u2713 Module generated: ${moduleName}/ (config.ts, types.ts)`));
181
125
  }
182
126
  console.log(import_chalk.default.green("\u2713 All custom modules generated."));
183
127
  console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
@@ -195,14 +139,90 @@ export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
195
139
  process.exit(1);
196
140
  } finally {
197
141
  if (tempSpecPath && import_fs.default.existsSync(tempSpecPath)) {
198
- import_fs.default.unlinkSync(tempSpecPath);
199
142
  console.log(import_chalk.default.gray("\nTemporary spec file cleaned up."));
200
143
  }
201
- if (ignoreFilePath && import_fs.default.existsSync(ignoreFilePath)) {
202
- import_fs.default.unlinkSync(ignoreFilePath);
203
- console.log(import_chalk.default.gray("Temporary ignore file cleaned up."));
144
+ }
145
+ }
146
+ function parseSpecToModules(spec) {
147
+ const modules = {};
148
+ for (const apiPath in spec.paths) {
149
+ for (const method in spec.paths[apiPath]) {
150
+ const endpoint = spec.paths[apiPath][method];
151
+ if (!endpoint.tags || endpoint.tags.length === 0) continue;
152
+ const tagName = endpoint.tags[0];
153
+ const moduleName = tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "") + "Api";
154
+ if (!modules[moduleName]) {
155
+ const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
156
+ modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {}, types: /* @__PURE__ */ new Set() };
157
+ }
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
+ }
179
+ [inputType, outputType].forEach((t) => {
180
+ if (t && t !== "unknown" && t !== "undefined" && t !== "any" && t !== "QueryOptions") {
181
+ modules[moduleName].types.add(t.replace("[]", ""));
182
+ }
183
+ });
184
+ const actionName = sanitizeActionName(endpoint.operationId);
185
+ const relativePath = apiPath.replace(modules[moduleName].baseEndpoint, "") || "/";
186
+ modules[moduleName].actions[actionName] = {
187
+ method: method.toUpperCase(),
188
+ path: relativePath,
189
+ description: endpoint.summary || "No description available.",
190
+ hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
191
+ autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
192
+ invalidates: [],
193
+ _inputType: inputType,
194
+ _outputType: outputType
195
+ };
196
+ }
197
+ }
198
+ return modules;
199
+ }
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
+ }
204
214
  }
205
215
  }
216
+ return finalTypes;
217
+ }
218
+ function sanitizeActionName(operationId) {
219
+ if (!operationId) return `unnamedAction_${Date.now()}`;
220
+ const name = operationId.split("_").slice(1).join("_");
221
+ return name.charAt(0).toLowerCase() + name.slice(1).replace(/_v\d+$/, "");
222
+ }
223
+ function refToTypeName(ref) {
224
+ if (!ref) return "unknown";
225
+ return ref.split("/").pop() || "unknown";
206
226
  }
207
227
 
208
228
  // src/cli.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.47",
3
+ "version": "12.0.49",
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"