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.
- package/dist/cli.cjs +115 -110
- 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,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
|
|
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
|
-
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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.
|
|
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"
|