envio 3.3.0-alpha.8 → 3.3.0-alpha.9

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.
@@ -3,38 +3,17 @@
3
3
  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
+ import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
6
7
  import * as EventRouter from "./sources/EventRouter.res.mjs";
7
8
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
9
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
10
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
10
11
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
12
+ import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
11
13
  import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
14
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
12
15
 
13
- let version = Utils.EnvioPackage.value.version;
14
-
15
- let existing = globalThis.__envioRegistry;
16
-
17
- let registry;
18
-
19
- if (existing == null) {
20
- let fresh_eventRegistrations = {};
21
- let fresh_activeRegistration = {
22
- contents: undefined
23
- };
24
- let fresh_preRegistered = [];
25
- let fresh = {
26
- version: version,
27
- eventRegistrations: fresh_eventRegistrations,
28
- activeRegistration: fresh_activeRegistration,
29
- preRegistered: fresh_preRegistered
30
- };
31
- globalThis.__envioRegistry = fresh;
32
- registry = fresh;
33
- } else {
34
- registry = existing.version === version ? existing : Stdlib_JsError.throwWithMessage(`Multiple incompatible envio versions loaded in the same process: ` + existing.version + ` and ` + version + `. Deduplicate the 'envio' dependency in your project.`);
35
- }
36
-
37
- let eventRegistrations = registry.eventRegistrations;
16
+ let eventRegistrations = EnvioGlobal.value.eventRegistrations;
38
17
 
39
18
  function getKey(contractName, eventName) {
40
19
  return contractName + "." + eventName;
@@ -57,12 +36,10 @@ function set(contractName, eventName, registration) {
57
36
  eventRegistrations[getKey(contractName, eventName)] = registration;
58
37
  }
59
38
 
60
- let activeRegistration = registry.activeRegistration;
61
-
62
- let preRegistered = registry.preRegistered;
39
+ let preRegistered = EnvioGlobal.value.preRegistered;
63
40
 
64
41
  function withRegistration(fn) {
65
- let r = activeRegistration.contents;
42
+ let r = EnvioGlobal.value.activeRegistration;
66
43
  if (r !== undefined) {
67
44
  if (r.finished) {
68
45
  return Stdlib_JsError.throwWithMessage("The indexer finished initializing, so no more handlers can be registered. Make sure the handlers are registered on the top level of the file.");
@@ -75,15 +52,13 @@ function withRegistration(fn) {
75
52
  }
76
53
  }
77
54
 
78
- function startRegistration(ecosystem) {
55
+ function startRegistration(config) {
79
56
  let r = {
80
- ecosystem: ecosystem,
81
- registrations: {
82
- onBlockByChainId: {}
83
- },
57
+ config: config,
58
+ registrationsByChainId: {},
84
59
  finished: false
85
60
  };
86
- activeRegistration.contents = r;
61
+ EnvioGlobal.value.activeRegistration = Primitive_option.some(r);
87
62
  while (preRegistered.length !== 0) {
88
63
  let fn = preRegistered.pop();
89
64
  if (fn !== undefined) {
@@ -92,17 +67,27 @@ function startRegistration(ecosystem) {
92
67
  };
93
68
  }
94
69
 
95
- function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
96
- let contractName = eventConfig.contractName;
97
- let eventName = eventConfig.name;
98
- let t = get(contractName, eventName);
99
- let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false);
100
- let handler = t.handler;
101
- let contractRegister = t.contractRegister;
70
+ function getPendingChainRegistrations(r, chainId) {
71
+ let key = chainId.toString();
72
+ let pending = r.registrationsByChainId[key];
73
+ if (pending !== undefined) {
74
+ return pending;
75
+ }
76
+ let fresh_onEventRegistrations = {};
77
+ let fresh_onBlockRegistrations = [];
78
+ let fresh = {
79
+ onEventRegistrations: fresh_onEventRegistrations,
80
+ onBlockRegistrations: fresh_onBlockRegistrations
81
+ };
82
+ r.registrationsByChainId[key] = fresh;
83
+ return fresh;
84
+ }
85
+
86
+ function buildOnEventRegistrationWith(config, chainId, eventConfig, isWildcard, handler, contractRegister, where, startBlock) {
102
87
  let match = config.ecosystem.name;
103
88
  switch (match) {
104
89
  case "evm" :
105
- return EventConfigBuilder.buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, Stdlib_Option.flatMap(t.eventOptions, v => v.where), chainId, config.ecosystem.onEventBlockFilterSchema, startBlock);
90
+ return EventConfigBuilder.buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, where, chainId, config.ecosystem.onEventBlockFilterSchema, startBlock);
106
91
  case "fuel" :
107
92
  return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
108
93
  case "svm" :
@@ -110,113 +95,9 @@ function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
110
95
  }
111
96
  }
112
97
 
113
- function buildOnEventRegistrations(chainConfig, config, notRegisteredEventsByContract) {
114
- let eventRouter = EventRouter.empty();
115
- let onEventRegistrations = [];
116
- chainConfig.contracts.forEach(contract => {
117
- let contractName = contract.name;
118
- contract.events.forEach(eventConfig => {
119
- let eventName = eventConfig.name;
120
- let onEventRegistration = buildOnEventRegistration(config, chainConfig.id, eventConfig, contract.startBlock);
121
- EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, onEventRegistration.isWildcard, eventName, ChainMap.Chain.makeUnsafe(chainConfig.id));
122
- let shouldBeIncluded;
123
- if (config.enableRawEvents) {
124
- shouldBeIncluded = true;
125
- } else {
126
- let isRegistered = Stdlib_Option.isSome(onEventRegistration.contractRegister) || Stdlib_Option.isSome(onEventRegistration.handler);
127
- if (!isRegistered) {
128
- let set = notRegisteredEventsByContract[contractName];
129
- let eventNames;
130
- if (set !== undefined) {
131
- eventNames = Primitive_option.valFromOption(set);
132
- } else {
133
- let set$1 = new Set();
134
- notRegisteredEventsByContract[contractName] = set$1;
135
- eventNames = set$1;
136
- }
137
- eventNames.add(eventName);
138
- }
139
- shouldBeIncluded = isRegistered;
140
- }
141
- let shouldSkip;
142
- try {
143
- let getEventFiltersOrThrow = onEventRegistration.getEventFiltersOrThrow;
144
- if (getEventFiltersOrThrow) {
145
- let match = getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(chainConfig.id));
146
- shouldSkip = match.TAG === "Static" ? match._0.length === 0 : false;
147
- } else {
148
- shouldSkip = false;
149
- }
150
- } catch (exn) {
151
- shouldSkip = false;
152
- }
153
- if (shouldBeIncluded && !shouldSkip) {
154
- onEventRegistrations.push(onEventRegistration);
155
- return;
156
- }
157
- });
158
- });
159
- return onEventRegistrations;
160
- }
161
-
162
- function finishRegistration(config) {
163
- let r = activeRegistration.contents;
164
- if (r === undefined) {
165
- return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
166
- }
167
- r.finished = true;
168
- let notRegisteredEventsByContract = {};
169
- let registrationsByChainId = {};
170
- ChainMap.values(config.chainMap).forEach(chainConfig => {
171
- let key = chainConfig.id.toString();
172
- registrationsByChainId[key] = {
173
- onEventRegistrations: buildOnEventRegistrations(chainConfig, config, notRegisteredEventsByContract),
174
- onBlockRegistrations: Stdlib_Option.getOr(r.registrations.onBlockByChainId[key], [])
175
- };
176
- });
177
- let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
178
- if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
179
- let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
180
- Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
181
- }
182
- return registrationsByChainId;
183
- }
184
-
185
- function isPendingRegistration() {
186
- let r = activeRegistration.contents;
187
- if (r !== undefined) {
188
- return !r.finished;
189
- } else {
190
- return false;
191
- }
192
- }
193
-
194
- function throwIfFinishedRegistration(methodName) {
195
- let match = activeRegistration.contents;
196
- if (match === undefined) {
197
- return;
198
- }
199
- let match$1 = match.finished;
200
- if (match$1) {
201
- 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.`);
202
- }
203
- }
204
-
205
- function registerOnBlock(name, chainId, interval, startBlock, endBlock, handler) {
206
- withRegistration(registration => {
207
- let onBlockByChainId = registration.registrations.onBlockByChainId;
208
- let key = chainId.toString();
209
- let index = Stdlib_Option.mapOr(onBlockByChainId[key], 0, configs => configs.length);
210
- Utils.Dict.push(onBlockByChainId, key, {
211
- index: index,
212
- name: name,
213
- chainId: chainId,
214
- startBlock: startBlock,
215
- endBlock: endBlock,
216
- interval: interval,
217
- handler: handler
218
- });
219
- });
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);
220
101
  }
221
102
 
222
103
  function getHandler(contractName, eventName) {
@@ -227,10 +108,6 @@ function getContractRegister(contractName, eventName) {
227
108
  return get(contractName, eventName).contractRegister;
228
109
  }
229
110
 
230
- function getOnEventWhere(contractName, eventName) {
231
- return Stdlib_Option.flatMap(get(contractName, eventName).eventOptions, value => value.where);
232
- }
233
-
234
111
  function isWildcard(contractName, eventName) {
235
112
  return Stdlib_Option.getOr(Stdlib_Option.flatMap(get(contractName, eventName).eventOptions, value => value.wildcard), false);
236
113
  }
@@ -255,22 +132,8 @@ function raiseDuplicateRegistration(contractName, eventName, msg, logger) {
255
132
 
256
133
  function eventOptionsMatch(existing, incoming) {
257
134
  if (existing !== undefined) {
258
- if (incoming !== undefined && existing.wildcard === incoming.wildcard) {
259
- let a = existing.where;
260
- let b = incoming.where;
261
- if (a !== undefined) {
262
- if (b !== undefined) {
263
- if (typeof a === "function" || typeof b === "function") {
264
- return a === b;
265
- } else {
266
- return Primitive_object.equal(a, b);
267
- }
268
- } else {
269
- return false;
270
- }
271
- } else {
272
- return b === undefined;
273
- }
135
+ if (incoming !== undefined) {
136
+ return existing.wildcard === incoming.wildcard;
274
137
  } else {
275
138
  return false;
276
139
  }
@@ -279,6 +142,31 @@ function eventOptionsMatch(existing, incoming) {
279
142
  }
280
143
  }
281
144
 
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);
150
+ ChainMap.values(config.chainMap).forEach(chainConfig => {
151
+ 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) {
157
+ return;
158
+ }
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;
166
+ });
167
+ });
168
+ }
169
+
282
170
  function setEventOptions(contractName, eventName, eventOptions, loggerOpt) {
283
171
  let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
284
172
  if (eventOptions === undefined) {
@@ -303,64 +191,242 @@ function setEventOptions(contractName, eventName, eventOptions, loggerOpt) {
303
191
 
304
192
  function setHandler(contractName, eventName, handler, eventOptions, loggerOpt) {
305
193
  let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
306
- withRegistration(_registration => {
194
+ withRegistration(registration => {
307
195
  let t = get(contractName, eventName);
196
+ let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
308
197
  let prevHandler = t.handler;
309
198
  if (prevHandler !== undefined) {
310
- let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
311
- if (!eventOptionsMatch(t.eventOptions, incomingEventOptions)) {
312
- return 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);
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);
313
211
  }
314
- let composedHandler = async args => {
315
- await prevHandler(args);
316
- return await handler(args);
317
- };
318
- return set(contractName, eventName, {
319
- handler: composedHandler,
320
- contractRegister: t.contractRegister,
321
- eventOptions: t.eventOptions
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
322
219
  });
323
220
  }
324
- setEventOptions(contractName, eventName, eventOptions, logger);
325
- let t$1 = get(contractName, eventName);
326
- set(contractName, eventName, {
327
- handler: handler,
328
- contractRegister: t$1.contractRegister,
329
- eventOptions: t$1.eventOptions
330
- });
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);
331
222
  });
332
223
  }
333
224
 
334
225
  function setContractRegister(contractName, eventName, contractRegister, eventOptions, loggerOpt) {
335
226
  let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
336
- withRegistration(_registration => {
227
+ withRegistration(registration => {
337
228
  let t = get(contractName, eventName);
229
+ let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
338
230
  let prevContractRegister = t.contractRegister;
339
231
  if (prevContractRegister !== undefined) {
340
- let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
341
- if (!eventOptionsMatch(t.eventOptions, incomingEventOptions)) {
342
- return 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);
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);
343
244
  }
344
- let composedContractRegister = async args => {
345
- await prevContractRegister(args);
346
- return await contractRegister(args);
347
- };
348
- return set(contractName, eventName, {
349
- handler: t.handler,
350
- contractRegister: composedContractRegister,
351
- eventOptions: t.eventOptions
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
352
252
  });
353
253
  }
354
- setEventOptions(contractName, eventName, eventOptions, logger);
355
- let t$1 = get(contractName, eventName);
356
- set(contractName, eventName, {
357
- handler: t$1.handler,
358
- contractRegister: contractRegister,
359
- eventOptions: t$1.eventOptions
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
+ });
256
+ }
257
+
258
+ let blockRangeSchema = S$RescriptSchema.strict(S$RescriptSchema.object(s => ({
259
+ _gte: s.f("_gte", S$RescriptSchema.option(S$RescriptSchema.int)),
260
+ _lte: s.f("_lte", S$RescriptSchema.option(S$RescriptSchema.int)),
261
+ _every: s.f("_every", S$RescriptSchema.Option.getOr(S$RescriptSchema.option(S$RescriptSchema.intMin(S$RescriptSchema.int, 1, undefined)), 1))
262
+ })));
263
+
264
+ let defaultBlockRange = {
265
+ _gte: undefined,
266
+ _lte: undefined,
267
+ _every: 1
268
+ };
269
+
270
+ function extractRange(filter, name, ecosystem) {
271
+ try {
272
+ let inner = S$RescriptSchema.parseOrThrow(filter, ecosystem.onBlockFilterSchema);
273
+ if (inner !== undefined) {
274
+ return S$RescriptSchema.parseOrThrow(Primitive_option.valFromOption(inner), blockRangeSchema);
275
+ } else {
276
+ return defaultBlockRange;
277
+ }
278
+ } catch (raw_exn) {
279
+ let exn = Primitive_exceptions.internalToException(raw_exn);
280
+ if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
281
+ return Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` \`where\` returned an invalid filter: ` + Utils.prettifyExn(exn._1));
282
+ }
283
+ throw exn;
284
+ }
285
+ }
286
+
287
+ function registerOnBlock(name, where, handler, getChainsObject) {
288
+ withRegistration(registration => {
289
+ let config = registration.config;
290
+ let ecosystem = config.ecosystem;
291
+ let chainsDict = getChainsObject(config);
292
+ let logger = Logging.createChild({
293
+ onBlock: name
360
294
  });
295
+ let where$1 = where === undefined || where === null ? undefined : (
296
+ typeof where === "function" ? where : Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` expected \`where\` to be a function or omitted, but got ` + typeof where + `.`)
297
+ );
298
+ let matchedAny = {
299
+ contents: false
300
+ };
301
+ ChainMap.values(config.chainMap).forEach(chainConfig => {
302
+ let chainId = chainConfig.id;
303
+ let chainObj = chainsDict[chainId.toString()];
304
+ let result = where$1 !== undefined ? where$1({
305
+ chain: chainObj
306
+ }) : true;
307
+ let match = result === true ? [
308
+ true,
309
+ defaultBlockRange
310
+ ] : (
311
+ result === false ? [
312
+ false,
313
+ defaultBlockRange
314
+ ] : (
315
+ typeof result === "object" && !Array.isArray(result) && result !== null ? [
316
+ true,
317
+ extractRange(result, name, ecosystem)
318
+ ] : Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` \`where\` predicate returned an invalid value of type ` + typeof result + `. Expected boolean or a filter object.`)
319
+ )
320
+ );
321
+ if (!match[0]) {
322
+ return;
323
+ }
324
+ let range = match[1];
325
+ matchedAny.contents = true;
326
+ if (Stdlib_Option.getOr(range._gte, chainConfig.startBlock) < chainConfig.startBlock) {
327
+ Stdlib_JsError.throwWithMessage(`The start block for onBlock handler "` + name + `" is less than the chain start block (` + chainConfig.startBlock.toString() + `). This is not supported yet.`);
328
+ }
329
+ let chainEndBlock = chainConfig.endBlock;
330
+ if (chainEndBlock !== undefined && Stdlib_Option.getOr(range._lte, chainEndBlock) > chainEndBlock) {
331
+ 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
+ }
333
+ let pending = getPendingChainRegistrations(registration, chainId);
334
+ pending.onBlockRegistrations.push({
335
+ index: pending.onBlockRegistrations.length,
336
+ name: name,
337
+ chainId: chainId,
338
+ startBlock: range._gte,
339
+ endBlock: range._lte,
340
+ interval: range._every,
341
+ handler: handler
342
+ });
343
+ });
344
+ if (!matchedAny.contents) {
345
+ return Logging.childWarn(logger, `\`indexer.` + ecosystem.onBlockMethodName + `\` matched 0 chains. Check the \`where\` predicate.`);
346
+ }
361
347
  });
362
348
  }
363
349
 
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
+ onEventRegistrations.push(registration$1);
393
+ return;
394
+ }
395
+ });
396
+ });
397
+ registrationsByChainId[key] = {
398
+ onEventRegistrations: onEventRegistrations,
399
+ onBlockRegistrations: pending !== undefined ? pending.onBlockRegistrations : []
400
+ };
401
+ });
402
+ let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
403
+ if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
404
+ let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
405
+ Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
406
+ }
407
+ return registrationsByChainId;
408
+ }
409
+
410
+ function isPendingRegistration() {
411
+ let r = EnvioGlobal.value.activeRegistration;
412
+ if (r !== undefined) {
413
+ return !r.finished;
414
+ } else {
415
+ return false;
416
+ }
417
+ }
418
+
419
+ function throwIfFinishedRegistration(methodName) {
420
+ let match = EnvioGlobal.value.activeRegistration;
421
+ if (match === undefined) {
422
+ return;
423
+ }
424
+ let match$1 = match.finished;
425
+ if (match$1) {
426
+ 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.`);
427
+ }
428
+ }
429
+
364
430
  export {
365
431
  startRegistration,
366
432
  isPendingRegistration,
@@ -371,9 +437,10 @@ export {
371
437
  setContractRegister,
372
438
  getHandler,
373
439
  getContractRegister,
374
- getOnEventWhere,
375
440
  isWildcard,
376
441
  hasRegistration,
442
+ blockRangeSchema,
443
+ defaultBlockRange,
377
444
  registerOnBlock,
378
445
  }
379
- /* existing Not a pure module */
446
+ /* blockRangeSchema Not a pure module */
@@ -4,7 +4,7 @@ type chainRegistrations = {
4
4
  }
5
5
  type registrationsByChainId = dict<chainRegistrations>
6
6
 
7
- let startRegistration: (~ecosystem: Ecosystem.t) => unit
7
+ let startRegistration: (~config: Config.t) => unit
8
8
  let isPendingRegistration: unit => bool
9
9
  let finishRegistration: (~config: Config.t) => registrationsByChainId
10
10
  let throwIfFinishedRegistration: (~methodName: string) => unit
@@ -35,15 +35,20 @@ let getContractRegister: (
35
35
  ~contractName: string,
36
36
  ~eventName: string,
37
37
  ) => option<Internal.contractRegister>
38
- let getOnEventWhere: (~contractName: string, ~eventName: string) => option<JSON.t>
39
38
  let isWildcard: (~contractName: string, ~eventName: string) => bool
40
39
  let hasRegistration: (~contractName: string, ~eventName: string) => bool
41
40
 
41
+ type blockRange = {
42
+ _gte: option<int>,
43
+ _lte: option<int>,
44
+ _every: int,
45
+ }
46
+ let blockRangeSchema: S.t<blockRange>
47
+ let defaultBlockRange: blockRange
48
+
42
49
  let registerOnBlock: (
43
50
  ~name: string,
44
- ~chainId: int,
45
- ~interval: int,
46
- ~startBlock: option<int>,
47
- ~endBlock: option<int>,
51
+ ~where: unknown,
48
52
  ~handler: Internal.onBlockArgs => promise<unit>,
53
+ ~getChainsObject: Config.t => dict<unknown>,
49
54
  ) => unit
package/src/Internal.res CHANGED
@@ -459,15 +459,34 @@ type topicSelection = {
459
459
  topic3: array<EvmTypes.Hex.t>,
460
460
  }
461
461
 
462
+ // A single topic position of a resolved `where`: either static pre-encoded
463
+ // values, or a marker for "the currently registered addresses of this
464
+ // contract", expanded to topic values when a source query is built.
465
+ type topicFilter =
466
+ | Values(array<EvmTypes.Hex.t>)
467
+ | ContractAddresses({contractName: string})
468
+
469
+ type resolvedTopicSelection = {
470
+ topic0: array<EvmTypes.Hex.t>,
471
+ topic1: topicFilter,
472
+ topic2: topicFilter,
473
+ topic3: topicFilter,
474
+ }
475
+
476
+ // The registered `where` fully resolved at registration time for one chain.
477
+ // `topicSelections` is in disjunctive normal form (outer array is OR);
478
+ // an empty array means the `where` returned `false` for this chain.
479
+ type resolvedWhere = {
480
+ topicSelections: array<resolvedTopicSelection>,
481
+ startBlock: option<int>,
482
+ }
483
+
462
484
  // Per-event, per-invocation arguments passed to a `where` callback. The
463
485
  // concrete `chain` shape (which contract key it exposes) is generated per
464
486
  // event in user-project codegen — here it's an open record so codegen'd
465
487
  // types subtype-coerce into it cleanly.
466
488
  type onEventWhereArgs<'chain> = {chain: 'chain}
467
489
 
468
- type eventFilters =
469
- Static(array<topicSelection>) | Dynamic(array<Address.t> => array<topicSelection>)
470
-
471
490
  type evmEventConfig = {
472
491
  ...eventConfig,
473
492
  selectedBlockFields: Utils.Set.t<evmBlockField>,
@@ -557,7 +576,7 @@ type onEventRegistration = {
557
576
 
558
577
  type evmOnEventRegistration = {
559
578
  ...onEventRegistration,
560
- getEventFiltersOrThrow: ChainMap.Chain.t => eventFilters,
579
+ resolvedWhere: resolvedWhere,
561
580
  }
562
581
 
563
582
  // Fuel and SVM registrations add no ecosystem-specific fetch state (their