fix: SQLite plugin initialization in Electron main process
- Changed from direct plugin usage to SQLiteConnection pattern - Matches how platform services use the SQLite plugin - Removed handle() method dependency - Added proper method routing in IPC handler The app now launches without initialization errors. Next steps: - Test actual SQLite operations (createConnection, query, etc.) - Verify database creation and access - Add error handling for database operations
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"appId": "app.timesafari",
|
"appId": "com.timesafari.app",
|
||||||
"appName": "TimeSafari",
|
"appName": "TimeSafari",
|
||||||
"webDir": "dist",
|
"webDir": "dist",
|
||||||
"bundledWebRuntime": false,
|
"bundledWebRuntime": false,
|
||||||
"server": {
|
"server": {
|
||||||
"cleartext": true
|
"cleartext": true,
|
||||||
|
"androidScheme": "https"
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"App": {
|
"App": {
|
||||||
@@ -29,6 +30,12 @@
|
|||||||
"biometricAuth": true,
|
"biometricAuth": true,
|
||||||
"biometricTitle": "Biometric login for TimeSafari"
|
"biometricTitle": "Biometric login for TimeSafari"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"CapacitorSQLite": {
|
||||||
|
"electronIsEncryption": false,
|
||||||
|
"electronMacLocation": "~/Library/Application Support/TimeSafari",
|
||||||
|
"electronWindowsLocation": "C:\\ProgramData\\TimeSafari",
|
||||||
|
"electronLinuxLocation": "~/.local/share/TimeSafari"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ios": {
|
"ios": {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"clean:electron": "rimraf dist-electron",
|
"clean:electron": "rimraf dist-electron",
|
||||||
"build:pywebview": "vite build --config vite.config.pywebview.mts",
|
"build:pywebview": "vite build --config vite.config.pywebview.mts",
|
||||||
"build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts",
|
"build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts",
|
||||||
"build:web:electron": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.electron.mts --mode electron",
|
"build:web:electron": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts && VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.electron.mts --mode electron",
|
||||||
"build:electron": "npm run clean:electron && npm run build:web:electron && tsc -p tsconfig.electron.json && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
|
"build:electron": "npm run clean:electron && npm run build:web:electron && tsc -p tsconfig.electron.json && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
|
||||||
"build:capacitor": "vite build --mode capacitor --config vite.config.capacitor.mts",
|
"build:capacitor": "vite build --mode capacitor --config vite.config.capacitor.mts",
|
||||||
"electron:dev": "npm run build && electron .",
|
"electron:dev": "npm run build && electron .",
|
||||||
@@ -181,7 +181,8 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist-electron/**/*",
|
"dist-electron/**/*",
|
||||||
"dist/**/*"
|
"dist/**/*",
|
||||||
|
"capacitor.config.json"
|
||||||
],
|
],
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const fse = require("fs-extra");
|
const fse = require("fs-extra");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
console.log("Starting Electron build finalization...");
|
console.log("Starting Electron build finalization...");
|
||||||
|
|
||||||
@@ -64,4 +65,21 @@ if (fs.existsSync(preloadSrc)) {
|
|||||||
console.error("Preload script not found at:", preloadSrc);
|
console.error("Preload script not found at:", preloadSrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy capacitor.config.json to dist-electron
|
||||||
|
try {
|
||||||
|
console.log("Copying capacitor.config.json to dist-electron...");
|
||||||
|
const configPath = path.join(process.cwd(), 'capacitor.config.json');
|
||||||
|
const targetPath = path.join(process.cwd(), 'dist-electron', 'capacitor.config.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
throw new Error('capacitor.config.json not found in project root');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.copyFileSync(configPath, targetPath);
|
||||||
|
console.log("Successfully copied capacitor.config.json");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to copy capacitor.config.json:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Electron index.html copied and patched for Electron context.");
|
console.log("Electron index.html copied and patched for Electron context.");
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { app, BrowserWindow, ipcMain } from "electron";
|
import { app, BrowserWindow, ipcMain } from "electron";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { CapacitorSQLiteElectron } from "@capacitor-community/sqlite/electron/dist/plugin";
|
import { Capacitor } from "@capacitor/core";
|
||||||
|
import { SQLiteConnection } from "@capacitor-community/sqlite";
|
||||||
|
|
||||||
// Simple logger implementation
|
// Simple logger implementation
|
||||||
const logger = {
|
const logger = {
|
||||||
@@ -17,12 +18,94 @@ const logger = {
|
|||||||
debug: (...args: unknown[]) => console.debug("[Main]", ...args),
|
debug: (...args: unknown[]) => console.debug("[Main]", ...args),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
logger.info("Starting main process initialization...");
|
||||||
|
|
||||||
|
// Initialize Capacitor for Electron in main process
|
||||||
|
try {
|
||||||
|
logger.info("About to initialize Capacitor...");
|
||||||
|
logger.info("Capacitor before init:", {
|
||||||
|
hasPlatform: 'platform' in Capacitor,
|
||||||
|
hasIsNativePlatform: 'isNativePlatform' in Capacitor,
|
||||||
|
platformType: typeof Capacitor.platform,
|
||||||
|
isNativePlatformType: typeof Capacitor.isNativePlatform
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try direct assignment first
|
||||||
|
try {
|
||||||
|
(Capacitor as any).platform = 'electron';
|
||||||
|
(Capacitor as any).isNativePlatform = true;
|
||||||
|
logger.info("Direct assignment successful");
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn("Direct assignment failed, trying defineProperty:", e);
|
||||||
|
Object.defineProperty(Capacitor, "isNativePlatform", {
|
||||||
|
get: () => true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(Capacitor, "platform", {
|
||||||
|
get: () => 'electron',
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Capacitor after init:", {
|
||||||
|
platform: Capacitor.platform,
|
||||||
|
isNativePlatform: Capacitor.isNativePlatform,
|
||||||
|
platformType: typeof Capacitor.platform,
|
||||||
|
isNativePlatformType: typeof Capacitor.isNativePlatform
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to initialize Capacitor:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize SQLite plugin for Electron
|
// Initialize SQLite plugin for Electron
|
||||||
let sqlitePlugin: CapacitorSQLiteElectron | null = null;
|
let sqliteConnection: SQLiteConnection | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info("Initializing SQLite plugin...");
|
logger.info("About to initialize SQLite plugin...");
|
||||||
sqlitePlugin = new CapacitorSQLiteElectron();
|
// Use require to ensure we get the CommonJS module
|
||||||
|
const sqliteModule = require('@capacitor-community/sqlite/electron/dist/plugin');
|
||||||
|
logger.info("SQLite module loaded:", Object.keys(sqliteModule));
|
||||||
|
|
||||||
|
// Debug the module contents
|
||||||
|
logger.info("SQLite module contents:", {
|
||||||
|
default: typeof sqliteModule.default,
|
||||||
|
defaultKeys: sqliteModule.default ? Object.keys(sqliteModule.default) : [],
|
||||||
|
CapacitorSQLite: typeof sqliteModule.CapacitorSQLite,
|
||||||
|
CapacitorSQLiteKeys: sqliteModule.CapacitorSQLite ? Object.keys(sqliteModule.CapacitorSQLite) : [],
|
||||||
|
moduleType: typeof sqliteModule,
|
||||||
|
moduleKeys: Object.keys(sqliteModule),
|
||||||
|
hasHandle: sqliteModule.default?.handle ? 'yes' : 'no',
|
||||||
|
handleType: typeof sqliteModule.default?.handle,
|
||||||
|
defaultProto: Object.getPrototypeOf(sqliteModule.default),
|
||||||
|
defaultProtoKeys: Object.getPrototypeOf(sqliteModule.default) ? Object.keys(Object.getPrototypeOf(sqliteModule.default)) : []
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the plugin constructor
|
||||||
|
const SQLitePlugin = sqliteModule.CapacitorSQLite;
|
||||||
|
logger.info("Got SQLite plugin constructor:", {
|
||||||
|
type: typeof SQLitePlugin,
|
||||||
|
name: SQLitePlugin?.name,
|
||||||
|
prototype: SQLitePlugin ? Object.getPrototypeOf(SQLitePlugin) : null,
|
||||||
|
prototypeKeys: SQLitePlugin ? Object.keys(Object.getPrototypeOf(SQLitePlugin)) : []
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof SQLitePlugin !== 'function') {
|
||||||
|
throw new Error(`SQLite plugin is not a constructor, got ${typeof SQLitePlugin}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a connection using the plugin
|
||||||
|
logger.info("Creating SQLite connection...");
|
||||||
|
sqliteConnection = new SQLiteConnection(SQLitePlugin);
|
||||||
|
|
||||||
|
logger.info("SQLite connection created:", {
|
||||||
|
type: typeof sqliteConnection,
|
||||||
|
keys: Object.keys(sqliteConnection),
|
||||||
|
prototype: Object.getPrototypeOf(sqliteConnection),
|
||||||
|
prototypeKeys: Object.getPrototypeOf(sqliteConnection) ? Object.keys(Object.getPrototypeOf(sqliteConnection)) : []
|
||||||
|
});
|
||||||
|
|
||||||
logger.info("SQLite plugin initialized successfully");
|
logger.info("SQLite plugin initialized successfully");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Failed to initialize SQLite plugin:", error);
|
logger.error("Failed to initialize SQLite plugin:", error);
|
||||||
@@ -31,15 +114,22 @@ try {
|
|||||||
|
|
||||||
// Set up IPC handler for SQLite
|
// Set up IPC handler for SQLite
|
||||||
ipcMain.handle("capacitor-sqlite", async (_event, ...args) => {
|
ipcMain.handle("capacitor-sqlite", async (_event, ...args) => {
|
||||||
if (!sqlitePlugin) {
|
if (!sqliteConnection) {
|
||||||
const error = new Error("SQLite plugin not initialized");
|
const error = new Error("SQLite connection not initialized");
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.debug("Handling SQLite request:", args);
|
logger.debug("Handling SQLite request:", args);
|
||||||
const result = await sqlitePlugin.handle(_event, ...args);
|
// The first argument should be the method name
|
||||||
|
const [method, ...restArgs] = args;
|
||||||
|
if (typeof method !== 'string' || !(method in sqliteConnection)) {
|
||||||
|
throw new Error(`Invalid SQLite method: ${method}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the method on the connection
|
||||||
|
const result = await (sqliteConnection as any)[method](...restArgs);
|
||||||
logger.debug("SQLite request completed successfully");
|
logger.debug("SQLite request completed successfully");
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createPinia } from "pinia";
|
|||||||
import { App as VueApp, ComponentPublicInstance, createApp } from "vue";
|
import { App as VueApp, ComponentPublicInstance, createApp } from "vue";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
|
// Use the browser version of axios for web builds
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import VueAxios from "vue-axios";
|
import VueAxios from "vue-axios";
|
||||||
import Notifications from "notiwind";
|
import Notifications from "notiwind";
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
import { initializeApp } from "./main.common";
|
import { initializeApp } from "./main.common";
|
||||||
import { logger } from "./utils/logger";
|
import { logger } from "./utils/logger";
|
||||||
import { Capacitor } from "@capacitor/core";
|
|
||||||
import { CapacitorSQLite } from "@capacitor-community/sqlite";
|
import { CapacitorSQLite } from "@capacitor-community/sqlite";
|
||||||
|
|
||||||
// Initialize Capacitor for Electron
|
|
||||||
Object.defineProperty(Capacitor, "isNativePlatform", {
|
|
||||||
get: () => true,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize SQLite plugin for Electron
|
// Initialize SQLite plugin for Electron
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
// Register the plugin globally
|
// Register the plugin globally
|
||||||
|
|||||||
@@ -1,120 +1,102 @@
|
|||||||
import { defineConfig, mergeConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { createBuildConfig } from "./vite.config.common.mts";
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export default defineConfig(async ({ mode }) => {
|
export default defineConfig({
|
||||||
const baseConfig = await createBuildConfig('electron');
|
build: {
|
||||||
const isWebBuild = mode === 'electron';
|
outDir: 'dist-electron',
|
||||||
|
rollupOptions: {
|
||||||
return mergeConfig(baseConfig, {
|
input: {
|
||||||
build: {
|
main: path.resolve(__dirname, 'src/electron/main.ts'),
|
||||||
outDir: isWebBuild ? 'dist' : 'dist-electron',
|
preload: path.resolve(__dirname, 'src/electron/preload.js'),
|
||||||
rollupOptions: isWebBuild ? {
|
|
||||||
// Web app build configuration
|
|
||||||
input: path.resolve(__dirname, 'index.html'),
|
|
||||||
output: {
|
|
||||||
format: 'esm',
|
|
||||||
entryFileNames: 'assets/[name]-[hash].js',
|
|
||||||
chunkFileNames: 'assets/[name]-[hash].js',
|
|
||||||
assetFileNames: 'assets/[name]-[hash].[ext]'
|
|
||||||
}
|
|
||||||
} : {
|
|
||||||
// Main process build configuration
|
|
||||||
input: {
|
|
||||||
main: path.resolve(__dirname, 'src/electron/main.ts'),
|
|
||||||
preload: path.resolve(__dirname, 'src/electron/preload.js'),
|
|
||||||
},
|
|
||||||
external: [
|
|
||||||
'electron',
|
|
||||||
'@capacitor-community/sqlite',
|
|
||||||
'@capacitor-community/sqlite/electron',
|
|
||||||
'better-sqlite3-multiple-ciphers'
|
|
||||||
],
|
|
||||||
output: {
|
|
||||||
format: 'cjs',
|
|
||||||
entryFileNames: '[name].js',
|
|
||||||
assetFileNames: 'assets/[name].[ext]',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
target: isWebBuild ? 'esnext' : 'node18',
|
external: [
|
||||||
minify: !isWebBuild,
|
// Node.js built-ins
|
||||||
sourcemap: true,
|
'stream',
|
||||||
},
|
'path',
|
||||||
resolve: {
|
'fs',
|
||||||
alias: {
|
'crypto',
|
||||||
'@': path.resolve(__dirname, 'src'),
|
'buffer',
|
||||||
|
'util',
|
||||||
|
'events',
|
||||||
|
'url',
|
||||||
|
'assert',
|
||||||
|
'os',
|
||||||
|
'net',
|
||||||
|
'http',
|
||||||
|
'https',
|
||||||
|
'zlib',
|
||||||
|
'child_process',
|
||||||
|
// Electron and Capacitor
|
||||||
|
'electron',
|
||||||
|
'@capacitor/core',
|
||||||
|
'@capacitor-community/sqlite',
|
||||||
|
'@capacitor-community/sqlite/electron',
|
||||||
|
'@capacitor-community/sqlite/electron/dist/plugin',
|
||||||
|
'better-sqlite3-multiple-ciphers',
|
||||||
|
// HTTP clients
|
||||||
|
'axios',
|
||||||
|
'axios/dist/axios',
|
||||||
|
'axios/dist/node/axios.cjs'
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
format: 'cjs',
|
||||||
|
entryFileNames: '[name].js',
|
||||||
|
assetFileNames: 'assets/[name].[ext]',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
target: 'node18',
|
||||||
include: ['@/utils/logger']
|
minify: false,
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, 'src'),
|
||||||
|
// Use Node.js version of axios in electron
|
||||||
|
'axios': 'axios/dist/node/axios.cjs'
|
||||||
},
|
},
|
||||||
plugins: [
|
},
|
||||||
{
|
optimizeDeps: {
|
||||||
name: 'typescript-transform',
|
exclude: [
|
||||||
transform(code: string, id: string) {
|
'stream',
|
||||||
if (id.endsWith('main.ts')) {
|
'path',
|
||||||
// Replace the logger import with inline logger
|
'fs',
|
||||||
return code.replace(
|
'crypto',
|
||||||
/import\s*{\s*logger\s*}\s*from\s*['"]@\/utils\/logger['"];?/,
|
'buffer',
|
||||||
`const logger = {
|
'util',
|
||||||
log: (...args) => console.log(...args),
|
'events',
|
||||||
error: (...args) => console.error(...args),
|
'url',
|
||||||
info: (...args) => console.info(...args),
|
'assert',
|
||||||
warn: (...args) => console.warn(...args),
|
'os',
|
||||||
debug: (...args) => console.debug(...args),
|
'net',
|
||||||
};`
|
'http',
|
||||||
);
|
'https',
|
||||||
}
|
'zlib',
|
||||||
return code;
|
'child_process',
|
||||||
}
|
'axios',
|
||||||
},
|
'axios/dist/axios',
|
||||||
{
|
'axios/dist/node/axios.cjs'
|
||||||
name: 'remove-sw-imports',
|
]
|
||||||
transform(code: string, id: string) {
|
},
|
||||||
// Remove service worker imports and registrations
|
plugins: [
|
||||||
if (id.includes('registerServiceWorker') ||
|
{
|
||||||
id.includes('register-service-worker') ||
|
name: 'typescript-transform',
|
||||||
id.includes('sw_scripts') ||
|
transform(code: string, id: string) {
|
||||||
id.includes('PushNotificationPermission') ||
|
if (id.endsWith('main.ts')) {
|
||||||
code.includes('navigator.serviceWorker')) {
|
return code.replace(
|
||||||
return {
|
/import\s*{\s*logger\s*}\s*from\s*['"]@\/utils\/logger['"];?/,
|
||||||
code: code
|
`const logger = {
|
||||||
.replace(/import.*registerServiceWorker.*$/mg, '')
|
log: (...args) => console.log(...args),
|
||||||
.replace(/import.*register-service-worker.*$/mg, '')
|
error: (...args) => console.error(...args),
|
||||||
.replace(/navigator\.serviceWorker/g, 'undefined')
|
info: (...args) => console.info(...args),
|
||||||
.replace(/if\s*\([^)]*serviceWorker[^)]*\)\s*{[^}]*}/g, '')
|
warn: (...args) => console.warn(...args),
|
||||||
.replace(/import.*workbox.*$/mg, '')
|
debug: (...args) => console.debug(...args),
|
||||||
.replace(/importScripts\([^)]*\)/g, '')
|
};`
|
||||||
};
|
);
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'remove-sw-files',
|
|
||||||
enforce: 'pre',
|
|
||||||
resolveId(id: string) {
|
|
||||||
// Prevent service worker files from being included
|
|
||||||
if (id.includes('sw.js') ||
|
|
||||||
id.includes('workbox') ||
|
|
||||||
id.includes('registerSW.js') ||
|
|
||||||
id.includes('manifest.webmanifest')) {
|
|
||||||
return '\0empty';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
load(id: string) {
|
|
||||||
if (id === '\0empty') {
|
|
||||||
return 'export default {}';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
ssr: {
|
],
|
||||||
noExternal: ['@/utils/logger']
|
base: './',
|
||||||
},
|
publicDir: 'public',
|
||||||
base: './',
|
|
||||||
publicDir: 'public',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user