Switch SPA to bundled sql.js runtime
This commit is contained in:
parent
944dcc643d
commit
e83de10d93
3 changed files with 15 additions and 50 deletions
|
@ -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.
|
||||
|
|
|
@ -365,8 +365,8 @@
|
|||
<!-- Third-party libs loaded from CDNs (no trackers). Pinned versions. -->
|
||||
<!-- fflate: tiny ZIP library used to unzip the downloaded DB archive client-side. -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/fflate@0.8.2/umd/index.js" crossorigin="anonymous"></script>
|
||||
<!-- SQLite WASM build (oo1 API) shipped locally to ensure FTS5 support. -->
|
||||
<script src="./vendor/sqlite3/sqlite3.js"></script>
|
||||
<!-- sql.js build (FTS5-enabled) shipped locally to ensure search support. -->
|
||||
<script src="./sql-wasm.js"></script>
|
||||
|
||||
<!-- App logic -->
|
||||
<script src="./script.js"></script>
|
||||
|
|
59
script.js
59
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return createSqlCompat(SQL);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue