@javalabs/prisma-client 1.0.4 → 1.0.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.
Files changed (58) hide show
  1. package/dist/scripts/data-migration/batch-migrator.d.ts +14 -19
  2. package/dist/scripts/data-migration/batch-migrator.js +98 -297
  3. package/dist/scripts/data-migration/batch-migrator.js.map +1 -1
  4. package/dist/scripts/data-migration/data-transformer.d.ts +16 -7
  5. package/dist/scripts/data-migration/data-transformer.js +169 -133
  6. package/dist/scripts/data-migration/data-transformer.js.map +1 -1
  7. package/dist/scripts/data-migration/db-connector.d.ts +6 -1
  8. package/dist/scripts/data-migration/db-connector.js +44 -8
  9. package/dist/scripts/data-migration/db-connector.js.map +1 -1
  10. package/dist/scripts/data-migration/dependency-resolver.d.ts +10 -10
  11. package/dist/scripts/data-migration/dependency-resolver.js +92 -211
  12. package/dist/scripts/data-migration/dependency-resolver.js.map +1 -1
  13. package/dist/scripts/data-migration/foreign-key-manager.d.ts +6 -5
  14. package/dist/scripts/data-migration/foreign-key-manager.js +108 -18
  15. package/dist/scripts/data-migration/foreign-key-manager.js.map +1 -1
  16. package/dist/scripts/data-migration/migration-config.json +63 -0
  17. package/dist/scripts/data-migration/migration-tool.d.ts +25 -6
  18. package/dist/scripts/data-migration/migration-tool.js +78 -38
  19. package/dist/scripts/data-migration/migration-tool.js.map +1 -1
  20. package/dist/scripts/data-migration/multi-source-migrator.d.ts +17 -0
  21. package/dist/scripts/data-migration/multi-source-migrator.js +130 -0
  22. package/dist/scripts/data-migration/multi-source-migrator.js.map +1 -0
  23. package/dist/scripts/data-migration/schema-utils.d.ts +3 -3
  24. package/dist/scripts/data-migration/schema-utils.js +62 -19
  25. package/dist/scripts/data-migration/schema-utils.js.map +1 -1
  26. package/dist/scripts/data-migration/tenant-migrator.js +9 -2
  27. package/dist/scripts/data-migration/tenant-migrator.js.map +1 -1
  28. package/dist/scripts/data-migration/typecast-manager.d.ts +7 -3
  29. package/dist/scripts/data-migration/typecast-manager.js +169 -25
  30. package/dist/scripts/data-migration/typecast-manager.js.map +1 -1
  31. package/dist/scripts/data-migration/types.d.ts +68 -2
  32. package/dist/scripts/fix-table-indexes.d.ts +26 -0
  33. package/dist/scripts/fix-table-indexes.js +460 -0
  34. package/dist/scripts/fix-table-indexes.js.map +1 -0
  35. package/dist/scripts/multi-db-migration.d.ts +1 -0
  36. package/dist/scripts/multi-db-migration.js +55 -0
  37. package/dist/scripts/multi-db-migration.js.map +1 -0
  38. package/dist/scripts/run-migration.js +41 -75
  39. package/dist/scripts/run-migration.js.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/migration-config.json +40 -72
  42. package/{migration-config-public.json → migration-config.json.bk} +14 -14
  43. package/package.json +6 -3
  44. package/src/scripts/data-migration/batch-migrator.ts +192 -513
  45. package/src/scripts/data-migration/data-transformer.ts +252 -203
  46. package/src/scripts/data-migration/db-connector.ts +66 -13
  47. package/src/scripts/data-migration/dependency-resolver.ts +121 -266
  48. package/src/scripts/data-migration/foreign-key-manager.ts +214 -32
  49. package/src/scripts/data-migration/migration-config.json +63 -0
  50. package/src/scripts/data-migration/migration-tool.ts +377 -225
  51. package/src/scripts/data-migration/schema-utils.ts +94 -32
  52. package/src/scripts/data-migration/tenant-migrator.ts +12 -5
  53. package/src/scripts/data-migration/typecast-manager.ts +186 -31
  54. package/src/scripts/data-migration/types.ts +78 -5
  55. package/src/scripts/dumps/source_dump_20250428_145606.sql +323 -0
  56. package/src/scripts/fix-table-indexes.ts +602 -0
  57. package/src/scripts/post-migration-validator.ts +206 -107
  58. package/src/scripts/run-migration.ts +87 -101
@@ -1,13 +1,29 @@
1
- import * as pg from 'pg';
2
- import * as dotenv from 'dotenv';
3
- import { Logger } from '@nestjs/common';
4
- import * as fs from 'fs';
5
- import * as path from 'path';
1
+ import * as pg from "pg";
2
+ import * as dotenv from "dotenv";
3
+ import { Logger } from "@nestjs/common";
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
6
 
7
7
  dotenv.config();
8
8
 
9
+ interface ValidationIssue {
10
+ type: string;
11
+ tenantId?: string;
12
+ providerId?: number | null;
13
+ message: string;
14
+ }
15
+
16
+ interface ValidationResult {
17
+ success: boolean;
18
+ issueCount: number;
19
+ timestamp: string;
20
+ sourceDatabase: string;
21
+ targetDatabase: string;
22
+ issues: ValidationIssue[];
23
+ }
24
+
9
25
  export class PostMigrationValidator {
10
- private readonly logger = new Logger('PostMigrationValidator');
26
+ private readonly logger = new Logger("PostMigrationValidator");
11
27
  private readonly sourcePool: pg.Pool;
12
28
  private readonly targetPool: pg.Pool;
13
29
  private readonly reportPath: string;
@@ -26,19 +42,45 @@ export class PostMigrationValidator {
26
42
  });
27
43
 
28
44
  // Crear directorio para reportes si no existe
29
- const reportsDir = path.join(process.cwd(), 'migration-logs');
45
+ const reportsDir = path.join(process.cwd(), "migration-logs");
30
46
  if (!fs.existsSync(reportsDir)) {
31
47
  fs.mkdirSync(reportsDir, { recursive: true });
32
48
  }
33
49
 
34
50
  // Archivo para guardar el reporte
35
- const timestamp = new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '');
36
- this.reportPath = path.join(reportsDir, `post-migration-report-${timestamp}.json`);
51
+ const timestamp = new Date()
52
+ .toISOString()
53
+ .replace(/:/g, "-")
54
+ .replace(/\..+/, "");
55
+ this.reportPath = path.join(
56
+ reportsDir,
57
+ `post-migration-report-${timestamp}.json`
58
+ );
37
59
  }
38
60
 
39
- async validate() {
61
+ async validate(options?: {
62
+ publicOnly?: boolean;
63
+ }): Promise<ValidationResult> {
64
+ const issues: ValidationIssue[] = [];
65
+ const timestamp = new Date().toISOString();
66
+
40
67
  try {
41
- this.logger.log('Starting post-migration validation');
68
+ // Si estamos en modo public-only, solo validamos el esquema público
69
+ if (options?.publicOnly) {
70
+ this.logger.log("Running validation in public-only mode");
71
+ // Aquí solo validaríamos las tablas públicas
72
+ return {
73
+ success: true,
74
+ issueCount: 0,
75
+ timestamp,
76
+ sourceDatabase: process.env.SOURCE_DATABASE_URL,
77
+ targetDatabase: process.env.DATABASE_URL,
78
+ issues: [],
79
+ };
80
+ }
81
+
82
+ // Si no es public-only, procedemos con la validación normal de todos los schemas
83
+ this.logger.log("Starting post-migration validation");
42
84
 
43
85
  // Obtener todos los API keys para determinar los schemas de tenant
44
86
  const apiKeysResult = await this.sourcePool.query(`
@@ -46,7 +88,9 @@ export class PostMigrationValidator {
46
88
  FROM api_keys
47
89
  `);
48
90
 
49
- this.logger.log(`Found ${apiKeysResult.rows.length} API keys to validate`);
91
+ this.logger.log(
92
+ `Found ${apiKeysResult.rows.length} API keys to validate`
93
+ );
50
94
 
51
95
  // Para cada API key (tenant), validar los datos migrados
52
96
  for (const apiKey of apiKeysResult.rows) {
@@ -60,37 +104,48 @@ export class PostMigrationValidator {
60
104
  this.saveReport();
61
105
 
62
106
  // Mostrar resumen
63
- this.logger.log(`Validation completed with ${this.issues.length} issues found`);
107
+ this.logger.log(
108
+ `Validation completed with ${this.issues.length} issues found`
109
+ );
64
110
  if (this.issues.length > 0) {
65
111
  this.logger.log(`Check the full report at: ${this.reportPath}`);
66
-
112
+
67
113
  // Mostrar los primeros 5 problemas como ejemplo
68
- this.logger.log('Sample issues:');
114
+ this.logger.log("Sample issues:");
69
115
  for (let i = 0; i < Math.min(5, this.issues.length); i++) {
70
116
  const issue = this.issues[i];
71
117
  this.logger.log(`- ${issue.type}: ${issue.message}`);
72
118
  }
73
119
  } else {
74
- this.logger.log('No issues found. Migration was successful.');
120
+ this.logger.log("No issues found. Migration was successful.");
75
121
  }
76
122
 
77
123
  return {
78
124
  success: this.issues.length === 0,
79
125
  issueCount: this.issues.length,
80
- reportPath: this.reportPath
126
+ timestamp,
127
+ sourceDatabase: this.sourceUrl,
128
+ targetDatabase: this.targetUrl,
129
+ issues: this.issues,
81
130
  };
82
131
  } catch (error) {
83
- this.logger.error(`Error during validation: ${error.message}`, error.stack);
132
+ this.logger.error(
133
+ `Error during validation: ${error.message}`,
134
+ error.stack
135
+ );
84
136
  this.issues.push({
85
- type: 'VALIDATION_ERROR',
137
+ type: "VALIDATION_ERROR",
86
138
  message: `Validation process failed: ${error.message}`,
87
- details: error.stack
139
+ details: error.stack,
88
140
  });
89
141
  this.saveReport();
90
142
  return {
91
143
  success: false,
92
144
  issueCount: this.issues.length,
93
- reportPath: this.reportPath
145
+ timestamp,
146
+ sourceDatabase: this.sourceUrl,
147
+ targetDatabase: this.targetUrl,
148
+ issues: this.issues,
94
149
  };
95
150
  } finally {
96
151
  await this.cleanup();
@@ -98,28 +153,35 @@ export class PostMigrationValidator {
98
153
  }
99
154
 
100
155
  private async validateTenantData(tenantId: string, providerId: number) {
101
- this.logger.log(`Validating data for tenant: ${tenantId} (Provider ID: ${providerId})`);
102
-
156
+ this.logger.log(
157
+ `Validating data for tenant: ${tenantId} (Provider ID: ${providerId})`
158
+ );
159
+
103
160
  try {
104
161
  // Verificar si el schema existe en la base de datos de destino
105
- const schemaExistsResult = await this.targetPool.query(`
162
+ const schemaExistsResult = await this.targetPool.query(
163
+ `
106
164
  SELECT 1
107
165
  FROM information_schema.schemata
108
166
  WHERE schema_name = $1
109
167
  LIMIT 1
110
- `, [tenantId]);
111
-
168
+ `,
169
+ [tenantId]
170
+ );
171
+
112
172
  if (schemaExistsResult.rows.length === 0) {
113
- this.logger.warn(`Schema ${tenantId} does not exist in target database`);
173
+ this.logger.warn(
174
+ `Schema ${tenantId} does not exist in target database`
175
+ );
114
176
  this.issues.push({
115
- type: 'MISSING_SCHEMA',
177
+ type: "MISSING_SCHEMA",
116
178
  tenantId,
117
179
  providerId,
118
- message: `Schema ${tenantId} does not exist in target database. Migration failed for this tenant.`
180
+ message: `Schema ${tenantId} does not exist in target database. Migration failed for this tenant.`,
119
181
  });
120
182
  return;
121
183
  }
122
-
184
+
123
185
  // Obtener todas las tablas de la base de datos de origen
124
186
  const tablesResult = await this.sourcePool.query(`
125
187
  SELECT table_name
@@ -128,99 +190,122 @@ export class PostMigrationValidator {
128
190
  AND table_type = 'BASE TABLE'
129
191
  AND table_name NOT IN ('_prisma_migrations', 'api_keys')
130
192
  `);
131
-
193
+
132
194
  // Para cada tabla, comparar el número de registros
133
195
  for (const tableRow of tablesResult.rows) {
134
196
  const tableName = tableRow.table_name;
135
197
  await this.validateTableData(tenantId, tableName, providerId);
136
198
  }
137
199
  } catch (error) {
138
- this.logger.error(`Error validating tenant ${tenantId}: ${error.message}`);
200
+ this.logger.error(
201
+ `Error validating tenant ${tenantId}: ${error.message}`
202
+ );
139
203
  this.issues.push({
140
- type: 'TENANT_VALIDATION_ERROR',
204
+ type: "TENANT_VALIDATION_ERROR",
141
205
  tenantId,
142
206
  providerId,
143
- message: `Error validating tenant ${tenantId}: ${error.message}`
207
+ message: `Error validating tenant ${tenantId}: ${error.message}`,
144
208
  });
145
209
  }
146
210
  }
147
211
 
148
- private async validateTableData(tenantId: string, tableName: string, providerId: number) {
212
+ private async validateTableData(
213
+ tenantId: string,
214
+ tableName: string,
215
+ providerId: number
216
+ ) {
149
217
  try {
150
218
  // Contar registros en la base de datos de origen
151
219
  let sourceCountQuery = `SELECT COUNT(*) as count FROM ${tableName}`;
152
-
220
+
153
221
  // Añadir filtro por provider_id si la tabla tiene esa columna
154
- const hasProviderIdResult = await this.sourcePool.query(`
222
+ const hasProviderIdResult = await this.sourcePool.query(
223
+ `
155
224
  SELECT 1
156
225
  FROM information_schema.columns
157
226
  WHERE table_schema = 'public'
158
227
  AND table_name = $1
159
228
  AND column_name = 'provider_id'
160
229
  LIMIT 1
161
- `, [tableName]);
162
-
230
+ `,
231
+ [tableName]
232
+ );
233
+
163
234
  if (hasProviderIdResult.rows.length > 0) {
164
235
  sourceCountQuery += ` WHERE provider_id = ${providerId}`;
165
236
  }
166
-
237
+
167
238
  const sourceCountResult = await this.sourcePool.query(sourceCountQuery);
168
239
  const sourceCount = parseInt(sourceCountResult.rows[0].count);
169
-
240
+
170
241
  // Contar registros en la base de datos de destino
171
242
  const targetCountQuery = `SELECT COUNT(*) as count FROM "${tenantId}"."${tableName}"`;
172
243
  const targetCountResult = await this.targetPool.query(targetCountQuery);
173
244
  const targetCount = parseInt(targetCountResult.rows[0].count);
174
-
245
+
175
246
  // Calcular el porcentaje de migración
176
- const migrationPercentage = sourceCount > 0 ? (targetCount / sourceCount) * 100 : 100;
177
-
178
- this.logger.log(`Table ${tableName} for tenant ${tenantId}: ${targetCount}/${sourceCount} records migrated (${migrationPercentage.toFixed(2)}%)`);
179
-
247
+ const migrationPercentage =
248
+ sourceCount > 0 ? (targetCount / sourceCount) * 100 : 100;
249
+
250
+ this.logger.log(
251
+ `Table ${tableName} for tenant ${tenantId}: ${targetCount}/${sourceCount} records migrated (${migrationPercentage.toFixed(
252
+ 2
253
+ )}%)`
254
+ );
255
+
180
256
  // Si hay una discrepancia significativa, registrarla como un problema
181
257
  if (migrationPercentage < 95) {
182
258
  this.issues.push({
183
- type: 'DATA_MISMATCH',
259
+ type: "DATA_MISMATCH",
184
260
  tenantId,
185
261
  tableName,
186
262
  sourceCount,
187
263
  targetCount,
188
264
  migrationPercentage: parseFloat(migrationPercentage.toFixed(2)),
189
- message: `Table ${tableName} for tenant ${tenantId}: Only ${migrationPercentage.toFixed(2)}% of records were migrated (${targetCount}/${sourceCount})`
265
+ message: `Table ${tableName} for tenant ${tenantId}: Only ${migrationPercentage.toFixed(
266
+ 2
267
+ )}% of records were migrated (${targetCount}/${sourceCount})`,
190
268
  });
191
269
  }
192
-
270
+
193
271
  // Si hay más registros en el destino que en el origen, también es un problema
194
272
  if (targetCount > sourceCount) {
195
273
  this.issues.push({
196
- type: 'EXCESS_DATA',
274
+ type: "EXCESS_DATA",
197
275
  tenantId,
198
276
  tableName,
199
277
  sourceCount,
200
278
  targetCount,
201
- message: `Table ${tableName} for tenant ${tenantId}: Target has more records (${targetCount}) than source (${sourceCount})`
279
+ message: `Table ${tableName} for tenant ${tenantId}: Target has more records (${targetCount}) than source (${sourceCount})`,
202
280
  });
203
281
  }
204
-
282
+
205
283
  // Validar algunos registros aleatorios para verificar la integridad de los datos
206
284
  if (sourceCount > 0 && targetCount > 0) {
207
285
  await this.validateRandomRecords(tenantId, tableName, providerId);
208
286
  }
209
287
  } catch (error) {
210
- this.logger.error(`Error validating table ${tableName} for tenant ${tenantId}: ${error.message}`);
288
+ this.logger.error(
289
+ `Error validating table ${tableName} for tenant ${tenantId}: ${error.message}`
290
+ );
211
291
  this.issues.push({
212
- type: 'TABLE_VALIDATION_ERROR',
292
+ type: "TABLE_VALIDATION_ERROR",
213
293
  tenantId,
214
294
  tableName,
215
- message: `Error validating table ${tableName} for tenant ${tenantId}: ${error.message}`
295
+ message: `Error validating table ${tableName} for tenant ${tenantId}: ${error.message}`,
216
296
  });
217
297
  }
218
298
  }
219
299
 
220
- private async validateRandomRecords(tenantId: string, tableName: string, providerId: number) {
300
+ private async validateRandomRecords(
301
+ tenantId: string,
302
+ tableName: string,
303
+ providerId: number
304
+ ) {
221
305
  try {
222
306
  // Obtener la clave primaria de la tabla
223
- const primaryKeyResult = await this.sourcePool.query(`
307
+ const primaryKeyResult = await this.sourcePool.query(
308
+ `
224
309
  SELECT kcu.column_name
225
310
  FROM information_schema.table_constraints tc
226
311
  JOIN information_schema.key_column_usage kcu
@@ -230,111 +315,123 @@ export class PostMigrationValidator {
230
315
  AND tc.table_schema = 'public'
231
316
  AND tc.table_name = $1
232
317
  LIMIT 1
233
- `, [tableName]);
234
-
318
+ `,
319
+ [tableName]
320
+ );
321
+
235
322
  if (primaryKeyResult.rows.length === 0) {
236
323
  this.logger.warn(`No primary key found for table ${tableName}`);
237
324
  return;
238
325
  }
239
-
326
+
240
327
  const primaryKeyColumn = primaryKeyResult.rows[0].column_name;
241
-
328
+
242
329
  // Obtener hasta 5 registros aleatorios de la base de datos de origen
243
330
  let randomRecordsQuery = `
244
331
  SELECT *
245
332
  FROM ${tableName}
246
333
  `;
247
-
334
+
248
335
  // Añadir filtro por provider_id si la tabla tiene esa columna
249
- const hasProviderIdResult = await this.sourcePool.query(`
336
+ const hasProviderIdResult = await this.sourcePool.query(
337
+ `
250
338
  SELECT 1
251
339
  FROM information_schema.columns
252
340
  WHERE table_schema = 'public'
253
341
  AND table_name = $1
254
342
  AND column_name = 'provider_id'
255
343
  LIMIT 1
256
- `, [tableName]);
257
-
344
+ `,
345
+ [tableName]
346
+ );
347
+
258
348
  if (hasProviderIdResult.rows.length > 0) {
259
349
  randomRecordsQuery += ` WHERE provider_id = ${providerId}`;
260
350
  }
261
-
351
+
262
352
  randomRecordsQuery += `
263
353
  ORDER BY RANDOM()
264
354
  LIMIT 5
265
355
  `;
266
-
267
- const randomRecordsResult = await this.sourcePool.query(randomRecordsQuery);
268
-
356
+
357
+ const randomRecordsResult = await this.sourcePool.query(
358
+ randomRecordsQuery
359
+ );
360
+
269
361
  // Para cada registro aleatorio, verificar si existe en la base de datos de destino
270
362
  for (const sourceRecord of randomRecordsResult.rows) {
271
363
  const primaryKeyValue = sourceRecord[primaryKeyColumn];
272
-
273
- const targetRecordResult = await this.targetPool.query(`
364
+
365
+ const targetRecordResult = await this.targetPool.query(
366
+ `
274
367
  SELECT *
275
368
  FROM "${tenantId}"."${tableName}"
276
369
  WHERE "${primaryKeyColumn}" = $1
277
370
  LIMIT 1
278
- `, [primaryKeyValue]);
279
-
371
+ `,
372
+ [primaryKeyValue]
373
+ );
374
+
280
375
  if (targetRecordResult.rows.length === 0) {
281
376
  this.issues.push({
282
- type: 'MISSING_RECORD',
377
+ type: "MISSING_RECORD",
283
378
  tenantId,
284
379
  tableName,
285
380
  recordId: primaryKeyValue,
286
- message: `Record with ID ${primaryKeyValue} in table ${tableName} for tenant ${tenantId} was not migrated`
381
+ message: `Record with ID ${primaryKeyValue} in table ${tableName} for tenant ${tenantId} was not migrated`,
287
382
  });
288
383
  continue;
289
384
  }
290
-
385
+
291
386
  const targetRecord = targetRecordResult.rows[0];
292
-
387
+
293
388
  // Comparar los valores de las columnas
294
389
  for (const columnName in sourceRecord) {
295
390
  // Ignorar columnas específicas de la base de datos de origen
296
- if (columnName === 'provider_id') {
391
+ if (columnName === "provider_id") {
297
392
  continue;
298
393
  }
299
-
394
+
300
395
  // Verificar si la columna existe en el registro de destino
301
396
  if (!(columnName in targetRecord)) {
302
397
  this.issues.push({
303
- type: 'MISSING_COLUMN',
398
+ type: "MISSING_COLUMN",
304
399
  tenantId,
305
400
  tableName,
306
401
  recordId: primaryKeyValue,
307
402
  columnName,
308
- message: `Column ${columnName} is missing in record with ID ${primaryKeyValue} in table ${tableName} for tenant ${tenantId}`
403
+ message: `Column ${columnName} is missing in record with ID ${primaryKeyValue} in table ${tableName} for tenant ${tenantId}`,
309
404
  });
310
405
  continue;
311
406
  }
312
-
407
+
313
408
  // Comparar los valores (con manejo especial para tipos de datos específicos)
314
409
  const sourceValue = sourceRecord[columnName];
315
410
  const targetValue = targetRecord[columnName];
316
-
411
+
317
412
  if (this.areValuesDifferent(sourceValue, targetValue)) {
318
413
  this.issues.push({
319
- type: 'VALUE_MISMATCH',
414
+ type: "VALUE_MISMATCH",
320
415
  tenantId,
321
416
  tableName,
322
417
  recordId: primaryKeyValue,
323
418
  columnName,
324
419
  sourceValue,
325
420
  targetValue,
326
- message: `Value mismatch for column ${columnName} in record with ID ${primaryKeyValue} in table ${tableName} for tenant ${tenantId}: ${sourceValue} (source) vs ${targetValue} (target)`
421
+ message: `Value mismatch for column ${columnName} in record with ID ${primaryKeyValue} in table ${tableName} for tenant ${tenantId}: ${sourceValue} (source) vs ${targetValue} (target)`,
327
422
  });
328
423
  }
329
424
  }
330
425
  }
331
426
  } catch (error) {
332
- this.logger.error(`Error validating random records for table ${tableName} in tenant ${tenantId}: ${error.message}`);
427
+ this.logger.error(
428
+ `Error validating random records for table ${tableName} in tenant ${tenantId}: ${error.message}`
429
+ );
333
430
  this.issues.push({
334
- type: 'RECORD_VALIDATION_ERROR',
431
+ type: "RECORD_VALIDATION_ERROR",
335
432
  tenantId,
336
433
  tableName,
337
- message: `Error validating random records for table ${tableName} in tenant ${tenantId}: ${error.message}`
434
+ message: `Error validating random records for table ${tableName} in tenant ${tenantId}: ${error.message}`,
338
435
  });
339
436
  }
340
437
  }
@@ -344,33 +441,33 @@ export class PostMigrationValidator {
344
441
  if (sourceValue == null && targetValue == null) {
345
442
  return false;
346
443
  }
347
-
444
+
348
445
  // Si uno es null y el otro no, son diferentes
349
446
  if (sourceValue == null || targetValue == null) {
350
447
  return true;
351
448
  }
352
-
449
+
353
450
  // Para fechas, comparar como strings ISO
354
451
  if (sourceValue instanceof Date && targetValue instanceof Date) {
355
452
  return sourceValue.toISOString() !== targetValue.toISOString();
356
453
  }
357
-
454
+
358
455
  // Para números, comparar con tolerancia para punto flotante
359
- if (typeof sourceValue === 'number' && typeof targetValue === 'number') {
456
+ if (typeof sourceValue === "number" && typeof targetValue === "number") {
360
457
  const epsilon = 0.0001;
361
458
  return Math.abs(sourceValue - targetValue) > epsilon;
362
459
  }
363
-
460
+
364
461
  // Para strings, comparar directamente
365
- if (typeof sourceValue === 'string' && typeof targetValue === 'string') {
462
+ if (typeof sourceValue === "string" && typeof targetValue === "string") {
366
463
  return sourceValue !== targetValue;
367
464
  }
368
-
465
+
369
466
  // Para booleanos, comparar directamente
370
- if (typeof sourceValue === 'boolean' && typeof targetValue === 'boolean') {
467
+ if (typeof sourceValue === "boolean" && typeof targetValue === "boolean") {
371
468
  return sourceValue !== targetValue;
372
469
  }
373
-
470
+
374
471
  // Para otros tipos, convertir a string y comparar
375
472
  return String(sourceValue) !== String(targetValue);
376
473
  }
@@ -382,13 +479,13 @@ export class PostMigrationValidator {
382
479
  sourceDatabase: this.sourceUrl,
383
480
  targetDatabase: this.targetUrl,
384
481
  issueCount: this.issues.length,
385
- issues: this.issues
482
+ issues: this.issues,
386
483
  };
387
-
484
+
388
485
  fs.writeFileSync(
389
486
  this.reportPath,
390
487
  JSON.stringify(report, null, 2),
391
- 'utf8'
488
+ "utf8"
392
489
  );
393
490
  } catch (error) {
394
491
  this.logger.error(`Error saving validation report: ${error.message}`);
@@ -396,7 +493,7 @@ export class PostMigrationValidator {
396
493
  }
397
494
 
398
495
  private async cleanup() {
399
- this.logger.log('Cleaning up database connections');
496
+ this.logger.log("Cleaning up database connections");
400
497
  await this.sourcePool.end();
401
498
  await this.targetPool.end();
402
499
  }
@@ -408,20 +505,22 @@ if (require.main === module) {
408
505
  try {
409
506
  const validator = new PostMigrationValidator();
410
507
  const result = await validator.validate();
411
-
508
+
412
509
  if (result.success) {
413
- console.log('Post-migration validation successful! No issues found.');
510
+ console.log("Post-migration validation successful! No issues found.");
414
511
  process.exit(0);
415
512
  } else {
416
- console.log(`Post-migration validation completed with ${result.issueCount} issues found.`);
417
- console.log(`Check the full report at: ${result.reportPath}`);
513
+ console.log(
514
+ `Post-migration validation completed with ${result.issueCount} issues found.`
515
+ );
516
+ console.log(JSON.stringify(result, null, 2));
418
517
  process.exit(1);
419
518
  }
420
519
  } catch (error) {
421
- console.error('Error:', error.message);
520
+ console.error("Error:", error.message);
422
521
  process.exit(1);
423
522
  }
424
523
  };
425
524
 
426
525
  run();
427
- }
526
+ }