@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.
- package/CHANGELOG.md +8 -289
- package/README.md +623 -427
- package/dist/cli.js +2 -1
- package/dist/commands/commit.js +25 -42
- package/dist/commands/createApp.js +1 -0
- package/dist/commands/createModule.js +151 -45
- package/dist/commands/diff.js +27 -40
- package/dist/commands/generateAll.js +141 -291
- package/dist/commands/migrateCommit.js +6 -18
- package/dist/commands/migratePush.d.ts +1 -0
- package/dist/commands/migratePush.js +135 -0
- package/dist/commands/migrateUpdate.d.ts +1 -0
- package/dist/commands/migrateUpdate.js +147 -0
- package/dist/commands/newGenerateAll.d.ts +4 -0
- package/dist/commands/newGenerateAll.js +336 -0
- package/dist/generators/controllerGenerator.d.ts +43 -19
- package/dist/generators/controllerGenerator.js +547 -329
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +276 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +518 -0
- package/dist/generators/newControllerGenerator.d.ts +55 -0
- package/dist/generators/newControllerGenerator.js +644 -0
- package/dist/generators/newServiceGenerator.d.ts +19 -0
- package/dist/generators/newServiceGenerator.js +266 -0
- package/dist/generators/newStoreGenerator.d.ts +39 -0
- package/dist/generators/newStoreGenerator.js +408 -0
- package/dist/generators/newTemplateGenerator.d.ts +29 -0
- package/dist/generators/newTemplateGenerator.js +510 -0
- package/dist/generators/serviceGenerator.d.ts +16 -51
- package/dist/generators/serviceGenerator.js +167 -586
- package/dist/generators/storeGenerator.d.ts +35 -32
- package/dist/generators/storeGenerator.js +291 -238
- package/dist/generators/storeGeneratorV2.d.ts +31 -0
- package/dist/generators/storeGeneratorV2.js +190 -0
- package/dist/generators/templateGenerator.d.ts +21 -21
- package/dist/generators/templateGenerator.js +393 -268
- package/dist/generators/templates/appTemplates.d.ts +3 -1
- package/dist/generators/templates/appTemplates.js +15 -10
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +76 -47
- package/dist/generators/templates/data/mainViewTemplate +1 -1
- package/dist/generators/templates/data/systemTsTemplate +5 -0
- package/dist/generators/templates/index.d.ts +0 -3
- package/dist/generators/templates/index.js +0 -3
- package/dist/generators/templates/newStoreTemplates.d.ts +5 -0
- package/dist/generators/templates/newStoreTemplates.js +141 -0
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +102 -219
- package/dist/generators/templates/viewTemplates.js +1 -1
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +188 -0
- package/dist/types/configTypes.d.ts +148 -0
- package/dist/types/configTypes.js +10 -0
- package/dist/utils/childEntityUtils.d.ts +18 -0
- package/dist/utils/childEntityUtils.js +78 -0
- package/dist/utils/commandUtils.d.ts +43 -0
- package/dist/utils/commandUtils.js +124 -0
- package/dist/utils/commitUtils.d.ts +4 -1
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +13 -1
- package/dist/utils/diResolver.d.ts +32 -0
- package/dist/utils/diResolver.js +204 -0
- package/dist/utils/new_parts_of_migrationUtils.d.ts +0 -0
- package/dist/utils/new_parts_of_migrationUtils.js +164 -0
- package/dist/utils/typeUtils.d.ts +19 -0
- package/dist/utils/typeUtils.js +70 -0
- package/package.json +7 -3
|
@@ -1,260 +1,143 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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:
|
|
9
|
-
updated_at:
|
|
10
|
-
deleted_at?:
|
|
8
|
+
created_at: string;
|
|
9
|
+
updated_at: string;
|
|
10
|
+
deleted_at?: string;
|
|
11
11
|
}`,
|
|
12
|
-
storeClass:
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
32
|
+
async getAll(page: number = 1, limit: number = 20): Promise<{{ENTITY_NAME}}[]> {
|
|
51
33
|
const offset = (page - 1) * limit;
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
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
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
111
|
+
|
|
112
|
+
throw new Error('Failed to update {{ENTITY_NAME}}');
|
|
182
113
|
}
|
|
183
114
|
|
|
184
115
|
async softDelete(id: number): Promise<boolean> {
|
|
185
|
-
const
|
|
186
|
-
const result = await this.db.query(
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
241
|
-
|
|
242
|
-
import {
|
|
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
|
-
|
|
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
|
+
`;
|
|
@@ -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;
|