api-core-lib 12.0.72 → 12.0.74
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 +214 -159
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -74,16 +74,142 @@ var import_chalk = __toESM(require("chalk"), 1);
|
|
|
74
74
|
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
75
75
|
var import_swagger_parser = __toESM(require("@apidevtools/swagger-parser"), 1);
|
|
76
76
|
var import_openapi_types = __toESM(require_dist(), 1);
|
|
77
|
+
|
|
78
|
+
// src/generator/core/_propToZod.ts
|
|
79
|
+
function _propToZod(prop) {
|
|
80
|
+
const getMessages = (keys, customErrorMessages) => {
|
|
81
|
+
const messages = {};
|
|
82
|
+
const keyMap = {
|
|
83
|
+
required: "required_error",
|
|
84
|
+
invalid_type: "invalid_type_error"
|
|
85
|
+
};
|
|
86
|
+
for (const key of keys) {
|
|
87
|
+
const zodKey = keyMap[key] || "message";
|
|
88
|
+
const customMessage = customErrorMessages?.[key];
|
|
89
|
+
messages[zodKey] = customMessage || `validation.${key}`;
|
|
90
|
+
}
|
|
91
|
+
return messages;
|
|
92
|
+
};
|
|
93
|
+
let zodChain;
|
|
94
|
+
switch (prop.type) {
|
|
95
|
+
case "string": {
|
|
96
|
+
const messages = getMessages(["required", "invalid_type"], prop.errorMessages);
|
|
97
|
+
zodChain = `z.string(${JSON.stringify(messages)})`;
|
|
98
|
+
if (prop.enum && prop.enum.length > 0) {
|
|
99
|
+
zodChain = `z.enum(${JSON.stringify(prop.enum)}, ${JSON.stringify(messages)})`;
|
|
100
|
+
} else {
|
|
101
|
+
if (prop.minLength !== void 0) {
|
|
102
|
+
const msg = { message: prop.errorMessages?.minLength || "validation.string.min" };
|
|
103
|
+
zodChain += `.min(${prop.minLength}, ${JSON.stringify(msg)})`;
|
|
104
|
+
} else if (prop.isRequired) {
|
|
105
|
+
const msg = { message: prop.errorMessages?.minLength || "validation.string.nonempty" };
|
|
106
|
+
zodChain += `.min(1, ${JSON.stringify(msg)})`;
|
|
107
|
+
}
|
|
108
|
+
if (prop.maxLength !== void 0) {
|
|
109
|
+
const msg = { message: prop.errorMessages?.maxLength || "validation.string.max" };
|
|
110
|
+
zodChain += `.max(${prop.maxLength}, ${JSON.stringify(msg)})`;
|
|
111
|
+
}
|
|
112
|
+
if (prop.pattern) {
|
|
113
|
+
const msg = { message: prop.errorMessages?.pattern || "validation.string.regex" };
|
|
114
|
+
zodChain += `.regex(/${prop.pattern}/, ${JSON.stringify(msg)})`;
|
|
115
|
+
}
|
|
116
|
+
if (prop.format) {
|
|
117
|
+
const msg = { message: prop.errorMessages?.format || `validation.string.${prop.format}` };
|
|
118
|
+
if (prop.format === "email") zodChain += `.email(${JSON.stringify(msg)})`;
|
|
119
|
+
if (prop.format === "url") zodChain += `.url(${JSON.stringify(msg)})`;
|
|
120
|
+
if (prop.format === "uuid") zodChain += `.uuid(${JSON.stringify(msg)})`;
|
|
121
|
+
if (prop.format === "datetime") zodChain += `.datetime(${JSON.stringify(msg)})`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case "integer": {
|
|
127
|
+
const messages = getMessages(["required", "invalid_type"], prop.errorMessages);
|
|
128
|
+
const intMessage = { message: prop.errorMessages?.integer || "validation.number.integer" };
|
|
129
|
+
zodChain = `z.number(${JSON.stringify(messages)}).int(${JSON.stringify(intMessage)})`;
|
|
130
|
+
if (prop.minimum !== void 0) {
|
|
131
|
+
const msg = { message: prop.errorMessages?.minimum || "validation.number.min" };
|
|
132
|
+
zodChain += `.min(${prop.minimum}, ${JSON.stringify(msg)})`;
|
|
133
|
+
}
|
|
134
|
+
if (prop.maximum !== void 0) {
|
|
135
|
+
const msg = { message: prop.errorMessages?.maximum || "validation.number.max" };
|
|
136
|
+
zodChain += `.max(${prop.maximum}, ${JSON.stringify(msg)})`;
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case "number": {
|
|
141
|
+
const messages = getMessages(["required", "invalid_type"], prop.errorMessages);
|
|
142
|
+
zodChain = `z.number(${JSON.stringify(messages)})`;
|
|
143
|
+
if (prop.minimum !== void 0) {
|
|
144
|
+
const msg = { message: prop.errorMessages?.minimum || "validation.number.min" };
|
|
145
|
+
zodChain += `.min(${prop.minimum}, ${JSON.stringify(msg)})`;
|
|
146
|
+
}
|
|
147
|
+
if (prop.maximum !== void 0) {
|
|
148
|
+
const msg = { message: prop.errorMessages?.maximum || "validation.number.max" };
|
|
149
|
+
zodChain += `.max(${prop.maximum}, ${JSON.stringify(msg)})`;
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case "boolean": {
|
|
154
|
+
const messages = getMessages(["required", "invalid_type"], prop.errorMessages);
|
|
155
|
+
zodChain = `z.boolean(${JSON.stringify(messages)})`;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "array": {
|
|
159
|
+
const itemSchema = prop.items ? _propToZod(prop.items) : "z.any()";
|
|
160
|
+
const messages = getMessages(["required", "invalid_type"], prop.errorMessages);
|
|
161
|
+
zodChain = `z.array(${itemSchema}, ${JSON.stringify(messages)})`;
|
|
162
|
+
if (prop.minItems !== void 0) {
|
|
163
|
+
const msg = { message: prop.errorMessages?.minItems || "validation.array.min" };
|
|
164
|
+
zodChain += `.min(${prop.minItems}, ${JSON.stringify(msg)})`;
|
|
165
|
+
}
|
|
166
|
+
if (prop.maxItems !== void 0) {
|
|
167
|
+
const msg = { message: prop.errorMessages?.maxItems || "validation.array.max" };
|
|
168
|
+
zodChain += `.max(${prop.maxItems}, ${JSON.stringify(msg)})`;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "object": {
|
|
173
|
+
if (prop.properties && prop.properties.length > 0) {
|
|
174
|
+
const shape = prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
175
|
+
zodChain = `z.object({
|
|
176
|
+
${shape}
|
|
177
|
+
})`;
|
|
178
|
+
} else {
|
|
179
|
+
zodChain = "z.record(z.unknown())";
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
default:
|
|
184
|
+
zodChain = "z.any()";
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
if (prop.description) {
|
|
188
|
+
zodChain += `.describe(${JSON.stringify(prop.description)})`;
|
|
189
|
+
}
|
|
190
|
+
if (!prop.isRequired) {
|
|
191
|
+
zodChain += ".optional()";
|
|
192
|
+
}
|
|
193
|
+
if (prop.isNullable) {
|
|
194
|
+
zodChain += ".nullable()";
|
|
195
|
+
}
|
|
196
|
+
if (prop.defaultValue !== void 0) {
|
|
197
|
+
zodChain += `.default(${JSON.stringify(prop.defaultValue)})`;
|
|
198
|
+
}
|
|
199
|
+
return zodChain;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/generator/index.ts
|
|
77
203
|
var DEBUG_MODE = process.env.DEBUG === "true";
|
|
78
204
|
var debugLog = (title, data) => DEBUG_MODE && console.log(import_chalk.default.yellow(`
|
|
79
|
-
[DEBUG: ${title}]`), import_util.default.inspect(data, { depth:
|
|
205
|
+
[DEBUG: ${title}]`), import_util.default.inspect(data, { depth: 5, colors: true }));
|
|
80
206
|
var toPascalCase = (str) => str.replace(/[^a-zA-Z0-9_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (w) => w.toUpperCase()).replace(/\s+/g, "");
|
|
81
207
|
var toCamelCase = (str) => {
|
|
82
208
|
const s = toPascalCase(str);
|
|
83
209
|
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
84
210
|
};
|
|
85
|
-
var
|
|
86
|
-
var getActionName = (opId) => toCamelCase(opId.
|
|
211
|
+
var sanitizeForModuleName = (tagName) => `${toPascalCase(tagName.replace(/[^a-zA-Z0-9]/g, " "))}Api`;
|
|
212
|
+
var getActionName = (opId) => toCamelCase(opId.replace(/^(central|tenant)/i, "").replace(/_v\d+$/, ""));
|
|
87
213
|
var findCommonPath = (paths) => {
|
|
88
214
|
if (!paths || paths.length === 0) return "/";
|
|
89
215
|
const sorted = [...paths].sort();
|
|
@@ -94,14 +220,21 @@ var findCommonPath = (paths) => {
|
|
|
94
220
|
const prefix = first.substring(0, i);
|
|
95
221
|
return prefix.substring(0, prefix.lastIndexOf("/") + 1) || "/";
|
|
96
222
|
};
|
|
97
|
-
function parseSchema(name, schema) {
|
|
223
|
+
function parseSchema(name, schema, allEnums) {
|
|
98
224
|
const properties = [];
|
|
225
|
+
const enums = {};
|
|
99
226
|
if (schema.properties) {
|
|
100
227
|
for (const propName in schema.properties) {
|
|
101
228
|
const propSchema = schema.properties[propName];
|
|
102
|
-
let
|
|
229
|
+
let itemSchema;
|
|
103
230
|
if (propSchema.type === "array" && propSchema.items) {
|
|
104
|
-
|
|
231
|
+
const itemTypeName = `${toPascalCase(name)}${toPascalCase(propName)}Item`;
|
|
232
|
+
itemSchema = parseSchema(itemTypeName, propSchema.items, allEnums).properties[0];
|
|
233
|
+
}
|
|
234
|
+
if (propSchema.enum) {
|
|
235
|
+
const enumName = `${toPascalCase(name)}${toPascalCase(propName)}Enum`;
|
|
236
|
+
enums[propName] = propSchema.enum;
|
|
237
|
+
if (!allEnums.has(enumName)) allEnums.set(enumName, { name: enumName, values: propSchema.enum });
|
|
105
238
|
}
|
|
106
239
|
properties.push({
|
|
107
240
|
name: propName,
|
|
@@ -112,24 +245,29 @@ function parseSchema(name, schema) {
|
|
|
112
245
|
example: propSchema.example,
|
|
113
246
|
enum: propSchema.enum,
|
|
114
247
|
format: propSchema.format,
|
|
115
|
-
items,
|
|
116
|
-
|
|
117
|
-
properties: propSchema.properties ? parseSchema("sub-object", propSchema).properties : void 0
|
|
248
|
+
items: itemSchema,
|
|
249
|
+
properties: propSchema.properties ? parseSchema(`${name}${toPascalCase(propName)}`, propSchema, allEnums).properties : void 0
|
|
118
250
|
});
|
|
119
251
|
}
|
|
120
252
|
}
|
|
121
|
-
return { name, description: schema.description, properties };
|
|
253
|
+
return { name, description: schema.description, properties, enums };
|
|
122
254
|
}
|
|
123
255
|
function parseSpecToModules(spec) {
|
|
124
256
|
const modules = /* @__PURE__ */ new Map();
|
|
125
257
|
const allSchemas = /* @__PURE__ */ new Map();
|
|
258
|
+
const allEnums = /* @__PURE__ */ new Map();
|
|
126
259
|
const modulePaths = /* @__PURE__ */ new Map();
|
|
127
260
|
const registerSchema = (schema, baseName) => {
|
|
128
261
|
if (!schema) return "unknown";
|
|
129
|
-
if (schema.type === "array" && schema.items)
|
|
262
|
+
if (schema.type === "array" && schema.items) {
|
|
263
|
+
const itemSchema = schema.items;
|
|
264
|
+
const itemBaseName = `${baseName}Item`;
|
|
265
|
+
if (itemSchema.properties || itemSchema.type === "object") return `${registerSchema(itemSchema, itemBaseName)}[]`;
|
|
266
|
+
return `${itemSchema.type || "unknown"}[]`;
|
|
267
|
+
}
|
|
130
268
|
if (schema.type === "object" || schema.properties || schema.allOf || !schema.type) {
|
|
131
269
|
const typeName = toPascalCase(baseName.replace(/_v\d+(Request|Response)$/, "$1"));
|
|
132
|
-
if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema));
|
|
270
|
+
if (!allSchemas.has(typeName)) allSchemas.set(typeName, parseSchema(typeName, schema, allEnums));
|
|
133
271
|
return typeName;
|
|
134
272
|
}
|
|
135
273
|
return schema.type === "integer" ? "number" : schema.type || "unknown";
|
|
@@ -140,10 +278,11 @@ function parseSpecToModules(spec) {
|
|
|
140
278
|
for (const method in pathItem) {
|
|
141
279
|
if (!Object.values(import_openapi_types.OpenAPIV3.HttpMethods).includes(method)) continue;
|
|
142
280
|
const endpoint = pathItem[method];
|
|
143
|
-
if (!endpoint.operationId) continue;
|
|
144
|
-
const
|
|
281
|
+
if (!endpoint.tags || endpoint.tags.length === 0 || !endpoint.operationId) continue;
|
|
282
|
+
const tagName = endpoint.tags[0];
|
|
283
|
+
const moduleName = sanitizeForModuleName(tagName);
|
|
145
284
|
if (!modules.has(moduleName)) {
|
|
146
|
-
modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set() });
|
|
285
|
+
modules.set(moduleName, { moduleName, baseEndpoint: "", actions: {}, schemas: /* @__PURE__ */ new Set(), enums: /* @__PURE__ */ new Set() });
|
|
147
286
|
modulePaths.set(moduleName, []);
|
|
148
287
|
}
|
|
149
288
|
const currentModule = modules.get(moduleName);
|
|
@@ -159,6 +298,8 @@ function parseSpecToModules(spec) {
|
|
|
159
298
|
const cleanType = t.replace("[]", "");
|
|
160
299
|
if (cleanType && !["unknown", "undefined", "void", "any", "QueryOptions", "Promise"].includes(cleanType)) {
|
|
161
300
|
currentModule.schemas.add(cleanType);
|
|
301
|
+
const schemaDef = allSchemas.get(cleanType);
|
|
302
|
+
if (schemaDef) Object.keys(schemaDef.enums).forEach((propName) => currentModule.enums.add(`${toPascalCase(cleanType)}${toPascalCase(propName)}Enum`));
|
|
162
303
|
}
|
|
163
304
|
});
|
|
164
305
|
const actionName = getActionName(endpoint.operationId);
|
|
@@ -166,28 +307,28 @@ function parseSpecToModules(spec) {
|
|
|
166
307
|
}
|
|
167
308
|
}
|
|
168
309
|
modules.forEach((mod, name) => {
|
|
169
|
-
|
|
170
|
-
mod.
|
|
171
|
-
Object.values(mod.actions).forEach((action) => {
|
|
172
|
-
const relativePath = action.path.replace(basePath, "").replace(/^\//, "");
|
|
173
|
-
action.path = relativePath === "" ? "/" : relativePath;
|
|
174
|
-
});
|
|
310
|
+
mod.baseEndpoint = findCommonPath(modulePaths.get(name));
|
|
311
|
+
Object.values(mod.actions).forEach((action) => action.path = action.path.replace(mod.baseEndpoint, "").replace(/^\//, "") || "/");
|
|
175
312
|
});
|
|
176
313
|
debugLog("Final Parsed Modules", Object.fromEntries(modules));
|
|
177
|
-
return { modules, allSchemas };
|
|
314
|
+
return { modules, allSchemas, allEnums };
|
|
178
315
|
}
|
|
179
|
-
async function generateModuleFiles(module2, allSchemas, outputDir) {
|
|
316
|
+
async function generateModuleFiles(module2, allSchemas, allEnums, outputDir) {
|
|
180
317
|
const moduleOutputPath = import_path.default.join(outputDir, module2.moduleName);
|
|
181
318
|
if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
|
|
182
319
|
console.log(import_chalk.default.cyan(`
|
|
183
320
|
Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
|
|
184
|
-
const
|
|
321
|
+
const schemasToImport = [...module2.schemas].sort();
|
|
322
|
+
const enumsToImport = [...module2.enums].sort();
|
|
323
|
+
const indexContent = [`// This file is auto-generated.
|
|
324
|
+
|
|
325
|
+
export * from './config';`];
|
|
185
326
|
let configContent = `/* eslint-disable */
|
|
186
327
|
// This file is auto-generated.
|
|
187
328
|
|
|
188
329
|
import type { ApiModuleConfig, ActionConfigModule, QueryOptions } from 'api-core-lib';
|
|
189
330
|
`;
|
|
190
|
-
if (
|
|
331
|
+
if (schemasToImport.length > 0) configContent += `import type { ${schemasToImport.join(", ")} } from './types';
|
|
191
332
|
`;
|
|
192
333
|
const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
|
|
193
334
|
const actionsValue = Object.values(module2.actions).map((a) => {
|
|
@@ -206,14 +347,32 @@ ${actionsValue}
|
|
|
206
347
|
`;
|
|
207
348
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
|
|
208
349
|
console.log(import_chalk.default.gray(` \u2713 config.ts`));
|
|
209
|
-
|
|
350
|
+
if (schemasToImport.length > 0) {
|
|
351
|
+
if (enumsToImport.length > 0) {
|
|
352
|
+
let enumsContent = `// This file is auto-generated.
|
|
210
353
|
|
|
211
|
-
|
|
212
|
-
|
|
354
|
+
`;
|
|
355
|
+
for (const enumName of enumsToImport) {
|
|
356
|
+
const enumDef = allEnums.get(enumName);
|
|
357
|
+
if (enumDef) {
|
|
358
|
+
enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
|
|
359
|
+
`;
|
|
360
|
+
enumsContent += `export type ${enumName} = typeof ${enumName}[number];
|
|
361
|
+
|
|
362
|
+
`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "enums.ts"), enumsContent);
|
|
366
|
+
console.log(import_chalk.default.gray(` \u2713 enums.ts`));
|
|
367
|
+
indexContent.push(`export * from './enums';`);
|
|
368
|
+
}
|
|
213
369
|
let typesContent = `// This file is auto-generated.
|
|
214
370
|
|
|
215
371
|
`;
|
|
216
|
-
|
|
372
|
+
if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
|
|
373
|
+
|
|
374
|
+
`;
|
|
375
|
+
for (const typeName of schemasToImport) {
|
|
217
376
|
const parsedSchema = allSchemas.get(typeName);
|
|
218
377
|
if (parsedSchema) {
|
|
219
378
|
if (parsedSchema.description) typesContent += `/**
|
|
@@ -228,7 +387,10 @@ export * from './config';`];
|
|
|
228
387
|
${prop.example ? ` * @example ${JSON.stringify(prop.example)}
|
|
229
388
|
` : ""} */
|
|
230
389
|
`;
|
|
231
|
-
|
|
390
|
+
let propType = prop.type;
|
|
391
|
+
if (prop.enum) propType = `${toPascalCase(typeName)}${toPascalCase(prop.name)}Enum`;
|
|
392
|
+
else if (prop.items) propType = prop.items.name ? `${toPascalCase(prop.items.name)}[]` : `${prop.items.type || "unknown"}[]`;
|
|
393
|
+
else if (prop.type === "object") propType = prop.properties && prop.properties.length > 0 ? `Record<string, unknown>` : `Record<string, unknown>`;
|
|
232
394
|
typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
|
|
233
395
|
`;
|
|
234
396
|
}
|
|
@@ -242,9 +404,11 @@ ${prop.example ? ` * @example ${JSON.stringify(prop.example)}
|
|
|
242
404
|
indexContent.push(`export * from './types';`);
|
|
243
405
|
let validationContent = `// This file is auto-generated.
|
|
244
406
|
import { z } from 'zod';
|
|
407
|
+
`;
|
|
408
|
+
if (enumsToImport.length > 0) validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
|
|
245
409
|
|
|
246
410
|
`;
|
|
247
|
-
for (const typeName of
|
|
411
|
+
for (const typeName of schemasToImport) {
|
|
248
412
|
const parsedSchema = allSchemas.get(typeName);
|
|
249
413
|
if (parsedSchema) {
|
|
250
414
|
let zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
@@ -259,10 +423,12 @@ ${zodShape}
|
|
|
259
423
|
console.log(import_chalk.default.gray(` \u2713 validation.ts`));
|
|
260
424
|
indexContent.push(`export * from './validation';`);
|
|
261
425
|
let mocksContent = `// This file is auto-generated.
|
|
262
|
-
import type { ${
|
|
426
|
+
import type { ${schemasToImport.join(", ")} } from './types';
|
|
427
|
+
`;
|
|
428
|
+
if (enumsToImport.length > 0) mocksContent += `import { ${enumsToImport.join(", ")} } from './enums';
|
|
263
429
|
|
|
264
430
|
`;
|
|
265
|
-
for (const typeName of
|
|
431
|
+
for (const typeName of schemasToImport) {
|
|
266
432
|
const parsedSchema = allSchemas.get(typeName);
|
|
267
433
|
if (parsedSchema) {
|
|
268
434
|
let mockObject = {};
|
|
@@ -281,144 +447,34 @@ import type { ${typesToImport.join(", ")} } from './types';
|
|
|
281
447
|
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "index.ts"), indexContent.join("\n"));
|
|
282
448
|
console.log(import_chalk.default.gray(` \u2713 index.ts`));
|
|
283
449
|
}
|
|
284
|
-
function _propToZod(prop) {
|
|
285
|
-
let chain;
|
|
286
|
-
const requiredErrorMessage = { required_error: `${prop.name} is required.` };
|
|
287
|
-
switch (prop.type) {
|
|
288
|
-
case "string":
|
|
289
|
-
chain = `z.string({ ...requiredErrorMessage, invalid_type_error: "Expected a string for ${prop.name}" })`;
|
|
290
|
-
if (prop.format === "email") chain += `.email({ message: "Invalid email address for ${prop.name}" })`;
|
|
291
|
-
if (prop.format === "uuid") chain += `.uuid({ message: "Invalid UUID for ${prop.name}" })`;
|
|
292
|
-
if (prop.format === "url") chain += `.url({ message: "Invalid URL for ${prop.name}" })`;
|
|
293
|
-
if (prop.format === "datetime") chain += `.datetime({ message: "Invalid datetime format for ${prop.name}" })`;
|
|
294
|
-
if (prop.minLength !== void 0) chain += `.min(${prop.minLength}, { message: "${prop.name} must be at least ${prop.minLength} characters long" })`;
|
|
295
|
-
if (prop.maxLength !== void 0) chain += `.max(${prop.maxLength}, { message: "${prop.name} must be at most ${prop.maxLength} characters long" })`;
|
|
296
|
-
if (prop.pattern) chain += `.regex(/${prop.pattern}/, { message: "Invalid format for ${prop.name}" })`;
|
|
297
|
-
if (prop.enum) {
|
|
298
|
-
if (prop.enum.length > 0) {
|
|
299
|
-
chain = `z.enum(${JSON.stringify(prop.enum)})`;
|
|
300
|
-
} else {
|
|
301
|
-
chain = `z.string().refine(() => false, { message: "Enum for ${prop.name} is empty" })`;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
break;
|
|
305
|
-
case "integer":
|
|
306
|
-
chain = `z.number({ ...requiredErrorMessage, invalid_type_error: "Expected a number for ${prop.name}" }).int({ message: "${prop.name} must be an integer" })`;
|
|
307
|
-
if (prop.minimum !== void 0) chain += `.min(${prop.minimum}, { message: "${prop.name} must be at least ${prop.minimum}" })`;
|
|
308
|
-
if (prop.maximum !== void 0) chain += `.max(${prop.maximum}, { message: "${prop.name} must be at most ${prop.maximum}" })`;
|
|
309
|
-
break;
|
|
310
|
-
case "number":
|
|
311
|
-
chain = `z.number({ ...requiredErrorMessage, invalid_type_error: "Expected a number for ${prop.name}" })`;
|
|
312
|
-
if (prop.minimum !== void 0) chain += `.min(${prop.minimum}, { message: "${prop.name} must be at least ${prop.minimum}" })`;
|
|
313
|
-
if (prop.maximum !== void 0) chain += `.max(${prop.maximum}, { message: "${prop.name} must be at most ${prop.maximum}" })`;
|
|
314
|
-
break;
|
|
315
|
-
case "boolean":
|
|
316
|
-
chain = `z.boolean({ ...requiredErrorMessage, invalid_type_error: "Expected a boolean for ${prop.name}" })`;
|
|
317
|
-
break;
|
|
318
|
-
case "array":
|
|
319
|
-
const itemSchema = prop.items ? _propToZod(prop.items) : "z.any()";
|
|
320
|
-
chain = `z.array(${itemSchema})`;
|
|
321
|
-
if (prop.minItems !== void 0) chain += `.min(${prop.minItems}, { message: "${prop.name} must contain at least ${prop.minItems} item(s)" })`;
|
|
322
|
-
if (prop.maxItems !== void 0) chain += `.max(${prop.maxItems}, { message: "${prop.name} must contain at most ${prop.maxItems} item(s)" })`;
|
|
323
|
-
break;
|
|
324
|
-
case "object":
|
|
325
|
-
if (prop.properties && prop.properties.length > 0) {
|
|
326
|
-
const shape = prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
327
|
-
chain = `z.object({
|
|
328
|
-
${shape}
|
|
329
|
-
})`;
|
|
330
|
-
} else {
|
|
331
|
-
chain = "z.record(z.unknown())";
|
|
332
|
-
}
|
|
333
|
-
break;
|
|
334
|
-
default:
|
|
335
|
-
chain = "z.any()";
|
|
336
|
-
break;
|
|
337
|
-
}
|
|
338
|
-
if (prop.description) {
|
|
339
|
-
chain += `.describe(${JSON.stringify(prop.description)})`;
|
|
340
|
-
}
|
|
341
|
-
if (!prop.isRequired) {
|
|
342
|
-
chain += ".optional()";
|
|
343
|
-
}
|
|
344
|
-
if (prop.isNullable) {
|
|
345
|
-
chain += ".nullable()";
|
|
346
|
-
}
|
|
347
|
-
return chain;
|
|
348
|
-
}
|
|
349
|
-
function _generateUUID() {
|
|
350
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
351
|
-
const r = Math.random() * 16 | 0;
|
|
352
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
353
|
-
return v.toString(16);
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
function _getRandomInt(min, max) {
|
|
357
|
-
min = Math.ceil(min);
|
|
358
|
-
max = Math.floor(max);
|
|
359
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
360
|
-
}
|
|
361
|
-
var _firstNames = ["Ahmed", "Fatima", "Mohammed", "Zainab", "Ali", "Nour"];
|
|
362
|
-
var _lastNames = ["Al-Masri", "Khan", "Hassan", "Abbas", "Said"];
|
|
363
450
|
function _propToMock(prop) {
|
|
364
|
-
if (prop.example
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (prop.enum && prop.enum.length > 0) {
|
|
368
|
-
return prop.enum[_getRandomInt(0, prop.enum.length - 1)];
|
|
369
|
-
}
|
|
451
|
+
if (prop.example) return prop.example;
|
|
452
|
+
if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
|
|
453
|
+
if (prop.enum) return prop.enum[0];
|
|
370
454
|
switch (prop.type) {
|
|
371
455
|
case "string":
|
|
372
|
-
if (prop.format === "email") return
|
|
373
|
-
if (prop.format === "uuid") return
|
|
374
|
-
|
|
375
|
-
if (prop.format === "datetime") return (/* @__PURE__ */ new Date()).toISOString();
|
|
376
|
-
const name = prop.name.toLowerCase();
|
|
377
|
-
if (name.includes("image") || name.includes("avatar") || name.includes("logo") || name.includes("picture")) return `https://via.placeholder.com/${_getRandomInt(150, 400)}`;
|
|
378
|
-
if (name.includes("firstname")) return _firstNames[_getRandomInt(0, _firstNames.length - 1)];
|
|
379
|
-
if (name.includes("lastname")) return _lastNames[_getRandomInt(0, _lastNames.length - 1)];
|
|
380
|
-
if (name.includes("name")) return `${_firstNames[_getRandomInt(0, _firstNames.length - 1)]} ${_lastNames[_getRandomInt(0, _lastNames.length - 1)]}`;
|
|
381
|
-
if (name.includes("city")) return "Riyadh";
|
|
382
|
-
if (name.includes("country")) return "Saudi Arabia";
|
|
383
|
-
if (name.includes("phone")) return `+1-${_getRandomInt(100, 999)}-${_getRandomInt(100, 999)}-${_getRandomInt(1e3, 9999)}`;
|
|
384
|
-
if (name.includes("description") || name.includes("comment")) return "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
|
385
|
-
const minLen = prop.minLength || 8;
|
|
386
|
-
const maxLen = prop.maxLength || 16;
|
|
387
|
-
return `Mock${toPascalCase(prop.name)}`.padEnd(_getRandomInt(minLen, maxLen), "x");
|
|
456
|
+
if (prop.format === "email") return "test@example.com";
|
|
457
|
+
if (prop.format === "uuid") return "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
|
|
458
|
+
return `Mock ${toPascalCase(prop.name)}`;
|
|
388
459
|
case "integer":
|
|
389
460
|
case "number":
|
|
390
|
-
|
|
391
|
-
const max = prop.maximum === void 0 ? 1e3 : prop.maximum;
|
|
392
|
-
const randomNum = _getRandomInt(min, max);
|
|
393
|
-
return prop.type === "integer" ? Math.floor(randomNum) : randomNum + Math.random();
|
|
461
|
+
return 1;
|
|
394
462
|
case "boolean":
|
|
395
|
-
return
|
|
463
|
+
return true;
|
|
396
464
|
case "array":
|
|
397
|
-
|
|
398
|
-
const maxItems = prop.maxItems || 3;
|
|
399
|
-
const count = _getRandomInt(minItems, maxItems);
|
|
400
|
-
if (!prop.items) return [];
|
|
401
|
-
const items = [];
|
|
402
|
-
for (let i = 0; i < count; i++) {
|
|
403
|
-
items.push(_propToMock(prop.items));
|
|
404
|
-
}
|
|
405
|
-
return items;
|
|
465
|
+
return prop.items ? [_propToMock(prop.items)] : [];
|
|
406
466
|
case "object":
|
|
407
467
|
const mock = {};
|
|
408
|
-
if (prop.properties) {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
mock[p.name] = _propToMock(p);
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
}
|
|
468
|
+
if (prop.properties) prop.properties.forEach((p) => {
|
|
469
|
+
mock[p.name] = _propToMock(p);
|
|
470
|
+
});
|
|
415
471
|
return mock;
|
|
416
472
|
default:
|
|
417
473
|
return null;
|
|
418
474
|
}
|
|
419
475
|
}
|
|
420
476
|
async function runGenerator(options) {
|
|
421
|
-
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (
|
|
477
|
+
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Sapphire Edition)..."));
|
|
422
478
|
import_dotenv.default.config({ path: options.envPath });
|
|
423
479
|
const specUrl = process.env.OPENAPI_SPEC_URL || "./swagger.json";
|
|
424
480
|
try {
|
|
@@ -426,13 +482,12 @@ async function runGenerator(options) {
|
|
|
426
482
|
\u23F3 Step 1: Dereferencing spec from ${specUrl}...`));
|
|
427
483
|
const spec = await import_swagger_parser.default.dereference(specUrl);
|
|
428
484
|
console.log(import_chalk.default.green("\u2713 Spec fully dereferenced."));
|
|
429
|
-
debugLog("Dereferenced Spec", spec);
|
|
430
485
|
console.log(import_chalk.default.blue("\n\u23F3 Step 2: Parsing spec with intelligent grouping..."));
|
|
431
|
-
const { modules, allSchemas } = parseSpecToModules(spec);
|
|
486
|
+
const { modules, allSchemas, allEnums } = parseSpecToModules(spec);
|
|
432
487
|
console.log(import_chalk.default.green(`\u2713 Found and grouped ${modules.size} logical modules.`));
|
|
433
488
|
console.log(import_chalk.default.blue("\n\u23F3 Step 3: Generating all module files..."));
|
|
434
489
|
for (const module2 of modules.values()) {
|
|
435
|
-
await generateModuleFiles(module2, allSchemas, options.output);
|
|
490
|
+
await generateModuleFiles(module2, allSchemas, allEnums, options.output);
|
|
436
491
|
}
|
|
437
492
|
console.log(import_chalk.default.green("\n\u2713 All module files generated successfully."));
|
|
438
493
|
console.log(import_chalk.default.bold.green("\n\u{1F389} API generation complete! Your development platform is ready."));
|