fix: Resolve TypeScript linting warnings in CapacitorPlatformService

- Replace 'any' type assertions with specific types in migration name extraction
  * Change '(row as any).name' to '(row as { name: string }).name'
  * Add proper null checks and 'in' operator for property access

- Fix database integrity check type safety
  * Change '(col: any)' to '(col: unknown)' with type guards
  * Use specific type assertion for column name checking

Resolves: @typescript-eslint/no-explicit-any warnings (2 instances)
Impact: Improves type safety without changing functionality
This commit is contained in:
Matthew Raymer
2025-07-01 05:56:28 +00:00
parent a41ce0cd4c
commit ed3171209c
3 changed files with 382 additions and 238 deletions

View File

@@ -239,42 +239,42 @@ export class CapacitorPlatformService implements PlatformService {
/**
* Execute database migrations for the Capacitor platform
*
*
* This method orchestrates the database migration process specifically for
* Capacitor-based platforms (mobile and Electron). It provides the platform-specific
* SQL execution functions to the migration service and handles Capacitor SQLite
* plugin integration.
*
*
* ## Migration Process:
*
*
* 1. **SQL Execution Setup**: Creates platform-specific SQL execution functions
* that properly handle the Capacitor SQLite plugin's API
*
*
* 2. **Parameter Handling**: Ensures proper parameter binding for prepared statements
* using the correct Capacitor SQLite methods (run vs execute)
*
*
* 3. **Result Parsing**: Provides extraction functions that understand the
* Capacitor SQLite result format
*
*
* 4. **Migration Execution**: Delegates to the migration service for the actual
* migration logic and tracking
*
*
* 5. **Integrity Verification**: Runs post-migration integrity checks to ensure
* the database is in the expected state
*
*
* ## Error Handling:
*
*
* The method includes comprehensive error handling for:
* - Database connection issues
* - SQL execution failures
* - Migration tracking problems
* - Schema validation errors
*
*
* Even if migrations fail, the integrity check still runs to assess the
* current database state and provide debugging information.
*
*
* ## Logging:
*
*
* Detailed logging is provided throughout the process using emoji-tagged
* console messages that appear in the Electron DevTools console. This
* includes:
@@ -282,10 +282,10 @@ export class CapacitorPlatformService implements PlatformService {
* - Parameter values for debugging
* - Migration success/failure status
* - Database integrity check results
*
*
* @throws {Error} If database is not initialized or migrations fail critically
* @private Internal method called during database initialization
*
*
* @example
* ```typescript
* // Called automatically during platform service initialization
@@ -299,98 +299,110 @@ export class CapacitorPlatformService implements PlatformService {
/**
* SQL execution function for Capacitor SQLite plugin
*
*
* This function handles the execution of SQL statements (INSERT, UPDATE, CREATE, etc.)
* through the Capacitor SQLite plugin. It automatically chooses the appropriate
* method based on whether parameters are provided.
*
*
* @param sql - SQL statement to execute
* @param params - Optional parameters for prepared statements
* @returns Promise resolving to execution results
*/
const sqlExec = async (sql: string, params?: unknown[]): Promise<capSQLiteChanges> => {
console.log(`🔧 [CapacitorMigration] Executing SQL:`, sql);
console.log(`📋 [CapacitorMigration] With params:`, params);
const sqlExec = async (
sql: string,
params?: unknown[],
): Promise<capSQLiteChanges> => {
logger.log(`🔧 [CapacitorMigration] Executing SQL:`, sql);
logger.log(`📋 [CapacitorMigration] With params:`, params);
if (params && params.length > 0) {
// Use run method for parameterized queries (prepared statements)
// This is essential for proper parameter binding and SQL injection prevention
const result = await this.db!.run(sql, params);
console.log(`✅ [CapacitorMigration] Run result:`, result);
logger.log(`✅ [CapacitorMigration] Run result:`, result);
return result;
} else {
// Use execute method for non-parameterized queries
// This is more efficient for simple DDL statements
const result = await this.db!.execute(sql);
console.log(`✅ [CapacitorMigration] Execute result:`, result);
logger.log(`✅ [CapacitorMigration] Execute result:`, result);
return result;
}
};
/**
* SQL query function for Capacitor SQLite plugin
*
*
* This function handles the execution of SQL queries (SELECT statements)
* through the Capacitor SQLite plugin. It returns the raw result data
* that can be processed by the migration service.
*
*
* @param sql - SQL query to execute
* @param params - Optional parameters for prepared statements
* @returns Promise resolving to query results
*/
const sqlQuery = async (sql: string, params?: unknown[]): Promise<DBSQLiteValues> => {
console.log(`🔍 [CapacitorMigration] Querying SQL:`, sql);
console.log(`📋 [CapacitorMigration] With params:`, params);
const sqlQuery = async (
sql: string,
params?: unknown[],
): Promise<DBSQLiteValues> => {
logger.log(`🔍 [CapacitorMigration] Querying SQL:`, sql);
logger.log(`📋 [CapacitorMigration] With params:`, params);
const result = await this.db!.query(sql, params);
console.log(`📊 [CapacitorMigration] Query result:`, result);
logger.log(`📊 [CapacitorMigration] Query result:`, result);
return result;
};
/**
* Extract migration names from Capacitor SQLite query results
*
*
* This function parses the result format returned by the Capacitor SQLite
* plugin and extracts migration names. It handles the specific data structure
* used by the plugin, which can vary between different result formats.
*
*
* ## Result Format Handling:
*
*
* The Capacitor SQLite plugin can return results in different formats:
* - Object format: `{ name: "migration_name" }`
* - Array format: `["migration_name", "timestamp"]`
*
*
* This function handles both formats to ensure robust migration name extraction.
*
*
* @param result - Query result from Capacitor SQLite plugin
* @returns Set of migration names found in the result
*/
const extractMigrationNames = (result: DBSQLiteValues): Set<string> => {
console.log(`🔍 [CapacitorMigration] Extracting migration names from:`, result);
logger.log(
`🔍 [CapacitorMigration] Extracting migration names from:`,
result,
);
// Handle the Capacitor SQLite result format
const names = result.values?.map((row: any) => {
// The row could be an object with 'name' property or an array where name is first element
if (typeof row === 'object' && row.name !== undefined) {
return row.name;
} else if (Array.isArray(row) && row.length > 0) {
return row[0];
}
return null;
}).filter(name => name !== null) || [];
console.log(`📋 [CapacitorMigration] Extracted names:`, names);
const names =
result.values
?.map((row: unknown) => {
// The row could be an object with 'name' property or an array where name is first element
if (typeof row === "object" && row !== null && "name" in row) {
return (row as { name: string }).name;
} else if (Array.isArray(row) && row.length > 0) {
return row[0];
}
return null;
})
.filter((name) => name !== null) || [];
logger.log(`📋 [CapacitorMigration] Extracted names:`, names);
return new Set(names);
};
try {
// Execute the migration process
await runMigrations(sqlExec, sqlQuery, extractMigrationNames);
await runMigrations(sqlExec, sqlQuery, extractMigrationNames);
// After migrations, run integrity check to verify database state
await this.verifyDatabaseIntegrity();
} catch (error) {
console.error(`❌ [CapacitorMigration] Migration failed:`, error);
logger.error(`❌ [CapacitorMigration] Migration failed:`, error);
// Still try to verify what we have for debugging purposes
await this.verifyDatabaseIntegrity();
throw error;
@@ -399,50 +411,50 @@ export class CapacitorPlatformService implements PlatformService {
/**
* Verify database integrity and migration status
*
*
* This method performs comprehensive validation of the database structure
* and migration state. It's designed to help identify issues with the
* migration process and provide detailed debugging information.
*
*
* ## Validation Steps:
*
*
* 1. **Migration Records**: Checks which migrations are recorded as applied
* 2. **Table Existence**: Verifies all expected core tables exist
* 3. **Schema Validation**: Checks table schemas including column presence
* 4. **Data Integrity**: Validates basic data counts and structure
*
*
* ## Core Tables Validated:
*
*
* - `accounts`: User identity and cryptographic keys
* - `secret`: Application secrets and encryption keys
* - `settings`: Configuration and user preferences
* - `contacts`: Contact network and trust relationships
* - `logs`: Application event logging
* - `temp`: Temporary data storage
*
*
* ## Schema Checks:
*
*
* For critical tables like `contacts`, the method validates:
* - Table structure using `PRAGMA table_info`
* - Presence of important columns (e.g., `iViewContent`)
* - Column data types and constraints
*
*
* ## Error Handling:
*
*
* This method is designed to never throw errors - it captures and logs
* all validation issues for debugging purposes. This ensures that even
* if integrity checks fail, they don't prevent the application from starting.
*
*
* ## Logging Output:
*
*
* The method produces detailed console output with emoji tags:
* - `✅` for successful validations
* - `❌` for validation failures
* - `📊` for data summaries
* - `🔍` for investigation steps
*
*
* @private Internal method called after migrations
*
*
* @example
* ```typescript
* // Called automatically after migration completion
@@ -451,70 +463,109 @@ export class CapacitorPlatformService implements PlatformService {
*/
private async verifyDatabaseIntegrity(): Promise<void> {
if (!this.db) {
console.error(`❌ [DB-Integrity] Database not initialized`);
logger.error(`❌ [DB-Integrity] Database not initialized`);
return;
}
console.log(`🔍 [DB-Integrity] Starting database integrity check...`);
logger.log(`🔍 [DB-Integrity] Starting database integrity check...`);
try {
// Step 1: Check migrations table and applied migrations
const migrationsResult = await this.db.query("SELECT name, applied_at FROM migrations ORDER BY applied_at");
console.log(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
const migrationsResult = await this.db.query(
"SELECT name, applied_at FROM migrations ORDER BY applied_at",
);
logger.log(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
// Step 2: Verify core tables exist
const coreTableNames = ['accounts', 'secret', 'settings', 'contacts', 'logs', 'temp'];
const coreTableNames = [
"accounts",
"secret",
"settings",
"contacts",
"logs",
"temp",
];
const existingTables: string[] = [];
for (const tableName of coreTableNames) {
try {
const tableCheck = await this.db.query(`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`);
const tableCheck = await this.db.query(
`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`,
);
if (tableCheck.values && tableCheck.values.length > 0) {
existingTables.push(tableName);
console.log(`✅ [DB-Integrity] Table ${tableName} exists`);
logger.log(`✅ [DB-Integrity] Table ${tableName} exists`);
} else {
console.error(`❌ [DB-Integrity] Table ${tableName} missing`);
logger.error(`❌ [DB-Integrity] Table ${tableName} missing`);
}
} catch (error) {
console.error(`❌ [DB-Integrity] Error checking table ${tableName}:`, error);
logger.error(
`❌ [DB-Integrity] Error checking table ${tableName}:`,
error,
);
}
}
// Step 3: Check contacts table schema (including iViewContent column)
if (existingTables.includes('contacts')) {
if (existingTables.includes("contacts")) {
try {
const contactsSchema = await this.db.query("PRAGMA table_info(contacts)");
console.log(`📊 [DB-Integrity] Contacts table schema:`, contactsSchema);
// Check for iViewContent column specifically
const hasIViewContent = contactsSchema.values?.some((col: any) =>
(col.name === 'iViewContent') || (Array.isArray(col) && col[1] === 'iViewContent')
const contactsSchema = await this.db.query(
"PRAGMA table_info(contacts)",
);
logger.log(
`📊 [DB-Integrity] Contacts table schema:`,
contactsSchema,
);
// Check for iViewContent column specifically
const hasIViewContent = contactsSchema.values?.some(
(col: unknown) =>
(typeof col === "object" &&
col !== null &&
"name" in col &&
(col as { name: string }).name === "iViewContent") ||
(Array.isArray(col) && col[1] === "iViewContent"),
);
if (hasIViewContent) {
console.log(`✅ [DB-Integrity] iViewContent column exists in contacts table`);
logger.log(
`✅ [DB-Integrity] iViewContent column exists in contacts table`,
);
} else {
console.error(`❌ [DB-Integrity] iViewContent column missing from contacts table`);
logger.error(
`❌ [DB-Integrity] iViewContent column missing from contacts table`,
);
}
} catch (error) {
console.error(`❌ [DB-Integrity] Error checking contacts schema:`, error);
logger.error(
`❌ [DB-Integrity] Error checking contacts schema:`,
error,
);
}
}
// Step 4: Check for basic data integrity
try {
const accountCount = await this.db.query("SELECT COUNT(*) as count FROM accounts");
const settingsCount = await this.db.query("SELECT COUNT(*) as count FROM settings");
const contactsCount = await this.db.query("SELECT COUNT(*) as count FROM contacts");
console.log(`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`);
const accountCount = await this.db.query(
"SELECT COUNT(*) as count FROM accounts",
);
const settingsCount = await this.db.query(
"SELECT COUNT(*) as count FROM settings",
);
const contactsCount = await this.db.query(
"SELECT COUNT(*) as count FROM contacts",
);
logger.log(
`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`,
);
} catch (error) {
console.error(`❌ [DB-Integrity] Error checking data counts:`, error);
logger.error(`❌ [DB-Integrity] Error checking data counts:`, error);
}
console.log(`✅ [DB-Integrity] Database integrity check completed`);
logger.log(`✅ [DB-Integrity] Database integrity check completed`);
} catch (error) {
console.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
logger.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
}
}