envio 3.12.1 → 3.13.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/package.json +6 -8
  2. package/src/BatchProcessing.res +15 -14
  3. package/src/BatchProcessing.res.mjs +8 -15
  4. package/src/Bin.res +24 -17
  5. package/src/Bin.res.mjs +5 -0
  6. package/src/ChainFetching.res +18 -24
  7. package/src/ChainFetching.res.mjs +14 -15
  8. package/src/ChainState.res +103 -38
  9. package/src/ChainState.res.mjs +79 -22
  10. package/src/ChainState.resi +14 -2
  11. package/src/Config.res +32 -0
  12. package/src/Config.res.mjs +24 -0
  13. package/src/Core.res +4 -0
  14. package/src/Core.res.mjs +4 -0
  15. package/src/CrossChainState.res +109 -11
  16. package/src/CrossChainState.res.mjs +71 -7
  17. package/src/CrossChainState.resi +11 -1
  18. package/src/Env.res +4 -0
  19. package/src/ErrorHandling.res +15 -4
  20. package/src/ErrorHandling.res.mjs +16 -5
  21. package/src/FinalizeBackfill.res +10 -4
  22. package/src/FinalizeBackfill.res.mjs +5 -3
  23. package/src/HandlerRegister.res +0 -37
  24. package/src/HandlerRegister.res.mjs +0 -18
  25. package/src/IndexerLoop.res +2 -0
  26. package/src/IndexerLoop.res.mjs +1 -0
  27. package/src/IndexerState.res +43 -1
  28. package/src/IndexerState.res.mjs +48 -4
  29. package/src/IndexerState.resi +11 -0
  30. package/src/Main.res +128 -268
  31. package/src/Main.res.mjs +27 -153
  32. package/src/Metrics.res +268 -102
  33. package/src/Metrics.res.mjs +227 -48
  34. package/src/Persistence.res +30 -2
  35. package/src/Persistence.res.mjs +12 -3
  36. package/src/PgStorage.res +19 -11
  37. package/src/PgStorage.res.mjs +13 -10
  38. package/src/Rollback.res +3 -3
  39. package/src/Server.res +178 -0
  40. package/src/Server.res.mjs +143 -0
  41. package/src/SimulateItems.res +1 -1
  42. package/src/SimulateItems.res.mjs +1 -1
  43. package/src/Supervisor.res +542 -0
  44. package/src/Supervisor.res.mjs +455 -0
  45. package/src/TestIndexer.res +1 -1
  46. package/src/TestIndexer.res.mjs +2 -2
  47. package/src/Worker.res +101 -0
  48. package/src/Worker.res.mjs +81 -0
  49. package/src/bindings/NodeJs.res +50 -0
  50. package/src/bindings/NodeJs.res.mjs +8 -1
  51. package/src/db/InternalTable.res +8 -1
  52. package/src/db/InternalTable.res.mjs +5 -1
  53. package/src/sources/AddressSet.res +5 -3
  54. package/src/sources/AddressStore.res +19 -11
  55. package/src/sources/AddressStore.res.mjs +2 -7
  56. package/src/sources/BlockStore.res +10 -9
  57. package/src/sources/BlockStore.res.mjs +4 -4
  58. package/src/sources/ChainSources.res +4 -0
  59. package/src/sources/ChainSources.res.mjs +2 -2
  60. package/src/sources/EvmChain.res +4 -0
  61. package/src/sources/EvmChain.res.mjs +3 -1
  62. package/src/sources/EvmEventItem.res +56 -0
  63. package/src/sources/EvmEventItem.res.mjs +32 -0
  64. package/src/sources/EvmHyperSyncSource.res +7 -37
  65. package/src/sources/EvmHyperSyncSource.res.mjs +8 -29
  66. package/src/sources/EvmRpcClient.res +41 -59
  67. package/src/sources/EvmRpcClient.res.mjs +4 -54
  68. package/src/sources/EvmRpcWs.res +4 -1
  69. package/src/sources/EvmRpcWs.res.mjs +6 -2
  70. package/src/sources/FuelHyperSyncSource.res +6 -4
  71. package/src/sources/FuelHyperSyncSource.res.mjs +7 -9
  72. package/src/sources/HeightFeed.res +7 -1
  73. package/src/sources/HeightFeed.res.mjs +9 -7
  74. package/src/sources/HyperSyncClient.res +1 -14
  75. package/src/sources/RequestStat.res +7 -0
  76. package/src/sources/RequestStat.res.mjs +14 -1
  77. package/src/sources/RpcSource.res +100 -1043
  78. package/src/sources/RpcSource.res.mjs +81 -1056
  79. package/src/sources/SimulateSource.res +2 -5
  80. package/src/sources/SimulateSource.res.mjs +2 -3
  81. package/src/sources/Source.res +34 -12
  82. package/src/sources/Source.res.mjs +11 -0
  83. package/src/sources/SourceManager.res +111 -31
  84. package/src/sources/SourceManager.res.mjs +65 -31
  85. package/src/sources/SourceManager.resi +4 -0
  86. package/src/sources/StartBlockResolver.res +8 -5
  87. package/src/sources/StartBlockResolver.res.mjs +4 -2
  88. package/src/sources/SvmHyperSyncSource.res +4 -2
  89. package/src/sources/SvmHyperSyncSource.res.mjs +4 -6
  90. package/src/sources/TransactionStore.res +9 -6
  91. package/src/sources/TransactionStore.res.mjs +2 -2
  92. package/src/tui/Tui.res +10 -0
  93. package/src/tui/Tui.res.mjs +15 -0
  94. package/src/tui/components/SyncETA.res +12 -6
  95. package/src/tui/components/SyncETA.res.mjs +12 -8
  96. package/src/LazyLoader.res +0 -135
  97. package/src/LazyLoader.res.mjs +0 -118
  98. package/src/bindings/SDSL.res +0 -12
  99. package/src/bindings/SDSL.res.mjs +0 -9
  100. package/src/bindings/Yargs.res +0 -8
  101. package/src/bindings/Yargs.res.mjs +0 -2
  102. package/src/sources/Rpc.res +0 -185
  103. package/src/sources/Rpc.res.mjs +0 -183
package/src/Server.res ADDED
@@ -0,0 +1,178 @@
1
+ // The indexer's own HTTP surface: metrics for a scraper, health for an
2
+ // orchestrator, and the console's view of the run. What it serves is handed to
3
+ // it, so one process's readings and a supervised group's merged ones render the
4
+ // same way.
5
+
6
+ // The public console/state chain shape. Kept to exactly this field set for
7
+ // backward compatibility with consumers like RACE — new metric fields stay off
8
+ // the HTTP response.
9
+ type chainData = {
10
+ chainId: ChainId.t,
11
+ poweredByHyperSync: bool,
12
+ firstEventBlockNumber: option<int>,
13
+ latestProcessedBlock: option<int>,
14
+ timestampCaughtUpToHeadOrEndblock: option<Date.t>,
15
+ numEventsProcessed: float,
16
+ latestFetchedBlockNumber: int,
17
+ // Need this for API backwards compatibility
18
+ @as("currentBlockHeight")
19
+ knownHeight: int,
20
+ numBatchesFetched: int,
21
+ startBlock: int,
22
+ endBlock: option<int>,
23
+ numAddresses: int,
24
+ }
25
+ @tag("status")
26
+ type state =
27
+ | @as("disabled") Disabled({})
28
+ | @as("initializing") Initializing({})
29
+ | @as("active")
30
+ Active({
31
+ envioVersion: string,
32
+ chains: array<chainData>,
33
+ indexerStartTime: Date.t,
34
+ isPreRegisteringDynamicContracts: bool,
35
+ rollbackOnReorg: bool,
36
+ })
37
+
38
+ let toChainData = (m: Metrics.chainMetrics): chainData => {
39
+ chainId: m.chainId,
40
+ poweredByHyperSync: m.poweredByHyperSync,
41
+ firstEventBlockNumber: m.firstEventBlockNumber,
42
+ latestProcessedBlock: m.latestProcessedBlock,
43
+ timestampCaughtUpToHeadOrEndblock: m.timestampCaughtUpToHeadOrEndblock,
44
+ numEventsProcessed: m.numEventsProcessed,
45
+ latestFetchedBlockNumber: m.latestFetchedBlockNumber,
46
+ knownHeight: m.knownHeight,
47
+ numBatchesFetched: m.numBatchesFetched,
48
+ startBlock: m.startBlock,
49
+ endBlock: m.endBlock,
50
+ numAddresses: m.numAddresses,
51
+ }
52
+
53
+ let chainDataSchema = S.schema((s): chainData => {
54
+ chainId: s.matches(ChainId.schema),
55
+ poweredByHyperSync: s.matches(S.bool),
56
+ firstEventBlockNumber: s.matches(S.option(S.int)),
57
+ latestProcessedBlock: s.matches(S.option(S.int)),
58
+ timestampCaughtUpToHeadOrEndblock: s.matches(S.option(S.datetime(S.string))),
59
+ numEventsProcessed: s.matches(S.float),
60
+ latestFetchedBlockNumber: s.matches(S.int),
61
+ knownHeight: s.matches(S.int),
62
+ numBatchesFetched: s.matches(S.int),
63
+ startBlock: s.matches(S.int),
64
+ endBlock: s.matches(S.option(S.int)),
65
+ numAddresses: s.matches(S.int),
66
+ })
67
+ let stateSchema = S.union([
68
+ S.literal(Disabled({})),
69
+ S.literal(Initializing({})),
70
+ S.schema(s => Active({
71
+ envioVersion: s.matches(S.string),
72
+ chains: s.matches(S.array(chainDataSchema)),
73
+ indexerStartTime: s.matches(S.datetime(S.string)),
74
+ // Keep the field, since Dev Console expects it to be present
75
+ isPreRegisteringDynamicContracts: false,
76
+ rollbackOnReorg: s.matches(S.bool),
77
+ })),
78
+ ])
79
+
80
+ let startServer = (
81
+ ~getMetrics: unit => option<Metrics.t>,
82
+ ~envioVersion: string,
83
+ ~onSyncCache: unit => promise<unit>,
84
+ ~collectRuntime: unit => string,
85
+ ~isDevelopmentMode: bool,
86
+ ) => {
87
+ open Express
88
+
89
+ let app = make()
90
+
91
+ let consoleCorsMiddleware = (req, res, next) => {
92
+ switch req.headers->Dict.get("origin") {
93
+ | Some(origin) if origin === Env.prodEnvioAppUrl || origin === Env.envioAppUrl =>
94
+ res->setHeader("Access-Control-Allow-Origin", origin)
95
+ | _ => ()
96
+ }
97
+
98
+ res->setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
99
+ res->setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
100
+
101
+ if req.method === Rest.Options {
102
+ res->sendStatus(200)
103
+ } else {
104
+ next()
105
+ }
106
+ }
107
+ app->useFor("/console", consoleCorsMiddleware)
108
+ app->useFor("/metrics", consoleCorsMiddleware)
109
+ app->useFor("/metrics/runtime", consoleCorsMiddleware)
110
+
111
+ app->get("/healthz", (_req, res) => {
112
+ // this is the machine readable port used in kubernetes to check the health of this service.
113
+ // aditional health information could be added in the future (info about errors, back-offs, etc).
114
+ res->sendStatus(200)
115
+ })
116
+
117
+ app->get("/console/state", (_req, res) => {
118
+ let state = if !isDevelopmentMode {
119
+ Disabled({})
120
+ } else {
121
+ switch getMetrics() {
122
+ | None => Initializing({})
123
+ | Some(metrics) =>
124
+ Active({
125
+ envioVersion,
126
+ chains: metrics.chains->Array.map(toChainData),
127
+ indexerStartTime: metrics.startTime,
128
+ isPreRegisteringDynamicContracts: false,
129
+ rollbackOnReorg: metrics.rollbackEnabled,
130
+ })
131
+ }
132
+ }
133
+
134
+ res->json(state->S.reverseConvertToJsonOrThrow(stateSchema))
135
+ })
136
+
137
+ app->post("/console/syncCache", (_req, res) => {
138
+ if isDevelopmentMode {
139
+ onSyncCache()
140
+ ->Promise.thenResolve(() => res->json(Boolean(true)))
141
+ // A dump that couldn't be made, or couldn't be confirmed, answers the
142
+ // same `false` a disabled console does. Leaving it unanswered would hold
143
+ // the request open for as long as the indexer runs.
144
+ ->Promise.catch(exn => {
145
+ Logging.errorWithExn(exn, "Failed to sync the effect cache")
146
+ res->json(Boolean(false))
147
+ Promise.resolve()
148
+ })
149
+ ->Promise.ignore
150
+ } else {
151
+ res->json(Boolean(false))
152
+ }
153
+ })
154
+
155
+ app->get("/metrics", (_req, res) => {
156
+ res->set("Content-Type", Metrics.contentType)
157
+ let _ = res->endWithData(Metrics.collect(~metrics=getMetrics()))
158
+ })
159
+
160
+ app->get("/metrics/runtime", (_req, res) => {
161
+ res->set("Content-Type", Metrics.contentType)
162
+ let _ = res->endWithData(collectRuntime())
163
+ })
164
+
165
+ let server = app->listen(Env.serverPort)
166
+ server->Express.onError(err => {
167
+ let code = (err->(Utils.magic: JsExn.t => {..}))["code"]
168
+ if code === "EADDRINUSE" {
169
+ Logging.error(
170
+ `Port ${Env.serverPort->Int.toString} is already in use. To fix this either:` ++
171
+ `\n 1. Kill the process using the port: lsof -ti :${Env.serverPort->Int.toString} | xargs kill -9` ++ `\n 2. Use a different port by setting the ENVIO_INDEXER_PORT environment variable: ENVIO_INDEXER_PORT=9899 envio start`,
172
+ )
173
+ } else {
174
+ Logging.errorWithExn(err, "Failed to start indexer server")
175
+ }
176
+ NodeJs.process->NodeJs.exitWithCode(Failure)
177
+ })
178
+ }
@@ -0,0 +1,143 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Env from "./Env.res.mjs";
4
+ import * as ChainId from "./ChainId.res.mjs";
5
+ import * as Logging from "./Logging.res.mjs";
6
+ import * as Metrics from "./Metrics.res.mjs";
7
+ import Express from "express";
8
+ import * as Process from "process";
9
+ import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
10
+ import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
11
+
12
+ function toChainData(m) {
13
+ return {
14
+ chainId: m.chainId,
15
+ poweredByHyperSync: m.poweredByHyperSync,
16
+ firstEventBlockNumber: m.firstEventBlockNumber,
17
+ latestProcessedBlock: m.latestProcessedBlock,
18
+ timestampCaughtUpToHeadOrEndblock: m.timestampCaughtUpToHeadOrEndblock,
19
+ numEventsProcessed: m.numEventsProcessed,
20
+ latestFetchedBlockNumber: m.latestFetchedBlockNumber,
21
+ currentBlockHeight: m.knownHeight,
22
+ numBatchesFetched: m.numBatchesFetched,
23
+ startBlock: m.startBlock,
24
+ endBlock: m.endBlock,
25
+ numAddresses: m.numAddresses
26
+ };
27
+ }
28
+
29
+ let chainDataSchema = S$RescriptSchema.schema(s => ({
30
+ chainId: s.m(ChainId.schema),
31
+ poweredByHyperSync: s.m(S$RescriptSchema.bool),
32
+ firstEventBlockNumber: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
33
+ latestProcessedBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
34
+ timestampCaughtUpToHeadOrEndblock: s.m(S$RescriptSchema.option(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined))),
35
+ numEventsProcessed: s.m(S$RescriptSchema.float),
36
+ latestFetchedBlockNumber: s.m(S$RescriptSchema.int),
37
+ currentBlockHeight: s.m(S$RescriptSchema.int),
38
+ numBatchesFetched: s.m(S$RescriptSchema.int),
39
+ startBlock: s.m(S$RescriptSchema.int),
40
+ endBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
41
+ numAddresses: s.m(S$RescriptSchema.int)
42
+ }));
43
+
44
+ let stateSchema = S$RescriptSchema.union([
45
+ S$RescriptSchema.literal({
46
+ status: "disabled"
47
+ }),
48
+ S$RescriptSchema.literal({
49
+ status: "initializing"
50
+ }),
51
+ S$RescriptSchema.schema(s => ({
52
+ status: "active",
53
+ envioVersion: s.m(S$RescriptSchema.string),
54
+ chains: s.m(S$RescriptSchema.array(chainDataSchema)),
55
+ indexerStartTime: s.m(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined)),
56
+ isPreRegisteringDynamicContracts: false,
57
+ rollbackOnReorg: s.m(S$RescriptSchema.bool)
58
+ }))
59
+ ]);
60
+
61
+ function startServer(getMetrics, envioVersion, onSyncCache, collectRuntime, isDevelopmentMode) {
62
+ let app = Express();
63
+ let consoleCorsMiddleware = (req, res, next) => {
64
+ let origin = req.headers["origin"];
65
+ if (origin !== undefined && (origin === Env.prodEnvioAppUrl || origin === Env.envioAppUrl)) {
66
+ res.setHeader("Access-Control-Allow-Origin", origin);
67
+ }
68
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
69
+ res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
70
+ if (req.method === "OPTIONS") {
71
+ res.sendStatus(200);
72
+ return;
73
+ } else {
74
+ return next();
75
+ }
76
+ };
77
+ app.use("/console", consoleCorsMiddleware);
78
+ app.use("/metrics", consoleCorsMiddleware);
79
+ app.use("/metrics/runtime", consoleCorsMiddleware);
80
+ app.get("/healthz", (_req, res) => {
81
+ res.sendStatus(200);
82
+ });
83
+ app.get("/console/state", (_req, res) => {
84
+ let state;
85
+ if (isDevelopmentMode) {
86
+ let metrics = getMetrics();
87
+ state = metrics !== undefined ? ({
88
+ status: "active",
89
+ envioVersion: envioVersion,
90
+ chains: metrics.chains.map(toChainData),
91
+ indexerStartTime: metrics.startTime,
92
+ isPreRegisteringDynamicContracts: false,
93
+ rollbackOnReorg: metrics.rollbackEnabled
94
+ }) : ({
95
+ status: "initializing"
96
+ });
97
+ } else {
98
+ state = {
99
+ status: "disabled"
100
+ };
101
+ }
102
+ res.json(S$RescriptSchema.reverseConvertToJsonOrThrow(state, stateSchema));
103
+ });
104
+ app.post("/console/syncCache", (_req, res) => {
105
+ if (isDevelopmentMode) {
106
+ Stdlib_Promise.$$catch(onSyncCache().then(() => {
107
+ res.json(true);
108
+ }), exn => {
109
+ Logging.errorWithExn(exn, "Failed to sync the effect cache");
110
+ res.json(false);
111
+ return Promise.resolve();
112
+ });
113
+ } else {
114
+ res.json(false);
115
+ }
116
+ });
117
+ app.get("/metrics", (_req, res) => {
118
+ res.set("Content-Type", Metrics.contentType);
119
+ res.end(Metrics.collect(getMetrics()));
120
+ });
121
+ app.get("/metrics/runtime", (_req, res) => {
122
+ res.set("Content-Type", Metrics.contentType);
123
+ res.end(collectRuntime());
124
+ });
125
+ let server = app.listen(Env.serverPort);
126
+ server.on("error", err => {
127
+ let code = err.code;
128
+ if (code === "EADDRINUSE") {
129
+ Logging.error(`Port ` + Env.serverPort.toString() + ` is already in use. To fix this either:` + (`\n 1. Kill the process using the port: lsof -ti :` + Env.serverPort.toString() + ` | xargs kill -9`) + `\n 2. Use a different port by setting the ENVIO_INDEXER_PORT environment variable: ENVIO_INDEXER_PORT=9899 envio start`);
130
+ } else {
131
+ Logging.errorWithExn(err, "Failed to start indexer server");
132
+ }
133
+ Process.exit(1);
134
+ });
135
+ }
136
+
137
+ export {
138
+ toChainData,
139
+ chainDataSchema,
140
+ stateSchema,
141
+ startServer,
142
+ }
143
+ /* chainDataSchema Not a pure module */
@@ -657,7 +657,7 @@ let parse = (
657
657
  | Svm => {
658
658
  items,
659
659
  transactionStore: Some(TransactionStore.fromSvmJs(svmTxs, svmActivities)),
660
- blockStore: Some(BlockStore.fromJs(svmBlocks, ~ecosystem=Svm, ~shouldChecksum=false)),
660
+ blockStore: Some(BlockStore.fromJs(svmBlocks, ~ecosystem=Svm)),
661
661
  }
662
662
  | _ => {
663
663
  items,
@@ -459,7 +459,7 @@ function parse(simulateItems, config, chainConfig, onEventRegistrations) {
459
459
  return {
460
460
  items: items,
461
461
  transactionStore: Primitive_option.some(TransactionStore.fromSvmJs(svmTxs, svmActivities)),
462
- blockStore: Primitive_option.some(BlockStore.fromJs(svmBlocks, "svm", false))
462
+ blockStore: Primitive_option.some(BlockStore.fromJs(svmBlocks, "svm"))
463
463
  };
464
464
  }
465
465
  }