fiberx-backend-toolkit 0.0.61 → 0.0.62

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.
@@ -1,7 +1,7 @@
1
- import { SchemaColumnInterface, SchemaDiffInterface } from "../types/schema_type";
1
+ import { SchemaColumnInterface, SchemaDefinitionInterface, SchemaDiffInterface } from "../types/schema_type";
2
2
  declare const SEQUELIZE_SCHEMA_CODE_TEMPLATE: (schema_name: string, table_name: string, model_name: string, migration_priority: number) => string;
3
- declare const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE: (schema_table_name: string, schema_model_name: string, schema_file_name: string) => string;
4
- declare const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE: (schema_table_name: string, schema_model_name: string, diff: SchemaDiffInterface, schema_file_name: string) => string;
3
+ declare const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE: (schema_table_name: string, schema_model_name: string, schema_file_name: string, schema_definition: SchemaDefinitionInterface) => string;
4
+ declare const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE: (table_name: string, model_name: string, diff: SchemaDiffInterface, current_schema: SchemaDefinitionInterface, previous_schema: SchemaDefinitionInterface) => string;
5
5
  declare const SEQUELIZE_SEEDER_TEMPLATE: (class_name: string, table_name: string) => string;
6
6
  declare const SEQUELIZE_MODEL_CODE_TEMPLATE: (schema_model_name: string, schema_file_name: string, schema_columns: Record<string, SchemaColumnInterface>) => string;
7
7
  declare const SEQUELIZE_MODELS_INDEX_CODE_TEMPLATE: (model_names: string[], imports: string) => string;
@@ -1,6 +1,68 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SEQUELIZE_MODELS_INDEX_CODE_TEMPLATE = exports.SEQUELIZE_MODEL_CODE_TEMPLATE = exports.SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = exports.SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = exports.SEQUELIZE_SEEDER_TEMPLATE = exports.SEQUELIZE_SCHEMA_CODE_TEMPLATE = void 0;
4
+ const serializeSequelizeDataType = (type) => {
5
+ if (!type)
6
+ return "undefined";
7
+ if (type.key) {
8
+ const key = type.key;
9
+ if (type.options?.length)
10
+ return `Sequelize.${key}(${type.options.length})`;
11
+ if (type.options?.values)
12
+ return `Sequelize.${key}(${JSON.stringify(type.options.values)})`;
13
+ if (type.options?.precision && type.options?.scale)
14
+ return `Sequelize.${key}(${type.options.precision}, ${type.options.scale})`;
15
+ return `Sequelize.${key}`;
16
+ }
17
+ return "undefined";
18
+ };
19
+ const serializeSequelizeColumnDefinition = (column) => {
20
+ const parts = [];
21
+ for (const [key, value] of Object.entries(column)) {
22
+ if (key === "type") {
23
+ parts.push(`type: ${serializeSequelizeDataType(value)}`);
24
+ }
25
+ else if (typeof value === "string") {
26
+ parts.push(`${key}: "${value}"`);
27
+ }
28
+ else if (typeof value === "boolean" || typeof value === "number") {
29
+ parts.push(`${key}: ${value}`);
30
+ }
31
+ else if (Array.isArray(value)) {
32
+ parts.push(`${key}: ${JSON.stringify(value)}`);
33
+ }
34
+ else if (value && typeof value === "object") {
35
+ parts.push(`${key}: ${JSON.stringify(value, null, 4)}`);
36
+ }
37
+ }
38
+ return `{
39
+ ${parts.join(",\n ")}
40
+ }`;
41
+ };
42
+ const serializeIndexDefinition = (index) => {
43
+ const clone = { ...index };
44
+ delete clone.name;
45
+ return JSON.stringify(clone, null, 4);
46
+ };
47
+ const serializeSequelizeColumnsObject = (columns) => {
48
+ const rendered = Object.entries(columns)
49
+ .map(([col_name, col_Def]) => {
50
+ return `"${col_name}": ${serializeSequelizeColumnDefinition(col_Def)}`;
51
+ })
52
+ .join(",\n ");
53
+ return `{
54
+ ${rendered}
55
+ }`;
56
+ };
57
+ const serializeSequelizeIndexesArray = (indexes) => {
58
+ return indexes.map(index => {
59
+ return `{
60
+ name: "${index.name}",
61
+ fields: ${JSON.stringify(index.fields)},
62
+ ${serializeIndexDefinition(index).replace(/^{|}$/g, "")}
63
+ }`;
64
+ }).join(",\n ");
65
+ };
4
66
  const SEQUELIZE_SCHEMA_CODE_TEMPLATE = (schema_name, table_name, model_name, migration_priority) => {
5
67
  return `
6
68
  import { DataTypes } from "sequelize";
@@ -47,239 +109,242 @@ export default ${schema_name};
47
109
  `;
48
110
  };
49
111
  exports.SEQUELIZE_SCHEMA_CODE_TEMPLATE = SEQUELIZE_SCHEMA_CODE_TEMPLATE;
50
- const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (schema_table_name, schema_model_name, schema_file_name) => `
112
+ const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (schema_table_name, schema_model_name, schema_file_name, schema_definition) => {
113
+ const rendered_columns = serializeSequelizeColumnsObject(schema_definition.columns);
114
+ const rendered_indexes = serializeSequelizeIndexesArray(schema_definition.indexes || []);
115
+ return `
51
116
  import { QueryInterface, Sequelize } from "sequelize";
52
117
  import { LoggerUtil } from "fiberx-backend-toolkit/dist/utils/main";
53
- import { SchemaDefinitionInterface } from "fiberx-backend-toolkit";
54
- import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
55
118
 
56
119
  class Create${schema_model_name}TableMigration {
57
120
  private name: string = "create_table_${schema_table_name}_migration_file";
58
- private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
59
121
  private readonly logger: LoggerUtil = new LoggerUtil(this.name);
60
122
 
61
123
  constructor() {}
62
124
 
63
- // Migration UP
64
- public async up(
65
- queryInterface: QueryInterface,
66
- Sequelize: Sequelize
67
- ) {
125
+ public async up(queryInterface: QueryInterface, Sequelize: Sequelize) {
68
126
  try {
69
- const { table_name, model_name, columns = {}, indexes = [] } = this.schema;
70
-
71
- await queryInterface.createTable(table_name, columns);
127
+ const table_name = "${schema_table_name}";
72
128
 
73
- for (const index_field_obj of indexes) {
129
+ await queryInterface.createTable(
130
+ table_name,
131
+ ${rendered_columns}
132
+ );
74
133
 
75
- const { fields, name, ...index_options } = index_field_obj;
76
- const index_name = \`idx_\${table_name}_\${fields.join("_")}\`
77
-
78
- await queryInterface.addIndex(
79
- table_name,
80
- fields,
81
- { name: index_name, ...index_options }
82
- );
83
- }
134
+ ${schema_definition.indexes?.map(index => `
135
+ await queryInterface.addIndex(
136
+ table_name,
137
+ ${JSON.stringify(index.fields)},
138
+ {
139
+ name: "${index.name}",
140
+ ${serializeIndexDefinition(index).replace(/^{|}$/g, "")}
141
+ }
142
+ );
143
+ `).join("") || ""}
84
144
 
85
- this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" created with "\${indexes.length}" indexed Fields.\`);
145
+ this.logger.success(\`✅ Table "\${table_name}" created successfully.\`);
86
146
  }
87
147
  catch (error: any) {
88
148
  this.logger.error(\`🚫 Migration failed: "\${error.message}"\`, { error });
89
- throw new Error(\`🚫 Migration failed: "\${error.message}"\`);
149
+ throw error;
90
150
  }
91
151
  }
92
152
 
93
- // Migration DOWN
94
- public async down(
95
- queryInterface: QueryInterface,
96
- Sequelize: Sequelize
97
- ) {
153
+ public async down(queryInterface: QueryInterface, Sequelize: Sequelize) {
98
154
  try {
99
- const { table_name, database_name, model_name } = this.schema;
100
- await queryInterface.dropTable(table_name, {});
101
- this.logger.success(\`🗑️ Table "\${table_name}" dropped successfully.\`);
155
+ await queryInterface.dropTable("${schema_table_name}");
156
+ this.logger.success(\`🗑️ Table "${schema_table_name}" dropped successfully.\`);
102
157
  } catch (error: any) {
103
- this.logger.error(\`🚫 Migration rollback failed: "\${error.message}"\`, { error });
104
- throw new Error(\`🚫 Migration rollback failed: "\${error.message}"\`);
158
+ this.logger.error(\`🚫 Rollback failed: "\${error.message}"\`, { error });
159
+ throw error;
105
160
  }
106
161
  }
107
162
  }
108
163
 
109
164
  export default Create${schema_model_name}TableMigration;
110
165
  `;
166
+ };
111
167
  exports.SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE;
112
- const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (schema_table_name, schema_model_name, diff, schema_file_name) => `
168
+ const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (table_name, model_name, diff, current_schema, previous_schema) => {
169
+ return `
113
170
  import { QueryInterface, Sequelize } from "sequelize";
114
171
  import { LoggerUtil } from "fiberx-backend-toolkit/dist/utils/main";
115
- import { SchemaDefinitionInterface } from "fiberx-backend-toolkit";
116
- import ${schema_model_name}Schema from "@/database/schemas/${schema_file_name.replace(".ts", "")}";
117
172
 
118
- class Update${schema_model_name}TableMigration {
119
- private name: string = "update_table_${schema_table_name}_migration_file";
120
- private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
173
+ class Update${model_name}TableMigration {
174
+ private readonly name: string = "update_table_${table_name}_migration_file";
121
175
  private readonly logger: LoggerUtil = new LoggerUtil(this.name);
122
176
 
123
177
  constructor() {}
124
178
 
125
- public async up(
126
- queryInterface: QueryInterface,
127
- Sequelize: Sequelize
128
- ) {
179
+ public async up(queryInterface: QueryInterface, Sequelize: Sequelize) {
129
180
  try {
130
- const { table_name, model_name, columns = {}, indexes = [] } = this.schema;
181
+ let table_name = "${table_name}";
131
182
 
132
- ${diff.table_renamed ? `
183
+ ${diff.table_renamed ? `
133
184
  await queryInterface.renameTable(
134
185
  "${diff.table_renamed.from}",
135
186
  "${diff.table_renamed.to}"
136
- );`
137
- :
138
- ""}
187
+ );
188
+ table_name = "${diff.table_renamed.to}";
189
+ ` : ""}
139
190
 
140
- // Add columns
141
- ${diff.columns.added.map((column_name) => `
191
+ // ----------- ADD COLUMNS -----------
192
+ ${diff.columns.added.map(col => `
142
193
  await queryInterface.addColumn(
143
194
  table_name,
144
- "${column_name}",
145
- columns["${column_name}"]
195
+ "${col}",
196
+ ${serializeSequelizeColumnDefinition(current_schema.columns[col])}
146
197
  );
147
- `).join("")}
198
+ `).join("")}
148
199
 
149
- // Modify columns
150
- ${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
200
+ // ----------- MODIFY COLUMNS -----------
201
+ ${diff.columns.modified.map(col => `
151
202
  await queryInterface.changeColumn(
152
- table_name,
153
- "${col_name}",
154
- ${JSON.stringify(col_modified?.after)}
203
+ table_name,
204
+ "${col}",
205
+ ${serializeSequelizeColumnDefinition(current_schema.columns[col])}
155
206
  );
156
207
  `).join("")}
157
208
 
158
- // Remove columns
159
- ${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
209
+ // ----------- REMOVE COLUMNS -----------
210
+ ${diff.columns.removed.map(col => `
160
211
  await queryInterface.removeColumn(
161
- table_name,
162
- "${col_name}"
212
+ table_name,
213
+ "${col}"
163
214
  );
164
215
  `).join("")}
165
216
 
166
-
167
- // Add indexes
168
- ${diff.indexes.added.map((idx) => `
217
+ // ----------- ADD INDEXES -----------
218
+ ${diff.indexes.added.map(name => {
219
+ const curr_indxs = current_schema?.indexes || [];
220
+ const idx = curr_indxs.find(i => i.name === name);
221
+ return idx ? `
169
222
  await queryInterface.addIndex(
170
- table_name,
171
- ${JSON.stringify(idx.fields)},
172
- ${JSON.stringify(idx)}
223
+ table_name,
224
+ ${JSON.stringify(idx.fields)},
225
+ {
226
+ name: "${idx.name}",
227
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
228
+ }
173
229
  );
174
- `).join("")}
230
+ ` : "";
231
+ }).join("")}
175
232
 
176
- // Modify indexes (remove + add)
177
- ${Object.entries(diff.indexes.modified).map(([idx_name, idx_modified]) => `
178
- await queryInterface.removeIndex(
179
- table_name,
180
- ${JSON.stringify(idx_modified.before.name)}
181
- );
233
+ // ----------- MODIFY INDEXES -----------
234
+ ${diff.indexes.modified.map(name => {
235
+ const curr_indxs = current_schema?.indexes || [];
236
+ const idx = curr_indxs.find(i => i.name === name);
237
+ return idx ? `
238
+ await queryInterface.removeIndex(table_name, "${name}");
182
239
 
183
240
  await queryInterface.addIndex(
184
- table_name,
185
- ${JSON.stringify(idx_modified.after.fields)},
186
- ${JSON.stringify(idx_modified.after)}
241
+ table_name,
242
+ ${JSON.stringify(idx.fields)},
243
+ {
244
+ name: "${idx.name}",
245
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
246
+ }
187
247
  );
188
- `).join("")}
248
+ ` : "";
249
+ }).join("")}
189
250
 
190
- // Remove indexes
191
- ${diff.indexes.removed.map((idx) => `
192
- await queryInterface.removeIndex(
193
- table_name,
194
- ${JSON.stringify(idx.name)}
195
- );
251
+ // ----------- REMOVE INDEXES -----------
252
+ ${diff.indexes.removed.map(name => `
253
+ await queryInterface.removeIndex(table_name, "${name}");
196
254
  `).join("")}
197
255
 
198
-
199
- this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" updated successfully.\`);
256
+ this.logger.success(\`✅ Table "\${table_name}" updated successfully.\`);
200
257
  }
201
258
  catch (error: any) {
202
259
  this.logger.error(\`🚫 Update migration failed: "\${error.message}"\`, { error });
203
- throw new Error(\`🚫 Update migration failed: "\${error.message}"\`);
260
+ throw error;
204
261
  }
205
262
  }
206
263
 
207
- public async down(
208
- queryInterface: QueryInterface,
209
- Sequelize: Sequelize
210
- ) {
264
+ public async down(queryInterface: QueryInterface, Sequelize: Sequelize) {
211
265
  try {
212
- const { table_name, model_name, columns = {} } = this.schema;
213
-
214
- // Revert Added indexes
215
- ${diff.indexes.added.map((idx) => `
216
- await queryInterface.removeIndex(
217
- table_name,
218
- ${JSON.stringify(idx.name)}
219
- );`).join("")}
220
-
221
- // Revert Modified Indexes
222
- ${Object.entries(diff.indexes.modified).map(([idx_name, idx_modified]) => `
223
- await queryInterface.removeIndex(
224
- table_name,
225
- ${JSON.stringify(idx_modified.after.name)}
226
- );
266
+ let table_name = "${table_name}";
227
267
 
268
+ // ----------- REVERT INDEXES -----------
269
+ ${diff.indexes.added.map(name => `
270
+ await queryInterface.removeIndex(table_name, "${name}");
271
+ `).join("")}
272
+
273
+ ${diff.indexes.modified.map(name => {
274
+ const prev_indxs = previous_schema?.indexes || [];
275
+ const idx = prev_indxs.find(i => i.name === name);
276
+ return idx ? `
277
+ await queryInterface.removeIndex(table_name, "${name}");
228
278
 
229
279
  await queryInterface.addIndex(
230
- table_name,
231
- ${JSON.stringify(idx_modified.before.fields)},
232
- ${JSON.stringify(idx_modified.before)}
233
- );`).join("")}
280
+ table_name,
281
+ ${JSON.stringify(idx.fields)},
282
+ {
283
+ name: "${idx.name}",
284
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
285
+ }
286
+ );
287
+ ` : "";
288
+ }).join("")}
234
289
 
235
- // Revert removed indexes
236
- ${diff.indexes.removed.map((idx) => `
290
+ ${diff.indexes.removed.map(name => {
291
+ const prev_indxs = previous_schema?.indexes || [];
292
+ const idx = prev_indxs.find(i => i.name === name);
293
+ return idx ? `
237
294
  await queryInterface.addIndex(
238
- table_name,
239
- ${JSON.stringify(idx.fields)},
240
- ${JSON.stringify(idx)}
241
- );`).join("")}
295
+ table_name,
296
+ ${JSON.stringify(idx.fields)},
297
+ {
298
+ name: "${idx.name}",
299
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
300
+ }
301
+ );
302
+ ` : "";
303
+ }).join("")}
242
304
 
243
- // Revert Modified columns
244
- ${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
305
+ // ----------- REVERT COLUMNS -----------
306
+ ${diff.columns.modified.map(col => `
245
307
  await queryInterface.changeColumn(
246
- table_name,
247
- "${col_name}",
248
- ${JSON.stringify(col_modified.before)}
249
- );`).join("")}
308
+ table_name,
309
+ "${col}",
310
+ ${serializeSequelizeColumnDefinition(previous_schema.columns[col])}
311
+ );
312
+ `).join("")}
250
313
 
251
- // Revert added columns
252
- ${diff.columns.added.map((column_name) => `
253
- await queryInterface.removeColumn(table_name, "${column_name}");
254
- `).join("")}
314
+ ${diff.columns.added.map(col => `
315
+ await queryInterface.removeColumn(table_name, "${col}");
316
+ `).join("")}
255
317
 
256
- ${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
318
+ ${diff.columns.removed.map(col => `
257
319
  await queryInterface.addColumn(
258
- table_name,
259
- "${col_name}",
260
- ${JSON.stringify(col_Def)}
320
+ table_name,
321
+ "${col}",
322
+ ${serializeSequelizeColumnDefinition(previous_schema.columns[col])}
261
323
  );
262
- `).join("")}
324
+ `).join("")}
263
325
 
264
- ${diff.table_renamed ? `
326
+ ${diff.table_renamed ? `
265
327
  await queryInterface.renameTable(
266
328
  "${diff.table_renamed.to}",
267
329
  "${diff.table_renamed.from}"
268
- );` : ""}
330
+ );
331
+ ` : ""}
269
332
 
270
- this.logger.success(\`🗑️ Rollback of table "\${table_name}" completed successfully.\`);
333
+ this.logger.success(\`🗑️ Rollback completed successfully.\`);
271
334
  }
272
335
  catch (error: any) {
273
336
  this.logger.error(\`🚫 Rollback migration failed: "\${error.message}"\`, { error });
274
- throw new Error(\`🚫 Rollback migration failed: "\${error.message}"\`);
337
+ throw error;
275
338
  }
276
339
  }
277
340
  }
278
341
 
279
- export default Update${schema_model_name}TableMigration;
342
+ export default Update${model_name}TableMigration;
280
343
  `;
344
+ };
281
345
  exports.SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE;
282
- const SEQUELIZE_SEEDER_TEMPLATE = (class_name, table_name) => `
346
+ const SEQUELIZE_SEEDER_TEMPLATE = (class_name, table_name) => {
347
+ return `
283
348
  'use strict';
284
349
 
285
350
  import { QueryInterface } from 'sequelize';
@@ -324,6 +389,7 @@ class ${class_name}Seeder {
324
389
 
325
390
  export default ${class_name}Seeder;
326
391
  `;
392
+ };
327
393
  exports.SEQUELIZE_SEEDER_TEMPLATE = SEQUELIZE_SEEDER_TEMPLATE;
328
394
  const SEQUELIZE_MODEL_CODE_TEMPLATE = (schema_model_name, schema_file_name, schema_columns) => {
329
395
  const attributes = Object.keys(schema_columns)
@@ -349,7 +415,7 @@ ${attributes}
349
415
  ${schema_model_name}Schema.columns,
350
416
  {
351
417
  sequelize,
352
- tableName: ${schema_model_name}Schema.table_name,
418
+ table_name: ${schema_model_name}Schema.table_name,
353
419
  modelName: ${schema_model_name}Schema.model_name,
354
420
  timestamps: ${schema_model_name}Schema.timestamps,
355
421
  indexes: ${schema_model_name}Schema.indexes,
@@ -1,5 +1,9 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const input_validator_util_1 = __importDefault(require("../../utils/input_validator_util"));
3
7
  class SchemaDiffUtil {
4
8
  static indexKey(i) {
5
9
  return JSON.stringify({
@@ -11,8 +15,8 @@ class SchemaDiffUtil {
11
15
  }
12
16
  static getSchemaDifference(prev, curr) {
13
17
  const diff = {
14
- columns: { added: [], removed: {}, modified: {} },
15
- indexes: { added: [], removed: [], modified: {} },
18
+ columns: { added: [], removed: [], modified: [] },
19
+ indexes: { added: [], removed: [], modified: [] }
16
20
  };
17
21
  // Table rename
18
22
  if (prev.table_name !== curr.table_name) {
@@ -22,46 +26,39 @@ class SchemaDiffUtil {
22
26
  };
23
27
  }
24
28
  // Columns
25
- for (const [col_name, col_def] of Object.entries(curr.columns)) {
26
- const prev_col = prev.columns[col_name];
27
- if (!prev_col) {
29
+ const prev_cols = prev.columns;
30
+ const curr_cols = curr.columns;
31
+ for (const [col_name, col_def] of Object.entries(curr_cols)) {
32
+ const prev_col_def = prev_cols[col_name];
33
+ if (!prev_col_def) {
28
34
  diff.columns.added.push(col_name);
29
35
  }
30
- else if (JSON.stringify(prev_col) !== JSON.stringify(col_def)) {
31
- diff.columns.modified[col_name] = {
32
- before: prev_col,
33
- after: col_def
34
- };
36
+ else if (!input_validator_util_1.default.isDeepEqual(prev_col_def, col_def)) {
37
+ diff.columns.modified.push(col_name);
35
38
  }
36
39
  }
37
- for (const [prev_col_name, prev_col_def] of Object.entries(prev.columns)) {
38
- if (!curr.columns[prev_col_name]) {
39
- diff.columns.removed[prev_col_name] = prev_col_def;
40
+ for (const col_name of Object.keys(prev_cols)) {
41
+ if (!curr_cols[col_name]) {
42
+ diff.columns.removed.push(col_name);
40
43
  }
41
44
  }
42
45
  // Indexes
43
46
  const prev_indexes = prev?.indexes || [];
44
47
  const curr_indexes = curr?.indexes || [];
45
- const prev_map = new Map(prev_indexes.map(i => [SchemaDiffUtil.indexKey(i), i]));
46
- const curr_map = new Map(curr_indexes.map(i => [SchemaDiffUtil.indexKey(i), i]));
47
- for (const [key, idx] of curr_map) {
48
- if (!prev_map.has(key)) {
49
- diff.indexes.added.push(idx);
48
+ const prev_map = new Map(prev_indexes.map(i => [i.name, i]));
49
+ const curr_map = new Map(curr_indexes.map(i => [i.name, i]));
50
+ for (const [name, curr_idx] of curr_map) {
51
+ const prev_idx = prev_map.get(name);
52
+ if (!prev_idx) {
53
+ diff.indexes.added.push(name);
50
54
  }
51
- }
52
- for (const [key, idx] of prev_map) {
53
- if (!curr_map.has(key)) {
54
- diff.indexes.removed.push(idx);
55
+ else if (!input_validator_util_1.default.isDeepEqual(prev_idx, curr_idx)) {
56
+ diff.indexes.modified.push(name);
55
57
  }
56
58
  }
57
- // Modified indexes (same fields, different options)
58
- for (const curr_idx of curr_indexes) {
59
- const prev_idx = prev_indexes.find(p => JSON.stringify([...p.fields].sort()) === JSON.stringify([...curr_idx.fields].sort()));
60
- if (prev_idx && JSON.stringify(prev_idx) !== JSON.stringify(curr_idx)) {
61
- diff.indexes.modified[curr_idx?.name] = {
62
- before: prev_idx,
63
- after: curr_idx
64
- };
59
+ for (const [name] of prev_map) {
60
+ if (!curr_map.has(name)) {
61
+ diff.indexes.removed.push(name);
65
62
  }
66
63
  }
67
64
  return diff;
@@ -2,7 +2,7 @@ import { SchemaDefinitionInterface, SchemaSnapshotInterface } from "../../types/
2
2
  declare class SchemaNormalizerUtil {
3
3
  static normalizeColumn(column: any): {
4
4
  type: any;
5
- allow_null: any;
5
+ allowNull: any;
6
6
  primary_key: boolean;
7
7
  auto_increment: boolean;
8
8
  unique: boolean;
@@ -4,7 +4,7 @@ class SchemaNormalizerUtil {
4
4
  static normalizeColumn(column) {
5
5
  return {
6
6
  type: column.type?.key ?? column.type?.toString(),
7
- allow_null: column.allowNull ?? true,
7
+ allowNull: column.allowNull ?? true,
8
8
  primary_key: !!column.primaryKey,
9
9
  auto_increment: !!column.autoIncrement,
10
10
  unique: !!column.unique,
@@ -17,7 +17,7 @@ class MakeMigrationsScript {
17
17
  logger = new main_1.LoggerUtil(this.name);
18
18
  constructor() { }
19
19
  getSnapshotPath(model_name) {
20
- return path_1.default.join(this.snapshot_dir, `${main_1.InputTransformerUtil.toSnakeCase(model_name)}.schema_snapshot.json`);
20
+ return path_1.default.join(this.snapshot_dir, `${main_1.InputTransformerUtil.toSnakeCase(model_name)}.schema_snapshot.ts`);
21
21
  }
22
22
  // Method to handle generating migration file name
23
23
  generateMigrationFilename(table_name, type, migration_priority) {
@@ -26,13 +26,14 @@ class MakeMigrationsScript {
26
26
  }
27
27
  ;
28
28
  // Methos to write schema snapshot
29
- writeSchemaSnapshot(model_name, normalized_schema) {
29
+ writeSchemaSnapshot(model_name, schema_file_path) {
30
30
  const snapshot_path = this.getSnapshotPath(model_name);
31
31
  try {
32
32
  // Ensure snapshot directory exists
33
33
  main_1.InputValidatorUtil.dirExists(this.snapshot_dir, true);
34
- fs_1.default.writeFileSync(snapshot_path, JSON.stringify(normalized_schema, null, 2), { encoding: "utf-8" });
35
- this.logger.success(`📸 Schema snapshot saved: ${snapshot_path}`);
34
+ // Copy schema file exactly as-is
35
+ fs_1.default.copyFileSync(schema_file_path, snapshot_path);
36
+ this.logger.success(`📸 Schema snapshot copied to: ${snapshot_path}`);
36
37
  return true;
37
38
  }
38
39
  catch (error) {
@@ -42,10 +43,10 @@ class MakeMigrationsScript {
42
43
  }
43
44
  }
44
45
  // Method to handle create update schema table migration file
45
- createUpdateSchemaMigrationFile(file_path, schema_file_path, schema_content, schema_diff) {
46
+ createUpdateSchemaMigrationFile(file_path, schema_file_path, schema_content, prev_schema_content, schema_diff) {
46
47
  const { table_name, model_name } = schema_content;
47
48
  const schema_file_name = path_1.default.basename(schema_file_path);
48
- const template = (0, sequelize_code_template_1.SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE)(table_name, model_name, schema_diff, schema_file_name);
49
+ const template = (0, sequelize_code_template_1.SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE)(table_name, model_name, schema_diff, schema_content, prev_schema_content);
49
50
  try {
50
51
  fs_1.default.writeFileSync(file_path, template, { encoding: "utf-8" });
51
52
  }
@@ -59,7 +60,7 @@ class MakeMigrationsScript {
59
60
  createNewSchemaMigrationFile(file_path, schema_file_path, schema_content) {
60
61
  const { table_name, model_name } = schema_content;
61
62
  const schema_file_name = path_1.default.basename(schema_file_path);
62
- const template = (0, sequelize_code_template_1.SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE)(table_name, model_name, schema_file_name);
63
+ const template = (0, sequelize_code_template_1.SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE)(table_name, model_name, schema_file_name, schema_content);
63
64
  try {
64
65
  fs_1.default.writeFileSync(file_path, template, { encoding: "utf-8" });
65
66
  }
@@ -77,7 +78,6 @@ class MakeMigrationsScript {
77
78
  this.logger.error(`❌ Schema has no table_name: ${schema_file_path}`);
78
79
  return;
79
80
  }
80
- const normalized_schema = main_2.SchemaNormalizerUtil.normalizeSchema(schema_content);
81
81
  const snapshot_path = this.getSnapshotPath(model_name);
82
82
  const operation = !fs_1.default.existsSync(snapshot_path) ? "create" : "update";
83
83
  const migration_file_name = this.generateMigrationFilename(table_name, operation, migration_priority);
@@ -90,8 +90,9 @@ class MakeMigrationsScript {
90
90
  else {
91
91
  // Existing migration → UPDATE
92
92
  this.logger.info(`🔄 Found existing migration snapshots: ${snapshot_path} → checking for updates...`);
93
- const previous_snapshot = JSON.parse(fs_1.default.readFileSync(snapshot_path, "utf-8"));
94
- const diff = main_2.SchemaDiffUtil.getSchemaDifference(previous_snapshot, normalized_schema);
93
+ delete require.cache[require.resolve(snapshot_path)];
94
+ const previous_snapshot = require(snapshot_path).default;
95
+ const diff = main_2.SchemaDiffUtil.getSchemaDifference(previous_snapshot, schema_content);
95
96
  if (!diff.table_renamed &&
96
97
  !diff.columns.added.length &&
97
98
  !diff.columns.removed.length &&
@@ -101,12 +102,12 @@ class MakeMigrationsScript {
101
102
  this.logger.info("ℹ️ No schema changes detected");
102
103
  return false;
103
104
  }
104
- migration_file_created = this.createUpdateSchemaMigrationFile(migration_file_path, schema_file_path, schema_content, diff);
105
+ migration_file_created = this.createUpdateSchemaMigrationFile(migration_file_path, schema_file_path, schema_content, previous_snapshot, diff);
105
106
  }
106
107
  if (migration_file_created) {
107
108
  this.logger.success(`✅ Migration file created: ${migration_file_name}`);
108
109
  // Update snapshot AFTER successful migration creation
109
- const snapshot_written = this.writeSchemaSnapshot(model_name, normalized_schema);
110
+ const snapshot_written = this.writeSchemaSnapshot(model_name, schema_file_path);
110
111
  if (!snapshot_written) {
111
112
  this.logger.error(`⚠️ Migration created but snapshot update failed for "${model_name}"`);
112
113
  }
@@ -69,18 +69,12 @@ export interface SchemaDiffInterface {
69
69
  };
70
70
  columns: {
71
71
  added: string[];
72
- removed: Record<string, SchemaColumnInterface>;
73
- modified: Record<string, {
74
- before: SchemaColumnInterface;
75
- after: SchemaColumnInterface;
76
- }>;
72
+ removed: string[];
73
+ modified: string[];
77
74
  };
78
75
  indexes: {
79
- added: IndexFieldOptionsInterface[];
80
- removed: IndexFieldOptionsInterface[];
81
- modified: Record<string, {
82
- before: IndexFieldOptionsInterface;
83
- after: IndexFieldOptionsInterface;
84
- }>;
76
+ added: string[];
77
+ removed: string[];
78
+ modified: string[];
85
79
  };
86
80
  }
@@ -50,5 +50,6 @@ declare class InputValidatorUtil {
50
50
  static isValidReCaptcha(recpatcha_token: string): Promise<any>;
51
51
  static dirExists(directory: string, create_directory?: boolean, return_dir_path?: boolean): boolean | string;
52
52
  static isSafeHashCompare(a: string, b: string): boolean;
53
+ static isDeepEqual(a: any, b: any): boolean;
53
54
  }
54
55
  export default InputValidatorUtil;
@@ -125,5 +125,54 @@ class InputValidatorUtil {
125
125
  }
126
126
  return crypto_1.default.timingSafeEqual(buf1, buf2);
127
127
  }
128
+ static isDeepEqual(a, b) {
129
+ // Strict equal handles primitives + same reference
130
+ if (a === b)
131
+ return true;
132
+ // Handle null / undefined
133
+ if (a == null || b == null)
134
+ return a === b;
135
+ // Handle Date
136
+ if (a instanceof Date && b instanceof Date) {
137
+ return a.getTime() === b.getTime();
138
+ }
139
+ // Handle functions (compare source)
140
+ if (typeof a === "function" && typeof b === "function") {
141
+ return a.toString() === b.toString();
142
+ }
143
+ // Handle Sequelize DataTypes
144
+ if (a?.key && b?.key) {
145
+ if (a.key !== b.key)
146
+ return false;
147
+ const aOpts = a.options || {};
148
+ const bOpts = b.options || {};
149
+ return InputValidatorUtil.isDeepEqual(aOpts, bOpts);
150
+ }
151
+ // Handle arrays
152
+ if (Array.isArray(a) && Array.isArray(b)) {
153
+ if (a.length !== b.length)
154
+ return false;
155
+ for (let i = 0; i < a.length; i++) {
156
+ if (!InputValidatorUtil.isDeepEqual(a[i], b[i]))
157
+ return false;
158
+ }
159
+ return true;
160
+ }
161
+ // Handle objects
162
+ if (typeof a === "object" && typeof b === "object") {
163
+ const aKeys = Object.keys(a);
164
+ const bKeys = Object.keys(b);
165
+ if (aKeys.length !== bKeys.length)
166
+ return false;
167
+ for (const key of aKeys) {
168
+ if (!bKeys.includes(key))
169
+ return false;
170
+ if (!InputValidatorUtil.isDeepEqual(a[key], b[key]))
171
+ return false;
172
+ }
173
+ return true;
174
+ }
175
+ return false;
176
+ }
128
177
  }
129
178
  exports.default = InputValidatorUtil;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiberx-backend-toolkit",
3
- "version": "0.0.61",
3
+ "version": "0.0.62",
4
4
  "description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",