mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-25 12:04:21 +01:00
Add readme info
Add basic limbo-opfs-test.html Remove unused browser-its structure - we may bring it back
This commit is contained in:
@@ -7,3 +7,30 @@ This source tree contains Limbo Wasm bindings.
|
||||
```
|
||||
./scripts/build
|
||||
```
|
||||
|
||||
# Browser Support
|
||||
|
||||
Adding experimental support for limbo in the browser. This is done by adding support for OPFS as a VFS.
|
||||
|
||||
To see a basic example of this `npm run dev` and navigate to `http://localhost:5173/limbo-opfs-test.html` and open the console.
|
||||
|
||||
## Design
|
||||
|
||||
This design mirrors sqlite's approach for OPFS support. It has a sync api in `opfs.js` which communicates with `opfs-sync-proxy.js` via `SharedArrayBuffer` and `Atomics.wait`. This allows us to live the VFS api in `lib.rs` unchanged.
|
||||
|
||||
You can see `limbo-opfs-test.html` for basic usage.
|
||||
|
||||
## UTs
|
||||
|
||||
There are OPFS specific unit tests and then some basic limbo unit tests. These are run via `npm test` or `npx vitest`.
|
||||
|
||||
For more info and log output you can run `npx vitest:ui` but you can get some parallel execution of test cases which cause issues.
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
-[] Add a wrapper js that provides a clean interface to the `limbo-worker.js`
|
||||
-[] Add more tests for opfs.js operations
|
||||
-[] Add error return handling
|
||||
-[] Make sure posix flags for open are handled instead of just being ignored (this requires creating a mapping of behaviors from posix to opfs as far as makes sense)
|
||||
|
||||
|
||||
2125
bindings/wasm/browser-its/package-lock.json
generated
2125
bindings/wasm/browser-its/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "limbo-wasm-integration-tests",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "PROVIDER=better-sqlite3 ava tests/test.js && PROVIDER=limbo-wasm ava tests/test.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^8.4.0",
|
||||
"limbo-wasm": "../pkg"
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import test from "ava";
|
||||
|
||||
test.beforeEach(async (t) => {
|
||||
const [db, errorType, provider] = await connect();
|
||||
db.exec(`
|
||||
DROP TABLE IF EXISTS users;
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)
|
||||
`);
|
||||
db.exec(
|
||||
"INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.org')"
|
||||
);
|
||||
db.exec(
|
||||
"INSERT INTO users (id, name, email) VALUES (2, 'Bob', 'bob@example.com')"
|
||||
);
|
||||
t.context = {
|
||||
db,
|
||||
errorType,
|
||||
provider
|
||||
};
|
||||
});
|
||||
|
||||
test.serial("Statement.raw().all()", async (t) => {
|
||||
const db = t.context.db;
|
||||
|
||||
const stmt = db.prepare("SELECT * FROM users");
|
||||
const expected = [
|
||||
[1, "Alice", "alice@example.org"],
|
||||
[2, "Bob", "bob@example.com"],
|
||||
];
|
||||
t.deepEqual(stmt.raw().all(), expected);
|
||||
});
|
||||
|
||||
test.serial("Statement.raw().get()", async (t) => {
|
||||
const db = t.context.db;
|
||||
|
||||
const stmt = db.prepare("SELECT * FROM users");
|
||||
const expected = [
|
||||
1, "Alice", "alice@example.org"
|
||||
];
|
||||
t.deepEqual(stmt.raw().get(), expected);
|
||||
|
||||
const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1");
|
||||
t.is(emptyStmt.raw().get(), undefined);
|
||||
});
|
||||
|
||||
test.serial("Statement.raw().iterate()", async (t) => {
|
||||
const db = t.context.db;
|
||||
|
||||
const stmt = db.prepare("SELECT * FROM users");
|
||||
const expected = [
|
||||
{ done: false, value: [1, "Alice", "alice@example.org"] },
|
||||
{ done: false, value: [2, "Bob", "bob@example.com"] },
|
||||
{ done: true, value: undefined },
|
||||
];
|
||||
|
||||
let iter = stmt.raw().iterate();
|
||||
t.is(typeof iter[Symbol.iterator], 'function');
|
||||
t.deepEqual(iter.next(), expected[0])
|
||||
t.deepEqual(iter.next(), expected[1])
|
||||
t.deepEqual(iter.next(), expected[2])
|
||||
|
||||
const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1");
|
||||
t.is(typeof emptyStmt[Symbol.iterator], 'undefined');
|
||||
t.throws(() => emptyStmt.next(), { instanceOf: TypeError });
|
||||
});
|
||||
|
||||
const connect = async (path_opt) => {
|
||||
const path = path_opt ?? "hello.db";
|
||||
const provider = process.env.PROVIDER;
|
||||
if (provider === "limbo-wasm") {
|
||||
const database = process.env.LIBSQL_DATABASE ?? path;
|
||||
const x = await import("limbo-wasm");
|
||||
const options = {};
|
||||
const db = new x.Database(database, options);
|
||||
return [db, x.SqliteError, provider];
|
||||
}
|
||||
if (provider == "better-sqlite3") {
|
||||
const x = await import("better-sqlite3");
|
||||
const options = {};
|
||||
const db = x.default(path, options);
|
||||
return [db, x.SqliteError, provider];
|
||||
}
|
||||
throw new Error("Unknown provider: " + provider);
|
||||
};
|
||||
@@ -1,62 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Limbo Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="runTests()">Run Tests</button>
|
||||
<script type="module">
|
||||
import init, { Database } from './pkg/limbo_wasm.js';
|
||||
<script type="module">
|
||||
function waitForMessage(worker, type, op) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const handler = (e) => {
|
||||
if (e.data.type === type && (!op || e.data.op === op)) {
|
||||
worker.removeEventListener('message', handler);
|
||||
resolve(e.data);
|
||||
} else if (e.data.type === 'error') {
|
||||
worker.removeEventListener('message', handler);
|
||||
reject(e.data.error);
|
||||
}
|
||||
};
|
||||
worker.addEventListener('message', handler);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Page loading, initializing WASM...');
|
||||
|
||||
try {
|
||||
await navigator.storage.getDirectory();
|
||||
console.log('OPFS access granted');
|
||||
|
||||
await init();
|
||||
console.log('WASM initialized successfully');
|
||||
} catch (e) {
|
||||
console.error('Initialization failed:', e);
|
||||
}
|
||||
|
||||
window.connect = async () => {
|
||||
console.log('Connect started...');
|
||||
try {
|
||||
console.log('Creating Database instance...');
|
||||
const db = await new Database("hello.db"); // Added await here
|
||||
console.log('Database instance created:', db);
|
||||
return [db, Error, "limbo-wasm"];
|
||||
} catch (e) {
|
||||
console.error('Connection error type:', e.constructor.name);
|
||||
console.error('Connection error:', e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
async function runTests() {
|
||||
const worker = new Worker('./src/limbo-worker.js', { type: 'module' });
|
||||
|
||||
// Wait for ready then send createDb
|
||||
await waitForMessage(worker, 'ready');
|
||||
worker.postMessage({
|
||||
op: 'createDb',
|
||||
path: 'test.db'
|
||||
});
|
||||
|
||||
window.runTests = async () => {
|
||||
console.log('Starting tests...');
|
||||
try {
|
||||
console.log('Before connect call');
|
||||
const [db] = await connect();
|
||||
console.log('After connect call');
|
||||
// Wait for createDb success then send exec
|
||||
await waitForMessage(worker, 'success', 'createDb');
|
||||
worker.postMessage({
|
||||
op: 'exec',
|
||||
sql: `
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);
|
||||
`
|
||||
});
|
||||
console.log("made it here");
|
||||
|
||||
|
||||
await db.exec(`
|
||||
DROP TABLE IF EXISTS users;
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)
|
||||
`);
|
||||
await db.exec("INSERT INTO users VALUES (1, 'Alice', 'alice@example.org')");
|
||||
await db.exec("INSERT INTO users VALUES (2, 'Bob', 'bob@example.com')");
|
||||
console.log('Test data inserted');
|
||||
// Wait for exec success then send prepare
|
||||
await waitForMessage(worker, 'success', 'exec');
|
||||
worker.postMessage({
|
||||
op: 'exec',
|
||||
sql: `
|
||||
INSERT INTO users VALUES (1, 'Alice', 'alice@example.org');
|
||||
`
|
||||
});
|
||||
|
||||
const stmt = db.prepare("SELECT * FROM users");
|
||||
const result = stmt.raw().all();
|
||||
console.log('Query result:', result);
|
||||
|
||||
console.log('Tests completed successfully');
|
||||
await waitForMessage(worker, 'success', 'exec');
|
||||
worker.postMessage({
|
||||
op: 'exec',
|
||||
sql: `
|
||||
INSERT INTO users VALUES (2, 'Bob', 'bob@example.org');
|
||||
`
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error('Test error:', e);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
await waitForMessage(worker, 'success', 'exec');
|
||||
worker.postMessage({
|
||||
op: 'exec',
|
||||
sql: `
|
||||
INSERT INTO users VALUES (3, 'bill', 'bill@example.com');
|
||||
`
|
||||
});
|
||||
|
||||
// Wait for exec success then send prepare
|
||||
await waitForMessage(worker, 'success', 'exec');
|
||||
worker.postMessage({
|
||||
op: 'prepare',
|
||||
sql: 'SELECT * FROM users;'
|
||||
});
|
||||
|
||||
const results = await waitForMessage(worker, 'result');
|
||||
console.log('Query results:', results);
|
||||
}
|
||||
|
||||
runTests().catch(console.error);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user