@xeno-js/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.github/.copilot-instruction.md +73 -0
  2. package/.github/agents/git-operator.agent.md +86 -0
  3. package/.github/agents/unit-tester.agent.md +68 -0
  4. package/.husky/commit-msg +1 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.husky/pre-push +1 -0
  7. package/.prettierignore +7 -0
  8. package/.prettierrc.json +37 -0
  9. package/LICENSE +15 -0
  10. package/README.md +236 -0
  11. package/commitlint.config.cjs +39 -0
  12. package/dist/domain/contracts/icommand-cli.contracts.js +2 -0
  13. package/dist/domain/contracts/icommand-runner.contracts.js +2 -0
  14. package/dist/domain/contracts/idispatch.contracts.js +2 -0
  15. package/dist/domain/contracts/ifile-service.contracts.js +2 -0
  16. package/dist/domain/contracts/igenerator.contracts.js +2 -0
  17. package/dist/domain/contracts/index.js +2 -0
  18. package/dist/domain/contracts/iscaffold-strategy.contracts.js +2 -0
  19. package/dist/domain/index.js +2 -0
  20. package/dist/generator.contracts.js +13 -0
  21. package/dist/index.js +31 -0
  22. package/dist/infrastructure/bootstrap/bootstrapper.js +32 -0
  23. package/dist/infrastructure/bootstrap/index.js +17 -0
  24. package/dist/infrastructure/builder/builder.js +16 -0
  25. package/dist/infrastructure/builder/builder.test.js +30 -0
  26. package/dist/infrastructure/builder/index.js +17 -0
  27. package/dist/infrastructure/command_launcher/command.utils.js +30 -0
  28. package/dist/infrastructure/command_launcher/index.js +2 -0
  29. package/dist/infrastructure/commands/command-runner.service.js +36 -0
  30. package/dist/infrastructure/commands/generate.command.js +48 -0
  31. package/dist/infrastructure/commands/help.command.js +44 -0
  32. package/dist/infrastructure/commands/index.js +20 -0
  33. package/dist/infrastructure/commands/new-project.command.js +43 -0
  34. package/dist/infrastructure/dispatcher/dispatcher.js +39 -0
  35. package/dist/infrastructure/dispatcher/index.js +17 -0
  36. package/dist/infrastructure/file/file.service.js +13 -0
  37. package/dist/infrastructure/file/index.js +17 -0
  38. package/dist/infrastructure/generators/bootstrap.generator.js +118 -0
  39. package/dist/infrastructure/generators/core/bootstrap.generator.js +118 -0
  40. package/dist/infrastructure/generators/core/command/cqrs.generator.js +49 -0
  41. package/dist/infrastructure/generators/core/command/generate-command.generator.js +163 -0
  42. package/dist/infrastructure/generators/core/command/generate-query.generator.js +131 -0
  43. package/dist/infrastructure/generators/core/drizzle-sql-lite.generator.js +78 -0
  44. package/dist/infrastructure/generators/core/drizzle.generator.js +85 -0
  45. package/dist/infrastructure/generators/core/env.generator.js +69 -0
  46. package/dist/infrastructure/generators/core/index.js +25 -0
  47. package/dist/infrastructure/generators/core/main.generator.js +45 -0
  48. package/dist/infrastructure/generators/core/packagejson.generator.js +78 -0
  49. package/dist/infrastructure/generators/core/readme.generator.js +431 -0
  50. package/dist/infrastructure/generators/core/registry.generator.js +46 -0
  51. package/dist/infrastructure/generators/core/tsconfig.generator.js +41 -0
  52. package/dist/infrastructure/generators/core-packagejson.generator.js +78 -0
  53. package/dist/infrastructure/generators/drizzle-sql-lite.generator.js +78 -0
  54. package/dist/infrastructure/generators/drizzle.generator.js +85 -0
  55. package/dist/infrastructure/generators/env-vue.generator.js +40 -0
  56. package/dist/infrastructure/generators/env.generator.js +71 -0
  57. package/dist/infrastructure/generators/gitignore.generator.js +62 -0
  58. package/dist/infrastructure/generators/index.js +26 -0
  59. package/dist/infrastructure/generators/main.generator.js +45 -0
  60. package/dist/infrastructure/generators/readme.generator.js +431 -0
  61. package/dist/infrastructure/generators/registry.generator.js +46 -0
  62. package/dist/infrastructure/generators/tsconfig-vue.generator.js +39 -0
  63. package/dist/infrastructure/generators/tsconfig.generator.js +41 -0
  64. package/dist/infrastructure/generators/vite-config.generator.js +39 -0
  65. package/dist/infrastructure/generators/vue/app-vue.generator.js +26 -0
  66. package/dist/infrastructure/generators/vue/bootstrap-ts.generator.js +59 -0
  67. package/dist/infrastructure/generators/vue/command_query/cqrs.generator.js +36 -0
  68. package/dist/infrastructure/generators/vue/command_query/generate-command-vue.generator.js +139 -0
  69. package/dist/infrastructure/generators/vue/command_query/generate-query-vue.generator.js +147 -0
  70. package/dist/infrastructure/generators/vue/env.generator.js +40 -0
  71. package/dist/infrastructure/generators/vue/index-html.generator.js +29 -0
  72. package/dist/infrastructure/generators/vue/index.js +28 -0
  73. package/dist/infrastructure/generators/vue/main.generator.js +45 -0
  74. package/dist/infrastructure/generators/vue/packagejson.generator.js +59 -0
  75. package/dist/infrastructure/generators/vue/readme.generator.js +394 -0
  76. package/dist/infrastructure/generators/vue/registry-ts.generator.js +28 -0
  77. package/dist/infrastructure/generators/vue/router.generator.js +33 -0
  78. package/dist/infrastructure/generators/vue/tailwind.generator.js +19 -0
  79. package/dist/infrastructure/generators/vue/tsconfig.generator.js +39 -0
  80. package/dist/infrastructure/generators/vue/use-app.generator.js +43 -0
  81. package/dist/infrastructure/generators/vue/vite-config.generator.js +39 -0
  82. package/dist/infrastructure/generators/vue-packagejson.generator.js +59 -0
  83. package/dist/infrastructure/generators/vue-readme.generator.js +53 -0
  84. package/dist/infrastructure/generators/vue-source-files.generator.js +136 -0
  85. package/dist/infrastructure/index.js +21 -0
  86. package/dist/infrastructure/strategies/core-scaffold.strategy.js +99 -0
  87. package/dist/infrastructure/strategies/index.js +18 -0
  88. package/dist/infrastructure/strategies/vue-scaffold.strategy.js +79 -0
  89. package/dist/scaffolding.engine.js +60 -0
  90. package/dist/shared/constants/command.constants.js +19 -0
  91. package/dist/shared/constants/core.constants.js +10 -0
  92. package/dist/shared/constants/index.js +18 -0
  93. package/dist/shared/index.js +18 -0
  94. package/dist/shared/types/common.types.js +2 -0
  95. package/dist/shared/types/index.js +2 -0
  96. package/dist/shared/utils/guards.utils.js +310 -0
  97. package/dist/shared/utils/index.js +18 -0
  98. package/dist/shared/utils/string.utils.js +62 -0
  99. package/eslint.config.mjs +305 -0
  100. package/lint-staged.config.mjs +5 -0
  101. package/logo/logo.png +0 -0
  102. package/package.json +65 -0
  103. package/src/domain/contracts/icommand-cli.contracts.ts +13 -0
  104. package/src/domain/contracts/icommand-runner.contracts.ts +3 -0
  105. package/src/domain/contracts/idispatch.contracts.ts +3 -0
  106. package/src/domain/contracts/ifile-service.contracts.ts +3 -0
  107. package/src/domain/contracts/igenerator.contracts.ts +5 -0
  108. package/src/domain/contracts/index.ts +6 -0
  109. package/src/domain/contracts/iscaffold-strategy.contracts.ts +7 -0
  110. package/src/domain/index.ts +1 -0
  111. package/src/index.ts +30 -0
  112. package/src/infrastructure/bootstrap/bootstrapper.ts +30 -0
  113. package/src/infrastructure/bootstrap/index.ts +1 -0
  114. package/src/infrastructure/builder/builder.test.ts +36 -0
  115. package/src/infrastructure/builder/builder.ts +15 -0
  116. package/src/infrastructure/builder/index.ts +1 -0
  117. package/src/infrastructure/command_launcher/command.utils.ts +31 -0
  118. package/src/infrastructure/command_launcher/index.ts +0 -0
  119. package/src/infrastructure/commands/command-runner.service.ts +36 -0
  120. package/src/infrastructure/commands/generate.command.ts +55 -0
  121. package/src/infrastructure/commands/help.command.ts +63 -0
  122. package/src/infrastructure/commands/index.ts +4 -0
  123. package/src/infrastructure/commands/new-project.command.ts +47 -0
  124. package/src/infrastructure/dispatcher/dispatcher.ts +45 -0
  125. package/src/infrastructure/dispatcher/index.ts +1 -0
  126. package/src/infrastructure/file/file.service.ts +14 -0
  127. package/src/infrastructure/file/index.ts +1 -0
  128. package/src/infrastructure/generators/core/bootstrap.generator.ts +118 -0
  129. package/src/infrastructure/generators/core/command/cqrs.generator.ts +63 -0
  130. package/src/infrastructure/generators/core/command/generate-command.generator.ts +191 -0
  131. package/src/infrastructure/generators/core/command/generate-query.generator.ts +150 -0
  132. package/src/infrastructure/generators/core/drizzle-sql-lite.generator.ts +76 -0
  133. package/src/infrastructure/generators/core/drizzle.generator.ts +86 -0
  134. package/src/infrastructure/generators/core/env.generator.ts +69 -0
  135. package/src/infrastructure/generators/core/index.ts +9 -0
  136. package/src/infrastructure/generators/core/main.generator.ts +41 -0
  137. package/src/infrastructure/generators/core/packagejson.generator.ts +70 -0
  138. package/src/infrastructure/generators/core/readme.generator.ts +427 -0
  139. package/src/infrastructure/generators/core/registry.generator.ts +40 -0
  140. package/src/infrastructure/generators/core/tsconfig.generator.ts +39 -0
  141. package/src/infrastructure/generators/gitignore.generator.ts +60 -0
  142. package/src/infrastructure/generators/vue/app-vue.generator.ts +21 -0
  143. package/src/infrastructure/generators/vue/bootstrap-ts.generator.ts +60 -0
  144. package/src/infrastructure/generators/vue/command_query/cqrs.generator.ts +36 -0
  145. package/src/infrastructure/generators/vue/command_query/generate-command-vue.generator.ts +153 -0
  146. package/src/infrastructure/generators/vue/command_query/generate-query-vue.generator.ts +161 -0
  147. package/src/infrastructure/generators/vue/env.generator.ts +39 -0
  148. package/src/infrastructure/generators/vue/index-html.generator.ts +24 -0
  149. package/src/infrastructure/generators/vue/index.ts +12 -0
  150. package/src/infrastructure/generators/vue/main.generator.ts +41 -0
  151. package/src/infrastructure/generators/vue/packagejson.generator.ts +50 -0
  152. package/src/infrastructure/generators/vue/readme.generator.ts +389 -0
  153. package/src/infrastructure/generators/vue/registry-ts.generator.ts +26 -0
  154. package/src/infrastructure/generators/vue/router.generator.ts +35 -0
  155. package/src/infrastructure/generators/vue/tailwind.generator.ts +17 -0
  156. package/src/infrastructure/generators/vue/tsconfig.generator.ts +38 -0
  157. package/src/infrastructure/generators/vue/use-app.generator.ts +38 -0
  158. package/src/infrastructure/generators/vue/vite-config.generator.ts +35 -0
  159. package/src/infrastructure/index.ts +5 -0
  160. package/src/infrastructure/strategies/core-scaffold.strategy.ts +100 -0
  161. package/src/infrastructure/strategies/index.ts +2 -0
  162. package/src/infrastructure/strategies/vue-scaffold.strategy.ts +79 -0
  163. package/src/shared/constants/command.constants.ts +18 -0
  164. package/src/shared/constants/core.constants.ts +7 -0
  165. package/src/shared/constants/index.ts +2 -0
  166. package/src/shared/index.ts +3 -0
  167. package/src/shared/types/common.types.ts +68 -0
  168. package/src/shared/types/index.ts +1 -0
  169. package/src/shared/utils/guards.utils.ts +334 -0
  170. package/src/shared/utils/index.ts +2 -0
  171. package/src/shared/utils/string.utils.ts +60 -0
  172. package/tsconfig.json +15 -0
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BootstrapGenerator = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const shared_1 = require("../../../shared");
9
+ class BootstrapGenerator {
10
+ _fileService;
11
+ constructor(_fileService) {
12
+ this._fileService = _fileService;
13
+ }
14
+ async generate(projectPath, options) {
15
+ // Costruisce in modo flessibile i nodi di configurazione
16
+ let builderNodes = `
17
+ const builder = new AppBuilder<MyRegistry>()
18
+ .addContext()
19
+ .addMiddlewares((opts) => {
20
+ // Add your options here
21
+ })
22
+ .addPipeline((opts) => {
23
+ // Add your options here
24
+ })`;
25
+ if (options.database !== shared_1.CORE_CONSTANTS.NONE) {
26
+ if (options.database === shared_1.CORE_CONSTANTS.DRIZZLE) {
27
+ builderNodes += `
28
+ .addDb((opts, config) => {
29
+ opts.enableSqlLite = false
30
+ opts.connectionString = config.getOrThrow('DATABASE_URL');
31
+ })`;
32
+ }
33
+ else if (options.database === shared_1.CORE_CONSTANTS.SQL_LITE) {
34
+ builderNodes += `
35
+ .addDb((opts, config) => {
36
+ opts.enableSqlLite = true;
37
+ opts.connectionString = config.getOrThrow('SQLITE_DATABASE_URL');
38
+ })`;
39
+ }
40
+ }
41
+ if (options.redis) {
42
+ builderNodes += `
43
+ .addCache((opts, config) => {
44
+ const isDev = config.get('NODE_ENV') === 'development';
45
+ opts.inMemory = isDev;
46
+ opts.redis = !isDev ? {
47
+ host: config.getOrThrow('REDIS_HOST'),
48
+ port: config.getNumber('REDIS_PORT') ?? 6379,
49
+ username: config.get('REDIS_USERNAME') ?? '',
50
+ password: config.get('REDIS_PASSWORD') ?? '',
51
+ tls: config.getBoolean('REDIS_TLS') ?? false,
52
+ maxRetriesPerRequest: 3
53
+ } : undefined
54
+ })`;
55
+ }
56
+ else {
57
+ // Default in-memory cache if no redis selected but CQRS pipeline exists
58
+ builderNodes += `
59
+ .addCache((opts) => {
60
+ opts.inMemory = true;
61
+ })`;
62
+ }
63
+ if (options.supabase) {
64
+ builderNodes += `
65
+ .addAuth((opts, config) => {
66
+ opts.url = config.getOrThrow('SUPABASE_URL');
67
+ opts.key = config.getOrThrow('SUPABASE_KEY');
68
+ })`;
69
+ }
70
+ if (options.pino || options.sentry) {
71
+ const pinoConfig = options.pino
72
+ ? `opts.pino.config = !isDev ? { env: config.getOrThrow('NODE_ENV'), destination: 'stdout', prettyPrint: false } : undefined;`
73
+ : '';
74
+ const sentryConfig = options.sentry
75
+ ? `opts.sentry.config = !isDev ? { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.getOrThrow('NODE_ENV') } : undefined;`
76
+ : '';
77
+ builderNodes += `
78
+ .addLogger((opts, config) => {
79
+ const isDev = config.get('NODE_ENV') === 'development';
80
+ opts.level = LOG_LEVEL.INFO;
81
+ opts.console = isDev;
82
+ ${pinoConfig}
83
+ ${sentryConfig}
84
+ })`;
85
+ }
86
+ builderNodes += `
87
+ .addServices((opts, config) => {
88
+ // Register custom repositories and handlers here
89
+ });`;
90
+ const content = `import type { IServiceContainer } from '@xeno-js/core';
91
+ import { AppBuilder, LOG_LEVEL, Nullable, TOKENS } from '@xeno-js/core';
92
+ import type { MyRegistry } from './registry';
93
+
94
+ let containerPromise: Nullable<Promise<IServiceContainer<MyRegistry>>> = null;
95
+
96
+ export async function bootstrap() {
97
+ if (!containerPromise) {
98
+ containerPromise = (async () => {
99
+ const builder = getContainer();
100
+ return await builder.build();
101
+ })().catch((err) => {
102
+ containerPromise = null;
103
+ throw err;
104
+ });
105
+ }
106
+ return containerPromise;
107
+ }
108
+
109
+ function getContainer() {
110
+ ${builderNodes}
111
+ return builder;
112
+ }
113
+ `;
114
+ const filePath = node_path_1.default.join(projectPath, 'src', 'bootstrap.ts');
115
+ await this._fileService.writeFileRecursive(filePath, content);
116
+ }
117
+ }
118
+ exports.BootstrapGenerator = BootstrapGenerator;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CqrsGenerator = void 0;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const shared_1 = require("../../../../shared");
10
+ class CqrsGenerator {
11
+ _fileService;
12
+ constructor(_fileService) {
13
+ this._fileService = _fileService;
14
+ }
15
+ async generate(targetDir, name, type, outputPath) {
16
+ const pascalName = shared_1.StringUtils.pascalCase(name);
17
+ const tokenPrefix = shared_1.StringUtils.splitCamelCaseWithUnderscore(pascalName).toUpperCase();
18
+ const lowerName = name.toLowerCase();
19
+ const destinationFolder = outputPath ?? lowerName;
20
+ const componentDir = node_path_1.default.join(targetDir, 'src', destinationFolder);
21
+ let hasZod = false;
22
+ let hasDrizzle = false;
23
+ try {
24
+ const pkgJsonStr = await promises_1.default.readFile(node_path_1.default.join(targetDir, 'package.json'), 'utf8');
25
+ const pkgJson = JSON.parse(pkgJsonStr);
26
+ const deps = { ...(pkgJson.dependencies ?? {}), ...(pkgJson.devDependencies ?? {}) };
27
+ hasZod = !!shared_1.Guards.isDefined(deps['zod']);
28
+ hasDrizzle = !!shared_1.Guards.isDefined(deps['drizzle-orm']);
29
+ }
30
+ catch (error) {
31
+ console.warn(' Warning: Could not read package.json. Assuming Zod and Drizzle are not installed.', error);
32
+ }
33
+ switch (type) {
34
+ case shared_1.COMMAND_CONSTANTS.COMMAND: {
35
+ const { GenerateCommandCoreGenerator } = await import('./generate-command.generator.js');
36
+ const generator = new GenerateCommandCoreGenerator(this._fileService);
37
+ return generator.generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod, hasDrizzle);
38
+ }
39
+ case shared_1.COMMAND_CONSTANTS.QUERY: {
40
+ const { GenerateQueryCoreGenerator } = await import('./generate-query.generator.js');
41
+ const generatorQuery = new GenerateQueryCoreGenerator(this._fileService);
42
+ return generatorQuery.generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod);
43
+ }
44
+ default:
45
+ throw new Error(`Unsupported generator type: '${String(type)}'. Use 'command' or 'query'.`);
46
+ }
47
+ }
48
+ }
49
+ exports.CqrsGenerator = CqrsGenerator;
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GenerateCommandCoreGenerator = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ class GenerateCommandCoreGenerator {
9
+ _fileService;
10
+ constructor(_fileService) {
11
+ this._fileService = _fileService;
12
+ }
13
+ async generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod, hasDrizzle) {
14
+ // 1. Command
15
+ const commandContent = `import { Command } from '@xeno-js/core';
16
+
17
+ /*
18
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
19
+ * Please replace <any> with your specific payload and response types.
20
+ */
21
+ export class ${pascalName}Command extends Command<any> {
22
+ constructor(public readonly payload: any) {
23
+ super('${tokenPrefix}_COMMAND_HANDLER');
24
+ }
25
+ }
26
+ `;
27
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.command.ts`), commandContent);
28
+ // 2. Handler
29
+ const handlerContent = `import type { IFactory, ResultType, UserContext } from '@xeno-js/core';
30
+ import { AppError, BaseHandler, Result } from '@xeno-js/core';
31
+ import { ${pascalName}Command } from './${lowerName}.command';
32
+
33
+ /*
34
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
35
+ * Please replace <any> with your specific response type.
36
+ */
37
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Command, any> {
38
+ constructor(
39
+ identityFactory: IFactory<void, UserContext>
40
+ ) {
41
+ super(identityFactory);
42
+ }
43
+
44
+ public async executeAsync(request: ${pascalName}Command, singal: AbortSignal): Promise<ResultType<any>> {
45
+ // TODO: Implement your business logic here
46
+
47
+ return Result.ok();
48
+ }
49
+ }
50
+ `;
51
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.handler.ts`), handlerContent);
52
+ // 3. Controller
53
+ const controllerContent = `import type { IContextAccessor, IMediator, RequestContext, ResponseDto } from '@xeno-js/core';
54
+ import { BaseController } from '@xeno-js/core';
55
+ import { ${pascalName}Command } from './${lowerName}.command';
56
+
57
+ /*
58
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
59
+ * Please replace <any, any> with your specific request and response types.
60
+ */
61
+ export class ${pascalName}Controller extends BaseController<any, any> {
62
+ constructor(
63
+ requestContext: IContextAccessor<RequestContext>,
64
+ mediator: IMediator,
65
+ ) {
66
+ super(requestContext, mediator);
67
+ }
68
+
69
+ public async handle(request: any): Promise<ResponseDto<any>> {
70
+ const cmd = new ${pascalName}Command(request);
71
+ const result = await this._send(cmd);
72
+
73
+ if (!result.isOk()) {
74
+ return this.fail(result.getErrorOrThrow(), 'Error during ${pascalName} operation');
75
+ }
76
+
77
+ return this.ok(result.getValueOrThrow());
78
+ }
79
+ }
80
+ `;
81
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.controller.ts`), controllerContent);
82
+ // 4. Entity
83
+ const entityContent = `import { Entity } from '@xeno-js/core';
84
+
85
+ export interface ${pascalName}Props {
86
+ // TODO: Define your entity properties
87
+ }
88
+
89
+ /*
90
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
91
+ * Adjust the properties and types according to your domain logic.
92
+ */
93
+ export class ${pascalName} extends Entity<${pascalName}Props> {
94
+ private constructor(props: ${pascalName}Props, id?: string) {
95
+ super(props, id);
96
+ }
97
+
98
+ static create(props: ${pascalName}Props, id?: string): ${pascalName} {
99
+ return new ${pascalName}(props, id);
100
+ }
101
+ }
102
+ `;
103
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.entity.ts`), entityContent);
104
+ // 5. Schema Zod (Condizionale)
105
+ if (hasZod) {
106
+ const zodContent = `import { z } from 'zod';
107
+ import { ZodUtils } from '@xeno-js/core';
108
+
109
+ /*
110
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
111
+ * Define the actual Zod validation schema for your command payload.
112
+ */
113
+ export const ${pascalName}Schema = ZodUtils.createCommandSchema({
114
+ payload: z.any() // TODO: Update with strict validation
115
+ });
116
+ `;
117
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.schema-zod.ts`), zodContent);
118
+ }
119
+ // 6. Schema DB (Condizionale)
120
+ if (hasDrizzle) {
121
+ const dbContent = `import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
122
+ // Note: If you are using SQLite, change the import to 'drizzle-orm/sqlite-core' and update the table definition.
123
+
124
+ /*
125
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
126
+ * Update the schema definition with your actual database columns.
127
+ */
128
+ export const ${lowerName}s = pgTable('${lowerName}s', {
129
+ id: text('id').primaryKey(),
130
+ createdAt: timestamp('created_at').defaultNow(),
131
+ updatedAt: timestamp('updated_at').defaultNow().$onUpdate(() => new Date()),
132
+ });
133
+
134
+ export type ${pascalName}Dto = typeof ${lowerName}s.$inferSelect;
135
+ export type New${pascalName}Dto = typeof ${lowerName}s.$inferInsert;
136
+ `;
137
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.schema-db.ts`), dbContent);
138
+ }
139
+ // 7. Module
140
+ const moduleContent = `import type { IServiceContainer, IConfigurationService } from '@xeno-js/core';
141
+ import { TOKENS } from '@xeno-js/core';
142
+ import { ${pascalName}Controller } from './${lowerName}.controller';
143
+ import { ${pascalName}Handler } from './${lowerName}.handler';
144
+
145
+ export const ${pascalName}Module = Object.freeze({
146
+ register(opts: IServiceContainer<any>, _config: IConfigurationService): void {
147
+ opts.addTransient('${tokenPrefix}_COMMAND_CONTROLLER', (c) => new ${pascalName}Controller(c.resolve(TOKENS.REQUEST_CONTEXT), c.resolve(TOKENS.MEDIATOR)));
148
+ opts.addScoped('${tokenPrefix}_COMMAND_HANDLER', (c) => new ${pascalName}Handler(c.resolve(TOKENS.USER_CONTEXT_FACTORY)));
149
+
150
+ /*
151
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
152
+ * Please copy and paste the following tokens to your MyRegistry interface (usually in src/registry.ts):
153
+ *
154
+ * ${tokenPrefix}_COMMAND_CONTROLLER: IController<any, any>;
155
+ * ${tokenPrefix}_COMMAND_HANDLER: IHandler<${pascalName}Command, any>;
156
+ */
157
+ }
158
+ } as const);
159
+ `;
160
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.module.ts`), moduleContent);
161
+ }
162
+ }
163
+ exports.GenerateCommandCoreGenerator = GenerateCommandCoreGenerator;
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GenerateQueryCoreGenerator = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ class GenerateQueryCoreGenerator {
9
+ _fileService;
10
+ constructor(_fileService) {
11
+ this._fileService = _fileService;
12
+ }
13
+ async generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod) {
14
+ const queryContent = `import { BaseQuery } from '@xeno-js/core';
15
+
16
+ /*
17
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
18
+ * Please replace <any> with your specific payload and response types.
19
+ */
20
+ export class ${pascalName}Query extends BaseQuery<any> {
21
+ constructor(public readonly payload: any) {
22
+ super(
23
+ '${tokenPrefix}_QUERY_HANDLER',
24
+ // Default cache options:
25
+ {
26
+ ttl: 60,
27
+ cacheKey: \`${lowerName}:\${JSON.stringify(payload)}\`,
28
+ bypassCache: false,
29
+ consistentRead: false
30
+ isUserScoped: false,
31
+ }
32
+ );
33
+ }
34
+ }
35
+ `;
36
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.query.ts`), queryContent);
37
+ // 2. Handler
38
+ const handlerContent = `import type { IFactory, ResultType, UserContext } from '@xeno-js/core';
39
+ import { AppError, BaseHandler, Result } from '@xeno-js/core';
40
+ import { ${pascalName}Query } from './${lowerName}.query';
41
+
42
+ /*
43
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
44
+ * Please replace <any> with your specific response type.
45
+ */
46
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, any> {
47
+ constructor(
48
+ identityFactory: IFactory<void, UserContext>
49
+ // TODO: Inject your ReadDao or DataSource here
50
+ ) {
51
+ super(identityFactory);
52
+ }
53
+
54
+ public async executeAsync(request: ${pascalName}Query, singal: AbortSignal): Promise<ResultType<any>> {
55
+ // TODO: Implement your query logic here
56
+
57
+ return Result.ok();
58
+ }
59
+ }
60
+ `;
61
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.handler.ts`), handlerContent);
62
+ // 3. Controller
63
+ const controllerContent = `import type { IContextAccessor, IMediator, RequestContext, ResponseDto } from '@xeno-js/core';
64
+ import { BaseController } from '@xeno-js/core';
65
+ import { ${pascalName}Query } from './${lowerName}.query';
66
+
67
+ /*
68
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
69
+ * Please replace <any, any> with your specific request and response types.
70
+ */
71
+ export class ${pascalName}Controller extends BaseController<any, any> {
72
+ constructor(
73
+ requestContext: IContextAccessor<RequestContext>,
74
+ mediator: IMediator,
75
+ ) {
76
+ super(requestContext, mediator);
77
+ }
78
+
79
+ public async handle(request: any): Promise<ResponseDto<any>> {
80
+ const query = new ${pascalName}Query(request);
81
+ const result = await this._query(query);
82
+
83
+ if (!result.isOk()) {
84
+ return this.fail(result.getErrorOrThrow(), 'Error during ${pascalName} operation');
85
+ }
86
+
87
+ return this.ok(result.getValueOrThrow());
88
+ }
89
+ }
90
+ `;
91
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.controller.ts`), controllerContent);
92
+ // 4. Schema Zod
93
+ if (hasZod) {
94
+ const zodContent = `import { z } from 'zod';
95
+ import { ZodUtils } from '@xeno-js/core';
96
+
97
+ /*
98
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
99
+ * Define the actual Zod validation schema for your query payload.
100
+ */
101
+ export const ${pascalName}Schema = ZodUtils.createQuerySchema({
102
+ payload: z.any() // TODO: Update with strict validation
103
+ });
104
+ `;
105
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.schema-zod.ts`), zodContent);
106
+ }
107
+ // 5. Module
108
+ const moduleContent = `import type { IServiceContainer, IConfigurationService } from '@xeno-js/core';
109
+ import { TOKENS } from '@xeno-js/core';
110
+ import { ${pascalName}Controller } from './${lowerName}.controller';
111
+ import { ${pascalName}Handler } from './${lowerName}.handler';
112
+
113
+ export const ${pascalName}Module = Object.freeze({
114
+ register(opts: IServiceContainer<any>, _config: IConfigurationService): void {
115
+ opts.addTransient('${tokenPrefix}_QUERY_CONTROLLER', (c) => new ${pascalName}Controller(c.resolve(TOKENS.REQUEST_CONTEXT), c.resolve(TOKENS.MEDIATOR)));
116
+ opts.addScoped('${tokenPrefix}_QUERY_HANDLER', (c) => new ${pascalName}Handler(c.resolve(TOKENS.USER_CONTEXT_FACTORY)));
117
+
118
+ /*
119
+ * ⚠️ WARNING: ACTION REQUIRED ⚠️
120
+ * Please copy and paste the following tokens to your MyRegistry interface (usually in src/registry.ts):
121
+ *
122
+ * ${tokenPrefix}_QUERY_CONTROLLER: IController<any, any>;
123
+ * ${tokenPrefix}_QUERY_HANDLER: IHandler<${pascalName}Query, any>;
124
+ */
125
+ }
126
+ } as const);
127
+ `;
128
+ await this._fileService.writeFileRecursive(node_path_1.default.join(componentDir, `${lowerName}.module.ts`), moduleContent);
129
+ }
130
+ }
131
+ exports.GenerateQueryCoreGenerator = GenerateQueryCoreGenerator;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DrizzleSqlLiteGenerator = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ /**
9
+ * @class DrizzleSqlLiteGenerator
10
+ * @description Generates a Drizzle configuration file (drizzle.config.ts) and a
11
+ * SQLite schema example (src/infrastructure/db/sqlite-schema.ts) when the user
12
+ * opts to use SQLite/libSQL instead of Postgres.
13
+ */
14
+ class DrizzleSqlLiteGenerator {
15
+ _fileService;
16
+ constructor(_fileService) {
17
+ this._fileService = _fileService;
18
+ }
19
+ async generate(projectPath, _options) {
20
+ const configContent = this.composeDrizzleConfig();
21
+ const filePath = node_path_1.default.join(projectPath, 'drizzle.config.ts');
22
+ await this._fileService.writeFileRecursive(filePath, configContent);
23
+ const schemaPath = node_path_1.default.join(projectPath, 'src', 'schema.ts');
24
+ const schemaContent = this.composeSchema();
25
+ await this._fileService.writeFileRecursive(schemaPath, schemaContent);
26
+ }
27
+ composeDrizzleConfig() {
28
+ return `import 'dotenv/config';
29
+ import { defineConfig } from 'drizzle-kit';
30
+
31
+ /**
32
+ * Drizzle Kit configuration for SQLite / libSQL.
33
+ * Uses environment variable SQLITE_DATABASE_URL to locate the database.
34
+ */
35
+ export default defineConfig({
36
+ schema: './src/schema.ts',
37
+ out: './drizzle',
38
+ dialect: 'sqlite',
39
+ dbCredentials: {
40
+ url: process.env.SQLITE_DATABASE_URL!,
41
+ },
42
+ verbose: true,
43
+ strict: true,
44
+ });
45
+ `;
46
+ }
47
+ composeSchema() {
48
+ return `import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
49
+
50
+ /**
51
+ * Example SQLite schema using Drizzle's libsql primitives.
52
+ * Place your application tables here (this file is scaffolded by the CLI when
53
+ * the user opts into SQLite support).
54
+ */
55
+ export const usersTable = sqliteTable('users', {
56
+ id: integer('id').primaryKey().notNull(),
57
+ name: text('name').notNull(),
58
+ description: text('description'),
59
+ });
60
+
61
+ /**
62
+ * Types inferred from the schema.
63
+ * - UserDto: Represents the shape of the data retrieved from the database ($inferSelect).
64
+ * - NewUserDto: Represents the shape of the data required to insert a new record ($inferInsert).
65
+ */
66
+ export type UserDto = typeof usersTable.$inferSelect;
67
+ export type NewUserDto = typeof usersTable.$inferInsert;
68
+
69
+ /**
70
+ * Centralized Database Schema dictionary.
71
+ */
72
+ export type SqliteSchema = {
73
+ users: typeof usersTable;
74
+ };
75
+ `;
76
+ }
77
+ }
78
+ exports.DrizzleSqlLiteGenerator = DrizzleSqlLiteGenerator;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DrizzleGenerator = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ /**
9
+ * @class DrizzleGenerator
10
+ * @description Generates the Drizzle configuration file (drizzle.config.ts) and a basic schema file (src/schema.ts) for database scaffolding.
11
+ * The generator is only invoked if the user has opted to include database support in their project.
12
+ *
13
+ * @author Xeno
14
+ * @version 1.0.0
15
+ * @license ISC
16
+ * @since 2025-09-30
17
+ * @link https://github.com/xeno-js/xeno-js
18
+ */
19
+ class DrizzleGenerator {
20
+ _fileService;
21
+ constructor(_fileService) {
22
+ this._fileService = _fileService;
23
+ }
24
+ async generate(projectPath, _options) {
25
+ const configContent = this.composeDrizzleConfig();
26
+ const filePath = node_path_1.default.join(projectPath, 'drizzle.config.ts');
27
+ await this._fileService.writeFileRecursive(filePath, configContent);
28
+ const schemaContent = this.composeSchema();
29
+ await this._fileService.writeFileRecursive(node_path_1.default.join(projectPath, 'src', 'schema.ts'), schemaContent);
30
+ }
31
+ composeDrizzleConfig() {
32
+ return `import 'dotenv/config';
33
+ import { defineConfig } from 'drizzle-kit';
34
+
35
+ /**
36
+ * Drizzle Kit configuration file.
37
+ * Defines the schema location, migration output directory, and database connection.
38
+ */
39
+ export default defineConfig({
40
+ schema: './src/schema.ts',
41
+ out: './drizzle',
42
+ dialect: 'postgresql',
43
+ dbCredentials: {
44
+ url: process.env.DATABASE_URL!,
45
+ },
46
+ verbose: true,
47
+ strict: true,
48
+ });
49
+ `;
50
+ }
51
+ composeSchema() {
52
+ return `import { boolean, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
53
+
54
+ /**
55
+ * @description Define your database tables here.
56
+ * The 'usersTable' serves as a baseline example.
57
+ * You can add more tables, relations, and indexes according to your domain requirements.
58
+ */
59
+ export const usersTable = pgTable('users', {
60
+ id: serial('id').primaryKey(),
61
+ name: text('name').notNull(),
62
+ email: text('email').notNull().unique(),
63
+ isDeleted: boolean('is_deleted').notNull().default(false),
64
+ deletedAt: timestamp('deleted_at'),
65
+ createdAt: timestamp('created_at').defaultNow(),
66
+ });
67
+
68
+ /**
69
+ * Types inferred from the schema.
70
+ * - UserDto: Represents the shape of the data retrieved from the database ($inferSelect).
71
+ * - NewUserDto: Represents the shape of the data required to insert a new record ($inferInsert).
72
+ */
73
+ export type UserDto = typeof usersTable.$inferSelect;
74
+ export type NewUserDto = typeof usersTable.$inferInsert;
75
+
76
+ /**
77
+ * Centralized Database Schema dictionary.
78
+ */
79
+ export type DbSchema = {
80
+ users: typeof usersTable;
81
+ };
82
+ `;
83
+ }
84
+ }
85
+ exports.DrizzleGenerator = DrizzleGenerator;