refactor: reduce logging noise and simplify database service code

- Removed excessive debug/success logs from CapacitorPlatformService
- Removed verbose singleton tracking logs from PlatformServiceFactory
- Removed unnecessary platform !== 'web' check in applyPragmas (service only loads for Capacitor)
- Compressed try-catch blocks in applyPragmas for cleaner code
- Extracted executor adapters to createExecutor() methods in both CapacitorPlatformService and AbsurdSqlDatabaseService for consistency and testability

This reduces console noise while maintaining essential error logging.
This commit is contained in:
Matthew Raymer
2025-11-09 10:34:43 +00:00
parent 85a64b06d7
commit 11658140ae
3 changed files with 68 additions and 292 deletions

View File

@@ -154,12 +154,11 @@ class AbsurdSqlDatabaseService implements DatabaseService {
this.processQueue();
}
private async processQueue(): Promise<void> {
if (!this.initialized || !this.db) {
return;
}
const executor: QueueExecutor<AbsurdSqlDatabase> = {
/**
* Create executor adapter for AbsurdSQL API
*/
private createExecutor(): QueueExecutor<AbsurdSqlDatabase> {
return {
executeRun: async (db, sql, params) => {
return await db.run(sql, params);
},
@@ -167,10 +166,16 @@ class AbsurdSqlDatabaseService implements DatabaseService {
return await db.exec(sql, params);
},
};
}
private async processQueue(): Promise<void> {
if (!this.initialized || !this.db) {
return;
}
await this.operationQueue.processQueue(
this.db,
executor,
this.createExecutor(),
"AbsurdSqlDatabaseService",
);
}

View File

@@ -38,12 +38,6 @@ function getGlobal(): typeof globalThis {
function create(): PlatformService {
const which = import.meta.env?.VITE_PLATFORM ?? "web";
// Use console.log to avoid circular dependency with logger
// eslint-disable-next-line no-console
console.log(
`[PlatformServiceFactory] Creating singleton instance for platform: ${which}`,
);
if (which === "capacitor") return new CapacitorPlatformService();
if (which === "electron") return new ElectronPlatformService();
return new WebPlatformService();
@@ -57,37 +51,17 @@ function create(): PlatformService {
function getPlatformSvc(): PlatformService {
const global = getGlobal();
// Debug: Check if singleton exists (always log in dev to diagnose)
const exists = global.__PLATFORM_SERVICE_SINGLETON__ !== undefined;
const hasKey = Object.prototype.hasOwnProperty.call(
global,
"__PLATFORM_SERVICE_SINGLETON__",
);
// eslint-disable-next-line no-console
console.log(
`[PlatformServiceFactory] Singleton check: ${exists ? "EXISTS" : "MISSING"} (global type: ${typeof global}, has key: ${hasKey})`,
);
if (!exists) {
// eslint-disable-next-line no-console
console.log("[PlatformServiceFactory] Creating new singleton instance");
global.__PLATFORM_SERVICE_SINGLETON__ = create();
// Verify it was stored
if (global.__PLATFORM_SERVICE_SINGLETON__) {
// eslint-disable-next-line no-console
console.log(
"[PlatformServiceFactory] Singleton created and stored successfully",
);
} else {
if (!global.__PLATFORM_SERVICE_SINGLETON__) {
// eslint-disable-next-line no-console
console.error(
"[PlatformServiceFactory] ERROR: Singleton creation failed - storage returned undefined",
);
}
} else {
// Log singleton reuse
// eslint-disable-next-line no-console
console.log("[PlatformServiceFactory] Reusing existing singleton instance");
}
// Type guard: ensure singleton exists (should never be undefined at this point)
@@ -133,17 +107,6 @@ export const PlatformSvc = new Proxy({} as PlatformService, {
// Preserve singleton across Vite HMR
if (import.meta?.hot) {
if (import.meta.env?.MODE === "development") {
// Defer logging until singleton is actually accessed
setTimeout(() => {
const global = getGlobal();
if (global.__PLATFORM_SERVICE_SINGLETON__) {
// Use console.log to avoid circular dependency with logger
// eslint-disable-next-line no-console
console.log("[PlatformSvc] HMR active; singleton pinned to global");
}
}, 0);
}
import.meta.hot.accept(() => {
// Don't recreate on HMR - keep existing instance
const global = getGlobal();

View File

@@ -64,13 +64,11 @@ export class CapacitorPlatformService
// ============================================================================
/**
* Phase execution wrapper - logs once, throws on failure
* Phase execution wrapper - logs errors only
*/
private async runPhase<T>(name: string, fn: () => Promise<T>): Promise<T> {
try {
const out = await fn();
logger.log(`[CapacitorPlatformService] ${name} ok`);
return out;
return await fn();
} catch (e) {
logger.error(`[CapacitorPlatformService] ${name} failed`, e);
throw e;
@@ -99,21 +97,18 @@ export class CapacitorPlatformService
/**
* Apply SQLite PRAGMAs - single vetted sequence
*
* Note: This service is only loaded when VITE_PLATFORM=capacitor, so we're
* always running on a native platform (iOS/Android/Electron). The Capacitor
* SQLite plugin requires native platform support.
*/
private async applyPragmas(db: SQLiteDBConnection): Promise<void> {
const platform = Capacitor.getPlatform();
if (platform !== "web") {
try {
await db.query("PRAGMA journal_mode = WAL;");
await db.query("PRAGMA synchronous = NORMAL;");
} catch {
// Non-fatal if WAL/synchronous fail
}
}
try {
await db.run("PRAGMA foreign_keys = ON;", []);
} catch {
// Ignore errors
// Non-fatal if any PRAGMA fails
}
try {
await db.query("PRAGMA busy_timeout = 5000;");
@@ -464,12 +459,11 @@ export class CapacitorPlatformService
}
}
private async processQueue(): Promise<void> {
if (!this.initialized || !this.db) {
return;
}
const executor: QueueExecutor<SQLiteDBConnection> = {
/**
* Create executor adapter for Capacitor SQLite API
*/
private createExecutor(): QueueExecutor<SQLiteDBConnection> {
return {
executeRun: async (db, sql, params) => {
const runResult = await db.run(sql, params);
return {
@@ -488,10 +482,16 @@ export class CapacitorPlatformService
return await db.query(sql, params);
},
};
}
private async processQueue(): Promise<void> {
if (!this.initialized || !this.db) {
return;
}
await this.operationQueue.processQueue(
this.db,
executor,
this.createExecutor(),
"CapacitorPlatformService",
);
}
@@ -501,11 +501,8 @@ export class CapacitorPlatformService
sql: string,
params: unknown[] = [],
): Promise<R> {
// Only log SQL operations in debug mode to reduce console noise
logger.debug(`[CapacitorPlatformService] queueOperation - SQL: ${sql}`);
// Convert parameters to SQLite-compatible types with robust serialization
const convertedParams = params.map((param, index) => {
const convertedParams = params.map((param) => {
if (param === null || param === undefined) {
return null;
}
@@ -519,9 +516,6 @@ export class CapacitorPlatformService
stringRep.includes("Proxy(") || stringRep.startsWith("Proxy");
if (isProxy || forceProxyDetection) {
logger.debug(
`[CapacitorPlatformService] Proxy object detected at index ${index}`,
);
try {
// AGGRESSIVE EXTRACTION: Try multiple methods to extract actual values
if (Array.isArray(param)) {
@@ -534,11 +528,6 @@ export class CapacitorPlatformService
return actualObject;
}
} catch (proxyError) {
logger.debug(
`[CapacitorPlatformService] Failed to extract from Proxy at index ${index}:`,
proxyError,
);
// FALLBACK: Try to extract primitive values manually
if (Array.isArray(param)) {
try {
@@ -560,11 +549,6 @@ export class CapacitorPlatformService
return JSON.stringify(param);
} catch (error) {
// Handle non-serializable objects
logger.debug(
`[CapacitorPlatformService] Failed to serialize parameter at index ${index}:`,
error,
);
// Fallback: Convert to string representation
if (Array.isArray(param)) {
return `[Array(${param.length})]`;
@@ -578,16 +562,10 @@ export class CapacitorPlatformService
}
if (typeof param === "function") {
// Functions can't be serialized - convert to string representation
logger.debug(
`[CapacitorPlatformService] Function parameter detected and converted to string at index ${index}`,
);
return `[Function ${param.name || "Anonymous"}]`;
}
if (typeof param === "symbol") {
// Symbols can't be serialized - convert to string representation
logger.debug(
`[CapacitorPlatformService] Symbol parameter detected and converted to string at index ${index}`,
);
return param.toString();
}
// Numbers, strings, bigints are supported, but ensure bigints are converted to strings
@@ -711,28 +689,18 @@ export class CapacitorPlatformService
// Method 1: Check toString representation
const objString = obj.toString();
if (objString.includes("Proxy(") || objString.startsWith("Proxy")) {
logger.debug(
"[CapacitorPlatformService] Proxy detected via toString:",
objString,
);
return true;
}
// Method 2: Check constructor name
const constructorName = obj.constructor?.name;
if (constructorName === "Proxy") {
logger.debug(
"[CapacitorPlatformService] Proxy detected via constructor name",
);
return true;
}
// Method 3: Check Object.prototype.toString
const objToString = Object.prototype.toString.call(obj);
if (objToString.includes("Proxy")) {
logger.debug(
"[CapacitorPlatformService] Proxy detected via Object.prototype.toString",
);
return true;
}
@@ -743,9 +711,6 @@ export class CapacitorPlatformService
(prop) => prop.startsWith("__v_") || prop.startsWith("__r_"),
);
if (hasVueProxy) {
logger.debug(
"[CapacitorPlatformService] Vue reactive Proxy detected",
);
return true;
}
}
@@ -754,9 +719,6 @@ export class CapacitorPlatformService
try {
const jsonString = JSON.stringify(obj);
if (jsonString.includes("Proxy")) {
logger.debug(
"[CapacitorPlatformService] Proxy detected in JSON serialization",
);
return true;
}
} catch (jsonError) {
@@ -768,9 +730,6 @@ export class CapacitorPlatformService
errorMessage.includes("circular") ||
errorMessage.includes("clone")
) {
logger.debug(
"[CapacitorPlatformService] Proxy detected via JSON serialization error",
);
return true;
}
}
@@ -858,8 +817,6 @@ export class CapacitorPlatformService
* @returns Promise resolving to execution results
*/
const sqlExec = async (sql: string, params?: unknown[]): Promise<void> => {
logger.debug(`🔧 [CapacitorMigration] Executing SQL:`, sql);
if (params && params.length > 0) {
// Use run method for parameterized queries (prepared statements)
// This is essential for proper parameter binding and SQL injection prevention
@@ -901,8 +858,6 @@ export class CapacitorPlatformService
sql: string,
params?: unknown[],
): Promise<DBSQLiteValues> => {
logger.debug(`🔍 [CapacitorMigration] Querying SQL:`, sql);
const result = await this.db!.query(sql, params);
return result;
};
@@ -926,11 +881,6 @@ export class CapacitorPlatformService
* @returns Set of migration names found in the result
*/
const extractMigrationNames = (result: DBSQLiteValues): Set<string> => {
logger.debug(
`🔍 [CapacitorMigration] Extracting migration names from:`,
result,
);
// Handle the Capacitor SQLite result format
const names =
result.values
@@ -945,7 +895,6 @@ export class CapacitorPlatformService
})
.filter((name) => name !== null) || [];
logger.debug(`📋 [CapacitorMigration] Extracted names:`, names);
return new Set(names);
};
@@ -956,7 +905,7 @@ export class CapacitorPlatformService
// After migrations, run integrity check to verify database state
await this.verifyDatabaseIntegrity();
} catch (error) {
logger.error(`[CapacitorMigration] Migration failed:`, error);
logger.error("[CapacitorPlatformService] Migration failed:", error);
// Still try to verify what we have for debugging purposes
await this.verifyDatabaseIntegrity();
throw error;
@@ -1017,18 +966,17 @@ export class CapacitorPlatformService
*/
private async verifyDatabaseIntegrity(): Promise<void> {
if (!this.db) {
logger.error(`❌ [DB-Integrity] Database not initialized`);
logger.error(
"[CapacitorPlatformService] Database not initialized for integrity check",
);
return;
}
logger.debug(`🔍 [DB-Integrity] Starting database integrity check...`);
try {
// Step 1: Check migrations table and applied migrations
const migrationsResult = await this.db.query(
await this.db.query(
"SELECT name, applied_at FROM migrations ORDER BY applied_at",
);
logger.debug(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
// Step 2: Verify core tables exist
const coreTableNames = [
@@ -1048,13 +996,14 @@ export class CapacitorPlatformService
);
if (tableCheck.values && tableCheck.values.length > 0) {
existingTables.push(tableName);
logger.debug(`✅ [DB-Integrity] Table ${tableName} exists`);
} else {
logger.error(`❌ [DB-Integrity] Table ${tableName} missing`);
logger.error(
`[CapacitorPlatformService] Table ${tableName} missing`,
);
}
} catch (error) {
logger.error(
`❌ [DB-Integrity] Error checking table ${tableName}:`,
`[CapacitorPlatformService] Error checking table ${tableName}:`,
error,
);
}
@@ -1066,10 +1015,6 @@ export class CapacitorPlatformService
const contactsSchema = await this.db.query(
"PRAGMA table_info(contacts)",
);
logger.debug(
`📊 [DB-Integrity] Contacts table schema:`,
contactsSchema,
);
// Check for iViewContent column specifically
const hasIViewContent = contactsSchema.values?.some(
@@ -1081,18 +1026,14 @@ export class CapacitorPlatformService
(Array.isArray(col) && col[1] === "iViewContent"),
);
if (hasIViewContent) {
logger.debug(
`✅ [DB-Integrity] iViewContent column exists in contacts table`,
);
} else {
if (!hasIViewContent) {
logger.error(
`❌ [DB-Integrity] iViewContent column missing from contacts table`,
"[CapacitorPlatformService] iViewContent column missing from contacts table",
);
}
} catch (error) {
logger.error(
`❌ [DB-Integrity] Error checking contacts schema:`,
"[CapacitorPlatformService] Error checking contacts schema:",
error,
);
}
@@ -1100,26 +1041,20 @@ export class CapacitorPlatformService
// 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",
);
logger.debug(
`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`,
);
await this.db.query("SELECT COUNT(*) as count FROM accounts");
await this.db.query("SELECT COUNT(*) as count FROM settings");
await this.db.query("SELECT COUNT(*) as count FROM contacts");
} catch (error) {
logger.error(`❌ [DB-Integrity] Error checking data counts:`, error);
logger.error(
"[CapacitorPlatformService] Error checking data counts:",
error,
);
}
logger.log(`✅ [DB-Integrity] Database integrity check completed`);
} catch (error) {
logger.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
logger.error(
"[CapacitorPlatformService] Database integrity check failed:",
error,
);
}
}
@@ -1148,15 +1083,6 @@ export class CapacitorPlatformService
*/
private async checkStoragePermissions(): Promise<void> {
try {
const logData = {
platform: this.getCapabilities().isIOS ? "iOS" : "Android",
timestamp: new Date().toISOString(),
};
logger.log(
"Checking storage permissions",
JSON.stringify(logData, null, 2),
);
if (this.getCapabilities().isIOS) {
// iOS uses different permission model
return;
@@ -1168,28 +1094,12 @@ export class CapacitorPlatformService
path: "/storage/emulated/0/Download",
directory: Directory.Documents,
});
logger.log(
"Storage permissions already granted",
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
);
return;
} catch (error: unknown) {
const err = error as Error;
const errorLogData = {
error: {
message: err.message,
name: err.name,
stack: err.stack,
},
timestamp: new Date().toISOString(),
};
// "File does not exist" is expected and not a permission error
if (err.message === "File does not exist") {
logger.log(
"Directory does not exist (expected), proceeding with write",
JSON.stringify(errorLogData, null, 2),
);
return;
}
@@ -1198,11 +1108,6 @@ export class CapacitorPlatformService
err.message.includes("permission") ||
err.message.includes("access")
) {
logger.log(
"Permission check failed, requesting permissions",
JSON.stringify(errorLogData, null, 2),
);
// The Filesystem plugin will automatically request permissions when needed
// We just need to try the operation again
try {
@@ -1210,10 +1115,6 @@ export class CapacitorPlatformService
path: "/storage/emulated/0/Download",
directory: Directory.Documents,
});
logger.log(
"Storage permissions granted after request",
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
);
return;
} catch (retryError: unknown) {
const retryErr = retryError as Error;
@@ -1223,26 +1124,14 @@ export class CapacitorPlatformService
}
}
// For any other error, log it but don't treat as permission error
logger.log(
"Unexpected error during permission check",
JSON.stringify(errorLogData, null, 2),
);
// For any other error, ignore it (not a permission error)
return;
}
} catch (error: unknown) {
const err = error as Error;
const errorLogData = {
error: {
message: err.message,
name: err.name,
stack: err.stack,
},
timestamp: new Date().toISOString(),
};
logger.error(
"Error checking/requesting permissions",
JSON.stringify(errorLogData, null, 2),
"[CapacitorPlatformService] Error checking/requesting permissions:",
err,
);
throw new Error(`Failed to obtain storage permissions: ${err.message}`);
}
@@ -1298,17 +1187,6 @@ export class CapacitorPlatformService
// Check storage permissions before proceeding
await this.checkStoragePermissions();
const logData = {
targetFileName: fileName,
contentLength: content.length,
platform: this.getCapabilities().isIOS ? "iOS" : "Android",
timestamp: new Date().toISOString(),
};
logger.log(
"Starting writeFile operation",
JSON.stringify(logData, null, 2),
);
// For Android, we need to handle content URIs differently
if (this.getCapabilities().isIOS) {
// Write to app's Documents directory for iOS
@@ -1319,15 +1197,6 @@ export class CapacitorPlatformService
encoding: Encoding.UTF8,
});
const writeSuccessLogData = {
path: writeResult.uri,
timestamp: new Date().toISOString(),
};
logger.log(
"File write successful",
JSON.stringify(writeSuccessLogData, null, 2),
);
// Offer to share the file
try {
await Share.share({
@@ -1336,23 +1205,11 @@ export class CapacitorPlatformService
url: writeResult.uri,
dialogTitle: "Share your backup",
});
logger.log(
"Share dialog shown",
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
);
} catch (shareError) {
// Log share error but don't fail the operation
logger.error(
"Share dialog failed",
JSON.stringify(
{
error: shareError,
timestamp: new Date().toISOString(),
},
null,
2,
),
"[CapacitorPlatformService] Share dialog failed:",
shareError,
);
}
} else {
@@ -1364,15 +1221,6 @@ export class CapacitorPlatformService
encoding: Encoding.UTF8,
});
const writeSuccessLogData = {
path: writeResult.uri,
timestamp: new Date().toISOString(),
};
logger.log(
"File write successful to app storage",
JSON.stringify(writeSuccessLogData, null, 2),
);
// Then share the file to let user choose where to save it
try {
await Share.share({
@@ -1381,39 +1229,19 @@ export class CapacitorPlatformService
url: writeResult.uri,
dialogTitle: "Save your backup",
});
logger.log(
"Share dialog shown for Android",
JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
);
} catch (shareError) {
// Log share error but don't fail the operation
logger.error(
"Share dialog failed for Android",
JSON.stringify(
{
error: shareError,
timestamp: new Date().toISOString(),
},
null,
2,
),
"[CapacitorPlatformService] Share dialog failed:",
shareError,
);
}
}
} catch (error: unknown) {
const err = error as Error;
const finalErrorLogData = {
error: {
message: err.message,
name: err.name,
stack: err.stack,
},
timestamp: new Date().toISOString(),
};
logger.error(
"Error in writeFile operation:",
JSON.stringify(finalErrorLogData, null, 2),
"[CapacitorPlatformService] Error in writeFile operation:",
err,
);
throw new Error(`Failed to save file: ${err.message}`);
}
@@ -1429,15 +1257,6 @@ export class CapacitorPlatformService
* @param content - The content to write to the file
*/
async writeAndShareFile(fileName: string, content: string): Promise<void> {
const timestamp = new Date().toISOString();
const logData = {
action: "writeAndShareFile",
fileName,
contentLength: content.length,
timestamp,
};
logger.log("[CapacitorPlatformService]", JSON.stringify(logData, null, 2));
try {
// Check storage permissions before proceeding
await this.checkStoragePermissions();
@@ -1450,11 +1269,6 @@ export class CapacitorPlatformService
recursive: true,
});
logger.log("[CapacitorPlatformService] File write successful:", {
uri,
timestamp: new Date().toISOString(),
});
await Share.share({
title: "TimeSafari Backup",
text: "Here is your backup file.",
@@ -1463,14 +1277,9 @@ export class CapacitorPlatformService
});
} catch (error) {
const err = error as Error;
const errLog = {
message: err.message,
stack: err.stack,
timestamp: new Date().toISOString(),
};
logger.error(
"[CapacitorPlatformService] Error writing or sharing file:",
JSON.stringify(errLog, null, 2),
err,
);
throw new Error(`Failed to write or share file: ${err.message}`);
}
@@ -1592,7 +1401,6 @@ export class CapacitorPlatformService
this.currentDirection === CameraDirection.Rear
? CameraDirection.Front
: CameraDirection.Rear;
logger.debug(`Camera rotated to ${this.currentDirection} camera`);
}
/**