refactor(electron): WIP - use window.CapacitorSQLite API for all DB ops in ElectronPlatformService

- Remove connection object and connection pool logic
- Call all database methods directly on window.CapacitorSQLite with db name
- Refactor migrations, queries, and exec to match Capacitor SQLite Electron API
- Ensure preload script exposes both window.electron and window.CapacitorSQLite
- Fixes runtime errors related to missing query/run methods on connection
- Improves security and cross-platform compatibility

Co-authored-by: Matthew Raymer
This commit is contained in:
Matthew Raymer
2025-06-02 13:17:48 +00:00
parent 1e63ddcb6e
commit 66929d9b14
8 changed files with 367 additions and 350 deletions

View File

@@ -40,52 +40,71 @@ if (electronIsDev) {
// Run Application
(async () => {
try {
// Wait for electron app to be ready.
await app.whenReady();
// Wait for electron app to be ready.
await app.whenReady();
// Security - Set Content-Security-Policy based on whether or not we are in dev mode.
setupContentSecurityPolicy(myCapacitorApp.getCustomURLScheme());
// Security - Set Content-Security-Policy based on whether or not we are in dev mode.
setupContentSecurityPolicy(myCapacitorApp.getCustomURLScheme());
// Initialize SQLite and register handlers BEFORE app initialization
console.log('[Main] Starting SQLite initialization...');
// Initialize our app, build windows, and load content first
console.log('[Electron Main Process] Starting app initialization...');
await myCapacitorApp.init();
console.log('[Electron Main Process] App initialization complete');
// Get the main window and wait for it to be ready
const mainWindow = myCapacitorApp.getMainWindow();
if (!mainWindow) {
throw new Error('Main window not available after app initialization');
}
// Wait for window to be ready
await new Promise<void>((resolve) => {
if (mainWindow.isVisible()) {
resolve();
} else {
mainWindow.once('show', () => resolve());
}
});
// Now initialize SQLite after window is ready
console.log('[Electron Main Process] Starting SQLite initialization...');
try {
// Register handlers first to prevent "no handler" errors
setupSQLiteHandlers();
console.log('[Main] SQLite handlers registered');
console.log('[Electron Main Process] SQLite handlers registered');
// Then initialize the plugin
await initializeSQLite();
console.log('[Main] SQLite plugin initialized successfully');
console.log('[Electron Main Process] SQLite plugin initialized successfully');
// Send SQLite ready signal since window is ready
if (!mainWindow.isDestroyed()) {
mainWindow.webContents.send('sqlite-ready');
console.log('[Electron Main Process] Sent SQLite ready signal to renderer');
} else {
console.warn('[Electron Main Process] Could not send SQLite ready signal - window was destroyed');
}
} catch (error) {
console.error('[Main] Failed to initialize SQLite:', error);
console.error('[Electron Main Process] Failed to initialize SQLite:', error);
// Notify renderer about database status
if (!mainWindow.isDestroyed()) {
mainWindow.webContents.send('database-status', {
status: 'error',
error: error instanceof Error ? error.message : 'Unknown error'
});
}
// Don't proceed with app initialization if SQLite fails
throw new Error(`SQLite initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
// Initialize our app, build windows, and load content.
console.log('[Main] Starting app initialization...');
await myCapacitorApp.init();
console.log('[Main] App initialization complete');
// Check for updates if we are in a packaged app.
// Check for updates if we are in a packaged app.
if (!electronIsDev) {
console.log('[Main] Checking for updates...');
autoUpdater.checkForUpdatesAndNotify();
console.log('[Electron Main Process] Checking for updates...');
autoUpdater.checkForUpdatesAndNotify();
}
} catch (error) {
console.error('[Main] Fatal error during app initialization:', error);
// Ensure we notify the user before quitting
const mainWindow = myCapacitorApp.getMainWindow();
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('app-error', {
type: 'initialization',
error: error instanceof Error ? error.message : 'Unknown error'
});
// Give the window time to show the error
setTimeout(() => app.quit(), 5000);
} else {
app.quit();
}
console.error('[Electron Main Process] Fatal error during initialization:', error);
app.quit();
}
})();

View File

@@ -41,12 +41,12 @@ const createSQLiteProxy = () => {
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt < MAX_RETRIES) {
logger.warn(`SQLite operation failed (attempt ${attempt}/${MAX_RETRIES}), retrying...`, error);
logger.warn(`[CapacitorSQLite] SQLite operation failed (attempt ${attempt}/${MAX_RETRIES}), retrying...`, error);
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
}
}
}
throw new Error(`SQLite operation failed after ${MAX_RETRIES} attempts: ${lastError?.message || 'Unknown error'}`);
throw new Error(`[CapacitorSQLite] SQLite operation failed after ${MAX_RETRIES} attempts: ${lastError?.message || 'Unknown error'}`);
};
const wrapOperation = (method: string) => {
@@ -65,8 +65,8 @@ const createSQLiteProxy = () => {
}
return await withRetry(ipcRenderer.invoke, 'sqlite-' + method, ...args);
} catch (error) {
logger.error(`SQLite ${method} failed:`, error);
throw new Error(`Database operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
logger.error(`[CapacitorSQLite] SQLite ${method} failed:`, error);
throw new Error(`[CapacitorSQLite] Database operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
};
@@ -85,13 +85,24 @@ const createSQLiteProxy = () => {
};
};
// Expose only the CapacitorSQLite proxy
// Expose the Electron IPC API
contextBridge.exposeInMainWorld('electron', {
ipcRenderer: {
on: (channel: string, func: (...args: unknown[]) => void) => ipcRenderer.on(channel, (event, ...args) => func(...args)),
once: (channel: string, func: (...args: unknown[]) => void) => ipcRenderer.once(channel, (event, ...args) => func(...args)),
send: (channel: string, data: unknown) => ipcRenderer.send(channel, data),
invoke: (channel: string, ...args: unknown[]) => ipcRenderer.invoke(channel, ...args),
},
// Add other APIs as needed
});
// Expose CapacitorSQLite proxy as before
contextBridge.exposeInMainWorld('CapacitorSQLite', createSQLiteProxy());
// Log startup
logger.log('Script starting...');
logger.log('[CapacitorSQLite] Preload script starting...');
// Handle window load
window.addEventListener('load', () => {
logger.log('Script complete');
logger.log('[CapacitorSQLite] Preload script complete');
});