api-core-lib 12.0.85 → 12.0.87
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 +218 -111
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -75,6 +75,72 @@ 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
77
|
|
|
78
|
+
// src/generator/core/_propToZod.ts
|
|
79
|
+
function _propToZod(prop) {
|
|
80
|
+
let zodChain;
|
|
81
|
+
const getMsg = (key) => `{ "message": "validation.${key}" }`;
|
|
82
|
+
switch (prop.type) {
|
|
83
|
+
case "string":
|
|
84
|
+
if (prop.enumName) {
|
|
85
|
+
zodChain = `z.enum(${prop.enumName})`;
|
|
86
|
+
} else {
|
|
87
|
+
zodChain = "z.string()";
|
|
88
|
+
if (prop.isRequired) {
|
|
89
|
+
zodChain += `.min(1, ${getMsg("string.nonempty")})`;
|
|
90
|
+
}
|
|
91
|
+
if (prop.maxLength !== void 0) zodChain += `.max(${prop.maxLength}, ${getMsg("string.max")})`;
|
|
92
|
+
if (prop.pattern) zodChain += `.regex(/${prop.pattern}/, ${getMsg("string.regex")})`;
|
|
93
|
+
if (prop.format === "email") zodChain += `.email(${getMsg("string.email")})`;
|
|
94
|
+
if (prop.format === "url") zodChain += `.url(${getMsg("string.url")})`;
|
|
95
|
+
if (prop.format === "uuid") zodChain += `.uuid(${getMsg("string.uuid")})`;
|
|
96
|
+
if (prop.format === "datetime") zodChain += `.datetime(${getMsg("string.datetime")})`;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case "integer":
|
|
100
|
+
zodChain = `z.number().int(${getMsg("number.integer")})`;
|
|
101
|
+
if (prop.minimum !== void 0) zodChain += `.min(${prop.minimum}, ${getMsg("number.min")})`;
|
|
102
|
+
if (prop.maximum !== void 0) zodChain += `.max(${prop.maximum}, ${getMsg("number.max")})`;
|
|
103
|
+
break;
|
|
104
|
+
case "number":
|
|
105
|
+
zodChain = `z.number()`;
|
|
106
|
+
if (prop.minimum !== void 0) zodChain += `.min(${prop.minimum}, ${getMsg("number.min")})`;
|
|
107
|
+
if (prop.maximum !== void 0) zodChain += `.max(${prop.maximum}, ${getMsg("number.max")})`;
|
|
108
|
+
break;
|
|
109
|
+
case "boolean":
|
|
110
|
+
zodChain = `z.boolean()`;
|
|
111
|
+
break;
|
|
112
|
+
case "array":
|
|
113
|
+
const itemSchema = prop.items ? _propToZod(prop.items) : "z.any()";
|
|
114
|
+
zodChain = `z.array(${itemSchema})`;
|
|
115
|
+
if (prop.minItems !== void 0) zodChain += `.min(${prop.minItems}, ${getMsg("array.min")})`;
|
|
116
|
+
if (prop.maxItems !== void 0) zodChain += `.max(${prop.maxItems}, ${getMsg("array.max")})`;
|
|
117
|
+
break;
|
|
118
|
+
case "object":
|
|
119
|
+
if (prop.properties && prop.properties.length > 0) {
|
|
120
|
+
const shape = prop.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
121
|
+
zodChain = `z.object({
|
|
122
|
+
${shape}
|
|
123
|
+
})`;
|
|
124
|
+
} else {
|
|
125
|
+
zodChain = "z.record(z.unknown())";
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
zodChain = "z.any()";
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (prop.description) {
|
|
133
|
+
zodChain += `.describe(${JSON.stringify(prop.description)})`;
|
|
134
|
+
}
|
|
135
|
+
if (!prop.isRequired) {
|
|
136
|
+
zodChain += ".optional()";
|
|
137
|
+
}
|
|
138
|
+
if (prop.isNullable) {
|
|
139
|
+
zodChain += ".nullable()";
|
|
140
|
+
}
|
|
141
|
+
return zodChain;
|
|
142
|
+
}
|
|
143
|
+
|
|
78
144
|
// src/generator/utils/index.ts
|
|
79
145
|
var getActionName = (opId) => {
|
|
80
146
|
const cleanOpId = opId.replace(/_v\d+$/, "");
|
|
@@ -256,140 +322,181 @@ async function generateModuleFiles(module2, allSchemas, allEnums, outputDir) {
|
|
|
256
322
|
if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
|
|
257
323
|
console.log(import_chalk.default.cyan(`
|
|
258
324
|
Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
});
|
|
265
|
-
const pathParamsType = allPathParams.size > 0 ? `{ ${[...allPathParams].map((p) => `${p}?: string | number`).join("; ")} }` : "never";
|
|
266
|
-
let endpointsContent = `// This file is auto-generated. Do not edit directly.
|
|
325
|
+
const BUILT_IN_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "void", "undefined", "unknown", "any", "Date", "Promise", "QueryOptions", "Record<string, any>", "Record<string, unknown>"]);
|
|
326
|
+
const schemasToImport = [...module2.schemas].filter((name) => !BUILT_IN_TYPES.has(name)).sort();
|
|
327
|
+
const enumsToImport = [...module2.enums].sort();
|
|
328
|
+
const createdFileExports = ["config"];
|
|
329
|
+
let configContent = `// This file is auto-generated. Do not edit directly.
|
|
267
330
|
|
|
331
|
+
import type { ApiModuleConfig, ActionConfigModule } from 'api-core-lib';
|
|
268
332
|
`;
|
|
269
|
-
|
|
333
|
+
if (schemasToImport.length > 0) configContent += `import type { ${schemasToImport.join(", ")} } from './types';
|
|
270
334
|
`;
|
|
271
|
-
const
|
|
272
|
-
Object.values(module2.actions).
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
335
|
+
const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
|
|
336
|
+
const actionsValue = Object.values(module2.actions).map((a) => {
|
|
337
|
+
const { inputType, outputType, name, ...c } = a;
|
|
338
|
+
return ` ${name}: ${JSON.stringify(c, null, 2).replace(/\n/g, "\n ")}`;
|
|
339
|
+
}).join(",\n");
|
|
340
|
+
configContent += `
|
|
341
|
+
export const ${module2.moduleName}: ApiModuleConfig<{
|
|
342
|
+
${actionsType}
|
|
343
|
+
}> = {
|
|
344
|
+
baseEndpoint: '${module2.baseEndpoint}',
|
|
345
|
+
actions: {
|
|
346
|
+
${actionsValue}
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
`;
|
|
350
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
|
|
351
|
+
console.log(import_chalk.default.gray(` \u2713 config.ts`));
|
|
352
|
+
if (schemasToImport.length > 0) {
|
|
353
|
+
if (enumsToImport.length > 0) {
|
|
354
|
+
let enumsContent = `// This file is auto-generated. Do not edit directly.
|
|
279
355
|
|
|
280
356
|
`;
|
|
281
|
-
|
|
282
|
-
|
|
357
|
+
enumsToImport.forEach((enumName) => {
|
|
358
|
+
const enumDef = allEnums.get(enumName);
|
|
359
|
+
if (enumDef) {
|
|
360
|
+
enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
|
|
283
361
|
`;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (action.hasQuery) {
|
|
293
|
-
params.push(`query?: QueryOptions`);
|
|
362
|
+
enumsContent += `export type ${enumName} = typeof ${enumName}[number];
|
|
363
|
+
|
|
364
|
+
`;
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "enums.ts"), enumsContent.trim());
|
|
368
|
+
console.log(import_chalk.default.gray(` \u2713 enums.ts`));
|
|
369
|
+
createdFileExports.push("enums");
|
|
294
370
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
* ${action.description}
|
|
298
|
-
*/
|
|
371
|
+
let typesContent = `// This file is auto-generated. Do not edit directly.
|
|
372
|
+
|
|
299
373
|
`;
|
|
300
|
-
|
|
374
|
+
if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
|
|
375
|
+
|
|
301
376
|
`;
|
|
302
|
-
|
|
377
|
+
schemasToImport.forEach((typeName) => {
|
|
378
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
379
|
+
if (parsedSchema) {
|
|
380
|
+
if (parsedSchema.description) typesContent += `/** ${parsedSchema.description} */
|
|
303
381
|
`;
|
|
304
|
-
|
|
382
|
+
typesContent += `export interface ${typeName} {
|
|
305
383
|
`;
|
|
306
|
-
|
|
384
|
+
parsedSchema.properties.forEach((prop) => {
|
|
385
|
+
if (prop.description) typesContent += ` /** ${prop.description} */
|
|
307
386
|
`;
|
|
308
|
-
|
|
387
|
+
let propType = prop.enumName || (prop.items ? `${prop.items.type || "any"}[]` : prop.type);
|
|
388
|
+
if (propType === "integer") propType = "number";
|
|
389
|
+
if (propType === "object") propType = "Record<string, any>";
|
|
390
|
+
typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
|
|
309
391
|
`;
|
|
310
|
-
|
|
392
|
+
});
|
|
393
|
+
typesContent += `}
|
|
311
394
|
|
|
312
395
|
`;
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
import { useApiModule, UseApiModuleOptions } from 'api-core-lib/client';
|
|
321
|
-
import { apiClient } from '@/lib/api-core/clientApi'; // Assuming a fixed path
|
|
322
|
-
import { ${module2.moduleName} as TModuleType } from './config';
|
|
323
|
-
import { ${module2.moduleName} } from './config';
|
|
324
|
-
|
|
325
|
-
type ModulePathParams = ${pathParamsType};
|
|
326
|
-
|
|
327
|
-
// Options for the custom hook, extending the base options with typed path parameters.
|
|
328
|
-
type ${moduleBaseName}ApiOptions = Omit<UseApiModuleOptions, 'pathParams'> & {
|
|
329
|
-
pathParams?: ModulePathParams;
|
|
330
|
-
};
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "types.ts"), typesContent.trim());
|
|
399
|
+
console.log(import_chalk.default.gray(` \u2713 types.ts`));
|
|
400
|
+
createdFileExports.push("types");
|
|
401
|
+
let validationContent = `// This file is auto-generated. Do not edit directly.
|
|
331
402
|
|
|
332
|
-
|
|
333
|
-
* Custom hook for interacting with the ${moduleBaseName} API module.
|
|
334
|
-
* Simplifies API calls by pre-configuring the API client and module.
|
|
335
|
-
*/
|
|
336
|
-
export const use${moduleBaseName}Api = (options: ${moduleBaseName}ApiOptions = {}) => {
|
|
337
|
-
return useApiModule<typeof TModuleType['actions']>(apiClient, ${module2.moduleName}, options);
|
|
338
|
-
};
|
|
403
|
+
import { z } from 'zod';
|
|
339
404
|
`;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
children: React.ReactNode;
|
|
353
|
-
options?: Options;
|
|
354
|
-
}
|
|
405
|
+
if (enumsToImport.length > 0) validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
|
|
406
|
+
`;
|
|
407
|
+
schemasToImport.forEach((typeName) => {
|
|
408
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
409
|
+
if (parsedSchema) {
|
|
410
|
+
const zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
|
|
411
|
+
validationContent += `
|
|
412
|
+
/** Zod schema for {@link ${typeName}}. */
|
|
413
|
+
`;
|
|
414
|
+
validationContent += `export const ${typeName}Schema = z.object({
|
|
415
|
+
${zodShape}
|
|
416
|
+
});
|
|
355
417
|
|
|
356
|
-
/**
|
|
357
|
-
* A dedicated React Provider that initializes and provides the ${moduleBaseName} API context.
|
|
358
|
-
*/
|
|
359
|
-
export function ${moduleBaseName}Provider({ children, options = {} }: ${moduleBaseName}ProviderProps) {
|
|
360
|
-
const api = use${moduleBaseName}Api(options);
|
|
361
|
-
return <ApiModuleProvider value={api}>{children}</ApiModuleProvider>;
|
|
362
|
-
}
|
|
363
418
|
`;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
419
|
+
validationContent += `export type ${typeName}Validated = z.infer<typeof ${typeName}Schema>;
|
|
420
|
+
`;
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "validation.ts"), validationContent.trim());
|
|
424
|
+
console.log(import_chalk.default.gray(` \u2713 validation.ts`));
|
|
425
|
+
createdFileExports.push("validation");
|
|
426
|
+
let mocksContent = `// This file is auto-generated. Do not edit directly.
|
|
427
|
+
import type { ${schemasToImport.join(", ")} } from './types';
|
|
428
|
+
`;
|
|
429
|
+
if (enumsToImport.length > 0) mocksContent += `import { ${enumsToImport.join(", ")} } from './enums';
|
|
367
430
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
431
|
+
`;
|
|
432
|
+
schemasToImport.forEach((typeName) => {
|
|
433
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
434
|
+
if (parsedSchema) {
|
|
435
|
+
const mockObject = {};
|
|
436
|
+
parsedSchema.properties.forEach((p) => {
|
|
437
|
+
mockObject[p.name] = _propToMock(p);
|
|
438
|
+
});
|
|
439
|
+
mocksContent += `export const mock${typeName}: ${typeName} = ${JSON.stringify(mockObject, null, 2)};
|
|
371
440
|
|
|
372
|
-
/**
|
|
373
|
-
* Creates a server-side instance of the ${moduleBaseName} API for pre-fetching data in RSC.
|
|
374
|
-
*/
|
|
375
|
-
export const create${moduleBaseName}ServerApi = () => {
|
|
376
|
-
return createServerApi(serverApiClient, ${module2.moduleName});
|
|
377
|
-
};
|
|
378
441
|
`;
|
|
379
|
-
|
|
380
|
-
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "mocks.ts"), mocksContent.trim());
|
|
445
|
+
console.log(import_chalk.default.gray(` \u2713 mocks.ts`));
|
|
446
|
+
createdFileExports.push("mocks");
|
|
447
|
+
}
|
|
381
448
|
const indexFilePath = import_path.default.join(moduleOutputPath, "index.ts");
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
export * from './${
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
console.log(import_chalk.default.gray(` \u2713
|
|
449
|
+
const initialIndexContent = `// This file is auto-generated. Do not edit directly.
|
|
450
|
+
|
|
451
|
+
` + createdFileExports.map((e) => `export * from './${e}';`).join("\n");
|
|
452
|
+
import_fs.default.writeFileSync(indexFilePath, initialIndexContent);
|
|
453
|
+
console.log(import_chalk.default.gray(` \u2713 index.ts (Initial)`));
|
|
454
|
+
const moduleBaseName = module2.moduleName.replace(/Api$/, "");
|
|
455
|
+
const camelCaseModuleName = toCamelCase(moduleBaseName);
|
|
456
|
+
const allPathParams = /* @__PURE__ */ new Set();
|
|
457
|
+
Object.values(module2.actions).forEach((action) => action.pathParams.forEach((param) => allPathParams.add(param)));
|
|
458
|
+
const pathParamsType = allPathParams.size > 0 ? `{ ${[...allPathParams].map((p) => `${p}?: string | number`).join("; ")} }` : "Record<string, never>";
|
|
459
|
+
console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.endpoints.ts (Type-Safe Endpoints)`));
|
|
460
|
+
console.log(import_chalk.default.gray(` \u2713 use${moduleBaseName}.ts (Custom Hook)`));
|
|
461
|
+
const providerFileName = `${moduleBaseName}.provider.tsx`;
|
|
462
|
+
console.log(import_chalk.default.gray(` \u2713 ${providerFileName} (React Provider)`));
|
|
463
|
+
console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.server.ts (Server-Side Helper)`));
|
|
464
|
+
const newExports = [
|
|
465
|
+
`
|
|
466
|
+
// Generated API helpers`,
|
|
467
|
+
`export * from './${camelCaseModuleName}.endpoints';`,
|
|
468
|
+
`export * from './use${moduleBaseName}';`,
|
|
469
|
+
`export * from './${moduleBaseName}.provider';`,
|
|
470
|
+
`export * from './${camelCaseModuleName}.server';`
|
|
471
|
+
].join("\n");
|
|
472
|
+
import_fs.default.appendFileSync(indexFilePath, newExports);
|
|
473
|
+
console.log(import_chalk.default.gray(` \u2713 index.ts (Updated with helpers)`));
|
|
474
|
+
}
|
|
475
|
+
function _propToMock(prop) {
|
|
476
|
+
if (prop.example) return prop.example;
|
|
477
|
+
if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
|
|
478
|
+
if (prop.enum) return prop.enum[0];
|
|
479
|
+
switch (prop.type) {
|
|
480
|
+
case "string":
|
|
481
|
+
if (prop.format === "email") return "test@example.com";
|
|
482
|
+
if (prop.format === "uuid") return "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
|
|
483
|
+
return `Mock ${toPascalCase(prop.name)}`;
|
|
484
|
+
case "integer":
|
|
485
|
+
case "number":
|
|
486
|
+
return 1;
|
|
487
|
+
case "boolean":
|
|
488
|
+
return true;
|
|
489
|
+
case "array":
|
|
490
|
+
return prop.items ? [_propToMock(prop.items)] : [];
|
|
491
|
+
case "object":
|
|
492
|
+
const mock = {};
|
|
493
|
+
if (prop.properties) prop.properties.forEach((p) => {
|
|
494
|
+
mock[p.name] = _propToMock(p);
|
|
495
|
+
});
|
|
496
|
+
return mock;
|
|
497
|
+
default:
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
393
500
|
}
|
|
394
501
|
async function runGenerator(options) {
|
|
395
502
|
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Sapphire Edition)..."));
|