api-core-lib 12.0.56 → 12.0.58
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 +35 -262
- package/package.json +2 -1
package/dist/cli.cjs
CHANGED
|
@@ -25,269 +25,41 @@ 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_path3 = __toESM(require("path"), 1);
|
|
29
|
-
|
|
30
|
-
// src/generator/index.ts
|
|
31
|
-
var import_path2 = __toESM(require("path"), 1);
|
|
32
|
-
var import_axios = __toESM(require("axios"), 1);
|
|
33
|
-
var import_chalk2 = __toESM(require("chalk"), 1);
|
|
34
|
-
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
35
|
-
var import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"), 1);
|
|
36
|
-
|
|
37
|
-
// src/generator/spec-parser.ts
|
|
38
|
-
function parseSpecToModules(spec) {
|
|
39
|
-
const modules = {};
|
|
40
|
-
for (const apiPath in spec.paths) {
|
|
41
|
-
for (const method in spec.paths[apiPath]) {
|
|
42
|
-
const endpoint = spec.paths[apiPath][method];
|
|
43
|
-
if (!endpoint.tags || endpoint.tags.length === 0) continue;
|
|
44
|
-
const tagName = endpoint.tags[0];
|
|
45
|
-
const moduleName = sanitizeTagName(tagName) + "Api";
|
|
46
|
-
if (!modules[moduleName]) {
|
|
47
|
-
const commonPath = apiPath.substring(0, apiPath.lastIndexOf("/"));
|
|
48
|
-
modules[moduleName] = { baseEndpoint: commonPath || "/", actions: {}, types: /* @__PURE__ */ new Set() };
|
|
49
|
-
}
|
|
50
|
-
const { inputType, outputType } = getInputOutputTypes(endpoint);
|
|
51
|
-
[inputType, outputType].forEach((t) => {
|
|
52
|
-
if (t && !["unknown", "undefined", "any", "QueryOptions", "Promise"].includes(t)) {
|
|
53
|
-
modules[moduleName].types.add(t.replace("[]", ""));
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
const actionName = sanitizeActionName(endpoint.operationId);
|
|
57
|
-
const relativePath = apiPath.replace(modules[moduleName].baseEndpoint, "") || "/";
|
|
58
|
-
modules[moduleName].actions[actionName] = {
|
|
59
|
-
method: method.toUpperCase(),
|
|
60
|
-
path: relativePath,
|
|
61
|
-
description: endpoint.summary || "No description available.",
|
|
62
|
-
hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
|
|
63
|
-
autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
|
|
64
|
-
invalidates: [],
|
|
65
|
-
_inputType: inputType,
|
|
66
|
-
_outputType: outputType
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return modules;
|
|
71
|
-
}
|
|
72
|
-
function getInputOutputTypes(endpoint) {
|
|
73
|
-
const requestBodySchema = endpoint.requestBody?.content?.["application/json"]?.schema;
|
|
74
|
-
const successResponseSchema = endpoint.responses?.["200"]?.content?.["application/json"]?.schema || endpoint.responses?.["201"]?.content?.["application/json"]?.schema;
|
|
75
|
-
let outputType = "unknown";
|
|
76
|
-
if (successResponseSchema) {
|
|
77
|
-
if (successResponseSchema.$ref) {
|
|
78
|
-
outputType = refToTypeName(successResponseSchema.$ref);
|
|
79
|
-
} else if (successResponseSchema.type === "array" && successResponseSchema.items?.$ref) {
|
|
80
|
-
outputType = `${refToTypeName(successResponseSchema.items.$ref)}[]`;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
let inputType = "undefined";
|
|
84
|
-
if (requestBodySchema) {
|
|
85
|
-
if (requestBodySchema.$ref) {
|
|
86
|
-
inputType = refToTypeName(requestBodySchema.$ref);
|
|
87
|
-
} else if (requestBodySchema.type === "object") {
|
|
88
|
-
inputType = "any";
|
|
89
|
-
}
|
|
90
|
-
} else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
|
|
91
|
-
inputType = "QueryOptions";
|
|
92
|
-
}
|
|
93
|
-
return { inputType, outputType };
|
|
94
|
-
}
|
|
95
|
-
function sanitizeTagName(tagName) {
|
|
96
|
-
return tagName.replace(/[^a-zA-Z0-9]/g, "").replace(/Central|Tenant/g, "");
|
|
97
|
-
}
|
|
98
|
-
function sanitizeActionName(operationId) {
|
|
99
|
-
if (!operationId) return `unnamedAction_${Date.now()}`;
|
|
100
|
-
const name = operationId.split("_").slice(1).join("_");
|
|
101
|
-
return name.charAt(0).toLowerCase() + name.slice(1).replace(/_v\d+$/, "");
|
|
102
|
-
}
|
|
103
|
-
function refToTypeName(ref) {
|
|
104
|
-
if (!ref) return "unknown";
|
|
105
|
-
return ref.split("/").pop() || "unknown";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// src/generator/file-generator.ts
|
|
109
|
-
var import_fs = __toESM(require("fs"), 1);
|
|
110
28
|
var import_path = __toESM(require("path"), 1);
|
|
111
|
-
var import_chalk = __toESM(require("chalk"), 1);
|
|
112
|
-
var import_json_schema_to_typescript = require("json-schema-to-typescript");
|
|
113
|
-
async function generateModuleFiles(moduleName, moduleData, spec, outputDir) {
|
|
114
|
-
console.log(import_chalk.default.cyan(`
|
|
115
|
-
Generating module: ${moduleName}`));
|
|
116
|
-
const moduleFolderPath = import_path.default.join(outputDir, moduleName);
|
|
117
|
-
if (!import_fs.default.existsSync(moduleFolderPath)) {
|
|
118
|
-
import_fs.default.mkdirSync(moduleFolderPath, { recursive: true });
|
|
119
|
-
}
|
|
120
|
-
await generateTypesFile(moduleFolderPath, moduleName, moduleData.types, spec);
|
|
121
|
-
await generateConfigFile(moduleFolderPath, moduleName, moduleData);
|
|
122
|
-
}
|
|
123
|
-
async function generateTypesFile(moduleFolderPath, moduleName, typeNames, spec) {
|
|
124
|
-
const typesCount = typeNames.size;
|
|
125
|
-
if (typesCount === 0) {
|
|
126
|
-
console.log(import_chalk.default.yellow(` - No types found for this module. types.ts will be empty.`));
|
|
127
|
-
} else {
|
|
128
|
-
console.log(import_chalk.default.gray(` - Found ${typesCount} types to generate for types.ts...`));
|
|
129
|
-
}
|
|
130
|
-
let typesContent = `// This file is auto-generated by the API generator. Do not edit.
|
|
131
|
-
|
|
132
|
-
`;
|
|
133
|
-
const allSchemas = spec.components?.schemas || {};
|
|
134
|
-
for (const typeName of typeNames) {
|
|
135
|
-
const schema = allSchemas[typeName];
|
|
136
|
-
if (schema) {
|
|
137
|
-
try {
|
|
138
|
-
const tsType = await (0, import_json_schema_to_typescript.compile)(schema, typeName, {
|
|
139
|
-
bannerComment: "",
|
|
140
|
-
additionalProperties: false,
|
|
141
|
-
style: {
|
|
142
|
-
bracketSpacing: true,
|
|
143
|
-
printWidth: 120,
|
|
144
|
-
semi: true,
|
|
145
|
-
singleQuote: true,
|
|
146
|
-
tabWidth: 2,
|
|
147
|
-
trailingComma: "es5",
|
|
148
|
-
useTabs: false
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
typesContent += tsType + "\n";
|
|
152
|
-
} catch (compileError) {
|
|
153
|
-
console.error(import_chalk.default.red(` - Error compiling type "${typeName}" for module "${moduleName}":`));
|
|
154
|
-
console.error(import_chalk.default.red(` ${compileError.message}`));
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
157
|
-
console.log(import_chalk.default.yellow(` - Warning: Schema for type "${typeName}" not found in dereferenced spec, skipping.`));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const typesFilePath = import_path.default.join(moduleFolderPath, "types.ts");
|
|
161
|
-
import_fs.default.writeFileSync(typesFilePath, typesContent);
|
|
162
|
-
}
|
|
163
|
-
async function generateConfigFile(moduleFolderPath, moduleName, moduleData) {
|
|
164
|
-
const typeNamesArray = [...moduleData.types];
|
|
165
|
-
const actionsCount = Object.keys(moduleData.actions).length;
|
|
166
|
-
console.log(import_chalk.default.gray(` - Found ${actionsCount} actions to generate for config.ts...`));
|
|
167
|
-
const typesImportStatement = typeNamesArray.length > 0 ? `import type { ${typeNamesArray.join(", ")} } from './types';` : ``;
|
|
168
|
-
const actionsTypeParts = Object.entries(moduleData.actions).map(
|
|
169
|
-
([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
|
|
170
|
-
);
|
|
171
|
-
const actionsTypeDefinition = `{
|
|
172
|
-
${actionsTypeParts.join("\n")}
|
|
173
|
-
}`;
|
|
174
|
-
const actionsValueParts = Object.entries(moduleData.actions).map(
|
|
175
|
-
([actionName, actionData]) => {
|
|
176
|
-
const { _inputType, _outputType, ...config } = actionData;
|
|
177
|
-
return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
|
|
178
|
-
}
|
|
179
|
-
);
|
|
180
|
-
const actionsValueDefinition = `{
|
|
181
|
-
${actionsValueParts.join(",\n")}
|
|
182
|
-
}`;
|
|
183
|
-
const configContent = `
|
|
184
|
-
import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
|
|
185
|
-
${typesImportStatement}
|
|
186
|
-
|
|
187
|
-
export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
|
|
188
|
-
baseEndpoint: '${moduleData.baseEndpoint}',
|
|
189
|
-
actions: ${actionsValueDefinition},
|
|
190
|
-
};
|
|
191
|
-
`;
|
|
192
|
-
const configFilePath = import_path.default.join(moduleFolderPath, "config.ts");
|
|
193
|
-
import_fs.default.writeFileSync(configFilePath, configContent);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// src/generator/spec-preprocessor.ts
|
|
197
|
-
function toPascalCase(str) {
|
|
198
|
-
return str.replace(/_v\d+$/, "").split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
199
|
-
}
|
|
200
|
-
function normalizeSchema(schema, schemas, baseName, suffix) {
|
|
201
|
-
if (!schema) return schema;
|
|
202
|
-
if (schema.$ref) return schema;
|
|
203
|
-
if (schema.type === "object" && schema.properties) {
|
|
204
|
-
const typeName = `${toPascalCase(baseName)}${suffix}Dto`;
|
|
205
|
-
if (!schemas[typeName]) {
|
|
206
|
-
schemas[typeName] = schema;
|
|
207
|
-
}
|
|
208
|
-
return { $ref: `#/components/schemas/${typeName}` };
|
|
209
|
-
}
|
|
210
|
-
if (schema.type === "array" && schema.items) {
|
|
211
|
-
schema.items = normalizeSchema(schema.items, schemas, baseName, suffix.replace("[]", ""));
|
|
212
|
-
return schema;
|
|
213
|
-
}
|
|
214
|
-
return schema;
|
|
215
|
-
}
|
|
216
|
-
function preprocessSpec(spec) {
|
|
217
|
-
if (!spec.components) spec.components = {};
|
|
218
|
-
if (!spec.components.schemas) spec.components.schemas = {};
|
|
219
|
-
for (const apiPath in spec.paths) {
|
|
220
|
-
for (const method in spec.paths[apiPath]) {
|
|
221
|
-
const endpoint = spec.paths[apiPath][method];
|
|
222
|
-
const operationId = endpoint.operationId;
|
|
223
|
-
if (!operationId) continue;
|
|
224
|
-
const requestBodySchema = endpoint.requestBody?.content?.["application/json"]?.schema;
|
|
225
|
-
if (requestBodySchema) {
|
|
226
|
-
endpoint.requestBody.content["application/json"].schema = normalizeSchema(
|
|
227
|
-
requestBodySchema,
|
|
228
|
-
spec.components.schemas,
|
|
229
|
-
operationId,
|
|
230
|
-
"Request"
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
if (endpoint.responses) {
|
|
234
|
-
for (const statusCode in endpoint.responses) {
|
|
235
|
-
const responseSchema = endpoint.responses[statusCode]?.content?.["application/json"]?.schema;
|
|
236
|
-
if (responseSchema) {
|
|
237
|
-
endpoint.responses[statusCode].content["application/json"].schema = normalizeSchema(
|
|
238
|
-
responseSchema,
|
|
239
|
-
spec.components.schemas,
|
|
240
|
-
operationId,
|
|
241
|
-
"Response"
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return spec;
|
|
249
|
-
}
|
|
250
29
|
|
|
251
30
|
// src/generator/index.ts
|
|
31
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
32
|
+
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
33
|
+
var import_openapi_typescript_codegen = require("openapi-typescript-codegen");
|
|
252
34
|
async function runGenerator(options) {
|
|
253
|
-
console.log(
|
|
254
|
-
console.log(
|
|
255
|
-
console.log(
|
|
256
|
-
console.log("\n" +
|
|
35
|
+
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Client Generation..."));
|
|
36
|
+
console.log(import_chalk.default.gray(`Output directory: ${options.output}`));
|
|
37
|
+
console.log(import_chalk.default.gray(`.env path: ${options.envPath}`));
|
|
38
|
+
console.log("\n" + import_chalk.default.blue("Step 1: Loading environment variables..."));
|
|
257
39
|
import_dotenv.default.config({ path: options.envPath });
|
|
258
40
|
const specUrl = getSpecUrl();
|
|
259
|
-
console.log(
|
|
41
|
+
console.log(import_chalk.default.green("\u2713 Environment variables loaded successfully."));
|
|
42
|
+
console.log(import_chalk.default.gray(`Fetching spec from: ${specUrl}`));
|
|
260
43
|
try {
|
|
261
|
-
console.log("\n" +
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
const modulesOutputPath = import_path2.default.join(options.output, "modules");
|
|
282
|
-
for (const moduleName in modules) {
|
|
283
|
-
const moduleData = modules[moduleName];
|
|
284
|
-
await generateModuleFiles(moduleName, moduleData, dereferencedSpec, modulesOutputPath);
|
|
285
|
-
}
|
|
286
|
-
if (modulesCount > 0) {
|
|
287
|
-
console.log(import_chalk2.default.green("\u2713 All custom modules generated."));
|
|
288
|
-
}
|
|
289
|
-
console.log(import_chalk2.default.bold.green("\n\u{1F389} API generation complete! All files are located in:"));
|
|
290
|
-
console.log(import_chalk2.default.bold.cyan(options.output));
|
|
44
|
+
console.log("\n" + import_chalk.default.blue("Step 2: Generating API client from OpenAPI spec..."));
|
|
45
|
+
await (0, import_openapi_typescript_codegen.generate)({
|
|
46
|
+
input: specUrl,
|
|
47
|
+
// مسار ملف المواصفات أو الرابط المباشر
|
|
48
|
+
output: options.output,
|
|
49
|
+
// مجلد المخرجات الرئيسي
|
|
50
|
+
clientName: "ApiClient",
|
|
51
|
+
// يمكنك تسمية العميل الرئيسي (اختياري)
|
|
52
|
+
useOptions: true,
|
|
53
|
+
// يضيف خيارات إضافية للدوال (مثل الفلاتر والترتيب)
|
|
54
|
+
useUnionTypes: false,
|
|
55
|
+
// استخدم الواجهات (interfaces) بدلاً من أنواع الاتحاد (union types)
|
|
56
|
+
exportSchemas: true,
|
|
57
|
+
// يقوم بتصدير جميع الـ schemas في ملف models.ts
|
|
58
|
+
exportServices: true
|
|
59
|
+
// يقوم بتوليد services بناءً على الـ tags
|
|
60
|
+
});
|
|
61
|
+
console.log(import_chalk.default.bold.green("\n\u{1F389} API client generation complete!"));
|
|
62
|
+
console.log(import_chalk.default.bold.cyan(`All files are located in: ${options.output}`));
|
|
291
63
|
} catch (error) {
|
|
292
64
|
handleGenerationError(error);
|
|
293
65
|
}
|
|
@@ -295,24 +67,25 @@ async function runGenerator(options) {
|
|
|
295
67
|
function getSpecUrl() {
|
|
296
68
|
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);
|
|
297
69
|
if (!specUrl) {
|
|
298
|
-
console.error(
|
|
70
|
+
console.error(import_chalk.default.red.bold("\n\u274C Error: API specification URL not found."));
|
|
71
|
+
console.log(import_chalk.default.yellow("Please set OPENAPI_SPEC_URL, API_URL, or NEXT_PUBLIC_API_URL in your .env file."));
|
|
299
72
|
process.exit(1);
|
|
300
73
|
}
|
|
301
74
|
return specUrl;
|
|
302
75
|
}
|
|
303
76
|
function handleGenerationError(error) {
|
|
304
|
-
console.error(
|
|
305
|
-
console.error(
|
|
77
|
+
console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
|
|
78
|
+
console.error(import_chalk.default.red(error.message || "An unknown error occurred."));
|
|
306
79
|
if (error.stack) {
|
|
307
|
-
console.error(
|
|
80
|
+
console.error(import_chalk.default.gray(error.stack));
|
|
308
81
|
}
|
|
309
82
|
process.exit(1);
|
|
310
83
|
}
|
|
311
84
|
|
|
312
85
|
// src/cli.ts
|
|
313
86
|
console.log("API Core Lib - Code Generator");
|
|
314
|
-
import_commander.program.option("-o, --output <path>", "Output directory for generated files", "src/lib/api-generated").option("--env-path <path>", ".env file path",
|
|
315
|
-
const absoluteOutputPath =
|
|
87
|
+
import_commander.program.option("-o, --output <path>", "Output directory for generated files", "src/lib/api-generated").option("--env-path <path>", ".env file path", import_path.default.resolve(process.cwd(), ".env")).action((options) => {
|
|
88
|
+
const absoluteOutputPath = import_path.default.resolve(process.cwd(), options.output);
|
|
316
89
|
runGenerator({ ...options, output: absoluteOutputPath });
|
|
317
90
|
});
|
|
318
91
|
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.
|
|
3
|
+
"version": "12.0.58",
|
|
4
4
|
"description": "A flexible and powerful API client library for modern web applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"fs-extra": "^11.3.1",
|
|
47
47
|
"json-schema-to-typescript": "^15.0.4",
|
|
48
48
|
"openapi-typescript": "^6.2.4",
|
|
49
|
+
"openapi-typescript-codegen": "^0.29.0",
|
|
49
50
|
"path": "^0.12.7",
|
|
50
51
|
"uuid": "^9.0.1"
|
|
51
52
|
},
|