@apibara/indexer 2.0.0-beta.9 → 2.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +271 -39
- package/dist/index.d.cts +1 -16
- package/dist/index.d.mts +1 -16
- package/dist/index.d.ts +1 -16
- package/dist/index.mjs +262 -25
- package/dist/internal/index.cjs +10 -0
- package/dist/internal/index.d.cts +3 -0
- package/dist/internal/index.d.mts +3 -0
- package/dist/internal/index.d.ts +3 -0
- package/dist/internal/index.mjs +8 -0
- package/dist/internal/plugins.cjs +38 -0
- package/dist/internal/plugins.d.cts +13 -0
- package/dist/internal/plugins.d.mts +13 -0
- package/dist/internal/plugins.d.ts +13 -0
- package/dist/internal/plugins.mjs +34 -0
- package/dist/internal/testing.cjs +118 -0
- package/dist/internal/testing.d.cts +42 -0
- package/dist/internal/testing.d.mts +42 -0
- package/dist/internal/testing.d.ts +42 -0
- package/dist/internal/testing.mjs +113 -0
- package/dist/plugins/index.cjs +39 -3
- package/dist/plugins/index.d.cts +16 -2
- package/dist/plugins/index.d.mts +16 -2
- package/dist/plugins/index.d.ts +16 -2
- package/dist/plugins/index.mjs +36 -3
- package/dist/shared/indexer.077335f3.cjs +15 -0
- package/dist/shared/indexer.2416906c.cjs +29 -0
- package/dist/shared/indexer.601ceab0.cjs +7 -0
- package/dist/shared/indexer.9b21ddd2.mjs +5 -0
- package/dist/shared/indexer.a55ad619.mjs +12 -0
- package/dist/shared/indexer.fedcd831.d.cts +100 -0
- package/dist/shared/indexer.fedcd831.d.mts +100 -0
- package/dist/shared/indexer.fedcd831.d.ts +100 -0
- package/dist/shared/indexer.ff25c953.mjs +26 -0
- package/dist/testing/index.cjs +52 -50
- package/dist/testing/index.d.cts +8 -36
- package/dist/testing/index.d.mts +8 -36
- package/dist/testing/index.d.ts +8 -36
- package/dist/testing/index.mjs +47 -47
- package/dist/vcr/index.cjs +84 -17
- package/dist/vcr/index.d.cts +16 -7
- package/dist/vcr/index.d.mts +16 -7
- package/dist/vcr/index.d.ts +16 -7
- package/dist/vcr/index.mjs +75 -11
- package/package.json +22 -42
- package/src/compose.test.ts +76 -0
- package/src/compose.ts +71 -0
- package/src/context.ts +14 -8
- package/src/index.ts +0 -5
- package/src/indexer.test.ts +125 -186
- package/src/indexer.ts +278 -151
- package/src/internal/index.ts +6 -0
- package/src/internal/plugins.ts +1 -0
- package/src/internal/testing.ts +148 -0
- package/src/plugins/config.ts +4 -4
- package/src/plugins/context.ts +40 -0
- package/src/plugins/index.ts +8 -1
- package/src/plugins/logger.ts +30 -0
- package/src/plugins/persistence.ts +24 -187
- package/src/testing/index.ts +58 -3
- package/src/vcr/record.ts +5 -3
- package/src/vcr/replay.ts +8 -18
- package/dist/plugins/kv.cjs +0 -131
- package/dist/plugins/kv.d.cts +0 -32
- package/dist/plugins/kv.d.mts +0 -32
- package/dist/plugins/kv.d.ts +0 -32
- package/dist/plugins/kv.mjs +0 -124
- package/dist/plugins/persistence.cjs +0 -182
- package/dist/plugins/persistence.d.cts +0 -50
- package/dist/plugins/persistence.d.mts +0 -50
- package/dist/plugins/persistence.d.ts +0 -50
- package/dist/plugins/persistence.mjs +0 -179
- package/dist/shared/indexer.2c23c9cd.mjs +0 -35
- package/dist/shared/indexer.318d3617.cjs +0 -47
- package/dist/shared/indexer.36530330.mjs +0 -249
- package/dist/shared/indexer.500fd281.d.cts +0 -23
- package/dist/shared/indexer.541d43eb.cjs +0 -266
- package/dist/shared/indexer.93d6b2eb.mjs +0 -17
- package/dist/shared/indexer.a8b7ab1f.cjs +0 -25
- package/dist/shared/indexer.b9c8f0d8.d.cts +0 -19
- package/dist/shared/indexer.b9c8f0d8.d.mts +0 -19
- package/dist/shared/indexer.b9c8f0d8.d.ts +0 -19
- package/dist/shared/indexer.c7ed6b83.d.cts +0 -82
- package/dist/shared/indexer.e1856641.d.mts +0 -23
- package/dist/shared/indexer.e4f2430f.d.ts +0 -23
- package/dist/shared/indexer.e8bd138d.d.mts +0 -82
- package/dist/shared/indexer.f761abcd.d.ts +0 -82
- package/dist/sinks/csv.cjs +0 -85
- package/dist/sinks/csv.d.cts +0 -66
- package/dist/sinks/csv.d.mts +0 -66
- package/dist/sinks/csv.d.ts +0 -66
- package/dist/sinks/csv.mjs +0 -78
- package/dist/sinks/drizzle/index.cjs +0 -212
- package/dist/sinks/drizzle/index.d.cts +0 -153
- package/dist/sinks/drizzle/index.d.mts +0 -153
- package/dist/sinks/drizzle/index.d.ts +0 -153
- package/dist/sinks/drizzle/index.mjs +0 -198
- package/dist/sinks/sqlite.cjs +0 -90
- package/dist/sinks/sqlite.d.cts +0 -71
- package/dist/sinks/sqlite.d.mts +0 -71
- package/dist/sinks/sqlite.d.ts +0 -71
- package/dist/sinks/sqlite.mjs +0 -87
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useKVStore.ts +0 -12
- package/src/hooks/useSink.ts +0 -13
- package/src/plugins/kv.test.ts +0 -120
- package/src/plugins/kv.ts +0 -132
- package/src/plugins/persistence.test.ts +0 -151
- package/src/sink.ts +0 -36
- package/src/sinks/csv.test.ts +0 -65
- package/src/sinks/csv.ts +0 -159
- package/src/sinks/drizzle/Int8Range.ts +0 -52
- package/src/sinks/drizzle/delete.ts +0 -42
- package/src/sinks/drizzle/drizzle.test.ts +0 -239
- package/src/sinks/drizzle/drizzle.ts +0 -115
- package/src/sinks/drizzle/index.ts +0 -6
- package/src/sinks/drizzle/insert.ts +0 -42
- package/src/sinks/drizzle/select.ts +0 -44
- package/src/sinks/drizzle/transaction.ts +0 -49
- package/src/sinks/drizzle/update.ts +0 -47
- package/src/sinks/drizzle/utils.ts +0 -99
- package/src/sinks/sqlite.test.ts +0 -99
- package/src/sinks/sqlite.ts +0 -170
- package/src/testing/helper.ts +0 -13
- package/src/testing/indexer.ts +0 -35
- package/src/testing/setup.ts +0 -59
- package/src/testing/vcr.ts +0 -54
package/dist/sinks/sqlite.cjs
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const sink = require('../shared/indexer.a8b7ab1f.cjs');
|
|
4
|
-
require('consola');
|
|
5
|
-
|
|
6
|
-
var __defProp = Object.defineProperty;
|
|
7
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
-
var __publicField = (obj, key, value) => {
|
|
9
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
10
|
-
return value;
|
|
11
|
-
};
|
|
12
|
-
const transactionHelper = (context) => {
|
|
13
|
-
return {
|
|
14
|
-
insert: (data) => {
|
|
15
|
-
context.buffer.push(...data);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
class SqliteSink extends sink.Sink {
|
|
20
|
-
constructor(options) {
|
|
21
|
-
super();
|
|
22
|
-
__publicField(this, "_config");
|
|
23
|
-
__publicField(this, "_db");
|
|
24
|
-
const { database, ...config } = options;
|
|
25
|
-
this._config = config;
|
|
26
|
-
this._db = database;
|
|
27
|
-
}
|
|
28
|
-
async write({
|
|
29
|
-
data,
|
|
30
|
-
endCursor
|
|
31
|
-
}) {
|
|
32
|
-
data = this.processCursorColumn(data, endCursor);
|
|
33
|
-
await this.insertJsonArray(data);
|
|
34
|
-
}
|
|
35
|
-
async transaction({ cursor, endCursor, finality }, cb) {
|
|
36
|
-
const context = {
|
|
37
|
-
buffer: []
|
|
38
|
-
};
|
|
39
|
-
const writer = transactionHelper(context);
|
|
40
|
-
await cb({ writer });
|
|
41
|
-
await this.write({ data: context.buffer, endCursor });
|
|
42
|
-
}
|
|
43
|
-
async invalidate(cursor) {
|
|
44
|
-
throw new Error("Not implemented");
|
|
45
|
-
}
|
|
46
|
-
async insertJsonArray(data) {
|
|
47
|
-
if (data.length === 0)
|
|
48
|
-
return;
|
|
49
|
-
const columns = Object.keys(data[0]);
|
|
50
|
-
const columnNames = columns.join(", ");
|
|
51
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
52
|
-
const conflictClause = this.buildConflictClause();
|
|
53
|
-
const insertSQL = `INSERT INTO ${this._config.tableName} (${columnNames}) VALUES `;
|
|
54
|
-
const valuePlaceholders = data.map(() => `(${placeholders})`).join(", ");
|
|
55
|
-
const statement = insertSQL + valuePlaceholders + conflictClause;
|
|
56
|
-
const values = data.flatMap((row) => columns.map((col) => row[col]));
|
|
57
|
-
this._db.prepare(statement).run(values);
|
|
58
|
-
}
|
|
59
|
-
processCursorColumn(data, endCursor) {
|
|
60
|
-
const { cursorColumn } = this._config;
|
|
61
|
-
if (cursorColumn && data.some(
|
|
62
|
-
(row) => Number(row[cursorColumn]) !== Number(endCursor?.orderKey)
|
|
63
|
-
)) {
|
|
64
|
-
throw new Error(
|
|
65
|
-
`Mismatch of ${cursorColumn} and Cursor ${Number(endCursor?.orderKey)}`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
if (cursorColumn) {
|
|
69
|
-
return data;
|
|
70
|
-
}
|
|
71
|
-
return data.map((row) => ({
|
|
72
|
-
...row,
|
|
73
|
-
_cursor: Number(endCursor?.orderKey)
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
buildConflictClause() {
|
|
77
|
-
const { on, update } = this._config.onConflict || {};
|
|
78
|
-
if (on && update && update.length > 0) {
|
|
79
|
-
const updateColumns = update.map((col) => `${col}=excluded.${col}`).join(", ");
|
|
80
|
-
return ` ON CONFLICT(${on}) DO UPDATE SET ${updateColumns}`;
|
|
81
|
-
}
|
|
82
|
-
return "";
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const sqlite = (args) => {
|
|
86
|
-
return new SqliteSink(args);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
exports.SqliteSink = SqliteSink;
|
|
90
|
-
exports.sqlite = sqlite;
|
package/dist/sinks/sqlite.d.cts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { Cursor } from '@apibara/protocol';
|
|
2
|
-
import { Database } from 'better-sqlite3';
|
|
3
|
-
import { S as Sink, a as SinkCursorParams, b as SinkData } from '../shared/indexer.b9c8f0d8.cjs';
|
|
4
|
-
|
|
5
|
-
type SqliteSinkOptions = {
|
|
6
|
-
/**
|
|
7
|
-
* Database instance of better-sqlite3
|
|
8
|
-
*/
|
|
9
|
-
database: Database;
|
|
10
|
-
/**
|
|
11
|
-
* The name of the table where data will be inserted.
|
|
12
|
-
*/
|
|
13
|
-
tableName: string;
|
|
14
|
-
/**
|
|
15
|
-
* An optional column name used to store the cursor value. If specified,
|
|
16
|
-
* the value of this column must match the `endCursor.orderKey` for each row.
|
|
17
|
-
*/
|
|
18
|
-
cursorColumn?: string;
|
|
19
|
-
/**
|
|
20
|
-
* An optional configuration to handle conflicts during data insertion.
|
|
21
|
-
* - `on`: The column name on which conflicts are detected.
|
|
22
|
-
* - `update`: An array of column names to be updated if a conflict occurs.
|
|
23
|
-
*/
|
|
24
|
-
onConflict?: {
|
|
25
|
-
on: string;
|
|
26
|
-
update: string[];
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
type TxnParams = {
|
|
30
|
-
writer: {
|
|
31
|
-
insert: (data: SinkData[]) => void;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* A sink that writes data to a SQLite database.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
*
|
|
39
|
-
* ```ts
|
|
40
|
-
* const sink = sqlite({
|
|
41
|
-
* database: db,
|
|
42
|
-
* tableName: "test",
|
|
43
|
-
* });
|
|
44
|
-
*
|
|
45
|
-
* ...
|
|
46
|
-
* async transform({context, endCursor}){
|
|
47
|
-
* const { writer } = useSink(context);
|
|
48
|
-
* const insertHelper = writer(endCursor);
|
|
49
|
-
*
|
|
50
|
-
* insertHelper.insert([
|
|
51
|
-
* { id: 1, name: "John" },
|
|
52
|
-
* { id: 2, name: "Jane" },
|
|
53
|
-
* ]);
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
declare class SqliteSink extends Sink {
|
|
59
|
-
private _config;
|
|
60
|
-
private _db;
|
|
61
|
-
constructor(options: SqliteSinkOptions);
|
|
62
|
-
private write;
|
|
63
|
-
transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: TxnParams) => Promise<void>): Promise<void>;
|
|
64
|
-
invalidate(cursor?: Cursor): Promise<void>;
|
|
65
|
-
private insertJsonArray;
|
|
66
|
-
private processCursorColumn;
|
|
67
|
-
private buildConflictClause;
|
|
68
|
-
}
|
|
69
|
-
declare const sqlite: (args: SqliteSinkOptions) => SqliteSink;
|
|
70
|
-
|
|
71
|
-
export { SqliteSink, type SqliteSinkOptions, sqlite };
|
package/dist/sinks/sqlite.d.mts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { Cursor } from '@apibara/protocol';
|
|
2
|
-
import { Database } from 'better-sqlite3';
|
|
3
|
-
import { S as Sink, a as SinkCursorParams, b as SinkData } from '../shared/indexer.b9c8f0d8.mjs';
|
|
4
|
-
|
|
5
|
-
type SqliteSinkOptions = {
|
|
6
|
-
/**
|
|
7
|
-
* Database instance of better-sqlite3
|
|
8
|
-
*/
|
|
9
|
-
database: Database;
|
|
10
|
-
/**
|
|
11
|
-
* The name of the table where data will be inserted.
|
|
12
|
-
*/
|
|
13
|
-
tableName: string;
|
|
14
|
-
/**
|
|
15
|
-
* An optional column name used to store the cursor value. If specified,
|
|
16
|
-
* the value of this column must match the `endCursor.orderKey` for each row.
|
|
17
|
-
*/
|
|
18
|
-
cursorColumn?: string;
|
|
19
|
-
/**
|
|
20
|
-
* An optional configuration to handle conflicts during data insertion.
|
|
21
|
-
* - `on`: The column name on which conflicts are detected.
|
|
22
|
-
* - `update`: An array of column names to be updated if a conflict occurs.
|
|
23
|
-
*/
|
|
24
|
-
onConflict?: {
|
|
25
|
-
on: string;
|
|
26
|
-
update: string[];
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
type TxnParams = {
|
|
30
|
-
writer: {
|
|
31
|
-
insert: (data: SinkData[]) => void;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* A sink that writes data to a SQLite database.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
*
|
|
39
|
-
* ```ts
|
|
40
|
-
* const sink = sqlite({
|
|
41
|
-
* database: db,
|
|
42
|
-
* tableName: "test",
|
|
43
|
-
* });
|
|
44
|
-
*
|
|
45
|
-
* ...
|
|
46
|
-
* async transform({context, endCursor}){
|
|
47
|
-
* const { writer } = useSink(context);
|
|
48
|
-
* const insertHelper = writer(endCursor);
|
|
49
|
-
*
|
|
50
|
-
* insertHelper.insert([
|
|
51
|
-
* { id: 1, name: "John" },
|
|
52
|
-
* { id: 2, name: "Jane" },
|
|
53
|
-
* ]);
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
declare class SqliteSink extends Sink {
|
|
59
|
-
private _config;
|
|
60
|
-
private _db;
|
|
61
|
-
constructor(options: SqliteSinkOptions);
|
|
62
|
-
private write;
|
|
63
|
-
transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: TxnParams) => Promise<void>): Promise<void>;
|
|
64
|
-
invalidate(cursor?: Cursor): Promise<void>;
|
|
65
|
-
private insertJsonArray;
|
|
66
|
-
private processCursorColumn;
|
|
67
|
-
private buildConflictClause;
|
|
68
|
-
}
|
|
69
|
-
declare const sqlite: (args: SqliteSinkOptions) => SqliteSink;
|
|
70
|
-
|
|
71
|
-
export { SqliteSink, type SqliteSinkOptions, sqlite };
|
package/dist/sinks/sqlite.d.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { Cursor } from '@apibara/protocol';
|
|
2
|
-
import { Database } from 'better-sqlite3';
|
|
3
|
-
import { S as Sink, a as SinkCursorParams, b as SinkData } from '../shared/indexer.b9c8f0d8.js';
|
|
4
|
-
|
|
5
|
-
type SqliteSinkOptions = {
|
|
6
|
-
/**
|
|
7
|
-
* Database instance of better-sqlite3
|
|
8
|
-
*/
|
|
9
|
-
database: Database;
|
|
10
|
-
/**
|
|
11
|
-
* The name of the table where data will be inserted.
|
|
12
|
-
*/
|
|
13
|
-
tableName: string;
|
|
14
|
-
/**
|
|
15
|
-
* An optional column name used to store the cursor value. If specified,
|
|
16
|
-
* the value of this column must match the `endCursor.orderKey` for each row.
|
|
17
|
-
*/
|
|
18
|
-
cursorColumn?: string;
|
|
19
|
-
/**
|
|
20
|
-
* An optional configuration to handle conflicts during data insertion.
|
|
21
|
-
* - `on`: The column name on which conflicts are detected.
|
|
22
|
-
* - `update`: An array of column names to be updated if a conflict occurs.
|
|
23
|
-
*/
|
|
24
|
-
onConflict?: {
|
|
25
|
-
on: string;
|
|
26
|
-
update: string[];
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
type TxnParams = {
|
|
30
|
-
writer: {
|
|
31
|
-
insert: (data: SinkData[]) => void;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* A sink that writes data to a SQLite database.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
*
|
|
39
|
-
* ```ts
|
|
40
|
-
* const sink = sqlite({
|
|
41
|
-
* database: db,
|
|
42
|
-
* tableName: "test",
|
|
43
|
-
* });
|
|
44
|
-
*
|
|
45
|
-
* ...
|
|
46
|
-
* async transform({context, endCursor}){
|
|
47
|
-
* const { writer } = useSink(context);
|
|
48
|
-
* const insertHelper = writer(endCursor);
|
|
49
|
-
*
|
|
50
|
-
* insertHelper.insert([
|
|
51
|
-
* { id: 1, name: "John" },
|
|
52
|
-
* { id: 2, name: "Jane" },
|
|
53
|
-
* ]);
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
declare class SqliteSink extends Sink {
|
|
59
|
-
private _config;
|
|
60
|
-
private _db;
|
|
61
|
-
constructor(options: SqliteSinkOptions);
|
|
62
|
-
private write;
|
|
63
|
-
transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: TxnParams) => Promise<void>): Promise<void>;
|
|
64
|
-
invalidate(cursor?: Cursor): Promise<void>;
|
|
65
|
-
private insertJsonArray;
|
|
66
|
-
private processCursorColumn;
|
|
67
|
-
private buildConflictClause;
|
|
68
|
-
}
|
|
69
|
-
declare const sqlite: (args: SqliteSinkOptions) => SqliteSink;
|
|
70
|
-
|
|
71
|
-
export { SqliteSink, type SqliteSinkOptions, sqlite };
|
package/dist/sinks/sqlite.mjs
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { S as Sink } from '../shared/indexer.93d6b2eb.mjs';
|
|
2
|
-
import 'consola';
|
|
3
|
-
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __publicField = (obj, key, value) => {
|
|
7
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
|
-
return value;
|
|
9
|
-
};
|
|
10
|
-
const transactionHelper = (context) => {
|
|
11
|
-
return {
|
|
12
|
-
insert: (data) => {
|
|
13
|
-
context.buffer.push(...data);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
class SqliteSink extends Sink {
|
|
18
|
-
constructor(options) {
|
|
19
|
-
super();
|
|
20
|
-
__publicField(this, "_config");
|
|
21
|
-
__publicField(this, "_db");
|
|
22
|
-
const { database, ...config } = options;
|
|
23
|
-
this._config = config;
|
|
24
|
-
this._db = database;
|
|
25
|
-
}
|
|
26
|
-
async write({
|
|
27
|
-
data,
|
|
28
|
-
endCursor
|
|
29
|
-
}) {
|
|
30
|
-
data = this.processCursorColumn(data, endCursor);
|
|
31
|
-
await this.insertJsonArray(data);
|
|
32
|
-
}
|
|
33
|
-
async transaction({ cursor, endCursor, finality }, cb) {
|
|
34
|
-
const context = {
|
|
35
|
-
buffer: []
|
|
36
|
-
};
|
|
37
|
-
const writer = transactionHelper(context);
|
|
38
|
-
await cb({ writer });
|
|
39
|
-
await this.write({ data: context.buffer, endCursor });
|
|
40
|
-
}
|
|
41
|
-
async invalidate(cursor) {
|
|
42
|
-
throw new Error("Not implemented");
|
|
43
|
-
}
|
|
44
|
-
async insertJsonArray(data) {
|
|
45
|
-
if (data.length === 0)
|
|
46
|
-
return;
|
|
47
|
-
const columns = Object.keys(data[0]);
|
|
48
|
-
const columnNames = columns.join(", ");
|
|
49
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
50
|
-
const conflictClause = this.buildConflictClause();
|
|
51
|
-
const insertSQL = `INSERT INTO ${this._config.tableName} (${columnNames}) VALUES `;
|
|
52
|
-
const valuePlaceholders = data.map(() => `(${placeholders})`).join(", ");
|
|
53
|
-
const statement = insertSQL + valuePlaceholders + conflictClause;
|
|
54
|
-
const values = data.flatMap((row) => columns.map((col) => row[col]));
|
|
55
|
-
this._db.prepare(statement).run(values);
|
|
56
|
-
}
|
|
57
|
-
processCursorColumn(data, endCursor) {
|
|
58
|
-
const { cursorColumn } = this._config;
|
|
59
|
-
if (cursorColumn && data.some(
|
|
60
|
-
(row) => Number(row[cursorColumn]) !== Number(endCursor?.orderKey)
|
|
61
|
-
)) {
|
|
62
|
-
throw new Error(
|
|
63
|
-
`Mismatch of ${cursorColumn} and Cursor ${Number(endCursor?.orderKey)}`
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
if (cursorColumn) {
|
|
67
|
-
return data;
|
|
68
|
-
}
|
|
69
|
-
return data.map((row) => ({
|
|
70
|
-
...row,
|
|
71
|
-
_cursor: Number(endCursor?.orderKey)
|
|
72
|
-
}));
|
|
73
|
-
}
|
|
74
|
-
buildConflictClause() {
|
|
75
|
-
const { on, update } = this._config.onConflict || {};
|
|
76
|
-
if (on && update && update.length > 0) {
|
|
77
|
-
const updateColumns = update.map((col) => `${col}=excluded.${col}`).join(", ");
|
|
78
|
-
return ` ON CONFLICT(${on}) DO UPDATE SET ${updateColumns}`;
|
|
79
|
-
}
|
|
80
|
-
return "";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const sqlite = (args) => {
|
|
84
|
-
return new SqliteSink(args);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export { SqliteSink, sqlite };
|
package/src/hooks/index.ts
DELETED
package/src/hooks/useKVStore.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { useIndexerContext } from "../context";
|
|
2
|
-
import type { KVStore } from "../plugins/kv";
|
|
3
|
-
|
|
4
|
-
export type UseKVStoreResult = InstanceType<typeof KVStore>;
|
|
5
|
-
|
|
6
|
-
export function useKVStore(): UseKVStoreResult {
|
|
7
|
-
const ctx = useIndexerContext();
|
|
8
|
-
|
|
9
|
-
if (!ctx?.kv) throw new Error("KV Plugin is not available in context!");
|
|
10
|
-
|
|
11
|
-
return ctx.kv;
|
|
12
|
-
}
|
package/src/hooks/useSink.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { IndexerContext } from "../context";
|
|
2
|
-
|
|
3
|
-
export function useSink<TTxnParams>({
|
|
4
|
-
context,
|
|
5
|
-
}: {
|
|
6
|
-
context: IndexerContext<TTxnParams>;
|
|
7
|
-
}) {
|
|
8
|
-
if (!context.sinkTransaction) {
|
|
9
|
-
throw new Error("Transaction context doesn't exist!");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return context.sinkTransaction;
|
|
13
|
-
}
|
package/src/plugins/kv.test.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import Database, { type Database as SqliteDatabase } from "better-sqlite3";
|
|
2
|
-
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
|
-
import { KVStore } from "./kv";
|
|
4
|
-
|
|
5
|
-
type ValueType = { data: bigint };
|
|
6
|
-
|
|
7
|
-
type DatabaseRowType = {
|
|
8
|
-
from_block: number;
|
|
9
|
-
k: string;
|
|
10
|
-
to_block: number;
|
|
11
|
-
v: unknown;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
describe("KVStore", () => {
|
|
15
|
-
let db: SqliteDatabase;
|
|
16
|
-
let store: KVStore;
|
|
17
|
-
const key = "test_key";
|
|
18
|
-
|
|
19
|
-
beforeAll(() => {
|
|
20
|
-
db = new Database(":memory:");
|
|
21
|
-
KVStore.initialize(db);
|
|
22
|
-
store = new KVStore(db, "finalized", { orderKey: 5_000_000n });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
afterAll(async () => {
|
|
26
|
-
db.close();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("should begin transaction", () => {
|
|
30
|
-
store.beginTransaction();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("should put and get a value", async () => {
|
|
34
|
-
const value = { data: 0n };
|
|
35
|
-
|
|
36
|
-
store.put<ValueType>(key, value);
|
|
37
|
-
const result = store.get<ValueType>(key);
|
|
38
|
-
|
|
39
|
-
expect(result).toEqual(value);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("should commit transaction", async () => {
|
|
43
|
-
store.commitTransaction();
|
|
44
|
-
|
|
45
|
-
const value = { data: 0n };
|
|
46
|
-
|
|
47
|
-
const result = store.get<ValueType>(key);
|
|
48
|
-
|
|
49
|
-
expect(result).toEqual(value);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("should return undefined for non-existing key", async () => {
|
|
53
|
-
const result = store.get<ValueType>("non_existent_key");
|
|
54
|
-
expect(result).toBeUndefined();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("should begin transaction", async () => {
|
|
58
|
-
store.beginTransaction();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should update an existing value", async () => {
|
|
62
|
-
store = new KVStore(db, "finalized", { orderKey: 5_000_020n });
|
|
63
|
-
|
|
64
|
-
const value = { data: 50n };
|
|
65
|
-
|
|
66
|
-
store.put<ValueType>(key, value);
|
|
67
|
-
const result = store.get<ValueType>(key);
|
|
68
|
-
|
|
69
|
-
expect(result).toEqual(value);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("should delete a value", async () => {
|
|
73
|
-
store.del(key);
|
|
74
|
-
const result = store.get<ValueType>(key);
|
|
75
|
-
|
|
76
|
-
expect(result).toBeUndefined();
|
|
77
|
-
|
|
78
|
-
const rows = db
|
|
79
|
-
.prepare<string, DatabaseRowType>(
|
|
80
|
-
`
|
|
81
|
-
SELECT from_block, to_block, k, v
|
|
82
|
-
FROM kvs
|
|
83
|
-
WHERE k = ?
|
|
84
|
-
`,
|
|
85
|
-
)
|
|
86
|
-
.all(key);
|
|
87
|
-
|
|
88
|
-
// Check that the old is correctly marked with to_block
|
|
89
|
-
expect(rows[0].to_block).toBe(Number(5_000_020n));
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("should rollback transaction", async () => {
|
|
93
|
-
await store.rollbackTransaction();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("should revert the changes to last commit", async () => {
|
|
97
|
-
const rows = db
|
|
98
|
-
.prepare(
|
|
99
|
-
`
|
|
100
|
-
SELECT from_block, to_block, k, v
|
|
101
|
-
FROM kvs
|
|
102
|
-
WHERE k = ?
|
|
103
|
-
`,
|
|
104
|
-
)
|
|
105
|
-
.all([key]);
|
|
106
|
-
|
|
107
|
-
expect(rows).toMatchInlineSnapshot(`
|
|
108
|
-
[
|
|
109
|
-
{
|
|
110
|
-
"from_block": 5000000,
|
|
111
|
-
"k": "test_key",
|
|
112
|
-
"to_block": null,
|
|
113
|
-
"v": "{
|
|
114
|
-
"data": "0n"
|
|
115
|
-
}",
|
|
116
|
-
},
|
|
117
|
-
]
|
|
118
|
-
`);
|
|
119
|
-
});
|
|
120
|
-
});
|
package/src/plugins/kv.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import type { Cursor, DataFinality } from "@apibara/protocol";
|
|
3
|
-
import type { Database as SqliteDatabase, Statement } from "better-sqlite3";
|
|
4
|
-
import { useIndexerContext } from "../context";
|
|
5
|
-
import { deserialize, serialize } from "../vcr";
|
|
6
|
-
import { defineIndexerPlugin } from "./config";
|
|
7
|
-
|
|
8
|
-
export function kv<TFilter, TBlock, TTxnParams>({
|
|
9
|
-
database,
|
|
10
|
-
}: { database: SqliteDatabase }) {
|
|
11
|
-
return defineIndexerPlugin<TFilter, TBlock, TTxnParams>((indexer) => {
|
|
12
|
-
indexer.hooks.hook("run:before", () => {
|
|
13
|
-
KVStore.initialize(database);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
indexer.hooks.hook("handler:before", ({ finality, endCursor }) => {
|
|
17
|
-
const ctx = useIndexerContext();
|
|
18
|
-
|
|
19
|
-
assert(endCursor, new Error("endCursor cannot be undefined"));
|
|
20
|
-
|
|
21
|
-
ctx.kv = new KVStore(database, finality, endCursor);
|
|
22
|
-
|
|
23
|
-
ctx.kv.beginTransaction();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
indexer.hooks.hook("handler:after", () => {
|
|
27
|
-
const ctx = useIndexerContext();
|
|
28
|
-
|
|
29
|
-
ctx.kv.commitTransaction();
|
|
30
|
-
|
|
31
|
-
ctx.kv = null;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
indexer.hooks.hook("handler:exception", () => {
|
|
35
|
-
const ctx = useIndexerContext();
|
|
36
|
-
|
|
37
|
-
ctx.kv.rollbackTransaction();
|
|
38
|
-
|
|
39
|
-
ctx.kv = null;
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class KVStore {
|
|
45
|
-
/** Sqlite Queries Prepare Statements */
|
|
46
|
-
private _beginTxnQuery: Statement;
|
|
47
|
-
private _commitTxnQuery: Statement;
|
|
48
|
-
private _rollbackTxnQuery: Statement;
|
|
49
|
-
private _getQuery: Statement<string, { v: string }>;
|
|
50
|
-
private _updateToBlockQuery: Statement<[number, string]>;
|
|
51
|
-
private _insertIntoKvsQuery: Statement<[number, string, string]>;
|
|
52
|
-
private _delQuery: Statement<[number, string]>;
|
|
53
|
-
|
|
54
|
-
constructor(
|
|
55
|
-
private _db: SqliteDatabase,
|
|
56
|
-
private _finality: DataFinality,
|
|
57
|
-
private _endCursor: Cursor,
|
|
58
|
-
) {
|
|
59
|
-
this._beginTxnQuery = this._db.prepare(statements.beginTxn);
|
|
60
|
-
this._commitTxnQuery = this._db.prepare(statements.commitTxn);
|
|
61
|
-
this._rollbackTxnQuery = this._db.prepare(statements.rollbackTxn);
|
|
62
|
-
this._getQuery = this._db.prepare(statements.get);
|
|
63
|
-
this._updateToBlockQuery = this._db.prepare(statements.updateToBlock);
|
|
64
|
-
this._insertIntoKvsQuery = this._db.prepare(statements.insertIntoKvs);
|
|
65
|
-
this._delQuery = this._db.prepare(statements.del);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
static initialize(db: SqliteDatabase) {
|
|
69
|
-
db.prepare(statements.createTable).run();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
beginTransaction() {
|
|
73
|
-
this._beginTxnQuery.run();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
commitTransaction() {
|
|
77
|
-
this._commitTxnQuery.run();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
rollbackTransaction() {
|
|
81
|
-
this._rollbackTxnQuery.run();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
get<T>(key: string): T {
|
|
85
|
-
const row = this._getQuery.get(key);
|
|
86
|
-
|
|
87
|
-
return row ? deserialize(row.v) : undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
put<T>(key: string, value: T) {
|
|
91
|
-
this._updateToBlockQuery.run(Number(this._endCursor.orderKey), key);
|
|
92
|
-
|
|
93
|
-
this._insertIntoKvsQuery.run(
|
|
94
|
-
Number(this._endCursor.orderKey),
|
|
95
|
-
key,
|
|
96
|
-
serialize(value as Record<string, unknown>),
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
del(key: string) {
|
|
101
|
-
this._delQuery.run(Number(this._endCursor.orderKey), key);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const statements = {
|
|
106
|
-
beginTxn: "BEGIN TRANSACTION",
|
|
107
|
-
commitTxn: "COMMIT TRANSACTION",
|
|
108
|
-
rollbackTxn: "ROLLBACK TRANSACTION",
|
|
109
|
-
createTable: `
|
|
110
|
-
CREATE TABLE IF NOT EXISTS kvs (
|
|
111
|
-
from_block INTEGER NOT NULL,
|
|
112
|
-
to_block INTEGER,
|
|
113
|
-
k TEXT NOT NULL,
|
|
114
|
-
v BLOB NOT NULL,
|
|
115
|
-
PRIMARY KEY (from_block, k)
|
|
116
|
-
);`,
|
|
117
|
-
get: `
|
|
118
|
-
SELECT v
|
|
119
|
-
FROM kvs
|
|
120
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
121
|
-
updateToBlock: `
|
|
122
|
-
UPDATE kvs
|
|
123
|
-
SET to_block = ?
|
|
124
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
125
|
-
insertIntoKvs: `
|
|
126
|
-
INSERT INTO kvs (from_block, to_block, k, v)
|
|
127
|
-
VALUES (?, NULL, ?, ?)`,
|
|
128
|
-
del: `
|
|
129
|
-
UPDATE kvs
|
|
130
|
-
SET to_block = ?
|
|
131
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
132
|
-
};
|