@forinda/kickjs-cli 0.5.0 → 0.6.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/dist/cli.js CHANGED
@@ -416,21 +416,12 @@ __name(pluralizePascal, "pluralizePascal");
416
416
 
417
417
  // src/generators/module.ts
418
418
  import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
419
- async function generateModule(options) {
420
- const { name, modulesDir, noEntity, noTests, repo = "inmemory", minimal } = options;
421
- const kebab = toKebabCase(name);
422
- const pascal = toPascalCase(name);
423
- const camel = toCamelCase(name);
424
- const plural = pluralize(kebab);
425
- const pluralPascal = pluralizePascal(pascal);
426
- const moduleDir = join2(modulesDir, plural);
427
- const files = [];
428
- const write = /* @__PURE__ */ __name(async (relativePath, content) => {
429
- const fullPath = join2(moduleDir, relativePath);
430
- await writeFileSafe(fullPath, content);
431
- files.push(fullPath);
432
- }, "write");
433
- await write("index.ts", `/**
419
+
420
+ // src/generators/templates/module-index.ts
421
+ function generateModuleIndex(pascal, kebab, plural, repo) {
422
+ const repoClass = repo === "inmemory" ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`;
423
+ const repoFile = repo === "inmemory" ? `in-memory-${kebab}` : `drizzle-${kebab}`;
424
+ return `/**
434
425
  * ${pascal} Module
435
426
  *
436
427
  * Self-contained feature module following Domain-Driven Design (DDD).
@@ -445,7 +436,7 @@ async function generateModule(options) {
445
436
  import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'
446
437
  import { buildRoutes } from '@forinda/kickjs-http'
447
438
  import { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'
448
- import { ${repo === "inmemory" ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`} } from './infrastructure/repositories/${repo === "inmemory" ? `in-memory-${kebab}` : `drizzle-${kebab}`}.repository'
439
+ import { ${repoClass} } from './infrastructure/repositories/${repoFile}.repository'
449
440
  import { ${pascal}Controller } from './presentation/${kebab}.controller'
450
441
 
451
442
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
@@ -462,7 +453,7 @@ export class ${pascal}Module implements AppModule {
462
453
  */
463
454
  register(container: Container): void {
464
455
  container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>
465
- container.resolve(${repo === "inmemory" ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`}),
456
+ container.resolve(${repoClass}),
466
457
  )
467
458
  }
468
459
 
@@ -479,24 +470,15 @@ export class ${pascal}Module implements AppModule {
479
470
  }
480
471
  }
481
472
  }
482
- `);
483
- await write(`presentation/${kebab}.controller.ts`, `/**
484
- * ${pascal} Controller
485
- *
486
- * Presentation layer \u2014 handles HTTP requests and delegates to use cases.
487
- * Each method receives a RequestContext with typed body, params, and query.
488
- *
489
- * Decorators:
490
- * @Controller(path?) \u2014 registers this class as an HTTP controller
491
- * @Get/@Post/@Put/@Delete(path?, validation?) \u2014 defines routes with optional Zod validation
492
- * @Autowired() \u2014 injects dependencies lazily from the DI container
493
- * @Middleware(...handlers) \u2014 attach middleware at class or method level
494
- *
495
- * Add Swagger decorators (@ApiTags, @ApiOperation, @ApiResponse) from @forinda/kickjs-swagger
496
- * for automatic OpenAPI documentation.
497
- */
498
- import { Controller, Get, Post, Put, Delete, Autowired } from '@forinda/kickjs-core'
499
- import { RequestContext } from '@forinda/kickjs-http'
473
+ `;
474
+ }
475
+ __name(generateModuleIndex, "generateModuleIndex");
476
+
477
+ // src/generators/templates/controller.ts
478
+ function generateController(pascal, kebab, plural, pluralPascal) {
479
+ return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
480
+ import type { RequestContext } from '@forinda/kickjs-http'
481
+ import { ApiTags } from '@forinda/kickjs-swagger'
500
482
  import { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'
501
483
  import { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'
502
484
  import { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'
@@ -504,6 +486,7 @@ import { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}
504
486
  import { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'
505
487
  import { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'
506
488
  import { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'
489
+ import { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'
507
490
 
508
491
  @Controller()
509
492
  export class ${pascal}Controller {
@@ -513,39 +496,65 @@ export class ${pascal}Controller {
513
496
  @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase
514
497
  @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase
515
498
 
516
- @Post('/', { body: create${pascal}Schema })
517
- async create(ctx: RequestContext) {
518
- const result = await this.create${pascal}UseCase.execute(ctx.body)
519
- ctx.created(result)
520
- }
521
-
522
499
  @Get('/')
500
+ @ApiTags('${pascal}')
501
+ @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)
523
502
  async list(ctx: RequestContext) {
524
- const result = await this.list${pluralPascal}UseCase.execute()
525
- ctx.json(result)
503
+ return ctx.paginate(
504
+ (parsed) => this.list${pluralPascal}UseCase.execute(parsed),
505
+ ${pascal.toUpperCase()}_QUERY_CONFIG,
506
+ )
526
507
  }
527
508
 
528
509
  @Get('/:id')
510
+ @ApiTags('${pascal}')
529
511
  async getById(ctx: RequestContext) {
530
512
  const result = await this.get${pascal}UseCase.execute(ctx.params.id)
531
513
  if (!result) return ctx.notFound('${pascal} not found')
532
514
  ctx.json(result)
533
515
  }
534
516
 
535
- @Put('/:id', { body: update${pascal}Schema })
517
+ @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })
518
+ @ApiTags('${pascal}')
519
+ async create(ctx: RequestContext) {
520
+ const result = await this.create${pascal}UseCase.execute(ctx.body)
521
+ ctx.created(result)
522
+ }
523
+
524
+ @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })
525
+ @ApiTags('${pascal}')
536
526
  async update(ctx: RequestContext) {
537
527
  const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)
538
528
  ctx.json(result)
539
529
  }
540
530
 
541
531
  @Delete('/:id')
532
+ @ApiTags('${pascal}')
542
533
  async remove(ctx: RequestContext) {
543
534
  await this.delete${pascal}UseCase.execute(ctx.params.id)
544
535
  ctx.noContent()
545
536
  }
546
537
  }
547
- `);
548
- await write(`application/dtos/create-${kebab}.dto.ts`, `import { z } from 'zod'
538
+ `;
539
+ }
540
+ __name(generateController, "generateController");
541
+
542
+ // src/generators/templates/constants.ts
543
+ function generateConstants(pascal) {
544
+ return `import type { QueryParamsConfig } from '@forinda/kickjs-core'
545
+
546
+ export const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
547
+ filterable: ['name'],
548
+ sortable: ['name', 'createdAt'],
549
+ searchable: ['name'],
550
+ }
551
+ `;
552
+ }
553
+ __name(generateConstants, "generateConstants");
554
+
555
+ // src/generators/templates/dtos.ts
556
+ function generateCreateDTO(pascal, kebab) {
557
+ return `import { z } from 'zod'
549
558
 
550
559
  /**
551
560
  * Create ${pascal} DTO \u2014 Zod schema for validating POST request bodies.
@@ -561,23 +570,34 @@ export const create${pascal}Schema = z.object({
561
570
  })
562
571
 
563
572
  export type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>
564
- `);
565
- await write(`application/dtos/update-${kebab}.dto.ts`, `import { z } from 'zod'
573
+ `;
574
+ }
575
+ __name(generateCreateDTO, "generateCreateDTO");
576
+ function generateUpdateDTO(pascal, kebab) {
577
+ return `import { z } from 'zod'
566
578
 
567
579
  export const update${pascal}Schema = z.object({
568
580
  name: z.string().min(1).max(200).optional(),
569
581
  })
570
582
 
571
583
  export type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>
572
- `);
573
- await write(`application/dtos/${kebab}-response.dto.ts`, `export interface ${pascal}ResponseDTO {
584
+ `;
585
+ }
586
+ __name(generateUpdateDTO, "generateUpdateDTO");
587
+ function generateResponseDTO(pascal, kebab) {
588
+ return `export interface ${pascal}ResponseDTO {
574
589
  id: string
575
590
  name: string
576
591
  createdAt: string
577
592
  updatedAt: string
578
593
  }
579
- `);
580
- const useCases = [
594
+ `;
595
+ }
596
+ __name(generateResponseDTO, "generateResponseDTO");
597
+
598
+ // src/generators/templates/use-cases.ts
599
+ function generateUseCases(pascal, kebab, plural, pluralPascal) {
600
+ return [
581
601
  {
582
602
  file: `create-${kebab}.use-case.ts`,
583
603
  content: `/**
@@ -626,7 +646,7 @@ export class Get${pascal}UseCase {
626
646
  file: `list-${plural}.use-case.ts`,
627
647
  content: `import { Service, Inject } from '@forinda/kickjs-core'
628
648
  import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
629
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
649
+ import type { ParsedQuery } from '@forinda/kickjs-http'
630
650
 
631
651
  @Service()
632
652
  export class List${pluralPascal}UseCase {
@@ -634,8 +654,8 @@ export class List${pluralPascal}UseCase {
634
654
  @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
635
655
  ) {}
636
656
 
637
- async execute(): Promise<${pascal}ResponseDTO[]> {
638
- return this.repo.findAll()
657
+ async execute(parsed: ParsedQuery) {
658
+ return this.repo.findPaginated(parsed)
639
659
  }
640
660
  }
641
661
  `
@@ -677,10 +697,12 @@ export class Delete${pascal}UseCase {
677
697
  `
678
698
  }
679
699
  ];
680
- for (const uc of useCases) {
681
- await write(`application/use-cases/${uc.file}`, uc.content);
682
- }
683
- await write(`domain/repositories/${kebab}.repository.ts`, `/**
700
+ }
701
+ __name(generateUseCases, "generateUseCases");
702
+
703
+ // src/generators/templates/repository.ts
704
+ function generateRepositoryInterface(pascal, kebab) {
705
+ return `/**
684
706
  * ${pascal} Repository Interface
685
707
  *
686
708
  * Domain layer \u2014 defines the contract for data access.
@@ -693,43 +715,23 @@ export class Delete${pascal}UseCase {
693
715
  import type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'
694
716
  import type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'
695
717
  import type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'
718
+ import type { ParsedQuery } from '@forinda/kickjs-http'
696
719
 
697
720
  export interface I${pascal}Repository {
698
721
  findById(id: string): Promise<${pascal}ResponseDTO | null>
699
722
  findAll(): Promise<${pascal}ResponseDTO[]>
723
+ findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>
700
724
  create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>
701
725
  update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>
702
726
  delete(id: string): Promise<void>
703
727
  }
704
728
 
705
729
  export const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')
706
- `);
707
- await write(`domain/services/${kebab}-domain.service.ts`, `/**
708
- * ${pascal} Domain Service
709
- *
710
- * Domain layer \u2014 contains business rules that don't belong to a single entity.
711
- * Use this for cross-entity logic, validation rules, and domain invariants.
712
- * Keep it free of HTTP/framework concerns.
713
- */
714
- import { Service, Inject, HttpException } from '@forinda/kickjs-core'
715
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'
716
-
717
- @Service()
718
- export class ${pascal}DomainService {
719
- constructor(
720
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
721
- ) {}
722
-
723
- async ensureExists(id: string): Promise<void> {
724
- const entity = await this.repo.findById(id)
725
- if (!entity) {
726
- throw HttpException.notFound('${pascal} not found')
727
- }
728
- }
730
+ `;
729
731
  }
730
- `);
731
- if (repo === "inmemory") {
732
- await write(`infrastructure/repositories/in-memory-${kebab}.repository.ts`, `/**
732
+ __name(generateRepositoryInterface, "generateRepositoryInterface");
733
+ function generateInMemoryRepository(pascal, kebab) {
734
+ return `/**
733
735
  * In-Memory ${pascal} Repository
734
736
  *
735
737
  * Infrastructure layer \u2014 implements the repository interface using a Map.
@@ -740,6 +742,7 @@ export class ${pascal}DomainService {
740
742
  */
741
743
  import { randomUUID } from 'node:crypto'
742
744
  import { Repository, HttpException } from '@forinda/kickjs-core'
745
+ import type { ParsedQuery } from '@forinda/kickjs-http'
743
746
  import type { I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
744
747
  import type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'
745
748
  import type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'
@@ -757,6 +760,12 @@ export class InMemory${pascal}Repository implements I${pascal}Repository {
757
760
  return Array.from(this.store.values())
758
761
  }
759
762
 
763
+ async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {
764
+ const all = Array.from(this.store.values())
765
+ const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)
766
+ return { data, total: all.length }
767
+ }
768
+
760
769
  async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
761
770
  const now = new Date().toISOString()
762
771
  const entity: ${pascal}ResponseDTO = {
@@ -782,10 +791,40 @@ export class InMemory${pascal}Repository implements I${pascal}Repository {
782
791
  this.store.delete(id)
783
792
  }
784
793
  }
785
- `);
794
+ `;
795
+ }
796
+ __name(generateInMemoryRepository, "generateInMemoryRepository");
797
+
798
+ // src/generators/templates/domain.ts
799
+ function generateDomainService(pascal, kebab) {
800
+ return `/**
801
+ * ${pascal} Domain Service
802
+ *
803
+ * Domain layer \u2014 contains business rules that don't belong to a single entity.
804
+ * Use this for cross-entity logic, validation rules, and domain invariants.
805
+ * Keep it free of HTTP/framework concerns.
806
+ */
807
+ import { Service, Inject, HttpException } from '@forinda/kickjs-core'
808
+ import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'
809
+
810
+ @Service()
811
+ export class ${pascal}DomainService {
812
+ constructor(
813
+ @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
814
+ ) {}
815
+
816
+ async ensureExists(id: string): Promise<void> {
817
+ const entity = await this.repo.findById(id)
818
+ if (!entity) {
819
+ throw HttpException.notFound('${pascal} not found')
820
+ }
786
821
  }
787
- if (!noEntity && !minimal) {
788
- await write(`domain/entities/${kebab}.entity.ts`, `/**
822
+ }
823
+ `;
824
+ }
825
+ __name(generateDomainService, "generateDomainService");
826
+ function generateEntity(pascal, kebab) {
827
+ return `/**
789
828
  * ${pascal} Entity
790
829
  *
791
830
  * Domain layer \u2014 the core business object.
@@ -854,8 +893,11 @@ export class ${pascal} {
854
893
  }
855
894
  }
856
895
  }
857
- `);
858
- await write(`domain/value-objects/${kebab}-id.vo.ts`, `/**
896
+ `;
897
+ }
898
+ __name(generateEntity, "generateEntity");
899
+ function generateValueObject(pascal, kebab) {
900
+ return `/**
859
901
  * ${pascal} ID Value Object
860
902
  *
861
903
  * Domain layer \u2014 wraps a primitive ID with type safety and validation.
@@ -889,7 +931,171 @@ export class ${pascal}Id {
889
931
  return this.value === other.value
890
932
  }
891
933
  }
892
- `);
934
+ `;
935
+ }
936
+ __name(generateValueObject, "generateValueObject");
937
+
938
+ // src/generators/templates/tests.ts
939
+ function generateControllerTest(pascal, kebab, plural) {
940
+ return `import { describe, it, expect, beforeEach } from 'vitest'
941
+ import { Container } from '@forinda/kickjs-core'
942
+
943
+ describe('${pascal}Controller', () => {
944
+ beforeEach(() => {
945
+ Container.reset()
946
+ })
947
+
948
+ it('should be defined', () => {
949
+ expect(true).toBe(true)
950
+ })
951
+
952
+ describe('POST /${plural}', () => {
953
+ it('should create a new ${kebab}', async () => {
954
+ // TODO: Set up test module, call create endpoint, assert 201
955
+ expect(true).toBe(true)
956
+ })
957
+ })
958
+
959
+ describe('GET /${plural}', () => {
960
+ it('should return paginated ${plural}', async () => {
961
+ // TODO: Set up test module, call list endpoint, assert { data, meta }
962
+ expect(true).toBe(true)
963
+ })
964
+ })
965
+
966
+ describe('GET /${plural}/:id', () => {
967
+ it('should return a ${kebab} by id', async () => {
968
+ // TODO: Create a ${kebab}, then fetch by id, assert match
969
+ expect(true).toBe(true)
970
+ })
971
+
972
+ it('should return 404 for non-existent ${kebab}', async () => {
973
+ // TODO: Fetch non-existent id, assert 404
974
+ expect(true).toBe(true)
975
+ })
976
+ })
977
+
978
+ describe('PUT /${plural}/:id', () => {
979
+ it('should update an existing ${kebab}', async () => {
980
+ // TODO: Create, update, assert changes
981
+ expect(true).toBe(true)
982
+ })
983
+ })
984
+
985
+ describe('DELETE /${plural}/:id', () => {
986
+ it('should delete a ${kebab}', async () => {
987
+ // TODO: Create, delete, assert gone
988
+ expect(true).toBe(true)
989
+ })
990
+ })
991
+ })
992
+ `;
993
+ }
994
+ __name(generateControllerTest, "generateControllerTest");
995
+ function generateRepositoryTest(pascal, kebab, plural) {
996
+ return `import { describe, it, expect, beforeEach } from 'vitest'
997
+ import { InMemory${pascal}Repository } from '../infrastructure/repositories/in-memory-${kebab}.repository'
998
+
999
+ describe('InMemory${pascal}Repository', () => {
1000
+ let repo: InMemory${pascal}Repository
1001
+
1002
+ beforeEach(() => {
1003
+ repo = new InMemory${pascal}Repository()
1004
+ })
1005
+
1006
+ it('should create and retrieve a ${kebab}', async () => {
1007
+ const created = await repo.create({ name: 'Test ${pascal}' })
1008
+ expect(created).toBeDefined()
1009
+ expect(created.name).toBe('Test ${pascal}')
1010
+ expect(created.id).toBeDefined()
1011
+
1012
+ const found = await repo.findById(created.id)
1013
+ expect(found).toEqual(created)
1014
+ })
1015
+
1016
+ it('should return null for non-existent id', async () => {
1017
+ const found = await repo.findById('non-existent')
1018
+ expect(found).toBeNull()
1019
+ })
1020
+
1021
+ it('should list all ${plural}', async () => {
1022
+ await repo.create({ name: '${pascal} 1' })
1023
+ await repo.create({ name: '${pascal} 2' })
1024
+
1025
+ const all = await repo.findAll()
1026
+ expect(all).toHaveLength(2)
1027
+ })
1028
+
1029
+ it('should return paginated results', async () => {
1030
+ await repo.create({ name: '${pascal} 1' })
1031
+ await repo.create({ name: '${pascal} 2' })
1032
+ await repo.create({ name: '${pascal} 3' })
1033
+
1034
+ const result = await repo.findPaginated({
1035
+ filters: [],
1036
+ sort: [],
1037
+ search: '',
1038
+ pagination: { page: 1, limit: 2, offset: 0 },
1039
+ })
1040
+
1041
+ expect(result.data).toHaveLength(2)
1042
+ expect(result.total).toBe(3)
1043
+ })
1044
+
1045
+ it('should update a ${kebab}', async () => {
1046
+ const created = await repo.create({ name: 'Original' })
1047
+ const updated = await repo.update(created.id, { name: 'Updated' })
1048
+ expect(updated.name).toBe('Updated')
1049
+ })
1050
+
1051
+ it('should delete a ${kebab}', async () => {
1052
+ const created = await repo.create({ name: 'To Delete' })
1053
+ await repo.delete(created.id)
1054
+ const found = await repo.findById(created.id)
1055
+ expect(found).toBeNull()
1056
+ })
1057
+ })
1058
+ `;
1059
+ }
1060
+ __name(generateRepositoryTest, "generateRepositoryTest");
1061
+
1062
+ // src/generators/module.ts
1063
+ async function generateModule(options) {
1064
+ const { name, modulesDir, noEntity, noTests, repo = "inmemory", minimal } = options;
1065
+ const kebab = toKebabCase(name);
1066
+ const pascal = toPascalCase(name);
1067
+ const camel = toCamelCase(name);
1068
+ const plural = pluralize(kebab);
1069
+ const pluralPascal = pluralizePascal(pascal);
1070
+ const moduleDir = join2(modulesDir, plural);
1071
+ const files = [];
1072
+ const write = /* @__PURE__ */ __name(async (relativePath, content) => {
1073
+ const fullPath = join2(moduleDir, relativePath);
1074
+ await writeFileSafe(fullPath, content);
1075
+ files.push(fullPath);
1076
+ }, "write");
1077
+ await write("index.ts", generateModuleIndex(pascal, kebab, plural, repo));
1078
+ await write("constants.ts", generateConstants(pascal));
1079
+ await write(`presentation/${kebab}.controller.ts`, generateController(pascal, kebab, plural, pluralPascal));
1080
+ await write(`application/dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab));
1081
+ await write(`application/dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab));
1082
+ await write(`application/dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab));
1083
+ const useCases = generateUseCases(pascal, kebab, plural, pluralPascal);
1084
+ for (const uc of useCases) {
1085
+ await write(`application/use-cases/${uc.file}`, uc.content);
1086
+ }
1087
+ await write(`domain/repositories/${kebab}.repository.ts`, generateRepositoryInterface(pascal, kebab));
1088
+ await write(`domain/services/${kebab}-domain.service.ts`, generateDomainService(pascal, kebab));
1089
+ if (repo === "inmemory") {
1090
+ await write(`infrastructure/repositories/in-memory-${kebab}.repository.ts`, generateInMemoryRepository(pascal, kebab));
1091
+ }
1092
+ if (!noEntity && !minimal) {
1093
+ await write(`domain/entities/${kebab}.entity.ts`, generateEntity(pascal, kebab));
1094
+ await write(`domain/value-objects/${kebab}-id.vo.ts`, generateValueObject(pascal, kebab));
1095
+ }
1096
+ if (!noTests) {
1097
+ await write(`__tests__/${kebab}.controller.test.ts`, generateControllerTest(pascal, kebab, plural));
1098
+ await write(`__tests__/${kebab}.repository.test.ts`, generateRepositoryTest(pascal, kebab, plural));
893
1099
  }
894
1100
  await autoRegisterModule(modulesDir, pascal, plural);
895
1101
  return files;
@@ -1143,7 +1349,7 @@ __name(generateService, "generateService");
1143
1349
 
1144
1350
  // src/generators/controller.ts
1145
1351
  import { join as join7 } from "path";
1146
- async function generateController(options) {
1352
+ async function generateController2(options) {
1147
1353
  const { name, outDir } = options;
1148
1354
  const kebab = toKebabCase(name);
1149
1355
  const pascal = toPascalCase(name);
@@ -1170,7 +1376,7 @@ export class ${pascal}Controller {
1170
1376
  files.push(filePath);
1171
1377
  return files;
1172
1378
  }
1173
- __name(generateController, "generateController");
1379
+ __name(generateController2, "generateController");
1174
1380
 
1175
1381
  // src/generators/dto.ts
1176
1382
  import { join as join8 } from "path";
@@ -1313,7 +1519,7 @@ function registerGenerateCommand(program) {
1313
1519
  printGenerated(files);
1314
1520
  });
1315
1521
  gen.command("controller <name>").description("Generate a @Controller() class with basic routes").option("-o, --out <dir>", "Output directory", "src/controllers").action(async (name, opts) => {
1316
- const files = await generateController({
1522
+ const files = await generateController2({
1317
1523
  name,
1318
1524
  outDir: resolve2(opts.out)
1319
1525
  });