diff --git a/frontend/core/.gitkeep b/frontend/core/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt b/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt index ac699802..1f77cc39 100644 --- a/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt +++ b/frontend/core/local-db/src/jsMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.js.kt @@ -25,7 +25,7 @@ actual class DatabaseDriverFactory { setVersion(driver, schemaVersion) console.log("Database Schema created and version set to $schemaVersion") } catch (e: Throwable) { - // If tables already exist but version was 0 (e.g. previous broken run), we might get here. + // If tables already exist but a version was 0 (e.g., previous broken run), we might get here. val msg = e.message ?: "" if (msg.contains("already exists", ignoreCase = true)) { console.warn("Tables already exist but version was 0. Assuming DB is initialized. Setting version to $schemaVersion.") @@ -58,7 +58,7 @@ actual class DatabaseDriverFactory { var cursorRef: SqlCursor? = null // executeQuery returns QueryResult because mapper returns QueryResult - val hasNext = driver.executeQuery( + val hasNext = driver.executeQuery( identifier = null, sql = "PRAGMA user_version;", mapper = { cursor -> diff --git a/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js b/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js index e429b4b5..88275dd9 100644 --- a/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js +++ b/frontend/core/local-db/src/jsMain/resources/sqlite.worker.js @@ -2,121 +2,121 @@ console.log("Worker: sqlite.worker.js loaded. Starting initialization..."); try { - // We do NOT import from node_modules anymore to avoid Webpack bundling issues. - // import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; + // We do NOT import from node_modules anymore to avoid Webpack bundling issues. + // import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; - // Message buffer for messages arriving before DB is ready - let messageQueue = []; - let db = null; - let isReady = false; + // Message buffer for messages arriving before DB is ready + let messageQueue = []; + let db = null; + let isReady = false; - // Minimal worker protocol compatible with SQLDelight's `web-worker-driver`. - self.onmessage = (event) => { - if (!isReady) { - console.log("Worker: Buffering message (DB not ready)", event.data); - messageQueue.push(event); - return; - } - processMessage(event); - }; - - function processMessage(event) { - const data = event.data; - try { - switch (data && data.action) { - case 'exec': { - if (!data.sql) throw new Error('exec: Missing query string'); - const rows = []; - db.exec({ - sql: data.sql, - bind: data.params ?? [], - rowMode: 'array', - callback: (row) => rows.push(row) - }); - return postMessage({ id: data.id, results: { values: rows } }); - } - case 'begin_transaction': - db.exec('BEGIN TRANSACTION;'); - return postMessage({ id: data.id, results: [] }); - case 'end_transaction': - db.exec('END TRANSACTION;'); - return postMessage({ id: data.id, results: [] }); - case 'rollback_transaction': - db.exec('ROLLBACK TRANSACTION;'); - return postMessage({ id: data.id, results: [] }); - default: - throw new Error(`Unsupported action: ${data && data.action}`); - } - } catch (err) { - console.error("Worker: Error processing message", err); - return postMessage({ id: data && data.id, error: err?.message ?? String(err) }); - } + // Minimal worker protocol compatible with SQLDelight's `web-worker-driver`. + self.onmessage = (event) => { + if (!isReady) { + console.log("Worker: Buffering message (DB not ready)", event.data); + messageQueue.push(event); + return; } + processMessage(event); + }; - self.onerror = function(event) { - console.error("Error in Web Worker (onerror):", event.message, event.filename, event.lineno); - // Don't postMessage here as it might confuse the driver if it expects a response to a query - }; - - async function init() { - try { - // 1. Load the sqlite3.js library manually via importScripts. - console.log("Worker: Loading sqlite3.js via importScripts..."); - try { - importScripts('sqlite3.js'); - } catch (e) { - throw new Error("Failed to importScripts('sqlite3.js'). Check if file exists at root. Error: " + e.message); - } - - if (typeof self.sqlite3InitModule !== 'function') { - throw new Error("sqlite3InitModule is not defined after importScripts. Check if sqlite3.js was loaded correctly."); - } - - console.log("Worker: Fetching sqlite3.wasm manually..."); - const response = await fetch('sqlite3.wasm'); - if (!response.ok) { - throw new Error(`Failed to fetch sqlite3.wasm: ${response.status} ${response.statusText}`); - } - const wasmBinary = await response.arrayBuffer(); - console.log("Worker: sqlite3.wasm fetched successfully, size:", wasmBinary.byteLength); - - console.log("Worker: Calling sqlite3InitModule with wasmBinary..."); - const sqlite3 = await self.sqlite3InitModule({ - print: console.log, - printErr: console.error, - wasmBinary: wasmBinary - }); - - console.log("Worker: sqlite3InitModule resolved successfully"); - const opfsAvailable = 'opfs' in sqlite3; - console.log("Worker: OPFS available:", opfsAvailable); - - // Initialize DB - const dbName = 'app.db'; - if (opfsAvailable) { - console.log("Initialisiere persistente OPFS Datenbank: " + dbName); - db = new sqlite3.oo1.OpfsDb(dbName); - } else { - console.warn("OPFS nicht verfügbar, Fallback auf In-Memory"); - db = new sqlite3.oo1.DB(dbName); - } - - // Mark as ready and process queue - isReady = true; - console.log("Worker: DB Ready. Processing " + messageQueue.length + " buffered messages."); - while (messageQueue.length > 0) { - processMessage(messageQueue.shift()); - } - - } catch (e) { - console.error("Database initialization error in worker:", e); - // We can't easily communicate this back to the driver during init, - // but console.error should show up. + function processMessage(event) { + const data = event.data; + try { + switch (data && data.action) { + case 'exec': { + if (!data.sql) throw new Error('exec: Missing query string'); + const rows = []; + db.exec({ + sql: data.sql, + bind: data.params ?? [], + rowMode: 'array', + callback: (row) => rows.push(row) + }); + return postMessage({id: data.id, results: {values: rows}}); } + case 'begin_transaction': + db.exec('BEGIN TRANSACTION;'); + return postMessage({id: data.id, results: []}); + case 'end_transaction': + db.exec('END TRANSACTION;'); + return postMessage({id: data.id, results: []}); + case 'rollback_transaction': + db.exec('ROLLBACK TRANSACTION;'); + return postMessage({id: data.id, results: []}); + default: + throw new Error(`Unsupported action: ${data && data.action}`); + } + } catch (err) { + console.error("Worker: Error processing message", err); + return postMessage({id: data && data.id, error: err?.message ?? String(err)}); } + } - init(); + self.onerror = function (event) { + console.error("Error in Web Worker (onerror):", event.message, event.filename, event.lineno); + // Don't postMessage here as it might confuse the driver if it expects a response to a query + }; + + async function init() { + try { + // 1. Load the sqlite3.js library manually via importScripts. + console.log("Worker: Loading sqlite3.js via importScripts..."); + try { + importScripts('sqlite3.js'); + } catch (e) { + throw new Error("Failed to importScripts('sqlite3.js'). Check if file exists at root. Error: " + e.message); + } + + if (typeof self.sqlite3InitModule !== 'function') { + throw new Error("sqlite3InitModule is not defined after importScripts. Check if sqlite3.js was loaded correctly."); + } + + console.log("Worker: Fetching sqlite3.wasm manually..."); + const response = await fetch('sqlite3.wasm'); + if (!response.ok) { + throw new Error(`Failed to fetch sqlite3.wasm: ${response.status} ${response.statusText}`); + } + const wasmBinary = await response.arrayBuffer(); + console.log("Worker: sqlite3.wasm fetched successfully, size:", wasmBinary.byteLength); + + console.log("Worker: Calling sqlite3InitModule with wasmBinary..."); + const sqlite3 = await self.sqlite3InitModule({ + print: console.log, + printErr: console.error, + wasmBinary: wasmBinary + }); + + console.log("Worker: sqlite3InitModule resolved successfully"); + const opfsAvailable = 'opfs' in sqlite3; + console.log("Worker: OPFS available:", opfsAvailable); + + // Initialize DB + const dbName = 'app.db'; + if (opfsAvailable) { + console.log("Initialisiere persistente OPFS Datenbank: " + dbName); + db = new sqlite3.oo1.OpfsDb(dbName); + } else { + console.warn("OPFS nicht verfügbar, Fallback auf In-Memory"); + db = new sqlite3.oo1.DB(dbName); + } + + // Mark as ready and process queue + isReady = true; + console.log("Worker: DB Ready. Processing " + messageQueue.length + " buffered messages."); + while (messageQueue.length > 0) { + processMessage(messageQueue.shift()); + } + + } catch (e) { + console.error("Database initialization error in worker:", e); + // We can't easily communicate this back to the driver during init, + // but console.error should show up. + } + } + + init(); } catch (e) { - console.error("Critical Worker Error:", e); + console.error("Critical Worker Error:", e); } diff --git a/frontend/core/local-db/src/jvmMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.jvm.kt b/frontend/core/local-db/src/jvmMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.jvm.kt index 01893f14..1e9682d9 100644 --- a/frontend/core/local-db/src/jvmMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.jvm.kt +++ b/frontend/core/local-db/src/jvmMain/kotlin/at/mocode/frontend/core/localdb/DatabaseDriverFactory.jvm.kt @@ -16,15 +16,15 @@ actual class DatabaseDriverFactory { // Schema creation/migration needs to be handled carefully. // For now, we just create it if it doesn't exist. - // In a real app, we'd check version and migrate. - // Since generateAsync=true, the Schema.create signature might be suspend or return AsyncResult. + // In a real app, we'd check the version and migrate. + // Since generateAsync=true, the Schema.create signature might be suspended or return AsyncResult. // However, JdbcSqliteDriver is synchronous. We might need to wrap or await. - // But wait! Schema.create(driver) returns void or Unit usually. + // But wait! Schema.create(driver) usually returns void or Unit. // Let's check the generated code later. For now, we assume standard behavior. try { AppDatabase.Schema.create(driver).await() - } catch (e: Exception) { + } catch (_: Exception) { // Schema might already exist. // SQLDelight doesn't have "createIfNotExists" built-in easily without version check. // We'll leave this simple for now and refine with proper migration logic later. diff --git a/frontend/core/sync/src/commonMain/kotlin/at/mocode/frontend/core/sync/SyncCore.kt b/frontend/core/sync/src/commonMain/kotlin/at/mocode/frontend/core/sync/SyncCore.kt index 01a2a092..92f84d71 100644 --- a/frontend/core/sync/src/commonMain/kotlin/at/mocode/frontend/core/sync/SyncCore.kt +++ b/frontend/core/sync/src/commonMain/kotlin/at/mocode/frontend/core/sync/SyncCore.kt @@ -12,9 +12,7 @@ import io.ktor.client.request.parameter interface SyncableRepository { /** * Cursor für Delta-Sync. - * * Konvention: UUIDv7 als String (Backend kann `>` vergleichen) oder ein kompatibler Cursor. - * * @return letzter bekannter Cursor lokal oder `null`, wenn noch keine Daten existieren. */ suspend fun getLatestSince(): String?