@loradb/lora-wasm 0.5.6 → 0.8.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.
Files changed (45) hide show
  1. package/README.md +76 -34
  2. package/dist/decode.d.ts +29 -0
  3. package/dist/decode.d.ts.map +1 -0
  4. package/dist/decode.js +338 -0
  5. package/dist/decode.js.map +1 -0
  6. package/dist/index.d.ts +21 -31
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +35 -37
  9. package/dist/index.js.map +1 -1
  10. package/dist/loader-node.d.ts +1 -1
  11. package/dist/snapshot.d.ts +49 -16
  12. package/dist/snapshot.d.ts.map +1 -1
  13. package/dist/snapshot.js +36 -54
  14. package/dist/snapshot.js.map +1 -1
  15. package/dist/types.d.ts +81 -7
  16. package/dist/types.d.ts.map +1 -1
  17. package/dist/types.js +10 -2
  18. package/dist/types.js.map +1 -1
  19. package/dist/worker-client.d.ts +20 -20
  20. package/dist/worker-client.d.ts.map +1 -1
  21. package/dist/worker-client.js +32 -22
  22. package/dist/worker-client.js.map +1 -1
  23. package/dist/worker-protocol.d.ts +5 -2
  24. package/dist/worker-protocol.d.ts.map +1 -1
  25. package/dist/worker.js +18 -10
  26. package/dist/worker.js.map +1 -1
  27. package/package.json +5 -5
  28. package/pkg-bundler/README.md +76 -34
  29. package/pkg-bundler/lora_wasm.d.ts +37 -10
  30. package/pkg-bundler/lora_wasm_bg.js +173 -26
  31. package/pkg-bundler/lora_wasm_bg.wasm +0 -0
  32. package/pkg-bundler/lora_wasm_bg.wasm.d.ts +5 -2
  33. package/pkg-bundler/package.json +1 -1
  34. package/pkg-node/README.md +76 -34
  35. package/pkg-node/lora_wasm.d.ts +37 -10
  36. package/pkg-node/lora_wasm.js +173 -26
  37. package/pkg-node/lora_wasm_bg.wasm +0 -0
  38. package/pkg-node/lora_wasm_bg.wasm.d.ts +5 -2
  39. package/pkg-node/package.json +1 -1
  40. package/pkg-web/README.md +76 -34
  41. package/pkg-web/lora_wasm.d.ts +42 -12
  42. package/pkg-web/lora_wasm.js +156 -26
  43. package/pkg-web/lora_wasm_bg.wasm +0 -0
  44. package/pkg-web/lora_wasm_bg.wasm.d.ts +5 -2
  45. package/pkg-web/package.json +1 -1
@@ -21,23 +21,35 @@ export class WasmDatabase {
21
21
  * transaction. Returns an array of query results in statement order.
22
22
  */
23
23
  transaction(statements: any, mode?: string | null): any;
24
- relationshipCount(): number;
25
24
  /**
26
- * Serialize the graph into a byte buffer. The caller is responsible for
27
- * writing it to disk, IndexedDB, localStorage, or wherever — WASM does
28
- * not have direct filesystem access. The returned bytes can later be
29
- * passed to `loadSnapshotFromBytes` on any `WasmDatabase` instance.
25
+ * Replace the graph state with a database snapshot decoded from `bytes`.
26
+ * Legacy store snapshot bytes are accepted for compatibility.
27
+ *
28
+ * Returns a plain object matching the shape of `SnapshotMeta`:
29
+ * `{ formatVersion, nodeCount, relationshipCount, walLsn }`.
30
+ */
31
+ loadSnapshot(bytes: Uint8Array, options: any): any;
32
+ /**
33
+ * Serialize the graph into database snapshot bytes. The caller is
34
+ * responsible for writing them to IndexedDB, localStorage, a server, or
35
+ * another host-provided store — WASM has no direct filesystem access.
36
+ * The returned bytes can later be passed to `loadSnapshot` on any
37
+ * `WasmDatabase` instance.
30
38
  *
31
39
  * Returns the serialized bytes as a `Uint8Array`.
32
40
  */
33
- saveSnapshotToBytes(): Uint8Array;
41
+ saveSnapshot(options: any): Uint8Array;
34
42
  /**
35
- * Replace the graph state with a snapshot decoded from `bytes`.
43
+ * Execute a Lora query and return the result as a binary buffer.
36
44
  *
37
- * Returns a plain object matching the shape of `SnapshotMeta`:
38
- * `{ formatVersion, nodeCount, relationshipCount, walLsn }`.
45
+ * Wire format is the shared `lora_binding_buffer` layout. The TS
46
+ * wrapper decodes it once into the same `{ columns, rows }` shape
47
+ * `execute()` returns, but in a tight V8 loop instead of going
48
+ * through `serde_wasm_bindgen` value-by-value. Materially faster
49
+ * on bulk reads.
39
50
  */
40
- loadSnapshotFromBytes(bytes: Uint8Array): any;
51
+ executeBuffer(query: string, params: any): Uint8Array;
52
+ relationshipCount(): number;
41
53
  constructor();
42
54
  clear(): void;
43
55
  /**
@@ -48,6 +60,21 @@ export class WasmDatabase {
48
60
  * as a plain JS object (structured-clonable).
49
61
  */
50
62
  execute(query: string, params: any): any;
63
+ /**
64
+ * Compile a query and return its plan without executing it.
65
+ *
66
+ * Mutating queries (CREATE / MERGE / SET / DELETE / REMOVE) leave
67
+ * the graph untouched — this method never runs the executor.
68
+ */
69
+ explain(query: string, params: any): any;
70
+ /**
71
+ * Execute a query and return the plan plus runtime metrics.
72
+ *
73
+ * **PROFILE executes the query for real.** Mutating queries are
74
+ * persisted exactly as in `execute()`. Use `explain()` to inspect
75
+ * a mutating plan without running it.
76
+ */
77
+ profile(query: string, params: any): any;
51
78
  }
52
79
  export class WasmQueryStream {
53
80
  private constructor();
@@ -181,17 +181,17 @@ function takeFromExternrefTable0(idx) {
181
181
  return value;
182
182
  }
183
183
 
184
- function getArrayU8FromWasm0(ptr, len) {
185
- ptr = ptr >>> 0;
186
- return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
187
- }
188
-
189
184
  function passArray8ToWasm0(arg, malloc) {
190
185
  const ptr = malloc(arg.length * 1, 1) >>> 0;
191
186
  getUint8ArrayMemory0().set(arg, ptr / 1);
192
187
  WASM_VECTOR_LEN = arg.length;
193
188
  return ptr;
194
189
  }
190
+
191
+ function getArrayU8FromWasm0(ptr, len) {
192
+ ptr = ptr >>> 0;
193
+ return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
194
+ }
195
195
  /**
196
196
  * Call once at module start to install a panic hook that routes Rust
197
197
  * panics to `console.error`. No-op if compiled without the default feature.
@@ -259,23 +259,37 @@ export class WasmDatabase {
259
259
  return takeFromExternrefTable0(ret[0]);
260
260
  }
261
261
  /**
262
- * @returns {number}
262
+ * Replace the graph state with a database snapshot decoded from `bytes`.
263
+ * Legacy store snapshot bytes are accepted for compatibility.
264
+ *
265
+ * Returns a plain object matching the shape of `SnapshotMeta`:
266
+ * `{ formatVersion, nodeCount, relationshipCount, walLsn }`.
267
+ * @param {Uint8Array} bytes
268
+ * @param {any} options
269
+ * @returns {any}
263
270
  */
264
- relationshipCount() {
265
- const ret = wasm.wasmdatabase_relationshipCount(this.__wbg_ptr);
266
- return ret >>> 0;
271
+ loadSnapshot(bytes, options) {
272
+ const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
273
+ const len0 = WASM_VECTOR_LEN;
274
+ const ret = wasm.wasmdatabase_loadSnapshot(this.__wbg_ptr, ptr0, len0, options);
275
+ if (ret[2]) {
276
+ throw takeFromExternrefTable0(ret[1]);
277
+ }
278
+ return takeFromExternrefTable0(ret[0]);
267
279
  }
268
280
  /**
269
- * Serialize the graph into a byte buffer. The caller is responsible for
270
- * writing it to disk, IndexedDB, localStorage, or wherever — WASM does
271
- * not have direct filesystem access. The returned bytes can later be
272
- * passed to `loadSnapshotFromBytes` on any `WasmDatabase` instance.
281
+ * Serialize the graph into database snapshot bytes. The caller is
282
+ * responsible for writing them to IndexedDB, localStorage, a server, or
283
+ * another host-provided store — WASM has no direct filesystem access.
284
+ * The returned bytes can later be passed to `loadSnapshot` on any
285
+ * `WasmDatabase` instance.
273
286
  *
274
287
  * Returns the serialized bytes as a `Uint8Array`.
288
+ * @param {any} options
275
289
  * @returns {Uint8Array}
276
290
  */
277
- saveSnapshotToBytes() {
278
- const ret = wasm.wasmdatabase_saveSnapshotToBytes(this.__wbg_ptr);
291
+ saveSnapshot(options) {
292
+ const ret = wasm.wasmdatabase_saveSnapshot(this.__wbg_ptr, options);
279
293
  if (ret[3]) {
280
294
  throw takeFromExternrefTable0(ret[2]);
281
295
  }
@@ -284,21 +298,34 @@ export class WasmDatabase {
284
298
  return v1;
285
299
  }
286
300
  /**
287
- * Replace the graph state with a snapshot decoded from `bytes`.
301
+ * Execute a Lora query and return the result as a binary buffer.
288
302
  *
289
- * Returns a plain object matching the shape of `SnapshotMeta`:
290
- * `{ formatVersion, nodeCount, relationshipCount, walLsn }`.
291
- * @param {Uint8Array} bytes
292
- * @returns {any}
303
+ * Wire format is the shared `lora_binding_buffer` layout. The TS
304
+ * wrapper decodes it once into the same `{ columns, rows }` shape
305
+ * `execute()` returns, but in a tight V8 loop instead of going
306
+ * through `serde_wasm_bindgen` value-by-value. Materially faster
307
+ * on bulk reads.
308
+ * @param {string} query
309
+ * @param {any} params
310
+ * @returns {Uint8Array}
293
311
  */
294
- loadSnapshotFromBytes(bytes) {
295
- const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
312
+ executeBuffer(query, params) {
313
+ const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
296
314
  const len0 = WASM_VECTOR_LEN;
297
- const ret = wasm.wasmdatabase_loadSnapshotFromBytes(this.__wbg_ptr, ptr0, len0);
298
- if (ret[2]) {
299
- throw takeFromExternrefTable0(ret[1]);
315
+ const ret = wasm.wasmdatabase_executeBuffer(this.__wbg_ptr, ptr0, len0, params);
316
+ if (ret[3]) {
317
+ throw takeFromExternrefTable0(ret[2]);
300
318
  }
301
- return takeFromExternrefTable0(ret[0]);
319
+ var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
320
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
321
+ return v2;
322
+ }
323
+ /**
324
+ * @returns {number}
325
+ */
326
+ relationshipCount() {
327
+ const ret = wasm.wasmdatabase_relationshipCount(this.__wbg_ptr);
328
+ return ret >>> 0;
302
329
  }
303
330
  constructor() {
304
331
  const ret = wasm.wasmdatabase_new();
@@ -328,6 +355,43 @@ export class WasmDatabase {
328
355
  }
329
356
  return takeFromExternrefTable0(ret[0]);
330
357
  }
358
+ /**
359
+ * Compile a query and return its plan without executing it.
360
+ *
361
+ * Mutating queries (CREATE / MERGE / SET / DELETE / REMOVE) leave
362
+ * the graph untouched — this method never runs the executor.
363
+ * @param {string} query
364
+ * @param {any} params
365
+ * @returns {any}
366
+ */
367
+ explain(query, params) {
368
+ const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
369
+ const len0 = WASM_VECTOR_LEN;
370
+ const ret = wasm.wasmdatabase_explain(this.__wbg_ptr, ptr0, len0, params);
371
+ if (ret[2]) {
372
+ throw takeFromExternrefTable0(ret[1]);
373
+ }
374
+ return takeFromExternrefTable0(ret[0]);
375
+ }
376
+ /**
377
+ * Execute a query and return the plan plus runtime metrics.
378
+ *
379
+ * **PROFILE executes the query for real.** Mutating queries are
380
+ * persisted exactly as in `execute()`. Use `explain()` to inspect
381
+ * a mutating plan without running it.
382
+ * @param {string} query
383
+ * @param {any} params
384
+ * @returns {any}
385
+ */
386
+ profile(query, params) {
387
+ const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
388
+ const len0 = WASM_VECTOR_LEN;
389
+ const ret = wasm.wasmdatabase_profile(this.__wbg_ptr, ptr0, len0, params);
390
+ if (ret[2]) {
391
+ throw takeFromExternrefTable0(ret[1]);
392
+ }
393
+ return takeFromExternrefTable0(ret[0]);
394
+ }
331
395
  }
332
396
 
333
397
  const WasmQueryStreamFinalization = (typeof FinalizationRegistry === 'undefined')
@@ -398,6 +462,16 @@ export function __wbg_call_672a4d21634d4a24() { return handleError(function (arg
398
462
  return ret;
399
463
  }, arguments) };
400
464
 
465
+ export function __wbg_call_7cccdd69e0791ae2() { return handleError(function (arg0, arg1, arg2) {
466
+ const ret = arg0.call(arg1, arg2);
467
+ return ret;
468
+ }, arguments) };
469
+
470
+ export function __wbg_crypto_86f2631e91b51511(arg0) {
471
+ const ret = arg0.crypto;
472
+ return ret;
473
+ };
474
+
401
475
  export function __wbg_done_769e5ede4b31c67b(arg0) {
402
476
  const ret = arg0.done;
403
477
  return ret;
@@ -420,6 +494,10 @@ export function __wbg_error_7534b8e9a36f1ab4(arg0, arg1) {
420
494
  }
421
495
  };
422
496
 
497
+ export function __wbg_getRandomValues_b3f15fcbfabb0f8b() { return handleError(function (arg0, arg1) {
498
+ arg0.getRandomValues(arg1);
499
+ }, arguments) };
500
+
423
501
  export function __wbg_get_67b2ba62fc30de12() { return handleError(function (arg0, arg1) {
424
502
  const ret = Reflect.get(arg0, arg1);
425
503
  return ret;
@@ -488,6 +566,11 @@ export function __wbg_length_e2d2a49132c1b256(arg0) {
488
566
  return ret;
489
567
  };
490
568
 
569
+ export function __wbg_msCrypto_d562bbe83e0d4b91(arg0) {
570
+ const ret = arg0.msCrypto;
571
+ return ret;
572
+ };
573
+
491
574
  export function __wbg_new_405e22f390576ce2() {
492
575
  const ret = new Object();
493
576
  return ret;
@@ -513,6 +596,21 @@ export function __wbg_new_a12002a7f91c75be(arg0) {
513
596
  return ret;
514
597
  };
515
598
 
599
+ export function __wbg_newnoargs_105ed471475aaf50(arg0, arg1) {
600
+ const ret = new Function(getStringFromWasm0(arg0, arg1));
601
+ return ret;
602
+ };
603
+
604
+ export function __wbg_newwithbyteoffsetandlength_d97e637ebe145a9a(arg0, arg1, arg2) {
605
+ const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0);
606
+ return ret;
607
+ };
608
+
609
+ export function __wbg_newwithlength_a381634e90c276d4(arg0) {
610
+ const ret = new Uint8Array(arg0 >>> 0);
611
+ return ret;
612
+ };
613
+
516
614
  export function __wbg_next_25feadfc0913fea9(arg0) {
517
615
  const ret = arg0.next;
518
616
  return ret;
@@ -523,11 +621,30 @@ export function __wbg_next_6574e1a8a62d1055() { return handleError(function (arg
523
621
  return ret;
524
622
  }, arguments) };
525
623
 
624
+ export function __wbg_node_e1f24f89a7336c2e(arg0) {
625
+ const ret = arg0.node;
626
+ return ret;
627
+ };
628
+
526
629
  export function __wbg_now_807e54c39636c349() {
527
630
  const ret = Date.now();
528
631
  return ret;
529
632
  };
530
633
 
634
+ export function __wbg_process_3975fd6c72f520aa(arg0) {
635
+ const ret = arg0.process;
636
+ return ret;
637
+ };
638
+
639
+ export function __wbg_randomFillSync_f8c153b79f285817() { return handleError(function (arg0, arg1) {
640
+ arg0.randomFillSync(arg1);
641
+ }, arguments) };
642
+
643
+ export function __wbg_require_b74f47fc2d022fd6() { return handleError(function () {
644
+ const ret = module.require;
645
+ return ret;
646
+ }, arguments) };
647
+
531
648
  export function __wbg_set_37837023f3d740e8(arg0, arg1, arg2) {
532
649
  arg0[arg1 >>> 0] = arg2;
533
650
  };
@@ -553,11 +670,41 @@ export function __wbg_stack_0ed75d68575b0f3c(arg0, arg1) {
553
670
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
554
671
  };
555
672
 
673
+ export function __wbg_static_accessor_GLOBAL_88a902d13a557d07() {
674
+ const ret = typeof global === 'undefined' ? null : global;
675
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
676
+ };
677
+
678
+ export function __wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0() {
679
+ const ret = typeof globalThis === 'undefined' ? null : globalThis;
680
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
681
+ };
682
+
683
+ export function __wbg_static_accessor_SELF_37c5d418e4bf5819() {
684
+ const ret = typeof self === 'undefined' ? null : self;
685
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
686
+ };
687
+
688
+ export function __wbg_static_accessor_WINDOW_5de37043a91a9c40() {
689
+ const ret = typeof window === 'undefined' ? null : window;
690
+ return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
691
+ };
692
+
693
+ export function __wbg_subarray_aa9065fa9dc5df96(arg0, arg1, arg2) {
694
+ const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0);
695
+ return ret;
696
+ };
697
+
556
698
  export function __wbg_value_cd1ffa7b1ab794f1(arg0) {
557
699
  const ret = arg0.value;
558
700
  return ret;
559
701
  };
560
702
 
703
+ export function __wbg_versions_4e31226f5e8dc909(arg0) {
704
+ const ret = arg0.versions;
705
+ return ret;
706
+ };
707
+
561
708
  export function __wbindgen_bigint_from_i64(arg0) {
562
709
  const ret = arg0;
563
710
  return ret;
Binary file
@@ -5,12 +5,15 @@ export const __wbg_wasmdatabase_free: (a: number, b: number) => void;
5
5
  export const __wbg_wasmquerystream_free: (a: number, b: number) => void;
6
6
  export const wasmdatabase_clear: (a: number) => void;
7
7
  export const wasmdatabase_execute: (a: number, b: number, c: number, d: any) => [number, number, number];
8
- export const wasmdatabase_loadSnapshotFromBytes: (a: number, b: number, c: number) => [number, number, number];
8
+ export const wasmdatabase_executeBuffer: (a: number, b: number, c: number, d: any) => [number, number, number, number];
9
+ export const wasmdatabase_explain: (a: number, b: number, c: number, d: any) => [number, number, number];
10
+ export const wasmdatabase_loadSnapshot: (a: number, b: number, c: number, d: any) => [number, number, number];
9
11
  export const wasmdatabase_new: () => number;
10
12
  export const wasmdatabase_nodeCount: (a: number) => number;
11
13
  export const wasmdatabase_openStream: (a: number, b: number, c: number, d: any) => [number, number, number];
14
+ export const wasmdatabase_profile: (a: number, b: number, c: number, d: any) => [number, number, number];
12
15
  export const wasmdatabase_relationshipCount: (a: number) => number;
13
- export const wasmdatabase_saveSnapshotToBytes: (a: number) => [number, number, number, number];
16
+ export const wasmdatabase_saveSnapshot: (a: number, b: any) => [number, number, number, number];
14
17
  export const wasmdatabase_transaction: (a: number, b: any, c: number, d: number) => [number, number, number];
15
18
  export const wasmquerystream_close: (a: number) => void;
16
19
  export const wasmquerystream_columns: (a: number) => [number, number, number];
@@ -5,7 +5,7 @@
5
5
  "LoraDB, Inc."
6
6
  ],
7
7
  "description": "WebAssembly bindings for the Lora in-memory graph database",
8
- "version": "0.5.6",
8
+ "version": "0.8.0",
9
9
  "license": "BUSL-1.1",
10
10
  "files": [
11
11
  "lora_wasm_bg.wasm",
@@ -1,17 +1,17 @@
1
1
  # lora-wasm
2
2
 
3
- WebAssembly bindings for the [Lora](../../README.md) in-memory graph
3
+ WebAssembly bindings for the [Lora](../../../README.md) in-memory graph
4
4
  database. The package is designed for browsers and Node.js and exposes a
5
5
  **strongly typed, async-facing API** that keeps the main thread responsive:
6
6
  heavy query work can run inside a Web Worker while your UI code simply awaits
7
7
  the result.
8
8
 
9
- > **Status:** prototype / feasibility check. Not published to npm.
9
+ > **Package:** `@loradb/lora-wasm`.
10
10
 
11
11
  ## Build
12
12
 
13
13
  ```bash
14
- cd crates/lora-wasm
14
+ cd crates/bindings/lora-wasm
15
15
  npm install
16
16
  npm run build # wasm-pack (node + bundler) + tsc
17
17
  npm test # vitest
@@ -52,7 +52,7 @@ fails during startup, the package emits one `console.warn` and falls back to
52
52
  the in-process WASM engine so the app can still run.
53
53
 
54
54
  ```ts
55
- import { createDatabase, isNode } from "lora-wasm";
55
+ import { createDatabase, isNode } from "@loradb/lora-wasm";
56
56
 
57
57
  const db = await createDatabase(); // Worker-backed in browsers when possible
58
58
  await db.execute("CREATE (:Person {name: $n})", { n: "Alice" });
@@ -63,6 +63,37 @@ for (const row of r.rows) {
63
63
  }
64
64
  ```
65
65
 
66
+ ### Explain & Profile
67
+
68
+ `db.explain()` and `db.profile()` are first-class methods alongside
69
+ `db.execute()`. They are intentionally *separate calls*, not a flag
70
+ on `execute()`, so plan inspection and runtime metrics must be
71
+ requested explicitly.
72
+
73
+ ```ts
74
+ const plan = await db.explain(
75
+ "MATCH (p:Person) WHERE p.name = $name RETURN p",
76
+ { name: "Alice" },
77
+ );
78
+ console.log(plan.shape); // "readOnly"
79
+ console.log(plan.tree.operator);
80
+
81
+ const profile = await db.profile(
82
+ "MATCH (p:Person) WHERE p.name = $name RETURN p",
83
+ { name: "Alice" },
84
+ );
85
+ console.log(profile.metrics.totalElapsedNs);
86
+ console.log(profile.metrics.perOperator); // per-step inclusive timing
87
+ ```
88
+
89
+ `explain()` never invokes the executor — calling it on a mutating
90
+ query (`CREATE`, `MERGE`, `SET`, `DELETE`, `REMOVE`) leaves the graph
91
+ untouched.
92
+
93
+ > **`profile()` executes the query for real.** Mutating queries
94
+ > produce the same side effects as `execute()`. Use `explain()` to
95
+ > inspect a mutating plan without running it.
96
+
66
97
  To force the in-process engine:
67
98
 
68
99
  ```ts
@@ -84,7 +115,7 @@ const db = await createDatabase({ warnOnFallback: false });
84
115
  ### 2. Explicit main-thread WASM
85
116
 
86
117
  ```ts
87
- import { createMainThreadDatabase } from "lora-wasm";
118
+ import { createMainThreadDatabase } from "@loradb/lora-wasm";
88
119
 
89
120
  const db = await createMainThreadDatabase();
90
121
  ```
@@ -116,35 +147,36 @@ useful for tests and for swapping execution backends behind the same API.
116
147
 
117
148
  ## Snapshots
118
149
 
119
- WASM has no filesystem access, so `saveSnapshot()` defaults to a `Uint8Array`.
120
- It also accepts typed formats: `"base64"` returns base64 text, `"blob"` returns
121
- a `Blob`, and `"download"` triggers a browser download from the main thread.
122
-
123
- `loadSnapshot` accepts a `WasmSnapshotSource`: `Uint8Array`, `ArrayBuffer`,
124
- `Blob`/`File`, `Response`, web `ReadableStream`, string URL, or `URL`. It does
125
- not include Node `Buffer`; use `Uint8Array` bytes when running the WASM package
126
- from Node.
150
+ WASM has no filesystem access, so snapshots never accept string paths.
151
+ `saveSnapshot()` defaults to `Uint8Array`, and can also return `ArrayBuffer`,
152
+ `Blob`, `Response`, `ReadableStream`, or an object `URL`. `loadSnapshot`
153
+ accepts `URL`, `Uint8Array`, `ArrayBuffer`, `Blob`, `Response`, and web
154
+ `ReadableStream<Uint8Array | ArrayBuffer>`. The bytes use the database
155
+ snapshot codec (`LORACOL1`) and can be loaded by native LoraDB. Older store
156
+ snapshots use the current `LORACOL1` columnar format.
157
+ Snapshot bytes are uncompressed by default; pass `{ compression: "gzip" }` or
158
+ `{ compression: { format: "gzip", level: 1 } }` to save smaller
159
+ WASM-portable snapshots. Gzip levels `0..9` are supported; level `1` is the
160
+ fast default.
127
161
 
128
162
  ```ts
129
- import { createDatabase } from "lora-wasm";
163
+ import { createDatabase } from "@loradb/lora-wasm";
130
164
 
131
165
  const db = await createDatabase();
132
166
  await db.execute("CREATE (:Person {name: 'Alice'})");
133
167
 
134
- const bytes = await db.saveSnapshotToBytes();
135
- const sameBytes = await db.saveSnapshot("binary");
136
- const blob = await db.saveSnapshot({ format: "blob" });
137
- await db.saveSnapshot({ format: "download", filename: "graph.lorasnap" });
168
+ const bytes = await db.saveSnapshot();
169
+ const compressed = await db.saveSnapshot({ compression: "gzip" });
170
+ const blob = await db.saveSnapshot({ format: "blob", compression: "gzip" });
171
+ const response = await db.saveSnapshot({ format: "response" });
138
172
 
139
173
  await db.loadSnapshot(bytes);
174
+ await db.loadSnapshot(compressed);
140
175
  await db.loadSnapshot(blob);
141
- await db.loadSnapshot(new Response(bytes).body!);
176
+ await db.loadSnapshot(response);
142
177
  await db.loadSnapshot(new URL("/graph.lorasnap", location.href));
143
178
  ```
144
179
 
145
- `loadSnapshotFromBytes(bytes)` remains available for the narrow
146
- `Uint8Array`-only path.
147
-
148
180
  ## Typed value model
149
181
 
150
182
  | TS type | Runtime shape |
@@ -171,31 +203,42 @@ Helper constructors: `date`, `time`, `datetime`, `localtime`, `localdatetime`,
171
203
  `isNode`, `isRelationship`, `isPath`, `isPoint`, `isTemporal`.
172
204
 
173
205
  > `distance()` on WGS-84-3D points ignores `height`. See
174
- > [functions reference](../../apps/loradb.com/docs/functions/overview.md) for the spatial
206
+ > [functions reference](../../../apps/loradb.com/docs/functions/overview.md) for the spatial
175
207
  > reference and out-of-scope operations.
176
208
 
177
209
  ## Errors
178
210
 
179
- `db.execute(...)` and the worker client throw `LoraError` with a narrowed
180
- `code`:
211
+ `db.execute(...)` and the worker client throw `LoraError` with a
212
+ narrowed `code` from the `LoraErrorCode` union — these mirror
213
+ `lora_database::LoraErrorCode` 1:1, plus `WORKER_ERROR` and `UNKNOWN`.
181
214
 
182
- - `LORA_ERROR` — parse / analyze / execute failure
183
- - `INVALID_PARAMS` — a param value could not be mapped to a Lora value
215
+ Common ones:
216
+
217
+ - `LORA_PARSE` — Cypher syntax could not be parsed
218
+ - `LORA_SEMANTIC` — analysis failure (unknown variable, label, type mismatch, …)
219
+ - `LORA_INVALID_PARAMS` — a parameter value could not be coerced to a `LoraValue`
220
+ - `LORA_INVALID_VECTOR` — vector value failed dimension / coordinate-type validation
221
+ - `LORA_TIMEOUT` — query exceeded its cooperative deadline
222
+ - `LORA_IO`, `LORA_SNAPSHOT_CODEC`, `LORA_SNAPSHOT_CRYPTO` — storage / snapshot failures
223
+ - `LORA_INTERNAL` — last-resort fallback when the engine cannot classify the failure
184
224
  - `WORKER_ERROR` — worker transport / lifecycle failure (worker client only)
185
225
 
226
+ See `ts/types.ts` (`LoraErrorCode`) for the full list.
227
+
186
228
  ## Shared type contract
187
229
 
188
230
  The public TypeScript value model (`LoraValue`, `LoraNode`, …,
189
231
  `QueryResult`, `LoraError`) lives in a single canonical file at
190
- `crates/shared-ts/types.ts` and is copied into each consumer package by
232
+ `crates/bindings/shared-ts/types.ts` and is copied into each consumer package by
191
233
  its `sync:types` npm script. CI runs `verify:types` to fail on drift.
192
234
  That keeps `lora-node` and `lora-wasm` locked to one identical
193
235
  public surface — consumers can swap backends without rewriting types.
194
236
 
195
237
  ## Known limitations
196
238
 
197
- - The wasm module is single-threaded. Parallel queries inside one worker
198
- serialise; spawn more workers for true parallelism.
239
+ - The wasm module runs inside one JavaScript runtime. Auto-commit reads can
240
+ overlap on engine snapshots; write commits serialize. Use more Workers for
241
+ separate graphs or stronger UI isolation.
199
242
  - I64 values are delivered as JS `number` and lose precision above 2^53.
200
243
  Applications that need bigint precision should use the native
201
244
  `lora-node` binding instead.
@@ -227,7 +270,7 @@ public surface — consumers can swap backends without rewriting types.
227
270
  forms, parameter validation errors, and concurrent queries across the
228
271
  worker message protocol.
229
272
  - The TypeScript public contract is shared verbatim with `lora-node`
230
- via `crates/shared-ts/types.ts`, enforced by `verify:types` in CI.
273
+ via `crates/bindings/shared-ts/types.ts`, enforced by `verify:types` in CI.
231
274
  - `npm pack --dry-run` produces a 45-file, 6.6 MB tarball containing
232
275
  all three wasm bundles plus the compiled TS wrapper. No `file:` deps,
233
276
  no postinstall scripts.
@@ -244,9 +287,8 @@ public surface — consumers can swap backends without rewriting types.
244
287
  - I64 precision is capped at `Number.MAX_SAFE_INTEGER`. For larger
245
288
  integer properties we need a `bigint`-aware serializer on the wasm
246
289
  boundary.
247
- - The engine is synchronous inside wasm — a single Worker serialises
248
- queries. Multi-tenant workloads need either multiple Workers (already
249
- possible), or an engine-level cooperative scheduler.
290
+ - The engine is synchronous inside wasm once a call enters the module. A slow
291
+ query blocks that Worker until completion.
250
292
  - No query cancellation or progress streaming crosses the Worker
251
293
  boundary; a slow query blocks that Worker until completion.
252
294
 
@@ -21,23 +21,35 @@ export class WasmDatabase {
21
21
  * transaction. Returns an array of query results in statement order.
22
22
  */
23
23
  transaction(statements: any, mode?: string | null): any;
24
- relationshipCount(): number;
25
24
  /**
26
- * Serialize the graph into a byte buffer. The caller is responsible for
27
- * writing it to disk, IndexedDB, localStorage, or wherever — WASM does
28
- * not have direct filesystem access. The returned bytes can later be
29
- * passed to `loadSnapshotFromBytes` on any `WasmDatabase` instance.
25
+ * Replace the graph state with a database snapshot decoded from `bytes`.
26
+ * Legacy store snapshot bytes are accepted for compatibility.
27
+ *
28
+ * Returns a plain object matching the shape of `SnapshotMeta`:
29
+ * `{ formatVersion, nodeCount, relationshipCount, walLsn }`.
30
+ */
31
+ loadSnapshot(bytes: Uint8Array, options: any): any;
32
+ /**
33
+ * Serialize the graph into database snapshot bytes. The caller is
34
+ * responsible for writing them to IndexedDB, localStorage, a server, or
35
+ * another host-provided store — WASM has no direct filesystem access.
36
+ * The returned bytes can later be passed to `loadSnapshot` on any
37
+ * `WasmDatabase` instance.
30
38
  *
31
39
  * Returns the serialized bytes as a `Uint8Array`.
32
40
  */
33
- saveSnapshotToBytes(): Uint8Array;
41
+ saveSnapshot(options: any): Uint8Array;
34
42
  /**
35
- * Replace the graph state with a snapshot decoded from `bytes`.
43
+ * Execute a Lora query and return the result as a binary buffer.
36
44
  *
37
- * Returns a plain object matching the shape of `SnapshotMeta`:
38
- * `{ formatVersion, nodeCount, relationshipCount, walLsn }`.
45
+ * Wire format is the shared `lora_binding_buffer` layout. The TS
46
+ * wrapper decodes it once into the same `{ columns, rows }` shape
47
+ * `execute()` returns, but in a tight V8 loop instead of going
48
+ * through `serde_wasm_bindgen` value-by-value. Materially faster
49
+ * on bulk reads.
39
50
  */
40
- loadSnapshotFromBytes(bytes: Uint8Array): any;
51
+ executeBuffer(query: string, params: any): Uint8Array;
52
+ relationshipCount(): number;
41
53
  constructor();
42
54
  clear(): void;
43
55
  /**
@@ -48,6 +60,21 @@ export class WasmDatabase {
48
60
  * as a plain JS object (structured-clonable).
49
61
  */
50
62
  execute(query: string, params: any): any;
63
+ /**
64
+ * Compile a query and return its plan without executing it.
65
+ *
66
+ * Mutating queries (CREATE / MERGE / SET / DELETE / REMOVE) leave
67
+ * the graph untouched — this method never runs the executor.
68
+ */
69
+ explain(query: string, params: any): any;
70
+ /**
71
+ * Execute a query and return the plan plus runtime metrics.
72
+ *
73
+ * **PROFILE executes the query for real.** Mutating queries are
74
+ * persisted exactly as in `execute()`. Use `explain()` to inspect
75
+ * a mutating plan without running it.
76
+ */
77
+ profile(query: string, params: any): any;
51
78
  }
52
79
  export class WasmQueryStream {
53
80
  private constructor();