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.
- package/dist/cli.cjs +137 -214
- 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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
338
|
-
|
|
263
|
+
if (enumsToImport.length > 0) indexExports.push("enums");
|
|
264
|
+
indexExports.push("types", "validation", "mocks");
|
|
339
265
|
}
|
|
340
|
-
const
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
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
|
-
|
|
383
|
-
|
|
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
|
-
|
|
390
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
422
|
-
${zodShape}
|
|
423
|
-
});
|
|
323
|
+
endpointsContent += ` }),
|
|
424
324
|
|
|
425
325
|
`;
|
|
426
|
-
|
|
326
|
+
}
|
|
327
|
+
endpointsContent += `};
|
|
427
328
|
`;
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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)..."));
|