@currentjs/gen 0.3.1 → 0.5.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 (69) hide show
  1. package/CHANGELOG.md +8 -289
  2. package/README.md +623 -427
  3. package/dist/cli.js +2 -1
  4. package/dist/commands/commit.js +25 -42
  5. package/dist/commands/createApp.js +1 -0
  6. package/dist/commands/createModule.js +151 -45
  7. package/dist/commands/diff.js +27 -40
  8. package/dist/commands/generateAll.js +141 -291
  9. package/dist/commands/migrateCommit.js +6 -18
  10. package/dist/commands/migratePush.d.ts +1 -0
  11. package/dist/commands/migratePush.js +135 -0
  12. package/dist/commands/migrateUpdate.d.ts +1 -0
  13. package/dist/commands/migrateUpdate.js +147 -0
  14. package/dist/commands/newGenerateAll.d.ts +4 -0
  15. package/dist/commands/newGenerateAll.js +336 -0
  16. package/dist/generators/controllerGenerator.d.ts +43 -19
  17. package/dist/generators/controllerGenerator.js +547 -329
  18. package/dist/generators/domainLayerGenerator.d.ts +21 -0
  19. package/dist/generators/domainLayerGenerator.js +276 -0
  20. package/dist/generators/dtoGenerator.d.ts +21 -0
  21. package/dist/generators/dtoGenerator.js +518 -0
  22. package/dist/generators/newControllerGenerator.d.ts +55 -0
  23. package/dist/generators/newControllerGenerator.js +644 -0
  24. package/dist/generators/newServiceGenerator.d.ts +19 -0
  25. package/dist/generators/newServiceGenerator.js +266 -0
  26. package/dist/generators/newStoreGenerator.d.ts +39 -0
  27. package/dist/generators/newStoreGenerator.js +408 -0
  28. package/dist/generators/newTemplateGenerator.d.ts +29 -0
  29. package/dist/generators/newTemplateGenerator.js +510 -0
  30. package/dist/generators/serviceGenerator.d.ts +16 -51
  31. package/dist/generators/serviceGenerator.js +167 -586
  32. package/dist/generators/storeGenerator.d.ts +35 -32
  33. package/dist/generators/storeGenerator.js +291 -238
  34. package/dist/generators/storeGeneratorV2.d.ts +31 -0
  35. package/dist/generators/storeGeneratorV2.js +190 -0
  36. package/dist/generators/templateGenerator.d.ts +21 -21
  37. package/dist/generators/templateGenerator.js +393 -268
  38. package/dist/generators/templates/appTemplates.d.ts +3 -1
  39. package/dist/generators/templates/appTemplates.js +15 -10
  40. package/dist/generators/templates/data/appYamlTemplate +5 -2
  41. package/dist/generators/templates/data/cursorRulesTemplate +315 -221
  42. package/dist/generators/templates/data/frontendScriptTemplate +76 -47
  43. package/dist/generators/templates/data/mainViewTemplate +1 -1
  44. package/dist/generators/templates/data/systemTsTemplate +5 -0
  45. package/dist/generators/templates/index.d.ts +0 -3
  46. package/dist/generators/templates/index.js +0 -3
  47. package/dist/generators/templates/newStoreTemplates.d.ts +5 -0
  48. package/dist/generators/templates/newStoreTemplates.js +141 -0
  49. package/dist/generators/templates/storeTemplates.d.ts +1 -5
  50. package/dist/generators/templates/storeTemplates.js +102 -219
  51. package/dist/generators/templates/viewTemplates.js +1 -1
  52. package/dist/generators/useCaseGenerator.d.ts +13 -0
  53. package/dist/generators/useCaseGenerator.js +188 -0
  54. package/dist/types/configTypes.d.ts +148 -0
  55. package/dist/types/configTypes.js +10 -0
  56. package/dist/utils/childEntityUtils.d.ts +18 -0
  57. package/dist/utils/childEntityUtils.js +78 -0
  58. package/dist/utils/commandUtils.d.ts +43 -0
  59. package/dist/utils/commandUtils.js +124 -0
  60. package/dist/utils/commitUtils.d.ts +4 -1
  61. package/dist/utils/constants.d.ts +10 -0
  62. package/dist/utils/constants.js +13 -1
  63. package/dist/utils/diResolver.d.ts +32 -0
  64. package/dist/utils/diResolver.js +204 -0
  65. package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
  66. package/dist/utils/new_parts_of_migrationUtils.js +164 -0
  67. package/dist/utils/typeUtils.d.ts +19 -0
  68. package/dist/utils/typeUtils.js +70 -0
  69. package/package.json +7 -3
@@ -1,260 +1,143 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fileTemplates = exports.storeTemplates = void 0;
3
+ exports.storeFileTemplate = exports.storeTemplates = void 0;
4
4
  exports.storeTemplates = {
5
5
  rowInterface: `export interface {{ENTITY_NAME}}Row {
6
6
  id: number;
7
7
  {{ROW_FIELDS}}
8
- created_at: Date;
9
- updated_at: Date;
10
- deleted_at?: Date;
8
+ created_at: string;
9
+ updated_at: string;
10
+ deleted_at?: string;
11
11
  }`,
12
- storeClass: `export class {{ENTITY_NAME}}Store implements StoreInterface<{{ENTITY_NAME}}, {{ENTITY_NAME}}Row> {
13
- private static readonly FILTERABLE_FIELDS = [{{FILTERABLE_FIELDS_ARRAY}}];
14
- private static readonly UPDATABLE_FIELDS = [{{UPDATABLE_FIELDS_ARRAY}}];
15
-
12
+ storeClass: `/**
13
+ * Data access layer for {{ENTITY_NAME}}
14
+ */
15
+ @Injectable()
16
+ export class {{ENTITY_NAME}}Store {
17
+ private tableName = '{{TABLE_NAME}}';
18
+
16
19
  constructor(private db: ISqlProvider) {}
17
20
 
18
- async getById(id: number): Promise<{{ENTITY_NAME}} | null> {
19
- try {
20
- const query = 'SELECT * FROM {{TABLE_NAME}} WHERE id = :id AND deleted_at IS NULL';
21
- const result = await this.db.query(query, { id });
22
-
23
- if (!result.success || result.data.length === 0) {
24
- return null;
25
- }
26
-
27
- return {{ENTITY_NAME}}Store.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
28
- } catch (error) {
29
- if (error instanceof MySQLConnectionError) {
30
- throw new Error(\`Database connection error while fetching {{ENTITY_NAME}} with id \${id}: \${error.message}\`);
31
- } else if (error instanceof MySQLQueryError) {
32
- throw new Error(\`Query error while fetching {{ENTITY_NAME}} with id \${id}: \${error.message}\`);
33
- }
34
- throw error;
35
- }
21
+ private toMySQLDatetime(date: Date): string {
22
+ return date.toISOString().slice(0, 19).replace('T', ' ');
36
23
  }
37
24
 
38
- async getAll(page: number = 1, limit: number = 10): Promise<{{ENTITY_NAME}}[]> {
39
- const offset = (page - 1) * limit;
40
- const query = \`SELECT * FROM {{TABLE_NAME}} WHERE deleted_at IS NULL ORDER BY created_at DESC LIMIT \${limit} OFFSET \${offset}\`;
41
- const result = await this.db.query(query, {});
42
-
43
- if (!result.success) {
44
- return [];
45
- }
46
-
47
- return result.data.map((row: {{ENTITY_NAME}}Row) => {{ENTITY_NAME}}Store.rowToModel(row));
25
+ private rowToModel(row: {{ENTITY_NAME}}Row): {{ENTITY_NAME}} {
26
+ return new {{ENTITY_NAME}}(
27
+ row.id,
28
+ {{ROW_TO_MODEL_MAPPING}}
29
+ );
48
30
  }
49
31
 
50
- async getAllByUserId(userId: number, page: number = 1, limit: number = 10): Promise<{{ENTITY_NAME}}[]> {
32
+ async getAll(page: number = 1, limit: number = 20): Promise<{{ENTITY_NAME}}[]> {
51
33
  const offset = (page - 1) * limit;
52
- const query = \`SELECT * FROM {{TABLE_NAME}} WHERE user_id = :userId AND deleted_at IS NULL ORDER BY created_at DESC LIMIT \${limit} OFFSET \${offset}\`;
53
- const result = await this.db.query(query, { userId });
54
-
55
- if (!result.success) {
56
- return [];
57
- }
58
-
59
- return result.data.map((row: {{ENTITY_NAME}}Row) => {{ENTITY_NAME}}Store.rowToModel(row));
60
- }
34
+ const result = await this.db.query(
35
+ \`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL LIMIT :limit OFFSET :offset\`,
36
+ { limit: String(limit), offset: String(offset) }
37
+ );
61
38
 
62
- async getBy(filters: Partial<Pick<{{ENTITY_NAME}}Row, {{FILTERABLE_FIELDS}}>>): Promise<{{ENTITY_NAME}} | null> {
63
- const filterKeys = Object.keys(filters);
64
-
65
- // Runtime validation against SQL injection
66
- for (const key of filterKeys) {
67
- if (!{{ENTITY_NAME}}Store.FILTERABLE_FIELDS.includes(key)) {
68
- throw new Error(\`Invalid filter field: \${key}\`);
69
- }
39
+ if (result.success && result.data) {
40
+ return result.data.map((row: {{ENTITY_NAME}}Row) => this.rowToModel(row));
70
41
  }
71
-
72
- const whereConditions = filterKeys.map(key => \`\${key} = :\${key}\`);
73
-
74
- if (whereConditions.length === 0) {
75
- throw new Error('At least one filter condition is required');
76
- }
77
-
78
- const query = \`SELECT * FROM {{TABLE_NAME}} WHERE \${whereConditions.join(' AND ')} AND deleted_at IS NULL LIMIT 1\`;
79
- const result = await this.db.query(query, filters);
80
-
81
- if (!result.success || result.data.length === 0) {
82
- return null;
83
- }
84
-
85
- return {{ENTITY_NAME}}Store.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
42
+ return [];
86
43
  }
87
44
 
88
- async getAllBy(filters: Partial<Pick<{{ENTITY_NAME}}Row, {{FILTERABLE_FIELDS}}>>): Promise<{{ENTITY_NAME}}[]> {
89
- const filterKeys = Object.keys(filters);
90
-
91
- // Runtime validation against SQL injection
92
- for (const key of filterKeys) {
93
- if (!{{ENTITY_NAME}}Store.FILTERABLE_FIELDS.includes(key)) {
94
- throw new Error(\`Invalid filter field: \${key}\`);
95
- }
96
- }
97
-
98
- const whereConditions = filterKeys.map(key => \`\${key} = :\${key}\`);
99
-
100
- if (whereConditions.length === 0) {
101
- return this.getAll();
102
- }
103
-
104
- const query = \`SELECT * FROM {{TABLE_NAME}} WHERE \${whereConditions.join(' AND ')} AND deleted_at IS NULL ORDER BY created_at DESC\`;
105
- const result = await this.db.query(query, filters);
106
-
107
- if (!result.success) {
108
- return [];
45
+ async count(): Promise<number> {
46
+ const result = await this.db.query(
47
+ \`SELECT COUNT(*) as count FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL\`,
48
+ {}
49
+ );
50
+
51
+ if (result.success && result.data && result.data.length > 0) {
52
+ return parseInt(result.data[0].count, 10);
109
53
  }
110
-
111
- return result.data.map((row: {{ENTITY_NAME}}Row) => {{ENTITY_NAME}}Store.rowToModel(row));
54
+ return 0;
112
55
  }
113
56
 
114
- async insert(model: Omit<{{ENTITY_NAME}}, 'id'>): Promise<{{ENTITY_NAME}}> {
115
- try {
116
- const row = {{ENTITY_NAME}}Store.modelToRow(model as {{ENTITY_NAME}});
117
- delete row.id; // Remove id for insert
118
- row.created_at = new Date();
119
- row.updated_at = new Date();
120
-
121
- const fields = Object.keys(row);
122
- const placeholders = fields.map(field => \`:\${field}\`).join(', ');
123
-
124
- const query = \`INSERT INTO {{TABLE_NAME}} (\${fields.join(', ')}) VALUES (\${placeholders})\`;
125
- const result = await this.db.query(query, row);
126
-
127
- if (!result.success || !result.insertId) {
128
- throw new Error('Failed to insert {{ENTITY_NAME}}: Insert operation did not return a valid ID');
129
- }
130
-
131
- return this.getById(Number(result.insertId));
132
- } catch (error) {
133
- if (error instanceof MySQLQueryError) {
134
- throw new Error(\`Failed to insert {{ENTITY_NAME}}: \${error.message}\`);
135
- } else if (error instanceof MySQLConnectionError) {
136
- throw new Error(\`Database connection error while inserting {{ENTITY_NAME}}: \${error.message}\`);
137
- }
138
- throw error;
57
+ async getById(id: number): Promise<{{ENTITY_NAME}} | null> {
58
+ const result = await this.db.query(
59
+ \`SELECT {{FIELD_NAMES}} FROM \\\`\${this.tableName}\\\` WHERE id = :id AND deleted_at IS NULL\`,
60
+ { id }
61
+ );
62
+
63
+ if (result.success && result.data && result.data.length > 0) {
64
+ return this.rowToModel(result.data[0] as {{ENTITY_NAME}}Row);
139
65
  }
66
+ return null;
140
67
  }
141
68
 
142
- async update(id: number, updates: Partial<Omit<{{ENTITY_NAME}}, 'id' | 'createdAt'>>): Promise<{{ENTITY_NAME}} | null> {
143
- const existing = await this.getById(id);
144
- if (!existing) {
145
- return null;
146
- }
147
-
148
- // Extract only data properties, not methods
149
- const updateData = this.extractDataProperties(updates);
150
- const updateKeys = Object.keys(updateData);
151
-
152
- // Runtime validation against SQL injection
153
- for (const key of updateKeys) {
154
- if (!{{ENTITY_NAME}}Store.UPDATABLE_FIELDS.includes(key)) {
155
- throw new Error(\`Invalid update field: \${key}\`);
156
- }
157
- }
158
-
159
- const updateFields = updateKeys.map(key => \`\${key} = :\${key}\`);
160
-
161
- if (updateFields.length === 0) {
162
- return existing;
69
+ async insert(entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
70
+ const now = new Date();
71
+ const data: Partial<{{ENTITY_NAME}}Row> = {
72
+ {{INSERT_DATA_MAPPING}},
73
+ created_at: this.toMySQLDatetime(now),
74
+ updated_at: this.toMySQLDatetime(now)
75
+ };
76
+
77
+ const fieldsList = Object.keys(data).map(f => \`\\\`\${f}\\\`\`).join(', ');
78
+ const placeholders = Object.keys(data).map(f => \`:\${f}\`).join(', ');
79
+
80
+ const result = await this.db.query(
81
+ \`INSERT INTO \\\`\${this.tableName}\\\` (\${fieldsList}) VALUES (\${placeholders})\`,
82
+ data
83
+ );
84
+
85
+ if (result.success && result.insertId) {
86
+ const newId = typeof result.insertId === 'string' ? parseInt(result.insertId, 10) : result.insertId;
87
+ return this.getById(newId) as Promise<{{ENTITY_NAME}}>;
163
88
  }
164
-
165
- const params = { ...updateData, updated_at: new Date(), id };
166
-
167
- const query = \`UPDATE {{TABLE_NAME}} SET \${updateFields.join(', ')}, updated_at = :updated_at WHERE id = :id\`;
168
- await this.db.query(query, params);
169
-
170
- return this.getById(id);
89
+
90
+ throw new Error('Failed to insert {{ENTITY_NAME}}');
171
91
  }
172
92
 
173
- async upsert(model: Partial<{{ENTITY_NAME}}>): Promise<{{ENTITY_NAME}}> {
174
- if (model.id) {
175
- const existing = await this.getById(model.id);
176
- if (existing) {
177
- return this.update(model.id, model) as Promise<{{ENTITY_NAME}}>;
178
- }
93
+ async update(id: number, entity: {{ENTITY_NAME}}): Promise<{{ENTITY_NAME}}> {
94
+ const now = new Date();
95
+ const data: Partial<{{ENTITY_NAME}}Row> & { id: number } = {
96
+ {{UPDATE_DATA_MAPPING}},
97
+ updated_at: this.toMySQLDatetime(now),
98
+ id
99
+ };
100
+
101
+ const updateFields = {{UPDATE_FIELDS_ARRAY}}.map(f => \`\\\`\${f}\\\` = :\${f}\`).join(', ');
102
+
103
+ const result = await this.db.query(
104
+ \`UPDATE \\\`\${this.tableName}\\\` SET \${updateFields}, updated_at = :updated_at WHERE id = :id\`,
105
+ data
106
+ );
107
+
108
+ if (result.success) {
109
+ return this.getById(id) as Promise<{{ENTITY_NAME}}>;
179
110
  }
180
-
181
- return this.insert(model as Omit<{{ENTITY_NAME}}, 'id'>);
111
+
112
+ throw new Error('Failed to update {{ENTITY_NAME}}');
182
113
  }
183
114
 
184
115
  async softDelete(id: number): Promise<boolean> {
185
- const query = 'UPDATE {{TABLE_NAME}} SET deleted_at = :deleted_at WHERE id = :id AND deleted_at IS NULL';
186
- const result = await this.db.query(query, { deleted_at: new Date(), id });
187
-
188
- return result.success && (result.affectedRows || 0) > 0;
189
- }
116
+ const now = new Date();
117
+ const result = await this.db.query(
118
+ \`UPDATE \\\`\${this.tableName}\\\` SET deleted_at = :deleted_at WHERE id = :id\`,
119
+ { deleted_at: this.toMySQLDatetime(now), id }
120
+ );
190
121
 
191
- async count(filters?: Partial<Pick<{{ENTITY_NAME}}Row, {{FILTERABLE_FIELDS}}>>): Promise<number> {
192
- let query = 'SELECT COUNT(*) as count FROM {{TABLE_NAME}} WHERE deleted_at IS NULL';
193
- let params: Record<string, any> = {};
194
-
195
- if (filters && Object.keys(filters).length > 0) {
196
- const whereConditions = Object.keys(filters).map(key => \`\${key} = :\${key}\`);
197
- params = { ...filters };
198
- query += \` AND \${whereConditions.join(' AND ')}\`;
199
- }
200
-
201
- const result = await this.db.query(query, params);
202
-
203
- if (!result.success || result.data.length === 0) {
204
- return 0;
205
- }
206
-
207
- return result.data[0].count;
122
+ return result.success;
208
123
  }
209
124
 
210
- {{CONVERSION_METHODS}}
211
- }`,
212
- conversionMethods: ` static rowToModel(row: {{ENTITY_NAME}}Row): {{ENTITY_NAME}} {
213
- return new {{ENTITY_NAME}}(
214
- row.id,
215
- {{ROW_TO_MODEL_MAPPING}}
125
+ async hardDelete(id: number): Promise<boolean> {
126
+ const result = await this.db.query(
127
+ \`DELETE FROM \\\`\${this.tableName}\\\` WHERE id = :id\`,
128
+ { id }
216
129
  );
217
- }
218
130
 
219
- static modelToRow(model: {{ENTITY_NAME}}): Partial<{{ENTITY_NAME}}Row> {
220
- return {
221
- id: model.id,
222
- {{MODEL_TO_ROW_MAPPING}},
223
- updated_at: new Date()
224
- };
131
+ return result.success;
225
132
  }
226
-
227
- private extractDataProperties(obj: any): Record<string, any> {
228
- const result: Record<string, any> = {};
229
-
230
- for (const [key, value] of Object.entries(obj)) {
231
- // Only include properties that are not functions and are in updatable fields
232
- if (typeof value !== 'function' && {{ENTITY_NAME}}Store.UPDATABLE_FIELDS.includes(key)) {
233
- result[key] = value;
234
- }
235
- }
236
-
237
- return result;
238
- }`
133
+ {{GET_BY_PARENT_ID_METHOD}}
134
+ {{GET_RESOURCE_OWNER_METHOD}}}`
239
135
  };
240
- exports.fileTemplates = {
241
- storeFile: `import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';
242
- import { StoreInterface } from '../interfaces/StoreInterface';
243
- import { ProviderMysql, ISqlProvider, MySQLQueryError, MySQLConnectionError } from '@currentjs/provider-mysql';
136
+ exports.storeFileTemplate = `import { Injectable } from '../../../../system';
137
+ import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';
138
+ import type { ISqlProvider } from '@currentjs/provider-mysql';{{VALUE_OBJECT_IMPORTS}}
244
139
 
245
140
  {{ROW_INTERFACE}}
246
141
 
247
- {{STORE_CLASS}}`,
248
- storeInterface: `export interface StoreInterface<TModel, TRow> {
249
- getById(id: number): Promise<TModel | null>;
250
- getAll(page?: number, limit?: number): Promise<TModel[]>;
251
- getAllByUserId(userId: number, page?: number, limit?: number): Promise<TModel[]>;
252
- getBy(filters: Partial<TRow>): Promise<TModel | null>;
253
- getAllBy(filters: Partial<TRow>): Promise<TModel[]>;
254
- insert(model: Omit<TModel, 'id'>): Promise<TModel>;
255
- update(id: number, updates: Partial<Omit<TModel, 'id' | 'createdAt'>>): Promise<TModel | null>;
256
- upsert(model: Partial<TModel>): Promise<TModel>;
257
- softDelete(id: number): Promise<boolean>;
258
- count(filters?: Partial<TRow>): Promise<number>;
259
- }`
260
- };
142
+ {{STORE_CLASS}}
143
+ `;
@@ -484,7 +484,7 @@ function renderLayoutTemplate(layoutName) {
484
484
  <title>${layoutName}</title>
485
485
  </head>
486
486
  <body>
487
- {{ content }}
487
+ {{{ content }}}
488
488
  </body>
489
489
  </html>
490
490
  `;
@@ -0,0 +1,13 @@
1
+ import { ModuleConfig } from '../types/configTypes';
2
+ export declare class UseCaseGenerator {
3
+ private availableAggregates;
4
+ private generateUseCaseMethod;
5
+ private generateGetResourceOwnerMethod;
6
+ private generateUseCase;
7
+ generateFromConfig(config: ModuleConfig): Record<string, string>;
8
+ generateFromYamlFile(yamlFilePath: string): Record<string, string>;
9
+ generateAndSaveFiles(yamlFilePath: string, moduleDir: string, opts?: {
10
+ force?: boolean;
11
+ skipOnConflict?: boolean;
12
+ }): Promise<void>;
13
+ }
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.UseCaseGenerator = void 0;
37
+ const yaml_1 = require("yaml");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const generationRegistry_1 = require("../utils/generationRegistry");
41
+ const colors_1 = require("../utils/colors");
42
+ const configTypes_1 = require("../types/configTypes");
43
+ const typeUtils_1 = require("../utils/typeUtils");
44
+ class UseCaseGenerator {
45
+ constructor() {
46
+ this.availableAggregates = new Map();
47
+ }
48
+ generateUseCaseMethod(modelName, actionName, useCaseConfig) {
49
+ const methodName = actionName;
50
+ const inputType = `${modelName}${(0, typeUtils_1.capitalize)(actionName)}Input`;
51
+ // Determine the return type based on action
52
+ let returnType;
53
+ if (useCaseConfig.output === 'void') {
54
+ returnType = '{ success: boolean; message: string }';
55
+ }
56
+ else if (actionName === 'list') {
57
+ returnType = `{ items: ${modelName}[]; total: number; page: number; limit: number }`;
58
+ }
59
+ else {
60
+ returnType = modelName;
61
+ }
62
+ // Generate handler calls
63
+ const handlerCalls = useCaseConfig.handlers.map((handler, index) => {
64
+ var _a;
65
+ const isLast = index === useCaseConfig.handlers.length - 1;
66
+ if (handler.startsWith('default:')) {
67
+ // Default handler - call service method directly
68
+ const defaultAction = handler.replace('default:', '');
69
+ const resultVar = isLast ? 'result' : `result${index}`;
70
+ // Determine parameters based on action
71
+ let params = '';
72
+ if (defaultAction === 'list') {
73
+ params = ((_a = useCaseConfig.input) === null || _a === void 0 ? void 0 : _a.pagination)
74
+ ? 'input.page || 1, input.limit || 20'
75
+ : '';
76
+ }
77
+ else if (defaultAction === 'get') {
78
+ params = 'input.id';
79
+ }
80
+ else if (defaultAction === 'create') {
81
+ params = 'input';
82
+ }
83
+ else if (defaultAction === 'update') {
84
+ params = 'input.id, input';
85
+ }
86
+ else if (defaultAction === 'delete') {
87
+ params = 'input.id';
88
+ }
89
+ else {
90
+ params = 'input';
91
+ }
92
+ return ` const ${resultVar} = await this.${modelName.toLowerCase()}Service.${defaultAction}(${params});`;
93
+ }
94
+ else {
95
+ // Custom handler
96
+ const prevResult = index === 0 ? 'null' : `result${index - 1}`;
97
+ const resultVar = isLast ? 'result' : `result${index}`;
98
+ return ` const ${resultVar} = await this.${modelName.toLowerCase()}Service.${handler}(${prevResult}, input);`;
99
+ }
100
+ }).join('\n');
101
+ const returnStatement = '\n return result;';
102
+ return ` async ${methodName}(input: ${inputType}): Promise<${returnType}> {
103
+ ${handlerCalls}${returnStatement}
104
+ }`;
105
+ }
106
+ generateGetResourceOwnerMethod(modelName) {
107
+ const serviceVar = `${modelName.toLowerCase()}Service`;
108
+ return `
109
+ /**
110
+ * Get the owner ID of a resource by its ID.
111
+ * Used for pre-mutation authorization checks in controllers.
112
+ */
113
+ async getResourceOwner(id: number): Promise<number | null> {
114
+ return await this.${serviceVar}.getResourceOwner(id);
115
+ }`;
116
+ }
117
+ generateUseCase(modelName, useCases) {
118
+ const className = `${modelName}UseCase`;
119
+ const serviceName = `${modelName}Service`;
120
+ const serviceVar = `${modelName.toLowerCase()}Service`;
121
+ // Generate imports for DTOs (only Input types since UseCases return models)
122
+ const dtoImports = Object.keys(useCases)
123
+ .map(actionName => {
124
+ const inputType = `${modelName}${(0, typeUtils_1.capitalize)(actionName)}Input`;
125
+ return `import { ${inputType} } from '../dto/${modelName}${(0, typeUtils_1.capitalize)(actionName)}';`;
126
+ })
127
+ .join('\n');
128
+ // Generate methods
129
+ const methods = Object.entries(useCases)
130
+ .map(([actionName, useCaseConfig]) => this.generateUseCaseMethod(modelName, actionName, useCaseConfig))
131
+ .join('\n\n');
132
+ const getResourceOwnerMethod = this.generateGetResourceOwnerMethod(modelName);
133
+ return `import { Injectable } from '../../../../system';
134
+ import { ${modelName} } from '../../domain/entities/${modelName}';
135
+ ${dtoImports}
136
+ import { ${serviceName} } from '../services/${serviceName}';
137
+
138
+ /**
139
+ * Use Case orchestrator for ${modelName}
140
+ * Coordinates business logic by calling service handlers in sequence
141
+ */
142
+ @Injectable()
143
+ export class ${className} {
144
+ constructor(
145
+ private ${serviceVar}: ${serviceName}
146
+ ) {}
147
+
148
+ ${methods}${getResourceOwnerMethod}
149
+ }`;
150
+ }
151
+ generateFromConfig(config) {
152
+ var _a;
153
+ const result = {};
154
+ // Collect all aggregates to know which are roots
155
+ this.availableAggregates.clear();
156
+ if ((_a = config.domain) === null || _a === void 0 ? void 0 : _a.aggregates) {
157
+ Object.entries(config.domain.aggregates).forEach(([name, aggConfig]) => {
158
+ this.availableAggregates.set(name, aggConfig);
159
+ });
160
+ }
161
+ // Generate a UseCase file for each model
162
+ Object.entries(config.useCases).forEach(([modelName, useCases]) => {
163
+ result[modelName] = this.generateUseCase(modelName, useCases);
164
+ });
165
+ return result;
166
+ }
167
+ generateFromYamlFile(yamlFilePath) {
168
+ const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
169
+ const config = (0, yaml_1.parse)(yamlContent);
170
+ if (!(0, configTypes_1.isValidModuleConfig)(config)) {
171
+ throw new Error('Configuration does not match new module format. Expected useCases structure.');
172
+ }
173
+ return this.generateFromConfig(config);
174
+ }
175
+ async generateAndSaveFiles(yamlFilePath, moduleDir, opts) {
176
+ const useCasesByModel = this.generateFromYamlFile(yamlFilePath);
177
+ const useCasesDir = path.join(moduleDir, 'application', 'useCases');
178
+ fs.mkdirSync(useCasesDir, { recursive: true });
179
+ for (const [modelName, code] of Object.entries(useCasesByModel)) {
180
+ const filePath = path.join(useCasesDir, `${modelName}UseCase.ts`);
181
+ // eslint-disable-next-line no-await-in-loop
182
+ await (0, generationRegistry_1.writeGeneratedFile)(filePath, code, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skipOnConflict) });
183
+ }
184
+ // eslint-disable-next-line no-console
185
+ console.log('\n' + colors_1.colors.green('Use Case files generated successfully!') + '\n');
186
+ }
187
+ }
188
+ exports.UseCaseGenerator = UseCaseGenerator;