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.
Files changed (2) hide show
  1. package/dist/cli.cjs +206 -112
  2. 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 schemasToImport = [...module2.schemas].sort();
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 indexExports = ["config"];
262
- if (schemasToImport.length > 0) {
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
- endpointsContent += `import type { QueryOptions } from 'api-core-lib';
333
+ if (schemasToImport.length > 0) configContent += `import type { ${schemasToImport.join(", ")} } from './types';
283
334
  `;
284
- const endpointTypesToImport = /* @__PURE__ */ new Set();
285
- Object.values(module2.actions).forEach((action) => {
286
- if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") {
287
- endpointTypesToImport.add(action.inputType);
288
- }
289
- });
290
- if (endpointTypesToImport.size > 0) {
291
- endpointsContent += `import type { ${[...endpointTypesToImport].join(", ")} } from './types';
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
- endpointsContent += `export const ${camelCaseModuleName}Endpoints = {
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
- for (const action of Object.values(module2.actions)) {
298
- const params = [];
299
- if (action.pathParams.length > 0) {
300
- params.push(`params: { ${action.pathParams.map((p) => `${p}: string | number`).join("; ")} }`);
301
- }
302
- if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") {
303
- params.push(`body: ${action.inputType}`);
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
- endpointsContent += ` /**
309
- * ${action.method} ${module2.baseEndpoint}${action.path}
310
- * ${action.description}
311
- */
371
+ let typesContent = `// This file is auto-generated. Do not edit directly.
372
+
312
373
  `;
313
- endpointsContent += ` ${action.name}: (${params.join(", ")}) => ({
374
+ if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
375
+
314
376
  `;
315
- endpointsContent += ` action: '${action.name}' as const,
377
+ schemasToImport.forEach((typeName) => {
378
+ const parsedSchema = allSchemas.get(typeName);
379
+ if (parsedSchema) {
380
+ if (parsedSchema.description) typesContent += `/** ${parsedSchema.description} */
316
381
  `;
317
- if (action.pathParams.length > 0) endpointsContent += ` pathParams: params,
382
+ typesContent += `export interface ${typeName} {
318
383
  `;
319
- if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") endpointsContent += ` input: body,
384
+ parsedSchema.properties.forEach((prop) => {
385
+ if (prop.description) typesContent += ` /** ${prop.description} */
320
386
  `;
321
- if (action.hasQuery) endpointsContent += ` input: query,
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
- endpointsContent += ` }),
392
+ });
393
+ typesContent += `}
324
394
 
325
395
  `;
326
- }
327
- endpointsContent += `};
328
- `;
329
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.endpoints.ts`), endpointsContent);
330
- console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.endpoints.ts (Type-Safe Endpoints)`));
331
- const hookContent = `// This file is auto-generated. Do not edit directly.
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
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `use${moduleBaseName}.ts`), hookContent);
354
- console.log(import_chalk.default.gray(` \u2713 use${moduleBaseName}.ts (Custom Hook)`));
355
- const providerContent = `// This file is auto-generated. Do not edit directly.
356
- 'use client';
357
-
358
- import React from 'react';
359
- import { ApiModuleProvider } from 'api-core-lib/client';
360
- import { use${moduleBaseName}Api } from './use${moduleBaseName}';
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
- type Options = Parameters<typeof use${moduleBaseName}Api>[0];
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
- interface ${moduleBaseName}ProviderProps {
365
- children: React.ReactNode;
366
- options?: Options;
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.86",
3
+ "version": "12.0.87",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {