@loradb/lora-wasm 0.1.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/LICENSE +87 -0
- package/README.md +197 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/loader-node.d.ts +12 -0
- package/dist/loader-node.d.ts.map +1 -0
- package/dist/loader-node.js +12 -0
- package/dist/loader-node.js.map +1 -0
- package/dist/types.d.ts +193 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +111 -0
- package/dist/types.js.map +1 -0
- package/dist/worker-client.d.ts +41 -0
- package/dist/worker-client.d.ts.map +1 -0
- package/dist/worker-client.js +69 -0
- package/dist/worker-client.js.map +1 -0
- package/dist/worker-protocol.d.ts +40 -0
- package/dist/worker-protocol.d.ts.map +1 -0
- package/dist/worker-protocol.js +9 -0
- package/dist/worker-protocol.js.map +1 -0
- package/dist/worker.d.ts +12 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +75 -0
- package/dist/worker.js.map +1 -0
- package/package.json +98 -0
- package/pkg-bundler/LICENSE +87 -0
- package/pkg-bundler/README.md +197 -0
- package/pkg-bundler/lora_wasm.d.ts +25 -0
- package/pkg-bundler/lora_wasm.js +5 -0
- package/pkg-bundler/lora_wasm_bg.js +559 -0
- package/pkg-bundler/lora_wasm_bg.wasm +0 -0
- package/pkg-bundler/lora_wasm_bg.wasm.d.ts +18 -0
- package/pkg-bundler/package.json +22 -0
- package/pkg-node/LICENSE +87 -0
- package/pkg-node/README.md +197 -0
- package/pkg-node/lora_wasm.d.ts +25 -0
- package/pkg-node/lora_wasm.js +566 -0
- package/pkg-node/lora_wasm_bg.wasm +0 -0
- package/pkg-node/lora_wasm_bg.wasm.d.ts +18 -0
- package/pkg-node/package.json +16 -0
- package/pkg-web/LICENSE +87 -0
- package/pkg-web/README.md +197 -0
- package/pkg-web/lora_wasm.d.ts +67 -0
- package/pkg-web/lora_wasm.js +607 -0
- package/pkg-web/lora_wasm_bg.wasm +0 -0
- package/pkg-web/lora_wasm_bg.wasm.d.ts +18 -0
- package/pkg-web/package.json +20 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# lora-wasm
|
|
2
|
+
|
|
3
|
+
WebAssembly bindings for the [Lora](../../README.md) in-memory graph
|
|
4
|
+
database. The package is designed for browsers and Node.js and exposes a
|
|
5
|
+
**strongly typed, async-facing API** that keeps the main thread responsive:
|
|
6
|
+
heavy query work can run inside a Web Worker while your UI code simply awaits
|
|
7
|
+
the result.
|
|
8
|
+
|
|
9
|
+
> **Status:** prototype / feasibility check. Not published to npm.
|
|
10
|
+
|
|
11
|
+
## Build
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd crates/lora-wasm
|
|
15
|
+
npm install
|
|
16
|
+
npm run build # wasm-pack (node + bundler) + tsc
|
|
17
|
+
npm test # vitest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Build artefacts:
|
|
21
|
+
|
|
22
|
+
| Directory | Target | Purpose |
|
|
23
|
+
|-------------------|---------------------|-----------------------------------------------------------------|
|
|
24
|
+
| `pkg-node/` | `--target nodejs` | In-process usage from Node (vitest, CLI, loader-node.ts) |
|
|
25
|
+
| `pkg-bundler/` | `--target bundler` | Consumption via Vite/webpack/esbuild |
|
|
26
|
+
| `pkg-web/` | `--target web` | Browser Worker entry — self-fetches the `.wasm` binary |
|
|
27
|
+
| `dist/` | TypeScript (`tsc`) | Compiled wrapper (`Database`, worker, worker-client, types) |
|
|
28
|
+
|
|
29
|
+
To run the full validation suite (typecheck, vitest, Playwright browser
|
|
30
|
+
test, npm pack dry-run), add:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm run typecheck
|
|
34
|
+
npm test
|
|
35
|
+
npm run test:browser:install # one-time chromium download
|
|
36
|
+
npm run test:browser
|
|
37
|
+
npm run pack:dry
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Two execution modes
|
|
41
|
+
|
|
42
|
+
### 1. In-process (Node / scripts)
|
|
43
|
+
|
|
44
|
+
`lora-wasm` is **async-only** — the sole initialization pattern is
|
|
45
|
+
`createDatabase()`, which bootstraps the WASM module on first call.
|
|
46
|
+
There is no synchronous constructor and no `Database.create()`
|
|
47
|
+
static; `Database` is a type-only export.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { createDatabase, isNode } from "lora-wasm";
|
|
51
|
+
|
|
52
|
+
const db = await createDatabase();
|
|
53
|
+
await db.execute("CREATE (:Person {name: $n})", { n: "Alice" });
|
|
54
|
+
|
|
55
|
+
const r = await db.execute("MATCH (n:Person) RETURN n");
|
|
56
|
+
for (const row of r.rows) {
|
|
57
|
+
if (isNode(row.n)) console.log(row.n.properties.name);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Non-blocking Web Worker (browser)
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { createWorkerDatabase } from "lora-wasm/worker-client";
|
|
65
|
+
|
|
66
|
+
const worker = new Worker(new URL("./worker.js", import.meta.url), {
|
|
67
|
+
type: "module",
|
|
68
|
+
});
|
|
69
|
+
const db = createWorkerDatabase(worker);
|
|
70
|
+
|
|
71
|
+
await db.execute("CREATE (:N {n: 1})"); // runs off-main-thread
|
|
72
|
+
const { rows } = await db.execute("MATCH (n) RETURN n.n AS n");
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The worker entry (`ts/worker.ts`) hosts the WASM module. The main thread only
|
|
76
|
+
posts messages, so long-running queries never block the event loop / UI.
|
|
77
|
+
|
|
78
|
+
### 3. In-process but typed like the worker (advanced)
|
|
79
|
+
|
|
80
|
+
The same `createWorkerDatabase` signature accepts any `WorkerLike` object —
|
|
81
|
+
useful for tests and for swapping execution backends behind the same API.
|
|
82
|
+
|
|
83
|
+
## Typed value model
|
|
84
|
+
|
|
85
|
+
| TS type | Runtime shape |
|
|
86
|
+
|--------------------------------------|-----------------------------------------------------------------------------|
|
|
87
|
+
| `null`/`boolean`/`number`/`string` | pass-through |
|
|
88
|
+
| `LoraValue[]` / nested record | arrays / objects |
|
|
89
|
+
| `LoraNode` | `{ kind: "node", id, labels, properties }` |
|
|
90
|
+
| `LoraRelationship` | `{ kind: "relationship", id, startId, endId, type, properties }` |
|
|
91
|
+
| `LoraPath` | `{ kind: "path", nodes: number[], rels: number[] }` |
|
|
92
|
+
| `LoraDate`…`LoraDuration` | `{ kind: "date", iso: "YYYY-MM-DD" }` etc. |
|
|
93
|
+
| `LoraPoint` | Discriminated union on `srid` — see below |
|
|
94
|
+
|
|
95
|
+
`LoraPoint` is a union of four CRS-specific shapes:
|
|
96
|
+
|
|
97
|
+
| Shape | Meaning |
|
|
98
|
+
|---------------------------------------------------------------------------------------------------------|---------------|
|
|
99
|
+
| `{ kind: "point", srid: 7203, crs: "cartesian", x, y }` | Cartesian 2D |
|
|
100
|
+
| `{ kind: "point", srid: 9157, crs: "cartesian-3D", x, y, z }` | Cartesian 3D |
|
|
101
|
+
| `{ kind: "point", srid: 4326, crs: "WGS-84-2D", x, y, longitude, latitude }` | WGS-84 2D |
|
|
102
|
+
| `{ kind: "point", srid: 4979, crs: "WGS-84-3D", x, y, z, longitude, latitude, height }` | WGS-84 3D |
|
|
103
|
+
|
|
104
|
+
Helper constructors: `date`, `time`, `datetime`, `localtime`, `localdatetime`,
|
|
105
|
+
`duration`, `cartesian`, `cartesian3d`, `wgs84`, `wgs84_3d`. Guards:
|
|
106
|
+
`isNode`, `isRelationship`, `isPath`, `isPoint`, `isTemporal`.
|
|
107
|
+
|
|
108
|
+
> `distance()` on WGS-84-3D points ignores `height`. See
|
|
109
|
+
> [functions reference](../../apps/loradb.com/docs/functions/overview.md) for the spatial
|
|
110
|
+
> reference and out-of-scope operations.
|
|
111
|
+
|
|
112
|
+
## Errors
|
|
113
|
+
|
|
114
|
+
`db.execute(...)` and the worker client throw `LoraError` with a narrowed
|
|
115
|
+
`code`:
|
|
116
|
+
|
|
117
|
+
- `LORA_ERROR` — parse / analyze / execute failure
|
|
118
|
+
- `INVALID_PARAMS` — a param value could not be mapped to a Lora value
|
|
119
|
+
- `WORKER_ERROR` — worker transport / lifecycle failure (worker client only)
|
|
120
|
+
|
|
121
|
+
## Shared type contract
|
|
122
|
+
|
|
123
|
+
The public TypeScript value model (`LoraValue`, `LoraNode`, …,
|
|
124
|
+
`QueryResult`, `LoraError`) lives in a single canonical file at
|
|
125
|
+
`crates/shared-ts/types.ts` and is copied into each consumer package by
|
|
126
|
+
its `sync:types` npm script. CI runs `verify:types` to fail on drift.
|
|
127
|
+
That keeps `lora-node` and `lora-wasm` locked to one identical
|
|
128
|
+
public surface — consumers can swap backends without rewriting types.
|
|
129
|
+
|
|
130
|
+
## Known limitations
|
|
131
|
+
|
|
132
|
+
- The wasm module is single-threaded. Parallel queries inside one worker
|
|
133
|
+
serialise; spawn more workers for true parallelism.
|
|
134
|
+
- I64 values are delivered as JS `number` and lose precision above 2^53.
|
|
135
|
+
Applications that need bigint precision should use the native
|
|
136
|
+
`lora-node` binding instead.
|
|
137
|
+
- Wall-clock reads (`date()`, `datetime()`, `time()`, `localdatetime()`,
|
|
138
|
+
`localtime()`) are routed through `js_sys::Date::now()` on wasm32 via
|
|
139
|
+
the shim in `lora-store::temporal::unix_now`. The browser clock is
|
|
140
|
+
millisecond-granular, so the nanosecond field on returned values is
|
|
141
|
+
zero below the millisecond boundary.
|
|
142
|
+
- Engine errors cross the worker boundary as `LoraError` with a
|
|
143
|
+
narrowed `code`; the engine does not currently stream query progress
|
|
144
|
+
or offer cancellation.
|
|
145
|
+
|
|
146
|
+
## Feasibility assessment
|
|
147
|
+
|
|
148
|
+
**What works today**
|
|
149
|
+
|
|
150
|
+
- `cargo check --target wasm32-unknown-unknown` passes for the whole
|
|
151
|
+
database pipeline (`lora-ast`, `lora-parser`, `lora-analyzer`,
|
|
152
|
+
`lora-compiler`, `lora-executor`, `lora-store`,
|
|
153
|
+
`lora-database`, `lora-wasm`).
|
|
154
|
+
- `wasm-pack build` succeeds for three targets (`nodejs`, `bundler`,
|
|
155
|
+
`web`) with a ~2.2 MB optimised `.wasm` each.
|
|
156
|
+
- The non-blocking worker-backed path is end-to-end verified: a real
|
|
157
|
+
Chromium instance spawns a module Worker, loads the `pkg-web` bundle,
|
|
158
|
+
and runs a CREATE + MATCH round-trip — asserted by a Playwright test
|
|
159
|
+
behind `npm run test:browser`.
|
|
160
|
+
- `vitest` runs 18 in-process tests against the `nodejs` bundle,
|
|
161
|
+
covering scalars, nested maps/lists, nodes, relationships, paths,
|
|
162
|
+
points, all temporal kinds including `date()`/`datetime()` no-arg
|
|
163
|
+
forms, parameter validation errors, and concurrent queries across the
|
|
164
|
+
worker message protocol.
|
|
165
|
+
- The TypeScript public contract is shared verbatim with `lora-node`
|
|
166
|
+
via `crates/shared-ts/types.ts`, enforced by `verify:types` in CI.
|
|
167
|
+
- `npm pack --dry-run` produces a 45-file, 6.6 MB tarball containing
|
|
168
|
+
all three wasm bundles plus the compiled TS wrapper. No `file:` deps,
|
|
169
|
+
no postinstall scripts.
|
|
170
|
+
|
|
171
|
+
**What still blocks a real npm publish**
|
|
172
|
+
|
|
173
|
+
- Package is marked `private: true` and the workspace is pre-1.0.
|
|
174
|
+
Publishing needs a final scope decision (e.g. `@lora/wasm`),
|
|
175
|
+
a LICENSE file, and a repository URL in `Cargo.toml`/`package.json`.
|
|
176
|
+
- The three wasm bundles are each ~2.2 MB. A production publish should
|
|
177
|
+
either ship conditional exports that load only the bundle the
|
|
178
|
+
consumer imports, or switch to compressed `.wasm` + `fetch` with
|
|
179
|
+
`Content-Encoding: br`.
|
|
180
|
+
- I64 precision is capped at `Number.MAX_SAFE_INTEGER`. For larger
|
|
181
|
+
integer properties we need a `bigint`-aware serializer on the wasm
|
|
182
|
+
boundary.
|
|
183
|
+
- The engine is synchronous inside wasm — a single Worker serialises
|
|
184
|
+
queries. Multi-tenant workloads need either multiple Workers (already
|
|
185
|
+
possible), or an engine-level cooperative scheduler.
|
|
186
|
+
- No query cancellation or progress streaming crosses the Worker
|
|
187
|
+
boundary; a slow query blocks that Worker until completion.
|
|
188
|
+
|
|
189
|
+
**Bottom line**
|
|
190
|
+
|
|
191
|
+
Yes — this database can be used from JavaScript and TypeScript in
|
|
192
|
+
practice, in the browser and in Node, without native bindings. The
|
|
193
|
+
worker-backed path keeps heavy query work off the main thread by
|
|
194
|
+
default, and the strongly-typed wrapper makes query results ergonomic
|
|
195
|
+
for TS consumers. The remaining gaps are packaging polish and two
|
|
196
|
+
well-known wasm constraints (i64 precision, no cancellation); neither
|
|
197
|
+
blocks feasibility.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* Call once at module start to install a panic hook that routes Rust
|
|
5
|
+
* panics to `console.error`. No-op if compiled without the default feature.
|
|
6
|
+
*/
|
|
7
|
+
export function init(): void;
|
|
8
|
+
/**
|
|
9
|
+
* In-memory Lora graph database handle.
|
|
10
|
+
*/
|
|
11
|
+
export class WasmDatabase {
|
|
12
|
+
free(): void;
|
|
13
|
+
nodeCount(): number;
|
|
14
|
+
relationshipCount(): number;
|
|
15
|
+
constructor();
|
|
16
|
+
clear(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Execute a Lora query. `params` may be `undefined`, `null`, or a
|
|
19
|
+
* plain object keyed by parameter name.
|
|
20
|
+
*
|
|
21
|
+
* Returns `{ columns: string[], rows: Array<Record<string, LoraValue>> }`
|
|
22
|
+
* as a plain JS object (structured-clonable).
|
|
23
|
+
*/
|
|
24
|
+
execute(query: string, params: any): any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
28
|
+
|
|
29
|
+
export interface InitOutput {
|
|
30
|
+
readonly memory: WebAssembly.Memory;
|
|
31
|
+
readonly __wbg_wasmdatabase_free: (a: number, b: number) => void;
|
|
32
|
+
readonly wasmdatabase_clear: (a: number) => void;
|
|
33
|
+
readonly wasmdatabase_execute: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
34
|
+
readonly wasmdatabase_new: () => number;
|
|
35
|
+
readonly wasmdatabase_nodeCount: (a: number) => number;
|
|
36
|
+
readonly wasmdatabase_relationshipCount: (a: number) => number;
|
|
37
|
+
readonly init: () => void;
|
|
38
|
+
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
39
|
+
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
40
|
+
readonly __wbindgen_exn_store: (a: number) => void;
|
|
41
|
+
readonly __externref_table_alloc: () => number;
|
|
42
|
+
readonly __wbindgen_export_4: WebAssembly.Table;
|
|
43
|
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
44
|
+
readonly __externref_table_dealloc: (a: number) => void;
|
|
45
|
+
readonly __wbindgen_start: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
|
49
|
+
/**
|
|
50
|
+
* Instantiates the given `module`, which can either be bytes or
|
|
51
|
+
* a precompiled `WebAssembly.Module`.
|
|
52
|
+
*
|
|
53
|
+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
|
|
54
|
+
*
|
|
55
|
+
* @returns {InitOutput}
|
|
56
|
+
*/
|
|
57
|
+
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
|
61
|
+
* for everything else, calls `WebAssembly.instantiate` directly.
|
|
62
|
+
*
|
|
63
|
+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
|
|
64
|
+
*
|
|
65
|
+
* @returns {Promise<InitOutput>}
|
|
66
|
+
*/
|
|
67
|
+
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
|