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.
- package/dist/cli.cjs +113 -93
- 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
|
|
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
|
|
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 "
|
|
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
|
-
|
|
128
|
-
console.log(import_chalk.default.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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.
|
|
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"
|