@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.
Files changed (49) hide show
  1. package/LICENSE +87 -0
  2. package/README.md +197 -0
  3. package/dist/index.d.ts +66 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +87 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/loader-node.d.ts +12 -0
  8. package/dist/loader-node.d.ts.map +1 -0
  9. package/dist/loader-node.js +12 -0
  10. package/dist/loader-node.js.map +1 -0
  11. package/dist/types.d.ts +193 -0
  12. package/dist/types.d.ts.map +1 -0
  13. package/dist/types.js +111 -0
  14. package/dist/types.js.map +1 -0
  15. package/dist/worker-client.d.ts +41 -0
  16. package/dist/worker-client.d.ts.map +1 -0
  17. package/dist/worker-client.js +69 -0
  18. package/dist/worker-client.js.map +1 -0
  19. package/dist/worker-protocol.d.ts +40 -0
  20. package/dist/worker-protocol.d.ts.map +1 -0
  21. package/dist/worker-protocol.js +9 -0
  22. package/dist/worker-protocol.js.map +1 -0
  23. package/dist/worker.d.ts +12 -0
  24. package/dist/worker.d.ts.map +1 -0
  25. package/dist/worker.js +75 -0
  26. package/dist/worker.js.map +1 -0
  27. package/package.json +98 -0
  28. package/pkg-bundler/LICENSE +87 -0
  29. package/pkg-bundler/README.md +197 -0
  30. package/pkg-bundler/lora_wasm.d.ts +25 -0
  31. package/pkg-bundler/lora_wasm.js +5 -0
  32. package/pkg-bundler/lora_wasm_bg.js +559 -0
  33. package/pkg-bundler/lora_wasm_bg.wasm +0 -0
  34. package/pkg-bundler/lora_wasm_bg.wasm.d.ts +18 -0
  35. package/pkg-bundler/package.json +22 -0
  36. package/pkg-node/LICENSE +87 -0
  37. package/pkg-node/README.md +197 -0
  38. package/pkg-node/lora_wasm.d.ts +25 -0
  39. package/pkg-node/lora_wasm.js +566 -0
  40. package/pkg-node/lora_wasm_bg.wasm +0 -0
  41. package/pkg-node/lora_wasm_bg.wasm.d.ts +18 -0
  42. package/pkg-node/package.json +16 -0
  43. package/pkg-web/LICENSE +87 -0
  44. package/pkg-web/README.md +197 -0
  45. package/pkg-web/lora_wasm.d.ts +67 -0
  46. package/pkg-web/lora_wasm.js +607 -0
  47. package/pkg-web/lora_wasm_bg.wasm +0 -0
  48. package/pkg-web/lora_wasm_bg.wasm.d.ts +18 -0
  49. 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,25 @@
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
+ }
@@ -0,0 +1,5 @@
1
+ import * as wasm from "./lora_wasm_bg.wasm";
2
+ export * from "./lora_wasm_bg.js";
3
+ import { __wbg_set_wasm } from "./lora_wasm_bg.js";
4
+ __wbg_set_wasm(wasm);
5
+ wasm.__wbindgen_start();