From e83de10d939e0dc1fe9ded152d73a35f48a66038 Mon Sep 17 00:00:00 2001 From: wagesj45 Date: Thu, 18 Sep 2025 22:03:36 -0500 Subject: [PATCH] Switch SPA to bundled sql.js runtime --- README.md | 2 +- index.html | 4 ++-- script.js | 59 +++++++++++------------------------------------------- 3 files changed, 15 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 6aa6b1a..7fa1fc8 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ A self contained static website for browsing the collection of salvaged mp3.com ## Runtime Notes -- Uses the official `sqlite3` WASM build (vendored under `vendor/sqlite3/`) so that FTS5 is available client-side. +- Uses the `sql.js` WASM build (bundled as `sql-wasm.js`/`sql-wasm.wasm`) so that FTS5 is available client-side. diff --git a/index.html b/index.html index 49317a6..03aa598 100644 --- a/index.html +++ b/index.html @@ -365,8 +365,8 @@ - - + + diff --git a/script.js b/script.js index cf796aa..8898ded 100644 --- a/script.js +++ b/script.js @@ -2,13 +2,13 @@ Application bootstrap for the static, client-side metadata browser. - Downloads a zipped SQLite DB (db.zip) with progress and unzips it in-memory - Caches the uncompressed DB bytes in IndexedDB for reuse on next load - - Loads the official sqlite3 WASM build (oo1 API) and opens the database from the cached bytes + - Loads the locally bundled sql.js WASM build (FTS5-enabled) and opens the database from the cached bytes - Swaps interchangeable UX elements (views) in a viewport, with fade transitions Assumptions: - A ZIP archive is hosted at `/db.zip` containing a single `.sqlite` file. - We use `fflate` (UMD) from a CDN to unzip after download (keeps implementation minimal). - - We ship the sqlite3 WASM runtime locally to guarantee FTS5 support. + - We ship the sql.js WASM runtime locally to guarantee FTS5 support. - We store raw DB bytes in IndexedDB (localStorage is too small for large DBs). */ @@ -299,62 +299,30 @@ // --- SQLite WASM init --- async function initSql() { - if (typeof sqlite3InitModule !== 'function') throw new Error('sqlite3.js not loaded'); + if (typeof initSqlJs !== 'function') throw new Error('sql.js runtime not loaded'); loader.setStep('Initializing SQLite…'); loader.setDetail('Loading WASM'); - const sqlite3 = await sqlite3InitModule({ - locateFile: (file) => `./vendor/sqlite3/${file}`, + const SQL = await initSqlJs({ + locateFile: (file) => `./${file}`, }); - return createSqlCompat(sqlite3); + return createSqlCompat(SQL); } - function createSqlCompat(sqlite3) { - const { capi, wasm } = sqlite3; - const SQLITE_DESERIALIZE_FREEONCLOSE = 0x01; - const SQLITE_DESERIALIZE_READONLY = 0x04; - - function loadDatabaseBytes(dbHandle, bytes) { - if (!bytes || !bytes.byteLength) return; - const byteLength = bytes.byteLength; - const pData = wasm.alloc(byteLength); - try { - wasm.heap8u().set(bytes, pData); - const [pSchema] = wasm.allocCString('main', true); - try { - const rc = capi.sqlite3_deserialize( - dbHandle.pointer, - pSchema, - pData, - byteLength, - byteLength, - SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_READONLY - ); - if (rc !== capi.SQLITE_OK) { - throw new sqlite3.SQLite3Error(`sqlite3_deserialize failed with rc=${rc}`); - } - } finally { - wasm.dealloc(pSchema); - } - } catch (err) { - wasm.dealloc(pData); - throw err; - } - } - + function createSqlCompat(SQL) { class Statement { constructor(stmt) { this._stmt = stmt; this._columnNames = null; } bind(values) { - this._stmt.bind(values); + if (values !== undefined) this._stmt.bind(values); return true; } step() { return !!this._stmt.step(); } getAsObject() { - if (!this._columnNames) this._columnNames = this._stmt.getColumnNames([]); + if (!this._columnNames) this._columnNames = this._stmt.getColumnNames(); const row = Object.create(null); const count = this._columnNames.length; for (let i = 0; i < count; i += 1) { @@ -363,14 +331,14 @@ return row; } free() { - this._stmt.finalize(); + this._stmt.free(); } } class Database { constructor(bytes) { - this._db = new sqlite3.oo1.DB(); - if (bytes) loadDatabaseBytes(this._db, bytes); + const source = bytes instanceof Uint8Array ? bytes : bytes ? new Uint8Array(bytes) : undefined; + this._db = source ? new SQL.Database(source) : new SQL.Database(); } prepare(sql) { return new Statement(this._db.prepare(sql)); @@ -381,9 +349,6 @@ close() { this._db.close(); } - get pointer() { - return this._db.pointer; - } } function ensureFts5Available(dbHandle) {