forked from jsnbuchanan/crowd-funder-for-time-pwa
- Replace PlatformServiceFactory with PlatformServiceMixin methods - Extract button styling classes to computed properties - Add comprehensive documentation and error handling - 87% faster than estimated migration time
256 lines
6.1 KiB
JavaScript
256 lines
6.1 KiB
JavaScript
// **WORKER-COMPATIBLE ENVIRONMENT SETUP**: Must be at the very top
|
|
// This prevents "window is not defined" errors when sql.js tries to access window.crypto
|
|
if (typeof window === "undefined") {
|
|
// We're in a worker context - provide minimal polyfills
|
|
globalThis.window = globalThis;
|
|
|
|
// Enhanced crypto polyfill
|
|
if (typeof crypto === "undefined") {
|
|
globalThis.crypto = {
|
|
getRandomValues: (array) => {
|
|
// Simple fallback for worker context
|
|
for (let i = 0; i < array.length; i++) {
|
|
array[i] = Math.floor(Math.random() * 256);
|
|
}
|
|
return array;
|
|
},
|
|
subtle: {
|
|
generateKey: async () => ({ type: "secret" }),
|
|
sign: async () => new Uint8Array(32),
|
|
verify: async () => true,
|
|
digest: async () => new Uint8Array(32),
|
|
},
|
|
};
|
|
} else if (!crypto.getRandomValues) {
|
|
// Crypto exists but doesn't have getRandomValues - extend it
|
|
crypto.getRandomValues = (array) => {
|
|
// Simple fallback for worker context
|
|
for (let i = 0; i < array.length; i++) {
|
|
array[i] = Math.floor(Math.random() * 256);
|
|
}
|
|
return array;
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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");
|