fiberx-backend-toolkit 0.0.61 → 0.0.63

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,82 @@
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 INDENT = " ".repeat(4);
5
+ const indentBlock = (code, level) => {
6
+ const indentation = INDENT.repeat(level);
7
+ return code
8
+ .split("\n")
9
+ .map(line => line.trim().length ? indentation + line : line)
10
+ .join("\n");
11
+ };
12
+ const serializeSequelizeDataType = (type) => {
13
+ if (!type)
14
+ return "undefined";
15
+ if (type.key) {
16
+ const key = type.key;
17
+ if (type.options?.length)
18
+ return `DataTypes.${key}(${type.options.length})`;
19
+ if (type.options?.values)
20
+ return `DataTypes.${key}(${JSON.stringify(type.options.values)})`;
21
+ if (type.options?.precision && type.options?.scale)
22
+ return `DataTypes.${key}(${type.options.precision}, ${type.options.scale})`;
23
+ return `DataTypes.${key}`;
24
+ }
25
+ return "undefined";
26
+ };
27
+ const serializeSequelizeColumnDefinition = (column) => {
28
+ const parts = [];
29
+ for (const [key, value] of Object.entries(column)) {
30
+ if (key === "type") {
31
+ parts.push(`type: ${serializeSequelizeDataType(value)}`);
32
+ }
33
+ else if (typeof value === "string") {
34
+ parts.push(`${key}: "${value}"`);
35
+ }
36
+ else if (typeof value === "boolean" || typeof value === "number") {
37
+ parts.push(`${key}: ${value}`);
38
+ }
39
+ else if (Array.isArray(value)) {
40
+ parts.push(`${key}: ${JSON.stringify(value)}`);
41
+ }
42
+ else if (value && typeof value === "object") {
43
+ parts.push(`${key}: ${JSON.stringify(value, null, 4)}`);
44
+ }
45
+ }
46
+ return `{
47
+ ${indentBlock(parts.join(",\n"), 1)}
48
+ }`;
49
+ };
50
+ const serializeIndexDefinition = (index) => {
51
+ const clone = { ...index };
52
+ delete clone.name;
53
+ delete clone.fields;
54
+ delete clone.comment;
55
+ const entries = Object.entries(clone)
56
+ .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
57
+ .join(",\n");
58
+ return entries;
59
+ };
60
+ const serializeSequelizeColumnsObject = (columns) => {
61
+ const rendered = Object.entries(columns)
62
+ .map(([col_name, col_def]) => {
63
+ const colCode = serializeSequelizeColumnDefinition(col_def);
64
+ return `"${col_name}": ${colCode}`;
65
+ })
66
+ .join(",\n");
67
+ return `{
68
+ ${indentBlock(rendered, 1)}
69
+ }`;
70
+ };
71
+ const serializeSequelizeIndexesArray = (indexes) => {
72
+ return indexes.map(index => {
73
+ return `{
74
+ name: "${index.name}",
75
+ fields: ${JSON.stringify(index.fields)},
76
+ ${serializeIndexDefinition(index).replace(/^{|}$/g, "")}
77
+ }`;
78
+ }).join(",\n ");
79
+ };
4
80
  const SEQUELIZE_SCHEMA_CODE_TEMPLATE = (schema_name, table_name, model_name, migration_priority) => {
5
81
  return `
6
82
  import { DataTypes } from "sequelize";
@@ -47,239 +123,245 @@ export default ${schema_name};
47
123
  `;
48
124
  };
49
125
  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) => `
51
- import { QueryInterface, Sequelize } from "sequelize";
126
+ const SEQUELIZE_CREATE_NEW_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (schema_table_name, schema_model_name, schema_file_name, schema_definition) => {
127
+ const rendered_columns = serializeSequelizeColumnsObject(schema_definition.columns);
128
+ const rendered_indexes = serializeSequelizeIndexesArray(schema_definition.indexes || []);
129
+ return `
130
+ import { QueryInterface, Sequelize, DataTypes } from "sequelize";
52
131
  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
132
 
56
133
  class Create${schema_model_name}TableMigration {
57
134
  private name: string = "create_table_${schema_table_name}_migration_file";
58
- private schema: SchemaDefinitionInterface = ${schema_model_name}Schema;
59
135
  private readonly logger: LoggerUtil = new LoggerUtil(this.name);
60
136
 
61
137
  constructor() {}
62
138
 
63
- // Migration UP
64
- public async up(
65
- queryInterface: QueryInterface,
66
- Sequelize: Sequelize
67
- ) {
139
+ public async up(queryInterface: QueryInterface, Sequelize: Sequelize) {
68
140
  try {
69
- const { table_name, model_name, columns = {}, indexes = [] } = this.schema;
70
-
71
- await queryInterface.createTable(table_name, columns);
141
+ const table_name = "${schema_table_name}";
72
142
 
73
- for (const index_field_obj of indexes) {
143
+ await queryInterface.createTable(
144
+ table_name,
145
+ ${indentBlock(rendered_columns, 4)}
146
+ );
74
147
 
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
- }
148
+ ${schema_definition.indexes?.map(index => {
149
+ const indexCode = `
150
+ await queryInterface.addIndex(
151
+ table_name,
152
+ ${JSON.stringify(index.fields)},
153
+ {
154
+ name: "${index.name}",
155
+ ${indentBlock(serializeIndexDefinition(index), 2)}
156
+ }
157
+ );
158
+ `;
159
+ return indentBlock(indexCode.trim(), 3);
160
+ }).join("\n\n") || ""}
84
161
 
85
- this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" created with "\${indexes.length}" indexed Fields.\`);
162
+ this.logger.success(\`✅ Table "\${table_name}" created successfully.\`);
86
163
  }
87
164
  catch (error: any) {
88
165
  this.logger.error(\`🚫 Migration failed: "\${error.message}"\`, { error });
89
- throw new Error(\`🚫 Migration failed: "\${error.message}"\`);
166
+ throw error;
90
167
  }
91
168
  }
92
169
 
93
- // Migration DOWN
94
- public async down(
95
- queryInterface: QueryInterface,
96
- Sequelize: Sequelize
97
- ) {
170
+ public async down(queryInterface: QueryInterface, Sequelize: Sequelize) {
98
171
  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.\`);
172
+ await queryInterface.dropTable("${schema_table_name}");
173
+ this.logger.success(\`🗑️ Table "${schema_table_name}" dropped successfully.\`);
102
174
  } catch (error: any) {
103
- this.logger.error(\`🚫 Migration rollback failed: "\${error.message}"\`, { error });
104
- throw new Error(\`🚫 Migration rollback failed: "\${error.message}"\`);
175
+ this.logger.error(\`🚫 Rollback failed: "\${error.message}"\`, { error });
176
+ throw error;
105
177
  }
106
178
  }
107
179
  }
108
180
 
109
181
  export default Create${schema_model_name}TableMigration;
110
182
  `;
183
+ };
111
184
  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) => `
185
+ const SEQUELIZE_UPDATE_EXISTING_SCHEMA_MIGRATION_FILE_CODE_TEMPLATE = (table_name, model_name, diff, current_schema, previous_schema) => {
186
+ return `
113
187
  import { QueryInterface, Sequelize } from "sequelize";
114
188
  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
189
 
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;
190
+ class Update${model_name}TableMigration {
191
+ private readonly name: string = "update_table_${table_name}_migration_file";
121
192
  private readonly logger: LoggerUtil = new LoggerUtil(this.name);
122
193
 
123
194
  constructor() {}
124
195
 
125
- public async up(
126
- queryInterface: QueryInterface,
127
- Sequelize: Sequelize
128
- ) {
196
+ public async up(queryInterface: QueryInterface, Sequelize: Sequelize) {
129
197
  try {
130
- const { table_name, model_name, columns = {}, indexes = [] } = this.schema;
198
+ let table_name = "${table_name}";
131
199
 
132
- ${diff.table_renamed ? `
200
+ ${diff.table_renamed ? `
133
201
  await queryInterface.renameTable(
134
202
  "${diff.table_renamed.from}",
135
203
  "${diff.table_renamed.to}"
136
- );`
137
- :
138
- ""}
204
+ );
205
+ table_name = "${diff.table_renamed.to}";
206
+ ` : ""}
139
207
 
140
- // Add columns
141
- ${diff.columns.added.map((column_name) => `
208
+ // ----------- ADD COLUMNS -----------
209
+ ${diff.columns.added.map(col => `
142
210
  await queryInterface.addColumn(
143
211
  table_name,
144
- "${column_name}",
145
- columns["${column_name}"]
212
+ "${col}",
213
+ ${serializeSequelizeColumnDefinition(current_schema.columns[col])}
146
214
  );
147
- `).join("")}
215
+ `).join("")}
148
216
 
149
- // Modify columns
150
- ${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
217
+ // ----------- MODIFY COLUMNS -----------
218
+ ${diff.columns.modified.map(col => `
151
219
  await queryInterface.changeColumn(
152
- table_name,
153
- "${col_name}",
154
- ${JSON.stringify(col_modified?.after)}
220
+ table_name,
221
+ "${col}",
222
+ ${serializeSequelizeColumnDefinition(current_schema.columns[col])}
155
223
  );
156
224
  `).join("")}
157
225
 
158
- // Remove columns
159
- ${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
226
+ // ----------- REMOVE COLUMNS -----------
227
+ ${diff.columns.removed.map(col => `
160
228
  await queryInterface.removeColumn(
161
- table_name,
162
- "${col_name}"
229
+ table_name,
230
+ "${col}"
163
231
  );
164
232
  `).join("")}
165
233
 
166
-
167
- // Add indexes
168
- ${diff.indexes.added.map((idx) => `
234
+ // ----------- ADD INDEXES -----------
235
+ ${diff.indexes.added.map(name => {
236
+ const curr_indxs = current_schema?.indexes || [];
237
+ const idx = curr_indxs.find(i => i.name === name);
238
+ return idx ? `
169
239
  await queryInterface.addIndex(
170
- table_name,
171
- ${JSON.stringify(idx.fields)},
172
- ${JSON.stringify(idx)}
240
+ table_name,
241
+ ${JSON.stringify(idx.fields)},
242
+ {
243
+ name: "${idx.name}",
244
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
245
+ }
173
246
  );
174
- `).join("")}
247
+ ` : "";
248
+ }).join("")}
175
249
 
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
- );
250
+ // ----------- MODIFY INDEXES -----------
251
+ ${diff.indexes.modified.map(name => {
252
+ const curr_indxs = current_schema?.indexes || [];
253
+ const idx = curr_indxs.find(i => i.name === name);
254
+ return idx ? `
255
+ await queryInterface.removeIndex(table_name, "${name}");
182
256
 
183
257
  await queryInterface.addIndex(
184
- table_name,
185
- ${JSON.stringify(idx_modified.after.fields)},
186
- ${JSON.stringify(idx_modified.after)}
258
+ table_name,
259
+ ${JSON.stringify(idx.fields)},
260
+ {
261
+ name: "${idx.name}",
262
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
263
+ }
187
264
  );
188
- `).join("")}
265
+ ` : "";
266
+ }).join("")}
189
267
 
190
- // Remove indexes
191
- ${diff.indexes.removed.map((idx) => `
192
- await queryInterface.removeIndex(
193
- table_name,
194
- ${JSON.stringify(idx.name)}
195
- );
268
+ // ----------- REMOVE INDEXES -----------
269
+ ${diff.indexes.removed.map(name => `
270
+ await queryInterface.removeIndex(table_name, "${name}");
196
271
  `).join("")}
197
272
 
198
-
199
- this.logger.success(\`✅ Table "\${table_name}" for schema "\${model_name}Schema" updated successfully.\`);
273
+ this.logger.success(\`✅ Table "\${table_name}" updated successfully.\`);
200
274
  }
201
275
  catch (error: any) {
202
276
  this.logger.error(\`🚫 Update migration failed: "\${error.message}"\`, { error });
203
- throw new Error(\`🚫 Update migration failed: "\${error.message}"\`);
277
+ throw error;
204
278
  }
205
279
  }
206
280
 
207
- public async down(
208
- queryInterface: QueryInterface,
209
- Sequelize: Sequelize
210
- ) {
281
+ public async down(queryInterface: QueryInterface, Sequelize: Sequelize) {
211
282
  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
- );
283
+ let table_name = "${table_name}";
284
+
285
+ // ----------- REVERT INDEXES -----------
286
+ ${diff.indexes.added.map(name => `
287
+ await queryInterface.removeIndex(table_name, "${name}");
288
+ `).join("")}
227
289
 
290
+ ${diff.indexes.modified.map(name => {
291
+ const prev_indxs = previous_schema?.indexes || [];
292
+ const idx = prev_indxs.find(i => i.name === name);
293
+ return idx ? `
294
+ await queryInterface.removeIndex(table_name, "${name}");
228
295
 
229
296
  await queryInterface.addIndex(
230
- table_name,
231
- ${JSON.stringify(idx_modified.before.fields)},
232
- ${JSON.stringify(idx_modified.before)}
233
- );`).join("")}
297
+ table_name,
298
+ ${JSON.stringify(idx.fields)},
299
+ {
300
+ name: "${idx.name}",
301
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
302
+ }
303
+ );
304
+ ` : "";
305
+ }).join("")}
234
306
 
235
- // Revert removed indexes
236
- ${diff.indexes.removed.map((idx) => `
307
+ ${diff.indexes.removed.map(name => {
308
+ const prev_indxs = previous_schema?.indexes || [];
309
+ const idx = prev_indxs.find(i => i.name === name);
310
+ return idx ? `
237
311
  await queryInterface.addIndex(
238
- table_name,
239
- ${JSON.stringify(idx.fields)},
240
- ${JSON.stringify(idx)}
241
- );`).join("")}
312
+ table_name,
313
+ ${JSON.stringify(idx.fields)},
314
+ {
315
+ name: "${idx.name}",
316
+ ${serializeIndexDefinition(idx).replace(/^{|}$/g, "")}
317
+ }
318
+ );
319
+ ` : "";
320
+ }).join("")}
242
321
 
243
- // Revert Modified columns
244
- ${Object.entries(diff.columns.modified).map(([col_name, col_modified]) => `
322
+ // ----------- REVERT COLUMNS -----------
323
+ ${diff.columns.modified.map(col => `
245
324
  await queryInterface.changeColumn(
246
- table_name,
247
- "${col_name}",
248
- ${JSON.stringify(col_modified.before)}
249
- );`).join("")}
325
+ table_name,
326
+ "${col}",
327
+ ${serializeSequelizeColumnDefinition(previous_schema.columns[col])}
328
+ );
329
+ `).join("")}
250
330
 
251
- // Revert added columns
252
- ${diff.columns.added.map((column_name) => `
253
- await queryInterface.removeColumn(table_name, "${column_name}");
254
- `).join("")}
331
+ ${diff.columns.added.map(col => `
332
+ await queryInterface.removeColumn(table_name, "${col}");
333
+ `).join("")}
255
334
 
256
- ${Object.entries(diff.columns.modified).map(([col_name, col_Def]) => `
335
+ ${diff.columns.removed.map(col => `
257
336
  await queryInterface.addColumn(
258
- table_name,
259
- "${col_name}",
260
- ${JSON.stringify(col_Def)}
337
+ table_name,
338
+ "${col}",
339
+ ${serializeSequelizeColumnDefinition(previous_schema.columns[col])}
261
340
  );
262
- `).join("")}
341
+ `).join("")}
263
342
 
264
- ${diff.table_renamed ? `
343
+ ${diff.table_renamed ? `
265
344
  await queryInterface.renameTable(
266
345
  "${diff.table_renamed.to}",
267
346
  "${diff.table_renamed.from}"
268
- );` : ""}
347
+ );
348
+ ` : ""}
269
349
 
270
- this.logger.success(\`🗑️ Rollback of table "\${table_name}" completed successfully.\`);
350
+ this.logger.success(\`🗑️ Rollback completed successfully.\`);
271
351
  }
272
352
  catch (error: any) {
273
353
  this.logger.error(\`🚫 Rollback migration failed: "\${error.message}"\`, { error });
274
- throw new Error(\`🚫 Rollback migration failed: "\${error.message}"\`);
354
+ throw error;
275
355
  }
276
356
  }
277
357
  }
278
358
 
279
- export default Update${schema_model_name}TableMigration;
359
+ export default Update${model_name}TableMigration;
280
360
  `;
361
+ };
281
362
  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) => `
363
+ const SEQUELIZE_SEEDER_TEMPLATE = (class_name, table_name) => {
364
+ return `
283
365
  'use strict';
284
366
 
285
367
  import { QueryInterface } from 'sequelize';
@@ -324,6 +406,7 @@ class ${class_name}Seeder {
324
406
 
325
407
  export default ${class_name}Seeder;
326
408
  `;
409
+ };
327
410
  exports.SEQUELIZE_SEEDER_TEMPLATE = SEQUELIZE_SEEDER_TEMPLATE;
328
411
  const SEQUELIZE_MODEL_CODE_TEMPLATE = (schema_model_name, schema_file_name, schema_columns) => {
329
412
  const attributes = Object.keys(schema_columns)
@@ -349,7 +432,7 @@ ${attributes}
349
432
  ${schema_model_name}Schema.columns,
350
433
  {
351
434
  sequelize,
352
- tableName: ${schema_model_name}Schema.table_name,
435
+ table_name: ${schema_model_name}Schema.table_name,
353
436
  modelName: ${schema_model_name}Schema.model_name,
354
437
  timestamps: ${schema_model_name}Schema.timestamps,
355
438
  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.63",
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",