@xeno-js/cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/index.js +3675 -18
  2. package/dist/index.js.map +1 -0
  3. package/eslint.config.mjs +1 -0
  4. package/package.json +4 -3
  5. package/tsup.config.ts +13 -0
  6. package/dist/domain/contracts/icommand-cli.contracts.js +0 -2
  7. package/dist/domain/contracts/icommand-runner.contracts.js +0 -2
  8. package/dist/domain/contracts/idispatch.contracts.js +0 -2
  9. package/dist/domain/contracts/ifile-service.contracts.js +0 -2
  10. package/dist/domain/contracts/igenerator.contracts.js +0 -2
  11. package/dist/domain/contracts/index.js +0 -2
  12. package/dist/domain/contracts/iscaffold-strategy.contracts.js +0 -2
  13. package/dist/domain/index.js +0 -2
  14. package/dist/generator.contracts.js +0 -13
  15. package/dist/infrastructure/bootstrap/bootstrapper.js +0 -32
  16. package/dist/infrastructure/bootstrap/index.js +0 -17
  17. package/dist/infrastructure/builder/builder.js +0 -16
  18. package/dist/infrastructure/builder/builder.test.js +0 -30
  19. package/dist/infrastructure/builder/index.js +0 -17
  20. package/dist/infrastructure/command_launcher/command.utils.js +0 -30
  21. package/dist/infrastructure/command_launcher/index.js +0 -2
  22. package/dist/infrastructure/commands/command-runner.service.js +0 -36
  23. package/dist/infrastructure/commands/generate.command.js +0 -48
  24. package/dist/infrastructure/commands/help.command.js +0 -44
  25. package/dist/infrastructure/commands/index.js +0 -20
  26. package/dist/infrastructure/commands/new-project.command.js +0 -43
  27. package/dist/infrastructure/dispatcher/dispatcher.js +0 -39
  28. package/dist/infrastructure/dispatcher/index.js +0 -17
  29. package/dist/infrastructure/file/file.service.js +0 -13
  30. package/dist/infrastructure/file/index.js +0 -17
  31. package/dist/infrastructure/generators/bootstrap.generator.js +0 -118
  32. package/dist/infrastructure/generators/core/bootstrap.generator.js +0 -118
  33. package/dist/infrastructure/generators/core/command/cqrs.generator.js +0 -49
  34. package/dist/infrastructure/generators/core/command/generate-command.generator.js +0 -163
  35. package/dist/infrastructure/generators/core/command/generate-query.generator.js +0 -131
  36. package/dist/infrastructure/generators/core/drizzle-sql-lite.generator.js +0 -78
  37. package/dist/infrastructure/generators/core/drizzle.generator.js +0 -85
  38. package/dist/infrastructure/generators/core/env.generator.js +0 -69
  39. package/dist/infrastructure/generators/core/index.js +0 -25
  40. package/dist/infrastructure/generators/core/main.generator.js +0 -45
  41. package/dist/infrastructure/generators/core/packagejson.generator.js +0 -78
  42. package/dist/infrastructure/generators/core/readme.generator.js +0 -431
  43. package/dist/infrastructure/generators/core/registry.generator.js +0 -46
  44. package/dist/infrastructure/generators/core/tsconfig.generator.js +0 -41
  45. package/dist/infrastructure/generators/core-packagejson.generator.js +0 -78
  46. package/dist/infrastructure/generators/drizzle-sql-lite.generator.js +0 -78
  47. package/dist/infrastructure/generators/drizzle.generator.js +0 -85
  48. package/dist/infrastructure/generators/env-vue.generator.js +0 -40
  49. package/dist/infrastructure/generators/env.generator.js +0 -71
  50. package/dist/infrastructure/generators/gitignore.generator.js +0 -62
  51. package/dist/infrastructure/generators/index.js +0 -26
  52. package/dist/infrastructure/generators/main.generator.js +0 -45
  53. package/dist/infrastructure/generators/readme.generator.js +0 -431
  54. package/dist/infrastructure/generators/registry.generator.js +0 -46
  55. package/dist/infrastructure/generators/tsconfig-vue.generator.js +0 -39
  56. package/dist/infrastructure/generators/tsconfig.generator.js +0 -41
  57. package/dist/infrastructure/generators/vite-config.generator.js +0 -39
  58. package/dist/infrastructure/generators/vue/app-vue.generator.js +0 -26
  59. package/dist/infrastructure/generators/vue/bootstrap-ts.generator.js +0 -59
  60. package/dist/infrastructure/generators/vue/command_query/cqrs.generator.js +0 -36
  61. package/dist/infrastructure/generators/vue/command_query/generate-command-vue.generator.js +0 -139
  62. package/dist/infrastructure/generators/vue/command_query/generate-query-vue.generator.js +0 -147
  63. package/dist/infrastructure/generators/vue/env.generator.js +0 -40
  64. package/dist/infrastructure/generators/vue/index-html.generator.js +0 -29
  65. package/dist/infrastructure/generators/vue/index.js +0 -28
  66. package/dist/infrastructure/generators/vue/main.generator.js +0 -45
  67. package/dist/infrastructure/generators/vue/packagejson.generator.js +0 -59
  68. package/dist/infrastructure/generators/vue/readme.generator.js +0 -394
  69. package/dist/infrastructure/generators/vue/registry-ts.generator.js +0 -28
  70. package/dist/infrastructure/generators/vue/router.generator.js +0 -33
  71. package/dist/infrastructure/generators/vue/tailwind.generator.js +0 -19
  72. package/dist/infrastructure/generators/vue/tsconfig.generator.js +0 -39
  73. package/dist/infrastructure/generators/vue/use-app.generator.js +0 -43
  74. package/dist/infrastructure/generators/vue/vite-config.generator.js +0 -39
  75. package/dist/infrastructure/generators/vue-packagejson.generator.js +0 -59
  76. package/dist/infrastructure/generators/vue-readme.generator.js +0 -53
  77. package/dist/infrastructure/generators/vue-source-files.generator.js +0 -136
  78. package/dist/infrastructure/index.js +0 -21
  79. package/dist/infrastructure/strategies/core-scaffold.strategy.js +0 -99
  80. package/dist/infrastructure/strategies/index.js +0 -18
  81. package/dist/infrastructure/strategies/vue-scaffold.strategy.js +0 -79
  82. package/dist/scaffolding.engine.js +0 -60
  83. package/dist/shared/constants/command.constants.js +0 -19
  84. package/dist/shared/constants/core.constants.js +0 -10
  85. package/dist/shared/constants/index.js +0 -18
  86. package/dist/shared/index.js +0 -18
  87. package/dist/shared/types/common.types.js +0 -2
  88. package/dist/shared/types/index.js +0 -2
  89. package/dist/shared/utils/guards.utils.js +0 -310
  90. package/dist/shared/utils/index.js +0 -18
  91. package/dist/shared/utils/string.utils.js +0 -62
package/dist/index.js CHANGED
@@ -1,11 +1,3680 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
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;
5
11
  };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const picocolors_1 = __importDefault(require("picocolors"));
8
- const builder_1 = require("./infrastructure/builder");
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+
33
+ // src/shared/constants/command.constants.ts
34
+ var ARGS_CONSTANTS, COMMAND_CONSTANTS, TARGET_ENV_CONSTANTS;
35
+ var init_command_constants = __esm({
36
+ "src/shared/constants/command.constants.ts"() {
37
+ "use strict";
38
+ ARGS_CONSTANTS = Object.freeze({
39
+ HELP: "--help",
40
+ VUE: "--vue",
41
+ CORE: "--core",
42
+ CONSOLE: "--console"
43
+ });
44
+ COMMAND_CONSTANTS = Object.freeze({
45
+ HELP: "help",
46
+ COMMAND: "command",
47
+ QUERY: "query"
48
+ });
49
+ TARGET_ENV_CONSTANTS = Object.freeze({
50
+ CONSOLE: "console",
51
+ VERCEL: "vercel",
52
+ FASTIFY: "fastify"
53
+ });
54
+ }
55
+ });
56
+
57
+ // src/shared/constants/core.constants.ts
58
+ var CORE_CONSTANTS;
59
+ var init_core_constants = __esm({
60
+ "src/shared/constants/core.constants.ts"() {
61
+ "use strict";
62
+ CORE_CONSTANTS = Object.freeze({
63
+ VERCEL: "vercel",
64
+ FASTIFY: "fastify",
65
+ DRIZZLE: "drizzle",
66
+ SQL_LITE: "sqlLite",
67
+ NONE: "none"
68
+ });
69
+ }
70
+ });
71
+
72
+ // src/shared/constants/index.ts
73
+ var init_constants = __esm({
74
+ "src/shared/constants/index.ts"() {
75
+ "use strict";
76
+ init_command_constants();
77
+ init_core_constants();
78
+ }
79
+ });
80
+
81
+ // src/shared/utils/guards.utils.ts
82
+ var OBJECT_TAG, DATE_TAG, Guards;
83
+ var init_guards_utils = __esm({
84
+ "src/shared/utils/guards.utils.ts"() {
85
+ "use strict";
86
+ OBJECT_TAG = "[object Object]";
87
+ DATE_TAG = "[object Date]";
88
+ Guards = Object.freeze({
89
+ /**
90
+ * @description Checks value is neither null nor undefined.
91
+ * @param value Candidate value.
92
+ * @returns True when value is defined.
93
+
94
+ *
95
+ * @author Xeno
96
+ * @version 1.0.0
97
+ * @since 2025-09-30
98
+ * @link https://github.com/xeno-js/xeno-js
99
+ */
100
+ isDefined(value) {
101
+ return value !== null && value !== void 0 && value !== "" && !Number.isNaN(value);
102
+ },
103
+ /**
104
+ * @description Checks value is null, undefined, empty string, or false.
105
+ * @param value Candidate value.
106
+ * @returns True when value is null, undefined, empty string, or false.
107
+
108
+ *
109
+ * @author Xeno
110
+ * @version 1.0.0
111
+ * @since 2025-09-30
112
+ * @link https://github.com/xeno-js/xeno-js
113
+ */
114
+ isNullOrEmpty(value) {
115
+ return !Guards.isDefined(value) || Guards.isString(value) && value.trim() === "" || Guards.isArray(value) && value.length === 0;
116
+ },
117
+ /**
118
+ * @description Throws an error if the value is null, undefined, empty string, or false.
119
+ * @param value Candidate value.
120
+ * @param errorMessage Error message to throw if the check fails.
121
+ * @throws Error with the provided message if the value is null, undefined, empty string, or false.
122
+
123
+ *
124
+ * @author Xeno
125
+ * @version 1.0.0
126
+ * @since 2025-09-30
127
+ * @link https://github.com/xeno-js/xeno-js
128
+ */
129
+ throwIfNullOrEmpty(value, errorMessage) {
130
+ if (Guards.isNullOrEmpty(value)) {
131
+ throw new Error(errorMessage);
132
+ }
133
+ },
134
+ /**
135
+ * @description Throws an error if the value is not a positive integer.
136
+ * @param value Candidate value.
137
+ * @param errorMessage Error message to throw if the check fails.
138
+ * @throws Error with the provided message if the value is not a positive integer.
139
+
140
+ *
141
+ * @author Xeno
142
+ * @version 1.0.0
143
+ * @since 2025-09-30
144
+ * @link https://github.com/xeno-js/xeno-js
145
+ */
146
+ throwIfNegative(value, errorMessage) {
147
+ if (Guards.isInteger(value) && value < 0) {
148
+ throw new Error(errorMessage);
149
+ }
150
+ },
151
+ /**
152
+ * @description Throws an error if the value is not an integer.
153
+ * @param value Candidate value.
154
+ * @param errorMessage Error message to throw if the check fails.
155
+ * @throws Error with the provided message if the value is not an integer.
156
+
157
+ *
158
+ * @author Xeno
159
+ * @version 1.0.0
160
+ * @since 2025-09-30
161
+ * @link https://github.com/xeno-js/xeno-js
162
+ */
163
+ throwIfNotInteger(value, errorMessage) {
164
+ if (!Guards.isInteger(value)) {
165
+ throw new Error(errorMessage);
166
+ }
167
+ },
168
+ /**
169
+ * @description Checks if an object has a method with the given name.
170
+ * @param obj Object to check.
171
+ * @param methodName Name of the method to look for.
172
+ * @returns True when obj has a function property named methodName.
173
+
174
+ *
175
+ * @author Xeno
176
+ * @version 1.0.0
177
+ * @since 2025-09-30
178
+ * @link https://github.com/xeno-js/xeno-js
179
+ */
180
+ hasMethod(obj, methodName) {
181
+ if (!this.isDefined(obj)) return false;
182
+ return Guards.isFunction(obj[methodName]);
183
+ },
184
+ /**
185
+ * @description Checks value is a string.
186
+ * @param value Candidate value.
187
+ * @returns True when value is string.
188
+
189
+ *
190
+ * @author Xeno
191
+ * @version 1.0.0
192
+ * @since 2025-09-30
193
+ * @link https://github.com/xeno-js/xeno-js
194
+ */
195
+ isString(value) {
196
+ return typeof value === "string";
197
+ },
198
+ /**
199
+ * @description Checks value is a finite number.
200
+ * @param value Candidate value.
201
+ * @returns True when value is finite number.
202
+
203
+ *
204
+ * @author Xeno
205
+ * @version 1.0.0
206
+ * @since 2025-09-30
207
+ * @link https://github.com/xeno-js/xeno-js
208
+ */
209
+ isNumber(value) {
210
+ return typeof value === "number" && Number.isFinite(value);
211
+ },
212
+ /**
213
+ * @description Checks value is an integer number.
214
+ * @param value Candidate value.
215
+ * @returns True when value is integer number.
216
+
217
+ *
218
+ * @author Xeno
219
+ * @version 1.0.0
220
+ * @since 2025-09-30
221
+ * @link https://github.com/xeno-js/xeno-js
222
+ */
223
+ isInteger(value) {
224
+ return Guards.isNumber(value) && Number.isInteger(value);
225
+ },
226
+ /**
227
+ * @description Checks value is boolean.
228
+ * @param value Candidate value.
229
+ * @returns True when value is boolean.
230
+
231
+ *
232
+ * @author Xeno
233
+ * @version 1.0.0
234
+ * @since 2025-09-30
235
+ * @link https://github.com/xeno-js/xeno-js
236
+ */
237
+ isBoolean(value) {
238
+ return typeof value === "boolean";
239
+ },
240
+ /**
241
+ * @description Checks value is bigint.
242
+ * @param value Candidate value.
243
+ * @returns True when value is bigint.
244
+
245
+ *
246
+ * @author Xeno
247
+ * @version 1.0.0
248
+ * @since 2025-09-30
249
+ * @link https://github.com/xeno-js/xeno-js
250
+ */
251
+ isBigInt(value) {
252
+ return typeof value === "bigint";
253
+ },
254
+ /**
255
+ * @description Checks value is symbol.
256
+ * @param value Candidate value.
257
+ * @returns True when value is symbol.
258
+
259
+ *
260
+ * @author Xeno
261
+ * @version 1.0.0
262
+ * @since 2025-09-30
263
+ * @link https://github.com/xeno-js/xeno-js
264
+ */
265
+ isSymbol(value) {
266
+ return typeof value === "symbol";
267
+ },
268
+ /**
269
+ * @description Checks value is a function.
270
+ * @param value Candidate value.
271
+ * @returns True when value is function.
272
+
273
+ *
274
+ * @author Xeno
275
+ * @version 1.0.0
276
+ * @since 2025-09-30
277
+ * @link https://github.com/xeno-js/xeno-js
278
+ */
279
+ isFunction(value) {
280
+ return typeof value === "function";
281
+ },
282
+ /**
283
+ * @description Checks value is an array.
284
+ * @param value Candidate value.
285
+ * @returns True when value is array.
286
+
287
+ *
288
+ * @author Xeno
289
+ * @version 1.0.0
290
+ * @since 2025-09-30
291
+ * @link https://github.com/xeno-js/xeno-js
292
+ */
293
+ isArray(value) {
294
+ return Array.isArray(value);
295
+ },
296
+ /**
297
+ * @description Checks value is a Date instance with valid timestamp.
298
+ * @param value Candidate value.
299
+ * @returns True when value is valid Date.
300
+
301
+ *
302
+ * @author Xeno
303
+ * @version 1.0.0
304
+ * @since 2025-09-30
305
+ * @link https://github.com/xeno-js/xeno-js
306
+ */
307
+ isDate(value) {
308
+ if (Object.prototype.toString.call(value) !== DATE_TAG) {
309
+ return false;
310
+ }
311
+ return Number.isFinite(value.getTime());
312
+ },
313
+ /**
314
+ * @description Checks value is an Error instance.
315
+ * @param value Candidate value.
316
+ * @returns True when value is Error.
317
+
318
+ *
319
+ * @author Xeno
320
+ * @version 1.0.0
321
+ * @since 2025-09-30
322
+ * @link https://github.com/xeno-js/xeno-js
323
+ */
324
+ isError(value) {
325
+ return value instanceof Error;
326
+ },
327
+ /**
328
+ * @description Checks value is a plain object record.
329
+ * @param value Candidate value.
330
+ * @returns True when value is object record.
331
+
332
+ *
333
+ * @author Xeno
334
+ * @version 1.0.0
335
+ * @since 2025-09-30
336
+ * @link https://github.com/xeno-js/xeno-js
337
+ */
338
+ isObjectRecord(value) {
339
+ if (!Guards.isDefined(value)) {
340
+ return false;
341
+ }
342
+ return Object.prototype.toString.call(value) === OBJECT_TAG;
343
+ },
344
+ /**
345
+ * @description Checks value is an object (not null).
346
+ * @param value Candidate value.
347
+ * @returns True when value is object.
348
+
349
+ *
350
+ * @author Xeno
351
+ * @version 1.0.0
352
+ * @since 2025-09-30
353
+ * @link https://github.com/xeno-js/xeno-js
354
+ */
355
+ isObject(value) {
356
+ return typeof value === "object" && Guards.isDefined(value);
357
+ },
358
+ /**
359
+ * @description Checks value is PromiseLike.
360
+ * @param value Candidate value.
361
+ * @returns True when value has then function.
362
+
363
+ *
364
+ * @author Xeno
365
+ * @version 1.0.0
366
+ * @since 2025-09-30
367
+ * @link https://github.com/xeno-js/xeno-js
368
+ */
369
+ isPromiseLike(value) {
370
+ if (!Guards.isDefined(value)) {
371
+ return false;
372
+ }
373
+ if (!Guards.isObject(value) && !Guards.isFunction(value)) {
374
+ return false;
375
+ }
376
+ const thenMember = value["then"];
377
+ return Guards.isFunction(thenMember);
378
+ }
379
+ });
380
+ }
381
+ });
382
+
383
+ // src/shared/utils/string.utils.ts
384
+ var StringUtils;
385
+ var init_string_utils = __esm({
386
+ "src/shared/utils/string.utils.ts"() {
387
+ "use strict";
388
+ init_guards_utils();
389
+ StringUtils = Object.freeze({
390
+ /**
391
+ * @description Inserisce un underscore (_) tra la fine di una parola e l'inizio della successiva in formato camelCase o PascalCase.
392
+ * @param input Stringa in input (es. "CreateUser").
393
+ * @returns Stringa separata da underscore (es. "Create_User").
394
+ *
395
+ * @author Xeno
396
+ * @version 1.0.0
397
+ * @since 2026-09-18
398
+ * @link https://github.com/xeno-js/xeno-js
399
+ */
400
+ splitCamelCaseWithUnderscore(input) {
401
+ if (!Guards.isString(input) || Guards.isNullOrEmpty(input)) return input;
402
+ return input.replace(/([a-z])([A-Z])/g, "$1_$2");
403
+ },
404
+ /**
405
+ * @description Converts a string to camelCase.
406
+ * @param input Input string (supports snake_case, kebab-case, or space-separated).
407
+ * @returns camelCase string.
408
+
409
+ *
410
+ * @author Xeno
411
+ * @version 1.0.0
412
+ * @since 2025-09-30
413
+ * @link https://github.com/xeno-js/xeno-js
414
+ */
415
+ camelCase(input) {
416
+ const segments = input.split(/[-_\s]+/);
417
+ const [first, ...rest] = segments;
418
+ const head = Guards.isDefined(first) ? first.toLowerCase() : "";
419
+ return head + rest.map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1)).join("");
420
+ },
421
+ /**
422
+ * @description Converts a string to PascalCase.
423
+ * @param input Input string (supports snake_case, kebab-case, space-separated, or camelCase).
424
+ * @returns PascalCase string.
425
+ *
426
+ * @author Xeno
427
+ * @version 1.0.0
428
+ * @since 2026-09-18
429
+ * @link https://github.com/xeno-js/xeno-js
430
+ */
431
+ pascalCase(input) {
432
+ if (!Guards.isString(input) || Guards.isNullOrEmpty(input)) return "";
433
+ const segments = input.split(/[-_\s]+/);
434
+ return segments.filter((seg) => seg.length > 0).map((seg) => {
435
+ const normalized = seg === seg.toUpperCase() ? seg.toLowerCase() : seg;
436
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
437
+ }).join("");
438
+ }
439
+ });
440
+ }
441
+ });
442
+
443
+ // src/shared/utils/index.ts
444
+ var init_utils = __esm({
445
+ "src/shared/utils/index.ts"() {
446
+ "use strict";
447
+ init_guards_utils();
448
+ init_string_utils();
449
+ }
450
+ });
451
+
452
+ // src/shared/index.ts
453
+ var init_shared = __esm({
454
+ "src/shared/index.ts"() {
455
+ "use strict";
456
+ init_constants();
457
+ init_utils();
458
+ }
459
+ });
460
+
461
+ // src/infrastructure/generators/core/command/generate-command.generator.ts
462
+ var generate_command_generator_exports = {};
463
+ __export(generate_command_generator_exports, {
464
+ GenerateCommandCoreGenerator: () => GenerateCommandCoreGenerator
465
+ });
466
+ var import_node_path, GenerateCommandCoreGenerator;
467
+ var init_generate_command_generator = __esm({
468
+ "src/infrastructure/generators/core/command/generate-command.generator.ts"() {
469
+ "use strict";
470
+ import_node_path = __toESM(require("path"));
471
+ GenerateCommandCoreGenerator = class {
472
+ constructor(_fileService) {
473
+ this._fileService = _fileService;
474
+ }
475
+ _fileService;
476
+ async generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod, hasDrizzle) {
477
+ const commandContent = `import { Command } from '@xeno-js/core';
478
+
479
+ /*
480
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
481
+ * Please replace <any> with your specific payload and response types.
482
+ */
483
+ export class ${pascalName}Command extends Command<any> {
484
+ constructor(public readonly payload: any) {
485
+ super('${tokenPrefix}_COMMAND_HANDLER');
486
+ }
487
+ }
488
+ `;
489
+ await this._fileService.writeFileRecursive(
490
+ import_node_path.default.join(componentDir, `${lowerName}.command.ts`),
491
+ commandContent
492
+ );
493
+ const handlerContent = `import type { IFactory, ResultType, UserContext } from '@xeno-js/core';
494
+ import { AppError, BaseHandler, Result } from '@xeno-js/core';
495
+ import { ${pascalName}Command } from './${lowerName}.command';
496
+
497
+ /*
498
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
499
+ * Please replace <any> with your specific response type.
500
+ */
501
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Command, any> {
502
+ constructor(
503
+ identityFactory: IFactory<void, UserContext>
504
+ ) {
505
+ super(identityFactory);
506
+ }
507
+
508
+ public async executeAsync(request: ${pascalName}Command, singal: AbortSignal): Promise<ResultType<any>> {
509
+ // TODO: Implement your business logic here
510
+
511
+ return Result.ok();
512
+ }
513
+ }
514
+ `;
515
+ await this._fileService.writeFileRecursive(
516
+ import_node_path.default.join(componentDir, `${lowerName}.handler.ts`),
517
+ handlerContent
518
+ );
519
+ const controllerContent = `import type { IContextAccessor, IMediator, RequestContext, ResponseDto } from '@xeno-js/core';
520
+ import { BaseController } from '@xeno-js/core';
521
+ import { ${pascalName}Command } from './${lowerName}.command';
522
+
523
+ /*
524
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
525
+ * Please replace <any, any> with your specific request and response types.
526
+ */
527
+ export class ${pascalName}Controller extends BaseController<any, any> {
528
+ constructor(
529
+ requestContext: IContextAccessor<RequestContext>,
530
+ mediator: IMediator,
531
+ ) {
532
+ super(requestContext, mediator);
533
+ }
534
+
535
+ public async handle(request: any): Promise<ResponseDto<any>> {
536
+ const cmd = new ${pascalName}Command(request);
537
+ const result = await this._send(cmd);
538
+
539
+ if (!result.isOk()) {
540
+ return this.fail(result.getErrorOrThrow(), 'Error during ${pascalName} operation');
541
+ }
542
+
543
+ return this.ok(result.getValueOrThrow());
544
+ }
545
+ }
546
+ `;
547
+ await this._fileService.writeFileRecursive(
548
+ import_node_path.default.join(componentDir, `${lowerName}.controller.ts`),
549
+ controllerContent
550
+ );
551
+ const entityContent = `import { Entity } from '@xeno-js/core';
552
+
553
+ export interface ${pascalName}Props {
554
+ // TODO: Define your entity properties
555
+ }
556
+
557
+ /*
558
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
559
+ * Adjust the properties and types according to your domain logic.
560
+ */
561
+ export class ${pascalName} extends Entity<${pascalName}Props> {
562
+ private constructor(props: ${pascalName}Props, id?: string) {
563
+ super(props, id);
564
+ }
565
+
566
+ static create(props: ${pascalName}Props, id?: string): ${pascalName} {
567
+ return new ${pascalName}(props, id);
568
+ }
569
+ }
570
+ `;
571
+ await this._fileService.writeFileRecursive(
572
+ import_node_path.default.join(componentDir, `${lowerName}.entity.ts`),
573
+ entityContent
574
+ );
575
+ if (hasZod) {
576
+ const zodContent = `import { z } from 'zod';
577
+ import { ZodUtils } from '@xeno-js/core';
578
+
579
+ /*
580
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
581
+ * Define the actual Zod validation schema for your command payload.
582
+ */
583
+ export const ${pascalName}Schema = ZodUtils.createCommandSchema({
584
+ payload: z.any() // TODO: Update with strict validation
585
+ });
586
+ `;
587
+ await this._fileService.writeFileRecursive(
588
+ import_node_path.default.join(componentDir, `${lowerName}.schema-zod.ts`),
589
+ zodContent
590
+ );
591
+ }
592
+ if (hasDrizzle) {
593
+ const dbContent = `import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
594
+ // Note: If you are using SQLite, change the import to 'drizzle-orm/sqlite-core' and update the table definition.
595
+
596
+ /*
597
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
598
+ * Update the schema definition with your actual database columns.
599
+ */
600
+ export const ${lowerName}s = pgTable('${lowerName}s', {
601
+ id: text('id').primaryKey(),
602
+ createdAt: timestamp('created_at').defaultNow(),
603
+ updatedAt: timestamp('updated_at').defaultNow().$onUpdate(() => new Date()),
604
+ });
605
+
606
+ export type ${pascalName}Dto = typeof ${lowerName}s.$inferSelect;
607
+ export type New${pascalName}Dto = typeof ${lowerName}s.$inferInsert;
608
+ `;
609
+ await this._fileService.writeFileRecursive(
610
+ import_node_path.default.join(componentDir, `${lowerName}.schema-db.ts`),
611
+ dbContent
612
+ );
613
+ }
614
+ const moduleContent = `import type { IServiceContainer, IConfigurationService } from '@xeno-js/core';
615
+ import { TOKENS } from '@xeno-js/core';
616
+ import { ${pascalName}Controller } from './${lowerName}.controller';
617
+ import { ${pascalName}Handler } from './${lowerName}.handler';
618
+
619
+ export const ${pascalName}Module = Object.freeze({
620
+ register(opts: IServiceContainer<any>, _config: IConfigurationService): void {
621
+ opts.addTransient('${tokenPrefix}_COMMAND_CONTROLLER', (c) => new ${pascalName}Controller(c.resolve(TOKENS.REQUEST_CONTEXT), c.resolve(TOKENS.MEDIATOR)));
622
+ opts.addScoped('${tokenPrefix}_COMMAND_HANDLER', (c) => new ${pascalName}Handler(c.resolve(TOKENS.USER_CONTEXT_FACTORY)));
623
+
624
+ /*
625
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
626
+ * Please copy and paste the following tokens to your MyRegistry interface (usually in src/registry.ts):
627
+ *
628
+ * ${tokenPrefix}_COMMAND_CONTROLLER: IController<any, any>;
629
+ * ${tokenPrefix}_COMMAND_HANDLER: IHandler<${pascalName}Command, any>;
630
+ */
631
+ }
632
+ } as const);
633
+ `;
634
+ await this._fileService.writeFileRecursive(
635
+ import_node_path.default.join(componentDir, `${lowerName}.module.ts`),
636
+ moduleContent
637
+ );
638
+ }
639
+ };
640
+ }
641
+ });
642
+
643
+ // src/infrastructure/generators/core/command/generate-query.generator.ts
644
+ var generate_query_generator_exports = {};
645
+ __export(generate_query_generator_exports, {
646
+ GenerateQueryCoreGenerator: () => GenerateQueryCoreGenerator
647
+ });
648
+ var import_node_path2, GenerateQueryCoreGenerator;
649
+ var init_generate_query_generator = __esm({
650
+ "src/infrastructure/generators/core/command/generate-query.generator.ts"() {
651
+ "use strict";
652
+ import_node_path2 = __toESM(require("path"));
653
+ GenerateQueryCoreGenerator = class {
654
+ constructor(_fileService) {
655
+ this._fileService = _fileService;
656
+ }
657
+ _fileService;
658
+ async generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod) {
659
+ const queryContent = `import { BaseQuery } from '@xeno-js/core';
660
+
661
+ /*
662
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
663
+ * Please replace <any> with your specific payload and response types.
664
+ */
665
+ export class ${pascalName}Query extends BaseQuery<any> {
666
+ constructor(public readonly payload: any) {
667
+ super(
668
+ '${tokenPrefix}_QUERY_HANDLER',
669
+ // Default cache options:
670
+ {
671
+ ttl: 60,
672
+ cacheKey: \`${lowerName}:\${JSON.stringify(payload)}\`,
673
+ bypassCache: false,
674
+ consistentRead: false
675
+ isUserScoped: false,
676
+ }
677
+ );
678
+ }
679
+ }
680
+ `;
681
+ await this._fileService.writeFileRecursive(
682
+ import_node_path2.default.join(componentDir, `${lowerName}.query.ts`),
683
+ queryContent
684
+ );
685
+ const handlerContent = `import type { IFactory, ResultType, UserContext } from '@xeno-js/core';
686
+ import { AppError, BaseHandler, Result } from '@xeno-js/core';
687
+ import { ${pascalName}Query } from './${lowerName}.query';
688
+
689
+ /*
690
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
691
+ * Please replace <any> with your specific response type.
692
+ */
693
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, any> {
694
+ constructor(
695
+ identityFactory: IFactory<void, UserContext>
696
+ // TODO: Inject your ReadDao or DataSource here
697
+ ) {
698
+ super(identityFactory);
699
+ }
700
+
701
+ public async executeAsync(request: ${pascalName}Query, singal: AbortSignal): Promise<ResultType<any>> {
702
+ // TODO: Implement your query logic here
703
+
704
+ return Result.ok();
705
+ }
706
+ }
707
+ `;
708
+ await this._fileService.writeFileRecursive(
709
+ import_node_path2.default.join(componentDir, `${lowerName}.handler.ts`),
710
+ handlerContent
711
+ );
712
+ const controllerContent = `import type { IContextAccessor, IMediator, RequestContext, ResponseDto } from '@xeno-js/core';
713
+ import { BaseController } from '@xeno-js/core';
714
+ import { ${pascalName}Query } from './${lowerName}.query';
715
+
716
+ /*
717
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
718
+ * Please replace <any, any> with your specific request and response types.
719
+ */
720
+ export class ${pascalName}Controller extends BaseController<any, any> {
721
+ constructor(
722
+ requestContext: IContextAccessor<RequestContext>,
723
+ mediator: IMediator,
724
+ ) {
725
+ super(requestContext, mediator);
726
+ }
727
+
728
+ public async handle(request: any): Promise<ResponseDto<any>> {
729
+ const query = new ${pascalName}Query(request);
730
+ const result = await this._query(query);
731
+
732
+ if (!result.isOk()) {
733
+ return this.fail(result.getErrorOrThrow(), 'Error during ${pascalName} operation');
734
+ }
735
+
736
+ return this.ok(result.getValueOrThrow());
737
+ }
738
+ }
739
+ `;
740
+ await this._fileService.writeFileRecursive(
741
+ import_node_path2.default.join(componentDir, `${lowerName}.controller.ts`),
742
+ controllerContent
743
+ );
744
+ if (hasZod) {
745
+ const zodContent = `import { z } from 'zod';
746
+ import { ZodUtils } from '@xeno-js/core';
747
+
748
+ /*
749
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
750
+ * Define the actual Zod validation schema for your query payload.
751
+ */
752
+ export const ${pascalName}Schema = ZodUtils.createQuerySchema({
753
+ payload: z.any() // TODO: Update with strict validation
754
+ });
755
+ `;
756
+ await this._fileService.writeFileRecursive(
757
+ import_node_path2.default.join(componentDir, `${lowerName}.schema-zod.ts`),
758
+ zodContent
759
+ );
760
+ }
761
+ const moduleContent = `import type { IServiceContainer, IConfigurationService } from '@xeno-js/core';
762
+ import { TOKENS } from '@xeno-js/core';
763
+ import { ${pascalName}Controller } from './${lowerName}.controller';
764
+ import { ${pascalName}Handler } from './${lowerName}.handler';
765
+
766
+ export const ${pascalName}Module = Object.freeze({
767
+ register(opts: IServiceContainer<any>, _config: IConfigurationService): void {
768
+ opts.addTransient('${tokenPrefix}_QUERY_CONTROLLER', (c) => new ${pascalName}Controller(c.resolve(TOKENS.REQUEST_CONTEXT), c.resolve(TOKENS.MEDIATOR)));
769
+ opts.addScoped('${tokenPrefix}_QUERY_HANDLER', (c) => new ${pascalName}Handler(c.resolve(TOKENS.USER_CONTEXT_FACTORY)));
770
+
771
+ /*
772
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
773
+ * Please copy and paste the following tokens to your MyRegistry interface (usually in src/registry.ts):
774
+ *
775
+ * ${tokenPrefix}_QUERY_CONTROLLER: IController<any, any>;
776
+ * ${tokenPrefix}_QUERY_HANDLER: IHandler<${pascalName}Query, any>;
777
+ */
778
+ }
779
+ } as const);
780
+ `;
781
+ await this._fileService.writeFileRecursive(
782
+ import_node_path2.default.join(componentDir, `${lowerName}.module.ts`),
783
+ moduleContent
784
+ );
785
+ }
786
+ };
787
+ }
788
+ });
789
+
790
+ // src/infrastructure/generators/core/command/cqrs.generator.ts
791
+ var cqrs_generator_exports = {};
792
+ __export(cqrs_generator_exports, {
793
+ CqrsGenerator: () => CqrsGenerator
794
+ });
795
+ var import_promises2, import_node_path3, CqrsGenerator;
796
+ var init_cqrs_generator = __esm({
797
+ "src/infrastructure/generators/core/command/cqrs.generator.ts"() {
798
+ "use strict";
799
+ import_promises2 = __toESM(require("fs/promises"));
800
+ import_node_path3 = __toESM(require("path"));
801
+ init_shared();
802
+ CqrsGenerator = class {
803
+ constructor(_fileService) {
804
+ this._fileService = _fileService;
805
+ }
806
+ _fileService;
807
+ async generate(targetDir, name, type, outputPath) {
808
+ const pascalName = StringUtils.pascalCase(name);
809
+ const tokenPrefix = StringUtils.splitCamelCaseWithUnderscore(pascalName).toUpperCase();
810
+ const lowerName = name.toLowerCase();
811
+ const destinationFolder = outputPath ?? lowerName;
812
+ const componentDir = import_node_path3.default.join(targetDir, "src", destinationFolder);
813
+ let hasZod = false;
814
+ let hasDrizzle = false;
815
+ try {
816
+ const pkgJsonStr = await import_promises2.default.readFile(import_node_path3.default.join(targetDir, "package.json"), "utf8");
817
+ const pkgJson = JSON.parse(pkgJsonStr);
818
+ const deps = { ...pkgJson.dependencies ?? {}, ...pkgJson.devDependencies ?? {} };
819
+ hasZod = !!Guards.isDefined(deps["zod"]);
820
+ hasDrizzle = !!Guards.isDefined(deps["drizzle-orm"]);
821
+ } catch (error) {
822
+ console.warn(
823
+ " Warning: Could not read package.json. Assuming Zod and Drizzle are not installed.",
824
+ error
825
+ );
826
+ }
827
+ switch (type) {
828
+ case COMMAND_CONSTANTS.COMMAND: {
829
+ const { GenerateCommandCoreGenerator: GenerateCommandCoreGenerator2 } = await Promise.resolve().then(() => (init_generate_command_generator(), generate_command_generator_exports));
830
+ const generator = new GenerateCommandCoreGenerator2(this._fileService);
831
+ return generator.generate(
832
+ pascalName,
833
+ lowerName,
834
+ tokenPrefix,
835
+ componentDir,
836
+ hasZod,
837
+ hasDrizzle
838
+ );
839
+ }
840
+ case COMMAND_CONSTANTS.QUERY: {
841
+ const { GenerateQueryCoreGenerator: GenerateQueryCoreGenerator2 } = await Promise.resolve().then(() => (init_generate_query_generator(), generate_query_generator_exports));
842
+ const generatorQuery = new GenerateQueryCoreGenerator2(this._fileService);
843
+ return generatorQuery.generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod);
844
+ }
845
+ default:
846
+ throw new Error(`Unsupported generator type: '${String(type)}'. Use 'command' or 'query'.`);
847
+ }
848
+ }
849
+ };
850
+ }
851
+ });
852
+
853
+ // src/infrastructure/generators/vue/command_query/generate-command-vue.generator.ts
854
+ var generate_command_vue_generator_exports = {};
855
+ __export(generate_command_vue_generator_exports, {
856
+ GenerateCommandVueGenerator: () => GenerateCommandVueGenerator
857
+ });
858
+ var import_node_path4, GenerateCommandVueGenerator;
859
+ var init_generate_command_vue_generator = __esm({
860
+ "src/infrastructure/generators/vue/command_query/generate-command-vue.generator.ts"() {
861
+ "use strict";
862
+ import_node_path4 = __toESM(require("path"));
863
+ GenerateCommandVueGenerator = class {
864
+ constructor(_fileService) {
865
+ this._fileService = _fileService;
866
+ }
867
+ _fileService;
868
+ async generate(pascalName, lowerName, tokenPrefix, componentDir) {
869
+ const commandContent = `import { Command } from '@xeno-js/vue';
870
+
871
+ export class ${pascalName}Command extends Command {
872
+ constructor() {
873
+ super('${tokenPrefix}_COMMAND');
874
+ }
875
+ }
876
+ `;
877
+ await this._fileService.writeFileRecursive(
878
+ import_node_path4.default.join(componentDir, `${lowerName}.command.ts`),
879
+ commandContent
880
+ );
881
+ const handlerContent = `import type { IHandler, ResultType } from '@xeno-js/vue';
882
+ import { AppError, Result } from '@xeno-js/vue';
883
+ import type { ${pascalName}Command } from './${lowerName}.command';
884
+ import type { ${pascalName}Response } from './${lowerName}.model';
885
+
886
+ /*
887
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
888
+ *
889
+ * Please register your handler in the IoC container (e.g. src/bootstrap.ts) and add its token to MyRegistry.
890
+ *
891
+ * 1. In src/registry.ts:
892
+ * ${tokenPrefix}_HANDLER: IHandler<${pascalName}Command, ${pascalName}Response>;
893
+ *
894
+ * 2. In src/bootstrap.ts:
895
+ * .addServices((config, register) => {
896
+ * register('${tokenPrefix}_HANDLER', new ${pascalName}Handler());
897
+ * });
898
+ */
899
+ export class ${pascalName}Handler implements IHandler<${pascalName}Command, ${pascalName}Response> {
900
+ constructor(
901
+ // TODO: Inject your remote DataSources or other services here
902
+ ) {}
903
+
904
+ public async handle(command: ${pascalName}Command, signal: AbortSignal): Promise<ResultType<${pascalName}Response>> {
905
+ AppError.throwIfAborted(signal, this.constructor.name);
906
+
907
+ // TODO: Implement your frontend business logic or API calls here
908
+ return Result.ok();
909
+ }
910
+ }
911
+ `;
912
+ await this._fileService.writeFileRecursive(
913
+ import_node_path4.default.join(componentDir, `${lowerName}.handler.ts`),
914
+ handlerContent
915
+ );
916
+ const composableContent = `import { ref } from 'vue';
917
+ import { AppError, Result, type ApiResponseDto, type ResultType } from '@xeno-js/vue';
918
+ import { ServicesUtils } from '@/use-app';
919
+ import { ${pascalName}Command } from './${lowerName}.command';
920
+ import type { ${pascalName}Request, ${pascalName}Response } from './${lowerName}.model';
921
+
922
+ /*
923
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
924
+ *
925
+ * Please register your handler in the IoC container (e.g. src/bootstrap.ts) and add its token to MyRegistry.
926
+ *
927
+ * 1. In src/registry.ts:
928
+ * ${tokenPrefix}_HANDLER: IHandler<${pascalName}Command, ${pascalName}Response>;
929
+ *
930
+ * 2. In src/bootstrap.ts:
931
+ * .addServices((config, register) => {
932
+ * register('${tokenPrefix}_HANDLER', new ${pascalName}Handler());
933
+ * });
934
+ */
935
+ export function use${pascalName}() {
936
+ const loading = ref(false);
937
+ const error = ref<string | null>(null);
938
+ let abortController: AbortController | null = null;
939
+
940
+ const execute = async (payload: ${pascalName}Request): Promise<ResultType<${pascalName}Response>> => {
941
+ abortController = new AbortController();
942
+
943
+ if (loading.value) return Result.fail(AppError.create({
944
+ name: '${pascalName}Command',
945
+ message: '${pascalName}Command is already in progress',
946
+ code: '${tokenPrefix}_COMMAND_ALREADY_IN_PROGRESS',
947
+ status: 500,
948
+ cause: new Error('${tokenPrefix}_COMMAND_ALREADY_IN_PROGRESS'),
949
+ }));
950
+ loading.value = true;
951
+ error.value = null;
952
+
953
+ try {
954
+ // Resolve mediator and handler from the container
955
+ const { mediator, ${tokenPrefix}_HANDLER: handler } = ServicesUtils.useApp();
956
+ const command = new ${pascalName}Command();
957
+
958
+ const result = await mediator.send(command, async () => {
959
+ return await handler.handle(command, abortController?.signal);
960
+ });
961
+
962
+ if (!result.isOk()) {
963
+ error.value = result.getErrorOrThrow().message;
964
+ }
965
+
966
+ return result;
967
+ } finally {
968
+ loading.value = false;
969
+ }
970
+ };
971
+
972
+ const abort = () => {
973
+ if (abortController) {
974
+ abortController.abort();
975
+ }
976
+ };
977
+
978
+ // Automatically cancel pending operations when the component unmounts
979
+ onUnmounted(() => {
980
+ abort();
981
+ });
982
+
983
+ return { loading, error, execute };
984
+ }
985
+ `;
986
+ await this._fileService.writeFileRecursive(
987
+ import_node_path4.default.join(componentDir, `use-${lowerName}.composable.ts`),
988
+ composableContent
989
+ );
990
+ const modelContent = `export interface ${pascalName}Request {
991
+ // TODO: Define your request payload properties here
992
+ }
993
+
994
+ export interface ${pascalName}Response {
995
+ // TODO: Define your response properties here
996
+ }
997
+ `;
998
+ await this._fileService.writeFileRecursive(
999
+ import_node_path4.default.join(componentDir, `${lowerName}.model.ts`),
1000
+ modelContent
1001
+ );
1002
+ }
1003
+ };
1004
+ }
1005
+ });
1006
+
1007
+ // src/infrastructure/generators/vue/command_query/generate-query-vue.generator.ts
1008
+ var generate_query_vue_generator_exports = {};
1009
+ __export(generate_query_vue_generator_exports, {
1010
+ GenerateQueryVueGenerator: () => GenerateQueryVueGenerator
1011
+ });
1012
+ var import_node_path5, GenerateQueryVueGenerator;
1013
+ var init_generate_query_vue_generator = __esm({
1014
+ "src/infrastructure/generators/vue/command_query/generate-query-vue.generator.ts"() {
1015
+ "use strict";
1016
+ import_node_path5 = __toESM(require("path"));
1017
+ GenerateQueryVueGenerator = class {
1018
+ constructor(_fileService) {
1019
+ this._fileService = _fileService;
1020
+ }
1021
+ _fileService;
1022
+ async generate(pascalName, lowerName, tokenPrefix, componentDir) {
1023
+ const queryContent = `import { Query } from '@xeno-js/vue';
1024
+ import type { ${pascalName}Response } from './${lowerName}.model';
1025
+
1026
+ export class ${pascalName}Query extends Query<${pascalName}Response> {
1027
+ constructor() {
1028
+ super('${tokenPrefix}_QUERY', {
1029
+ ttl: 60,
1030
+ cacheKey: \`${lowerName}.query\`,
1031
+ bypassCache: false,
1032
+ consistentRead: false,
1033
+ isUserScoped: true
1034
+ });
1035
+ }
1036
+ }
1037
+ `;
1038
+ await this._fileService.writeFileRecursive(
1039
+ import_node_path5.default.join(componentDir, `${lowerName}.query.ts`),
1040
+ queryContent
1041
+ );
1042
+ const handlerContent = `import type { IHandler, ResultType } from '@xeno-js/vue';
1043
+ import { AppError, Result } from '@xeno-js/vue';
1044
+ import type { ${pascalName}Query } from './${lowerName}.query';
1045
+ import type { ${pascalName}Response } from './${lowerName}.model';
1046
+
1047
+ /*
1048
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
1049
+ *
1050
+ * Please register your handler in the IoC container (e.g. src/bootstrap.ts) and add its token to MyRegistry.
1051
+ *
1052
+ * 1. In src/registry.ts:
1053
+ * ${tokenPrefix}_HANDLER: IHandler<${pascalName}Query, ${pascalName}Response>;
1054
+ *
1055
+ * 2. In src/bootstrap.ts:
1056
+ * .addServices((config, register) => {
1057
+ * register('${tokenPrefix}_HANDLER', new ${pascalName}Handler());
1058
+ * });
1059
+ */
1060
+ export class ${pascalName}Handler implements IHandler<${pascalName}Query, ${pascalName}Response> {
1061
+ constructor(
1062
+ // TODO: Inject your remote DataSources or other services here
1063
+ ) {}
1064
+
1065
+ public async handle(query: ${pascalName}Query, signal: AbortSignal): Promise<ResultType<${pascalName}Response>> {
1066
+ AppError.throwIfAborted(signal, this.constructor.name);
1067
+
1068
+ // TODO: Implement your frontend business logic or API calls here
1069
+ return Result.ok();
1070
+ }
1071
+ }
1072
+ `;
1073
+ await this._fileService.writeFileRecursive(
1074
+ import_node_path5.default.join(componentDir, `${lowerName}.handler.ts`),
1075
+ handlerContent
1076
+ );
1077
+ const composableContent = `import { ref } from 'vue';
1078
+ import { AppError, Result, type ApiResponseDto, type ResultType } from '@xeno-js/vue';
1079
+ import { ServicesUtils } from '@/use-app';
1080
+ import { ${pascalName}Query } from './${lowerName}.query';
1081
+ import type { ${pascalName}Response } from './${lowerName}.model';
1082
+
1083
+ export function use${pascalName}() {
1084
+ const loading = ref(false);
1085
+ const error = ref<string | null>(null);
1086
+ const data = ref<${pascalName}Response | null>(null);
1087
+ let abortController: AbortController | null = null;
1088
+
1089
+ /*
1090
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
1091
+ *
1092
+ * Please register your handler in the IoC container (e.g. src/bootstrap.ts) and add its token to MyRegistry.
1093
+ *
1094
+ * 1. In src/registry.ts:
1095
+ * ${tokenPrefix}_HANDLER: IHandler<${pascalName}Query, ${pascalName}Response>;
1096
+ *
1097
+ * 2. In src/bootstrap.ts:
1098
+ * .addServices((config, register) => {
1099
+ * register('${tokenPrefix}_HANDLER', new ${pascalName}Handler());
1100
+ * });
1101
+ */
1102
+ const fetch = async (): Promise<ResultType<${pascalName}Response>> => {
1103
+ abortController = new AbortController();
1104
+
1105
+ if (loading.value) return Result.fail(AppError.create({
1106
+ name: '${pascalName}Query',
1107
+ message: '${pascalName}Query is already in progress',
1108
+ code: '${tokenPrefix}_QUERY_ALREADY_IN_PROGRESS',
1109
+ status: 500,
1110
+ cause: new Error('${tokenPrefix}_QUERY_ALREADY_IN_PROGRESS'),
1111
+ }));
1112
+ loading.value = true;
1113
+ error.value = null;
1114
+
1115
+ try {
1116
+ try {
1117
+ // Resolve mediator and handler from the container
1118
+ const { mediator, ${tokenPrefix}_HANDLER: handler } = ServicesUtils.useApp();
1119
+ const query = new ${pascalName}Query();
1120
+
1121
+ const result = await mediator.query(query, async () => {
1122
+ return await handler.handle(query, abortController?.signal);
1123
+ });
1124
+
1125
+ if (result.isOk()) {
1126
+ data.value = result.getValueOrThrow();
1127
+ } else {
1128
+ error.value = result.getErrorOrThrow().message;
1129
+ }
1130
+
1131
+ return result;
1132
+ } finally {
1133
+ loading.value = false;
1134
+ }
1135
+ };
1136
+
1137
+ const abort = () => {
1138
+ if (abortController) {
1139
+ abortController.abort();
1140
+ }
1141
+ };
1142
+
1143
+ // Automatically cancel pending operations when the component unmounts
1144
+ onUnmounted(() => {
1145
+ abort();
1146
+ });
1147
+
1148
+ return { loading, error, data, fetch };
1149
+ }
1150
+ `;
1151
+ await this._fileService.writeFileRecursive(
1152
+ import_node_path5.default.join(componentDir, `use-${lowerName}.composable.ts`),
1153
+ composableContent
1154
+ );
1155
+ const modelContent = `export interface ${pascalName}Response {
1156
+ // TODO: Define your response properties here
1157
+ }
1158
+ `;
1159
+ await this._fileService.writeFileRecursive(
1160
+ import_node_path5.default.join(componentDir, `${lowerName}.model.ts`),
1161
+ modelContent
1162
+ );
1163
+ }
1164
+ };
1165
+ }
1166
+ });
1167
+
1168
+ // src/infrastructure/generators/vue/command_query/cqrs.generator.ts
1169
+ var cqrs_generator_exports2 = {};
1170
+ __export(cqrs_generator_exports2, {
1171
+ CqrsVueGenerator: () => CqrsVueGenerator
1172
+ });
1173
+ var import_node_path6, CqrsVueGenerator;
1174
+ var init_cqrs_generator2 = __esm({
1175
+ "src/infrastructure/generators/vue/command_query/cqrs.generator.ts"() {
1176
+ "use strict";
1177
+ import_node_path6 = __toESM(require("path"));
1178
+ init_shared();
1179
+ CqrsVueGenerator = class {
1180
+ constructor(_fileService) {
1181
+ this._fileService = _fileService;
1182
+ }
1183
+ _fileService;
1184
+ async generate(targetDir, name, type, outputPath) {
1185
+ const pascalName = StringUtils.pascalCase(name);
1186
+ const tokenPrefix = StringUtils.splitCamelCaseWithUnderscore(pascalName).toUpperCase();
1187
+ const lowerName = name.toLowerCase();
1188
+ const destinationFolder = outputPath ?? lowerName;
1189
+ const componentDir = import_node_path6.default.join(targetDir, "src", destinationFolder);
1190
+ switch (type) {
1191
+ case COMMAND_CONSTANTS.COMMAND: {
1192
+ const { GenerateCommandVueGenerator: GenerateCommandVueGenerator2 } = await Promise.resolve().then(() => (init_generate_command_vue_generator(), generate_command_vue_generator_exports));
1193
+ const generator = new GenerateCommandVueGenerator2(this._fileService);
1194
+ return generator.generate(pascalName, lowerName, tokenPrefix, componentDir);
1195
+ }
1196
+ case COMMAND_CONSTANTS.QUERY: {
1197
+ const { GenerateQueryVueGenerator: GenerateQueryVueGenerator2 } = await Promise.resolve().then(() => (init_generate_query_vue_generator(), generate_query_vue_generator_exports));
1198
+ const generatorQuery = new GenerateQueryVueGenerator2(this._fileService);
1199
+ return generatorQuery.generate(pascalName, lowerName, tokenPrefix, componentDir);
1200
+ }
1201
+ default:
1202
+ throw new Error(`Unsupported generator type: '${String(type)}'. Use 'command' or 'query'.`);
1203
+ }
1204
+ }
1205
+ };
1206
+ }
1207
+ });
1208
+
1209
+ // src/infrastructure/generators/vue/packagejson.generator.ts
1210
+ var packagejson_generator_exports = {};
1211
+ __export(packagejson_generator_exports, {
1212
+ PackageJsonVueGenerator: () => PackageJsonVueGenerator
1213
+ });
1214
+ var import_node_path8, PackageJsonVueGenerator;
1215
+ var init_packagejson_generator = __esm({
1216
+ "src/infrastructure/generators/vue/packagejson.generator.ts"() {
1217
+ "use strict";
1218
+ import_node_path8 = __toESM(require("path"));
1219
+ PackageJsonVueGenerator = class {
1220
+ constructor(_fileService) {
1221
+ this._fileService = _fileService;
1222
+ }
1223
+ _fileService;
1224
+ async generate(projectPath, options) {
1225
+ const packageJson = {
1226
+ name: options.targetDir,
1227
+ version: "1.0.0",
1228
+ private: true,
1229
+ type: "module",
1230
+ scripts: {
1231
+ dev: "vite",
1232
+ build: "vue-tsc -b && vite build",
1233
+ preview: "vite preview",
1234
+ g: "xeno-js generate"
1235
+ },
1236
+ dependencies: {
1237
+ "@xeno-js/vue": "latest",
1238
+ "vue": "^3.5.0"
1239
+ },
1240
+ devDependencies: {
1241
+ "@types/node": "^20.0.0",
1242
+ "@vitejs/plugin-vue": "^5.1.0",
1243
+ "typescript": "^5.5.0",
1244
+ "vite": "^5.4.0",
1245
+ "vue-tsc": "^2.1.0"
1246
+ }
1247
+ };
1248
+ if (options.axios) packageJson.dependencies["axios"] = "^1.7.0";
1249
+ if (options.cockatiel) packageJson.dependencies["cockatiel"] = "^4.0.0";
1250
+ if (options.supabase) packageJson.dependencies["@supabase/supabase-js"] = "^2.45.0";
1251
+ if (options.sentry) packageJson.dependencies["@sentry/vue"] = "^8.28.0";
1252
+ if (options.pinia) packageJson.dependencies["pinia"] = "^2.2.0";
1253
+ if (options.router) packageJson.dependencies["vue-router"] = "^4.4.0";
1254
+ if (options.zod) packageJson.dependencies["zod"] = "^4.4.3";
1255
+ if (options.tailwind) {
1256
+ packageJson.devDependencies["tailwindcss"] = "^4.0.0";
1257
+ packageJson.devDependencies["@tailwindcss/vite"] = "^4.0.0";
1258
+ }
1259
+ const filePath = import_node_path8.default.join(projectPath, "package.json");
1260
+ await this._fileService.writeFileRecursive(filePath, JSON.stringify(packageJson, null, 2));
1261
+ }
1262
+ };
1263
+ }
1264
+ });
1265
+
1266
+ // src/infrastructure/generators/vue/vite-config.generator.ts
1267
+ var vite_config_generator_exports = {};
1268
+ __export(vite_config_generator_exports, {
1269
+ ViteconfigGenerator: () => ViteconfigGenerator
1270
+ });
1271
+ var import_node_path9, ViteconfigGenerator;
1272
+ var init_vite_config_generator = __esm({
1273
+ "src/infrastructure/generators/vue/vite-config.generator.ts"() {
1274
+ "use strict";
1275
+ import_node_path9 = __toESM(require("path"));
1276
+ ViteconfigGenerator = class {
1277
+ constructor(_fileService) {
1278
+ this._fileService = _fileService;
1279
+ }
1280
+ _fileService;
1281
+ async generate(projectPath, options) {
1282
+ const tailwindImport = options.tailwind ? `
1283
+ import tailwindcss from '@tailwindcss/vite';` : "";
1284
+ const tailwindPlugin = options.tailwind ? `
1285
+ tailwindcss(),` : "";
1286
+ const content = `import { defineConfig } from 'vite';
1287
+ import vue from '@vitejs/plugin-vue';${tailwindImport}
1288
+ import path from 'node:path';
1289
+
1290
+ export default defineConfig({
1291
+ plugins: [
1292
+ vue(),${tailwindPlugin}
1293
+ ],
1294
+ resolve: {
1295
+ alias: {
1296
+ '@': path.resolve(__dirname, './src'),
1297
+ },
1298
+ },
1299
+ server: {
1300
+ port: 5173,
1301
+ }
1302
+ });
1303
+ `;
1304
+ const filePath = import_node_path9.default.join(projectPath, "vite.config.ts");
1305
+ await this._fileService.writeFileRecursive(filePath, content);
1306
+ }
1307
+ };
1308
+ }
1309
+ });
1310
+
1311
+ // src/infrastructure/generators/vue/env.generator.ts
1312
+ var env_generator_exports = {};
1313
+ __export(env_generator_exports, {
1314
+ EnvVueGenerator: () => EnvVueGenerator
1315
+ });
1316
+ var import_node_path10, EnvVueGenerator;
1317
+ var init_env_generator = __esm({
1318
+ "src/infrastructure/generators/vue/env.generator.ts"() {
1319
+ "use strict";
1320
+ import_node_path10 = __toESM(require("path"));
1321
+ EnvVueGenerator = class {
1322
+ constructor(_fileService) {
1323
+ this._fileService = _fileService;
1324
+ }
1325
+ _fileService;
1326
+ async generate(projectPath, options) {
1327
+ let content = `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1328
+ # Xeno VUE APPLICATION ENVIRONMENT VARIABLES
1329
+ # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1330
+ VITE_APP_ENV=development
1331
+ `;
1332
+ if (options.axios) {
1333
+ content += `
1334
+ # --- Axios ---
1335
+ VITE_API_BASE_URL=http://localhost:3000
1336
+ `;
1337
+ }
1338
+ if (options.supabase) {
1339
+ content += `
1340
+ # --- Authentication (Supabase) ---
1341
+ VITE_SUPABASE_URL=https://your-project.supabase.co
1342
+ VITE_SUPABASE_KEY=your-anon-key
1343
+ `;
1344
+ }
1345
+ if (options.sentry) {
1346
+ content += `
1347
+ # --- Error Tracking (Sentry) ---
1348
+ VITE_SENTRY_DSN=https://your-sentry-dsn@o0.ingest.sentry.io/0
1349
+ `;
1350
+ }
1351
+ await this._fileService.writeFileRecursive(import_node_path10.default.join(projectPath, ".env"), content);
1352
+ await this._fileService.writeFileRecursive(import_node_path10.default.join(projectPath, ".env.example"), content);
1353
+ }
1354
+ };
1355
+ }
1356
+ });
1357
+
1358
+ // src/infrastructure/generators/vue/tsconfig.generator.ts
1359
+ var tsconfig_generator_exports = {};
1360
+ __export(tsconfig_generator_exports, {
1361
+ TsconfigVueGenerator: () => TsconfigVueGenerator
1362
+ });
1363
+ var import_node_path11, TsconfigVueGenerator;
1364
+ var init_tsconfig_generator = __esm({
1365
+ "src/infrastructure/generators/vue/tsconfig.generator.ts"() {
1366
+ "use strict";
1367
+ import_node_path11 = __toESM(require("path"));
1368
+ TsconfigVueGenerator = class {
1369
+ constructor(_fileService) {
1370
+ this._fileService = _fileService;
1371
+ }
1372
+ _fileService;
1373
+ async generate(projectPath, _options) {
1374
+ const tsconfig = {
1375
+ compilerOptions: {
1376
+ target: "ES2022",
1377
+ useDefineForClassFields: true,
1378
+ module: "ESNext",
1379
+ moduleResolution: "bundler",
1380
+ strict: true,
1381
+ jsx: "preserve",
1382
+ resolveJsonModule: true,
1383
+ isolatedModules: true,
1384
+ esModuleInterop: true,
1385
+ lib: ["ES2022", "DOM", "DOM.Iterable"],
1386
+ skipLibCheck: true,
1387
+ noEmit: true,
1388
+ types: ["vite/client"],
1389
+ paths: {
1390
+ "@/*": ["./src/*"]
1391
+ }
1392
+ },
1393
+ include: ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
1394
+ };
1395
+ await this._fileService.writeFileRecursive(
1396
+ import_node_path11.default.join(projectPath, "tsconfig.json"),
1397
+ JSON.stringify(tsconfig, null, 2)
1398
+ );
1399
+ }
1400
+ };
1401
+ }
1402
+ });
1403
+
1404
+ // src/infrastructure/generators/vue/readme.generator.ts
1405
+ var readme_generator_exports = {};
1406
+ __export(readme_generator_exports, {
1407
+ ReadmeVueGenerator: () => ReadmeVueGenerator
1408
+ });
1409
+ var import_node_path12, ReadmeVueGenerator;
1410
+ var init_readme_generator = __esm({
1411
+ "src/infrastructure/generators/vue/readme.generator.ts"() {
1412
+ "use strict";
1413
+ import_node_path12 = __toESM(require("path"));
1414
+ ReadmeVueGenerator = class {
1415
+ constructor(_fileService) {
1416
+ this._fileService = _fileService;
1417
+ }
1418
+ _fileService;
1419
+ async generate(projectPath, options) {
1420
+ const content = `# ${options.targetDir}
1421
+
1422
+ <div align="center">
1423
+ <img src="logo/logo.png" alt="Xeno Logo" width="140" />
1424
+
1425
+ <h1>Xeno Vue</h1>
1426
+
1427
+ <p><em>Enterprise-grade DDD & CQRS framework for Vue.js</em></p>
1428
+
1429
+ <p>
1430
+ <a href="https://github.com/xeno-js/xeno-js">
1431
+ <img src="https://img.shields.io/badge/Powered%20by-Xeno-blueviolet?style=flat-square" alt="Powered by Xeno" />
1432
+ </a>
1433
+ <a href="https://github.com/xeno-js/xeno-fe/blob/main/LICENSE">
1434
+ <img src="https://img.shields.io/npm/l/@xeno-js/vue?style=flat-square" alt="License: ISC" />
1435
+ </a>
1436
+ <a href="https://www.npmjs.com/package/@xeno-js/vue">
1437
+ <img src="https://img.shields.io/npm/v/@xeno-js/vue?style=flat-square" alt="NPM Version" />
1438
+ </a>
1439
+ <a href="https://buymeacoffee.com/xenojs">
1440
+ <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
1441
+ </a>
1442
+ </p>
1443
+ </div>
1444
+
1445
+ ---
1446
+
1447
+ ## What is Xeno Vue?
1448
+
1449
+ **Xeno Vue** (\`@xeno-js/vue\`) is an enterprise-grade, deterministic
1450
+ architectural framework that brings the strictness of **Domain-Driven Design
1451
+ (DDD)** and **Command Query Responsibility Segregation (CQRS)** natively to the
1452
+ browser.
1453
+
1454
+ By shifting operational logic, remote data fetching, and state mutations away
1455
+ from Vue components and Pinia stores, Xeno ensures your frontend architecture
1456
+ remains pristine, highly testable, and completely decoupled from the UI layer.
1457
+ It treats the browser as a complex distributed client, not just a document
1458
+ viewer.
1459
+
1460
+ ---
1461
+
1462
+ ## \u{1F4A1} Why Choose Xeno Vue?
1463
+
1464
+ Modern frontend development often leads to "Spaghetti State" where API calls,
1465
+ business rules, and DOM manipulations are tightly coupled inside components.
1466
+ Xeno Vue fixes this with an emphasis on pure Dependency Injection (DI) and clean
1467
+ boundaries.
1468
+
1469
+ - **Zero-Magic Dependency Injection**: Xeno provides an explicit
1470
+ \`XenoAppBuilder\` to construct your IoC container at bootstrap. No hidden Vue
1471
+ plugins doing implicit injections. You have total control over the dependency
1472
+ graph.
1473
+ - **Total UI Decoupling**: Vue components act strictly as the Presentation
1474
+ Layer. Business logic, API calls, and CQRS handlers live in pure, isolated
1475
+ TypeScript classes. You can swap Vue for React tomorrow without touching your
1476
+ core domain.
1477
+ - **Enterprise-Grade Client Resiliency**: The browser is a hostile, unreliable
1478
+ environment. Xeno Vue natively integrates \`Cockatiel\` and \`Axios\` to provide
1479
+ out-of-the-box circuit breakers, retries with jitter, and bulkheads directly
1480
+ in the client.
1481
+ - **Frontend Middleware Pipeline**: Handle Authentication, CSRF validation,
1482
+ aggressive Query Caching, and Performance logging _before_ a command is
1483
+ executed or an API call fires, using the native \`Mediator\` pipeline.
1484
+
1485
+ ---
1486
+
1487
+ ## \u{1F4D6} Documentation & Getting Started
1488
+
1489
+ To explore the architecture, programmatic configurations, and extension
1490
+ workflows of Xeno Vue, read the full technical manuals located inside the main
1491
+ documentation hub:
1492
+
1493
+ - **[Framework Documentation Repository](https://www.xeno-js.it/vue/overview)**
1494
+
1495
+ Inside, you will find exhaustive, step-by-step assembly guides covering frontend
1496
+ IoC building (\`XenoAppBuilder\`), Vue \`Provide/Inject\` boundaries, and CQRS
1497
+ composables.
1498
+
1499
+ ---
1500
+
1501
+ ## \u{1F680} Live Executable Demos
1502
+
1503
+ Want to see how Xeno Vue works? Check out the functional example application
1504
+ showcasing end-to-end command/query segregation, resilient HTTP fetching, and
1505
+ pure Composition API integration.
1506
+
1507
+ - **[Xeno Vue Demo Architecture](https://www.xeno-js.it/demo)**
1508
+
1509
+ ---
1510
+
1511
+ ## \u{1F4E6} Installation
1512
+
1513
+ Install the Vue package:
1514
+
1515
+ \`\`\`bash
1516
+ npm install @xeno-js/vue
1517
+
1518
+ \`\`\`
1519
+
1520
+ Xeno uses **Optional Peer Dependencies**. You only install the external
1521
+ libraries you actually need.
1522
+
1523
+ \`\`\`bash
1524
+ # Example: Install tools only if you enable them in the builder
1525
+ npm install axios cockatiel zod @supabase/supabase-js @sentry/vue
1526
+
1527
+ \`\`\`
1528
+
1529
+ ---
1530
+
1531
+ ## \u26A1 Bootstrapping & Component Example
1532
+
1533
+ Below is an architectural example of how to configure the Xeno \`XenoAppBuilder\`,
1534
+ inject it into the Vue application, and consume it using Composition API
1535
+ Composables.
1536
+
1537
+ ### 1. Initialize the Container (\`src/bootstrap.ts\`)
1538
+
1539
+ \`\`\`typescript
1540
+ import { XenoAppBuilder, LOG_LEVEL, TOKENS } from '@xeno-js/vue'
1541
+ import { BffRemoteDataSource } from './infrastructure'
1542
+
1543
+ import type { RemoteDataSource, XenoVueRegistry } from '@xeno-js/vue'
1544
+
1545
+ export type MyRegistry = XenoVueRegistry<{
1546
+ BFF_REMOTE_DS: RemoteDataSource
1547
+ }>
1548
+
1549
+ // Create the root IoC container context for the browser
1550
+ const builder = XenoAppBuilder.create<MyRegistry>()
1551
+ .addContext((opts) => {
1552
+ opts.contextAccessor = useContextStore()
1553
+ })
1554
+ .addLogger((opts, config) => {
1555
+ opts.console = config.get('VITE_APP_ENV') === 'development'
1556
+ opts.level = LOG_LEVEL.DEBUG
1557
+ })
1558
+ .addAuth((opts, config) => {
1559
+ opts.url = config.getOrThrow('VITE_SUPABASE_URL')
1560
+ opts.key = config.getOrThrow('VITE_SUPABASE_KEY')
1561
+ })
1562
+ .addHttpCore('BFF_REMOTE_DS', (opts, config) => {
1563
+ opts.client = {
1564
+ baseURL: config.getOrThrow('VITE_API_BASE_URL'),
1565
+ timeoutMs: 10000,
1566
+ }
1567
+ opts.resilience.retry.attempts = 3
1568
+ opts.factory = (http, resilience) =>
1569
+ new BffRemoteDataSource(http, resilience)
1570
+ })
1571
+ .addPipeline((config) => {
1572
+ config.queryCaching = true // Enable in-memory caching for Queries
1573
+ })
1574
+
1575
+ export async function bootstrap() {
1576
+ return await builder.build()
1577
+ }
1578
+ \`\`\`
1579
+
1580
+ ### 2. Inject into Vue (\`src/main.ts\`)
1581
+
1582
+ \`\`\`typescript
1583
+ import { createApp } from 'vue'
1584
+ import App from './App.vue'
1585
+ import { bootstrap } from './bootstrap'
1586
+ import { XENO_SERVICES_KEY, ServicesUtils } from '@xeno-js/vue'
1587
+
1588
+ async function mountApp() {
1589
+ try {
1590
+ const app = createApp(App)
1591
+ const container = await bootstrap()
1592
+
1593
+ // Provide the Xeno IoC container globally to all components
1594
+ app.provide(XENO_SERVICES_KEY, container)
1595
+
1596
+ // Register global services utility for composables
1597
+ ServicesUtils.setGlobalServices(container)
1598
+
1599
+ app.mount('#app')
1600
+ } catch (error) {
1601
+ console.error('Critical error during frontend bootstrap:', error)
1602
+ }
1603
+ }
1604
+
1605
+ mountApp()
1606
+ \`\`\`
1607
+
1608
+ ### 3. CQRS Composable Usage (\`src/features/users/use-create-user.ts\`)
1609
+
1610
+ \`\`\`typescript
1611
+ import { ref } from 'vue'
1612
+ import { Result } from '@xeno-js/shared'
1613
+ import { ServicesUtils } from '@/use-app'
1614
+ import { CreateUserCommand } from './create-user.command'
1615
+
1616
+ export function useCreateUser() {
1617
+ const loading = ref(false)
1618
+ const error = ref<string | null>(null)
1619
+
1620
+ const execute = async (payload: { email: string; name: string }) => {
1621
+ if (loading.value) return Result.fail(new Error('Already executing'))
1622
+ loading.value = true
1623
+ error.value = null
1624
+
1625
+ try {
1626
+ // Safely resolve the Mediator from the Xeno Container
1627
+ const { mediator, BFF_REMOTE_DS } = ServicesUtils.useApp()
1628
+ const command = new CreateUserCommand()
1629
+
1630
+ const result = await mediator.send(command, async () => {
1631
+ const apiResult = await bffRemoteDs.post('/v1/users', payload)
1632
+
1633
+ if (!apiResult.isOk()) return Result.fail(apiResult.getErrorOrThrow())
1634
+ return Result.ok(apiResult.getValueOrThrow())
1635
+ })
1636
+
1637
+ if (!result.isOk()) {
1638
+ error.value = result.getErrorOrThrow().message
1639
+ }
1640
+
1641
+ return result
1642
+ } finally {
1643
+ loading.value = false
1644
+ }
1645
+ }
1646
+
1647
+ return { loading, error, execute }
1648
+ }
1649
+ \`\`\`
1650
+
1651
+ ---
1652
+
1653
+ ## \u{1F6E0} Scaffold your project with CLI
1654
+
1655
+ Xeno includes an official CLI tool, \`@xeno-js/cli\`, designed to bootstrap your
1656
+ new application in seconds. It offers an interactive setup for Vue projects,
1657
+ instantly scaffolding the CQRS files, composables, and dependency injection
1658
+ boundaries.
1659
+
1660
+ \`\`\`bash
1661
+ # Generate a new Vue project
1662
+ npx @xeno-js/cli new my-frontend-app --vue
1663
+
1664
+ # Generate a Vue Command with its Composable
1665
+ npx @xeno-js/cli g command CreateUser --vue
1666
+
1667
+ \`\`\`
1668
+
1669
+ Check the
1670
+ **[CLI Documentation](https://www.google.com/search?q=https://www.xeno-js.it/cli/overview&utm_source=gemini)**
1671
+ for full options.
1672
+
1673
+ ---
1674
+
1675
+ ## \u{1F91D} For Contributors
1676
+
1677
+ We welcome contributions to Xeno! To maintain the highest code quality and
1678
+ stability of the core framework, **direct pushes to the \`main\` and \`develop\`
1679
+ branches are strictly prohibited.** Please follow this Git Flow to contribute:
1680
+
1681
+ 1. **Branch off from \`develop**\`: Create a new branch for your feature or
1682
+ bugfix.
1683
+
1684
+ \`\`\`bash
1685
+ git checkout develop
1686
+ git pull origin develop
1687
+ git checkout -b feat/your-awesome-feature
1688
+
1689
+ \`\`\`
1690
+
1691
+ 2. **Make your changes**: Write your code and ensure it passes all local checks
1692
+ (linting, types, and tests).
1693
+
1694
+ \`\`\`bash
1695
+ npm run check
1696
+
1697
+ \`\`\`
1698
+
1699
+ 3. **Commit your changes**: We enforce
1700
+ [Conventional Commits](https://www.conventionalcommits.org/?utm_source=gemini).
1701
+ Husky will verify your commit message format.
1702
+ 4. **Commit Format:**
1703
+
1704
+ \`\`\`bash
1705
+ feat(scope): add new feature
1706
+ fix(scope): resolve bug
1707
+ chore(scope): update dependencies
1708
+
1709
+ \`\`\`
1710
+
1711
+ 5. **Submit a Pull Request (PR)**: Push your branch to GitHub and open a Pull
1712
+ Request targeting the **\`develop\`** branch.
1713
+ 6. **Review**: The repository owner will review your code, run pipeline tests,
1714
+ and merge it into \`develop\`.
1715
+
1716
+ _Note: The \`main\` branch is strictly reserved for production releases. Code
1717
+ flows from feature branches \u27A1\uFE0F \`develop\` \u27A1\uFE0F \`main\`._
1718
+
1719
+ ### Scripts
1720
+
1721
+ | Command | Description |
1722
+ | ----------------------- | ---------------------------------------------- |
1723
+ | \`npm run build\` | Builds the TypeScript source code into \`dist/\` |
1724
+ | \`npm run typecheck\` | Checks types without emitting files |
1725
+ | \`npm run lint\` | Runs ESLint |
1726
+ | \`npm run format\` | Formats code with Prettier |
1727
+ | \`npm run test\` | Runs the Vitest test suite |
1728
+ | \`npm run test:coverage\` | Runs tests and generates a coverage report |
1729
+
1730
+ ### Code Quality (Husky & Git Hooks)
1731
+
1732
+ This project strictly enforces code quality rules before pushing to the
1733
+ repository:
1734
+
1735
+ - **\`pre-commit\`**: Runs \`lint-staged\` on staged files (ESLint + Prettier).
1736
+ - **\`commit-msg\`**: Checks commit messages with \`commitlint\` (we use
1737
+ Conventional Commits).
1738
+ - **\`pre-push\`**: Runs type checking, linting, and testing before code leaves
1739
+ your machine.
1740
+
1741
+ ---
1742
+
1743
+ ## \u{1F331} Support & Appreciation
1744
+
1745
+ Building, benchmarking, and maintaining a progressive, enterprise-ready
1746
+ open-source framework requires a massive amount of continuous dedication and
1747
+ architectural engineering.
1748
+
1749
+ If Xeno has brought value to your development workflows, helped decouple your
1750
+ core business logic, or simplified your system infrastructure layout, consider
1751
+ supporting its open-source lifecycle. Your backing directly accelerates our
1752
+ strategic roadmap for new out-of-the-box transport integrations and keeps the
1753
+ documentation pristine.
1754
+
1755
+ **Want to know how you can contribute or sponsor Xeno?** We rely on the
1756
+ commitment of our community to keep the project independent and thriving.
1757
+ Whether you are an individual developer or a business using Xeno, your support
1758
+ makes a real difference.
1759
+
1760
+ \u{1F449}
1761
+ **[Read our support guidelines and find out how to help](https://www.xeno-js.it/support-us)**
1762
+
1763
+ Thank you for being part of this decoupled open-source journey!
1764
+
1765
+ <amp-bounce>
1766
+ </amp-bounce>
1767
+ <a href="https://www.buymeacoffee.com/xenojs" target="_blank">
1768
+ <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42" style="height: 42px !important;" />
1769
+ </a>
1770
+
1771
+ ---
1772
+
1773
+ ## \u{1F6E1}\uFE0F Powered by Xeno
1774
+
1775
+ If you are using Xeno in your project, let the world know! Add this badge to
1776
+ your README:
1777
+
1778
+ \`\`\`html
1779
+ <a
1780
+ href="[https://github.com/xeno-js/xeno-fe](https://github.com/xeno-js/xeno-fe)"
1781
+ target="_blank"
1782
+ >
1783
+ <img
1784
+ src="[https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square](https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square)"
1785
+ alt="Powered by Xeno"
1786
+ height="20"
1787
+ />
1788
+ </a>
1789
+ \`\`\`
1790
+
1791
+ ## \u{1F4C4} License
1792
+
1793
+ Copyright (c) 2026 Xeno. Licensed under the
1794
+ [ISC License](https://www.google.com/search?q=LICENSE&utm_source=gemini).
1795
+ `;
1796
+ await this._fileService.writeFileRecursive(import_node_path12.default.join(projectPath, "README.md"), content);
1797
+ }
1798
+ };
1799
+ }
1800
+ });
1801
+
1802
+ // src/infrastructure/generators/gitignore.generator.ts
1803
+ var gitignore_generator_exports = {};
1804
+ __export(gitignore_generator_exports, {
1805
+ GitIgnoreGenerator: () => GitIgnoreGenerator
1806
+ });
1807
+ var import_node_path13, GitIgnoreGenerator;
1808
+ var init_gitignore_generator = __esm({
1809
+ "src/infrastructure/generators/gitignore.generator.ts"() {
1810
+ "use strict";
1811
+ import_node_path13 = __toESM(require("path"));
1812
+ init_shared();
1813
+ GitIgnoreGenerator = class {
1814
+ constructor(_fileService) {
1815
+ this._fileService = _fileService;
1816
+ }
1817
+ _fileService;
1818
+ async generate(projectPath, options) {
1819
+ let content = `# Dependencies
1820
+ node_modules/
1821
+
1822
+ # Build artifacts
1823
+ dist/
1824
+ out/
1825
+ build/
1826
+
1827
+ # Environment variables
1828
+ # We ignore actual .env files for security, but keep .env.example for documentation
1829
+ .env
1830
+ .env.local
1831
+ .env.*
1832
+ !.env.example
1833
+
1834
+ # Logs
1835
+ *.log
1836
+ logs/
1837
+ npm-debug.log*
1838
+
1839
+ # Test coverage
1840
+ coverage/
1841
+ .nyc_output/
1842
+
1843
+ # OS and IDE
1844
+ .DS_Store
1845
+ Thumbs.db
1846
+ .vscode/
1847
+ .idea/
1848
+ `;
1849
+ if (options.database === CORE_CONSTANTS.SQL_LITE) {
1850
+ content += `
1851
+ # SQLite Database
1852
+ *.sqlite
1853
+ *.sqlite3
1854
+ *.sqlite-journal
1855
+ `;
1856
+ }
1857
+ if (options.targetEnv === CORE_CONSTANTS.VERCEL) {
1858
+ content += `
1859
+ # Vercel
1860
+ .vercel/
1861
+ `;
1862
+ }
1863
+ const filePath = import_node_path13.default.join(projectPath, ".gitignore");
1864
+ await this._fileService.writeFileRecursive(filePath, content);
1865
+ }
1866
+ };
1867
+ }
1868
+ });
1869
+
1870
+ // src/infrastructure/generators/vue/app-vue.generator.ts
1871
+ var app_vue_generator_exports = {};
1872
+ __export(app_vue_generator_exports, {
1873
+ AppVueGenerator: () => AppVueGenerator
1874
+ });
1875
+ var import_node_path14, AppVueGenerator;
1876
+ var init_app_vue_generator = __esm({
1877
+ "src/infrastructure/generators/vue/app-vue.generator.ts"() {
1878
+ "use strict";
1879
+ import_node_path14 = __toESM(require("path"));
1880
+ AppVueGenerator = class {
1881
+ constructor(_fileService) {
1882
+ this._fileService = _fileService;
1883
+ }
1884
+ _fileService;
1885
+ async generate(projectPath, options) {
1886
+ const appVue = `<script setup lang="ts">
1887
+ </script>
1888
+
1889
+ <template>
1890
+ <div id="xeno-app">
1891
+ ${options.router ? "<router-view />" : "<h1>Welcome to Xeno Vue App</h1>"}
1892
+ </div>
1893
+ </template>`;
1894
+ await this._fileService.writeFileRecursive(import_node_path14.default.join(projectPath, "src", "App.vue"), appVue);
1895
+ }
1896
+ };
1897
+ }
1898
+ });
1899
+
1900
+ // src/infrastructure/generators/vue/bootstrap-ts.generator.ts
1901
+ var bootstrap_ts_generator_exports = {};
1902
+ __export(bootstrap_ts_generator_exports, {
1903
+ BootstrapGenerator: () => BootstrapGenerator
1904
+ });
1905
+ var import_node_path15, BootstrapGenerator;
1906
+ var init_bootstrap_ts_generator = __esm({
1907
+ "src/infrastructure/generators/vue/bootstrap-ts.generator.ts"() {
1908
+ "use strict";
1909
+ import_node_path15 = __toESM(require("path"));
1910
+ BootstrapGenerator = class {
1911
+ constructor(_fileService) {
1912
+ this._fileService = _fileService;
1913
+ }
1914
+ _fileService;
1915
+ async generate(projectPath, options) {
1916
+ let builderNodes = ` const builder = XenoAppBuilder.create<MyRegistry>()
1917
+ .addContext(() => {})`;
1918
+ if (options.supabase) {
1919
+ builderNodes += `
1920
+ .addAuth((opts, config) => {
1921
+ opts.url = config.getOrThrow('VITE_SUPABASE_URL');
1922
+ opts.key = config.getOrThrow('VITE_SUPABASE_KEY');
1923
+ })`;
1924
+ }
1925
+ if (options.sentry) {
1926
+ builderNodes += `
1927
+ .addLogger((opts, config) => {
1928
+ const env = config.get(COMMON_CONSTANTS.ENV) ?? 'development'
1929
+ const isDev = env.toLowerCase() === 'development'
1930
+ opts.console = isDev
1931
+ if (!isDev) {
1932
+ opts.sentry = {
1933
+ dsn: config.getOrThrow('VITE_SENTRY_DSN'),
1934
+ env,
1935
+ app,
1936
+ router,
1937
+ tracesSampleRate: 0.2,
1938
+ level: LOG_LEVEL.WARN,
1939
+ };
1940
+ }
1941
+ })`;
1942
+ } else {
1943
+ builderNodes += `
1944
+ .addLogger((opts, config) => {
1945
+ opts.console = config.get('VITE_APP_ENV') === 'development';
1946
+ })`;
1947
+ }
1948
+ builderNodes += `
1949
+ .addServices((config, register) => {
1950
+ // Register custom stores, API clients, and CQRS services here
1951
+ })`;
1952
+ const bootstrapTs = `import { XenoAppBuilder, LOG_LEVEL } from '@xeno-js/vue';
1953
+ import type { MyRegistry } from './registry';
1954
+
1955
+ export async function bootstrap(app: any, router: any) {
1956
+ ${builderNodes}
1957
+
1958
+ return await builder.build();
1959
+ }
1960
+ `;
1961
+ await this._fileService.writeFileRecursive(
1962
+ import_node_path15.default.join(projectPath, "src", "bootstrap.ts"),
1963
+ bootstrapTs
1964
+ );
1965
+ }
1966
+ };
1967
+ }
1968
+ });
1969
+
1970
+ // src/infrastructure/generators/vue/index-html.generator.ts
1971
+ var index_html_generator_exports = {};
1972
+ __export(index_html_generator_exports, {
1973
+ IndexVueGenerator: () => IndexVueGenerator
1974
+ });
1975
+ var import_node_path16, IndexVueGenerator;
1976
+ var init_index_html_generator = __esm({
1977
+ "src/infrastructure/generators/vue/index-html.generator.ts"() {
1978
+ "use strict";
1979
+ import_node_path16 = __toESM(require("path"));
1980
+ IndexVueGenerator = class {
1981
+ constructor(_fileService) {
1982
+ this._fileService = _fileService;
1983
+ }
1984
+ _fileService;
1985
+ async generate(projectPath, options) {
1986
+ const indexHtml = `<!DOCTYPE html>
1987
+ <html lang="en">
1988
+ <head>
1989
+ <meta charset="UTF-8" />
1990
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1991
+ <title>${options.targetDir}</title>
1992
+ </head>
1993
+ <body>
1994
+ <div id="app"></div>
1995
+ <script type="module" src="/src/main.ts"></script>
1996
+ </body>
1997
+ </html>`;
1998
+ await this._fileService.writeFileRecursive(import_node_path16.default.join(projectPath, "index.html"), indexHtml);
1999
+ }
2000
+ };
2001
+ }
2002
+ });
2003
+
2004
+ // src/infrastructure/generators/vue/main.generator.ts
2005
+ var main_generator_exports = {};
2006
+ __export(main_generator_exports, {
2007
+ MainGenerator: () => MainGenerator
2008
+ });
2009
+ var import_node_path17, MainGenerator;
2010
+ var init_main_generator = __esm({
2011
+ "src/infrastructure/generators/vue/main.generator.ts"() {
2012
+ "use strict";
2013
+ import_node_path17 = __toESM(require("path"));
2014
+ MainGenerator = class {
2015
+ constructor(_fileService) {
2016
+ this._fileService = _fileService;
2017
+ }
2018
+ _fileService;
2019
+ async generate(projectPath, options) {
2020
+ const routerImport = options.router ? `
2021
+ import router from './router';` : "";
2022
+ const piniaImport = options.pinia ? `
2023
+ import { createPinia } from 'pinia';` : "";
2024
+ const tailwindImport = options.tailwind ? `
2025
+ import './assets/style.css';` : "";
2026
+ const mainTs = `import { createApp } from 'vue';
2027
+ import App from './App.vue';
2028
+ import { bootstrap } from './bootstrap';
2029
+ import { XENO_SERVICES_KEY } from '@xeno-js/vue';${routerImport}${piniaImport}${tailwindImport}
2030
+
2031
+ async function mountApp() {
2032
+ try {
2033
+ const app = createApp(App);
2034
+ ${options.pinia ? "\n app.use(createPinia());" : ""}
2035
+ const container = await bootstrap(app, router);
2036
+ ${options.router ? "\n app.use(router);" : ""}
2037
+
2038
+
2039
+ // Provide the Xeno IoC container globally to all components
2040
+ app.provide(XENO_SERVICES_KEY, container);
2041
+
2042
+ app.mount('#app');
2043
+ } catch (error) {
2044
+ console.error('Critical error during frontend bootstrap:', error);
2045
+ }
2046
+ }
2047
+
2048
+ mountApp();
2049
+ `;
2050
+ await this._fileService.writeFileRecursive(import_node_path17.default.join(projectPath, "src", "main.ts"), mainTs);
2051
+ }
2052
+ };
2053
+ }
2054
+ });
2055
+
2056
+ // src/infrastructure/generators/vue/registry-ts.generator.ts
2057
+ var registry_ts_generator_exports = {};
2058
+ __export(registry_ts_generator_exports, {
2059
+ RegistryGenerator: () => RegistryGenerator
2060
+ });
2061
+ var import_node_path18, RegistryGenerator;
2062
+ var init_registry_ts_generator = __esm({
2063
+ "src/infrastructure/generators/vue/registry-ts.generator.ts"() {
2064
+ "use strict";
2065
+ import_node_path18 = __toESM(require("path"));
2066
+ RegistryGenerator = class {
2067
+ constructor(_fileService) {
2068
+ this._fileService = _fileService;
2069
+ }
2070
+ _fileService;
2071
+ async generate(projectPath, _options) {
2072
+ const registryTs = `import type { XenoVueRegistry } from '@xeno-js/vue';
2073
+
2074
+ /**
2075
+ * @file registry.ts
2076
+ * @description Centralized Type Contract for Dependency Injection in Vue.
2077
+ */
2078
+ export interface MyRegistry extends XenoVueRegistry {
2079
+ // Add your frontend injection tokens here
2080
+ // e.g., USER_DATA_SOURCE: IUserDataSource;
2081
+ }
2082
+ `;
2083
+ await this._fileService.writeFileRecursive(
2084
+ import_node_path18.default.join(projectPath, "src", "registry.ts"),
2085
+ registryTs
2086
+ );
2087
+ }
2088
+ };
2089
+ }
2090
+ });
2091
+
2092
+ // src/infrastructure/generators/vue/router.generator.ts
2093
+ var router_generator_exports = {};
2094
+ __export(router_generator_exports, {
2095
+ RouterGenerator: () => RouterGenerator
2096
+ });
2097
+ var import_node_path19, RouterGenerator;
2098
+ var init_router_generator = __esm({
2099
+ "src/infrastructure/generators/vue/router.generator.ts"() {
2100
+ "use strict";
2101
+ import_node_path19 = __toESM(require("path"));
2102
+ RouterGenerator = class {
2103
+ constructor(_fileService) {
2104
+ this._fileService = _fileService;
2105
+ }
2106
+ _fileService;
2107
+ async generate(projectPath, options) {
2108
+ if (options.router) {
2109
+ const routerTs = `import { createRouter, createWebHistory } from 'vue-router';
2110
+
2111
+ export default createRouter({
2112
+ history: createWebHistory(),
2113
+ routes: [
2114
+ {
2115
+ path: '/',
2116
+ name: 'Home',
2117
+ component: () => import('../views/HomeView.vue')
2118
+ }
2119
+ ]
2120
+ });`;
2121
+ await this._fileService.writeFileRecursive(
2122
+ import_node_path19.default.join(projectPath, "src", "router", "index.ts"),
2123
+ routerTs
2124
+ );
2125
+ await this._fileService.writeFileRecursive(
2126
+ import_node_path19.default.join(projectPath, "src", "views", "HomeView.vue"),
2127
+ `<template>
2128
+ <h1>Home View</h1>
2129
+ </template>`
2130
+ );
2131
+ }
2132
+ }
2133
+ };
2134
+ }
2135
+ });
2136
+
2137
+ // src/infrastructure/generators/vue/tailwind.generator.ts
2138
+ var tailwind_generator_exports = {};
2139
+ __export(tailwind_generator_exports, {
2140
+ TailwindGenerator: () => TailwindGenerator
2141
+ });
2142
+ var import_node_path20, TailwindGenerator;
2143
+ var init_tailwind_generator = __esm({
2144
+ "src/infrastructure/generators/vue/tailwind.generator.ts"() {
2145
+ "use strict";
2146
+ import_node_path20 = __toESM(require("path"));
2147
+ TailwindGenerator = class {
2148
+ constructor(_fileService) {
2149
+ this._fileService = _fileService;
2150
+ }
2151
+ _fileService;
2152
+ async generate(projectPath, options) {
2153
+ if (options.tailwind) {
2154
+ await this._fileService.writeFileRecursive(
2155
+ import_node_path20.default.join(projectPath, "src", "assets", "style.css"),
2156
+ `@import "tailwindcss";
2157
+ `
2158
+ );
2159
+ }
2160
+ }
2161
+ };
2162
+ }
2163
+ });
2164
+
2165
+ // src/infrastructure/generators/vue/use-app.generator.ts
2166
+ var use_app_generator_exports = {};
2167
+ __export(use_app_generator_exports, {
2168
+ UseAppVueGenerator: () => UseAppVueGenerator
2169
+ });
2170
+ var import_node_path21, UseAppVueGenerator;
2171
+ var init_use_app_generator = __esm({
2172
+ "src/infrastructure/generators/vue/use-app.generator.ts"() {
2173
+ "use strict";
2174
+ import_node_path21 = __toESM(require("path"));
2175
+ UseAppVueGenerator = class {
2176
+ constructor(_fileService) {
2177
+ this._fileService = _fileService;
2178
+ }
2179
+ _fileService;
2180
+ async generate(projectPath, _options) {
2181
+ const content = `import type { Nullable } from "@xeno-js/vue";
2182
+ import { Guards, XENO_SERVICES_KEY } from "@xeno-js/vue";
2183
+ import { inject } from "vue";
2184
+ import type { MyRegistry } from "./registry";
2185
+
2186
+ let globalServices: Nullable<MyRegistry> = null;
2187
+
2188
+ export const ServicesUtils = Object.freeze({
2189
+ // Metodo per registrare i servizi al bootstrap
2190
+ setGlobalServices(services: MyRegistry) {
2191
+ globalServices = services;
2192
+ },
2193
+ useApp(): MyRegistry {
2194
+ if (Guards.isDefined(globalServices))
2195
+ return globalServices;
2196
+
2197
+ const services = inject<MyRegistry>(XENO_SERVICES_KEY);
2198
+ if (!services)
2199
+ throw new Error(
2200
+ '[Xeno Error]: XenoServices not found. Did you provide them using app.provide(XENO_SERVICES_KEY, services)?',
2201
+ );
2202
+
2203
+ return services;
2204
+ }
2205
+ } as const);
2206
+ `;
2207
+ await this._fileService.writeFileRecursive(import_node_path21.default.join(projectPath, "src", "use-app.ts"), content);
2208
+ }
2209
+ };
2210
+ }
2211
+ });
2212
+
2213
+ // src/infrastructure/strategies/vue-scaffold.strategy.ts
2214
+ var vue_scaffold_strategy_exports = {};
2215
+ __export(vue_scaffold_strategy_exports, {
2216
+ VueScaffoldStrategy: () => VueScaffoldStrategy
2217
+ });
2218
+ var import_prompts, VueScaffoldStrategy;
2219
+ var init_vue_scaffold_strategy = __esm({
2220
+ "src/infrastructure/strategies/vue-scaffold.strategy.ts"() {
2221
+ "use strict";
2222
+ import_prompts = __toESM(require("prompts"));
2223
+ VueScaffoldStrategy = class {
2224
+ constructor(_fileService) {
2225
+ this._fileService = _fileService;
2226
+ }
2227
+ _fileService;
2228
+ async promptOptions(targetDir) {
2229
+ const response = await (0, import_prompts.default)([
2230
+ { type: "confirm", name: "axios", message: "Install Axios?", initial: true },
2231
+ {
2232
+ type: "confirm",
2233
+ name: "cockatiel",
2234
+ message: "Install Cockatiel (Resilience)?",
2235
+ initial: true
2236
+ },
2237
+ { type: "confirm", name: "supabase", message: "Install Supabase Auth?", initial: true },
2238
+ { type: "confirm", name: "sentry", message: "Install Sentry Logger?", initial: true },
2239
+ {
2240
+ type: "confirm",
2241
+ name: "pinia",
2242
+ message: "Install Pinia (State Management)?",
2243
+ initial: true
2244
+ },
2245
+ { type: "confirm", name: "router", message: "Install vue-router?", initial: true },
2246
+ { type: "confirm", name: "tailwind", message: "Install Tailwind CSS?", initial: true },
2247
+ { type: "confirm", name: "zod", message: "Install Zod Validator?", initial: true },
2248
+ {
2249
+ type: "confirm",
2250
+ name: "gitInit",
2251
+ message: "Initialize a new Git repository?",
2252
+ initial: true
2253
+ }
2254
+ ]);
2255
+ return {
2256
+ targetDir,
2257
+ projectType: "vue",
2258
+ ...response
2259
+ };
2260
+ }
2261
+ async getGenerators(_options) {
2262
+ const generators = [];
2263
+ const { PackageJsonVueGenerator: PackageJsonVueGenerator2 } = await Promise.resolve().then(() => (init_packagejson_generator(), packagejson_generator_exports));
2264
+ const { ViteconfigGenerator: ViteconfigGenerator2 } = await Promise.resolve().then(() => (init_vite_config_generator(), vite_config_generator_exports));
2265
+ const { EnvVueGenerator: EnvVueGenerator2 } = await Promise.resolve().then(() => (init_env_generator(), env_generator_exports));
2266
+ const { TsconfigVueGenerator: TsconfigVueGenerator2 } = await Promise.resolve().then(() => (init_tsconfig_generator(), tsconfig_generator_exports));
2267
+ const { ReadmeVueGenerator: ReadmeVueGenerator2 } = await Promise.resolve().then(() => (init_readme_generator(), readme_generator_exports));
2268
+ const { GitIgnoreGenerator: GitIgnoreGenerator2 } = await Promise.resolve().then(() => (init_gitignore_generator(), gitignore_generator_exports));
2269
+ const { AppVueGenerator: AppVueGenerator2 } = await Promise.resolve().then(() => (init_app_vue_generator(), app_vue_generator_exports));
2270
+ const { BootstrapGenerator: BootstrapGenerator3 } = await Promise.resolve().then(() => (init_bootstrap_ts_generator(), bootstrap_ts_generator_exports));
2271
+ const { IndexVueGenerator: IndexVueGenerator2 } = await Promise.resolve().then(() => (init_index_html_generator(), index_html_generator_exports));
2272
+ const { MainGenerator: MainGenerator3 } = await Promise.resolve().then(() => (init_main_generator(), main_generator_exports));
2273
+ const { RegistryGenerator: RegistryGenerator3 } = await Promise.resolve().then(() => (init_registry_ts_generator(), registry_ts_generator_exports));
2274
+ const { RouterGenerator: RouterGenerator2 } = await Promise.resolve().then(() => (init_router_generator(), router_generator_exports));
2275
+ const { TailwindGenerator: TailwindGenerator2 } = await Promise.resolve().then(() => (init_tailwind_generator(), tailwind_generator_exports));
2276
+ const { UseAppVueGenerator: UseAppVueGenerator2 } = await Promise.resolve().then(() => (init_use_app_generator(), use_app_generator_exports));
2277
+ generators.push(new PackageJsonVueGenerator2(this._fileService));
2278
+ generators.push(new ViteconfigGenerator2(this._fileService));
2279
+ generators.push(new EnvVueGenerator2(this._fileService));
2280
+ generators.push(new TsconfigVueGenerator2(this._fileService));
2281
+ generators.push(new AppVueGenerator2(this._fileService));
2282
+ generators.push(new BootstrapGenerator3(this._fileService));
2283
+ generators.push(new IndexVueGenerator2(this._fileService));
2284
+ generators.push(new MainGenerator3(this._fileService));
2285
+ generators.push(new RegistryGenerator3(this._fileService));
2286
+ generators.push(new RouterGenerator2(this._fileService));
2287
+ generators.push(new TailwindGenerator2(this._fileService));
2288
+ generators.push(new ReadmeVueGenerator2(this._fileService));
2289
+ generators.push(new GitIgnoreGenerator2(this._fileService));
2290
+ generators.push(new UseAppVueGenerator2(this._fileService));
2291
+ return generators;
2292
+ }
2293
+ };
2294
+ }
2295
+ });
2296
+
2297
+ // src/infrastructure/generators/core/packagejson.generator.ts
2298
+ var packagejson_generator_exports2 = {};
2299
+ __export(packagejson_generator_exports2, {
2300
+ PackageJsonCoreGenerator: () => PackageJsonCoreGenerator
2301
+ });
2302
+ var import_node_path22, PackageJsonCoreGenerator;
2303
+ var init_packagejson_generator2 = __esm({
2304
+ "src/infrastructure/generators/core/packagejson.generator.ts"() {
2305
+ "use strict";
2306
+ import_node_path22 = __toESM(require("path"));
2307
+ init_shared();
2308
+ PackageJsonCoreGenerator = class {
2309
+ constructor(_fileService) {
2310
+ this._fileService = _fileService;
2311
+ }
2312
+ _fileService;
2313
+ async generate(projectPath, options) {
2314
+ const packageJson = {
2315
+ name: options.targetDir,
2316
+ version: "1.0.0",
2317
+ private: true,
2318
+ main: "./dist/index.js",
2319
+ module: "./dist/index.js",
2320
+ types: "./dist/index.d.ts",
2321
+ scripts: {
2322
+ start: "tsx src/main.ts",
2323
+ build: "tsc --project tsconfig.json",
2324
+ g: "xeno-js generate"
2325
+ },
2326
+ dependencies: {
2327
+ "@xeno-js/core": "latest",
2328
+ "dotenv": "^16.4.5"
2329
+ },
2330
+ devDependencies: {
2331
+ "@types/node": "^20.0.0",
2332
+ "tsx": "^4.7.0",
2333
+ "typescript": "^5.4.0"
2334
+ }
2335
+ };
2336
+ if (options.database !== CORE_CONSTANTS.NONE) {
2337
+ if (options.database === CORE_CONSTANTS.DRIZZLE) {
2338
+ packageJson.dependencies["drizzle-orm"] = "^0.45.2";
2339
+ packageJson.dependencies["pg"] = "^8.22.0";
2340
+ packageJson.dependencies["postgres"] = "^3.4.9";
2341
+ packageJson.devDependencies["@types/pg"] = "^8.11.0";
2342
+ packageJson.devDependencies["drizzle-kit"] = "^0.31.10";
2343
+ packageJson.scripts["db:migrate"] = "drizzle-kit migrate";
2344
+ } else if (options.database === CORE_CONSTANTS.SQL_LITE) {
2345
+ packageJson.dependencies["drizzle-orm"] = "^0.45.2";
2346
+ packageJson.dependencies["@libsql/client"] = "^0.14.0";
2347
+ packageJson.devDependencies["drizzle-kit"] = "^0.31.10";
2348
+ }
2349
+ packageJson.scripts["db:generate"] = "drizzle-kit generate";
2350
+ packageJson.scripts["db:push"] = "drizzle-kit push";
2351
+ }
2352
+ if (options.axios) packageJson.dependencies["axios"] = "^1.16.1";
2353
+ if (options.cockatiel) packageJson.dependencies["cockatiel"] = "^4.0.0";
2354
+ if (options.pino) {
2355
+ packageJson.dependencies["pino"] = "^10.3.1";
2356
+ packageJson.devDependencies["pino-pretty"] = "^11.2.2";
2357
+ }
2358
+ if (options.sentry) packageJson.dependencies["@sentry/node"] = "^7.64.0";
2359
+ if (options.redis) packageJson.dependencies["ioredis"] = "^5.3.1";
2360
+ if (options.supabase) packageJson.dependencies["@supabase/supabase-js"] = "^2.35.0";
2361
+ if (options.zod) packageJson.dependencies["zod"] = "^4.4.3";
2362
+ const filePath = import_node_path22.default.join(projectPath, "package.json");
2363
+ await this._fileService.writeFileRecursive(filePath, JSON.stringify(packageJson, null, 2));
2364
+ }
2365
+ };
2366
+ }
2367
+ });
2368
+
2369
+ // src/infrastructure/generators/core/bootstrap.generator.ts
2370
+ var bootstrap_generator_exports = {};
2371
+ __export(bootstrap_generator_exports, {
2372
+ BootstrapGenerator: () => BootstrapGenerator2
2373
+ });
2374
+ var import_node_path23, BootstrapGenerator2;
2375
+ var init_bootstrap_generator = __esm({
2376
+ "src/infrastructure/generators/core/bootstrap.generator.ts"() {
2377
+ "use strict";
2378
+ import_node_path23 = __toESM(require("path"));
2379
+ init_shared();
2380
+ BootstrapGenerator2 = class {
2381
+ constructor(_fileService) {
2382
+ this._fileService = _fileService;
2383
+ }
2384
+ _fileService;
2385
+ async generate(projectPath, options) {
2386
+ let builderNodes = `
2387
+ const builder = new AppBuilder<MyRegistry>()
2388
+ .addContext()
2389
+ .addMiddlewares((opts) => {
2390
+ // Add your options here
2391
+ })
2392
+ .addPipeline((opts) => {
2393
+ // Add your options here
2394
+ })`;
2395
+ if (options.database !== CORE_CONSTANTS.NONE) {
2396
+ if (options.database === CORE_CONSTANTS.DRIZZLE) {
2397
+ builderNodes += `
2398
+ .addDb((opts, config) => {
2399
+ opts.enableSqlLite = false
2400
+ opts.connectionString = config.getOrThrow('DATABASE_URL');
2401
+ })`;
2402
+ } else if (options.database === CORE_CONSTANTS.SQL_LITE) {
2403
+ builderNodes += `
2404
+ .addDb((opts, config) => {
2405
+ opts.enableSqlLite = true;
2406
+ opts.connectionString = config.getOrThrow('SQLITE_DATABASE_URL');
2407
+ })`;
2408
+ }
2409
+ }
2410
+ if (options.redis) {
2411
+ builderNodes += `
2412
+ .addCache((opts, config) => {
2413
+ const isDev = config.get('NODE_ENV') === 'development';
2414
+ opts.inMemory = isDev;
2415
+ opts.redis = !isDev ? {
2416
+ host: config.getOrThrow('REDIS_HOST'),
2417
+ port: config.getNumber('REDIS_PORT') ?? 6379,
2418
+ username: config.get('REDIS_USERNAME') ?? '',
2419
+ password: config.get('REDIS_PASSWORD') ?? '',
2420
+ tls: config.getBoolean('REDIS_TLS') ?? false,
2421
+ maxRetriesPerRequest: 3
2422
+ } : undefined
2423
+ })`;
2424
+ } else {
2425
+ builderNodes += `
2426
+ .addCache((opts) => {
2427
+ opts.inMemory = true;
2428
+ })`;
2429
+ }
2430
+ if (options.supabase) {
2431
+ builderNodes += `
2432
+ .addAuth((opts, config) => {
2433
+ opts.url = config.getOrThrow('SUPABASE_URL');
2434
+ opts.key = config.getOrThrow('SUPABASE_KEY');
2435
+ })`;
2436
+ }
2437
+ if (options.pino || options.sentry) {
2438
+ const pinoConfig = options.pino ? `opts.pino.config = !isDev ? { env: config.getOrThrow('NODE_ENV'), destination: 'stdout', prettyPrint: false } : undefined;` : "";
2439
+ const sentryConfig = options.sentry ? `opts.sentry.config = !isDev ? { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.getOrThrow('NODE_ENV') } : undefined;` : "";
2440
+ builderNodes += `
2441
+ .addLogger((opts, config) => {
2442
+ const isDev = config.get('NODE_ENV') === 'development';
2443
+ opts.level = LOG_LEVEL.INFO;
2444
+ opts.console = isDev;
2445
+ ${pinoConfig}
2446
+ ${sentryConfig}
2447
+ })`;
2448
+ }
2449
+ builderNodes += `
2450
+ .addServices((opts, config) => {
2451
+ // Register custom repositories and handlers here
2452
+ });`;
2453
+ const content = `import type { IServiceContainer } from '@xeno-js/core';
2454
+ import { AppBuilder, LOG_LEVEL, Nullable, TOKENS } from '@xeno-js/core';
2455
+ import type { MyRegistry } from './registry';
2456
+
2457
+ let containerPromise: Nullable<Promise<IServiceContainer<MyRegistry>>> = null;
2458
+
2459
+ export async function bootstrap() {
2460
+ if (!containerPromise) {
2461
+ containerPromise = (async () => {
2462
+ const builder = getContainer();
2463
+ return await builder.build();
2464
+ })().catch((err) => {
2465
+ containerPromise = null;
2466
+ throw err;
2467
+ });
2468
+ }
2469
+ return containerPromise;
2470
+ }
2471
+
2472
+ function getContainer() {
2473
+ ${builderNodes}
2474
+ return builder;
2475
+ }
2476
+ `;
2477
+ const filePath = import_node_path23.default.join(projectPath, "src", "bootstrap.ts");
2478
+ await this._fileService.writeFileRecursive(filePath, content);
2479
+ }
2480
+ };
2481
+ }
2482
+ });
2483
+
2484
+ // src/infrastructure/generators/core/env.generator.ts
2485
+ var env_generator_exports2 = {};
2486
+ __export(env_generator_exports2, {
2487
+ EnvGenerator: () => EnvGenerator
2488
+ });
2489
+ var import_node_path24, EnvGenerator;
2490
+ var init_env_generator2 = __esm({
2491
+ "src/infrastructure/generators/core/env.generator.ts"() {
2492
+ "use strict";
2493
+ import_node_path24 = __toESM(require("path"));
2494
+ init_shared();
2495
+ EnvGenerator = class {
2496
+ constructor(_fileService) {
2497
+ this._fileService = _fileService;
2498
+ }
2499
+ _fileService;
2500
+ async generate(projectPath, options) {
2501
+ let content = `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2502
+ # Xeno APPLICATION ENVIRONMENT VARIABLES
2503
+ # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2504
+ NODE_ENV=development
2505
+ `;
2506
+ if (options.database !== CORE_CONSTANTS.NONE) {
2507
+ if (options.database === CORE_CONSTANTS.DRIZZLE) {
2508
+ content += `
2509
+ # --- Database (PostgreSQL) ---
2510
+ DATABASE_URL=postgres://postgres:password@localhost:5432/xeno_db
2511
+ `;
2512
+ } else if (options.database === CORE_CONSTANTS.SQL_LITE) {
2513
+ content += `
2514
+ # --- Database (SQLite) ---
2515
+ SQLITE_DATABASE_URL=file:./dev.sqlite
2516
+ `;
2517
+ }
2518
+ }
2519
+ if (options.axios) {
2520
+ content += `
2521
+ # --- Axios ---
2522
+ BASE_URL=
2523
+ `;
2524
+ }
2525
+ if (options.redis) {
2526
+ content += `
2527
+ # --- Cache (Redis) ---
2528
+ REDIS_HOST=localhost
2529
+ REDIS_PORT=6379
2530
+ REDIS_PASSWORD=
2531
+ REDIS_USERNAME=
2532
+ REDIS_TLS=false
2533
+ `;
2534
+ }
2535
+ if (options.supabase) {
2536
+ content += `
2537
+ # --- Authentication (Supabase) ---
2538
+ SUPABASE_URL=https://your-project.supabase.co
2539
+ SUPABASE_KEY=your-anon-key
2540
+ `;
2541
+ }
2542
+ if (options.sentry) {
2543
+ content += `
2544
+ # --- Error Tracking (Sentry) ---
2545
+ SENTRY_DSN=https://your-sentry-dsn@o0.ingest.sentry.io/0
2546
+ SENTRY_ENVIRONMENT=development
2547
+ `;
2548
+ }
2549
+ const envPath = import_node_path24.default.join(projectPath, ".env");
2550
+ const envExamplePath = import_node_path24.default.join(projectPath, ".env.example");
2551
+ await this._fileService.writeFileRecursive(envPath, content);
2552
+ await this._fileService.writeFileRecursive(envExamplePath, content);
2553
+ }
2554
+ };
2555
+ }
2556
+ });
2557
+
2558
+ // src/infrastructure/generators/core/readme.generator.ts
2559
+ var readme_generator_exports2 = {};
2560
+ __export(readme_generator_exports2, {
2561
+ ReadmeGenerator: () => ReadmeGenerator
2562
+ });
2563
+ var import_node_path25, ReadmeGenerator;
2564
+ var init_readme_generator2 = __esm({
2565
+ "src/infrastructure/generators/core/readme.generator.ts"() {
2566
+ "use strict";
2567
+ import_node_path25 = __toESM(require("path"));
2568
+ ReadmeGenerator = class {
2569
+ constructor(_fileService) {
2570
+ this._fileService = _fileService;
2571
+ }
2572
+ _fileService;
2573
+ async generate(projectPath, _options) {
2574
+ const content = `
2575
+ <div align="center">
2576
+ <img src="logo/logo.png" alt="Xeno Logo" width="140" />
2577
+
2578
+ <h1>Xeno</h1>
2579
+
2580
+ <p><em>Enterprise-grade DDD & CQRS framework for Node.js</em></p>
2581
+
2582
+ <p>
2583
+ <a href="https://github.com/xeno-js/xeno-js">
2584
+ <img src="https://img.shields.io/badge/Powered%20by-Xeno-blueviolet?style=flat-square" alt="Powered by Xeno" />
2585
+ </a>
2586
+ <a href="https://github.com/xeno-js/xeno-js/blob/main/LICENSE">
2587
+ <img src="https://img.shields.io/npm/l/@xeno?style=flat-square" alt="License: ISC" />
2588
+ </a>
2589
+ <a href="https://www.npmjs.com/package/@xeno/core">
2590
+ <img src="https://img.shields.io/npm/v/@xeno/core?style=flat-square" alt="NPM Version" />
2591
+ </a>
2592
+ <a href="https://buymeacoffee.com/xenojs">
2593
+ <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
2594
+ </a>
2595
+ </p>
2596
+ </div>
2597
+
2598
+ ---
2599
+
2600
+ ## What is Xeno?
2601
+
2602
+ **Xeno** is an enterprise-grade, runtime-agnostic architectural framework for
2603
+ Node.js built natively with TypeScript. It provides structural primitives for
2604
+ implementing robust **Domain-Driven Design (DDD)** and **Command Query
2605
+ Responsibility Segregation (CQRS)** patterns. By shifting operational logic away
2606
+ from delivery mechanisms and transport frameworks, Xeno ensures your core
2607
+ application architecture remains pristine, testable, and completely isolated
2608
+ from external infrastructural churn.
2609
+
2610
+ ---
2611
+
2612
+ ## \u{1F4A1} Why Choose Xeno?
2613
+
2614
+ Modern Node.js frameworks often tie business workflows tightly to HTTP server
2615
+ abstractions or rely heavily on experimental language features. Xeno fixes this
2616
+ with an emphasis on developer experience, type safety, and clean separation of
2617
+ concerns.
2618
+
2619
+ - **Zero Decorators**: Xeno eliminates reliance on experimental or unstable TS
2620
+ decorator specifications (\`reflect-metadata\`). The IoC container
2621
+ (\`ServiceContainer\`) uses pure, explicit functional factories that optimize
2622
+ compilation speeds and eliminate runtime black-box behaviors.
2623
+ - **Complete Server Decoupling**: Xeno does not care if you use Fastify, Hono,
2624
+ Express, Koa, or AWS Lambda. The presentation layer handles incoming data
2625
+ using plain, primitive contracts, making migration or multi-runtime hosting
2626
+ completely seamless.
2627
+ - **Pay-For-What-You-Use (Opt-in Modularity)**: Core dependencies are
2628
+ strategically classified as optional peer dependencies. If your architecture
2629
+ doesn't use Redis, Sentry, or Supabase, you do not pull them into your node
2630
+ modules.
2631
+ - **Enterprise-Grade Resiliency & Cross-Cutting Pipelines**: Address complex
2632
+ distributed patterns natively without code duplication. Xeno provides
2633
+ out-of-the-box composite behaviors:
2634
+ - **Idempotency**: Implements multi-tenant logic keyspaces matching advanced
2635
+ SaaS factory patterns for logical partitioning.
2636
+ - **Concurrency Control**: Mitigates thundering herd impacts via advanced
2637
+ backoff retry strategies coupled with randomized jitter.
2638
+ - **Resilience Policies**: Deep integration with circuit breakers, bulkheads,
2639
+ and fallbacks.
2640
+ - **Deterministic Type Safety**: Strong infrastructure validation strategies
2641
+ using Zod schemas.
2642
+
2643
+ ---
2644
+
2645
+ ## \u{1F4D6} Documentation & Getting Started
2646
+
2647
+ To explore the architecture, programmatic configurations, and extension
2648
+ workflows of Xeno, read the full technical manuals located inside the main
2649
+ documentation hub:
2650
+
2651
+ - **[Framework Documentation Repository](https://www.xeno-js.it/introduction)**
2652
+
2653
+ Inside, you will find exhaustive, step-by-step assembly guides covering core
2654
+ host building (\`AppBuilder\`), isolated request middleware lifecycles, functional
2655
+ \`Result\` monads, and zero-trust authorization pipeline behavior tracks.
2656
+
2657
+ ---
2658
+
2659
+ ## \u{1F680} Live Executable Demos
2660
+
2661
+ Want to see how Xeno works? Check out the functional example application
2662
+ showcasing end-to-end command/query segregation, multi-tenant databases, and
2663
+ resilient schema handling.
2664
+
2665
+ You can dive straight into the explicit source code modules of specialized
2666
+ sandbox environments:
2667
+
2668
+ - **[\`pipelines_middleware_demo/\`](https://www.xeno-js.it/demo)**: Traces
2669
+ an execution thread from the raw HTTP transport presentation layer, executing
2670
+ automated header extraction and anchoring metadata variables into
2671
+ \`AsyncLocalStorage\` thread boundaries.
2672
+
2673
+ ---
2674
+
2675
+ ## \u{1F4E6} Installation
2676
+
2677
+ Install the core package:
2678
+
2679
+ \`\`\`bash
2680
+ npm install @xeno-js/core
2681
+
2682
+ \`\`\`
2683
+
2684
+ Xeno uses **Optional Peer Dependencies**. You only install the external
2685
+ libraries you actually need. Node.js will strictly lazy-load only the modules
2686
+ you enable in the configuration.
2687
+
2688
+ \`\`\`bash
2689
+ # Example: Install tools only if you enable them in the builder
2690
+ npm install zod pino cockatiel drizzle-orm
2691
+
2692
+ \`\`\`
2693
+
2694
+ ---
2695
+
2696
+ ## \u26A1 Bootstrapping & Middleware Example
2697
+
2698
+ Below is an architectural example of how to configure the Xeno
2699
+ \`ServiceContainer\`, load core modules, and process an incoming application
2700
+ payload natively inside a server middleware wrapper.
2701
+
2702
+ ### 1. Initialize the Container and Configure Modules
2703
+
2704
+ \`\`\`typescript
2705
+ import { AppBuilder, LOG_LEVEL, TOKENS, XenoRegistry } from '@xeno-js/core'
2706
+ import { FindUserQueryHandler } from './user/cqrs/handlers/index'
2707
+ import { FindUserController } from './user/controllers/index'
2708
+ import { UserMapper } from './user/mappers/user.mapper'
2709
+ import { UserWriteRepository } from './user/repositories/user-write.repository'
2710
+ import { UserDataSource } from './user/datasources/user.datasource'
2711
+
2712
+ // Map your registry token with XenoRegistry<TSchemaDb, TExtension>
2713
+ type MyRegistry = XenoRegistry<{ /** Your Db Schema here **/}, {
2714
+ USER_MAPPER_TOKEN: UserMapper
2715
+ USER_DS_TOKEN: UserDataSource
2716
+ USER_REPOSITORY_TOKEN: UserWriteRepository
2717
+ FIND_USER_QUERY_HANDLER_TOKEN: FindUserQueryHandler
2718
+ FIND_USER_CONTROLLER_TOKEN: FindUserController
2719
+ }>
2720
+
2721
+ // Create the root IoC container context
2722
+ export const bootstrap = new AppBuilder<MyRegistry>()
2723
+ // Configure middleware and only PUBLIC routes
2724
+ .addMiddlewares(opts => {
2725
+ opts.routeRegistry = {
2726
+ '/api/user/:id': ['GET', 'POST'],
2727
+ }
2728
+ })
2729
+ // Configure the CQRS pipeline
2730
+ // Can register Policies for your intent
2731
+ .addPipeline((config) => {
2732
+ config.authorization.policies = {
2733
+ 'FIND_USER_QUERY_HANDLER_TOKEN': {
2734
+ // Add authz by user id
2735
+ userId: true
2736
+ // Add authz by tenant id
2737
+ tenantId: true
2738
+ // Add authz by roles
2739
+ roles: ['admin']
2740
+ // Add authz by perissions
2741
+ permissions: ['read'],
2742
+ }
2743
+ }
2744
+ // Can add idempotency pipeline for command
2745
+ config.commandBus.idempotency = { lockTtlSeconds: 30, processedTtlSeconds: 60 }
2746
+ // Can add concurrency pipeline for command
2747
+ config.commandBus.concurrency = { delayConfig: { baseDelayMs: 100, maxJitterMs: 500 }, maxRetries: 3 }
2748
+ // Can add caching pipeline for query
2749
+ config.queryBus.isEnabled = true
2750
+ })
2751
+ // Configure Database with drizzle
2752
+ .addDb((opts, config) => {
2753
+ opts.connectionString = config.getOrThrow('DATABASE_URL')
2754
+ })
2755
+ // Configure Authentication with supabase
2756
+ .addAuth((opts, config) => {
2757
+ opts.key = 'demo-key'
2758
+ opts.url = config.getOrThrow('API_BASE_URL')
2759
+ })
2760
+ // Configure your logger (e.g. Console, Sentry, Pino or custom logger)
2761
+ .addLogger((config) => {
2762
+ config.level = LOG_LEVEL.INFO
2763
+ config.console = true
2764
+ })
2765
+ // Register your services
2766
+ .addServices((services) => {
2767
+ // REGISTER MAPPER
2768
+ services.addScoped('USER_MAPPER_TOKEN', () => new UserMapper())
2769
+
2770
+ // REGISTER DATASOURCES
2771
+ services.addScoped('USER_DS_TOKEN', (c) => new UserDataSource(c.resolve(TOKENS.DB_CONTEXT)))
2772
+
2773
+ // REGISTER REPOSITORIES
2774
+ services.addScoped('USER_REPOSITORY_TOKEN', (c) => new UserWriteRepository(c.resolve('USER_DS_TOKEN'), c.resolve('USER_MAPPER_TOKEN')))
2775
+
2776
+ // REGISTER HANDLERS
2777
+ services.addScoped('FIND_USER_QUERY_HANDLER_TOKEN', (c) => {
2778
+ const requestcontext = c.resolve('USER_CONTEXT_FACTORY')
2779
+ const repository = c.resolve('USER_READ_REPOSITORY')
2780
+ return new FindUserQueryHandler(repository, requestcontext)
2781
+ })
2782
+
2783
+ // REGISTER CONTROLLERS
2784
+ services.addTransient('FIND_USER_CONTROLLER_TOKEN', (c) => {
2785
+ return new FindUserController(c.resolve('CONTEXT_ACCESSOR'), c.resolve('MEDIATOR'))
2786
+ })
2787
+ })
2788
+
2789
+ \`\`\`
2790
+
2791
+ ### 2. Wrap and Run within Server Middleware (e.g., Fastify / Hono)
2792
+
2793
+ \`\`\`typescript
2794
+ import 'dotenv/config'
2795
+ import { ContainerUtils } from '@xeno-js/core'
2796
+ import fastify from 'fastify'
2797
+ import { bootstrap } from './bootstrap'
2798
+
2799
+ async function runDemo() {
2800
+ console.log('\u2699\uFE0F Initialized Xeno Container...')
2801
+ try {
2802
+ // 1. Bootstrap the application and get the service container
2803
+ const xenoApp = await bootstrap.build()
2804
+
2805
+ console.log('\u{1F680} Starting Fastify server on http://localhost:3000...')
2806
+
2807
+ // 2. Resolve the middleware from the container
2808
+ const middleware = xeno.resolve('MIDDLEWARE')
2809
+ // 3. Create a Fastify instance to handle HTTP requests
2810
+ const app = fastify()
2811
+
2812
+ // \u2500\u2500\u2500 ENDPOINT 2: QUERY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2813
+ app.get('/api/user/:id', async (request, reply) => {
2814
+ // 4. Execute the middleware to handle the request context and authentication, then call the StatusController's handle method with the request payload.
2815
+ const responseDto = await middleware.execute(
2816
+ { path: '/api/user/:id', method: 'GET', transport: { res: reply, req: request } },
2817
+ { ...request.headers },
2818
+ async () => {
2819
+ const { id } = request.params as any
2820
+ const controller = ContainerUtils.resolveServiceScoped('FIND_USER_CONTROLLER_TOKEN', xenoApp)
2821
+ return await controller.handle({ id: id ?? '123' });
2822
+ }
2823
+ );
2824
+
2825
+ return reply
2826
+ .status(responseDto.status)
2827
+ .type('application/json')
2828
+ .send(responseDto.data)
2829
+ })
2830
+
2831
+ console.log('\u2705 Routes set up. Ready to accept requests.')
2832
+
2833
+ // \u2500\u2500\u2500 START SERVER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2834
+ try {
2835
+ await app.listen({ port: 3000 })
2836
+ console.log('\u{1F680} Application running on http://localhost:3000')
2837
+ console.log('\u{1F449} GET /api/user/:id (GET: api/user/1)')
2838
+ } catch (err) {
2839
+ console.error('Error starting Fastify server:', err)
2840
+ app.log.error(err)
2841
+ process.exit(1)
2842
+ }
2843
+ } catch (error) {
2844
+ console.error('Error during bootstrap or server setup:', error)
2845
+ process.exit(1)
2846
+ }
2847
+ }
2848
+
2849
+ runDemo()
2850
+ \`\`\`
2851
+
2852
+ ---
2853
+
2854
+ ## \u{1F6E0} Scaffold your project with CLI
2855
+
2856
+ Xeno includes an official CLI tool, \`@xeno-js/cli\`, designed to bootstrap your
2857
+ new application in seconds. It offers an interactive setup to select exactly the
2858
+ modules you need (Database, HTTP, Auth, Logging, etc.), ensuring you start with
2859
+ a clean, pre-configured architecture tailored to your specific requirements.
2860
+
2861
+ If you want to learn how to use it, see the full options available, or
2862
+ understand how the scaffolding engine works, check the
2863
+ **[CLI Documentation](https://www.xeno-js.it/cli/overview)**.
2864
+
2865
+ ---
2866
+
2867
+ ## \u{1F91D} For Contributors
2868
+
2869
+ We welcome contributions to Xeno! To maintain the highest code quality and
2870
+ stability of the core framework, **direct pushes to the \`main\` and \`develop\`
2871
+ branches are strictly prohibited.** Please follow this Git Flow to contribute:
2872
+
2873
+ 1. **Branch off from \`develop\`**: Create a new branch for your feature or
2874
+ bugfix.
2875
+
2876
+ \`\`\`bash
2877
+ git checkout develop
2878
+ git pull origin develop
2879
+ git checkout -b feat/your-awesome-feature
2880
+ \`\`\`
2881
+
2882
+ 2. **Make your changes**: Write your code and ensure it passes all local checks
2883
+ (linting, types, and tests).
2884
+
2885
+ \`\`\`bash
2886
+ npm run check
2887
+ \`\`\`
2888
+
2889
+ 3. **Commit your changes**: We enforce
2890
+ [Conventional Commits](https://www.conventionalcommits.org/). Husky will
2891
+ verify your commit message format.
2892
+
2893
+ 4. **Commit Format:**
2894
+
2895
+ \`\`\`bash
2896
+ feat(scope): add new feature
2897
+ fix(scope): resolve bug
2898
+ chore(scope): update dependencies
2899
+ \`\`\`
2900
+
2901
+ 5. **Submit a Pull Request (PR)**: Push your branch to GitHub and open a Pull
2902
+ Request targeting the **\`develop\`** branch.
2903
+
2904
+ 6. **Review**: The repository owner will review your code, run pipeline tests,
2905
+ and merge it into \`develop\`.
2906
+
2907
+ _Note: The \`main\` branch is strictly reserved for production releases. Code
2908
+ flows from feature branches \u27A1\uFE0F \`develop\` \u27A1\uFE0F \`main\`._
2909
+
2910
+ ### Scripts
2911
+
2912
+ | Command | Description |
2913
+ | ----------------------- | ---------------------------------------------- |
2914
+ | \`npm run build\` | Builds the TypeScript source code into \`dist/\` |
2915
+ | \`npm run typecheck\` | Checks types without emitting files |
2916
+ | \`npm run lint\` | Runs ESLint |
2917
+ | \`npm run format\` | Formats code with Prettier |
2918
+ | \`npm run test\` | Runs the Vitest test suite |
2919
+ | \`npm run test:coverage\` | Runs tests and generates a coverage report |
2920
+
2921
+ ### Code Quality (Husky & Git Hooks)
2922
+
2923
+ This project strictly enforces code quality rules before pushing to the
2924
+ repository:
2925
+
2926
+ - **\`pre-commit\`**: Runs \`lint-staged\` on staged files (ESLint + Prettier).
2927
+ - **\`commit-msg\`**: Checks commit messages with \`commitlint\` (we use
2928
+ Conventional Commits).
2929
+ - **\`pre-push\`**: Runs type checking, linting, and testing before code leaves
2930
+ your machine.
2931
+
2932
+ ---
2933
+
2934
+ ## \u{1F331} Support & Appreciation
2935
+
2936
+ Building, benchmarking, and maintaining a progressive, enterprise-ready
2937
+ open-source framework requires a massive amount of continuous dedication and
2938
+ architectural engineering.
2939
+
2940
+ If Xeno has brought value to your development workflows, helped decouple your
2941
+ core business logic, or simplified your system infrastructure layout, consider
2942
+ supporting its open-source lifecycle. Your backing directly accelerates our
2943
+ strategic roadmap for new out-of-the-box transport integrations (such as gRPC,
2944
+ RabbitMQ, and GraphQL) and keeps the documentation pristine.
2945
+
2946
+ **Want to know how you can contribute or sponsor Xeno?** We rely on the
2947
+ commitment of our community to keep the project independent and thriving.
2948
+ Whether you are an individual developer or a business using Xeno, your support
2949
+ makes a real difference.
2950
+
2951
+ \u{1F449}
2952
+ **[Read our support guidelines and find out how to help](https://www.xeno-js.it/support-us)**
2953
+
2954
+ Thank you for being part of this decoupled open-source journey!
2955
+
2956
+ <amp-bounce>
2957
+ </amp-bounce>
2958
+ <a href="https://www.buymeacoffee.com/xenojs" target="_blank">
2959
+ <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42" style="height: 42px !important;" />
2960
+ </a>
2961
+
2962
+ ---
2963
+
2964
+ ## \u{1F6E1}\uFE0F Powered by Xeno
2965
+
2966
+ If you are using Xeno in your project, let the world know! Add this badge to
2967
+ your README:
2968
+
2969
+ \`\`\`html
2970
+ <a
2971
+ href="[https://github.com/xeno-js/xeno-js](https://github.com/xeno-js/xeno-js)"
2972
+ target="_blank"
2973
+ >
2974
+ <img
2975
+ src="[https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square](https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square)"
2976
+ alt="Powered by Xeno"
2977
+ height="20"
2978
+ />
2979
+ </a>
2980
+ \`\`\`
2981
+
2982
+ ## \u{1F4C4} License
2983
+
2984
+ Copyright (c) 2026 Xeno. Licensed under the [ISC License](LICENSE).
2985
+
2986
+ `;
2987
+ const filePath = import_node_path25.default.join(projectPath, "readme");
2988
+ await this._fileService.writeFileRecursive(filePath, content);
2989
+ }
2990
+ };
2991
+ }
2992
+ });
2993
+
2994
+ // src/infrastructure/generators/core/registry.generator.ts
2995
+ var registry_generator_exports = {};
2996
+ __export(registry_generator_exports, {
2997
+ RegistryGenerator: () => RegistryGenerator2
2998
+ });
2999
+ var import_node_path26, RegistryGenerator2;
3000
+ var init_registry_generator = __esm({
3001
+ "src/infrastructure/generators/core/registry.generator.ts"() {
3002
+ "use strict";
3003
+ import_node_path26 = __toESM(require("path"));
3004
+ init_shared();
3005
+ RegistryGenerator2 = class {
3006
+ constructor(_fileService) {
3007
+ this._fileService = _fileService;
3008
+ }
3009
+ _fileService;
3010
+ async generate(projectPath, options) {
3011
+ let dbImport = "";
3012
+ let registryType = "";
3013
+ if (options.database !== CORE_CONSTANTS.NONE) {
3014
+ if (options.database === CORE_CONSTANTS.DRIZZLE) {
3015
+ dbImport = `import type { DbSchema } from './schema';`;
3016
+ registryType = `XenoRegistry<DbSchema>`;
3017
+ } else if (options.database === CORE_CONSTANTS.SQL_LITE) {
3018
+ dbImport = `import type { SqliteSchema } from './schema';`;
3019
+ registryType = `XenoRegistry<SqliteSchema>`;
3020
+ }
3021
+ } else {
3022
+ registryType = `XenoRegistry`;
3023
+ }
3024
+ const content = `import type { XenoRegistry } from '@xeno-js/core';
3025
+ ${dbImport}
3026
+
3027
+ /**
3028
+ * @file registry.ts
3029
+ * @description Centralized Type Contract for Dependency Injection.
3030
+ */
3031
+ export interface MyRegistry extends ${registryType} {
3032
+ // Add your application injection tokens here
3033
+ // e.g., USER_REPOSITORY: IUserRepository;
3034
+ }
3035
+ `;
3036
+ const filePath = import_node_path26.default.join(projectPath, "src", "registry.ts");
3037
+ await this._fileService.writeFileRecursive(filePath, content);
3038
+ }
3039
+ };
3040
+ }
3041
+ });
3042
+
3043
+ // src/infrastructure/generators/core/tsconfig.generator.ts
3044
+ var tsconfig_generator_exports2 = {};
3045
+ __export(tsconfig_generator_exports2, {
3046
+ TsconfigGenerator: () => TsconfigGenerator
3047
+ });
3048
+ var import_node_path27, TsconfigGenerator;
3049
+ var init_tsconfig_generator2 = __esm({
3050
+ "src/infrastructure/generators/core/tsconfig.generator.ts"() {
3051
+ "use strict";
3052
+ import_node_path27 = __toESM(require("path"));
3053
+ init_shared();
3054
+ TsconfigGenerator = class {
3055
+ constructor(_fileService) {
3056
+ this._fileService = _fileService;
3057
+ }
3058
+ _fileService;
3059
+ async generate(projectPath, options) {
3060
+ const tsconfig = {
3061
+ compilerOptions: {
3062
+ target: "ES2022",
3063
+ module: "ESNext",
3064
+ moduleResolution: "bundler",
3065
+ strict: true,
3066
+ esModuleInterop: true,
3067
+ skipLibCheck: true,
3068
+ forceConsistentCasingInFileNames: true,
3069
+ outDir: "./dist",
3070
+ rootDir: "./src",
3071
+ types: ["node"],
3072
+ paths: {
3073
+ "@/*": ["./src/*"]
3074
+ }
3075
+ },
3076
+ include: ["src/**/*.ts"]
3077
+ };
3078
+ if (options.database === CORE_CONSTANTS.DRIZZLE || options.database === CORE_CONSTANTS.SQL_LITE) {
3079
+ tsconfig.include.push("drizzle.config.ts");
3080
+ }
3081
+ const filePath = import_node_path27.default.join(projectPath, "tsconfig.json");
3082
+ await this._fileService.writeFileRecursive(filePath, JSON.stringify(tsconfig, null, 2));
3083
+ }
3084
+ };
3085
+ }
3086
+ });
3087
+
3088
+ // src/infrastructure/generators/core/drizzle.generator.ts
3089
+ var drizzle_generator_exports = {};
3090
+ __export(drizzle_generator_exports, {
3091
+ DrizzleGenerator: () => DrizzleGenerator
3092
+ });
3093
+ var import_node_path28, DrizzleGenerator;
3094
+ var init_drizzle_generator = __esm({
3095
+ "src/infrastructure/generators/core/drizzle.generator.ts"() {
3096
+ "use strict";
3097
+ import_node_path28 = __toESM(require("path"));
3098
+ DrizzleGenerator = class {
3099
+ constructor(_fileService) {
3100
+ this._fileService = _fileService;
3101
+ }
3102
+ _fileService;
3103
+ async generate(projectPath, _options) {
3104
+ const configContent = this.composeDrizzleConfig();
3105
+ const filePath = import_node_path28.default.join(projectPath, "drizzle.config.ts");
3106
+ await this._fileService.writeFileRecursive(filePath, configContent);
3107
+ const schemaContent = this.composeSchema();
3108
+ await this._fileService.writeFileRecursive(
3109
+ import_node_path28.default.join(projectPath, "src", "schema.ts"),
3110
+ schemaContent
3111
+ );
3112
+ }
3113
+ composeDrizzleConfig() {
3114
+ return `import 'dotenv/config';
3115
+ import { defineConfig } from 'drizzle-kit';
3116
+
3117
+ /**
3118
+ * Drizzle Kit configuration file.
3119
+ * Defines the schema location, migration output directory, and database connection.
3120
+ */
3121
+ export default defineConfig({
3122
+ schema: './src/schema.ts',
3123
+ out: './drizzle',
3124
+ dialect: 'postgresql',
3125
+ dbCredentials: {
3126
+ url: process.env.DATABASE_URL!,
3127
+ },
3128
+ verbose: true,
3129
+ strict: true,
3130
+ });
3131
+ `;
3132
+ }
3133
+ composeSchema() {
3134
+ return `import { boolean, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
3135
+
3136
+ /**
3137
+ * @description Define your database tables here.
3138
+ * The 'usersTable' serves as a baseline example.
3139
+ * You can add more tables, relations, and indexes according to your domain requirements.
3140
+ */
3141
+ export const usersTable = pgTable('users', {
3142
+ id: serial('id').primaryKey(),
3143
+ name: text('name').notNull(),
3144
+ email: text('email').notNull().unique(),
3145
+ isDeleted: boolean('is_deleted').notNull().default(false),
3146
+ deletedAt: timestamp('deleted_at'),
3147
+ createdAt: timestamp('created_at').defaultNow(),
3148
+ });
3149
+
3150
+ /**
3151
+ * Types inferred from the schema.
3152
+ * - UserDto: Represents the shape of the data retrieved from the database ($inferSelect).
3153
+ * - NewUserDto: Represents the shape of the data required to insert a new record ($inferInsert).
3154
+ */
3155
+ export type UserDto = typeof usersTable.$inferSelect;
3156
+ export type NewUserDto = typeof usersTable.$inferInsert;
3157
+
3158
+ /**
3159
+ * Centralized Database Schema dictionary.
3160
+ */
3161
+ export type DbSchema = {
3162
+ users: typeof usersTable;
3163
+ };
3164
+ `;
3165
+ }
3166
+ };
3167
+ }
3168
+ });
3169
+
3170
+ // src/infrastructure/generators/core/drizzle-sql-lite.generator.ts
3171
+ var drizzle_sql_lite_generator_exports = {};
3172
+ __export(drizzle_sql_lite_generator_exports, {
3173
+ DrizzleSqlLiteGenerator: () => DrizzleSqlLiteGenerator
3174
+ });
3175
+ var import_node_path29, DrizzleSqlLiteGenerator;
3176
+ var init_drizzle_sql_lite_generator = __esm({
3177
+ "src/infrastructure/generators/core/drizzle-sql-lite.generator.ts"() {
3178
+ "use strict";
3179
+ import_node_path29 = __toESM(require("path"));
3180
+ DrizzleSqlLiteGenerator = class {
3181
+ constructor(_fileService) {
3182
+ this._fileService = _fileService;
3183
+ }
3184
+ _fileService;
3185
+ async generate(projectPath, _options) {
3186
+ const configContent = this.composeDrizzleConfig();
3187
+ const filePath = import_node_path29.default.join(projectPath, "drizzle.config.ts");
3188
+ await this._fileService.writeFileRecursive(filePath, configContent);
3189
+ const schemaPath = import_node_path29.default.join(projectPath, "src", "schema.ts");
3190
+ const schemaContent = this.composeSchema();
3191
+ await this._fileService.writeFileRecursive(schemaPath, schemaContent);
3192
+ }
3193
+ composeDrizzleConfig() {
3194
+ return `import 'dotenv/config';
3195
+ import { defineConfig } from 'drizzle-kit';
3196
+
3197
+ /**
3198
+ * Drizzle Kit configuration for SQLite / libSQL.
3199
+ * Uses environment variable SQLITE_DATABASE_URL to locate the database.
3200
+ */
3201
+ export default defineConfig({
3202
+ schema: './src/schema.ts',
3203
+ out: './drizzle',
3204
+ dialect: 'sqlite',
3205
+ dbCredentials: {
3206
+ url: process.env.SQLITE_DATABASE_URL!,
3207
+ },
3208
+ verbose: true,
3209
+ strict: true,
3210
+ });
3211
+ `;
3212
+ }
3213
+ composeSchema() {
3214
+ return `import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
3215
+
3216
+ /**
3217
+ * Example SQLite schema using Drizzle's libsql primitives.
3218
+ * Place your application tables here (this file is scaffolded by the CLI when
3219
+ * the user opts into SQLite support).
3220
+ */
3221
+ export const usersTable = sqliteTable('users', {
3222
+ id: integer('id').primaryKey().notNull(),
3223
+ name: text('name').notNull(),
3224
+ description: text('description'),
3225
+ });
3226
+
3227
+ /**
3228
+ * Types inferred from the schema.
3229
+ * - UserDto: Represents the shape of the data retrieved from the database ($inferSelect).
3230
+ * - NewUserDto: Represents the shape of the data required to insert a new record ($inferInsert).
3231
+ */
3232
+ export type UserDto = typeof usersTable.$inferSelect;
3233
+ export type NewUserDto = typeof usersTable.$inferInsert;
3234
+
3235
+ /**
3236
+ * Centralized Database Schema dictionary.
3237
+ */
3238
+ export type SqliteSchema = {
3239
+ users: typeof usersTable;
3240
+ };
3241
+ `;
3242
+ }
3243
+ };
3244
+ }
3245
+ });
3246
+
3247
+ // src/infrastructure/generators/core/main.generator.ts
3248
+ var main_generator_exports2 = {};
3249
+ __export(main_generator_exports2, {
3250
+ MainGenerator: () => MainGenerator2
3251
+ });
3252
+ var import_node_path30, MainGenerator2;
3253
+ var init_main_generator2 = __esm({
3254
+ "src/infrastructure/generators/core/main.generator.ts"() {
3255
+ "use strict";
3256
+ import_node_path30 = __toESM(require("path"));
3257
+ MainGenerator2 = class {
3258
+ constructor(_fileService) {
3259
+ this._fileService = _fileService;
3260
+ }
3261
+ _fileService;
3262
+ async generate(projectPath, options) {
3263
+ const content = `import 'dotenv/config';
3264
+ import { bootstrap } from './bootstrap';
3265
+ import { TOKENS } from '@xeno-js/core';
3266
+
3267
+ /**
3268
+ * Main Application Entry Point
3269
+ */
3270
+ async function main() {
3271
+ try {
3272
+ console.info('\u23F3 Bootstrapping ${options.targetDir} application...');
3273
+
3274
+ // Initialize the dependency injection container and infrastructure modules
3275
+ const container = await bootstrap();
3276
+
3277
+ const logger = container.resolve(TOKENS.LOGGER)
3278
+ logger.info('\u2705 Application started successfully!');
3279
+
3280
+ // TODO: Implement your logic here
3281
+
3282
+ } catch (error) {
3283
+ console.error('\u274C Critical error during startup:', error);
3284
+ process.exit(1);
3285
+ }
3286
+ }
3287
+
3288
+ main();
3289
+ `;
3290
+ const filePath = import_node_path30.default.join(projectPath, "src", "main.ts");
3291
+ await this._fileService.writeFileRecursive(filePath, content);
3292
+ }
3293
+ };
3294
+ }
3295
+ });
3296
+
3297
+ // src/infrastructure/strategies/core-scaffold.strategy.ts
3298
+ var core_scaffold_strategy_exports = {};
3299
+ __export(core_scaffold_strategy_exports, {
3300
+ CoreScaffoldStrategy: () => CoreScaffoldStrategy
3301
+ });
3302
+ var import_prompts2, CoreScaffoldStrategy;
3303
+ var init_core_scaffold_strategy = __esm({
3304
+ "src/infrastructure/strategies/core-scaffold.strategy.ts"() {
3305
+ "use strict";
3306
+ import_prompts2 = __toESM(require("prompts"));
3307
+ init_shared();
3308
+ CoreScaffoldStrategy = class {
3309
+ constructor(_fileService) {
3310
+ this._fileService = _fileService;
3311
+ }
3312
+ _fileService;
3313
+ async promptOptions(targetDir) {
3314
+ const response = await (0, import_prompts2.default)([
3315
+ { type: "confirm", name: "axios", message: "Install Axios?", initial: true },
3316
+ { type: "confirm", name: "cockatiel", message: "Install Cockatiel?", initial: true },
3317
+ { type: "confirm", name: "supabase", message: "Install Supabase Auth?", initial: true },
3318
+ {
3319
+ type: "select",
3320
+ name: "database",
3321
+ message: "Select database type",
3322
+ choices: [
3323
+ { title: "Drizzle PostgreSQL", value: "drizzle" },
3324
+ { title: "SQL Lite", value: "sqlLite" },
3325
+ { title: "None", value: "none" }
3326
+ ]
3327
+ },
3328
+ { type: "confirm", name: "pino", message: "Install Pino Logger?", initial: true },
3329
+ { type: "confirm", name: "sentry", message: "Install Sentry Logger?", initial: true },
3330
+ { type: "confirm", name: "redis", message: "Install Redis?", initial: true },
3331
+ { type: "confirm", name: "zod", message: "Install Zod?", initial: true },
3332
+ // {
3333
+ // type: 'select',
3334
+ // name: 'targetEnv',
3335
+ // message: 'Select target environment',
3336
+ // choices: [
3337
+ // { title: 'Console', value: 'console' },
3338
+ // { title: 'Vercel (Serverless)', value: 'vercel' },
3339
+ // { title: 'Fastify (Standalone)', value: 'fastify' },
3340
+ // ],
3341
+ // },
3342
+ {
3343
+ type: "confirm",
3344
+ name: "gitInit",
3345
+ message: "Initialize a new Git repository?",
3346
+ initial: true
3347
+ }
3348
+ ]);
3349
+ return {
3350
+ targetDir,
3351
+ projectType: "core",
3352
+ targetEnv: "console",
3353
+ ...response
3354
+ };
3355
+ }
3356
+ async getGenerators(options) {
3357
+ const generators = [];
3358
+ const { PackageJsonCoreGenerator: PackageJsonCoreGenerator2 } = await Promise.resolve().then(() => (init_packagejson_generator2(), packagejson_generator_exports2));
3359
+ const { BootstrapGenerator: BootstrapGenerator3 } = await Promise.resolve().then(() => (init_bootstrap_generator(), bootstrap_generator_exports));
3360
+ const { EnvGenerator: EnvGenerator2 } = await Promise.resolve().then(() => (init_env_generator2(), env_generator_exports2));
3361
+ const { GitIgnoreGenerator: GitIgnoreGenerator2 } = await Promise.resolve().then(() => (init_gitignore_generator(), gitignore_generator_exports));
3362
+ const { ReadmeGenerator: ReadmeGenerator2 } = await Promise.resolve().then(() => (init_readme_generator2(), readme_generator_exports2));
3363
+ const { RegistryGenerator: RegistryGenerator3 } = await Promise.resolve().then(() => (init_registry_generator(), registry_generator_exports));
3364
+ const { TsconfigGenerator: TsconfigGenerator2 } = await Promise.resolve().then(() => (init_tsconfig_generator2(), tsconfig_generator_exports2));
3365
+ generators.push(new PackageJsonCoreGenerator2(this._fileService));
3366
+ generators.push(new BootstrapGenerator3(this._fileService));
3367
+ generators.push(new EnvGenerator2(this._fileService));
3368
+ generators.push(new GitIgnoreGenerator2(this._fileService));
3369
+ generators.push(new ReadmeGenerator2(this._fileService));
3370
+ generators.push(new RegistryGenerator3(this._fileService));
3371
+ generators.push(new TsconfigGenerator2(this._fileService));
3372
+ if (options.database !== CORE_CONSTANTS.NONE) {
3373
+ if (options.database === CORE_CONSTANTS.DRIZZLE) {
3374
+ const { DrizzleGenerator: DrizzleGenerator2 } = await Promise.resolve().then(() => (init_drizzle_generator(), drizzle_generator_exports));
3375
+ generators.push(new DrizzleGenerator2(this._fileService));
3376
+ } else if (options.database === CORE_CONSTANTS.SQL_LITE) {
3377
+ const { DrizzleSqlLiteGenerator: DrizzleSqlLiteGenerator2 } = await Promise.resolve().then(() => (init_drizzle_sql_lite_generator(), drizzle_sql_lite_generator_exports));
3378
+ generators.push(new DrizzleSqlLiteGenerator2(this._fileService));
3379
+ }
3380
+ }
3381
+ if (options.targetEnv === TARGET_ENV_CONSTANTS.CONSOLE) {
3382
+ const { MainGenerator: MainGenerator3 } = await Promise.resolve().then(() => (init_main_generator2(), main_generator_exports2));
3383
+ generators.push(new MainGenerator3(this._fileService));
3384
+ }
3385
+ return generators;
3386
+ }
3387
+ };
3388
+ }
3389
+ });
3390
+
3391
+ // src/index.ts
3392
+ var import_picocolors6 = __toESM(require("picocolors"));
3393
+
3394
+ // src/infrastructure/commands/command-runner.service.ts
3395
+ var import_node_child_process = require("child_process");
3396
+ var CommandRunner = class {
3397
+ async run(command, args, cwd, isWin = false) {
3398
+ return new Promise((resolve2, reject) => {
3399
+ const cmd = isWin ? `${command}.cmd` : command;
3400
+ const child = (0, import_node_child_process.spawn)(cmd, args, {
3401
+ cwd,
3402
+ stdio: "inherit",
3403
+ shell: false
3404
+ });
3405
+ child.on("error", (error) => {
3406
+ reject(new Error(`Error during execution of the command ${command}: ${error.message}`));
3407
+ });
3408
+ child.on("close", (code) => {
3409
+ if (code === 0) resolve2();
3410
+ else reject(new Error(`Command ${command} failed with code ${code}`));
3411
+ });
3412
+ });
3413
+ }
3414
+ };
3415
+
3416
+ // src/infrastructure/commands/generate.command.ts
3417
+ var import_picocolors = __toESM(require("picocolors"));
3418
+ init_shared();
3419
+ init_command_constants();
3420
+
3421
+ // src/infrastructure/file/file.service.ts
3422
+ var import_promises = require("fs/promises");
3423
+ var import_path = require("path");
3424
+ var FileUtils = class {
3425
+ async writeFileRecursive(filePath, content) {
3426
+ const dir = (0, import_path.dirname)(filePath);
3427
+ await (0, import_promises.mkdir)(dir, { recursive: true });
3428
+ await (0, import_promises.writeFile)(filePath, content, "utf8");
3429
+ }
3430
+ };
3431
+
3432
+ // src/infrastructure/commands/generate.command.ts
3433
+ var GenerateCommandQuery = class {
3434
+ name = "generate";
3435
+ aliases = ["g"];
3436
+ async execute(args) {
3437
+ const type = args[0];
3438
+ const componentName = args[1];
3439
+ const isCore = args.includes("--core");
3440
+ const isVue = args.includes("--vue");
3441
+ if (Guards.isNullOrEmpty(type) || Guards.isNullOrEmpty(componentName)) {
3442
+ throw new Error(`Invalid syntax. Usage: xeno-js g [command | query] <Name> [--core | --vue]`);
3443
+ }
3444
+ let outputPath = void 0;
3445
+ const outputIndex = args.findIndex((arg) => arg === "--output" || arg === "-o" || arg === "--o");
3446
+ if (outputIndex !== -1 && args.length > outputIndex + 1) {
3447
+ outputPath = args[outputIndex + 1];
3448
+ }
3449
+ const targetDir = process.cwd();
3450
+ const fileService = new FileUtils();
3451
+ if (isCore || !isVue) {
3452
+ const cqrsType = type === COMMAND_CONSTANTS.COMMAND ? COMMAND_CONSTANTS.COMMAND : COMMAND_CONSTANTS.QUERY;
3453
+ const { CqrsGenerator: CqrsGenerator2 } = await Promise.resolve().then(() => (init_cqrs_generator(), cqrs_generator_exports));
3454
+ const generator = new CqrsGenerator2(fileService);
3455
+ await generator.generate(targetDir, componentName, cqrsType, outputPath);
3456
+ console.info(import_picocolors.default.green(`
3457
+ Successfully generated ${type} component '${componentName}'!`));
3458
+ } else if (isVue) {
3459
+ const cqrsType = type === COMMAND_CONSTANTS.COMMAND ? COMMAND_CONSTANTS.COMMAND : COMMAND_CONSTANTS.QUERY;
3460
+ const { CqrsVueGenerator: CqrsVueGenerator2 } = await Promise.resolve().then(() => (init_cqrs_generator2(), cqrs_generator_exports2));
3461
+ const generator = new CqrsVueGenerator2(fileService);
3462
+ await generator.generate(targetDir, componentName, cqrsType, outputPath);
3463
+ console.info(
3464
+ import_picocolors.default.green(`
3465
+ \u2705 Successfully generated Vue ${type} component '${componentName}'!`)
3466
+ );
3467
+ } else {
3468
+ throw new Error(`Unsupported generator type: '${type}'. Use 'command' or 'query'.`);
3469
+ }
3470
+ }
3471
+ };
3472
+
3473
+ // src/infrastructure/commands/help.command.ts
3474
+ var import_picocolors2 = __toESM(require("picocolors"));
3475
+ init_shared();
3476
+ var HelpCommand = class {
3477
+ get name() {
3478
+ return COMMAND_CONSTANTS.HELP;
3479
+ }
3480
+ get aliases() {
3481
+ return [ARGS_CONSTANTS.HELP, "-h"];
3482
+ }
3483
+ async execute() {
3484
+ console.info(import_picocolors2.default.greenBright("\n\u{1F680} Xeno CLI - Usage Guide\n"));
3485
+ console.info(import_picocolors2.default.bold(import_picocolors2.default.blue("1. Create a new project:")));
3486
+ console.info(` xeno-js new ${import_picocolors2.default.yellow("<name>")} ${import_picocolors2.default.cyan("[--vue | --core]")}`);
3487
+ console.info(` ${import_picocolors2.default.gray("\u2022")} ${import_picocolors2.default.yellow("<name>")}: The name of your project directory.`);
3488
+ console.info(
3489
+ ` ${import_picocolors2.default.gray("\u2022")} ${import_picocolors2.default.cyan("[--vue | --core]")}: Project type. ${import_picocolors2.default.gray("(Default: --core)")}`
3490
+ );
3491
+ console.info(` - ${import_picocolors2.default.cyan("--core")}: Initializes a Backend project (Node.js/TS).`);
3492
+ console.info(` - ${import_picocolors2.default.cyan("--vue")}: Initializes a Frontend project (Vue.js).
3493
+ `);
3494
+ console.info(import_picocolors2.default.bold(import_picocolors2.default.blue("2. Generate CQRS components:")));
3495
+ console.info(
3496
+ ` xeno-js generate ${import_picocolors2.default.magenta("<type>")} ${import_picocolors2.default.yellow("<Name>")} ${import_picocolors2.default.cyan("[--vue | --core]")} ${import_picocolors2.default.green("[--output <path>]")}`
3497
+ );
3498
+ console.info(
3499
+ ` ${import_picocolors2.default.gray("\u2022")} ${import_picocolors2.default.magenta("<type>")}: Component type to generate (${import_picocolors2.default.magenta("command")} | ${import_picocolors2.default.magenta("query")}).`
3500
+ );
3501
+ console.info(
3502
+ ` ${import_picocolors2.default.gray("\u2022")} ${import_picocolors2.default.yellow("<Name>")}: The name of the component (e.g., UserCreate).`
3503
+ );
3504
+ console.info(
3505
+ ` ${import_picocolors2.default.gray("\u2022")} ${import_picocolors2.default.cyan("[--vue | --core]")}: Target context. ${import_picocolors2.default.gray("(Default: --core)")}`
3506
+ );
3507
+ console.info(
3508
+ ` - ${import_picocolors2.default.cyan("--core")}: Generates Controller, Handler, Command/Query, Module, and Zod/DB schemas.`
3509
+ );
3510
+ console.info(
3511
+ ` - ${import_picocolors2.default.cyan("--vue")}: Generates frontend-specific files (Stores, Services, etc.).`
3512
+ );
3513
+ console.info(
3514
+ ` ${import_picocolors2.default.gray("\u2022")} ${import_picocolors2.default.green("[--output | -o]")}: Custom destination path inside 'src/'. ${import_picocolors2.default.gray("(Default: src/<name-in-lowercase>)")}`
3515
+ );
3516
+ console.info(
3517
+ ` ${import_picocolors2.default.gray("Alias:")} ${import_picocolors2.default.gray("xeno-js g <type> <Name> [--vue | --core] [-o | --o <path>]")}
3518
+ `
3519
+ );
3520
+ console.info(import_picocolors2.default.bold(import_picocolors2.default.blue("3. Help:")));
3521
+ console.info(` xeno-js --help ${import_picocolors2.default.gray("Displays this guide.")}`);
3522
+ console.info(` ${import_picocolors2.default.gray("Alias:")} ${import_picocolors2.default.gray("xeno-js --h | -h")}
3523
+ `);
3524
+ console.info(import_picocolors2.default.bold(import_picocolors2.default.blue("Documentation:")));
3525
+ console.info(` ${import_picocolors2.default.underline("https://www.xeno-js.it")}
3526
+ `);
3527
+ }
3528
+ };
3529
+
3530
+ // src/infrastructure/commands/new-project.command.ts
3531
+ var import_picocolors4 = __toESM(require("picocolors"));
3532
+
3533
+ // src/infrastructure/bootstrap/bootstrapper.ts
3534
+ var import_promises3 = require("fs/promises");
3535
+ var import_node_path7 = require("path");
3536
+ var import_picocolors3 = __toESM(require("picocolors"));
3537
+ var Bootstrapper = class {
3538
+ constructor(_generators) {
3539
+ this._generators = _generators;
3540
+ }
3541
+ _generators;
3542
+ async run(projectPath, options) {
3543
+ console.info(import_picocolors3.default.cyan("\n\u{1F680} Initializing scaffolding..."));
3544
+ const absolutePath = (0, import_node_path7.resolve)(process.cwd(), projectPath);
3545
+ await (0, import_promises3.mkdir)(absolutePath, { recursive: true });
3546
+ for (const generator of this._generators) {
3547
+ try {
3548
+ await generator.generate(absolutePath, options);
3549
+ } catch (error) {
3550
+ console.error(import_picocolors3.default.red(`
3551
+ \u274C Error during execution of ${generator.constructor.name}`));
3552
+ throw error;
3553
+ }
3554
+ }
3555
+ console.info(import_picocolors3.default.green("\n\u2728 Scaffolding completed succesfully!"));
3556
+ }
3557
+ };
3558
+
3559
+ // src/infrastructure/commands/new-project.command.ts
3560
+ var NewProjectCommand = class {
3561
+ name = "new";
3562
+ aliases = [];
3563
+ async execute(args) {
3564
+ const targetDir = args.find((arg) => !arg.startsWith("--")) ?? "my-xeno-app";
3565
+ const isVue = args.includes("--vue");
3566
+ const fileService = new FileUtils();
3567
+ let strategy;
3568
+ if (isVue) {
3569
+ const { VueScaffoldStrategy: VueScaffoldStrategy2 } = await Promise.resolve().then(() => (init_vue_scaffold_strategy(), vue_scaffold_strategy_exports));
3570
+ strategy = new VueScaffoldStrategy2(fileService);
3571
+ } else {
3572
+ const { CoreScaffoldStrategy: CoreScaffoldStrategy2 } = await Promise.resolve().then(() => (init_core_scaffold_strategy(), core_scaffold_strategy_exports));
3573
+ strategy = new CoreScaffoldStrategy2(fileService);
3574
+ }
3575
+ const options = await strategy.promptOptions(targetDir);
3576
+ const generators = await strategy.getGenerators(options);
3577
+ const bootstrapper = new Bootstrapper(generators);
3578
+ await bootstrapper.run(targetDir, options);
3579
+ const commandRunner = new CommandRunner();
3580
+ if (options.gitInit) {
3581
+ console.info(import_picocolors4.default.greenBright("\n\u{1F5C2}\uFE0F Initializing Git repository..."));
3582
+ await commandRunner.run("git", ["init"], targetDir);
3583
+ }
3584
+ console.info(import_picocolors4.default.greenBright("\n\u{1F4E6} Installing dependencies..."));
3585
+ const isWin = process.platform === "win32";
3586
+ await commandRunner.run("npm", ["install"], targetDir, isWin);
3587
+ console.info(import_picocolors4.default.green(`
3588
+ \u2705 Project successfully created in ./${targetDir}`));
3589
+ console.info(import_picocolors4.default.white(`
3590
+ Next steps:
3591
+ cd ${targetDir}
3592
+ npm run start
3593
+ `));
3594
+ }
3595
+ };
3596
+
3597
+ // src/infrastructure/dispatcher/dispatcher.ts
3598
+ var import_picocolors5 = __toESM(require("picocolors"));
3599
+ init_shared();
3600
+ var CliDispatcher = class {
3601
+ _commands = /* @__PURE__ */ new Map();
3602
+ constructor(commands) {
3603
+ for (const command of commands) {
3604
+ this._commands.set(command.name, command);
3605
+ for (const alias of command.aliases) {
3606
+ this._commands.set(alias, command);
3607
+ }
3608
+ }
3609
+ }
3610
+ async dispatch(rawArgs) {
3611
+ const action = rawArgs[0];
3612
+ if (!Guards.isDefined(action) || action === ARGS_CONSTANTS.HELP || action === "-h" || action === "--h") {
3613
+ const helpCommand = this._commands.get(COMMAND_CONSTANTS.HELP);
3614
+ await helpCommand?.execute([]);
3615
+ return;
3616
+ }
3617
+ const command = this._commands.get(action);
3618
+ if (!Guards.isDefined(command)) {
3619
+ console.error(import_picocolors5.default.red(`
3620
+ \u274C '${action}' is not a command`));
3621
+ console.info(
3622
+ import_picocolors5.default.yellow(`See 'xeno-js --help' for cli usage guide or https://www.xeno-js.it for docs.
3623
+ `)
3624
+ );
3625
+ process.exit(1);
3626
+ }
3627
+ const commandArgs = rawArgs.slice(1);
3628
+ await command.execute(commandArgs);
3629
+ }
3630
+ };
3631
+
3632
+ // src/infrastructure/builder/builder.ts
3633
+ var Builder = class {
3634
+ static getDispatcher() {
3635
+ const dispatcher = new CliDispatcher([
3636
+ new NewProjectCommand(),
3637
+ new GenerateCommandQuery(),
3638
+ new HelpCommand()
3639
+ ]);
3640
+ return dispatcher;
3641
+ }
3642
+ };
3643
+
3644
+ // src/index.ts
3645
+ async function init() {
3646
+ try {
3647
+ const dispatcher = Builder.getDispatcher();
3648
+ const rawArgs = process.argv.slice(2);
3649
+ await dispatcher.dispatch(rawArgs);
3650
+ } catch (error) {
3651
+ console.error(import_picocolors6.default.red("\n\u274C Execution interrupted:"));
3652
+ console.error(error instanceof Error ? error.message : error);
3653
+ process.exit(1);
3654
+ }
3655
+ }
3656
+ init().catch(console.error);
3657
+ /**
3658
+ * @class DrizzleGenerator
3659
+ * @description Generates the Drizzle configuration file (drizzle.config.ts) and a basic schema file (src/schema.ts) for database scaffolding.
3660
+ * The generator is only invoked if the user has opted to include database support in their project.
3661
+ *
3662
+ * @author Xeno
3663
+ * @version 1.0.0
3664
+ * @license ISC
3665
+ * @since 2025-09-30
3666
+ * @link https://github.com/xeno-js/xeno-js
3667
+ */
3668
+ /**
3669
+ * @class CommandUtils
3670
+ * @description Utility class for executing shell commands in a cross-platform manner.
3671
+ *
3672
+ * @author Xeno
3673
+ * @version 1.0.0
3674
+ * @license ISC
3675
+ * @since 2025-09-30
3676
+ * @link https://github.com/xeno-js/xeno-js
3677
+ */
9
3678
  /**
10
3679
  * @function init
11
3680
  * @description Initializes the scaffolding process for a new Xeno project.
@@ -16,16 +3685,4 @@ const builder_1 = require("./infrastructure/builder");
16
3685
  * @since 2025-09-30
17
3686
  * @link https://github.com/xeno-js/xeno-js
18
3687
  */
19
- async function init() {
20
- try {
21
- const dispatcher = builder_1.Builder.getDispatcher();
22
- const rawArgs = process.argv.slice(2);
23
- await dispatcher.dispatch(rawArgs);
24
- }
25
- catch (error) {
26
- console.error(picocolors_1.default.red('\n❌ Execution interrupted:'));
27
- console.error(error instanceof Error ? error.message : error);
28
- process.exit(1);
29
- }
30
- }
31
- init().catch(console.error);
3688
+ //# sourceMappingURL=index.js.map