envio 3.3.1 → 3.4.0

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 (60) hide show
  1. package/package.json +6 -6
  2. package/src/Api.res +1 -8
  3. package/src/Api.res.mjs +1 -5
  4. package/src/ChainState.res +6 -1
  5. package/src/ChainState.res.mjs +4 -3
  6. package/src/Config.res +33 -4
  7. package/src/Config.res.mjs +34 -5
  8. package/src/Core.res +30 -19
  9. package/src/Core.res.mjs +5 -5
  10. package/src/EnvioGlobal.res +0 -2
  11. package/src/EnvioGlobal.res.mjs +0 -1
  12. package/src/ExitOnCaughtUp.res +6 -2
  13. package/src/ExitOnCaughtUp.res.mjs +4 -0
  14. package/src/FetchState.res +3 -3
  15. package/src/HandlerRegister.res +357 -434
  16. package/src/HandlerRegister.res.mjs +202 -242
  17. package/src/HandlerRegister.resi +7 -15
  18. package/src/IndexerState.res +10 -0
  19. package/src/IndexerState.res.mjs +9 -3
  20. package/src/IndexerState.resi +3 -0
  21. package/src/Internal.res +10 -1
  22. package/src/Main.res.mjs +4 -4
  23. package/src/PgStorage.res +52 -26
  24. package/src/PgStorage.res.mjs +32 -23
  25. package/src/SimulateItems.res +61 -40
  26. package/src/SimulateItems.res.mjs +37 -21
  27. package/src/TestIndexer.res +446 -453
  28. package/src/TestIndexer.res.mjs +320 -343
  29. package/src/bindings/ClickHouse.res +68 -2
  30. package/src/bindings/ClickHouse.res.mjs +47 -2
  31. package/src/sources/EvmChain.res +1 -1
  32. package/src/sources/EvmChain.res.mjs +2 -2
  33. package/src/sources/FuelHyperSync.res +74 -0
  34. package/src/sources/FuelHyperSync.res.mjs +80 -0
  35. package/src/sources/FuelHyperSync.resi +22 -0
  36. package/src/sources/FuelHyperSyncClient.res +120 -0
  37. package/src/sources/FuelHyperSyncClient.res.mjs +71 -0
  38. package/src/sources/FuelHyperSyncSource.res +257 -0
  39. package/src/sources/FuelHyperSyncSource.res.mjs +252 -0
  40. package/src/sources/HyperSyncClient.res +2 -2
  41. package/src/sources/HyperSyncClient.res.mjs +1 -1
  42. package/src/sources/SvmHyperSyncClient.res +139 -102
  43. package/src/sources/SvmHyperSyncClient.res.mjs +45 -5
  44. package/src/sources/SvmHyperSyncSource.res +60 -440
  45. package/src/sources/SvmHyperSyncSource.res.mjs +42 -363
  46. package/src/TestIndexerProxyStorage.res +0 -196
  47. package/src/TestIndexerProxyStorage.res.mjs +0 -121
  48. package/src/TestIndexerWorker.res +0 -4
  49. package/src/TestIndexerWorker.res.mjs +0 -7
  50. package/src/sources/EventRouter.res +0 -165
  51. package/src/sources/EventRouter.res.mjs +0 -153
  52. package/src/sources/HyperFuel.res +0 -179
  53. package/src/sources/HyperFuel.res.mjs +0 -146
  54. package/src/sources/HyperFuel.resi +0 -36
  55. package/src/sources/HyperFuelClient.res +0 -127
  56. package/src/sources/HyperFuelClient.res.mjs +0 -20
  57. package/src/sources/HyperFuelSource.res +0 -502
  58. package/src/sources/HyperFuelSource.res.mjs +0 -481
  59. /package/src/sources/{HyperSyncSource.res → EvmHyperSyncSource.res} +0 -0
  60. /package/src/sources/{HyperSyncSource.res.mjs → EvmHyperSyncSource.res.mjs} +0 -0
@@ -4,7 +4,6 @@ import * as Utils from "./Utils.res.mjs";
4
4
  import * as Logging from "./Logging.res.mjs";
5
5
  import * as ChainMap from "./ChainMap.res.mjs";
6
6
  import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
7
- import * as EventRouter from "./sources/EventRouter.res.mjs";
8
7
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
10
9
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
@@ -13,27 +12,12 @@ import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
13
12
  import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
14
13
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
15
14
 
16
- let eventRegistrations = EnvioGlobal.value.eventRegistrations;
17
-
18
15
  function getKey(contractName, eventName) {
19
16
  return contractName + "." + eventName;
20
17
  }
21
18
 
22
- function get(contractName, eventName) {
23
- let existing = eventRegistrations[getKey(contractName, eventName)];
24
- if (existing !== undefined) {
25
- return existing;
26
- } else {
27
- return {
28
- handler: undefined,
29
- contractRegister: undefined,
30
- eventOptions: undefined
31
- };
32
- }
33
- }
34
-
35
- function set(contractName, eventName, registration) {
36
- eventRegistrations[getKey(contractName, eventName)] = registration;
19
+ function resetOnEventRegistrations() {
20
+ EnvioGlobal.value.activeRegistration = undefined;
37
21
  }
38
22
 
39
23
  let preRegistered = EnvioGlobal.value.preRegistered;
@@ -53,27 +37,28 @@ function withRegistration(fn) {
53
37
  }
54
38
 
55
39
  function startRegistration(config) {
40
+ let match = EnvioGlobal.value.activeRegistration;
41
+ if (match !== undefined) {
42
+ return;
43
+ }
56
44
  let r = {
57
45
  config: config,
58
46
  registrationsByChainId: {},
59
47
  finished: false
60
48
  };
61
49
  EnvioGlobal.value.activeRegistration = Primitive_option.some(r);
62
- while (preRegistered.length !== 0) {
63
- let fn = preRegistered.pop();
64
- if (fn !== undefined) {
65
- fn(r);
66
- }
67
- };
50
+ let queued = preRegistered.slice();
51
+ preRegistered.splice(0, preRegistered.length);
52
+ queued.forEach(fn => fn(r));
68
53
  }
69
54
 
70
- function getPendingChainRegistrations(r, chainId) {
55
+ function getChainRegistrations(r, chainId) {
71
56
  let key = chainId.toString();
72
- let pending = r.registrationsByChainId[key];
73
- if (pending !== undefined) {
74
- return pending;
57
+ let existing = r.registrationsByChainId[key];
58
+ if (existing !== undefined) {
59
+ return existing;
75
60
  }
76
- let fresh_onEventRegistrations = {};
61
+ let fresh_onEventRegistrations = [];
77
62
  let fresh_onBlockRegistrations = [];
78
63
  let fresh = {
79
64
  onEventRegistrations: fresh_onEventRegistrations,
@@ -95,164 +80,221 @@ function buildOnEventRegistrationWith(config, chainId, eventConfig, isWildcard,
95
80
  }
96
81
  }
97
82
 
98
- function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
99
- let t = get(eventConfig.contractName, eventConfig.name);
100
- return buildOnEventRegistrationWith(config, chainId, eventConfig, Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false), t.handler, t.contractRegister, Stdlib_Option.flatMap(t.eventOptions, v => v.where), startBlock);
83
+ function sameEventAndFilter(a, b, config) {
84
+ if (a.eventConfig.contractName !== b.eventConfig.contractName || a.eventConfig.name !== b.eventConfig.name || a.isWildcard !== b.isWildcard) {
85
+ return false;
86
+ }
87
+ let match = config.ecosystem.name;
88
+ switch (match) {
89
+ case "evm" :
90
+ return Primitive_object.equal(a.resolvedWhere, b.resolvedWhere);
91
+ case "fuel" :
92
+ case "svm" :
93
+ return true;
94
+ }
101
95
  }
102
96
 
103
- function getHandler(contractName, eventName) {
104
- return get(contractName, eventName).handler;
97
+ function mergeRegistrations(resolved, config) {
98
+ let merged = {
99
+ contents: []
100
+ };
101
+ resolved.forEach(reg => {
102
+ if (Stdlib_Option.isSome(reg.handler)) {
103
+ let i = merged.contents.findIndex(m => {
104
+ if (Stdlib_Option.isNone(m.handler) && Stdlib_Option.isSome(m.contractRegister)) {
105
+ return sameEventAndFilter(m, reg, config);
106
+ } else {
107
+ return false;
108
+ }
109
+ });
110
+ if (i === -1) {
111
+ merged.contents = merged.contents.concat([reg]);
112
+ return;
113
+ }
114
+ let target = merged.contents[i];
115
+ let newrecord = {...reg};
116
+ merged.contents = merged.contents.filter((param, j) => j !== i).concat([(newrecord.contractRegister = target.contractRegister, newrecord)]);
117
+ return;
118
+ }
119
+ let i$1 = merged.contents.findIndex(m => {
120
+ if (Stdlib_Option.isSome(m.handler) && Stdlib_Option.isNone(m.contractRegister)) {
121
+ return sameEventAndFilter(m, reg, config);
122
+ } else {
123
+ return false;
124
+ }
125
+ });
126
+ if (i$1 === -1) {
127
+ merged.contents = merged.contents.concat([reg]);
128
+ return;
129
+ }
130
+ let target$1 = merged.contents[i$1];
131
+ let next = merged.contents.slice();
132
+ let newrecord$1 = {...target$1};
133
+ newrecord$1.contractRegister = reg.contractRegister;
134
+ next[i$1] = newrecord$1;
135
+ merged.contents = next;
136
+ });
137
+ return merged.contents;
105
138
  }
106
139
 
107
- function getContractRegister(contractName, eventName) {
108
- return get(contractName, eventName).contractRegister;
140
+ function isDroppedByWhere(config, reg) {
141
+ if (config.ecosystem.name === "evm") {
142
+ return Utils.$$Array.isEmpty(reg.resolvedWhere.topicSelections);
143
+ } else {
144
+ return false;
145
+ }
109
146
  }
110
147
 
111
- function isWildcard(contractName, eventName) {
112
- return Stdlib_Option.getOr(Stdlib_Option.flatMap(get(contractName, eventName).eventOptions, value => value.wildcard), false);
148
+ function addOnEventRegistration(registration, contractName, eventName, handler, contractRegister, eventOptions) {
149
+ let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(eventOptions, v => v.wildcard), false);
150
+ let where = Stdlib_Option.flatMap(eventOptions, v => v.where);
151
+ ChainMap.values(registration.config.chainMap).forEach(chainConfig => {
152
+ let contract = chainConfig.contracts.find(c => c.name === contractName);
153
+ if (contract === undefined) {
154
+ return;
155
+ }
156
+ let eventConfig = contract.events.find(e => e.name === eventName);
157
+ if (eventConfig === undefined) {
158
+ return;
159
+ }
160
+ let reg = buildOnEventRegistrationWith(registration.config, chainConfig.id, eventConfig, isWildcard, handler, contractRegister, where, contract.startBlock);
161
+ getChainRegistrations(registration, chainConfig.id).onEventRegistrations.push(reg);
162
+ });
113
163
  }
114
164
 
115
- function hasRegistration(contractName, eventName) {
116
- let r = get(contractName, eventName);
117
- if (Stdlib_Option.isSome(r.handler)) {
118
- return true;
165
+ function setHandler(contractName, eventName, handler, eventOptions) {
166
+ withRegistration(registration => {
167
+ let eventOptions$1 = Stdlib_Option.map(eventOptions, v => v);
168
+ addOnEventRegistration(registration, contractName, eventName, handler, undefined, eventOptions$1);
169
+ });
170
+ }
171
+
172
+ function setContractRegister(contractName, eventName, contractRegister, eventOptions) {
173
+ withRegistration(registration => {
174
+ let eventOptions$1 = Stdlib_Option.map(eventOptions, v => v);
175
+ addOnEventRegistration(registration, contractName, eventName, undefined, contractRegister, eventOptions$1);
176
+ });
177
+ }
178
+
179
+ function storedOnEventRegistrations(r, chainId) {
180
+ let chainRegs = r.registrationsByChainId[chainId.toString()];
181
+ if (chainRegs !== undefined) {
182
+ return chainRegs.onEventRegistrations;
119
183
  } else {
120
- return Stdlib_Option.isSome(r.contractRegister);
184
+ return [];
121
185
  }
122
186
  }
123
187
 
124
- function raiseDuplicateRegistration(contractName, eventName, msg, logger) {
125
- let fullMsg = msg + " for " + contractName + "." + eventName;
126
- Logging.childError(Logging.createChildFrom(logger, {
127
- contractName: contractName,
128
- eventName: eventName
129
- }), fullMsg);
130
- return Stdlib_JsError.throwWithMessage(fullMsg);
188
+ function isWildcard(contractName, eventName) {
189
+ let r = EnvioGlobal.value.activeRegistration;
190
+ if (r !== undefined) {
191
+ return Object.values(r.registrationsByChainId).some(chainRegs => chainRegs.onEventRegistrations.some(reg => {
192
+ if (reg.eventConfig.contractName === contractName && reg.eventConfig.name === eventName) {
193
+ return reg.isWildcard;
194
+ } else {
195
+ return false;
196
+ }
197
+ }));
198
+ } else {
199
+ return false;
200
+ }
131
201
  }
132
202
 
133
- function eventOptionsMatch(existing, incoming) {
134
- if (existing !== undefined) {
135
- if (incoming !== undefined) {
136
- return existing.wildcard === incoming.wildcard;
203
+ function getSimulateOnEventRegistrations(config, chainId, eventConfig) {
204
+ let r = EnvioGlobal.value.activeRegistration;
205
+ let stored = r !== undefined ? storedOnEventRegistrations(r, chainId) : [];
206
+ let matching = mergeRegistrations(stored, config).filter(reg => {
207
+ if (reg.eventConfig.contractName === eventConfig.contractName) {
208
+ return reg.eventConfig.name === eventConfig.name;
137
209
  } else {
138
210
  return false;
139
211
  }
212
+ });
213
+ if (Utils.$$Array.notEmpty(matching)) {
214
+ return matching;
140
215
  } else {
141
- return incoming === undefined;
216
+ return [buildOnEventRegistrationWith(config, chainId, eventConfig, false, undefined, undefined, undefined, undefined)];
142
217
  }
143
218
  }
144
219
 
145
- function syncOnEventRegistrations(r, contractName, eventName, where, duplicateMsg, logger) {
146
- let config = r.config;
147
- let t = get(contractName, eventName);
148
- let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false);
149
- let key = getKey(contractName, eventName);
220
+ function finishRegistration(config) {
221
+ let r = EnvioGlobal.value.activeRegistration;
222
+ if (r === undefined) {
223
+ return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
224
+ }
225
+ r.finished = true;
226
+ let notRegisteredEventsByContract = {};
227
+ let registrationsByChainId = {};
150
228
  ChainMap.values(config.chainMap).forEach(chainConfig => {
229
+ let chainId = chainConfig.id;
230
+ let key = chainId.toString();
231
+ let builtRegs = mergeRegistrations(storedOnEventRegistrations(r, chainId), config);
232
+ let registeredKeys = new Set();
233
+ builtRegs.forEach(reg => {
234
+ registeredKeys.add(getKey(reg.eventConfig.contractName, reg.eventConfig.name));
235
+ });
236
+ let rawEventRegs = [];
151
237
  chainConfig.contracts.forEach(contract => {
152
- if (contract.name !== contractName) {
153
- return;
154
- }
155
- let eventConfig = contract.events.find(e => e.name === eventName);
156
- if (eventConfig === undefined) {
238
+ contract.events.forEach(eventConfig => {
239
+ if (registeredKeys.has(getKey(contract.name, eventConfig.name))) {
240
+ return;
241
+ }
242
+ if (config.enableRawEvents) {
243
+ rawEventRegs.push(buildOnEventRegistrationWith(config, chainId, eventConfig, false, undefined, undefined, undefined, contract.startBlock));
244
+ return;
245
+ }
246
+ let set = notRegisteredEventsByContract[contract.name];
247
+ let eventNames;
248
+ if (set !== undefined) {
249
+ eventNames = Primitive_option.valFromOption(set);
250
+ } else {
251
+ let set$1 = new Set();
252
+ notRegisteredEventsByContract[contract.name] = set$1;
253
+ eventNames = set$1;
254
+ }
255
+ eventNames.add(eventConfig.name);
256
+ });
257
+ });
258
+ let onEventRegistrations = [];
259
+ builtRegs.concat(rawEventRegs).forEach(reg => {
260
+ if (isDroppedByWhere(config, reg)) {
157
261
  return;
158
262
  }
159
- let newRegistration = buildOnEventRegistrationWith(config, chainConfig.id, eventConfig, isWildcard, t.handler, t.contractRegister, where, contract.startBlock);
160
- let pending = getPendingChainRegistrations(r, chainConfig.id);
161
- let existing = pending.onEventRegistrations[key];
162
- if (existing !== undefined && config.ecosystem.name === "evm" && !Primitive_object.equal(existing.resolvedWhere, newRegistration.resolvedWhere)) {
163
- raiseDuplicateRegistration(contractName, eventName, duplicateMsg, logger);
164
- }
165
- pending.onEventRegistrations[key] = newRegistration;
263
+ let newrecord = {...reg};
264
+ onEventRegistrations.push((newrecord.index = onEventRegistrations.length, newrecord));
166
265
  });
266
+ let chainRegs = r.registrationsByChainId[key];
267
+ registrationsByChainId[key] = {
268
+ onEventRegistrations: onEventRegistrations,
269
+ onBlockRegistrations: chainRegs !== undefined ? chainRegs.onBlockRegistrations.slice() : []
270
+ };
167
271
  });
272
+ let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
273
+ if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
274
+ let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
275
+ Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
276
+ }
277
+ return registrationsByChainId;
168
278
  }
169
279
 
170
- function setEventOptions(contractName, eventName, eventOptions, loggerOpt) {
171
- let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
172
- if (eventOptions === undefined) {
173
- return;
174
- }
175
- let t = get(contractName, eventName);
176
- let existingValue = t.eventOptions;
177
- if (existingValue !== undefined) {
178
- if (!eventOptionsMatch(existingValue, eventOptions)) {
179
- return raiseDuplicateRegistration(contractName, eventName, "Cannot register handler with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
180
- } else {
181
- return;
182
- }
280
+ function isPendingRegistration() {
281
+ let r = EnvioGlobal.value.activeRegistration;
282
+ if (r !== undefined) {
283
+ return !r.finished;
183
284
  } else {
184
- return set(contractName, eventName, {
185
- handler: t.handler,
186
- contractRegister: t.contractRegister,
187
- eventOptions: eventOptions
188
- });
285
+ return false;
189
286
  }
190
287
  }
191
288
 
192
- function setHandler(contractName, eventName, handler, eventOptions, loggerOpt) {
193
- let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
194
- withRegistration(registration => {
195
- let t = get(contractName, eventName);
196
- let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
197
- let prevHandler = t.handler;
198
- if (prevHandler !== undefined) {
199
- if (eventOptionsMatch(t.eventOptions, incomingEventOptions)) {
200
- let composedHandler = async args => {
201
- await prevHandler(args);
202
- return await handler(args);
203
- };
204
- set(contractName, eventName, {
205
- handler: composedHandler,
206
- contractRegister: t.contractRegister,
207
- eventOptions: t.eventOptions
208
- });
209
- } else {
210
- raiseDuplicateRegistration(contractName, eventName, "Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
211
- }
212
- } else {
213
- setEventOptions(contractName, eventName, eventOptions, logger);
214
- let t$1 = get(contractName, eventName);
215
- set(contractName, eventName, {
216
- handler: handler,
217
- contractRegister: t$1.contractRegister,
218
- eventOptions: t$1.eventOptions
219
- });
220
- }
221
- syncOnEventRegistrations(registration, contractName, eventName, Stdlib_Option.flatMap(incomingEventOptions, v => v.where), "Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
222
- });
223
- }
224
-
225
- function setContractRegister(contractName, eventName, contractRegister, eventOptions, loggerOpt) {
226
- let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
227
- withRegistration(registration => {
228
- let t = get(contractName, eventName);
229
- let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
230
- let prevContractRegister = t.contractRegister;
231
- if (prevContractRegister !== undefined) {
232
- if (eventOptionsMatch(t.eventOptions, incomingEventOptions)) {
233
- let composedContractRegister = async args => {
234
- await prevContractRegister(args);
235
- return await contractRegister(args);
236
- };
237
- set(contractName, eventName, {
238
- handler: t.handler,
239
- contractRegister: composedContractRegister,
240
- eventOptions: t.eventOptions
241
- });
242
- } else {
243
- raiseDuplicateRegistration(contractName, eventName, "Cannot register a second contractRegister with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
244
- }
245
- } else {
246
- setEventOptions(contractName, eventName, eventOptions, logger);
247
- let t$1 = get(contractName, eventName);
248
- set(contractName, eventName, {
249
- handler: t$1.handler,
250
- contractRegister: contractRegister,
251
- eventOptions: t$1.eventOptions
252
- });
253
- }
254
- syncOnEventRegistrations(registration, contractName, eventName, Stdlib_Option.flatMap(incomingEventOptions, v => v.where), "Cannot register a second contractRegister with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
255
- });
289
+ function throwIfFinishedRegistration(methodName) {
290
+ let match = EnvioGlobal.value.activeRegistration;
291
+ if (match === undefined) {
292
+ return;
293
+ }
294
+ let match$1 = match.finished;
295
+ if (match$1) {
296
+ return Stdlib_JsError.throwWithMessage(`Cannot call \`indexer.` + methodName + `\` after the indexer has started. Make sure all handlers are registered at the top level of your handler module.`);
297
+ }
256
298
  }
257
299
 
258
300
  let blockRangeSchema = S$RescriptSchema.strict(S$RescriptSchema.object(s => ({
@@ -330,9 +372,9 @@ function registerOnBlock(name, where, handler, getChainsObject) {
330
372
  if (chainEndBlock !== undefined && Stdlib_Option.getOr(range._lte, chainEndBlock) > chainEndBlock) {
331
373
  Stdlib_JsError.throwWithMessage(`The end block for onBlock handler "` + name + `" is greater than the chain end block (` + chainEndBlock.toString() + `). This is not supported yet.`);
332
374
  }
333
- let pending = getPendingChainRegistrations(registration, chainId);
334
- pending.onBlockRegistrations.push({
335
- index: pending.onBlockRegistrations.length,
375
+ let chainRegs = getChainRegistrations(registration, chainId);
376
+ chainRegs.onBlockRegistrations.push({
377
+ index: chainRegs.onBlockRegistrations.length,
336
378
  name: name,
337
379
  chainId: chainId,
338
380
  startBlock: range._gte,
@@ -347,99 +389,17 @@ function registerOnBlock(name, where, handler, getChainsObject) {
347
389
  });
348
390
  }
349
391
 
350
- function finishRegistration(config) {
351
- let r = EnvioGlobal.value.activeRegistration;
352
- if (r === undefined) {
353
- return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
354
- }
355
- r.finished = true;
356
- let notRegisteredEventsByContract = {};
357
- let registrationsByChainId = {};
358
- ChainMap.values(config.chainMap).forEach(chainConfig => {
359
- let key = chainConfig.id.toString();
360
- let pending = r.registrationsByChainId[key];
361
- let eventRouter = EventRouter.empty();
362
- let onEventRegistrations = [];
363
- chainConfig.contracts.forEach(contract => {
364
- let contractName = contract.name;
365
- contract.events.forEach(eventConfig => {
366
- let eventName = eventConfig.name;
367
- let registration = Stdlib_Option.flatMap(pending, pending => pending.onEventRegistrations[getKey(contractName, eventName)]);
368
- EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, registration !== undefined ? registration.isWildcard : isWildcard(contractName, eventName), eventName, ChainMap.Chain.makeUnsafe(chainConfig.id));
369
- let registration$1;
370
- if (registration !== undefined) {
371
- registration$1 = registration;
372
- } else if (hasRegistration(contractName, eventName) || config.enableRawEvents) {
373
- registration$1 = buildOnEventRegistration(config, chainConfig.id, eventConfig, contract.startBlock);
374
- } else {
375
- let set = notRegisteredEventsByContract[contractName];
376
- let eventNames;
377
- if (set !== undefined) {
378
- eventNames = Primitive_option.valFromOption(set);
379
- } else {
380
- let set$1 = new Set();
381
- notRegisteredEventsByContract[contractName] = set$1;
382
- eventNames = set$1;
383
- }
384
- eventNames.add(eventName);
385
- registration$1 = undefined;
386
- }
387
- if (registration$1 === undefined) {
388
- return;
389
- }
390
- let isDroppedByWhere = config.ecosystem.name === "evm" && Utils.$$Array.isEmpty(registration$1.resolvedWhere.topicSelections);
391
- if (isDroppedByWhere) {
392
- return;
393
- }
394
- let newrecord = {...registration$1};
395
- onEventRegistrations.push((newrecord.index = onEventRegistrations.length, newrecord));
396
- });
397
- });
398
- registrationsByChainId[key] = {
399
- onEventRegistrations: onEventRegistrations,
400
- onBlockRegistrations: pending !== undefined ? pending.onBlockRegistrations : []
401
- };
402
- });
403
- let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
404
- if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
405
- let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
406
- Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
407
- }
408
- return registrationsByChainId;
409
- }
410
-
411
- function isPendingRegistration() {
412
- let r = EnvioGlobal.value.activeRegistration;
413
- if (r !== undefined) {
414
- return !r.finished;
415
- } else {
416
- return false;
417
- }
418
- }
419
-
420
- function throwIfFinishedRegistration(methodName) {
421
- let match = EnvioGlobal.value.activeRegistration;
422
- if (match === undefined) {
423
- return;
424
- }
425
- let match$1 = match.finished;
426
- if (match$1) {
427
- return Stdlib_JsError.throwWithMessage(`Cannot call \`indexer.` + methodName + `\` after the indexer has started. Make sure all handlers are registered at the top level of your handler module.`);
428
- }
429
- }
430
-
431
392
  export {
432
393
  startRegistration,
394
+ resetOnEventRegistrations,
433
395
  isPendingRegistration,
434
396
  finishRegistration,
435
397
  throwIfFinishedRegistration,
436
- buildOnEventRegistration,
437
398
  setHandler,
438
399
  setContractRegister,
439
- getHandler,
440
- getContractRegister,
441
400
  isWildcard,
442
- hasRegistration,
401
+ isDroppedByWhere,
402
+ getSimulateOnEventRegistrations,
443
403
  blockRangeSchema,
444
404
  defaultBlockRange,
445
405
  registerOnBlock,
@@ -5,38 +5,30 @@ type chainRegistrations = {
5
5
  type registrationsByChainId = dict<chainRegistrations>
6
6
 
7
7
  let startRegistration: (~config: Config.t) => unit
8
+ let resetOnEventRegistrations: unit => unit
8
9
  let isPendingRegistration: unit => bool
9
10
  let finishRegistration: (~config: Config.t) => registrationsByChainId
10
11
  let throwIfFinishedRegistration: (~methodName: string) => unit
11
12
 
12
- let buildOnEventRegistration: (
13
- ~config: Config.t,
14
- ~chainId: int,
15
- ~eventConfig: Internal.eventConfig,
16
- ~startBlock: int=?,
17
- ) => Internal.onEventRegistration
18
-
19
13
  let setHandler: (
20
14
  ~contractName: string,
21
15
  ~eventName: string,
22
16
  Internal.genericHandler<Internal.genericHandlerArgs<'event, Internal.handlerContext>>,
23
17
  ~eventOptions: option<Internal.eventOptions<'where>>,
24
- ~logger: Pino.t=?,
25
18
  ) => unit
26
19
  let setContractRegister: (
27
20
  ~contractName: string,
28
21
  ~eventName: string,
29
22
  Internal.genericContractRegister<Internal.genericContractRegisterArgs<'event, 'context>>,
30
23
  ~eventOptions: option<Internal.eventOptions<'where>>,
31
- ~logger: Pino.t=?,
32
24
  ) => unit
33
- let getHandler: (~contractName: string, ~eventName: string) => option<Internal.handler>
34
- let getContractRegister: (
35
- ~contractName: string,
36
- ~eventName: string,
37
- ) => option<Internal.contractRegister>
38
25
  let isWildcard: (~contractName: string, ~eventName: string) => bool
39
- let hasRegistration: (~contractName: string, ~eventName: string) => bool
26
+ let isDroppedByWhere: (~config: Config.t, Internal.onEventRegistration) => bool
27
+ let getSimulateOnEventRegistrations: (
28
+ ~config: Config.t,
29
+ ~chainId: int,
30
+ ~eventConfig: Internal.eventConfig,
31
+ ) => array<Internal.onEventRegistration>
40
32
 
41
33
  type blockRange = {
42
34
  _gte: option<int>,
@@ -115,6 +115,11 @@ type t = {
115
115
  exitAfterFirstEventBlock: bool,
116
116
  // The single fatal-error handler.
117
117
  onError: ErrorHandling.t => unit,
118
+ // Invoked once when the indexer catches up and would otherwise exit the
119
+ // process. `None` keeps the production behavior (exit the process); the
120
+ // in-process test runner injects a callback that resolves its run promise
121
+ // instead, so a caught-up run doesn't kill the test process.
122
+ onExit: option<unit => unit>,
118
123
  // Set once on any fatal error. Every loop checks it to stop iterating and
119
124
  // every launch skips when it's set, so a single failure quiesces the indexer.
120
125
  mutable isStopped: bool,
@@ -149,6 +154,7 @@ let make = (
149
154
  ~shouldUseTui=false,
150
155
  ~exitAfterFirstEventBlock=false,
151
156
  ~onError: ErrorHandling.t => unit,
157
+ ~onExit=?,
152
158
  ) => {
153
159
  let chainMetaThrottler = {
154
160
  let intervalMillis = Env.ThrottleWrites.chainMetadataIntervalMillis
@@ -194,6 +200,7 @@ let make = (
194
200
  keepProcessAlive: isDevelopmentMode || shouldUseTui,
195
201
  exitAfterFirstEventBlock,
196
202
  onError,
203
+ onExit,
197
204
  isStopped: false,
198
205
  epoch: 0,
199
206
  simulateDeadInputTracker: SimulateDeadInputTracker.makeFromConfig(config),
@@ -229,6 +236,7 @@ let makeFromDbState = (
229
236
  ~reducedPollingInterval=?,
230
237
  ~targetBufferSize=CrossChainState.calculateTargetBufferSize(),
231
238
  ~onError,
239
+ ~onExit=?,
232
240
  ) => {
233
241
  let isInReorgThreshold = if initialState.cleanRun {
234
242
  false
@@ -280,6 +288,7 @@ let makeFromDbState = (
280
288
  ~shouldUseTui,
281
289
  ~exitAfterFirstEventBlock,
282
290
  ~onError,
291
+ ~onExit?,
283
292
  )
284
293
  initialState.cache->Utils.Dict.forEach(({effectName, count, scope}) => {
285
294
  state.effectState->EffectState.setUnregisteredCacheCount(~effectName, ~scope, ~count)
@@ -415,6 +424,7 @@ let indexerStartTime = (state: t) => state.indexerStartTime
415
424
  let loadManager = (state: t) => state.loadManager
416
425
  let keepProcessAlive = (state: t) => state.keepProcessAlive
417
426
  let exitAfterFirstEventBlock = (state: t) => state.exitAfterFirstEventBlock
427
+ let onExit = (state: t) => state.onExit
418
428
  let isStopped = (state: t) => state.isStopped
419
429
  let epoch = (state: t) => state.epoch
420
430
  let lastPrunedAtMillis = (state: t) => state.lastPrunedAtMillis