@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/src/indexer.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
Client,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import {
|
|
2
|
+
type Client,
|
|
3
|
+
ClientError,
|
|
4
|
+
type Cursor,
|
|
5
|
+
type DataFinality,
|
|
6
|
+
type Finalize,
|
|
7
|
+
type Heartbeat,
|
|
8
|
+
type Invalidate,
|
|
9
|
+
Status,
|
|
10
|
+
type StreamConfig,
|
|
11
|
+
type StreamDataOptions,
|
|
12
|
+
type StreamDataRequest,
|
|
13
|
+
type StreamDataResponse,
|
|
14
|
+
type SystemMessage,
|
|
9
15
|
} from "@apibara/protocol";
|
|
10
16
|
import consola from "consola";
|
|
11
17
|
import {
|
|
@@ -16,6 +22,7 @@ import {
|
|
|
16
22
|
} from "hookable";
|
|
17
23
|
|
|
18
24
|
import assert from "node:assert";
|
|
25
|
+
import { type MiddlewareFunction, type NextFunction, compose } from "./compose";
|
|
19
26
|
import {
|
|
20
27
|
type IndexerContext,
|
|
21
28
|
indexerAsyncContext,
|
|
@@ -23,7 +30,10 @@ import {
|
|
|
23
30
|
} from "./context";
|
|
24
31
|
import { tracer } from "./otel";
|
|
25
32
|
import type { IndexerPlugin } from "./plugins";
|
|
26
|
-
|
|
33
|
+
|
|
34
|
+
export type UseMiddlewareFunction = (
|
|
35
|
+
fn: MiddlewareFunction<IndexerContext>,
|
|
36
|
+
) => void;
|
|
27
37
|
|
|
28
38
|
export interface IndexerHooks<TFilter, TBlock> {
|
|
29
39
|
"run:before": () => void;
|
|
@@ -35,7 +45,9 @@ export interface IndexerHooks<TFilter, TBlock> {
|
|
|
35
45
|
request: StreamDataRequest<TFilter>;
|
|
36
46
|
options: StreamDataOptions;
|
|
37
47
|
}) => void;
|
|
38
|
-
"connect:after": (
|
|
48
|
+
"connect:after": ({
|
|
49
|
+
request,
|
|
50
|
+
}: { request: StreamDataRequest<TFilter> }) => void;
|
|
39
51
|
"connect:factory": ({
|
|
40
52
|
request,
|
|
41
53
|
endCursor,
|
|
@@ -43,45 +55,36 @@ export interface IndexerHooks<TFilter, TBlock> {
|
|
|
43
55
|
request: StreamDataRequest<TFilter>;
|
|
44
56
|
endCursor?: Cursor;
|
|
45
57
|
}) => void;
|
|
46
|
-
"handler:
|
|
47
|
-
block,
|
|
48
|
-
finality,
|
|
49
|
-
endCursor,
|
|
50
|
-
}: {
|
|
51
|
-
block: TBlock;
|
|
52
|
-
finality: DataFinality;
|
|
53
|
-
endCursor?: Cursor;
|
|
54
|
-
}) => void;
|
|
55
|
-
"handler:after": ({
|
|
56
|
-
block,
|
|
57
|
-
finality,
|
|
58
|
-
endCursor,
|
|
59
|
-
}: {
|
|
60
|
-
block: TBlock;
|
|
61
|
-
finality: DataFinality;
|
|
62
|
-
endCursor?: Cursor;
|
|
63
|
-
}) => void;
|
|
64
|
-
"transaction:commit": ({
|
|
65
|
-
finality,
|
|
66
|
-
endCursor,
|
|
67
|
-
}: {
|
|
68
|
-
finality: DataFinality;
|
|
69
|
-
endCursor?: Cursor;
|
|
70
|
-
}) => void;
|
|
71
|
-
"handler:exception": ({ error }: { error: Error }) => void;
|
|
58
|
+
"handler:middleware": ({ use }: { use: UseMiddlewareFunction }) => void;
|
|
72
59
|
message: ({ message }: { message: StreamDataResponse<TBlock> }) => void;
|
|
60
|
+
"message:invalidate": ({ message }: { message: Invalidate }) => void;
|
|
61
|
+
"message:finalize": ({ message }: { message: Finalize }) => void;
|
|
62
|
+
"message:heartbeat": ({ message }: { message: Heartbeat }) => void;
|
|
63
|
+
"message:systemMessage": ({ message }: { message: SystemMessage }) => void;
|
|
73
64
|
}
|
|
74
65
|
|
|
75
|
-
export
|
|
66
|
+
export type IndexerStartingCursor =
|
|
67
|
+
| {
|
|
68
|
+
startingCursor?: never;
|
|
69
|
+
startingBlock: bigint;
|
|
70
|
+
}
|
|
71
|
+
| {
|
|
72
|
+
startingCursor: Cursor;
|
|
73
|
+
startingBlock?: never;
|
|
74
|
+
}
|
|
75
|
+
| {
|
|
76
|
+
startingCursor?: never;
|
|
77
|
+
startingBlock?: never;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type IndexerConfig<TFilter, TBlock> = {
|
|
76
81
|
streamUrl: string;
|
|
77
82
|
filter: TFilter;
|
|
78
83
|
finality?: DataFinality;
|
|
79
|
-
startingCursor?: Cursor;
|
|
80
|
-
sink?: Sink<TTxnParams>;
|
|
81
84
|
factory?: ({
|
|
82
85
|
block,
|
|
83
86
|
context,
|
|
84
|
-
}: { block: TBlock; context: IndexerContext
|
|
87
|
+
}: { block: TBlock; context: IndexerContext }) => Promise<{
|
|
85
88
|
filter?: TFilter;
|
|
86
89
|
}>;
|
|
87
90
|
transform: (args: {
|
|
@@ -89,40 +92,42 @@ export interface IndexerConfig<TFilter, TBlock, TTxnParams> {
|
|
|
89
92
|
cursor?: Cursor | undefined;
|
|
90
93
|
endCursor?: Cursor | undefined;
|
|
91
94
|
finality: DataFinality;
|
|
92
|
-
context: IndexerContext
|
|
95
|
+
context: IndexerContext;
|
|
93
96
|
}) => Promise<void>;
|
|
94
97
|
hooks?: NestedHooks<IndexerHooks<TFilter, TBlock>>;
|
|
95
|
-
plugins?: ReadonlyArray<IndexerPlugin<TFilter, TBlock
|
|
98
|
+
plugins?: ReadonlyArray<IndexerPlugin<TFilter, TBlock>>;
|
|
96
99
|
debug?: boolean;
|
|
97
|
-
}
|
|
100
|
+
} & IndexerStartingCursor;
|
|
98
101
|
|
|
99
|
-
export
|
|
100
|
-
|
|
102
|
+
export type IndexerWithStreamConfig<TFilter, TBlock> = IndexerConfig<
|
|
103
|
+
TFilter,
|
|
104
|
+
TBlock
|
|
105
|
+
> & {
|
|
101
106
|
streamConfig: StreamConfig<TFilter, TBlock>;
|
|
102
|
-
}
|
|
107
|
+
};
|
|
103
108
|
|
|
104
109
|
export function defineIndexer<TFilter, TBlock>(
|
|
105
110
|
streamConfig: StreamConfig<TFilter, TBlock>,
|
|
106
111
|
) {
|
|
107
|
-
return
|
|
108
|
-
config: IndexerConfig<TFilter, TBlock
|
|
109
|
-
): IndexerWithStreamConfig<TFilter, TBlock
|
|
112
|
+
return (
|
|
113
|
+
config: IndexerConfig<TFilter, TBlock>,
|
|
114
|
+
): IndexerWithStreamConfig<TFilter, TBlock> => ({
|
|
110
115
|
streamConfig,
|
|
111
116
|
...config,
|
|
112
117
|
});
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
export interface Indexer<TFilter, TBlock
|
|
120
|
+
export interface Indexer<TFilter, TBlock> {
|
|
116
121
|
streamConfig: StreamConfig<TFilter, TBlock>;
|
|
117
|
-
options: IndexerConfig<TFilter, TBlock
|
|
122
|
+
options: IndexerConfig<TFilter, TBlock>;
|
|
118
123
|
hooks: Hookable<IndexerHooks<TFilter, TBlock>>;
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
export function createIndexer<TFilter, TBlock
|
|
126
|
+
export function createIndexer<TFilter, TBlock>({
|
|
122
127
|
streamConfig,
|
|
123
128
|
...options
|
|
124
|
-
}: IndexerWithStreamConfig<TFilter, TBlock
|
|
125
|
-
const indexer: Indexer<TFilter, TBlock
|
|
129
|
+
}: IndexerWithStreamConfig<TFilter, TBlock>) {
|
|
130
|
+
const indexer: Indexer<TFilter, TBlock> = {
|
|
126
131
|
options,
|
|
127
132
|
streamConfig,
|
|
128
133
|
hooks: createHooks<IndexerHooks<TFilter, TBlock>>(),
|
|
@@ -141,29 +146,101 @@ export function createIndexer<TFilter, TBlock, TTxnParams>({
|
|
|
141
146
|
return indexer;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
|
-
export
|
|
149
|
+
export interface ReconnectOptions {
|
|
150
|
+
maxRetries?: number;
|
|
151
|
+
retryDelay?: number;
|
|
152
|
+
maxWait?: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function runWithReconnect<TFilter, TBlock>(
|
|
145
156
|
client: Client<TFilter, TBlock>,
|
|
146
|
-
indexer: Indexer<TFilter, TBlock
|
|
157
|
+
indexer: Indexer<TFilter, TBlock>,
|
|
158
|
+
options: ReconnectOptions = {},
|
|
147
159
|
) {
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
let retryCount = 0;
|
|
161
|
+
|
|
162
|
+
const maxRetries = options.maxRetries ?? 10;
|
|
163
|
+
const retryDelay = options.retryDelay ?? 1_000;
|
|
164
|
+
const maxWait = options.maxWait ?? 30_000;
|
|
165
|
+
|
|
166
|
+
const runOptions: RunOptions = {
|
|
167
|
+
onConnect() {
|
|
168
|
+
retryCount = 0;
|
|
169
|
+
},
|
|
170
|
+
};
|
|
150
171
|
|
|
151
|
-
|
|
172
|
+
while (true) {
|
|
173
|
+
try {
|
|
174
|
+
await run(client, indexer, runOptions);
|
|
175
|
+
return;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
// Only reconnect on internal/server errors.
|
|
178
|
+
// All other errors should be rethrown.
|
|
179
|
+
|
|
180
|
+
retryCount++;
|
|
181
|
+
|
|
182
|
+
if (error instanceof ClientError) {
|
|
183
|
+
if (error.code === Status.INTERNAL) {
|
|
184
|
+
if (retryCount < maxRetries) {
|
|
185
|
+
consola.error(
|
|
186
|
+
"Internal server error, reconnecting...",
|
|
187
|
+
error.message,
|
|
188
|
+
);
|
|
152
189
|
|
|
153
|
-
|
|
190
|
+
// Add jitter to the retry delay to avoid all clients retrying at the same time.
|
|
191
|
+
const delay = Math.random() * (retryDelay * 0.2) + retryDelay;
|
|
192
|
+
await new Promise((resolve) =>
|
|
193
|
+
setTimeout(resolve, Math.min(retryCount * delay, maxWait)),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface RunOptions {
|
|
207
|
+
onConnect?: () => void | Promise<void>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function run<TFilter, TBlock>(
|
|
211
|
+
client: Client<TFilter, TBlock>,
|
|
212
|
+
indexer: Indexer<TFilter, TBlock>,
|
|
213
|
+
runOptions: RunOptions = {},
|
|
214
|
+
) {
|
|
215
|
+
await indexerAsyncContext.callAsync({}, async () => {
|
|
216
|
+
const context = useIndexerContext();
|
|
217
|
+
const middleware = await registerMiddleware(indexer);
|
|
154
218
|
|
|
155
219
|
await indexer.hooks.callHook("run:before");
|
|
156
220
|
|
|
157
|
-
// Check if the it's factory mode or not
|
|
158
221
|
const isFactoryMode = indexer.options.factory !== undefined;
|
|
159
222
|
|
|
160
|
-
//
|
|
223
|
+
// Give priority to startingCursor over startingBlock.
|
|
224
|
+
let startingCursor: Cursor | undefined;
|
|
225
|
+
if (indexer.options.startingCursor) {
|
|
226
|
+
startingCursor = indexer.options.startingCursor;
|
|
227
|
+
} else if (indexer.options.startingBlock !== undefined) {
|
|
228
|
+
if (indexer.options.startingBlock === 0n) {
|
|
229
|
+
startingCursor = undefined;
|
|
230
|
+
} else if (indexer.options.startingBlock > 0n) {
|
|
231
|
+
startingCursor = {
|
|
232
|
+
orderKey: indexer.options.startingBlock - 1n,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// if factory mode we add a empty filter at the end of the filter array.
|
|
161
238
|
const request = indexer.streamConfig.Request.make({
|
|
162
239
|
filter: isFactoryMode
|
|
163
240
|
? [indexer.options.filter, {} as TFilter]
|
|
164
241
|
: [indexer.options.filter],
|
|
165
242
|
finality: indexer.options.finality,
|
|
166
|
-
startingCursor
|
|
243
|
+
startingCursor,
|
|
167
244
|
});
|
|
168
245
|
|
|
169
246
|
const options: StreamDataOptions = {};
|
|
@@ -176,13 +253,14 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
176
253
|
mainFilter = request.filter[1];
|
|
177
254
|
}
|
|
178
255
|
|
|
179
|
-
// create stream
|
|
180
256
|
let stream: AsyncIterator<
|
|
181
257
|
StreamDataResponse<TBlock>,
|
|
182
258
|
StreamDataResponse<TBlock>
|
|
183
259
|
> = client.streamData(request, options)[Symbol.asyncIterator]();
|
|
184
260
|
|
|
185
|
-
await indexer.hooks.callHook("connect:after");
|
|
261
|
+
await indexer.hooks.callHook("connect:after", { request });
|
|
262
|
+
|
|
263
|
+
let onConnectCalled = false;
|
|
186
264
|
|
|
187
265
|
while (true) {
|
|
188
266
|
const { value: message, done } = await stream.next();
|
|
@@ -191,6 +269,13 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
191
269
|
break;
|
|
192
270
|
}
|
|
193
271
|
|
|
272
|
+
if (!onConnectCalled) {
|
|
273
|
+
onConnectCalled = true;
|
|
274
|
+
if (runOptions.onConnect) {
|
|
275
|
+
await runOptions.onConnect();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
194
279
|
await indexer.hooks.callHook("message", { message });
|
|
195
280
|
|
|
196
281
|
switch (message._tag) {
|
|
@@ -199,117 +284,138 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
199
284
|
const blocks = message.data.data;
|
|
200
285
|
const { cursor, endCursor, finality } = message.data;
|
|
201
286
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// attach transaction to context
|
|
206
|
-
context.sinkTransaction = txn as TTxnParams;
|
|
287
|
+
context.cursor = cursor;
|
|
288
|
+
context.endCursor = endCursor;
|
|
289
|
+
context.finality = finality;
|
|
207
290
|
|
|
208
|
-
|
|
291
|
+
await middleware(context, async () => {
|
|
292
|
+
let block: TBlock | null;
|
|
209
293
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
294
|
+
// when factory mode
|
|
295
|
+
if (isFactoryMode) {
|
|
296
|
+
assert(indexer.options.factory !== undefined);
|
|
213
297
|
|
|
214
|
-
|
|
298
|
+
const [factoryBlock, mainBlock] = blocks;
|
|
215
299
|
|
|
216
|
-
|
|
300
|
+
block = mainBlock;
|
|
217
301
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// write returned data from factory function if filter is not defined
|
|
225
|
-
if (filter) {
|
|
226
|
-
// when filter is defined
|
|
227
|
-
// merge old and new filters
|
|
228
|
-
mainFilter = indexer.streamConfig.mergeFilter(
|
|
229
|
-
mainFilter,
|
|
230
|
-
filter,
|
|
231
|
-
);
|
|
302
|
+
if (factoryBlock !== null) {
|
|
303
|
+
const { filter } = await indexer.options.factory({
|
|
304
|
+
block: factoryBlock,
|
|
305
|
+
context,
|
|
306
|
+
});
|
|
232
307
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
308
|
+
// write returned data from factory function if filter is not defined
|
|
309
|
+
if (filter) {
|
|
310
|
+
// when filter is defined
|
|
311
|
+
// merge old and new filters
|
|
312
|
+
mainFilter = indexer.streamConfig.mergeFilter(
|
|
313
|
+
mainFilter,
|
|
314
|
+
filter,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// create request with new filters
|
|
318
|
+
const request = indexer.streamConfig.Request.make({
|
|
319
|
+
filter: [indexer.options.filter, mainFilter],
|
|
320
|
+
finality: indexer.options.finality,
|
|
321
|
+
startingCursor: cursor,
|
|
322
|
+
});
|
|
239
323
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
324
|
+
await indexer.hooks.callHook("connect:factory", {
|
|
325
|
+
request,
|
|
326
|
+
endCursor,
|
|
327
|
+
});
|
|
244
328
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
329
|
+
// create new stream with new request
|
|
330
|
+
stream = client
|
|
331
|
+
.streamData(request, options)
|
|
332
|
+
[Symbol.asyncIterator]();
|
|
249
333
|
|
|
250
|
-
|
|
334
|
+
const { value: message } = await stream.next();
|
|
251
335
|
|
|
252
|
-
|
|
336
|
+
assert(message._tag === "data");
|
|
253
337
|
|
|
254
|
-
|
|
338
|
+
const [_factoryBlock, _block] = message.data.data;
|
|
255
339
|
|
|
256
|
-
|
|
257
|
-
}
|
|
340
|
+
block = _block;
|
|
258
341
|
}
|
|
259
|
-
} else {
|
|
260
|
-
// when not in factory mode
|
|
261
|
-
block = blocks[0];
|
|
262
342
|
}
|
|
263
|
-
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
endCursor,
|
|
278
|
-
finality,
|
|
279
|
-
context,
|
|
280
|
-
});
|
|
281
|
-
await indexer.hooks.callHook("handler:after", {
|
|
282
|
-
block,
|
|
283
|
-
finality,
|
|
284
|
-
endCursor,
|
|
285
|
-
});
|
|
286
|
-
} catch (error) {
|
|
287
|
-
assert(error instanceof Error);
|
|
288
|
-
await indexer.hooks.callHook("handler:exception", {
|
|
289
|
-
error,
|
|
290
|
-
});
|
|
291
|
-
throw error;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
span.end();
|
|
343
|
+
} else {
|
|
344
|
+
// when not in factory mode
|
|
345
|
+
block = blocks[0];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// if block is not null
|
|
349
|
+
if (block) {
|
|
350
|
+
await tracer.startActiveSpan("handler", async (span) => {
|
|
351
|
+
await indexer.options.transform({
|
|
352
|
+
block,
|
|
353
|
+
cursor,
|
|
354
|
+
endCursor,
|
|
355
|
+
finality,
|
|
356
|
+
context,
|
|
295
357
|
});
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
finality,
|
|
301
|
-
endCursor,
|
|
358
|
+
|
|
359
|
+
span.end();
|
|
360
|
+
});
|
|
361
|
+
}
|
|
302
362
|
});
|
|
363
|
+
|
|
303
364
|
span.end();
|
|
304
365
|
});
|
|
366
|
+
|
|
367
|
+
context.cursor = undefined;
|
|
368
|
+
context.endCursor = undefined;
|
|
369
|
+
context.finality = undefined;
|
|
370
|
+
|
|
305
371
|
break;
|
|
306
372
|
}
|
|
307
373
|
case "invalidate": {
|
|
308
374
|
await tracer.startActiveSpan("message invalidate", async (span) => {
|
|
309
|
-
await
|
|
375
|
+
await indexer.hooks.callHook("message:invalidate", { message });
|
|
376
|
+
span.end();
|
|
377
|
+
});
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case "finalize": {
|
|
381
|
+
await tracer.startActiveSpan("message finalize", async (span) => {
|
|
382
|
+
await indexer.hooks.callHook("message:finalize", { message });
|
|
383
|
+
span.end();
|
|
384
|
+
});
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case "heartbeat": {
|
|
388
|
+
await tracer.startActiveSpan("message heartbeat", async (span) => {
|
|
389
|
+
await indexer.hooks.callHook("message:heartbeat", { message });
|
|
390
|
+
span.end();
|
|
310
391
|
});
|
|
311
392
|
break;
|
|
312
393
|
}
|
|
394
|
+
case "systemMessage": {
|
|
395
|
+
await tracer.startActiveSpan(
|
|
396
|
+
"message systemMessage",
|
|
397
|
+
async (span) => {
|
|
398
|
+
switch (message.systemMessage.output?._tag) {
|
|
399
|
+
case "stderr": {
|
|
400
|
+
consola.warn(message.systemMessage.output.stderr);
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
case "stdout": {
|
|
404
|
+
consola.info(message.systemMessage.output.stdout);
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
default: {
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
await indexer.hooks.callHook("message:systemMessage", {
|
|
412
|
+
message,
|
|
413
|
+
});
|
|
414
|
+
span.end();
|
|
415
|
+
},
|
|
416
|
+
);
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
313
419
|
default: {
|
|
314
420
|
consola.warn("unexpected message", message);
|
|
315
421
|
throw new Error("not implemented");
|
|
@@ -320,3 +426,24 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
320
426
|
}
|
|
321
427
|
});
|
|
322
428
|
}
|
|
429
|
+
|
|
430
|
+
async function registerMiddleware<TFilter, TBlock>(
|
|
431
|
+
indexer: Indexer<TFilter, TBlock>,
|
|
432
|
+
): Promise<MiddlewareFunction<IndexerContext>> {
|
|
433
|
+
const middleware: MiddlewareFunction<IndexerContext>[] = [];
|
|
434
|
+
const use = (fn: MiddlewareFunction<IndexerContext>) => {
|
|
435
|
+
middleware.push(fn);
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
await indexer.hooks.callHook("handler:middleware", { use });
|
|
439
|
+
|
|
440
|
+
const composed = compose(middleware);
|
|
441
|
+
|
|
442
|
+
// Return a named function to help debugging
|
|
443
|
+
return async function _composedIndexerMiddleware(
|
|
444
|
+
context: IndexerContext,
|
|
445
|
+
next?: NextFunction,
|
|
446
|
+
) {
|
|
447
|
+
await composed(context, next);
|
|
448
|
+
};
|
|
449
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../plugins/context";
|