opticore-feature-component 1.0.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/index.cjs ADDED
@@ -0,0 +1,2243 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+
29
+ // node_modules/tsup/assets/cjs_shims.js
30
+ var init_cjs_shims = __esm({
31
+ "node_modules/tsup/assets/cjs_shims.js"() {
32
+ "use strict";
33
+ }
34
+ });
35
+
36
+ // src/utils/welcomeMessage.utils.ts
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
+ ));
41
+ }
42
+ var import_gradient_string;
43
+ var init_welcomeMessage_utils = __esm({
44
+ "src/utils/welcomeMessage.utils.ts"() {
45
+ "use strict";
46
+ init_cjs_shims();
47
+ import_gradient_string = __toESM(require("gradient-string"), 1);
48
+ }
49
+ });
50
+
51
+ // src/utils/fsModule.utils.ts
52
+ var import_fs, UFsModule;
53
+ var init_fsModule_utils = __esm({
54
+ "src/utils/fsModule.utils.ts"() {
55
+ "use strict";
56
+ init_cjs_shims();
57
+ import_fs = __toESM(require("fs"), 1);
58
+ UFsModule = class {
59
+ static createDirectoryRecursively(dirPath) {
60
+ try {
61
+ import_fs.default.mkdirSync(dirPath, { recursive: true });
62
+ } catch (e) {
63
+ console.error(e.message);
64
+ }
65
+ }
66
+ static createFile(filePath, fileContent) {
67
+ try {
68
+ import_fs.default.writeFileSync(filePath, fileContent);
69
+ } catch (e) {
70
+ console.error(e.message);
71
+ }
72
+ }
73
+ static removeDir(directory, recursive, force) {
74
+ try {
75
+ if (import_fs.default.existsSync(directory)) {
76
+ import_fs.default.rmSync(directory, { recursive, force });
77
+ }
78
+ } catch (e) {
79
+ console.error(`Error removing folder: ${e.message}`, e);
80
+ process.exit(0);
81
+ }
82
+ }
83
+ };
84
+ }
85
+ });
86
+
87
+ // src/utils/prompt.utils.ts
88
+ var import_prompts, import_ansi_colors, import_opticore_logger, import_opticore_http_response, UPrompt;
89
+ var init_prompt_utils = __esm({
90
+ "src/utils/prompt.utils.ts"() {
91
+ "use strict";
92
+ init_cjs_shims();
93
+ import_prompts = require("@clack/prompts");
94
+ import_ansi_colors = __toESM(require("ansi-colors"), 1);
95
+ init_fsModule_utils();
96
+ import_opticore_logger = require("opticore-logger");
97
+ import_opticore_http_response = require("opticore-http-response");
98
+ UPrompt = class {
99
+ static logger = new import_opticore_logger.LoggerCore();
100
+ static async text(message, placeholder, validate) {
101
+ return await (0, import_prompts.text)(
102
+ {
103
+ message,
104
+ placeholder,
105
+ validate
106
+ }
107
+ );
108
+ }
109
+ static async confirm(message, defaultValue = true) {
110
+ return await (0, import_prompts.confirm)({
111
+ message,
112
+ initialValue: defaultValue
113
+ });
114
+ }
115
+ static cancelOperation(controllerPath) {
116
+ console.log(`${import_ansi_colors.default.bgRed(`${import_ansi_colors.default.white("Operation cancelled.")}`)}`);
117
+ if (controllerPath) {
118
+ try {
119
+ UFsModule.removeDir(controllerPath, true, true);
120
+ this.logger.info({
121
+ title: "DIR_REMOVED",
122
+ message: `Removed controller directory: ${controllerPath}`
123
+ });
124
+ } catch (error) {
125
+ this.logger.error({
126
+ errorType: error.name,
127
+ httpCodeValue: import_opticore_http_response.HttpStatusCode.INTERNAL_SERVER_ERROR,
128
+ message: error.message,
129
+ stackTrace: error.stack,
130
+ title: "FAILED_TO_REMOVE_CONTROLLER_DIR"
131
+ });
132
+ }
133
+ }
134
+ process.exit(0);
135
+ }
136
+ static async select(message, defaultValue, options) {
137
+ let optSelected = await (0, import_prompts.select)({
138
+ message,
139
+ initialValue: defaultValue,
140
+ options
141
+ });
142
+ return optSelected[0];
143
+ }
144
+ };
145
+ }
146
+ });
147
+
148
+ // src/utils/choiceFeatureCleanModule.ts
149
+ async function choiceFeatureCleanModule() {
150
+ const featureSelected = await UPrompt.select(
151
+ "Please choose the creation principle for your feature :",
152
+ ["opticore_principle"],
153
+ [
154
+ { label: "OptiCoreJs CLEAN Module", value: ["opticore_clean_module"], hint: "recommended" },
155
+ { label: "Custom feature", value: ["custom_feature"] }
156
+ ]
157
+ );
158
+ if ((0, import_prompts2.isCancel)(featureSelected)) {
159
+ console.log(`${import_ansi_colors2.default.bgRed(`${import_ansi_colors2.default.white("Operation cancelled.")}`)}`);
160
+ process.exit(0);
161
+ } else {
162
+ return featureSelected;
163
+ }
164
+ }
165
+ var import_ansi_colors2, import_prompts2;
166
+ var init_choiceFeatureCleanModule = __esm({
167
+ "src/utils/choiceFeatureCleanModule.ts"() {
168
+ "use strict";
169
+ init_cjs_shims();
170
+ import_ansi_colors2 = __toESM(require("ansi-colors"), 1);
171
+ import_prompts2 = require("@clack/prompts");
172
+ init_prompt_utils();
173
+ }
174
+ });
175
+
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
+ // src/utils/customFeatureInfo.utils.ts
192
+ 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 !"));
207
+ }
208
+ var import_ansi_colors4;
209
+ var init_customFeatureInfo_utils = __esm({
210
+ "src/utils/customFeatureInfo.utils.ts"() {
211
+ "use strict";
212
+ init_cjs_shims();
213
+ import_ansi_colors4 = __toESM(require("ansi-colors"), 1);
214
+ }
215
+ });
216
+
217
+ // src/services/featureName.service.ts
218
+ var _featureName;
219
+ var init_featureName_service = __esm({
220
+ "src/services/featureName.service.ts"() {
221
+ "use strict";
222
+ init_cjs_shims();
223
+ init_prompt_utils();
224
+ _featureName = async () => {
225
+ return await UPrompt.text(
226
+ "Enter feature's name :",
227
+ "login",
228
+ (value) => {
229
+ let pattern = new RegExp("^[a-z][A-Za-z]+$");
230
+ if (!value) {
231
+ return "Please enter a feature's name.";
232
+ }
233
+ if (!pattern.test(value)) {
234
+ return "Please enter a valide feature's name.";
235
+ }
236
+ }
237
+ );
238
+ };
239
+ }
240
+ });
241
+
242
+ // src/services/base.service.ts
243
+ var import_path, import_node_fs, import_ansi_colors5, import_process2, BaseService;
244
+ var init_base_service = __esm({
245
+ "src/services/base.service.ts"() {
246
+ "use strict";
247
+ init_cjs_shims();
248
+ import_path = __toESM(require("path"), 1);
249
+ import_node_fs = __toESM(require("fs"), 1);
250
+ import_ansi_colors5 = __toESM(require("ansi-colors"), 1);
251
+ import_process2 = __toESM(require("process"), 1);
252
+ init_fsModule_utils();
253
+ BaseService = class {
254
+ static _isSubdirectoryExists(subdirectory) {
255
+ const subdirectoryString = typeof subdirectory === "symbol" ? subdirectory.toString() : String(subdirectory);
256
+ const subdirectoryPath = import_path.default.join(import_process2.default.cwd(), "src", "features", subdirectoryString);
257
+ try {
258
+ const stats = import_node_fs.default.statSync(subdirectoryPath);
259
+ return stats.isDirectory();
260
+ } catch (error) {
261
+ return false;
262
+ }
263
+ }
264
+ 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);
267
+ }
268
+ 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();
271
+ }
272
+ 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();
275
+ }
276
+ static _cancelOperation(controllerPath) {
277
+ console.log(`${import_ansi_colors5.default.bgRed(`${import_ansi_colors5.default.white("Operation cancelled.")}`)}`);
278
+ UFsModule.removeDir(controllerPath, true, true);
279
+ import_process2.default.exit(0);
280
+ }
281
+ static async _createFeatureModuleDir(featureDir, feature) {
282
+ let ora = (await import("ora")).default;
283
+ const spinner = ora("Feature's name creation...").start();
284
+ 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.
286
+ `);
287
+ return { featureDir, feature };
288
+ }
289
+ static async _createApplicationDirComponent(feature) {
290
+ let application = "";
291
+ application = `${feature}/application`;
292
+ UFsModule.createDirectoryRecursively(application);
293
+ return application;
294
+ }
295
+ static async _createDomainDirComponent(feature) {
296
+ let domain = "";
297
+ domain = `${feature}/domain`;
298
+ UFsModule.createDirectoryRecursively(domain);
299
+ return domain;
300
+ }
301
+ static async _createInfraDirComponent(feature) {
302
+ let infrastructure = "";
303
+ infrastructure = `${feature}/infrastructure`;
304
+ UFsModule.createDirectoryRecursively(infrastructure);
305
+ return infrastructure;
306
+ }
307
+ static async _createPersistenceDirComponent(feature) {
308
+ let persistence = "";
309
+ persistence = `${feature}/persistence`;
310
+ UFsModule.createDirectoryRecursively(persistence);
311
+ return persistence;
312
+ }
313
+ };
314
+ }
315
+ });
316
+
317
+ // src/services/controllerType.service.ts
318
+ async function _controllerType(messageText, defaultValue, options) {
319
+ return await UPrompt.select(
320
+ messageText,
321
+ [defaultValue],
322
+ options
323
+ );
324
+ }
325
+ var init_controllerType_service = __esm({
326
+ "src/services/controllerType.service.ts"() {
327
+ "use strict";
328
+ init_cjs_shims();
329
+ init_prompt_utils();
330
+ }
331
+ });
332
+
333
+ // src/core/domains/constants/cleanType.constant.ts
334
+ var CCleanType;
335
+ var init_cleanType_constant = __esm({
336
+ "src/core/domains/constants/cleanType.constant.ts"() {
337
+ "use strict";
338
+ init_cjs_shims();
339
+ CCleanType = {
340
+ "clean_component_by_step": "clean_component_by_step",
341
+ "full_clean_component": "full_clean_component",
342
+ "simple_component": "simple_component"
343
+ };
344
+ }
345
+ });
346
+
347
+ // src/templates/cleanComponents/controller.template.ts
348
+ var generateControllerTemplate, generateMethod, generateFindAllMethod, generateFindByIdMethod, generateCreateMethod, generateUpdateMethod, generateDeleteMethod, errorHandlerAppend, capitalize;
349
+ var init_controller_template = __esm({
350
+ "src/templates/cleanComponents/controller.template.ts"() {
351
+ "use strict";
352
+ init_cjs_shims();
353
+ generateControllerTemplate = (controllerName, featureName, methods) => {
354
+ const capitalized = capitalize(featureName);
355
+ const useCaseName = `${capitalized}UseCase`;
356
+ const presenterName = `${capitalized}Presenter`;
357
+ const methodsCode = methods.map((method) => generateMethod(method, featureName, capitalized)).join("\n");
358
+ const errorHandler = errorHandlerAppend(capitalized);
359
+ return `import { Request, Response } from "express";
360
+ import { ResponseHandler, HttpStatusCode, IResponseHandlerSuccessData } from "opticore-http-response";
361
+ import { ${useCaseName} } from "../../../application/use-cases/${featureName}.usecase";
362
+ import { ${presenterName} } from "../presenters/${featureName}.presenter";
363
+ import { ${capitalized}Repository } from "../repositories/${featureName}.repository";
364
+
365
+ /**
366
+ * ${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
+ */
375
+ export class ${controllerName} {
376
+ private static buildUseCase(): ${useCaseName} {
377
+ const repository = new ${capitalized}Repository();
378
+ return new ${useCaseName}(repository);
379
+ }
380
+
381
+ private static buildPresenter(): ${presenterName} {
382
+ return new ${presenterName}();
383
+ }
384
+
385
+ ${methodsCode}
386
+ ${errorHandler}
387
+ }
388
+ `;
389
+ };
390
+ generateMethod = (method, featureName, capitalized) => {
391
+ const lower = method.toLowerCase();
392
+ if (lower.includes("findall") || lower.includes("getall")) {
393
+ return generateFindAllMethod(method, featureName, capitalized);
394
+ }
395
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) {
396
+ return generateFindByIdMethod(method, featureName, capitalized);
397
+ }
398
+ if (lower.includes("create") || lower.includes("add")) {
399
+ return generateCreateMethod(method, featureName, capitalized);
400
+ }
401
+ if (lower.includes("update") || lower.includes("edit")) {
402
+ return generateUpdateMethod(method, featureName, capitalized);
403
+ }
404
+ if (lower.includes("delete") || lower.includes("remove")) {
405
+ return generateDeleteMethod(method, featureName, capitalized);
406
+ }
407
+ return `
408
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
409
+ try {
410
+ const useCase = ${capitalized}Controller.buildUseCase();
411
+ const presenter = ${capitalized}Controller.buildPresenter();
412
+ // TODO: Implement ${method} logic
413
+ // const result = await useCase.${method}(...);
414
+ return ResponseHandler.success(presenter.presentOne({} as any), "success", HttpStatusCode.OK);
415
+ } catch (error) {
416
+ return ${capitalized}Controller.handleError(error);
417
+ }
418
+ }
419
+ `;
420
+ };
421
+ generateFindAllMethod = (method, featureName, capitalized) => `
422
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
423
+ try {
424
+ const useCase = ${capitalized}Controller.buildUseCase();
425
+ const presenter = ${capitalized}Controller.buildPresenter();
426
+
427
+ const results = await useCase.findAll();
428
+ return ResponseHandler.success(presenter.presentMany(results), "success", HttpStatusCode.OK);
429
+ } catch (error) {
430
+ return ${capitalized}Controller.handleError(error);
431
+ }
432
+ }
433
+ `;
434
+ generateFindByIdMethod = (method, featureName, capitalized) => `
435
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
436
+ try {
437
+ const { id } = req.params;
438
+ const useCase = ${capitalized}Controller.buildUseCase();
439
+ const presenter = ${capitalized}Controller.buildPresenter();
440
+
441
+ const result = await useCase.findById(id);
442
+ if (!result) {
443
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
444
+ }
445
+ return ResponseHandler.success(presenter.presentOne(result), "success", HttpStatusCode.OK);
446
+ } catch (error) {
447
+ return ${capitalized}Controller.handleError(error);
448
+ }
449
+ }
450
+ `;
451
+ generateCreateMethod = (method, featureName, capitalized) => `
452
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
453
+ try {
454
+ const dto = req.body; // TODO: Validate with your validation layer
455
+ const useCase = ${capitalized}Controller.buildUseCase();
456
+ const presenter = ${capitalized}Controller.buildPresenter();
457
+
458
+ const result = await useCase.create(dto);
459
+ return ResponseHandler.success(presenter.presentOne(result), "created", HttpStatusCode.CREATED);
460
+ } catch (error) {
461
+ return ${capitalized}Controller.handleError(error);
462
+ }
463
+ }
464
+ `;
465
+ generateUpdateMethod = (method, featureName, capitalized) => `
466
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
467
+ try {
468
+ const { id } = req.params;
469
+ const dto = { ...req.body, id }; // TODO: Validate with your validation layer
470
+ const useCase = ${capitalized}Controller.buildUseCase();
471
+ const presenter = ${capitalized}Controller.buildPresenter();
472
+
473
+ const result = await useCase.update(dto);
474
+ if (!result) {
475
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
476
+ }
477
+ return ResponseHandler.success(presenter.presentOne(result), "success", HttpStatusCode.OK);
478
+ } catch (error) {
479
+ return ${capitalized}Controller.handleError(error);
480
+ }
481
+ }
482
+ `;
483
+ generateDeleteMethod = (method, featureName, capitalized) => `
484
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
485
+ try {
486
+ const { id } = req.params;
487
+ const useCase = ${capitalized}Controller.buildUseCase();
488
+
489
+ const deleted = await useCase.delete(id);
490
+ if (!deleted) {
491
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
492
+ }
493
+ return ResponseHandler.success(null, "deleted", HttpStatusCode.NO_CONTENT);
494
+ } catch (error) {
495
+ return ${capitalized}Controller.handleError(error);
496
+ }
497
+ }
498
+ `;
499
+ errorHandlerAppend = (capitalized) => `
500
+ private static handleError(error: unknown) {
501
+ const message = error instanceof Error ? error.message : "Internal server error";
502
+ return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
503
+ }
504
+ `;
505
+ capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
506
+ }
507
+ });
508
+
509
+ // src/templates/cleanComponents/entity.template.ts
510
+ var generateEntityTemplate, capitalize2;
511
+ var init_entity_template = __esm({
512
+ "src/templates/cleanComponents/entity.template.ts"() {
513
+ "use strict";
514
+ init_cjs_shims();
515
+ generateEntityTemplate = (entityName, featureName) => {
516
+ const capitalized = capitalize2(featureName);
517
+ return `/**
518
+ * ${entityName} \u2014 Domain Entity
519
+ * Represents the core business object for the "${featureName}" feature.
520
+ * No framework dependency, pure business logic only.
521
+ */
522
+ export class ${entityName} {
523
+ private readonly _id: string;
524
+ private _createdAt: Date;
525
+ private _updatedAt: Date;
526
+
527
+ constructor(
528
+ id: string,
529
+ // TODO: Add your business properties here
530
+ createdAt?: Date,
531
+ updatedAt?: Date,
532
+ ) {
533
+ this._id = id;
534
+ this._createdAt = createdAt ?? new Date();
535
+ this._updatedAt = updatedAt ?? new Date();
536
+
537
+ this.validate();
538
+ }
539
+
540
+ // -------------------------------------------------------------------------
541
+ // Getters
542
+ // -------------------------------------------------------------------------
543
+
544
+ get id(): string {
545
+ return this._id;
546
+ }
547
+
548
+ get createdAt(): Date {
549
+ return this._createdAt;
550
+ }
551
+
552
+ get updatedAt(): Date {
553
+ return this._updatedAt;
554
+ }
555
+
556
+ // -------------------------------------------------------------------------
557
+ // Business methods (behavior belongs to the entity)
558
+ // -------------------------------------------------------------------------
559
+
560
+ /**
561
+ * Marks the entity as updated.
562
+ * Call this whenever a business mutation occurs.
563
+ */
564
+ public touch(): void {
565
+ this._updatedAt = new Date();
566
+ }
567
+
568
+ /**
569
+ * Returns a plain object snapshot of the entity.
570
+ * Useful for persistence or presentation mapping.
571
+ */
572
+ public toSnapshot(): Record<string, unknown> {
573
+ return {
574
+ id: this._id,
575
+ createdAt: this._createdAt,
576
+ updatedAt: this._updatedAt,
577
+ };
578
+ }
579
+
580
+ // -------------------------------------------------------------------------
581
+ // Invariant validation (domain rules enforced at construction)
582
+ // -------------------------------------------------------------------------
583
+
584
+ private validate(): void {
585
+ if (!this._id || this._id.trim().length === 0) {
586
+ throw new Error(\`[${entityName}] id must not be empty.\`);
587
+ }
588
+ // TODO: Add your domain invariant checks here
589
+ }
590
+ }
591
+ `;
592
+ };
593
+ capitalize2 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
594
+ }
595
+ });
596
+
597
+ // src/templates/cleanComponents/repositoryInterface.template.ts
598
+ var generateRepositoryInterfaceTemplate, capitalize3;
599
+ var init_repositoryInterface_template = __esm({
600
+ "src/templates/cleanComponents/repositoryInterface.template.ts"() {
601
+ "use strict";
602
+ init_cjs_shims();
603
+ generateRepositoryInterfaceTemplate = (interfaceName, featureName) => {
604
+ const entityName = `${capitalize3(featureName)}Entity`;
605
+ const entityImport = `../../../domain/entities/${featureName}.entity`;
606
+ return `import { ${entityName} } from "${entityImport}";
607
+
608
+ /**
609
+ * ${interfaceName} \u2014 Repository Port (Application Layer)
610
+ *
611
+ * Defines the persistence contract. The infrastructure layer must implement this.
612
+ * The application layer depends on this abstraction \u2014 never on a concrete ORM or DB.
613
+ */
614
+ export interface ${interfaceName} {
615
+
616
+ /**
617
+ * Returns all ${featureName} entities.
618
+ */
619
+ findAll(): Promise<${entityName}[]>;
620
+
621
+ /**
622
+ * Finds a single ${featureName} by its unique identifier.
623
+ * Returns null if not found (avoid throwing for "not found" in repositories).
624
+ */
625
+ findById(id: string): Promise<${entityName} | null>;
626
+
627
+ /**
628
+ * Persists a new ${featureName} entity.
629
+ * Returns the created entity (with generated id if applicable).
630
+ */
631
+ create(entity: ${entityName}): Promise<${entityName}>;
632
+
633
+ /**
634
+ * Persists changes to an existing ${featureName} entity.
635
+ * Returns the updated entity, or null if not found.
636
+ */
637
+ update(entity: ${entityName}): Promise<${entityName} | null>;
638
+
639
+ /**
640
+ * Removes a ${featureName} entity by id.
641
+ * Returns true if deleted, false if not found.
642
+ */
643
+ delete(id: string): Promise<boolean>;
644
+ }
645
+ `;
646
+ };
647
+ capitalize3 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
648
+ }
649
+ });
650
+
651
+ // src/templates/cleanComponents/repositoryImpl.template.ts
652
+ var generateRepositoryImplTemplate, capitalize4;
653
+ var init_repositoryImpl_template = __esm({
654
+ "src/templates/cleanComponents/repositoryImpl.template.ts"() {
655
+ "use strict";
656
+ init_cjs_shims();
657
+ generateRepositoryImplTemplate = (featureName, interfaceImportPath) => {
658
+ const capitalized = capitalize4(featureName);
659
+ const implName = `${capitalized}Repository`;
660
+ const interfaceName = `I${capitalized}Repository`;
661
+ const entityName = `${capitalized}Entity`;
662
+ const entityImport = `../../../domain/entities/${featureName}.entity`;
663
+ return `import { ${interfaceName} } from "${interfaceImportPath}";
664
+ import { ${entityName} } from "${entityImport}";
665
+
666
+ /**
667
+ * ${implName} \u2014 Repository Implementation (Infrastructure Layer)
668
+ *
669
+ * Provides the actual persistence logic for ${featureName}.
670
+ * Replace the in-memory store below with your ORM (TypeORM, Prisma, Mongoose, etc.).
671
+ * This class depends on the ${interfaceName} contract defined in the application layer.
672
+ */
673
+ export class ${implName} implements ${interfaceName} {
674
+
675
+ /**
676
+ * TODO: Inject your database client / ORM model here via the constructor.
677
+ * Example with TypeORM:
678
+ * constructor(private readonly repo: Repository<${capitalized}Model>) {}
679
+ *
680
+ * Example with Prisma:
681
+ * constructor(private readonly prisma: PrismaClient) {}
682
+ */
683
+
684
+ // -------------------------------------------------------------------------
685
+ // In-memory store (replace with real persistence)
686
+ // -------------------------------------------------------------------------
687
+ private store: Map<string, ${entityName}> = new Map();
688
+
689
+ async findAll(): Promise<${entityName}[]> {
690
+ // TODO: Replace with your ORM query
691
+ // Example (TypeORM): return this.repo.find();
692
+ // Example (Prisma): const rows = await this.prisma.${featureName}.findMany();
693
+ // return rows.map(row => this.toDomain(row));
694
+ return Array.from(this.store.values());
695
+ }
696
+
697
+ async findById(id: string): Promise<${entityName} | null> {
698
+ // TODO: Replace with your ORM query
699
+ // Example (TypeORM): return this.repo.findOneBy({ id }) ?? null;
700
+ // Example (Prisma): const row = await this.prisma.${featureName}.findUnique({ where: { id } });
701
+ // return row ? this.toDomain(row) : null;
702
+ return this.store.get(id) ?? null;
703
+ }
704
+
705
+ async create(entity: ${entityName}): Promise<${entityName}> {
706
+ // TODO: Replace with your ORM save
707
+ // Example (TypeORM): const model = this.repo.create(this.toPersistence(entity));
708
+ // return this.toDomain(await this.repo.save(model));
709
+ // Example (Prisma): const row = await this.prisma.${featureName}.create({ data: this.toPersistence(entity) });
710
+ // return this.toDomain(row);
711
+ this.store.set(entity.id, entity);
712
+ return entity;
713
+ }
714
+
715
+ async update(entity: ${entityName}): Promise<${entityName} | null> {
716
+ // TODO: Replace with your ORM update
717
+ if (!this.store.has(entity.id)) return null;
718
+ this.store.set(entity.id, entity);
719
+ return entity;
720
+ }
721
+
722
+ async delete(id: string): Promise<boolean> {
723
+ // TODO: Replace with your ORM delete
724
+ // Example (TypeORM): const result = await this.repo.delete(id);
725
+ // return (result.affected ?? 0) > 0;
726
+ return this.store.delete(id);
727
+ }
728
+
729
+ // -------------------------------------------------------------------------
730
+ // Private mappers (ORM model \u2194 Domain entity)
731
+ // -------------------------------------------------------------------------
732
+
733
+ /**
734
+ * Maps an ORM/DB row to a domain entity.
735
+ * TODO: Adapt field mapping to your actual persistence model.
736
+ */
737
+ private toDomain(row: any): ${entityName} {
738
+ return new ${entityName}(
739
+ row.id,
740
+ // TODO: Map your ORM fields here
741
+ row.createdAt,
742
+ row.updatedAt,
743
+ );
744
+ }
745
+
746
+ /**
747
+ * Maps a domain entity to an ORM/DB-friendly plain object.
748
+ * TODO: Adapt to your schema.
749
+ */
750
+ private toPersistence(entity: ${entityName}): Record<string, unknown> {
751
+ return {
752
+ id: entity.id,
753
+ // TODO: Map your domain fields here
754
+ createdAt: entity.createdAt,
755
+ updatedAt: entity.updatedAt,
756
+ };
757
+ }
758
+ }
759
+ `;
760
+ };
761
+ capitalize4 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
762
+ }
763
+ });
764
+
765
+ // src/templates/cleanComponents/useCase.template.ts
766
+ var generateUseCaseTemplate, capitalize5;
767
+ var init_useCase_template = __esm({
768
+ "src/templates/cleanComponents/useCase.template.ts"() {
769
+ "use strict";
770
+ init_cjs_shims();
771
+ generateUseCaseTemplate = (useCaseName, featureName) => {
772
+ const capitalized = capitalize5(featureName);
773
+ const interfaceName = `I${capitalized}Repository`;
774
+ const entityName = `${capitalized}Entity`;
775
+ return `import { ${interfaceName} } from "../ports/repositories/${featureName}.repository.interface";
776
+ import { ${entityName} } from "../../domain/entities/${featureName}.entity";
777
+ import {
778
+ Create${capitalized}Dto,
779
+ Update${capitalized}Dto,
780
+ ${capitalized}ResponseDto,
781
+ ${capitalized}DtoMapper,
782
+ } from "../dtos/${featureName}.dto";
783
+
784
+ /**
785
+ * ${useCaseName} \u2014 Application Use Case
786
+ *
787
+ * Orchestrates business operations for the "${featureName}" feature.
788
+ * It depends ONLY on the repository port (interface), never on a concrete implementation.
789
+ * The concrete repository is injected at the infrastructure level (DI container or manual wiring).
790
+ */
791
+ export class ${useCaseName} {
792
+
793
+ constructor(
794
+ private readonly repository: ${interfaceName},
795
+ ) {}
796
+
797
+ // -------------------------------------------------------------------------
798
+ // Query methods (read)
799
+ // -------------------------------------------------------------------------
800
+
801
+ async findAll(): Promise<${capitalized}ResponseDto[]> {
802
+ const entities = await this.repository.findAll();
803
+ return ${capitalized}DtoMapper.toResponseList(entities);
804
+ }
805
+
806
+ async findById(id: string): Promise<${capitalized}ResponseDto | null> {
807
+ const entity = await this.repository.findById(id);
808
+ if (!entity) return null;
809
+ return ${capitalized}DtoMapper.toResponse(entity);
810
+ }
811
+
812
+ // -------------------------------------------------------------------------
813
+ // Command methods (write)
814
+ // -------------------------------------------------------------------------
815
+
816
+ async create(dto: Create${capitalized}Dto): Promise<${capitalized}ResponseDto> {
817
+ // TODO: Map DTO to domain entity. Generate id using your preferred strategy (uuid, etc.)
818
+ const id = crypto.randomUUID();
819
+ const entity = new ${entityName}(
820
+ id,
821
+ // TODO: Pass your domain fields from dto
822
+ );
823
+
824
+ const saved = await this.repository.create(entity);
825
+ return ${capitalized}DtoMapper.toResponse(saved);
826
+ }
827
+
828
+ async update(dto: Update${capitalized}Dto): Promise<${capitalized}ResponseDto | null> {
829
+ const existing = await this.repository.findById(dto.id);
830
+ if (!existing) return null;
831
+
832
+ // TODO: Apply the update to the domain entity
833
+ // existing.updateName(dto.name);
834
+ existing.touch();
835
+
836
+ const updated = await this.repository.update(existing);
837
+ if (!updated) return null;
838
+ return ${capitalized}DtoMapper.toResponse(updated);
839
+ }
840
+
841
+ async delete(id: string): Promise<boolean> {
842
+ return this.repository.delete(id);
843
+ }
844
+ }
845
+ `;
846
+ };
847
+ capitalize5 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
848
+ }
849
+ });
850
+
851
+ // src/templates/cleanComponents/dto.template.ts
852
+ var generateDtoTemplate, capitalize6;
853
+ var init_dto_template = __esm({
854
+ "src/templates/cleanComponents/dto.template.ts"() {
855
+ "use strict";
856
+ init_cjs_shims();
857
+ generateDtoTemplate = (featureName) => {
858
+ const capitalized = capitalize6(featureName);
859
+ return `/**
860
+ * ${capitalized}Dto \u2014 Data Transfer Objects (Application Layer)
861
+ *
862
+ * DTOs define the shape of data entering and leaving use cases.
863
+ * They decouple the domain model from HTTP requests/responses.
864
+ * Validation is handled at the infrastructure layer (e.g., in the controller).
865
+ */
866
+
867
+ // -------------------------------------------------------------------------
868
+ // Input DTOs (data coming IN to use cases)
869
+ // -------------------------------------------------------------------------
870
+
871
+ export interface Create${capitalized}Dto {
872
+ // TODO: Define the fields required to create a ${featureName}
873
+ // Example:
874
+ // name: string;
875
+ // email: string;
876
+ }
877
+
878
+ export interface Update${capitalized}Dto {
879
+ 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;
884
+ }
885
+
886
+ // -------------------------------------------------------------------------
887
+ // Output DTOs (data going OUT from use cases to the controller/presenter)
888
+ // -------------------------------------------------------------------------
889
+
890
+ export interface ${capitalized}ResponseDto {
891
+ id: string;
892
+ createdAt: Date;
893
+ updatedAt: Date;
894
+ // TODO: Mirror the fields you expose to clients
895
+ // Avoid leaking internal domain model details (e.g., private fields, passwords)
896
+ }
897
+
898
+ // -------------------------------------------------------------------------
899
+ // Mapper helper (Domain Entity \u2192 Response DTO)
900
+ // -------------------------------------------------------------------------
901
+
902
+ import { ${capitalized}Entity } from "../../domain/entities/${featureName}.entity";
903
+
904
+ export class ${capitalized}DtoMapper {
905
+
906
+ /**
907
+ * Maps a domain entity to a safe response DTO.
908
+ * Use this in your presenter or use case output.
909
+ */
910
+ static toResponse(entity: ${capitalized}Entity): ${capitalized}ResponseDto {
911
+ return {
912
+ id: entity.id,
913
+ createdAt: entity.createdAt,
914
+ updatedAt: entity.updatedAt,
915
+ // TODO: Map your additional fields here
916
+ };
917
+ }
918
+
919
+ /**
920
+ * Maps an array of entities to response DTOs.
921
+ */
922
+ static toResponseList(entities: ${capitalized}Entity[]): ${capitalized}ResponseDto[] {
923
+ return entities.map((entity) => this.toResponse(entity));
924
+ }
925
+ }
926
+ `;
927
+ };
928
+ capitalize6 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
929
+ }
930
+ });
931
+
932
+ // src/templates/cleanComponents/presenter.template.ts
933
+ var generatePresenterInterfaceTemplate, generatePresenterImplTemplate, capitalize7;
934
+ var init_presenter_template = __esm({
935
+ "src/templates/cleanComponents/presenter.template.ts"() {
936
+ "use strict";
937
+ init_cjs_shims();
938
+ generatePresenterInterfaceTemplate = (featureName) => {
939
+ const capitalized = capitalize7(featureName);
940
+ const interfaceName = `I${capitalized}Presenter`;
941
+ return `import { ${capitalized}ResponseDto } from "../../dtos/${featureName}.dto";
942
+
943
+ /**
944
+ * ${interfaceName} \u2014 Presenter Port (Application Layer)
945
+ *
946
+ * Defines how a use case result is formatted for the client.
947
+ * The infrastructure layer provides the concrete implementation
948
+ * (e.g., JSON serialization, HTTP envelopes, pagination wrappers).
949
+ */
950
+ export interface ${interfaceName} {
951
+
952
+ /**
953
+ * Formats a single ${featureName} response.
954
+ */
955
+ presentOne(data: ${capitalized}ResponseDto): unknown;
956
+
957
+ /**
958
+ * Formats a list of ${featureName} responses.
959
+ */
960
+ presentMany(data: ${capitalized}ResponseDto[]): unknown;
961
+
962
+ /**
963
+ * Formats a "not found" response.
964
+ */
965
+ presentNotFound(id?: string): unknown;
966
+ }
967
+ `;
968
+ };
969
+ generatePresenterImplTemplate = (featureName) => {
970
+ const capitalized = capitalize7(featureName);
971
+ const implName = `${capitalized}Presenter`;
972
+ const interfaceName = `I${capitalized}Presenter`;
973
+ return `import { ${interfaceName} } from "../../../application/ports/presenters/${featureName}.presenter.interface";
974
+ import { ${capitalized}ResponseDto } from "../../../application/dtos/${featureName}.dto";
975
+
976
+ /**
977
+ * ${implName} \u2014 Presenter Implementation (Infrastructure Layer)
978
+ *
979
+ * Formats use case output into a client-facing structure.
980
+ * Keeps HTTP/JSON concerns OUT of the domain and application layers.
981
+ */
982
+ export class ${implName} implements ${interfaceName} {
983
+
984
+ presentOne(data: ${capitalized}ResponseDto): unknown {
985
+ return {
986
+ success: true,
987
+ data,
988
+ };
989
+ }
990
+
991
+ presentMany(data: ${capitalized}ResponseDto[]): unknown {
992
+ return {
993
+ success: true,
994
+ count: data.length,
995
+ data,
996
+ };
997
+ }
998
+
999
+ presentNotFound(id?: string): unknown {
1000
+ return {
1001
+ success: false,
1002
+ message: id
1003
+ ? \`${capitalized} with id "\${id}" was not found.\`
1004
+ : \`${capitalized} not found.\`,
1005
+ };
1006
+ }
1007
+ }
1008
+ `;
1009
+ };
1010
+ capitalize7 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1011
+ }
1012
+ });
1013
+
1014
+ // src/templates/cleanComponents/router.template.ts
1015
+ var resolveRoute, generateRouterHandlerTemplate, generateRouterTemplate, capitalize8;
1016
+ var init_router_template = __esm({
1017
+ "src/templates/cleanComponents/router.template.ts"() {
1018
+ "use strict";
1019
+ init_cjs_shims();
1020
+ resolveRoute = (methodName, featureName) => {
1021
+ const lower = methodName.toLowerCase();
1022
+ if (lower.includes("findall") || lower.includes("getall")) return { httpMethod: "get", path: `/${featureName}` };
1023
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return { httpMethod: "get", path: `/${featureName}/:id` };
1024
+ if (lower.includes("create") || lower.includes("add")) return { httpMethod: "post", path: `/${featureName}` };
1025
+ if (lower.includes("update") || lower.includes("edit")) return { httpMethod: "put", path: `/${featureName}/:id` };
1026
+ if (lower.includes("delete") || lower.includes("remove")) return { httpMethod: "delete", path: `/${featureName}/:id` };
1027
+ return { httpMethod: "get", path: `/${featureName}/${lower}` };
1028
+ };
1029
+ generateRouterHandlerTemplate = (featureName, controllerMethods) => {
1030
+ const capitalized = capitalize8(featureName);
1031
+ const handlerName = `${capitalized}HandlerRouter`;
1032
+ const controllerName = `${capitalized}Controller`;
1033
+ const routesCode = (controllerMethods || []).map((method) => {
1034
+ const { httpMethod, path: path7 } = resolveRoute(method, featureName);
1035
+ return `{
1036
+ path: \`${path7}\`,
1037
+ method: "${httpMethod}",
1038
+ middlewares: [],
1039
+ handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
1040
+ }`;
1041
+ }).join(",\n");
1042
+ return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
1043
+ import { ${controllerName} } from "../adapters/controllers/${featureName}.controller";
1044
+
1045
+ export const ${handlerName}: () => IMultipleRouteDefinition = () => {
1046
+ return OpticoreRouting.routes(
1047
+ ${controllerName},
1048
+ [
1049
+ ${routesCode.replace(/\n/g, "\n ")}
1050
+ ]
1051
+ );
1052
+ };
1053
+ `;
1054
+ };
1055
+ generateRouterTemplate = (featureName) => {
1056
+ const capitalized = capitalize8(featureName);
1057
+ const handlerName = `${capitalized}HandlerRouter`;
1058
+ const routerName = `${capitalized}Router`;
1059
+ return `import { ${handlerName} } from "./${featureName}.router.handler";
1060
+ import { TFeatureRoutes } from "opticore-router";
1061
+
1062
+ export const ${routerName}: TFeatureRoutes = {
1063
+ routes: [
1064
+ {
1065
+ path: ${handlerName}().path,
1066
+ handler: ${handlerName}().handler
1067
+ }
1068
+ ]
1069
+ };
1070
+ `;
1071
+ };
1072
+ capitalize8 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1073
+ }
1074
+ });
1075
+
1076
+ // src/templates/cleanComponents/event.template.ts
1077
+ var generateEventTemplate, capitalize9;
1078
+ var init_event_template = __esm({
1079
+ "src/templates/cleanComponents/event.template.ts"() {
1080
+ "use strict";
1081
+ init_cjs_shims();
1082
+ generateEventTemplate = (featureName) => {
1083
+ const capitalized = capitalize9(featureName);
1084
+ return `/**
1085
+ * ${capitalized}Event \u2014 Domain Events
1086
+ *
1087
+ * Domain events capture facts that happened in the domain.
1088
+ * They are immutable, named in the past tense, and carry the data
1089
+ * that was relevant at the moment the event occurred.
1090
+ *
1091
+ * Use cases:
1092
+ * - Trigger side effects (send email, notify another bounded context)
1093
+ * - Feed an event bus / message broker (RabbitMQ, Kafka, etc.)
1094
+ * - Build an audit log or event sourcing stream
1095
+ */
1096
+
1097
+ // -------------------------------------------------------------------------
1098
+ // Base interface \u2014 all domain events share these fields
1099
+ // -------------------------------------------------------------------------
1100
+
1101
+ export interface IDomainEvent {
1102
+ /** Unique event identifier (uuid) */
1103
+ readonly eventId: string;
1104
+ /** When the event occurred */
1105
+ readonly occurredAt: Date;
1106
+ /** The aggregate that emitted this event */
1107
+ readonly aggregateId: string;
1108
+ }
1109
+
1110
+ // -------------------------------------------------------------------------
1111
+ // Concrete events for the "${featureName}" feature
1112
+ // -------------------------------------------------------------------------
1113
+
1114
+ export class ${capitalized}CreatedEvent implements IDomainEvent {
1115
+ readonly eventId: string;
1116
+ readonly occurredAt: Date;
1117
+ readonly aggregateId: string;
1118
+
1119
+ constructor(
1120
+ aggregateId: string,
1121
+ // TODO: Add the fields that describe what was created
1122
+ ) {
1123
+ this.eventId = crypto.randomUUID();
1124
+ this.occurredAt = new Date();
1125
+ this.aggregateId = aggregateId;
1126
+ }
1127
+ }
1128
+
1129
+ export class ${capitalized}UpdatedEvent implements IDomainEvent {
1130
+ readonly eventId: string;
1131
+ readonly occurredAt: Date;
1132
+ readonly aggregateId: string;
1133
+
1134
+ constructor(
1135
+ aggregateId: string,
1136
+ // TODO: Add the fields that changed
1137
+ ) {
1138
+ this.eventId = crypto.randomUUID();
1139
+ this.occurredAt = new Date();
1140
+ this.aggregateId = aggregateId;
1141
+ }
1142
+ }
1143
+
1144
+ export class ${capitalized}DeletedEvent implements IDomainEvent {
1145
+ readonly eventId: string;
1146
+ readonly occurredAt: Date;
1147
+ readonly aggregateId: string;
1148
+
1149
+ constructor(aggregateId: string) {
1150
+ this.eventId = crypto.randomUUID();
1151
+ this.occurredAt = new Date();
1152
+ this.aggregateId = aggregateId;
1153
+ }
1154
+ }
1155
+ `;
1156
+ };
1157
+ capitalize9 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1158
+ }
1159
+ });
1160
+
1161
+ // src/templates/cleanComponents/exception.template.ts
1162
+ var generateExceptionTemplate, capitalize10;
1163
+ var init_exception_template = __esm({
1164
+ "src/templates/cleanComponents/exception.template.ts"() {
1165
+ "use strict";
1166
+ init_cjs_shims();
1167
+ generateExceptionTemplate = (featureName) => {
1168
+ const capitalized = capitalize10(featureName);
1169
+ return `/**
1170
+ * ${capitalized}Exception \u2014 Domain Exceptions
1171
+ *
1172
+ * Domain exceptions represent violations of business rules specific to "${featureName}".
1173
+ * They are thrown inside the domain or use case layer and caught at the infrastructure
1174
+ * layer (controller) to be translated into appropriate HTTP responses.
1175
+ *
1176
+ * Rule: never import HTTP status codes or Express here \u2014 this is pure domain logic.
1177
+ */
1178
+
1179
+ // -------------------------------------------------------------------------
1180
+ // Base domain exception
1181
+ // -------------------------------------------------------------------------
1182
+
1183
+ export class ${capitalized}DomainException extends Error {
1184
+ /** Machine-readable error code for programmatic handling */
1185
+ readonly code: string;
1186
+
1187
+ constructor(message: string, code: string) {
1188
+ super(message);
1189
+ this.name = "${capitalized}DomainException";
1190
+ this.code = code;
1191
+
1192
+ // Maintains proper prototype chain in TypeScript
1193
+ Object.setPrototypeOf(this, new.target.prototype);
1194
+ }
1195
+ }
1196
+
1197
+ // -------------------------------------------------------------------------
1198
+ // Specific domain exceptions
1199
+ // -------------------------------------------------------------------------
1200
+
1201
+ /**
1202
+ * Thrown when a ${featureName} cannot be found by its identifier.
1203
+ * The controller should translate this to HTTP 404.
1204
+ */
1205
+ export class ${capitalized}NotFoundException extends ${capitalized}DomainException {
1206
+ constructor(id: string) {
1207
+ super(
1208
+ \`${capitalized} with id "\${id}" does not exist.\`,
1209
+ "${featureName.toUpperCase()}_NOT_FOUND",
1210
+ );
1211
+ this.name = "${capitalized}NotFoundException";
1212
+ }
1213
+ }
1214
+
1215
+ /**
1216
+ * Thrown when an operation violates a uniqueness constraint.
1217
+ * The controller should translate this to HTTP 409.
1218
+ */
1219
+ export class ${capitalized}AlreadyExistsException extends ${capitalized}DomainException {
1220
+ constructor(field: string, value: string) {
1221
+ super(
1222
+ \`A ${featureName} with \${field} "\${value}" already exists.\`,
1223
+ "${featureName.toUpperCase()}_ALREADY_EXISTS",
1224
+ );
1225
+ this.name = "${capitalized}AlreadyExistsException";
1226
+ }
1227
+ }
1228
+
1229
+ /**
1230
+ * Thrown when the provided data violates a business invariant.
1231
+ * The controller should translate this to HTTP 422.
1232
+ */
1233
+ export class ${capitalized}InvalidDataException extends ${capitalized}DomainException {
1234
+ constructor(reason: string) {
1235
+ super(
1236
+ \`Invalid ${featureName} data: \${reason}\`,
1237
+ "${featureName.toUpperCase()}_INVALID_DATA",
1238
+ );
1239
+ this.name = "${capitalized}InvalidDataException";
1240
+ }
1241
+ }
1242
+
1243
+ // -------------------------------------------------------------------------
1244
+ // Exception guard helper
1245
+ // -------------------------------------------------------------------------
1246
+
1247
+ /**
1248
+ * Type guard to check if an unknown error is a ${capitalized}DomainException.
1249
+ * Use this in your controller's catch block to map domain errors to HTTP responses.
1250
+ *
1251
+ * @example
1252
+ * try {
1253
+ * await useCase.create(dto);
1254
+ * } catch (error) {
1255
+ * if (is${capitalized}DomainException(error)) {
1256
+ * switch (error.code) {
1257
+ * case "${featureName.toUpperCase()}_NOT_FOUND": return res.status(404).json(...);
1258
+ * case "${featureName.toUpperCase()}_ALREADY_EXISTS": return res.status(409).json(...);
1259
+ * case "${featureName.toUpperCase()}_INVALID_DATA": return res.status(422).json(...);
1260
+ * }
1261
+ * }
1262
+ * // Unknown error \u2014 rethrow or return 500
1263
+ * throw error;
1264
+ * }
1265
+ */
1266
+ export const is${capitalized}DomainException = (error: unknown): error is ${capitalized}DomainException =>
1267
+ error instanceof ${capitalized}DomainException;
1268
+ `;
1269
+ };
1270
+ capitalize10 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1271
+ }
1272
+ });
1273
+
1274
+ // src/templates/cleanComponents/service.template.ts
1275
+ var generateServiceTemplate, capitalize11;
1276
+ var init_service_template = __esm({
1277
+ "src/templates/cleanComponents/service.template.ts"() {
1278
+ "use strict";
1279
+ init_cjs_shims();
1280
+ generateServiceTemplate = (featureName) => {
1281
+ const capitalized = capitalize11(featureName);
1282
+ return `import { ${capitalized}ResponseDto } from "../../dtos/${featureName}.dto";
1283
+
1284
+ /**
1285
+ * I${capitalized}Service \u2014 Application Service Port
1286
+ *
1287
+ * Application services handle orchestration logic that sits ABOVE a single use case
1288
+ * but BELOW the HTTP layer. They are the right place for:
1289
+ *
1290
+ * - Cross-use-case workflows (e.g. "create + notify + log in one transaction")
1291
+ * - Business rules that span multiple aggregates
1292
+ * - Coordination with external systems (email, payment, 3rd-party APIs)
1293
+ * whose contracts are defined here as abstractions
1294
+ *
1295
+ * The infrastructure layer provides the concrete implementation.
1296
+ * Use cases depend on this interface \u2014 never on the concrete class.
1297
+ */
1298
+ export interface I${capitalized}Service {
1299
+
1300
+ /**
1301
+ * Full creation workflow for a ${featureName}.
1302
+ * May involve validation, persistence, event publishing, notifications, etc.
1303
+ *
1304
+ * @param data - Raw input (already validated by the controller)
1305
+ * @returns The created ${featureName} as a response DTO
1306
+ */
1307
+ handleCreate(data: Record<string, unknown>): Promise<${capitalized}ResponseDto>;
1308
+
1309
+ /**
1310
+ * Full update workflow for a ${featureName}.
1311
+ *
1312
+ * @param id - Identifier of the ${featureName} to update
1313
+ * @param data - Fields to update
1314
+ * @returns The updated ${featureName}, or null if not found
1315
+ */
1316
+ handleUpdate(id: string, data: Record<string, unknown>): Promise<${capitalized}ResponseDto | null>;
1317
+
1318
+ /**
1319
+ * Full deletion workflow for a ${featureName}.
1320
+ * May involve cleanup tasks (remove related files, revoke tokens, etc.)
1321
+ *
1322
+ * @param id - Identifier of the ${featureName} to delete
1323
+ * @returns true if deleted, false if not found
1324
+ */
1325
+ handleDelete(id: string): Promise<boolean>;
1326
+
1327
+ // TODO: Add feature-specific service methods below
1328
+ // Example:
1329
+ // sendWelcomeNotification(id: string): Promise<void>;
1330
+ // exportToCsv(filters: Record<string, unknown>): Promise<Buffer>;
1331
+ }
1332
+ `;
1333
+ };
1334
+ capitalize11 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1335
+ }
1336
+ });
1337
+
1338
+ // src/templates/simpleComponent/model.template.ts
1339
+ var generateModelTemplate, capitalize12;
1340
+ var init_model_template = __esm({
1341
+ "src/templates/simpleComponent/model.template.ts"() {
1342
+ "use strict";
1343
+ init_cjs_shims();
1344
+ generateModelTemplate = (featureName) => {
1345
+ const capitalized = capitalize12(featureName);
1346
+ const modelName = `${capitalized}Model`;
1347
+ return `/**
1348
+ * ${modelName} \u2014 Data Model
1349
+ * Plain data structure for the "${featureName}" feature.
1350
+ */
1351
+ export class ${modelName} {
1352
+ id: string;
1353
+ createdAt: Date;
1354
+ updatedAt: Date;
1355
+
1356
+ constructor(
1357
+ id: string,
1358
+ // TODO: Add your model properties here
1359
+ createdAt?: Date,
1360
+ updatedAt?: Date,
1361
+ ) {
1362
+ this.id = id;
1363
+ this.createdAt = createdAt ?? new Date();
1364
+ this.updatedAt = updatedAt ?? new Date();
1365
+ }
1366
+ }
1367
+ `;
1368
+ };
1369
+ capitalize12 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1370
+ }
1371
+ });
1372
+
1373
+ // src/templates/simpleComponent/simpleRepository.template.ts
1374
+ var generateSimpleRepositoryTemplate, capitalize13;
1375
+ var init_simpleRepository_template = __esm({
1376
+ "src/templates/simpleComponent/simpleRepository.template.ts"() {
1377
+ "use strict";
1378
+ init_cjs_shims();
1379
+ generateSimpleRepositoryTemplate = (featureName) => {
1380
+ const capitalized = capitalize13(featureName);
1381
+ const repoName = `${capitalized}Repository`;
1382
+ const modelName = `${capitalized}Model`;
1383
+ return `import { ${modelName} } from "../models/${featureName}.model";
1384
+
1385
+ export class ${repoName} {
1386
+ private store: Map<string, ${modelName}> = new Map();
1387
+
1388
+ async findAll(): Promise<${modelName}[]> {
1389
+ // TODO: Replace with your ORM query
1390
+ return Array.from(this.store.values());
1391
+ }
1392
+
1393
+ async findById(id: string): Promise<${modelName} | null> {
1394
+ // TODO: Replace with your ORM query
1395
+ return this.store.get(id) ?? null;
1396
+ }
1397
+
1398
+ async create(model: ${modelName}): Promise<${modelName}> {
1399
+ // TODO: Replace with your ORM save
1400
+ this.store.set(model.id, model);
1401
+ return model;
1402
+ }
1403
+
1404
+ async update(model: ${modelName}): Promise<${modelName} | null> {
1405
+ // TODO: Replace with your ORM update
1406
+ if (!this.store.has(model.id)) return null;
1407
+ this.store.set(model.id, model);
1408
+ return model;
1409
+ }
1410
+
1411
+ async delete(id: string): Promise<boolean> {
1412
+ // TODO: Replace with your ORM delete
1413
+ return this.store.delete(id);
1414
+ }
1415
+ }
1416
+ `;
1417
+ };
1418
+ capitalize13 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1419
+ }
1420
+ });
1421
+
1422
+ // src/templates/simpleComponent/simpleService.template.ts
1423
+ var generateSimpleServiceTemplate, capitalize14;
1424
+ var init_simpleService_template = __esm({
1425
+ "src/templates/simpleComponent/simpleService.template.ts"() {
1426
+ "use strict";
1427
+ init_cjs_shims();
1428
+ generateSimpleServiceTemplate = (featureName) => {
1429
+ const capitalized = capitalize14(featureName);
1430
+ const serviceName = `${capitalized}Service`;
1431
+ const repoName = `${capitalized}Repository`;
1432
+ const modelName = `${capitalized}Model`;
1433
+ return `import { ${repoName} } from "../repositories/${featureName}.repository";
1434
+ import { ${modelName} } from "../models/${featureName}.model";
1435
+
1436
+ export class ${serviceName} {
1437
+ private readonly repository: ${repoName};
1438
+
1439
+ constructor() {
1440
+ this.repository = new ${repoName}();
1441
+ }
1442
+
1443
+ async findAll(): Promise<${modelName}[]> {
1444
+ return this.repository.findAll();
1445
+ }
1446
+
1447
+ async findById(id: string): Promise<${modelName} | null> {
1448
+ return this.repository.findById(id);
1449
+ }
1450
+
1451
+ async create(data: Record<string, unknown>): Promise<${modelName}> {
1452
+ const model = new ${modelName}(String(Date.now()));
1453
+ // TODO: Map data fields onto model
1454
+ return this.repository.create(model);
1455
+ }
1456
+
1457
+ async update(id: string, data: Record<string, unknown>): Promise<${modelName} | null> {
1458
+ const existing = await this.repository.findById(id);
1459
+ if (!existing) return null;
1460
+ // TODO: Apply data fields onto existing model
1461
+ return this.repository.update(existing);
1462
+ }
1463
+
1464
+ async delete(id: string): Promise<boolean> {
1465
+ return this.repository.delete(id);
1466
+ }
1467
+ }
1468
+ `;
1469
+ };
1470
+ capitalize14 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1471
+ }
1472
+ });
1473
+
1474
+ // src/templates/simpleComponent/simpleController.template.ts
1475
+ var generateSimpleControllerTemplate, generateMethod2, generateFindAllMethod2, generateFindByIdMethod2, generateCreateMethod2, generateUpdateMethod2, generateDeleteMethod2, capitalize15;
1476
+ var init_simpleController_template = __esm({
1477
+ "src/templates/simpleComponent/simpleController.template.ts"() {
1478
+ "use strict";
1479
+ init_cjs_shims();
1480
+ generateSimpleControllerTemplate = (controllerName, featureName, methods) => {
1481
+ const capitalized = capitalize15(featureName);
1482
+ const serviceName = `${capitalized}Service`;
1483
+ const methodsCode = methods.map((method) => generateMethod2(method, capitalized, serviceName)).join("\n");
1484
+ return `import { Request, Response } from "express";
1485
+ import { ResponseHandler, HttpStatusCode, IResponseHandlerSuccessData } from "opticore-http-response";
1486
+ import { ${serviceName} } from "../services/${featureName}.service";
1487
+
1488
+ export class ${controllerName} {
1489
+ private static buildService(): ${serviceName} {
1490
+ return new ${serviceName}();
1491
+ }
1492
+
1493
+ ${methodsCode}
1494
+ private static handleError(error: unknown) {
1495
+ const message = error instanceof Error ? error.message : "Internal server error";
1496
+ return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
1497
+ }
1498
+ }
1499
+ `;
1500
+ };
1501
+ generateMethod2 = (method, capitalized, serviceName) => {
1502
+ 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);
1508
+ return `
1509
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1510
+ try {
1511
+ const service = ${capitalized}Controller.buildService();
1512
+ // TODO: Implement ${method} logic
1513
+ return ResponseHandler.success({}, "success", HttpStatusCode.OK);
1514
+ } catch (error) {
1515
+ return ${capitalized}Controller.handleError(error);
1516
+ }
1517
+ }
1518
+ `;
1519
+ };
1520
+ generateFindAllMethod2 = (method, capitalized) => `
1521
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1522
+ try {
1523
+ const service = ${capitalized}Controller.buildService();
1524
+ const results = await service.findAll();
1525
+ return ResponseHandler.success(results, "success", HttpStatusCode.OK);
1526
+ } catch (error) {
1527
+ return ${capitalized}Controller.handleError(error);
1528
+ }
1529
+ }
1530
+ `;
1531
+ generateFindByIdMethod2 = (method, capitalized) => `
1532
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1533
+ try {
1534
+ const { id } = req.params;
1535
+ const service = ${capitalized}Controller.buildService();
1536
+ const result = await service.findById(id);
1537
+ if (!result) {
1538
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
1539
+ }
1540
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
1541
+ } catch (error) {
1542
+ return ${capitalized}Controller.handleError(error);
1543
+ }
1544
+ }
1545
+ `;
1546
+ generateCreateMethod2 = (method, capitalized) => `
1547
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1548
+ try {
1549
+ const service = ${capitalized}Controller.buildService();
1550
+ const result = await service.create(req.body);
1551
+ return ResponseHandler.success(result, "created", HttpStatusCode.CREATED);
1552
+ } catch (error) {
1553
+ return ${capitalized}Controller.handleError(error);
1554
+ }
1555
+ }
1556
+ `;
1557
+ generateUpdateMethod2 = (method, capitalized) => `
1558
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1559
+ try {
1560
+ const { id } = req.params;
1561
+ const service = ${capitalized}Controller.buildService();
1562
+ const result = await service.update(id, req.body);
1563
+ if (!result) {
1564
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
1565
+ }
1566
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
1567
+ } catch (error) {
1568
+ return ${capitalized}Controller.handleError(error);
1569
+ }
1570
+ }
1571
+ `;
1572
+ generateDeleteMethod2 = (method, capitalized) => `
1573
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1574
+ try {
1575
+ const { id } = req.params;
1576
+ const service = ${capitalized}Controller.buildService();
1577
+ const deleted = await service.delete(id);
1578
+ if (!deleted) {
1579
+ return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
1580
+ }
1581
+ return ResponseHandler.success(null, "deleted", HttpStatusCode.NO_CONTENT);
1582
+ } catch (error) {
1583
+ return ${capitalized}Controller.handleError(error);
1584
+ }
1585
+ }
1586
+ `;
1587
+ capitalize15 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1588
+ }
1589
+ });
1590
+
1591
+ // src/templates/simpleComponent/simpleRouter.template.ts
1592
+ var resolveRoute2, generateSimpleRouterHandlerTemplate, generateSimpleRouterTemplate, capitalize16;
1593
+ var init_simpleRouter_template = __esm({
1594
+ "src/templates/simpleComponent/simpleRouter.template.ts"() {
1595
+ "use strict";
1596
+ init_cjs_shims();
1597
+ resolveRoute2 = (methodName, featureName) => {
1598
+ const lower = methodName.toLowerCase();
1599
+ if (lower.includes("findall") || lower.includes("getall")) return { httpMethod: "get", path: `/${featureName}` };
1600
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return { httpMethod: "get", path: `/${featureName}/:id` };
1601
+ if (lower.includes("create") || lower.includes("add")) return { httpMethod: "post", path: `/${featureName}` };
1602
+ if (lower.includes("update") || lower.includes("edit")) return { httpMethod: "put", path: `/${featureName}/:id` };
1603
+ if (lower.includes("delete") || lower.includes("remove")) return { httpMethod: "delete", path: `/${featureName}/:id` };
1604
+ return { httpMethod: "get", path: `/${featureName}/${lower}` };
1605
+ };
1606
+ generateSimpleRouterHandlerTemplate = (featureName, controllerMethods) => {
1607
+ const capitalized = capitalize16(featureName);
1608
+ const handlerName = `${capitalized}HandlerRouter`;
1609
+ const controllerName = `${capitalized}Controller`;
1610
+ const routesCode = (controllerMethods || []).map((method) => {
1611
+ const { httpMethod, path: path7 } = resolveRoute2(method, featureName);
1612
+ return `{
1613
+ path: \`${path7}\`,
1614
+ method: "${httpMethod}",
1615
+ middlewares: [],
1616
+ handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
1617
+ }`;
1618
+ }).join(",\n");
1619
+ return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
1620
+ import { ${controllerName} } from "../controllers/${featureName}.controller";
1621
+
1622
+ export const ${handlerName}: () => IMultipleRouteDefinition = () => {
1623
+ return OpticoreRouting.routes(
1624
+ ${controllerName},
1625
+ [
1626
+ ${routesCode.replace(/\n/g, "\n ")}
1627
+ ]
1628
+ );
1629
+ };
1630
+ `;
1631
+ };
1632
+ generateSimpleRouterTemplate = (featureName) => {
1633
+ const capitalized = capitalize16(featureName);
1634
+ const handlerName = `${capitalized}HandlerRouter`;
1635
+ const routerName = `${capitalized}Router`;
1636
+ return `import { ${handlerName} } from "./${featureName}.router.handler";
1637
+ import { TFeatureRoutes } from "opticore-router";
1638
+
1639
+ export const ${routerName}: TFeatureRoutes = {
1640
+ routes: [
1641
+ {
1642
+ path: ${handlerName}().path,
1643
+ handler: ${handlerName}().handler
1644
+ }
1645
+ ]
1646
+ };
1647
+ `;
1648
+ };
1649
+ capitalize16 = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1650
+ }
1651
+ });
1652
+
1653
+ // src/utils/featureComponentGenerator.utils.ts
1654
+ var import_path2, import_ansi_colors6, import_fs2, import_opticore_logger2, FeatureComponentGeneratorUtils;
1655
+ var init_featureComponentGenerator_utils = __esm({
1656
+ "src/utils/featureComponentGenerator.utils.ts"() {
1657
+ "use strict";
1658
+ init_cjs_shims();
1659
+ import_path2 = __toESM(require("path"), 1);
1660
+ import_ansi_colors6 = __toESM(require("ansi-colors"), 1);
1661
+ import_fs2 = __toESM(require("fs"), 1);
1662
+ init_prompt_utils();
1663
+ init_fsModule_utils();
1664
+ import_opticore_logger2 = require("opticore-logger");
1665
+ init_controller_template();
1666
+ init_entity_template();
1667
+ init_repositoryInterface_template();
1668
+ init_repositoryImpl_template();
1669
+ init_useCase_template();
1670
+ init_dto_template();
1671
+ init_presenter_template();
1672
+ init_router_template();
1673
+ init_event_template();
1674
+ init_exception_template();
1675
+ init_service_template();
1676
+ init_model_template();
1677
+ init_simpleRepository_template();
1678
+ init_simpleService_template();
1679
+ init_simpleController_template();
1680
+ init_simpleRouter_template();
1681
+ FeatureComponentGeneratorUtils = class {
1682
+ static logger = new import_opticore_logger2.LoggerCore();
1683
+ static REGISTER_ROUTER_PATH = import_path2.default.join(process.cwd(), "src/app/router/register.router.ts");
1684
+ static structure = "clean";
1685
+ static setStructure(s) {
1686
+ this.structure = s;
1687
+ }
1688
+ /**
1689
+ * Creates the domain entity file.
1690
+ * File: domain/entities/<featureName>.entity.ts
1691
+ */
1692
+ static async createEntity(featureName, entityDir) {
1693
+ const entityName = `${this.capitalize(featureName)}Entity`;
1694
+ const filePath = import_path2.default.join(entityDir, `${featureName}.entity.ts`);
1695
+ UFsModule.createFile(filePath, generateEntityTemplate(entityName, featureName));
1696
+ console.log(import_ansi_colors6.default.green(`\u2705 Entity created: ${filePath}`));
1697
+ }
1698
+ /**
1699
+ * Creates the domain event file.
1700
+ * File: domain/events/<featureName>.event.ts
1701
+ */
1702
+ static async createEvent(featureName, eventDir) {
1703
+ const filePath = import_path2.default.join(eventDir, `${featureName}.event.ts`);
1704
+ UFsModule.createFile(filePath, generateEventTemplate(featureName));
1705
+ console.log(import_ansi_colors6.default.green(`\u2705 Event created: ${filePath}`));
1706
+ }
1707
+ /**
1708
+ * Creates the domain exception file.
1709
+ * File: domain/exceptions/<featureName>.exception.ts
1710
+ */
1711
+ static async createException(featureName, exceptionDir) {
1712
+ const filePath = import_path2.default.join(exceptionDir, `${featureName}.exception.ts`);
1713
+ UFsModule.createFile(filePath, generateExceptionTemplate(featureName));
1714
+ console.log(import_ansi_colors6.default.green(`\u2705 Exception created: ${filePath}`));
1715
+ }
1716
+ /**
1717
+ * Creates the repository port interface.
1718
+ * File: application/ports/repositories/<featureName>.repository.interface.ts
1719
+ */
1720
+ static async createRepositoryInterface(featureName, interfaceDir) {
1721
+ const interfaceName = `I${this.capitalize(featureName)}Repository`;
1722
+ const filePath = import_path2.default.join(interfaceDir, `${featureName}.repository.interface.ts`);
1723
+ UFsModule.createFile(filePath, generateRepositoryInterfaceTemplate(interfaceName, featureName));
1724
+ console.log(import_ansi_colors6.default.green(`\u2705 Repository interface created: ${filePath}`));
1725
+ }
1726
+ /**
1727
+ * Creates the presenter port interface.
1728
+ * File: application/ports/presenters/<featureName>.presenter.interface.ts
1729
+ */
1730
+ static async createPresenterInterface(featureName, presenterInterfaceDir) {
1731
+ const filePath = import_path2.default.join(presenterInterfaceDir, `${featureName}.presenter.interface.ts`);
1732
+ UFsModule.createFile(filePath, generatePresenterInterfaceTemplate(featureName));
1733
+ console.log(import_ansi_colors6.default.green(`\u2705 Presenter interface created: ${filePath}`));
1734
+ }
1735
+ /**
1736
+ * Creates the application service port interface.
1737
+ * File: application/ports/services/<featureName>.service.ts
1738
+ */
1739
+ static async createService(featureName, serviceDir) {
1740
+ const filePath = import_path2.default.join(serviceDir, `${featureName}.service.ts`);
1741
+ UFsModule.createFile(filePath, generateServiceTemplate(featureName));
1742
+ console.log(import_ansi_colors6.default.green(`\u2705 Service interface created: ${filePath}`));
1743
+ }
1744
+ /**
1745
+ * Creates the DTO file (input + output shapes + mapper).
1746
+ * File: application/dtos/<featureName>.dto.ts
1747
+ */
1748
+ static async createDto(featureName, dtoDir) {
1749
+ const filePath = import_path2.default.join(dtoDir, `${featureName}.dto.ts`);
1750
+ UFsModule.createFile(filePath, generateDtoTemplate(featureName));
1751
+ console.log(import_ansi_colors6.default.green(`\u2705 DTO created: ${filePath}`));
1752
+ }
1753
+ /**
1754
+ * Creates the use case file.
1755
+ * File: application/use-cases/<featureName>.usecase.ts
1756
+ */
1757
+ static async createUseCase(featureName, useCaseDir) {
1758
+ const useCaseName = `${this.capitalize(featureName)}UseCase`;
1759
+ const filePath = import_path2.default.join(useCaseDir, `${featureName}.usecase.ts`);
1760
+ UFsModule.createFile(filePath, generateUseCaseTemplate(useCaseName, featureName));
1761
+ console.log(import_ansi_colors6.default.green(`\u2705 UseCase created: ${filePath}`));
1762
+ }
1763
+ // =========================================================================
1764
+ // INFRASTRUCTURE LAYER
1765
+ // =========================================================================
1766
+ /**
1767
+ * Creates the concrete repository implementation.
1768
+ * File: infrastructure/adapters/repositories/<featureName>.repository.ts
1769
+ */
1770
+ static async createRepositoryImpl(featureName, repositoryImplDir) {
1771
+ const interfaceImportPath = `../../../application/ports/repositories/${featureName}.repository.interface`;
1772
+ const filePath = import_path2.default.join(repositoryImplDir, `${featureName}.repository.ts`);
1773
+ UFsModule.createFile(filePath, generateRepositoryImplTemplate(featureName, interfaceImportPath));
1774
+ console.log(import_ansi_colors6.default.green(`\u2705 Repository implementation created: ${filePath}`));
1775
+ }
1776
+ /**
1777
+ * Creates the concrete presenter implementation.
1778
+ * File: infrastructure/adapters/presenters/<featureName>.presenter.ts
1779
+ */
1780
+ static async createPresenterImpl(featureName, presenterImplDir) {
1781
+ const filePath = import_path2.default.join(presenterImplDir, `${featureName}.presenter.ts`);
1782
+ UFsModule.createFile(filePath, generatePresenterImplTemplate(featureName));
1783
+ console.log(import_ansi_colors6.default.green(`\u2705 Presenter implementation created: ${filePath}`));
1784
+ }
1785
+ /**
1786
+ * Creates the HTTP controller.
1787
+ * Prompts the user for method names, then generates the file.
1788
+ * File: infrastructure/adapters/controllers/<featureName>.controller.ts
1789
+ *
1790
+ * @returns The controller class name and the list of chosen methods.
1791
+ */
1792
+ static async createController(featureName, controllerDir) {
1793
+ const controllerName = `${this.capitalize(featureName)}Controller`;
1794
+ const filePath = import_path2.default.join(controllerDir, `${featureName}.controller.ts`);
1795
+ const wantsMethods = await UPrompt.confirm("Do you want to add methods to the controller?");
1796
+ let methods = [];
1797
+ let content;
1798
+ if (wantsMethods) {
1799
+ const methodsText = await UPrompt.text(
1800
+ "Enter the method names (comma separated):",
1801
+ "create, findAll, findById, update, delete",
1802
+ (v) => v ? void 0 : "You must enter at least one method."
1803
+ );
1804
+ methods = methodsText.split(",").map((m) => m.trim()).filter(Boolean);
1805
+ content = generateControllerTemplate(controllerName, featureName, methods);
1806
+ } else {
1807
+ content = `import { Request, Response } from "express";
1808
+
1809
+ export class ${controllerName} {}
1810
+ `;
1811
+ }
1812
+ UFsModule.createFile(filePath, content);
1813
+ console.log(import_ansi_colors6.default.green(`\u2705 Controller created: ${filePath}`));
1814
+ return { controllerName, methods };
1815
+ }
1816
+ /**
1817
+ * Creates the data model file (simple component).
1818
+ * File: models/<featureName>.model.ts
1819
+ */
1820
+ static async createModel(featureName, modelDir) {
1821
+ const filePath = import_path2.default.join(modelDir, `${featureName}.model.ts`);
1822
+ UFsModule.createFile(filePath, generateModelTemplate(featureName));
1823
+ console.log(import_ansi_colors6.default.green(`\u2705 Model created: ${filePath}`));
1824
+ }
1825
+ /**
1826
+ * Creates the simple repository (no interface, uses the model directly).
1827
+ * File: repositories/<featureName>.repository.ts
1828
+ */
1829
+ static async createSimpleRepository(featureName, repositoryDir) {
1830
+ const filePath = import_path2.default.join(repositoryDir, `${featureName}.repository.ts`);
1831
+ UFsModule.createFile(filePath, generateSimpleRepositoryTemplate(featureName));
1832
+ console.log(import_ansi_colors6.default.green(`\u2705 Repository created: ${filePath}`));
1833
+ }
1834
+ /**
1835
+ * Creates the simple concrete service (no interface, wires the repository).
1836
+ * File: services/<featureName>.service.ts
1837
+ */
1838
+ static async createSimpleService(featureName, serviceDir) {
1839
+ const filePath = import_path2.default.join(serviceDir, `${featureName}.service.ts`);
1840
+ UFsModule.createFile(filePath, generateSimpleServiceTemplate(featureName));
1841
+ console.log(import_ansi_colors6.default.green(`\u2705 Service created: ${filePath}`));
1842
+ }
1843
+ /**
1844
+ * Creates the simple controller (delegates to service, no use-case/presenter).
1845
+ * File: controllers/<featureName>.controller.ts
1846
+ */
1847
+ static async createSimpleController(featureName, controllerDir) {
1848
+ const controllerName = `${this.capitalize(featureName)}Controller`;
1849
+ const filePath = import_path2.default.join(controllerDir, `${featureName}.controller.ts`);
1850
+ const wantsMethods = await UPrompt.confirm("Do you want to add methods to the controller?");
1851
+ let methods = [];
1852
+ let content;
1853
+ if (wantsMethods) {
1854
+ const methodsText = await UPrompt.text(
1855
+ "Enter the method names (comma separated):",
1856
+ "create, findAll, findById, update, delete",
1857
+ (v) => v ? void 0 : "You must enter at least one method."
1858
+ );
1859
+ methods = methodsText.split(",").map((m) => m.trim()).filter(Boolean);
1860
+ content = generateSimpleControllerTemplate(controllerName, featureName, methods);
1861
+ } else {
1862
+ content = `import { Request, Response } from "express";
1863
+
1864
+ export class ${controllerName} {}
1865
+ `;
1866
+ }
1867
+ UFsModule.createFile(filePath, content);
1868
+ console.log(import_ansi_colors6.default.green(`\u2705 Controller created: ${filePath}`));
1869
+ return { controllerName, methods };
1870
+ }
1871
+ /**
1872
+ * 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)
1875
+ */
1876
+ static async createRouterHandler(featureName, routerHandlerDir, controllerMethods) {
1877
+ const filePath = import_path2.default.join(routerHandlerDir, `${featureName}.router.handler.ts`);
1878
+ const content = this.structure === "simple" ? generateSimpleRouterHandlerTemplate(featureName, controllerMethods) : generateRouterHandlerTemplate(featureName, controllerMethods);
1879
+ UFsModule.createFile(filePath, content);
1880
+ console.log(import_ansi_colors6.default.green(`\u2705 Router Handler created: ${filePath}`));
1881
+ }
1882
+ /**
1883
+ * 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)
1886
+ */
1887
+ static async createRouter(featureName, routerDir) {
1888
+ const filePath = import_path2.default.join(routerDir, `${featureName}.router.ts`);
1889
+ const content = this.structure === "simple" ? generateSimpleRouterTemplate(featureName) : generateRouterTemplate(featureName);
1890
+ UFsModule.createFile(filePath, content);
1891
+ await this.updateRegisterRouter(featureName);
1892
+ console.log(import_ansi_colors6.default.green(`\u2705 Router created: ${filePath}`));
1893
+ }
1894
+ static capitalize(str) {
1895
+ return str.charAt(0).toUpperCase() + str.slice(1);
1896
+ }
1897
+ /**
1898
+ * Appends an import and registers the new router in register.router.ts.
1899
+ */
1900
+ static async updateRegisterRouter(featureName) {
1901
+ const routerName = `${this.capitalize(featureName)}Router`;
1902
+ const routerPath = this.structure === "simple" ? `../../features/${featureName}/routes/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1903
+ const importStatement = `import { ${routerName} } from "${routerPath}"`;
1904
+ try {
1905
+ if (!import_fs2.default.existsSync(this.REGISTER_ROUTER_PATH)) {
1906
+ console.log(
1907
+ import_ansi_colors6.default.yellow(`\u26A0\uFE0F register.router.ts not found at ${this.REGISTER_ROUTER_PATH}, skipping auto-registration.`)
1908
+ );
1909
+ return;
1910
+ }
1911
+ let content = import_fs2.default.readFileSync(this.REGISTER_ROUTER_PATH, "utf-8");
1912
+ if (content.includes(`import { ${routerName} } from`)) {
1913
+ console.log(import_ansi_colors6.default.cyan(`\u2139\uFE0F ${routerName} is already registered.`));
1914
+ return;
1915
+ }
1916
+ const lines = content.split("\n");
1917
+ let lastImportIndex = -1;
1918
+ for (let i = 0; i < lines.length; i++) {
1919
+ if (lines[i].trim().startsWith("import ")) lastImportIndex = i;
1920
+ }
1921
+ if (lastImportIndex !== -1) {
1922
+ lines.splice(lastImportIndex + 1, 0, importStatement);
1923
+ content = lines.join("\n");
1924
+ } else {
1925
+ content = importStatement + "\n" + content;
1926
+ }
1927
+ const ARRAY_OPEN = "registered([";
1928
+ const ARRAY_CLOSE = "]);";
1929
+ const startIdx = content.indexOf(ARRAY_OPEN);
1930
+ if (startIdx === -1) {
1931
+ console.log(import_ansi_colors6.default.yellow(`\u26A0\uFE0F Could not find 'registered([' in register.router.ts.`));
1932
+ return;
1933
+ }
1934
+ const endIdx = content.indexOf(ARRAY_CLOSE, startIdx + ARRAY_OPEN.length);
1935
+ if (endIdx === -1) {
1936
+ console.log(import_ansi_colors6.default.yellow(`\u26A0\uFE0F Could not find matching ']);' in register.router.ts.`));
1937
+ return;
1938
+ }
1939
+ const arrayContent = content.substring(startIdx + ARRAY_OPEN.length, endIdx);
1940
+ const existingRoutes = arrayContent.split("\n").map((l) => l.trim().replace(/,+$/, "")).filter(Boolean);
1941
+ if (!existingRoutes.includes(routerName)) {
1942
+ existingRoutes.push(routerName);
1943
+ }
1944
+ const indentedRoutes = existingRoutes.map((r) => ` ${r},`).join("\n");
1945
+ const newContent = content.substring(0, startIdx + ARRAY_OPEN.length) + `
1946
+ ${indentedRoutes}
1947
+ ` + content.substring(endIdx);
1948
+ 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}.`));
1950
+ } catch (error) {
1951
+ console.error(import_ansi_colors6.default.red(`\u274C Failed to update register.router.ts: ${error.message}`));
1952
+ }
1953
+ }
1954
+ };
1955
+ }
1956
+ });
1957
+
1958
+ // src/templates/simpleComponent/simpleComponent.template.ts
1959
+ var import_path3, import_ansi_colors7, simpleComponentTemplate;
1960
+ var init_simpleComponent_template = __esm({
1961
+ "src/templates/simpleComponent/simpleComponent.template.ts"() {
1962
+ "use strict";
1963
+ init_cjs_shims();
1964
+ import_path3 = __toESM(require("path"), 1);
1965
+ import_ansi_colors7 = __toESM(require("ansi-colors"), 1);
1966
+ init_fsModule_utils();
1967
+ init_featureComponentGenerator_utils();
1968
+ simpleComponentTemplate = async (featureName) => {
1969
+ FeatureComponentGeneratorUtils.setStructure("simple");
1970
+ const basePath = import_path3.default.join("src", "features", featureName);
1971
+ const dirs = [
1972
+ import_path3.default.join(basePath, "routes"),
1973
+ import_path3.default.join(basePath, "controllers"),
1974
+ import_path3.default.join(basePath, "services"),
1975
+ import_path3.default.join(basePath, "repositories"),
1976
+ import_path3.default.join(basePath, "models")
1977
+ ];
1978
+ dirs.forEach((dir) => UFsModule.createDirectoryRecursively(dir));
1979
+ const paths = {
1980
+ modelsDir: import_path3.default.join(basePath, "models"),
1981
+ repositoriesDir: import_path3.default.join(basePath, "repositories"),
1982
+ servicesDir: import_path3.default.join(basePath, "services"),
1983
+ controllersDir: import_path3.default.join(basePath, "controllers"),
1984
+ routesDir: import_path3.default.join(basePath, "routes")
1985
+ };
1986
+ await FeatureComponentGeneratorUtils.createModel(featureName, paths.modelsDir);
1987
+ await FeatureComponentGeneratorUtils.createSimpleRepository(featureName, paths.repositoriesDir);
1988
+ await FeatureComponentGeneratorUtils.createSimpleService(featureName, paths.servicesDir);
1989
+ const controller = await FeatureComponentGeneratorUtils.createSimpleController(
1990
+ featureName,
1991
+ paths.controllersDir
1992
+ );
1993
+ await FeatureComponentGeneratorUtils.createRouterHandler(featureName, paths.routesDir, controller.methods);
1994
+ await FeatureComponentGeneratorUtils.createRouter(featureName, paths.routesDir);
1995
+ console.log(
1996
+ import_ansi_colors7.default.bgGreen(
1997
+ import_ansi_colors7.default.white(`
1998
+ \u{1F389} Feature "${featureName}" scaffolded with Simple Component!
1999
+ `)
2000
+ )
2001
+ );
2002
+ };
2003
+ }
2004
+ });
2005
+
2006
+ // src/templates/fullCleanComponent/fullCleanComponent.template.ts
2007
+ var import_path4, import_ansi_colors8, fullCleanComponentTemplate;
2008
+ var init_fullCleanComponent_template = __esm({
2009
+ "src/templates/fullCleanComponent/fullCleanComponent.template.ts"() {
2010
+ "use strict";
2011
+ init_cjs_shims();
2012
+ import_path4 = __toESM(require("path"), 1);
2013
+ import_ansi_colors8 = __toESM(require("ansi-colors"), 1);
2014
+ init_fsModule_utils();
2015
+ init_featureComponentGenerator_utils();
2016
+ fullCleanComponentTemplate = async (featureName) => {
2017
+ const basePath = import_path4.default.join("src", "features", featureName);
2018
+ const dirs = [
2019
+ // Domain
2020
+ import_path4.default.join(basePath, "domain", "entities"),
2021
+ import_path4.default.join(basePath, "domain", "events"),
2022
+ import_path4.default.join(basePath, "domain", "exceptions"),
2023
+ // Application
2024
+ import_path4.default.join(basePath, "application", "use-cases"),
2025
+ import_path4.default.join(basePath, "application", "dtos"),
2026
+ import_path4.default.join(basePath, "application", "ports", "repositories"),
2027
+ import_path4.default.join(basePath, "application", "ports", "presenters"),
2028
+ import_path4.default.join(basePath, "application", "ports", "services"),
2029
+ // Infrastructure
2030
+ import_path4.default.join(basePath, "infrastructure", "adapters", "controllers"),
2031
+ import_path4.default.join(basePath, "infrastructure", "adapters", "repositories"),
2032
+ import_path4.default.join(basePath, "infrastructure", "adapters", "presenters"),
2033
+ import_path4.default.join(basePath, "infrastructure", "routes")
2034
+ ];
2035
+ dirs.forEach((dir) => UFsModule.createDirectoryRecursively(dir));
2036
+ const paths = {
2037
+ // Domain
2038
+ entityDir: import_path4.default.join(basePath, "domain", "entities"),
2039
+ eventDir: import_path4.default.join(basePath, "domain", "events"),
2040
+ exceptionDir: import_path4.default.join(basePath, "domain", "exceptions"),
2041
+ // Application
2042
+ repositoryInterfaceDir: import_path4.default.join(basePath, "application", "ports", "repositories"),
2043
+ presenterInterfaceDir: import_path4.default.join(basePath, "application", "ports", "presenters"),
2044
+ serviceDir: import_path4.default.join(basePath, "application", "ports", "services"),
2045
+ dtoDir: import_path4.default.join(basePath, "application", "dtos"),
2046
+ useCaseDir: import_path4.default.join(basePath, "application", "use-cases"),
2047
+ // Infrastructure
2048
+ repositoryImplDir: import_path4.default.join(basePath, "infrastructure", "adapters", "repositories"),
2049
+ presenterImplDir: import_path4.default.join(basePath, "infrastructure", "adapters", "presenters"),
2050
+ controllerDir: import_path4.default.join(basePath, "infrastructure", "adapters", "controllers"),
2051
+ routerDir: import_path4.default.join(basePath, "infrastructure", "routes")
2052
+ };
2053
+ await FeatureComponentGeneratorUtils.createEntity(featureName, paths.entityDir);
2054
+ await FeatureComponentGeneratorUtils.createEvent(featureName, paths.eventDir);
2055
+ await FeatureComponentGeneratorUtils.createException(featureName, paths.exceptionDir);
2056
+ await FeatureComponentGeneratorUtils.createRepositoryInterface(featureName, paths.repositoryInterfaceDir);
2057
+ await FeatureComponentGeneratorUtils.createPresenterInterface(featureName, paths.presenterInterfaceDir);
2058
+ await FeatureComponentGeneratorUtils.createService(featureName, paths.serviceDir);
2059
+ await FeatureComponentGeneratorUtils.createDto(featureName, paths.dtoDir);
2060
+ await FeatureComponentGeneratorUtils.createUseCase(featureName, paths.useCaseDir);
2061
+ await FeatureComponentGeneratorUtils.createRepositoryImpl(featureName, paths.repositoryImplDir);
2062
+ await FeatureComponentGeneratorUtils.createPresenterImpl(featureName, paths.presenterImplDir);
2063
+ const controller = await FeatureComponentGeneratorUtils.createController(
2064
+ featureName,
2065
+ paths.controllerDir
2066
+ );
2067
+ await FeatureComponentGeneratorUtils.createRouterHandler(featureName, paths.routerDir, controller.methods);
2068
+ await FeatureComponentGeneratorUtils.createRouter(featureName, paths.routerDir);
2069
+ console.log(
2070
+ import_ansi_colors8.default.bgGreen(
2071
+ import_ansi_colors8.default.white(`
2072
+ \u{1F389} Feature "${featureName}" scaffolded with Clean Architecture!
2073
+ `)
2074
+ )
2075
+ );
2076
+ };
2077
+ }
2078
+ });
2079
+
2080
+ // src/templates/cleanComponentByStep/cleanComponentByStep.template.ts
2081
+ var import_path5, import_ansi_colors9, import_prompts3, cleanComponentByStepTemplate;
2082
+ var init_cleanComponentByStep_template = __esm({
2083
+ "src/templates/cleanComponentByStep/cleanComponentByStep.template.ts"() {
2084
+ "use strict";
2085
+ init_cjs_shims();
2086
+ import_path5 = __toESM(require("path"), 1);
2087
+ import_ansi_colors9 = __toESM(require("ansi-colors"), 1);
2088
+ import_prompts3 = require("@clack/prompts");
2089
+ init_fsModule_utils();
2090
+ init_prompt_utils();
2091
+ cleanComponentByStepTemplate = async (featureName) => {
2092
+ const base = import_path5.default.join("src", "features", featureName);
2093
+ let created = 0;
2094
+ const ask = async (label) => {
2095
+ const answer = await UPrompt.confirm(label, false);
2096
+ if ((0, import_prompts3.isCancel)(answer)) {
2097
+ UFsModule.removeDir(base, true, true);
2098
+ process.exit(0);
2099
+ }
2100
+ return answer;
2101
+ };
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}`));
2107
+ created++;
2108
+ };
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`);
2138
+ if (created === 0) {
2139
+ console.log(import_ansi_colors9.default.yellow(`
2140
+ \u26A0\uFE0F No files were created for feature "${featureName}".`));
2141
+ } else {
2142
+ console.log(
2143
+ import_ansi_colors9.default.bgGreen(import_ansi_colors9.default.white(`
2144
+ \u{1F389} Feature "${featureName}" \u2014 ${created} file(s) created step by step.
2145
+ `))
2146
+ );
2147
+ }
2148
+ };
2149
+ }
2150
+ });
2151
+
2152
+ // src/core/components/application/createController.application.ts
2153
+ var import_prompts4, createComponentApplication;
2154
+ var init_createController_application = __esm({
2155
+ "src/core/components/application/createController.application.ts"() {
2156
+ "use strict";
2157
+ init_cjs_shims();
2158
+ init_controllerType_service();
2159
+ import_prompts4 = require("@clack/prompts");
2160
+ init_base_service();
2161
+ init_cleanType_constant();
2162
+ init_simpleComponent_template();
2163
+ init_fullCleanComponent_template();
2164
+ init_cleanComponentByStep_template();
2165
+ createComponentApplication = async (featureDirectory, featureName) => {
2166
+ const askComponentType = await _controllerType(
2167
+ "Choose a type of component :",
2168
+ CCleanType.full_clean_component,
2169
+ [
2170
+ { label: "Simple component", value: [CCleanType.simple_component] },
2171
+ { label: "CLEAN Architecture component by step", value: [CCleanType.clean_component_by_step] },
2172
+ { label: "Full CLEAN Architecture component", value: [CCleanType.full_clean_component] }
2173
+ ]
2174
+ );
2175
+ if ((0, import_prompts4.isCancel)(askComponentType) === true || askComponentType === void 0) {
2176
+ BaseService._cancelOperation(featureDirectory);
2177
+ } else {
2178
+ switch (askComponentType) {
2179
+ case CCleanType.simple_component:
2180
+ await simpleComponentTemplate(featureName);
2181
+ break;
2182
+ case CCleanType.clean_component_by_step:
2183
+ await cleanComponentByStepTemplate(featureName);
2184
+ break;
2185
+ case CCleanType.full_clean_component:
2186
+ await fullCleanComponentTemplate(featureName);
2187
+ break;
2188
+ }
2189
+ }
2190
+ };
2191
+ }
2192
+ });
2193
+
2194
+ // src/core/core.ts
2195
+ var core_exports = {};
2196
+ var import_process3, import_node_fs2, import_path6, import_prompts5;
2197
+ var init_core = __esm({
2198
+ "src/core/core.ts"() {
2199
+ "use strict";
2200
+ init_cjs_shims();
2201
+ import_process3 = __toESM(require("process"), 1);
2202
+ import_node_fs2 = __toESM(require("fs"), 1);
2203
+ import_path6 = __toESM(require("path"), 1);
2204
+ import_prompts5 = require("@clack/prompts");
2205
+ init_welcomeMessage_utils();
2206
+ init_choiceFeatureCleanModule();
2207
+ init_customFeatureInfoMessage_constant();
2208
+ init_customFeatureInfo_utils();
2209
+ init_featureName_service();
2210
+ init_base_service();
2211
+ init_createController_application();
2212
+ (async () => {
2213
+ UWelcomeMessage();
2214
+ const featureChoice = await choiceFeatureCleanModule();
2215
+ featureChoice !== "opticore_clean_module" || featureChoice === void 0 ? customFeatureInfo() : UCustomFeatureInfo(featureChoice);
2216
+ 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());
2219
+ const featureCleanModule = BaseService._isSubdirectoryExists(featureName);
2220
+ if (featureCleanModule) {
2221
+ BaseService._featureFounded(featureName);
2222
+ }
2223
+ if ((0, import_prompts5.isCancel)(featureName)) {
2224
+ BaseService._cancelOperation(featureDirectory);
2225
+ } else {
2226
+ try {
2227
+ if (import_node_fs2.default.existsSync(directory)) {
2228
+ await createComponentApplication(featureDirectory, featureName);
2229
+ } else {
2230
+ BaseService._checkFeatureDir();
2231
+ }
2232
+ } catch (err) {
2233
+ console.error(err.message);
2234
+ import_process3.default.exit();
2235
+ }
2236
+ }
2237
+ })();
2238
+ }
2239
+ });
2240
+
2241
+ // src/index.ts
2242
+ init_cjs_shims();
2243
+ Promise.resolve().then(() => init_core());