@apibara/indexer 2.0.0-beta.4 → 2.0.0-beta.40
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 +278 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +267 -0
- 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 +43 -0
- package/dist/plugins/index.d.cts +18 -0
- package/dist/plugins/index.d.mts +18 -0
- package/dist/plugins/index.d.ts +18 -0
- package/dist/plugins/index.mjs +38 -0
- 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 +66 -0
- package/dist/testing/index.d.cts +12 -0
- package/dist/testing/index.d.mts +12 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.mjs +60 -0
- package/dist/vcr/index.cjs +92 -0
- package/dist/vcr/index.d.cts +27 -0
- package/dist/vcr/index.d.mts +27 -0
- package/dist/vcr/index.d.ts +27 -0
- package/dist/vcr/index.mjs +78 -0
- package/package.json +43 -41
- 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 +274 -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 +6 -4
- package/src/vcr/replay.ts +8 -18
- 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 -39
- 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 -36
- 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,97 @@ 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
|
+
startingCursor = {
|
|
229
|
+
orderKey: indexer.options.startingBlock,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// if factory mode we add a empty filter at the end of the filter array.
|
|
161
234
|
const request = indexer.streamConfig.Request.make({
|
|
162
235
|
filter: isFactoryMode
|
|
163
236
|
? [indexer.options.filter, {} as TFilter]
|
|
164
237
|
: [indexer.options.filter],
|
|
165
238
|
finality: indexer.options.finality,
|
|
166
|
-
startingCursor
|
|
239
|
+
startingCursor,
|
|
167
240
|
});
|
|
168
241
|
|
|
169
242
|
const options: StreamDataOptions = {};
|
|
@@ -176,13 +249,14 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
176
249
|
mainFilter = request.filter[1];
|
|
177
250
|
}
|
|
178
251
|
|
|
179
|
-
// create stream
|
|
180
252
|
let stream: AsyncIterator<
|
|
181
253
|
StreamDataResponse<TBlock>,
|
|
182
254
|
StreamDataResponse<TBlock>
|
|
183
255
|
> = client.streamData(request, options)[Symbol.asyncIterator]();
|
|
184
256
|
|
|
185
|
-
await indexer.hooks.callHook("connect:after");
|
|
257
|
+
await indexer.hooks.callHook("connect:after", { request });
|
|
258
|
+
|
|
259
|
+
let onConnectCalled = false;
|
|
186
260
|
|
|
187
261
|
while (true) {
|
|
188
262
|
const { value: message, done } = await stream.next();
|
|
@@ -191,6 +265,13 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
191
265
|
break;
|
|
192
266
|
}
|
|
193
267
|
|
|
268
|
+
if (!onConnectCalled) {
|
|
269
|
+
onConnectCalled = true;
|
|
270
|
+
if (runOptions.onConnect) {
|
|
271
|
+
await runOptions.onConnect();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
194
275
|
await indexer.hooks.callHook("message", { message });
|
|
195
276
|
|
|
196
277
|
switch (message._tag) {
|
|
@@ -199,117 +280,138 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
199
280
|
const blocks = message.data.data;
|
|
200
281
|
const { cursor, endCursor, finality } = message.data;
|
|
201
282
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// attach transaction to context
|
|
206
|
-
context.sinkTransaction = txn as TTxnParams;
|
|
207
|
-
|
|
208
|
-
let block: TBlock | null;
|
|
283
|
+
context.cursor = cursor;
|
|
284
|
+
context.endCursor = endCursor;
|
|
285
|
+
context.finality = finality;
|
|
209
286
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
assert(indexer.options.factory !== undefined);
|
|
287
|
+
await middleware(context, async () => {
|
|
288
|
+
let block: TBlock | null;
|
|
213
289
|
|
|
214
|
-
|
|
290
|
+
// when factory mode
|
|
291
|
+
if (isFactoryMode) {
|
|
292
|
+
assert(indexer.options.factory !== undefined);
|
|
215
293
|
|
|
216
|
-
|
|
294
|
+
const [factoryBlock, mainBlock] = blocks;
|
|
217
295
|
|
|
218
|
-
|
|
219
|
-
const { filter } = await indexer.options.factory({
|
|
220
|
-
block: factoryBlock,
|
|
221
|
-
context,
|
|
222
|
-
});
|
|
296
|
+
block = mainBlock;
|
|
223
297
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
mainFilter,
|
|
230
|
-
filter,
|
|
231
|
-
);
|
|
298
|
+
if (factoryBlock !== null) {
|
|
299
|
+
const { filter } = await indexer.options.factory({
|
|
300
|
+
block: factoryBlock,
|
|
301
|
+
context,
|
|
302
|
+
});
|
|
232
303
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
304
|
+
// write returned data from factory function if filter is not defined
|
|
305
|
+
if (filter) {
|
|
306
|
+
// when filter is defined
|
|
307
|
+
// merge old and new filters
|
|
308
|
+
mainFilter = indexer.streamConfig.mergeFilter(
|
|
309
|
+
mainFilter,
|
|
310
|
+
filter,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// create request with new filters
|
|
314
|
+
const request = indexer.streamConfig.Request.make({
|
|
315
|
+
filter: [indexer.options.filter, mainFilter],
|
|
316
|
+
finality: indexer.options.finality,
|
|
317
|
+
startingCursor: cursor,
|
|
318
|
+
});
|
|
239
319
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
320
|
+
await indexer.hooks.callHook("connect:factory", {
|
|
321
|
+
request,
|
|
322
|
+
endCursor,
|
|
323
|
+
});
|
|
244
324
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
325
|
+
// create new stream with new request
|
|
326
|
+
stream = client
|
|
327
|
+
.streamData(request, options)
|
|
328
|
+
[Symbol.asyncIterator]();
|
|
249
329
|
|
|
250
|
-
|
|
330
|
+
const { value: message } = await stream.next();
|
|
251
331
|
|
|
252
|
-
|
|
332
|
+
assert(message._tag === "data");
|
|
253
333
|
|
|
254
|
-
|
|
334
|
+
const [_factoryBlock, _block] = message.data.data;
|
|
255
335
|
|
|
256
|
-
|
|
257
|
-
}
|
|
336
|
+
block = _block;
|
|
258
337
|
}
|
|
259
|
-
} else {
|
|
260
|
-
// when not in factory mode
|
|
261
|
-
block = blocks[0];
|
|
262
338
|
}
|
|
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();
|
|
339
|
+
} else {
|
|
340
|
+
// when not in factory mode
|
|
341
|
+
block = blocks[0];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// if block is not null
|
|
345
|
+
if (block) {
|
|
346
|
+
await tracer.startActiveSpan("handler", async (span) => {
|
|
347
|
+
await indexer.options.transform({
|
|
348
|
+
block,
|
|
349
|
+
cursor,
|
|
350
|
+
endCursor,
|
|
351
|
+
finality,
|
|
352
|
+
context,
|
|
295
353
|
});
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
finality,
|
|
301
|
-
endCursor,
|
|
354
|
+
|
|
355
|
+
span.end();
|
|
356
|
+
});
|
|
357
|
+
}
|
|
302
358
|
});
|
|
359
|
+
|
|
303
360
|
span.end();
|
|
304
361
|
});
|
|
362
|
+
|
|
363
|
+
context.cursor = undefined;
|
|
364
|
+
context.endCursor = undefined;
|
|
365
|
+
context.finality = undefined;
|
|
366
|
+
|
|
305
367
|
break;
|
|
306
368
|
}
|
|
307
369
|
case "invalidate": {
|
|
308
370
|
await tracer.startActiveSpan("message invalidate", async (span) => {
|
|
309
|
-
await
|
|
371
|
+
await indexer.hooks.callHook("message:invalidate", { message });
|
|
372
|
+
span.end();
|
|
373
|
+
});
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
case "finalize": {
|
|
377
|
+
await tracer.startActiveSpan("message finalize", async (span) => {
|
|
378
|
+
await indexer.hooks.callHook("message:finalize", { message });
|
|
379
|
+
span.end();
|
|
310
380
|
});
|
|
311
381
|
break;
|
|
312
382
|
}
|
|
383
|
+
case "heartbeat": {
|
|
384
|
+
await tracer.startActiveSpan("message heartbeat", async (span) => {
|
|
385
|
+
await indexer.hooks.callHook("message:heartbeat", { message });
|
|
386
|
+
span.end();
|
|
387
|
+
});
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
case "systemMessage": {
|
|
391
|
+
await tracer.startActiveSpan(
|
|
392
|
+
"message systemMessage",
|
|
393
|
+
async (span) => {
|
|
394
|
+
switch (message.systemMessage.output?._tag) {
|
|
395
|
+
case "stderr": {
|
|
396
|
+
consola.warn(message.systemMessage.output.stderr);
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case "stdout": {
|
|
400
|
+
consola.info(message.systemMessage.output.stdout);
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
default: {
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
await indexer.hooks.callHook("message:systemMessage", {
|
|
408
|
+
message,
|
|
409
|
+
});
|
|
410
|
+
span.end();
|
|
411
|
+
},
|
|
412
|
+
);
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
313
415
|
default: {
|
|
314
416
|
consola.warn("unexpected message", message);
|
|
315
417
|
throw new Error("not implemented");
|
|
@@ -320,3 +422,24 @@ export async function run<TFilter, TBlock, TTxnParams>(
|
|
|
320
422
|
}
|
|
321
423
|
});
|
|
322
424
|
}
|
|
425
|
+
|
|
426
|
+
async function registerMiddleware<TFilter, TBlock>(
|
|
427
|
+
indexer: Indexer<TFilter, TBlock>,
|
|
428
|
+
): Promise<MiddlewareFunction<IndexerContext>> {
|
|
429
|
+
const middleware: MiddlewareFunction<IndexerContext>[] = [];
|
|
430
|
+
const use = (fn: MiddlewareFunction<IndexerContext>) => {
|
|
431
|
+
middleware.push(fn);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
await indexer.hooks.callHook("handler:middleware", { use });
|
|
435
|
+
|
|
436
|
+
const composed = compose(middleware);
|
|
437
|
+
|
|
438
|
+
// Return a named function to help debugging
|
|
439
|
+
return async function _composedIndexerMiddleware(
|
|
440
|
+
context: IndexerContext,
|
|
441
|
+
next?: NextFunction,
|
|
442
|
+
) {
|
|
443
|
+
await composed(context, next);
|
|
444
|
+
};
|
|
445
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../plugins/context";
|