diff --git a/examples/folio/app/webApp/src/wasmJsMain/resources/sqlite.worker.js b/examples/folio/app/webApp/src/wasmJsMain/resources/sqlite.worker.js new file mode 100644 index 0000000..8cba41c --- /dev/null +++ b/examples/folio/app/webApp/src/wasmJsMain/resources/sqlite.worker.js @@ -0,0 +1,71 @@ +import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; + +let db = null; +let isPersistent = false; + +async function init() { + const sqlite3 = await sqlite3InitModule({ + print: (msg) => console.log('[sqlite]', msg), + printErr: (msg) => console.error('[sqlite]', msg), + }); + + try { + const pool = await sqlite3.installOpfsSAHPoolVfs({ + directory: '/folio-sqlite-pool', + clearOnInit: false, + initialCapacity: 6, + }); + db = new pool.OpfsSAHPoolDb('/folio.sqlite3'); + isPersistent = true; + console.log('[sqlite] using OPFS SAH pool'); + } catch (sahErr) { + console.warn('[sqlite] OPFS SAH pool unavailable, falling back to in-memory:', sahErr?.message ?? sahErr); + db = new sqlite3.oo1.DB(':memory:', 'ct'); + isPersistent = false; + } +} + +function execQuery(sql, params) { + const rows = db.exec({ + sql, + bind: params, + returnValue: 'resultRows', + }); + return { values: rows }; +} + +function dispatch(data) { + switch (data && data.action) { + case 'exec': + if (!data.sql) throw new Error('exec: missing sql'); + return execQuery(data.sql, data.params); + case 'begin_transaction': + db.exec('BEGIN TRANSACTION;'); + return { values: [] }; + case 'end_transaction': + db.exec('COMMIT;'); + return { values: [] }; + case 'rollback_transaction': + db.exec('ROLLBACK;'); + return { values: [] }; + default: + throw new Error('Unsupported action: ' + (data && data.action)); + } +} + +const ready = init(); + +self.onmessage = (event) => { + const data = event.data; + ready + .then(() => { + const results = dispatch(data); + self.postMessage({ id: data.id, results }); + }) + .catch((err) => { + const message = err && err.message ? String(err.message) : String(err); + self.postMessage({ id: data && data.id, error: { message, name: err && err.name } }); + }); +}; + +self.postMessage({ ready: true, persistent: isPersistent }); diff --git a/examples/folio/app/webApp/webpack.config.d/coopcoep.js b/examples/folio/app/webApp/webpack.config.d/coopcoep.js new file mode 100644 index 0000000..2fd5b5f --- /dev/null +++ b/examples/folio/app/webApp/webpack.config.d/coopcoep.js @@ -0,0 +1,9 @@ +config.devServer = { + ...(config.devServer || {}), + headers: { + ...((config.devServer && config.devServer.headers) || {}), + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Resource-Policy': 'cross-origin', + }, +}; diff --git a/examples/folio/app/webApp/webpack.config.d/sqlite-wasm.js b/examples/folio/app/webApp/webpack.config.d/sqlite-wasm.js new file mode 100644 index 0000000..412eaac --- /dev/null +++ b/examples/folio/app/webApp/webpack.config.d/sqlite-wasm.js @@ -0,0 +1,9 @@ +// Webpack picks up `new Worker(new URL("./sqlite.worker.js", import.meta.url), +// { type: "module" })` from the compiled Kotlin/wasm output and bundles the +// worker. The bundled worker imports `@sqlite.org/sqlite-wasm`, which in turn +// loads sqlite3.wasm via `new URL("sqlite3.wasm", import.meta.url)`. Enabling +// asyncWebAssembly lets webpack process that .wasm reference correctly. +config.experiments = { + ...(config.experiments || {}), + asyncWebAssembly: true, +};