@currentjs/gen 0.5.4 → 0.5.6

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.
@@ -3,7 +3,22 @@
3
3
  * Type definitions for the Clean Architecture module configuration
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.normalizeIdentifierType = normalizeIdentifierType;
7
+ exports.idTsType = idTsType;
6
8
  exports.isValidModuleConfig = isValidModuleConfig;
9
+ function normalizeIdentifierType(value) {
10
+ const lower = value.toLowerCase();
11
+ if (lower === 'id' || lower === 'numeric')
12
+ return 'numeric';
13
+ if (lower === 'uuid')
14
+ return 'uuid';
15
+ if (lower === 'nanoid')
16
+ return 'nanoid';
17
+ throw new Error(`Unknown identifier type: "${value}". Expected: numeric, uuid, or nanoid`);
18
+ }
19
+ function idTsType(identifiers) {
20
+ return identifiers === 'numeric' ? 'number' : 'string';
21
+ }
7
22
  // Type guard to validate module config (domain + useCases)
8
23
  function isValidModuleConfig(config) {
9
24
  return config && typeof config === 'object' && 'domain' in config && 'useCases' in config;
@@ -28,7 +28,7 @@ export interface ModuleEntryResolved {
28
28
  path: string;
29
29
  database: string;
30
30
  styling: string;
31
- identifiers: string;
31
+ identifiers: import('../types/configTypes').IdentifierType;
32
32
  }
33
33
  export declare function getModuleEntries(config: AppConfig): ModuleEntryResolved[];
34
34
  export declare function shouldIncludeModule(moduleYamlRel: string, moduleName?: string): boolean;
@@ -51,9 +51,10 @@ const serviceGenerator_1 = require("../generators/serviceGenerator");
51
51
  const controllerGenerator_1 = require("../generators/controllerGenerator");
52
52
  const storeGenerator_1 = require("../generators/storeGenerator");
53
53
  const templateGenerator_1 = require("../generators/templateGenerator");
54
+ const configTypes_1 = require("../types/configTypes");
54
55
  const DEFAULT_DATABASE = 'mysql';
55
56
  const DEFAULT_STYLING = 'bootstrap';
56
- const DEFAULT_IDENTIFIERS = 'id';
57
+ const DEFAULT_IDENTIFIERS = 'numeric';
57
58
  function loadAppConfig(yamlPath) {
58
59
  const raw = fs.readFileSync(yamlPath, 'utf8');
59
60
  const parsed = (0, yaml_1.parse)(raw);
@@ -81,7 +82,8 @@ function getModuleEntries(config) {
81
82
  const global = config.config || {};
82
83
  const database = (_a = global.database) !== null && _a !== void 0 ? _a : DEFAULT_DATABASE;
83
84
  const styling = (_b = global.styling) !== null && _b !== void 0 ? _b : DEFAULT_STYLING;
84
- const identifiers = (_c = global.identifiers) !== null && _c !== void 0 ? _c : DEFAULT_IDENTIFIERS;
85
+ const rawIdentifiers = (_c = global.identifiers) !== null && _c !== void 0 ? _c : DEFAULT_IDENTIFIERS;
86
+ const identifiers = (0, configTypes_1.normalizeIdentifierType)(rawIdentifiers);
85
87
  const globalConfig = { database, styling, identifiers };
86
88
  return Object.entries(config.modules).map(([name, entry]) => {
87
89
  var _a, _b, _c;
@@ -90,7 +92,7 @@ function getModuleEntries(config) {
90
92
  path: entry.path,
91
93
  database: (_a = entry.database) !== null && _a !== void 0 ? _a : globalConfig.database,
92
94
  styling: (_b = entry.styling) !== null && _b !== void 0 ? _b : globalConfig.styling,
93
- identifiers: (_c = entry.identifiers) !== null && _c !== void 0 ? _c : globalConfig.identifiers
95
+ identifiers: (0, configTypes_1.normalizeIdentifierType)((_c = entry.identifiers) !== null && _c !== void 0 ? _c : globalConfig.identifiers)
94
96
  });
95
97
  });
96
98
  }
@@ -1,4 +1,4 @@
1
- import { AggregateConfig, AggregateFieldConfig } from '../types/configTypes';
1
+ import { AggregateConfig, AggregateFieldConfig, IdentifierType } from '../types/configTypes';
2
2
  export interface SchemaState {
3
3
  aggregates: Record<string, AggregateConfig>;
4
4
  version: string;
@@ -21,19 +21,27 @@ export interface ForeignKeyInfo {
21
21
  REFERENCED_TABLE_NAME: string;
22
22
  REFERENCED_COLUMN_NAME: string;
23
23
  }
24
- export declare function mapYamlTypeToSql(yamlType: string, availableAggregates: Set<string>, availableValueObjects?: Set<string>): string;
24
+ export declare function getIdColumnDefinition(idType?: IdentifierType): string;
25
+ export declare function getFkColumnType(idType?: IdentifierType): string;
26
+ export declare function mapYamlTypeToSql(yamlType: string, availableAggregates: Set<string>, availableValueObjects?: Set<string>, identifiers?: IdentifierType): string;
27
+ /** Table name matches the store convention: singular lowercase aggregate name. */
25
28
  export declare function getTableName(aggregateName: string): string;
26
29
  export declare function getForeignKeyFieldName(fieldName: string): string;
27
30
  export declare function isRelationshipField(fieldType: string, availableAggregates: Set<string>): boolean;
28
- export declare function generateCreateTableSQL(name: string, aggregate: AggregateConfig, availableAggregates: Set<string>): string;
31
+ /**
32
+ * Build a map of child entity name → parent entity name from the aggregates config.
33
+ * Used to determine parent ID column names for child entity tables.
34
+ */
35
+ export declare function buildChildToParentMap(aggregates: Record<string, AggregateConfig>): Map<string, string>;
36
+ export declare function generateCreateTableSQL(name: string, aggregate: AggregateConfig, availableAggregates: Set<string>, availableValueObjects?: Set<string>, parentIdField?: string, identifiers?: IdentifierType): string;
29
37
  export declare function generateDropTableSQL(tableName: string): string;
30
- export declare function generateAddColumnSQL(tableName: string, fieldName: string, field: AggregateFieldConfig, availableAggregates: Set<string>): string;
38
+ export declare function generateAddColumnSQL(tableName: string, fieldName: string, field: AggregateFieldConfig, availableAggregates: Set<string>, availableValueObjects?: Set<string>, identifiers?: IdentifierType): string;
31
39
  export declare function generateDropColumnSQL(tableName: string, columnName: string): string;
32
- export declare function generateModifyColumnSQL(tableName: string, fieldName: string, field: AggregateFieldConfig, availableAggregates: Set<string>): string;
40
+ export declare function generateModifyColumnSQL(tableName: string, fieldName: string, field: AggregateFieldConfig, availableAggregates: Set<string>, availableValueObjects?: Set<string>, identifiers?: IdentifierType): string;
33
41
  export declare function loadSchemaState(stateFilePath: string): SchemaState | null;
34
42
  export declare function saveSchemaState(stateFilePath: string, state: SchemaState): void;
35
43
  export declare function loadMigrationLog(logFilePath: string): MigrationLog;
36
44
  export declare function saveMigrationLog(logFilePath: string, log: MigrationLog): void;
37
- export declare function compareSchemas(oldState: SchemaState | null, newAggregates: Record<string, AggregateConfig>): string[];
45
+ export declare function compareSchemas(oldState: SchemaState | null, newAggregates: Record<string, AggregateConfig>, availableValueObjects?: Set<string>, identifiers?: IdentifierType): string[];
38
46
  export declare function generateTimestamp(): string;
39
47
  export declare function getMigrationFileName(timestamp: string): string;
@@ -33,10 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getIdColumnDefinition = getIdColumnDefinition;
37
+ exports.getFkColumnType = getFkColumnType;
36
38
  exports.mapYamlTypeToSql = mapYamlTypeToSql;
37
39
  exports.getTableName = getTableName;
38
40
  exports.getForeignKeyFieldName = getForeignKeyFieldName;
39
41
  exports.isRelationshipField = isRelationshipField;
42
+ exports.buildChildToParentMap = buildChildToParentMap;
40
43
  exports.generateCreateTableSQL = generateCreateTableSQL;
41
44
  exports.generateDropTableSQL = generateDropTableSQL;
42
45
  exports.generateAddColumnSQL = generateAddColumnSQL;
@@ -56,16 +59,34 @@ const typeUtils_1 = require("./typeUtils");
56
59
  const TYPE_MAPPING = {
57
60
  string: 'VARCHAR(255)',
58
61
  number: 'INT',
62
+ integer: 'INT',
63
+ decimal: 'DECIMAL(10,2)',
59
64
  boolean: 'TINYINT(1)',
60
65
  datetime: 'DATETIME',
66
+ date: 'DATETIME',
67
+ id: 'INT',
61
68
  json: 'JSON',
62
69
  array: 'JSON',
63
70
  object: 'JSON'
64
71
  };
65
- function mapYamlTypeToSql(yamlType, availableAggregates, availableValueObjects) {
66
- // Simple aggregate reference → foreign key INT
72
+ function getIdColumnDefinition(idType = 'numeric') {
73
+ switch (idType) {
74
+ case 'uuid': return 'id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID(), 1))';
75
+ case 'nanoid': return 'id VARCHAR(21) PRIMARY KEY';
76
+ default: return 'id INT AUTO_INCREMENT PRIMARY KEY';
77
+ }
78
+ }
79
+ function getFkColumnType(idType = 'numeric') {
80
+ switch (idType) {
81
+ case 'uuid': return 'BINARY(16)';
82
+ case 'nanoid': return 'VARCHAR(21)';
83
+ default: return 'INT';
84
+ }
85
+ }
86
+ function mapYamlTypeToSql(yamlType, availableAggregates, availableValueObjects, identifiers = 'numeric') {
87
+ // Simple aggregate reference → foreign key column matching PK type
67
88
  if (availableAggregates.has(yamlType)) {
68
- return 'INT';
89
+ return getFkColumnType(identifiers);
69
90
  }
70
91
  // Compound types: array ("Foo[]") or union ("Foo | Bar") → JSON column
71
92
  const parsed = (0, typeUtils_1.parseFieldType)(yamlType);
@@ -78,8 +99,9 @@ function mapYamlTypeToSql(yamlType, availableAggregates, availableValueObjects)
78
99
  }
79
100
  return TYPE_MAPPING[yamlType] || 'VARCHAR(255)';
80
101
  }
102
+ /** Table name matches the store convention: singular lowercase aggregate name. */
81
103
  function getTableName(aggregateName) {
82
- return aggregateName.toLowerCase() + 's';
104
+ return aggregateName.toLowerCase();
83
105
  }
84
106
  function getForeignKeyFieldName(fieldName) {
85
107
  return fieldName + 'Id';
@@ -87,17 +109,40 @@ function getForeignKeyFieldName(fieldName) {
87
109
  function isRelationshipField(fieldType, availableAggregates) {
88
110
  return availableAggregates.has(fieldType);
89
111
  }
90
- function generateCreateTableSQL(name, aggregate, availableAggregates) {
112
+ /**
113
+ * Build a map of child entity name → parent entity name from the aggregates config.
114
+ * Used to determine parent ID column names for child entity tables.
115
+ */
116
+ function buildChildToParentMap(aggregates) {
117
+ const map = new Map();
118
+ for (const [parentName, config] of Object.entries(aggregates)) {
119
+ for (const childName of (config.entities || [])) {
120
+ map.set(childName, parentName);
121
+ }
122
+ }
123
+ return map;
124
+ }
125
+ function generateCreateTableSQL(name, aggregate, availableAggregates, availableValueObjects, parentIdField, identifiers = 'numeric') {
91
126
  const tableName = getTableName(name);
92
127
  const columns = [];
93
128
  const indexes = [];
94
129
  const foreignKeys = [];
95
- columns.push(' id INT AUTO_INCREMENT PRIMARY KEY');
130
+ const fkType = getFkColumnType(identifiers);
131
+ columns.push(` ${getIdColumnDefinition(identifiers)}`);
132
+ // Root aggregates get an ownerId column; child entities get a parent ID column.
133
+ if (parentIdField) {
134
+ columns.push(` ${parentIdField} ${fkType} NOT NULL`);
135
+ indexes.push(` INDEX idx_${tableName}_${parentIdField} (${parentIdField})`);
136
+ }
137
+ else if (aggregate.root !== false) {
138
+ columns.push(` ownerId ${fkType} NOT NULL`);
139
+ indexes.push(` INDEX idx_${tableName}_ownerId (ownerId)`);
140
+ }
96
141
  for (const [fieldName, field] of Object.entries(aggregate.fields)) {
97
142
  if (isRelationshipField(field.type, availableAggregates)) {
98
143
  const foreignKeyName = getForeignKeyFieldName(fieldName);
99
144
  const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
100
- columns.push(` ${foreignKeyName} INT ${nullable}`);
145
+ columns.push(` ${foreignKeyName} ${fkType} ${nullable}`);
101
146
  indexes.push(` INDEX idx_${tableName}_${foreignKeyName} (${foreignKeyName})`);
102
147
  const refTableName = getTableName(field.type);
103
148
  foreignKeys.push(` CONSTRAINT fk_${tableName}_${foreignKeyName} \n` +
@@ -107,50 +152,50 @@ function generateCreateTableSQL(name, aggregate, availableAggregates) {
107
152
  ` ON UPDATE CASCADE`);
108
153
  }
109
154
  else {
110
- const sqlType = mapYamlTypeToSql(field.type, availableAggregates);
155
+ const sqlType = mapYamlTypeToSql(field.type, availableAggregates, availableValueObjects, identifiers);
111
156
  const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
112
157
  columns.push(` ${fieldName} ${sqlType} ${nullable}`);
113
- if (['string', 'number'].includes(field.type)) {
158
+ if (['string', 'number', 'integer', 'id'].includes(field.type)) {
114
159
  indexes.push(` INDEX idx_${tableName}_${fieldName} (${fieldName})`);
115
160
  }
116
161
  }
117
162
  }
118
- columns.push(' created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP');
119
- columns.push(' updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
120
- columns.push(' deleted_at DATETIME NULL DEFAULT NULL');
121
- indexes.push(` INDEX idx_${tableName}_deleted_at (deleted_at)`);
122
- indexes.push(` INDEX idx_${tableName}_created_at (created_at)`);
163
+ columns.push(' createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP');
164
+ columns.push(' updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
165
+ columns.push(' deletedAt DATETIME NULL DEFAULT NULL');
166
+ indexes.push(` INDEX idx_${tableName}_deletedAt (deletedAt)`);
167
+ indexes.push(` INDEX idx_${tableName}_createdAt (createdAt)`);
123
168
  const allParts = [...columns, ...indexes, ...foreignKeys];
124
- return `CREATE TABLE IF NOT EXISTS ${tableName} (\n${allParts.join(',\n')}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`;
169
+ return `CREATE TABLE IF NOT EXISTS \`${tableName}\` (\n${allParts.join(',\n')}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`;
125
170
  }
126
171
  function generateDropTableSQL(tableName) {
127
- return `DROP TABLE IF EXISTS ${tableName};`;
172
+ return `DROP TABLE IF EXISTS \`${tableName}\`;`;
128
173
  }
129
- function generateAddColumnSQL(tableName, fieldName, field, availableAggregates) {
174
+ function generateAddColumnSQL(tableName, fieldName, field, availableAggregates, availableValueObjects, identifiers = 'numeric') {
130
175
  if (isRelationshipField(field.type, availableAggregates)) {
131
176
  const foreignKeyName = getForeignKeyFieldName(fieldName);
132
177
  const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
133
- return `ALTER TABLE ${tableName} ADD COLUMN ${foreignKeyName} INT ${nullable};`;
178
+ return `ALTER TABLE \`${tableName}\` ADD COLUMN \`${foreignKeyName}\` ${getFkColumnType(identifiers)} ${nullable};`;
134
179
  }
135
180
  else {
136
- const sqlType = mapYamlTypeToSql(field.type, availableAggregates);
181
+ const sqlType = mapYamlTypeToSql(field.type, availableAggregates, availableValueObjects, identifiers);
137
182
  const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
138
- return `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${sqlType} ${nullable};`;
183
+ return `ALTER TABLE \`${tableName}\` ADD COLUMN \`${fieldName}\` ${sqlType} ${nullable};`;
139
184
  }
140
185
  }
141
186
  function generateDropColumnSQL(tableName, columnName) {
142
- return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
187
+ return `ALTER TABLE \`${tableName}\` DROP COLUMN \`${columnName}\`;`;
143
188
  }
144
- function generateModifyColumnSQL(tableName, fieldName, field, availableAggregates) {
189
+ function generateModifyColumnSQL(tableName, fieldName, field, availableAggregates, availableValueObjects, identifiers = 'numeric') {
145
190
  if (isRelationshipField(field.type, availableAggregates)) {
146
191
  const foreignKeyName = getForeignKeyFieldName(fieldName);
147
192
  const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
148
- return `ALTER TABLE ${tableName} MODIFY COLUMN ${foreignKeyName} INT ${nullable};`;
193
+ return `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${foreignKeyName}\` ${getFkColumnType(identifiers)} ${nullable};`;
149
194
  }
150
195
  else {
151
- const sqlType = mapYamlTypeToSql(field.type, availableAggregates);
196
+ const sqlType = mapYamlTypeToSql(field.type, availableAggregates, availableValueObjects, identifiers);
152
197
  const nullable = field.required === false ? 'NULL DEFAULT NULL' : 'NOT NULL';
153
- return `ALTER TABLE ${tableName} MODIFY COLUMN ${fieldName} ${sqlType} ${nullable};`;
198
+ return `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${fieldName}\` ${sqlType} ${nullable};`;
154
199
  }
155
200
  }
156
201
  function loadSchemaState(stateFilePath) {
@@ -197,14 +242,18 @@ function sortAggregatesByDependencies(aggregates, availableAggregates) {
197
242
  }
198
243
  return sorted;
199
244
  }
200
- function compareSchemas(oldState, newAggregates) {
245
+ function compareSchemas(oldState, newAggregates, availableValueObjects, identifiers = 'numeric') {
201
246
  const sqlStatements = [];
202
247
  const availableAggregates = new Set(Object.keys(newAggregates));
248
+ const childToParent = buildChildToParentMap(newAggregates);
203
249
  if (!oldState || !oldState.aggregates || Object.keys(oldState.aggregates).length === 0) {
204
250
  const sorted = sortAggregatesByDependencies(newAggregates, availableAggregates);
205
251
  for (const [name, aggregate] of sorted) {
206
- sqlStatements.push(`-- Create ${name.toLowerCase()}s table`);
207
- sqlStatements.push(generateCreateTableSQL(name, aggregate, availableAggregates));
252
+ const tableName = getTableName(name);
253
+ const parentName = childToParent.get(name);
254
+ const parentIdField = parentName ? `${parentName.toLowerCase()}Id` : undefined;
255
+ sqlStatements.push(`-- Create ${tableName} table`);
256
+ sqlStatements.push(generateCreateTableSQL(name, aggregate, availableAggregates, availableValueObjects, parentIdField, identifiers));
208
257
  sqlStatements.push('');
209
258
  }
210
259
  return sqlStatements;
@@ -223,9 +272,11 @@ function compareSchemas(oldState, newAggregates) {
223
272
  for (const [name, newAggregate] of Object.entries(newAggregates)) {
224
273
  const oldAggregate = oldAggregates[name];
225
274
  const tableName = getTableName(name);
275
+ const parentName = childToParent.get(name);
276
+ const parentIdField = parentName ? `${parentName.toLowerCase()}Id` : undefined;
226
277
  if (!oldAggregate) {
227
278
  sqlStatements.push(`-- Create ${tableName} table`);
228
- sqlStatements.push(generateCreateTableSQL(name, newAggregate, availableAggregates));
279
+ sqlStatements.push(generateCreateTableSQL(name, newAggregate, availableAggregates, availableValueObjects, parentIdField, identifiers));
229
280
  sqlStatements.push('');
230
281
  }
231
282
  else {
@@ -247,7 +298,7 @@ function compareSchemas(oldState, newAggregates) {
247
298
  const oldField = oldFields[fieldName];
248
299
  if (!oldField) {
249
300
  sqlStatements.push(`-- Add column ${fieldName} to ${tableName}`);
250
- sqlStatements.push(generateAddColumnSQL(tableName, fieldName, newField, availableAggregates));
301
+ sqlStatements.push(generateAddColumnSQL(tableName, fieldName, newField, availableAggregates, availableValueObjects, identifiers));
251
302
  sqlStatements.push('');
252
303
  }
253
304
  else {
@@ -255,7 +306,7 @@ function compareSchemas(oldState, newAggregates) {
255
306
  const requiredChanged = oldField.required !== newField.required;
256
307
  if (typeChanged || requiredChanged) {
257
308
  sqlStatements.push(`-- Modify column ${fieldName} in ${tableName}`);
258
- sqlStatements.push(generateModifyColumnSQL(tableName, fieldName, newField, availableAggregates));
309
+ sqlStatements.push(generateModifyColumnSQL(tableName, fieldName, newField, availableAggregates, availableValueObjects, identifiers));
259
310
  sqlStatements.push('');
260
311
  }
261
312
  }
@@ -36,9 +36,9 @@ exports.ROW_TYPE_MAPPING = {
36
36
  datetime: 'string',
37
37
  date: 'string',
38
38
  id: 'number',
39
- json: 'string',
40
- array: 'string',
41
- object: 'string',
39
+ json: 'any',
40
+ array: 'any[]',
41
+ object: 'any',
42
42
  enum: 'string'
43
43
  };
44
44
  function capitalize(str) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@currentjs/gen",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "CLI code generator",
5
5
  "license": "LGPL-3.0",
6
6
  "author": "Konstantin Zavalny",