nestcraftx 0.1.0

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/utils/utils.js ADDED
@@ -0,0 +1,723 @@
1
+ import { logInfo } from "./loggers/logInfo.js";
2
+ import { logSuccess } from "./loggers/logSuccess.js";
3
+ import { createDirectory, createFile, updateFile } from "./userInput.js";
4
+
5
+ export async function generateEntityFileContent(entity) {
6
+ // console.log("Entity name:", entity.name); // Log de l'entité
7
+
8
+ if (!entity || !entity.name) {
9
+ throw new Error("Nom de l'entité manquant !");
10
+ }
11
+ const entityName = capitalize(entity.name);
12
+ const className = `${entityName}Entity`;
13
+
14
+ const defaultFields = [
15
+ {
16
+ name: "id",
17
+ type: "string",
18
+ comment:
19
+ "L'identifiant unique de l'entité.\n * Utilisé pour retrouver de manière unique un enregistrement dans la base de données.\n *\n * Exemple : '123e4567-e89b-12d3-a456-426614174000'",
20
+ },
21
+ {
22
+ name: "createdAt",
23
+ type: "Date",
24
+ comment:
25
+ "La date de création de l'entité.\n * Définie lors de la création et ne change pas.\n *\n * Exemple : new Date('2022-01-01T10:00:00Z')",
26
+ },
27
+ {
28
+ name: "updatedAt",
29
+ type: "Date",
30
+ comment:
31
+ "La date de dernière mise à jour de l'entité.\n * Mise à jour à chaque modification.\n *\n * Exemple : new Date('2022-02-01T15:00:00Z')",
32
+ },
33
+ ];
34
+
35
+ const isUserEntityWithRole =
36
+ entity.name.toLowerCase() === "user" &&
37
+ entity.fields.some((f) => f.name === "role");
38
+
39
+ const allFields = [...defaultFields, ...entity.fields];
40
+
41
+ const constructorParams = allFields
42
+ .map(
43
+ (f) => `
44
+ /**
45
+ * ${f.comment || `Champ ${f.name}`}
46
+ */
47
+ private readonly ${f.name}: ${getFormattedType(f)}
48
+ ,`
49
+ )
50
+ .join("");
51
+
52
+ const constructorAssignments = allFields
53
+ .map((f) => ` this.${f.name} = ${f.name};`)
54
+ .join("\n");
55
+
56
+ const getters = allFields
57
+ .map(
58
+ (f) => `
59
+ get${capitalize(f.name)}(): ${getFormattedType(f)}
60
+ {
61
+ return this.${f.name};
62
+ }`
63
+ )
64
+ .join("\n");
65
+
66
+ const jsonFields = allFields
67
+ .map((f) => ` ${f.name}: this.${f.name},`)
68
+ .join("\n");
69
+
70
+ let importStatements = "";
71
+ if (isUserEntityWithRole) {
72
+ importStatements += `import { Role } from 'src/modules/user/domain/enums/role.enum';\n\n`;
73
+ }
74
+
75
+ return `${importStatements}/**
76
+ * ${className} représente l'entité principale de ${entityName} dans le domaine.
77
+ * Elle contient les propriétés de base nécessaires à la gestion des données liées à ${entityName}.
78
+ */
79
+ export class ${className} {
80
+ constructor(${constructorParams}
81
+ ) {
82
+ ${constructorAssignments}
83
+ }
84
+ ${getters}
85
+
86
+ toJSON() {
87
+ return {
88
+ ${jsonFields}
89
+ };
90
+ }
91
+ }
92
+ `;
93
+ }
94
+
95
+ export async function generateMapper(entity) {
96
+ const entityName = capitalize(entity.name);
97
+
98
+ // verification de l'existance de user entity
99
+ const isUserWithRole =
100
+ entity.name.toLowerCase() === "user" &&
101
+ entity.fields.some((f) => f.name.toLowerCase() === "role");
102
+
103
+ const domainArgs = ["data.id"]
104
+ .concat(["data.createdAt", "data.updatedAt"])
105
+ .concat(entity.fields.map((f) => `data.${f.name}`))
106
+ .join(",\n ");
107
+
108
+ const toPersistenceFields = entity.fields
109
+ .map((f) => `${f.name}: dto.${f.name},`)
110
+ .join("\n ");
111
+
112
+ const toUpdateFields = entity.fields
113
+ .map(
114
+ (f) => `if (dto.${f.name} !== undefined) data.${f.name} = dto.${f.name};`
115
+ )
116
+ .join("\n ");
117
+
118
+ return `
119
+ import { Injectable } from '@nestjs/common';
120
+ import { ${entityName}Entity } from 'src/${decapitalize(
121
+ entity.name
122
+ )}/domain/entities/${decapitalize(entity.name)}.entity';
123
+ import { Create${entityName}Dto, Update${entityName}Dto } from 'src/${
124
+ entity.name
125
+ }/application/dtos/${decapitalize(entity.name)}.dto';
126
+
127
+ ${
128
+ isUserWithRole
129
+ ? "import { Role } from 'src/modules/user/domain/enums/role.enum';"
130
+ : ""
131
+ }
132
+
133
+
134
+ @Injectable()
135
+ export class ${entityName}Mapper {
136
+ toDomain(data: any): ${entityName}Entity {
137
+ return new ${entityName}Entity(
138
+ ${domainArgs}
139
+ );
140
+ }
141
+
142
+ toPersistence(dto: Create${entityName}Dto): any {
143
+ return {
144
+ ${toPersistenceFields}
145
+ };
146
+ }
147
+
148
+ toUpdatePersistence(dto: Update${entityName}Dto): any {
149
+ const data: any = {};
150
+ ${toUpdateFields}
151
+ return data;
152
+ }
153
+ }
154
+ `;
155
+ }
156
+
157
+ export async function generateDto(entity) {
158
+ const entityName = capitalize(entity.name);
159
+
160
+ const getExampleForField = (f) => {
161
+ // Dynamique et plus réaliste, basé sur le type de champ et des exemples courants
162
+ switch (f.type) {
163
+ case "string":
164
+ return `"${f.name}_example"`; // Exemple générique basé sur le nom du champ
165
+ case "number":
166
+ return f.name === "price" ? 199.99 : 123; // Exemple spécifique pour 'price'
167
+ case "boolean":
168
+ return true; // Exemple générique pour les booléens
169
+ case "Date":
170
+ case "date":
171
+ return `"2024-01-01T00:00:00Z"`; // Exemple générique pour les dates
172
+ case "role":
173
+ return `"user"`; // Exemple pour les rôles, par défaut 'user'
174
+ case "discount":
175
+ return 10; // Exemple pour une valeur de type discount
176
+ case "account":
177
+ return `"acc_${f.name}_12345"`; // Exemple pour un compte
178
+ case "amount":
179
+ return 500.75; // Exemple pour un montant
180
+ default:
181
+ return `"sample_${f.name}"`; // Fallback générique pour les autres types
182
+ }
183
+ };
184
+
185
+ const generateFieldLine = (f) => {
186
+ const typeDecorator =
187
+ {
188
+ string: "IsString",
189
+ number: "IsInt",
190
+ boolean: "IsBoolean",
191
+ date: "IsDate",
192
+ }[f.type] || "IsString";
193
+
194
+ const apiPropertyDecorator = f.optional
195
+ ? `@ApiPropertyOptional({ example: ${getExampleForField(f)}, type: '${
196
+ f.type
197
+ }' })`
198
+ : `@ApiProperty({ example: ${getExampleForField(f)}, type: '${
199
+ f.type
200
+ }' })`;
201
+
202
+ return `${apiPropertyDecorator}
203
+ @${typeDecorator}()
204
+ ${f.name}${f.optional ? "?" : ""}: ${f.type};`;
205
+ };
206
+
207
+ const dtoFields = entity.fields
208
+ .map((f) => generateFieldLine({ ...f, optional: false }))
209
+ .join("\n\n");
210
+
211
+ const updateDtoFields = entity.fields
212
+ .map((f) => generateFieldLine({ ...f, optional: true }))
213
+ .join("\n\n");
214
+
215
+ return `
216
+ import { IsOptional, IsString, IsInt, IsBoolean, IsDate } from 'class-validator';
217
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
218
+
219
+ // Vous pouvez décommenter ce champ si vous voulez gérer le rôle de l'utilisateur.
220
+ // import { Role } from 'src/modules/user/domain/enums/role.enum';
221
+
222
+ export class Create${entityName}Dto {
223
+ ${dtoFields}
224
+
225
+ // Décommentez et ajustez le rôle si nécessaire.
226
+ // @ApiProperty({ example: 'admin', type: 'string' })
227
+ // @IsString()
228
+ // role: Role;
229
+ }
230
+
231
+ export class Update${entityName}Dto {
232
+ ${updateDtoFields}
233
+
234
+ // Décommentez et ajustez le rôle si nécessaire.
235
+ // @ApiPropertyOptional({ example: 'admin', type: 'string' })
236
+ // @IsString()
237
+ // role?: Role;
238
+ }
239
+ `;
240
+ }
241
+
242
+ export async function generateMiddlewares() {
243
+ logInfo(
244
+ "\u2728 G\u00e9n\u00e9ration des middlewares, interceptors, guards et filters personnalis\u00e9s..."
245
+ );
246
+
247
+ const basePath = "src/common";
248
+ await createDirectory(`${basePath}/middlewares`);
249
+ await createDirectory(`${basePath}/interceptors`);
250
+ await createDirectory(`${basePath}/filters`);
251
+ await createDirectory(`${basePath}/decorators`);
252
+
253
+ // Logger Middleware
254
+ await createFile({
255
+ path: `${basePath}/middlewares/logger.middleware.ts`,
256
+ contente: `import { Request, Response, NextFunction } from 'express';
257
+
258
+ export function LoggerMiddleware(
259
+ req: Request,
260
+ res: Response,
261
+ next: NextFunction,
262
+ ) {
263
+ console.log(\`[Request] \${req.method} \${req.originalUrl} - \${res.statusCode}\`);
264
+ next();
265
+ }
266
+ `,
267
+ });
268
+
269
+ // Error Handling Filter
270
+ await createFile({
271
+ path: `${basePath}/filters/all-exceptions.filter.ts`,
272
+ contente: `import {
273
+ ExceptionFilter,
274
+ Catch,
275
+ ArgumentsHost,
276
+ HttpException,
277
+ HttpStatus,
278
+ } from '@nestjs/common';
279
+ import { Request, Response } from 'express';
280
+
281
+ @Catch()
282
+ export class AllExceptionsFilter implements ExceptionFilter {
283
+ catch(exception: unknown, host: ArgumentsHost) {
284
+ const ctx = host.switchToHttp();
285
+ const response = ctx.getResponse<Response>();
286
+ const request = ctx.getRequest<Request>();
287
+ const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
288
+
289
+ const message = exception instanceof HttpException ? exception.getResponse() : 'Internal server error';
290
+ console.log('exception: ', exception);
291
+
292
+ response.status(status).json({
293
+ statusCode: status,
294
+ timestamp: new Date().toISOString(),
295
+ path: request.url,
296
+ message: message,
297
+ });
298
+ }
299
+ }`,
300
+ });
301
+
302
+ // Response Interceptor
303
+ await createFile({
304
+ path: `${basePath}/interceptors/response.interceptor.ts`,
305
+ contente: `import {
306
+ CallHandler,
307
+ ExecutionContext,
308
+ Injectable,
309
+ NestInterceptor,
310
+ } from '@nestjs/common';
311
+ import { Observable } from 'rxjs';
312
+ import { map } from 'rxjs/operators';
313
+
314
+ @Injectable()
315
+ export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
316
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
317
+ const httpContext = context.switchToHttp();
318
+ const response = httpContext.getResponse();
319
+ const request = httpContext.getRequest();
320
+
321
+ return next.handle().pipe(
322
+ map((data) => {
323
+ const message =
324
+ response.locals.message || this.getDefaultMessage(request.method);
325
+
326
+ return {
327
+ statusCode: response.statusCode,
328
+ message,
329
+ path: request.url,
330
+ method: request.method,
331
+ timestamp: new Date().toISOString(),
332
+ data: data ?? null,
333
+ };
334
+ }),
335
+ );
336
+ }
337
+
338
+ private getDefaultMessage(method: string): string {
339
+ switch (method.toUpperCase()) {
340
+ case 'POST':
341
+ return 'Création réussie';
342
+ case 'PUT':
343
+ case 'PATCH':
344
+ return 'Mise à jour réussie';
345
+ case 'DELETE':
346
+ return 'Suppression réussie';
347
+ default:
348
+ return 'Requête traitée avec succès';
349
+ }
350
+ }
351
+ }
352
+ `,
353
+ });
354
+
355
+ // 📌 public Decorator
356
+ await createFile({
357
+ path: `${basePath}/decorators/public.decorator.ts`,
358
+ contente: `import { SetMetadata } from '@nestjs/common';
359
+
360
+ export const IS_PUBLIC_KEY = 'isPublic';
361
+ export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
362
+ `,
363
+ });
364
+
365
+ // 📌 Auth role Decorator
366
+ await createFile({
367
+ path: `${basePath}/decorators/role.decorator.ts`,
368
+ contente: `import { SetMetadata } from '@nestjs/common';
369
+
370
+ export const ROLES_KEY = 'roles';
371
+ export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
372
+ `,
373
+ });
374
+
375
+ // Modification de main.ts pour intégrer les middlewares
376
+ // Chemin vers main.ts
377
+ const mainTsPath = "src/main.ts";
378
+
379
+ // ✅ 1. Mise à jour des imports
380
+ const importPattern = "import { AppModule } from './app.module';";
381
+ const importReplacer = `import { AppModule } from 'src/app.module';
382
+ import { AllExceptionsFilter } from 'src/common/filters/all-exceptions.filter';
383
+ import { ResponseInterceptor } from 'src/common/interceptors/response.interceptor';
384
+ import { LoggerMiddleware } from 'src/common/middlewares/logger.middleware';
385
+ import { ValidationPipe } from '@nestjs/common';`;
386
+
387
+ // ✅ 2. Injection dans le contenu de bootstrap()
388
+ const contentPattern = `const app = await NestFactory.create(AppModule);`;
389
+ const contentReplacer = `const app = await NestFactory.create(AppModule);
390
+
391
+ // 🔒 Global filter pour gérer toutes les exceptions
392
+ app.useGlobalFilters(new AllExceptionsFilter());
393
+
394
+ // 🔁 Global interceptor pour structurer les réponses
395
+ // app.useGlobalInterceptors(new ResponseInterceptor()); //deja appliquer dans le app.module.ts par convention (ne choisir que l'un des deux)
396
+
397
+ // 📋 Middleware pour logger toutes les requêtes entrantes
398
+ app.use(LoggerMiddleware);`;
399
+
400
+ // ✅ Appels
401
+ await updateFile({
402
+ path: mainTsPath,
403
+ pattern: importPattern,
404
+ replacement: importReplacer,
405
+ });
406
+
407
+ await updateFile({
408
+ path: mainTsPath,
409
+ pattern: contentPattern,
410
+ replacement: contentReplacer,
411
+ });
412
+
413
+ // modification de AppModule
414
+ const appModulePath = "src/app.module.ts";
415
+
416
+ const addNestModuleInterface = `providers: [`;
417
+ const replaceWithNestModule = `providers: [
418
+ {
419
+ provide: APP_INTERCEPTOR,
420
+ useClass: ResponseInterceptor, // 🔁 Global interceptor pour structurer les réponses
421
+ },`;
422
+
423
+ await updateFile({
424
+ path: appModulePath,
425
+ pattern: addNestModuleInterface,
426
+ replacement: replaceWithNestModule,
427
+ });
428
+
429
+ logSuccess(
430
+ "\u2705 Middlewares, filters, interceptors et guards g\u00e9n\u00e9r\u00e9s avec succ\u00e8s !"
431
+ );
432
+ }
433
+
434
+ export async function generateRepository(entityName, orm) {
435
+ const entityNameCapitalized = capitalize(entityName);
436
+ const entityNameLower = entityName.toLowerCase();
437
+ const entityPath = `src/${entityNameLower}`;
438
+
439
+ console.log(`entity name: ${entityNameLower} \n omr selected: ${orm}`);
440
+
441
+ // Gère le switch en fonction de l'ORM choisi
442
+ switch (orm) {
443
+ case "typeorm":
444
+ // Implémentation du repository pour TypeORM
445
+ await createFile({
446
+ path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
447
+ contente: `import { Injectable, NotFoundException } from '@nestjs/common';
448
+ import { Repository } from 'typeorm';
449
+ import { InjectRepository } from '@nestjs/typeorm';
450
+ import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
451
+ import { ${entityNameCapitalized} } from 'src/entities/${entityNameCapitalized}.entity';
452
+ import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
453
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
454
+ import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
455
+
456
+ @Injectable()
457
+ export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
458
+ constructor(
459
+ @InjectRepository(${entityNameCapitalized})
460
+ private readonly repository: Repository<${entityNameCapitalized}>,
461
+ private readonly mapper: ${entityNameCapitalized}Mapper,
462
+ ) {}
463
+
464
+ // create
465
+ async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
466
+ const toPersist = this.mapper.toPersistence(data);
467
+ const created = await this.repository.save(toPersist);
468
+ return this.mapper.toDomain(created);
469
+ }
470
+
471
+ // find by id
472
+ async findById(id: string): Promise<${entityNameCapitalized}Entity> {
473
+ const record = await this.repository.findOne({
474
+ where: { id },
475
+ });
476
+
477
+ if (!record) {
478
+ throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
479
+ }
480
+
481
+ return this.mapper.toDomain(record);
482
+ }
483
+
484
+ // update
485
+ async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
486
+ const toUpdate = this.mapper.toUpdatePersistence(data);
487
+ const updated = await this.repository.save({ ...toUpdate, id });
488
+ return this.mapper.toDomain(updated);
489
+ }
490
+
491
+ // find all
492
+ async findAll(): Promise<${entityNameCapitalized}Entity[]> {
493
+ const records = await this.repository.find();
494
+ return records.map(record => this.mapper.toDomain(record));
495
+ }
496
+
497
+ // delete
498
+ async delete(id: string): Promise<void> {
499
+ await this.repository.delete(id);
500
+ }
501
+ }
502
+ `,
503
+ });
504
+ break;
505
+
506
+ case "prisma":
507
+ // Implémentation pour Prisma
508
+ await createFile({
509
+ path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
510
+ contente: `import { Injectable, NotFoundException } from '@nestjs/common';
511
+ import { PrismaService } from 'src/prisma/prisma.service';
512
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
513
+ import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
514
+ import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
515
+ import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
516
+
517
+ @Injectable()
518
+ export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
519
+ constructor(
520
+ private readonly prisma: PrismaService,
521
+ private readonly mapper: ${entityNameCapitalized}Mapper,
522
+ ) {}
523
+
524
+ // create
525
+ async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
526
+ const toPersist = this.mapper.toPersistence(data);
527
+ const created = await this.prisma.${entityNameLower}.create({ data: toPersist });
528
+ return this.mapper.toDomain(created);
529
+ }
530
+
531
+ // find by id
532
+ async findById(id: string): Promise<${entityNameCapitalized}Entity> {
533
+ const record = await this.prisma.${entityNameLower}.findUnique({
534
+ where: { id },
535
+ });
536
+
537
+ if (!record) {
538
+ throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
539
+ }
540
+
541
+ return this.mapper.toDomain(record);
542
+ }
543
+
544
+ // update
545
+ async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
546
+ const toUpdate = this.mapper.toUpdatePersistence(data);
547
+ const updated = await this.prisma.${entityNameLower}.update({
548
+ where: { id },
549
+ data: toUpdate,
550
+ });
551
+
552
+ return this.mapper.toDomain(updated);
553
+ }
554
+
555
+ // find all
556
+ async findAll(): Promise<${entityNameCapitalized}Entity[]> {
557
+ const records = await this.prisma.${entityNameLower}.findMany();
558
+ return records.map(record => this.mapper.toDomain(record));
559
+ }
560
+
561
+ // delete
562
+ async delete(id: string): Promise<void> {
563
+ await this.prisma.${entityNameLower}.delete({
564
+ where: { id },
565
+ });
566
+ }
567
+ }
568
+ `,
569
+ });
570
+ break;
571
+
572
+ case "mongoose":
573
+ // Implémentation pour MongoDB avec Mongoose
574
+ await createFile({
575
+ path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
576
+ contente: `import { Injectable, NotFoundException } from '@nestjs/common';
577
+ import { InjectModel } from '@nestjs/mongoose';
578
+ import { Model } from 'mongoose';
579
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
580
+ import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
581
+ import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
582
+ import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
583
+
584
+ @Injectable()
585
+ export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
586
+ constructor(
587
+ @InjectModel(${entityNameCapitalized}Entity.name)
588
+ private readonly model: Model<${entityNameCapitalized}Entity>,
589
+ private readonly mapper: ${entityNameCapitalized}Mapper,
590
+ ) {}
591
+
592
+ // create
593
+ async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
594
+ const toPersist = this.mapper.toPersistence(data);
595
+ const created = await this.model.create(toPersist);
596
+ return this.mapper.toDomain(created);
597
+ }
598
+
599
+ // find by id
600
+ async findById(id: string): Promise<${entityNameCapitalized}Entity> {
601
+ const record = await this.model.findById(id);
602
+
603
+ if (!record) {
604
+ throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
605
+ }
606
+
607
+ return this.mapper.toDomain(record);
608
+ }
609
+
610
+ // update
611
+ async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
612
+ const toUpdate = this.mapper.toUpdatePersistence(data);
613
+ const updated = await this.model.findByIdAndUpdate(id, toUpdate, { new: true });
614
+ return this.mapper.toDomain(updated);
615
+ }
616
+
617
+ // find all
618
+ async findAll(): Promise<${entityNameCapitalized}Entity[]> {
619
+ const records = await this.model.find();
620
+ return records.map(record => this.mapper.toDomain(record));
621
+ }
622
+
623
+ // delete
624
+ async delete(id: string): Promise<void> {
625
+ await this.model.findByIdAndDelete(id);
626
+ }
627
+ }
628
+ `,
629
+ });
630
+ break;
631
+
632
+ case "sequelize":
633
+ // Implémentation pour Sequelize
634
+ await createFile({
635
+ path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
636
+ contente: `import { Injectable, NotFoundException } from '@nestjs/common';
637
+ import { InjectModel } from '@nestjs/sequelize';
638
+ import { Model } from 'sequelize-typescript';
639
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
640
+ import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
641
+ import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
642
+ import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
643
+
644
+ @Injectable()
645
+ export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
646
+ constructor(
647
+ @InjectModel(${entityNameCapitalized}Entity)
648
+ private readonly model: Model<${entityNameCapitalized}Entity>,
649
+ private readonly mapper: ${entityNameCapitalized}Mapper,
650
+ ) {}
651
+
652
+ // create
653
+ async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
654
+ const toPersist = this.mapper.toPersistence(data);
655
+ const created = await this.model.create(toPersist);
656
+ return this.mapper.toDomain(created);
657
+ }
658
+
659
+ // find by id
660
+ async findById(id: string): Promise<${entityNameCapitalized}Entity> {
661
+ const record = await this.model.findByPk(id);
662
+
663
+ if (!record) {
664
+ throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
665
+ }
666
+
667
+ return this.mapper.toDomain(record);
668
+ }
669
+
670
+ // update
671
+ async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
672
+ const toUpdate = this.mapper.toUpdatePersistence(data);
673
+ const updated = await this.model.update(toUpdate, { where: { id } });
674
+ return this.mapper.toDomain(updated);
675
+ }
676
+
677
+ // find all
678
+ async findAll(): Promise<${entityNameCapitalized}Entity[]> {
679
+ const records = await this.model.findAll();
680
+ return records.map(record => this.mapper.toDomain(record));
681
+ }
682
+
683
+ // delete
684
+ async delete(id: string): Promise<void> {
685
+ await this.model.destroy({ where: { id } });
686
+ }
687
+ }
688
+ `,
689
+ });
690
+ break;
691
+
692
+ default:
693
+ console.error("Unsupported ORM: " + orm);
694
+ break;
695
+ }
696
+ }
697
+
698
+ function capitalize(str) {
699
+ return str.charAt(0).toUpperCase() + str.slice(1);
700
+ }
701
+ function decapitalize(str) {
702
+ return str.charAt(0).toLowerCase() + str.slice(1);
703
+ }
704
+
705
+ function formatType(type) {
706
+ switch (type) {
707
+ case "number":
708
+ return "number";
709
+ case "boolean":
710
+ return "boolean";
711
+ case "Date":
712
+ return "Date";
713
+ default:
714
+ return "string";
715
+ }
716
+ }
717
+
718
+ function getFormattedType(field) {
719
+ if (field.name === "role" && field.type === "string") {
720
+ return "Role"; // 🔁 Remplacer le type string par Role
721
+ }
722
+ return formatType(field.type);
723
+ }