opticore-feature-component 1.0.0 → 1.0.2

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/index.cjs CHANGED
@@ -35,16 +35,35 @@ var init_cjs_shims = __esm({
35
35
 
36
36
  // src/utils/welcomeMessage.utils.ts
37
37
  function UWelcomeMessage() {
38
- return console.log((0, import_gradient_string.default)("cyan", "pink", "orange")(
39
- "\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n\u2502 \u2502\n\u2502 Welcome to Feature Component CLI \u2502\n\u2502 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n"
40
- ));
38
+ import_cfonts.default.say("OpticoreJS", {
39
+ font: "block",
40
+ align: "left",
41
+ colors: ["yellow", "#FF6B35"],
42
+ background: "transparent",
43
+ letterSpacing: 1,
44
+ lineHeight: 1,
45
+ space: true,
46
+ maxLength: "0"
47
+ });
48
+ const blue = import_chalk.default.bold.hex("#00D4FF");
49
+ const orange = import_chalk.default.bold.hex("#FF6B35");
50
+ const dim = import_chalk.default.bold.yellow;
51
+ const subTitle = ` ${blue("")} ${orange(" OPTICORE F E A T U R E M O D U L E")}
52
+ ${blue("")} ${dim(" Create \xB7 Structure \xB7 Scaffold \xB7 Generate \xB7 Organize ")} ${blue("")}
53
+ ${blue("")}`;
54
+ const FOOTER = `
55
+ ${import_chalk.default.bold.dim("Documentation")} ${import_chalk.default.underline.cyan("https://github.com/guyzoum77/opticore-feature-cli")}
56
+ `;
57
+ console.log(subTitle);
58
+ console.log(FOOTER);
41
59
  }
42
- var import_gradient_string;
60
+ var import_cfonts, import_chalk;
43
61
  var init_welcomeMessage_utils = __esm({
44
62
  "src/utils/welcomeMessage.utils.ts"() {
45
63
  "use strict";
46
64
  init_cjs_shims();
47
- import_gradient_string = __toESM(require("gradient-string"), 1);
65
+ import_cfonts = __toESM(require("cfonts"), 1);
66
+ import_chalk = __toESM(require("chalk"), 1);
48
67
  }
49
68
  });
50
69
 
@@ -173,44 +192,44 @@ var init_choiceFeatureCleanModule = __esm({
173
192
  }
174
193
  });
175
194
 
176
- // src/utils/constants/customFeatureInfoMessage.constant.ts
177
- function customFeatureInfo() {
178
- console.log(`${import_ansi_colors3.default.bgCyan(`${import_ansi_colors3.default.white("\nYou will have to create and configure your created modules manually.")}`)}`);
179
- import_process.default.exit(0);
180
- }
181
- var import_ansi_colors3, import_process;
182
- var init_customFeatureInfoMessage_constant = __esm({
183
- "src/utils/constants/customFeatureInfoMessage.constant.ts"() {
184
- "use strict";
185
- init_cjs_shims();
186
- import_ansi_colors3 = __toESM(require("ansi-colors"), 1);
187
- import_process = __toESM(require("process"), 1);
188
- }
189
- });
190
-
191
195
  // src/utils/customFeatureInfo.utils.ts
192
196
  function UCustomFeatureInfo(choice) {
193
- console.log(`You have chosen : ${choice === "opticore_clean_module" ? import_ansi_colors4.default.cyan("OptiCoreJs CLEAN Module") : import_ansi_colors4.default.cyan("Custom feature")}
194
- `);
195
- console.log(`Clean module contains the following files :`);
196
- console.log(`${import_ansi_colors4.default.cyan("application")}`);
197
- console.log(` \u2570\u2500\u2500${import_ansi_colors4.default.bgBlackBright(`${import_ansi_colors4.default.white("{controller, service, validator}")}`)}`);
198
- console.log(`${import_ansi_colors4.default.cyan("domain")}`);
199
- console.log(` \u2570\u2500\u2500${import_ansi_colors4.default.bgBlackBright(`${import_ansi_colors4.default.white("{constants, entities, enums, interfaces}")}`)}`);
200
- console.log(`${import_ansi_colors4.default.cyan("infrastructures")}`);
201
- console.log(` \u2570\u2500\u2500${import_ansi_colors4.default.bgBlackBright(`${import_ansi_colors4.default.white("{api, middleware}")}`)}`);
202
- console.log(`${import_ansi_colors4.default.cyan("persistence")}`);
203
- console.log(` \u2570\u2500\u2500${import_ansi_colors4.default.bgBlackBright(`${import_ansi_colors4.default.white("{cache, contrats, repositories, usecases}")}`)}`);
204
- console.log(`${import_ansi_colors4.default.cyan("file")} : route of feature named ${import_ansi_colors4.default.cyan("featureName.router")}
205
- `);
206
- console.log(import_ansi_colors4.default.cyan("Now let's start create a feature !"));
197
+ console.log(
198
+ `
199
+ You have chosen : ${choice === "opticore_clean_module" ? import_ansi_colors3.default.cyan("OptiCoreJs CLEAN Module") : import_ansi_colors3.default.cyan("Custom feature")}
200
+ `
201
+ );
202
+ const I = (s) => import_ansi_colors3.default.yellow(s);
203
+ const A = (s) => import_ansi_colors3.default.cyan(s);
204
+ const D = (s) => import_ansi_colors3.default.yellowBright(s);
205
+ const E = (s) => import_ansi_colors3.default.white(s);
206
+ const st = (s) => import_ansi_colors3.default.gray(s);
207
+ const diagram = [
208
+ ` ` + I(`INFRASTRUCTURE`),
209
+ ` ` + I(`controllers \xB7 repositories \xB7 routes`),
210
+ ` ` + I(`presenters \xB7 routes`),
211
+ ``,
212
+ ` ` + A(`APPLICATION`),
213
+ ` ` + A(`ports \xB7 interfaces \xB7 dtos \xB7 use-cases`),
214
+ ``,
215
+ ` ` + D(`DOMAIN`),
216
+ ` ` + D(`events \xB7 exceptions`),
217
+ ``,
218
+ ` ` + E(`ENTITIES`),
219
+ ` ` + E(`core \xB7 no external deps`),
220
+ ``,
221
+ ` ` + import_ansi_colors3.default.dim(`Dependency direction : outer layers depend on inner layers`),
222
+ ``
223
+ ];
224
+ console.log(diagram.join(`
225
+ `));
207
226
  }
208
- var import_ansi_colors4;
227
+ var import_ansi_colors3;
209
228
  var init_customFeatureInfo_utils = __esm({
210
229
  "src/utils/customFeatureInfo.utils.ts"() {
211
230
  "use strict";
212
231
  init_cjs_shims();
213
- import_ansi_colors4 = __toESM(require("ansi-colors"), 1);
232
+ import_ansi_colors3 = __toESM(require("ansi-colors"), 1);
214
233
  }
215
234
  });
216
235
 
@@ -240,20 +259,20 @@ var init_featureName_service = __esm({
240
259
  });
241
260
 
242
261
  // src/services/base.service.ts
243
- var import_path, import_node_fs, import_ansi_colors5, import_process2, BaseService;
262
+ var import_path, import_node_fs, import_ansi_colors4, import_process, BaseService;
244
263
  var init_base_service = __esm({
245
264
  "src/services/base.service.ts"() {
246
265
  "use strict";
247
266
  init_cjs_shims();
248
267
  import_path = __toESM(require("path"), 1);
249
268
  import_node_fs = __toESM(require("fs"), 1);
250
- import_ansi_colors5 = __toESM(require("ansi-colors"), 1);
251
- import_process2 = __toESM(require("process"), 1);
269
+ import_ansi_colors4 = __toESM(require("ansi-colors"), 1);
270
+ import_process = __toESM(require("process"), 1);
252
271
  init_fsModule_utils();
253
272
  BaseService = class {
254
273
  static _isSubdirectoryExists(subdirectory) {
255
274
  const subdirectoryString = typeof subdirectory === "symbol" ? subdirectory.toString() : String(subdirectory);
256
- const subdirectoryPath = import_path.default.join(import_process2.default.cwd(), "src", "features", subdirectoryString);
275
+ const subdirectoryPath = import_path.default.join(import_process.default.cwd(), "src", "features", subdirectoryString);
257
276
  try {
258
277
  const stats = import_node_fs.default.statSync(subdirectoryPath);
259
278
  return stats.isDirectory();
@@ -262,27 +281,27 @@ var init_base_service = __esm({
262
281
  }
263
282
  }
264
283
  static _featureFounded(feature) {
265
- console.log(`${import_ansi_colors5.default.bgCyan(`${import_ansi_colors5.default.white(`${import_ansi_colors5.default.bold(`feature ${feature} is already exists.`)}`)}`)}`);
266
- import_process2.default.exit(0);
284
+ console.log(`${import_ansi_colors4.default.bgCyan(`${import_ansi_colors4.default.white(`${import_ansi_colors4.default.bold(`feature ${feature} is already exists.`)}`)}`)}`);
285
+ import_process.default.exit(0);
267
286
  }
268
287
  static _checkFeatureDir() {
269
- console.error(`${import_ansi_colors5.default.bgRed(`${import_ansi_colors5.default.white("Check if directory src/features exists, and try again.")}`)}`);
270
- import_process2.default.exit();
288
+ console.error(`${import_ansi_colors4.default.bgRed(`${import_ansi_colors4.default.white("Check if directory src/features exists, and try again.")}`)}`);
289
+ import_process.default.exit();
271
290
  }
272
291
  static _dirFounded(feature) {
273
- console.error(`${import_ansi_colors5.default.bgRed(`${import_ansi_colors5.default.white(`The ${import_ansi_colors5.default.bgCyan(`${import_ansi_colors5.default.bold(`feature ${feature} is already exists.`)}`)}`)}`)}`);
274
- import_process2.default.exit();
292
+ console.error(`${import_ansi_colors4.default.bgRed(`${import_ansi_colors4.default.white(`The ${import_ansi_colors4.default.bgCyan(`${import_ansi_colors4.default.bold(`feature ${feature} is already exists.`)}`)}`)}`)}`);
293
+ import_process.default.exit();
275
294
  }
276
295
  static _cancelOperation(controllerPath) {
277
- console.log(`${import_ansi_colors5.default.bgRed(`${import_ansi_colors5.default.white("Operation cancelled.")}`)}`);
296
+ console.log(`${import_ansi_colors4.default.bgRed(`${import_ansi_colors4.default.white("Operation cancelled.")}`)}`);
278
297
  UFsModule.removeDir(controllerPath, true, true);
279
- import_process2.default.exit(0);
298
+ import_process.default.exit(0);
280
299
  }
281
300
  static async _createFeatureModuleDir(featureDir, feature) {
282
301
  let ora = (await import("ora")).default;
283
302
  const spinner = ora("Feature's name creation...").start();
284
303
  UFsModule.createDirectoryRecursively(featureDir);
285
- spinner.succeed(`Your feature"s name ${import_ansi_colors5.default.bgCyan(import_ansi_colors5.default.white(`${feature}`))} has been created successfully.
304
+ spinner.succeed(`Your feature"s name ${import_ansi_colors4.default.bgCyan(import_ansi_colors4.default.white(`${feature}`))} has been created successfully.
286
305
  `);
287
306
  return { featureDir, feature };
288
307
  }
@@ -364,13 +383,6 @@ import { ${capitalized}Repository } from "../repositories/${featureName}.reposit
364
383
 
365
384
  /**
366
385
  * ${controllerName} \u2014 HTTP Controller (Infrastructure Layer)
367
- *
368
- * Handles Express Request/Response only.
369
- * Delegates all business logic to the use case.
370
- * Uses the presenter to format the output.
371
- *
372
- * Wiring: Controller \u2192 UseCase \u2192 Repository (all injected here manually).
373
- * For larger projects, replace manual wiring with a DI container (e.g., tsyringe, awilix).
374
386
  */
375
387
  export class ${controllerName} {
376
388
  private static buildUseCase(): ${useCaseName} {
@@ -498,7 +510,9 @@ ${errorHandler}
498
510
  `;
499
511
  errorHandlerAppend = (capitalized) => `
500
512
  private static handleError(error: unknown) {
501
- const message = error instanceof Error ? error.message : "Internal server error";
513
+ const message = error instanceof Error
514
+ ? error.message
515
+ : "Internal server error";
502
516
  return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
503
517
  }
504
518
  `;
@@ -537,10 +551,6 @@ export class ${entityName} {
537
551
  this.validate();
538
552
  }
539
553
 
540
- // -------------------------------------------------------------------------
541
- // Getters
542
- // -------------------------------------------------------------------------
543
-
544
554
  get id(): string {
545
555
  return this._id;
546
556
  }
@@ -553,10 +563,6 @@ export class ${entityName} {
553
563
  return this._updatedAt;
554
564
  }
555
565
 
556
- // -------------------------------------------------------------------------
557
- // Business methods (behavior belongs to the entity)
558
- // -------------------------------------------------------------------------
559
-
560
566
  /**
561
567
  * Marks the entity as updated.
562
568
  * Call this whenever a business mutation occurs.
@@ -577,10 +583,6 @@ export class ${entityName} {
577
583
  };
578
584
  }
579
585
 
580
- // -------------------------------------------------------------------------
581
- // Invariant validation (domain rules enforced at construction)
582
- // -------------------------------------------------------------------------
583
-
584
586
  private validate(): void {
585
587
  if (!this._id || this._id.trim().length === 0) {
586
588
  throw new Error(\`[${entityName}] id must not be empty.\`);
@@ -674,16 +676,14 @@ export class ${implName} implements ${interfaceName} {
674
676
 
675
677
  /**
676
678
  * TODO: Inject your database client / ORM model here via the constructor.
677
- * Example with TypeORM:
678
- * constructor(private readonly repo: Repository<${capitalized}Model>) {}
679
+ * Example with TypeORM: constructor(private readonly repo: Repository<${capitalized}Model>) {}
679
680
  *
680
- * Example with Prisma:
681
- * constructor(private readonly prisma: PrismaClient) {}
681
+ * Example with Prisma: constructor(private readonly prisma: PrismaClient) {}
682
682
  */
683
683
 
684
- // -------------------------------------------------------------------------
685
- // In-memory store (replace with real persistence)
686
- // -------------------------------------------------------------------------
684
+ /**
685
+ * In-memory store (replace with real persistence)
686
+ */
687
687
  private store: Map<string, ${entityName}> = new Map();
688
688
 
689
689
  async findAll(): Promise<${entityName}[]> {
@@ -726,9 +726,6 @@ export class ${implName} implements ${interfaceName} {
726
726
  return this.store.delete(id);
727
727
  }
728
728
 
729
- // -------------------------------------------------------------------------
730
- // Private mappers (ORM model \u2194 Domain entity)
731
- // -------------------------------------------------------------------------
732
729
 
733
730
  /**
734
731
  * Maps an ORM/DB row to a domain entity.
@@ -790,13 +787,7 @@ import {
790
787
  */
791
788
  export class ${useCaseName} {
792
789
 
793
- constructor(
794
- private readonly repository: ${interfaceName},
795
- ) {}
796
-
797
- // -------------------------------------------------------------------------
798
- // Query methods (read)
799
- // -------------------------------------------------------------------------
790
+ constructor(private readonly repository: ${interfaceName}) {}
800
791
 
801
792
  async findAll(): Promise<${capitalized}ResponseDto[]> {
802
793
  const entities = await this.repository.findAll();
@@ -805,14 +796,12 @@ export class ${useCaseName} {
805
796
 
806
797
  async findById(id: string): Promise<${capitalized}ResponseDto | null> {
807
798
  const entity = await this.repository.findById(id);
808
- if (!entity) return null;
799
+ if (!entity) {
800
+ return null;
801
+ }
809
802
  return ${capitalized}DtoMapper.toResponse(entity);
810
803
  }
811
804
 
812
- // -------------------------------------------------------------------------
813
- // Command methods (write)
814
- // -------------------------------------------------------------------------
815
-
816
805
  async create(dto: Create${capitalized}Dto): Promise<${capitalized}ResponseDto> {
817
806
  // TODO: Map DTO to domain entity. Generate id using your preferred strategy (uuid, etc.)
818
807
  const id = crypto.randomUUID();
@@ -827,14 +816,18 @@ export class ${useCaseName} {
827
816
 
828
817
  async update(dto: Update${capitalized}Dto): Promise<${capitalized}ResponseDto | null> {
829
818
  const existing = await this.repository.findById(dto.id);
830
- if (!existing) return null;
819
+ if (!existing) {
820
+ return null;
821
+ }
831
822
 
832
823
  // TODO: Apply the update to the domain entity
833
824
  // existing.updateName(dto.name);
834
825
  existing.touch();
835
826
 
836
827
  const updated = await this.repository.update(existing);
837
- if (!updated) return null;
828
+ if (!updated) {
829
+ return null;
830
+ }
838
831
  return ${capitalized}DtoMapper.toResponse(updated);
839
832
  }
840
833
 
@@ -864,9 +857,6 @@ var init_dto_template = __esm({
864
857
  * Validation is handled at the infrastructure layer (e.g., in the controller).
865
858
  */
866
859
 
867
- // -------------------------------------------------------------------------
868
- // Input DTOs (data coming IN to use cases)
869
- // -------------------------------------------------------------------------
870
860
 
871
861
  export interface Create${capitalized}Dto {
872
862
  // TODO: Define the fields required to create a ${featureName}
@@ -877,28 +867,17 @@ export interface Create${capitalized}Dto {
877
867
 
878
868
  export interface Update${capitalized}Dto {
879
869
  id: string;
880
- // TODO: Define the fields allowed to be updated
881
- // All fields should be optional (partial update pattern)
882
- // Example:
883
- // name?: string;
870
+ // TODO: Define the fields allowed to be updated. All fields should be optional (partial update pattern)
871
+ // Example: name?: string;
884
872
  }
885
873
 
886
- // -------------------------------------------------------------------------
887
- // Output DTOs (data going OUT from use cases to the controller/presenter)
888
- // -------------------------------------------------------------------------
889
-
890
874
  export interface ${capitalized}ResponseDto {
891
875
  id: string;
892
876
  createdAt: Date;
893
877
  updatedAt: Date;
894
- // TODO: Mirror the fields you expose to clients
895
- // Avoid leaking internal domain model details (e.g., private fields, passwords)
878
+ // TODO: Mirror the fields you expose to clients. Avoid leaking internal domain model details (e.g., private fields, passwords)
896
879
  }
897
880
 
898
- // -------------------------------------------------------------------------
899
- // Mapper helper (Domain Entity \u2192 Response DTO)
900
- // -------------------------------------------------------------------------
901
-
902
881
  import { ${capitalized}Entity } from "../../domain/entities/${featureName}.entity";
903
882
 
904
883
  export class ${capitalized}DtoMapper {
@@ -1031,9 +1010,9 @@ var init_router_template = __esm({
1031
1010
  const handlerName = `${capitalized}HandlerRouter`;
1032
1011
  const controllerName = `${capitalized}Controller`;
1033
1012
  const routesCode = (controllerMethods || []).map((method) => {
1034
- const { httpMethod, path: path7 } = resolveRoute(method, featureName);
1013
+ const { httpMethod, path: path8 } = resolveRoute(method, featureName);
1035
1014
  return `{
1036
- path: \`${path7}\`,
1015
+ path: \`${path8}\`,
1037
1016
  method: "${httpMethod}",
1038
1017
  middlewares: [],
1039
1018
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
@@ -1093,24 +1072,22 @@ var init_event_template = __esm({
1093
1072
  * - Feed an event bus / message broker (RabbitMQ, Kafka, etc.)
1094
1073
  * - Build an audit log or event sourcing stream
1095
1074
  */
1075
+
1096
1076
 
1097
- // -------------------------------------------------------------------------
1098
- // Base interface \u2014 all domain events share these fields
1099
- // -------------------------------------------------------------------------
1100
-
1077
+ /**
1078
+ * Unique event identifier (uuid)
1079
+ * When the event occurred
1080
+ * The aggregate that emitted this event
1081
+ */
1101
1082
  export interface IDomainEvent {
1102
- /** Unique event identifier (uuid) */
1103
1083
  readonly eventId: string;
1104
- /** When the event occurred */
1105
1084
  readonly occurredAt: Date;
1106
- /** The aggregate that emitted this event */
1107
1085
  readonly aggregateId: string;
1108
1086
  }
1109
1087
 
1110
- // -------------------------------------------------------------------------
1111
- // Concrete events for the "${featureName}" feature
1112
- // -------------------------------------------------------------------------
1113
-
1088
+ /**
1089
+ * Concrete events for the "${featureName}" feature
1090
+ */
1114
1091
  export class ${capitalized}CreatedEvent implements IDomainEvent {
1115
1092
  readonly eventId: string;
1116
1093
  readonly occurredAt: Date;
@@ -1176,10 +1153,6 @@ var init_exception_template = __esm({
1176
1153
  * Rule: never import HTTP status codes or Express here \u2014 this is pure domain logic.
1177
1154
  */
1178
1155
 
1179
- // -------------------------------------------------------------------------
1180
- // Base domain exception
1181
- // -------------------------------------------------------------------------
1182
-
1183
1156
  export class ${capitalized}DomainException extends Error {
1184
1157
  /** Machine-readable error code for programmatic handling */
1185
1158
  readonly code: string;
@@ -1194,9 +1167,6 @@ export class ${capitalized}DomainException extends Error {
1194
1167
  }
1195
1168
  }
1196
1169
 
1197
- // -------------------------------------------------------------------------
1198
- // Specific domain exceptions
1199
- // -------------------------------------------------------------------------
1200
1170
 
1201
1171
  /**
1202
1172
  * Thrown when a ${featureName} cannot be found by its identifier.
@@ -1240,10 +1210,6 @@ export class ${capitalized}InvalidDataException extends ${capitalized}DomainExce
1240
1210
  }
1241
1211
  }
1242
1212
 
1243
- // -------------------------------------------------------------------------
1244
- // Exception guard helper
1245
- // -------------------------------------------------------------------------
1246
-
1247
1213
  /**
1248
1214
  * Type guard to check if an unknown error is a ${capitalized}DomainException.
1249
1215
  * Use this in your controller's catch block to map domain errors to HTTP responses.
@@ -1456,7 +1422,9 @@ export class ${serviceName} {
1456
1422
 
1457
1423
  async update(id: string, data: Record<string, unknown>): Promise<${modelName} | null> {
1458
1424
  const existing = await this.repository.findById(id);
1459
- if (!existing) return null;
1425
+ if (!existing) {
1426
+ return null;
1427
+ }
1460
1428
  // TODO: Apply data fields onto existing model
1461
1429
  return this.repository.update(existing);
1462
1430
  }
@@ -1500,11 +1468,21 @@ ${methodsCode}
1500
1468
  };
1501
1469
  generateMethod2 = (method, capitalized, serviceName) => {
1502
1470
  const lower = method.toLowerCase();
1503
- if (lower.includes("findall") || lower.includes("getall")) return generateFindAllMethod2(method, capitalized);
1504
- if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return generateFindByIdMethod2(method, capitalized);
1505
- if (lower.includes("create") || lower.includes("add")) return generateCreateMethod2(method, capitalized);
1506
- if (lower.includes("update") || lower.includes("edit")) return generateUpdateMethod2(method, capitalized);
1507
- if (lower.includes("delete") || lower.includes("remove")) return generateDeleteMethod2(method, capitalized);
1471
+ if (lower.includes("findall") || lower.includes("getall")) {
1472
+ return generateFindAllMethod2(method, capitalized);
1473
+ }
1474
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) {
1475
+ return generateFindByIdMethod2(method, capitalized);
1476
+ }
1477
+ if (lower.includes("create") || lower.includes("add")) {
1478
+ return generateCreateMethod2(method, capitalized);
1479
+ }
1480
+ if (lower.includes("update") || lower.includes("edit")) {
1481
+ return generateUpdateMethod2(method, capitalized);
1482
+ }
1483
+ if (lower.includes("delete") || lower.includes("remove")) {
1484
+ return generateDeleteMethod2(method, capitalized);
1485
+ }
1508
1486
  return `
1509
1487
  static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1510
1488
  try {
@@ -1608,9 +1586,9 @@ var init_simpleRouter_template = __esm({
1608
1586
  const handlerName = `${capitalized}HandlerRouter`;
1609
1587
  const controllerName = `${capitalized}Controller`;
1610
1588
  const routesCode = (controllerMethods || []).map((method) => {
1611
- const { httpMethod, path: path7 } = resolveRoute2(method, featureName);
1589
+ const { httpMethod, path: path8 } = resolveRoute2(method, featureName);
1612
1590
  return `{
1613
- path: \`${path7}\`,
1591
+ path: \`${path8}\`,
1614
1592
  method: "${httpMethod}",
1615
1593
  middlewares: [],
1616
1594
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
@@ -1651,13 +1629,13 @@ export const ${routerName}: TFeatureRoutes = {
1651
1629
  });
1652
1630
 
1653
1631
  // src/utils/featureComponentGenerator.utils.ts
1654
- var import_path2, import_ansi_colors6, import_fs2, import_opticore_logger2, FeatureComponentGeneratorUtils;
1632
+ var import_path2, import_ansi_colors5, import_fs2, import_opticore_logger2, FeatureComponentGeneratorUtils;
1655
1633
  var init_featureComponentGenerator_utils = __esm({
1656
1634
  "src/utils/featureComponentGenerator.utils.ts"() {
1657
1635
  "use strict";
1658
1636
  init_cjs_shims();
1659
1637
  import_path2 = __toESM(require("path"), 1);
1660
- import_ansi_colors6 = __toESM(require("ansi-colors"), 1);
1638
+ import_ansi_colors5 = __toESM(require("ansi-colors"), 1);
1661
1639
  import_fs2 = __toESM(require("fs"), 1);
1662
1640
  init_prompt_utils();
1663
1641
  init_fsModule_utils();
@@ -1685,107 +1663,101 @@ var init_featureComponentGenerator_utils = __esm({
1685
1663
  static setStructure(s) {
1686
1664
  this.structure = s;
1687
1665
  }
1666
+ /**
1667
+ * Registers a custom module router in register.router.ts.
1668
+ * Must be preceded by setStructure("custom").
1669
+ */
1670
+ static async registerModuleRouter(moduleName) {
1671
+ await this.updateRegisterRouter(moduleName);
1672
+ this.structure = "clean";
1673
+ }
1688
1674
  /**
1689
1675
  * Creates the domain entity file.
1690
- * File: domain/entities/<featureName>.entity.ts
1691
1676
  */
1692
1677
  static async createEntity(featureName, entityDir) {
1693
1678
  const entityName = `${this.capitalize(featureName)}Entity`;
1694
1679
  const filePath = import_path2.default.join(entityDir, `${featureName}.entity.ts`);
1695
1680
  UFsModule.createFile(filePath, generateEntityTemplate(entityName, featureName));
1696
- console.log(import_ansi_colors6.default.green(`\u2705 Entity created: ${filePath}`));
1681
+ console.log(import_ansi_colors5.default.green(`\u2705 Entity created: ${filePath}`));
1697
1682
  }
1698
1683
  /**
1699
1684
  * Creates the domain event file.
1700
- * File: domain/events/<featureName>.event.ts
1701
1685
  */
1702
1686
  static async createEvent(featureName, eventDir) {
1703
1687
  const filePath = import_path2.default.join(eventDir, `${featureName}.event.ts`);
1704
1688
  UFsModule.createFile(filePath, generateEventTemplate(featureName));
1705
- console.log(import_ansi_colors6.default.green(`\u2705 Event created: ${filePath}`));
1689
+ console.log(import_ansi_colors5.default.green(`\u2705 Event created: ${filePath}`));
1706
1690
  }
1707
1691
  /**
1708
1692
  * Creates the domain exception file.
1709
- * File: domain/exceptions/<featureName>.exception.ts
1710
1693
  */
1711
1694
  static async createException(featureName, exceptionDir) {
1712
1695
  const filePath = import_path2.default.join(exceptionDir, `${featureName}.exception.ts`);
1713
1696
  UFsModule.createFile(filePath, generateExceptionTemplate(featureName));
1714
- console.log(import_ansi_colors6.default.green(`\u2705 Exception created: ${filePath}`));
1697
+ console.log(import_ansi_colors5.default.green(`\u2705 Exception created: ${filePath}`));
1715
1698
  }
1716
1699
  /**
1717
1700
  * Creates the repository port interface.
1718
- * File: application/ports/repositories/<featureName>.repository.interface.ts
1719
1701
  */
1720
1702
  static async createRepositoryInterface(featureName, interfaceDir) {
1721
1703
  const interfaceName = `I${this.capitalize(featureName)}Repository`;
1722
1704
  const filePath = import_path2.default.join(interfaceDir, `${featureName}.repository.interface.ts`);
1723
1705
  UFsModule.createFile(filePath, generateRepositoryInterfaceTemplate(interfaceName, featureName));
1724
- console.log(import_ansi_colors6.default.green(`\u2705 Repository interface created: ${filePath}`));
1706
+ console.log(import_ansi_colors5.default.green(`\u2705 Repository interface created: ${filePath}`));
1725
1707
  }
1726
1708
  /**
1727
1709
  * Creates the presenter port interface.
1728
- * File: application/ports/presenters/<featureName>.presenter.interface.ts
1729
1710
  */
1730
1711
  static async createPresenterInterface(featureName, presenterInterfaceDir) {
1731
1712
  const filePath = import_path2.default.join(presenterInterfaceDir, `${featureName}.presenter.interface.ts`);
1732
1713
  UFsModule.createFile(filePath, generatePresenterInterfaceTemplate(featureName));
1733
- console.log(import_ansi_colors6.default.green(`\u2705 Presenter interface created: ${filePath}`));
1714
+ console.log(import_ansi_colors5.default.green(`\u2705 Presenter interface created: ${filePath}`));
1734
1715
  }
1735
1716
  /**
1736
1717
  * Creates the application service port interface.
1737
- * File: application/ports/services/<featureName>.service.ts
1738
1718
  */
1739
1719
  static async createService(featureName, serviceDir) {
1740
1720
  const filePath = import_path2.default.join(serviceDir, `${featureName}.service.ts`);
1741
1721
  UFsModule.createFile(filePath, generateServiceTemplate(featureName));
1742
- console.log(import_ansi_colors6.default.green(`\u2705 Service interface created: ${filePath}`));
1722
+ console.log(import_ansi_colors5.default.green(`\u2705 Service interface created: ${filePath}`));
1743
1723
  }
1744
1724
  /**
1745
1725
  * Creates the DTO file (input + output shapes + mapper).
1746
- * File: application/dtos/<featureName>.dto.ts
1747
1726
  */
1748
1727
  static async createDto(featureName, dtoDir) {
1749
1728
  const filePath = import_path2.default.join(dtoDir, `${featureName}.dto.ts`);
1750
1729
  UFsModule.createFile(filePath, generateDtoTemplate(featureName));
1751
- console.log(import_ansi_colors6.default.green(`\u2705 DTO created: ${filePath}`));
1730
+ console.log(import_ansi_colors5.default.green(`\u2705 DTO created: ${filePath}`));
1752
1731
  }
1753
1732
  /**
1754
1733
  * Creates the use case file.
1755
- * File: application/use-cases/<featureName>.usecase.ts
1756
1734
  */
1757
1735
  static async createUseCase(featureName, useCaseDir) {
1758
1736
  const useCaseName = `${this.capitalize(featureName)}UseCase`;
1759
1737
  const filePath = import_path2.default.join(useCaseDir, `${featureName}.usecase.ts`);
1760
1738
  UFsModule.createFile(filePath, generateUseCaseTemplate(useCaseName, featureName));
1761
- console.log(import_ansi_colors6.default.green(`\u2705 UseCase created: ${filePath}`));
1739
+ console.log(import_ansi_colors5.default.green(`\u2705 UseCase created: ${filePath}`));
1762
1740
  }
1763
- // =========================================================================
1764
- // INFRASTRUCTURE LAYER
1765
- // =========================================================================
1766
1741
  /**
1767
1742
  * Creates the concrete repository implementation.
1768
- * File: infrastructure/adapters/repositories/<featureName>.repository.ts
1769
1743
  */
1770
1744
  static async createRepositoryImpl(featureName, repositoryImplDir) {
1771
1745
  const interfaceImportPath = `../../../application/ports/repositories/${featureName}.repository.interface`;
1772
1746
  const filePath = import_path2.default.join(repositoryImplDir, `${featureName}.repository.ts`);
1773
1747
  UFsModule.createFile(filePath, generateRepositoryImplTemplate(featureName, interfaceImportPath));
1774
- console.log(import_ansi_colors6.default.green(`\u2705 Repository implementation created: ${filePath}`));
1748
+ console.log(import_ansi_colors5.default.green(`\u2705 Repository implementation created: ${filePath}`));
1775
1749
  }
1776
1750
  /**
1777
1751
  * Creates the concrete presenter implementation.
1778
- * File: infrastructure/adapters/presenters/<featureName>.presenter.ts
1779
1752
  */
1780
1753
  static async createPresenterImpl(featureName, presenterImplDir) {
1781
1754
  const filePath = import_path2.default.join(presenterImplDir, `${featureName}.presenter.ts`);
1782
1755
  UFsModule.createFile(filePath, generatePresenterImplTemplate(featureName));
1783
- console.log(import_ansi_colors6.default.green(`\u2705 Presenter implementation created: ${filePath}`));
1756
+ console.log(import_ansi_colors5.default.green(`\u2705 Presenter implementation created: ${filePath}`));
1784
1757
  }
1785
1758
  /**
1786
1759
  * Creates the HTTP controller.
1787
1760
  * Prompts the user for method names, then generates the file.
1788
- * File: infrastructure/adapters/controllers/<featureName>.controller.ts
1789
1761
  *
1790
1762
  * @returns The controller class name and the list of chosen methods.
1791
1763
  */
@@ -1810,39 +1782,35 @@ export class ${controllerName} {}
1810
1782
  `;
1811
1783
  }
1812
1784
  UFsModule.createFile(filePath, content);
1813
- console.log(import_ansi_colors6.default.green(`\u2705 Controller created: ${filePath}`));
1785
+ console.log(import_ansi_colors5.default.green(`\u2705 Controller created: ${filePath}`));
1814
1786
  return { controllerName, methods };
1815
1787
  }
1816
1788
  /**
1817
1789
  * Creates the data model file (simple component).
1818
- * File: models/<featureName>.model.ts
1819
1790
  */
1820
1791
  static async createModel(featureName, modelDir) {
1821
1792
  const filePath = import_path2.default.join(modelDir, `${featureName}.model.ts`);
1822
1793
  UFsModule.createFile(filePath, generateModelTemplate(featureName));
1823
- console.log(import_ansi_colors6.default.green(`\u2705 Model created: ${filePath}`));
1794
+ console.log(import_ansi_colors5.default.green(`\u2705 Model created: ${filePath}`));
1824
1795
  }
1825
1796
  /**
1826
1797
  * Creates the simple repository (no interface, uses the model directly).
1827
- * File: repositories/<featureName>.repository.ts
1828
1798
  */
1829
1799
  static async createSimpleRepository(featureName, repositoryDir) {
1830
1800
  const filePath = import_path2.default.join(repositoryDir, `${featureName}.repository.ts`);
1831
1801
  UFsModule.createFile(filePath, generateSimpleRepositoryTemplate(featureName));
1832
- console.log(import_ansi_colors6.default.green(`\u2705 Repository created: ${filePath}`));
1802
+ console.log(import_ansi_colors5.default.green(`\u2705 Repository created: ${filePath}`));
1833
1803
  }
1834
1804
  /**
1835
1805
  * Creates the simple concrete service (no interface, wires the repository).
1836
- * File: services/<featureName>.service.ts
1837
1806
  */
1838
1807
  static async createSimpleService(featureName, serviceDir) {
1839
1808
  const filePath = import_path2.default.join(serviceDir, `${featureName}.service.ts`);
1840
1809
  UFsModule.createFile(filePath, generateSimpleServiceTemplate(featureName));
1841
- console.log(import_ansi_colors6.default.green(`\u2705 Service created: ${filePath}`));
1810
+ console.log(import_ansi_colors5.default.green(`\u2705 Service created: ${filePath}`));
1842
1811
  }
1843
1812
  /**
1844
1813
  * Creates the simple controller (delegates to service, no use-case/presenter).
1845
- * File: controllers/<featureName>.controller.ts
1846
1814
  */
1847
1815
  static async createSimpleController(featureName, controllerDir) {
1848
1816
  const controllerName = `${this.capitalize(featureName)}Controller`;
@@ -1865,31 +1833,29 @@ export class ${controllerName} {}
1865
1833
  `;
1866
1834
  }
1867
1835
  UFsModule.createFile(filePath, content);
1868
- console.log(import_ansi_colors6.default.green(`\u2705 Controller created: ${filePath}`));
1836
+ console.log(import_ansi_colors5.default.green(`\u2705 Controller created: ${filePath}`));
1869
1837
  return { controllerName, methods };
1870
1838
  }
1871
1839
  /**
1872
1840
  * Creates the route handler (HTTP verb + path → controller method bindings).
1873
- * File: infrastructure/routes/<featureName>.router.handler.ts (clean)
1874
- * routes/<featureName>.router.handler.ts (simple)
1841
+ *
1875
1842
  */
1876
1843
  static async createRouterHandler(featureName, routerHandlerDir, controllerMethods) {
1877
1844
  const filePath = import_path2.default.join(routerHandlerDir, `${featureName}.router.handler.ts`);
1878
1845
  const content = this.structure === "simple" ? generateSimpleRouterHandlerTemplate(featureName, controllerMethods) : generateRouterHandlerTemplate(featureName, controllerMethods);
1879
1846
  UFsModule.createFile(filePath, content);
1880
- console.log(import_ansi_colors6.default.green(`\u2705 Router Handler created: ${filePath}`));
1847
+ console.log(import_ansi_colors5.default.green(`\u2705 Router Handler created: ${filePath}`));
1881
1848
  }
1882
1849
  /**
1883
1850
  * Creates the feature router and registers it in register.router.ts.
1884
- * File: infrastructure/routes/<featureName>.router.ts (clean)
1885
- * routes/<featureName>.router.ts (simple)
1851
+ *
1886
1852
  */
1887
1853
  static async createRouter(featureName, routerDir) {
1888
1854
  const filePath = import_path2.default.join(routerDir, `${featureName}.router.ts`);
1889
1855
  const content = this.structure === "simple" ? generateSimpleRouterTemplate(featureName) : generateRouterTemplate(featureName);
1890
1856
  UFsModule.createFile(filePath, content);
1891
1857
  await this.updateRegisterRouter(featureName);
1892
- console.log(import_ansi_colors6.default.green(`\u2705 Router created: ${filePath}`));
1858
+ console.log(import_ansi_colors5.default.green(`\u2705 Router created: ${filePath}`));
1893
1859
  }
1894
1860
  static capitalize(str) {
1895
1861
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -1899,18 +1865,18 @@ export class ${controllerName} {}
1899
1865
  */
1900
1866
  static async updateRegisterRouter(featureName) {
1901
1867
  const routerName = `${this.capitalize(featureName)}Router`;
1902
- const routerPath = this.structure === "simple" ? `../../features/${featureName}/routes/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1868
+ const routerPath = this.structure === "simple" ? `../../features/${featureName}/routes/${featureName}.router` : this.structure === "custom" ? `../../features/${featureName}/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1903
1869
  const importStatement = `import { ${routerName} } from "${routerPath}"`;
1904
1870
  try {
1905
1871
  if (!import_fs2.default.existsSync(this.REGISTER_ROUTER_PATH)) {
1906
1872
  console.log(
1907
- import_ansi_colors6.default.yellow(`\u26A0\uFE0F register.router.ts not found at ${this.REGISTER_ROUTER_PATH}, skipping auto-registration.`)
1873
+ import_ansi_colors5.default.yellow(`\u26A0\uFE0F register.router.ts not found at ${this.REGISTER_ROUTER_PATH}, skipping auto-registration.`)
1908
1874
  );
1909
1875
  return;
1910
1876
  }
1911
1877
  let content = import_fs2.default.readFileSync(this.REGISTER_ROUTER_PATH, "utf-8");
1912
1878
  if (content.includes(`import { ${routerName} } from`)) {
1913
- console.log(import_ansi_colors6.default.cyan(`\u2139\uFE0F ${routerName} is already registered.`));
1879
+ console.log(import_ansi_colors5.default.cyan(`\u2139\uFE0F ${routerName} is already registered.`));
1914
1880
  return;
1915
1881
  }
1916
1882
  const lines = content.split("\n");
@@ -1928,12 +1894,12 @@ export class ${controllerName} {}
1928
1894
  const ARRAY_CLOSE = "]);";
1929
1895
  const startIdx = content.indexOf(ARRAY_OPEN);
1930
1896
  if (startIdx === -1) {
1931
- console.log(import_ansi_colors6.default.yellow(`\u26A0\uFE0F Could not find 'registered([' in register.router.ts.`));
1897
+ console.log(import_ansi_colors5.default.yellow(`\u26A0\uFE0F Could not find 'registered([' in register.router.ts.`));
1932
1898
  return;
1933
1899
  }
1934
1900
  const endIdx = content.indexOf(ARRAY_CLOSE, startIdx + ARRAY_OPEN.length);
1935
1901
  if (endIdx === -1) {
1936
- console.log(import_ansi_colors6.default.yellow(`\u26A0\uFE0F Could not find matching ']);' in register.router.ts.`));
1902
+ console.log(import_ansi_colors5.default.yellow(`\u26A0\uFE0F Could not find matching ']);' in register.router.ts.`));
1937
1903
  return;
1938
1904
  }
1939
1905
  const arrayContent = content.substring(startIdx + ARRAY_OPEN.length, endIdx);
@@ -1946,9 +1912,9 @@ export class ${controllerName} {}
1946
1912
  ${indentedRoutes}
1947
1913
  ` + content.substring(endIdx);
1948
1914
  import_fs2.default.writeFileSync(this.REGISTER_ROUTER_PATH, newContent, "utf-8");
1949
- console.log(import_ansi_colors6.default.green(`\u2705 register.router.ts updated with ${routerName}.`));
1915
+ console.log(import_ansi_colors5.default.green(`\u2705 register.router.ts updated with ${routerName}.`));
1950
1916
  } catch (error) {
1951
- console.error(import_ansi_colors6.default.red(`\u274C Failed to update register.router.ts: ${error.message}`));
1917
+ console.error(import_ansi_colors5.default.red(`\u274C Failed to update register.router.ts: ${error.message}`));
1952
1918
  }
1953
1919
  }
1954
1920
  };
@@ -1956,13 +1922,13 @@ ${indentedRoutes}
1956
1922
  });
1957
1923
 
1958
1924
  // src/templates/simpleComponent/simpleComponent.template.ts
1959
- var import_path3, import_ansi_colors7, simpleComponentTemplate;
1925
+ var import_path3, import_ansi_colors6, simpleComponentTemplate;
1960
1926
  var init_simpleComponent_template = __esm({
1961
1927
  "src/templates/simpleComponent/simpleComponent.template.ts"() {
1962
1928
  "use strict";
1963
1929
  init_cjs_shims();
1964
1930
  import_path3 = __toESM(require("path"), 1);
1965
- import_ansi_colors7 = __toESM(require("ansi-colors"), 1);
1931
+ import_ansi_colors6 = __toESM(require("ansi-colors"), 1);
1966
1932
  init_fsModule_utils();
1967
1933
  init_featureComponentGenerator_utils();
1968
1934
  simpleComponentTemplate = async (featureName) => {
@@ -1993,8 +1959,8 @@ var init_simpleComponent_template = __esm({
1993
1959
  await FeatureComponentGeneratorUtils.createRouterHandler(featureName, paths.routesDir, controller.methods);
1994
1960
  await FeatureComponentGeneratorUtils.createRouter(featureName, paths.routesDir);
1995
1961
  console.log(
1996
- import_ansi_colors7.default.bgGreen(
1997
- import_ansi_colors7.default.white(`
1962
+ import_ansi_colors6.default.bgGreen(
1963
+ import_ansi_colors6.default.white(`
1998
1964
  \u{1F389} Feature "${featureName}" scaffolded with Simple Component!
1999
1965
  `)
2000
1966
  )
@@ -2004,29 +1970,26 @@ var init_simpleComponent_template = __esm({
2004
1970
  });
2005
1971
 
2006
1972
  // src/templates/fullCleanComponent/fullCleanComponent.template.ts
2007
- var import_path4, import_ansi_colors8, fullCleanComponentTemplate;
1973
+ var import_path4, import_ansi_colors7, fullCleanComponentTemplate;
2008
1974
  var init_fullCleanComponent_template = __esm({
2009
1975
  "src/templates/fullCleanComponent/fullCleanComponent.template.ts"() {
2010
1976
  "use strict";
2011
1977
  init_cjs_shims();
2012
1978
  import_path4 = __toESM(require("path"), 1);
2013
- import_ansi_colors8 = __toESM(require("ansi-colors"), 1);
1979
+ import_ansi_colors7 = __toESM(require("ansi-colors"), 1);
2014
1980
  init_fsModule_utils();
2015
1981
  init_featureComponentGenerator_utils();
2016
1982
  fullCleanComponentTemplate = async (featureName) => {
2017
1983
  const basePath = import_path4.default.join("src", "features", featureName);
2018
1984
  const dirs = [
2019
- // Domain
2020
1985
  import_path4.default.join(basePath, "domain", "entities"),
2021
1986
  import_path4.default.join(basePath, "domain", "events"),
2022
1987
  import_path4.default.join(basePath, "domain", "exceptions"),
2023
- // Application
2024
1988
  import_path4.default.join(basePath, "application", "use-cases"),
2025
1989
  import_path4.default.join(basePath, "application", "dtos"),
2026
1990
  import_path4.default.join(basePath, "application", "ports", "repositories"),
2027
1991
  import_path4.default.join(basePath, "application", "ports", "presenters"),
2028
1992
  import_path4.default.join(basePath, "application", "ports", "services"),
2029
- // Infrastructure
2030
1993
  import_path4.default.join(basePath, "infrastructure", "adapters", "controllers"),
2031
1994
  import_path4.default.join(basePath, "infrastructure", "adapters", "repositories"),
2032
1995
  import_path4.default.join(basePath, "infrastructure", "adapters", "presenters"),
@@ -2034,17 +1997,14 @@ var init_fullCleanComponent_template = __esm({
2034
1997
  ];
2035
1998
  dirs.forEach((dir) => UFsModule.createDirectoryRecursively(dir));
2036
1999
  const paths = {
2037
- // Domain
2038
2000
  entityDir: import_path4.default.join(basePath, "domain", "entities"),
2039
2001
  eventDir: import_path4.default.join(basePath, "domain", "events"),
2040
2002
  exceptionDir: import_path4.default.join(basePath, "domain", "exceptions"),
2041
- // Application
2042
2003
  repositoryInterfaceDir: import_path4.default.join(basePath, "application", "ports", "repositories"),
2043
2004
  presenterInterfaceDir: import_path4.default.join(basePath, "application", "ports", "presenters"),
2044
2005
  serviceDir: import_path4.default.join(basePath, "application", "ports", "services"),
2045
2006
  dtoDir: import_path4.default.join(basePath, "application", "dtos"),
2046
2007
  useCaseDir: import_path4.default.join(basePath, "application", "use-cases"),
2047
- // Infrastructure
2048
2008
  repositoryImplDir: import_path4.default.join(basePath, "infrastructure", "adapters", "repositories"),
2049
2009
  presenterImplDir: import_path4.default.join(basePath, "infrastructure", "adapters", "presenters"),
2050
2010
  controllerDir: import_path4.default.join(basePath, "infrastructure", "adapters", "controllers"),
@@ -2067,8 +2027,8 @@ var init_fullCleanComponent_template = __esm({
2067
2027
  await FeatureComponentGeneratorUtils.createRouterHandler(featureName, paths.routerDir, controller.methods);
2068
2028
  await FeatureComponentGeneratorUtils.createRouter(featureName, paths.routerDir);
2069
2029
  console.log(
2070
- import_ansi_colors8.default.bgGreen(
2071
- import_ansi_colors8.default.white(`
2030
+ import_ansi_colors7.default.bgGreen(
2031
+ import_ansi_colors7.default.white(`
2072
2032
  \u{1F389} Feature "${featureName}" scaffolded with Clean Architecture!
2073
2033
  `)
2074
2034
  )
@@ -2078,19 +2038,21 @@ var init_fullCleanComponent_template = __esm({
2078
2038
  });
2079
2039
 
2080
2040
  // src/templates/cleanComponentByStep/cleanComponentByStep.template.ts
2081
- var import_path5, import_ansi_colors9, import_prompts3, cleanComponentByStepTemplate;
2041
+ var import_path5, import_ansi_colors8, import_prompts3, cleanComponentByStepTemplate;
2082
2042
  var init_cleanComponentByStep_template = __esm({
2083
2043
  "src/templates/cleanComponentByStep/cleanComponentByStep.template.ts"() {
2084
2044
  "use strict";
2085
2045
  init_cjs_shims();
2086
2046
  import_path5 = __toESM(require("path"), 1);
2087
- import_ansi_colors9 = __toESM(require("ansi-colors"), 1);
2047
+ import_ansi_colors8 = __toESM(require("ansi-colors"), 1);
2088
2048
  import_prompts3 = require("@clack/prompts");
2089
2049
  init_fsModule_utils();
2090
2050
  init_prompt_utils();
2051
+ init_featureComponentGenerator_utils();
2091
2052
  cleanComponentByStepTemplate = async (featureName) => {
2092
2053
  const base = import_path5.default.join("src", "features", featureName);
2093
2054
  let created = 0;
2055
+ let controller = { controllerName: "", methods: [] };
2094
2056
  const ask = async (label) => {
2095
2057
  const answer = await UPrompt.confirm(label, false);
2096
2058
  if ((0, import_prompts3.isCancel)(answer)) {
@@ -2099,48 +2061,74 @@ var init_cleanComponentByStep_template = __esm({
2099
2061
  }
2100
2062
  return answer;
2101
2063
  };
2102
- const write = (dir, filename) => {
2103
- UFsModule.createDirectoryRecursively(dir);
2104
- const filePath = import_path5.default.join(dir, filename);
2105
- UFsModule.createFile(filePath, "");
2106
- console.log(import_ansi_colors9.default.green(`\u2705 Created: ${filePath}`));
2064
+ const track = () => {
2107
2065
  created++;
2108
2066
  };
2109
- console.log(import_ansi_colors9.default.bold(import_ansi_colors9.default.cyan("\n\u2500\u2500 Domain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
2110
- if (await ask(`Entity \u2192 ${featureName}.entity.ts`))
2111
- write(import_path5.default.join(base, "domain", "entities"), `${featureName}.entity.ts`);
2112
- if (await ask(`Event \u2192 ${featureName}.event.ts`))
2113
- write(import_path5.default.join(base, "domain", "events"), `${featureName}.event.ts`);
2114
- if (await ask(`Exception \u2192 ${featureName}.exception.ts`))
2115
- write(import_path5.default.join(base, "domain", "exceptions"), `${featureName}.exception.ts`);
2116
- console.log(import_ansi_colors9.default.bold(import_ansi_colors9.default.cyan("\n\u2500\u2500 Application \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
2117
- if (await ask(`Repo Interface \u2192 ${featureName}.repository.interface.ts`))
2118
- write(import_path5.default.join(base, "application", "ports", "repositories"), `${featureName}.repository.interface.ts`);
2119
- if (await ask(`Presenter Port \u2192 ${featureName}.presenter.interface.ts`))
2120
- write(import_path5.default.join(base, "application", "ports", "presenters"), `${featureName}.presenter.interface.ts`);
2121
- if (await ask(`Service Port \u2192 ${featureName}.service.ts`))
2122
- write(import_path5.default.join(base, "application", "ports", "services"), `${featureName}.service.ts`);
2123
- if (await ask(`DTO \u2192 ${featureName}.dto.ts`))
2124
- write(import_path5.default.join(base, "application", "dtos"), `${featureName}.dto.ts`);
2125
- if (await ask(`Use Case \u2192 ${featureName}.usecase.ts`))
2126
- write(import_path5.default.join(base, "application", "use-cases"), `${featureName}.usecase.ts`);
2127
- console.log(import_ansi_colors9.default.bold(import_ansi_colors9.default.cyan("\n\u2500\u2500 Infrastructure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
2128
- if (await ask(`Repo Impl \u2192 ${featureName}.repository.ts`))
2129
- write(import_path5.default.join(base, "infrastructure", "adapters", "repositories"), `${featureName}.repository.ts`);
2130
- if (await ask(`Presenter Impl \u2192 ${featureName}.presenter.ts`))
2131
- write(import_path5.default.join(base, "infrastructure", "adapters", "presenters"), `${featureName}.presenter.ts`);
2132
- if (await ask(`Controller \u2192 ${featureName}.controller.ts`))
2133
- write(import_path5.default.join(base, "infrastructure", "adapters", "controllers"), `${featureName}.controller.ts`);
2134
- if (await ask(`Router Handler \u2192 ${featureName}.router.handler.ts`))
2135
- write(import_path5.default.join(base, "infrastructure", "routes"), `${featureName}.router.handler.ts`);
2136
- if (await ask(`Router \u2192 ${featureName}.router.ts`))
2137
- write(import_path5.default.join(base, "infrastructure", "routes"), `${featureName}.router.ts`);
2067
+ console.log(import_ansi_colors8.default.bold(import_ansi_colors8.default.cyan("\n\u2500\u2500 Domain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
2068
+ const mkDir = (dir) => {
2069
+ UFsModule.createDirectoryRecursively(dir);
2070
+ return dir;
2071
+ };
2072
+ if (await ask(`Entity - ${featureName}.entity.ts`)) {
2073
+ await FeatureComponentGeneratorUtils.createEntity(featureName, mkDir(import_path5.default.join(base, "domain", "entities")));
2074
+ track();
2075
+ }
2076
+ if (await ask(`Event - ${featureName}.event.ts`)) {
2077
+ await FeatureComponentGeneratorUtils.createEvent(featureName, mkDir(import_path5.default.join(base, "domain", "events")));
2078
+ track();
2079
+ }
2080
+ if (await ask(`Exception - ${featureName}.exception.ts`)) {
2081
+ await FeatureComponentGeneratorUtils.createException(featureName, mkDir(import_path5.default.join(base, "domain", "exceptions")));
2082
+ track();
2083
+ }
2084
+ console.log(import_ansi_colors8.default.bold(import_ansi_colors8.default.cyan("\n\u2500\u2500 Application \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
2085
+ if (await ask(`Repo Interface - ${featureName}.repository.interface.ts`)) {
2086
+ await FeatureComponentGeneratorUtils.createRepositoryInterface(featureName, mkDir(import_path5.default.join(base, "application", "ports", "repositories")));
2087
+ track();
2088
+ }
2089
+ if (await ask(`Presenter Port - ${featureName}.presenter.interface.ts`)) {
2090
+ await FeatureComponentGeneratorUtils.createPresenterInterface(featureName, mkDir(import_path5.default.join(base, "application", "ports", "presenters")));
2091
+ track();
2092
+ }
2093
+ if (await ask(`Service Port - ${featureName}.service.ts`)) {
2094
+ await FeatureComponentGeneratorUtils.createService(featureName, mkDir(import_path5.default.join(base, "application", "ports", "services")));
2095
+ track();
2096
+ }
2097
+ if (await ask(`DTO - ${featureName}.dto.ts`)) {
2098
+ await FeatureComponentGeneratorUtils.createDto(featureName, mkDir(import_path5.default.join(base, "application", "dtos")));
2099
+ track();
2100
+ }
2101
+ if (await ask(`Use Case - ${featureName}.usecase.ts`)) {
2102
+ await FeatureComponentGeneratorUtils.createUseCase(featureName, mkDir(import_path5.default.join(base, "application", "use-cases")));
2103
+ track();
2104
+ }
2105
+ console.log(import_ansi_colors8.default.bold(import_ansi_colors8.default.cyan("\n\u2500\u2500 Infrastructure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
2106
+ if (await ask(`Repo Impl - ${featureName}.repository.ts`)) {
2107
+ await FeatureComponentGeneratorUtils.createRepositoryImpl(featureName, mkDir(import_path5.default.join(base, "infrastructure", "adapters", "repositories")));
2108
+ track();
2109
+ }
2110
+ if (await ask(`Presenter Impl - ${featureName}.presenter.ts`)) {
2111
+ await FeatureComponentGeneratorUtils.createPresenterImpl(featureName, mkDir(import_path5.default.join(base, "infrastructure", "adapters", "presenters")));
2112
+ track();
2113
+ }
2114
+ if (await ask(`Controller - ${featureName}.controller.ts`)) {
2115
+ controller = await FeatureComponentGeneratorUtils.createController(featureName, mkDir(import_path5.default.join(base, "infrastructure", "adapters", "controllers")));
2116
+ track();
2117
+ }
2118
+ if (await ask(`Router Handler - ${featureName}.router.handler.ts`)) {
2119
+ await FeatureComponentGeneratorUtils.createRouterHandler(featureName, mkDir(import_path5.default.join(base, "infrastructure", "routes")), controller.methods);
2120
+ track();
2121
+ }
2122
+ if (await ask(`Router - ${featureName}.router.ts`)) {
2123
+ await FeatureComponentGeneratorUtils.createRouter(featureName, mkDir(import_path5.default.join(base, "infrastructure", "routes")));
2124
+ track();
2125
+ }
2138
2126
  if (created === 0) {
2139
- console.log(import_ansi_colors9.default.yellow(`
2127
+ console.log(import_ansi_colors8.default.yellow(`
2140
2128
  \u26A0\uFE0F No files were created for feature "${featureName}".`));
2141
2129
  } else {
2142
2130
  console.log(
2143
- import_ansi_colors9.default.bgGreen(import_ansi_colors9.default.white(`
2131
+ import_ansi_colors8.default.bgGreen(import_ansi_colors8.default.white(`
2144
2132
  \u{1F389} Feature "${featureName}" \u2014 ${created} file(s) created step by step.
2145
2133
  `))
2146
2134
  );
@@ -2191,36 +2179,443 @@ var init_createController_application = __esm({
2191
2179
  }
2192
2180
  });
2193
2181
 
2182
+ // src/core/domains/constants/resolveRoute.constant.ts
2183
+ var CResolveRoute;
2184
+ var init_resolveRoute_constant = __esm({
2185
+ "src/core/domains/constants/resolveRoute.constant.ts"() {
2186
+ "use strict";
2187
+ init_cjs_shims();
2188
+ CResolveRoute = (methodName, moduleName) => {
2189
+ const lower = methodName.toLowerCase();
2190
+ if (lower.includes("findall") || lower.includes("getall")) return { httpMethod: "get", path: `/${moduleName}` };
2191
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return { httpMethod: "get", path: `/${moduleName}/:id` };
2192
+ if (lower.includes("create") || lower.includes("add")) return { httpMethod: "post", path: `/${moduleName}` };
2193
+ if (lower.includes("update") || lower.includes("edit")) return { httpMethod: "put", path: `/${moduleName}/:id` };
2194
+ if (lower.includes("delete") || lower.includes("remove")) return { httpMethod: "delete", path: `/${moduleName}/:id` };
2195
+ return { httpMethod: "get", path: `/${moduleName}/${lower}` };
2196
+ };
2197
+ }
2198
+ });
2199
+
2200
+ // src/templates/customModule/customModule.template.ts
2201
+ var capitalize17, generateCustomControllerTemplate, generateMethod3, methodFindAll, methodFindById, methodCreate, methodUpdate, methodDelete, generateCustomServiceTemplate, generateCustomRepositoryTemplate, generateCustomModelTemplate, generateCustomTypesTemplate, generateCustomMiddlewareTemplate, generateCustomUtilsTemplate, generateCustomErrorsTemplate, generateCustomConstantsTemplate, generateCustomRouterHandlerTemplate, generateCustomRouterTemplate;
2202
+ var init_customModule_template = __esm({
2203
+ "src/templates/customModule/customModule.template.ts"() {
2204
+ "use strict";
2205
+ init_cjs_shims();
2206
+ init_resolveRoute_constant();
2207
+ capitalize17 = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2208
+ generateCustomControllerTemplate = (controllerName, moduleName, methods) => {
2209
+ const cap = capitalize17(moduleName);
2210
+ const serviceName = `${cap}Service`;
2211
+ const body = methods.length === 0 ? ` // TODO: add your handler methods here` : methods.map((m) => generateMethod3(m, cap, serviceName)).join("\n");
2212
+ const serviceImport = methods.length === 0 ? `` : `import { ${serviceName} } from "./${moduleName}.service";
2213
+ `;
2214
+ return `import { Request, Response } from "opticore-express";
2215
+ import { ResponseHandler, HttpStatusCode, IResponseHandlerSuccessData } from "opticore-http-response";
2216
+ ${serviceImport}
2217
+ export class ${controllerName} {${methods.length > 0 ? `
2218
+ private static buildService(): ${serviceName} {
2219
+ return new ${serviceName}();
2220
+ }
2221
+ ` : ""}
2222
+ ${body}
2223
+ private static handleError(error: unknown) {
2224
+ const message = error instanceof Error ? error.message : "Internal server error";
2225
+ return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
2226
+ }
2227
+ }`.trimEnd();
2228
+ };
2229
+ generateMethod3 = (method, cap, serviceName) => {
2230
+ const lower = method.toLowerCase();
2231
+ if (lower.includes("findall") || lower.includes("getall")) return methodFindAll(method, cap);
2232
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return methodFindById(method, cap);
2233
+ if (lower.includes("create") || lower.includes("add")) return methodCreate(method, cap);
2234
+ if (lower.includes("update") || lower.includes("edit")) return methodUpdate(method, cap);
2235
+ if (lower.includes("delete") || lower.includes("remove")) return methodDelete(method, cap);
2236
+ return `
2237
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2238
+ try {
2239
+ const service = ${cap}Controller.buildService();
2240
+ // TODO: implement ${method}
2241
+ return ResponseHandler.success({}, "success", HttpStatusCode.OK);
2242
+ } catch (error) {
2243
+ return ${cap}Controller.handleError(error);
2244
+ }
2245
+ }`;
2246
+ };
2247
+ methodFindAll = (m, cap) => `
2248
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2249
+ try {
2250
+ const service = ${cap}Controller.buildService();
2251
+ const results = await service.findAll();
2252
+ return ResponseHandler.success(results, "success", HttpStatusCode.OK);
2253
+ } catch (error) {
2254
+ return ${cap}Controller.handleError(error);
2255
+ }
2256
+ }`;
2257
+ methodFindById = (m, cap) => `
2258
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2259
+ try {
2260
+ const { id } = req.params;
2261
+ const service = ${cap}Controller.buildService();
2262
+ const result = await service.findById(id);
2263
+ if (!result) return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2264
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
2265
+ } catch (error) {
2266
+ return ${cap}Controller.handleError(error);
2267
+ }
2268
+ }`;
2269
+ methodCreate = (m, cap) => `
2270
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2271
+ try {
2272
+ const service = ${cap}Controller.buildService();
2273
+ const result = await service.create(req.body);
2274
+ return ResponseHandler.success(result, "created", HttpStatusCode.CREATED);
2275
+ } catch (error) {
2276
+ return ${cap}Controller.handleError(error);
2277
+ }
2278
+ }`;
2279
+ methodUpdate = (m, cap) => `
2280
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2281
+ try {
2282
+ const { id } = req.params;
2283
+ const service = ${cap}Controller.buildService();
2284
+ const result = await service.update(id, req.body);
2285
+ if (!result) return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2286
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
2287
+ } catch (error) {
2288
+ return ${cap}Controller.handleError(error);
2289
+ }
2290
+ }`;
2291
+ methodDelete = (m, cap) => `
2292
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2293
+ try {
2294
+ const { id } = req.params;
2295
+ const service = ${cap}Controller.buildService();
2296
+ const deleted = await service.delete(id);
2297
+ if (!deleted) return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2298
+ return ResponseHandler.success(null, "deleted", HttpStatusCode.NO_CONTENT);
2299
+ } catch (error) {
2300
+ return ${cap}Controller.handleError(error);
2301
+ }
2302
+ }`;
2303
+ generateCustomServiceTemplate = (moduleName) => {
2304
+ const cap = capitalize17(moduleName);
2305
+ return `export class ${cap}Service {
2306
+ async findAll(): Promise<unknown[]> {
2307
+ // TODO: implement
2308
+ return [];
2309
+ }
2310
+ async findById(id: string): Promise<unknown | null> {
2311
+ // TODO: implement
2312
+ return null;
2313
+ }
2314
+ async create(data: Record<string, unknown>): Promise<unknown> {
2315
+ // TODO: implement
2316
+ return data;
2317
+ }
2318
+ async update(id: string, data: Record<string, unknown>): Promise<unknown | null> {
2319
+ // TODO: implement
2320
+ return null;
2321
+ }
2322
+ async delete(id: string): Promise<boolean> {
2323
+ // TODO: implement
2324
+ return false;
2325
+ }
2326
+ }`.trimEnd();
2327
+ };
2328
+ generateCustomRepositoryTemplate = (moduleName) => {
2329
+ const cap = capitalize17(moduleName);
2330
+ const name = moduleName.toLowerCase();
2331
+ const isAuthRelated = name.includes("auth") || name.includes("authenticate") || name.includes("authentication") || name.includes("login") || name.includes("security") || name.includes("session") || name.includes("connect") || name.includes("connexion") || name.includes("connection");
2332
+ if (isAuthRelated) {
2333
+ return `export class ${cap}Repository {
2334
+ // TODO: inject your data source (ORM, DB client, etc.)
2335
+
2336
+ async findByEmail(email: string): Promise<User | null> {
2337
+ throw new Error("Not implemented");
2338
+ }
2339
+
2340
+ async findByUsernameOrEmail(identity: string): Promise<User | null> {
2341
+ throw new Error("Not implemented");
2342
+ }
2343
+
2344
+ async updateLastLogin(userId: string, date: Date): Promise<void> {
2345
+ throw new Error("Not implemented");
2346
+ }
2347
+
2348
+ async incrementLoginAttempts(userId: string): Promise<number> {
2349
+ throw new Error("Not implemented");
2350
+ }
2351
+
2352
+ async resetLoginAttempts(userId: string): Promise<void> {
2353
+ throw new Error("Not implemented");
2354
+ }
2355
+
2356
+ async saveToken(userId: string, token: string): Promise<void> {
2357
+ throw new Error("Not implemented");
2358
+ }
2359
+ }`.trimEnd();
2360
+ }
2361
+ return `export class ${cap}Repository {
2362
+ // TODO: inject your data source (ORM, DB client, etc.)
2363
+ async findAll(): Promise<unknown[]> { throw new Error("Not implemented"); }
2364
+ async findById(id: string): Promise<unknown | null> { throw new Error("Not implemented"); }
2365
+ async create(data: Record<string, unknown>): Promise<unknown> { throw new Error("Not implemented"); }
2366
+ async update(id: string, data: Record<string, unknown>): Promise<unknown | null> { throw new Error("Not implemented"); }
2367
+ async delete(id: string): Promise<boolean> { throw new Error("Not implemented"); }
2368
+ }`.trimEnd();
2369
+ };
2370
+ generateCustomModelTemplate = (moduleName) => {
2371
+ const cap = capitalize17(moduleName);
2372
+ return `export class ${cap}Model {
2373
+ constructor(
2374
+ public readonly id: string,
2375
+ // TODO: add your fields here
2376
+ ) {}
2377
+ }`.trimEnd();
2378
+ };
2379
+ generateCustomTypesTemplate = (moduleName) => `// ${moduleName} types
2380
+ export type ${capitalize17(moduleName)}Id = string;
2381
+ // TODO: add your domain types here`.trimEnd();
2382
+ generateCustomMiddlewareTemplate = (moduleName) => {
2383
+ const cap = capitalize17(moduleName);
2384
+ return `import { Request, Response, NextFunction } from "opticore-express";
2385
+
2386
+ export function ${moduleName}Middleware(req: Request, res: Response, next: NextFunction): void {
2387
+ // TODO: implement ${cap} middleware logic
2388
+ next();
2389
+ }`.trimEnd();
2390
+ };
2391
+ generateCustomUtilsTemplate = (moduleName) => `// ${moduleName} utilities
2392
+ // TODO: add helper functions here`.trimEnd();
2393
+ generateCustomErrorsTemplate = (moduleName) => {
2394
+ const cap = capitalize17(moduleName);
2395
+ return `export class ${cap}NotFoundError extends Error {
2396
+ constructor(id: string) {
2397
+ super(\`${cap} not found: \${id}\`);
2398
+ this.name = "${cap}NotFoundError";
2399
+ }
2400
+ }
2401
+ export class ${cap}ValidationError extends Error {
2402
+ constructor(message: string) {
2403
+ super(message);
2404
+ this.name = "${cap}ValidationError";
2405
+ }
2406
+ }`.trimEnd();
2407
+ };
2408
+ generateCustomConstantsTemplate = (moduleName) => `// ${moduleName} constants
2409
+ export const ${moduleName.toUpperCase()}_PREFIX = "/${moduleName}";
2410
+ // TODO: add your constants here`.trimEnd();
2411
+ generateCustomRouterHandlerTemplate = (moduleName, controllerMethods) => {
2412
+ const cap = capitalize17(moduleName);
2413
+ const handlerName = `${cap}HandlerRouter`;
2414
+ const controllerName = `${cap}Controller`;
2415
+ const routesCode = (controllerMethods || []).map((method) => {
2416
+ const { httpMethod, path: path8 } = CResolveRoute(method, moduleName);
2417
+ return `{
2418
+ path: \`${path8}\`,
2419
+ method: "${httpMethod}",
2420
+ middlewares: [],
2421
+ handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
2422
+ }`;
2423
+ }).join(",\n");
2424
+ return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
2425
+ import { ${controllerName} } from "../../app/${moduleName}.controller";
2426
+
2427
+ export const ${handlerName}: () => IMultipleRouteDefinition = () => {
2428
+ return OpticoreRouting.routes(
2429
+ ${controllerName},
2430
+ [
2431
+ ${routesCode ? routesCode.replace(/\n/g, "\n ") : ""}
2432
+ ]
2433
+ );
2434
+ };`.trimEnd();
2435
+ };
2436
+ generateCustomRouterTemplate = (moduleName) => {
2437
+ const cap = capitalize17(moduleName);
2438
+ const handlerName = `${cap}HandlerRouter`;
2439
+ const routerName = `${cap}Router`;
2440
+ return `import { ${handlerName} } from "./core/handler/${moduleName}.router.handler";
2441
+ import { TFeatureRoutes } from "opticore-router";
2442
+
2443
+ export const ${routerName}: TFeatureRoutes = {
2444
+ routes: [
2445
+ {
2446
+ path: ${handlerName}().path,
2447
+ handler: ${handlerName}().handler
2448
+ }
2449
+ ]
2450
+ };`.trimEnd();
2451
+ };
2452
+ }
2453
+ });
2454
+
2455
+ // src/templates/customModule/customModuleScaffold.template.ts
2456
+ async function askFile(label) {
2457
+ const answer = await UPrompt.confirm(`Create ${label}?`, false);
2458
+ return answer === true;
2459
+ }
2460
+ async function askMethods() {
2461
+ const wantsMethods = await UPrompt.confirm("Do you want to add methods to the controller?");
2462
+ if (!wantsMethods) return [];
2463
+ const raw = await UPrompt.text(
2464
+ "Enter the method names (comma separated):",
2465
+ "create, findAll, findById, update, delete",
2466
+ (v) => v ? void 0 : "Enter at least one method name."
2467
+ );
2468
+ if ((0, import_prompts5.isCancel)(raw)) return [];
2469
+ return raw.split(",").map((m) => m.trim()).filter(Boolean);
2470
+ }
2471
+ var import_path6, import_fs3, import_ansi_colors9, import_prompts5, ok, tip, capitalize18, customModuleScaffoldTemplate;
2472
+ var init_customModuleScaffold_template = __esm({
2473
+ "src/templates/customModule/customModuleScaffold.template.ts"() {
2474
+ "use strict";
2475
+ init_cjs_shims();
2476
+ import_path6 = __toESM(require("path"), 1);
2477
+ import_fs3 = __toESM(require("fs"), 1);
2478
+ import_ansi_colors9 = __toESM(require("ansi-colors"), 1);
2479
+ import_prompts5 = require("@clack/prompts");
2480
+ init_fsModule_utils();
2481
+ init_prompt_utils();
2482
+ init_featureName_service();
2483
+ init_customModule_template();
2484
+ init_featureComponentGenerator_utils();
2485
+ ok = (msg) => {
2486
+ console.log(import_ansi_colors9.default.green(`\u2705 ${msg}`));
2487
+ };
2488
+ tip = (msg) => {
2489
+ console.log(import_ansi_colors9.default.yellow(` ${msg}`));
2490
+ };
2491
+ capitalize18 = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2492
+ customModuleScaffoldTemplate = async () => {
2493
+ const rawName = await _featureName();
2494
+ if ((0, import_prompts5.isCancel)(rawName)) {
2495
+ console.log(import_ansi_colors9.default.bgRed(import_ansi_colors9.default.white("Operation cancelled.")));
2496
+ process.exit(0);
2497
+ }
2498
+ const moduleName = rawName.toString();
2499
+ const moduleDir = import_path6.default.join(process.cwd(), "src", "features", moduleName);
2500
+ if (import_fs3.default.existsSync(moduleDir)) {
2501
+ console.log(import_ansi_colors9.default.bgCyan(import_ansi_colors9.default.white(`Module "${moduleName}" already exists.`)));
2502
+ process.exit(0);
2503
+ }
2504
+ const appDir = import_path6.default.join(moduleDir, "app");
2505
+ const coreMiddlewaresDir = import_path6.default.join(moduleDir, "core", "middlewares");
2506
+ const coreHandlerDir = import_path6.default.join(moduleDir, "core", "handler");
2507
+ const sharedUtilsDir = import_path6.default.join(moduleDir, "shared", "utils");
2508
+ const sharedErrorsDir = import_path6.default.join(moduleDir, "shared", "errors");
2509
+ const sharedConstantsDir = import_path6.default.join(moduleDir, "shared", "constants");
2510
+ UFsModule.createDirectoryRecursively(appDir);
2511
+ UFsModule.createDirectoryRecursively(coreHandlerDir);
2512
+ UFsModule.createDirectoryRecursively(sharedUtilsDir);
2513
+ UFsModule.createDirectoryRecursively(sharedErrorsDir);
2514
+ UFsModule.createDirectoryRecursively(sharedConstantsDir);
2515
+ let controllerMethods = [];
2516
+ if (await askFile(`${moduleName}.controller.ts`)) {
2517
+ controllerMethods = await askMethods();
2518
+ UFsModule.createFile(
2519
+ import_path6.default.join(appDir, `${moduleName}.controller.ts`),
2520
+ generateCustomControllerTemplate(`${capitalize18(moduleName)}Controller`, moduleName, controllerMethods)
2521
+ );
2522
+ ok(`app/${moduleName}.controller.ts`);
2523
+ }
2524
+ if (await askFile(`${moduleName}.service.ts`)) {
2525
+ UFsModule.createFile(import_path6.default.join(appDir, `${moduleName}.service.ts`), generateCustomServiceTemplate(moduleName));
2526
+ ok(`app/${moduleName}.service.ts`);
2527
+ }
2528
+ if (await askFile(`${moduleName}.repository.ts`)) {
2529
+ UFsModule.createFile(import_path6.default.join(appDir, `${moduleName}.repository.ts`), generateCustomRepositoryTemplate(moduleName));
2530
+ ok(`app/${moduleName}.repository.ts`);
2531
+ }
2532
+ if (await askFile(`${moduleName}.model.ts`)) {
2533
+ UFsModule.createFile(import_path6.default.join(appDir, `${moduleName}.model.ts`), generateCustomModelTemplate(moduleName));
2534
+ ok(`app/${moduleName}.model.ts`);
2535
+ }
2536
+ if (await askFile(`${moduleName}.types.ts`)) {
2537
+ UFsModule.createFile(import_path6.default.join(appDir, `${moduleName}.types.ts`), generateCustomTypesTemplate(moduleName));
2538
+ ok(`app/${moduleName}.types.ts`);
2539
+ }
2540
+ const wantsMiddleware = await UPrompt.confirm("Does your module need a middleware?", false);
2541
+ if (wantsMiddleware === true) {
2542
+ UFsModule.createDirectoryRecursively(coreMiddlewaresDir);
2543
+ UFsModule.createFile(
2544
+ import_path6.default.join(coreMiddlewaresDir, `${moduleName}.middleware.ts`),
2545
+ generateCustomMiddlewareTemplate(moduleName)
2546
+ );
2547
+ ok(`core/middlewares/${moduleName}.middleware.ts`);
2548
+ }
2549
+ UFsModule.createFile(
2550
+ import_path6.default.join(coreHandlerDir, `${moduleName}.router.handler.ts`),
2551
+ generateCustomRouterHandlerTemplate(moduleName, controllerMethods)
2552
+ );
2553
+ ok(`core/handler/${moduleName}.router.handler.ts`);
2554
+ UFsModule.createFile(import_path6.default.join(sharedUtilsDir, `${moduleName}.utils.ts`), generateCustomUtilsTemplate(moduleName));
2555
+ UFsModule.createFile(import_path6.default.join(sharedErrorsDir, `${moduleName}.errors.ts`), generateCustomErrorsTemplate(moduleName));
2556
+ UFsModule.createFile(import_path6.default.join(sharedConstantsDir, `${moduleName}.constants.ts`), generateCustomConstantsTemplate(moduleName));
2557
+ ok(`shared/utils/${moduleName}.utils.ts`);
2558
+ ok(`shared/errors/${moduleName}.errors.ts`);
2559
+ ok(`shared/constants/${moduleName}.constants.ts`);
2560
+ UFsModule.createFile(import_path6.default.join(moduleDir, `${moduleName}.router.ts`), generateCustomRouterTemplate(moduleName));
2561
+ ok(`${moduleName}.router.ts`);
2562
+ FeatureComponentGeneratorUtils.setStructure("custom");
2563
+ await FeatureComponentGeneratorUtils.registerModuleRouter(moduleName);
2564
+ tip(`src/features/${moduleName}/`);
2565
+ tip(`\u251C\u2500\u2500 app/`);
2566
+ tip(`\u251C\u2500\u2500 core/`);
2567
+ tip(`\u2502 \u251C\u2500\u2500 middlewares/`);
2568
+ tip(`\u2502 \u2514\u2500\u2500 handler/`);
2569
+ tip(`\u251C\u2500\u2500 shared/`);
2570
+ tip(`\u2502 \u251C\u2500\u2500 utils/`);
2571
+ tip(`\u2502 \u251C\u2500\u2500 errors/`);
2572
+ tip(`\u2502 \u2514\u2500\u2500 constants/`);
2573
+ tip(`\u2514\u2500\u2500 ${moduleName}.router.ts`);
2574
+ console.log(`
2575
+ ${import_ansi_colors9.default.bgGreen(import_ansi_colors9.default.white(`
2576
+ \u{1F389} Custom module "${moduleName}" scaffolded successfully!
2577
+ `))}`);
2578
+ };
2579
+ }
2580
+ });
2581
+
2194
2582
  // src/core/core.ts
2195
2583
  var core_exports = {};
2196
- var import_process3, import_node_fs2, import_path6, import_prompts5;
2584
+ var import_process2, import_node_fs2, import_path7, import_prompts6;
2197
2585
  var init_core = __esm({
2198
2586
  "src/core/core.ts"() {
2199
2587
  "use strict";
2200
2588
  init_cjs_shims();
2201
- import_process3 = __toESM(require("process"), 1);
2589
+ import_process2 = __toESM(require("process"), 1);
2202
2590
  import_node_fs2 = __toESM(require("fs"), 1);
2203
- import_path6 = __toESM(require("path"), 1);
2204
- import_prompts5 = require("@clack/prompts");
2591
+ import_path7 = __toESM(require("path"), 1);
2592
+ import_prompts6 = require("@clack/prompts");
2205
2593
  init_welcomeMessage_utils();
2206
2594
  init_choiceFeatureCleanModule();
2207
- init_customFeatureInfoMessage_constant();
2208
2595
  init_customFeatureInfo_utils();
2209
2596
  init_featureName_service();
2210
2597
  init_base_service();
2211
2598
  init_createController_application();
2599
+ init_customModuleScaffold_template();
2212
2600
  (async () => {
2213
2601
  UWelcomeMessage();
2214
2602
  const featureChoice = await choiceFeatureCleanModule();
2215
- featureChoice !== "opticore_clean_module" || featureChoice === void 0 ? customFeatureInfo() : UCustomFeatureInfo(featureChoice);
2603
+ if (featureChoice === "custom_feature") {
2604
+ await customModuleScaffoldTemplate();
2605
+ return;
2606
+ }
2607
+ if (!featureChoice || featureChoice !== "opticore_clean_module") {
2608
+ import_process2.default.exit(0);
2609
+ }
2610
+ UCustomFeatureInfo(featureChoice);
2216
2611
  const featureName = await _featureName();
2217
- const directory = import_path6.default.join(import_process3.default.cwd(), "src", "features");
2218
- const featureDirectory = import_path6.default.join(directory, featureName.toString());
2612
+ const directory = import_path7.default.join(import_process2.default.cwd(), "src", "features");
2613
+ const featureDirectory = import_path7.default.join(directory, featureName.toString());
2219
2614
  const featureCleanModule = BaseService._isSubdirectoryExists(featureName);
2220
2615
  if (featureCleanModule) {
2221
2616
  BaseService._featureFounded(featureName);
2222
2617
  }
2223
- if ((0, import_prompts5.isCancel)(featureName)) {
2618
+ if ((0, import_prompts6.isCancel)(featureName)) {
2224
2619
  BaseService._cancelOperation(featureDirectory);
2225
2620
  } else {
2226
2621
  try {
@@ -2231,7 +2626,7 @@ var init_core = __esm({
2231
2626
  }
2232
2627
  } catch (err) {
2233
2628
  console.error(err.message);
2234
- import_process3.default.exit();
2629
+ import_process2.default.exit();
2235
2630
  }
2236
2631
  }
2237
2632
  })();