Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@sqlite.org/sqlite-wasm

Package Overview
Dependencies
Maintainers
2
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sqlite.org/sqlite-wasm - npm Package Compare versions

Comparing version
3.51.2-build9
to
3.53.0-build1
+360
-150
dist/sqlite3-opfs-async-proxy.js

@@ -52,5 +52,15 @@ "use strict";

_need_ to change anything for this, but at some point (after Chrome
versions (approximately) 104-107 are extinct) should change our
versions (approximately) 104-107 are extinct) we should change our
usage of those methods to remove the "await".
*/
const urlParams = new URL(globalThis.location.href).searchParams;
const vfsName = urlParams.get("vfs");
if (!vfsName) throw new Error("Expecting vfs=opfs|opfs-wl URL argument for this worker");
/**
We use this to allow us to differentiate debug output from
multiple instances, e.g. multiple Workers to the "opfs"
VFS or both the "opfs" and "opfs-wl" VFSes.
*/
const workerId = Math.random() * 1e7 | 0;
const isWebLocker = "opfs-wl" === urlParams.get("vfs");
const wPost = (type, ...args) => postMessage({

@@ -71,2 +81,156 @@ type,

const state = Object.create(null);
const initS11n = function() {
/**
This proxy de/serializes cross-thread function arguments and
output-pointer values via the state.sabIO SharedArrayBuffer,
using the region defined by (state.sabS11nOffset,
state.sabS11nOffset + state.sabS11nSize]. Only one dataset is
recorded at a time.
This is not a general-purpose format. It only supports the
range of operations, and data sizes, needed by the
sqlite3_vfs and sqlite3_io_methods operations. Serialized
data are transient and this serialization algorithm may
change at any time.
The data format can be succinctly summarized as:
Nt...Td...D
Where:
- N = number of entries (1 byte)
- t = type ID of first argument (1 byte)
- ...T = type IDs of the 2nd and subsequent arguments (1 byte
each).
- d = raw bytes of first argument (per-type size).
- ...D = raw bytes of the 2nd and subsequent arguments (per-type
size).
All types except strings have fixed sizes. Strings are stored
using their TextEncoder/TextDecoder representations. It would
arguably make more sense to store them as Int16Arrays of
their JS character values, but how best/fastest to get that
in and out of string form is an open point. Initial
experimentation with that approach did not gain us any speed.
Historical note: this impl was initially about 1% this size by
using using JSON.stringify/parse(), but using fit-to-purpose
serialization saves considerable runtime.
*/
if (state.s11n) return state.s11n;
const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
state.s11n = Object.create(null);
const TypeIds = Object.create(null);
TypeIds.number = {
id: 1,
size: 8,
getter: "getFloat64",
setter: "setFloat64"
};
TypeIds.bigint = {
id: 2,
size: 8,
getter: "getBigInt64",
setter: "setBigInt64"
};
TypeIds.boolean = {
id: 3,
size: 4,
getter: "getInt32",
setter: "setInt32"
};
TypeIds.string = { id: 4 };
const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v);
const getTypeIdById = (tid) => {
switch (tid) {
case TypeIds.number.id: return TypeIds.number;
case TypeIds.bigint.id: return TypeIds.bigint;
case TypeIds.boolean.id: return TypeIds.boolean;
case TypeIds.string.id: return TypeIds.string;
default: toss("Invalid type ID:", tid);
}
};
/**
Returns an array of the deserialized state stored by the most
recent serialize() operation (from this thread or the
counterpart thread), or null if the serialization buffer is
empty. If passed a truthy argument, the serialization buffer
is cleared after deserialization.
*/
state.s11n.deserialize = function(clear = false) {
performance.now();
const argc = viewU8[0];
const rc = argc ? [] : null;
if (argc) {
const typeIds = [];
let offset = 1, i, n, v;
for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset]));
for (i = 0; i < argc; ++i) {
const t = typeIds[i];
if (t.getter) {
v = viewDV[t.getter](offset, state.littleEndian);
offset += t.size;
} else {
n = viewDV.getInt32(offset, state.littleEndian);
offset += 4;
v = textDecoder.decode(viewU8.slice(offset, offset + n));
offset += n;
}
rc.push(v);
}
}
if (clear) viewU8[0] = 0;
return rc;
};
/**
Serializes all arguments to the shared buffer for consumption
by the counterpart thread.
This routine is only intended for serializing OPFS VFS
arguments and (in at least one special case) result values,
and the buffer is sized to be able to comfortably handle
those.
If passed no arguments then it zeroes out the serialization
state.
*/
state.s11n.serialize = function(...args) {
performance.now();
if (args.length) {
const typeIds = [];
let i = 0, offset = 1;
viewU8[0] = args.length & 255;
for (; i < args.length; ++i, ++offset) {
typeIds.push(getTypeId(args[i]));
viewU8[offset] = typeIds[i].id;
}
for (i = 0; i < args.length; ++i) {
const t = typeIds[i];
if (t.setter) {
viewDV[t.setter](offset, args[i], state.littleEndian);
offset += t.size;
} else {
const s = textEncoder.encode(args[i]);
viewDV.setInt32(offset, s.byteLength, state.littleEndian);
offset += 4;
viewU8.set(s, offset);
offset += s.byteLength;
}
}
} else viewU8[0] = 0;
};
state.s11n.storeException = state.asyncS11nExceptions ? ((priority, e) => {
if (priority <= state.asyncS11nExceptions) state.s11n.serialize([
e.name,
": ",
e.message
].join(""));
}) : () => {};
return state.s11n;
};
/**

@@ -87,3 +251,3 @@ verbose:

const logImpl = (level, ...args) => {
if (state.verbose > level) loggers[level]("OPFS asyncer:", ...args);
if (state.verbose > level) loggers[level](vfsName + " async-proxy", workerId + ":", ...args);
};

@@ -102,8 +266,9 @@ const log = (...args) => logImpl(2, ...args);

/**
__implicitLocks is a Set of sqlite3_file pointers (integers) which were
"auto-locked". i.e. those for which we obtained a sync access
handle without an explicit xLock() call. Such locks will be
released during db connection idle time, whereas a sync access
handle obtained via xLock(), or subsequently xLock()'d after
auto-acquisition, will not be released until xUnlock() is called.
__implicitLocks is a Set of sqlite3_file pointers (integers)
which were "auto-locked". i.e. those for which we necessarily
obtain a sync access handle without an explicit xLock() call
guarding access. Such locks will be released during
`waitLoop()`'s idle time, whereas a sync access handle obtained
via xLock(), or subsequently xLock()'d after auto-acquisition,
will not be released until xUnlock() is called.

@@ -248,6 +413,7 @@ Maintenance reminder: if we relinquish auto-locks at the end of the

an exception is thrown while acquiring the handle, this routine
will wait briefly and try again, up to some fixed number of
times. If acquisition still fails at that point it will give up
and propagate the exception. Client-level code will see that as
an I/O error.
will wait briefly and try again, up to `maxTries` of times. If
acquisition still fails at that point it will give up and
propagate the exception. Client-level code will see that either
as an I/O error or SQLITE_BUSY, depending on the exception and
the context.

@@ -267,8 +433,27 @@ 2024-06-12: there is a rare race condition here which has been

so far.
Interface quirk: if fh.xLock is falsy and the handle is acquired
then fh.fid is added to __implicitLocks(). If fh.xLock is truthy,
it is not added as an implicit lock. i.e. xLock() impls must set
fh.xLock immediately _before_ calling this and must arrange to
restore it to its previous value if this function throws.
2026-03-06:
- baseWaitTime is the number of milliseconds to wait for the
first retry, increasing by one factor for each retry. It defaults
to (state.asyncIdleWaitTime*2).
- maxTries is the number of attempt to make, each one spaced out
by one additional factor of the baseWaitTime (e.g. 300, then 600,
then 900, the 1200...). This MUST be an integer >0.
Only the Web Locks impl should use the 3rd and 4th parameters.
*/
const getSyncHandle = async (fh, opName) => {
const getSyncHandle = async (fh, opName, baseWaitTime, maxTries = 6) => {
if (!fh.syncHandle) {
const t = performance.now();
log("Acquiring sync handle for", fh.filenameAbs);
const maxTries = 6, msBase = state.asyncIdleWaitTime * 2;
const msBase = baseWaitTime ?? state.asyncIdleWaitTime * 2;
maxTries ??= 6;
let i = 1, ms = msBase;

@@ -294,2 +479,5 @@ for (;; ms = msBase * ++i) try {

Atomics.notify()'s it.
The opName is only used for logging and debugging - all result
codes are expected on the same state.sabOPView slot.
*/

@@ -398,17 +586,2 @@ const storeAndNotify = (opName, value) => {

},
xLock: async function(fid, lockType) {
const fh = __openFiles[fid];
let rc = 0;
const oldLockType = fh.xLock;
fh.xLock = lockType;
if (!fh.syncHandle) try {
await getSyncHandle(fh, "xLock");
__implicitLocks.delete(fid);
} catch (e) {
state.s11n.storeException(1, e);
rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_LOCK);
fh.xLock = oldLockType;
}
storeAndNotify("xLock", rc);
},
xOpen: async function(fid, filename, flags, opfsFlags) {

@@ -459,3 +632,2 @@ const opName = "xOpen";

} catch (e) {
error("xRead() failed", e, fh);
state.s11n.storeException(1, e);

@@ -485,3 +657,2 @@ rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_READ);

} catch (e) {
error("xTruncate():", e, fh);
state.s11n.storeException(2, e);

@@ -493,13 +664,2 @@ rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_TRUNCATE);

},
xUnlock: async function(fid, lockType) {
let rc = 0;
const fh = __openFiles[fid];
if (fh.syncHandle && state.sq3Codes.SQLITE_LOCK_NONE === lockType) try {
await closeSyncHandle(fh);
} catch (e) {
state.s11n.storeException(1, e);
rc = state.sq3Codes.SQLITE_IOERR_UNLOCK;
}
storeAndNotify("xUnlock", rc);
},
xWrite: async function(fid, n, offset64) {

@@ -512,3 +672,2 @@ let rc;

} catch (e) {
error("xWrite():", e, fh);
state.s11n.storeException(1, e);

@@ -521,118 +680,167 @@ rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_WRITE);

};
const initS11n = () => {
/**
ACHTUNG: this code is 100% duplicated in the other half of this
proxy! The documentation is maintained in the "synchronous half".
*/
if (state.s11n) return state.s11n;
const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
state.s11n = Object.create(null);
const TypeIds = Object.create(null);
TypeIds.number = {
id: 1,
size: 8,
getter: "getFloat64",
setter: "setFloat64"
if (isWebLocker) {
/** Registry of active Web Locks: fid -> { mode, resolveRelease } */
const __activeWebLocks = Object.create(null);
vfsAsyncImpls.xLock = async function(fid, lockType, isFromUnlock) {
const whichOp = isFromUnlock ? "xUnlock" : "xLock";
const fh = __openFiles[fid];
const requestedMode = lockType >= state.sq3Codes.SQLITE_LOCK_RESERVED ? "exclusive" : "shared";
const existing = __activeWebLocks[fid];
if (existing) {
if (existing.mode === requestedMode || existing.mode === "exclusive" && requestedMode === "shared") {
fh.xLock = lockType;
storeAndNotify(whichOp, 0);
return 0;
}
await closeSyncHandle(fh);
existing.resolveRelease();
delete __activeWebLocks[fid];
}
const lockName = "sqlite3-vfs-opfs:" + fh.filenameAbs;
const oldLockType = fh.xLock;
return new Promise((resolveWaitLoop) => {
navigator.locks.request(lockName, { mode: requestedMode }, async (lock) => {
__implicitLocks.delete(fid);
let rc = 0;
try {
fh.xLock = lockType;
await getSyncHandle(fh, "xLock", state.asyncIdleWaitTime, 5);
} catch (e) {
fh.xLock = oldLockType;
state.s11n.storeException(1, e);
rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_BUSY);
}
const releasePromise = rc ? void 0 : new Promise((resolveRelease) => {
__activeWebLocks[fid] = {
mode: requestedMode,
resolveRelease
};
});
storeAndNotify(whichOp, rc);
resolveWaitLoop(0);
await releasePromise;
});
});
};
TypeIds.bigint = {
id: 2,
size: 8,
getter: "getBigInt64",
setter: "setBigInt64"
/** Internal helper for the opfs-wl xUnlock() */
const wlCloseHandle = async (fh) => {
let rc = 0;
try {
await closeSyncHandle(fh);
} catch (e) {
state.s11n.storeException(1, e);
rc = state.sq3Codes.SQLITE_IOERR_UNLOCK;
}
return rc;
};
TypeIds.boolean = {
id: 3,
size: 4,
getter: "getInt32",
setter: "setInt32"
vfsAsyncImpls.xUnlock = async function(fid, lockType) {
const fh = __openFiles[fid];
const existing = __activeWebLocks[fid];
if (!existing) {
const rc = await wlCloseHandle(fh);
storeAndNotify("xUnlock", rc);
return rc;
}
let rc = 0;
if (lockType === state.sq3Codes.SQLITE_LOCK_NONE) {
rc = await wlCloseHandle(fh);
existing.resolveRelease();
delete __activeWebLocks[fid];
fh.xLock = lockType;
} else if (lockType === state.sq3Codes.SQLITE_LOCK_SHARED && existing.mode === "exclusive") {
rc = await wlCloseHandle(fh);
if (0 === rc) {
fh.xLock = lockType;
existing.resolveRelease();
delete __activeWebLocks[fid];
return vfsAsyncImpls.xLock(fid, lockType, true);
}
} else error("xUnlock() unhandled condition", fh);
storeAndNotify("xUnlock", rc);
return 0;
};
TypeIds.string = { id: 4 };
const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v);
const getTypeIdById = (tid) => {
switch (tid) {
case TypeIds.number.id: return TypeIds.number;
case TypeIds.bigint.id: return TypeIds.bigint;
case TypeIds.boolean.id: return TypeIds.boolean;
case TypeIds.string.id: return TypeIds.string;
default: toss("Invalid type ID:", tid);
} else {
vfsAsyncImpls.xLock = async function(fid, lockType) {
const fh = __openFiles[fid];
let rc = 0;
const oldLockType = fh.xLock;
fh.xLock = lockType;
if (!fh.syncHandle) try {
await getSyncHandle(fh, "xLock");
__implicitLocks.delete(fid);
} catch (e) {
state.s11n.storeException(1, e);
rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_LOCK);
fh.xLock = oldLockType;
}
storeAndNotify("xLock", rc);
};
state.s11n.deserialize = function(clear = false) {
const argc = viewU8[0];
const rc = argc ? [] : null;
if (argc) {
const typeIds = [];
let offset = 1, i, n, v;
for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset]));
for (i = 0; i < argc; ++i) {
const t = typeIds[i];
if (t.getter) {
v = viewDV[t.getter](offset, state.littleEndian);
offset += t.size;
} else {
n = viewDV.getInt32(offset, state.littleEndian);
offset += 4;
v = textDecoder.decode(viewU8.slice(offset, offset + n));
offset += n;
}
rc.push(v);
}
vfsAsyncImpls.xUnlock = async function(fid, lockType) {
let rc = 0;
const fh = __openFiles[fid];
if (fh.syncHandle && state.sq3Codes.SQLITE_LOCK_NONE === lockType) try {
await closeSyncHandle(fh);
} catch (e) {
state.s11n.storeException(1, e);
rc = state.sq3Codes.SQLITE_IOERR_UNLOCK;
}
if (clear) viewU8[0] = 0;
return rc;
storeAndNotify("xUnlock", rc);
};
state.s11n.serialize = function(...args) {
if (args.length) {
const typeIds = [];
let i = 0, offset = 1;
viewU8[0] = args.length & 255;
for (; i < args.length; ++i, ++offset) {
typeIds.push(getTypeId(args[i]));
viewU8[offset] = typeIds[i].id;
}
for (i = 0; i < args.length; ++i) {
const t = typeIds[i];
if (t.setter) {
viewDV[t.setter](offset, args[i], state.littleEndian);
offset += t.size;
} else {
const s = textEncoder.encode(args[i]);
viewDV.setInt32(offset, s.byteLength, state.littleEndian);
offset += 4;
viewU8.set(s, offset);
offset += s.byteLength;
}
}
} else viewU8[0] = 0;
};
state.s11n.storeException = state.asyncS11nExceptions ? ((priority, e) => {
if (priority <= state.asyncS11nExceptions) state.s11n.serialize([
e.name,
": ",
e.message
].join(""));
}) : () => {};
return state.s11n;
};
}
const waitLoop = async function f() {
const opHandlers = Object.create(null);
for (let k of Object.keys(state.opIds)) {
const vi = vfsAsyncImpls[k];
if (!vi) continue;
const o = Object.create(null);
opHandlers[state.opIds[k]] = o;
o.key = k;
o.f = vi;
if (!f.inited) {
f.inited = true;
f.opHandlers = Object.create(null);
for (let k of Object.keys(state.opIds)) {
const vi = vfsAsyncImpls[k];
if (!vi) continue;
const o = Object.create(null);
f.opHandlers[state.opIds[k]] = o;
o.key = k;
o.f = vi;
}
}
const opIds = state.opIds;
const opView = state.sabOPView;
const slotWhichOp = opIds.whichOp;
const idleWaitTime = state.asyncIdleWaitTime;
const hasWaitAsync = !!Atomics.waitAsync;
while (!flagAsyncShutdown) try {
if ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.whichOp, 0, state.asyncIdleWaitTime)) {
await releaseImplicitLocks();
continue;
let opId;
if (hasWaitAsync) {
opId = Atomics.load(opView, slotWhichOp);
if (0 === opId) {
const rv = Atomics.waitAsync(opView, slotWhichOp, 0, idleWaitTime);
if (rv.async) await rv.value;
await releaseImplicitLocks();
continue;
}
} else {
/**
For browsers without Atomics.waitAsync(), we require
the legacy implementation. Browser versions where
waitAsync() arrived:
Chrome: 90 (2021-04-13)
Firefox: 145 (2025-11-11)
Safari: 16.4 (2023-03-27)
The "opfs" VFS was not born until Chrome was somewhere in
the v104-108 range (Summer/Autumn 2022) and did not work
with Safari < v17 (2023-09-18) due to a WebKit bug which
restricted OPFS access from sub-Workers.
The waitAsync() counterpart of this block can be used by
both "opfs" and "opfs-wl", whereas this block can only be
used by "opfs". Performance comparisons between the two
in high-contention tests have been indecisive.
*/
if ("not-equal" !== Atomics.wait(state.sabOPView, slotWhichOp, 0, state.asyncIdleWaitTime)) {
await releaseImplicitLocks();
continue;
}
opId = Atomics.load(state.sabOPView, slotWhichOp);
}
const opId = Atomics.load(state.sabOPView, state.opIds.whichOp);
Atomics.store(state.sabOPView, state.opIds.whichOp, 0);
const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #", opId);
const args = state.s11n.deserialize(true) || [];
if (hnd.f) await hnd.f(...args);
else error("Missing callback for opId", opId);
Atomics.store(opView, slotWhichOp, 0);
await (f.opHandlers[opId]?.f ?? toss("No waitLoop handler for whichOp #", opId))(...state.s11n.deserialize(true) || []);
} catch (e) {

@@ -674,7 +882,9 @@ error("in waitLoop():", e);

};
if (!globalThis.SharedArrayBuffer) wPost("opfs-unavailable", "Missing SharedArrayBuffer API.", "The server must emit the COOP/COEP response headers to enable that.");
if (globalThis.window === globalThis) wPost("opfs-unavailable", "This code cannot run from the main thread.", "Load it as a Worker from a separate Worker.");
else if (!globalThis.SharedArrayBuffer) wPost("opfs-unavailable", "Missing SharedArrayBuffer API.", "The server must emit the COOP/COEP response headers to enable that.");
else if (!globalThis.Atomics) wPost("opfs-unavailable", "Missing Atomics API.", "The server must emit the COOP/COEP response headers to enable that.");
else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) wPost("opfs-unavailable", "Missing required OPFS APIs.");
else if (isWebLocker && !globalThis.Atomics.waitAsync) wPost("opfs-unavailable", "Missing required Atomics.waitSync() for " + vfsName);
else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle?.prototype?.createSyncAccessHandle || !navigator?.storage?.getDirectory) wPost("opfs-unavailable", "Missing required OPFS APIs.");
else installAsyncProxy();
//#endregion
})();
{
"name": "@sqlite.org/sqlite-wasm",
"version": "3.51.2-build9",
"version": "3.53.0-build1",
"description": "SQLite Wasm conveniently wrapped as an ES Module.",

@@ -44,2 +44,5 @@ "type": "module",

},
"workspaces": [
"demos/*"
],
"files": [

@@ -56,7 +59,17 @@ "dist",

"build": "tsdown",
"dev:kvvfs": "npm run dev -w demos/kvvfs-demo",
"dev:opfs": "npm run dev -w demos/opfs-demo",
"dev:opfs-wl": "npm run dev -w demos/opfs-wl-demo",
"dev:sahpool": "npm run dev -w demos/sahpool-demo",
"dev:sahpool-webpack": "npm run dev -w demos/sahpool-webpack-demo",
"dev:sahpool-parcel": "npm run dev -w demos/sahpool-parcel-demo",
"dev:sahpool-rsbuild": "npm run dev -w demos/sahpool-rsbuild-demo",
"dev:main-thread": "npm run dev -w demos/main-thread-demo",
"dev:in-worker": "npm run dev -w demos/in-worker-demo",
"start": "npx http-server --coop",
"start:node": "cd demo && node node.mjs",
"fix": "npx prettier . --write",
"format": "oxfmt",
"format:check": "oxfmt --check",
"prepare": "lefthook install",
"prepublishOnly": "npm run build && npm run fix && npm run publint && npm run check-types",
"prepublishOnly": "npm run build && npm run format && npm run publint && npm run check-types",
"deploy": "npm run prepublishOnly && git add . && git commit -am 'New release' && git push && npm publish --tag=latest"

@@ -69,16 +82,14 @@ },

"@types/node": "^25.6.0",
"@typescript/native-preview": "^7.0.0-dev.20260414.1",
"@vitest/browser": "^4.1.4",
"@vitest/browser-playwright": "^4.1.4",
"@typescript/native-preview": "^7.0.0-dev.20260421.2",
"@vitest/browser": "^4.1.5",
"@vitest/browser-playwright": "^4.1.5",
"happy-dom": "20.9.0",
"http-server": "^14.1.1",
"lefthook": "2.1.5",
"lefthook": "2.1.6",
"oxfmt": "^0.46.0",
"playwright": "^1.59.1",
"prettier": "^3.8.2",
"prettier-plugin-jsdoc": "^1.8.0",
"publint": "^0.3.18",
"tsdown": "^0.21.8",
"typescript": "^6.0.2",
"vitest": "^4.1.4"
"tsdown": "^0.21.9",
"typescript": "^6.0.3",
"vitest": "^4.1.5"
}
}
+94
-154

@@ -5,2 +5,8 @@ # SQLite Wasm

## Installation
```bash
npm install @sqlite.org/sqlite-wasm
```
## Bug reports

@@ -10,9 +16,7 @@

>
> This project wraps the code of
> [SQLite Wasm](https://sqlite.org/wasm/doc/trunk/index.md) with _no_ changes,
> apart from added TypeScript types. Please do _not_ file issues or feature
> requests regarding the underlying SQLite Wasm code here. Instead, please
> follow the
> [SQLite bug filing instructions](https://www.sqlite.org/src/wiki?name=Bug+Reports).
> Filing TypeScript type related issues and feature requests is fine.
> This project wraps the code of [SQLite Wasm](https://sqlite.org/wasm/doc/trunk/index.md) with _no_
> changes, apart from added TypeScript types. Please do _not_ file issues or feature requests
> regarding the underlying SQLite Wasm code here. Instead, please follow the
> [SQLite bug filing instructions](https://www.sqlite.org/src/wiki?name=Bug+Reports). Filing
> TypeScript type related issues and feature requests is fine.

@@ -23,72 +27,13 @@ ## Node.js support

>
> Node.js is currently only supported for in-memory databases without
> persistence.
> Node.js is currently only supported for in-memory databases without persistence.
## Installation
```bash
npm install @sqlite.org/sqlite-wasm
```
## Usage
There are three ways to use SQLite Wasm:
There are two ways to use SQLite Wasm:
- [in the main thread with a wrapped worker](#in-a-wrapped-worker-with-opfs-if-available)
(🏆 preferred option)
- [in a worker](#in-a-worker-with-opfs-if-available)
- [in the main thread](#in-the-main-thread-without-opfs)
Only the worker versions allow you to use the origin private file system (OPFS)
storage back-end.
Only the worker versions allow you to use the origin private file system (OPFS) storage back-end.
### In a wrapped worker (with OPFS if available):
> [!Warning]
>
> For this to work, you need to set the following headers on your server:
>
> `Cross-Origin-Opener-Policy: same-origin`
>
> `Cross-Origin-Embedder-Policy: require-corp`
```ts
import {
sqlite3Worker1Promiser,
type Worker1Promiser,
} from '@sqlite.org/sqlite-wasm';
const initializeSQLite = async () => {
try {
const promiser = await sqlite3Worker1Promiser.v2();
const configResponse = await promiser('config-get', {});
console.log(
'Running SQLite3 version',
configResponse.result.version.libVersion,
);
const openResponse = await promiser('open', {
filename: 'file:mydb-v2.sqlite3?vfs=opfs',
});
console.log(
'OPFS is available, created persisted database at',
openResponse.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
);
} catch (err: any) {
if (!(err instanceof Error)) {
err = new Error(err.result.message || 'Unknown error');
}
console.error(err.name, err.message);
}
};
await initializeSQLite();
```
The `promiser` object above implements the
[Worker1 API](https://sqlite.org/wasm/doc/trunk/api-worker1.md#worker1-methods).
### In a worker (with OPFS if available):

@@ -113,7 +58,4 @@

const log = console.log;
const error = console.error;
const start = (sqlite3) => {
log('Running SQLite3 version', sqlite3.version.libVersion);
console.log('Running SQLite3 version', sqlite3.version.libVersion);
const db =

@@ -123,3 +65,3 @@ 'opfs' in sqlite3

: new sqlite3.oo1.DB('/mydb.sqlite3', 'ct');
log(
console.log(
'opfs' in sqlite3

@@ -134,8 +76,8 @@ ? `OPFS is available, created persisted database at ${db.filename}`

try {
log('Loading and initializing SQLite3 module...');
console.log('Loading and initializing SQLite3 module...');
const sqlite3 = await sqlite3InitModule();
log('Done initializing. Running demo...');
console.log('Done initializing. Running demo...');
start(sqlite3);
} catch (err) {
error('Initialization error:', err.name, err.message);
console.error('Initialization error:', err.name, err.message);
}

@@ -148,3 +90,3 @@ };

The `db` object above implements the
[Object Oriented API #1](https://sqlite.org/wasm/doc/trunk/api-oo1.md).
[Object-Oriented API #1](https://sqlite.org/wasm/doc/trunk/api-oo1.md).

@@ -156,5 +98,2 @@ ### In the main thread (without OPFS):

const log = console.log;
const error = console.error;
const start = (sqlite3) => {

@@ -165,11 +104,10 @@ log('Running SQLite3 version', sqlite3.version.libVersion);

};
const initializeSQLite = async () => {
try {
log('Loading and initializing SQLite3 module...');
console.log('Loading and initializing SQLite3 module...');
const sqlite3 = await sqlite3InitModule();
log('Done initializing. Running demo...');
console.log('Done initializing. Running demo...');
start(sqlite3);
} catch (err) {
error('Initialization error:', err.name, err.message);
console.error('Initialization error:', err.name, err.message);
}

@@ -182,8 +120,8 @@ };

The `db` object above implements the
[Object Oriented API #1](https://sqlite.org/wasm/doc/trunk/api-oo1.md).
[Object-Oriented API #1](https://sqlite.org/wasm/doc/trunk/api-oo1.md).
## Usage with vite
If you are using [vite](https://vitejs.dev/), you need to add the following
config option in `vite.config.js`:
If you are using [vite](https://vitejs.dev/), you need to add the following config option in
`vite.config.js`:

@@ -206,19 +144,16 @@ ```js

Check out a
[sample project](https://stackblitz.com/edit/vitejs-vite-ttrbwh?file=main.js)
that shows this in action.
Check out a [sample project](https://stackblitz.com/edit/vitejs-vite-ttrbwh?file=main.js) that shows
this in action.
## Demo
See the [demo](https://github.com/sqlite/sqlite-wasm/tree/main/demo) folder for
examples of how to use this in the main thread and in a worker. (Note that the
worker variant requires special HTTP headers, so it can't be hosted on GitHub
Pages.) An example that shows how to use this with vite is available on
[StackBlitz](https://stackblitz.com/edit/vitejs-vite-ttrbwh?file=main.js).
See the [demo](https://github.com/sqlite/sqlite-wasm/tree/main/demo) folder for examples of how to
use this in the main thread and in a worker. (Note that the worker variant requires special HTTP
headers, so it can't be hosted on GitHub Pages.) An example that shows how to use this with vite is
available on [StackBlitz](https://stackblitz.com/edit/vitejs-vite-ttrbwh?file=main.js).
## Projects using this package
See the list of
[npm dependents](https://www.npmjs.com/browse/depended/@sqlite.org/sqlite-wasm)
for this package.
See the list of [npm dependents](https://www.npmjs.com/browse/depended/@sqlite.org/sqlite-wasm) for
this package.

@@ -229,77 +164,82 @@ ## Deploying a new version

1. Manually trigger the
[GitHub Actions workflow](../../actions/workflows/build-wasm.yml). By
default, it uses the latest SQLite tag. This pull request will contain the
latest `sqlite3.wasm` and related bindings.
1. Manually trigger the [GitHub Actions workflow](../../actions/workflows/build-wasm.yml). By
default, it uses the latest SQLite tag. This pull request will contain the latest `sqlite3.wasm`
and related bindings.
2. Once the above pull request is validated and merged, update the version
number in `package.json`, reflecting the current
[SQLite version number](https://sqlite.org/download.html) and add a build
identifier suffix like `-build1`. The complete version number should read
something like `3.41.2-build1`.
2. Once the above pull request is validated and merged, update the version number in `package.json`,
reflecting the current [SQLite version number](https://sqlite.org/download.html) and add a build
identifier suffix like `-build1`. The complete version number should read something like
`3.41.2-build1`.
## Building the SQLite Wasm locally
1. Build the Docker image:
1. Build the Docker image:
```bash
docker build -t sqlite-wasm-builder:env .
```
```bash
docker build -t sqlite-wasm-builder:env .
```
2. Run the build:
2. Run the build:
**Unix (Linux/macOS):**
**Unix (Linux/macOS):**
```bash
docker run --rm \
-e SQLITE_REF="master" \
-v "$(pwd)/out":/out \
-v "$(pwd)/src/bin":/src/bin \
sqlite-wasm-builder:env build
```
```bash
docker run --rm \
-e SQLITE_REF="master" \
-v "$(pwd)/out":/out \
-v "$(pwd)/src/bin":/src/bin \
sqlite-wasm-builder:env build
```
**Windows (PowerShell):**
**Windows (PowerShell):**
```powershell
docker run --rm `
-e SQLITE_REF="master" `
-v "${PWD}/out:/out" `
-v "${PWD}/src/bin:/src/bin" `
sqlite-wasm-builder:env build
```
```powershell
docker run --rm `
-e SQLITE_REF="master" `
-v "${PWD}/out:/out" `
-v "${PWD}/src/bin:/src/bin" `
sqlite-wasm-builder:env build
```
**Windows (Command Prompt):**
**Windows (Command Prompt):**
```cmd
docker run --rm ^
-e SQLITE_REF="master" ^
-v "%cd%/out:/out" ^
-v "%cd%/src/bin:/src/bin" ^
sqlite-wasm-builder:env build
```
```cmd
docker run --rm ^
-e SQLITE_REF="master" ^
-v "%cd%/out:/out" ^
-v "%cd%/src/bin:/src/bin" ^
sqlite-wasm-builder:env build
```
## Running tests
The test suite consists of Node.js tests and browser-based tests (using Vitest
Browser Mode). Tests aim to sanity-check the exported scripts. We test for
correct exports and **very** basic functionality.
The test suite consists of Node.js tests and browser-based tests (using Vitest Browser Mode). Tests
aim to sanity-check the exported scripts. We test for correct exports and **very** basic
functionality.
1. Install dependencies:
1. Install dependencies:
```bash
npm install
```
```bash
npm install
```
2. Install Playwright browsers (required for browser tests):
2. Install Playwright browsers (required for browser tests):
```bash
npx playwright install chromium --with-deps --no-shell
```
```bash
npx playwright install chromium --with-deps --no-shell
```
3. Run all tests:
3. Run all tests:
```bash
npm test
```
```bash
npm test
```
## Deprecations
The Worker1 and Promiser1 APIs are, as of 2026-04-15, deprecated. They _will not be removed_, but
they also will not be extended further. It is their author's considered opinion that they are too
fragile, too imperformant, and too limited for any non-toy software, and their use is _actively
discouraged_. The "correct" way to use this library is documented in [Usage](#usage) section above.
## License

@@ -311,4 +251,4 @@

This project is based on [SQLite Wasm](https://sqlite.org/wasm), which it
conveniently wraps as an ES Module and publishes to npm as
This project is based on [SQLite Wasm](https://sqlite.org/wasm), which it conveniently wraps as an
ES Module and publishes to npm as
[`@sqlite.org/sqlite-wasm`](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm).

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet