@laminardb/node 0.30.0-alpha.2 → 0.30.0-alpha.3
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/dist/arrow.d.ts +13 -0
- package/dist/arrow.js +31 -0
- package/dist/errors.d.ts +53 -0
- package/dist/errors.js +114 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +420 -0
- package/package.json +8 -8
package/dist/arrow.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laminardb/node",
|
|
3
|
-
"version": "0.30.0-alpha.
|
|
3
|
+
"version": "0.30.0-alpha.3",
|
|
4
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",
|
|
@@ -76,12 +76,12 @@
|
|
|
76
76
|
"./package.json": "./package.json"
|
|
77
77
|
},
|
|
78
78
|
"optionalDependencies": {
|
|
79
|
-
"@laminardb/node-linux-x64-gnu": "0.30.0-alpha.
|
|
80
|
-
"@laminardb/node-linux-x64-musl": "0.30.0-alpha.
|
|
81
|
-
"@laminardb/node-linux-arm64-gnu": "0.30.0-alpha.
|
|
82
|
-
"@laminardb/node-linux-arm64-musl": "0.30.0-alpha.
|
|
83
|
-
"@laminardb/node-darwin-x64": "0.30.0-alpha.
|
|
84
|
-
"@laminardb/node-darwin-arm64": "0.30.0-alpha.
|
|
85
|
-
"@laminardb/node-win32-x64-msvc": "0.30.0-alpha.
|
|
79
|
+
"@laminardb/node-linux-x64-gnu": "0.30.0-alpha.3",
|
|
80
|
+
"@laminardb/node-linux-x64-musl": "0.30.0-alpha.3",
|
|
81
|
+
"@laminardb/node-linux-arm64-gnu": "0.30.0-alpha.3",
|
|
82
|
+
"@laminardb/node-linux-arm64-musl": "0.30.0-alpha.3",
|
|
83
|
+
"@laminardb/node-darwin-x64": "0.30.0-alpha.3",
|
|
84
|
+
"@laminardb/node-darwin-arm64": "0.30.0-alpha.3",
|
|
85
|
+
"@laminardb/node-win32-x64-msvc": "0.30.0-alpha.3"
|
|
86
86
|
}
|
|
87
87
|
}
|