@laminardb/node 0.30.0-alpha.1 → 0.30.0-alpha.2

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,16 +1,18 @@
1
- # laminardb-nodejs
1
+ # @laminardb/node
2
2
 
3
- Embedded streaming SQL for Node.js and TypeScript. This is the official Node.js binding
4
- for [LaminarDB](https://github.com/laminardb/laminardb), built as a
5
- [napi-rs](https://napi.rs) native addon over a pinned core release — the same two-layer
6
- shape as [`laminardb-java`](https://github.com/laminardb/laminardb-java) and
7
- `laminardb-python`, adapted to Node idioms: every data-plane call is a `Promise`, Arrow
8
- data moves as IPC `Buffer`s, and failures throw a typed error hierarchy.
3
+ Embedded streaming SQL for Node.js and TypeScript, with no compilation step: install,
4
+ import, and query. Prebuilt native binaries ship for every major platform — nothing to
5
+ build, no postinstall scripts, no node-gyp.
9
6
 
10
- Status: **alpha** — embedded MVP (Phase 1). Subscriptions (async iterators), Windows/musl
11
- CI, and npm distribution land over the next phases. Not yet on npm — build from source.
7
+ ```sh
8
+ npm install @laminardb/node
9
+ ```
12
10
 
13
- ## Quickstart
11
+ **Requirements:** Node.js 20 or later, on macOS (x64, arm64), Linux (x64 or arm64, glibc
12
+ or musl), or Windows (x64). TypeScript types are included — no `@types` package needed.
13
+ (npm 11+ recommended; pnpm and yarn work as-is.)
14
+
15
+ ## Your first pipeline
14
16
 
15
17
  ```js
16
18
  import { LaminarDB } from '@laminardb/node'
@@ -27,37 +29,41 @@ await conn.insert('sensors', [
27
29
  const result = await conn.query(
28
30
  'SELECT device, avg(value) AS avg_value FROM sensors GROUP BY device',
29
31
  )
30
- console.log(result.toArray()) // [{ device: 'd1', avg_value: 21.5 }, ...]
31
-
32
- await conn.close()
32
+ console.log(result.toArray())
33
+ // [{ device: 'd1', avg_value: 21.5 }, { device: 'd2', avg_value: 18.25 }]
33
34
  ```
34
35
 
35
- Durable embedded mode is one argument plus checkpointing:
36
+ That's the whole loop: define a source, start the pipeline, insert rows, query. `CommonJS`
37
+ works too — `const { LaminarDB } = require('@laminardb/node')`.
36
38
 
37
- ```js
38
- const conn = await LaminarDB.open('./data', { checkpoint: { intervalMs: 5000 } })
39
- ```
39
+ **Durable mode** is one argument:
40
+ `LaminarDB.open('./data', { checkpoint: { intervalMs: 5000 } })` keeps your pipeline and
41
+ data across restarts.
42
+
43
+ > Two rules from the engine: `CREATE SOURCE` / `CREATE STREAM` / `CREATE SINK` must run
44
+ > **before** `start()`, and manual `checkpoint()` needs at least one stream or sink in the
45
+ > topology.
46
+
47
+ ## Reading results
40
48
 
41
- Topology DDL (`CREATE SOURCE` / `STREAM` / `SINK`) must run before `start()`; manual
42
- `checkpoint()` requires at least one stream or sink in the topology (the core wires the
43
- checkpoint coordinator only for real pipelines).
49
+ - `result.toArray()` plain row objects, zero dependencies. `Date`-like columns are epoch
50
+ milliseconds; 64-bit integers are JS `BigInt`.
51
+ - `result.toIPC()` an [Apache Arrow](https://www.npmjs.com/package/apache-arrow) IPC
52
+ `Buffer`, for when rows get big: `tableFromIPC(result.toIPC())` (or the bundled
53
+ `tableFrom(result)`).
54
+ - Batch at a time: `result.numBatches()`, `result.batch(i)`.
44
55
 
45
- ## Data access
56
+ ## Writing data
46
57
 
47
- - `result.toArray()` / `batch.toArray()` — row objects, no dependencies. Conventions:
48
- temporal columns are **milliseconds since epoch** in and out; `Int64`/`UInt64` cross as
49
- JS `BigInt`.
50
- - `result.toIPC()` / `batch.toIPC()` one Arrow IPC stream `Buffer`; rehydrate with
51
- [`apache-arrow`](https://www.npmjs.com/package/apache-arrow) (an optional peer
52
- dependency): `tableFromIPC(result.toIPC())` or the bundled `tableFrom(result)` helper.
53
- - `conn.insertArrow(source, buffer)` / `writer.writeArrow(buffer)` — bulk ingestion
54
- straight from Arrow IPC data.
55
- - `conn.writer(source)` — streaming writer with `writeRows`, `watermark`, and backpressure
56
- visibility (`pending` / `capacity` / `isBackpressured`).
58
+ - `conn.insert('sensors', rows)` — row objects, validated per value with a clear error
59
+ naming the column.
60
+ - `conn.insertArrow('sensors', ipcBuffer)` — bulk load straight from Arrow IPC data.
61
+ - `conn.writer('sensors')` — streaming writer with event-time `watermark(ts)` and
62
+ backpressure visibility (`pending()`, `isBackpressured()`).
57
63
 
58
- ## Subscriptions
64
+ ## Subscribing to streams
59
65
 
60
- Streams and materialized views are consumable frame by frame — async-iterable first:
66
+ Consume a stream or materialized view as it computes — async iteration first:
61
67
 
62
68
  ```js
63
69
  const sub = await conn.subscribe('sensor_rollup')
@@ -67,56 +73,60 @@ for await (const frame of sub) {
67
73
  }
68
74
  ```
69
75
 
70
- Push style delivers awaited frames to handlers (a slow handler backpressures instead of
71
- queueing): `conn.subscribeWith('sensor_rollup', { onData, onError, onClose })`. Streaming
72
- queries work the same way: `for await (const batch of conn.streamQuery(sql))`.
73
-
74
- Telemetry (`metrics()`, `sourceMetrics()`, `pipelineState()`, `pipelineWatermark()`,
75
- `totalEventsProcessed()`) and query cancellation (`cancelQuery(id)`) round out the runtime
76
- surface. Benchmark baseline: `docs/benchmarks.md`.
76
+ Prefer callbacks? `conn.subscribeWith('sensor_rollup', { onData, onError, onClose })`
77
+ delivers awaited frames a slow handler slows the stream instead of growing a queue.
78
+ Streaming queries work the same way:
79
+ `for await (const batch of conn.streamQuery(sql)) {}`.
77
80
 
78
81
  ## Errors
79
82
 
80
- Every failure throws a `LaminarError` subclass carrying the core's numeric `code`:
81
- `LaminarConnectionError` (100s), `LaminarSchemaError` (200s), `LaminarIngestionError`
82
- (300s), `LaminarQueryError` (400s), `LaminarSubscriptionError` (500s),
83
- `LaminarInternalError` (900s).
83
+ Every failure throws a `LaminarError` subclass with a numeric `code`:
84
+
85
+ | Class | Codes | Meaning |
86
+ | -------------------------- | ----- | ----------------------------------------- |
87
+ | `LaminarConnectionError` | 100s | connection lifecycle |
88
+ | `LaminarSchemaError` | 200s | unknown table, schema problems |
89
+ | `LaminarIngestionError` | 300s | bad rows, wrong types, closed writer |
90
+ | `LaminarQueryError` | 400s | SQL errors, non-queries |
91
+ | `LaminarSubscriptionError` | 500s | subscription failures (502 = fell behind) |
92
+ | `LaminarInternalError` | 900s | engine or binding internals |
84
93
 
85
94
  ```js
86
95
  try {
87
96
  conn.insert('sensors', [{ ts: 1, device: 'd1', value: 'oops' }])
88
97
  } catch (error) {
89
- if (error.code === 300) {
90
- // error.message: column 'value': expected a number, got string (row 0)
98
+ if (error instanceof LaminarIngestionError) {
99
+ // "column 'value': expected a number, got string (row 0)"
91
100
  }
92
101
  }
93
102
  ```
94
103
 
95
- ## Build from source
104
+ Runtime observability: `conn.metrics()`, `conn.sourceMetrics(name)`,
105
+ `conn.pipelineState()`, `conn.pipelineWatermark()`, `conn.totalEventsProcessed()`; long
106
+ queries can be cancelled with `conn.cancelQuery(id)`.
96
107
 
97
- Requires Rust stable (≥ 1.95) and Node ≥ 20 with pnpm.
108
+ ## Status
98
109
 
99
- ```sh
100
- just install # pnpm install (@napi-rs/cli, vitest, prettier, typescript, apache-arrow)
101
- just build # debug addon + generated loader + TypeScript layer (dist/)
102
- just test # vitest suites against the built addon
103
- just verify # fmt + clippy -D warnings + rust tests + build + vitest
104
- ```
110
+ `0.30.0-alpha` — the embedded surface is complete (queries, ingestion, subscriptions,
111
+ telemetry); the API may still change before 1.0. This binding pins
112
+ [LaminarDB](https://github.com/laminardb/laminardb) core `v0.30.0` and covers embedded
113
+ mode; multi-node clusters run through the server, not in-process.
105
114
 
106
- The first build clones and compiles the pinned LaminarDB core (git tag in `Cargo.toml`,
107
- registry in `CORE_PIN.md`) — expect a long cold build.
115
+ ## Developing this repository
108
116
 
109
- ## Platform support
117
+ Contributions need Rust stable (≥ 1.95), Node ≥ 20, and
118
+ [just](https://github.com/casey/just):
110
119
 
111
- The release matrix (Phase 3) covers macOS x64/arm64, Linux x64/arm64 (glibc and musl), and
112
- Windows x64, distributed as per-platform optional npm packages with no postinstall and no
113
- source compilation. Until then, `napi build` works anywhere the toolchain does.
114
-
115
- ## Documentation
120
+ ```sh
121
+ just install # pnpm install
122
+ just build # native addon + TypeScript layer
123
+ just test # full suite against the built addon
124
+ just verify # fmt + clippy + rust tests + build + vitest
125
+ ```
116
126
 
117
- - `docs/plans/`decision records and phase plans (start at
118
- `00-overview-and-decisions.md`)
119
- - `CORE_PIN.md` which core release each binding version ships
120
- - `CHANGELOG.md`
127
+ The first build compiles the pinned Rust core expect a long cold build. Engineering
128
+ records live in `docs/plans/` (decision records, phase plans) and `docs/reviews/`;
129
+ `CORE_PIN.md` tracks which core release each version ships; `docs/benchmarks.md` holds the
130
+ measured baseline.
121
131
 
122
132
  Apache-2.0, like the core.
package/index.mjs ADDED
@@ -0,0 +1,24 @@
1
+ // ESM entry. The implementation layer (dist/index.js) is CommonJS; this
2
+ // shim gives `import { LaminarDB } from '@laminardb/node'` real named ESM
3
+ // exports on every Node version, independent of CJS named-export detection.
4
+ // __test__/package-surface.spec.mjs asserts this list matches the CJS
5
+ // surface, so the two cannot drift.
6
+ import laminardb from './dist/index.js'
7
+
8
+ export const {
9
+ LaminarDB,
10
+ Connection,
11
+ Writer,
12
+ Subscription,
13
+ PushSubscription,
14
+ QueryStream,
15
+ toLaminarError,
16
+ tableFrom,
17
+ LaminarError,
18
+ LaminarConnectionError,
19
+ LaminarSchemaError,
20
+ LaminarIngestionError,
21
+ LaminarQueryError,
22
+ LaminarSubscriptionError,
23
+ LaminarInternalError,
24
+ } = laminardb
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@laminardb/node",
3
- "version": "0.30.0-alpha.1",
4
- "description": "Embedded streaming SQL for Node.js \u2014 official LaminarDB native binding",
3
+ "version": "0.30.0-alpha.2",
4
+ "description": "Embedded streaming SQL for Node.js official LaminarDB native binding",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
8
8
  "index.js",
9
+ "index.mjs",
9
10
  "dist"
10
11
  ],
11
12
  "napi": {
@@ -65,13 +66,22 @@
65
66
  "access": "public",
66
67
  "registry": "https://registry.npmjs.org/"
67
68
  },
69
+ "exports": {
70
+ ".": {
71
+ "types": "./dist/index.d.ts",
72
+ "import": "./index.mjs",
73
+ "require": "./dist/index.js",
74
+ "default": "./dist/index.js"
75
+ },
76
+ "./package.json": "./package.json"
77
+ },
68
78
  "optionalDependencies": {
69
- "@laminardb/node-darwin-x64": "0.30.0-alpha.1",
70
- "@laminardb/node-darwin-arm64": "0.30.0-alpha.1",
71
- "@laminardb/node-linux-x64-gnu": "0.30.0-alpha.1",
72
- "@laminardb/node-linux-x64-musl": "0.30.0-alpha.1",
73
- "@laminardb/node-linux-arm64-gnu": "0.30.0-alpha.1",
74
- "@laminardb/node-linux-arm64-musl": "0.30.0-alpha.1",
75
- "@laminardb/node-win32-x64-msvc": "0.30.0-alpha.1"
79
+ "@laminardb/node-linux-x64-gnu": "0.30.0-alpha.2",
80
+ "@laminardb/node-linux-x64-musl": "0.30.0-alpha.2",
81
+ "@laminardb/node-linux-arm64-gnu": "0.30.0-alpha.2",
82
+ "@laminardb/node-linux-arm64-musl": "0.30.0-alpha.2",
83
+ "@laminardb/node-darwin-x64": "0.30.0-alpha.2",
84
+ "@laminardb/node-darwin-arm64": "0.30.0-alpha.2",
85
+ "@laminardb/node-win32-x64-msvc": "0.30.0-alpha.2"
76
86
  }
77
- }
87
+ }
package/dist/arrow.d.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * `apache-arrow` interop helpers (plan 00 D6).
3
- *
4
- * `apache-arrow` is an optional peer dependency: these helpers load it
5
- * lazily and throw a clear `LaminarError` when it is absent. The IPC
6
- * `Buffer` API on results/batches always works without it.
7
- */
8
- import type { ArrowBatch, QueryResult } from './index.js';
9
- /**
10
- * Rehydrate a whole result (or a single batch) as an `apache-arrow` `Table`
11
- * via `tableFromIPC`. Requires the optional `apache-arrow` dependency.
12
- */
13
- export declare function tableFrom(source: QueryResult | ArrowBatch): unknown;
package/dist/arrow.js DELETED
@@ -1,31 +0,0 @@
1
- "use strict";
2
- /**
3
- * `apache-arrow` interop helpers (plan 00 D6).
4
- *
5
- * `apache-arrow` is an optional peer dependency: these helpers load it
6
- * lazily and throw a clear `LaminarError` when it is absent. The IPC
7
- * `Buffer` API on results/batches always works without it.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.tableFrom = tableFrom;
11
- const errors_js_1 = require("./errors.js");
12
- let arrowModule;
13
- function arrow() {
14
- if (arrowModule === undefined) {
15
- try {
16
- // WHY require: optional peer — load lazily so absence only fails here
17
- arrowModule = require('apache-arrow');
18
- }
19
- catch {
20
- throw new errors_js_1.LaminarInternalError('apache-arrow is not installed; add it as a dependency to use tableFrom(), or use the toIPC()/toArray() APIs', 900);
21
- }
22
- }
23
- return arrowModule;
24
- }
25
- /**
26
- * Rehydrate a whole result (or a single batch) as an `apache-arrow` `Table`
27
- * via `tableFromIPC`. Requires the optional `apache-arrow` dependency.
28
- */
29
- function tableFrom(source) {
30
- return arrow().tableFromIPC(source.toIPC());
31
- }
package/dist/errors.d.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * The typed error hierarchy (plan 00 D2/D8).
3
- *
4
- * The native seam throws plain `Error`s whose message starts with
5
- * `[LAMINAR_<code>]` (napi-rs 3.12 cannot carry custom codes across promise
6
- * rejections — see docs/plans/01 spike results). The public API re-throws
7
- * them as `LaminarError` subclasses with a real `code` property and the
8
- * prefix stripped from `message`.
9
- */
10
- /** Base class: engine or binding failure with a numeric core error code. */
11
- export declare class LaminarError extends Error {
12
- /** Numeric code from the core taxonomy (e.g. `400`). */
13
- readonly code: number;
14
- /** Class name (e.g. `LaminarQueryError`). */
15
- readonly codeName: string;
16
- constructor(message: string, code: number, options?: {
17
- cause?: unknown;
18
- });
19
- }
20
- /** 100–199: connection lifecycle failures. */
21
- export declare class LaminarConnectionError extends LaminarError {
22
- }
23
- /** 200–299: schema and catalog failures. */
24
- export declare class LaminarSchemaError extends LaminarError {
25
- }
26
- /** 300–399: ingestion failures. */
27
- export declare class LaminarIngestionError extends LaminarError {
28
- }
29
- /** 400–499: query failures. */
30
- export declare class LaminarQueryError extends LaminarError {
31
- }
32
- /** 500–599: subscription failures. */
33
- export declare class LaminarSubscriptionError extends LaminarError {
34
- }
35
- /** 900–999: internal engine or binding failures. */
36
- export declare class LaminarInternalError extends LaminarError {
37
- }
38
- /**
39
- * Convert a thrown native error into the typed hierarchy. Coded errors get
40
- * the matching subclass with the prefix stripped; napi argument-coercion
41
- * failures (below the engine layer) and anything unrecognized wrap into
42
- * `LaminarInternalError` with code `900`, preserving the original as
43
- * `cause`.
44
- */
45
- export declare function toLaminarError(error: unknown): LaminarError;
46
- /** Run `thunk`, re-throwing any failure through {@link toLaminarError}. */
47
- export declare function wrapSync<T>(thunk: () => T): T;
48
- /**
49
- * Call `work` and await its result, re-throwing both synchronous throws (napi
50
- * argument coercion happens before the promise exists) and rejections through
51
- * {@link toLaminarError}.
52
- */
53
- export declare function wrapAsync<T>(work: () => PromiseLike<T>): Promise<T>;
package/dist/errors.js DELETED
@@ -1,114 +0,0 @@
1
- "use strict";
2
- /**
3
- * The typed error hierarchy (plan 00 D2/D8).
4
- *
5
- * The native seam throws plain `Error`s whose message starts with
6
- * `[LAMINAR_<code>]` (napi-rs 3.12 cannot carry custom codes across promise
7
- * rejections — see docs/plans/01 spike results). The public API re-throws
8
- * them as `LaminarError` subclasses with a real `code` property and the
9
- * prefix stripped from `message`.
10
- */
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.LaminarInternalError = void 0;
13
- exports.LaminarSubscriptionError = void 0;
14
- exports.LaminarQueryError = void 0;
15
- exports.LaminarIngestionError = void 0;
16
- exports.LaminarSchemaError = void 0;
17
- exports.LaminarConnectionError = void 0;
18
- exports.LaminarError = void 0;
19
- exports.toLaminarError = toLaminarError;
20
- exports.wrapSync = wrapSync;
21
- exports.wrapAsync = wrapAsync;
22
- const CODE_PREFIX = /^\[LAMINAR_(\d+)\]\s?/;
23
- /** Base class: engine or binding failure with a numeric core error code. */
24
- class LaminarError extends Error {
25
- /** Numeric code from the core taxonomy (e.g. `400`). */
26
- code;
27
- /** Class name (e.g. `LaminarQueryError`). */
28
- codeName;
29
- constructor(message, code, options) {
30
- super(message, options);
31
- this.name = new.target.name;
32
- this.code = code;
33
- this.codeName = new.target.name;
34
- }
35
- }
36
- exports.LaminarError = LaminarError;
37
- /** 100–199: connection lifecycle failures. */
38
- class LaminarConnectionError extends LaminarError {
39
- }
40
- exports.LaminarConnectionError = LaminarConnectionError;
41
- /** 200–299: schema and catalog failures. */
42
- class LaminarSchemaError extends LaminarError {
43
- }
44
- exports.LaminarSchemaError = LaminarSchemaError;
45
- /** 300–399: ingestion failures. */
46
- class LaminarIngestionError extends LaminarError {
47
- }
48
- exports.LaminarIngestionError = LaminarIngestionError;
49
- /** 400–499: query failures. */
50
- class LaminarQueryError extends LaminarError {
51
- }
52
- exports.LaminarQueryError = LaminarQueryError;
53
- /** 500–599: subscription failures. */
54
- class LaminarSubscriptionError extends LaminarError {
55
- }
56
- exports.LaminarSubscriptionError = LaminarSubscriptionError;
57
- /** 900–999: internal engine or binding failures. */
58
- class LaminarInternalError extends LaminarError {
59
- }
60
- exports.LaminarInternalError = LaminarInternalError;
61
- function classFor(code) {
62
- if (code >= 100 && code <= 199)
63
- return LaminarConnectionError;
64
- if (code >= 200 && code <= 299)
65
- return LaminarSchemaError;
66
- if (code >= 300 && code <= 399)
67
- return LaminarIngestionError;
68
- if (code >= 400 && code <= 499)
69
- return LaminarQueryError;
70
- if (code >= 500 && code <= 599)
71
- return LaminarSubscriptionError;
72
- return LaminarInternalError;
73
- }
74
- /**
75
- * Convert a thrown native error into the typed hierarchy. Coded errors get
76
- * the matching subclass with the prefix stripped; napi argument-coercion
77
- * failures (below the engine layer) and anything unrecognized wrap into
78
- * `LaminarInternalError` with code `900`, preserving the original as
79
- * `cause`.
80
- */
81
- function toLaminarError(error) {
82
- if (error instanceof LaminarError)
83
- return error;
84
- const message = error instanceof Error ? error.message : String(error);
85
- const match = CODE_PREFIX.exec(message);
86
- if (match) {
87
- const code = Number.parseInt(match[1], 10);
88
- const Class = classFor(code);
89
- return new Class(message.slice(match[0].length), code, { cause: error });
90
- }
91
- return new LaminarInternalError(message, 900, { cause: error });
92
- }
93
- /** Run `thunk`, re-throwing any failure through {@link toLaminarError}. */
94
- function wrapSync(thunk) {
95
- try {
96
- return thunk();
97
- }
98
- catch (error) {
99
- throw toLaminarError(error);
100
- }
101
- }
102
- /**
103
- * Call `work` and await its result, re-throwing both synchronous throws (napi
104
- * argument coercion happens before the promise exists) and rejections through
105
- * {@link toLaminarError}.
106
- */
107
- async function wrapAsync(work) {
108
- try {
109
- return await work();
110
- }
111
- catch (error) {
112
- throw toLaminarError(error);
113
- }
114
- }
package/dist/index.d.ts DELETED
@@ -1,407 +0,0 @@
1
- /**
2
- * Public API of `@laminardb/node` (plan 00 D8).
3
- *
4
- * This module is the documented surface; the generated napi binding
5
- * (`index.js` at the package root) is an internal seam. Everything here
6
- * wraps the native calls so failures surface as the typed
7
- * {@link LaminarError} hierarchy.
8
- */
9
- export { LaminarError, LaminarConnectionError, LaminarSchemaError, LaminarIngestionError, LaminarQueryError, LaminarSubscriptionError, LaminarInternalError, toLaminarError, } from './errors.js';
10
- export { tableFrom } from './arrow.js';
11
- /** One column of a result schema; `dataType` is informational. */
12
- export interface FieldInfo {
13
- name: string;
14
- dataType: string;
15
- nullable: boolean;
16
- }
17
- /** Checkpointing options; an empty object enables manual checkpoints only. */
18
- export interface CheckpointConfig {
19
- /** Interval in milliseconds; omitted = manual `checkpoint()` only. */
20
- intervalMs?: number;
21
- /** One attempt deadline in milliseconds; omitted = core default (120 s). */
22
- timeoutMs?: number;
23
- /** Checkpoint directory; omitted = storage directory, then `./data`. */
24
- dataDir?: string;
25
- maxNodeDataBytes?: number;
26
- }
27
- /** Connection options for {@link LaminarDB.open}. */
28
- export interface OpenConfig {
29
- /** Local durability directory; wins over the positional path argument. */
30
- storageDir?: string;
31
- checkpoint?: CheckpointConfig;
32
- /** Default source buffer size in rows. */
33
- bufferSize?: number;
34
- /** Emit windowed aggregates incrementally before window close. */
35
- incrementalEmit?: boolean;
36
- /** Object-store URL for cloud checkpoints (e.g. `s3://bucket/prefix`). */
37
- objectStoreUrl?: string;
38
- objectStoreOptions?: Record<string, string>;
39
- }
40
- /** Row object: column name to value. `null` marks null slots. */
41
- export type Row = Record<string, unknown>;
42
- /** A fully collected query result. Obtain via `Connection.query()` or
43
- * `ExecuteOutcome.result`. */
44
- export interface QueryResult {
45
- /** Schema fields in declaration order. */
46
- schema(): FieldInfo[];
47
- numRows(): number;
48
- numBatches(): number;
49
- /** Batch `index` (0-based); throws `LaminarQueryError` (400) if out of range. */
50
- batch(index: number): ArrowBatch;
51
- /** The whole result as one Arrow IPC stream `Buffer`. */
52
- toIPC(): Buffer;
53
- /** All rows as objects; see the conversion notes in the README. */
54
- toArray(): Row[];
55
- }
56
- /** One Arrow RecordBatch of query output. */
57
- export interface ArrowBatch {
58
- numRows(): number;
59
- numColumns(): number;
60
- schema(): FieldInfo[];
61
- toIPC(): Buffer;
62
- toArray(): Row[];
63
- }
64
- /** One executed statement's outcome; `kind` discriminates the payload. */
65
- export interface ExecuteOutcome {
66
- readonly kind: 'ddl' | 'rows-affected' | 'query' | 'metadata';
67
- readonly statementType?: string;
68
- readonly objectName?: string;
69
- readonly rowsAffected?: number;
70
- readonly queryId?: number;
71
- /** The collected result for `query`/`metadata` kinds; `undefined` otherwise. */
72
- readonly result?: QueryResult;
73
- }
74
- /** One frame from a subscription: `data` carries a batch, `barrier` marks
75
- * checkpoint progress. */
76
- export interface SubscriptionFrame {
77
- readonly kind: 'data' | 'barrier';
78
- readonly batch?: ArrowBatch;
79
- /** Portal-local sequence (neither durable nor cluster-global). */
80
- readonly sequence: number;
81
- readonly epoch?: number;
82
- readonly checkpointId?: number;
83
- readonly throughSequence?: number;
84
- }
85
- /** Options for the subscription styles. */
86
- export interface SubscribeOptions {
87
- /** Optional SQL row filter applied server-side. */
88
- filter?: string;
89
- /** Replay entries after this committed checkpoint epoch (rejects when
90
- * unretained). */
91
- fromEpoch?: number;
92
- }
93
- /** Push-style handlers for `subscribeWith`. */
94
- export interface PushHandlers {
95
- /** May be sync or async: the facade normalizes every delivery to an
96
- * awaited promise, so slow handlers backpressure the stream. Rejections
97
- * surface once via `onError` and stop delivery. */
98
- onData: (frame: SubscriptionFrame) => void | Promise<void>;
99
- onError?: (error: {
100
- code: number;
101
- message: string;
102
- }) => void;
103
- onClose?: () => void;
104
- }
105
- /** Aggregate pipeline counters. */
106
- export interface PipelineMetricsInfo {
107
- totalEventsIngested: number;
108
- totalEventsEmitted: number;
109
- totalEventsDropped: number;
110
- totalCycles: number;
111
- totalBatches: number;
112
- uptimeMs: number;
113
- state: string;
114
- sourceCount: number;
115
- streamCount: number;
116
- sinkCount: number;
117
- pipelineWatermark: number;
118
- mvUpdates: number;
119
- mvBytesStored: number;
120
- }
121
- /** Counters for one source. */
122
- export interface SourceMetricsInfo {
123
- name: string;
124
- totalEvents: number;
125
- pending: number;
126
- capacity: number;
127
- isBackpressured: boolean;
128
- watermark: number;
129
- utilization: number;
130
- }
131
- /** Counters for one stream. */
132
- export interface StreamMetricsInfo {
133
- name: string;
134
- totalEvents: number;
135
- sql?: string;
136
- }
137
- /** One manual checkpoint's outcome. */
138
- export interface CheckpointOutcome {
139
- readonly success: boolean;
140
- readonly checkpointId: number;
141
- readonly epoch: number;
142
- readonly durationMs: number;
143
- readonly error?: string;
144
- }
145
- /** One registered source. */
146
- export interface SourceInfo {
147
- name: string;
148
- schema: FieldInfo[];
149
- watermarkColumn?: string;
150
- }
151
- interface NativeFieldInfo {
152
- name: string;
153
- dataType: string;
154
- nullable: boolean;
155
- }
156
- interface NativeArrowBatch {
157
- numRows(): number;
158
- numColumns(): number;
159
- schema(): NativeFieldInfo[];
160
- toIPC(): Buffer;
161
- toArray(): Record<string, unknown>[];
162
- }
163
- interface NativeQueryResult {
164
- schema(): NativeFieldInfo[];
165
- numRows(): number;
166
- numBatches(): number;
167
- batch(index: number): NativeArrowBatch;
168
- toIPC(): Buffer;
169
- toArray(): Record<string, unknown>[];
170
- }
171
- interface NativeExecuteOutcome {
172
- readonly kind: string;
173
- readonly statementType?: string;
174
- readonly objectName?: string;
175
- readonly rowsAffected?: number;
176
- readonly queryId?: number;
177
- readonly result?: NativeQueryResult;
178
- }
179
- interface NativeSubscriptionFrame {
180
- readonly kind: 'data' | 'barrier' | string;
181
- readonly batch?: NativeArrowBatch;
182
- readonly sequence: number;
183
- readonly epoch?: number;
184
- readonly checkpointId?: number;
185
- readonly throughSequence?: number;
186
- }
187
- interface NativeSubscription {
188
- schema(): NativeFieldInfo[];
189
- nextFrame(): Promise<NativeSubscriptionFrame | null>;
190
- isActive(): boolean;
191
- cancel(): void;
192
- }
193
- interface NativePushSubscription {
194
- isActive(): boolean;
195
- close(): Promise<void>;
196
- }
197
- interface NativeQueryStream {
198
- schema(): NativeFieldInfo[];
199
- queryId(): number;
200
- nextBatch(): Promise<NativeArrowBatch | null>;
201
- cancel(): void;
202
- }
203
- interface NativeConnection {
204
- subscribe(name: string, filter: string | null, fromEpoch: number | null): Promise<NativeSubscription>;
205
- subscribeWith(name: string, filter: string | null, fromEpoch: number | null, onData: (frame: SubscriptionFrame) => void | Promise<void>, onError: (error: {
206
- code: number;
207
- message: string;
208
- }) => void, onClose: () => void): NativePushSubscription;
209
- streamQuery(sql: string): Promise<NativeQueryStream>;
210
- cancelQuery(queryId: number): Promise<void>;
211
- metrics(): Promise<Omit<PipelineMetricsInfo, never>>;
212
- sourceMetrics(name: string): Promise<SourceMetricsInfo>;
213
- allSourceMetrics(): Promise<SourceMetricsInfo[]>;
214
- streamMetrics(name: string): Promise<StreamMetricsInfo>;
215
- allStreamMetrics(): Promise<StreamMetricsInfo[]>;
216
- pipelineState(): Promise<string>;
217
- pipelineWatermark(): Promise<number>;
218
- totalEventsProcessed(): Promise<number>;
219
- execute(sql: string): Promise<NativeExecuteOutcome>;
220
- query(sql: string): Promise<NativeQueryResult>;
221
- insert(source: string, rows: Record<string, unknown>[]): number;
222
- insertArrow(source: string, bytes: Buffer): number;
223
- writer(source: string): NativeWriter;
224
- start(): Promise<void>;
225
- checkpoint(): Promise<{
226
- success: boolean;
227
- checkpointId: number;
228
- epoch: number;
229
- durationMs: number;
230
- error?: string;
231
- }>;
232
- isCheckpointEnabled(): boolean;
233
- listSources(): Promise<string[]>;
234
- listStreams(): Promise<string[]>;
235
- listSinks(): Promise<string[]>;
236
- sourceInfos(): Promise<{
237
- name: string;
238
- schema: NativeFieldInfo[];
239
- watermarkColumn?: string;
240
- }[]>;
241
- schema(name: string): Promise<NativeFieldInfo[]>;
242
- isClosed(): boolean;
243
- close(): Promise<void>;
244
- }
245
- interface NativeWriter {
246
- name(): string;
247
- schema(): NativeFieldInfo[];
248
- writeRows(rows: Record<string, unknown>[]): number;
249
- writeArrow(bytes: Buffer): number;
250
- watermark(timestamp: number): void;
251
- currentWatermark(): number;
252
- pending(): number;
253
- capacity(): number;
254
- isBackpressured(): boolean;
255
- close(): void;
256
- }
257
- /** Streaming writer for one source; single-owner. */
258
- export declare class Writer {
259
- #private;
260
- /** @internal */
261
- constructor(native: NativeWriter);
262
- name(): string;
263
- schema(): FieldInfo[];
264
- /** Push one batch built from row objects; returns rows written. `Date`
265
- * values are converted to epoch milliseconds automatically. */
266
- writeRows(rows: Row[]): number;
267
- /** Push every batch from an Arrow IPC stream `Buffer`. */
268
- writeArrow(bytes: Buffer): number;
269
- /** Advance the event-time watermark (milliseconds since epoch). */
270
- watermark(timestamp: number): void;
271
- currentWatermark(): number;
272
- /** Rows buffered in the source, not yet consumed by the pipeline. */
273
- pending(): number;
274
- capacity(): number;
275
- /** True when the source buffer is more than 80% full — slow down. */
276
- isBackpressured(): boolean;
277
- /** Idempotent; writes after close throw `LaminarIngestionError` (301). */
278
- close(): void;
279
- }
280
- /**
281
- * An open LaminarDB connection. Safe to share across async contexts;
282
- * `close()` is idempotent, and use after close throws
283
- * `LaminarConnectionError` (101) rather than crashing.
284
- *
285
- * DDL that changes topology (`CREATE SOURCE`/`STREAM`/`SINK`) must run
286
- * before `start()`; the engine rejects topology changes on a running
287
- * pipeline.
288
- */
289
- export declare class Connection {
290
- #private;
291
- /** @internal */
292
- constructor(native: NativeConnection);
293
- /**
294
- * Execute one SQL statement. `SELECT` returns `kind: 'query'` with the
295
- * fully collected `result`; SHOW/DESCRIBE return `kind: 'metadata'`.
296
- */
297
- execute(sql: string): Promise<ExecuteOutcome>;
298
- /** Execute a query and return its collected result; non-query SQL throws
299
- * `LaminarQueryError` (400). */
300
- query(sql: string): Promise<QueryResult>;
301
- /** Ingest row objects into a source; returns rows pushed. `Date` values
302
- * are converted to epoch milliseconds automatically. */
303
- insert(source: string, rows: Row[]): number;
304
- /** Ingest an Arrow IPC stream `Buffer` into a source; returns rows pushed. */
305
- insertArrow(source: string, bytes: Buffer): number;
306
- /** Open a streaming writer for a source (throws 200 if unknown). */
307
- writer(source: string): Writer;
308
- /** Start the streaming pipeline (idempotent). */
309
- start(): Promise<void>;
310
- /**
311
- * Trigger a manual checkpoint. Requires checkpointing in the open config
312
- * and at least one stream or sink in the topology (the core wires the
313
- * coordinator only for real pipelines).
314
- */
315
- checkpoint(): Promise<CheckpointOutcome>;
316
- isCheckpointEnabled(): boolean;
317
- listSources(): Promise<string[]>;
318
- listStreams(): Promise<string[]>;
319
- listSinks(): Promise<string[]>;
320
- sourceInfos(): Promise<SourceInfo[]>;
321
- /** Schema of a source; unknown names throw `LaminarSchemaError` (200). */
322
- schema(name: string): Promise<FieldInfo[]>;
323
- /** Subscribe to a stream or materialized view (pull style). The returned
324
- * subscription is async-iterable; terminal failures throw
325
- * `LaminarSubscriptionError` (502 lag / 500 otherwise) and end iteration. */
326
- subscribe(name: string, options?: SubscribeOptions): Promise<Subscription>;
327
- /** Subscribe push style: `onData` per frame (awaited per delivery —
328
- * backpressure, not queueing). Errors and open failures surface via
329
- * `onError`, always followed by `onClose`. */
330
- subscribeWith(name: string, handlers: PushHandlers, options?: SubscribeOptions): PushSubscription;
331
- /** Execute a query and stream its batches on demand; non-query SQL throws
332
- * `LaminarQueryError` (400). The stream is async-iterable. */
333
- streamQuery(sql: string): Promise<QueryStream>;
334
- /** Cancel a query by the id reported by `streamQuery().queryId`. */
335
- cancelQuery(queryId: number): Promise<void>;
336
- /** Aggregate pipeline counters. */
337
- metrics(): Promise<PipelineMetricsInfo>;
338
- /** Counters for one source; unknown names throw (200). */
339
- sourceMetrics(name: string): Promise<SourceMetricsInfo>;
340
- /** Counters for every source. */
341
- allSourceMetrics(): Promise<SourceMetricsInfo[]>;
342
- /** Counters for one stream; unknown names throw (200). */
343
- streamMetrics(name: string): Promise<StreamMetricsInfo>;
344
- /** Counters for every stream. */
345
- allStreamMetrics(): Promise<StreamMetricsInfo[]>;
346
- /** Engine lifecycle state name (e.g. `Running`). */
347
- pipelineState(): Promise<string>;
348
- /** Minimum event-time watermark across sources (epoch milliseconds). */
349
- pipelineWatermark(): Promise<number>;
350
- /** Total events the pipeline has processed. */
351
- totalEventsProcessed(): Promise<number>;
352
- isClosed(): boolean;
353
- /** Graceful shutdown; idempotent and safe under concurrent calls. */
354
- close(): Promise<void>;
355
- }
356
- /** A pull-based framed subscription; async-iterable:
357
- * `for await (const frame of conn.subscribe('rollup'))`. */
358
- export declare class Subscription {
359
- #private;
360
- /** @internal */
361
- constructor(native: NativeSubscription);
362
- schema(): FieldInfo[];
363
- /** Next frame, or `null` at end-of-stream / after `cancel()`. */
364
- nextFrame(): Promise<SubscriptionFrame | null>;
365
- isActive(): boolean;
366
- /** Idempotent; a pending `nextFrame` resolves `null`. */
367
- cancel(): void;
368
- [Symbol.asyncIterator](): AsyncIterator<SubscriptionFrame>;
369
- }
370
- /** A push-based subscription handle; `close()` stops delivery and resolves
371
- * after the reader has stopped (no callbacks fire afterwards). */
372
- export declare class PushSubscription {
373
- #private;
374
- /** @internal */
375
- constructor(native: NativePushSubscription);
376
- isActive(): boolean;
377
- /** Stop delivery and wait for the reader. Idempotent. */
378
- close(): Promise<void>;
379
- }
380
- /** A streaming query result; async-iterable over `ArrowBatch`es:
381
- * `for await (const batch of conn.streamQuery(sql))`. */
382
- export declare class QueryStream {
383
- #private;
384
- /** @internal */
385
- constructor(native: NativeQueryStream);
386
- schema(): FieldInfo[];
387
- /** Query id, usable with `Connection.cancelQuery`. */
388
- queryId(): number;
389
- /** Next batch, or `null` at end-of-stream / after `cancel()`. */
390
- nextBatch(): Promise<ArrowBatch | null>;
391
- /** Idempotent; a pending `nextBatch` resolves `null`. */
392
- cancel(): void;
393
- [Symbol.asyncIterator](): AsyncIterator<ArrowBatch>;
394
- }
395
- /** Entry point. */
396
- export declare class LaminarDB {
397
- private constructor();
398
- /**
399
- * Open an embedded database. `open()` and `open(':memory:')` are
400
- * in-memory; `open(path)` sets the storage directory (local-durable
401
- * embedded mode when `checkpoint` is configured); `config.storageDir`
402
- * wins over the positional path.
403
- */
404
- static open(path?: string, config?: OpenConfig): Promise<Connection>;
405
- /** Binding and pinned-core version, e.g. `0.30.0-alpha.1 (core v0.30.0)`. */
406
- static version(): string;
407
- }
package/dist/index.js DELETED
@@ -1,420 +0,0 @@
1
- "use strict";
2
- /**
3
- * Public API of `@laminardb/node` (plan 00 D8).
4
- *
5
- * This module is the documented surface; the generated napi binding
6
- * (`index.js` at the package root) is an internal seam. Everything here
7
- * wraps the native calls so failures surface as the typed
8
- * {@link LaminarError} hierarchy.
9
- */
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.LaminarDB = void 0;
12
- exports.QueryStream = void 0;
13
- exports.PushSubscription = void 0;
14
- exports.Subscription = void 0;
15
- exports.Connection = void 0;
16
- exports.Writer = void 0;
17
- exports.tableFrom = void 0;
18
- exports.toLaminarError = void 0;
19
- exports.LaminarInternalError = void 0;
20
- exports.LaminarSubscriptionError = void 0;
21
- exports.LaminarQueryError = void 0;
22
- exports.LaminarIngestionError = void 0;
23
- exports.LaminarSchemaError = void 0;
24
- exports.LaminarConnectionError = void 0;
25
- exports.LaminarError = void 0;
26
- const errors_js_1 = require("./errors.js");
27
- var errors_js_2 = require("./errors.js");
28
- Object.defineProperty(exports, "LaminarError", { enumerable: true, get: function () { return errors_js_2.LaminarError; } });
29
- Object.defineProperty(exports, "LaminarConnectionError", { enumerable: true, get: function () { return errors_js_2.LaminarConnectionError; } });
30
- Object.defineProperty(exports, "LaminarSchemaError", { enumerable: true, get: function () { return errors_js_2.LaminarSchemaError; } });
31
- Object.defineProperty(exports, "LaminarIngestionError", { enumerable: true, get: function () { return errors_js_2.LaminarIngestionError; } });
32
- Object.defineProperty(exports, "LaminarQueryError", { enumerable: true, get: function () { return errors_js_2.LaminarQueryError; } });
33
- Object.defineProperty(exports, "LaminarSubscriptionError", { enumerable: true, get: function () { return errors_js_2.LaminarSubscriptionError; } });
34
- Object.defineProperty(exports, "LaminarInternalError", { enumerable: true, get: function () { return errors_js_2.LaminarInternalError; } });
35
- Object.defineProperty(exports, "toLaminarError", { enumerable: true, get: function () { return errors_js_2.toLaminarError; } });
36
- var arrow_js_1 = require("./arrow.js");
37
- Object.defineProperty(exports, "tableFrom", { enumerable: true, get: function () { return arrow_js_1.tableFrom; } });
38
- const native = require('../index.js') ?? undefined;
39
- /** Streaming writer for one source; single-owner. */
40
- class Writer {
41
- #native;
42
- /** @internal */
43
- constructor(native) {
44
- this.#native = native;
45
- }
46
- name() {
47
- return this.#native.name();
48
- }
49
- schema() {
50
- return (0, errors_js_1.wrapSync)(() => this.#native.schema());
51
- }
52
- /** Push one batch built from row objects; returns rows written. `Date`
53
- * values are converted to epoch milliseconds automatically. */
54
- writeRows(rows) {
55
- return (0, errors_js_1.wrapSync)(() => this.#native.writeRows(normalizeRows(rows)));
56
- }
57
- /** Push every batch from an Arrow IPC stream `Buffer`. */
58
- writeArrow(bytes) {
59
- return (0, errors_js_1.wrapSync)(() => this.#native.writeArrow(bytes));
60
- }
61
- /** Advance the event-time watermark (milliseconds since epoch). */
62
- watermark(timestamp) {
63
- this.#native.watermark(timestamp);
64
- }
65
- currentWatermark() {
66
- return this.#native.currentWatermark();
67
- }
68
- /** Rows buffered in the source, not yet consumed by the pipeline. */
69
- pending() {
70
- return this.#native.pending();
71
- }
72
- capacity() {
73
- return this.#native.capacity();
74
- }
75
- /** True when the source buffer is more than 80% full — slow down. */
76
- isBackpressured() {
77
- return this.#native.isBackpressured();
78
- }
79
- /** Idempotent; writes after close throw `LaminarIngestionError` (301). */
80
- close() {
81
- this.#native.close();
82
- }
83
- }
84
- exports.Writer = Writer;
85
- /** Convert `Date` instances to epoch milliseconds (the native seam's
86
- * temporal convention) inside row objects; other values pass through. */
87
- function normalizeRows(rows) {
88
- let changed = false;
89
- const normalized = rows.map((row) => {
90
- let rowChanged = false;
91
- const copy = {};
92
- for (const [key, value] of Object.entries(row)) {
93
- if (value instanceof Date) {
94
- copy[key] = value.getTime();
95
- rowChanged = true;
96
- }
97
- else {
98
- copy[key] = value;
99
- }
100
- }
101
- if (rowChanged)
102
- changed = true;
103
- return rowChanged ? copy : row;
104
- });
105
- return changed ? normalized : rows;
106
- }
107
- /**
108
- * An open LaminarDB connection. Safe to share across async contexts;
109
- * `close()` is idempotent, and use after close throws
110
- * `LaminarConnectionError` (101) rather than crashing.
111
- *
112
- * DDL that changes topology (`CREATE SOURCE`/`STREAM`/`SINK`) must run
113
- * before `start()`; the engine rejects topology changes on a running
114
- * pipeline.
115
- */
116
- class Connection {
117
- #native;
118
- /** @internal */
119
- constructor(native) {
120
- this.#native = native;
121
- }
122
- /**
123
- * Execute one SQL statement. `SELECT` returns `kind: 'query'` with the
124
- * fully collected `result`; SHOW/DESCRIBE return `kind: 'metadata'`.
125
- */
126
- execute(sql) {
127
- return (0, errors_js_1.wrapAsync)(() => this.#native.execute(sql)).then(mapOutcome);
128
- }
129
- /** Execute a query and return its collected result; non-query SQL throws
130
- * `LaminarQueryError` (400). */
131
- query(sql) {
132
- return (0, errors_js_1.wrapAsync)(() => this.#native.query(sql)).then(wrapResult);
133
- }
134
- /** Ingest row objects into a source; returns rows pushed. `Date` values
135
- * are converted to epoch milliseconds automatically. */
136
- insert(source, rows) {
137
- return (0, errors_js_1.wrapSync)(() => this.#native.insert(source, normalizeRows(rows)));
138
- }
139
- /** Ingest an Arrow IPC stream `Buffer` into a source; returns rows pushed. */
140
- insertArrow(source, bytes) {
141
- return (0, errors_js_1.wrapSync)(() => this.#native.insertArrow(source, bytes));
142
- }
143
- /** Open a streaming writer for a source (throws 200 if unknown). */
144
- writer(source) {
145
- return (0, errors_js_1.wrapSync)(() => new Writer(this.#native.writer(source)));
146
- }
147
- /** Start the streaming pipeline (idempotent). */
148
- start() {
149
- return (0, errors_js_1.wrapAsync)(() => this.#native.start());
150
- }
151
- /**
152
- * Trigger a manual checkpoint. Requires checkpointing in the open config
153
- * and at least one stream or sink in the topology (the core wires the
154
- * coordinator only for real pipelines).
155
- */
156
- checkpoint() {
157
- return (0, errors_js_1.wrapAsync)(() => this.#native.checkpoint());
158
- }
159
- isCheckpointEnabled() {
160
- return this.#native.isCheckpointEnabled();
161
- }
162
- listSources() {
163
- return (0, errors_js_1.wrapAsync)(() => this.#native.listSources());
164
- }
165
- listStreams() {
166
- return (0, errors_js_1.wrapAsync)(() => this.#native.listStreams());
167
- }
168
- listSinks() {
169
- return (0, errors_js_1.wrapAsync)(() => this.#native.listSinks());
170
- }
171
- sourceInfos() {
172
- return (0, errors_js_1.wrapAsync)(() => this.#native.sourceInfos());
173
- }
174
- /** Schema of a source; unknown names throw `LaminarSchemaError` (200). */
175
- schema(name) {
176
- return (0, errors_js_1.wrapAsync)(() => this.#native.schema(name));
177
- }
178
- /** Subscribe to a stream or materialized view (pull style). The returned
179
- * subscription is async-iterable; terminal failures throw
180
- * `LaminarSubscriptionError` (502 lag / 500 otherwise) and end iteration. */
181
- subscribe(name, options) {
182
- return (0, errors_js_1.wrapAsync)(() => this.#native.subscribe(name, options?.filter ?? null, options?.fromEpoch ?? null)).then((subscription) => new Subscription(subscription));
183
- }
184
- /** Subscribe push style: `onData` per frame (awaited per delivery —
185
- * backpressure, not queueing). Errors and open failures surface via
186
- * `onError`, always followed by `onClose`. */
187
- subscribeWith(name, handlers, options) {
188
- // WHY the wrapper: native delivery awaits the returned promise, so sync
189
- // handlers must still produce one; this is the settlement-aware
190
- // backpressure contract (plan 03).
191
- const userHandler = handlers.onData;
192
- const promiseReturning = (frame) => {
193
- const settled = userHandler(frame);
194
- return settled instanceof Promise ? settled : Promise.resolve();
195
- };
196
- const native = (0, errors_js_1.wrapSync)(() => this.#native.subscribeWith(name, options?.filter ?? null, options?.fromEpoch ?? null, promiseReturning, handlers.onError ?? (() => { }), handlers.onClose ?? (() => { })));
197
- return new PushSubscription(native);
198
- }
199
- /** Execute a query and stream its batches on demand; non-query SQL throws
200
- * `LaminarQueryError` (400). The stream is async-iterable. */
201
- streamQuery(sql) {
202
- return (0, errors_js_1.wrapAsync)(() => this.#native.streamQuery(sql)).then((stream) => new QueryStream(stream));
203
- }
204
- /** Cancel a query by the id reported by `streamQuery().queryId`. */
205
- cancelQuery(queryId) {
206
- return (0, errors_js_1.wrapAsync)(() => this.#native.cancelQuery(queryId));
207
- }
208
- /** Aggregate pipeline counters. */
209
- metrics() {
210
- return (0, errors_js_1.wrapAsync)(() => this.#native.metrics());
211
- }
212
- /** Counters for one source; unknown names throw (200). */
213
- sourceMetrics(name) {
214
- return (0, errors_js_1.wrapAsync)(() => this.#native.sourceMetrics(name));
215
- }
216
- /** Counters for every source. */
217
- allSourceMetrics() {
218
- return (0, errors_js_1.wrapAsync)(() => this.#native.allSourceMetrics());
219
- }
220
- /** Counters for one stream; unknown names throw (200). */
221
- streamMetrics(name) {
222
- return (0, errors_js_1.wrapAsync)(() => this.#native.streamMetrics(name));
223
- }
224
- /** Counters for every stream. */
225
- allStreamMetrics() {
226
- return (0, errors_js_1.wrapAsync)(() => this.#native.allStreamMetrics());
227
- }
228
- /** Engine lifecycle state name (e.g. `Running`). */
229
- pipelineState() {
230
- return (0, errors_js_1.wrapAsync)(() => this.#native.pipelineState());
231
- }
232
- /** Minimum event-time watermark across sources (epoch milliseconds). */
233
- pipelineWatermark() {
234
- return (0, errors_js_1.wrapAsync)(() => this.#native.pipelineWatermark());
235
- }
236
- /** Total events the pipeline has processed. */
237
- totalEventsProcessed() {
238
- return (0, errors_js_1.wrapAsync)(() => this.#native.totalEventsProcessed());
239
- }
240
- isClosed() {
241
- return this.#native.isClosed();
242
- }
243
- /** Graceful shutdown; idempotent and safe under concurrent calls. */
244
- close() {
245
- return (0, errors_js_1.wrapAsync)(() => this.#native.close());
246
- }
247
- }
248
- exports.Connection = Connection;
249
- /** A pull-based framed subscription; async-iterable:
250
- * `for await (const frame of conn.subscribe('rollup'))`. */
251
- class Subscription {
252
- #native;
253
- /** @internal */
254
- constructor(native) {
255
- this.#native = native;
256
- }
257
- schema() {
258
- return (0, errors_js_1.wrapSync)(() => this.#native.schema());
259
- }
260
- /** Next frame, or `null` at end-of-stream / after `cancel()`. */
261
- nextFrame() {
262
- return (0, errors_js_1.wrapAsync)(() => this.#native.nextFrame());
263
- }
264
- isActive() {
265
- return this.#native.isActive();
266
- }
267
- /** Idempotent; a pending `nextFrame` resolves `null`. */
268
- cancel() {
269
- this.#native.cancel();
270
- }
271
- [Symbol.asyncIterator]() {
272
- return {
273
- next: async () => {
274
- const frame = await this.nextFrame();
275
- return frame === null
276
- ? { value: undefined, done: true }
277
- : { value: frame, done: false };
278
- },
279
- return: async () => {
280
- this.cancel();
281
- return { value: undefined, done: true };
282
- },
283
- };
284
- }
285
- }
286
- exports.Subscription = Subscription;
287
- /** A push-based subscription handle; `close()` stops delivery and resolves
288
- * after the reader has stopped (no callbacks fire afterwards). */
289
- class PushSubscription {
290
- #native;
291
- /** @internal */
292
- constructor(native) {
293
- this.#native = native;
294
- }
295
- isActive() {
296
- return this.#native.isActive();
297
- }
298
- /** Stop delivery and wait for the reader. Idempotent. */
299
- close() {
300
- return (0, errors_js_1.wrapAsync)(() => this.#native.close());
301
- }
302
- }
303
- exports.PushSubscription = PushSubscription;
304
- /** A streaming query result; async-iterable over `ArrowBatch`es:
305
- * `for await (const batch of conn.streamQuery(sql))`. */
306
- class QueryStream {
307
- #native;
308
- /** @internal */
309
- constructor(native) {
310
- this.#native = native;
311
- }
312
- schema() {
313
- return (0, errors_js_1.wrapSync)(() => this.#native.schema());
314
- }
315
- /** Query id, usable with `Connection.cancelQuery`. */
316
- queryId() {
317
- return this.#native.queryId();
318
- }
319
- /** Next batch, or `null` at end-of-stream / after `cancel()`. */
320
- nextBatch() {
321
- return (0, errors_js_1.wrapAsync)(() => this.#native.nextBatch());
322
- }
323
- /** Idempotent; a pending `nextBatch` resolves `null`. */
324
- cancel() {
325
- this.#native.cancel();
326
- }
327
- [Symbol.asyncIterator]() {
328
- return {
329
- next: async () => {
330
- const batch = await this.nextBatch();
331
- return batch === null
332
- ? { value: undefined, done: true }
333
- : { value: batch, done: false };
334
- },
335
- return: async () => {
336
- this.cancel();
337
- return { value: undefined, done: true };
338
- },
339
- };
340
- }
341
- }
342
- exports.QueryStream = QueryStream;
343
- /** Entry point. */
344
- class LaminarDB {
345
- constructor() {
346
- throw new Error('LaminarDB is a static entry point; use LaminarDB.open()');
347
- }
348
- /**
349
- * Open an embedded database. `open()` and `open(':memory:')` are
350
- * in-memory; `open(path)` sets the storage directory (local-durable
351
- * embedded mode when `checkpoint` is configured); `config.storageDir`
352
- * wins over the positional path.
353
- */
354
- static open(path, config) {
355
- return (0, errors_js_1.wrapAsync)(() => native.open(path, config)).then((connection) => new Connection(connection));
356
- }
357
- /** Binding and pinned-core version, e.g. `0.30.0-alpha.1 (core v0.30.0)`. */
358
- static version() {
359
- return native.version();
360
- }
361
- }
362
- exports.LaminarDB = LaminarDB;
363
- class QueryResultImpl {
364
- #native;
365
- constructor(native) {
366
- this.#native = native;
367
- }
368
- schema() {
369
- return (0, errors_js_1.wrapSync)(() => this.#native.schema());
370
- }
371
- numRows() {
372
- return this.#native.numRows();
373
- }
374
- numBatches() {
375
- return this.#native.numBatches();
376
- }
377
- batch(index) {
378
- return (0, errors_js_1.wrapSync)(() => new ArrowBatchImpl(this.#native.batch(index)));
379
- }
380
- toIPC() {
381
- return (0, errors_js_1.wrapSync)(() => this.#native.toIPC());
382
- }
383
- toArray() {
384
- return (0, errors_js_1.wrapSync)(() => this.#native.toArray());
385
- }
386
- }
387
- class ArrowBatchImpl {
388
- #native;
389
- constructor(native) {
390
- this.#native = native;
391
- }
392
- numRows() {
393
- return this.#native.numRows();
394
- }
395
- numColumns() {
396
- return this.#native.numColumns();
397
- }
398
- schema() {
399
- return (0, errors_js_1.wrapSync)(() => this.#native.schema());
400
- }
401
- toIPC() {
402
- return (0, errors_js_1.wrapSync)(() => this.#native.toIPC());
403
- }
404
- toArray() {
405
- return (0, errors_js_1.wrapSync)(() => this.#native.toArray());
406
- }
407
- }
408
- function wrapResult(native) {
409
- return new QueryResultImpl(native);
410
- }
411
- function mapOutcome(native) {
412
- return {
413
- kind: native.kind,
414
- statementType: native.statementType,
415
- objectName: native.objectName,
416
- rowsAffected: native.rowsAffected,
417
- queryId: native.queryId,
418
- result: native.result === undefined ? undefined : wrapResult(native.result),
419
- };
420
- }