@currentjs/gen 0.3.2 → 0.5.1

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 (60) hide show
  1. package/CHANGELOG.md +18 -609
  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/generators/controllerGenerator.d.ts +50 -19
  11. package/dist/generators/controllerGenerator.js +588 -331
  12. package/dist/generators/domainLayerGenerator.d.ts +21 -0
  13. package/dist/generators/domainLayerGenerator.js +286 -0
  14. package/dist/generators/dtoGenerator.d.ts +21 -0
  15. package/dist/generators/dtoGenerator.js +523 -0
  16. package/dist/generators/serviceGenerator.d.ts +22 -51
  17. package/dist/generators/serviceGenerator.js +345 -568
  18. package/dist/generators/storeGenerator.d.ts +39 -32
  19. package/dist/generators/storeGenerator.js +396 -236
  20. package/dist/generators/templateGenerator.d.ts +21 -21
  21. package/dist/generators/templateGenerator.js +393 -268
  22. package/dist/generators/templates/appTemplates.d.ts +3 -1
  23. package/dist/generators/templates/appTemplates.js +16 -11
  24. package/dist/generators/templates/data/appYamlTemplate +5 -2
  25. package/dist/generators/templates/data/cursorRulesTemplate +315 -221
  26. package/dist/generators/templates/data/frontendScriptTemplate +56 -15
  27. package/dist/generators/templates/data/mainViewTemplate +2 -1
  28. package/dist/generators/templates/data/systemTsTemplate +5 -0
  29. package/dist/generators/templates/index.d.ts +0 -3
  30. package/dist/generators/templates/index.js +0 -3
  31. package/dist/generators/templates/storeTemplates.d.ts +1 -5
  32. package/dist/generators/templates/storeTemplates.js +84 -224
  33. package/dist/generators/useCaseGenerator.d.ts +13 -0
  34. package/dist/generators/useCaseGenerator.js +191 -0
  35. package/dist/types/configTypes.d.ts +149 -0
  36. package/dist/types/configTypes.js +10 -0
  37. package/dist/utils/childEntityUtils.d.ts +18 -0
  38. package/dist/utils/childEntityUtils.js +78 -0
  39. package/dist/utils/commandUtils.d.ts +43 -0
  40. package/dist/utils/commandUtils.js +124 -0
  41. package/dist/utils/commitUtils.d.ts +4 -1
  42. package/dist/utils/constants.d.ts +10 -0
  43. package/dist/utils/constants.js +13 -1
  44. package/dist/utils/diResolver.d.ts +32 -0
  45. package/dist/utils/diResolver.js +204 -0
  46. package/dist/utils/typeUtils.d.ts +23 -0
  47. package/dist/utils/typeUtils.js +77 -0
  48. package/package.json +7 -3
  49. package/dist/generators/domainModelGenerator.d.ts +0 -41
  50. package/dist/generators/domainModelGenerator.js +0 -242
  51. package/dist/generators/templates/controllerTemplates.d.ts +0 -43
  52. package/dist/generators/templates/controllerTemplates.js +0 -82
  53. package/dist/generators/templates/serviceTemplates.d.ts +0 -16
  54. package/dist/generators/templates/serviceTemplates.js +0 -59
  55. package/dist/generators/templates/validationTemplates.d.ts +0 -25
  56. package/dist/generators/templates/validationTemplates.js +0 -66
  57. package/dist/generators/templates/viewTemplates.d.ts +0 -25
  58. package/dist/generators/templates/viewTemplates.js +0 -491
  59. package/dist/generators/validationGenerator.d.ts +0 -29
  60. package/dist/generators/validationGenerator.js +0 -250
@@ -0,0 +1,523 @@
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.DtoGenerator = 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 childEntityUtils_1 = require("../utils/childEntityUtils");
44
+ const typeUtils_1 = require("../utils/typeUtils");
45
+ class DtoGenerator {
46
+ constructor() {
47
+ this.availableAggregates = new Map();
48
+ this.availableValueObjects = new Map();
49
+ }
50
+ mapType(yamlType) {
51
+ return (0, typeUtils_1.mapType)(yamlType, this.availableAggregates, this.availableValueObjects);
52
+ }
53
+ isValueObjectType(yamlType) {
54
+ const capitalizedType = (0, typeUtils_1.capitalize)(yamlType);
55
+ return this.availableValueObjects.has(capitalizedType);
56
+ }
57
+ getValidationCode(fieldName, fieldType, isRequired) {
58
+ const checks = [];
59
+ if (isRequired) {
60
+ if (fieldType === 'string') {
61
+ checks.push(` if (typeof b.${fieldName} !== 'string' || !b.${fieldName}) {
62
+ throw new Error('${fieldName} is required');
63
+ }`);
64
+ }
65
+ else if (fieldType === 'number' || fieldType === 'integer' || fieldType === 'decimal' || fieldType === 'id') {
66
+ checks.push(` if (b.${fieldName} === undefined || b.${fieldName} === null) {
67
+ throw new Error('${fieldName} is required');
68
+ }`);
69
+ }
70
+ else if (fieldType === 'boolean') {
71
+ checks.push(` if (typeof b.${fieldName} !== 'boolean') {
72
+ throw new Error('${fieldName} is required');
73
+ }`);
74
+ }
75
+ else if (fieldType === 'datetime' || fieldType === 'date') {
76
+ checks.push(` if (!b.${fieldName}) {
77
+ throw new Error('${fieldName} is required');
78
+ }`);
79
+ }
80
+ else {
81
+ checks.push(` if (b.${fieldName} === undefined) {
82
+ throw new Error('${fieldName} is required');
83
+ }`);
84
+ }
85
+ }
86
+ return checks;
87
+ }
88
+ getTransformCode(fieldName, fieldType) {
89
+ if (fieldType === 'datetime' || fieldType === 'date') {
90
+ return `b.${fieldName} ? new Date(b.${fieldName} as string) : undefined`;
91
+ }
92
+ else if (fieldType === 'number' || fieldType === 'integer' || fieldType === 'decimal' || fieldType === 'id') {
93
+ return `typeof b.${fieldName} === 'string' ? parseFloat(b.${fieldName}) : b.${fieldName} as number`;
94
+ }
95
+ else if (fieldType === 'boolean') {
96
+ return `Boolean(b.${fieldName})`;
97
+ }
98
+ return `b.${fieldName} as ${this.mapType(fieldType)}`;
99
+ }
100
+ generateInputDto(modelName, actionName, inputConfig, aggregateConfig, childInfo) {
101
+ var _a, _b, _c, _d;
102
+ const className = `${modelName}${(0, typeUtils_1.capitalize)(actionName)}Input`;
103
+ if (!inputConfig) {
104
+ return `export class ${className} {
105
+ private constructor() {}
106
+
107
+ static parse(body: unknown): ${className} {
108
+ return new ${className}();
109
+ }
110
+ }`;
111
+ }
112
+ const fieldDeclarations = [];
113
+ const constructorParams = [];
114
+ const constructorAssignments = [];
115
+ const validationChecks = [];
116
+ const fieldTransforms = [];
117
+ // Handle identifier (for get, update, delete)
118
+ if (inputConfig.identifier) {
119
+ const fieldName = inputConfig.identifier;
120
+ fieldDeclarations.push(` readonly ${fieldName}: number;`);
121
+ constructorParams.push(`${fieldName}: number`);
122
+ constructorAssignments.push(` this.${fieldName} = ${fieldName};`);
123
+ validationChecks.push(` if (b.${fieldName} === undefined || b.${fieldName} === null) {
124
+ throw new Error('${fieldName} is required');
125
+ }`);
126
+ fieldTransforms.push(` ${fieldName}: typeof b.${fieldName} === 'string' ? parseInt(b.${fieldName}, 10) : b.${fieldName} as number`);
127
+ }
128
+ // Handle pagination
129
+ if (inputConfig.pagination) {
130
+ fieldDeclarations.push(` readonly page: number;`);
131
+ fieldDeclarations.push(` readonly limit: number;`);
132
+ constructorParams.push(`page: number`);
133
+ constructorParams.push(`limit: number`);
134
+ constructorAssignments.push(` this.page = page;`);
135
+ constructorAssignments.push(` this.limit = limit;`);
136
+ const defaultLimit = ((_a = inputConfig.pagination.defaults) === null || _a === void 0 ? void 0 : _a.limit) || 20;
137
+ const maxLimit = ((_b = inputConfig.pagination.defaults) === null || _b === void 0 ? void 0 : _b.maxLimit) || 100;
138
+ fieldTransforms.push(` page: typeof b.page === 'string' ? parseInt(b.page, 10) : (b.page as number || 1)`);
139
+ fieldTransforms.push(` limit: Math.min(typeof b.limit === 'string' ? parseInt(b.limit, 10) : (b.limit as number || ${defaultLimit}), ${maxLimit})`);
140
+ if (inputConfig.pagination.type === 'cursor') {
141
+ fieldDeclarations.push(` readonly cursor?: string;`);
142
+ constructorParams.push(`cursor?: string`);
143
+ constructorAssignments.push(` this.cursor = cursor;`);
144
+ fieldTransforms.push(` cursor: b.cursor as string | undefined`);
145
+ }
146
+ }
147
+ // Handle fields from aggregate (pick/omit/add)
148
+ if (inputConfig.from) {
149
+ const aggregateFields = Object.entries(aggregateConfig.fields);
150
+ let fieldsToInclude = aggregateFields;
151
+ // Apply pick
152
+ if (inputConfig.pick && inputConfig.pick.length > 0) {
153
+ fieldsToInclude = fieldsToInclude.filter(([fieldName]) => inputConfig.pick.includes(fieldName));
154
+ }
155
+ // Apply omit
156
+ if (inputConfig.omit && inputConfig.omit.length > 0) {
157
+ fieldsToInclude = fieldsToInclude.filter(([fieldName]) => !inputConfig.omit.includes(fieldName));
158
+ }
159
+ const isCreateAction = !inputConfig.identifier && !inputConfig.partial;
160
+ if (isCreateAction) {
161
+ const ownerOrParentField = childInfo ? childInfo.parentIdField : 'ownerId';
162
+ fieldDeclarations.push(` readonly ${ownerOrParentField}: number;`);
163
+ constructorParams.push(`${ownerOrParentField}: number`);
164
+ constructorAssignments.push(` this.${ownerOrParentField} = ${ownerOrParentField};`);
165
+ validationChecks.push(` if (b.${ownerOrParentField} === undefined || b.${ownerOrParentField} === null) {
166
+ throw new Error('${ownerOrParentField} is required');
167
+ }`);
168
+ fieldTransforms.push(` ${ownerOrParentField}: typeof b.${ownerOrParentField} === 'string' ? parseInt(b.${ownerOrParentField}, 10) : b.${ownerOrParentField} as number`);
169
+ }
170
+ // Add fields
171
+ fieldsToInclude.forEach(([fieldName, fieldConfig]) => {
172
+ if (fieldName === 'id' || fieldConfig.auto)
173
+ return;
174
+ const isAggRef = (0, typeUtils_1.isAggregateReference)(fieldConfig.type, this.availableAggregates);
175
+ const tsType = isAggRef ? 'number' : this.mapType(fieldConfig.type);
176
+ const effectiveFieldType = isAggRef ? 'number' : fieldConfig.type;
177
+ // Aggregate references are always optional in DTOs; other fields default to required
178
+ const isRequired = !isAggRef && !inputConfig.partial && fieldConfig.required !== false;
179
+ const optional = isRequired ? '' : '?';
180
+ fieldDeclarations.push(` readonly ${fieldName}${optional}: ${tsType};`);
181
+ constructorParams.push(`${fieldName}${optional}: ${tsType}`);
182
+ constructorAssignments.push(` this.${fieldName} = ${fieldName};`);
183
+ validationChecks.push(...this.getValidationCode(fieldName, effectiveFieldType, isRequired));
184
+ fieldTransforms.push(` ${fieldName}: ${this.getTransformCode(fieldName, effectiveFieldType)}`);
185
+ });
186
+ }
187
+ // Handle filters
188
+ if (inputConfig.filters) {
189
+ Object.entries(inputConfig.filters).forEach(([filterName, filterConfig]) => {
190
+ const tsType = this.mapType(filterConfig.type);
191
+ const isRequired = !filterConfig.optional;
192
+ const optional = isRequired ? '' : '?';
193
+ fieldDeclarations.push(` readonly ${filterName}${optional}: ${tsType};`);
194
+ constructorParams.push(`${filterName}${optional}: ${tsType}`);
195
+ constructorAssignments.push(` this.${filterName} = ${filterName};`);
196
+ if (isRequired) {
197
+ validationChecks.push(...this.getValidationCode(filterName, filterConfig.type, true));
198
+ }
199
+ fieldTransforms.push(` ${filterName}: ${this.getTransformCode(filterName, filterConfig.type)}`);
200
+ });
201
+ }
202
+ // Handle sorting
203
+ if (inputConfig.sorting) {
204
+ fieldDeclarations.push(` readonly sortBy?: string;`);
205
+ fieldDeclarations.push(` readonly sortOrder?: 'asc' | 'desc';`);
206
+ constructorParams.push(`sortBy?: string`);
207
+ constructorParams.push(`sortOrder?: 'asc' | 'desc'`);
208
+ constructorAssignments.push(` this.sortBy = sortBy;`);
209
+ constructorAssignments.push(` this.sortOrder = sortOrder;`);
210
+ const allowedFields = inputConfig.sorting.allow.map(f => `'${f}'`).join(', ');
211
+ const defaultField = ((_c = inputConfig.sorting.default) === null || _c === void 0 ? void 0 : _c.field) || inputConfig.sorting.allow[0];
212
+ const defaultOrder = ((_d = inputConfig.sorting.default) === null || _d === void 0 ? void 0 : _d.order) || 'asc';
213
+ fieldTransforms.push(` sortBy: [${allowedFields}].includes(b.sortBy as string) ? b.sortBy as string : '${defaultField}'`);
214
+ fieldTransforms.push(` sortOrder: b.sortOrder === 'desc' ? 'desc' : '${defaultOrder}'`);
215
+ }
216
+ const fieldsStr = fieldDeclarations.length > 0 ? fieldDeclarations.join('\n') : ' // No fields';
217
+ const paramsStr = constructorParams.join(', ');
218
+ const assignmentsStr = constructorAssignments.join('\n');
219
+ const validationsStr = validationChecks.length > 0 ? '\n' + validationChecks.join('\n') + '\n' : '';
220
+ const transformsStr = fieldTransforms.join(',\n');
221
+ return `export class ${className} {
222
+ ${fieldsStr}
223
+
224
+ private constructor(data: { ${constructorParams.map(p => p.replace('readonly ', '')).join('; ')} }) {
225
+ ${constructorAssignments.map(a => a.replace('this.', 'this.').replace(' = ', ' = data.')).join('\n').replace(/= data\./g, '= data.').split('\n').map(line => {
226
+ const match = line.match(/this\.(\w+) = data\.(\w+)/);
227
+ if (match) {
228
+ return ` this.${match[1]} = data.${match[1]};`;
229
+ }
230
+ return line;
231
+ }).join('\n')}
232
+ }
233
+
234
+ static parse(body: unknown): ${className} {
235
+ if (!body || typeof body !== 'object') {
236
+ throw new Error('Invalid request body');
237
+ }
238
+ const b = body as Record<string, unknown>;
239
+ ${validationsStr}
240
+ return new ${className}({
241
+ ${transformsStr}
242
+ });
243
+ }
244
+ }`;
245
+ }
246
+ generateOutputDto(modelName, actionName, outputConfig, aggregateConfig, allAggregates) {
247
+ const className = `${modelName}${(0, typeUtils_1.capitalize)(actionName)}Output`;
248
+ if (outputConfig === 'void') {
249
+ return `export type ${className} = void;`;
250
+ }
251
+ const fieldDeclarations = [];
252
+ const constructorParams = [];
253
+ const fromMappings = [];
254
+ // Always include id for entity outputs
255
+ if (outputConfig.from) {
256
+ fieldDeclarations.push(` readonly id: number;`);
257
+ constructorParams.push(`id: number`);
258
+ fromMappings.push(` id: entity.id`);
259
+ }
260
+ // Handle fields from aggregate (pick)
261
+ if (outputConfig.from) {
262
+ const aggregateFields = Object.entries(aggregateConfig.fields);
263
+ let fieldsToInclude = aggregateFields;
264
+ // Apply pick
265
+ if (outputConfig.pick && outputConfig.pick.length > 0) {
266
+ fieldsToInclude = fieldsToInclude.filter(([fieldName]) => outputConfig.pick.includes(fieldName));
267
+ }
268
+ // Add fields
269
+ fieldsToInclude.forEach(([fieldName, fieldConfig]) => {
270
+ if (fieldName === 'id')
271
+ return;
272
+ const isAggRef = (0, typeUtils_1.isAggregateReference)(fieldConfig.type, this.availableAggregates);
273
+ const tsType = isAggRef ? 'number' : this.mapType(fieldConfig.type);
274
+ const isOptional = fieldConfig.required === false || isAggRef;
275
+ const optional = isOptional ? '?' : '';
276
+ fieldDeclarations.push(` readonly ${fieldName}${optional}: ${tsType};`);
277
+ constructorParams.push(`${fieldName}${optional}: ${tsType}`);
278
+ fromMappings.push(isAggRef
279
+ ? ` ${fieldName}: entity.${fieldName}?.id`
280
+ : ` ${fieldName}: entity.${fieldName}`);
281
+ });
282
+ }
283
+ // Handle includes (nested objects)
284
+ if (outputConfig.include) {
285
+ Object.entries(outputConfig.include).forEach(([includeName, includeConfig]) => {
286
+ const relatedAggregate = allAggregates.get(includeConfig.from);
287
+ if (relatedAggregate && includeConfig.pick && includeConfig.pick.length > 0) {
288
+ // Filter out 'id' from picked fields since we add it separately
289
+ const pickedFieldsFiltered = includeConfig.pick.filter(f => f !== 'id');
290
+ const pickedFields = pickedFieldsFiltered.map(fieldName => {
291
+ const fieldConfig = relatedAggregate.fields[fieldName];
292
+ if (fieldConfig) {
293
+ const tsType = this.mapType(fieldConfig.type);
294
+ return `${fieldName}: ${tsType}`;
295
+ }
296
+ return `${fieldName}: any`;
297
+ }).join('; ');
298
+ const fieldsStr = pickedFields ? `id: number; ${pickedFields}` : 'id: number';
299
+ fieldDeclarations.push(` readonly ${includeName}?: { ${fieldsStr} };`);
300
+ constructorParams.push(`${includeName}?: { ${fieldsStr} }`);
301
+ fromMappings.push(` ${includeName}: (entity as any).${includeName}`);
302
+ }
303
+ else {
304
+ fieldDeclarations.push(` readonly ${includeName}?: ${includeConfig.from};`);
305
+ constructorParams.push(`${includeName}?: ${includeConfig.from}`);
306
+ fromMappings.push(` ${includeName}: (entity as any).${includeName}`);
307
+ }
308
+ });
309
+ }
310
+ // Handle additional fields
311
+ if (outputConfig.add) {
312
+ Object.entries(outputConfig.add).forEach(([fieldName, fieldDef]) => {
313
+ const tsType = this.mapType(fieldDef.type);
314
+ fieldDeclarations.push(` readonly ${fieldName}: ${tsType};`);
315
+ constructorParams.push(`${fieldName}: ${tsType}`);
316
+ fromMappings.push(` ${fieldName}: (entity as any).${fieldName}`);
317
+ });
318
+ }
319
+ const fieldsStr = fieldDeclarations.length > 0 ? fieldDeclarations.join('\n') : ' // No fields';
320
+ const paramsStr = constructorParams.map(p => p.replace('readonly ', '')).join('; ');
321
+ const mappingsStr = fromMappings.join(',\n');
322
+ // Wrap in pagination if needed
323
+ if (outputConfig.pagination) {
324
+ return `export class ${className}Item {
325
+ ${fieldsStr}
326
+
327
+ private constructor(data: { ${paramsStr} }) {
328
+ ${fieldDeclarations.map(d => {
329
+ const match = d.match(/readonly (\w+)/);
330
+ if (match)
331
+ return ` this.${match[1]} = data.${match[1]};`;
332
+ return '';
333
+ }).filter(Boolean).join('\n')}
334
+ }
335
+
336
+ static from(entity: ${modelName}): ${className}Item {
337
+ return new ${className}Item({
338
+ ${mappingsStr}
339
+ });
340
+ }
341
+ }
342
+
343
+ export class ${className} {
344
+ readonly items: ${className}Item[];
345
+ readonly total: number;
346
+ readonly page?: number;
347
+ readonly limit?: number;
348
+
349
+ private constructor(data: { items: ${className}Item[]; total: number; page?: number; limit?: number }) {
350
+ this.items = data.items;
351
+ this.total = data.total;
352
+ this.page = data.page;
353
+ this.limit = data.limit;
354
+ }
355
+
356
+ static from(data: { items: ${modelName}[]; total: number; page?: number; limit?: number }): ${className} {
357
+ return new ${className}({
358
+ items: data.items.map(item => ${className}Item.from(item)),
359
+ total: data.total,
360
+ page: data.page,
361
+ limit: data.limit
362
+ });
363
+ }
364
+ }`;
365
+ }
366
+ // List action without pagination: still wrap items in a list container
367
+ if (actionName === 'list') {
368
+ return `export class ${className}Item {
369
+ ${fieldsStr}
370
+
371
+ private constructor(data: { ${paramsStr} }) {
372
+ ${fieldDeclarations.map(d => {
373
+ const match = d.match(/readonly (\w+)/);
374
+ if (match)
375
+ return ` this.${match[1]} = data.${match[1]};`;
376
+ return '';
377
+ }).filter(Boolean).join('\n')}
378
+ }
379
+
380
+ static from(entity: ${modelName}): ${className}Item {
381
+ return new ${className}Item({
382
+ ${mappingsStr}
383
+ });
384
+ }
385
+ }
386
+
387
+ export class ${className} {
388
+ readonly items: ${className}Item[];
389
+
390
+ private constructor(data: { items: ${className}Item[] }) {
391
+ this.items = data.items;
392
+ }
393
+
394
+ static from(data: { items: ${modelName}[] }): ${className} {
395
+ return new ${className}({
396
+ items: data.items.map(item => ${className}Item.from(item))
397
+ });
398
+ }
399
+ }`;
400
+ }
401
+ return `export class ${className} {
402
+ ${fieldsStr}
403
+
404
+ private constructor(data: { ${paramsStr} }) {
405
+ ${fieldDeclarations.map(d => {
406
+ const match = d.match(/readonly (\w+)/);
407
+ if (match)
408
+ return ` this.${match[1]} = data.${match[1]};`;
409
+ return '';
410
+ }).filter(Boolean).join('\n')}
411
+ }
412
+
413
+ static from(entity: ${modelName}): ${className} {
414
+ return new ${className}({
415
+ ${mappingsStr}
416
+ });
417
+ }
418
+ }`;
419
+ }
420
+ /**
421
+ * Collect types that need to be imported for a use case DTO.
422
+ */
423
+ collectRequiredImports(modelName, aggregateConfig, outputConfig) {
424
+ const valueObjects = new Set();
425
+ const entities = new Set();
426
+ // Check aggregate fields for value object types
427
+ if (outputConfig !== 'void' && outputConfig.from) {
428
+ const aggregateFields = Object.entries(aggregateConfig.fields);
429
+ let fieldsToCheck = aggregateFields;
430
+ // Apply pick filter if specified
431
+ if (outputConfig.pick && outputConfig.pick.length > 0) {
432
+ fieldsToCheck = fieldsToCheck.filter(([fieldName]) => outputConfig.pick.includes(fieldName));
433
+ }
434
+ // Check each field for value object types
435
+ fieldsToCheck.forEach(([, fieldConfig]) => {
436
+ if (this.isValueObjectType(fieldConfig.type)) {
437
+ valueObjects.add((0, typeUtils_1.capitalize)(fieldConfig.type));
438
+ }
439
+ });
440
+ }
441
+ // Check include for child entities
442
+ if (outputConfig !== 'void' && outputConfig.include) {
443
+ Object.entries(outputConfig.include).forEach(([, includeConfig]) => {
444
+ if (includeConfig.from && includeConfig.from !== modelName) {
445
+ entities.add(includeConfig.from);
446
+ }
447
+ });
448
+ }
449
+ return { valueObjects, entities };
450
+ }
451
+ generateFromConfig(config) {
452
+ const result = {};
453
+ // Collect all aggregates
454
+ if (config.domain.aggregates) {
455
+ Object.entries(config.domain.aggregates).forEach(([name, aggConfig]) => {
456
+ this.availableAggregates.set(name, aggConfig);
457
+ });
458
+ }
459
+ // Collect all value objects
460
+ if (config.domain.valueObjects) {
461
+ Object.entries(config.domain.valueObjects).forEach(([name, voConfig]) => {
462
+ this.availableValueObjects.set(name, voConfig);
463
+ });
464
+ }
465
+ const childEntityMap = (0, childEntityUtils_1.buildChildEntityMap)(config);
466
+ // Generate DTOs for each use case
467
+ Object.entries(config.useCases).forEach(([modelName, useCases]) => {
468
+ const aggregateConfig = this.availableAggregates.get(modelName);
469
+ if (!aggregateConfig) {
470
+ console.warn(`Warning: No aggregate found for model ${modelName}`);
471
+ return;
472
+ }
473
+ const childInfo = childEntityMap.get(modelName);
474
+ Object.entries(useCases).forEach(([actionName, useCaseConfig]) => {
475
+ // Generate Input DTO
476
+ const inputDto = this.generateInputDto(modelName, actionName, useCaseConfig.input, aggregateConfig, childInfo);
477
+ // Generate Output DTO
478
+ const outputDto = this.generateOutputDto(modelName, actionName, useCaseConfig.output || 'void', aggregateConfig, this.availableAggregates);
479
+ // Collect required imports
480
+ const imports = [];
481
+ const needsModelImport = useCaseConfig.output !== 'void';
482
+ if (needsModelImport) {
483
+ imports.push(`import { ${modelName} } from '../../domain/entities/${modelName}';`);
484
+ }
485
+ // Collect value objects and entities needed
486
+ const { valueObjects, entities } = this.collectRequiredImports(modelName, aggregateConfig, useCaseConfig.output || 'void');
487
+ // Add value object imports
488
+ valueObjects.forEach(voName => {
489
+ imports.push(`import { ${voName} } from '../../domain/valueObjects/${voName}';`);
490
+ });
491
+ // Add entity imports for includes
492
+ entities.forEach(entityName => {
493
+ imports.push(`import { ${entityName} } from '../../domain/entities/${entityName}';`);
494
+ });
495
+ const importStatement = imports.length > 0 ? imports.join('\n') + '\n\n' : '';
496
+ const dtoFileName = `${modelName}${(0, typeUtils_1.capitalize)(actionName)}`;
497
+ result[dtoFileName] = `${importStatement}${inputDto}\n\n${outputDto}`;
498
+ });
499
+ });
500
+ return result;
501
+ }
502
+ generateFromYamlFile(yamlFilePath) {
503
+ const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
504
+ const config = (0, yaml_1.parse)(yamlContent);
505
+ if (!(0, configTypes_1.isValidModuleConfig)(config)) {
506
+ throw new Error('Configuration does not match new module format. Expected useCases structure.');
507
+ }
508
+ return this.generateFromConfig(config);
509
+ }
510
+ async generateAndSaveFiles(yamlFilePath, moduleDir, opts) {
511
+ const dtosByName = this.generateFromYamlFile(yamlFilePath);
512
+ const dtoDir = path.join(moduleDir, 'application', 'dto');
513
+ fs.mkdirSync(dtoDir, { recursive: true });
514
+ for (const [name, code] of Object.entries(dtosByName)) {
515
+ const filePath = path.join(dtoDir, `${name}.ts`);
516
+ // eslint-disable-next-line no-await-in-loop
517
+ 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) });
518
+ }
519
+ // eslint-disable-next-line no-console
520
+ console.log('\n' + colors_1.colors.green('DTO files generated successfully!') + '\n');
521
+ }
522
+ }
523
+ exports.DtoGenerator = DtoGenerator;
@@ -1,57 +1,28 @@
1
- interface FieldConfig {
2
- name: string;
3
- type: string;
4
- required?: boolean;
5
- unique?: boolean;
6
- auto?: boolean;
7
- displayFields?: string[];
8
- }
9
- interface ActionConfig {
10
- handlers: string[];
11
- }
12
- interface PermissionConfig {
13
- role: string;
14
- actions: string[];
15
- }
16
- interface ModelConfig {
17
- name: string;
18
- fields: FieldConfig[];
19
- }
20
- type ModuleConfig = {
21
- models?: ModelConfig[];
22
- actions?: Record<string, ActionConfig>;
23
- permissions?: PermissionConfig[];
24
- };
1
+ import { ModuleConfig } from '../types/configTypes';
25
2
  export declare class ServiceGenerator {
26
- private availableModels;
27
- private setAvailableModels;
28
- private isRelationshipField;
29
- private getForeignKeyFieldName;
30
- private hasPermissions;
31
- private getActionPermissions;
32
- private generatePermissionCheck;
33
- private getResourceIdForAction;
34
- private generateMethodParams;
35
- private generateReturnType;
36
- private generateMethodImplementation;
37
- private extractFunctionName;
38
- private getMethodCallParams;
39
- private sortFieldsByRequired;
40
- private generateConstructorArgs;
41
- private generateRelationshipLoading;
42
- private generateUpdateSetterCalls;
43
- private replaceTemplateVars;
44
- private getServiceMethodName;
45
- private generateHandlerMethod;
46
- generateServiceForModel(model: ModelConfig, moduleName: string, moduleConfig: ModuleConfig, hasGlobalPermissions: boolean): string;
47
- generateService(moduleName: string, moduleConfig: ModuleConfig, hasGlobalPermissions: boolean): string;
48
- private generateForeignStoreImports;
49
- private generateForeignStoreConstructorParams;
50
- private generateCustomImports;
3
+ private availableAggregates;
4
+ private mapType;
5
+ private getDefaultHandlerReturnType;
6
+ private buildHandlerContextMap;
7
+ private deriveInputType;
8
+ private deriveCustomHandlerTypes;
9
+ private getInputDtoFields;
10
+ private computeDtoFieldsForHandler;
11
+ private generateListHandler;
12
+ private generateGetHandler;
13
+ private generateCreateHandler;
14
+ private generateUpdateHandler;
15
+ private generateDeleteHandler;
16
+ private generateDefaultHandlerMethod;
17
+ private generateCustomHandlerMethod;
18
+ private collectHandlers;
19
+ private generateListByParentMethod;
20
+ private generateGetResourceOwnerMethod;
21
+ private generateService;
22
+ generateFromConfig(config: ModuleConfig): Record<string, string>;
51
23
  generateFromYamlFile(yamlFilePath: string): Record<string, string>;
52
- generateAndSaveFiles(yamlFilePath?: string, outputDir?: string, opts?: {
24
+ generateAndSaveFiles(yamlFilePath: string, moduleDir: string, opts?: {
53
25
  force?: boolean;
54
26
  skipOnConflict?: boolean;
55
27
  }): Promise<void>;
56
28
  }
57
- export {};