api-core-lib 12.0.66 → 12.0.68
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 +264 -175
- package/package.json +7 -4
package/dist/cli.cjs
CHANGED
|
@@ -69,17 +69,82 @@ var import_path2 = __toESM(require("path"), 1);
|
|
|
69
69
|
// src/generator/index.ts
|
|
70
70
|
var import_fs = __toESM(require("fs"), 1);
|
|
71
71
|
var import_path = __toESM(require("path"), 1);
|
|
72
|
+
var import_util = __toESM(require("util"), 1);
|
|
72
73
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
73
74
|
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
74
75
|
var import_swagger_parser = __toESM(require("@apidevtools/swagger-parser"), 1);
|
|
75
76
|
var import_openapi_types = __toESM(require_dist(), 1);
|
|
76
|
-
var
|
|
77
|
+
var DEBUG_MODE = process.env.DEBUG === "true";
|
|
78
|
+
var debugLog = (title, data) => DEBUG_MODE && console.log(import_chalk.default.yellow(`
|
|
79
|
+
[DEBUG: ${title}]`), import_util.default.inspect(data, { depth: 4, colors: true }));
|
|
80
|
+
var toPascalCase = (str) => str.replace(/[^a-zA-Z0-9_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (w) => w.toUpperCase()).replace(/\s+/g, "");
|
|
81
|
+
var toCamelCase = (str) => {
|
|
82
|
+
const s = toPascalCase(str);
|
|
83
|
+
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
84
|
+
};
|
|
85
|
+
var getModuleName = (opId) => `${opId.split("_")[0].replace(/Controller$/, "")}Api`;
|
|
86
|
+
var getActionName = (opId) => toCamelCase(opId.split("_")[1] || opId);
|
|
87
|
+
var findCommonPath = (paths) => {
|
|
88
|
+
if (!paths || paths.length === 0) return "/";
|
|
89
|
+
const sorted = [...paths].sort();
|
|
90
|
+
const first = sorted[0];
|
|
91
|
+
const last = sorted[sorted.length - 1];
|
|
92
|
+
let i = 0;
|
|
93
|
+
while (i < first.length && first.charAt(i) === last.charAt(i)) i++;
|
|
94
|
+
const prefix = first.substring(0, i);
|
|
95
|
+
return prefix.substring(0, prefix.lastIndexOf("/") + 1) || "/";
|
|
96
|
+
};
|
|
97
|
+
var generateFriendlyMessage = (fieldName, validation) => {
|
|
98
|
+
const friendlyName = fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
|
|
99
|
+
switch (validation) {
|
|
100
|
+
case "required":
|
|
101
|
+
return `${friendlyName} is required.`;
|
|
102
|
+
case "email":
|
|
103
|
+
return `Please enter a valid email for ${friendlyName}.`;
|
|
104
|
+
default:
|
|
105
|
+
return `Invalid value for ${friendlyName}.`;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
function parseSchema(name, schema) {
|
|
109
|
+
const properties = [];
|
|
110
|
+
if (schema.properties) {
|
|
111
|
+
for (const propName in schema.properties) {
|
|
112
|
+
const propSchema = schema.properties[propName];
|
|
113
|
+
let items = void 0;
|
|
114
|
+
if (propSchema.type === "array" && propSchema.items) {
|
|
115
|
+
items = parseSchema("item", propSchema.items).properties[0];
|
|
116
|
+
}
|
|
117
|
+
properties.push({
|
|
118
|
+
name: propName,
|
|
119
|
+
type: propSchema.type || "object",
|
|
120
|
+
isRequired: (schema.required || []).includes(propName),
|
|
121
|
+
isNullable: propSchema.nullable || false,
|
|
122
|
+
description: propSchema.description,
|
|
123
|
+
example: propSchema.example,
|
|
124
|
+
enum: propSchema.enum,
|
|
125
|
+
format: propSchema.format,
|
|
126
|
+
items,
|
|
127
|
+
// نستخدم المتغير الذي تم حسابه بأمان
|
|
128
|
+
properties: propSchema.properties ? parseSchema("sub-object", propSchema).properties : void 0
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { name, description: schema.description, properties };
|
|
133
|
+
}
|
|
77
134
|
function parseSpecToModules(spec) {
|
|
78
|
-
const modules =
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
135
|
+
const modules = /* @__PURE__ */ new Map();
|
|
136
|
+
const allSchemas = /* @__PURE__ */ new Map();
|
|
137
|
+
const modulePaths = /* @__PURE__ */ new Map();
|
|
138
|
+
const registerSchema = (schema, baseName) => {
|
|
139
|
+
if (!schema) return "unknown";
|
|
140
|
+
if (schema.type === "array" && schema.items) return `${registerSchema(schema.items, `${baseName}Item`)}[]`;
|
|
141
|
+
if (schema.type === "object" || schema.properties || schema.allOf || !schema.type) {
|
|
142
|
+
const typeName = toPascalCase(baseName.replace(/_v\d+(Request|Response)$/, "$1"));
|
|
143
|
+
if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema));
|
|
144
|
+
return typeName;
|
|
145
|
+
}
|
|
146
|
+
return schema.type === "integer" ? "number" : schema.type || "unknown";
|
|
147
|
+
};
|
|
83
148
|
for (const apiPath in spec.paths) {
|
|
84
149
|
const pathItem = spec.paths[apiPath];
|
|
85
150
|
if (!pathItem) continue;
|
|
@@ -87,206 +152,230 @@ function parseSpecToModules(spec) {
|
|
|
87
152
|
if (!Object.values(import_openapi_types.OpenAPIV3.HttpMethods).includes(method)) continue;
|
|
88
153
|
const endpoint = pathItem[method];
|
|
89
154
|
if (!endpoint.operationId) continue;
|
|
90
|
-
const moduleName =
|
|
91
|
-
if (!modules
|
|
92
|
-
modules
|
|
93
|
-
modulePaths
|
|
155
|
+
const moduleName = getModuleName(endpoint.operationId);
|
|
156
|
+
if (!modules.has(moduleName)) {
|
|
157
|
+
modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set() });
|
|
158
|
+
modulePaths.set(moduleName, []);
|
|
94
159
|
}
|
|
95
|
-
|
|
96
|
-
|
|
160
|
+
const currentModule = modules.get(moduleName);
|
|
161
|
+
modulePaths.get(moduleName).push(apiPath);
|
|
162
|
+
const successKey = Object.keys(endpoint.responses).find((c) => c.startsWith("2"));
|
|
163
|
+
const successRes = successKey ? endpoint.responses[successKey] : void 0;
|
|
164
|
+
const outputType = successKey === "204" ? "void" : registerSchema(successRes?.content?.["application/json"]?.schema, `${endpoint.operationId}Response`);
|
|
165
|
+
const reqBody = endpoint.requestBody;
|
|
166
|
+
let inputType = "undefined";
|
|
167
|
+
if (reqBody?.content?.["application/json"]?.schema) inputType = registerSchema(reqBody.content["application/json"].schema, `${endpoint.operationId}Request`);
|
|
168
|
+
else if ((endpoint.parameters || []).some((p) => p.in === "query")) inputType = "QueryOptions";
|
|
97
169
|
[inputType, outputType].forEach((t) => {
|
|
98
|
-
|
|
99
|
-
|
|
170
|
+
const cleanType = t.replace("[]", "");
|
|
171
|
+
if (cleanType && !["unknown", "undefined", "void", "any", "QueryOptions", "Promise"].includes(cleanType)) {
|
|
172
|
+
currentModule.schemas.add(cleanType);
|
|
100
173
|
}
|
|
101
174
|
});
|
|
102
|
-
const actionName =
|
|
103
|
-
|
|
104
|
-
method: method.toUpperCase(),
|
|
105
|
-
path: apiPath,
|
|
106
|
-
// سنقوم بتعديل هذا لاحقًا ليصبح نسبيًا
|
|
107
|
-
description: endpoint.summary || "",
|
|
108
|
-
hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
|
|
109
|
-
autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
|
|
110
|
-
invalidates: [],
|
|
111
|
-
_inputType: inputType,
|
|
112
|
-
_outputType: outputType
|
|
113
|
-
};
|
|
175
|
+
const actionName = getActionName(endpoint.operationId);
|
|
176
|
+
currentModule.actions[actionName] = { name: actionName, method: method.toUpperCase(), path: apiPath, description: endpoint.summary || "", hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"), autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"), requiresAuth: !!endpoint.security && endpoint.security.length > 0, inputType, outputType };
|
|
114
177
|
}
|
|
115
178
|
}
|
|
116
|
-
|
|
117
|
-
const basePath =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const action = modules[moduleName].actions[actionName];
|
|
179
|
+
modules.forEach((mod, name) => {
|
|
180
|
+
const basePath = findCommonPath(modulePaths.get(name));
|
|
181
|
+
mod.baseEndpoint = basePath;
|
|
182
|
+
Object.values(mod.actions).forEach((action) => {
|
|
121
183
|
const relativePath = action.path.replace(basePath, "").replace(/^\//, "");
|
|
122
184
|
action.path = relativePath === "" ? "/" : relativePath;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
function _extractInputOutputTypes(endpoint, schemas) {
|
|
128
|
-
const operationId = endpoint.operationId || "UnnamedOperation";
|
|
129
|
-
const successResponseKey = Object.keys(endpoint.responses).find((code) => code.startsWith("2"));
|
|
130
|
-
const successResponse = successResponseKey ? endpoint.responses[successResponseKey] : void 0;
|
|
131
|
-
const responseSchema = successResponse?.content?.["application/json"]?.schema;
|
|
132
|
-
const outputType = successResponseKey === "204" ? "void" : _schemaToTypeName(responseSchema, `${operationId}Response`, schemas);
|
|
133
|
-
const requestBody = endpoint.requestBody;
|
|
134
|
-
const requestSchema = requestBody?.content?.["application/json"]?.schema;
|
|
135
|
-
let inputType = "undefined";
|
|
136
|
-
if (requestSchema) {
|
|
137
|
-
inputType = _schemaToTypeName(requestSchema, `${operationId}Request`, schemas);
|
|
138
|
-
} else if ((endpoint.parameters || []).some((p) => p.in === "query")) {
|
|
139
|
-
inputType = "QueryOptions";
|
|
140
|
-
}
|
|
141
|
-
return { inputType, outputType };
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
debugLog("Final Parsed Modules", Object.fromEntries(modules));
|
|
188
|
+
return { modules, allSchemas };
|
|
142
189
|
}
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
190
|
+
async function generateModuleFiles(module2, allSchemas, outputDir) {
|
|
191
|
+
const moduleOutputPath = import_path.default.join(outputDir, module2.moduleName);
|
|
192
|
+
if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
|
|
193
|
+
console.log(import_chalk.default.cyan(`
|
|
194
|
+
Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
|
|
195
|
+
const typesToImport = [...module2.schemas].sort();
|
|
196
|
+
let configContent = `/* eslint-disable */
|
|
197
|
+
// This file is auto-generated.
|
|
198
|
+
|
|
199
|
+
import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
|
|
200
|
+
`;
|
|
201
|
+
if (typesToImport.length > 0) configContent += `import type { ${typesToImport.join(", ")} } from './types';
|
|
202
|
+
`;
|
|
203
|
+
const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
|
|
204
|
+
const actionsValue = Object.values(module2.actions).map((a) => {
|
|
205
|
+
const { inputType, outputType, name, ...c } = a;
|
|
206
|
+
return ` ${name}: ${JSON.stringify(c, null, 2).replace(/\n/g, "\n ")}`;
|
|
207
|
+
}).join(",\n");
|
|
208
|
+
configContent += `
|
|
209
|
+
export const ${module2.moduleName}Module: ApiModuleConfig<{
|
|
210
|
+
${actionsType}
|
|
211
|
+
}> = {
|
|
212
|
+
baseEndpoint: '${module2.baseEndpoint}',
|
|
213
|
+
actions: {
|
|
214
|
+
${actionsValue}
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
`;
|
|
218
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
|
|
219
|
+
console.log(import_chalk.default.gray(` \u2713 config.ts`));
|
|
220
|
+
const indexContent = [`// This file is auto-generated.
|
|
221
|
+
|
|
222
|
+
export * from './config';`];
|
|
223
|
+
if (typesToImport.length > 0) {
|
|
224
|
+
let typesContent = `// This file is auto-generated.
|
|
225
|
+
|
|
226
|
+
`;
|
|
227
|
+
for (const typeName of typesToImport) {
|
|
228
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
229
|
+
if (parsedSchema) {
|
|
230
|
+
if (parsedSchema.description) typesContent += `/**
|
|
231
|
+
* ${parsedSchema.description}
|
|
232
|
+
*/
|
|
233
|
+
`;
|
|
234
|
+
typesContent += `export interface ${typeName} {
|
|
235
|
+
`;
|
|
236
|
+
for (const prop of parsedSchema.properties) {
|
|
237
|
+
if (prop.description) typesContent += ` /**
|
|
238
|
+
* ${prop.description}
|
|
239
|
+
${prop.example ? ` * @example ${JSON.stringify(prop.example)}
|
|
240
|
+
` : ""} */
|
|
241
|
+
`;
|
|
242
|
+
let propType = prop.type;
|
|
243
|
+
if (prop.enum) propType = prop.enum.map((e) => typeof e === "string" ? `'${e}'` : e).join(" | ");
|
|
244
|
+
else if (prop.items) propType = `${toPascalCase(prop.items.name)}[]`;
|
|
245
|
+
typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
|
|
246
|
+
`;
|
|
247
|
+
}
|
|
248
|
+
typesContent += `}
|
|
249
|
+
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
163
252
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
console.log(import_chalk.default.gray(` - Generating types.ts with ${typeNames.size} types...`));
|
|
181
|
-
let typesContent = `// This file is auto-generated by the API generator. Do not edit.
|
|
253
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "types.ts"), typesContent);
|
|
254
|
+
console.log(import_chalk.default.gray(` \u2713 types.ts`));
|
|
255
|
+
indexContent.push(`export * from './types';`);
|
|
256
|
+
let validationContent = `// This file is auto-generated.
|
|
257
|
+
import { z } from 'zod';
|
|
258
|
+
|
|
259
|
+
`;
|
|
260
|
+
for (const typeName of typesToImport) {
|
|
261
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
262
|
+
if (parsedSchema) {
|
|
263
|
+
let zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
264
|
+
validationContent += `export const ${typeName}Schema = z.object({
|
|
265
|
+
${zodShape}
|
|
266
|
+
});
|
|
182
267
|
|
|
183
268
|
`;
|
|
184
|
-
const compilationOptions = {
|
|
185
|
-
bannerComment: "",
|
|
186
|
-
style: { bracketSpacing: true, printWidth: 120, semi: true, singleQuote: true, tabWidth: 2, trailingComma: "es5", useTabs: false },
|
|
187
|
-
additionalProperties: false
|
|
188
|
-
};
|
|
189
|
-
for (const typeName of Array.from(typeNames).sort()) {
|
|
190
|
-
const schema = allSchemas[typeName];
|
|
191
|
-
if (schema) {
|
|
192
|
-
try {
|
|
193
|
-
const tsType = await (0, import_json_schema_to_typescript.compile)(schema, typeName, compilationOptions);
|
|
194
|
-
typesContent += tsType + "\n";
|
|
195
|
-
} catch (compileError) {
|
|
196
|
-
console.error(import_chalk.default.red(` - Error compiling type "${typeName}": ${compileError.message}`));
|
|
197
269
|
}
|
|
198
|
-
} else {
|
|
199
|
-
console.log(import_chalk.default.yellow(` - Warning: Schema for type "${typeName}" not found.`));
|
|
200
270
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const actionsCount = Object.keys(moduleData.actions).length;
|
|
207
|
-
console.log(import_chalk.default.gray(` - Generating config.ts with ${actionsCount} actions...`));
|
|
208
|
-
const typeNamesArray = [...moduleData.types].sort();
|
|
209
|
-
const typesImportStatement = typeNamesArray.length > 0 ? `import type { ${typeNamesArray.join(", ")} } from './types';` : ``;
|
|
210
|
-
const actionsTypeParts = Object.entries(moduleData.actions).map(
|
|
211
|
-
([actionName, actionData]) => ` ${actionName}: ActionConfigModule<${actionData._inputType}, ${actionData._outputType}>;`
|
|
212
|
-
);
|
|
213
|
-
const actionsTypeDefinition = `{
|
|
214
|
-
${actionsTypeParts.join("\n")}
|
|
215
|
-
}`;
|
|
216
|
-
const actionsValueParts = Object.entries(moduleData.actions).map(([actionName, actionData]) => {
|
|
217
|
-
const { _inputType, _outputType, ...config } = actionData;
|
|
218
|
-
return ` ${actionName}: ${JSON.stringify(config, null, 2).replace(/\n/g, "\n ")}`;
|
|
219
|
-
});
|
|
220
|
-
const actionsValueDefinition = `{
|
|
221
|
-
${actionsValueParts.join(",\n")}
|
|
222
|
-
}`;
|
|
223
|
-
const configContent = `/* eslint-disable */
|
|
224
|
-
// This file is auto-generated...
|
|
271
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "validation.ts"), validationContent);
|
|
272
|
+
console.log(import_chalk.default.gray(` \u2713 validation.ts`));
|
|
273
|
+
indexContent.push(`export * from './validation';`);
|
|
274
|
+
let mocksContent = `// This file is auto-generated.
|
|
275
|
+
import type { ${typesToImport.join(", ")} } from './types';
|
|
225
276
|
|
|
226
|
-
|
|
227
|
-
|
|
277
|
+
`;
|
|
278
|
+
for (const typeName of typesToImport) {
|
|
279
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
280
|
+
if (parsedSchema) {
|
|
281
|
+
let mockObject = {};
|
|
282
|
+
parsedSchema.properties.forEach((p) => {
|
|
283
|
+
mockObject[p.name] = _propToMock(p);
|
|
284
|
+
});
|
|
285
|
+
mocksContent += `export const mock${typeName}: ${typeName} = ${JSON.stringify(mockObject, null, 2)};
|
|
228
286
|
|
|
229
|
-
export const ${moduleName}Module: ApiModuleConfig<${actionsTypeDefinition}> = {
|
|
230
|
-
baseEndpoint: '${moduleData.baseEndpoint}',
|
|
231
|
-
actions: ${actionsValueDefinition},
|
|
232
|
-
};
|
|
233
287
|
`;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return `${controllerName}Api`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "mocks.ts"), mocksContent);
|
|
291
|
+
console.log(import_chalk.default.gray(` \u2713 mocks.ts`));
|
|
292
|
+
indexContent.push(`export * from './mocks';`);
|
|
293
|
+
}
|
|
294
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "index.ts"), indexContent.join("\n"));
|
|
295
|
+
console.log(import_chalk.default.gray(` \u2713 index.ts`));
|
|
243
296
|
}
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
297
|
+
function _propToZod(prop) {
|
|
298
|
+
let zodString = "z.any()";
|
|
299
|
+
const requiredMsg = { required_error: prop.description || generateFriendlyMessage(prop.name, "required") };
|
|
300
|
+
switch (prop.type) {
|
|
301
|
+
case "string":
|
|
302
|
+
zodString = `z.string(${JSON.stringify(requiredMsg)})`;
|
|
303
|
+
if (prop.format === "email") zodString += `.email({ message: "${generateFriendlyMessage(prop.name, "email")}" })`;
|
|
304
|
+
if (prop.format === "uuid") zodString += `.uuid()`;
|
|
305
|
+
if (prop.enum) zodString = `z.enum(${JSON.stringify(prop.enum)}, ${JSON.stringify(requiredMsg)})`;
|
|
306
|
+
break;
|
|
307
|
+
case "integer":
|
|
308
|
+
case "number":
|
|
309
|
+
zodString = `z.number(${JSON.stringify(requiredMsg)})`;
|
|
310
|
+
break;
|
|
311
|
+
case "boolean":
|
|
312
|
+
zodString = `z.boolean(${JSON.stringify(requiredMsg)})`;
|
|
313
|
+
break;
|
|
314
|
+
case "array":
|
|
315
|
+
zodString = `z.array(${prop.items ? _propToZod(prop.items) : "z.any()"})`;
|
|
316
|
+
break;
|
|
317
|
+
case "object":
|
|
318
|
+
let shape = "z.any()";
|
|
319
|
+
if (prop.properties) shape = `z.object({
|
|
320
|
+
${prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n")}
|
|
321
|
+
})`;
|
|
322
|
+
zodString = shape;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
if (!prop.isRequired) zodString += ".optional()";
|
|
326
|
+
if (prop.isNullable) zodString += ".nullable()";
|
|
327
|
+
return zodString;
|
|
249
328
|
}
|
|
250
|
-
function
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
329
|
+
function _propToMock(prop) {
|
|
330
|
+
if (prop.example) return prop.example;
|
|
331
|
+
if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
|
|
332
|
+
if (prop.enum) return prop.enum[0];
|
|
333
|
+
switch (prop.type) {
|
|
334
|
+
case "string":
|
|
335
|
+
if (prop.format === "email") return "test@example.com";
|
|
336
|
+
if (prop.format === "uuid") return "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
|
|
337
|
+
return `Mock ${toPascalCase(prop.name)}`;
|
|
338
|
+
case "integer":
|
|
339
|
+
case "number":
|
|
340
|
+
return 1;
|
|
341
|
+
case "boolean":
|
|
342
|
+
return true;
|
|
343
|
+
case "array":
|
|
344
|
+
return prop.items ? [_propToMock(prop.items)] : [];
|
|
345
|
+
case "object":
|
|
346
|
+
const mock = {};
|
|
347
|
+
if (prop.properties) prop.properties.forEach((p) => {
|
|
348
|
+
mock[p.name] = _propToMock(p);
|
|
349
|
+
});
|
|
350
|
+
return mock;
|
|
351
|
+
default:
|
|
352
|
+
return null;
|
|
258
353
|
}
|
|
259
|
-
let prefix = first.substring(0, i);
|
|
260
|
-
return prefix.substring(0, prefix.lastIndexOf("/") + 1) || "/";
|
|
261
354
|
}
|
|
262
355
|
async function runGenerator(options) {
|
|
263
|
-
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API
|
|
356
|
+
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Phoenix Edition)..."));
|
|
264
357
|
import_dotenv.default.config({ path: options.envPath });
|
|
265
358
|
const specUrl = process.env.OPENAPI_SPEC_URL || "./swagger.json";
|
|
266
|
-
console.log(import_chalk.default.blue("\u2713 Step 1: Environment variables loaded."));
|
|
267
359
|
try {
|
|
268
360
|
console.log(import_chalk.default.blue(`
|
|
269
|
-
\u23F3 Step
|
|
270
|
-
const
|
|
271
|
-
console.log(import_chalk.default.green("\u2713 Spec
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
console.log(import_chalk.default.green(`\u2713 Found and grouped ${
|
|
276
|
-
console.log(import_chalk.default.blue("\n\u23F3 Step
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
for (const moduleName in modules) {
|
|
280
|
-
const moduleData = modules[moduleName];
|
|
281
|
-
await generateModuleFiles(moduleName, moduleData, allSchemas, modulesOutputPath);
|
|
361
|
+
\u23F3 Step 1: Dereferencing spec from ${specUrl}...`));
|
|
362
|
+
const spec = await import_swagger_parser.default.dereference(specUrl);
|
|
363
|
+
console.log(import_chalk.default.green("\u2713 Spec fully dereferenced."));
|
|
364
|
+
debugLog("Dereferenced Spec", spec);
|
|
365
|
+
console.log(import_chalk.default.blue("\n\u23F3 Step 2: Parsing spec with intelligent grouping..."));
|
|
366
|
+
const { modules, allSchemas } = parseSpecToModules(spec);
|
|
367
|
+
console.log(import_chalk.default.green(`\u2713 Found and grouped ${modules.size} logical modules.`));
|
|
368
|
+
console.log(import_chalk.default.blue("\n\u23F3 Step 3: Generating all module files..."));
|
|
369
|
+
for (const module2 of modules.values()) {
|
|
370
|
+
await generateModuleFiles(module2, allSchemas, options.output);
|
|
282
371
|
}
|
|
283
|
-
console.log(import_chalk.default.green("\u2713 All module files generated."));
|
|
284
|
-
console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete!"));
|
|
372
|
+
console.log(import_chalk.default.green("\n\u2713 All module files generated successfully."));
|
|
373
|
+
console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! Your development platform is ready."));
|
|
285
374
|
console.log(import_chalk.default.bold.cyan(` Output directory: ${options.output}`));
|
|
286
375
|
} catch (error) {
|
|
287
376
|
console.error(import_chalk.default.red.bold("\n\u274C An error occurred during generation:"));
|
|
288
377
|
console.error(import_chalk.default.red(`Error Message: ${error.message}`));
|
|
289
|
-
if (error.stack) console.error(import_chalk.default.gray(error.stack));
|
|
378
|
+
if (error.stack && DEBUG_MODE) console.error(import_chalk.default.gray(error.stack));
|
|
290
379
|
process.exit(1);
|
|
291
380
|
}
|
|
292
381
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-core-lib",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.68",
|
|
4
4
|
"description": "A flexible and powerful API client library for modern web applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -42,24 +42,27 @@
|
|
|
42
42
|
"chalk": "^4.1.2",
|
|
43
43
|
"colorette": "^2.0.20",
|
|
44
44
|
"commander": "^9.4.1",
|
|
45
|
-
"dotenv": "^16.
|
|
45
|
+
"dotenv": "^16.6.1",
|
|
46
46
|
"fast-deep-equal": "^3.1.3",
|
|
47
47
|
"fs-extra": "^11.3.1",
|
|
48
48
|
"json-schema-to-typescript": "^15.0.4",
|
|
49
49
|
"openapi-typescript": "^6.2.4",
|
|
50
50
|
"openapi-typescript-codegen": "^0.29.0",
|
|
51
51
|
"path": "^0.12.7",
|
|
52
|
-
"
|
|
52
|
+
"swagger-parser": "^10.0.3",
|
|
53
|
+
"uuid": "^9.0.1",
|
|
54
|
+
"zod": "^4.1.5"
|
|
53
55
|
},
|
|
54
56
|
"devDependencies": {
|
|
55
57
|
"@openapitools/openapi-generator-cli": "^2.23.1",
|
|
56
58
|
"@types/fs-extra": "^11.0.4",
|
|
57
59
|
"@types/jest": "^30.0.0",
|
|
58
|
-
"@types/node": "^24.
|
|
60
|
+
"@types/node": "^24.3.0",
|
|
59
61
|
"@types/react": "^18.3.2",
|
|
60
62
|
"@types/uuid": "^9.0.8",
|
|
61
63
|
"javascript-obfuscator": "^4.1.1",
|
|
62
64
|
"jest": "^30.0.5",
|
|
65
|
+
"openapi-types": "^12.1.3",
|
|
63
66
|
"react": "19.1.0",
|
|
64
67
|
"react-dom": "19.1.0",
|
|
65
68
|
"ts-jest": "^29.4.1",
|