api-core-lib 12.0.83 → 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 +141 -221
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -75,83 +75,22 @@ 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()";
78
+ // src/generator/utils/index.ts
79
+ var getActionName = (opId) => {
80
+ const cleanOpId = opId.replace(/_v\d+$/, "");
81
+ const parts = cleanOpId.split("_");
82
+ if (parts.length > 1) {
83
+ const actionPart = parts.slice(1).join("_");
84
+ return toCamelCase(actionPart);
140
85
  }
141
- return zodChain;
142
- }
143
-
144
- // src/generator/index.ts
145
- var DEBUG_MODE = process.env.DEBUG === "true";
146
- var debugLog = (title, data) => DEBUG_MODE && console.log(import_chalk.default.yellow(`
147
- [DEBUG: ${title}]`), import_util.default.inspect(data, { depth: 5, colors: true }));
148
- var toPascalCase = (str) => str.replace(/[^a-zA-Z0-9_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (w) => w.toUpperCase()).replace(/\s+/g, "");
86
+ return toCamelCase(cleanOpId);
87
+ };
149
88
  var toCamelCase = (str) => {
150
89
  const s = toPascalCase(str);
151
90
  return s.charAt(0).toLowerCase() + s.slice(1);
152
91
  };
92
+ var toPascalCase = (str) => str.replace(/[^a-zA-Z0-9_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (w) => w.toUpperCase()).replace(/\s+/g, "");
153
93
  var sanitizeForModuleName = (tagName) => `${toPascalCase(tagName.replace(/[^a-zA-Z0-9]/g, " "))}Api`;
154
- var getActionName = (opId) => toCamelCase(opId.replace(/^(central|tenant)/i, "").replace(/_v\d+$/, ""));
155
94
  var findCommonPath = (paths) => {
156
95
  if (!paths || paths.length === 0) return "/";
157
96
  const sorted = [...paths].sort();
@@ -162,6 +101,11 @@ var findCommonPath = (paths) => {
162
101
  const prefix = first.substring(0, i);
163
102
  return prefix.substring(0, prefix.lastIndexOf("/") + 1) || "/";
164
103
  };
104
+
105
+ // src/generator/index.ts
106
+ var DEBUG_MODE = process.env.DEBUG === "true";
107
+ var debugLog = (title, data) => DEBUG_MODE && console.log(import_chalk.default.yellow(`
108
+ [DEBUG: ${title}]`), import_util.default.inspect(data, { depth: 5, colors: true }));
165
109
  function parseSchema(name, schema, allEnums) {
166
110
  const properties = [];
167
111
  const enums = {};
@@ -284,7 +228,20 @@ function parseSpecToModules(spec) {
284
228
  }
285
229
  });
286
230
  const actionName = getActionName(endpoint.operationId);
287
- 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
+ };
288
245
  }
289
246
  }
290
247
  modules.forEach((mod, name) => {
@@ -299,177 +256,140 @@ async function generateModuleFiles(module2, allSchemas, allEnums, outputDir) {
299
256
  if (!import_fs.default.existsSync(moduleOutputPath)) import_fs.default.mkdirSync(moduleOutputPath, { recursive: true });
300
257
  console.log(import_chalk.default.cyan(`
301
258
  Generating module: ${import_chalk.default.bold(module2.moduleName)}`));
302
- const BUILT_IN_TYPES = /* @__PURE__ */ new Set([
303
- "string",
304
- "number",
305
- "boolean",
306
- "void",
307
- "undefined",
308
- "unknown",
309
- "any",
310
- "Date",
311
- "Promise",
312
- "QueryOptions",
313
- "Record<string, any>",
314
- "Record<string, unknown>"
315
- ]);
316
- const allCustomSchemaNames = [...module2.schemas].sort();
317
- const schemasToImport = allCustomSchemaNames.filter((name) => !BUILT_IN_TYPES.has(name));
318
- const enumsToImport = [...module2.enums].sort();
319
- const indexContent = [`// This file is auto-generated. Do not edit directly.
320
-
321
- export * from './config';`];
322
- 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.
323
267
 
324
- import type { ApiModuleConfig, ActionConfigModule } from 'api-core-lib';
325
268
  `;
326
- if (schemasToImport.length > 0) {
327
- configContent += `import type { ${schemasToImport.join(", ")} } from './types';
269
+ endpointsContent += `import type { QueryOptions } from 'api-core-lib';
328
270
  `;
329
- }
330
- const actionsType = Object.values(module2.actions).map((a) => ` ${a.name}: ActionConfigModule<${a.inputType}, ${a.outputType}>;`).join("\n");
331
- const actionsValue = Object.values(module2.actions).map((a) => {
332
- const { inputType, outputType, name, ...c } = a;
333
- return ` ${name}: ${JSON.stringify(c, null, 2).replace(/\n/g, "\n ")}`;
334
- }).join(",\n");
335
- configContent += `
336
- export const ${module2.moduleName}Module: ApiModuleConfig<{
337
- ${actionsType}
338
- }> = {
339
- baseEndpoint: '${module2.baseEndpoint}',
340
- actions: {
341
- ${actionsValue}
342
- },
343
- };
344
- `;
345
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "config.ts"), configContent.trim());
346
- console.log(import_chalk.default.gray(` \u2713 config.ts`));
347
- if (allCustomSchemaNames.length > 0) {
348
- if (enumsToImport.length > 0) {
349
- 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';
350
279
 
351
280
  `;
352
- for (const enumName of enumsToImport) {
353
- const enumDef = allEnums.get(enumName);
354
- if (enumDef) {
355
- enumsContent += `export const ${enumName} = ${JSON.stringify(enumDef.values)} as const;
356
- `;
357
- enumsContent += `export type ${enumName} = typeof ${enumName}[number];
358
-
281
+ }
282
+ endpointsContent += `export const ${camelCaseModuleName}Endpoints = {
359
283
  `;
360
- }
361
- }
362
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "enums.ts"), enumsContent);
363
- console.log(import_chalk.default.gray(` \u2713 enums.ts`));
364
- 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("; ")} }`);
365
288
  }
366
- let typesContent = `// This file is auto-generated. Do not edit directly.
367
-
368
- `;
369
- if (enumsToImport.length > 0) typesContent += `import type { ${enumsToImport.join(", ")} } from './enums';
370
-
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
+ */
371
299
  `;
372
- for (const typeName of schemasToImport) {
373
- const parsedSchema = allSchemas.get(typeName);
374
- if (parsedSchema) {
375
- if (parsedSchema.description) typesContent += `/** ${parsedSchema.description} */
300
+ endpointsContent += ` ${action.name}: (${params.join(", ")}) => ({
376
301
  `;
377
- typesContent += `export interface ${typeName} {
302
+ endpointsContent += ` action: '${action.name}' as const,
378
303
  `;
379
- for (const prop of parsedSchema.properties) {
380
- if (prop.description) typesContent += ` /** ${prop.description} */
304
+ if (action.pathParams.length > 0) endpointsContent += ` pathParams: params,
381
305
  `;
382
- let propType = prop.enumName || (prop.items ? `${prop.items.type || "any"}[]` : prop.type);
383
- if (propType === "integer") propType = "number";
384
- if (propType === "object") propType = "Record<string, any>";
385
- typesContent += ` ${prop.name}${prop.isRequired ? "" : "?"}: ${propType};
306
+ if (action.inputType !== "undefined" && action.inputType !== "QueryOptions") endpointsContent += ` input: body,
386
307
  `;
387
- }
388
- typesContent += `}
389
-
308
+ if (action.hasQuery) endpointsContent += ` input: query,
390
309
  `;
391
- }
392
- }
393
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "types.ts"), typesContent);
394
- console.log(import_chalk.default.gray(` \u2713 types.ts`));
395
- indexContent.push(`export * from './types';`);
396
- let validationContent = `// This file is auto-generated. Do not edit directly.
310
+ endpointsContent += ` }),
397
311
 
398
- import { z } from 'zod';
399
- `;
400
- if (enumsToImport.length > 0) {
401
- validationContent += `import { ${enumsToImport.join(", ")} } from './enums';
402
312
  `;
403
- }
404
- for (const typeName of schemasToImport) {
405
- const parsedSchema = allSchemas.get(typeName);
406
- if (parsedSchema) {
407
- const zodShape = parsedSchema.properties.map((p) => ` ${p.name}: ${_propToZod(p)}`).join(",\n");
408
- validationContent += `
409
- /** Zod schema for {@link ${typeName}}. */
313
+ }
314
+ endpointsContent += `};
410
315
  `;
411
- validationContent += `export const ${typeName}Schema = z.object({
412
- ${zodShape}
413
- });
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.
414
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
+ };
415
339
  `;
416
- validationContent += `export type ${typeName}Validated = z.infer<typeof ${typeName}Schema>;
417
- `;
418
- }
419
- }
420
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "validation.ts"), validationContent);
421
- console.log(import_chalk.default.gray(` \u2713 validation.ts`));
422
- indexContent.push(`export * from './validation';`);
423
- let mocksContent = `// This file is auto-generated. Do not edit directly.
424
- import type { ${schemasToImport.join(", ")} } from './types';
425
- `;
426
- 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}';
348
+
349
+ type Options = Parameters<typeof use${moduleBaseName}Api>[0];
350
+
351
+ interface ${moduleBaseName}ProviderProps {
352
+ children: React.ReactNode;
353
+ options?: Options;
354
+ }
427
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
+ }
428
363
  `;
429
- for (const typeName of schemasToImport) {
430
- const parsedSchema = allSchemas.get(typeName);
431
- if (parsedSchema) {
432
- let mockObject = {};
433
- parsedSchema.properties.forEach((p) => {
434
- mockObject[p.name] = _propToMock(p);
435
- });
436
- 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.
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';
437
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
+ };
438
378
  `;
439
- }
440
- }
441
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "mocks.ts"), mocksContent);
442
- console.log(import_chalk.default.gray(` \u2713 mocks.ts`));
443
- indexContent.push(`export * from './mocks';`);
444
- }
445
- import_fs.default.writeFileSync(import_path.default.join(moduleOutputPath, "index.ts"), indexContent.join("\n"));
446
- console.log(import_chalk.default.gray(` \u2713 index.ts`));
447
- }
448
- function _propToMock(prop) {
449
- if (prop.example) return prop.example;
450
- if (prop.name.match(/image|avatar|logo|url/i)) return "https://via.placeholder.com/150";
451
- if (prop.enum) return prop.enum[0];
452
- switch (prop.type) {
453
- case "string":
454
- if (prop.format === "email") return "test@example.com";
455
- if (prop.format === "uuid") return "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
456
- return `Mock ${toPascalCase(prop.name)}`;
457
- case "integer":
458
- case "number":
459
- return 1;
460
- case "boolean":
461
- return true;
462
- case "array":
463
- return prop.items ? [_propToMock(prop.items)] : [];
464
- case "object":
465
- const mock = {};
466
- if (prop.properties) prop.properties.forEach((p) => {
467
- mock[p.name] = _propToMock(p);
468
- });
469
- return mock;
470
- default:
471
- return null;
472
- }
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)`));
473
393
  }
474
394
  async function runGenerator(options) {
475
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.83",
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": {