@sqd-pipes/delta-db 0.0.1-alpha.7 → 0.0.1-alpha.8
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 +147 -57
- package/dist/column.d.ts +20 -0
- package/dist/column.js +20 -0
- package/dist/column.js.map +1 -0
- package/dist/ddl.d.ts +45 -0
- package/dist/ddl.js +165 -0
- package/dist/ddl.js.map +1 -0
- package/dist/delta-db.d.ts +58 -0
- package/dist/delta-db.js +88 -0
- package/dist/delta-db.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/{src/index.d.ts → dist/native/native.d.ts} +43 -34
- package/{src → dist/native}/native.js +4 -3
- package/dist/pipeline.d.ts +33 -0
- package/dist/pipeline.js +107 -0
- package/dist/pipeline.js.map +1 -0
- package/package.json +19 -10
- package/src/{delta-db.node → native/delta-db.darwin-arm64.node} +0 -0
- package/src/{delta-db.darwin-x64.node → native/delta-db.darwin-x64.node} +0 -0
- package/src/{delta-db.linux-arm64-gnu.node → native/delta-db.linux-arm64-gnu.node} +0 -0
- package/src/{delta-db.linux-x64-gnu.node → native/delta-db.linux-x64-gnu.node} +0 -0
- package/src/native/native.d.ts +93 -0
- package/src/native/native.js +61 -0
- package/src/delta-db.darwin-arm64.node +0 -0
- package/src/index.js +0 -91
package/README.md
CHANGED
|
@@ -1,83 +1,173 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @sqd-pipes/delta-db
|
|
2
2
|
|
|
3
|
-
Embedded rollback-aware computation engine for blockchain data. Routes raw events through **reducers** and **materialized views**, producing delta records (insert/update/delete) for downstream targets.
|
|
3
|
+
Embedded rollback-aware computation engine for blockchain data. Routes raw events through **reducers** (TypeScript functions) and **materialized views**, producing delta records (insert/update/delete) for downstream targets.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
Requires **Rust** (stable) and **Node.js** >= 18.
|
|
5
|
+
## Install
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
pnpm add @sqd-pipes/delta-db
|
|
9
|
+
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
## Build from source
|
|
12
|
+
|
|
13
|
+
Requires **Rust** (stable) and **Node.js** >= 18.
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
```bash
|
|
16
|
+
pnpm install
|
|
17
|
+
pnpm run build # release
|
|
18
|
+
pnpm run build:debug # debug
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
## Quick start
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
```typescript
|
|
24
|
+
import { Pipeline, uint64, string, float64, interval } from '@sqd-pipes/delta-db'
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
const { DeltaDb } = require('./delta-db.node')
|
|
26
|
-
module.exports.DeltaDb = DeltaDb
|
|
27
|
-
```
|
|
26
|
+
const p = new Pipeline()
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
const swaps = p.table('swaps', {
|
|
29
|
+
block_number: uint64(),
|
|
30
|
+
pool: string(),
|
|
31
|
+
amount: float64(),
|
|
32
|
+
})
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
swaps
|
|
35
|
+
.createReducer('totals', {
|
|
36
|
+
groupBy: 'pool',
|
|
37
|
+
initialState: { volume: 0 },
|
|
38
|
+
reduce(state, row) {
|
|
39
|
+
const volume = state.volume + row.amount
|
|
40
|
+
return [{ volume }, { pool: row.pool, volume }]
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
.createView('pool_volume', {
|
|
44
|
+
groupBy: ['pool'],
|
|
45
|
+
select: (agg) => ({
|
|
46
|
+
pool: agg.key.pool,
|
|
47
|
+
totalVolume: agg.sum('volume'),
|
|
48
|
+
tradeCount: agg.count(),
|
|
49
|
+
}),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const db = p.build() // in-memory
|
|
53
|
+
// const db = p.build({ dataDir: './data' }) // persistent (RocksDB)
|
|
54
|
+
|
|
55
|
+
db.processBatch('swaps', 1000, [
|
|
56
|
+
{ pool: 'ETH/USDC', amount: 100 },
|
|
57
|
+
{ pool: 'ETH/USDC', amount: 200 },
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
const batch = db.flush()!
|
|
61
|
+
// batch.tables.pool_volume → [{ key: { pool: 'ETH/USDC' }, values: { totalVolume: 300, tradeCount: 2 } }]
|
|
33
62
|
```
|
|
34
63
|
|
|
35
|
-
##
|
|
64
|
+
## API
|
|
36
65
|
|
|
37
|
-
###
|
|
66
|
+
### Pipeline builder
|
|
38
67
|
|
|
39
68
|
```typescript
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
CREATE MATERIALIZED VIEW volume AS
|
|
50
|
-
SELECT pool, sum(amount) AS total, count() AS cnt
|
|
51
|
-
FROM swaps GROUP BY pool;
|
|
52
|
-
`,
|
|
53
|
-
dataDir: './data', // optional, enables persistence
|
|
69
|
+
const p = new Pipeline()
|
|
70
|
+
|
|
71
|
+
// Define a table — column types are inferred into the row type
|
|
72
|
+
const table = p.table('name', {
|
|
73
|
+
block_number: uint64(),
|
|
74
|
+
user: string(),
|
|
75
|
+
amount: float64(),
|
|
76
|
+
timestamp: datetime(),
|
|
77
|
+
metadata: json<MyType>(), // json() is generic
|
|
54
78
|
})
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
// Create a reducer from a table (or another reducer)
|
|
81
|
+
const reducer = table.createReducer('reducer_name', {
|
|
82
|
+
groupBy: 'user', // validated against row keys
|
|
83
|
+
initialState: { total: 0 }, // state type inferred
|
|
84
|
+
reduce(state, row) { // state & row fully typed
|
|
85
|
+
return [
|
|
86
|
+
{ total: state.total + row.amount }, // new state
|
|
87
|
+
{ user: row.user, total: ... }, // emit (or null to skip)
|
|
88
|
+
]
|
|
61
89
|
},
|
|
62
|
-
rollbackChain: [{ number: 1000, hash: '0x...' }],
|
|
63
|
-
finalizedHead: { number: 999, hash: '0x...' },
|
|
64
90
|
})
|
|
65
|
-
|
|
91
|
+
|
|
92
|
+
// Chain a reducer from another reducer's output
|
|
93
|
+
const chained = reducer.createReducer('downstream', { ... })
|
|
94
|
+
|
|
95
|
+
// Create a materialized view
|
|
96
|
+
reducer.createView('summary', {
|
|
97
|
+
groupBy: ['user'],
|
|
98
|
+
select: (agg) => ({
|
|
99
|
+
user: agg.key.user,
|
|
100
|
+
total: agg.sum('total'), // validated against emit keys
|
|
101
|
+
count: agg.count(),
|
|
102
|
+
first: agg.first('total'),
|
|
103
|
+
last: agg.last('total'),
|
|
104
|
+
}),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// Time-window grouping
|
|
108
|
+
reducer.createView('candles_5m', {
|
|
109
|
+
groupBy: ['pool', interval('timestamp', '5 minutes').as('window_start')],
|
|
110
|
+
select: (agg) => ({
|
|
111
|
+
pool: agg.key.pool,
|
|
112
|
+
windowStart: agg.key.window_start,
|
|
113
|
+
open: agg.first('price'),
|
|
114
|
+
high: agg.max('price'),
|
|
115
|
+
low: agg.min('price'),
|
|
116
|
+
close: agg.last('price'),
|
|
117
|
+
volume: agg.sum('volume'),
|
|
118
|
+
}),
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Build the database
|
|
122
|
+
const db = p.build() // in-memory
|
|
123
|
+
const db = p.build({ dataDir: './data' }) // RocksDB persistence
|
|
124
|
+
const db = p.build({ dataDir: ':memory:' }) // explicit in-memory (SQLite convention)
|
|
66
125
|
```
|
|
67
126
|
|
|
68
|
-
###
|
|
127
|
+
### Column types
|
|
128
|
+
|
|
129
|
+
| Function | SQL type | TypeScript type |
|
|
130
|
+
|-------------|------------|-----------------|
|
|
131
|
+
| `uint64()` | `UInt64` | `number` |
|
|
132
|
+
| `int64()` | `Int64` | `number` |
|
|
133
|
+
| `float64()` | `Float64` | `number` |
|
|
134
|
+
| `uint256()` | `Uint256` | `bigint` |
|
|
135
|
+
| `string()` | `String` | `string` |
|
|
136
|
+
| `datetime()` | `DateTime` | `number` |
|
|
137
|
+
| `boolean()` | `Boolean` | `boolean` |
|
|
138
|
+
| `bytes()` | `Bytes` | `Uint8Array` |
|
|
139
|
+
| `base58()` | `Base58` | `string` |
|
|
140
|
+
| `json<T>()` | `Json` | `T` (default `any`) |
|
|
141
|
+
|
|
142
|
+
### DeltaDb (low-level)
|
|
69
143
|
|
|
70
144
|
```typescript
|
|
71
|
-
import {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
145
|
+
import { DeltaDb } from '@sqd-pipes/delta-db'
|
|
146
|
+
|
|
147
|
+
const db = DeltaDb.open({ schema: 'CREATE TABLE ...', dataDir: './data' })
|
|
148
|
+
|
|
149
|
+
db.processBatch('table', blockNumber, rows)
|
|
150
|
+
db.rollback(forkPoint)
|
|
151
|
+
db.finalize(blockNumber)
|
|
152
|
+
db.flush() // → DeltaBatch | null
|
|
153
|
+
db.ingest(input) // atomic: process + finalize + flush
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Aggregation functions
|
|
157
|
+
|
|
158
|
+
`sum`, `count`, `min`, `max`, `avg`, `first`, `last`
|
|
159
|
+
|
|
160
|
+
### Virtual tables
|
|
161
|
+
|
|
162
|
+
Tables declared with `{ virtual: true }` are processed by reducers but don't emit raw row deltas:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const orders = p.table('orders', { ... }, { virtual: true })
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Test
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
pnpm test
|
|
172
|
+
pnpm run lint
|
|
83
173
|
```
|
package/dist/column.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ColumnType<T = any> {
|
|
2
|
+
readonly _sql: string;
|
|
3
|
+
/** Phantom field for type inference — not present at runtime. */
|
|
4
|
+
readonly _type?: T;
|
|
5
|
+
}
|
|
6
|
+
export declare const uint64: () => ColumnType<number>;
|
|
7
|
+
export declare const int64: () => ColumnType<number>;
|
|
8
|
+
export declare const float64: () => ColumnType<number>;
|
|
9
|
+
export declare const uint256: () => ColumnType<bigint>;
|
|
10
|
+
export declare const string: () => ColumnType<string>;
|
|
11
|
+
export declare const datetime: () => ColumnType<number>;
|
|
12
|
+
export declare const boolean: () => ColumnType<boolean>;
|
|
13
|
+
export declare const bytes: () => ColumnType<Uint8Array<ArrayBufferLike>>;
|
|
14
|
+
export declare const base58: () => ColumnType<string>;
|
|
15
|
+
export declare function json<T = any>(): ColumnType<T>;
|
|
16
|
+
/** Infer the row type from a column definition record. */
|
|
17
|
+
export type InferRow<T extends Record<string, ColumnType>> = {
|
|
18
|
+
[K in keyof T]: T[K] extends ColumnType<infer V> ? V : unknown;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=column.d.ts.map
|
package/dist/column.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.base58 = exports.bytes = exports.boolean = exports.datetime = exports.string = exports.uint256 = exports.float64 = exports.int64 = exports.uint64 = void 0;
|
|
4
|
+
exports.json = json;
|
|
5
|
+
function col(sql) {
|
|
6
|
+
return () => ({ _sql: sql });
|
|
7
|
+
}
|
|
8
|
+
exports.uint64 = col('UInt64');
|
|
9
|
+
exports.int64 = col('Int64');
|
|
10
|
+
exports.float64 = col('Float64');
|
|
11
|
+
exports.uint256 = col('Uint256');
|
|
12
|
+
exports.string = col('String');
|
|
13
|
+
exports.datetime = col('DateTime');
|
|
14
|
+
exports.boolean = col('Boolean');
|
|
15
|
+
exports.bytes = col('Bytes');
|
|
16
|
+
exports.base58 = col('Base58');
|
|
17
|
+
function json() {
|
|
18
|
+
return { _sql: 'Json' };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=column.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column.js","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":";;;AAmBA,oBAEC;AAfD,SAAS,GAAG,CAAI,GAAW;IACzB,OAAO,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAA;AAC9B,CAAC;AAEY,QAAA,MAAM,GAAG,GAAG,CAAS,QAAQ,CAAC,CAAA;AAC9B,QAAA,KAAK,GAAG,GAAG,CAAS,OAAO,CAAC,CAAA;AAC5B,QAAA,OAAO,GAAG,GAAG,CAAS,SAAS,CAAC,CAAA;AAChC,QAAA,OAAO,GAAG,GAAG,CAAS,SAAS,CAAC,CAAA;AAChC,QAAA,MAAM,GAAG,GAAG,CAAS,QAAQ,CAAC,CAAA;AAC9B,QAAA,QAAQ,GAAG,GAAG,CAAS,UAAU,CAAC,CAAA;AAClC,QAAA,OAAO,GAAG,GAAG,CAAU,SAAS,CAAC,CAAA;AACjC,QAAA,KAAK,GAAG,GAAG,CAAa,OAAO,CAAC,CAAA;AAChC,QAAA,MAAM,GAAG,GAAG,CAAS,QAAQ,CAAC,CAAA;AAC3C,SAAgB,IAAI;IAClB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AACzB,CAAC"}
|
package/dist/ddl.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ColumnType } from './column';
|
|
2
|
+
import type { StateFieldDef } from './delta-db';
|
|
3
|
+
export declare function parseDuration(s: string): number;
|
|
4
|
+
export interface IntervalExpr {
|
|
5
|
+
_type: 'interval';
|
|
6
|
+
column: string;
|
|
7
|
+
seconds: number;
|
|
8
|
+
alias?: string;
|
|
9
|
+
as(alias: string): IntervalExpr;
|
|
10
|
+
}
|
|
11
|
+
export declare function interval(column: string, duration: string): IntervalExpr;
|
|
12
|
+
export interface AggExpr {
|
|
13
|
+
_type: 'agg';
|
|
14
|
+
func: string;
|
|
15
|
+
column: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface KeyRef {
|
|
18
|
+
_type: 'key';
|
|
19
|
+
column: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AggProxy<TSource = any> {
|
|
22
|
+
key: Record<string, KeyRef>;
|
|
23
|
+
sum(column: string & keyof TSource): AggExpr;
|
|
24
|
+
count(): AggExpr;
|
|
25
|
+
first(column: string & keyof TSource): AggExpr;
|
|
26
|
+
last(column: string & keyof TSource): AggExpr;
|
|
27
|
+
min(column: string & keyof TSource): AggExpr;
|
|
28
|
+
max(column: string & keyof TSource): AggExpr;
|
|
29
|
+
avg(column: string & keyof TSource): AggExpr;
|
|
30
|
+
}
|
|
31
|
+
export type GroupByItem = string | IntervalExpr;
|
|
32
|
+
export interface ReducerOptions<TState, TRow, TEmit> {
|
|
33
|
+
groupBy: (string & keyof TRow) | (string & keyof TRow)[];
|
|
34
|
+
initialState: TState;
|
|
35
|
+
reduce: (state: TState, row: TRow) => [TState, TEmit | TEmit[] | null];
|
|
36
|
+
}
|
|
37
|
+
export interface ViewOptions<TSource = any> {
|
|
38
|
+
groupBy: GroupByItem | GroupByItem[];
|
|
39
|
+
select: (agg: AggProxy<TSource>) => Record<string, AggExpr | KeyRef>;
|
|
40
|
+
}
|
|
41
|
+
export declare function inferStateFields(initialState: Record<string, unknown>): StateFieldDef[];
|
|
42
|
+
export declare function tableToSql(name: string, columns: Record<string, ColumnType>, virtual: boolean): string;
|
|
43
|
+
export declare function reducerToSql(name: string, source: string, groupBy: string[], stateFields: StateFieldDef[]): string;
|
|
44
|
+
export declare function viewToSql(name: string, source: string, groupByItems: GroupByItem[], selectFn: (agg: AggProxy<any>) => Record<string, AggExpr | KeyRef>): string;
|
|
45
|
+
//# sourceMappingURL=ddl.d.ts.map
|
package/dist/ddl.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseDuration = parseDuration;
|
|
4
|
+
exports.interval = interval;
|
|
5
|
+
exports.inferStateFields = inferStateFields;
|
|
6
|
+
exports.tableToSql = tableToSql;
|
|
7
|
+
exports.reducerToSql = reducerToSql;
|
|
8
|
+
exports.viewToSql = viewToSql;
|
|
9
|
+
// ─── Duration parsing ────────────────────────────────────────────
|
|
10
|
+
const DURATION_UNITS = {
|
|
11
|
+
second: 1,
|
|
12
|
+
seconds: 1,
|
|
13
|
+
sec: 1,
|
|
14
|
+
s: 1,
|
|
15
|
+
minute: 60,
|
|
16
|
+
minutes: 60,
|
|
17
|
+
min: 60,
|
|
18
|
+
m: 60,
|
|
19
|
+
hour: 3600,
|
|
20
|
+
hours: 3600,
|
|
21
|
+
hr: 3600,
|
|
22
|
+
h: 3600,
|
|
23
|
+
day: 86400,
|
|
24
|
+
days: 86400,
|
|
25
|
+
d: 86400,
|
|
26
|
+
};
|
|
27
|
+
function parseDuration(s) {
|
|
28
|
+
const match = s.trim().match(/^(\d+)\s*(\w+)$/);
|
|
29
|
+
if (!match) {
|
|
30
|
+
throw new Error(`invalid duration: '${s}'`);
|
|
31
|
+
}
|
|
32
|
+
const n = parseInt(match[1], 10);
|
|
33
|
+
const unit = match[2].toLowerCase();
|
|
34
|
+
const mult = DURATION_UNITS[unit];
|
|
35
|
+
if (!mult) {
|
|
36
|
+
throw new Error(`unknown duration unit: '${unit}'`);
|
|
37
|
+
}
|
|
38
|
+
return n * mult;
|
|
39
|
+
}
|
|
40
|
+
function makeInterval(column, seconds, alias) {
|
|
41
|
+
return {
|
|
42
|
+
_type: 'interval',
|
|
43
|
+
column,
|
|
44
|
+
seconds,
|
|
45
|
+
alias,
|
|
46
|
+
as(a) {
|
|
47
|
+
return makeInterval(column, seconds, a);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function interval(column, duration) {
|
|
52
|
+
return makeInterval(column, parseDuration(duration));
|
|
53
|
+
}
|
|
54
|
+
// ─── Type inference from initialState ────────────────────────────
|
|
55
|
+
function inferStateFields(initialState) {
|
|
56
|
+
const fields = [];
|
|
57
|
+
for (const [name, value] of Object.entries(initialState)) {
|
|
58
|
+
let columnType;
|
|
59
|
+
let defaultValue;
|
|
60
|
+
switch (typeof value) {
|
|
61
|
+
case 'number':
|
|
62
|
+
columnType = 'Float64';
|
|
63
|
+
defaultValue = String(value);
|
|
64
|
+
break;
|
|
65
|
+
case 'bigint':
|
|
66
|
+
columnType = 'UInt64';
|
|
67
|
+
defaultValue = String(value);
|
|
68
|
+
break;
|
|
69
|
+
case 'string':
|
|
70
|
+
columnType = 'String';
|
|
71
|
+
defaultValue = `'${value}'`;
|
|
72
|
+
break;
|
|
73
|
+
case 'boolean':
|
|
74
|
+
columnType = 'Boolean';
|
|
75
|
+
defaultValue = value ? 'true' : 'false';
|
|
76
|
+
break;
|
|
77
|
+
case 'object':
|
|
78
|
+
columnType = 'Json';
|
|
79
|
+
defaultValue = `'${JSON.stringify(value)}'`;
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
columnType = 'String';
|
|
83
|
+
defaultValue = `'${String(value)}'`;
|
|
84
|
+
}
|
|
85
|
+
fields.push({ name, columnType, defaultValue });
|
|
86
|
+
}
|
|
87
|
+
return fields;
|
|
88
|
+
}
|
|
89
|
+
// ─── DDL generators ──────────────────────────────────────────────
|
|
90
|
+
function tableToSql(name, columns, virtual) {
|
|
91
|
+
const prefix = virtual ? 'CREATE VIRTUAL TABLE' : 'CREATE TABLE';
|
|
92
|
+
const cols = Object.entries(columns)
|
|
93
|
+
.map(([name, ct]) => `${name} ${ct._sql}`)
|
|
94
|
+
.join(', ');
|
|
95
|
+
return `${prefix} ${name} (${cols});`;
|
|
96
|
+
}
|
|
97
|
+
function reducerToSql(name, source, groupBy, stateFields) {
|
|
98
|
+
const gb = groupBy.join(', ');
|
|
99
|
+
const state = stateFields
|
|
100
|
+
.map((f) => `${f.name} ${f.columnType} DEFAULT ${f.defaultValue}`)
|
|
101
|
+
.join(', ');
|
|
102
|
+
return `CREATE REDUCER ${name} SOURCE ${source} GROUP BY ${gb} STATE (${state}) LANGUAGE EXTERNAL;`;
|
|
103
|
+
}
|
|
104
|
+
function viewToSql(name, source, groupByItems, selectFn) {
|
|
105
|
+
const groupByCols = [];
|
|
106
|
+
const intervalDefs = [];
|
|
107
|
+
for (const item of groupByItems) {
|
|
108
|
+
if (typeof item === 'string') {
|
|
109
|
+
groupByCols.push(item);
|
|
110
|
+
}
|
|
111
|
+
else if (item._type === 'interval') {
|
|
112
|
+
const alias = item.alias || `${item.column}_interval`;
|
|
113
|
+
intervalDefs.push({ column: item.column, seconds: item.seconds, alias });
|
|
114
|
+
groupByCols.push(alias);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const keyProxy = new Proxy({}, {
|
|
118
|
+
get(_target, prop) {
|
|
119
|
+
return { _type: 'key', column: prop };
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const aggProxy = {
|
|
123
|
+
key: keyProxy,
|
|
124
|
+
sum(col) {
|
|
125
|
+
return { _type: 'agg', func: 'sum', column: col };
|
|
126
|
+
},
|
|
127
|
+
count() {
|
|
128
|
+
return { _type: 'agg', func: 'count', column: null };
|
|
129
|
+
},
|
|
130
|
+
first(col) {
|
|
131
|
+
return { _type: 'agg', func: 'first', column: col };
|
|
132
|
+
},
|
|
133
|
+
last(col) {
|
|
134
|
+
return { _type: 'agg', func: 'last', column: col };
|
|
135
|
+
},
|
|
136
|
+
min(col) {
|
|
137
|
+
return { _type: 'agg', func: 'min', column: col };
|
|
138
|
+
},
|
|
139
|
+
max(col) {
|
|
140
|
+
return { _type: 'agg', func: 'max', column: col };
|
|
141
|
+
},
|
|
142
|
+
avg(col) {
|
|
143
|
+
return { _type: 'agg', func: 'avg', column: col };
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const selectResult = selectFn(aggProxy);
|
|
147
|
+
const selectItems = [];
|
|
148
|
+
for (const [alias, expr] of Object.entries(selectResult)) {
|
|
149
|
+
if (expr._type === 'key') {
|
|
150
|
+
const intv = intervalDefs.find((d) => d.alias === expr.column);
|
|
151
|
+
if (intv) {
|
|
152
|
+
selectItems.push(`toStartOfInterval(${intv.column}, INTERVAL ${intv.seconds} SECOND) AS ${alias}`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
selectItems.push(alias === expr.column ? alias : `${expr.column} AS ${alias}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else if (expr._type === 'agg') {
|
|
159
|
+
const arg = expr.column ? `(${expr.column})` : '()';
|
|
160
|
+
selectItems.push(`${expr.func}${arg} AS ${alias}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return `CREATE MATERIALIZED VIEW ${name} AS SELECT ${selectItems.join(', ')} FROM ${source} GROUP BY ${groupByCols.join(', ')};`;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=ddl.js.map
|
package/dist/ddl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ddl.js","sourceRoot":"","sources":["../src/ddl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface DeltaDbConfig {
|
|
2
|
+
schema: string;
|
|
3
|
+
dataDir?: string;
|
|
4
|
+
maxBufferSize?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface DeltaDbCursor {
|
|
7
|
+
number: number;
|
|
8
|
+
hash: string;
|
|
9
|
+
}
|
|
10
|
+
export type DeltaOperation = 'insert' | 'update' | 'delete';
|
|
11
|
+
export interface DeltaRecord {
|
|
12
|
+
table: string;
|
|
13
|
+
operation: DeltaOperation;
|
|
14
|
+
key: Record<string, any>;
|
|
15
|
+
values: Record<string, any>;
|
|
16
|
+
prevValues: Record<string, any> | null;
|
|
17
|
+
}
|
|
18
|
+
export interface DeltaBatch {
|
|
19
|
+
sequence: number;
|
|
20
|
+
finalizedHead: DeltaDbCursor | null;
|
|
21
|
+
latestHead: DeltaDbCursor | null;
|
|
22
|
+
tables: Record<string, DeltaRecord[]>;
|
|
23
|
+
}
|
|
24
|
+
export interface IngestInput {
|
|
25
|
+
data: Record<string, Record<string, any>[]>;
|
|
26
|
+
rollbackChain?: DeltaDbCursor[];
|
|
27
|
+
finalizedHead: DeltaDbCursor;
|
|
28
|
+
onDelta?: (batch: DeltaBatch) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface StateFieldDef {
|
|
31
|
+
name: string;
|
|
32
|
+
columnType: string;
|
|
33
|
+
defaultValue: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ExternalReducerOptions<TState = any, TRow = any, TEmit = any> {
|
|
36
|
+
name: string;
|
|
37
|
+
source: string;
|
|
38
|
+
groupBy: string[];
|
|
39
|
+
state: StateFieldDef[];
|
|
40
|
+
reduce: (state: TState, row: TRow) => [TState, TEmit | TEmit[] | null];
|
|
41
|
+
}
|
|
42
|
+
export declare class DeltaDb {
|
|
43
|
+
#private;
|
|
44
|
+
private constructor();
|
|
45
|
+
static open(config: DeltaDbConfig): DeltaDb;
|
|
46
|
+
processBatch(table: string, block: number, rows: Record<string, any>[]): boolean;
|
|
47
|
+
rollback(forkPoint: number): void;
|
|
48
|
+
finalize(block: number): void;
|
|
49
|
+
ingest(input: IngestInput): DeltaBatch | null;
|
|
50
|
+
resolveForkCursor(previousBlocks: DeltaDbCursor[]): DeltaDbCursor | null;
|
|
51
|
+
flush(): DeltaBatch | null;
|
|
52
|
+
ack(sequence: number): void;
|
|
53
|
+
get pendingCount(): number;
|
|
54
|
+
get isBackpressured(): boolean;
|
|
55
|
+
get cursor(): DeltaDbCursor | null;
|
|
56
|
+
registerReducer<TState = any, TRow = any, TEmit = any>(options: ExternalReducerOptions<TState, TRow, TEmit>): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=delta-db.d.ts.map
|
package/dist/delta-db.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeltaDb = void 0;
|
|
4
|
+
const msgpack_1 = require("@msgpack/msgpack");
|
|
5
|
+
const native_js_1 = require("./native/native.js");
|
|
6
|
+
const encoder = new msgpack_1.Encoder({ useBigInt64: true });
|
|
7
|
+
// ─── DeltaDb class ───────────────────────────────────────────────
|
|
8
|
+
class DeltaDb {
|
|
9
|
+
#native;
|
|
10
|
+
constructor(native) {
|
|
11
|
+
this.#native = native;
|
|
12
|
+
}
|
|
13
|
+
static open(config) {
|
|
14
|
+
return new DeltaDb(native_js_1.DeltaDb.open(config));
|
|
15
|
+
}
|
|
16
|
+
processBatch(table, block, rows) {
|
|
17
|
+
return this.#native.processBatch(table, block, Buffer.from(encoder.encode(rows)));
|
|
18
|
+
}
|
|
19
|
+
rollback(forkPoint) {
|
|
20
|
+
this.#native.rollback(forkPoint);
|
|
21
|
+
}
|
|
22
|
+
finalize(block) {
|
|
23
|
+
this.#native.finalize(block);
|
|
24
|
+
}
|
|
25
|
+
ingest(input) {
|
|
26
|
+
const buf = this.#native.ingest({
|
|
27
|
+
data: Buffer.from(encoder.encode(input.data)),
|
|
28
|
+
rollbackChain: input.rollbackChain,
|
|
29
|
+
finalizedHead: input.finalizedHead,
|
|
30
|
+
});
|
|
31
|
+
const batch = buf ? (0, msgpack_1.decode)(buf) : null;
|
|
32
|
+
if (batch && input.onDelta) {
|
|
33
|
+
input.onDelta(batch);
|
|
34
|
+
this.#native.ack(batch.sequence);
|
|
35
|
+
}
|
|
36
|
+
return batch;
|
|
37
|
+
}
|
|
38
|
+
resolveForkCursor(previousBlocks) {
|
|
39
|
+
return this.#native.resolveForkCursor(previousBlocks);
|
|
40
|
+
}
|
|
41
|
+
flush() {
|
|
42
|
+
const buf = this.#native.flush();
|
|
43
|
+
return buf ? (0, msgpack_1.decode)(buf) : null;
|
|
44
|
+
}
|
|
45
|
+
ack(sequence) {
|
|
46
|
+
this.#native.ack(sequence);
|
|
47
|
+
}
|
|
48
|
+
get pendingCount() {
|
|
49
|
+
return this.#native.pendingCount;
|
|
50
|
+
}
|
|
51
|
+
get isBackpressured() {
|
|
52
|
+
return this.#native.isBackpressured;
|
|
53
|
+
}
|
|
54
|
+
get cursor() {
|
|
55
|
+
return this.#native.cursor;
|
|
56
|
+
}
|
|
57
|
+
registerReducer(options) {
|
|
58
|
+
const { reduce } = options;
|
|
59
|
+
const batchFn = (groups) => {
|
|
60
|
+
return groups.map(({ state, rows }) => {
|
|
61
|
+
let s = state;
|
|
62
|
+
const emits = [];
|
|
63
|
+
for (const row of rows) {
|
|
64
|
+
const [newState, emit] = reduce(s, row);
|
|
65
|
+
s = newState;
|
|
66
|
+
if (emit != null) {
|
|
67
|
+
if (Array.isArray(emit)) {
|
|
68
|
+
for (let i = 0; i < emit.length; i++)
|
|
69
|
+
emits.push(emit[i]);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
emits.push(emit);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { state: s, emits };
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
this.#native.registerReducer({
|
|
80
|
+
name: options.name,
|
|
81
|
+
source: options.source,
|
|
82
|
+
groupBy: options.groupBy,
|
|
83
|
+
state: options.state,
|
|
84
|
+
}, batchFn);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.DeltaDb = DeltaDb;
|
|
88
|
+
//# sourceMappingURL=delta-db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta-db.js","sourceRoot":"","sources":["../src/delta-db.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './column';
|
|
2
|
+
export { type AggExpr, type AggProxy, type GroupByItem, type IntervalExpr, interval, type KeyRef, type ReducerOptions, type ViewOptions, } from './ddl';
|
|
3
|
+
export { type DeltaBatch, DeltaDb, type DeltaDbConfig, type DeltaDbCursor, type DeltaOperation, type DeltaRecord, type ExternalReducerOptions, type IngestInput, type StateFieldDef, } from './delta-db';
|
|
4
|
+
export { Pipeline, ReducerHandle, TableHandle, ViewHandle } from './pipeline';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.ViewHandle = exports.TableHandle = exports.ReducerHandle = exports.Pipeline = exports.DeltaDb = exports.interval = void 0;
|
|
18
|
+
__exportStar(require("./column"), exports);
|
|
19
|
+
var ddl_1 = require("./ddl");
|
|
20
|
+
Object.defineProperty(exports, "interval", { enumerable: true, get: function () { return ddl_1.interval; } });
|
|
21
|
+
var delta_db_1 = require("./delta-db");
|
|
22
|
+
Object.defineProperty(exports, "DeltaDb", { enumerable: true, get: function () { return delta_db_1.DeltaDb; } });
|
|
23
|
+
var pipeline_1 = require("./pipeline");
|
|
24
|
+
Object.defineProperty(exports, "Pipeline", { enumerable: true, get: function () { return pipeline_1.Pipeline; } });
|
|
25
|
+
Object.defineProperty(exports, "ReducerHandle", { enumerable: true, get: function () { return pipeline_1.ReducerHandle; } });
|
|
26
|
+
Object.defineProperty(exports, "TableHandle", { enumerable: true, get: function () { return pipeline_1.TableHandle; } });
|
|
27
|
+
Object.defineProperty(exports, "ViewHandle", { enumerable: true, get: function () { return pipeline_1.ViewHandle; } });
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAAwB;AACxB,6BASc;AAJZ,+FAAA,QAAQ,OAAA;AAKV,uCAUmB;AARjB,mGAAA,OAAO,OAAA;AAST,uCAA6E;AAApE,oGAAA,QAAQ,OAAA;AAAE,yGAAA,aAAa,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,sGAAA,UAAU,OAAA"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/* auto-generated by NAPI-RS */
|
|
5
|
+
|
|
1
6
|
/** Configuration for opening a DeltaDb instance. */
|
|
2
7
|
export interface DeltaDbConfig {
|
|
3
8
|
/** SQL schema definition string. */
|
|
@@ -10,69 +15,73 @@ export interface DeltaDbConfig {
|
|
|
10
15
|
/** Maximum buffer size before backpressure (default: 10000). */
|
|
11
16
|
maxBufferSize?: number
|
|
12
17
|
}
|
|
13
|
-
|
|
14
18
|
/** Block cursor: number + hash. */
|
|
15
19
|
export interface DeltaDbCursor {
|
|
16
20
|
number: number
|
|
17
21
|
hash: string
|
|
18
22
|
}
|
|
19
|
-
|
|
20
|
-
export type DeltaOperation = 'insert' | 'update' | 'delete'
|
|
21
|
-
|
|
22
|
-
export interface DeltaRecord {
|
|
23
|
-
table: string
|
|
24
|
-
operation: DeltaOperation
|
|
25
|
-
key: Record<string, any>
|
|
26
|
-
values: Record<string, any>
|
|
27
|
-
prevValues: Record<string, any> | null
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface DeltaBatch {
|
|
31
|
-
sequence: number
|
|
32
|
-
finalizedHead: DeltaDbCursor | null
|
|
33
|
-
latestHead: DeltaDbCursor | null
|
|
34
|
-
/** Records grouped by table name. */
|
|
35
|
-
tables: Record<string, DeltaRecord[]>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
23
|
/** Input for the atomic `ingest()` method. */
|
|
39
24
|
export interface IngestInput {
|
|
40
|
-
/** Table name → rows
|
|
41
|
-
data:
|
|
25
|
+
/** Table name → rows, msgpack-encoded as `{tableName: [{col: val}, ...], ...}`. */
|
|
26
|
+
data: Buffer
|
|
42
27
|
/** Unfinalized blocks with hashes for fork resolution. */
|
|
43
|
-
rollbackChain?: DeltaDbCursor
|
|
28
|
+
rollbackChain?: Array<DeltaDbCursor>
|
|
44
29
|
/** Finalized head cursor — both number and hash stored. */
|
|
45
30
|
finalizedHead: DeltaDbCursor
|
|
46
|
-
/** Called with each delta batch. When provided, batch is auto-acked. */
|
|
47
|
-
onDelta?: (batch: DeltaBatch) => void
|
|
48
31
|
}
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
/** State field definition for external reducers. */
|
|
33
|
+
export interface ExternalStateField {
|
|
34
|
+
name: string
|
|
35
|
+
/** Column type: "Float64", "UInt64", "Int64", "String", "Boolean", "Json" */
|
|
36
|
+
columnType: string
|
|
37
|
+
/** Default value as a string literal (e.g., "0", "'hello'", "{}") */
|
|
38
|
+
defaultValue: string
|
|
39
|
+
}
|
|
40
|
+
/** Configuration for registering an external reducer. */
|
|
41
|
+
export interface ExternalReducerConfig {
|
|
42
|
+
name: string
|
|
43
|
+
source: string
|
|
44
|
+
groupBy: Array<string>
|
|
45
|
+
state: Array<ExternalStateField>
|
|
46
|
+
}
|
|
47
|
+
/** Delta DB N-API wrapper. */
|
|
51
48
|
export declare class DeltaDb {
|
|
52
49
|
/** Open a new DeltaDb instance. */
|
|
53
50
|
static open(config: DeltaDbConfig): DeltaDb
|
|
51
|
+
/**
|
|
52
|
+
* Register an external reducer with a JS batch callback.
|
|
53
|
+
*
|
|
54
|
+
* The callback receives an array of `{ state, rows }` groups and must
|
|
55
|
+
* return an array of `{ state, emits }` results (same length, same order).
|
|
56
|
+
*
|
|
57
|
+
* Must be called before any `processBatch` or `ingest` calls.
|
|
58
|
+
*/
|
|
59
|
+
registerReducer(config: ExternalReducerConfig, callback: (...args: any[]) => any): void
|
|
54
60
|
/**
|
|
55
61
|
* Process a batch of rows for a raw table.
|
|
62
|
+
* `rows` is a msgpack-encoded Buffer: `[{col: val, ...}, ...]`.
|
|
56
63
|
* Returns true if backpressure should be applied.
|
|
57
64
|
*/
|
|
58
|
-
processBatch(table: string, block: number, rows:
|
|
65
|
+
processBatch(table: string, block: number, rows: Buffer): boolean
|
|
59
66
|
/** Roll back all state after fork_point. */
|
|
60
67
|
rollback(forkPoint: number): void
|
|
61
68
|
/** Finalize all state up to and including the given block. */
|
|
62
69
|
finalize(block: number): void
|
|
63
70
|
/**
|
|
64
71
|
* Atomic ingest: process all tables, store rollback chain, finalize, flush.
|
|
65
|
-
* Returns
|
|
66
|
-
* When `onDelta` is provided in input, it is called and batch is auto-acked.
|
|
72
|
+
* Returns a msgpack-encoded DeltaBatch buffer, or null if no records produced.
|
|
67
73
|
*/
|
|
68
|
-
ingest(input: IngestInput):
|
|
74
|
+
ingest(input: IngestInput): Buffer | null
|
|
69
75
|
/**
|
|
70
76
|
* Find the common ancestor between our state and the Portal's chain.
|
|
71
77
|
* Returns the matching block cursor, or null if no common ancestor found.
|
|
72
78
|
*/
|
|
73
|
-
resolveForkCursor(previousBlocks: DeltaDbCursor
|
|
74
|
-
/**
|
|
75
|
-
|
|
79
|
+
resolveForkCursor(previousBlocks: Array<DeltaDbCursor>): DeltaDbCursor | null
|
|
80
|
+
/**
|
|
81
|
+
* Flush buffered deltas into a msgpack-encoded batch.
|
|
82
|
+
* Returns null if no pending records.
|
|
83
|
+
*/
|
|
84
|
+
flush(): Buffer | null
|
|
76
85
|
/** Acknowledge a flushed batch by sequence number. */
|
|
77
86
|
ack(sequence: number): void
|
|
78
87
|
/** Number of pending (unflushed) delta records. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* Platform-aware native module loader for @sqd-pipes/delta-db */
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
const { join } = require('path')
|
|
2
|
+
const { existsSync } = require('node:fs')
|
|
3
|
+
const { join } = require('node:path')
|
|
5
4
|
|
|
6
5
|
const { platform, arch } = process
|
|
7
6
|
|
|
@@ -17,6 +16,7 @@ function getPlatformFile() {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
const key = `${platform}-${arch}`
|
|
19
|
+
// @ts-ignore
|
|
20
20
|
return suffixes[key] ? `delta-db.${suffixes[key]}.node` : null
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -50,6 +50,7 @@ if (!nativeBinding) {
|
|
|
50
50
|
`Failed to load native binding for ${platform}-${arch}.`,
|
|
51
51
|
platformFile ? `Looked for: ${platformFile}` : `Unsupported platform: ${platform}-${arch}`,
|
|
52
52
|
'',
|
|
53
|
+
// @ts-ignore
|
|
53
54
|
loadError ? `Error: ${loadError.message}` : '',
|
|
54
55
|
'',
|
|
55
56
|
'Build from source: npm run build',
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ColumnType, InferRow } from './column';
|
|
2
|
+
import { type ReducerOptions, type ViewOptions } from './ddl';
|
|
3
|
+
import { DeltaDb } from './delta-db';
|
|
4
|
+
export declare class Pipeline {
|
|
5
|
+
#private;
|
|
6
|
+
table<TCols extends Record<string, ColumnType>>(name: string, columns: TCols, opts?: {
|
|
7
|
+
virtual?: boolean;
|
|
8
|
+
}): TableHandle<InferRow<TCols>>;
|
|
9
|
+
build(opts?: {
|
|
10
|
+
dataDir?: string;
|
|
11
|
+
maxBufferSize?: number;
|
|
12
|
+
}): DeltaDb;
|
|
13
|
+
}
|
|
14
|
+
export declare class TableHandle<TRow = any> {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(pipeline: Pipeline, name: string);
|
|
17
|
+
get name(): string;
|
|
18
|
+
createReducer<TState, TEmit>(name: string, opts: ReducerOptions<TState, TRow, TEmit>): ReducerHandle<TEmit>;
|
|
19
|
+
createView(name: string, opts: ViewOptions<TRow>): ViewHandle;
|
|
20
|
+
}
|
|
21
|
+
export declare class ReducerHandle<TOutput = any> {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(pipeline: Pipeline, name: string);
|
|
24
|
+
get name(): string;
|
|
25
|
+
createReducer<TState, TEmit>(name: string, opts: ReducerOptions<TState, TOutput, TEmit>): ReducerHandle<TEmit>;
|
|
26
|
+
createView(name: string, opts: ViewOptions<TOutput>): ViewHandle;
|
|
27
|
+
}
|
|
28
|
+
export declare class ViewHandle {
|
|
29
|
+
#private;
|
|
30
|
+
constructor(name: string);
|
|
31
|
+
get name(): string;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
package/dist/pipeline.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ViewHandle = exports.ReducerHandle = exports.TableHandle = exports.Pipeline = void 0;
|
|
4
|
+
const ddl_1 = require("./ddl");
|
|
5
|
+
const delta_db_1 = require("./delta-db");
|
|
6
|
+
// ─── Pipeline ────────────────────────────────────────────────────
|
|
7
|
+
class Pipeline {
|
|
8
|
+
#tables = [];
|
|
9
|
+
#reducers = [];
|
|
10
|
+
#views = [];
|
|
11
|
+
table(name, columns, opts) {
|
|
12
|
+
this.#tables.push({ name, columns, virtual: opts?.virtual ?? false });
|
|
13
|
+
return new TableHandle(this, name);
|
|
14
|
+
}
|
|
15
|
+
/** @internal */
|
|
16
|
+
_addReducer(name, source, opts) {
|
|
17
|
+
const groupBy = Array.isArray(opts.groupBy) ? opts.groupBy : [opts.groupBy];
|
|
18
|
+
const stateFields = (0, ddl_1.inferStateFields)(opts.initialState);
|
|
19
|
+
this.#reducers.push({ name, source, groupBy, stateFields, reduce: opts.reduce });
|
|
20
|
+
return new ReducerHandle(this, name);
|
|
21
|
+
}
|
|
22
|
+
/** @internal */
|
|
23
|
+
_addView(name, source, opts) {
|
|
24
|
+
const groupByItems = Array.isArray(opts.groupBy) ? opts.groupBy : [opts.groupBy];
|
|
25
|
+
const sql = (0, ddl_1.viewToSql)(name, source, groupByItems, opts.select);
|
|
26
|
+
this.#views.push({ sql });
|
|
27
|
+
return new ViewHandle(name);
|
|
28
|
+
}
|
|
29
|
+
build(opts) {
|
|
30
|
+
const ddl = [];
|
|
31
|
+
for (const t of this.#tables) {
|
|
32
|
+
ddl.push((0, ddl_1.tableToSql)(t.name, t.columns, t.virtual));
|
|
33
|
+
}
|
|
34
|
+
for (const r of this.#reducers) {
|
|
35
|
+
ddl.push((0, ddl_1.reducerToSql)(r.name, r.source, r.groupBy, r.stateFields));
|
|
36
|
+
}
|
|
37
|
+
for (const v of this.#views) {
|
|
38
|
+
ddl.push(v.sql);
|
|
39
|
+
}
|
|
40
|
+
// ':memory:' (or omitted) uses in-memory storage — same convention as SQLite
|
|
41
|
+
const dataDir = opts?.dataDir === ':memory:' ? undefined : opts?.dataDir;
|
|
42
|
+
const db = delta_db_1.DeltaDb.open({
|
|
43
|
+
schema: ddl.join('\n'),
|
|
44
|
+
dataDir,
|
|
45
|
+
maxBufferSize: opts?.maxBufferSize,
|
|
46
|
+
});
|
|
47
|
+
for (const r of this.#reducers) {
|
|
48
|
+
db.registerReducer({
|
|
49
|
+
name: r.name,
|
|
50
|
+
source: r.source,
|
|
51
|
+
groupBy: r.groupBy,
|
|
52
|
+
state: r.stateFields,
|
|
53
|
+
reduce: r.reduce,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return db;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.Pipeline = Pipeline;
|
|
60
|
+
// ─── Handles ─────────────────────────────────────────────────────
|
|
61
|
+
class TableHandle {
|
|
62
|
+
#pipeline;
|
|
63
|
+
#name;
|
|
64
|
+
constructor(pipeline, name) {
|
|
65
|
+
this.#pipeline = pipeline;
|
|
66
|
+
this.#name = name;
|
|
67
|
+
}
|
|
68
|
+
get name() {
|
|
69
|
+
return this.#name;
|
|
70
|
+
}
|
|
71
|
+
createReducer(name, opts) {
|
|
72
|
+
return this.#pipeline._addReducer(name, this.#name, opts);
|
|
73
|
+
}
|
|
74
|
+
createView(name, opts) {
|
|
75
|
+
return this.#pipeline._addView(name, this.#name, opts);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.TableHandle = TableHandle;
|
|
79
|
+
class ReducerHandle {
|
|
80
|
+
#pipeline;
|
|
81
|
+
#name;
|
|
82
|
+
constructor(pipeline, name) {
|
|
83
|
+
this.#pipeline = pipeline;
|
|
84
|
+
this.#name = name;
|
|
85
|
+
}
|
|
86
|
+
get name() {
|
|
87
|
+
return this.#name;
|
|
88
|
+
}
|
|
89
|
+
createReducer(name, opts) {
|
|
90
|
+
return this.#pipeline._addReducer(name, this.#name, opts);
|
|
91
|
+
}
|
|
92
|
+
createView(name, opts) {
|
|
93
|
+
return this.#pipeline._addView(name, this.#name, opts);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.ReducerHandle = ReducerHandle;
|
|
97
|
+
class ViewHandle {
|
|
98
|
+
#name;
|
|
99
|
+
constructor(name) {
|
|
100
|
+
this.#name = name;
|
|
101
|
+
}
|
|
102
|
+
get name() {
|
|
103
|
+
return this.#name;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.ViewHandle = ViewHandle;
|
|
107
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqd-pipes/delta-db",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.8",
|
|
4
4
|
"description": "Embedded rollback-aware computation engine for blockchain data",
|
|
5
|
-
"main": "
|
|
6
|
-
"types": "
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
7
|
"napi": {
|
|
8
8
|
"name": "delta-db"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "napi build --cargo-cwd ../../.. --features napi --release --platform --dts native.d.ts --js false src",
|
|
12
|
-
"build:debug": "napi build --cargo-cwd ../../.. --features napi --platform --dts native.d.ts --js false src",
|
|
11
|
+
"build:native": "napi build --cargo-cwd ../../.. --features napi --release --platform --dts native.d.ts --js false src/native",
|
|
12
|
+
"build:native:debug": "napi build --cargo-cwd ../../.. --features napi --platform --dts native.d.ts --js false src/native",
|
|
13
|
+
"build:ts": "tsc -p tsconfig.build.json && cp -r src/native dist/native",
|
|
14
|
+
"build": "pnpm run build:native && pnpm run build:ts",
|
|
13
15
|
"artifacts": "napi artifacts",
|
|
14
16
|
"test": "vitest run",
|
|
15
|
-
"test:watch": "vitest"
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"lint": "biome check",
|
|
19
|
+
"lint:fix": "biome check --fix"
|
|
16
20
|
},
|
|
17
21
|
"dependencies": {
|
|
18
22
|
"@msgpack/msgpack": "^3.0.0"
|
|
19
23
|
},
|
|
20
24
|
"devDependencies": {
|
|
25
|
+
"@biomejs/biome": "^2.4.7",
|
|
21
26
|
"@napi-rs/cli": "^2.18.0",
|
|
27
|
+
"@types/node": "^25.5.0",
|
|
28
|
+
"typescript": "^5.9.3",
|
|
22
29
|
"vitest": "^3.0.0"
|
|
23
30
|
},
|
|
24
31
|
"files": [
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"src
|
|
32
|
+
"dist/**/*.js",
|
|
33
|
+
"dist/**/*.d.ts",
|
|
34
|
+
"dist/**/*.js.map",
|
|
35
|
+
"src/native/native.js",
|
|
36
|
+
"src/native/native.d.ts",
|
|
37
|
+
"src/native/*.node"
|
|
29
38
|
],
|
|
30
39
|
"license": "MIT"
|
|
31
40
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/* auto-generated by NAPI-RS */
|
|
5
|
+
|
|
6
|
+
/** Configuration for opening a DeltaDb instance. */
|
|
7
|
+
export interface DeltaDbConfig {
|
|
8
|
+
/** SQL schema definition string. */
|
|
9
|
+
schema: string
|
|
10
|
+
/**
|
|
11
|
+
* Path to RocksDB data directory for persistence.
|
|
12
|
+
* When omitted, uses in-memory storage (data lost on restart).
|
|
13
|
+
*/
|
|
14
|
+
dataDir?: string
|
|
15
|
+
/** Maximum buffer size before backpressure (default: 10000). */
|
|
16
|
+
maxBufferSize?: number
|
|
17
|
+
}
|
|
18
|
+
/** Block cursor: number + hash. */
|
|
19
|
+
export interface DeltaDbCursor {
|
|
20
|
+
number: number
|
|
21
|
+
hash: string
|
|
22
|
+
}
|
|
23
|
+
/** Input for the atomic `ingest()` method. */
|
|
24
|
+
export interface IngestInput {
|
|
25
|
+
/** Table name → rows, msgpack-encoded as `{tableName: [{col: val}, ...], ...}`. */
|
|
26
|
+
data: Buffer
|
|
27
|
+
/** Unfinalized blocks with hashes for fork resolution. */
|
|
28
|
+
rollbackChain?: Array<DeltaDbCursor>
|
|
29
|
+
/** Finalized head cursor — both number and hash stored. */
|
|
30
|
+
finalizedHead: DeltaDbCursor
|
|
31
|
+
}
|
|
32
|
+
/** State field definition for external reducers. */
|
|
33
|
+
export interface ExternalStateField {
|
|
34
|
+
name: string
|
|
35
|
+
/** Column type: "Float64", "UInt64", "Int64", "String", "Boolean", "Json" */
|
|
36
|
+
columnType: string
|
|
37
|
+
/** Default value as a string literal (e.g., "0", "'hello'", "{}") */
|
|
38
|
+
defaultValue: string
|
|
39
|
+
}
|
|
40
|
+
/** Configuration for registering an external reducer. */
|
|
41
|
+
export interface ExternalReducerConfig {
|
|
42
|
+
name: string
|
|
43
|
+
source: string
|
|
44
|
+
groupBy: Array<string>
|
|
45
|
+
state: Array<ExternalStateField>
|
|
46
|
+
}
|
|
47
|
+
/** Delta DB N-API wrapper. */
|
|
48
|
+
export declare class DeltaDb {
|
|
49
|
+
/** Open a new DeltaDb instance. */
|
|
50
|
+
static open(config: DeltaDbConfig): DeltaDb
|
|
51
|
+
/**
|
|
52
|
+
* Register an external reducer with a JS batch callback.
|
|
53
|
+
*
|
|
54
|
+
* The callback receives an array of `{ state, rows }` groups and must
|
|
55
|
+
* return an array of `{ state, emits }` results (same length, same order).
|
|
56
|
+
*
|
|
57
|
+
* Must be called before any `processBatch` or `ingest` calls.
|
|
58
|
+
*/
|
|
59
|
+
registerReducer(config: ExternalReducerConfig, callback: (...args: any[]) => any): void
|
|
60
|
+
/**
|
|
61
|
+
* Process a batch of rows for a raw table.
|
|
62
|
+
* `rows` is a msgpack-encoded Buffer: `[{col: val, ...}, ...]`.
|
|
63
|
+
* Returns true if backpressure should be applied.
|
|
64
|
+
*/
|
|
65
|
+
processBatch(table: string, block: number, rows: Buffer): boolean
|
|
66
|
+
/** Roll back all state after fork_point. */
|
|
67
|
+
rollback(forkPoint: number): void
|
|
68
|
+
/** Finalize all state up to and including the given block. */
|
|
69
|
+
finalize(block: number): void
|
|
70
|
+
/**
|
|
71
|
+
* Atomic ingest: process all tables, store rollback chain, finalize, flush.
|
|
72
|
+
* Returns a msgpack-encoded DeltaBatch buffer, or null if no records produced.
|
|
73
|
+
*/
|
|
74
|
+
ingest(input: IngestInput): Buffer | null
|
|
75
|
+
/**
|
|
76
|
+
* Find the common ancestor between our state and the Portal's chain.
|
|
77
|
+
* Returns the matching block cursor, or null if no common ancestor found.
|
|
78
|
+
*/
|
|
79
|
+
resolveForkCursor(previousBlocks: Array<DeltaDbCursor>): DeltaDbCursor | null
|
|
80
|
+
/**
|
|
81
|
+
* Flush buffered deltas into a msgpack-encoded batch.
|
|
82
|
+
* Returns null if no pending records.
|
|
83
|
+
*/
|
|
84
|
+
flush(): Buffer | null
|
|
85
|
+
/** Acknowledge a flushed batch by sequence number. */
|
|
86
|
+
ack(sequence: number): void
|
|
87
|
+
/** Number of pending (unflushed) delta records. */
|
|
88
|
+
get pendingCount(): number
|
|
89
|
+
/** Whether backpressure should be applied. */
|
|
90
|
+
get isBackpressured(): boolean
|
|
91
|
+
/** Current cursor: latest processed block + hash. Null if no blocks processed. */
|
|
92
|
+
get cursor(): DeltaDbCursor | null
|
|
93
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* Platform-aware native module loader for @sqd-pipes/delta-db */
|
|
2
|
+
const { existsSync } = require('node:fs')
|
|
3
|
+
const { join } = require('node:path')
|
|
4
|
+
|
|
5
|
+
const { platform, arch } = process
|
|
6
|
+
|
|
7
|
+
let nativeBinding = null
|
|
8
|
+
let loadError = null
|
|
9
|
+
|
|
10
|
+
function getPlatformFile() {
|
|
11
|
+
const suffixes = {
|
|
12
|
+
'darwin-x64': 'darwin-x64',
|
|
13
|
+
'darwin-arm64': 'darwin-arm64',
|
|
14
|
+
'linux-x64': 'linux-x64-gnu',
|
|
15
|
+
'linux-arm64': 'linux-arm64-gnu',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const key = `${platform}-${arch}`
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
return suffixes[key] ? `delta-db.${suffixes[key]}.node` : null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Try platform-specific file (e.g. delta-db.linux-x64-gnu.node)
|
|
24
|
+
const platformFile = getPlatformFile()
|
|
25
|
+
if (platformFile) {
|
|
26
|
+
const platformPath = join(__dirname, platformFile)
|
|
27
|
+
if (existsSync(platformPath)) {
|
|
28
|
+
try {
|
|
29
|
+
nativeBinding = require(platformPath)
|
|
30
|
+
} catch (e) {
|
|
31
|
+
loadError = e
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Fallback: try unqualified .node file (local dev build)
|
|
37
|
+
if (!nativeBinding) {
|
|
38
|
+
const localFile = join(__dirname, 'delta-db.node')
|
|
39
|
+
if (existsSync(localFile)) {
|
|
40
|
+
try {
|
|
41
|
+
nativeBinding = require(localFile)
|
|
42
|
+
} catch (e) {
|
|
43
|
+
loadError = e
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!nativeBinding) {
|
|
49
|
+
const help = [
|
|
50
|
+
`Failed to load native binding for ${platform}-${arch}.`,
|
|
51
|
+
platformFile ? `Looked for: ${platformFile}` : `Unsupported platform: ${platform}-${arch}`,
|
|
52
|
+
'',
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
loadError ? `Error: ${loadError.message}` : '',
|
|
55
|
+
'',
|
|
56
|
+
'Build from source: npm run build',
|
|
57
|
+
].join('\n')
|
|
58
|
+
throw new Error(help)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = nativeBinding
|
|
Binary file
|
package/src/index.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
const { DeltaDb: NativeDeltaDb } = require('./native.js')
|
|
2
|
-
const { Encoder, decode } = require('@msgpack/msgpack')
|
|
3
|
-
|
|
4
|
-
const encoder = new Encoder({ useBigInt64: true })
|
|
5
|
-
|
|
6
|
-
class DeltaDb {
|
|
7
|
-
/** @type {InstanceType<typeof NativeDeltaDb>} */
|
|
8
|
-
#native
|
|
9
|
-
|
|
10
|
-
/** @param {InstanceType<typeof NativeDeltaDb>} native */
|
|
11
|
-
constructor(native) {
|
|
12
|
-
this.#native = native
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Open a new DeltaDb instance. */
|
|
16
|
-
static open(config) {
|
|
17
|
-
return new DeltaDb(NativeDeltaDb.open(config))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Process a batch of rows for a raw table.
|
|
22
|
-
* Returns true if backpressure should be applied.
|
|
23
|
-
*/
|
|
24
|
-
processBatch(table, block, rows) {
|
|
25
|
-
return this.#native.processBatch(table, block, Buffer.from(encoder.encode(rows)))
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Roll back all state after fork_point. */
|
|
29
|
-
rollback(forkPoint) {
|
|
30
|
-
this.#native.rollback(forkPoint)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Finalize all state up to and including the given block. */
|
|
34
|
-
finalize(block) {
|
|
35
|
-
this.#native.finalize(block)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Atomic ingest: process all tables, store rollback chain, finalize, flush.
|
|
40
|
-
* Returns the delta batch, or null if no records produced.
|
|
41
|
-
*/
|
|
42
|
-
ingest(input) {
|
|
43
|
-
const buf = this.#native.ingest({
|
|
44
|
-
data: Buffer.from(encoder.encode(input.data)),
|
|
45
|
-
rollbackChain: input.rollbackChain,
|
|
46
|
-
finalizedHead: input.finalizedHead,
|
|
47
|
-
})
|
|
48
|
-
const batch = buf ? decode(buf) : null
|
|
49
|
-
if (batch && input.onDelta) {
|
|
50
|
-
input.onDelta(batch)
|
|
51
|
-
this.#native.ack(batch.sequence)
|
|
52
|
-
}
|
|
53
|
-
return batch
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Find the common ancestor between our state and the Portal's chain.
|
|
58
|
-
* Returns the matching block cursor, or null if no common ancestor found.
|
|
59
|
-
*/
|
|
60
|
-
resolveForkCursor(previousBlocks) {
|
|
61
|
-
return this.#native.resolveForkCursor(previousBlocks)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Flush buffered deltas into a batch. Returns null if no pending records. */
|
|
65
|
-
flush() {
|
|
66
|
-
const buf = this.#native.flush()
|
|
67
|
-
return buf ? decode(buf) : null
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Acknowledge a flushed batch by sequence number. */
|
|
71
|
-
ack(sequence) {
|
|
72
|
-
this.#native.ack(sequence)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Number of pending (unflushed) delta records. */
|
|
76
|
-
get pendingCount() {
|
|
77
|
-
return this.#native.pendingCount
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Whether backpressure should be applied. */
|
|
81
|
-
get isBackpressured() {
|
|
82
|
-
return this.#native.isBackpressured
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Current cursor: latest processed block + hash. Null if no blocks processed. */
|
|
86
|
-
get cursor() {
|
|
87
|
-
return this.#native.cursor
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
module.exports.DeltaDb = DeltaDb
|