envio 3.14.0-subgraph → 3.14.1

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 (63) hide show
  1. package/evm.schema.json +1 -1
  2. package/package.json +6 -6
  3. package/src/Config.res +0 -9
  4. package/src/Config.res.mjs +2 -6
  5. package/src/Core.res +0 -21
  6. package/src/Core.res.mjs +0 -17
  7. package/src/EventProcessing.res +6 -6
  8. package/src/EventProcessing.res.mjs +6 -8
  9. package/src/HandlerLoader.res +22 -48
  10. package/src/HandlerLoader.res.mjs +15 -41
  11. package/src/HandlerTypeCheck.res +207 -0
  12. package/src/HandlerTypeCheck.res.mjs +262 -0
  13. package/src/LoadLayer.res +7 -39
  14. package/src/LoadLayer.res.mjs +38 -65
  15. package/src/LoadLayer.resi +0 -28
  16. package/src/PgStorage.res +5 -24
  17. package/src/PgStorage.res.mjs +1 -18
  18. package/src/UserContext.res +62 -377
  19. package/src/UserContext.res.mjs +39 -297
  20. package/src/bindings/NodeJs.res +31 -0
  21. package/src/bindings/NodeJs.res.mjs +3 -0
  22. package/src/db/Table.res +0 -10
  23. package/src/db/Table.res.mjs +11 -22
  24. package/src/sources/EvmRpcClient.res +2 -0
  25. package/src/sources/RpcSource.res +17 -4
  26. package/src/sources/RpcSource.res.mjs +23 -5
  27. package/src/sources/SourceManager.res +63 -40
  28. package/src/sources/SourceManager.res.mjs +32 -20
  29. package/src/HandlerTypeCheck.mjs +0 -14
  30. package/src/HandlerTypeCheckWorker.mjs +0 -68
  31. package/src/SyncWrites.res +0 -86
  32. package/src/SyncWrites.res.mjs +0 -85
  33. package/src/subgraph/abi-types.ts +0 -63
  34. package/src/subgraph/assemblyscript.ts +0 -85
  35. package/src/subgraph/blocks.ts +0 -183
  36. package/src/subgraph/calls.ts +0 -149
  37. package/src/subgraph/conformance.ts +0 -83
  38. package/src/subgraph/errors.ts +0 -96
  39. package/src/subgraph/graph-cli-worker.mjs +0 -26
  40. package/src/subgraph/graph-cli.ts +0 -190
  41. package/src/subgraph/graph-ts-enums.ts +0 -28
  42. package/src/subgraph/graph-ts-types/VERSION +0 -2
  43. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
  44. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
  45. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
  46. package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
  47. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
  48. package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
  49. package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
  50. package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
  51. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  52. package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
  53. package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
  54. package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
  55. package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
  56. package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
  57. package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
  58. package/src/subgraph/graph-ts-types/index.d.ts +0 -102
  59. package/src/subgraph/graph-ts.ts +0 -2285
  60. package/src/subgraph/hosts.ts +0 -158
  61. package/src/subgraph/rpc.ts +0 -65
  62. package/src/subgraph/runtime.ts +0 -595
  63. package/src/subgraph/scope.ts +0 -68
@@ -93,6 +93,14 @@ function make(param) {
93
93
  },
94
94
  Error: new Error()
95
95
  };
96
+ case "BehindHead" :
97
+ let blockNumber$1 = result.blockNumber;
98
+ throw {
99
+ RE_EXN_ID: Source.SourceBehindHead,
100
+ blockNumber: blockNumber$1 !== undefined ? blockNumber$1 : missing("blockNumber"),
101
+ requestStats: result.requestStats,
102
+ Error: new Error()
103
+ };
96
104
  }
97
105
  let parsingTimeRef = Performance.now();
98
106
  let parsedQueueItems = EvmEventItem.toInternalItems(result.items, onEventRegistrations, chainId);
@@ -114,12 +122,22 @@ function make(param) {
114
122
  let getBlockHashes = async (blockNumbers, param) => {
115
123
  let match = await rpcClient.getBlockHashes(blockNumbers);
116
124
  let result = match[0];
117
- let message = result.message;
125
+ let match$1 = result.message;
126
+ let match$2 = result.behindHeadBlock;
118
127
  return {
119
- result: message !== undefined ? ({
120
- TAG: "Error",
121
- _0: Primitive_exceptions.internalToException(new Error(message))
122
- }) : ({
128
+ result: match$1 !== undefined ? (
129
+ match$2 !== undefined ? ({
130
+ TAG: "Error",
131
+ _0: {
132
+ RE_EXN_ID: Source.SourceBehindHead,
133
+ blockNumber: match$2,
134
+ requestStats: result.requestStats
135
+ }
136
+ }) : ({
137
+ TAG: "Error",
138
+ _0: Primitive_exceptions.internalToException(new Error(match$1))
139
+ })
140
+ ) : ({
123
141
  TAG: "Ok",
124
142
  _0: match[1]
125
143
  }),
@@ -24,7 +24,7 @@ let recordStatsInto = (
24
24
  aggregates: dict<requestStatAgg>,
25
25
  requestStats: array<Source.requestStat>,
26
26
  ) => {
27
- requestStats->Array.forEach(({method, seconds, responseBlocks: ?responseBlocks}) => {
27
+ requestStats->Array.forEach(({method, seconds, ?responseBlocks}) => {
28
28
  let agg = switch aggregates->Utils.Dict.dangerouslyGetNonOption(method) {
29
29
  | Some(agg) => agg
30
30
  | None =>
@@ -577,11 +577,14 @@ let backoffBeforeRetry = async (
577
577
  ~isRealtime,
578
578
  ~backoffMillis,
579
579
  ~minBackoffMillis=0,
580
+ ~failoverAfterRetries=2,
580
581
  ~excludedSources=?,
581
582
  ) => {
582
- // Give the source two attempts before demoting it, then re-try a failover
583
+ // Give the source a few attempts before demoting it, then re-try a failover
583
584
  // every second attempt.
584
- let switchedToWorkingSource = if retry >= 2 && retry->mod(2) === 0 {
585
+ let switchedToWorkingSource = if (
586
+ retry >= failoverAfterRetries && (retry - failoverAfterRetries)->mod(2) === 0
587
+ ) {
585
588
  let now = Date.now()
586
589
  sourceState.lastFailedAt = Some(now)
587
590
  switch sourceManager->getNextSource(~isRealtime, ~excludedSources?) {
@@ -608,6 +611,25 @@ let backoffBeforeRetry = async (
608
611
  }
609
612
  }
610
613
 
614
+ // A replica behind its siblings usually catches up within seconds, and failing
615
+ // over demotes an otherwise healthy source for the whole recovery timeout. So
616
+ // the source keeps the query for about 10s (100ms doubling, capped at 1s per
617
+ // attempt) before a failover is tried. Past that window the source is behind
618
+ // for real, and with nowhere to fail over to it is polled less and less often,
619
+ // doubling from 1s up to the shared cap.
620
+ let behindHeadFailoverRetries = 12
621
+
622
+ let behindHeadBackoffMillis = retry =>
623
+ if retry < behindHeadFailoverRetries {
624
+ Utils.expBackoff(~base=100, ~exp=retry, ~maxMillis=1_000)
625
+ } else {
626
+ Utils.expBackoff(
627
+ ~base=1_000,
628
+ ~exp=retry - behindHeadFailoverRetries,
629
+ ~maxMillis=maxRetryBackoffMillis,
630
+ )
631
+ }
632
+
611
633
  // The queried block hasn't reached the backend instance that served the
612
634
  // request. Expected around the head of a load-balanced backend, so early
613
635
  // attempts stay quiet and short; a source that stays behind fails over like any
@@ -619,18 +641,28 @@ let retryBehindHead = async (
619
641
  ~isRealtime,
620
642
  ~logger: Pino.t,
621
643
  ~blockNumber: int,
622
- ~method: string,
623
- ~err: exn,
624
644
  ~excludedSources=?,
625
645
  ) => {
626
- let backoffMillis = retry->retryBackoffMillis
627
- let log = retry >= 4 ? Logging.childWarn : Logging.childTrace
646
+ let backoffMillis = retry->behindHeadBackoffMillis
647
+ let source = sourceState.source.name
648
+ let block = blockNumber->Int.toString
649
+ // The condition is routine and resolves without the user doing anything, so
650
+ // the wording says so; the error object would only add internals.
651
+ let (log, msg) = if retry < behindHeadFailoverRetries {
652
+ (
653
+ Logging.childTrace,
654
+ `${source} hasn't reached block ${block} on all of its servers yet. This is normal near the latest block and resolves by itself - retrying shortly.`,
655
+ )
656
+ } else {
657
+ (
658
+ Logging.childWarn,
659
+ `${source} is still catching up to block ${block}. This is a delay on the provider's side; indexing will continue automatically, using another data source if one is configured.`,
660
+ )
661
+ }
628
662
  logger->log({
629
- "msg": `Block #${blockNumber->Int.toString} is not available on the ${sourceState.source.name} source yet. Instances of a load-balanced backend drift slightly around the head, so this is expected - indexing continues after an automatic retry.`,
630
- "method": method,
663
+ "msg": msg,
631
664
  "retry": retry,
632
665
  "backOffMilliseconds": backoffMillis,
633
- "err": err->Utils.prettifyExn,
634
666
  })
635
667
  await sourceManager->backoffBeforeRetry(
636
668
  sourceState,
@@ -638,6 +670,7 @@ let retryBehindHead = async (
638
670
  ~isRealtime,
639
671
  ~backoffMillis,
640
672
  ~minBackoffMillis=minRecoverableBackoffMillis,
673
+ ~failoverAfterRetries=behindHeadFailoverRetries,
641
674
  ~excludedSources?,
642
675
  )
643
676
  }
@@ -861,8 +894,7 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
861
894
  // Re-armed because distrusting a stream is a one-shot — the next height it
862
895
  // delivers takes it back at its word — and the silence that earned it can
863
896
  // come straight back.
864
- let rec armPokeTimeout = () =>
865
- pokeTimeoutId := Some(setTimeout(() => {
897
+ let rec armPokeTimeout = () => pokeTimeoutId := Some(setTimeout(() => {
866
898
  pokeTimeoutId := None
867
899
  if !settled.contents {
868
900
  watched->Array.forEach(subscription => subscription.distrustStream())
@@ -879,8 +911,7 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
879
911
  // operator about when they hear about a quiet chain, and spreading that
880
912
  // would report it early. Fires once — the warning is worth saying once per
881
913
  // wait, and a fallback stays recruited.
882
- let armStallTimeout = () =>
883
- stallTimeoutId := Some(setTimeout(() => {
914
+ let armStallTimeout = () => stallTimeoutId := Some(setTimeout(() => {
884
915
  stallTimeoutId := None
885
916
  if !settled.contents {
886
917
  stalled := true
@@ -917,9 +948,7 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
917
948
  }
918
949
 
919
950
  // Recruited to poll, not to stream: see `watch`.
920
- fallbackSources->Array.forEach(
921
- sourceState => sourceState->watch(~withStream=false),
922
- )
951
+ fallbackSources->Array.forEach(sourceState => sourceState->watch(~withStream=false))
923
952
 
924
953
  // A fallback recruited here can still be holding a stream from an
925
954
  // earlier wait that made it a primary, and a live stream is not
@@ -940,7 +969,6 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
940
969
  })
941
970
  }
942
971
 
943
-
944
972
  let executeQuery = async (
945
973
  sourceManager: t,
946
974
  ~query: FetchState.query,
@@ -949,11 +977,12 @@ let executeQuery = async (
949
977
  ) => {
950
978
  // Read when the manager actually runs out of sources, not on the way in: the
951
979
  // reason is recorded by the failure that disabled the last one.
952
- let noSourcesError = () => switch sourceManager.disableReason {
953
- | Some(reason) =>
954
- `The indexer doesn't have data-sources which can continue fetching. The last one was disabled because ${reason}`
955
- | None => "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team."
956
- }
980
+ let noSourcesError = () =>
981
+ switch sourceManager.disableReason {
982
+ | Some(reason) =>
983
+ `The indexer doesn't have data-sources which can continue fetching. The last one was disabled because ${reason}`
984
+ | None => "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team."
985
+ }
957
986
 
958
987
  // Sources where the query is impossible - lazily allocated, excluded for the duration of this query
959
988
  let excludedSourcesRef = ref(None)
@@ -1030,7 +1059,7 @@ let executeQuery = async (
1030
1059
  await sourceManager->waitForRateLimitReset(~resetMs, ~retry, ~logger)
1031
1060
  retryRef := retryRef.contents + 1
1032
1061
 
1033
- | Source.SourceBehindHead({blockNumber, requestStats}) as err =>
1062
+ | Source.SourceBehindHead({blockNumber, requestStats}) =>
1034
1063
  sourceState->recordRequestStats(requestStats)
1035
1064
  await sourceManager->retryBehindHead(
1036
1065
  sourceState,
@@ -1038,8 +1067,6 @@ let executeQuery = async (
1038
1067
  ~isRealtime,
1039
1068
  ~logger,
1040
1069
  ~blockNumber,
1041
- ~method="getItems",
1042
- ~err,
1043
1070
  ~excludedSources=?excludedSourcesRef.contents,
1044
1071
  )
1045
1072
  retryRef := retryRef.contents + 1
@@ -1135,7 +1162,11 @@ let executeQuery = async (
1135
1162
  })
1136
1163
  retryRef := 0
1137
1164
 
1138
- | FailedGettingItems({?exn, attemptedToBlock, retry: WithBackoff({message, backoffMillis})}) =>
1165
+ | FailedGettingItems({
1166
+ ?exn,
1167
+ attemptedToBlock,
1168
+ retry: WithBackoff({message, backoffMillis}),
1169
+ }) =>
1139
1170
  await sourceManager->retryFailedPage(
1140
1171
  sourceState,
1141
1172
  ~retry,
@@ -1200,22 +1231,14 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
1200
1231
  | Error(exn) => throw(exn)
1201
1232
  }
1202
1233
  } catch {
1203
- | Source.RateLimited({resetMs, requestStats}) =>
1204
- sourceState->recordRequestStats(requestStats)
1234
+ // A failure arrives as `res.result`, whose `res.requestStats` is already
1235
+ // recorded above, so the stats the error carries are not recorded again.
1236
+ | Source.RateLimited({resetMs}) =>
1205
1237
  await sourceManager->waitForRateLimitReset(~resetMs, ~retry, ~logger)
1206
1238
  retryRef := retryRef.contents + 1
1207
1239
 
1208
- | Source.SourceBehindHead({blockNumber, requestStats}) as err =>
1209
- sourceState->recordRequestStats(requestStats)
1210
- await sourceManager->retryBehindHead(
1211
- sourceState,
1212
- ~retry,
1213
- ~isRealtime,
1214
- ~logger,
1215
- ~blockNumber,
1216
- ~method="getBlockHashes",
1217
- ~err,
1218
- )
1240
+ | Source.SourceBehindHead({blockNumber}) =>
1241
+ await sourceManager->retryBehindHead(sourceState, ~retry, ~isRealtime, ~logger, ~blockNumber)
1219
1242
  retryRef := retryRef.contents + 1
1220
1243
 
1221
1244
  | Source.InconsistentResponse(_) as err =>
@@ -470,10 +470,11 @@ function getNextSource(sourceManager, isRealtime, excludedSources) {
470
470
  }
471
471
  }
472
472
 
473
- async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillisOpt, excludedSources) {
473
+ async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillisOpt, failoverAfterRetriesOpt, excludedSources) {
474
474
  let minBackoffMillis = minBackoffMillisOpt !== undefined ? minBackoffMillisOpt : 0;
475
+ let failoverAfterRetries = failoverAfterRetriesOpt !== undefined ? failoverAfterRetriesOpt : 2;
475
476
  let switchedToWorkingSource;
476
- if (retry >= 2 && retry % 2 === 0) {
477
+ if (retry >= failoverAfterRetries && (retry - failoverAfterRetries | 0) % 2 === 0) {
477
478
  let now = Date.now();
478
479
  sourceState.lastFailedAt = now;
479
480
  let next = getNextSource(sourceManager, isRealtime, excludedSources);
@@ -497,17 +498,31 @@ async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime,
497
498
  }
498
499
  }
499
500
 
500
- async function retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger, blockNumber, method, err, excludedSources) {
501
- let backoffMillis = Utils.expBackoff(100, retry, 60000);
502
- let log = retry >= 4 ? Logging.childWarn : Logging.childTrace;
503
- log(logger, {
504
- msg: `Block #` + blockNumber.toString() + ` is not available on the ` + sourceState.source.name + ` source yet. Instances of a load-balanced backend drift slightly around the head, so this is expected - indexing continues after an automatic retry.`,
505
- method: method,
501
+ function behindHeadBackoffMillis(retry) {
502
+ if (retry < 12) {
503
+ return Utils.expBackoff(100, retry, 1000);
504
+ } else {
505
+ return Utils.expBackoff(1000, retry - 12 | 0, 60000);
506
+ }
507
+ }
508
+
509
+ async function retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger, blockNumber, excludedSources) {
510
+ let backoffMillis = behindHeadBackoffMillis(retry);
511
+ let source = sourceState.source.name;
512
+ let block = blockNumber.toString();
513
+ let match = retry < 12 ? [
514
+ Logging.childTrace,
515
+ source + ` hasn't reached block ` + block + ` on all of its servers yet. This is normal near the latest block and resolves by itself - retrying shortly.`
516
+ ] : [
517
+ Logging.childWarn,
518
+ source + ` is still catching up to block ` + block + `. This is a delay on the provider's side; indexing will continue automatically, using another data source if one is configured.`
519
+ ];
520
+ match[0](logger, {
521
+ msg: match[1],
506
522
  retry: retry,
507
- backOffMilliseconds: backoffMillis,
508
- err: Utils.prettifyExn(err)
523
+ backOffMilliseconds: backoffMillis
509
524
  });
510
- return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, excludedSources);
525
+ return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, 12, excludedSources);
511
526
  }
512
527
 
513
528
  function stalledLog(retry, warnAfter, msg, stalled) {
@@ -536,7 +551,7 @@ async function retryInconsistentResponse(sourceManager, sourceState, retry, isRe
536
551
  err: Utils.prettifyExn(err)
537
552
  });
538
553
  Stdlib_Option.forEach(sourceState.source.onReorg, cb => cb());
539
- return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, excludedSources);
554
+ return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined, excludedSources);
540
555
  }
541
556
 
542
557
  async function retryFailedPage(sourceManager, sourceState, retry, isRealtime, logger, msg, attemptedToBlock, backoffMillis, minBackoffMillisOpt, err, excludedSources) {
@@ -549,7 +564,7 @@ async function retryFailedPage(sourceManager, sourceState, retry, isRealtime, lo
549
564
  retry: retry,
550
565
  err: Stdlib_Option.map(err, Utils.prettifyExn)
551
566
  });
552
- return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillis, excludedSources);
567
+ return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillis, undefined, excludedSources);
553
568
  }
554
569
 
555
570
  function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPolling) {
@@ -736,7 +751,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
736
751
  retryRef = retryRef + 1 | 0;
737
752
  } else if (err.RE_EXN_ID === Source.SourceBehindHead) {
738
753
  recordStatsInto(sourceState.requestStats, err.requestStats);
739
- await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$2, err.blockNumber, "getItems", err, excludedSourcesRef);
754
+ await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$2, err.blockNumber, excludedSourcesRef);
740
755
  retryRef = retryRef + 1 | 0;
741
756
  } else if (err.RE_EXN_ID === Source.InconsistentResponse) {
742
757
  await retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger$2, "getItems", err, excludedSourcesRef);
@@ -866,13 +881,10 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
866
881
  } catch (raw_err) {
867
882
  let err = Primitive_exceptions.internalToException(raw_err);
868
883
  if (err.RE_EXN_ID === Source.RateLimited) {
869
- let match = err._1;
870
- recordStatsInto(sourceState.requestStats, match.requestStats);
871
- await waitForRateLimitReset(sourceManager, match.resetMs, retry, logger$1);
884
+ await waitForRateLimitReset(sourceManager, err._1.resetMs, retry, logger$1);
872
885
  retryRef = retryRef + 1 | 0;
873
886
  } else if (err.RE_EXN_ID === Source.SourceBehindHead) {
874
- recordStatsInto(sourceState.requestStats, err.requestStats);
875
- await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$1, err.blockNumber, "getBlockHashes", err, undefined);
887
+ await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$1, err.blockNumber, undefined);
876
888
  retryRef = retryRef + 1 | 0;
877
889
  } else if (err.RE_EXN_ID === Source.InconsistentResponse) {
878
890
  await retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger$1, "getBlockHashes", err, undefined);
@@ -886,7 +898,7 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
886
898
  backOffMilliseconds: backoffMillis,
887
899
  err: Utils.prettifyExn(err)
888
900
  });
889
- await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined);
901
+ await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined, undefined);
890
902
  retryRef = retryRef + 1 | 0;
891
903
  }
892
904
  }
@@ -1,14 +0,0 @@
1
- import { Worker } from "node:worker_threads";
2
-
3
- // The compiler blocks the thread it runs on, so it gets its own and the
4
- // database setup carries on meanwhile.
5
- export const check = (cwd, files) =>
6
- new Promise((resolve, reject) => {
7
- const worker = new Worker(new URL("./HandlerTypeCheckWorker.mjs", import.meta.url), {
8
- workerData: { cwd, files },
9
- });
10
- worker.once("message", resolve);
11
- worker.once("error", reject);
12
- // Settles nothing once the result has arrived.
13
- worker.once("exit", () => reject(new Error("The handler type check exited without a result.")));
14
- });
@@ -1,68 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import path from "node:path";
4
- import { stripVTControlCharacters } from "node:util";
5
- import { parentPort, workerData } from "node:worker_threads";
6
-
7
- const check = ({ cwd, files }) => {
8
- // The ReScript compiler checks a ReScript project's handlers, so it has no
9
- // reason to set up TypeScript, and the warning would only be noise there.
10
- // `rescript.json` at the root is what makes codegen treat it as ReScript.
11
- const skip = (warning) => (existsSync(path.join(cwd, "rescript.json")) ? {} : { skipped: warning });
12
-
13
- let ts;
14
- try {
15
- // The project's own compiler, so handlers are checked exactly as the
16
- // user's editor and `tsc` check them.
17
- ts = createRequire(path.join(cwd, "package.json"))("typescript");
18
- } catch {
19
- return skip("Skipped the handler type check: the project doesn't depend on typescript.");
20
- }
21
-
22
- const configPath = ts.findConfigFile(cwd, ts.sys.fileExists);
23
- if (configPath === undefined) {
24
- return skip(
25
- "Skipped the handler type check: no tsconfig.json found. Add one to type-check handlers on start, like the one envio init creates."
26
- );
27
- }
28
-
29
- const host = {
30
- getCanonicalFileName: (file) => file,
31
- getCurrentDirectory: () => cwd,
32
- getNewLine: () => "\n",
33
- };
34
- const format = (diagnostics) =>
35
- stripVTControlCharacters(ts.formatDiagnosticsWithColorAndContext(diagnostics, host)).trimEnd();
36
-
37
- const parsed = ts.getParsedCommandLineOfConfigFile(
38
- configPath,
39
- { noEmit: true },
40
- {
41
- ...ts.sys,
42
- onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
43
- throw new Error(format([diagnostic]));
44
- },
45
- }
46
- );
47
-
48
- // The tsconfig's files are roots, as tsc makes them, since any of them can
49
- // declare globals a handler relies on. Only the handlers' errors count.
50
- const handlerFiles = files.map((file) => path.resolve(cwd, file));
51
- const program = ts.createProgram({
52
- rootNames: [...parsed.fileNames, ...handlerFiles],
53
- options: parsed.options,
54
- projectReferences: parsed.projectReferences,
55
- });
56
-
57
- // Every file is a root, so each has a source file. Without one,
58
- // `getSemanticDiagnostics` would check the whole program.
59
- const diagnostics = handlerFiles
60
- .map((file) => program.getSourceFile(file))
61
- .flatMap((sourceFile) => [
62
- ...program.getSyntacticDiagnostics(sourceFile),
63
- ...program.getSemanticDiagnostics(sourceFile),
64
- ]);
65
- return diagnostics.length === 0 ? {} : { errors: format(diagnostics) };
66
- };
67
-
68
- parentPort.postMessage(check(workerData));
@@ -1,86 +0,0 @@
1
- // The writes one round of a synchronous handler has made. A round that
2
- // suspends is replayed from the top, so its writes are held here rather than
3
- // applied: the replay has to see the store exactly as the first round did, or a
4
- // read-modify-write lands once per round.
5
-
6
- type table = {
7
- entityConfig: Internal.entityConfig,
8
- scope: Internal.chainScope,
9
- changes: dict<Change.t<Internal.entity>>,
10
- }
11
-
12
- // A handler runs on one chain, so an entity's scope is fixed for the whole
13
- // round and the entity name is enough to key its table.
14
- type t = dict<table>
15
-
16
- let make = (): t => Dict.make()
17
-
18
- let record = (writes: t, ~entityConfig: Internal.entityConfig, ~scope, change) => {
19
- let table = switch writes->Utils.Dict.dangerouslyGetNonOption(entityConfig.name) {
20
- | Some(table) => table
21
- | None =>
22
- let table = {entityConfig, scope, changes: Dict.make()}
23
- writes->Dict.set(entityConfig.name, table)
24
- table
25
- }
26
- table.changes->Dict.set(change->Change.getEntityId->EntityId.toKey, change)
27
- }
28
-
29
- let changesOf = (writes: t, ~entityConfig: Internal.entityConfig) =>
30
- switch writes->Utils.Dict.dangerouslyGetNonOption(entityConfig.name) {
31
- | Some({changes}) => Some(changes)
32
- | None => None
33
- }
34
-
35
- // `Some(None)` is a delete this round made: the entity is known not to exist.
36
- let getById = (writes: t, ~entityConfig, ~entityId: string) =>
37
- switch writes->changesOf(~entityConfig) {
38
- | Some(changes) =>
39
- changes
40
- ->Utils.Dict.dangerouslyGetNonOption(entityId)
41
- ->Option.map(InMemoryTable.Entity.mapChangeToEntity)
42
- | None => None
43
- }
44
-
45
- // What the store answered, as it reads once this round's writes land: a row
46
- // the round rewrote is re-tested against the filter, and one it created joins.
47
- let overlayFilter = (
48
- writes: t,
49
- entities: array<Internal.entity>,
50
- ~entityConfig: Internal.entityConfig,
51
- ~filter: EntityFilter.t,
52
- ) =>
53
- switch writes->changesOf(~entityConfig) {
54
- | None => entities
55
- | Some(changes) =>
56
- let matches = filter->EntityFilter.makeMatcher(~table=entityConfig.table)
57
- let untouched =
58
- entities->Array.filter(entity =>
59
- !(changes->Dict.has(entity->InMemoryTable.Entity.getEntityIdUnsafe))
60
- )
61
- let written =
62
- changes
63
- ->Dict.valuesToArray
64
- ->Array.filterMap(change =>
65
- switch change->InMemoryTable.Entity.mapChangeToEntity {
66
- | Some(entity) if matches(entity) => Some(entity)
67
- | _ => None
68
- }
69
- )
70
- untouched->Array.concat(written)
71
- }
72
-
73
- // How a change lands in the in-memory store, whether written directly or
74
- // committed at the end of a round.
75
- let write = (indexerState, ~entityConfig, ~scope, change) =>
76
- indexerState
77
- ->InMemoryStore.getInMemTable(~entityConfig, ~scope)
78
- ->InMemoryTable.Entity.set(
79
- ~committedCheckpointId=indexerState->IndexerState.committedCheckpointIdFor(~scope),
80
- change,
81
- )
82
-
83
- let commit = (writes: t, ~indexerState) =>
84
- writes->Utils.Dict.forEach(({entityConfig, scope, changes}) =>
85
- changes->Utils.Dict.forEach(change => indexerState->write(~entityConfig, ~scope, change))
86
- )
@@ -1,85 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Utils from "./Utils.res.mjs";
4
- import * as EntityId from "./EntityId.res.mjs";
5
- import * as EntityFilter from "./db/EntityFilter.res.mjs";
6
- import * as IndexerState from "./IndexerState.res.mjs";
7
- import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
8
- import * as InMemoryStore from "./InMemoryStore.res.mjs";
9
- import * as InMemoryTable from "./InMemoryTable.res.mjs";
10
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
11
-
12
- function make() {
13
- return {};
14
- }
15
-
16
- function record(writes, entityConfig, scope, change) {
17
- let table = writes[entityConfig.name];
18
- let table$1;
19
- if (table !== undefined) {
20
- table$1 = table;
21
- } else {
22
- let table_changes = {};
23
- let table$2 = {
24
- entityConfig: entityConfig,
25
- scope: scope,
26
- changes: table_changes
27
- };
28
- writes[entityConfig.name] = table$2;
29
- table$1 = table$2;
30
- }
31
- table$1.changes[EntityId.toKey(change.entityId)] = change;
32
- }
33
-
34
- function changesOf(writes, entityConfig) {
35
- let match = writes[entityConfig.name];
36
- if (match !== undefined) {
37
- return match.changes;
38
- }
39
- }
40
-
41
- function getById(writes, entityConfig, entityId) {
42
- let changes = changesOf(writes, entityConfig);
43
- if (changes !== undefined) {
44
- return Stdlib_Option.map(changes[entityId], InMemoryTable.Entity.mapChangeToEntity);
45
- }
46
- }
47
-
48
- function overlayFilter(writes, entities, entityConfig, filter) {
49
- let changes = changesOf(writes, entityConfig);
50
- if (changes === undefined) {
51
- return entities;
52
- }
53
- let matches = EntityFilter.makeMatcher(filter, entityConfig.table);
54
- let untouched = entities.filter(entity => !(InMemoryTable.Entity.getEntityIdUnsafe(entity) in changes));
55
- let written = Stdlib_Array.filterMap(Object.values(changes), change => {
56
- let entity = InMemoryTable.Entity.mapChangeToEntity(change);
57
- if (entity !== undefined && matches(entity)) {
58
- return entity;
59
- }
60
- });
61
- return untouched.concat(written);
62
- }
63
-
64
- function write(indexerState, entityConfig, scope, change) {
65
- InMemoryTable.Entity.set(InMemoryStore.getInMemTable(indexerState, entityConfig, scope), IndexerState.committedCheckpointIdFor(indexerState, scope), change);
66
- }
67
-
68
- function commit(writes, indexerState) {
69
- Utils.Dict.forEach(writes, param => {
70
- let scope = param.scope;
71
- let entityConfig = param.entityConfig;
72
- Utils.Dict.forEach(param.changes, change => write(indexerState, entityConfig, scope, change));
73
- });
74
- }
75
-
76
- export {
77
- make,
78
- record,
79
- changesOf,
80
- getById,
81
- overlayFilter,
82
- write,
83
- commit,
84
- }
85
- /* Utils Not a pure module */
@@ -1,63 +0,0 @@
1
- /**
2
- * Contract-call signatures and ABI type lists as graph codegen writes them.
3
- * Free of envio imports: the graph-ts shim reads them too.
4
- */
5
-
6
- export type ParsedSignature = {
7
- name: string;
8
- inputs: string;
9
- outputs: string;
10
- };
11
-
12
- /**
13
- * graph codegen emits `"balanceOf(address):(uint256)"`. Types only, no names —
14
- * which is all `parseAbiParameters` needs.
15
- */
16
- export function parseSignature(signature: string): ParsedSignature {
17
- const open = signature.indexOf("(");
18
- if (open === -1) {
19
- throw new Error(`Unreadable contract call signature "${signature}"`);
20
- }
21
- const name = signature.slice(0, open);
22
-
23
- let depth = 0;
24
- let close = -1;
25
- for (let index = open; index < signature.length; index++) {
26
- const char = signature[index];
27
- if (char === "(") depth++;
28
- if (char === ")") {
29
- depth--;
30
- if (depth === 0) {
31
- close = index;
32
- break;
33
- }
34
- }
35
- }
36
- if (close === -1) {
37
- throw new Error(`Unreadable contract call signature "${signature}"`);
38
- }
39
-
40
- const inputs = signature.slice(open + 1, close);
41
- const rest = signature.slice(close + 1).replace(/^:/, "");
42
- const outputs = rest.startsWith("(") ? rest.slice(1, -1) : rest;
43
-
44
- return { name, inputs, outputs };
45
- }
46
-
47
- /** `address,(uint256,bytes)` → `["address", "(uint256,bytes)"]`. */
48
- export function splitTypes(list: string): string[] {
49
- const types: string[] = [];
50
- let depth = 0;
51
- let start = 0;
52
- for (let index = 0; index < list.length; index++) {
53
- const char = list[index];
54
- if (char === "(") depth++;
55
- else if (char === ")") depth--;
56
- else if (char === "," && depth === 0) {
57
- types.push(list.slice(start, index));
58
- start = index + 1;
59
- }
60
- }
61
- if (list.length > 0) types.push(list.slice(start));
62
- return types;
63
- }