@loradb/lora-wasm 0.6.0 → 0.8.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.
- package/README.md +60 -19
- package/dist/decode.d.ts +29 -0
- package/dist/decode.d.ts.map +1 -0
- package/dist/decode.js +338 -0
- package/dist/decode.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/snapshot.js +3 -3
- package/dist/snapshot.js.map +1 -1
- package/dist/types.d.ts +68 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/worker-client.d.ts.map +1 -1
- package/dist/worker-client.js +11 -1
- package/dist/worker-client.js.map +1 -1
- package/dist/worker.js +11 -6
- package/dist/worker.js.map +1 -1
- package/package.json +5 -5
- package/pkg-bundler/README.md +60 -19
- package/pkg-bundler/lora_wasm.d.ts +25 -0
- package/pkg-bundler/lora_wasm_bg.js +60 -0
- package/pkg-bundler/lora_wasm_bg.wasm +0 -0
- package/pkg-bundler/lora_wasm_bg.wasm.d.ts +3 -0
- package/pkg-bundler/package.json +1 -1
- package/pkg-node/README.md +60 -19
- package/pkg-node/lora_wasm.d.ts +25 -0
- package/pkg-node/lora_wasm.js +60 -0
- package/pkg-node/lora_wasm_bg.wasm +0 -0
- package/pkg-node/lora_wasm_bg.wasm.d.ts +3 -0
- package/pkg-node/package.json +1 -1
- package/pkg-web/README.md +60 -19
- package/pkg-web/lora_wasm.d.ts +28 -0
- package/pkg-web/lora_wasm.js +60 -0
- package/pkg-web/lora_wasm_bg.wasm +0 -0
- package/pkg-web/lora_wasm_bg.wasm.d.ts +3 -0
- package/pkg-web/package.json +1 -1
|
@@ -5,10 +5,13 @@ 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_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];
|
|
8
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
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];
|
package/pkg-bundler/package.json
CHANGED
package/pkg-node/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# lora-wasm
|
|
2
2
|
|
|
3
|
-
WebAssembly bindings for the [Lora](
|
|
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
|
-
> **
|
|
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
|
```
|
|
@@ -122,14 +153,14 @@ WASM has no filesystem access, so snapshots never accept string paths.
|
|
|
122
153
|
accepts `URL`, `Uint8Array`, `ArrayBuffer`, `Blob`, `Response`, and web
|
|
123
154
|
`ReadableStream<Uint8Array | ArrayBuffer>`. The bytes use the database
|
|
124
155
|
snapshot codec (`LORACOL1`) and can be loaded by native LoraDB. Older store
|
|
125
|
-
snapshots
|
|
156
|
+
snapshots use the current `LORACOL1` columnar format.
|
|
126
157
|
Snapshot bytes are uncompressed by default; pass `{ compression: "gzip" }` or
|
|
127
158
|
`{ compression: { format: "gzip", level: 1 } }` to save smaller
|
|
128
159
|
WASM-portable snapshots. Gzip levels `0..9` are supported; level `1` is the
|
|
129
160
|
fast default.
|
|
130
161
|
|
|
131
162
|
```ts
|
|
132
|
-
import { createDatabase } from "lora-wasm";
|
|
163
|
+
import { createDatabase } from "@loradb/lora-wasm";
|
|
133
164
|
|
|
134
165
|
const db = await createDatabase();
|
|
135
166
|
await db.execute("CREATE (:Person {name: 'Alice'})");
|
|
@@ -172,31 +203,42 @@ Helper constructors: `date`, `time`, `datetime`, `localtime`, `localdatetime`,
|
|
|
172
203
|
`isNode`, `isRelationship`, `isPath`, `isPoint`, `isTemporal`.
|
|
173
204
|
|
|
174
205
|
> `distance()` on WGS-84-3D points ignores `height`. See
|
|
175
|
-
> [functions reference](
|
|
206
|
+
> [functions reference](../../../apps/loradb.com/docs/functions/overview.md) for the spatial
|
|
176
207
|
> reference and out-of-scope operations.
|
|
177
208
|
|
|
178
209
|
## Errors
|
|
179
210
|
|
|
180
|
-
`db.execute(...)` and the worker client throw `LoraError` with a
|
|
181
|
-
`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`.
|
|
182
214
|
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
185
224
|
- `WORKER_ERROR` — worker transport / lifecycle failure (worker client only)
|
|
186
225
|
|
|
226
|
+
See `ts/types.ts` (`LoraErrorCode`) for the full list.
|
|
227
|
+
|
|
187
228
|
## Shared type contract
|
|
188
229
|
|
|
189
230
|
The public TypeScript value model (`LoraValue`, `LoraNode`, …,
|
|
190
231
|
`QueryResult`, `LoraError`) lives in a single canonical file at
|
|
191
|
-
`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
|
|
192
233
|
its `sync:types` npm script. CI runs `verify:types` to fail on drift.
|
|
193
234
|
That keeps `lora-node` and `lora-wasm` locked to one identical
|
|
194
235
|
public surface — consumers can swap backends without rewriting types.
|
|
195
236
|
|
|
196
237
|
## Known limitations
|
|
197
238
|
|
|
198
|
-
- The wasm module
|
|
199
|
-
|
|
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.
|
|
200
242
|
- I64 values are delivered as JS `number` and lose precision above 2^53.
|
|
201
243
|
Applications that need bigint precision should use the native
|
|
202
244
|
`lora-node` binding instead.
|
|
@@ -228,7 +270,7 @@ public surface — consumers can swap backends without rewriting types.
|
|
|
228
270
|
forms, parameter validation errors, and concurrent queries across the
|
|
229
271
|
worker message protocol.
|
|
230
272
|
- The TypeScript public contract is shared verbatim with `lora-node`
|
|
231
|
-
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.
|
|
232
274
|
- `npm pack --dry-run` produces a 45-file, 6.6 MB tarball containing
|
|
233
275
|
all three wasm bundles plus the compiled TS wrapper. No `file:` deps,
|
|
234
276
|
no postinstall scripts.
|
|
@@ -245,9 +287,8 @@ public surface — consumers can swap backends without rewriting types.
|
|
|
245
287
|
- I64 precision is capped at `Number.MAX_SAFE_INTEGER`. For larger
|
|
246
288
|
integer properties we need a `bigint`-aware serializer on the wasm
|
|
247
289
|
boundary.
|
|
248
|
-
- The engine is synchronous inside wasm
|
|
249
|
-
|
|
250
|
-
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.
|
|
251
292
|
- No query cancellation or progress streaming crosses the Worker
|
|
252
293
|
boundary; a slow query blocks that Worker until completion.
|
|
253
294
|
|
package/pkg-node/lora_wasm.d.ts
CHANGED
|
@@ -39,6 +39,16 @@ export class WasmDatabase {
|
|
|
39
39
|
* Returns the serialized bytes as a `Uint8Array`.
|
|
40
40
|
*/
|
|
41
41
|
saveSnapshot(options: any): Uint8Array;
|
|
42
|
+
/**
|
|
43
|
+
* Execute a Lora query and return the result as a binary buffer.
|
|
44
|
+
*
|
|
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.
|
|
50
|
+
*/
|
|
51
|
+
executeBuffer(query: string, params: any): Uint8Array;
|
|
42
52
|
relationshipCount(): number;
|
|
43
53
|
constructor();
|
|
44
54
|
clear(): void;
|
|
@@ -50,6 +60,21 @@ export class WasmDatabase {
|
|
|
50
60
|
* as a plain JS object (structured-clonable).
|
|
51
61
|
*/
|
|
52
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;
|
|
53
78
|
}
|
|
54
79
|
export class WasmQueryStream {
|
|
55
80
|
private constructor();
|
package/pkg-node/lora_wasm.js
CHANGED
|
@@ -293,6 +293,29 @@ class WasmDatabase {
|
|
|
293
293
|
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
294
294
|
return v1;
|
|
295
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Execute a Lora query and return the result as a binary buffer.
|
|
298
|
+
*
|
|
299
|
+
* Wire format is the shared `lora_binding_buffer` layout. The TS
|
|
300
|
+
* wrapper decodes it once into the same `{ columns, rows }` shape
|
|
301
|
+
* `execute()` returns, but in a tight V8 loop instead of going
|
|
302
|
+
* through `serde_wasm_bindgen` value-by-value. Materially faster
|
|
303
|
+
* on bulk reads.
|
|
304
|
+
* @param {string} query
|
|
305
|
+
* @param {any} params
|
|
306
|
+
* @returns {Uint8Array}
|
|
307
|
+
*/
|
|
308
|
+
executeBuffer(query, params) {
|
|
309
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
310
|
+
const len0 = WASM_VECTOR_LEN;
|
|
311
|
+
const ret = wasm.wasmdatabase_executeBuffer(this.__wbg_ptr, ptr0, len0, params);
|
|
312
|
+
if (ret[3]) {
|
|
313
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
314
|
+
}
|
|
315
|
+
var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
316
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
317
|
+
return v2;
|
|
318
|
+
}
|
|
296
319
|
/**
|
|
297
320
|
* @returns {number}
|
|
298
321
|
*/
|
|
@@ -328,6 +351,43 @@ class WasmDatabase {
|
|
|
328
351
|
}
|
|
329
352
|
return takeFromExternrefTable0(ret[0]);
|
|
330
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Compile a query and return its plan without executing it.
|
|
356
|
+
*
|
|
357
|
+
* Mutating queries (CREATE / MERGE / SET / DELETE / REMOVE) leave
|
|
358
|
+
* the graph untouched — this method never runs the executor.
|
|
359
|
+
* @param {string} query
|
|
360
|
+
* @param {any} params
|
|
361
|
+
* @returns {any}
|
|
362
|
+
*/
|
|
363
|
+
explain(query, params) {
|
|
364
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
365
|
+
const len0 = WASM_VECTOR_LEN;
|
|
366
|
+
const ret = wasm.wasmdatabase_explain(this.__wbg_ptr, ptr0, len0, params);
|
|
367
|
+
if (ret[2]) {
|
|
368
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
369
|
+
}
|
|
370
|
+
return takeFromExternrefTable0(ret[0]);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Execute a query and return the plan plus runtime metrics.
|
|
374
|
+
*
|
|
375
|
+
* **PROFILE executes the query for real.** Mutating queries are
|
|
376
|
+
* persisted exactly as in `execute()`. Use `explain()` to inspect
|
|
377
|
+
* a mutating plan without running it.
|
|
378
|
+
* @param {string} query
|
|
379
|
+
* @param {any} params
|
|
380
|
+
* @returns {any}
|
|
381
|
+
*/
|
|
382
|
+
profile(query, params) {
|
|
383
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
384
|
+
const len0 = WASM_VECTOR_LEN;
|
|
385
|
+
const ret = wasm.wasmdatabase_profile(this.__wbg_ptr, ptr0, len0, params);
|
|
386
|
+
if (ret[2]) {
|
|
387
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
388
|
+
}
|
|
389
|
+
return takeFromExternrefTable0(ret[0]);
|
|
390
|
+
}
|
|
331
391
|
}
|
|
332
392
|
module.exports.WasmDatabase = WasmDatabase;
|
|
333
393
|
|
|
Binary file
|
|
@@ -5,10 +5,13 @@ 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_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];
|
|
8
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
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];
|
package/pkg-node/package.json
CHANGED
package/pkg-web/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# lora-wasm
|
|
2
2
|
|
|
3
|
-
WebAssembly bindings for the [Lora](
|
|
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
|
-
> **
|
|
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
|
```
|
|
@@ -122,14 +153,14 @@ WASM has no filesystem access, so snapshots never accept string paths.
|
|
|
122
153
|
accepts `URL`, `Uint8Array`, `ArrayBuffer`, `Blob`, `Response`, and web
|
|
123
154
|
`ReadableStream<Uint8Array | ArrayBuffer>`. The bytes use the database
|
|
124
155
|
snapshot codec (`LORACOL1`) and can be loaded by native LoraDB. Older store
|
|
125
|
-
snapshots
|
|
156
|
+
snapshots use the current `LORACOL1` columnar format.
|
|
126
157
|
Snapshot bytes are uncompressed by default; pass `{ compression: "gzip" }` or
|
|
127
158
|
`{ compression: { format: "gzip", level: 1 } }` to save smaller
|
|
128
159
|
WASM-portable snapshots. Gzip levels `0..9` are supported; level `1` is the
|
|
129
160
|
fast default.
|
|
130
161
|
|
|
131
162
|
```ts
|
|
132
|
-
import { createDatabase } from "lora-wasm";
|
|
163
|
+
import { createDatabase } from "@loradb/lora-wasm";
|
|
133
164
|
|
|
134
165
|
const db = await createDatabase();
|
|
135
166
|
await db.execute("CREATE (:Person {name: 'Alice'})");
|
|
@@ -172,31 +203,42 @@ Helper constructors: `date`, `time`, `datetime`, `localtime`, `localdatetime`,
|
|
|
172
203
|
`isNode`, `isRelationship`, `isPath`, `isPoint`, `isTemporal`.
|
|
173
204
|
|
|
174
205
|
> `distance()` on WGS-84-3D points ignores `height`. See
|
|
175
|
-
> [functions reference](
|
|
206
|
+
> [functions reference](../../../apps/loradb.com/docs/functions/overview.md) for the spatial
|
|
176
207
|
> reference and out-of-scope operations.
|
|
177
208
|
|
|
178
209
|
## Errors
|
|
179
210
|
|
|
180
|
-
`db.execute(...)` and the worker client throw `LoraError` with a
|
|
181
|
-
`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`.
|
|
182
214
|
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
185
224
|
- `WORKER_ERROR` — worker transport / lifecycle failure (worker client only)
|
|
186
225
|
|
|
226
|
+
See `ts/types.ts` (`LoraErrorCode`) for the full list.
|
|
227
|
+
|
|
187
228
|
## Shared type contract
|
|
188
229
|
|
|
189
230
|
The public TypeScript value model (`LoraValue`, `LoraNode`, …,
|
|
190
231
|
`QueryResult`, `LoraError`) lives in a single canonical file at
|
|
191
|
-
`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
|
|
192
233
|
its `sync:types` npm script. CI runs `verify:types` to fail on drift.
|
|
193
234
|
That keeps `lora-node` and `lora-wasm` locked to one identical
|
|
194
235
|
public surface — consumers can swap backends without rewriting types.
|
|
195
236
|
|
|
196
237
|
## Known limitations
|
|
197
238
|
|
|
198
|
-
- The wasm module
|
|
199
|
-
|
|
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.
|
|
200
242
|
- I64 values are delivered as JS `number` and lose precision above 2^53.
|
|
201
243
|
Applications that need bigint precision should use the native
|
|
202
244
|
`lora-node` binding instead.
|
|
@@ -228,7 +270,7 @@ public surface — consumers can swap backends without rewriting types.
|
|
|
228
270
|
forms, parameter validation errors, and concurrent queries across the
|
|
229
271
|
worker message protocol.
|
|
230
272
|
- The TypeScript public contract is shared verbatim with `lora-node`
|
|
231
|
-
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.
|
|
232
274
|
- `npm pack --dry-run` produces a 45-file, 6.6 MB tarball containing
|
|
233
275
|
all three wasm bundles plus the compiled TS wrapper. No `file:` deps,
|
|
234
276
|
no postinstall scripts.
|
|
@@ -245,9 +287,8 @@ public surface — consumers can swap backends without rewriting types.
|
|
|
245
287
|
- I64 precision is capped at `Number.MAX_SAFE_INTEGER`. For larger
|
|
246
288
|
integer properties we need a `bigint`-aware serializer on the wasm
|
|
247
289
|
boundary.
|
|
248
|
-
- The engine is synchronous inside wasm
|
|
249
|
-
|
|
250
|
-
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.
|
|
251
292
|
- No query cancellation or progress streaming crosses the Worker
|
|
252
293
|
boundary; a slow query blocks that Worker until completion.
|
|
253
294
|
|
package/pkg-web/lora_wasm.d.ts
CHANGED
|
@@ -39,6 +39,16 @@ export class WasmDatabase {
|
|
|
39
39
|
* Returns the serialized bytes as a `Uint8Array`.
|
|
40
40
|
*/
|
|
41
41
|
saveSnapshot(options: any): Uint8Array;
|
|
42
|
+
/**
|
|
43
|
+
* Execute a Lora query and return the result as a binary buffer.
|
|
44
|
+
*
|
|
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.
|
|
50
|
+
*/
|
|
51
|
+
executeBuffer(query: string, params: any): Uint8Array;
|
|
42
52
|
relationshipCount(): number;
|
|
43
53
|
constructor();
|
|
44
54
|
clear(): void;
|
|
@@ -50,6 +60,21 @@ export class WasmDatabase {
|
|
|
50
60
|
* as a plain JS object (structured-clonable).
|
|
51
61
|
*/
|
|
52
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;
|
|
53
78
|
}
|
|
54
79
|
export class WasmQueryStream {
|
|
55
80
|
private constructor();
|
|
@@ -67,10 +92,13 @@ export interface InitOutput {
|
|
|
67
92
|
readonly __wbg_wasmquerystream_free: (a: number, b: number) => void;
|
|
68
93
|
readonly wasmdatabase_clear: (a: number) => void;
|
|
69
94
|
readonly wasmdatabase_execute: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
95
|
+
readonly wasmdatabase_executeBuffer: (a: number, b: number, c: number, d: any) => [number, number, number, number];
|
|
96
|
+
readonly wasmdatabase_explain: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
70
97
|
readonly wasmdatabase_loadSnapshot: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
71
98
|
readonly wasmdatabase_new: () => number;
|
|
72
99
|
readonly wasmdatabase_nodeCount: (a: number) => number;
|
|
73
100
|
readonly wasmdatabase_openStream: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
101
|
+
readonly wasmdatabase_profile: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
74
102
|
readonly wasmdatabase_relationshipCount: (a: number) => number;
|
|
75
103
|
readonly wasmdatabase_saveSnapshot: (a: number, b: any) => [number, number, number, number];
|
|
76
104
|
readonly wasmdatabase_transaction: (a: number, b: any, c: number, d: number) => [number, number, number];
|
package/pkg-web/lora_wasm.js
CHANGED
|
@@ -289,6 +289,29 @@ export class WasmDatabase {
|
|
|
289
289
|
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
290
290
|
return v1;
|
|
291
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Execute a Lora query and return the result as a binary buffer.
|
|
294
|
+
*
|
|
295
|
+
* Wire format is the shared `lora_binding_buffer` layout. The TS
|
|
296
|
+
* wrapper decodes it once into the same `{ columns, rows }` shape
|
|
297
|
+
* `execute()` returns, but in a tight V8 loop instead of going
|
|
298
|
+
* through `serde_wasm_bindgen` value-by-value. Materially faster
|
|
299
|
+
* on bulk reads.
|
|
300
|
+
* @param {string} query
|
|
301
|
+
* @param {any} params
|
|
302
|
+
* @returns {Uint8Array}
|
|
303
|
+
*/
|
|
304
|
+
executeBuffer(query, params) {
|
|
305
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
306
|
+
const len0 = WASM_VECTOR_LEN;
|
|
307
|
+
const ret = wasm.wasmdatabase_executeBuffer(this.__wbg_ptr, ptr0, len0, params);
|
|
308
|
+
if (ret[3]) {
|
|
309
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
310
|
+
}
|
|
311
|
+
var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
312
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
313
|
+
return v2;
|
|
314
|
+
}
|
|
292
315
|
/**
|
|
293
316
|
* @returns {number}
|
|
294
317
|
*/
|
|
@@ -324,6 +347,43 @@ export class WasmDatabase {
|
|
|
324
347
|
}
|
|
325
348
|
return takeFromExternrefTable0(ret[0]);
|
|
326
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Compile a query and return its plan without executing it.
|
|
352
|
+
*
|
|
353
|
+
* Mutating queries (CREATE / MERGE / SET / DELETE / REMOVE) leave
|
|
354
|
+
* the graph untouched — this method never runs the executor.
|
|
355
|
+
* @param {string} query
|
|
356
|
+
* @param {any} params
|
|
357
|
+
* @returns {any}
|
|
358
|
+
*/
|
|
359
|
+
explain(query, params) {
|
|
360
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
361
|
+
const len0 = WASM_VECTOR_LEN;
|
|
362
|
+
const ret = wasm.wasmdatabase_explain(this.__wbg_ptr, ptr0, len0, params);
|
|
363
|
+
if (ret[2]) {
|
|
364
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
365
|
+
}
|
|
366
|
+
return takeFromExternrefTable0(ret[0]);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Execute a query and return the plan plus runtime metrics.
|
|
370
|
+
*
|
|
371
|
+
* **PROFILE executes the query for real.** Mutating queries are
|
|
372
|
+
* persisted exactly as in `execute()`. Use `explain()` to inspect
|
|
373
|
+
* a mutating plan without running it.
|
|
374
|
+
* @param {string} query
|
|
375
|
+
* @param {any} params
|
|
376
|
+
* @returns {any}
|
|
377
|
+
*/
|
|
378
|
+
profile(query, params) {
|
|
379
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
380
|
+
const len0 = WASM_VECTOR_LEN;
|
|
381
|
+
const ret = wasm.wasmdatabase_profile(this.__wbg_ptr, ptr0, len0, params);
|
|
382
|
+
if (ret[2]) {
|
|
383
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
384
|
+
}
|
|
385
|
+
return takeFromExternrefTable0(ret[0]);
|
|
386
|
+
}
|
|
327
387
|
}
|
|
328
388
|
|
|
329
389
|
const WasmQueryStreamFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
Binary file
|
|
@@ -5,10 +5,13 @@ 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_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];
|
|
8
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
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];
|