picovolt 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -17
- package/browser.js +40 -0
- package/package.json +7 -3
- package/picovolt_bg.js +15 -15
- package/picovolt_bg.wasm +0 -0
- package/sqlite.js +27 -4
- package/worker.js +37 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# PicoVolt (PVDB)
|
|
2
2
|
|
|
3
3
|
[](https://github.com/MiniJe/picovolt/actions/workflows/ci.yml)
|
|
4
|
-
[](CHANGELOG.md)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|

|
|
7
7
|
[](https://github.com/MiniJe/picovolt)
|
|
@@ -28,7 +28,7 @@ cache efficiency.
|
|
|
28
28
|
|
|
29
29
|
## Status
|
|
30
30
|
|
|
31
|
-
The engine is built out across four phases, all implemented, with
|
|
31
|
+
The engine is built out across four phases, all implemented, with over 180 unit and
|
|
32
32
|
integration tests plus doctests passing and a clean `cargo clippy -D warnings` on
|
|
33
33
|
Linux and Windows. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).
|
|
34
34
|
|
|
@@ -52,7 +52,7 @@ Linux and Windows. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).
|
|
|
52
52
|
| [`storage/compress.rs`](src/storage/compress.rs) | Delta-Z, LEB128 varints, dictionary bit-packing |
|
|
53
53
|
| [`storage/index.rs`](src/storage/index.rs) | in-memory ordered secondary index (value to record addresses; point and range) |
|
|
54
54
|
| [`storage/record.rs`](src/storage/record.rs) | row and record-body serialization with CAS interception |
|
|
55
|
-
| [`storage/vle.rs`](src/storage/vle.rs) | dev directory store, prod
|
|
55
|
+
| [`storage/vle.rs`](src/storage/vle.rs) | dev directory store, owned prod snapshot, streamed reads, `bake` |
|
|
56
56
|
| [`engine/mvcc.rs`](src/engine/mvcc.rs) | transaction clock and snapshot visibility |
|
|
57
57
|
| [`engine/wasm.rs`](src/engine/wasm.rs) | sandboxed `wasmi` extension runtime and the `WasmExec` backend trait |
|
|
58
58
|
| [`engine/interp.rs`](src/engine/interp.rs) | `pv-wasm`: a from-scratch WASM interpreter (integer subset) |
|
|
@@ -89,12 +89,15 @@ Linux and Windows. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).
|
|
|
89
89
|
- **Hardened against untrusted input.** Opening a `.pvdb` or workspace, or running
|
|
90
90
|
a WASM module, validates manifest hashes (no path traversal), bounds-checks CAS
|
|
91
91
|
offsets and page chains (no out-of-bounds reads or infinite loops on a crafted
|
|
92
|
-
file), and
|
|
92
|
+
file), and meters WASM instructions, memory, and output. The decoders are fuzzed (a cross-platform
|
|
93
93
|
fuzz-lite test and a [`fuzz/`](fuzz) cargo-fuzz crate), and `cargo audit`
|
|
94
|
-
reports no
|
|
94
|
+
currently reports no vulnerability failures. Both run in CI. See
|
|
95
|
+
[SECURITY.md](SECURITY.md).
|
|
95
96
|
|
|
96
97
|
## Build
|
|
97
98
|
|
|
99
|
+
Rust 1.86 or newer is required.
|
|
100
|
+
|
|
98
101
|
```sh
|
|
99
102
|
cargo build
|
|
100
103
|
cargo test
|
|
@@ -109,7 +112,13 @@ cargo run --release --example repl # interactive SQL shell (pvsql)
|
|
|
109
112
|
cargo run --release --example bench # evaluation harness across modes and workloads
|
|
110
113
|
```
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
Install the first-class CLI with `cargo install picovolt`, then use `pv query`,
|
|
116
|
+
`pv inspect`, `pv import`, `pv export`, and `pv bake`. Copyable Rust, Python, Go,
|
|
117
|
+
and browser projects are in [`starters/`](starters/README.md); supported adapters
|
|
118
|
+
are catalogued in [`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md).
|
|
119
|
+
|
|
120
|
+
SQL supported: `CREATE TABLE` with `PRIMARY KEY`, `UNIQUE`, and `NOT NULL`,
|
|
121
|
+
`CREATE [UNIQUE] INDEX ON t (col)`, `INSERT`,
|
|
113
122
|
`UPDATE ... SET ... WHERE`, `DELETE ... WHERE`, `DROP TABLE`, and
|
|
114
123
|
`SELECT [DISTINCT] {* | col [AS alias], ... | COUNT/SUM/MIN/MAX/AVG(...) [AS alias]}
|
|
115
124
|
FROM t [WHERE <pred>] [GROUP BY cols] [HAVING <pred>] [BEFORE tx]
|
|
@@ -117,6 +126,9 @@ FROM t [WHERE <pred>] [GROUP BY cols] [HAVING <pred>] [BEFORE tx]
|
|
|
117
126
|
`col <op> value` (`=`, `!=`, `<`, `<=`, `>`, `>=`, `LIKE`, `NOT LIKE`),
|
|
118
127
|
`col [NOT] IN (...)`, `col [NOT] BETWEEN a AND b`, and `col IS [NOT] NULL` with
|
|
119
128
|
`AND`, `OR`, and parentheses. Integer and decimal values compare by magnitude.
|
|
129
|
+
Basic `SELECT * FROM a [INNER|LEFT] JOIN b ON a_key = b_key` equality joins are
|
|
130
|
+
also supported. Rust callers can cache `Database::prepare(...)` templates and use
|
|
131
|
+
atomic `Database::transaction(...)` closures with in-memory databases.
|
|
120
132
|
Durability is selectable via `Database::set_durability` (`Fast` OS-cache default,
|
|
121
133
|
or crash-safe `Sync` with fsync and an atomic manifest).
|
|
122
134
|
|
|
@@ -126,9 +138,9 @@ around 33k rows/s, linear), larger-than-RAM reads through a bounded buffer pool
|
|
|
126
138
|
667-page dataset serves from a 16-page pool), ordered secondary indexes (point
|
|
127
139
|
lookups roughly 11,000 times faster than a scan, plus range predicates), MVCC
|
|
128
140
|
time-travel, opt-in crash-safe durability (`Durability::Sync`), and a fast
|
|
129
|
-
compile-and-publish path (CAS dedup, columnar compression,
|
|
130
|
-
artifacts). Current limits
|
|
131
|
-
no
|
|
141
|
+
compile-and-publish path (CAS dedup, columnar compression, memory-mappable
|
|
142
|
+
single-file artifacts). Current limits include no filesystem `BEGIN`/`COMMIT`
|
|
143
|
+
transactions, only basic two-table equality joins, and no concurrent writers.
|
|
132
144
|
|
|
133
145
|
## Install and distribution
|
|
134
146
|
|
|
@@ -136,10 +148,12 @@ no concurrency.
|
|
|
136
148
|
|--------|-----|
|
|
137
149
|
| **Rust** (crates.io) | `cargo add picovolt` |
|
|
138
150
|
| **JavaScript / npm** (WebAssembly, browser and Node) | `npm install picovolt` |
|
|
139
|
-
| **
|
|
151
|
+
| **Python** (native wheels) | `python -m pip install picovolt` (after the first PyPI release) |
|
|
152
|
+
| **C / Go** (native, via the C ABI) | `cargo build --release --features capi`, then see [`bindings/`](bindings) |
|
|
140
153
|
| **In-memory** (native, no filesystem) | `Database::open_memory()`, export with `bake_to_bytes()` |
|
|
141
154
|
|
|
142
|
-
PicoVolt runs in the browser through its in-memory backend
|
|
155
|
+
PicoVolt runs in the browser through its in-memory backend plus an OPFS persistence
|
|
156
|
+
wrapper and Web Worker endpoint. Build the WebAssembly
|
|
143
157
|
package with `wasm-pack build --target bundler --release -- --features wasm`, then
|
|
144
158
|
`import { Db } from "picovolt"` and run SQL with `db.query(...)`. See
|
|
145
159
|
[src/wasm_api.rs](src/wasm_api.rs) for the JavaScript surface.
|
|
@@ -155,9 +169,9 @@ All bindings accept positional `?` parameters
|
|
|
155
169
|
a familiar surface, drop-in adapters are provided: a `better-sqlite3`-style
|
|
156
170
|
JavaScript API (`import Database from "picovolt/sqlite"`), a Python DB-API 2.0
|
|
157
171
|
module (`import picovolt.dbapi2 as sqlite`), and the Go `database/sql` driver
|
|
158
|
-
([`bindings/go/pvsql`](bindings/go/pvsql)). Shared limits
|
|
159
|
-
|
|
160
|
-
|
|
172
|
+
([`bindings/go/pvsql`](bindings/go/pvsql)). Shared limits include positional `?`
|
|
173
|
+
only and the intentionally compact SQL grammar; JavaScript and in-memory Rust
|
|
174
|
+
also expose rollback-capable transaction wrappers.
|
|
161
175
|
|
|
162
176
|
## Server mode
|
|
163
177
|
|
|
@@ -169,13 +183,22 @@ thread over a channel, so the single-threaded core is unchanged.
|
|
|
169
183
|
```sh
|
|
170
184
|
cargo build --release --features server
|
|
171
185
|
./target/release/picovolt-server --memory --addr 127.0.0.1:8080
|
|
172
|
-
curl -s localhost:8080/v1/query
|
|
186
|
+
curl -s localhost:8080/v1/query \
|
|
187
|
+
-H 'Content-Type: application/json' \
|
|
188
|
+
-d '{"sql":"CREATE TABLE demo (value)","params":[]}'
|
|
173
189
|
```
|
|
174
190
|
|
|
175
|
-
Endpoints are `POST /v1/query`, `GET /v1/tx`, and `GET /v1/health`.
|
|
176
|
-
authentication
|
|
191
|
+
Endpoints are `POST /v1/query`, `GET /v1/tx`, and `GET /v1/health`. Loopback use
|
|
192
|
+
may omit authentication. A non-loopback bind is refused unless a bearer token is
|
|
193
|
+
provided with `--token-file` or `PICOVOLT_SERVER_TOKEN`; send it as
|
|
194
|
+
`Authorization: Bearer ...`. Query bodies, queues, execution time, rows scanned,
|
|
195
|
+
result rows, and response size are bounded. TLS is not built in, so network
|
|
196
|
+
deployments still belong behind a TLS-terminating reverse proxy. See
|
|
177
197
|
[src/bin/server.rs](src/bin/server.rs).
|
|
178
198
|
|
|
199
|
+
Applications accepting SQL from users can also call `Database::query_with_limits`
|
|
200
|
+
directly and choose their own scan, result, memory, and deadline budgets.
|
|
201
|
+
|
|
179
202
|
## Extending PicoVolt
|
|
180
203
|
|
|
181
204
|
There are two extension paths: sandboxed WebAssembly user-defined functions, and
|
|
@@ -187,6 +210,7 @@ native modules built on the public API. Both are documented in
|
|
|
187
210
|
| | |
|
|
188
211
|
|--|--|
|
|
189
212
|
| Roadmap | [ROADMAP.md](ROADMAP.md) |
|
|
213
|
+
| One-million-download plan | [docs/ROADMAP_1M_DOWNLOADS.md](docs/ROADMAP_1M_DOWNLOADS.md) |
|
|
190
214
|
| Contributing | [CONTRIBUTING.md](CONTRIBUTING.md) |
|
|
191
215
|
| Code of conduct | [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) |
|
|
192
216
|
| Changelog | [CHANGELOG.md](CHANGELOG.md) |
|
package/browser.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Durable browser helper backed by the Origin Private File System (OPFS).
|
|
2
|
+
import { Db } from "./picovolt.js";
|
|
3
|
+
|
|
4
|
+
export class PersistentDb {
|
|
5
|
+
constructor(name, db) {
|
|
6
|
+
this.name = name;
|
|
7
|
+
this.db = db;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static async open(name = "picovolt.pvdb") {
|
|
11
|
+
if (!globalThis.navigator?.storage?.getDirectory) {
|
|
12
|
+
throw new Error("PicoVolt OPFS persistence is unavailable in this browser/context");
|
|
13
|
+
}
|
|
14
|
+
const root = await navigator.storage.getDirectory();
|
|
15
|
+
const handle = await root.getFileHandle(name, { create: true });
|
|
16
|
+
const file = await handle.getFile();
|
|
17
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
18
|
+
const db = bytes.length ? Db.fromBytes(bytes) : new Db();
|
|
19
|
+
return new PersistentDb(name, db);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
query(sql, params) {
|
|
23
|
+
const json = params === undefined ? this.db.query(sql) : this.db.query(sql, params);
|
|
24
|
+
return JSON.parse(json);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async save() {
|
|
28
|
+
const root = await navigator.storage.getDirectory();
|
|
29
|
+
const handle = await root.getFileHandle(this.name, { create: true });
|
|
30
|
+
const writable = await handle.createWritable();
|
|
31
|
+
await writable.write(this.db.export());
|
|
32
|
+
await writable.close();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async close() {
|
|
36
|
+
await this.save();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default PersistentDb;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "picovolt",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "PicoVolt (PVDB): a polymorphic embedded database engine in Rust.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.4.0",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"picovolt.js",
|
|
14
14
|
"picovolt_bg.js",
|
|
15
15
|
"picovolt.d.ts",
|
|
16
|
-
"sqlite.js"
|
|
16
|
+
"sqlite.js",
|
|
17
|
+
"browser.js",
|
|
18
|
+
"worker.js"
|
|
17
19
|
],
|
|
18
20
|
"main": "picovolt.js",
|
|
19
21
|
"types": "picovolt.d.ts",
|
|
@@ -30,6 +32,8 @@
|
|
|
30
32
|
],
|
|
31
33
|
"exports": {
|
|
32
34
|
".": "./picovolt.js",
|
|
33
|
-
"./sqlite": "./sqlite.js"
|
|
35
|
+
"./sqlite": "./sqlite.js",
|
|
36
|
+
"./browser": "./browser.js",
|
|
37
|
+
"./worker": "./worker.js"
|
|
34
38
|
}
|
|
35
39
|
}
|
package/picovolt_bg.js
CHANGED
|
@@ -135,33 +135,33 @@ export class Db {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
if (Symbol.dispose) Db.prototype[Symbol.dispose] = Db.prototype.free;
|
|
138
|
-
export function
|
|
138
|
+
export function __wbg___wbindgen_boolean_get_c9c83ebd41b34df3(arg0) {
|
|
139
139
|
const v = arg0;
|
|
140
140
|
const ret = typeof(v) === 'boolean' ? v : undefined;
|
|
141
141
|
return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0;
|
|
142
142
|
}
|
|
143
|
-
export function
|
|
143
|
+
export function __wbg___wbindgen_debug_string_a57024b9c6e4a48b(arg0, arg1) {
|
|
144
144
|
const ret = debugString(arg1);
|
|
145
145
|
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
146
146
|
const len1 = WASM_VECTOR_LEN;
|
|
147
147
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
148
148
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
149
149
|
}
|
|
150
|
-
export function
|
|
150
|
+
export function __wbg___wbindgen_is_null_7d13f41e1a2d5140(arg0) {
|
|
151
151
|
const ret = arg0 === null;
|
|
152
152
|
return ret;
|
|
153
153
|
}
|
|
154
|
-
export function
|
|
154
|
+
export function __wbg___wbindgen_is_undefined_6cff064c44e0d823(arg0) {
|
|
155
155
|
const ret = arg0 === undefined;
|
|
156
156
|
return ret;
|
|
157
157
|
}
|
|
158
|
-
export function
|
|
158
|
+
export function __wbg___wbindgen_number_get_136b9679cab35cfb(arg0, arg1) {
|
|
159
159
|
const obj = arg1;
|
|
160
160
|
const ret = typeof(obj) === 'number' ? obj : undefined;
|
|
161
161
|
getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
|
|
162
162
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
|
|
163
163
|
}
|
|
164
|
-
export function
|
|
164
|
+
export function __wbg___wbindgen_string_get_d154f1e671052120(arg0, arg1) {
|
|
165
165
|
const obj = arg1;
|
|
166
166
|
const ret = typeof(obj) === 'string' ? obj : undefined;
|
|
167
167
|
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
@@ -169,14 +169,14 @@ export function __wbg___wbindgen_string_get_b0ca35b86a603356(arg0, arg1) {
|
|
|
169
169
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
170
170
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
171
171
|
}
|
|
172
|
-
export function
|
|
172
|
+
export function __wbg___wbindgen_throw_bb96b2010945f0bc(arg0, arg1) {
|
|
173
173
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
174
174
|
}
|
|
175
|
-
export function
|
|
175
|
+
export function __wbg_call_0f2a9af232c18fd2() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
176
176
|
const ret = arg0.call(arg1, arg2, arg3);
|
|
177
177
|
return ret;
|
|
178
178
|
}, arguments); }
|
|
179
|
-
export function
|
|
179
|
+
export function __wbg_error_757e9472f8410341(arg0, arg1) {
|
|
180
180
|
let deferred0_0;
|
|
181
181
|
let deferred0_1;
|
|
182
182
|
try {
|
|
@@ -187,15 +187,15 @@ export function __wbg_error_a6fa202b58aa1cd3(arg0, arg1) {
|
|
|
187
187
|
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
export function
|
|
190
|
+
export function __wbg_from_74f3d90e0ff11240(arg0) {
|
|
191
191
|
const ret = Array.from(arg0);
|
|
192
192
|
return ret;
|
|
193
193
|
}
|
|
194
|
-
export function
|
|
194
|
+
export function __wbg_get_unchecked_e20b893aeafc3fca(arg0, arg1) {
|
|
195
195
|
const ret = arg0[arg1 >>> 0];
|
|
196
196
|
return ret;
|
|
197
197
|
}
|
|
198
|
-
export function
|
|
198
|
+
export function __wbg_instanceof_Uint8Array_f935dbb0aa7cdeed(arg0) {
|
|
199
199
|
let result;
|
|
200
200
|
try {
|
|
201
201
|
result = arg0 instanceof Uint8Array;
|
|
@@ -205,11 +205,11 @@ export function __wbg_instanceof_Uint8Array_309b927aaf7a3fc7(arg0) {
|
|
|
205
205
|
const ret = result;
|
|
206
206
|
return ret;
|
|
207
207
|
}
|
|
208
|
-
export function
|
|
208
|
+
export function __wbg_length_36bd29c6848c2144(arg0) {
|
|
209
209
|
const ret = arg0.length;
|
|
210
210
|
return ret;
|
|
211
211
|
}
|
|
212
|
-
export function
|
|
212
|
+
export function __wbg_length_ecfa2c63d3d0d82c(arg0) {
|
|
213
213
|
const ret = arg0.length;
|
|
214
214
|
return ret;
|
|
215
215
|
}
|
|
@@ -217,7 +217,7 @@ export function __wbg_new_227d7c05414eb861() {
|
|
|
217
217
|
const ret = new Error();
|
|
218
218
|
return ret;
|
|
219
219
|
}
|
|
220
|
-
export function
|
|
220
|
+
export function __wbg_prototypesetcall_de8e0d9553586985(arg0, arg1, arg2) {
|
|
221
221
|
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
|
222
222
|
}
|
|
223
223
|
export function __wbg_stack_3b0d974bbf31e44f(arg0, arg1) {
|
package/picovolt_bg.wasm
CHANGED
|
Binary file
|
package/sqlite.js
CHANGED
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
// // [ { id: 1, name: "alice" } ]
|
|
10
10
|
//
|
|
11
11
|
// Limitations: parameters are positional `?` only (named `:id` params are not
|
|
12
|
-
// supported);
|
|
12
|
+
// supported); blob parameters are unsupported.
|
|
13
13
|
|
|
14
14
|
import { Db } from "./picovolt.js";
|
|
15
15
|
|
|
16
16
|
function rowToObject(columns, row) {
|
|
17
|
-
|
|
17
|
+
// A null prototype prevents hostile column names such as `__proto__` from
|
|
18
|
+
// changing the shape or prototype of the returned record.
|
|
19
|
+
const obj = Object.create(null);
|
|
18
20
|
for (let i = 0; i < columns.length; i++) obj[columns[i]] = row[i];
|
|
19
21
|
return obj;
|
|
20
22
|
}
|
|
@@ -63,6 +65,7 @@ class Statement {
|
|
|
63
65
|
class Database {
|
|
64
66
|
constructor() {
|
|
65
67
|
this._db = new Db();
|
|
68
|
+
this._inTransaction = false;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
prepare(sql) {
|
|
@@ -91,8 +94,28 @@ class Database {
|
|
|
91
94
|
throw new Error("picovolt: pragma is not supported");
|
|
92
95
|
}
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
// Wrap a synchronous callback in an atomic unit. A compact PVDB snapshot is
|
|
98
|
+
// restored if the callback throws, matching better-sqlite3's common pattern.
|
|
99
|
+
transaction(fn) {
|
|
100
|
+
if (typeof fn !== "function") throw new TypeError("transaction expects a function");
|
|
101
|
+
const db = this;
|
|
102
|
+
function wrapped(...args) {
|
|
103
|
+
if (db._inTransaction) return fn(...args);
|
|
104
|
+
const snapshot = db.serialize();
|
|
105
|
+
db._inTransaction = true;
|
|
106
|
+
try {
|
|
107
|
+
return fn(...args);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
db._db = Db.fromBytes(snapshot);
|
|
110
|
+
throw error;
|
|
111
|
+
} finally {
|
|
112
|
+
db._inTransaction = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
wrapped.deferred = wrapped;
|
|
116
|
+
wrapped.immediate = wrapped;
|
|
117
|
+
wrapped.exclusive = wrapped;
|
|
118
|
+
return wrapped;
|
|
96
119
|
}
|
|
97
120
|
|
|
98
121
|
close() {
|
package/worker.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Module-worker RPC endpoint. Use `new Worker(url, { type: "module" })` and
|
|
2
|
+
// send `{ id, method, ... }`; every response echoes `id` and carries result/error.
|
|
3
|
+
import { PersistentDb } from "./browser.js";
|
|
4
|
+
|
|
5
|
+
let database;
|
|
6
|
+
|
|
7
|
+
self.addEventListener("message", async ({ data }) => {
|
|
8
|
+
const { id, method } = data ?? {};
|
|
9
|
+
try {
|
|
10
|
+
let result;
|
|
11
|
+
switch (method) {
|
|
12
|
+
case "open":
|
|
13
|
+
database = await PersistentDb.open(data.name);
|
|
14
|
+
result = true;
|
|
15
|
+
break;
|
|
16
|
+
case "query":
|
|
17
|
+
if (!database) throw new Error("open the database first");
|
|
18
|
+
result = database.query(data.sql, data.params);
|
|
19
|
+
break;
|
|
20
|
+
case "save":
|
|
21
|
+
if (!database) throw new Error("open the database first");
|
|
22
|
+
await database.save();
|
|
23
|
+
result = true;
|
|
24
|
+
break;
|
|
25
|
+
case "close":
|
|
26
|
+
if (database) await database.close();
|
|
27
|
+
database = undefined;
|
|
28
|
+
result = true;
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(`unknown PicoVolt worker method: ${method}`);
|
|
32
|
+
}
|
|
33
|
+
self.postMessage({ id, result });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
self.postMessage({ id, error: error instanceof Error ? error.message : String(error) });
|
|
36
|
+
}
|
|
37
|
+
});
|