@apibara/indexer 2.0.0-beta.3 → 2.0.0-beta.30

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.
Files changed (74) hide show
  1. package/dist/index.cjs +270 -0
  2. package/dist/index.d.cts +3 -0
  3. package/dist/index.d.mts +3 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.mjs +259 -0
  6. package/dist/internal/testing.cjs +109 -0
  7. package/dist/internal/testing.d.cts +40 -0
  8. package/dist/internal/testing.d.mts +40 -0
  9. package/dist/internal/testing.d.ts +40 -0
  10. package/dist/internal/testing.mjs +104 -0
  11. package/dist/plugins/index.cjs +43 -0
  12. package/dist/plugins/index.d.cts +18 -0
  13. package/dist/plugins/index.d.mts +18 -0
  14. package/dist/plugins/index.d.ts +18 -0
  15. package/dist/plugins/index.mjs +38 -0
  16. package/dist/shared/indexer.077335f3.cjs +15 -0
  17. package/dist/shared/indexer.2416906c.cjs +29 -0
  18. package/dist/shared/indexer.601ceab0.cjs +7 -0
  19. package/dist/shared/indexer.8939ecc8.d.cts +91 -0
  20. package/dist/shared/indexer.8939ecc8.d.mts +91 -0
  21. package/dist/shared/indexer.8939ecc8.d.ts +91 -0
  22. package/dist/shared/indexer.9b21ddd2.mjs +5 -0
  23. package/dist/shared/indexer.a55ad619.mjs +12 -0
  24. package/dist/shared/indexer.ff25c953.mjs +26 -0
  25. package/dist/testing/index.cjs +58 -0
  26. package/dist/testing/index.d.cts +12 -0
  27. package/dist/testing/index.d.mts +12 -0
  28. package/dist/testing/index.d.ts +12 -0
  29. package/dist/testing/index.mjs +52 -0
  30. package/dist/vcr/index.cjs +92 -0
  31. package/dist/vcr/index.d.cts +27 -0
  32. package/dist/vcr/index.d.mts +27 -0
  33. package/dist/vcr/index.d.ts +27 -0
  34. package/dist/vcr/index.mjs +78 -0
  35. package/package.json +31 -41
  36. package/src/compose.test.ts +76 -0
  37. package/src/compose.ts +71 -0
  38. package/src/context.ts +14 -8
  39. package/src/index.ts +0 -5
  40. package/src/indexer.test.ts +109 -186
  41. package/src/indexer.ts +244 -144
  42. package/src/internal/testing.ts +135 -0
  43. package/src/plugins/config.ts +4 -4
  44. package/src/plugins/index.ts +8 -1
  45. package/src/plugins/logger.ts +30 -0
  46. package/src/plugins/persistence.ts +24 -187
  47. package/src/testing/index.ts +50 -3
  48. package/src/vcr/record.ts +6 -4
  49. package/src/vcr/replay.ts +8 -18
  50. package/src/hooks/index.ts +0 -2
  51. package/src/hooks/useKVStore.ts +0 -12
  52. package/src/hooks/useSink.ts +0 -13
  53. package/src/plugins/kv.test.ts +0 -120
  54. package/src/plugins/kv.ts +0 -132
  55. package/src/plugins/persistence.test.ts +0 -151
  56. package/src/sink.ts +0 -36
  57. package/src/sinks/csv.test.ts +0 -65
  58. package/src/sinks/csv.ts +0 -159
  59. package/src/sinks/drizzle/Int8Range.ts +0 -52
  60. package/src/sinks/drizzle/delete.ts +0 -42
  61. package/src/sinks/drizzle/drizzle.test.ts +0 -239
  62. package/src/sinks/drizzle/drizzle.ts +0 -115
  63. package/src/sinks/drizzle/index.ts +0 -6
  64. package/src/sinks/drizzle/insert.ts +0 -39
  65. package/src/sinks/drizzle/select.ts +0 -44
  66. package/src/sinks/drizzle/transaction.ts +0 -49
  67. package/src/sinks/drizzle/update.ts +0 -47
  68. package/src/sinks/drizzle/utils.ts +0 -36
  69. package/src/sinks/sqlite.test.ts +0 -99
  70. package/src/sinks/sqlite.ts +0 -170
  71. package/src/testing/helper.ts +0 -13
  72. package/src/testing/indexer.ts +0 -35
  73. package/src/testing/setup.ts +0 -59
  74. package/src/testing/vcr.ts +0 -54
package/dist/index.cjs ADDED
@@ -0,0 +1,270 @@
1
+ 'use strict';
2
+
3
+ const protocol = require('@apibara/protocol');
4
+ const consola = require('consola');
5
+ const hookable = require('hookable');
6
+ const assert = require('node:assert');
7
+ const context = require('./shared/indexer.077335f3.cjs');
8
+ const api = require('@opentelemetry/api');
9
+ require('node:async_hooks');
10
+ require('unctx');
11
+
12
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
13
+
14
+ const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
15
+ const assert__default = /*#__PURE__*/_interopDefaultCompat(assert);
16
+
17
+ function compose(middleware) {
18
+ return (context, next) => {
19
+ let index = -1;
20
+ return dispatch(0);
21
+ async function dispatch(i) {
22
+ if (i <= index) {
23
+ throw new Error("next() called multiple times");
24
+ }
25
+ index = i;
26
+ let handler;
27
+ if (i >= middleware.length) {
28
+ if (next) {
29
+ await next();
30
+ }
31
+ return;
32
+ }
33
+ if (middleware[i]) {
34
+ handler = middleware[i];
35
+ } else {
36
+ handler = i === middleware.length ? next : void 0;
37
+ }
38
+ if (!handler) {
39
+ throw new Error("Handler not found");
40
+ }
41
+ await handler(context, () => dispatch(i + 1));
42
+ }
43
+ };
44
+ }
45
+
46
+ const tracer = api.trace.getTracer("@apibara/indexer");
47
+
48
+ function defineIndexer(streamConfig) {
49
+ return (config) => ({
50
+ streamConfig,
51
+ ...config
52
+ });
53
+ }
54
+ function createIndexer({
55
+ streamConfig,
56
+ ...options
57
+ }) {
58
+ const indexer = {
59
+ options,
60
+ streamConfig,
61
+ hooks: hookable.createHooks()
62
+ };
63
+ if (indexer.options.debug) {
64
+ hookable.createDebugger(indexer.hooks, { tag: "indexer" });
65
+ }
66
+ indexer.hooks.addHooks(indexer.options.hooks ?? {});
67
+ for (const plugin of indexer.options.plugins ?? []) {
68
+ plugin(indexer);
69
+ }
70
+ return indexer;
71
+ }
72
+ async function runWithReconnect(client, indexer, options = {}) {
73
+ let retryCount = 0;
74
+ const maxRetries = options.maxRetries ?? 10;
75
+ const retryDelay = options.retryDelay ?? 1e3;
76
+ const maxWait = options.maxWait ?? 3e4;
77
+ const runOptions = {
78
+ onConnect() {
79
+ retryCount = 0;
80
+ }
81
+ };
82
+ while (true) {
83
+ try {
84
+ await run(client, indexer, runOptions);
85
+ return;
86
+ } catch (error) {
87
+ retryCount++;
88
+ if (error instanceof protocol.ClientError) {
89
+ if (error.code === protocol.Status.INTERNAL) {
90
+ if (retryCount < maxRetries) {
91
+ consola__default.error(
92
+ "Internal server error, reconnecting...",
93
+ error.message
94
+ );
95
+ const delay = Math.random() * (retryDelay * 0.2) + retryDelay;
96
+ await new Promise(
97
+ (resolve) => setTimeout(resolve, Math.min(retryCount * delay, maxWait))
98
+ );
99
+ continue;
100
+ }
101
+ }
102
+ }
103
+ throw error;
104
+ }
105
+ }
106
+ }
107
+ async function run(client, indexer, runOptions = {}) {
108
+ await context.indexerAsyncContext.callAsync({}, async () => {
109
+ const context$1 = context.useIndexerContext();
110
+ const middleware = await registerMiddleware(indexer);
111
+ await indexer.hooks.callHook("run:before");
112
+ const isFactoryMode = indexer.options.factory !== void 0;
113
+ const request = indexer.streamConfig.Request.make({
114
+ filter: isFactoryMode ? [indexer.options.filter, {}] : [indexer.options.filter],
115
+ finality: indexer.options.finality,
116
+ startingCursor: indexer.options.startingCursor
117
+ });
118
+ const options = {};
119
+ await indexer.hooks.callHook("connect:before", { request, options });
120
+ let mainFilter;
121
+ if (isFactoryMode) {
122
+ mainFilter = request.filter[1];
123
+ }
124
+ let stream = client.streamData(request, options)[Symbol.asyncIterator]();
125
+ await indexer.hooks.callHook("connect:after", { request });
126
+ let onConnectCalled = false;
127
+ while (true) {
128
+ const { value: message, done } = await stream.next();
129
+ if (done) {
130
+ break;
131
+ }
132
+ if (!onConnectCalled) {
133
+ onConnectCalled = true;
134
+ if (runOptions.onConnect) {
135
+ await runOptions.onConnect();
136
+ }
137
+ }
138
+ await indexer.hooks.callHook("message", { message });
139
+ switch (message._tag) {
140
+ case "data": {
141
+ await tracer.startActiveSpan("message data", async (span) => {
142
+ const blocks = message.data.data;
143
+ const { cursor, endCursor, finality } = message.data;
144
+ context$1.cursor = cursor;
145
+ context$1.endCursor = endCursor;
146
+ context$1.finality = finality;
147
+ await middleware(context$1, async () => {
148
+ let block;
149
+ if (isFactoryMode) {
150
+ assert__default(indexer.options.factory !== void 0);
151
+ const [factoryBlock, mainBlock] = blocks;
152
+ block = mainBlock;
153
+ if (factoryBlock !== null) {
154
+ const { filter } = await indexer.options.factory({
155
+ block: factoryBlock,
156
+ context: context$1
157
+ });
158
+ if (filter) {
159
+ mainFilter = indexer.streamConfig.mergeFilter(
160
+ mainFilter,
161
+ filter
162
+ );
163
+ const request2 = indexer.streamConfig.Request.make({
164
+ filter: [indexer.options.filter, mainFilter],
165
+ finality: indexer.options.finality,
166
+ startingCursor: cursor
167
+ });
168
+ await indexer.hooks.callHook("connect:factory", {
169
+ request: request2,
170
+ endCursor
171
+ });
172
+ stream = client.streamData(request2, options)[Symbol.asyncIterator]();
173
+ const { value: message2 } = await stream.next();
174
+ assert__default(message2._tag === "data");
175
+ const [_factoryBlock, _block] = message2.data.data;
176
+ block = _block;
177
+ }
178
+ }
179
+ } else {
180
+ block = blocks[0];
181
+ }
182
+ if (block) {
183
+ await tracer.startActiveSpan("handler", async (span2) => {
184
+ await indexer.options.transform({
185
+ block,
186
+ cursor,
187
+ endCursor,
188
+ finality,
189
+ context: context$1
190
+ });
191
+ span2.end();
192
+ });
193
+ }
194
+ });
195
+ span.end();
196
+ });
197
+ context$1.cursor = void 0;
198
+ context$1.endCursor = void 0;
199
+ context$1.finality = void 0;
200
+ break;
201
+ }
202
+ case "invalidate": {
203
+ await tracer.startActiveSpan("message invalidate", async (span) => {
204
+ await indexer.hooks.callHook("message:invalidate", { message });
205
+ span.end();
206
+ });
207
+ break;
208
+ }
209
+ case "finalize": {
210
+ await tracer.startActiveSpan("message finalize", async (span) => {
211
+ await indexer.hooks.callHook("message:finalize", { message });
212
+ span.end();
213
+ });
214
+ break;
215
+ }
216
+ case "heartbeat": {
217
+ await tracer.startActiveSpan("message heartbeat", async (span) => {
218
+ await indexer.hooks.callHook("message:heartbeat", { message });
219
+ span.end();
220
+ });
221
+ break;
222
+ }
223
+ case "systemMessage": {
224
+ await tracer.startActiveSpan(
225
+ "message systemMessage",
226
+ async (span) => {
227
+ switch (message.systemMessage.output?._tag) {
228
+ case "stderr": {
229
+ consola__default.warn(message.systemMessage.output.stderr);
230
+ break;
231
+ }
232
+ case "stdout": {
233
+ consola__default.info(message.systemMessage.output.stdout);
234
+ break;
235
+ }
236
+ }
237
+ await indexer.hooks.callHook("message:systemMessage", {
238
+ message
239
+ });
240
+ span.end();
241
+ }
242
+ );
243
+ break;
244
+ }
245
+ default: {
246
+ consola__default.warn("unexpected message", message);
247
+ throw new Error("not implemented");
248
+ }
249
+ }
250
+ await indexer.hooks.callHook("run:after");
251
+ }
252
+ });
253
+ }
254
+ async function registerMiddleware(indexer) {
255
+ const middleware = [];
256
+ const use = (fn) => {
257
+ middleware.push(fn);
258
+ };
259
+ await indexer.hooks.callHook("handler:middleware", { use });
260
+ const composed = compose(middleware);
261
+ return async function _composedIndexerMiddleware(context, next) {
262
+ await composed(context, next);
263
+ };
264
+ }
265
+
266
+ exports.useIndexerContext = context.useIndexerContext;
267
+ exports.createIndexer = createIndexer;
268
+ exports.defineIndexer = defineIndexer;
269
+ exports.run = run;
270
+ exports.runWithReconnect = runWithReconnect;
@@ -0,0 +1,3 @@
1
+ export { c as Indexer, b as IndexerConfig, e as IndexerHooks, I as IndexerWithStreamConfig, R as ReconnectOptions, h as RunOptions, U as UseMiddlewareFunction, g as createIndexer, f as defineIndexer, i as run, r as runWithReconnect, u as useIndexerContext } from './shared/indexer.8939ecc8.cjs';
2
+ import '@apibara/protocol';
3
+ import 'hookable';
@@ -0,0 +1,3 @@
1
+ export { c as Indexer, b as IndexerConfig, e as IndexerHooks, I as IndexerWithStreamConfig, R as ReconnectOptions, h as RunOptions, U as UseMiddlewareFunction, g as createIndexer, f as defineIndexer, i as run, r as runWithReconnect, u as useIndexerContext } from './shared/indexer.8939ecc8.mjs';
2
+ import '@apibara/protocol';
3
+ import 'hookable';
@@ -0,0 +1,3 @@
1
+ export { c as Indexer, b as IndexerConfig, e as IndexerHooks, I as IndexerWithStreamConfig, R as ReconnectOptions, h as RunOptions, U as UseMiddlewareFunction, g as createIndexer, f as defineIndexer, i as run, r as runWithReconnect, u as useIndexerContext } from './shared/indexer.8939ecc8.js';
2
+ import '@apibara/protocol';
3
+ import 'hookable';
package/dist/index.mjs ADDED
@@ -0,0 +1,259 @@
1
+ import { ClientError, Status } from '@apibara/protocol';
2
+ import consola from 'consola';
3
+ import { createHooks, createDebugger } from 'hookable';
4
+ import assert from 'node:assert';
5
+ import { i as indexerAsyncContext, u as useIndexerContext } from './shared/indexer.a55ad619.mjs';
6
+ import { trace } from '@opentelemetry/api';
7
+ import 'node:async_hooks';
8
+ import 'unctx';
9
+
10
+ function compose(middleware) {
11
+ return (context, next) => {
12
+ let index = -1;
13
+ return dispatch(0);
14
+ async function dispatch(i) {
15
+ if (i <= index) {
16
+ throw new Error("next() called multiple times");
17
+ }
18
+ index = i;
19
+ let handler;
20
+ if (i >= middleware.length) {
21
+ if (next) {
22
+ await next();
23
+ }
24
+ return;
25
+ }
26
+ if (middleware[i]) {
27
+ handler = middleware[i];
28
+ } else {
29
+ handler = i === middleware.length ? next : void 0;
30
+ }
31
+ if (!handler) {
32
+ throw new Error("Handler not found");
33
+ }
34
+ await handler(context, () => dispatch(i + 1));
35
+ }
36
+ };
37
+ }
38
+
39
+ const tracer = trace.getTracer("@apibara/indexer");
40
+
41
+ function defineIndexer(streamConfig) {
42
+ return (config) => ({
43
+ streamConfig,
44
+ ...config
45
+ });
46
+ }
47
+ function createIndexer({
48
+ streamConfig,
49
+ ...options
50
+ }) {
51
+ const indexer = {
52
+ options,
53
+ streamConfig,
54
+ hooks: createHooks()
55
+ };
56
+ if (indexer.options.debug) {
57
+ createDebugger(indexer.hooks, { tag: "indexer" });
58
+ }
59
+ indexer.hooks.addHooks(indexer.options.hooks ?? {});
60
+ for (const plugin of indexer.options.plugins ?? []) {
61
+ plugin(indexer);
62
+ }
63
+ return indexer;
64
+ }
65
+ async function runWithReconnect(client, indexer, options = {}) {
66
+ let retryCount = 0;
67
+ const maxRetries = options.maxRetries ?? 10;
68
+ const retryDelay = options.retryDelay ?? 1e3;
69
+ const maxWait = options.maxWait ?? 3e4;
70
+ const runOptions = {
71
+ onConnect() {
72
+ retryCount = 0;
73
+ }
74
+ };
75
+ while (true) {
76
+ try {
77
+ await run(client, indexer, runOptions);
78
+ return;
79
+ } catch (error) {
80
+ retryCount++;
81
+ if (error instanceof ClientError) {
82
+ if (error.code === Status.INTERNAL) {
83
+ if (retryCount < maxRetries) {
84
+ consola.error(
85
+ "Internal server error, reconnecting...",
86
+ error.message
87
+ );
88
+ const delay = Math.random() * (retryDelay * 0.2) + retryDelay;
89
+ await new Promise(
90
+ (resolve) => setTimeout(resolve, Math.min(retryCount * delay, maxWait))
91
+ );
92
+ continue;
93
+ }
94
+ }
95
+ }
96
+ throw error;
97
+ }
98
+ }
99
+ }
100
+ async function run(client, indexer, runOptions = {}) {
101
+ await indexerAsyncContext.callAsync({}, async () => {
102
+ const context = useIndexerContext();
103
+ const middleware = await registerMiddleware(indexer);
104
+ await indexer.hooks.callHook("run:before");
105
+ const isFactoryMode = indexer.options.factory !== void 0;
106
+ const request = indexer.streamConfig.Request.make({
107
+ filter: isFactoryMode ? [indexer.options.filter, {}] : [indexer.options.filter],
108
+ finality: indexer.options.finality,
109
+ startingCursor: indexer.options.startingCursor
110
+ });
111
+ const options = {};
112
+ await indexer.hooks.callHook("connect:before", { request, options });
113
+ let mainFilter;
114
+ if (isFactoryMode) {
115
+ mainFilter = request.filter[1];
116
+ }
117
+ let stream = client.streamData(request, options)[Symbol.asyncIterator]();
118
+ await indexer.hooks.callHook("connect:after", { request });
119
+ let onConnectCalled = false;
120
+ while (true) {
121
+ const { value: message, done } = await stream.next();
122
+ if (done) {
123
+ break;
124
+ }
125
+ if (!onConnectCalled) {
126
+ onConnectCalled = true;
127
+ if (runOptions.onConnect) {
128
+ await runOptions.onConnect();
129
+ }
130
+ }
131
+ await indexer.hooks.callHook("message", { message });
132
+ switch (message._tag) {
133
+ case "data": {
134
+ await tracer.startActiveSpan("message data", async (span) => {
135
+ const blocks = message.data.data;
136
+ const { cursor, endCursor, finality } = message.data;
137
+ context.cursor = cursor;
138
+ context.endCursor = endCursor;
139
+ context.finality = finality;
140
+ await middleware(context, async () => {
141
+ let block;
142
+ if (isFactoryMode) {
143
+ assert(indexer.options.factory !== void 0);
144
+ const [factoryBlock, mainBlock] = blocks;
145
+ block = mainBlock;
146
+ if (factoryBlock !== null) {
147
+ const { filter } = await indexer.options.factory({
148
+ block: factoryBlock,
149
+ context
150
+ });
151
+ if (filter) {
152
+ mainFilter = indexer.streamConfig.mergeFilter(
153
+ mainFilter,
154
+ filter
155
+ );
156
+ const request2 = indexer.streamConfig.Request.make({
157
+ filter: [indexer.options.filter, mainFilter],
158
+ finality: indexer.options.finality,
159
+ startingCursor: cursor
160
+ });
161
+ await indexer.hooks.callHook("connect:factory", {
162
+ request: request2,
163
+ endCursor
164
+ });
165
+ stream = client.streamData(request2, options)[Symbol.asyncIterator]();
166
+ const { value: message2 } = await stream.next();
167
+ assert(message2._tag === "data");
168
+ const [_factoryBlock, _block] = message2.data.data;
169
+ block = _block;
170
+ }
171
+ }
172
+ } else {
173
+ block = blocks[0];
174
+ }
175
+ if (block) {
176
+ await tracer.startActiveSpan("handler", async (span2) => {
177
+ await indexer.options.transform({
178
+ block,
179
+ cursor,
180
+ endCursor,
181
+ finality,
182
+ context
183
+ });
184
+ span2.end();
185
+ });
186
+ }
187
+ });
188
+ span.end();
189
+ });
190
+ context.cursor = void 0;
191
+ context.endCursor = void 0;
192
+ context.finality = void 0;
193
+ break;
194
+ }
195
+ case "invalidate": {
196
+ await tracer.startActiveSpan("message invalidate", async (span) => {
197
+ await indexer.hooks.callHook("message:invalidate", { message });
198
+ span.end();
199
+ });
200
+ break;
201
+ }
202
+ case "finalize": {
203
+ await tracer.startActiveSpan("message finalize", async (span) => {
204
+ await indexer.hooks.callHook("message:finalize", { message });
205
+ span.end();
206
+ });
207
+ break;
208
+ }
209
+ case "heartbeat": {
210
+ await tracer.startActiveSpan("message heartbeat", async (span) => {
211
+ await indexer.hooks.callHook("message:heartbeat", { message });
212
+ span.end();
213
+ });
214
+ break;
215
+ }
216
+ case "systemMessage": {
217
+ await tracer.startActiveSpan(
218
+ "message systemMessage",
219
+ async (span) => {
220
+ switch (message.systemMessage.output?._tag) {
221
+ case "stderr": {
222
+ consola.warn(message.systemMessage.output.stderr);
223
+ break;
224
+ }
225
+ case "stdout": {
226
+ consola.info(message.systemMessage.output.stdout);
227
+ break;
228
+ }
229
+ }
230
+ await indexer.hooks.callHook("message:systemMessage", {
231
+ message
232
+ });
233
+ span.end();
234
+ }
235
+ );
236
+ break;
237
+ }
238
+ default: {
239
+ consola.warn("unexpected message", message);
240
+ throw new Error("not implemented");
241
+ }
242
+ }
243
+ await indexer.hooks.callHook("run:after");
244
+ }
245
+ });
246
+ }
247
+ async function registerMiddleware(indexer) {
248
+ const middleware = [];
249
+ const use = (fn) => {
250
+ middleware.push(fn);
251
+ };
252
+ await indexer.hooks.callHook("handler:middleware", { use });
253
+ const composed = compose(middleware);
254
+ return async function _composedIndexerMiddleware(context, next) {
255
+ await composed(context, next);
256
+ };
257
+ }
258
+
259
+ export { createIndexer, defineIndexer, run, runWithReconnect, useIndexerContext };
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ const protocol = require('@apibara/protocol');
4
+ const testing = require('@apibara/protocol/testing');
5
+ const context = require('../shared/indexer.077335f3.cjs');
6
+ const index = require('../index.cjs');
7
+ const config = require('../shared/indexer.601ceab0.cjs');
8
+ require('consola');
9
+ require('node:async_hooks');
10
+ require('unctx');
11
+ require('hookable');
12
+ require('node:assert');
13
+ require('@opentelemetry/api');
14
+
15
+ function generateMockMessages(count = 10, options) {
16
+ const invalidateAt = options?.invalidate;
17
+ const finalizeAt = options?.finalize;
18
+ const messages = [];
19
+ for (let i = 0; i < count; i++) {
20
+ if (invalidateAt && i === invalidateAt.invalidateTriggerIndex) {
21
+ messages.push({
22
+ _tag: "invalidate",
23
+ invalidate: {
24
+ cursor: {
25
+ orderKey: BigInt(5e6 + invalidateAt.invalidateFromIndex)
26
+ }
27
+ }
28
+ });
29
+ } else if (finalizeAt && i === finalizeAt.finalizeTriggerIndex) {
30
+ messages.push({
31
+ _tag: "finalize",
32
+ finalize: {
33
+ cursor: {
34
+ orderKey: BigInt(5e6 + finalizeAt.finalizeToIndex)
35
+ }
36
+ }
37
+ });
38
+ } else {
39
+ messages.push({
40
+ _tag: "data",
41
+ data: {
42
+ cursor: { orderKey: BigInt(5e6 + i - 1) },
43
+ finality: "accepted",
44
+ data: [{ data: `${5e6 + i}` }],
45
+ endCursor: { orderKey: BigInt(5e6 + i) }
46
+ }
47
+ });
48
+ }
49
+ }
50
+ return messages;
51
+ }
52
+ function getMockIndexer({
53
+ plugins,
54
+ override
55
+ } = {}) {
56
+ return index.createIndexer(
57
+ index.defineIndexer(testing.MockStream)({
58
+ streamUrl: "https://sepolia.ethereum.a5a.ch",
59
+ finality: "accepted",
60
+ filter: {},
61
+ async transform({ block: { data }, context }) {
62
+ },
63
+ plugins,
64
+ ...override
65
+ })
66
+ );
67
+ }
68
+ function mockSink({
69
+ output,
70
+ metadata
71
+ }) {
72
+ return config.defineIndexerPlugin((indexer) => {
73
+ indexer.hooks.hook("connect:before", ({ request }) => {
74
+ if (metadata?.lastCursor && protocol.isCursor(metadata.lastCursor)) {
75
+ request.startingCursor = metadata.lastCursor;
76
+ }
77
+ if (metadata?.lastFilter) {
78
+ request.filter[1] = metadata.lastFilter;
79
+ }
80
+ });
81
+ indexer.hooks.hook("connect:factory", ({ request, endCursor }) => {
82
+ if (request.filter[1]) {
83
+ if (metadata) {
84
+ metadata.lastCursor = endCursor;
85
+ metadata.lastFilter = request.filter[1];
86
+ }
87
+ }
88
+ });
89
+ indexer.hooks.hook("handler:middleware", ({ use }) => {
90
+ use(async (context, next) => {
91
+ context.output = output;
92
+ await next();
93
+ context.output = null;
94
+ if (metadata) {
95
+ metadata.lastCursor = context.endCursor;
96
+ }
97
+ });
98
+ });
99
+ });
100
+ }
101
+ function useMockSink() {
102
+ const context$1 = context.useIndexerContext();
103
+ return { output: context$1.output };
104
+ }
105
+
106
+ exports.generateMockMessages = generateMockMessages;
107
+ exports.getMockIndexer = getMockIndexer;
108
+ exports.mockSink = mockSink;
109
+ exports.useMockSink = useMockSink;
@@ -0,0 +1,40 @@
1
+ import { a as IndexerPlugin, b as IndexerConfig, c as Indexer } from '../shared/indexer.8939ecc8.cjs';
2
+ import { MockStreamResponse, MockFilter, MockBlock } from '@apibara/protocol/testing';
3
+ import '@apibara/protocol';
4
+ import 'hookable';
5
+
6
+ type MockMessagesOptions = {
7
+ invalidate?: {
8
+ invalidateFromIndex: number;
9
+ invalidateTriggerIndex: number;
10
+ };
11
+ finalize?: {
12
+ finalizeToIndex: number;
13
+ finalizeTriggerIndex: number;
14
+ };
15
+ };
16
+ declare function generateMockMessages(count?: number, options?: MockMessagesOptions): MockStreamResponse[];
17
+ declare function getMockIndexer({ plugins, override, }?: {
18
+ plugins?: ReadonlyArray<IndexerPlugin<MockFilter, MockBlock>>;
19
+ override?: Partial<IndexerConfig<MockFilter, MockBlock>>;
20
+ }): Indexer<{
21
+ readonly filter?: string | undefined;
22
+ }, {
23
+ readonly data?: string | undefined;
24
+ }>;
25
+ type MockRet = {
26
+ data: string;
27
+ };
28
+ /**
29
+ * A mock sink used for testing. The indexer function can write to the output array.
30
+ * The indexer context is optionally written to the metadata object.
31
+ */
32
+ declare function mockSink<TFilter, TBlock>({ output, metadata, }: {
33
+ output: unknown[];
34
+ metadata?: Record<string, unknown>;
35
+ }): IndexerPlugin<TFilter, TBlock>;
36
+ declare function useMockSink(): {
37
+ output: unknown[];
38
+ };
39
+
40
+ export { type MockMessagesOptions, type MockRet, generateMockMessages, getMockIndexer, mockSink, useMockSink };