forked from jsnbuchanan/crowd-funder-for-time-pwa
Fix worker-only database architecture and Vue Proxy serialization
- Implement worker-only database access to eliminate double migrations - Add parameter serialization in usePlatformService to prevent Capacitor "object could not be cloned" errors - Fix infinite logging loop with circuit breaker in databaseUtil - Use dynamic imports in WebPlatformService to prevent worker thread errors - Add higher-level database methods (getContacts, getSettings) to composable - Eliminate Vue Proxy objects through JSON serialization and Object.freeze protection Resolves Proxy(Array) serialization failures and worker context conflicts across Web/Capacitor/Electron platforms.
This commit is contained in:
@@ -1,6 +1,236 @@
|
||||
import databaseService from "./services/AbsurdSqlDatabaseService";
|
||||
/**
|
||||
* SQL Worker Thread Handler for TimeSafari Web Platform
|
||||
*
|
||||
* This worker handles all database operations for the web platform,
|
||||
* ensuring single-threaded database access and preventing double migrations.
|
||||
*
|
||||
* Architecture:
|
||||
* - Main thread sends messages to this worker
|
||||
* - Worker initializes database once and handles all SQL operations
|
||||
* - Results are sent back to main thread via postMessage
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
* @since 2025-07-02
|
||||
*/
|
||||
|
||||
async function run() {
|
||||
await databaseService.initialize();
|
||||
// import { logger } from "./utils/logger"; // DISABLED FOR DEBUGGING
|
||||
|
||||
/**
|
||||
* Worker state management
|
||||
*/
|
||||
let isInitialized = false;
|
||||
let initializationPromise = null;
|
||||
let databaseService = null;
|
||||
|
||||
/**
|
||||
* Lazy load database service to prevent circular dependencies
|
||||
*/
|
||||
async function getDatabaseService() {
|
||||
if (!databaseService) {
|
||||
// Dynamic import to prevent circular dependency
|
||||
const { default: service } = await import("./services/AbsurdSqlDatabaseService");
|
||||
databaseService = service;
|
||||
}
|
||||
return databaseService;
|
||||
}
|
||||
run();
|
||||
|
||||
/**
|
||||
* Send response back to main thread
|
||||
*/
|
||||
function sendResponse(id, type, data = null, error = null) {
|
||||
const response = {
|
||||
id,
|
||||
type,
|
||||
...(data && { data }),
|
||||
...(error && { error }),
|
||||
};
|
||||
postMessage(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize database service
|
||||
*/
|
||||
async function initializeDatabase() {
|
||||
if (isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (initializationPromise) {
|
||||
return initializationPromise;
|
||||
}
|
||||
|
||||
initializationPromise = (async () => {
|
||||
try {
|
||||
// logger.log("[SQLWorker] Starting database initialization..."); // DISABLED
|
||||
const dbService = await getDatabaseService();
|
||||
await dbService.initialize();
|
||||
isInitialized = true;
|
||||
// logger.log("[SQLWorker] Database initialization completed successfully"); // DISABLED
|
||||
} catch (error) {
|
||||
// logger.error("[SQLWorker] Database initialization failed:", error); // DISABLED
|
||||
console.error("[SQLWorker] Database initialization failed:", error); // Keep only critical errors
|
||||
isInitialized = false;
|
||||
initializationPromise = null;
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
|
||||
return initializationPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle database query operations
|
||||
*/
|
||||
async function handleQuery(id, sql, params = []) {
|
||||
try {
|
||||
await initializeDatabase();
|
||||
// logger.log(`[SQLWorker] Executing query: ${sql}`, params); // DISABLED
|
||||
|
||||
const dbService = await getDatabaseService();
|
||||
const result = await dbService.query(sql, params);
|
||||
// logger.log(`[SQLWorker] Query completed successfully`); // DISABLED
|
||||
|
||||
sendResponse(id, "success", { result });
|
||||
} catch (error) {
|
||||
// logger.error(`[SQLWorker] Query failed:`, error); // DISABLED
|
||||
console.error(`[SQLWorker] Query failed:`, error); // Keep only critical errors
|
||||
sendResponse(id, "error", null, {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle database execution operations (INSERT, UPDATE, DELETE)
|
||||
*/
|
||||
async function handleExec(id, sql, params = []) {
|
||||
try {
|
||||
await initializeDatabase();
|
||||
// logger.log(`[SQLWorker] Executing statement: ${sql}`, params); // DISABLED
|
||||
|
||||
const dbService = await getDatabaseService();
|
||||
const result = await dbService.run(sql, params);
|
||||
// logger.log(`[SQLWorker] Statement executed successfully:`, result); // DISABLED
|
||||
|
||||
sendResponse(id, "success", result);
|
||||
} catch (error) {
|
||||
// logger.error(`[SQLWorker] Statement execution failed:`, error); // DISABLED
|
||||
console.error(`[SQLWorker] Statement execution failed:`, error); // Keep only critical errors
|
||||
sendResponse(id, "error", null, {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle database get one row operations
|
||||
*/
|
||||
async function handleGetOneRow(id, sql, params = []) {
|
||||
try {
|
||||
await initializeDatabase();
|
||||
// logger.log(`[SQLWorker] Executing getOneRow: ${sql}`, params); // DISABLED
|
||||
|
||||
const dbService = await getDatabaseService();
|
||||
const result = await dbService.query(sql, params);
|
||||
const oneRow = result?.[0]?.values?.[0];
|
||||
// logger.log(`[SQLWorker] GetOneRow completed successfully`); // DISABLED
|
||||
|
||||
sendResponse(id, "success", oneRow);
|
||||
} catch (error) {
|
||||
// logger.error(`[SQLWorker] GetOneRow failed:`, error); // DISABLED
|
||||
console.error(`[SQLWorker] GetOneRow failed:`, error); // Keep only critical errors
|
||||
sendResponse(id, "error", null, {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle initialization request
|
||||
*/
|
||||
async function handleInit(id) {
|
||||
try {
|
||||
await initializeDatabase();
|
||||
// logger.log("[SQLWorker] Initialization request completed"); // DISABLED
|
||||
sendResponse(id, "init-complete");
|
||||
} catch (error) {
|
||||
// logger.error("[SQLWorker] Initialization request failed:", error); // DISABLED
|
||||
console.error("[SQLWorker] Initialization request failed:", error); // Keep only critical errors
|
||||
sendResponse(id, "error", null, {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ping request for health check
|
||||
*/
|
||||
function handlePing(id) {
|
||||
// logger.log("[SQLWorker] Ping received"); // DISABLED
|
||||
sendResponse(id, "pong");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main message handler
|
||||
*/
|
||||
onmessage = function (event) {
|
||||
const { id, type, sql, params } = event.data;
|
||||
|
||||
if (!id || !type) {
|
||||
// logger.error("[SQLWorker] Invalid message received:", event.data); // DISABLED
|
||||
console.error("[SQLWorker] Invalid message received:", event.data);
|
||||
return;
|
||||
}
|
||||
|
||||
// logger.log(`[SQLWorker] Received message: ${type} (${id})`); // DISABLED
|
||||
|
||||
switch (type) {
|
||||
case "query":
|
||||
handleQuery(id, sql, params);
|
||||
break;
|
||||
|
||||
case "exec":
|
||||
handleExec(id, sql, params);
|
||||
break;
|
||||
|
||||
case "getOneRow":
|
||||
handleGetOneRow(id, sql, params);
|
||||
break;
|
||||
|
||||
case "init":
|
||||
handleInit(id);
|
||||
break;
|
||||
|
||||
case "ping":
|
||||
handlePing(id);
|
||||
break;
|
||||
|
||||
default:
|
||||
// logger.error(`[SQLWorker] Unknown message type: ${type}`); // DISABLED
|
||||
console.error(`[SQLWorker] Unknown message type: ${type}`);
|
||||
sendResponse(id, "error", null, {
|
||||
message: `Unknown message type: ${type}`,
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle worker errors
|
||||
*/
|
||||
onerror = function (error) {
|
||||
// logger.error("[SQLWorker] Worker error:", error); // DISABLED
|
||||
console.error("[SQLWorker] Worker error:", error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Auto-initialize on worker startup (removed to prevent circular dependency)
|
||||
* Initialization now happens on first database operation
|
||||
*/
|
||||
// logger.log("[SQLWorker] Worker loaded, ready to receive messages"); // DISABLED
|
||||
console.log("[SQLWorker] Worker loaded, ready to receive messages");
|
||||
|
||||
Reference in New Issue
Block a user