@ -225,6 +225,104 @@ export function registerMigration(migration: Migration): void {
* }
* }
* ` ` `
* ` ` `
* /
* /
/ * *
* Helper function to check if a SQLite result indicates a table exists
* @param result - The result from a sqlite_master query
* @returns true if the table exists
* /
function checkSqliteTableResult ( result : unknown ) : boolean {
return (
( result as unknown as { values : unknown [ ] [ ] } ) ? . values ? . length > 0 ||
( Array . isArray ( result ) && result . length > 0 )
) ;
}
/ * *
* Helper function to validate that a table exists in the database
* @param tableName - Name of the table to check
* @param sqlQuery - Function to execute SQL queries
* @returns Promise resolving to true if table exists
* /
async function validateTableExists < T > (
tableName : string ,
sqlQuery : ( sql : string , params? : unknown [ ] ) = > Promise < T > ,
) : Promise < boolean > {
try {
const result = await sqlQuery (
` SELECT name FROM sqlite_master WHERE type='table' AND name=' ${ tableName } ' ` ,
) ;
return checkSqliteTableResult ( result ) ;
} catch ( error ) {
logger . error ( ` ❌ [Validation] Error checking table ${ tableName } : ` , error ) ;
return false ;
}
}
/ * *
* Helper function to validate that a column exists in a table
* @param tableName - Name of the table
* @param columnName - Name of the column to check
* @param sqlQuery - Function to execute SQL queries
* @returns Promise resolving to true if column exists
* /
async function validateColumnExists < T > (
tableName : string ,
columnName : string ,
sqlQuery : ( sql : string , params? : unknown [ ] ) = > Promise < T > ,
) : Promise < boolean > {
try {
await sqlQuery ( ` SELECT ${ columnName } FROM ${ tableName } LIMIT 1 ` ) ;
return true ;
} catch ( error ) {
logger . error (
` ❌ [Validation] Error checking column ${ columnName } in ${ tableName } : ` ,
error ,
) ;
return false ;
}
}
/ * *
* Helper function to validate multiple tables exist
* @param tableNames - Array of table names to check
* @param sqlQuery - Function to execute SQL queries
* @returns Promise resolving to array of validation results
* /
async function validateMultipleTables < T > (
tableNames : string [ ] ,
sqlQuery : ( sql : string , params? : unknown [ ] ) = > Promise < T > ,
) : Promise < { exists : boolean ; missing : string [ ] } > {
const missing : string [ ] = [ ] ;
for ( const tableName of tableNames ) {
const exists = await validateTableExists ( tableName , sqlQuery ) ;
if ( ! exists ) {
missing . push ( tableName ) ;
}
}
return {
exists : missing.length === 0 ,
missing ,
} ;
}
/ * *
* Helper function to add validation error with consistent logging
* @param validation - The validation object to update
* @param message - Error message to add
* @param error - The error object for logging
* /
function addValidationError (
validation : MigrationValidation ,
message : string ,
error : unknown ,
) : void {
validation . isValid = false ;
validation . errors . push ( message ) ;
logger . error ( ` ❌ [Migration-Validation] ${ message } : ` , error ) ;
}
async function validateMigrationApplication < T > (
async function validateMigrationApplication < T > (
migration : Migration ,
migration : Migration ,
sqlQuery : ( sql : string , params? : unknown [ ] ) = > Promise < T > ,
sqlQuery : ( sql : string , params? : unknown [ ] ) = > Promise < T > ,
@ -248,94 +346,80 @@ async function validateMigrationApplication<T>(
"temp" ,
"temp" ,
] ;
] ;
for ( const tableName of tables ) {
const tableValidation = await validateMultipleTables ( tables , sqlQuery ) ;
try {
if ( ! tableValidation . exists ) {
await sqlQuery (
` SELECT name FROM sqlite_master WHERE type='table' AND name=' ${ tableName } ' ` ,
) ;
// Reduced logging - only log on error
} catch ( error ) {
validation . isValid = false ;
validation . errors . push ( ` Table ${ tableName } missing ` ) ;
logger . error (
` ❌ [Migration-Validation] Table ${ tableName } missing: ` ,
error ,
) ;
}
}
validation . tableExists = validation . errors . length === 0 ;
} else if ( migration . name === "002_add_iViewContent_to_contacts" ) {
// Validate iViewContent column exists in contacts table
try {
await sqlQuery ( ` SELECT iViewContent FROM contacts LIMIT 1 ` ) ;
validation . hasExpectedColumns = true ;
// Reduced logging - only log on error
} catch ( error ) {
validation . isValid = false ;
validation . isValid = false ;
validation . errors . push (
validation . errors . push (
` Column iViewContent missing from contacts table ` ,
` Missing tables: ${ tableValidation . missing . join ( ", " ) } ` ,
) ;
) ;
logger . error (
logger . error (
` ❌ [Migration-Validation] Column iViewContent missing : ` ,
` ❌ [Migration-Validation] Missing tables: ` ,
error ,
tableValidation . missing ,
) ;
) ;
}
}
} else if ( migration . name === "003_add_hasBackedUpSeed_to_settings" ) {
validation . tableExists = tableValidation . exists ;
// Validate hasBackedUpSeed column exists in settings table
} else if ( migration . name === "002_add_iViewContent_to_contacts" ) {
try {
// Validate iViewContent column exists in contacts table
await sqlQuery ( ` SELECT hasBackedUpSeed FROM settings LIMIT 1 ` ) ;
const columnExists = await validateColumnExists (
validation . isValid = true ;
"contacts" ,
validation . hasExpectedColumns = true ;
"iViewContent" ,
} catch ( error ) {
sqlQuery ,
validation . isValid = false ;
) ;
validation . errors . push (
if ( ! columnExists ) {
` Column hasBackedUpSeed missing from settings table ` ,
addValidationError (
) ;
validation ,
logger . error (
"Column iViewContent missing from contacts table" ,
` ❌ [Migration-Validation] Column hasBackedUpSeed missing: ` ,
new Error ( "Column not found" ) ,
error ,
) ;
) ;
} else {
validation . hasExpectedColumns = true ;
}
}
} else if ( migration . name === "004_active_identity_and_seed_backup" ) {
} else if ( migration . name === "003 _active_identity_and_seed_backup" ) {
// Validate active_identity table exists and has correct structure
// Validate active_identity table exists and has correct structure
try {
const activeIdentityExists = await validateTableExists (
// Check that active_identity table exists
"active_identity" ,
const activeIdentityResult = await sqlQuery (
sqlQuery ,
` SELECT name FROM sqlite_master WHERE type='table' AND name='active_identity' ` ,
) ;
) ;
const hasActiveIdentityTable =
( activeIdentityResult as unknown as { values : unknown [ ] [ ] } ) ? . values
? . length > 0 ||
( Array . isArray ( activeIdentityResult ) &&
activeIdentityResult . length > 0 ) ;
if ( ! hasActiveIdentityTable ) {
if ( ! activeIdentityExists ) {
validation . isValid = false ;
addValidationError (
validation . errors . push ( ` Table active_identity missing ` ) ;
validation ,
}
"Table active_identity missing" ,
new Error ( "Table not found" ) ,
) ;
} else {
validation . tableExists = true ;
// Check that active_identity has the expected structure
// Check that active_identity has the expected structure
try {
const hasExpectedColumns = await validateColumnExists (
await sqlQuery (
"active_identity" ,
` SELECT id, activeDid, lastUpdated FROM active_identity LIMIT 1 ` ,
"id, activeDid, lastUpdated" ,
sqlQuery ,
) ;
if ( ! hasExpectedColumns ) {
addValidationError (
validation ,
"active_identity table missing expected columns" ,
new Error ( "Columns not found" ) ,
) ;
) ;
} else {
validation . hasExpectedColumns = true ;
validation . hasExpectedColumns = true ;
} catch ( error ) {
validation . isValid = false ;
validation . errors . push (
` active_identity table missing expected columns ` ,
) ;
}
}
}
validation . tableExists = hasActiveIdentityTable ;
// Check that hasBackedUpSeed column exists in settings table
} catch ( error ) {
const hasBackedUpSeedExists = await validateColumnExists (
validation . isValid = false ;
"settings" ,
validation . errors . push (
"hasBackedUpSeed" ,
` Validation error for active_identity_and_seed_backup: ${ error } ` ,
sqlQuery ,
) ;
) ;
logger . error (
` ❌ [Migration-Validation] Validation failed for ${ migration . name } : ` ,
if ( ! hasBackedUpSeedExists ) {
error ,
addValidationError (
validation ,
"Column hasBackedUpSeed missing from settings table" ,
new Error ( "Column not found" ) ,
) ;
) ;
}
}
}
}
@ -615,34 +699,6 @@ export async function runMigrations<T>(
` 🔧 [Migration] SQL execution result: ${ JSON . stringify ( execResult ) } ` ,
` 🔧 [Migration] SQL execution result: ${ JSON . stringify ( execResult ) } ` ,
) ;
) ;
// Debug: Check if active_identity table exists and has data
if ( migration . name === "004_active_identity_and_seed_backup" ) {
try {
const tableCheck = await sqlQuery (
"SELECT name FROM sqlite_master WHERE type='table' AND name='active_identity'" ,
) ;
migrationLog (
` 🔍 [Migration] Table check result: ${ JSON . stringify ( tableCheck ) } ` ,
) ;
const rowCount = await sqlQuery (
"SELECT COUNT(*) as count FROM active_identity" ,
) ;
migrationLog (
` 🔍 [Migration] Row count in active_identity: ${ JSON . stringify ( rowCount ) } ` ,
) ;
const allRows = await sqlQuery ( "SELECT * FROM active_identity" ) ;
migrationLog (
` 🔍 [Migration] All rows in active_identity: ${ JSON . stringify ( allRows ) } ` ,
) ;
} catch ( error ) {
migrationLog (
` ❌ [Migration] Debug query failed: ${ JSON . stringify ( error ) } ` ,
) ;
}
}
// Validate the migration was applied correctly
// Validate the migration was applied correctly
const validation = await validateMigrationApplication (
const validation = await validateMigrationApplication (
migration ,
migration ,