@loradb/lora-node 0.3.0 → 0.5.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # lora-node
2
2
 
3
- Node.js / TypeScript bindings for the [Lora](../../README.md) in-memory
4
- graph engine. The package exposes a first-class typed API: query results are
3
+ Node.js / TypeScript bindings for the [Lora](../../README.md) graph
4
+ engine. The package exposes a first-class typed API: query results are
5
5
  modelled as discriminated unions, temporal values carry `kind` tags, and the
6
6
  `Database` class is strongly typed in both directions (params and rows).
7
7
 
@@ -29,13 +29,13 @@ to `package.json`.
29
29
  ## Usage
30
30
 
31
31
  `lora-node` is **async-only** — the sole initialization pattern is
32
- `createDatabase()`. There is no synchronous constructor and no
32
+ `createDatabase(...)`. There is no synchronous constructor and no
33
33
  `Database.create()` static; `Database` is a type-only export.
34
34
 
35
35
  ```ts
36
36
  import { createDatabase, isNode, type LoraNode } from "lora-node";
37
37
 
38
- const db = await createDatabase();
38
+ const db = await createDatabase(); // in-memory by default
39
39
  await db.execute("CREATE (:Person {name: $n, age: $a})", { n: "Alice", a: 30 });
40
40
 
41
41
  const res = await db.execute<{ n: LoraNode }>("MATCH (n:Person) RETURN n");
@@ -46,9 +46,66 @@ for (const row of res.rows) {
46
46
  }
47
47
  ```
48
48
 
49
- The API matches `lora-wasm` verbatim — same shared types, same
50
- `createDatabase()` entry point, same async method signatures — so
51
- switching backends is a single import change.
49
+ The initialization rule is:
50
+
51
+ ```ts
52
+ import { createDatabase } from "lora-node";
53
+
54
+ const inMemory = await createDatabase(); // in-memory only
55
+ const persistent = await createDatabase("./app"); // persistent: pass a directory string
56
+ ```
57
+
58
+ If you want persistence, pass a **directory string** to `createDatabase(...)`.
59
+
60
+ Node also has a WAL-backed convenience overload:
61
+
62
+ ```ts
63
+ import { createDatabase } from "lora-node";
64
+
65
+ const db = await createDatabase("./.lora-wal"); // persistent: directory string
66
+ ```
67
+
68
+ The string is treated as the WAL directory path verbatim. Relative
69
+ paths resolve from the current working directory. This is a Node-only
70
+ initialization convenience; the query surface, shared types, and async
71
+ method signatures still match `lora-wasm`.
72
+
73
+ Call `db.dispose()` when you need to release the native handle eagerly,
74
+ especially before reopening the same WAL directory in the same process.
75
+
76
+ ## Snapshots
77
+
78
+ `saveSnapshot(path)` writes the current graph to a local file. It also accepts
79
+ typed formats: `"binary"` returns a Node `Buffer`, `"base64"` returns base64
80
+ text, and `{ format: "path", path }` accepts either a path string or `file:`
81
+ URL.
82
+
83
+ `loadSnapshot` accepts a `NodeSnapshotSource`: a filesystem path, `file:` URL,
84
+ HTTP(S) or `data:` URL, `Buffer`, `Uint8Array`, `ArrayBuffer`, Node
85
+ `Readable`, web `ReadableStream`, or async iterable of byte chunks.
86
+
87
+ ```ts
88
+ import { readFile } from "node:fs/promises";
89
+ import { createReadStream } from "node:fs";
90
+ import { pathToFileURL } from "node:url";
91
+ import { createDatabase } from "lora-node";
92
+
93
+ const db = await createDatabase();
94
+ await db.execute("CREATE (:Person {name: 'Alice'})");
95
+ await db.saveSnapshot("./graph.lorasnap");
96
+ const bytes = await db.saveSnapshot("binary");
97
+ const base64 = await db.saveSnapshot({ format: "base64" });
98
+
99
+ await db.loadSnapshot("./graph.lorasnap");
100
+ await db.loadSnapshot(pathToFileURL("./graph.lorasnap"));
101
+ await db.loadSnapshot(await readFile("./graph.lorasnap"));
102
+ await db.loadSnapshot(createReadStream("./graph.lorasnap"));
103
+ await db.loadSnapshot(bytes);
104
+ await db.loadSnapshot(new URL("https://example.com/graph.lorasnap"));
105
+ ```
106
+
107
+ Use `loadSnapshotFromBytes(bytes)` when you already have in-memory bytes and
108
+ want to make that intent explicit.
52
109
 
53
110
  ## Typed value model
54
111
 
@@ -116,15 +173,20 @@ identical to `lora-wasm`.
116
173
 
117
174
  ## Known limitations
118
175
 
119
- - **Concurrent writes.** Each `execute()` hops through the threadpool and
120
- serialises on the store mutex inside Rust, so concurrent read/read and
121
- read/write traffic works. Firing many concurrent write queries against
122
- the same `Database` (e.g. 2 000 parallel `CREATE`s via `Promise.all`)
123
- can expose races in the underlying engine — treat the mutex as
124
- per-operation, not per-query. Prefer `await`-in-a-loop or a single
125
- batched query for heavy write workloads.
176
+ - **Concurrent writes.** Each `execute()` hops through the threadpool; read
177
+ queries can share the store read lock, while writes serialize on the store
178
+ write lock. Firing many concurrent write queries against the same
179
+ `Database` (e.g. 2 000 parallel `CREATE`s via `Promise.all`) works but
180
+ queues behind that write lock. Prefer `await`-in-a-loop or a single batched
181
+ query for heavy write workloads.
126
182
  - **I64 precision.** Integer values above `Number.MAX_SAFE_INTEGER`
127
183
  (2^53) are returned as JS `number` and lose precision. A `bigint`-aware
128
184
  path would require extending the value serializer.
129
185
  - **Cancellation.** The napi `Task` abstraction does not support
130
186
  cancellation once dispatched; a runaway query runs to completion.
187
+ - **WAL surface.** This first Node persistence slice only exposes
188
+ WAL-backed initialization via `createDatabase(walDir)`. Checkpoint,
189
+ truncate, status, and sync-mode controls are not exposed yet.
190
+ - **WAL directory ownership.** A WAL directory can only be open by one
191
+ live handle at a time. Dispose the first handle before reopening the
192
+ same directory.
package/dist/index.d.ts CHANGED
@@ -7,10 +7,13 @@
7
7
  * native surface into the strongly-typed `QueryResult<T>` / `LoraParams`
8
8
  * shapes defined in the shared TS contract (`crates/shared-ts/types.ts`).
9
9
  *
10
- * **Initialization is async-only.** The one canonical entry point is
11
- * `createDatabase()`; there is no synchronous constructor. See the docs at
10
+ * **Initialization is async-only.** The canonical entry point is
11
+ * `createDatabase(...)`, optionally with a WAL directory path. There is no
12
+ * synchronous constructor. See the docs at
12
13
  * https://loradb.com/docs/getting-started/node for the full rationale.
13
14
  */
15
+ import { Buffer } from "node:buffer";
16
+ import { Readable } from "node:stream";
14
17
  import type { LoraParams, LoraValue, QueryResult } from "./types.js";
15
18
  declare const NativeDatabase: typeof import("./native.js").Database;
16
19
  export * from "./types.js";
@@ -26,17 +29,40 @@ export interface SnapshotMeta {
26
29
  relationshipCount: number;
27
30
  walLsn: number | null;
28
31
  }
32
+ export type NodeSnapshotChunk = string | Uint8Array | ArrayBuffer | Buffer;
33
+ export type NodeSnapshotSource = string | URL | Uint8Array | ArrayBuffer | Buffer | Readable | ReadableStream<NodeSnapshotChunk> | AsyncIterable<NodeSnapshotChunk>;
34
+ export type NodeSnapshotSaveFormat = "binary" | "base64";
35
+ export type NodeSnapshotSaveOptions = {
36
+ format: "path";
37
+ path: string | URL;
38
+ } | {
39
+ format: NodeSnapshotSaveFormat;
40
+ };
41
+ export type NodeSnapshotSaveTarget = string | URL | NodeSnapshotSaveFormat | NodeSnapshotSaveOptions;
42
+ export interface CreateDatabaseOptions {
43
+ databaseDir?: string;
44
+ }
45
+ export type TransactionMode = "read_write" | "read_only" | "readwrite" | "readonly" | "rw" | "ro";
46
+ export interface TransactionStatement {
47
+ query: string;
48
+ params?: LoraParams | null;
49
+ }
50
+ export interface RowStream<T extends Record<string, LoraValue> = Record<string, LoraValue>> extends AsyncIterableIterator<T> {
51
+ columns(): string[];
52
+ toArray(): Promise<T[]>;
53
+ close(): void;
54
+ }
29
55
  /**
30
- * In-memory Lora graph database instance.
56
+ * Lora graph database instance.
31
57
  *
32
- * Obtained exclusively via `createDatabase()`. There is no public constructor
33
- * and no synchronous factory — the async entry point lets the binding
34
- * extend initialization later (lazy native loading, warmup, schema
35
- * preflight) without breaking callers.
58
+ * Obtained exclusively via `createDatabase()`. There is no public
59
+ * constructor and no synchronous factory. With no args the instance is
60
+ * purely in-memory; with a WAL directory path it replays committed WAL
61
+ * state from disk before serving queries.
36
62
  *
37
63
  * Instances are independent — each owns its own in-memory graph. Multiple
38
- * concurrent `execute()` calls against one instance run one at a time
39
- * (serialised on the store mutex) but none of them block the event loop.
64
+ * concurrent read-only `execute()` calls against one instance can share
65
+ * the store read lock; writes serialize without blocking the event loop.
40
66
  */
41
67
  declare class DatabaseImpl {
42
68
  #private;
@@ -47,6 +73,24 @@ declare class DatabaseImpl {
47
73
  * with a narrowed `code` (`LORA_ERROR` / `INVALID_PARAMS`).
48
74
  */
49
75
  execute<T extends Record<string, LoraValue> = Record<string, LoraValue>>(query: string, params?: LoraParams): Promise<QueryResult<T>>;
76
+ /**
77
+ * Return an async row iterator for a query.
78
+ *
79
+ * The binding exposes the same `for await` shape as the Rust stream API.
80
+ * Rows are materialized by the native `execute()` promise today, then
81
+ * yielded one at a time to JS consumers.
82
+ */
83
+ stream<T extends Record<string, LoraValue> = Record<string, LoraValue>>(query: string, params?: LoraParams): RowStream<T>;
84
+ /** Alias for `stream()`, useful when naming the row-level API explicitly. */
85
+ rows<T extends Record<string, LoraValue> = Record<string, LoraValue>>(query: string, params?: LoraParams): RowStream<T>;
86
+ /**
87
+ * Execute a statement batch inside one native transaction.
88
+ *
89
+ * Results are returned in statement order. If any statement fails, the
90
+ * native transaction is dropped before commit and all prior writes in the
91
+ * batch are rolled back.
92
+ */
93
+ transaction<T extends Record<string, LoraValue> = Record<string, LoraValue>>(statements: TransactionStatement[], mode?: TransactionMode): Promise<Array<QueryResult<T>>>;
50
94
  /** Drop every node and relationship. Constant-time under the hood. */
51
95
  clear(): Promise<void>;
52
96
  /** Number of nodes currently in the graph. */
@@ -54,21 +98,33 @@ declare class DatabaseImpl {
54
98
  /** Number of relationships currently in the graph. */
55
99
  relationshipCount(): Promise<number>;
56
100
  /**
57
- * Save the graph to a snapshot file. Writes atomically via a `.tmp` +
58
- * rename dance — the target path is only replaced once the full payload
59
- * has been written and fsync'd.
101
+ * Release the native database handle. Idempotent.
60
102
  *
61
- * Synchronous in the native layer (point-in-time consistency requires
62
- * holding the store mutex for the duration of the save); the returned
63
- * Promise resolves immediately once the save returns.
103
+ * Call this when a WAL-backed database needs to be reopened in the same
104
+ * process. New operations after disposal reject with `database is closed`.
64
105
  */
65
- saveSnapshot(path: string): Promise<SnapshotMeta>;
106
+ dispose(): void;
107
+ saveSnapshot(format: "binary"): Promise<Buffer>;
108
+ saveSnapshot(format: "base64"): Promise<string>;
109
+ saveSnapshot(path: string | URL): Promise<SnapshotMeta>;
110
+ saveSnapshot(options: {
111
+ format: "binary";
112
+ }): Promise<Buffer>;
113
+ saveSnapshot(options: {
114
+ format: "base64";
115
+ }): Promise<string>;
116
+ saveSnapshot(options: {
117
+ format: "path";
118
+ path: string | URL;
119
+ }): Promise<SnapshotMeta>;
120
+ saveSnapshotToBytes(): Promise<Buffer>;
66
121
  /**
67
122
  * Replace the current graph state with a snapshot loaded from `path`.
68
- * Concurrent `execute()` calls block on the store mutex until the load
69
- * completes.
123
+ * Concurrent `execute()` calls block on the store write lock until the
124
+ * load completes.
70
125
  */
71
- loadSnapshot(path: string): Promise<SnapshotMeta>;
126
+ loadSnapshot(source: NodeSnapshotSource): Promise<SnapshotMeta>;
127
+ loadSnapshotFromBytes(bytes: Uint8Array | ArrayBuffer | Buffer): Promise<SnapshotMeta>;
72
128
  }
73
129
  /**
74
130
  * Public type for a LoraDB instance.
@@ -84,24 +140,36 @@ declare class DatabaseImpl {
84
140
  */
85
141
  export type Database = DatabaseImpl;
86
142
  /**
87
- * Create and initialize a new in-memory LoraDB instance.
143
+ * Create and initialize a new LoraDB instance.
88
144
  *
89
145
  * **This is the only supported initialization pattern** for `lora-node`.
90
146
  * Synchronous construction is not available — the async factory guarantees
91
- * the native layer is ready before the first query dispatches and keeps
92
- * the Node and WASM surfaces symmetric.
147
+ * the native layer is ready before the first query dispatches.
93
148
  *
94
149
  * ```ts
95
150
  * import { createDatabase } from "lora-node";
96
151
  *
97
- * const db = await createDatabase();
152
+ * const db = await createDatabase(); // in-memory by default
98
153
  * const res = await db.execute(
99
154
  * "CREATE (n:Person {name: $name}) RETURN n",
100
155
  * { name: "Alice" },
101
156
  * );
102
157
  * ```
103
158
  *
159
+ * Optional Node-only persistence convenience:
160
+ *
161
+ * ```ts
162
+ * import { createDatabase } from "lora-node";
163
+ *
164
+ * const inMemory = await createDatabase(); // in-memory
165
+ * const persistent = await createDatabase("./app"); // persistent: pass a directory string
166
+ * ```
167
+ *
168
+ * If you want persistence, pass a directory string. The string is treated
169
+ * as a WAL directory path verbatim. Relative paths resolve from the
170
+ * current working directory.
171
+ *
104
172
  * Each call returns an independent graph — no shared state between instances.
105
173
  */
106
- export declare function createDatabase(): Promise<Database>;
174
+ export declare function createDatabase(databaseName?: string, options?: CreateDatabaseOptions): Promise<Database>;
107
175
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACZ,MAAM,YAAY,CAAC;AAOpB,QAAA,MAAM,cAAc,EAAE,cAAc,aAAa,EAAE,QAA0B,CAAC;AAE9E,cAAc,YAAY,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,cAAM,YAAY;;gBAGJ,KAAK,EAAE,YAAY,CAAC,OAAO,cAAc,CAAC;IAItD;;;;OAIG;IACG,OAAO,CACX,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC/D,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAS9D,sEAAsE;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,8CAA8C;IACxC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAIlC,sDAAsD;IAChD,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI1C;;;;;;;;OAQG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQvD;;;;OAIG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAOxD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,GAAG,YAAY,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,QAAQ,CAAC,CAExD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -8,8 +8,9 @@
8
8
  * native surface into the strongly-typed `QueryResult<T>` / `LoraParams`
9
9
  * shapes defined in the shared TS contract (`crates/shared-ts/types.ts`).
10
10
  *
11
- * **Initialization is async-only.** The one canonical entry point is
12
- * `createDatabase()`; there is no synchronous constructor. See the docs at
11
+ * **Initialization is async-only.** The canonical entry point is
12
+ * `createDatabase(...)`, optionally with a WAL directory path. There is no
13
+ * synchronous constructor. See the docs at
13
14
  * https://loradb.com/docs/getting-started/node for the full rationale.
14
15
  */
15
16
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
@@ -31,23 +32,189 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
32
  };
32
33
  Object.defineProperty(exports, "__esModule", { value: true });
33
34
  exports.createDatabase = createDatabase;
35
+ const node_buffer_1 = require("node:buffer");
36
+ const node_stream_1 = require("node:stream");
37
+ const node_url_1 = require("node:url");
34
38
  const types_js_1 = require("./types.js");
35
39
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
36
40
  // @ts-ignore - resolved at runtime via native.js loader
37
41
  const native_js_1 = __importDefault(require("./native.js"));
38
42
  const NativeDatabase = native_js_1.default.Database;
39
43
  __exportStar(require("./types.js"), exports);
44
+ class NativeRowStream {
45
+ #inner;
46
+ #streamId;
47
+ #closed = false;
48
+ constructor(inner, streamId) {
49
+ this.#inner = inner;
50
+ this.#streamId = streamId;
51
+ }
52
+ [Symbol.asyncIterator]() {
53
+ return this;
54
+ }
55
+ columns() {
56
+ try {
57
+ return this.#inner.streamColumns(this.#streamId);
58
+ }
59
+ catch (err) {
60
+ throw (0, types_js_1.wrapError)(err);
61
+ }
62
+ }
63
+ async next() {
64
+ if (this.#closed) {
65
+ return { done: true, value: undefined };
66
+ }
67
+ try {
68
+ const row = this.#inner.streamNext(this.#streamId);
69
+ if (row === null) {
70
+ this.#closed = true;
71
+ return { done: true, value: undefined };
72
+ }
73
+ return { done: false, value: row };
74
+ }
75
+ catch (err) {
76
+ this.#closed = true;
77
+ throw (0, types_js_1.wrapError)(err);
78
+ }
79
+ }
80
+ async return() {
81
+ this.close();
82
+ return { done: true, value: undefined };
83
+ }
84
+ async toArray() {
85
+ const rows = [];
86
+ for (;;) {
87
+ const next = await this.next();
88
+ if (next.done) {
89
+ return rows;
90
+ }
91
+ rows.push(next.value);
92
+ }
93
+ }
94
+ close() {
95
+ if (this.#closed)
96
+ return;
97
+ this.#closed = true;
98
+ try {
99
+ this.#inner.streamClose(this.#streamId);
100
+ }
101
+ catch (err) {
102
+ throw (0, types_js_1.wrapError)(err);
103
+ }
104
+ }
105
+ }
106
+ function isFetchUrl(url) {
107
+ return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "data:";
108
+ }
109
+ function stringToUrl(value) {
110
+ try {
111
+ return new URL(value);
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
117
+ function bytesToUint8Array(bytes) {
118
+ if (bytes instanceof Uint8Array) {
119
+ return bytes;
120
+ }
121
+ return new Uint8Array(bytes);
122
+ }
123
+ function snapshotChunkToBuffer(chunk) {
124
+ return typeof chunk === "string"
125
+ ? node_buffer_1.Buffer.from(chunk)
126
+ : node_buffer_1.Buffer.from(bytesToUint8Array(chunk));
127
+ }
128
+ function resolveNodeSnapshotPath(path) {
129
+ if (path instanceof URL) {
130
+ if (path.protocol !== "file:") {
131
+ throw new Error(`LORA_ERROR: unsupported snapshot save URL protocol '${path.protocol}'`);
132
+ }
133
+ return (0, node_url_1.fileURLToPath)(path);
134
+ }
135
+ return path;
136
+ }
137
+ async function fetchSnapshotBytes(url) {
138
+ const res = await fetch(url);
139
+ if (!res.ok) {
140
+ throw new Error(`LORA_ERROR: snapshot fetch failed (${res.status} ${res.statusText})`);
141
+ }
142
+ return node_buffer_1.Buffer.from(await res.arrayBuffer());
143
+ }
144
+ function isReadableStream(source) {
145
+ return typeof source.getReader === "function";
146
+ }
147
+ function isAsyncIterable(source) {
148
+ return typeof source[Symbol.asyncIterator] === "function";
149
+ }
150
+ async function readableStreamToBuffer(stream) {
151
+ const reader = stream.getReader();
152
+ const chunks = [];
153
+ try {
154
+ for (;;) {
155
+ const { done, value } = await reader.read();
156
+ if (done) {
157
+ return node_buffer_1.Buffer.concat(chunks);
158
+ }
159
+ chunks.push(snapshotChunkToBuffer(value));
160
+ }
161
+ }
162
+ finally {
163
+ reader.releaseLock();
164
+ }
165
+ }
166
+ async function asyncIterableToBuffer(stream) {
167
+ const chunks = [];
168
+ for await (const chunk of stream) {
169
+ chunks.push(snapshotChunkToBuffer(chunk));
170
+ }
171
+ return node_buffer_1.Buffer.concat(chunks);
172
+ }
173
+ async function resolveNodeSnapshotSource(source) {
174
+ if (source instanceof URL) {
175
+ if (source.protocol === "file:") {
176
+ return (0, node_url_1.fileURLToPath)(source);
177
+ }
178
+ if (isFetchUrl(source)) {
179
+ return fetchSnapshotBytes(source);
180
+ }
181
+ throw new Error(`LORA_ERROR: unsupported snapshot URL protocol '${source.protocol}'`);
182
+ }
183
+ if (typeof source === "string") {
184
+ const url = stringToUrl(source);
185
+ if (!url) {
186
+ return source;
187
+ }
188
+ if (url.protocol === "file:") {
189
+ return (0, node_url_1.fileURLToPath)(url);
190
+ }
191
+ if (isFetchUrl(url)) {
192
+ return fetchSnapshotBytes(url);
193
+ }
194
+ return source;
195
+ }
196
+ if (isReadableStream(source)) {
197
+ return readableStreamToBuffer(source);
198
+ }
199
+ if (source instanceof node_stream_1.Readable) {
200
+ return asyncIterableToBuffer(source);
201
+ }
202
+ if (isAsyncIterable(source)) {
203
+ return asyncIterableToBuffer(source);
204
+ }
205
+ return node_buffer_1.Buffer.from(bytesToUint8Array(source));
206
+ }
40
207
  /**
41
- * In-memory Lora graph database instance.
208
+ * Lora graph database instance.
42
209
  *
43
- * Obtained exclusively via `createDatabase()`. There is no public constructor
44
- * and no synchronous factory — the async entry point lets the binding
45
- * extend initialization later (lazy native loading, warmup, schema
46
- * preflight) without breaking callers.
210
+ * Obtained exclusively via `createDatabase()`. There is no public
211
+ * constructor and no synchronous factory. With no args the instance is
212
+ * purely in-memory; with a WAL directory path it replays committed WAL
213
+ * state from disk before serving queries.
47
214
  *
48
215
  * Instances are independent — each owns its own in-memory graph. Multiple
49
- * concurrent `execute()` calls against one instance run one at a time
50
- * (serialised on the store mutex) but none of them block the event loop.
216
+ * concurrent read-only `execute()` calls against one instance can share
217
+ * the store read lock; writes serialize without blocking the event loop.
51
218
  */
52
219
  class DatabaseImpl {
53
220
  #inner;
@@ -68,30 +235,121 @@ class DatabaseImpl {
68
235
  throw (0, types_js_1.wrapError)(err);
69
236
  }
70
237
  }
238
+ /**
239
+ * Return an async row iterator for a query.
240
+ *
241
+ * The binding exposes the same `for await` shape as the Rust stream API.
242
+ * Rows are materialized by the native `execute()` promise today, then
243
+ * yielded one at a time to JS consumers.
244
+ */
245
+ stream(query, params) {
246
+ try {
247
+ const streamId = this.#inner.openStream(query, params ?? null);
248
+ return new NativeRowStream(this.#inner, streamId);
249
+ }
250
+ catch (err) {
251
+ throw (0, types_js_1.wrapError)(err);
252
+ }
253
+ }
254
+ /** Alias for `stream()`, useful when naming the row-level API explicitly. */
255
+ rows(query, params) {
256
+ return this.stream(query, params);
257
+ }
258
+ /**
259
+ * Execute a statement batch inside one native transaction.
260
+ *
261
+ * Results are returned in statement order. If any statement fails, the
262
+ * native transaction is dropped before commit and all prior writes in the
263
+ * batch are rolled back.
264
+ */
265
+ async transaction(statements, mode = "read_write") {
266
+ try {
267
+ return (await this.#inner.transaction(statements, mode));
268
+ }
269
+ catch (err) {
270
+ throw (0, types_js_1.wrapError)(err);
271
+ }
272
+ }
71
273
  /** Drop every node and relationship. Constant-time under the hood. */
72
274
  async clear() {
73
- this.#inner.clear();
275
+ try {
276
+ this.#inner.clear();
277
+ }
278
+ catch (err) {
279
+ throw (0, types_js_1.wrapError)(err);
280
+ }
74
281
  }
75
282
  /** Number of nodes currently in the graph. */
76
283
  async nodeCount() {
77
- return this.#inner.nodeCount();
284
+ try {
285
+ return this.#inner.nodeCount();
286
+ }
287
+ catch (err) {
288
+ throw (0, types_js_1.wrapError)(err);
289
+ }
78
290
  }
79
291
  /** Number of relationships currently in the graph. */
80
292
  async relationshipCount() {
81
- return this.#inner.relationshipCount();
293
+ try {
294
+ return this.#inner.relationshipCount();
295
+ }
296
+ catch (err) {
297
+ throw (0, types_js_1.wrapError)(err);
298
+ }
82
299
  }
83
300
  /**
84
- * Save the graph to a snapshot file. Writes atomically via a `.tmp` +
85
- * rename dance — the target path is only replaced once the full payload
86
- * has been written and fsync'd.
301
+ * Release the native database handle. Idempotent.
87
302
  *
88
- * Synchronous in the native layer (point-in-time consistency requires
89
- * holding the store mutex for the duration of the save); the returned
90
- * Promise resolves immediately once the save returns.
303
+ * Call this when a WAL-backed database needs to be reopened in the same
304
+ * process. New operations after disposal reject with `database is closed`.
91
305
  */
92
- async saveSnapshot(path) {
306
+ dispose() {
93
307
  try {
94
- return this.#inner.saveSnapshot(path);
308
+ this.#inner.dispose();
309
+ }
310
+ catch (err) {
311
+ throw (0, types_js_1.wrapError)(err);
312
+ }
313
+ }
314
+ /**
315
+ * Save the graph as a snapshot.
316
+ *
317
+ * - `saveSnapshot(path)` and `saveSnapshot({ format: "path", path })`
318
+ * write atomically to a local file and return `SnapshotMeta`.
319
+ * - `saveSnapshot("binary")` / `{ format: "binary" }` return a `Buffer`.
320
+ * - `saveSnapshot("base64")` / `{ format: "base64" }` return base64 text.
321
+ */
322
+ async saveSnapshot(target) {
323
+ try {
324
+ if (target === "binary" || (typeof target === "object"
325
+ && !(target instanceof URL)
326
+ && target.format === "binary")) {
327
+ return this.saveSnapshotToBytes();
328
+ }
329
+ if (target === "base64" || (typeof target === "object"
330
+ && !(target instanceof URL)
331
+ && target.format === "base64")) {
332
+ return (await this.saveSnapshotToBytes()).toString("base64");
333
+ }
334
+ let path;
335
+ if (typeof target === "object" && !(target instanceof URL)) {
336
+ if (target.format !== "path") {
337
+ throw new Error(`LORA_ERROR: unsupported snapshot save format '${target.format}'`);
338
+ }
339
+ path = target.path;
340
+ }
341
+ else {
342
+ path = target;
343
+ }
344
+ return this.#inner.saveSnapshot(resolveNodeSnapshotPath(path));
345
+ }
346
+ catch (err) {
347
+ throw (0, types_js_1.wrapError)(err);
348
+ }
349
+ }
350
+ async saveSnapshotToBytes() {
351
+ try {
352
+ return this.#inner.saveSnapshotToBytes();
95
353
  }
96
354
  catch (err) {
97
355
  throw (0, types_js_1.wrapError)(err);
@@ -99,12 +357,24 @@ class DatabaseImpl {
99
357
  }
100
358
  /**
101
359
  * Replace the current graph state with a snapshot loaded from `path`.
102
- * Concurrent `execute()` calls block on the store mutex until the load
103
- * completes.
360
+ * Concurrent `execute()` calls block on the store write lock until the
361
+ * load completes.
104
362
  */
105
- async loadSnapshot(path) {
363
+ async loadSnapshot(source) {
106
364
  try {
107
- return this.#inner.loadSnapshot(path);
365
+ const resolved = await resolveNodeSnapshotSource(source);
366
+ if (typeof resolved === "string") {
367
+ return this.#inner.loadSnapshot(resolved);
368
+ }
369
+ return this.#inner.loadSnapshotFromBytes(resolved);
370
+ }
371
+ catch (err) {
372
+ throw (0, types_js_1.wrapError)(err);
373
+ }
374
+ }
375
+ async loadSnapshotFromBytes(bytes) {
376
+ try {
377
+ return this.#inner.loadSnapshotFromBytes(node_buffer_1.Buffer.from(bytesToUint8Array(bytes)));
108
378
  }
109
379
  catch (err) {
110
380
  throw (0, types_js_1.wrapError)(err);
@@ -112,26 +382,45 @@ class DatabaseImpl {
112
382
  }
113
383
  }
114
384
  /**
115
- * Create and initialize a new in-memory LoraDB instance.
385
+ * Create and initialize a new LoraDB instance.
116
386
  *
117
387
  * **This is the only supported initialization pattern** for `lora-node`.
118
388
  * Synchronous construction is not available — the async factory guarantees
119
- * the native layer is ready before the first query dispatches and keeps
120
- * the Node and WASM surfaces symmetric.
389
+ * the native layer is ready before the first query dispatches.
121
390
  *
122
391
  * ```ts
123
392
  * import { createDatabase } from "lora-node";
124
393
  *
125
- * const db = await createDatabase();
394
+ * const db = await createDatabase(); // in-memory by default
126
395
  * const res = await db.execute(
127
396
  * "CREATE (n:Person {name: $name}) RETURN n",
128
397
  * { name: "Alice" },
129
398
  * );
130
399
  * ```
131
400
  *
401
+ * Optional Node-only persistence convenience:
402
+ *
403
+ * ```ts
404
+ * import { createDatabase } from "lora-node";
405
+ *
406
+ * const inMemory = await createDatabase(); // in-memory
407
+ * const persistent = await createDatabase("./app"); // persistent: pass a directory string
408
+ * ```
409
+ *
410
+ * If you want persistence, pass a directory string. The string is treated
411
+ * as a WAL directory path verbatim. Relative paths resolve from the
412
+ * current working directory.
413
+ *
132
414
  * Each call returns an independent graph — no shared state between instances.
133
415
  */
134
- async function createDatabase() {
135
- return new DatabaseImpl(new NativeDatabase());
416
+ async function createDatabase(databaseName, options = {}) {
417
+ try {
418
+ return new DatabaseImpl(databaseName === undefined
419
+ ? new NativeDatabase()
420
+ : new NativeDatabase(databaseName, options.databaseDir ?? null));
421
+ }
422
+ catch (err) {
423
+ throw (0, types_js_1.wrapError)(err);
424
+ }
136
425
  }
137
426
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;;;;;;;;;;;;;;;;;AAiJH,wCAEC;AA5ID,yCAAuC;AAEvC,6DAA6D;AAC7D,wDAAwD;AACxD,4DAAiC;AAEjC,MAAM,cAAc,GAA0C,mBAAM,CAAC,QAAQ,CAAC;AAE9E,6CAA2B;AAe3B;;;;;;;;;;;GAWG;AACH,MAAM,YAAY;IACP,MAAM,CAAsC;IAErD,YAAY,KAA0C;QACpD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAEX,KAAa,EAAE,MAAmB;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;YAC7D,OAAO,GAAqB,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,8CAA8C;IAC9C,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;IACjC,CAAC;IAED,sDAAsD;IACtD,KAAK,CAAC,iBAAiB;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;IACzC,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,YAAY,CAAC,IAAY;QAC7B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAiB,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,IAAY;QAC7B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAiB,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF;AAgBD;;;;;;;;;;;;;;;;;;;GAmBG;AACI,KAAK,UAAU,cAAc;IAClC,OAAO,IAAI,YAAY,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;AAwgBH,wCAaC;AAnhBD,6CAAqC;AACrC,6CAAuC;AACvC,uCAAyC;AAOzC,yCAAuC;AAEvC,6DAA6D;AAC7D,wDAAwD;AACxD,4DAAiC;AAEjC,MAAM,cAAc,GAA0C,mBAAM,CAAC,QAAQ,CAAC;AAE9E,6CAA2B;AA4D3B,MAAM,eAAe;IAGV,MAAM,CAAsC;IAC5C,SAAS,CAAS;IAC3B,OAAO,GAAG,KAAK,CAAC;IAEhB,YAAY,KAA0C,EAAE,QAAgB;QACtE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAa,CAAC;YAC/D,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBACjB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;YAC1C,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,GAAQ,EAAE,CAAC;QACrB,SAAS,CAAC;YACR,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF;AAED,SAAS,UAAU,CAAC,GAAQ;IAC1B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC;AAC3F,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAwC;IACjE,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAwB;IACrD,OAAO,OAAO,KAAK,KAAK,QAAQ;QAC9B,CAAC,CAAC,oBAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC,CAAC,oBAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,uBAAuB,CAAC,IAAkB;IACjD,IAAI,IAAI,YAAY,GAAG,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,uDAAuD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAA,wBAAa,EAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,GAAQ;IACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,oBAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAe;IACvC,OAAO,OAAQ,MAAkC,CAAC,SAAS,KAAK,UAAU,CAAC;AAC7E,CAAC;AAED,SAAS,eAAe,CAAC,MAAe;IACtC,OAAO,OAAQ,MAA+C,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,UAAU,CAAC;AACtG,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,MAAyC;IAEzC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC;QACH,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,oBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/B,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,MAAwC;IAExC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,oBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,yBAAyB,CACtC,MAA0B;IAE1B,IAAI,MAAM,YAAY,GAAG,EAAE,CAAC;QAC1B,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAChC,OAAO,IAAA,wBAAa,EAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,kDAAkD,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC7B,OAAO,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,MAAM,YAAY,sBAAQ,EAAE,CAAC;QAC/B,OAAO,qBAAqB,CAAC,MAA0C,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,oBAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,YAAY;IACP,MAAM,CAAsC;IAErD,YAAY,KAA0C;QACpD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAEX,KAAa,EAAE,MAAmB;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;YAC7D,OAAO,GAAqB,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CACJ,KAAa,EACb,MAAmB;QAEnB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;YAC/D,OAAO,IAAI,eAAe,CAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,IAAI,CACF,KAAa,EACb,MAAmB;QAEnB,OAAO,IAAI,CAAC,MAAM,CAAI,KAAK,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,CAGf,UAAkC,EAClC,OAAwB,YAAY;QAEpC,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,CAA0B,CAAC;QACpF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,KAAK;QACT,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,8CAA8C;IAC9C,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,sDAAsD;IACtD,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAQD;;;;;;;OAOG;IACH,KAAK,CAAC,YAAY,CAChB,MAA8B;QAE9B,IAAI,CAAC;YACH,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ;mBACjD,CAAC,CAAC,MAAM,YAAY,GAAG,CAAC;mBACxB,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACpC,CAAC;YAED,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ;mBACjD,CAAC,CAAC,MAAM,YAAY,GAAG,CAAC;mBACxB,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,IAAkB,CAAC;YACvB,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,YAAY,GAAG,CAAC,EAAE,CAAC;gBAC3D,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CAAC,iDAAiD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;gBACrF,CAAC;gBACD,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,MAAM,CAAC;YAChB,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAiB,CAAC;QACjF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,MAA0B;QAC3C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACzD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAiB,CAAC;YAC5D,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,QAAQ,CAAiB,CAAC;QACrE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,qBAAqB,CACzB,KAAwC;QAExC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,qBAAqB,CACtC,oBAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CACtB,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF;AAgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACI,KAAK,UAAU,cAAc,CAClC,YAAqB,EACrB,UAAiC,EAAE;IAEnC,IAAI,CAAC;QACH,OAAO,IAAI,YAAY,CACrB,YAAY,KAAK,SAAS;YACxB,CAAC,CAAC,IAAI,cAAc,EAAE;YACtB,CAAC,CAAC,IAAI,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,CAClE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAA,oBAAS,EAAC,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loradb/lora-node",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Node.js / TypeScript bindings for the Lora in-memory graph database",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "LoraDB, Inc.",
@@ -89,10 +89,10 @@
89
89
  "vitest": "^2.1.1"
90
90
  },
91
91
  "optionalDependencies": {
92
- "@loradb/lora-node-linux-x64-gnu": "0.3.0",
93
- "@loradb/lora-node-linux-arm64-gnu": "0.3.0",
94
- "@loradb/lora-node-darwin-x64": "0.3.0",
95
- "@loradb/lora-node-darwin-arm64": "0.3.0",
96
- "@loradb/lora-node-win32-x64-msvc": "0.3.0"
92
+ "@loradb/lora-node-linux-x64-gnu": "0.5.0",
93
+ "@loradb/lora-node-linux-arm64-gnu": "0.5.0",
94
+ "@loradb/lora-node-darwin-x64": "0.5.0",
95
+ "@loradb/lora-node-darwin-arm64": "0.5.0",
96
+ "@loradb/lora-node-win32-x64-msvc": "0.5.0"
97
97
  }
98
98
  }
package/ts/native.d.ts CHANGED
@@ -19,17 +19,33 @@ export interface NativeSnapshotMeta {
19
19
  }
20
20
 
21
21
  export declare class Database {
22
- constructor();
22
+ constructor(databaseName?: string | null, databaseDir?: string | null);
23
23
  /** Non-blocking: runs on the libuv threadpool, returns a Promise. */
24
24
  execute(
25
25
  query: string,
26
26
  params?: Record<string, unknown> | null,
27
27
  ): Promise<NativeQueryResult>;
28
+ openStream(
29
+ query: string,
30
+ params?: Record<string, unknown> | null,
31
+ ): number;
32
+ streamColumns(streamId: number): string[];
33
+ streamNext(streamId: number): Record<string, unknown> | null;
34
+ streamClose(streamId: number): void;
35
+ transaction(
36
+ statements: Array<{ query: string; params?: Record<string, unknown> | null }>,
37
+ mode?: "read_write" | "read_only" | "readwrite" | "readonly" | "rw" | "ro" | null,
38
+ ): Promise<NativeQueryResult[]>;
28
39
  clear(): void;
29
40
  nodeCount(): number;
30
41
  relationshipCount(): number;
31
- /** Atomic save. Synchronous under the store mutex. */
42
+ dispose(): void;
43
+ /** Atomic save. Synchronous under the store read lock. */
32
44
  saveSnapshot(path: string): NativeSnapshotMeta;
45
+ /** Serialize the current graph into snapshot bytes. */
46
+ saveSnapshotToBytes(): Buffer;
33
47
  /** Replace the current graph with the snapshot at `path`. */
34
48
  loadSnapshot(path: string): NativeSnapshotMeta;
49
+ /** Replace the current graph with snapshot bytes. */
50
+ loadSnapshotFromBytes(bytes: Uint8Array | Buffer): NativeSnapshotMeta;
35
51
  }