api-core-lib 12.0.84 → 12.0.86

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 +137 -214
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -75,72 +75,6 @@ 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
-
144
78
  // src/generator/utils/index.ts
145
79
  var getActionName = (opId) => {
146
80
  const cleanOpId = opId.replace(/_v\d+$/, "");
@@ -294,7 +228,20 @@ function parseSpecToModules(spec) {
294
228
  }
295
229
  });
296
230
  const actionName = getActionName(endpoint.operationId);
297
- 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 };
231
+ const pathParams = (apiPath.match(/{(\w+)}/g) || []).map((p) => p.slice(1, -1));
232
+ currentModule.actions[actionName] = {
233
+ name: actionName,
234
+ method: method.toUpperCase(),
235
+ path: apiPath,
236
+ description: endpoint.summary || "",
237
+ hasQuery: (endpoint.parameters || []).some((p) => p.in === "query"),
238
+ autoFetch: method.toUpperCase() === "GET" && !apiPath.includes("{"),
239
+ requiresAuth: !!endpoint.security && endpoint.security.length > 0,
240
+ inputType,
241
+ outputType,
242
+ pathParams
243
+ // <-- حفظ البارامترات المستخرجة
244
+ };
298
245
  }
299
246
  }
300
247
  modules.forEach((mod, name) => {
@@ -309,177 +256,153 @@ async function generateModuleFiles(module2, allSchemas, allEnums, outputDir) {
309
256
  if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
310
257
  console.log(import_chalk.default.cyan(`
311
258
  Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
312
- const BUILT_IN_TYPES = /* @__PURE__ */ new Set([
313
- "string",
314
- "number",
315
- "boolean",
316
- "void",
317
- "undefined",
318
- "unknown",
319
- "any",
320
- "Date",
321
- "Promise",
322
- "QueryOptions",
323
- "Record<string, any>",
324
- "Record<string, unknown>"
325
- ]);
326
- const allCustomSchemaNames = [...module2.schemas].sort();
327
- const schemasToImport = allCustomSchemaNames.filter((name) => !BUILT_IN_TYPES.has(name));
259
+ const schemasToImport = [...module2.schemas].sort();
328
260
  const enumsToImport = [...module2.enums].sort();
329
- const indexContent = [`// This file is auto-generated. Do not edit directly.
330
-
331
- export * from './config';`];
332
- let configContent = `// This file is auto-generated. Do not edit directly.
333
-
334
- import type { ApiModuleConfig, ActionConfigModule } from 'api-core-lib';
335
- `;
261
+ const indexExports = ["config"];
336
262
  if (schemasToImport.length > 0) {
337
- configContent += `import type { ${schemasToImport.join(", ")} } from './types';
338
- `;
263
+ if (enumsToImport.length > 0) indexExports.push("enums");
264
+ indexExports.push("types", "validation", "mocks");
339
265
  }
340
- const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
341
- const actionsValue = Object.values(module2.actions).map((a) => {
342
- const { inputType, outputType, name, ...c } = a;
343
- return ` ${name}: ${JSON.stringify(c, null, 2).replace(/\n/g, "\n ")}`;
344
- }).join(",\n");
345
- configContent += `
346
- export const ${module2.moduleName}Module: ApiModuleConfig<{
347
- ${actionsType}
348
- }> = {
349
- baseEndpoint: '${module2.baseEndpoint}',
350
- actions: {
351
- ${actionsValue}
352
- },
353
- };
354
- `;
355
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
356
- console.log(import_chalk.default.gray(` \u2713 config.ts`));
357
- if (allCustomSchemaNames.length > 0) {
358
- if (enumsToImport.length > 0) {
359
- let enumsContent = `// This file is auto-generated. Do not edit directly.
266
+ const initialIndexContent = `// This file is auto-generated. Do not edit directly.
360
267
 
361
- `;
362
- for (const enumName of enumsToImport) {
363
- const enumDef = allEnums.get(enumName);
364
- if (enumDef) {
365
- enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
366
- `;
367
- enumsContent += `export type ${enumName} = typeof ${enumName}[number];
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.
368
280
 
369
281
  `;
370
- }
371
- }
372
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "enums.ts"), enumsContent);
373
- console.log(import_chalk.default.gray(` \u2713 enums.ts`));
374
- indexContent.push(`export * from './enums';`);
375
- }
376
- let typesContent = `// This file is auto-generated. Do not edit directly.
377
-
282
+ endpointsContent += `import type { QueryOptions } from 'api-core-lib';
378
283
  `;
379
- if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
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';
380
292
 
381
293
  `;
382
- for (const typeName of schemasToImport) {
383
- const parsedSchema = allSchemas.get(typeName);
384
- if (parsedSchema) {
385
- if (parsedSchema.description) typesContent += `/** ${parsedSchema.description} */
386
- `;
387
- typesContent += `export interface ${typeName} {
294
+ }
295
+ endpointsContent += `export const ${camelCaseModuleName}Endpoints = {
388
296
  `;
389
- for (const prop of parsedSchema.properties) {
390
- if (prop.description) typesContent += ` /** ${prop.description} */
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`);
307
+ }
308
+ endpointsContent += ` /**
309
+ * ${action.method} ${module2.baseEndpoint}${action.path}
310
+ * ${action.description}
311
+ */
391
312
  `;
392
- let propType = prop.enumName || (prop.items ? `${prop.items.type || "any"}[]` : prop.type);
393
- if (propType === "integer") propType = "number";
394
- if (propType === "object") propType = "Record<string, any>";
395
- typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
313
+ endpointsContent += ` ${action.name}: (${params.join(", ")}) => ({
396
314
  `;
397
- }
398
- typesContent += `}
399
-
315
+ endpointsContent += ` action: '${action.name}' as const,
400
316
  `;
401
- }
402
- }
403
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "types.ts"), typesContent);
404
- console.log(import_chalk.default.gray(` \u2713 types.ts`));
405
- indexContent.push(`export * from './types';`);
406
- let validationContent = `// This file is auto-generated. Do not edit directly.
407
-
408
- import { z } from 'zod';
317
+ if (action.pathParams.length > 0) endpointsContent += ` pathParams: params,
409
318
  `;
410
- if (enumsToImport.length > 0) {
411
- validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
319
+ if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") endpointsContent += ` input: body,
412
320
  `;
413
- }
414
- for (const typeName of schemasToImport) {
415
- const parsedSchema = allSchemas.get(typeName);
416
- if (parsedSchema) {
417
- const zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
418
- validationContent += `
419
- /** Zod schema for {@link ${typeName}}. */
321
+ if (action.hasQuery) endpointsContent += ` input: query,
420
322
  `;
421
- validationContent += `export const ${typeName}Schema = z.object({
422
- ${zodShape}
423
- });
323
+ endpointsContent += ` }),
424
324
 
425
325
  `;
426
- validationContent += `export type ${typeName}Validated = z.infer<typeof ${typeName}Schema>;
326
+ }
327
+ endpointsContent += `};
427
328
  `;
428
- }
429
- }
430
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "validation.ts"), validationContent);
431
- console.log(import_chalk.default.gray(` \u2713 validation.ts`));
432
- indexContent.push(`export * from './validation';`);
433
- let mocksContent = `// This file is auto-generated. Do not edit directly.
434
- import type { ${schemasToImport.join(", ")} } from './types';
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
+ };
344
+
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
+ };
435
352
  `;
436
- if (enumsToImport.length > 0) mocksContent += `import { ${enumsToImport.join(", ")} } from './enums';
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}';
361
+
362
+ type Options = Parameters<typeof use${moduleBaseName}Api>[0];
363
+
364
+ interface ${moduleBaseName}ProviderProps {
365
+ children: React.ReactNode;
366
+ options?: Options;
367
+ }
437
368
 
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
+ }
438
376
  `;
439
- for (const typeName of schemasToImport) {
440
- const parsedSchema = allSchemas.get(typeName);
441
- if (parsedSchema) {
442
- let mockObject = {};
443
- parsedSchema.properties.forEach((p) => {
444
- mockObject[p.name] = _propToMock(p);
445
- });
446
- mocksContent += `export const mock${typeName}: ${typeName} = ${JSON.stringify(mockObject, null, 2)};
377
+ const providerFileName = `${moduleBaseName}.provider.tsx`;
378
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, providerFileName), providerContent);
379
+ 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';
447
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
+ };
448
392
  `;
449
- }
450
- }
451
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "mocks.ts"), mocksContent);
452
- console.log(import_chalk.default.gray(` \u2713 mocks.ts`));
453
- indexContent.push(`export * from './mocks';`);
454
- }
455
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "index.ts"), indexContent.join("\n"));
456
- console.log(import_chalk.default.gray(` \u2713 index.ts`));
457
- }
458
- function _propToMock(prop) {
459
- if (prop.example) return prop.example;
460
- if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
461
- if (prop.enum) return prop.enum[0];
462
- switch (prop.type) {
463
- case "string":
464
- if (prop.format === "email") return "test@example.com";
465
- if (prop.format === "uuid") return "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
466
- return `Mock ${toPascalCase(prop.name)}`;
467
- case "integer":
468
- case "number":
469
- return 1;
470
- case "boolean":
471
- return true;
472
- case "array":
473
- return prop.items ? [_propToMock(prop.items)] : [];
474
- case "object":
475
- const mock = {};
476
- if (prop.properties) prop.properties.forEach((p) => {
477
- mock[p.name] = _propToMock(p);
478
- });
479
- return mock;
480
- default:
481
- return null;
482
- }
393
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.server.ts`), serverContent);
394
+ console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.server.ts (Server-Side Helper)`));
395
+ const newExports = [
396
+ `
397
+ // Generated API helpers`,
398
+ `export * from './${camelCaseModuleName}.endpoints';`,
399
+ `export * from './use${moduleBaseName}';`,
400
+ `export * from './${moduleBaseName}.provider';`,
401
+ // <-- Note: no .tsx in export
402
+ `export * from './${camelCaseModuleName}.server';`
403
+ ].join("\n");
404
+ import_fs.default.appendFileSync(indexFilePath, newExports);
405
+ console.log(import_chalk.default.gray(` \u2713 index.ts (Updated with helpers)`));
483
406
  }
484
407
  async function runGenerator(options) {
485
408
  console.log(import_chalk.default.cyan.bold("\u{1F680} Starting API Development Platform Generator (Sapphire Edition)..."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-core-lib",
3
- "version": "12.0.84",
3
+ "version": "12.0.86",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {