fix: wa-sqlite initialisation fix (#210)

* fix: wa-sqlite initialisation fix

* chore: adding changeset

* fix: passing wasm between workers to reduce bundle size

* fix: fixing issue with vault names
This commit is contained in:
Kacper 2026-05-04 10:51:44 +01:00 committed by GitHub
parent 6d4dcc5ece
commit 42a6541650
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 32 additions and 57 deletions

View file

@ -0,0 +1,5 @@
---
"sqlseal": patch
---
fixing issue with new wa-sqlite on mobile

View file

@ -98,31 +98,8 @@ const workerPlugin = {
}));
build.onLoad({ filter: /.*/, namespace: 'sqlocal-worker-code' }, async () => {
// Create a plugin for the worker that resolves virtual imports
const workerVirtualPlugin = {
name: 'worker-virtual',
setup(build) {
// Handle virtual WASM URL for wa-sqlite
build.onResolve({ filter: /^virtual:wa-sqlite-wasm-url$/ }, args => ({
path: args.path,
namespace: 'wa-sqlite-wasm-url',
}));
build.onLoad({ filter: /.*/, namespace: 'wa-sqlite-wasm-url' }, async () => {
const wasmPath = join(process.cwd(), 'node_modules/wa-sqlite/dist/wa-sqlite-async.wasm');
const wasmContents = readFileSync(wasmPath);
const wasmBase64 = wasmContents.toString('base64');
const wasmDataUrl = `data:application/wasm;base64,${wasmBase64}`;
return {
contents: `export default ${JSON.stringify(wasmDataUrl)};`,
loader: 'js',
};
});
}
};
// Build sqlocal worker code
// WASM binary is NOT embedded in the worker — it's passed from the main thread via Comlink
const result = await esbuild.build({
entryPoints: ['src/modules/database/sqlocal/sqlocalWorkerDatabase.ts'],
bundle: true,
@ -130,7 +107,7 @@ const workerPlugin = {
format: 'iife',
target: 'es2020',
external: ['fs', 'path', 'obsidian'],
plugins: [wasmPlugin, workerVirtualPlugin, polyfillNode({
plugins: [wasmPlugin, polyfillNode({
})],
minify: process.argv[2] === 'production',
define: {
@ -158,10 +135,13 @@ const workerPlugin = {
const wasmPath = join(process.cwd(), 'node_modules/wa-sqlite/dist/wa-sqlite-async.wasm');
const wasmContents = readFileSync(wasmPath);
const wasmBase64 = wasmContents.toString('base64');
const wasmDataUrl = `data:application/wasm;base64,${wasmBase64}`;
return {
contents: `export default ${JSON.stringify(wasmDataUrl)};`,
contents: `
const wasmBase64 = "${wasmBase64}";
const wasmBinary = Uint8Array.from(atob(wasmBase64), c => c.charCodeAt(0));
export default wasmBinary;
`,
loader: 'js',
};
});

View file

@ -8,8 +8,9 @@ export class DatabaseProvider {
constructor(private app: App) { }
get prefix() {
const appId = ((this.app as any).appId ?? '').replace(/[^a-zA-Z0-9_-]/g, '_');
const filename = `sqlseal_1__` +
sanitise(this.app.vault.getName()) + "___" + (this.app as any).appId;
sanitise(this.app.vault.getName()) + "___" + appId;
return filename;
}

View file

@ -3,6 +3,8 @@ import * as Comlink from 'comlink';
import { SqlocalWorkerDatabase } from "./sqlocalWorkerDatabase";
import { ColumnDefinition } from "../../../utils/types";
import { sanitise } from "../../../utils/sanitiseColumn";
// @ts-ignore
import wasmBinary from 'virtual:wa-sqlite-wasm-url';
/**
* Main-thread proxy for SqlocalWorkerDatabase.
@ -49,7 +51,7 @@ export class SqlocalDatabaseProxy {
const instance = await new DatabaseWrap(this.dbName);
await instance.connect();
await instance.connect(wasmBinary);
this.db = instance;
this.isConnected = true;

View file

@ -5,10 +5,6 @@ import { IDBBatchAtomicVFS } from 'wa-sqlite/src/examples/IDBBatchAtomicVFS.js';
import { ColumnDefinition } from "../../../utils/types";
import { sanitise } from "../../../utils/sanitiseColumn";
// Get the WASM URL from the virtual module
// @ts-ignore
import wasmUrl from 'virtual:wa-sqlite-wasm-url';
/**
* Retry an async operation with exponential backoff
* @param operation - The async operation to retry
@ -70,6 +66,7 @@ export class SqlocalWorkerDatabase {
private isConnected = false;
private vfsRegistered = false;
private isRecreating = false;
private wasmBinary?: Uint8Array;
constructor(private readonly dbName: string) {
}
@ -109,16 +106,12 @@ export class SqlocalWorkerDatabase {
return this.sqlite3;
}
if (!this.wasmBinary) {
throw new Error('SqlocalWorkerDatabase: wasmBinary not provided. Call connect() with the WASM binary.');
}
try {
// Initialize the module with bundled WASM
const asyncModule = await SQLiteAsyncESMFactory({
locateFile: (file: string) => {
if (file.endsWith('.wasm')) {
return wasmUrl;
}
return file;
}
});
const asyncModule = await SQLiteAsyncESMFactory({ wasmBinary: this.wasmBinary, locateFile: (file: string) => file });
// Use Factory to get the actual sqlite3 API
this.sqlite3 = SQLite.Factory(asyncModule);
@ -148,11 +141,13 @@ export class SqlocalWorkerDatabase {
}
}
async connect() {
async connect(wasmBinary: Uint8Array) {
if (this.isConnected) {
return Promise.resolve();
}
this.wasmBinary = wasmBinary;
try {
// Initialize SQLite
await this.initializeSQLite();

View file

@ -1,10 +1,10 @@
// Mock for virtual:wa-sqlite-wasm-url used in tests
// Returns a file:// URL that works with the fetch polyfill in jest.setup.mjs
// Returns the WASM binary as Uint8Array (same as production build)
import { join } from 'path';
import { pathToFileURL } from 'url';
import { readFileSync } from 'fs';
const wasmPath = join(process.cwd(), 'node_modules/wa-sqlite/dist/wa-sqlite-async.wasm');
const wasmUrl = pathToFileURL(wasmPath).href;
const wasmBinary = new Uint8Array(readFileSync(wasmPath));
export default wasmUrl;
export default wasmBinary;

View file

@ -4,7 +4,7 @@ import SQLiteAsyncESMFactory from 'wa-sqlite/dist/wa-sqlite-async.mjs';
import * as SQLite from 'wa-sqlite';
import { MemoryAsyncVFS } from 'wa-sqlite/src/examples/MemoryAsyncVFS.js';
// @ts-ignore - Virtual module from esbuild
import wasmUrl from 'virtual:wa-sqlite-wasm-url';
import wasmBinary from 'virtual:wa-sqlite-wasm-url';
type ParamsObject = Record<string, any>;
@ -33,15 +33,7 @@ export class WaSqliteMemoryDatabase {
throw new Error('Invalid SQLite database file format');
}
// Initialize wa-sqlite
const asyncModule = await SQLiteAsyncESMFactory({
locateFile: (file: string) => {
if (file.endsWith('.wasm')) {
return wasmUrl;
}
return file;
}
});
const asyncModule = await SQLiteAsyncESMFactory({ wasmBinary, locateFile: (file: string) => file });
this.sqlite3 = SQLite.Factory(asyncModule);