envio 3.3.0-alpha.7 → 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.
Files changed (95) hide show
  1. package/index.d.ts +23 -5
  2. package/package.json +6 -6
  3. package/src/Api.res +1 -1
  4. package/src/Api.res.mjs +1 -1
  5. package/src/ChainFetching.res +21 -10
  6. package/src/ChainFetching.res.mjs +11 -10
  7. package/src/ChainState.res +401 -171
  8. package/src/ChainState.res.mjs +270 -104
  9. package/src/ChainState.resi +19 -7
  10. package/src/Config.res +11 -27
  11. package/src/Config.res.mjs +8 -7
  12. package/src/Core.res +7 -0
  13. package/src/CrossChainState.res +82 -82
  14. package/src/CrossChainState.res.mjs +52 -59
  15. package/src/CrossChainState.resi +1 -1
  16. package/src/Ecosystem.res +3 -3
  17. package/src/Ecosystem.res.mjs +3 -3
  18. package/src/Envio.res +14 -9
  19. package/src/EnvioGlobal.res +53 -0
  20. package/src/EnvioGlobal.res.mjs +31 -0
  21. package/src/EventConfigBuilder.res +117 -69
  22. package/src/EventConfigBuilder.res.mjs +71 -34
  23. package/src/EventProcessing.res +19 -15
  24. package/src/EventProcessing.res.mjs +13 -12
  25. package/src/FetchState.res +410 -185
  26. package/src/FetchState.res.mjs +268 -253
  27. package/src/HandlerLoader.res +8 -113
  28. package/src/HandlerLoader.res.mjs +2 -88
  29. package/src/HandlerRegister.res +518 -144
  30. package/src/HandlerRegister.res.mjs +285 -133
  31. package/src/HandlerRegister.resi +24 -8
  32. package/src/IndexerState.res +6 -3
  33. package/src/IndexerState.res.mjs +3 -3
  34. package/src/IndexerState.resi +1 -1
  35. package/src/IndexingAddresses.res +10 -7
  36. package/src/IndexingAddresses.res.mjs +8 -7
  37. package/src/IndexingAddresses.resi +3 -1
  38. package/src/Internal.res +104 -39
  39. package/src/Internal.res.mjs +11 -1
  40. package/src/LogSelection.res +102 -165
  41. package/src/LogSelection.res.mjs +101 -116
  42. package/src/Main.res +49 -164
  43. package/src/Main.res.mjs +39 -104
  44. package/src/Metrics.res +43 -2
  45. package/src/Metrics.res.mjs +39 -3
  46. package/src/Prometheus.res +0 -35
  47. package/src/Prometheus.res.mjs +33 -68
  48. package/src/RawEvent.res +7 -2
  49. package/src/RawEvent.res.mjs +4 -4
  50. package/src/RollbackCommit.res +4 -1
  51. package/src/RollbackCommit.res.mjs +3 -2
  52. package/src/SimulateItems.res +20 -7
  53. package/src/SimulateItems.res.mjs +7 -11
  54. package/src/TestIndexer.res +1 -1
  55. package/src/TestIndexer.res.mjs +1 -1
  56. package/src/sources/BlockStore.res +46 -0
  57. package/src/sources/BlockStore.res.mjs +24 -0
  58. package/src/sources/EventRouter.res +19 -12
  59. package/src/sources/EventRouter.res.mjs +7 -5
  60. package/src/sources/Evm.res +58 -5
  61. package/src/sources/Evm.res.mjs +44 -5
  62. package/src/sources/EvmChain.res +14 -18
  63. package/src/sources/EvmChain.res.mjs +12 -13
  64. package/src/sources/FieldMask.res +39 -0
  65. package/src/sources/FieldMask.res.mjs +42 -0
  66. package/src/sources/Fuel.res +8 -3
  67. package/src/sources/Fuel.res.mjs +5 -4
  68. package/src/sources/HyperFuelSource.res +31 -40
  69. package/src/sources/HyperFuelSource.res.mjs +52 -44
  70. package/src/sources/HyperSync.res +31 -9
  71. package/src/sources/HyperSync.res.mjs +47 -31
  72. package/src/sources/HyperSync.resi +10 -3
  73. package/src/sources/HyperSyncClient.res +15 -4
  74. package/src/sources/HyperSyncHeightStream.res +1 -8
  75. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  76. package/src/sources/HyperSyncSource.res +58 -90
  77. package/src/sources/HyperSyncSource.res.mjs +57 -66
  78. package/src/sources/RpcSource.res +118 -120
  79. package/src/sources/RpcSource.res.mjs +92 -74
  80. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  81. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  82. package/src/sources/SimulateSource.res +5 -3
  83. package/src/sources/SimulateSource.res.mjs +12 -4
  84. package/src/sources/Source.res +19 -5
  85. package/src/sources/SourceManager.res +60 -10
  86. package/src/sources/SourceManager.res.mjs +54 -8
  87. package/src/sources/SourceManager.resi +10 -0
  88. package/src/sources/Svm.res +14 -10
  89. package/src/sources/Svm.res.mjs +23 -6
  90. package/src/sources/SvmHyperSyncClient.res +5 -6
  91. package/src/sources/SvmHyperSyncSource.res +82 -41
  92. package/src/sources/SvmHyperSyncSource.res.mjs +86 -41
  93. package/src/sources/TransactionStore.res +6 -107
  94. package/src/sources/TransactionStore.res.mjs +5 -86
  95. package/svm.schema.json +19 -0
package/src/Main.res CHANGED
@@ -39,40 +39,24 @@ let stateSchema = S.union([
39
39
  })),
40
40
  ])
41
41
 
42
- // Shape of the user-returned `{_gte?, _lte?, _every?}` filter chunk after
43
- // the ecosystem-specific wrapper is stripped. Shared across all ecosystems
44
- // the outer `block.number` / `block.height` / `slot` unwrap lives on each
45
- // ecosystem's `onBlockFilterSchema`, and the inner range fields are the
46
- // same everywhere.
47
- type blockRange = {
48
- _gte: option<int>,
49
- _lte: option<int>,
50
- _every: int,
51
- }
52
-
53
- // `S.strict` rejects unknown fields so typos like `_gt` / `_evry` surface
54
- // with a readable schema error pointing at the offending key, instead of
55
- // silently registering a broken filter. `_every` defaults to 1 inside the
56
- // schema so the caller always sees a plain `int`, and `intMin(1)` rejects
57
- // zero/negative strides — `(blockNumber - startBlock) % 0` would crash and
58
- // any negative stride would never match.
59
- let blockRangeSchema: S.t<blockRange> = S.object(s => {
60
- _gte: s.field("_gte", S.option(S.int)),
61
- _lte: s.field("_lte", S.option(S.int)),
62
- _every: s.field("_every", S.option(S.int->S.intMin(1))->S.Option.getOr(1)),
63
- })->S.strict
64
-
65
- let defaultBlockRange: blockRange = {_gte: None, _lte: None, _every: 1}
66
-
67
- let indexerStateRef: ref<option<IndexerState.t>> = ref(None)
42
+ // Runtime state lives in the process-wide `EnvioGlobal` record (shared
43
+ // across duplicate envio module instances); the slots are opaque there, so
44
+ // cast them to the real types here.
45
+ let getIndexerState = () =>
46
+ EnvioGlobal.value.indexerState->(Utils.magic: option<unknown> => option<IndexerState.t>)
47
+ let setIndexerState = (state: IndexerState.t) =>
48
+ EnvioGlobal.value.indexerState = Some(state->(Utils.magic: IndexerState.t => unknown))
68
49
 
69
50
  // Persistence is set by Main.start before handler modules load, so that
70
51
  // the exported indexer value can lazily expose DB state (startBlock,
71
52
  // endBlock, isRealtime, dynamic contract addresses) once it's ready.
72
- let globalPersistenceRef: ref<option<Persistence.t>> = ref(None)
53
+ let getGlobalPersistence = () =>
54
+ EnvioGlobal.value.persistence->(Utils.magic: option<unknown> => option<Persistence.t>)
55
+ let setGlobalPersistence = (persistence: Persistence.t) =>
56
+ EnvioGlobal.value.persistence = Some(persistence->(Utils.magic: Persistence.t => unknown))
73
57
 
74
58
  let getInitialChainState = (~chainId: int): option<Persistence.initialChainState> => {
75
- switch globalPersistenceRef.contents {
59
+ switch getGlobalPersistence() {
76
60
  | Some(persistence) =>
77
61
  switch persistence.storageStatus {
78
62
  | Ready(initialState) => initialState.chains->Array.find(c => c.id === chainId)
@@ -82,7 +66,7 @@ let getInitialChainState = (~chainId: int): option<Persistence.initialChainState
82
66
  }
83
67
  }
84
68
 
85
- // Importing `generated` must not trigger `Config.loadWithoutRegistrations()`,
69
+ // Importing `generated` must not trigger `Config.load()`,
86
70
  // so the exported indexer calls this lazily on first `indexer.chains` access.
87
71
  let buildChainsObject = (~config: Config.t) => {
88
72
  let chainIds = []
@@ -130,7 +114,7 @@ let buildChainsObject = (~config: Config.t) => {
130
114
  {
131
115
  enumerable: true,
132
116
  get: () => {
133
- switch indexerStateRef.contents {
117
+ switch getIndexerState() {
134
118
  | Some(state) => state->IndexerState.isRealtime
135
119
  // Before the global state is available (eg during handler
136
120
  // module load after resume), derive from persistence: every chain
@@ -164,7 +148,7 @@ let buildChainsObject = (~config: Config.t) => {
164
148
  {
165
149
  enumerable: true,
166
150
  get: () => {
167
- switch indexerStateRef.contents {
151
+ switch getIndexerState() {
168
152
  | Some(state) => {
169
153
  let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
170
154
  let chainState = state->IndexerState.getChainState(~chain)
@@ -345,121 +329,24 @@ let getGlobalIndexer = (): 'indexer => {
345
329
  let _ = RollbackCommit.register(callback->(Utils.magic: 'a => RollbackCommit.callback))
346
330
  }
347
331
 
348
- // Two-stage parse: first the ecosystem-specific outer schema unwraps the
349
- // wrapper (`block.number` / `block.height` / `slot`) and surfaces the
350
- // inner chunk as raw `unknown`; then the shared `blockRangeSchema`
351
- // validates the `{_gte?, _lte?, _every?}` fields. Keeping the inner
352
- // validation in one place means typos and shape mismatches surface with
353
- // the same user-friendly error regardless of ecosystem.
354
- let extractRange = (filter: unknown, ~name, ~ecosystem: Ecosystem.t): blockRange =>
355
- try {
356
- switch filter->S.parseOrThrow(ecosystem.onBlockFilterSchema) {
357
- | None => defaultBlockRange
358
- | Some(inner) => inner->S.parseOrThrow(blockRangeSchema)
359
- }
360
- } catch {
361
- | S.Raised(exn) =>
362
- JsError.throwWithMessage(
363
- `\`indexer.${ecosystem.onBlockMethodName}("${name}")\` \`where\` returned an invalid filter: ${exn
364
- ->Utils.prettifyExn
365
- ->(Utils.magic: exn => string)}`,
366
- )
367
- }
368
-
369
- // `where` is evaluated once per configured chain at registration time.
370
- // Decoded ranges/stride feed directly into `HandlerRegister.registerOnBlock`
371
- // so the fetcher's `(blockNumber - handlerStartBlock) % interval === 0`
372
- // math at `FetchState.res:619` stays untouched.
373
332
  let onBlockFn = (rawOptions: 'a, handler: 'b) => {
374
333
  HandlerRegister.throwIfFinishedRegistration(~methodName="onBlock")
375
- let config = Config.loadWithoutRegistrations()
376
- let ecosystem = config.ecosystem
377
334
  let raw =
378
335
  rawOptions->(
379
336
  Utils.magic: 'a => {
380
337
  "name": string,
381
- "where": option<Envio.onBlockWhereArgs<unknown> => unknown>,
338
+ "where": unknown,
382
339
  }
383
340
  )
384
- let typedHandler = handler->(Utils.magic: 'b => Internal.onBlockArgs => promise<unit>)
385
- let (chains, _) = buildChainsObject(~config)
386
- let chainsDict = chains->(Utils.magic: {..} => dict<unknown>)
387
- let name = raw["name"]
388
- let logger = Logging.createChild(~params={"onBlock": name})
389
-
390
- // `where` must be a function (unlike onEvent, which also accepts a static
391
- // value). A static value would have to be evaluated against every chain
392
- // independently, which has no useful semantic for block handlers.
393
- // Normalize undefined/null to None up front so the per-chain loop below
394
- // can't accidentally call `null` as a predicate (ReScript treats a JS
395
- // `null` value as `Some(null)` when the field is typed as option).
396
- let where = switch raw["where"]->(Utils.magic: option<'a> => unknown) {
397
- | w if w === %raw(`undefined`) || w === %raw(`null`) => None
398
- | w if typeof(w) === #function => Some(raw["where"]->Option.getUnsafe)
399
- | w =>
400
- JsError.throwWithMessage(
401
- `\`indexer.${ecosystem.onBlockMethodName}("${name}")\` expected \`where\` to be a function or omitted, but got ${(typeof(
402
- w,
403
- ) :> string)}.`,
404
- )
405
- }
406
-
407
- let matchedAny = ref(false)
408
-
409
- config.chainMap
410
- ->ChainMap.values
411
- ->Array.forEach(chainConfig => {
412
- let chainId = chainConfig.id
413
- let chainObj = chainsDict->Dict.getUnsafe(chainId->Int.toString)
414
-
415
- // Predicate returns `true` → match with no filter; `false` → skip;
416
- // any plain object → structured filter. `undefined`/`null` returns
417
- // are rejected — the TS type excludes `void`, so a missing return is
418
- // a user bug we surface early rather than silently match-all.
419
- let result = switch where {
420
- | None => %raw(`true`)
421
- | Some(predicate) => predicate({chain: chainObj})
422
- }
423
-
424
- let (shouldRegister, range) = if result === %raw(`true`) {
425
- (true, defaultBlockRange)
426
- } else if result === %raw(`false`) {
427
- (false, defaultBlockRange)
428
- } else if typeof(result) === #object && !(result->Array.isArray) && result !== %raw(`null`) {
429
- (true, extractRange(result, ~name, ~ecosystem))
430
- } else {
431
- // Reject numbers, strings, functions, arrays, undefined, null —
432
- // anything that isn't bool or a plain object would silently
433
- // misregister.
434
- JsError.throwWithMessage(
435
- `\`indexer.${ecosystem.onBlockMethodName}("${name}")\` \`where\` predicate returned an invalid value of type ${(typeof(
436
- result,
437
- ) :> string)}. Expected boolean or a filter object.`,
438
- )
439
- }
440
-
441
- if shouldRegister {
442
- matchedAny := true
443
- HandlerRegister.registerOnBlock(
444
- ~name,
445
- ~chainId,
446
- ~interval=range._every,
447
- ~startBlock=range._gte,
448
- ~endBlock=range._lte,
449
- ~handler=typedHandler,
450
- )
451
- }
452
- })
453
-
454
- // Catches misconfigured `where` predicates that return `false` for every
455
- // configured chain — the handler would otherwise never fire with no hint.
456
- // Includes the ecosystem-specific method name so SVM users see "onSlot"
457
- // and don't get confused looking for a "Block handler" they never wrote.
458
- if !matchedAny.contents {
459
- logger->Logging.childWarn(
460
- `\`indexer.${ecosystem.onBlockMethodName}\` matched 0 chains. Check the \`where\` predicate.`,
461
- )
462
- }
341
+ HandlerRegister.registerOnBlock(
342
+ ~name=raw["name"],
343
+ ~where=raw["where"],
344
+ ~handler=handler->(Utils.magic: 'b => Internal.onBlockArgs => promise<unit>),
345
+ ~getChainsObject=config => {
346
+ let (chains, _) = buildChainsObject(~config)
347
+ chains->(Utils.magic: {..} => dict<unknown>)
348
+ },
349
+ )
463
350
  }
464
351
 
465
352
  // Ecosystem-specific surface: EVM/Fuel expose event + block handlers; SVM
@@ -471,13 +358,13 @@ let getGlobalIndexer = (): 'indexer => {
471
358
  // `Api.res` calls `getGlobalIndexer()` at envio-package load, so the keys
472
359
  // array is memoized lazily: an early `createEffect` / `S` import that
473
360
  // never touches the indexer must not trigger a config parse. The memo is
474
- // safe because `Config.loadWithoutRegistrations` is itself pure.
361
+ // safe because `Config.load` is itself pure.
475
362
  let keysMemo: ref<option<array<string>>> = ref(None)
476
363
  let getKeys = () =>
477
364
  switch keysMemo.contents {
478
365
  | Some(k) => k
479
366
  | None => {
480
- let keys = switch Config.loadWithoutRegistrations().ecosystem.name {
367
+ let keys = switch Config.load().ecosystem.name {
481
368
  | Evm | Fuel => [
482
369
  "name",
483
370
  "description",
@@ -505,15 +392,14 @@ let getGlobalIndexer = (): 'indexer => {
505
392
 
506
393
  let get = (~prop: string) =>
507
394
  switch prop {
508
- | "name" => Config.loadWithoutRegistrations().name->(Utils.magic: string => unknown)
509
- | "description" =>
510
- Config.loadWithoutRegistrations().description->(Utils.magic: option<string> => unknown)
395
+ | "name" => Config.load().name->(Utils.magic: string => unknown)
396
+ | "description" => Config.load().description->(Utils.magic: option<string> => unknown)
511
397
  | "chainIds" => {
512
- let (_, chainIds) = buildChainsObject(~config=Config.loadWithoutRegistrations())
398
+ let (_, chainIds) = buildChainsObject(~config=Config.load())
513
399
  chainIds->(Utils.magic: array<int> => unknown)
514
400
  }
515
401
  | "chains" => {
516
- let (chains, _) = buildChainsObject(~config=Config.loadWithoutRegistrations())
402
+ let (chains, _) = buildChainsObject(~config=Config.load())
517
403
  chains->(Utils.magic: {..} => unknown)
518
404
  }
519
405
  | "onEvent" => onEventFn->Utils.magic
@@ -616,7 +502,7 @@ let startServer = (~getState, ~persistence: Persistence.t, ~isDevelopmentMode: b
616
502
  app->get("/metrics", (_req, res) => {
617
503
  res->set("Content-Type", PromClient.defaultRegister->PromClient.getContentType)
618
504
  let _ =
619
- Metrics.collect(~state=indexerStateRef.contents)->Promise.thenResolve(metrics =>
505
+ Metrics.collect(~state=getIndexerState())->Promise.thenResolve(metrics =>
620
506
  res->endWithData(metrics)
621
507
  )
622
508
  })
@@ -657,7 +543,7 @@ type mainArgs = Yargs.parsedArgs<args>
657
543
  let getEnvioInfo = () => Config.getPublicConfigJson()->Config.stripSensitiveData
658
544
 
659
545
  let migrate = async (~reset) => {
660
- let config = Config.loadWithoutRegistrations()
546
+ let config = Config.load()
661
547
  let persistence = PgStorage.makePersistenceFromConfig(~config)
662
548
  await persistence->Persistence.init(
663
549
  ~reset,
@@ -670,7 +556,7 @@ let migrate = async (~reset) => {
670
556
  }
671
557
 
672
558
  let dropSchema = async () => {
673
- let config = Config.loadWithoutRegistrations()
559
+ let config = Config.load()
674
560
  let persistence = PgStorage.makePersistenceFromConfig(~config)
675
561
  await persistence.storage.reset()
676
562
  await persistence.storage.close()
@@ -685,7 +571,7 @@ let start = async (
685
571
  ~reset=false,
686
572
  ~isTest=false,
687
573
  ~exitAfterFirstEventBlock=false,
688
- ~patchConfig: option<(Config.t, HandlerRegister.registrations) => Config.t>=?,
574
+ ~patchConfig: option<(Config.t, HandlerRegister.registrationsByChainId) => Config.t>=?,
689
575
  ) => {
690
576
  let mainArgs: mainArgs = process->argv->Yargs.hideBin->Yargs.yargs->Yargs.argv
691
577
  let explicitTui = switch mainArgs.tuiOff {
@@ -699,31 +585,30 @@ let start = async (
699
585
  }
700
586
  // Initialize persistence first so the exported indexer value contains state from the database
701
587
  // when handler files are loaded (they may access the indexer at module top level).
702
- let configWithoutRegistrations = Config.loadWithoutRegistrations()
588
+ let config = Config.load()
703
589
  // isDevelopmentMode controls whether the indexer stays alive after all
704
590
  // chains finish (keepProcessAlive) and whether the console API is exposed.
705
591
  // Set by `envio dev` via the public config's `isDev` field; `envio start`
706
592
  // leaves it false so the process exits cleanly when indexing completes.
707
- let isDevelopmentMode = !isTest && configWithoutRegistrations.isDev
593
+ let isDevelopmentMode = !isTest && config.isDev
708
594
  let persistence = switch persistence {
709
595
  | Some(p) => p
710
- | None => PgStorage.makePersistenceFromConfig(~config=configWithoutRegistrations)
596
+ | None => PgStorage.makePersistenceFromConfig(~config)
711
597
  }
712
- globalPersistenceRef := Some(persistence)
598
+ setGlobalPersistence(persistence)
713
599
  await persistence->Persistence.init(
714
600
  ~reset,
715
- ~chainConfigs=configWithoutRegistrations.chainMap->ChainMap.values,
601
+ ~chainConfigs=config.chainMap->ChainMap.values,
716
602
  ~envioInfo=getEnvioInfo(),
717
603
  ~resetCommand=isDevelopmentMode ? "envio dev -r" : "envio start -r",
718
604
  ~runCommand=Some(isDevelopmentMode ? "envio dev" : "envio start"),
719
605
  )
720
606
 
721
- // `Config.loadWithoutRegistrations` never sees registration state; handler,
722
- // contractRegister, and eventFilters are baked into each event config only
723
- // by the returned value here.
724
- let (config, registrations) = await HandlerLoader.registerAllHandlers(
725
- ~config=configWithoutRegistrations,
726
- )
607
+ // Loads user handler files, which register handler/contractRegister/where
608
+ // state into the global `HandlerRegister` registry as a side effect; this
609
+ // returns that state resolved into per-chain registrations. `config` itself
610
+ // is never mutated by registration — it holds only event definitions.
611
+ let registrationsByChainId = await HandlerLoader.registerAllHandlers(~config)
727
612
  let config = if isTest {
728
613
  {...config, shouldRollbackOnReorg: false}
729
614
  } else {
@@ -731,7 +616,7 @@ let start = async (
731
616
  }
732
617
 
733
618
  let config = switch patchConfig {
734
- | Some(patchConfig) => patchConfig(config, registrations)
619
+ | Some(patchConfig) => patchConfig(config, registrationsByChainId)
735
620
  | None => config
736
621
  }
737
622
  // The single fatal-error handler, invoked once via IndexerState.errorExit.
@@ -757,7 +642,7 @@ let start = async (
757
642
 
758
643
  if !isTest {
759
644
  startServer(~persistence, ~isDevelopmentMode, ~getState=() =>
760
- switch indexerStateRef.contents {
645
+ switch getIndexerState() {
761
646
  | None => Initializing({})
762
647
  | Some(state) => {
763
648
  let chains =
@@ -778,7 +663,7 @@ let start = async (
778
663
  ~config,
779
664
  ~persistence,
780
665
  ~initialState=persistence->Persistence.getInitializedState,
781
- ~registrations,
666
+ ~registrationsByChainId,
782
667
  ~isDevelopmentMode,
783
668
  ~shouldUseTui,
784
669
  ~exitAfterFirstEventBlock,
@@ -787,7 +672,7 @@ let start = async (
787
672
  if shouldUseTui {
788
673
  let _rerender = Tui.start(~getState=() => state)
789
674
  }
790
- indexerStateRef := Some(state)
675
+ setIndexerState(state)
791
676
  state->IndexerLoop.start
792
677
  await runUntilFatalError
793
678
  }
package/src/Main.res.mjs CHANGED
@@ -13,6 +13,7 @@ import * as ChainMap from "./ChainMap.res.mjs";
13
13
  import * as PgStorage from "./PgStorage.res.mjs";
14
14
  import * as ChainState from "./ChainState.res.mjs";
15
15
  import * as Prometheus from "./Prometheus.res.mjs";
16
+ import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
16
17
  import * as IndexerLoop from "./IndexerLoop.res.mjs";
17
18
  import * as Persistence from "./Persistence.res.mjs";
18
19
  import * as PromClient from "prom-client";
@@ -61,28 +62,24 @@ let stateSchema = S$RescriptSchema.union([
61
62
  }))
62
63
  ]);
63
64
 
64
- let blockRangeSchema = S$RescriptSchema.strict(S$RescriptSchema.object(s => ({
65
- _gte: s.f("_gte", S$RescriptSchema.option(S$RescriptSchema.int)),
66
- _lte: s.f("_lte", S$RescriptSchema.option(S$RescriptSchema.int)),
67
- _every: s.f("_every", S$RescriptSchema.Option.getOr(S$RescriptSchema.option(S$RescriptSchema.intMin(S$RescriptSchema.int, 1, undefined)), 1))
68
- })));
65
+ function getIndexerState() {
66
+ return EnvioGlobal.value.indexerState;
67
+ }
69
68
 
70
- let defaultBlockRange = {
71
- _gte: undefined,
72
- _lte: undefined,
73
- _every: 1
74
- };
69
+ function setIndexerState(state) {
70
+ EnvioGlobal.value.indexerState = Primitive_option.some(state);
71
+ }
75
72
 
76
- let indexerStateRef = {
77
- contents: undefined
78
- };
73
+ function getGlobalPersistence() {
74
+ return EnvioGlobal.value.persistence;
75
+ }
79
76
 
80
- let globalPersistenceRef = {
81
- contents: undefined
82
- };
77
+ function setGlobalPersistence(persistence) {
78
+ EnvioGlobal.value.persistence = Primitive_option.some(persistence);
79
+ }
83
80
 
84
81
  function getInitialChainState(chainId) {
85
- let persistence = globalPersistenceRef.contents;
82
+ let persistence = EnvioGlobal.value.persistence;
86
83
  if (persistence === undefined) {
87
84
  return;
88
85
  }
@@ -134,7 +131,7 @@ function buildChainsObject(config) {
134
131
  }), "isRealtime", {
135
132
  enumerable: true,
136
133
  get: () => {
137
- let state = indexerStateRef.contents;
134
+ let state = EnvioGlobal.value.indexerState;
138
135
  if (state !== undefined) {
139
136
  return IndexerState.isRealtime(Primitive_option.valFromOption(state));
140
137
  } else if (Env.updateSyncTimeOnRestart) {
@@ -162,7 +159,7 @@ function buildChainsObject(config) {
162
159
  }), "addresses", {
163
160
  enumerable: true,
164
161
  get: () => {
165
- let state = indexerStateRef.contents;
162
+ let state = EnvioGlobal.value.indexerState;
166
163
  if (state !== undefined) {
167
164
  let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
168
165
  let chainState = IndexerState.getChainState(Primitive_option.valFromOption(state), chain);
@@ -278,69 +275,9 @@ function getGlobalIndexer() {
278
275
  HandlerRegister.throwIfFinishedRegistration("~internalAndWillBeRemovedSoon_onRollbackCommit");
279
276
  RollbackCommit.register(callback);
280
277
  };
281
- let extractRange = (filter, name, ecosystem) => {
282
- try {
283
- let inner = S$RescriptSchema.parseOrThrow(filter, ecosystem.onBlockFilterSchema);
284
- if (inner !== undefined) {
285
- return S$RescriptSchema.parseOrThrow(Primitive_option.valFromOption(inner), blockRangeSchema);
286
- } else {
287
- return defaultBlockRange;
288
- }
289
- } catch (raw_exn) {
290
- let exn = Primitive_exceptions.internalToException(raw_exn);
291
- if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
292
- return Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` \`where\` returned an invalid filter: ` + Utils.prettifyExn(exn._1));
293
- }
294
- throw exn;
295
- }
296
- };
297
278
  let onBlockFn = (rawOptions, handler) => {
298
279
  HandlerRegister.throwIfFinishedRegistration("onBlock");
299
- let config = Config.loadWithoutRegistrations();
300
- let ecosystem = config.ecosystem;
301
- let match = buildChainsObject(config);
302
- let chains = match[0];
303
- let name = rawOptions.name;
304
- let logger = Logging.createChild({
305
- onBlock: name
306
- });
307
- let w = rawOptions.where;
308
- let where = w === undefined || w === null ? undefined : (
309
- typeof w === "function" ? rawOptions.where : Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` expected \`where\` to be a function or omitted, but got ` + typeof w + `.`)
310
- );
311
- let matchedAny = {
312
- contents: false
313
- };
314
- ChainMap.values(config.chainMap).forEach(chainConfig => {
315
- let chainId = chainConfig.id;
316
- let chainObj = chains[chainId.toString()];
317
- let result = where !== undefined ? where({
318
- chain: chainObj
319
- }) : true;
320
- let match = result === true ? [
321
- true,
322
- defaultBlockRange
323
- ] : (
324
- result === false ? [
325
- false,
326
- defaultBlockRange
327
- ] : (
328
- typeof result === "object" && !Array.isArray(result) && result !== null ? [
329
- true,
330
- extractRange(result, name, ecosystem)
331
- ] : Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` \`where\` predicate returned an invalid value of type ` + typeof result + `. Expected boolean or a filter object.`)
332
- )
333
- );
334
- if (!match[0]) {
335
- return;
336
- }
337
- let range = match[1];
338
- matchedAny.contents = true;
339
- HandlerRegister.registerOnBlock(name, chainId, range._every, range._gte, range._lte, handler);
340
- });
341
- if (!matchedAny.contents) {
342
- return Logging.childWarn(logger, `\`indexer.` + ecosystem.onBlockMethodName + `\` matched 0 chains. Check the \`where\` predicate.`);
343
- }
280
+ HandlerRegister.registerOnBlock(rawOptions.name, rawOptions.where, handler, config => buildChainsObject(config)[0]);
344
281
  };
345
282
  let keysMemo = {
346
283
  contents: undefined
@@ -350,7 +287,7 @@ function getGlobalIndexer() {
350
287
  if (k !== undefined) {
351
288
  return k;
352
289
  }
353
- let match = Config.loadWithoutRegistrations().ecosystem.name;
290
+ let match = Config.load().ecosystem.name;
354
291
  let keys;
355
292
  let exit = 0;
356
293
  switch (match) {
@@ -388,15 +325,15 @@ function getGlobalIndexer() {
388
325
  let get = prop => {
389
326
  switch (prop) {
390
327
  case "chainIds" :
391
- return buildChainsObject(Config.loadWithoutRegistrations())[1];
328
+ return buildChainsObject(Config.load())[1];
392
329
  case "chains" :
393
- return buildChainsObject(Config.loadWithoutRegistrations())[0];
330
+ return buildChainsObject(Config.load())[0];
394
331
  case "contractRegister" :
395
332
  return contractRegisterFn;
396
333
  case "description" :
397
- return Config.loadWithoutRegistrations().description;
334
+ return Config.load().description;
398
335
  case "name" :
399
- return Config.loadWithoutRegistrations().name;
336
+ return Config.load().name;
400
337
  case "onEvent" :
401
338
  return onEventFn;
402
339
  case "onInstruction" :
@@ -478,7 +415,7 @@ function startServer(getState, persistence, isDevelopmentMode) {
478
415
  });
479
416
  app.get("/metrics", (_req, res) => {
480
417
  res.set("Content-Type", PromClient.register.contentType);
481
- Metrics.collect(indexerStateRef.contents).then(metrics => res.end(metrics));
418
+ Metrics.collect(EnvioGlobal.value.indexerState).then(metrics => res.end(metrics));
482
419
  });
483
420
  app.get("/metrics/runtime", (_req, res) => {
484
421
  res.set("Content-Type", runtimeRegistry.contentType);
@@ -501,14 +438,14 @@ function getEnvioInfo() {
501
438
  }
502
439
 
503
440
  async function migrate(reset) {
504
- let config = Config.loadWithoutRegistrations();
441
+ let config = Config.load();
505
442
  let persistence = PgStorage.makePersistenceFromConfig(config, undefined);
506
443
  await Persistence.init(persistence, ChainMap.values(config.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), "envio local db-migrate setup", undefined, reset);
507
444
  return await persistence.storage.close();
508
445
  }
509
446
 
510
447
  async function dropSchema() {
511
- let config = Config.loadWithoutRegistrations();
448
+ let config = Config.load();
512
449
  let persistence = PgStorage.makePersistenceFromConfig(config, undefined);
513
450
  await persistence.storage.reset();
514
451
  return await persistence.storage.close();
@@ -526,14 +463,12 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
526
463
  let shouldUseTui = isTest ? false : (
527
464
  explicitTui !== undefined ? explicitTui : !Envio.isNonInteractive()
528
465
  );
529
- let configWithoutRegistrations = Config.loadWithoutRegistrations();
530
- let isDevelopmentMode = !isTest && configWithoutRegistrations.isDev;
531
- let persistence$1 = persistence !== undefined ? persistence : PgStorage.makePersistenceFromConfig(configWithoutRegistrations, undefined);
532
- globalPersistenceRef.contents = persistence$1;
533
- await Persistence.init(persistence$1, ChainMap.values(configWithoutRegistrations.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), isDevelopmentMode ? "envio dev -r" : "envio start -r", isDevelopmentMode ? "envio dev" : "envio start", reset);
534
- let match = await HandlerLoader.registerAllHandlers(configWithoutRegistrations);
535
- let registrations = match[1];
536
- let config = match[0];
466
+ let config = Config.load();
467
+ let isDevelopmentMode = !isTest && config.isDev;
468
+ let persistence$1 = persistence !== undefined ? persistence : PgStorage.makePersistenceFromConfig(config, undefined);
469
+ EnvioGlobal.value.persistence = Primitive_option.some(persistence$1);
470
+ await Persistence.init(persistence$1, ChainMap.values(config.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), isDevelopmentMode ? "envio dev -r" : "envio start -r", isDevelopmentMode ? "envio dev" : "envio start", reset);
471
+ let registrationsByChainId = await HandlerLoader.registerAllHandlers(config);
537
472
  let config$1 = isTest ? ({
538
473
  name: config.name,
539
474
  description: config.description,
@@ -555,7 +490,7 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
555
490
  allEntities: config.allEntities,
556
491
  allEnums: config.allEnums
557
492
  }) : config;
558
- let config$2 = patchConfig !== undefined ? patchConfig(config$1, registrations) : config$1;
493
+ let config$2 = patchConfig !== undefined ? patchConfig(config$1, registrationsByChainId) : config$1;
559
494
  let onErrorReject = {
560
495
  contents: undefined
561
496
  };
@@ -575,7 +510,7 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
575
510
  Prometheus.RollbackEnabled.set(config$2.shouldRollbackOnReorg);
576
511
  if (!isTest) {
577
512
  startServer(() => {
578
- let state = indexerStateRef.contents;
513
+ let state = EnvioGlobal.value.indexerState;
579
514
  if (state === undefined) {
580
515
  return {
581
516
  status: "initializing"
@@ -593,11 +528,11 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
593
528
  };
594
529
  }, persistence$1, isDevelopmentMode);
595
530
  }
596
- let state = IndexerState.makeFromDbState(config$2, persistence$1, Persistence.getInitializedState(persistence$1), registrations, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, undefined, undefined, onError);
531
+ let state = IndexerState.makeFromDbState(config$2, persistence$1, Persistence.getInitializedState(persistence$1), registrationsByChainId, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, undefined, undefined, onError);
597
532
  if (shouldUseTui) {
598
533
  Tui.start(() => state);
599
534
  }
600
- indexerStateRef.contents = Primitive_option.some(state);
535
+ EnvioGlobal.value.indexerState = Primitive_option.some(state);
601
536
  IndexerLoop.start(state);
602
537
  return await runUntilFatalError;
603
538
  }
@@ -605,10 +540,10 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
605
540
  export {
606
541
  chainDataSchema,
607
542
  stateSchema,
608
- blockRangeSchema,
609
- defaultBlockRange,
610
- indexerStateRef,
611
- globalPersistenceRef,
543
+ getIndexerState,
544
+ setIndexerState,
545
+ getGlobalPersistence,
546
+ setGlobalPersistence,
612
547
  getInitialChainState,
613
548
  buildChainsObject,
614
549
  getGlobalIndexer,