envio 3.0.0-alpha.10 → 3.0.0-alpha.12

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 (40) hide show
  1. package/evm.schema.json +18 -0
  2. package/index.d.ts +13 -0
  3. package/package.json +5 -5
  4. package/src/Batch.res +2 -0
  5. package/src/Batch.res.mjs +1 -0
  6. package/src/ChainManager.res +3 -1
  7. package/src/ChainManager.res.mjs +1 -1
  8. package/src/Config.res +7 -1
  9. package/src/Config.res.mjs +6 -3
  10. package/src/Env.res +1 -0
  11. package/src/Env.res.mjs +4 -1
  12. package/src/GlobalState.res +0 -1
  13. package/src/GlobalState.res.mjs +0 -1
  14. package/src/PgStorage.res +2 -1
  15. package/src/PgStorage.res.mjs +2 -1
  16. package/src/Prometheus.res +12 -0
  17. package/src/Prometheus.res.mjs +45 -30
  18. package/src/TestIndexer.res +76 -4
  19. package/src/TestIndexer.res.mjs +144 -100
  20. package/src/Utils.res +22 -0
  21. package/src/Utils.res.mjs +18 -0
  22. package/src/db/InternalTable.res +5 -3
  23. package/src/db/InternalTable.res.mjs +6 -4
  24. package/src/sources/EvmChain.res +3 -0
  25. package/src/sources/EvmChain.res.mjs +2 -1
  26. package/src/sources/HyperFuelSource.res +5 -1
  27. package/src/sources/HyperFuelSource.res.mjs +5 -1
  28. package/src/sources/HyperSync.res +12 -3
  29. package/src/sources/HyperSync.res.mjs +9 -7
  30. package/src/sources/HyperSync.resi +4 -0
  31. package/src/sources/HyperSyncHeightStream.res +3 -1
  32. package/src/sources/HyperSyncHeightStream.res.mjs +3 -1
  33. package/src/sources/HyperSyncSource.res +10 -3
  34. package/src/sources/HyperSyncSource.res.mjs +8 -4
  35. package/src/sources/Rpc.res +21 -21
  36. package/src/sources/Rpc.res.mjs +18 -18
  37. package/src/sources/RpcSource.res +51 -56
  38. package/src/sources/RpcSource.res.mjs +50 -53
  39. package/src/sources/Svm.res +15 -2
  40. package/src/sources/Svm.res.mjs +7 -1
package/evm.schema.json CHANGED
@@ -458,6 +458,15 @@
458
458
  ],
459
459
  "format": "uint32",
460
460
  "minimum": 0
461
+ },
462
+ "polling_interval": {
463
+ "description": "How frequently (in milliseconds) to check for new blocks in realtime. Default is 1000ms. Note: Setting this higher than block time does not reduce RPC usage as every block is still fetched to check for reorgs.",
464
+ "type": [
465
+ "integer",
466
+ "null"
467
+ ],
468
+ "format": "uint32",
469
+ "minimum": 0
461
470
  }
462
471
  },
463
472
  "additionalProperties": false,
@@ -553,6 +562,15 @@
553
562
  ],
554
563
  "format": "uint32",
555
564
  "minimum": 0
565
+ },
566
+ "polling_interval": {
567
+ "description": "How frequently (in milliseconds) to check for new blocks in realtime. Default is 1000ms. Note: Setting this higher than block time does not reduce RPC usage as every block is still fetched to check for reorgs.",
568
+ "type": [
569
+ "integer",
570
+ "null"
571
+ ],
572
+ "format": "uint32",
573
+ "minimum": 0
556
574
  }
557
575
  },
558
576
  "additionalProperties": false,
package/index.d.ts CHANGED
@@ -518,6 +518,14 @@ type AddressRegistration = {
518
518
  type ConfigEntities<Config extends IndexerConfigTypes> =
519
519
  Config["entities"] extends Record<string, object> ? Config["entities"] : {};
520
520
 
521
+ /** Entity operations available on test indexer for direct entity manipulation. */
522
+ type EntityOps<Entity> = {
523
+ /** Get an entity by ID. Returns undefined if not found. */
524
+ readonly get: (id: string) => Promise<Entity | undefined>;
525
+ /** Set (create or update) an entity. */
526
+ readonly set: (entity: Entity) => void;
527
+ };
528
+
521
529
  /** A single change representing entity modifications at a specific block. */
522
530
  type EntityChange<Config extends IndexerConfigTypes> = {
523
531
  /** The block where the changes occurred. */
@@ -581,4 +589,9 @@ export type TestIndexerFromConfig<Config extends IndexerConfigTypes> = {
581
589
  /** Changes happened during the processing. */
582
590
  readonly changes: readonly EntityChange<Config>[];
583
591
  }>;
592
+ } & {
593
+ /** Entity operations for direct manipulation outside of handlers. */
594
+ readonly [K in keyof ConfigEntities<Config>]: EntityOps<
595
+ ConfigEntities<Config>[K]
596
+ >;
584
597
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "v3.0.0-alpha.10",
3
+ "version": "v3.0.0-alpha.12",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.js",
@@ -29,10 +29,10 @@
29
29
  "node": ">=22.0.0"
30
30
  },
31
31
  "optionalDependencies": {
32
- "envio-linux-x64": "v3.0.0-alpha.10",
33
- "envio-linux-arm64": "v3.0.0-alpha.10",
34
- "envio-darwin-x64": "v3.0.0-alpha.10",
35
- "envio-darwin-arm64": "v3.0.0-alpha.10"
32
+ "envio-linux-x64": "v3.0.0-alpha.12",
33
+ "envio-linux-arm64": "v3.0.0-alpha.12",
34
+ "envio-darwin-x64": "v3.0.0-alpha.12",
35
+ "envio-darwin-arm64": "v3.0.0-alpha.12"
36
36
  },
37
37
  "dependencies": {
38
38
  "@clickhouse/client": "1.12.1",
package/src/Batch.res CHANGED
@@ -6,6 +6,7 @@ open Utils.UnsafeIntOperators
6
6
  type chainAfterBatch = {
7
7
  batchSize: int,
8
8
  progressBlockNumber: int,
9
+ sourceBlockNumber: int,
9
10
  totalEventsProcessed: int,
10
11
  fetchState: FetchState.t,
11
12
  isProgressAtHeadWhenBatchCreated: bool,
@@ -108,6 +109,7 @@ let getProgressedChainsById = {
108
109
  {
109
110
  batchSize,
110
111
  progressBlockNumber: progressBlockNumberAfterBatch,
112
+ sourceBlockNumber: chainBeforeBatch.sourceBlockNumber,
111
113
  totalEventsProcessed: chainBeforeBatch.totalEventsProcessed + batchSize,
112
114
  fetchState: fetchStateAfterBatch,
113
115
  isProgressAtHeadWhenBatchCreated: progressBlockNumberAfterBatch >=
package/src/Batch.res.mjs CHANGED
@@ -62,6 +62,7 @@ function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAft
62
62
  return {
63
63
  batchSize: batchSize,
64
64
  progressBlockNumber: progressBlockNumberAfterBatch,
65
+ sourceBlockNumber: chainBeforeBatch.sourceBlockNumber,
65
66
  totalEventsProcessed: chainBeforeBatch.totalEventsProcessed + batchSize,
66
67
  fetchState: fetchStateAfterBatch,
67
68
  isProgressAtHeadWhenBatchCreated: progressBlockNumberAfterBatch >= chainBeforeBatch.sourceBlockNumber
@@ -11,7 +11,9 @@ type t = {
11
11
  // A chain is in reorg threshold when progressBlockNumber > sourceBlockNumber - maxReorgDepth.
12
12
  // This matches the logic in InternalTable.Checkpoints.makeGetReorgCheckpointsQuery.
13
13
  let isProgressInReorgThreshold = (~progressBlockNumber, ~sourceBlockNumber, ~maxReorgDepth) => {
14
- maxReorgDepth > 0 && progressBlockNumber > sourceBlockNumber - maxReorgDepth
14
+ maxReorgDepth > 0 &&
15
+ sourceBlockNumber > 0 &&
16
+ progressBlockNumber > sourceBlockNumber - maxReorgDepth
15
17
  }
16
18
 
17
19
  let calculateTargetBufferSize = (~activeChainsCount, ~config: Config.t) => {
@@ -11,7 +11,7 @@ import * as ChainFetcher from "./ChainFetcher.res.mjs";
11
11
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
12
12
 
13
13
  function isProgressInReorgThreshold(progressBlockNumber, sourceBlockNumber, maxReorgDepth) {
14
- if (maxReorgDepth > 0) {
14
+ if (maxReorgDepth > 0 && sourceBlockNumber > 0) {
15
15
  return progressBlockNumber > (sourceBlockNumber - maxReorgDepth | 0);
16
16
  } else {
17
17
  return false;
package/src/Config.res CHANGED
@@ -8,6 +8,7 @@ type sourceSyncOptions = {
8
8
  backoffMillis?: int,
9
9
  queryTimeoutMillis?: int,
10
10
  fallbackStallTimeout?: int,
11
+ pollingInterval?: int,
11
12
  }
12
13
 
13
14
  type contract = {
@@ -65,6 +66,7 @@ type sourceSync = {
65
66
  backoffMillis: int,
66
67
  queryTimeoutMillis: int,
67
68
  fallbackStallTimeout: int,
69
+ pollingInterval: int,
68
70
  }
69
71
 
70
72
  type multichain = | @as("ordered") Ordered | @as("unordered") Unordered
@@ -109,6 +111,7 @@ let rpcConfigSchema = S.schema(s =>
109
111
  "backoffMillis": s.matches(S.option(S.int)),
110
112
  "fallbackStallTimeout": s.matches(S.option(S.int)),
111
113
  "queryTimeoutMillis": s.matches(S.option(S.int)),
114
+ "pollingInterval": s.matches(S.option(S.int)),
112
115
  }
113
116
  )
114
117
 
@@ -323,6 +326,7 @@ let fromPublic = (
323
326
  let backoffMillis = rpcConfig["backoffMillis"]
324
327
  let queryTimeoutMillis = rpcConfig["queryTimeoutMillis"]
325
328
  let fallbackStallTimeout = rpcConfig["fallbackStallTimeout"]
329
+ let pollingInterval = rpcConfig["pollingInterval"]
326
330
  let hasSyncConfig =
327
331
  initialBlockInterval->Option.isSome ||
328
332
  backoffMultiplicative->Option.isSome ||
@@ -330,7 +334,8 @@ let fromPublic = (
330
334
  intervalCeiling->Option.isSome ||
331
335
  backoffMillis->Option.isSome ||
332
336
  queryTimeoutMillis->Option.isSome ||
333
- fallbackStallTimeout->Option.isSome
337
+ fallbackStallTimeout->Option.isSome ||
338
+ pollingInterval->Option.isSome
334
339
  let syncConfig: option<sourceSyncOptions> = if hasSyncConfig {
335
340
  Some({
336
341
  ?initialBlockInterval,
@@ -340,6 +345,7 @@ let fromPublic = (
340
345
  ?backoffMillis,
341
346
  ?queryTimeoutMillis,
342
347
  ?fallbackStallTimeout,
348
+ ?pollingInterval,
343
349
  })
344
350
  } else {
345
351
  None
@@ -30,7 +30,8 @@ var rpcConfigSchema = S$RescriptSchema.schema(function (s) {
30
30
  intervalCeiling: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
31
31
  backoffMillis: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
32
32
  fallbackStallTimeout: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
33
- queryTimeoutMillis: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int))
33
+ queryTimeoutMillis: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int)),
34
+ pollingInterval: s.m(S$RescriptSchema.option(S$RescriptSchema.$$int))
34
35
  };
35
36
  });
36
37
 
@@ -235,7 +236,8 @@ function fromPublic(publicConfigJson, codegenChainsOpt, maxAddrInPartitionOpt, u
235
236
  var backoffMillis = rpcConfig.backoffMillis;
236
237
  var queryTimeoutMillis = rpcConfig.queryTimeoutMillis;
237
238
  var fallbackStallTimeout = rpcConfig.fallbackStallTimeout;
238
- var hasSyncConfig = Belt_Option.isSome(initialBlockInterval) || Belt_Option.isSome(backoffMultiplicative) || Belt_Option.isSome(accelerationAdditive) || Belt_Option.isSome(intervalCeiling) || Belt_Option.isSome(backoffMillis) || Belt_Option.isSome(queryTimeoutMillis) || Belt_Option.isSome(fallbackStallTimeout);
239
+ var pollingInterval = rpcConfig.pollingInterval;
240
+ var hasSyncConfig = Belt_Option.isSome(initialBlockInterval) || Belt_Option.isSome(backoffMultiplicative) || Belt_Option.isSome(accelerationAdditive) || Belt_Option.isSome(intervalCeiling) || Belt_Option.isSome(backoffMillis) || Belt_Option.isSome(queryTimeoutMillis) || Belt_Option.isSome(fallbackStallTimeout) || Belt_Option.isSome(pollingInterval);
239
241
  var syncConfig = hasSyncConfig ? ({
240
242
  initialBlockInterval: initialBlockInterval,
241
243
  backoffMultiplicative: backoffMultiplicative,
@@ -243,7 +245,8 @@ function fromPublic(publicConfigJson, codegenChainsOpt, maxAddrInPartitionOpt, u
243
245
  intervalCeiling: intervalCeiling,
244
246
  backoffMillis: backoffMillis,
245
247
  queryTimeoutMillis: queryTimeoutMillis,
246
- fallbackStallTimeout: fallbackStallTimeout
248
+ fallbackStallTimeout: fallbackStallTimeout,
249
+ pollingInterval: pollingInterval
247
250
  }) : undefined;
248
251
  return {
249
252
  url: rpcConfig.url,
package/src/Env.res CHANGED
@@ -153,6 +153,7 @@ module Db = {
153
153
  //the SSL modes should be provided as string otherwise as 'require' | 'allow' | 'prefer' | 'verify-full'
154
154
  ~devFallback=Bool(false),
155
155
  )
156
+ let maxConnections = envSafe->EnvSafe.get("ENVIO_PG_MAX_CONNECTIONS", S.int, ~fallback=2)
156
157
  }
157
158
 
158
159
  module ClickHouseSink = {
package/src/Env.res.mjs CHANGED
@@ -139,6 +139,8 @@ var publicSchema = EnvSafe.get(envSafe, "ENVIO_PG_PUBLIC_SCHEMA", S$RescriptSche
139
139
 
140
140
  var ssl = EnvSafe.get(envSafe, "ENVIO_PG_SSL_MODE", Postgres.sslOptionsSchema, undefined, undefined, false, undefined);
141
141
 
142
+ var maxConnections = EnvSafe.get(envSafe, "ENVIO_PG_MAX_CONNECTIONS", S$RescriptSchema.$$int, undefined, 2, undefined, undefined);
143
+
142
144
  var Db = {
143
145
  host: host,
144
146
  port: port,
@@ -146,7 +148,8 @@ var Db = {
146
148
  password: password,
147
149
  database: database,
148
150
  publicSchema: publicSchema,
149
- ssl: ssl
151
+ ssl: ssl,
152
+ maxConnections: maxConnections
150
153
  };
151
154
 
152
155
  var host$1 = EnvSafe.get(envSafe, "ENVIO_CLICKHOUSE_SINK_HOST", S$RescriptSchema.option(S$RescriptSchema.string), undefined, undefined, undefined, undefined);
@@ -159,7 +159,6 @@ let updateChainMetadataTable = (
159
159
  chainsData->Js.Dict.set(
160
160
  cf.chainConfig.id->Belt.Int.toString,
161
161
  {
162
- blockHeight: cf.fetchState.knownHeight,
163
162
  firstEventBlockNumber: cf.firstEventBlockNumber->Js.Null.fromOption,
164
163
  isHyperSync: (cf.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
165
164
  latestFetchedBlockNumber: cf.fetchState->FetchState.bufferBlockNumber,
@@ -127,7 +127,6 @@ function updateChainMetadataTable(cm, persistence, throttler) {
127
127
  chainsData[String(cf.chainConfig.id)] = {
128
128
  first_event_block: Js_null.fromOption(cf.firstEventBlockNumber),
129
129
  buffer_block: FetchState.bufferBlockNumber(cf.fetchState),
130
- source_block: cf.fetchState.knownHeight,
131
130
  ready_at: Js_null.fromOption(cf.timestampCaughtUpToHeadOrEndblock),
132
131
  _is_hyper_sync: SourceManager.getActiveSource(cf.sourceManager).poweredByHyperSync,
133
132
  _num_batches_fetched: cf.numBatchesFetched
package/src/PgStorage.res CHANGED
@@ -19,7 +19,7 @@ let makeClient = () => {
19
19
  : Some(_str => ())
20
20
  ),
21
21
  transform: {undefined: Null},
22
- max: 2,
22
+ max: Env.Db.maxConnections,
23
23
  // debug: (~connection, ~query, ~params as _, ~types as _) => Js.log2(connection, query),
24
24
  },
25
25
  )
@@ -933,6 +933,7 @@ let rec writeBatch = async (
933
933
  ): InternalTable.Chains.progressedChain => {
934
934
  chainId: chainAfterBatch.fetchState.chainId,
935
935
  progressBlockNumber: chainAfterBatch.progressBlockNumber,
936
+ sourceBlockNumber: chainAfterBatch.sourceBlockNumber,
936
937
  totalEventsProcessed: chainAfterBatch.totalEventsProcessed,
937
938
  }),
938
939
  ),
@@ -37,7 +37,7 @@ function makeClient() {
37
37
  username: Env.Db.user,
38
38
  password: Env.Db.password,
39
39
  ssl: Env.Db.ssl,
40
- max: 2,
40
+ max: Env.Db.maxConnections,
41
41
  onnotice: Caml_obj.equal(Env.userLogLevel, "warn") || Caml_obj.equal(Env.userLogLevel, "error") ? undefined : (function (_str) {
42
42
 
43
43
  }),
@@ -635,6 +635,7 @@ async function writeBatch(sql, batch, rawEvents, pgSchema, rollbackTargetCheckpo
635
635
  return {
636
636
  chainId: chainAfterBatch.fetchState.chainId,
637
637
  progressBlockNumber: chainAfterBatch.progressBlockNumber,
638
+ sourceBlockNumber: chainAfterBatch.sourceBlockNumber,
638
639
  totalEventsProcessed: chainAfterBatch.totalEventsProcessed
639
640
  };
640
641
  })));
@@ -416,6 +416,18 @@ let sourceLabelsSchema = S.schema(s =>
416
416
  }
417
417
  )
418
418
 
419
+ module SourceRequestCount = {
420
+ let counter = SafeCounter.makeOrThrow(
421
+ ~name="envio_source_request_count",
422
+ ~help="The number of requests made to data sources.",
423
+ ~labelSchema=sourceLabelsSchema,
424
+ )
425
+
426
+ let increment = (~sourceName, ~chainId) => {
427
+ counter->SafeCounter.increment(~labels={"source": sourceName, "chainId": chainId})
428
+ }
429
+ }
430
+
419
431
  module SourceHeight = {
420
432
  let gauge = SafeGauge.makeOrThrow(
421
433
  ~name="envio_source_height",
@@ -513,6 +513,20 @@ var sourceLabelsSchema = S$RescriptSchema.schema(function (s) {
513
513
  };
514
514
  });
515
515
 
516
+ var counter$3 = makeOrThrow("envio_source_request_count", "The number of requests made to data sources.", sourceLabelsSchema);
517
+
518
+ function increment$2(sourceName, chainId) {
519
+ increment(counter$3, {
520
+ source: sourceName,
521
+ chainId: chainId
522
+ });
523
+ }
524
+
525
+ var SourceRequestCount = {
526
+ counter: counter$3,
527
+ increment: increment$2
528
+ };
529
+
516
530
  var gauge$11 = makeOrThrow$1("envio_source_height", "The latest known block number reported by the source. This value may lag behind the actual chain height, as it is updated only when queried.", sourceLabelsSchema);
517
531
 
518
532
  function set$11(sourceName, chainId, blockNumber) {
@@ -540,13 +554,13 @@ var SourceGetHeightDuration = {
540
554
 
541
555
  var gauge$12 = makeOrThrow$1("envio_reorg_count", "Total number of reorgs detected", chainIdLabelsSchema);
542
556
 
543
- function increment$2(chain) {
557
+ function increment$3(chain) {
544
558
  increment$1(gauge$12, chain);
545
559
  }
546
560
 
547
561
  var ReorgCount = {
548
562
  gauge: gauge$12,
549
- increment: increment$2
563
+ increment: increment$3
550
564
  };
551
565
 
552
566
  var gauge$13 = makeOrThrow$1("envio_reorg_detection_block_number", "The block number where reorg was detected the last time. This doesn't mean that the block was reorged, this is simply where we found block hash to be different.", chainIdLabelsSchema);
@@ -593,7 +607,7 @@ var timeCounter = new PromClient.Counter({
593
607
  help: "Rollback on reorg total time in milliseconds"
594
608
  });
595
609
 
596
- var counter$3 = new PromClient.Counter({
610
+ var counter$4 = new PromClient.Counter({
597
611
  name: "envio_rollback_count",
598
612
  help: "Number of successful rollbacks on reorg"
599
613
  });
@@ -603,17 +617,17 @@ var eventsCounter = new PromClient.Counter({
603
617
  help: "Number of events rollbacked on reorg"
604
618
  });
605
619
 
606
- function increment$3(timeMillis, rollbackedProcessedEvents) {
620
+ function increment$4(timeMillis, rollbackedProcessedEvents) {
607
621
  timeCounter.inc(Hrtime.intFromMillis(timeMillis));
608
- counter$3.inc();
622
+ counter$4.inc();
609
623
  eventsCounter.inc(rollbackedProcessedEvents);
610
624
  }
611
625
 
612
626
  var RollbackSuccess = {
613
627
  timeCounter: timeCounter,
614
- counter: counter$3,
628
+ counter: counter$4,
615
629
  eventsCounter: eventsCounter,
616
- increment: increment$3
630
+ increment: increment$4
617
631
  };
618
632
 
619
633
  var entityNameLabelsSchema = S$RescriptSchema.object(function (s) {
@@ -622,18 +636,18 @@ var entityNameLabelsSchema = S$RescriptSchema.object(function (s) {
622
636
 
623
637
  var timeCounter$1 = makeOrThrow("envio_rollback_history_prune_time", "The total time spent pruning entity history which is not in the reorg threshold. (milliseconds)", entityNameLabelsSchema);
624
638
 
625
- var counter$4 = makeOrThrow("envio_rollback_history_prune_count", "Number of successful entity history prunes", entityNameLabelsSchema);
639
+ var counter$5 = makeOrThrow("envio_rollback_history_prune_count", "Number of successful entity history prunes", entityNameLabelsSchema);
626
640
 
627
- function increment$4(timeMillis, entityName) {
641
+ function increment$5(timeMillis, entityName) {
628
642
  handleInt(timeCounter$1, entityName, Hrtime.intFromMillis(timeMillis));
629
- increment(counter$4, entityName);
643
+ increment(counter$5, entityName);
630
644
  }
631
645
 
632
646
  var RollbackHistoryPrune = {
633
647
  entityNameLabelsSchema: entityNameLabelsSchema,
634
648
  timeCounter: timeCounter$1,
635
- counter: counter$4,
636
- increment: increment$4
649
+ counter: counter$5,
650
+ increment: increment$5
637
651
  };
638
652
 
639
653
  var gauge$16 = makeOrThrow$1("envio_rollback_target_block_number", "The block number reorg was rollbacked to the last time.", chainIdLabelsSchema);
@@ -693,18 +707,18 @@ var ProgressEventsCount = {
693
707
  set: set$18
694
708
  };
695
709
 
696
- var counter$5 = new PromClient.Counter({
710
+ var counter$6 = new PromClient.Counter({
697
711
  name: "envio_progress_batches_count",
698
712
  help: "The number of batches processed and reflected in the database."
699
713
  });
700
714
 
701
- function increment$5() {
702
- counter$5.inc();
715
+ function increment$6() {
716
+ counter$6.inc();
703
717
  }
704
718
 
705
719
  var ProgressBatchCount = {
706
- counter: counter$5,
707
- increment: increment$5
720
+ counter: counter$6,
721
+ increment: increment$6
708
722
  };
709
723
 
710
724
  var gauge$20 = makeOrThrow$1("envio_progress_latency", "The latency in milliseconds between the latest processed event creation and the time it was written to storage.", chainIdLabelsSchema);
@@ -748,15 +762,15 @@ var EffectCacheCount = {
748
762
  set: set$20
749
763
  };
750
764
 
751
- var counter$6 = makeOrThrow("envio_effect_cache_invalidations_count", "The number of effect cache invalidations.", effectLabelsSchema);
765
+ var counter$7 = makeOrThrow("envio_effect_cache_invalidations_count", "The number of effect cache invalidations.", effectLabelsSchema);
752
766
 
753
- function increment$6(effectName) {
754
- increment(counter$6, effectName);
767
+ function increment$7(effectName) {
768
+ increment(counter$7, effectName);
755
769
  }
756
770
 
757
771
  var EffectCacheInvalidationsCount = {
758
- counter: counter$6,
759
- increment: increment$6
772
+ counter: counter$7,
773
+ increment: increment$7
760
774
  };
761
775
 
762
776
  var gauge$22 = makeOrThrow$1("envio_effect_queue_count", "The number of effect calls waiting in the rate limit queue.", effectLabelsSchema);
@@ -781,7 +795,7 @@ var timeCounter$4 = makeOrThrow("envio_storage_load_time", "Processing time take
781
795
 
782
796
  var sumTimeCounter$1 = makeOrThrow("envio_storage_load_sum_time", "Cumulative time spent loading data from storage during the indexing process. (milliseconds)", operationLabelsSchema);
783
797
 
784
- var counter$7 = makeOrThrow("envio_storage_load_count", "Cumulative number of successful storage load operations during the indexing process.", operationLabelsSchema);
798
+ var counter$8 = makeOrThrow("envio_storage_load_count", "Cumulative number of successful storage load operations during the indexing process.", operationLabelsSchema);
785
799
 
786
800
  var whereSizeCounter = makeOrThrow("envio_storage_load_where_size", "Cumulative number of filter conditions ('where' items) used in storage load operations during the indexing process.", operationLabelsSchema);
787
801
 
@@ -810,7 +824,7 @@ function endOperation(timerRef, operation, whereSize, size) {
810
824
  Utils.Dict.deleteInPlace(operations, operation);
811
825
  }
812
826
  handleInt(sumTimeCounter$1, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(timerRef))));
813
- increment(counter$7, operation);
827
+ increment(counter$8, operation);
814
828
  handleInt(whereSizeCounter, operation, whereSize);
815
829
  handleInt(sizeCounter, operation, size);
816
830
  }
@@ -819,7 +833,7 @@ var StorageLoad = {
819
833
  operationLabelsSchema: operationLabelsSchema,
820
834
  timeCounter: timeCounter$4,
821
835
  sumTimeCounter: sumTimeCounter$1,
822
- counter: counter$7,
836
+ counter: counter$8,
823
837
  whereSizeCounter: whereSizeCounter,
824
838
  sizeCounter: sizeCounter,
825
839
  operations: operations,
@@ -833,18 +847,18 @@ var sinkLabelsSchema = S$RescriptSchema.object(function (s) {
833
847
 
834
848
  var timeCounter$5 = makeOrThrow("envio_sink_write_time", "Processing time taken to write data to sink. (milliseconds)", sinkLabelsSchema);
835
849
 
836
- var counter$8 = makeOrThrow("envio_sink_write_count", "Cumulative number of successful sink write operations during the indexing process.", sinkLabelsSchema);
850
+ var counter$9 = makeOrThrow("envio_sink_write_count", "Cumulative number of successful sink write operations during the indexing process.", sinkLabelsSchema);
837
851
 
838
- function increment$7(sinkName, timeMillis) {
852
+ function increment$8(sinkName, timeMillis) {
839
853
  handleInt(timeCounter$5, sinkName, timeMillis);
840
- increment(counter$8, sinkName);
854
+ increment(counter$9, sinkName);
841
855
  }
842
856
 
843
857
  var SinkWrite = {
844
858
  sinkLabelsSchema: sinkLabelsSchema,
845
859
  timeCounter: timeCounter$5,
846
- counter: counter$8,
847
- increment: increment$7
860
+ counter: counter$9,
861
+ increment: increment$8
848
862
  };
849
863
 
850
864
  export {
@@ -884,6 +898,7 @@ export {
884
898
  IndexingBufferBlockNumber ,
885
899
  IndexingEndBlock ,
886
900
  sourceLabelsSchema ,
901
+ SourceRequestCount ,
887
902
  SourceHeight ,
888
903
  SourceGetHeightDuration ,
889
904
  ReorgCount ,
@@ -324,6 +324,51 @@ let validateBlockRange = (
324
324
  }
325
325
  }
326
326
 
327
+ // Entity operations for direct manipulation outside of handlers
328
+ let makeEntityGet = (
329
+ ~state: testIndexerState,
330
+ ~entityConfig: Internal.entityConfig,
331
+ ): (string => promise<option<Internal.entity>>) => {
332
+ entityId => {
333
+ if state.processInProgress {
334
+ Js.Exn.raiseError(
335
+ `Cannot call ${entityConfig.name}.get() while indexer.process() is running. ` ++
336
+ "Wait for process() to complete before accessing entities directly.",
337
+ )
338
+ }
339
+ let entityDict =
340
+ state.entities->Js.Dict.get(entityConfig.name)->Option.getWithDefault(Js.Dict.empty())
341
+ Promise.resolve(entityDict->Js.Dict.get(entityId))
342
+ }
343
+ }
344
+
345
+ let makeEntitySet = (
346
+ ~state: testIndexerState,
347
+ ~entityConfig: Internal.entityConfig,
348
+ ): (Internal.entity => unit) => {
349
+ entity => {
350
+ if state.processInProgress {
351
+ Js.Exn.raiseError(
352
+ `Cannot call ${entityConfig.name}.set() while indexer.process() is running. ` ++
353
+ "Wait for process() to complete before modifying entities directly.",
354
+ )
355
+ }
356
+ let entityDict = switch state.entities->Js.Dict.get(entityConfig.name) {
357
+ | Some(dict) => dict
358
+ | None =>
359
+ let dict = Js.Dict.empty()
360
+ state.entities->Js.Dict.set(entityConfig.name, dict)
361
+ dict
362
+ }
363
+ entityDict->Js.Dict.set(entity.id, entity)
364
+ }
365
+ }
366
+
367
+ type entityOps = {
368
+ get: string => promise<option<Internal.entity>>,
369
+ set: Internal.entity => unit,
370
+ }
371
+
327
372
  let makeCreateTestIndexer = (
328
373
  ~config: Config.t,
329
374
  ~workerPath: string,
@@ -343,8 +388,33 @@ let makeCreateTestIndexer = (
343
388
  entityConfigs,
344
389
  processChanges: [],
345
390
  }
346
- {
347
- process: processConfig => {
391
+
392
+ // Build entity operations for each user entity
393
+ let entityOpsDict: Js.Dict.t<entityOps> = Js.Dict.empty()
394
+ allEntities->Array.forEach(entityConfig => {
395
+ // Only create ops for user entities (not internal tables like dynamic_contract_registry)
396
+ if entityConfig.name !== InternalTable.DynamicContractRegistry.name {
397
+ entityOpsDict->Js.Dict.set(
398
+ entityConfig.name,
399
+ {
400
+ get: makeEntityGet(~state, ~entityConfig),
401
+ set: makeEntitySet(~state, ~entityConfig),
402
+ },
403
+ )
404
+ }
405
+ })
406
+
407
+ // Build the result object with process + entity operations
408
+ let result: Js.Dict.t<unknown> = Js.Dict.empty()
409
+ entityOpsDict
410
+ ->Js.Dict.entries
411
+ ->Array.forEach(((name, ops)) => {
412
+ result->Js.Dict.set(name, ops->(Utils.magic: entityOps => unknown))
413
+ })
414
+
415
+ result->Js.Dict.set(
416
+ "process",
417
+ (processConfig => {
348
418
  // Check if already processing
349
419
  if state.processInProgress {
350
420
  Js.Exn.raiseError(
@@ -491,8 +561,10 @@ let makeCreateTestIndexer = (
491
561
  }
492
562
  })
493
563
  })
494
- },
495
- }
564
+ })->(Utils.magic: ('a => promise<processResult>) => unknown),
565
+ )
566
+
567
+ result->(Utils.magic: Js.Dict.t<unknown> => t<'processConfig>)
496
568
  }
497
569
  }
498
570