You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
5.0 KiB
220 lines
5.0 KiB
/**
|
|
* 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
|
|
*/
|
|
|
|
import { logger } from "./utils/logger";
|
|
|
|
/**
|
|
* 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: AbsurdSqlDatabaseService } = await import(
|
|
"./services/AbsurdSqlDatabaseService"
|
|
);
|
|
// Get the singleton instance (only created when needed)
|
|
databaseService = AbsurdSqlDatabaseService.getInstance();
|
|
}
|
|
return databaseService;
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
const dbService = await getDatabaseService();
|
|
await dbService.initialize();
|
|
isInitialized = true;
|
|
} catch (error) {
|
|
logger.error("[SQLWorker] Database initialization failed:", error);
|
|
isInitialized = false;
|
|
initializationPromise = null;
|
|
throw error;
|
|
}
|
|
})();
|
|
|
|
return initializationPromise;
|
|
}
|
|
|
|
/**
|
|
* Handle database query operations
|
|
*/
|
|
async function handleQuery(id, sql, params = []) {
|
|
try {
|
|
await initializeDatabase();
|
|
|
|
const dbService = await getDatabaseService();
|
|
const result = await dbService.query(sql, params);
|
|
|
|
sendResponse(id, "success", { result });
|
|
} catch (error) {
|
|
logger.error(`[SQLWorker] Query failed:`, error);
|
|
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();
|
|
|
|
const dbService = await getDatabaseService();
|
|
const result = await dbService.run(sql, params);
|
|
|
|
sendResponse(id, "success", result);
|
|
} catch (error) {
|
|
logger.error(`[SQLWorker] Statement execution failed:`, error);
|
|
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();
|
|
|
|
const dbService = await getDatabaseService();
|
|
const result = await dbService.query(sql, params);
|
|
const oneRow = result?.[0]?.values?.[0];
|
|
|
|
sendResponse(id, "success", oneRow);
|
|
} catch (error) {
|
|
logger.error(`[SQLWorker] GetOneRow failed:`, error);
|
|
sendResponse(id, "error", null, {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle initialization request
|
|
*/
|
|
async function handleInit(id) {
|
|
try {
|
|
await initializeDatabase();
|
|
sendResponse(id, "init-complete");
|
|
} catch (error) {
|
|
logger.error("[SQLWorker] Initialization request failed:", error);
|
|
sendResponse(id, "error", null, {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle ping request for health check
|
|
*/
|
|
function handlePing(id) {
|
|
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);
|
|
return;
|
|
}
|
|
|
|
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}`);
|
|
sendResponse(id, "error", null, {
|
|
message: `Unknown message type: ${type}`,
|
|
});
|
|
break;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle worker errors
|
|
*/
|
|
onerror = function (error) {
|
|
logger.error("[SQLWorker] Worker error:", error);
|
|
};
|
|
|
|
/**
|
|
* Auto-initialize on worker startup (removed to prevent circular dependency)
|
|
* Initialization now happens on first database operation
|
|
*/
|
|
// Use console for critical startup message to avoid circular dependency
|
|
// eslint-disable-next-line no-console
|
|
console.log("[SQLWorker] Worker loaded, ready to receive messages");
|
|
|