api-core-lib 12.0.86 → 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 +206 -112
- 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,141 +322,144 @@ 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
|
|
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();
|
|
260
327
|
const enumsToImport = [...module2.enums].sort();
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
if (enumsToImport.length > 0) indexExports.push("enums");
|
|
264
|
-
indexExports.push("types", "validation", "mocks");
|
|
265
|
-
}
|
|
266
|
-
const initialIndexContent = `// This file is auto-generated. Do not edit directly.
|
|
267
|
-
|
|
268
|
-
` + indexExports.map((e) => `export * from './${e}';`).join("\n");
|
|
269
|
-
const indexFilePath = import_path.default.join(moduleOutputPath, "index.ts");
|
|
270
|
-
import_fs.default.writeFileSync(indexFilePath, initialIndexContent);
|
|
271
|
-
console.log(import_chalk.default.gray(` \u2713 index.ts (Initial)`));
|
|
272
|
-
const moduleBaseName = module2.moduleName.replace(/Api$/, "");
|
|
273
|
-
const camelCaseModuleName = toCamelCase(moduleBaseName);
|
|
274
|
-
const allPathParams = /* @__PURE__ */ new Set();
|
|
275
|
-
Object.values(module2.actions).forEach((action) => {
|
|
276
|
-
action.pathParams.forEach((param) => allPathParams.add(param));
|
|
277
|
-
});
|
|
278
|
-
const pathParamsType = allPathParams.size > 0 ? `{ ${[...allPathParams].map((p) => `${p}?: string | number`).join("; ")} }` : "Record<string, never>";
|
|
279
|
-
let endpointsContent = `// This file is auto-generated. Do not edit directly.
|
|
328
|
+
const createdFileExports = ["config"];
|
|
329
|
+
let configContent = `// This file is auto-generated. Do not edit directly.
|
|
280
330
|
|
|
331
|
+
import type { ApiModuleConfig, ActionConfigModule } from 'api-core-lib';
|
|
281
332
|
`;
|
|
282
|
-
|
|
333
|
+
if (schemasToImport.length > 0) configContent += `import type { ${schemasToImport.join(", ")} } from './types';
|
|
283
334
|
`;
|
|
284
|
-
const
|
|
285
|
-
Object.values(module2.actions).
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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.
|
|
292
355
|
|
|
293
356
|
`;
|
|
294
|
-
|
|
295
|
-
|
|
357
|
+
enumsToImport.forEach((enumName) => {
|
|
358
|
+
const enumDef = allEnums.get(enumName);
|
|
359
|
+
if (enumDef) {
|
|
360
|
+
enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
|
|
296
361
|
`;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (action.hasQuery) {
|
|
306
|
-
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");
|
|
307
370
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
* ${action.description}
|
|
311
|
-
*/
|
|
371
|
+
let typesContent = `// This file is auto-generated. Do not edit directly.
|
|
372
|
+
|
|
312
373
|
`;
|
|
313
|
-
|
|
374
|
+
if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
|
|
375
|
+
|
|
314
376
|
`;
|
|
315
|
-
|
|
377
|
+
schemasToImport.forEach((typeName) => {
|
|
378
|
+
const parsedSchema = allSchemas.get(typeName);
|
|
379
|
+
if (parsedSchema) {
|
|
380
|
+
if (parsedSchema.description) typesContent += `/** ${parsedSchema.description} */
|
|
316
381
|
`;
|
|
317
|
-
|
|
382
|
+
typesContent += `export interface ${typeName} {
|
|
318
383
|
`;
|
|
319
|
-
|
|
384
|
+
parsedSchema.properties.forEach((prop) => {
|
|
385
|
+
if (prop.description) typesContent += ` /** ${prop.description} */
|
|
320
386
|
`;
|
|
321
|
-
|
|
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};
|
|
322
391
|
`;
|
|
323
|
-
|
|
392
|
+
});
|
|
393
|
+
typesContent += `}
|
|
324
394
|
|
|
325
395
|
`;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
import { useApiModule, UseApiModuleOptions } from 'api-core-lib/client';
|
|
334
|
-
import { apiClient } from '@/lib/api-core/clientApi'; // Assuming a fixed path
|
|
335
|
-
import { ${module2.moduleName} as TModuleType } from './config';
|
|
336
|
-
import { ${module2.moduleName} } from './config';
|
|
337
|
-
|
|
338
|
-
type ModulePathParams = ${pathParamsType};
|
|
339
|
-
|
|
340
|
-
// Options for the custom hook, extending the base options with typed path parameters.
|
|
341
|
-
type ${moduleBaseName}ApiOptions = Omit<UseApiModuleOptions, 'pathParams'> & {
|
|
342
|
-
pathParams?: ModulePathParams;
|
|
343
|
-
};
|
|
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.
|
|
344
402
|
|
|
345
|
-
|
|
346
|
-
* Custom hook for interacting with the ${moduleBaseName} API module.
|
|
347
|
-
* Simplifies API calls by pre-configuring the API client and module.
|
|
348
|
-
*/
|
|
349
|
-
export const use${moduleBaseName}Api = (options: ${moduleBaseName}ApiOptions = {}) => {
|
|
350
|
-
return useApiModule<typeof TModuleType['actions']>(apiClient, ${module2.moduleName}, options);
|
|
351
|
-
};
|
|
403
|
+
import { z } from 'zod';
|
|
352
404
|
`;
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
+
});
|
|
361
417
|
|
|
362
|
-
|
|
418
|
+
`;
|
|
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';
|
|
363
430
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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)};
|
|
368
440
|
|
|
369
|
-
/**
|
|
370
|
-
* A dedicated React Provider that initializes and provides the ${moduleBaseName} API context.
|
|
371
|
-
*/
|
|
372
|
-
export function ${moduleBaseName}Provider({ children, options = {} }: ${moduleBaseName}ProviderProps) {
|
|
373
|
-
const api = use${moduleBaseName}Api(options);
|
|
374
|
-
return <ApiModuleProvider value={api}>{children}</ApiModuleProvider>;
|
|
375
|
-
}
|
|
376
441
|
`;
|
|
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
|
+
}
|
|
448
|
+
const indexFilePath = import_path.default.join(moduleOutputPath, "index.ts");
|
|
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)`));
|
|
377
461
|
const providerFileName = `${moduleBaseName}.provider.tsx`;
|
|
378
|
-
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, providerFileName), providerContent);
|
|
379
462
|
console.log(import_chalk.default.gray(` \u2713 ${providerFileName} (React Provider)`));
|
|
380
|
-
const serverContent = `// This file is auto-generated. For server-side use only.
|
|
381
|
-
|
|
382
|
-
import { createServerApi } from 'api-core-lib/server';
|
|
383
|
-
import { serverApiClient } from '@/lib/api-core/serverApi'; // Assuming a fixed path
|
|
384
|
-
import { ${module2.moduleName} } from './config';
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Creates a server-side instance of the ${moduleBaseName} API for pre-fetching data in RSC.
|
|
388
|
-
*/
|
|
389
|
-
export const create${moduleBaseName}ServerApi = () => {
|
|
390
|
-
return createServerApi(serverApiClient, ${module2.moduleName});
|
|
391
|
-
};
|
|
392
|
-
`;
|
|
393
|
-
import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.server.ts`), serverContent);
|
|
394
463
|
console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.server.ts (Server-Side Helper)`));
|
|
395
464
|
const newExports = [
|
|
396
465
|
`
|
|
@@ -398,12 +467,37 @@ export const create${moduleBaseName}ServerApi = () => {
|
|
|
398
467
|
`export * from './${camelCaseModuleName}.endpoints';`,
|
|
399
468
|
`export * from './use${moduleBaseName}';`,
|
|
400
469
|
`export * from './${moduleBaseName}.provider';`,
|
|
401
|
-
// <-- Note: no .tsx in export
|
|
402
470
|
`export * from './${camelCaseModuleName}.server';`
|
|
403
471
|
].join("\n");
|
|
404
472
|
import_fs.default.appendFileSync(indexFilePath, newExports);
|
|
405
473
|
console.log(import_chalk.default.gray(` \u2713 index.ts (Updated with helpers)`));
|
|
406
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
|
+
}
|
|
500
|
+
}
|
|
407
501
|
async function runGenerator(options) {
|
|
408
502
|
console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Sapphire Edition)..."));
|
|
409
503
|
import_dotenv.default.config({ path: options.envPath });
|