api-core-lib 12.0.84 → 12.0.85

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 +126 -216
  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,140 @@ 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));
328
- 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.
259
+ const moduleBaseName = module2.moduleName.replace(/Api$/, "");
260
+ const camelCaseModuleName = toCamelCase(moduleBaseName);
261
+ const allPathParams = /* @__PURE__ */ new Set();
262
+ Object.values(module2.actions).forEach((action) => {
263
+ action.pathParams.forEach((param) => allPathParams.add(param));
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.
333
267
 
334
- import type { ApiModuleConfig, ActionConfigModule } from 'api-core-lib';
335
268
  `;
336
- if (schemasToImport.length > 0) {
337
- configContent += `import type { ${schemasToImport.join(", ")} } from './types';
269
+ endpointsContent += `import type { QueryOptions } from 'api-core-lib';
338
270
  `;
339
- }
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.
271
+ const endpointTypesToImport = /* @__PURE__ */ new Set();
272
+ Object.values(module2.actions).forEach((action) => {
273
+ if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") {
274
+ endpointTypesToImport.add(action.inputType);
275
+ }
276
+ });
277
+ if (endpointTypesToImport.size > 0) {
278
+ endpointsContent += `import type { ${[...endpointTypesToImport].join(", ")} } from './types';
360
279
 
361
280
  `;
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];
368
-
281
+ }
282
+ endpointsContent += `export const ${camelCaseModuleName}Endpoints = {
369
283
  `;
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';`);
284
+ for (const action of Object.values(module2.actions)) {
285
+ const params = [];
286
+ if (action.pathParams.length > 0) {
287
+ params.push(`params: { ${action.pathParams.map((p) => `${p}: string | number`).join("; ")} }`);
375
288
  }
376
- let typesContent = `// This file is auto-generated. Do not edit directly.
377
-
289
+ if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") {
290
+ params.push(`body: ${action.inputType}`);
291
+ }
292
+ if (action.hasQuery) {
293
+ params.push(`query?: QueryOptions`);
294
+ }
295
+ endpointsContent += ` /**
296
+ * ${action.method} ${module2.baseEndpoint}${action.path}
297
+ * ${action.description}
298
+ */
378
299
  `;
379
- if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
380
-
300
+ endpointsContent += ` ${action.name}: (${params.join(", ")}) => ({
381
301
  `;
382
- for (const typeName of schemasToImport) {
383
- const parsedSchema = allSchemas.get(typeName);
384
- if (parsedSchema) {
385
- if (parsedSchema.description) typesContent += `/** ${parsedSchema.description} */
302
+ endpointsContent += ` action: '${action.name}' as const,
386
303
  `;
387
- typesContent += `export interface ${typeName} {
304
+ if (action.pathParams.length > 0) endpointsContent += ` pathParams: params,
388
305
  `;
389
- for (const prop of parsedSchema.properties) {
390
- if (prop.description) typesContent += ` /** ${prop.description} */
306
+ if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") endpointsContent += ` input: body,
391
307
  `;
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};
308
+ if (action.hasQuery) endpointsContent += ` input: query,
396
309
  `;
397
- }
398
- typesContent += `}
399
-
400
- `;
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.
310
+ endpointsContent += ` }),
407
311
 
408
- import { z } from 'zod';
409
312
  `;
410
- if (enumsToImport.length > 0) {
411
- validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
412
- `;
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}}. */
313
+ }
314
+ endpointsContent += `};
420
315
  `;
421
- validationContent += `export const ${typeName}Schema = z.object({
422
- ${zodShape}
423
- });
316
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.endpoints.ts`), endpointsContent);
317
+ console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.endpoints.ts (Type-Safe Endpoints)`));
318
+ const hookContent = `// This file is auto-generated. Do not edit directly.
424
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
+ };
331
+
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
+ };
425
339
  `;
426
- validationContent += `export type ${typeName}Validated = z.infer<typeof ${typeName}Schema>;
427
- `;
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';
435
- `;
436
- if (enumsToImport.length > 0) mocksContent += `import { ${enumsToImport.join(", ")} } from './enums';
340
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `use${moduleBaseName}.ts`), hookContent);
341
+ console.log(import_chalk.default.gray(` \u2713 use${moduleBaseName}.ts (Custom Hook)`));
342
+ const providerContent = `// This file is auto-generated. Do not edit directly.
343
+ 'use client';
344
+
345
+ import React from 'react';
346
+ import { ApiModuleProvider } from 'api-core-lib/client';
347
+ import { use${moduleBaseName}Api } from './use${moduleBaseName}';
437
348
 
349
+ type Options = Parameters<typeof use${moduleBaseName}Api>[0];
350
+
351
+ interface ${moduleBaseName}ProviderProps {
352
+ children: React.ReactNode;
353
+ options?: Options;
354
+ }
355
+
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
+ }
438
363
  `;
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)};
364
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${moduleBaseName}.provider.tsx`), providerContent);
365
+ console.log(import_chalk.default.gray(` \u2713 ${moduleBaseName}.provider.tsx (React Provider)`));
366
+ const serverContent = `// This file is auto-generated. For server-side use only.
447
367
 
368
+ import { createServerApi } from 'api-core-lib/server';
369
+ import { serverApiClient } from '@/lib/api-core/serverApi'; // Assuming a fixed path
370
+ import { ${module2.moduleName} } from './config';
371
+
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
+ };
448
378
  `;
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
- }
379
+ import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, `${camelCaseModuleName}.server.ts`), serverContent);
380
+ console.log(import_chalk.default.gray(` \u2713 ${camelCaseModuleName}.server.ts (Server-Side Helper)`));
381
+ const indexFilePath = import_path.default.join(moduleOutputPath, "index.ts");
382
+ let indexFileContent = import_fs.default.readFileSync(indexFilePath, "utf-8");
383
+ indexFileContent += `
384
+ export * from './${camelCaseModuleName}.endpoints';`;
385
+ indexFileContent += `
386
+ export * from './use${moduleBaseName}';`;
387
+ indexFileContent += `
388
+ export * from './${moduleBaseName}.provider';`;
389
+ indexFileContent += `
390
+ export * from './${camelCaseModuleName}.server';`;
391
+ import_fs.default.writeFileSync(indexFilePath, indexFileContent);
392
+ console.log(import_chalk.default.gray(` \u2713 index.ts (Updated with new exports)`));
483
393
  }
484
394
  async function runGenerator(options) {
485
395
  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.85",
4
4
  "description": "A flexible and powerful API client library for modern web applications.",
5
5
  "type": "module",
6
6
  "exports": {