Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c861c61
Fix resetting isHandleRequested
simolus3 Apr 30, 2025
289c2c5
Merge pull request #269 from powersync-ja/opfs-sync-coop-typo
rhashimoto Apr 30, 2025
97db43a
Bump package version.
rhashimoto Apr 30, 2025
1caeb13
Merge pull request #270 from rhashimoto/bump-version
rhashimoto Apr 30, 2025
e4e87d6
Fix hello demo import paths.
shoestringr May 17, 2025
fa72fb0
Merge pull request #276 from rhashimoto/hello-path-fixes
rhashimoto May 17, 2025
7d45baf
Permit JS booleans to be bound to queries, as integer 0/1 values (#272)
grantcox May 17, 2025
848ca54
Using a single TextEncoder for all string conversions
grantcox May 13, 2025
a883e22
Merge pull request #274 from grantcox/single-textencoder-instance
rhashimoto May 19, 2025
687ab82
Export HEAP* module members for recent EMSDK changes.
shoestringr Jun 1, 2025
5b2ba05
Merge pull request #279 from rhashimoto/heap-module-members
rhashimoto Jun 1, 2025
1c7c058
Bump tar-fs from 3.0.8 to 3.0.9
dependabot[bot] Jun 3, 2025
6f40f89
Merge pull request #280 from rhashimoto/dependabot/npm_and_yarn/tar-f…
rhashimoto Jun 3, 2025
1f65ff0
Update SQLite to 3.50.1.
shoestringr Jun 7, 2025
98465d9
Merge pull request #281 from rhashimoto/update-sqlite
rhashimoto Jun 7, 2025
c8dd51d
Bump package version.
rhashimoto Jun 7, 2025
03c00ed
Merge pull request #282 from rhashimoto/update-version
rhashimoto Jun 7, 2025
8623ef6
Update issue templates
rhashimoto Jun 18, 2025
4f2b7e8
Replace Facade Proxy with handwritten proxy. (#285)
rhashimoto Jun 21, 2025
21cc38a
Use non-CAPTCHA SQLite download URL. (#289)
rhashimoto Aug 22, 2025
a47eda0
Fix WebLocksMixin state initialization. (#293)
rhashimoto Sep 24, 2025
a3b1324
Bump package version.
shoestringr Sep 24, 2025
ba784f5
Merge remote-tracking branch 'upstream/master' into rhashimoto-merge-…
shoestringr Sep 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: " Do not post anything other than a bug report"
about: Issues are only for possible bugs in project code.
title: ''
labels: ''
assignees: ''

---


4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# dependencies
SQLITE_VERSION = version-3.47.0
SQLITE_TARBALL_URL = https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=${SQLITE_VERSION}
SQLITE_VERSION = version-3.50.1
SQLITE_TARBALL_URL = https://www.sqlite.org/src/tarball/$(SQLITE_VERSION)/sqlite.tar.gz

EXTENSION_FUNCTIONS = extension-functions.c
EXTENSION_FUNCTIONS_URL = https://www.sqlite.org/contrib/download/extension-functions.c?get=25
Expand Down
12 changes: 6 additions & 6 deletions demo/hello/hello.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
// to use. Note that an asynchronous VFS requires an asynchronous build
// (Asyncify or JSPI). As of 2024-05-26, JSPI is only available behind
// a flag on Chromium browsers.
// import SQLiteESMFactory from '../dist/wa-sqlite.mjs';
// import SQLiteESMFactory from '../../dist/wa-sqlite.mjs';
import SQLiteESMFactory from '../../dist/wa-sqlite-async.mjs';
// import SQLiteESMFactory from '../dist/wa-sqlite-jspi.mjs';
// import SQLiteESMFactory from '../../dist/wa-sqlite-jspi.mjs';

// Uncomment one of the following imports to choose a VFS. Note that an
// asynchronous VFS requires an asynchronous build, and an VFS using
Expand All @@ -19,11 +19,11 @@ import SQLiteESMFactory from '../../dist/wa-sqlite-async.mjs';
// clear the appropriate storage for things to work.
import { IDBBatchAtomicVFS as MyVFS } from '../../src/examples/IDBBatchAtomicVFS.js';
// import { IDBMirrorVFS as MyVFS } from '../../src/examples/IDBMirrorVFS.js';
// import { AccessHandlePoolVFS as MyVFS } from '../src/examples/AccessHandlePoolVFS.js';
// import { OPFSAdaptiveVFS as MyVFS } from '../src/examples/OPFSAdaptiveVFS.js';
// import { AccessHandlePoolVFS as MyVFS } from '../../src/examples/AccessHandlePoolVFS.js';
// import { OPFSAdaptiveVFS as MyVFS } from '../../src/examples/OPFSAdaptiveVFS.js';
// import { OPFSAnyContextVFS as MyVFS } from '../../src/examples/OPFSAnyContextVFS.js';
// import { OPFSCoopSyncVFS as MyVFS } from '../src/examples/OPFSCoopSyncVFS.js';
// import { OPFSPermutedVFS as MyVFS } from '../src/examples/OPFSPermutedVFS.js';
// import { OPFSCoopSyncVFS as MyVFS } from '../../src/examples/OPFSCoopSyncVFS.js';
// import { OPFSPermutedVFS as MyVFS } from '../../src/examples/OPFSPermutedVFS.js';

import * as SQLite from '../../src/sqlite-api.js';

Expand Down
Binary file modified dist/wa-sqlite-async.wasm
Binary file not shown.
Binary file modified dist/wa-sqlite-jspi.wasm
Binary file not shown.
Binary file modified dist/wa-sqlite.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wa-sqlite",
"version": "1.0.6",
"version": "1.0.9",
"type": "module",
"main": "src/sqlite-api.js",
"types": "src/types/index.d.ts",
Expand Down
265 changes: 219 additions & 46 deletions src/FacadeVFS.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,64 +405,30 @@ export class FacadeVFS extends VFS.Base {

/**
* Wrapped DataView for pointer arguments.
* Pointers to a single value are passed using DataView. A Proxy
* wrapper prevents use of incorrect type or endianness.
* Pointers to a single value are passed using a DataView-like class.
* This wrapper class prevents use of incorrect type or endianness, and
* reacquires the underlying buffer when the WebAssembly memory is resized.
* @param {'Int32'|'BigInt64'} type
* @param {number} byteOffset
* @returns {DataView}
*/
#makeTypedDataView(type, byteOffset) {
const byteLength = type === 'Int32' ? 4 : 8;
const getter = `get${type}`;
const setter = `set${type}`;
const makeDataView = () => new DataView(
this._module.HEAPU8.buffer,
this._module.HEAPU8.byteOffset + byteOffset,
byteLength);
let dataView = makeDataView();
return new Proxy(dataView, {
get(_, prop) {
if (dataView.buffer.byteLength === 0) {
// WebAssembly memory resize detached the buffer.
dataView = makeDataView();
}
if (prop === getter) {
return function(byteOffset, littleEndian) {
if (!littleEndian) throw new Error('must be little endian');
return dataView[prop](byteOffset, littleEndian);
}
}
if (prop === setter) {
return function(byteOffset, value, littleEndian) {
if (!littleEndian) throw new Error('must be little endian');
return dataView[prop](byteOffset, value, littleEndian);
}
}
if (typeof prop === 'string' && (prop.match(/^(get)|(set)/))) {
throw new Error('invalid type');
}
const result = dataView[prop];
return typeof result === 'function' ? result.bind(dataView) : result;
}
});
// @ts-ignore
return new DataViewProxy(this._module, byteOffset, type);
}

/**
* Wrapped Uint8Array for buffer arguments.
* Memory blocks are passed as a Uint8Array-like class. This wrapper
* class reacquires the underlying buffer when the WebAssembly memory
* is resized.
* @param {number} byteOffset
* @param {number} byteLength
* @returns {Uint8Array}
*/
#makeDataArray(byteOffset, byteLength) {
let target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength);
return new Proxy(target, {
get: (_, prop, receiver) => {
if (target.buffer.byteLength === 0) {
// WebAssembly memory resize detached the buffer.
target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength);
}
const result = target[prop];
return typeof result === 'function' ? result.bind(target) : result;
}
});
// @ts-ignore
return new Uint8ArrayProxy(this._module, byteOffset, byteLength);
}

#decodeFilename(zName, flags) {
Expand Down Expand Up @@ -506,3 +472,210 @@ export class FacadeVFS extends VFS.Base {
function delegalize(lo32, hi32) {
return (hi32 * 0x100000000) + lo32 + (lo32 < 0 ? 2**32 : 0);
}

// This class provides a Uint8Array-like interface for a WebAssembly memory
// buffer. It is used to access memory blocks passed as arguments to
// xRead, xWrite, etc. The class reacquires the underlying buffer when the
// WebAssembly memory is resized, which can happen when the memory is
// detached and resized by the WebAssembly module.
//
// Note that although this class implements the same methods as Uint8Array,
// it is not a real Uint8Array and passing it to functions that expect
// a Uint8Array may not work. Use subarray() to get a real Uint8Array
// if needed.
class Uint8ArrayProxy {
#module;

#_array = new Uint8Array()
get #array() {
if (this.#_array.buffer.byteLength === 0) {
// WebAssembly memory resize detached the buffer so re-create the
// array with the new buffer.
this.#_array = this.#module.HEAPU8.subarray(
this.byteOffset,
this.byteOffset + this.byteLength);
}
return this.#_array;
}

/**
* @param {*} module
* @param {number} byteOffset
* @param {number} byteLength
*/
constructor(module, byteOffset, byteLength) {
this.#module = module;
this.byteOffset = byteOffset;
this.length = this.byteLength = byteLength;
}

get buffer() {
return this.#array.buffer;
}

at(index) {
return this.#array.at(index);
}
copyWithin(target, start, end) {
this.#array.copyWithin(target, start, end);
}
entries() {
return this.#array.entries();
}
every(predicate) {
return this.#array.every(predicate);
}
fill(value, start, end) {
this.#array.fill(value, start, end);
}
filter(predicate) {
return this.#array.filter(predicate);
}
find(predicate) {
return this.#array.find(predicate);
}
findIndex(predicate) {
return this.#array.findIndex(predicate);
}
findLast(predicate) {
return this.#array.findLast(predicate);
}
findLastIndex(predicate) {
return this.#array.findLastIndex(predicate);
}
forEach(callback) {
this.#array.forEach(callback);
}
includes(value, start) {
return this.#array.includes(value, start);
}
indexOf(value, start) {
return this.#array.indexOf(value, start);
}
join(separator) {
return this.#array.join(separator);
}
keys() {
return this.#array.keys();
}
lastIndexOf(value, start) {
return this.#array.lastIndexOf(value, start);
}
map(callback) {
return this.#array.map(callback);
}
reduce(callback, initialValue) {
return this.#array.reduce(callback, initialValue);
}
reduceRight(callback, initialValue) {
return this.#array.reduceRight(callback, initialValue);
}
reverse() {
this.#array.reverse();
}
set(array, offset) {
this.#array.set(array, offset);
}
slice(start, end) {
return this.#array.slice(start, end);
}
some(predicate) {
return this.#array.some(predicate);
}
sort(compareFn) {
this.#array.sort(compareFn);
}
subarray(begin, end) {
return this.#array.subarray(begin, end);
}
toLocaleString(locales, options) {
// @ts-ignore
return this.#array.toLocaleString(locales, options);
}
toReversed() {
return this.#array.toReversed();
}
toSorted(compareFn) {
return this.#array.toSorted(compareFn);
}
toString() {
return this.#array.toString();
}
values() {
return this.#array.values();
}
with(index, value) {
return this.#array.with(index, value);
}
[Symbol.iterator]() {
return this.#array[Symbol.iterator]();
}
}

// This class provides a DataView-like interface for a WebAssembly memory
// buffer, restricted to either Int32 or BigInt64 types. It also reacquires
// the underlying buffer when the WebAssembly memory is resized, which can
// happen when the memory is detached and resized by the WebAssembly module.
class DataViewProxy {
#module;
#type;

#_view = new DataView(new ArrayBuffer(0));
get #view() {
if (this.#_view.buffer.byteLength === 0) {
// WebAssembly memory resize detached the buffer so re-create the
// view with the new buffer.
this.#_view = new DataView(
this.#module.HEAPU8.buffer,
this.#module.HEAPU8.byteOffset + this.byteOffset);
}
return this.#_view;
}

/**
* @param {*} module
* @param {number} byteOffset
* @param {'Int32'|'BigInt64'} type
*/
constructor(module, byteOffset, type) {
this.#module = module;
this.byteOffset = byteOffset;
this.#type = type;
}

get buffer() {
return this.#view.buffer;
}
get byteLength() {
return this.#type === 'Int32' ? 4 : 8;
}

getInt32(byteOffset, littleEndian) {
if (this.#type !== 'Int32') {
throw new Error('invalid type');
}
if (!littleEndian) throw new Error('must be little endian');
return this.#view.getInt32(byteOffset, littleEndian);
}
setInt32(byteOffset, value, littleEndian) {
if (this.#type !== 'Int32') {
throw new Error('invalid type');
}
if (!littleEndian) throw new Error('must be little endian');
this.#view.setInt32(byteOffset, value, littleEndian);
}
getBigInt64(byteOffset, littleEndian) {
if (this.#type !== 'BigInt64') {
throw new Error('invalid type');
}
if (!littleEndian) throw new Error('must be little endian');
return this.#view.getBigInt64(byteOffset, littleEndian);
}
setBigInt64(byteOffset, value, littleEndian) {
if (this.#type !== 'BigInt64') {
throw new Error('invalid type');
}
if (!littleEndian) throw new Error('must be little endian');
this.#view.setBigInt64(byteOffset, value, littleEndian);
}
}
Loading
Loading