envio 3.11.0 → 3.12.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 (109) hide show
  1. package/index.d.ts +3 -0
  2. package/package.json +6 -6
  3. package/src/Batch.res +111 -78
  4. package/src/Batch.res.mjs +53 -46
  5. package/src/BatchProcessing.res +1 -11
  6. package/src/BatchProcessing.res.mjs +1 -4
  7. package/src/ChainFetching.res +7 -3
  8. package/src/ChainFetching.res.mjs +1 -1
  9. package/src/ChainState.res +82 -20
  10. package/src/ChainState.res.mjs +51 -13
  11. package/src/ChainState.resi +6 -1
  12. package/src/Config.res +58 -37
  13. package/src/Config.res.mjs +63 -53
  14. package/src/CrossChainState.res +50 -53
  15. package/src/CrossChainState.res.mjs +25 -23
  16. package/src/CrossChainState.resi +6 -13
  17. package/src/EffectState.res +13 -4
  18. package/src/EffectState.resi +7 -1
  19. package/src/EventProcessing.res +6 -6
  20. package/src/EventProcessing.res.mjs +6 -6
  21. package/src/FetchState.res +65 -46
  22. package/src/FetchState.res.mjs +68 -36
  23. package/src/FinalizeBackfill.res +16 -12
  24. package/src/FinalizeBackfill.res.mjs +3 -3
  25. package/src/HistoryPolicy.res +46 -0
  26. package/src/HistoryPolicy.res.mjs +50 -0
  27. package/src/InMemoryStore.res +30 -32
  28. package/src/InMemoryStore.res.mjs +33 -28
  29. package/src/InMemoryTable.res +155 -67
  30. package/src/InMemoryTable.res.mjs +151 -60
  31. package/src/IndexerState.res +66 -45
  32. package/src/IndexerState.res.mjs +57 -29
  33. package/src/IndexerState.resi +8 -13
  34. package/src/Internal.res +4 -2
  35. package/src/LoadLayer.res +68 -38
  36. package/src/LoadLayer.res.mjs +50 -64
  37. package/src/LoadLayer.resi +1 -1
  38. package/src/Main.res +25 -24
  39. package/src/Main.res.mjs +24 -25
  40. package/src/Metrics.res +49 -1
  41. package/src/Metrics.res.mjs +26 -3
  42. package/src/Persistence.res +55 -16
  43. package/src/Persistence.res.mjs +18 -3
  44. package/src/PgStorage.res +286 -180
  45. package/src/PgStorage.res.mjs +209 -122
  46. package/src/PruneStaleHistory.res +9 -15
  47. package/src/PruneStaleHistory.res.mjs +11 -20
  48. package/src/Rollback.res +9 -14
  49. package/src/Rollback.res.mjs +5 -3
  50. package/src/Sink.res +7 -5
  51. package/src/Sink.res.mjs +4 -4
  52. package/src/Staging.res +298 -0
  53. package/src/Staging.res.mjs +257 -0
  54. package/src/TestIndexer.res +14 -30
  55. package/src/TestIndexer.res.mjs +11 -9
  56. package/src/UserContext.res +20 -36
  57. package/src/UserContext.res.mjs +6 -9
  58. package/src/Utils.res +10 -4
  59. package/src/Utils.res.mjs +16 -16
  60. package/src/Writing.res +34 -20
  61. package/src/Writing.res.mjs +15 -20
  62. package/src/bindings/ClickHouse.res +41 -34
  63. package/src/bindings/ClickHouse.res.mjs +36 -30
  64. package/src/bindings/ClickHouseSink.res +45 -186
  65. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  66. package/src/db/CheckpointSequence.res +109 -0
  67. package/src/db/CheckpointSequence.res.mjs +99 -0
  68. package/src/db/EntityFilter.res +487 -275
  69. package/src/db/EntityFilter.res.mjs +557 -309
  70. package/src/db/EntityHistory.res +6 -5
  71. package/src/db/EntityHistory.res.mjs +5 -5
  72. package/src/db/Frontier.res +86 -0
  73. package/src/db/Frontier.res.mjs +126 -0
  74. package/src/db/InternalTable.res +145 -27
  75. package/src/db/InternalTable.res.mjs +115 -29
  76. package/src/db/RollbackFloors.res +34 -41
  77. package/src/db/RollbackFloors.res.mjs +27 -71
  78. package/src/db/Table.res +21 -6
  79. package/src/db/Table.res.mjs +13 -4
  80. package/src/sources/BlockStore.res +8 -0
  81. package/src/sources/EvmHyperSyncSource.res +5 -1
  82. package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
  83. package/src/sources/FuelHyperSyncSource.res +1 -0
  84. package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
  85. package/src/sources/HyperSync.res +6 -0
  86. package/src/sources/HyperSync.res.mjs +5 -2
  87. package/src/sources/HyperSync.resi +2 -0
  88. package/src/sources/HyperSyncClient.res +6 -0
  89. package/src/sources/HyperSyncSSE.res +1 -1
  90. package/src/sources/HyperSyncSSE.res.mjs +4 -10
  91. package/src/sources/RequestStat.res +8 -1
  92. package/src/sources/RpcSource.res +1 -0
  93. package/src/sources/RpcSource.res.mjs +1 -1
  94. package/src/sources/SimulateSource.res +1 -0
  95. package/src/sources/SimulateSource.res.mjs +1 -1
  96. package/src/sources/Source.res +11 -3
  97. package/src/sources/SourceManager.res +34 -13
  98. package/src/sources/SourceManager.res.mjs +26 -9
  99. package/src/sources/SourceManager.resi +2 -0
  100. package/src/sources/SvmHyperSyncClient.res +5 -0
  101. package/src/sources/SvmHyperSyncSource.res +3 -0
  102. package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
  103. package/src/tui/Tui.res +56 -143
  104. package/src/tui/Tui.res.mjs +70 -166
  105. package/src/tui/components/TuiData.res +61 -16
  106. package/src/tui/components/TuiData.res.mjs +53 -15
  107. package/svm.schema.json +0 -7
  108. package/src/db/CheckpointBounds.res +0 -53
  109. package/src/db/CheckpointBounds.res.mjs +0 -44
package/src/tui/Tui.res CHANGED
@@ -6,9 +6,9 @@ module ChainLine = {
6
6
  ~chainId,
7
7
  ~maxChainIdLength,
8
8
  ~stdoutColumns: int,
9
- ~progressBlock,
10
- ~bufferBlock,
11
- ~sourceBlock,
9
+ ~progressBlock: int,
10
+ ~bufferBlock: int,
11
+ ~toBlock: int,
12
12
  ~startBlock,
13
13
  ~endBlock,
14
14
  ~poweredByHyperSync,
@@ -18,65 +18,45 @@ module ChainLine = {
18
18
  let chainsWidth = Pervasives.min(stdoutColumns - 2, 60)
19
19
  let headerWidth = maxChainIdLength + 10 // 10 for additional text
20
20
 
21
- switch (progressBlock, bufferBlock, sourceBlock) {
22
- | (Some(progressBlock), Some(bufferBlock), Some(sourceBlock)) =>
23
- let toBlock = switch endBlock {
24
- | Some(endBlock) => Pervasives.min(sourceBlock, endBlock)
25
- | None => sourceBlock
26
- }
27
- let progressBlockStr = progressBlock->TuiData.formatLocaleString
28
- let toBlockStr = toBlock->TuiData.formatLocaleString
29
- let eventsStr = eventsProcessed->TuiData.formatFloatLocaleString
21
+ let progressBlockStr = progressBlock->TuiData.formatLocaleString
22
+ let toBlockStr = toBlock->TuiData.formatLocaleString
23
+ let eventsStr = eventsProcessed->TuiData.formatFloatLocaleString
30
24
 
31
- let endLabel = ` (End ${blockUnit})`
32
- let blocksText =
33
- `${blockUnit}s: ${progressBlockStr} / ${toBlockStr}` ++
34
- (endBlock->Option.isSome ? endLabel : "") ++ ` `
35
- let eventsText = `Events: ${eventsStr}`
25
+ let endLabel = ` (End ${blockUnit})`
26
+ let blocksText =
27
+ `${blockUnit}s: ${progressBlockStr} / ${toBlockStr}` ++
28
+ (endBlock->Option.isSome ? endLabel : "") ++ ` `
29
+ let eventsText = `Events: ${eventsStr}`
36
30
 
37
- let fitsSameLine = blocksText->String.length + eventsText->String.length <= chainsWidth
31
+ let fitsSameLine = blocksText->String.length + eventsText->String.length <= chainsWidth
38
32
 
39
- <Box flexDirection={Column}>
40
- <Box flexDirection=Row width=Num(chainsWidth)>
41
- <Box width={Num(headerWidth)}>
42
- <Text> {"Chain: "->React.string} </Text>
43
- <Text bold=true> {chainId->React.string} </Text>
44
- <Text> {" "->React.string} </Text>
45
- {poweredByHyperSync ? <Text color=Secondary> {"⚡"->React.string} </Text> : React.null}
46
- </Box>
47
- <BufferedProgressBar
48
- barWidth={chainsWidth - headerWidth}
49
- loaded={progressBlock - startBlock}
50
- buffered={bufferBlock - startBlock}
51
- outOf={toBlock - startBlock}
52
- loadingColor={Secondary}
53
- />
54
- </Box>
55
- <Box flexDirection={Row}>
56
- <Text color={Gray}> {blocksText->React.string} </Text>
57
- {fitsSameLine ? <Text color={Gray}> {eventsText->React.string} </Text> : React.null}
33
+ <Box flexDirection={Column}>
34
+ <Box flexDirection=Row width=Num(chainsWidth)>
35
+ <Box width={Num(headerWidth)}>
36
+ <Text> {"Chain: "->React.string} </Text>
37
+ <Text bold=true> {chainId->React.string} </Text>
38
+ <Text> {" "->React.string} </Text>
39
+ {poweredByHyperSync ? <Text color=Secondary> {"⚡"->React.string} </Text> : React.null}
58
40
  </Box>
59
- {fitsSameLine
60
- ? React.null
61
- : <Box flexDirection={Row}>
62
- <Text color={Gray}> {eventsText->String.trim->React.string} </Text>
63
- </Box>}
64
- <Newline />
41
+ <BufferedProgressBar
42
+ barWidth={chainsWidth - headerWidth}
43
+ loaded={progressBlock - startBlock}
44
+ buffered={bufferBlock - startBlock}
45
+ outOf={toBlock - startBlock}
46
+ loadingColor={Secondary}
47
+ />
65
48
  </Box>
66
- | (_, _, _) =>
67
- <>
68
- <Box flexDirection=Row width=Num(chainsWidth)>
69
- <Box width={Num(headerWidth)}>
70
- <Text> {"Chain: "->React.string} </Text>
71
- <Text bold=true> {chainId->React.string} </Text>
72
- <Text> {" "->React.string} </Text>
73
- {poweredByHyperSync ? <Text color=Secondary> {"⚡"->React.string} </Text> : React.null}
74
- </Box>
75
- <Text> {"Loading progress..."->React.string} </Text>
76
- </Box>
77
- <Newline />
78
- </>
79
- }
49
+ <Box flexDirection={Row}>
50
+ <Text color={Gray}> {blocksText->React.string} </Text>
51
+ {fitsSameLine ? <Text color={Gray}> {eventsText->React.string} </Text> : React.null}
52
+ </Box>
53
+ {fitsSameLine
54
+ ? React.null
55
+ : <Box flexDirection={Row}>
56
+ <Text color={Gray}> {eventsText->String.trim->React.string} </Text>
57
+ </Box>}
58
+ <Newline />
59
+ </Box>
80
60
  }
81
61
  }
82
62
 
@@ -132,13 +112,13 @@ module TotalEventsProcessed = {
132
112
 
133
113
  module App = {
134
114
  @react.component
135
- let make = (~getState) => {
115
+ let make = (~config: Config.t, ~getMetrics) => {
136
116
  let stdoutColumns = Hooks.useStdoutColumns()
137
- // IndexerState is mutated in place — passing the same ref to useState
138
- // would bail out via Object.is and skip the re-render. Tick a counter
139
- // instead and read state freshly from getState() on every render.
117
+ // Metrics are rebuilt from state mutated in place — passing the same value
118
+ // to useState would bail out via Object.is and skip the re-render. Tick a
119
+ // counter instead and read metrics freshly on every render.
140
120
  let (tick, setTick) = React.useState(() => 0)
141
- let state: IndexerState.t = getState()
121
+ let metrics: Metrics.t = getMetrics()
142
122
 
143
123
  React.useEffect(() => {
144
124
  let intervalId = setInterval(() => {
@@ -150,80 +130,13 @@ module App = {
150
130
  clearInterval(intervalId)
151
131
  },
152
132
  )
153
- }, [getState])
154
-
155
- let chains =
156
- state
157
- ->IndexerState.chainStates
158
- ->Dict.valuesToArray
159
- ->Array.map(cs => {
160
- let data = cs->ChainState.toMetrics
161
- let numEventsProcessed = data.numEventsProcessed
162
- let committedProgressBlockNumber = cs->ChainState.committedProgressBlockNumber
163
- let timestampCaughtUpToHeadOrEndblock = data.timestampCaughtUpToHeadOrEndblock
164
- let sourceManager = cs->ChainState.sourceManager
165
- let latestFetchedBlockNumber = data.latestFetchedBlockNumber
166
- let hasProcessedToEndblock = cs->ChainState.hasProcessedToEndblock
167
-
168
- let firstEventBlock = data.firstEventBlockNumber
169
- let progress: TuiData.progress = if hasProcessedToEndblock {
170
- // If the endblock has been reached then set the progress to synced.
171
- // if there's chains that have no events in the block range start->end,
172
- // it's possible there are no events in that block range (ie firstEventBlock = None)
173
- // This ensures TUI still displays synced in this case
174
- Synced({
175
- firstEventBlockNumber: firstEventBlock->Option.getOr(0),
176
- latestProcessedBlock: committedProgressBlockNumber,
177
- timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock->Option.getOr(
178
- Date.now()->Date.fromTime,
179
- ),
180
- numEventsProcessed,
181
- })
182
- } else {
183
- switch (firstEventBlock, timestampCaughtUpToHeadOrEndblock) {
184
- | (Some(firstEventBlockNumber), Some(timestampCaughtUpToHeadOrEndblock)) =>
185
- Synced({
186
- firstEventBlockNumber,
187
- latestProcessedBlock: committedProgressBlockNumber,
188
- timestampCaughtUpToHeadOrEndblock,
189
- numEventsProcessed,
190
- })
191
- | (Some(firstEventBlockNumber), None) =>
192
- Syncing({
193
- firstEventBlockNumber,
194
- latestProcessedBlock: committedProgressBlockNumber,
195
- numEventsProcessed,
196
- })
197
- | (None, _) => SearchingForEvents
198
- }
199
- }
133
+ }, [getMetrics])
200
134
 
201
- (
202
- {
203
- progress,
204
- knownHeight: data.knownHeight,
205
- latestFetchedBlockNumber,
206
- eventsProcessed: numEventsProcessed,
207
- chainId: (cs->ChainState.chainConfig).id->ChainId.toString,
208
- progressBlock: committedProgressBlockNumber < data.startBlock
209
- ? Some(data.startBlock)
210
- : Some(committedProgressBlockNumber),
211
- bufferBlock: Some(latestFetchedBlockNumber),
212
- sourceBlock: Some(cs->ChainState.knownHeight),
213
- firstEventBlockNumber: firstEventBlock,
214
- startBlock: data.startBlock,
215
- endBlock: data.endBlock,
216
- poweredByHyperSync: data.poweredByHyperSync,
217
- blockUnit: switch (state->IndexerState.config).ecosystem.name {
218
- | Svm => "Slot"
219
- | Evm | Fuel => "Block"
220
- },
221
- rateLimitTimeMs: sourceManager->SourceManager.getRateLimitTimeMs,
222
- isRateLimited: sourceManager->SourceManager.isRateLimited,
223
- rateLimitResetInMs: sourceManager->SourceManager.getRateLimitResetInMs,
224
- }: TuiData.chain
225
- )
226
- })
135
+ let blockUnit = switch config.ecosystem.name {
136
+ | Svm => "Slot"
137
+ | Evm | Fuel => "Block"
138
+ }
139
+ let chains = metrics.chains->Array.map(m => m->TuiData.fromChainMetrics(~blockUnit))
227
140
 
228
141
  let totalEventsProcessed = chains->Array.reduce(0., (acc, chain) => {
229
142
  acc +. chain.eventsProcessed
@@ -254,7 +167,7 @@ module App = {
254
167
  maxChainIdLength={maxChainIdLength}
255
168
  progressBlock={chainData.progressBlock}
256
169
  bufferBlock={chainData.bufferBlock}
257
- sourceBlock={chainData.sourceBlock}
170
+ toBlock={chainData.toBlock}
258
171
  startBlock={chainData.startBlock}
259
172
  endBlock={chainData.endBlock}
260
173
  stdoutColumns={stdoutColumns}
@@ -268,7 +181,7 @@ module App = {
268
181
  totalEventsProcessed
269
182
  eventsPerSecond={SyncETA.isIndexerFullySynced(chains) ? None : eventsPerSecond}
270
183
  />
271
- <SyncETA chains indexerStartTime={state->IndexerState.indexerStartTime} />
184
+ <SyncETA chains indexerStartTime={metrics.startTime} />
272
185
  {
273
186
  let maxRateLimitTimeMs =
274
187
  chains->Array.reduce(0., (acc, chain) => Pervasives.max(acc, chain.rateLimitTimeMs))
@@ -314,7 +227,7 @@ module App = {
314
227
  }
315
228
  }
316
229
  </Box>
317
- {if (state->IndexerState.config).isDev {
230
+ {if config.isDev {
318
231
  <Box flexDirection={Row}>
319
232
  <Text> {"Dev Console: "->React.string} </Text>
320
233
  <Text color={Info} underline=true> {`${Env.envioAppUrl}/console`->React.string} </Text>
@@ -322,7 +235,7 @@ module App = {
322
235
  } else {
323
236
  React.null
324
237
  }}
325
- {switch ((state->IndexerState.config).storage.clickhouse, Env.ClickHouse.host()) {
238
+ {switch (config.storage.clickhouse, Env.ClickHouse.host()) {
326
239
  | (true, Some(host)) =>
327
240
  <Box flexDirection={Row}>
328
241
  <Text> {"ClickHouse: "->React.string} </Text>
@@ -330,14 +243,14 @@ module App = {
330
243
  </Box>
331
244
  | _ => React.null
332
245
  }}
333
- <Messages config={state->IndexerState.config} />
246
+ <Messages config />
334
247
  </Box>
335
248
  }
336
249
  }
337
250
 
338
- let start = (~getState) => {
339
- let {rerender} = render(<App getState />)
251
+ let start = (~config, ~getMetrics) => {
252
+ let {rerender} = render(<App config getMetrics />)
340
253
  () => {
341
- rerender(<App getState />)
254
+ rerender(<App config getMetrics />)
342
255
  }
343
256
  }
@@ -4,16 +4,12 @@ import * as Env from "../Env.res.mjs";
4
4
  import * as Ink from "./bindings/Ink.res.mjs";
5
5
  import * as Ink$1 from "ink";
6
6
  import * as React from "react";
7
- import * as ChainId from "../ChainId.res.mjs";
8
7
  import * as SyncETA from "./components/SyncETA.res.mjs";
9
8
  import * as TuiData from "./components/TuiData.res.mjs";
10
9
  import * as Messages from "./components/Messages.res.mjs";
11
- import * as ChainState from "../ChainState.res.mjs";
12
- import * as IndexerState from "../IndexerState.res.mjs";
13
10
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
14
11
  import InkBigText from "ink-big-text";
15
12
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
16
- import * as SourceManager from "../sources/SourceManager.res.mjs";
17
13
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
18
14
  import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
19
15
  import * as JsxRuntime from "react/jsx-runtime";
@@ -21,86 +17,21 @@ import * as BufferedProgressBar from "./components/BufferedProgressBar.res.mjs";
21
17
 
22
18
  function Tui$ChainLine(props) {
23
19
  let blockUnit = props.blockUnit;
24
- let poweredByHyperSync = props.poweredByHyperSync;
25
- let endBlock = props.endBlock;
26
20
  let startBlock = props.startBlock;
27
- let sourceBlock = props.sourceBlock;
28
- let bufferBlock = props.bufferBlock;
21
+ let toBlock = props.toBlock;
29
22
  let progressBlock = props.progressBlock;
30
- let chainId = props.chainId;
31
23
  let chainsWidth = Primitive_int.min(props.stdoutColumns - 2 | 0, 60);
32
24
  let headerWidth = props.maxChainIdLength + 10 | 0;
33
- if (progressBlock !== undefined && bufferBlock !== undefined && sourceBlock !== undefined) {
34
- let toBlock = endBlock !== undefined ? Primitive_int.min(sourceBlock, endBlock) : sourceBlock;
35
- let progressBlockStr = TuiData.formatLocaleString(progressBlock);
36
- let toBlockStr = TuiData.formatLocaleString(toBlock);
37
- let eventsStr = TuiData.formatFloatLocaleString(props.eventsProcessed);
38
- let endLabel = ` (End ` + blockUnit + `)`;
39
- let blocksText = blockUnit + `s: ` + progressBlockStr + ` / ` + toBlockStr + (
40
- Stdlib_Option.isSome(endBlock) ? endLabel : ""
41
- ) + ` `;
42
- let eventsText = `Events: ` + eventsStr;
43
- let fitsSameLine = (blocksText.length + eventsText.length | 0) <= chainsWidth;
44
- return JsxRuntime.jsxs(Ink$1.Box, {
45
- children: [
46
- JsxRuntime.jsxs(Ink$1.Box, {
47
- children: [
48
- JsxRuntime.jsxs(Ink$1.Box, {
49
- children: [
50
- JsxRuntime.jsx(Ink$1.Text, {
51
- children: "Chain: "
52
- }),
53
- JsxRuntime.jsx(Ink$1.Text, {
54
- children: chainId,
55
- bold: true
56
- }),
57
- JsxRuntime.jsx(Ink$1.Text, {
58
- children: " "
59
- }),
60
- poweredByHyperSync ? JsxRuntime.jsx(Ink$1.Text, {
61
- children: "⚡",
62
- color: "#FFBB2F"
63
- }) : null
64
- ],
65
- width: headerWidth
66
- }),
67
- JsxRuntime.jsx(BufferedProgressBar.make, {
68
- loaded: progressBlock - startBlock | 0,
69
- buffered: bufferBlock - startBlock | 0,
70
- outOf: toBlock - startBlock | 0,
71
- barWidth: chainsWidth - headerWidth | 0,
72
- loadingColor: "#FFBB2F"
73
- })
74
- ],
75
- width: chainsWidth,
76
- flexDirection: "row"
77
- }),
78
- JsxRuntime.jsxs(Ink$1.Box, {
79
- children: [
80
- JsxRuntime.jsx(Ink$1.Text, {
81
- children: blocksText,
82
- color: "gray"
83
- }),
84
- fitsSameLine ? JsxRuntime.jsx(Ink$1.Text, {
85
- children: eventsText,
86
- color: "gray"
87
- }) : null
88
- ],
89
- flexDirection: "row"
90
- }),
91
- fitsSameLine ? null : JsxRuntime.jsx(Ink$1.Box, {
92
- children: JsxRuntime.jsx(Ink$1.Text, {
93
- children: eventsText.trim(),
94
- color: "gray"
95
- }),
96
- flexDirection: "row"
97
- }),
98
- JsxRuntime.jsx(Ink.Newline.make, {})
99
- ],
100
- flexDirection: "column"
101
- });
102
- }
103
- return JsxRuntime.jsxs(JsxRuntime.Fragment, {
25
+ let progressBlockStr = TuiData.formatLocaleString(progressBlock);
26
+ let toBlockStr = TuiData.formatLocaleString(toBlock);
27
+ let eventsStr = TuiData.formatFloatLocaleString(props.eventsProcessed);
28
+ let endLabel = ` (End ` + blockUnit + `)`;
29
+ let blocksText = blockUnit + `s: ` + progressBlockStr + ` / ` + toBlockStr + (
30
+ Stdlib_Option.isSome(props.endBlock) ? endLabel : ""
31
+ ) + ` `;
32
+ let eventsText = `Events: ` + eventsStr;
33
+ let fitsSameLine = (blocksText.length + eventsText.length | 0) <= chainsWidth;
34
+ return JsxRuntime.jsxs(Ink$1.Box, {
104
35
  children: [
105
36
  JsxRuntime.jsxs(Ink$1.Box, {
106
37
  children: [
@@ -110,28 +41,53 @@ function Tui$ChainLine(props) {
110
41
  children: "Chain: "
111
42
  }),
112
43
  JsxRuntime.jsx(Ink$1.Text, {
113
- children: chainId,
44
+ children: props.chainId,
114
45
  bold: true
115
46
  }),
116
47
  JsxRuntime.jsx(Ink$1.Text, {
117
48
  children: " "
118
49
  }),
119
- poweredByHyperSync ? JsxRuntime.jsx(Ink$1.Text, {
50
+ props.poweredByHyperSync ? JsxRuntime.jsx(Ink$1.Text, {
120
51
  children: "⚡",
121
52
  color: "#FFBB2F"
122
53
  }) : null
123
54
  ],
124
55
  width: headerWidth
125
56
  }),
126
- JsxRuntime.jsx(Ink$1.Text, {
127
- children: "Loading progress..."
57
+ JsxRuntime.jsx(BufferedProgressBar.make, {
58
+ loaded: progressBlock - startBlock | 0,
59
+ buffered: props.bufferBlock - startBlock | 0,
60
+ outOf: toBlock - startBlock | 0,
61
+ barWidth: chainsWidth - headerWidth | 0,
62
+ loadingColor: "#FFBB2F"
128
63
  })
129
64
  ],
130
65
  width: chainsWidth,
131
66
  flexDirection: "row"
132
67
  }),
68
+ JsxRuntime.jsxs(Ink$1.Box, {
69
+ children: [
70
+ JsxRuntime.jsx(Ink$1.Text, {
71
+ children: blocksText,
72
+ color: "gray"
73
+ }),
74
+ fitsSameLine ? JsxRuntime.jsx(Ink$1.Text, {
75
+ children: eventsText,
76
+ color: "gray"
77
+ }) : null
78
+ ],
79
+ flexDirection: "row"
80
+ }),
81
+ fitsSameLine ? null : JsxRuntime.jsx(Ink$1.Box, {
82
+ children: JsxRuntime.jsx(Ink$1.Text, {
83
+ children: eventsText.trim(),
84
+ color: "gray"
85
+ }),
86
+ flexDirection: "row"
87
+ }),
133
88
  JsxRuntime.jsx(Ink.Newline.make, {})
134
- ]
89
+ ],
90
+ flexDirection: "column"
135
91
  });
136
92
  }
137
93
 
@@ -196,84 +152,30 @@ let TotalEventsProcessed = {
196
152
  };
197
153
 
198
154
  function Tui$App(props) {
199
- let getState = props.getState;
155
+ let getMetrics = props.getMetrics;
156
+ let config = props.config;
200
157
  let stdoutColumns = Ink.Hooks.useStdoutColumns();
201
158
  let match = React.useState(() => 0);
202
159
  let setTick = match[1];
203
- let state = getState();
160
+ let metrics = getMetrics();
204
161
  React.useEffect(() => {
205
162
  let intervalId = setInterval(() => setTick(t => t + 1 | 0), 500);
206
163
  return () => {
207
164
  clearInterval(intervalId);
208
165
  };
209
- }, [getState]);
210
- let chains = Object.values(IndexerState.chainStates(state)).map(cs => {
211
- let data = ChainState.toMetrics(cs);
212
- let numEventsProcessed = data.numEventsProcessed;
213
- let committedProgressBlockNumber = ChainState.committedProgressBlockNumber(cs);
214
- let timestampCaughtUpToHeadOrEndblock = data.timestampCaughtUpToHeadOrEndblock;
215
- let sourceManager = ChainState.sourceManager(cs);
216
- let latestFetchedBlockNumber = data.latestFetchedBlockNumber;
217
- let hasProcessedToEndblock = ChainState.hasProcessedToEndblock(cs);
218
- let firstEventBlock = data.firstEventBlockNumber;
219
- let progress = hasProcessedToEndblock ? ({
220
- TAG: "Synced",
221
- _0: {
222
- firstEventBlockNumber: Stdlib_Option.getOr(firstEventBlock, 0),
223
- latestProcessedBlock: committedProgressBlockNumber,
224
- numEventsProcessed: numEventsProcessed,
225
- timestampCaughtUpToHeadOrEndblock: Stdlib_Option.getOr(timestampCaughtUpToHeadOrEndblock, new Date(Date.now()))
226
- }
227
- }) : (
228
- firstEventBlock !== undefined ? (
229
- timestampCaughtUpToHeadOrEndblock !== undefined ? ({
230
- TAG: "Synced",
231
- _0: {
232
- firstEventBlockNumber: firstEventBlock,
233
- latestProcessedBlock: committedProgressBlockNumber,
234
- numEventsProcessed: numEventsProcessed,
235
- timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock
236
- }
237
- }) : ({
238
- TAG: "Syncing",
239
- _0: {
240
- firstEventBlockNumber: firstEventBlock,
241
- latestProcessedBlock: committedProgressBlockNumber,
242
- numEventsProcessed: numEventsProcessed
243
- }
244
- })
245
- ) : "SearchingForEvents"
246
- );
247
- let match = IndexerState.config(state).ecosystem.name;
248
- let tmp;
249
- switch (match) {
250
- case "evm" :
251
- case "fuel" :
252
- tmp = "Block";
253
- break;
254
- case "svm" :
255
- tmp = "Slot";
256
- break;
257
- }
258
- return {
259
- chainId: ChainId.toString(ChainState.chainConfig(cs).id),
260
- eventsProcessed: numEventsProcessed,
261
- progressBlock: committedProgressBlockNumber < data.startBlock ? data.startBlock : committedProgressBlockNumber,
262
- bufferBlock: latestFetchedBlockNumber,
263
- sourceBlock: ChainState.knownHeight(cs),
264
- startBlock: data.startBlock,
265
- endBlock: data.endBlock,
266
- firstEventBlockNumber: firstEventBlock,
267
- poweredByHyperSync: data.poweredByHyperSync,
268
- progress: progress,
269
- latestFetchedBlockNumber: latestFetchedBlockNumber,
270
- knownHeight: data.knownHeight,
271
- blockUnit: tmp,
272
- rateLimitTimeMs: SourceManager.getRateLimitTimeMs(sourceManager),
273
- isRateLimited: SourceManager.isRateLimited(sourceManager),
274
- rateLimitResetInMs: SourceManager.getRateLimitResetInMs(sourceManager)
275
- };
276
- });
166
+ }, [getMetrics]);
167
+ let match$1 = config.ecosystem.name;
168
+ let blockUnit;
169
+ switch (match$1) {
170
+ case "evm" :
171
+ case "fuel" :
172
+ blockUnit = "Block";
173
+ break;
174
+ case "svm" :
175
+ blockUnit = "Slot";
176
+ break;
177
+ }
178
+ let chains = metrics.chains.map(m => TuiData.fromChainMetrics(m, blockUnit));
277
179
  let totalEventsProcessed = Stdlib_Array.reduce(chains, 0, (acc, chain) => acc + chain.eventsProcessed);
278
180
  let maxChainIdLength = Stdlib_Array.reduce(chains, 0, (acc, chain) => {
279
181
  let chainIdLength = chain.chainId.length;
@@ -328,8 +230,8 @@ function Tui$App(props) {
328
230
  tmp = null;
329
231
  }
330
232
  let defaultPassword = "testing";
331
- let match$1 = IndexerState.config(state).storage.clickhouse;
332
- let match$2 = Env.ClickHouse.host();
233
+ let match$2 = config.storage.clickhouse;
234
+ let match$3 = Env.ClickHouse.host();
333
235
  return JsxRuntime.jsxs(Ink$1.Box, {
334
236
  children: [
335
237
  JsxRuntime.jsx(InkBigText, {
@@ -348,7 +250,7 @@ function Tui$App(props) {
348
250
  stdoutColumns: stdoutColumns,
349
251
  progressBlock: chainData.progressBlock,
350
252
  bufferBlock: chainData.bufferBlock,
351
- sourceBlock: chainData.sourceBlock,
253
+ toBlock: chainData.toBlock,
352
254
  startBlock: chainData.startBlock,
353
255
  endBlock: chainData.endBlock,
354
256
  poweredByHyperSync: chainData.poweredByHyperSync,
@@ -361,7 +263,7 @@ function Tui$App(props) {
361
263
  }),
362
264
  JsxRuntime.jsx(SyncETA.make, {
363
265
  chains: chains,
364
- indexerStartTime: IndexerState.indexerStartTime(state)
266
+ indexerStartTime: metrics.startTime
365
267
  }),
366
268
  tmp,
367
269
  JsxRuntime.jsx(Ink.Newline.make, {}),
@@ -382,7 +284,7 @@ function Tui$App(props) {
382
284
  ],
383
285
  flexDirection: "row"
384
286
  }),
385
- IndexerState.config(state).isDev ? JsxRuntime.jsxs(Ink$1.Box, {
287
+ config.isDev ? JsxRuntime.jsxs(Ink$1.Box, {
386
288
  children: [
387
289
  JsxRuntime.jsx(Ink$1.Text, {
388
290
  children: "Dev Console: "
@@ -395,13 +297,13 @@ function Tui$App(props) {
395
297
  ],
396
298
  flexDirection: "row"
397
299
  }) : null,
398
- match$1 && match$2 !== undefined ? JsxRuntime.jsxs(Ink$1.Box, {
300
+ match$2 && match$3 !== undefined ? JsxRuntime.jsxs(Ink$1.Box, {
399
301
  children: [
400
302
  JsxRuntime.jsx(Ink$1.Text, {
401
303
  children: "ClickHouse: "
402
304
  }),
403
305
  JsxRuntime.jsx(Ink$1.Text, {
404
- children: match$2 + `/play`,
306
+ children: match$3 + `/play`,
405
307
  color: "#6CBFEE",
406
308
  underline: true
407
309
  })
@@ -409,7 +311,7 @@ function Tui$App(props) {
409
311
  flexDirection: "row"
410
312
  }) : null,
411
313
  JsxRuntime.jsx(Messages.make, {
412
- config: IndexerState.config(state)
314
+ config: config
413
315
  })
414
316
  ],
415
317
  flexDirection: "column"
@@ -420,13 +322,15 @@ let App = {
420
322
  make: Tui$App
421
323
  };
422
324
 
423
- function start(getState) {
325
+ function start(config, getMetrics) {
424
326
  let match = Ink.render(undefined, JsxRuntime.jsx(Tui$App, {
425
- getState: getState
327
+ config: config,
328
+ getMetrics: getMetrics
426
329
  }));
427
330
  let rerender = match.rerender;
428
331
  return () => rerender(JsxRuntime.jsx(Tui$App, {
429
- getState: getState
332
+ config: config,
333
+ getMetrics: getMetrics
430
334
  }));
431
335
  }
432
336