@currentjs/gen 0.5.3 → 0.5.5

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.5] - 2026-04-07
4
+
5
+ - data access layer generation fixes and improvements
6
+
7
+ ## [0.5.4] - 2026-04-07
8
+
9
+ - Array and union value objects
10
+
3
11
  ## [0.5.3] - 2026-04-07
4
12
 
5
13
  - expand autowiring (DI) for providers
@@ -42,11 +42,12 @@ const cliUtils_1 = require("../utils/cliUtils");
42
42
  const commandUtils_1 = require("../utils/commandUtils");
43
43
  const configTypes_1 = require("../types/configTypes");
44
44
  const migrationUtils_1 = require("../utils/migrationUtils");
45
- function collectAggregatesFromModules(appYamlPath) {
45
+ function collectSchemaFromModules(appYamlPath) {
46
46
  const appConfig = (0, commandUtils_1.loadAppConfig)(appYamlPath);
47
47
  const moduleEntries = (0, commandUtils_1.getModuleEntries)(appConfig);
48
48
  const projectRoot = path.dirname(appYamlPath);
49
49
  const allAggregates = {};
50
+ const allValueObjects = new Set();
50
51
  const sources = [];
51
52
  for (const entry of moduleEntries) {
52
53
  const moduleYamlPath = path.isAbsolute(entry.path)
@@ -67,13 +68,18 @@ function collectAggregatesFromModules(appYamlPath) {
67
68
  const aggregates = moduleConfig.domain.aggregates;
68
69
  const count = Object.keys(aggregates).length;
69
70
  Object.assign(allAggregates, aggregates);
71
+ if (moduleConfig.domain.valueObjects) {
72
+ for (const voName of Object.keys(moduleConfig.domain.valueObjects)) {
73
+ allValueObjects.add(voName);
74
+ }
75
+ }
70
76
  sources.push(`${entry.name} (${count} aggregate(s))`);
71
77
  }
72
78
  if (sources.length > 0) {
73
79
  // eslint-disable-next-line no-console
74
80
  console.log(colors_1.colors.gray(` Sources: ${sources.join(', ')}`));
75
81
  }
76
- return allAggregates;
82
+ return { aggregates: allAggregates, valueObjects: allValueObjects };
77
83
  }
78
84
  function handleMigrateCommit(yamlPath) {
79
85
  try {
@@ -94,7 +100,7 @@ function handleMigrateCommit(yamlPath) {
94
100
  }
95
101
  // eslint-disable-next-line no-console
96
102
  console.log(colors_1.colors.cyan('\nšŸ“‹ Collecting aggregates from all modules...'));
97
- const currentAggregates = collectAggregatesFromModules(resolvedYamlPath);
103
+ const { aggregates: currentAggregates, valueObjects: currentValueObjects } = collectSchemaFromModules(resolvedYamlPath);
98
104
  if (Object.keys(currentAggregates).length === 0) {
99
105
  // eslint-disable-next-line no-console
100
106
  console.log(colors_1.colors.yellow('āš ļø No aggregates found in module configuration.'));
@@ -113,7 +119,7 @@ function handleMigrateCommit(yamlPath) {
113
119
  }
114
120
  // eslint-disable-next-line no-console
115
121
  console.log(colors_1.colors.cyan('\nšŸ” Comparing schemas...'));
116
- const sqlStatements = (0, migrationUtils_1.compareSchemas)(oldState, currentAggregates);
122
+ const sqlStatements = (0, migrationUtils_1.compareSchemas)(oldState, currentAggregates, currentValueObjects);
117
123
  if (sqlStatements.length === 0 || sqlStatements.every(s => s.trim() === '' || s.startsWith('--'))) {
118
124
  // eslint-disable-next-line no-console
119
125
  console.log(colors_1.colors.yellow('āš ļø No changes detected. Schema is up to date.'));
@@ -132,7 +138,7 @@ function handleMigrateCommit(yamlPath) {
132
138
  const newState = {
133
139
  aggregates: currentAggregates,
134
140
  version: timestamp,
135
- timestamp: new Date().toISOString()
141
+ timestamp: new Date().toISOString(),
136
142
  };
137
143
  (0, migrationUtils_1.saveSchemaState)(stateFilePath, newState);
138
144
  // eslint-disable-next-line no-console
@@ -137,14 +137,13 @@ class DomainLayerGenerator {
137
137
  .filter(entityName => entityName !== name && allAggregates[entityName])
138
138
  .map(entityName => `import { ${entityName} } from './${entityName}';`)
139
139
  .join('\n');
140
- // Generate imports for value objects used in fields
141
- const valueObjectImports = fields
142
- .filter(([, fieldConfig]) => this.availableValueObjects.has((0, typeUtils_1.capitalize)(fieldConfig.type)))
143
- .map(([, fieldConfig]) => {
144
- const voName = (0, typeUtils_1.capitalize)(fieldConfig.type);
145
- return `import { ${voName} } from '../valueObjects/${voName}';`;
146
- })
147
- .filter((imp, idx, arr) => arr.indexOf(imp) === idx) // dedupe
140
+ // Generate imports for value objects used in fields (including compound types like Foo[] and Foo | Bar)
141
+ const referencedVoNames = new Set();
142
+ fields.forEach(([, fieldConfig]) => {
143
+ (0, typeUtils_1.getReferencedValueObjects)(fieldConfig.type, this.availableValueObjects).forEach(name => referencedVoNames.add(name));
144
+ });
145
+ const valueObjectImports = [...referencedVoNames]
146
+ .map(voName => `import { ${voName} } from '../valueObjects/${voName}';`)
148
147
  .join('\n');
149
148
  // Generate imports for aggregate references in fields (e.g. idea: { type: Idea })
150
149
  const aggregateRefImports = fields
@@ -51,8 +51,7 @@ class DtoGenerator {
51
51
  return (0, typeUtils_1.mapType)(yamlType, this.availableAggregates, this.availableValueObjects);
52
52
  }
53
53
  isValueObjectType(yamlType) {
54
- const capitalizedType = (0, typeUtils_1.capitalize)(yamlType);
55
- return this.availableValueObjects.has(capitalizedType);
54
+ return (0, typeUtils_1.isValueObjectFieldType)(yamlType, this.availableValueObjects);
56
55
  }
57
56
  getValidationCode(fieldName, fieldType, isRequired) {
58
57
  const checks = [];
@@ -431,11 +430,11 @@ ${mappingsStr}
431
430
  if (outputConfig.pick && outputConfig.pick.length > 0) {
432
431
  fieldsToCheck = fieldsToCheck.filter(([fieldName]) => outputConfig.pick.includes(fieldName));
433
432
  }
434
- // Check each field for value object types
433
+ // Check each field for value object types (including compound types like Foo[] and Foo | Bar)
435
434
  fieldsToCheck.forEach(([, fieldConfig]) => {
436
- if (this.isValueObjectType(fieldConfig.type)) {
437
- valueObjects.add((0, typeUtils_1.capitalize)(fieldConfig.type));
438
- }
435
+ (0, typeUtils_1.getReferencedValueObjects)(fieldConfig.type, this.availableValueObjects).forEach(voName => {
436
+ valueObjects.add(voName);
437
+ });
439
438
  });
440
439
  }
441
440
  // Check include for child entities
@@ -4,6 +4,8 @@ export declare class StoreGenerator {
4
4
  private availableAggregates;
5
5
  private isAggregateField;
6
6
  private isValueObjectType;
7
+ private isArrayVoType;
8
+ private isUnionVoType;
7
9
  private getValueObjectName;
8
10
  private getValueObjectConfig;
9
11
  /**
@@ -21,8 +23,20 @@ export declare class StoreGenerator {
21
23
  private mapTypeToRowType;
22
24
  /** Single line for serializing a value object field to DB (insert/update). */
23
25
  private generateValueObjectSerialization;
26
+ /** Serialization for an array-of-VOs field. */
27
+ private generateArrayVoSerialization;
28
+ /** Serialization for a union-of-VOs field. Uses _type discriminator. */
29
+ private generateUnionVoSerialization;
24
30
  /** Single line for deserializing a value object from row (rowToModel). */
25
31
  private generateValueObjectDeserialization;
32
+ /** Deserialization for an array-of-VOs field. */
33
+ private generateArrayVoDeserialization;
34
+ /** Deserialization for a union-of-VOs field. Uses _type discriminator. */
35
+ private generateUnionVoDeserialization;
36
+ /** Serialization for an array-of-union-VOs field. Each element tagged with _type discriminator. */
37
+ private generateArrayUnionVoSerialization;
38
+ /** Deserialization for an array-of-union-VOs field. Each element reconstructed via _type. */
39
+ private generateArrayUnionVoDeserialization;
26
40
  /** Single line for datetime conversion: toDate (row->model) or toMySQL (entity->row). */
27
41
  private generateDatetimeConversion;
28
42
  private replaceTemplateVars;
@@ -52,8 +52,16 @@ class StoreGenerator {
52
52
  return (0, typeUtils_1.isAggregateReference)(fieldConfig.type, this.availableAggregates);
53
53
  }
54
54
  isValueObjectType(fieldType) {
55
- const capitalizedType = (0, typeUtils_1.capitalize)(fieldType);
56
- return this.availableValueObjects.has(capitalizedType);
55
+ const { baseTypes } = (0, typeUtils_1.parseFieldType)(fieldType);
56
+ return baseTypes.some(bt => this.availableValueObjects.has((0, typeUtils_1.capitalize)(bt)));
57
+ }
58
+ isArrayVoType(fieldType) {
59
+ const parsed = (0, typeUtils_1.parseFieldType)(fieldType);
60
+ return parsed.isArray && parsed.baseTypes.some(bt => this.availableValueObjects.has((0, typeUtils_1.capitalize)(bt)));
61
+ }
62
+ isUnionVoType(fieldType) {
63
+ const parsed = (0, typeUtils_1.parseFieldType)(fieldType);
64
+ return parsed.isUnion && parsed.baseTypes.some(bt => this.availableValueObjects.has((0, typeUtils_1.capitalize)(bt)));
57
65
  }
58
66
  getValueObjectName(fieldType) {
59
67
  return (0, typeUtils_1.capitalize)(fieldType);
@@ -105,12 +113,24 @@ class StoreGenerator {
105
113
  }
106
114
  return ` ${fieldName}: entity.${fieldName} ? JSON.stringify(entity.${fieldName}) : undefined`;
107
115
  }
116
+ /** Serialization for an array-of-VOs field. */
117
+ generateArrayVoSerialization(fieldName) {
118
+ return ` ${fieldName}: entity.${fieldName} ? JSON.stringify(entity.${fieldName}) : undefined`;
119
+ }
120
+ /** Serialization for a union-of-VOs field. Uses _type discriminator. */
121
+ generateUnionVoSerialization(fieldName, unionVoNames) {
122
+ const checks = unionVoNames
123
+ .map(voName => `entity.${fieldName} instanceof ${voName} ? '${voName}'`)
124
+ .join(' : ');
125
+ const discriminator = `${checks} : 'unknown'`;
126
+ return ` ${fieldName}: entity.${fieldName} ? JSON.stringify({ _type: ${discriminator}, ...entity.${fieldName} }) : undefined`;
127
+ }
108
128
  /** Single line for deserializing a value object from row (rowToModel). */
109
129
  generateValueObjectDeserialization(fieldName, voName, voConfig) {
110
130
  const voFields = Object.keys(voConfig.fields);
111
131
  if (voFields.length > 1) {
112
132
  const voArgs = voFields.map(f => `parsed.${f}`).join(', ');
113
- return ` row.${fieldName} ? (() => { const parsed = JSON.parse(row.${fieldName}); return new ${voName}(${voArgs}); })() : undefined`;
133
+ return ` row.${fieldName} ? (() => { const parsed = this.ensureParsed(row.${fieldName}); return new ${voName}(${voArgs}); })() : undefined`;
114
134
  }
115
135
  if (voFields.length === 1) {
116
136
  const singleFieldType = voConfig.fields[voFields[0]];
@@ -121,7 +141,47 @@ class StoreGenerator {
121
141
  }
122
142
  return ` row.${fieldName} ? new ${voName}(row.${fieldName}) : undefined`;
123
143
  }
124
- return ` row.${fieldName} ? new ${voName}(...Object.values(JSON.parse(row.${fieldName}))) : undefined`;
144
+ return ` row.${fieldName} ? new ${voName}(...Object.values(this.ensureParsed(row.${fieldName}))) : undefined`;
145
+ }
146
+ /** Deserialization for an array-of-VOs field. */
147
+ generateArrayVoDeserialization(fieldName, voName, voConfig) {
148
+ const voFields = Object.keys(voConfig.fields);
149
+ const itemArgs = voFields.length > 0
150
+ ? voFields.map(f => `item.${f}`).join(', ')
151
+ : '...Object.values(item)';
152
+ return ` row.${fieldName} ? (this.ensureParsed(row.${fieldName}) as any[]).map((item: any) => new ${voName}(${itemArgs})) : []`;
153
+ }
154
+ /** Deserialization for a union-of-VOs field. Uses _type discriminator. */
155
+ generateUnionVoDeserialization(fieldName, unionVoNames, unionVoConfigs) {
156
+ const cases = unionVoNames.map(voName => {
157
+ const cfg = unionVoConfigs[voName];
158
+ const voFields = cfg ? Object.keys(cfg.fields) : [];
159
+ const args = voFields.length > 0
160
+ ? voFields.map(f => `parsed.${f}`).join(', ')
161
+ : '...Object.values(parsed)';
162
+ return `if (parsed._type === '${voName}') return new ${voName}(${args});`;
163
+ }).join(' ');
164
+ return ` row.${fieldName} ? (() => { const parsed = this.ensureParsed(row.${fieldName}); ${cases} return undefined; })() : undefined`;
165
+ }
166
+ /** Serialization for an array-of-union-VOs field. Each element tagged with _type discriminator. */
167
+ generateArrayUnionVoSerialization(fieldName, unionVoNames) {
168
+ const checks = unionVoNames
169
+ .map(voName => `item instanceof ${voName} ? '${voName}'`)
170
+ .join(' : ');
171
+ const discriminator = `${checks} : 'unknown'`;
172
+ return ` ${fieldName}: entity.${fieldName} ? JSON.stringify(entity.${fieldName}.map((item: any) => ({ _type: ${discriminator}, ...item }))) : undefined`;
173
+ }
174
+ /** Deserialization for an array-of-union-VOs field. Each element reconstructed via _type. */
175
+ generateArrayUnionVoDeserialization(fieldName, unionVoNames, unionVoConfigs) {
176
+ const cases = unionVoNames.map(voName => {
177
+ const cfg = unionVoConfigs[voName];
178
+ const voFields = cfg ? Object.keys(cfg.fields) : [];
179
+ const args = voFields.length > 0
180
+ ? voFields.map(f => `item.${f}`).join(', ')
181
+ : '...Object.values(item)';
182
+ return `if (item._type === '${voName}') return new ${voName}(${args});`;
183
+ }).join(' ');
184
+ return ` row.${fieldName} ? (this.ensureParsed(row.${fieldName}) as any[]).map((item: any) => { ${cases} return undefined; }) : []`;
125
185
  }
126
186
  /** Single line for datetime conversion: toDate (row->model) or toMySQL (entity->row). */
127
187
  generateDatetimeConversion(fieldName, direction) {
@@ -144,7 +204,7 @@ class StoreGenerator {
144
204
  result.push(` ${ownerOrParentField}: number;`);
145
205
  fields.forEach(([fieldName, fieldConfig]) => {
146
206
  if (this.isAggregateField(fieldConfig)) {
147
- result.push(` ${fieldName}_id?: number;`);
207
+ result.push(` ${fieldName}Id?: number;`);
148
208
  return;
149
209
  }
150
210
  const tsType = this.mapTypeToRowType(fieldConfig.type);
@@ -156,7 +216,7 @@ class StoreGenerator {
156
216
  generateFieldNamesStr(fields, childInfo) {
157
217
  const fieldNames = ['id'];
158
218
  fieldNames.push(childInfo ? childInfo.parentIdField : 'ownerId');
159
- fieldNames.push(...fields.map(([name, config]) => this.isAggregateField(config) ? `${name}_id` : name));
219
+ fieldNames.push(...fields.map(([name, config]) => this.isAggregateField(config) ? `${name}Id` : name));
160
220
  return fieldNames.map(f => `\\\`${f}\\\``).join(', ');
161
221
  }
162
222
  generateRowToModelMapping(modelName, fields, childInfo) {
@@ -173,7 +233,7 @@ class StoreGenerator {
173
233
  }
174
234
  // Handle aggregate reference - create stub from FK
175
235
  if (this.isAggregateField(fieldConfig)) {
176
- result.push(` row.${fieldName}_id != null ? ({ id: row.${fieldName}_id } as unknown as ${fieldType}) : undefined`);
236
+ result.push(` row.${fieldName}Id != null ? ({ id: row.${fieldName}Id } as unknown as ${fieldType}) : undefined`);
177
237
  return;
178
238
  }
179
239
  // Handle datetime/date conversion
@@ -186,15 +246,44 @@ class StoreGenerator {
186
246
  result.push(` Boolean(row.${fieldName})`);
187
247
  return;
188
248
  }
189
- // Handle value object conversion - deserialize from JSON
249
+ // Handle value object conversion - deserialize from JSON (simple, array, union, or array-of-union)
190
250
  if (this.isValueObjectType(fieldType)) {
251
+ const parsed = (0, typeUtils_1.parseFieldType)(fieldType);
252
+ if (parsed.isArray && parsed.isUnion) {
253
+ const unionVoNames = parsed.baseTypes.map(bt => (0, typeUtils_1.capitalize)(bt)).filter(name => this.availableValueObjects.has(name));
254
+ const unionVoConfigs = {};
255
+ unionVoNames.forEach(name => {
256
+ const cfg = this.availableValueObjects.get(name);
257
+ if (cfg)
258
+ unionVoConfigs[name] = cfg;
259
+ });
260
+ result.push(this.generateArrayUnionVoDeserialization(fieldName, unionVoNames, unionVoConfigs));
261
+ return;
262
+ }
263
+ if (parsed.isArray) {
264
+ const voName = (0, typeUtils_1.capitalize)(parsed.baseTypes[0]);
265
+ const voConfig = this.availableValueObjects.get(voName);
266
+ result.push(this.generateArrayVoDeserialization(fieldName, voName, voConfig || { fields: {} }));
267
+ return;
268
+ }
269
+ if (parsed.isUnion) {
270
+ const unionVoNames = parsed.baseTypes.map(bt => (0, typeUtils_1.capitalize)(bt)).filter(name => this.availableValueObjects.has(name));
271
+ const unionVoConfigs = {};
272
+ unionVoNames.forEach(name => {
273
+ const cfg = this.availableValueObjects.get(name);
274
+ if (cfg)
275
+ unionVoConfigs[name] = cfg;
276
+ });
277
+ result.push(this.generateUnionVoDeserialization(fieldName, unionVoNames, unionVoConfigs));
278
+ return;
279
+ }
191
280
  const voName = this.getValueObjectName(fieldType);
192
281
  const voConfig = this.getValueObjectConfig(fieldType);
193
282
  if (voConfig) {
194
283
  result.push(this.generateValueObjectDeserialization(fieldName, voName, voConfig));
195
284
  }
196
285
  else {
197
- result.push(` row.${fieldName} ? new ${voName}(...Object.values(JSON.parse(row.${fieldName}))) : undefined`);
286
+ result.push(` row.${fieldName} ? new ${voName}(...Object.values(this.ensureParsed(row.${fieldName}))) : undefined`);
198
287
  }
199
288
  return;
200
289
  }
@@ -210,7 +299,7 @@ class StoreGenerator {
210
299
  const fieldType = fieldConfig.type;
211
300
  // Handle aggregate reference - extract FK id
212
301
  if (this.isAggregateField(fieldConfig)) {
213
- result.push(` ${fieldName}_id: entity.${fieldName}?.id`);
302
+ result.push(` ${fieldName}Id: entity.${fieldName}?.id`);
214
303
  return;
215
304
  }
216
305
  // Handle datetime/date - convert Date to MySQL DATETIME format
@@ -218,8 +307,23 @@ class StoreGenerator {
218
307
  result.push(this.generateDatetimeConversion(fieldName, 'toMySQL'));
219
308
  return;
220
309
  }
221
- // Handle value object - serialize to JSON
310
+ // Handle value object - serialize to JSON (simple, array, union, or array-of-union)
222
311
  if (this.isValueObjectType(fieldType)) {
312
+ const parsed = (0, typeUtils_1.parseFieldType)(fieldType);
313
+ if (parsed.isArray && parsed.isUnion) {
314
+ const unionVoNames = parsed.baseTypes.map(bt => (0, typeUtils_1.capitalize)(bt)).filter(name => this.availableValueObjects.has(name));
315
+ result.push(this.generateArrayUnionVoSerialization(fieldName, unionVoNames));
316
+ return;
317
+ }
318
+ if (parsed.isArray) {
319
+ result.push(this.generateArrayVoSerialization(fieldName));
320
+ return;
321
+ }
322
+ if (parsed.isUnion) {
323
+ const unionVoNames = parsed.baseTypes.map(bt => (0, typeUtils_1.capitalize)(bt)).filter(name => this.availableValueObjects.has(name));
324
+ result.push(this.generateUnionVoSerialization(fieldName, unionVoNames));
325
+ return;
326
+ }
223
327
  const voConfig = this.getValueObjectConfig(fieldType);
224
328
  if (voConfig) {
225
329
  result.push(this.generateValueObjectSerialization(fieldName, this.getValueObjectName(fieldType), voConfig));
@@ -238,12 +342,24 @@ class StoreGenerator {
238
342
  .map(([fieldName, fieldConfig]) => {
239
343
  const fieldType = fieldConfig.type;
240
344
  if (this.isAggregateField(fieldConfig)) {
241
- return ` ${fieldName}_id: entity.${fieldName}?.id`;
345
+ return ` ${fieldName}Id: entity.${fieldName}?.id`;
242
346
  }
243
347
  if (fieldType === 'datetime' || fieldType === 'date') {
244
348
  return this.generateDatetimeConversion(fieldName, 'toMySQL');
245
349
  }
246
350
  if (this.isValueObjectType(fieldType)) {
351
+ const parsed = (0, typeUtils_1.parseFieldType)(fieldType);
352
+ if (parsed.isArray && parsed.isUnion) {
353
+ const unionVoNames = parsed.baseTypes.map(bt => (0, typeUtils_1.capitalize)(bt)).filter(name => this.availableValueObjects.has(name));
354
+ return this.generateArrayUnionVoSerialization(fieldName, unionVoNames);
355
+ }
356
+ if (parsed.isArray) {
357
+ return this.generateArrayVoSerialization(fieldName);
358
+ }
359
+ if (parsed.isUnion) {
360
+ const unionVoNames = parsed.baseTypes.map(bt => (0, typeUtils_1.capitalize)(bt)).filter(name => this.availableValueObjects.has(name));
361
+ return this.generateUnionVoSerialization(fieldName, unionVoNames);
362
+ }
247
363
  const voConfig = this.getValueObjectConfig(fieldType);
248
364
  if (voConfig) {
249
365
  return this.generateValueObjectSerialization(fieldName, this.getValueObjectName(fieldType), voConfig);
@@ -255,31 +371,30 @@ class StoreGenerator {
255
371
  .join(',\n');
256
372
  }
257
373
  generateUpdateFieldsArray(fields) {
258
- return JSON.stringify(fields.map(([name, config]) => this.isAggregateField(config) ? `${name}_id` : name));
374
+ return JSON.stringify(fields.map(([name, config]) => this.isAggregateField(config) ? `${name}Id` : name));
259
375
  }
260
376
  generateValueObjectImports(fields) {
261
377
  const imports = [];
262
378
  fields.forEach(([, fieldConfig]) => {
263
- if (this.isValueObjectType(fieldConfig.type)) {
264
- const voName = this.getValueObjectName(fieldConfig.type);
265
- const voConfig = this.getValueObjectConfig(fieldConfig.type);
266
- // Import the class
379
+ if (!this.isValueObjectType(fieldConfig.type))
380
+ return;
381
+ // Collect all VO names referenced in this field type (handles Foo[], Foo | Bar)
382
+ const referencedVoNames = (0, typeUtils_1.getReferencedValueObjects)(fieldConfig.type, this.availableValueObjects);
383
+ referencedVoNames.forEach(voName => {
384
+ const voConfig = this.availableValueObjects.get(voName);
267
385
  const importItems = [voName];
268
- // Also import the type if it's an enum
269
- if (voConfig) {
386
+ // Also import enum type alias if present (only for simple single-VO fields)
387
+ if (voConfig && !(0, typeUtils_1.parseFieldType)(fieldConfig.type).isArray && !(0, typeUtils_1.parseFieldType)(fieldConfig.type).isUnion) {
270
388
  const enumTypeName = this.getValueObjectFieldTypeName(voName, voConfig);
271
- if (enumTypeName) {
389
+ if (enumTypeName)
272
390
  importItems.push(enumTypeName);
273
- }
274
391
  }
275
392
  imports.push(`import { ${importItems.join(', ')} } from '../../domain/valueObjects/${voName}';`);
276
- }
393
+ });
277
394
  });
278
- // Dedupe imports
279
395
  const uniqueImports = [...new Set(imports)];
280
- if (uniqueImports.length === 0) {
396
+ if (uniqueImports.length === 0)
281
397
  return '';
282
- }
283
398
  return '\n' + uniqueImports.join('\n');
284
399
  }
285
400
  generateAggregateRefImports(modelName, fields) {
@@ -308,7 +423,7 @@ class StoreGenerator {
308
423
  const offset = (page - 1) * limit;${ownerFilter}
309
424
  const params: Record<string, any> = { limit: String(limit), offset: String(offset) };${ownerParamsSetup}
310
425
  const result = await this.db.query(
311
- \`SELECT ${fieldNamesStr} FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL${ownerFilterRef} LIMIT :limit OFFSET :offset\`,
426
+ \`SELECT ${fieldNamesStr} FROM \\\`\${this.tableName}\\\` WHERE deletedAt IS NULL${ownerFilterRef} LIMIT :limit OFFSET :offset\`,
312
427
  params
313
428
  );
314
429
 
@@ -320,7 +435,7 @@ class StoreGenerator {
320
435
  const getAll = ` async getAll(${isRoot ? 'ownerId?: number' : ''}): Promise<${modelName}[]> {${ownerFilter}
321
436
  const params: Record<string, any> = {};${ownerParamsSetup}
322
437
  const result = await this.db.query(
323
- \`SELECT ${fieldNamesStr} FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL${ownerFilterRef}\`,
438
+ \`SELECT ${fieldNamesStr} FROM \\\`\${this.tableName}\\\` WHERE deletedAt IS NULL${ownerFilterRef}\`,
324
439
  params
325
440
  );
326
441
 
@@ -332,7 +447,7 @@ class StoreGenerator {
332
447
  const count = ` async count(${isRoot ? 'ownerId?: number' : ''}): Promise<number> {${ownerFilter}
333
448
  const params: Record<string, any> = {};${ownerParamsSetup}
334
449
  const result = await this.db.query(
335
- \`SELECT COUNT(*) as count FROM \\\`\${this.tableName}\\\` WHERE deleted_at IS NULL${ownerFilterRef}\`,
450
+ \`SELECT COUNT(*) as count FROM \\\`\${this.tableName}\\\` WHERE deletedAt IS NULL${ownerFilterRef}\`,
336
451
  params
337
452
  );
338
453
 
@@ -346,13 +461,13 @@ class StoreGenerator {
346
461
  generateGetByParentIdMethod(modelName, fields, childInfo) {
347
462
  if (!childInfo)
348
463
  return '';
349
- const fieldList = ['id', childInfo.parentIdField, ...fields.map(([name, config]) => this.isAggregateField(config) ? `${name}_id` : name)].map(f => '\\`' + f + '\\`').join(', ');
464
+ const fieldList = ['id', childInfo.parentIdField, ...fields.map(([name, config]) => this.isAggregateField(config) ? `${name}Id` : name)].map(f => '\\`' + f + '\\`').join(', ');
350
465
  const parentIdField = childInfo.parentIdField;
351
466
  return `
352
467
 
353
468
  async getByParentId(parentId: number): Promise<${modelName}[]> {
354
469
  const result = await this.db.query(
355
- \`SELECT ${fieldList} FROM \\\`\${this.tableName}\\\` WHERE \\\`${parentIdField}\\\` = :parentId AND deleted_at IS NULL\`,
470
+ \`SELECT ${fieldList} FROM \\\`\${this.tableName}\\\` WHERE \\\`${parentIdField}\\\` = :parentId AND deletedAt IS NULL\`,
356
471
  { parentId }
357
472
  );
358
473
 
@@ -374,7 +489,7 @@ class StoreGenerator {
374
489
  */
375
490
  async getResourceOwner(id: number): Promise<number | null> {
376
491
  const result = await this.db.query(
377
- \`SELECT p.ownerId FROM \\\`\${this.tableName}\\\` c INNER JOIN \\\`${parentTable}\\\` p ON p.id = c.\\\`${parentIdField}\\\` WHERE c.id = :id AND c.deleted_at IS NULL\`,
492
+ \`SELECT p.ownerId FROM \\\`\${this.tableName}\\\` c INNER JOIN \\\`${parentTable}\\\` p ON p.id = c.\\\`${parentIdField}\\\` WHERE c.id = :id AND c.deletedAt IS NULL\`,
378
493
  { id }
379
494
  );
380
495
 
@@ -392,7 +507,7 @@ class StoreGenerator {
392
507
  */
393
508
  async getResourceOwner(id: number): Promise<number | null> {
394
509
  const result = await this.db.query(
395
- \`SELECT ownerId FROM \\\`\${this.tableName}\\\` WHERE id = :id AND deleted_at IS NULL\`,
510
+ \`SELECT ownerId FROM \\\`\${this.tableName}\\\` WHERE id = :id AND deletedAt IS NULL\`,
396
511
  { id }
397
512
  );
398
513
 
@@ -19,6 +19,21 @@ export declare class TemplateGenerator {
19
19
  private renderEditTemplate;
20
20
  private getInputType;
21
21
  private renderValueObjectField;
22
+ /**
23
+ * Render an array-of-VOs field as checkboxes.
24
+ * If the VO has a single enum field: one checkbox per enum value.
25
+ * If the VO has multiple / non-enum fields: one labeled checkbox group per VO subfield.
26
+ */
27
+ private renderArrayVoField;
28
+ /**
29
+ * Render a union-of-VOs field as a type selector with sub-fields for each VO type.
30
+ */
31
+ private renderUnionVoField;
32
+ /**
33
+ * Render an array-of-union-VOs field as a repeatable group where each item
34
+ * has a type selector and conditionally-shown sub-fields per VO type.
35
+ */
36
+ private renderArrayUnionVoField;
22
37
  private renderFormField;
23
38
  private getEnumValuesMap;
24
39
  generateFromConfig(config: ModuleConfig): Record<string, string>;