@surrealdb/wasm-native 3.3.4 → 4.0.0-beta.1

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.
Binary file
@@ -1,17 +1,10 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
- export const __wbg_surrealwasmengine_free: (a: number, b: number) => void;
5
- export const surrealwasmengine_connect: (a: number, b: number, c: number) => number;
6
- export const surrealwasmengine_execute: (a: number, b: number, c: number) => number;
7
- export const surrealwasmengine_export: (a: number, b: number, c: number) => number;
8
- export const surrealwasmengine_import: (a: number, b: number, c: number) => number;
9
- export const surrealwasmengine_notifications: (a: number) => number;
10
- export const surrealwasmengine_query_stream: (a: number, b: number, c: number) => number;
11
- export const surrealwasmengine_version: (a: number) => void;
12
4
  export const __wbg_intounderlyingbytesource_free: (a: number, b: number) => void;
13
5
  export const __wbg_intounderlyingsink_free: (a: number, b: number) => void;
14
6
  export const __wbg_intounderlyingsource_free: (a: number, b: number) => void;
7
+ export const __wbg_surrealwasmengine_free: (a: number, b: number) => void;
15
8
  export const intounderlyingbytesource_autoAllocateChunkSize: (a: number) => number;
16
9
  export const intounderlyingbytesource_cancel: (a: number) => void;
17
10
  export const intounderlyingbytesource_pull: (a: number, b: number) => number;
@@ -22,11 +15,28 @@ export const intounderlyingsink_close: (a: number) => number;
22
15
  export const intounderlyingsink_write: (a: number, b: number) => number;
23
16
  export const intounderlyingsource_cancel: (a: number) => void;
24
17
  export const intounderlyingsource_pull: (a: number, b: number) => number;
25
- export const __wasm_bindgen_func_elem_29890: (a: number, b: number, c: number, d: number) => void;
26
- export const __wasm_bindgen_func_elem_4538: (a: number, b: number, c: number, d: number) => void;
27
- export const __wasm_bindgen_func_elem_29891: (a: number, b: number, c: number, d: number) => void;
28
- export const __wasm_bindgen_func_elem_4606: (a: number, b: number, c: number, d: number) => void;
29
- export const __wasm_bindgen_func_elem_8031: (a: number, b: number) => void;
18
+ export const surrealwasmengine_close: (a: number) => number;
19
+ export const surrealwasmengine_connect: (a: number, b: number, c: number) => number;
20
+ export const surrealwasmengine_execute: (a: number, b: number, c: number) => number;
21
+ export const surrealwasmengine_export: (a: number, b: number, c: number) => number;
22
+ export const surrealwasmengine_import: (a: number, b: number, c: number) => number;
23
+ export const surrealwasmengine_notifications: (a: number) => number;
24
+ export const surrealwasmengine_persisted: () => number;
25
+ export const surrealwasmengine_query_stream: (a: number, b: number, c: number) => number;
26
+ export const surrealwasmengine_version: (a: number) => void;
27
+ export const rust_zstd_wasm_shim_calloc: (a: number, b: number) => number;
28
+ export const rust_zstd_wasm_shim_free: (a: number) => void;
29
+ export const rust_zstd_wasm_shim_malloc: (a: number) => number;
30
+ export const rust_zstd_wasm_shim_memcmp: (a: number, b: number, c: number) => number;
31
+ export const rust_zstd_wasm_shim_memcpy: (a: number, b: number, c: number) => number;
32
+ export const rust_zstd_wasm_shim_memmove: (a: number, b: number, c: number) => number;
33
+ export const rust_zstd_wasm_shim_memset: (a: number, b: number, c: number) => number;
34
+ export const rust_zstd_wasm_shim_qsort: (a: number, b: number, c: number, d: number) => void;
35
+ export const __wasm_bindgen_func_elem_5798: (a: number, b: number, c: number, d: number) => void;
36
+ export const __wasm_bindgen_func_elem_5862: (a: number, b: number, c: number, d: number) => void;
37
+ export const __wasm_bindgen_func_elem_46027: (a: number, b: number, c: number) => void;
38
+ export const __wasm_bindgen_func_elem_46027_22: (a: number, b: number, c: number) => void;
39
+ export const __wasm_bindgen_func_elem_9452: (a: number, b: number) => void;
30
40
  export const __wbindgen_export: (a: number, b: number) => number;
31
41
  export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
32
42
  export const __wbindgen_export3: (a: number) => void;
package/dist/worker.js ADDED
@@ -0,0 +1,86 @@
1
+ // ts/worker.ts
2
+ var wasm;
3
+ var engine;
4
+ var inFlight = new Set;
5
+ function tracked(work) {
6
+ inFlight.add(work);
7
+ const done = () => inFlight.delete(work);
8
+ work.then(done, done);
9
+ return work;
10
+ }
11
+ function loaded() {
12
+ if (!wasm)
13
+ throw new Error("The engine module has not been initialised");
14
+ return wasm;
15
+ }
16
+ function connected() {
17
+ if (!engine)
18
+ throw new Error("The engine is not connected");
19
+ return engine;
20
+ }
21
+ function describe(error) {
22
+ return error instanceof Error ? error.message : String(error);
23
+ }
24
+ async function handle(request) {
25
+ switch (request.op) {
26
+ case "init": {
27
+ wasm = await import(new URL("./index.js", import.meta.url).href);
28
+ await wasm.default(request.module ? { module_or_path: request.module } : undefined);
29
+ return {};
30
+ }
31
+ case "connect": {
32
+ if (engine)
33
+ throw new Error("The worker already hosts an engine");
34
+ engine = await loaded().SurrealWasmEngine.connect(request.endpoint, request.options);
35
+ return {};
36
+ }
37
+ case "execute": {
38
+ const value = await tracked(connected().execute(request.data));
39
+ return { value, transfer: [value.buffer] };
40
+ }
41
+ case "queryStream": {
42
+ const value = await tracked(connected().query_stream(request.data));
43
+ return { value, transfer: [value] };
44
+ }
45
+ case "notifications": {
46
+ const value = connected().notifications();
47
+ return { value, transfer: [value] };
48
+ }
49
+ case "export":
50
+ return { value: await tracked(connected().export(request.config)) };
51
+ case "import":
52
+ await tracked(connected().import(request.input));
53
+ return {};
54
+ case "version":
55
+ return { value: loaded().SurrealWasmEngine.version() };
56
+ case "persisted":
57
+ return { value: await loaded().SurrealWasmEngine.persisted() };
58
+ case "close": {
59
+ const closing = engine;
60
+ engine = undefined;
61
+ if (closing) {
62
+ await closing.close();
63
+ await Promise.allSettled([...inFlight]);
64
+ closing.free();
65
+ }
66
+ return {};
67
+ }
68
+ }
69
+ }
70
+ var dedicated = (() => {
71
+ const scope = globalThis.DedicatedWorkerGlobalScope;
72
+ return scope !== undefined && self instanceof scope;
73
+ })();
74
+ self.onmessage = async (event) => {
75
+ if (!dedicated || event.origin !== "")
76
+ return;
77
+ const request = event.data;
78
+ try {
79
+ const { value, transfer } = await handle(request);
80
+ const reply = { id: request.id, ok: true, value };
81
+ self.postMessage(reply, transfer ?? []);
82
+ } catch (error) {
83
+ const reply = { id: request.id, ok: false, error: describe(error) };
84
+ self.postMessage(reply);
85
+ }
86
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@surrealdb/wasm-native",
3
- "version": "3.3.4",
3
+ "version": "4.0.0-beta.1",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "WebAssembly bindings to an embedded SurrealDB engine, for the browser",
@@ -29,6 +29,10 @@
29
29
  "import": "./dist/index.js",
30
30
  "types": "./dist/index.d.ts"
31
31
  },
32
+ "./worker": {
33
+ "import": "./dist/client.js",
34
+ "types": "./dist/client.d.ts"
35
+ },
32
36
  "./module": "./dist/index_bg.wasm"
33
37
  },
34
38
  "files": [
@@ -1,357 +0,0 @@
1
- // IndexedDB driver for the surrealdb-kvs-indxdb backend.
2
- //
3
- // IndexedDB transactions go inactive whenever an await crosses a
4
- // non-IndexedDB microtask, so a single IndexedDB transaction cannot span a
5
- // Rust-side kvs transaction. Instead every read runs in its own short-lived
6
- // readonly transaction, and `Tx.commit` validates the accumulated read-set
7
- // and applies the write-set atomically inside one readwrite transaction that
8
- // only ever awaits IndexedDB requests (which keeps it active).
9
- //
10
- // The database layout matches the layout previously created by the `indxdb`
11
- // crate: a single object store named "kv" (database version 1) with
12
- // out-of-line binary keys and Uint8Array values, so databases created by
13
- // older versions open without migration. Future layout changes must bump the
14
- // database version and migrate in `onupgradeneeded`.
15
-
16
- // Read-state flags, low nibble of a commit entry flag byte.
17
- const READ_UNKNOWN = 0;
18
- const READ_EXISTS = 1;
19
- const READ_READ = 2;
20
- const READ_EMPTY = 3;
21
-
22
- // Write-state flags, high nibble of a commit entry flag byte.
23
- const WRITE_UNCHANGED = 0;
24
- const WRITE_WRITTEN = 1;
25
- const WRITE_DELETED = 2;
26
-
27
- function promiseReq(req) {
28
- return new Promise((resolve, reject) => {
29
- req.onsuccess = () => resolve(req.result);
30
- req.onerror = () => reject(req.error);
31
- });
32
- }
33
-
34
- // IndexedDB hands binary keys back as ArrayBuffer and values in whatever
35
- // shape they were stored in; normalize both to Uint8Array.
36
- function toBytes(value) {
37
- if (value instanceof Uint8Array) {
38
- return value;
39
- }
40
- return new Uint8Array(value);
41
- }
42
-
43
- function bytesEqual(a, b) {
44
- if (a.length !== b.length) {
45
- return false;
46
- }
47
- for (let i = 0; i < a.length; i += 1) {
48
- if (a[i] !== b[i]) {
49
- return false;
50
- }
51
- }
52
- return true;
53
- }
54
-
55
- const EMPTY_BATCH = {
56
- keys: new Uint8Array(0),
57
- keyEnds: new Uint32Array(0),
58
- values: null,
59
- valueEnds: null,
60
- };
61
-
62
- export class Tx {
63
- constructor(db) {
64
- this.db = db;
65
- }
66
-
67
- store(mode) {
68
- return this.db.transaction("kv", mode).objectStore("kv");
69
- }
70
-
71
- async read(key) {
72
- const value = await promiseReq(this.store("readonly").get(key));
73
- return value === undefined ? null : toBytes(value);
74
- }
75
-
76
- async has(key) {
77
- const count = await promiseReq(this.store("readonly").count(key));
78
- return count > 0;
79
- }
80
-
81
- scan(start, end, reverse, limit, keysOnly) {
82
- if (limit === 0) {
83
- // ensure we always return a promise.
84
- return new Promise((res) => res(EMPTY_BATCH));
85
- }
86
-
87
- let range;
88
- try {
89
- // Start inclusive, end exclusive.
90
- range = IDBKeyRange.bound(start, end, false, true);
91
- } catch {
92
- // Thrown when start >= end: an empty range.
93
- // ensure we always return a promise.
94
- return new Promise((res) => res(EMPTY_BATCH));
95
- }
96
-
97
- const keys = [];
98
- const values = keysOnly ? null : [];
99
-
100
- const store = this.store("readonly");
101
- const direction = reverse ? "prev" : "next";
102
- const req = keysOnly
103
- ? store.getAllKeys({ query: range, count: limit, direction })
104
- : store.getAllRecords({ query: range, count: limit, direction })
105
-
106
- return new Promise((resolve, reject) => {
107
- req.onsuccess = () => {
108
-
109
- const results = req.result;
110
- const resLength = results.length;
111
-
112
- let valuesLen = 0;
113
- let keysLen = 0;
114
-
115
- if (keysOnly){
116
- for(let i = 0;i < resLength;i++){
117
- keysLen += results[i].byteLength;
118
- }
119
- }else{
120
- for(let i = 0;i < resLength;i++){
121
- keysLen += results[i].key.byteLength;
122
- valuesLen += results[i].value.byteLength;
123
- }
124
- }
125
-
126
- const keyEnds = new Uint32Array(resLength);
127
-
128
- const keys = new Uint8Array(keysLen);
129
-
130
- if (keysOnly) {
131
- let offset = 0;
132
- for(let i = 0;i < resLength;i++){
133
- const bytes = toBytes(results[i]);
134
- keys.set(bytes,offset);
135
- offset += bytes.length;
136
- keyEnds[i] = offset;
137
- }
138
-
139
- // No values were read: signal that explicitly instead of
140
- // returning empty buffers, which the Rust side would split
141
- // into one empty value per key.
142
- return resolve({
143
- keys,
144
- keyEnds,
145
- values: null,
146
- valueEnds: null,
147
- });
148
- }
149
-
150
- const valueEnds = new Uint32Array(resLength);
151
- const values = new Uint8Array(valuesLen);
152
-
153
- let koffset = 0;
154
- let voffset = 0;
155
- for(let i = 0;i < resLength;i++){
156
-
157
- const kbytes = toBytes(results[i].key);
158
- keys.set(kbytes,koffset);
159
- koffset += kbytes.length;
160
- keyEnds[i] = koffset;
161
-
162
- const vbytes = toBytes(results[i].value);
163
- values.set(vbytes,voffset);
164
- voffset += vbytes.length;
165
- valueEnds[i] = voffset;
166
- }
167
-
168
- return resolve({
169
- keys,
170
- keyEnds,
171
- values,
172
- valueEnds,
173
- });
174
-
175
-
176
- };
177
- req.onerror = () => reject(req.error);
178
- });
179
-
180
- }
181
-
182
- async commit(keys, keySlices, read, readSlices, written, writtenSlices, flags) {
183
- // The typed-array arguments may be views into wasm memory. While this
184
- // function awaits, other wasm tasks can run and grow the wasm memory,
185
- // detaching those views. So we need to copy read before the first await as it
186
- // might be used after an await.
187
- read = read.slice();
188
-
189
- const tx = this.db.transaction("kv", "readwrite");
190
- const store = tx.objectStore("kv");
191
-
192
- let readIdx = 0;
193
- let writeIdx = 0;
194
-
195
- // To improve speed we issue all read checks in parallel and do not wait for
196
- // the request to return instead checking once at the end if all the checks
197
- // succeeded.
198
- let conflict = false;
199
- let pendingChecks = 0;
200
-
201
- const fail = () => {
202
- if (!conflict) {
203
- conflict = true;
204
- tx.abort();
205
- }
206
- };
207
-
208
- // Explicitly committing skips one idle event-loop turn, but is only
209
- // legal once no validation can still abort: after commit() the
210
- // transaction enters the committing state where abort() throws.
211
- const checkDone = () => {
212
- pendingChecks -= 1;
213
- if (pendingChecks === 0 && !conflict && tx.commit) {
214
- tx.commit();
215
- }
216
- };
217
-
218
- // All requests are fired without awaiting; IndexedDB executes them in
219
- // issue order within the transaction, so every validation read of a
220
- // key observes the pre-transaction state (each key appears at most
221
- // once). This pipelines the whole commit into a single pass instead
222
- // of one event-loop round trip per validated key.
223
- for (let i = 0; i < flags.length; i += 1) {
224
- const readFlag = flags[i] & 0xf;
225
- const writeFlag = flags[i] >> 4;
226
-
227
- const key = keys.subarray(keySlices[i * 2], keySlices[i * 2 + 1]);
228
-
229
- switch (readFlag) {
230
- case READ_EMPTY: {
231
- pendingChecks += 1;
232
- const req = store.count(key);
233
- req.onsuccess = () => {
234
- if (req.result !== 0) {
235
- fail();
236
- } else {
237
- checkDone();
238
- }
239
- };
240
- break;
241
- }
242
- case READ_EXISTS: {
243
- pendingChecks += 1;
244
- const req = store.count(key);
245
- req.onsuccess = () => {
246
- if (req.result === 0) {
247
- fail();
248
- } else {
249
- checkDone();
250
- }
251
- };
252
- break;
253
- }
254
- case READ_READ: {
255
- const idx = readIdx;
256
- readIdx += 1;
257
- const expect = read.subarray(readSlices[idx * 2], readSlices[idx * 2 + 1]);
258
- pendingChecks += 1;
259
- const req = store.get(key);
260
- req.onsuccess = () => {
261
- const value = req.result;
262
- if (value === undefined || !bytesEqual(toBytes(value), expect)) {
263
- fail();
264
- } else {
265
- checkDone();
266
- }
267
- };
268
- break;
269
- }
270
- default:
271
- break;
272
- }
273
-
274
- switch (writeFlag) {
275
- case WRITE_DELETED: {
276
- store.delete(key);
277
- break;
278
- }
279
- case WRITE_WRITTEN: {
280
- const idx = writeIdx;
281
- writeIdx += 1;
282
- // slice(), not subarray(): the structured clone of a view
283
- // serializes its entire backing buffer, so storing a
284
- // subarray would persist a copy of the whole packed
285
- // commit payload with every entry.
286
- const value = written.slice(writtenSlices[idx * 2], writtenSlices[idx * 2 + 1]);
287
- store.put(value, key);
288
- break;
289
- }
290
- default:
291
- break;
292
- }
293
- }
294
-
295
- const applied = await new Promise((resolve, reject) => {
296
- tx.oncomplete = () => resolve(true);
297
- tx.onabort = () =>
298
- conflict ? resolve(false) : reject(tx.error ?? new Error("transaction aborted"));
299
- // Swallow per-request error events (an abort after a failed
300
- // validation errors every later queued request) without
301
- // cancelling their default action, so a genuine request failure
302
- // still aborts the transaction and surfaces via onabort.
303
- tx.onerror = () => {};
304
- if (pendingChecks === 0 && tx.commit) {
305
- tx.commit();
306
- }
307
- });
308
- return applied;
309
- }
310
- }
311
-
312
- export class Db {
313
- constructor(db) {
314
- this.db = db;
315
- }
316
-
317
- begin() {
318
- return new Tx(this.db);
319
- }
320
-
321
- close() {
322
- this.db.close();
323
- }
324
- }
325
-
326
- export function open(name) {
327
- return new Promise((resolve, reject) => {
328
- if (typeof indexedDB === "undefined") {
329
- reject(new Error("IndexedDB api is not supported on this platform"));
330
- return;
331
- }
332
-
333
- const req = indexedDB.open(name, 1);
334
-
335
- req.onerror = () => reject(req.error);
336
-
337
- req.onupgradeneeded = () => {
338
- // Fresh database: create the store in the indxdb-crate-compatible
339
- // layout described at the top of this file.
340
- req.result.createObjectStore("kv");
341
- };
342
-
343
- req.onsuccess = () => {
344
- const db = req.result;
345
- if (!db.objectStoreNames.contains("kv")) {
346
- db.close();
347
- reject(new Error(`indexedDB database ${name} is missing the "kv" object store`));
348
- } else {
349
- resolve(new Db(db));
350
- }
351
- };
352
- });
353
- }
354
-
355
- export function to_string(v){
356
- return v.toString()
357
- }