envio 3.13.0 → 3.14.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.
- package/bin.mjs +10 -1
- package/index.d.ts +11 -0
- package/package.json +8 -20
- package/rescript.json +1 -4
- package/src/Config.res +69 -100
- package/src/Config.res.mjs +50 -63
- package/src/Core.res +46 -6
- package/src/Core.res.mjs +41 -4
- package/src/EventConfigBuilder.res +4 -10
- package/src/EventConfigBuilder.res.mjs +1 -3
- package/src/ExitOnCaughtUp.res +5 -1
- package/src/ExitOnCaughtUp.res.mjs +8 -3
- package/src/HandlerLoader.res +74 -32
- package/src/HandlerLoader.res.mjs +69 -23
- package/src/HandlerTypeCheck.mjs +14 -0
- package/src/HandlerTypeCheckWorker.mjs +68 -0
- package/src/Internal.res +1 -1
- package/src/Main.res +17 -12
- package/src/Main.res.mjs +9 -3
- package/src/NodeVersion.mjs +13 -0
- package/src/Persistence.res +79 -38
- package/src/Persistence.res.mjs +41 -10
- package/src/PgStorage.res +62 -34
- package/src/PgStorage.res.mjs +65 -31
- package/src/ResumePlan.res +172 -0
- package/src/ResumePlan.res.mjs +122 -0
- package/src/Supervisor.res +8 -8
- package/src/Supervisor.res.mjs +6 -1
- package/src/TestIndexer.res +14 -4
- package/src/TestIndexer.res.mjs +3 -2
- package/src/TsModuleHooks.mjs +121 -0
- package/src/Utils.res +0 -8
- package/src/Worker.res +8 -0
- package/src/Worker.res.mjs +11 -0
- package/src/bindings/NodeJs.res +8 -7
- package/src/bindings/NodeJs.res.mjs +0 -3
- package/src/bindings/Pino.res +2 -2
- package/src/db/InternalTable.res +52 -2
- package/src/db/InternalTable.res.mjs +32 -3
- package/src/sources/FuelHyperSync.res +2 -0
- package/src/sources/FuelHyperSync.res.mjs +1 -0
- package/src/sources/FuelHyperSync.resi +1 -0
- package/src/sources/FuelHyperSyncClient.res +25 -8
- package/src/sources/FuelHyperSyncClient.res.mjs +8 -2
- package/src/sources/FuelHyperSyncSource.res +24 -22
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -13
- package/src/tui/InitApi.res +85 -0
- package/src/tui/{components/CustomHooks.res.mjs → InitApi.res.mjs} +11 -64
- package/src/tui/Tui.res +142 -249
- package/src/tui/Tui.res.mjs +123 -332
- package/src/Js.shim.ts +0 -11
- package/src/bindings/DateFns.res +0 -74
- package/src/bindings/DateFns.res.mjs +0 -22
- package/src/sources/FuelSDK.res +0 -38
- package/src/sources/FuelSDK.res.mjs +0 -27
- package/src/sources/vendored-fuel-abi-coder.js +0 -1792
- package/src/tui/bindings/Ink.res +0 -369
- package/src/tui/bindings/Ink.res.mjs +0 -66
- package/src/tui/bindings/Style.res +0 -123
- package/src/tui/bindings/Style.res.mjs +0 -2
- package/src/tui/components/BufferedProgressBar.res +0 -40
- package/src/tui/components/BufferedProgressBar.res.mjs +0 -57
- package/src/tui/components/CustomHooks.res +0 -122
- package/src/tui/components/Messages.res +0 -40
- package/src/tui/components/Messages.res.mjs +0 -71
- package/src/tui/components/SyncETA.res +0 -179
- package/src/tui/components/SyncETA.res.mjs +0 -249
- package/src/tui/components/TuiData.res +0 -98
- package/src/tui/components/TuiData.res.mjs +0 -73
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
module InitApi = {
|
|
2
|
-
type ecosystem = | @as("evm") Evm | @as("fuel") Fuel | @as("svm") Svm
|
|
3
|
-
type body = {
|
|
4
|
-
envioVersion: string,
|
|
5
|
-
envioApiToken: option<string>,
|
|
6
|
-
ecosystem: ecosystem,
|
|
7
|
-
hyperSyncNetworks: array<ChainId.t>,
|
|
8
|
-
rpcNetworks: array<ChainId.t>,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let bodySchema = S.object(s => {
|
|
12
|
-
envioVersion: s.field("envioVersion", S.string),
|
|
13
|
-
envioApiToken: s.field("envioApiToken", S.option(S.string)),
|
|
14
|
-
ecosystem: s.field("ecosystem", S.enum([Evm, Fuel, Svm])),
|
|
15
|
-
hyperSyncNetworks: s.field("hyperSyncNetworks", S.array(ChainId.schema)),
|
|
16
|
-
rpcNetworks: s.field("rpcNetworks", S.array(ChainId.schema)),
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
let makeBody = (~envioVersion, ~envioApiToken, ~config: Config.t) => {
|
|
20
|
-
let hyperSyncNetworks = []
|
|
21
|
-
let rpcNetworks = []
|
|
22
|
-
config.chainMap
|
|
23
|
-
->ChainMap.values
|
|
24
|
-
->Array.forEach(({sourceConfig, id}) => {
|
|
25
|
-
// Check if chain uses HyperSync based on sourceConfig
|
|
26
|
-
let usesHyperSync = switch sourceConfig {
|
|
27
|
-
| Config.EvmSourceConfig({hypersync: Some(_)}) => true
|
|
28
|
-
| Config.EvmSourceConfig({hypersync: None}) => false
|
|
29
|
-
| Config.FuelSourceConfig(_) => true // Fuel always uses HyperFuel
|
|
30
|
-
| Config.SvmSourceConfig(_) => false
|
|
31
|
-
| Config.SimulateSourceConfig(_) => false
|
|
32
|
-
| Config.CustomSources(sources) => sources->Array.some(s => s.poweredByHyperSync)
|
|
33
|
-
}
|
|
34
|
-
switch usesHyperSync {
|
|
35
|
-
| true => hyperSyncNetworks
|
|
36
|
-
| false => rpcNetworks
|
|
37
|
-
}
|
|
38
|
-
->Array.push(id)
|
|
39
|
-
->ignore
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
envioVersion,
|
|
44
|
-
envioApiToken,
|
|
45
|
-
ecosystem: (config.ecosystem.name :> ecosystem),
|
|
46
|
-
hyperSyncNetworks,
|
|
47
|
-
rpcNetworks,
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
type messageColor =
|
|
52
|
-
| @as("primary") Primary
|
|
53
|
-
| @as("secondary") Secondary
|
|
54
|
-
| @as("info") Info
|
|
55
|
-
| @as("danger") Danger
|
|
56
|
-
| @as("success") Success
|
|
57
|
-
| @as("white") White
|
|
58
|
-
| @as("gray") Gray
|
|
59
|
-
|
|
60
|
-
let toTheme = (color: messageColor): Style.chalkTheme =>
|
|
61
|
-
switch color {
|
|
62
|
-
| Primary => Primary
|
|
63
|
-
| Secondary => Secondary
|
|
64
|
-
| Info => Info
|
|
65
|
-
| Danger => Danger
|
|
66
|
-
| Success => Success
|
|
67
|
-
| White => White
|
|
68
|
-
| Gray => Gray
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
type message = {
|
|
72
|
-
color: messageColor,
|
|
73
|
-
content: string,
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let messageSchema = S.object(s => {
|
|
77
|
-
color: s.field("color", S.enum([Primary, Secondary, Info, Danger, Success, White, Gray])),
|
|
78
|
-
content: s.field("content", S.string),
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
let client = Rest.client(Env.envioAppUrl ++ "/api")
|
|
82
|
-
|
|
83
|
-
let route = Rest.route(() => {
|
|
84
|
-
method: Post,
|
|
85
|
-
path: "/hyperindex/init",
|
|
86
|
-
input: s => s.body(bodySchema),
|
|
87
|
-
responses: [s => s.field("messages", S.array(messageSchema))],
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
let getMessages = async (~config) => {
|
|
91
|
-
let envioVersion = Utils.EnvioPackage.value.version
|
|
92
|
-
let body = makeBody(~envioVersion, ~envioApiToken=Env.envioApiToken, ~config)
|
|
93
|
-
|
|
94
|
-
switch await route->Rest.fetch(body, ~client) {
|
|
95
|
-
| exception exn => Error(exn->Obj.magic)
|
|
96
|
-
| messages => Ok(messages)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
type request<'ok, 'err> = Data('ok) | Loading | Err('err)
|
|
102
|
-
|
|
103
|
-
let useMessages = (~config) => {
|
|
104
|
-
let (request, setRequest) = React.useState(_ => Loading)
|
|
105
|
-
React.useEffect0(() => {
|
|
106
|
-
InitApi.getMessages(~config)
|
|
107
|
-
->Promise.thenResolve(res =>
|
|
108
|
-
switch res {
|
|
109
|
-
| Ok(data) => setRequest(_ => Data(data))
|
|
110
|
-
| Error(e) =>
|
|
111
|
-
Logging.error({
|
|
112
|
-
"msg": "Failed to load messages from envio server",
|
|
113
|
-
"err": e->Utils.prettifyExn,
|
|
114
|
-
})
|
|
115
|
-
setRequest(_ => Err(e))
|
|
116
|
-
}
|
|
117
|
-
)
|
|
118
|
-
->ignore
|
|
119
|
-
None
|
|
120
|
-
})
|
|
121
|
-
request
|
|
122
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
open Ink
|
|
2
|
-
module Message = {
|
|
3
|
-
@react.component
|
|
4
|
-
let make = (~message: CustomHooks.InitApi.message) => {
|
|
5
|
-
<Text color={message.color->CustomHooks.InitApi.toTheme}>
|
|
6
|
-
{message.content->React.string}
|
|
7
|
-
</Text>
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module Notifications = {
|
|
12
|
-
@react.component
|
|
13
|
-
let make = (~children) => {
|
|
14
|
-
<>
|
|
15
|
-
<Newline />
|
|
16
|
-
<Text bold=true> {"Notifications:"->React.string} </Text>
|
|
17
|
-
{children}
|
|
18
|
-
</>
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@react.component
|
|
23
|
-
let make = (~config) => {
|
|
24
|
-
let messages = CustomHooks.useMessages(~config)
|
|
25
|
-
<>
|
|
26
|
-
{switch messages {
|
|
27
|
-
| Data([]) | Loading => React.null //Don't show anything while loading or no messages
|
|
28
|
-
| Data(messages) =>
|
|
29
|
-
<Notifications>
|
|
30
|
-
{messages
|
|
31
|
-
->Array.mapWithIndex((message, i) => {<Message key={i->Int.toString} message />})
|
|
32
|
-
->React.array}
|
|
33
|
-
</Notifications>
|
|
34
|
-
| Err(_) =>
|
|
35
|
-
<Notifications>
|
|
36
|
-
<Message message={color: Danger, content: "Failed to load messages from envio server"} />
|
|
37
|
-
</Notifications>
|
|
38
|
-
}}
|
|
39
|
-
</>
|
|
40
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Ink from "../bindings/Ink.res.mjs";
|
|
4
|
-
import * as Ink$1 from "ink";
|
|
5
|
-
import * as CustomHooks from "./CustomHooks.res.mjs";
|
|
6
|
-
import * as JsxRuntime from "react/jsx-runtime";
|
|
7
|
-
|
|
8
|
-
function Messages$Message(props) {
|
|
9
|
-
let message = props.message;
|
|
10
|
-
return JsxRuntime.jsx(Ink$1.Text, {
|
|
11
|
-
children: message.content,
|
|
12
|
-
color: CustomHooks.InitApi.toTheme(message.color)
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let Message = {
|
|
17
|
-
make: Messages$Message
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function Messages$Notifications(props) {
|
|
21
|
-
return JsxRuntime.jsxs(JsxRuntime.Fragment, {
|
|
22
|
-
children: [
|
|
23
|
-
JsxRuntime.jsx(Ink.Newline.make, {}),
|
|
24
|
-
JsxRuntime.jsx(Ink$1.Text, {
|
|
25
|
-
children: "Notifications:",
|
|
26
|
-
bold: true
|
|
27
|
-
}),
|
|
28
|
-
props.children
|
|
29
|
-
]
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let Notifications = {
|
|
34
|
-
make: Messages$Notifications
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
function Messages(props) {
|
|
38
|
-
let messages = CustomHooks.useMessages(props.config);
|
|
39
|
-
let tmp;
|
|
40
|
-
if (typeof messages !== "object") {
|
|
41
|
-
tmp = null;
|
|
42
|
-
} else if (messages.TAG === "Data") {
|
|
43
|
-
let messages$1 = messages._0;
|
|
44
|
-
tmp = messages$1.length !== 0 ? JsxRuntime.jsx(Messages$Notifications, {
|
|
45
|
-
children: messages$1.map((message, i) => JsxRuntime.jsx(Messages$Message, {
|
|
46
|
-
message: message
|
|
47
|
-
}, i.toString()))
|
|
48
|
-
}) : null;
|
|
49
|
-
} else {
|
|
50
|
-
tmp = JsxRuntime.jsx(Messages$Notifications, {
|
|
51
|
-
children: JsxRuntime.jsx(Messages$Message, {
|
|
52
|
-
message: {
|
|
53
|
-
color: "danger",
|
|
54
|
-
content: "Failed to load messages from envio server"
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return JsxRuntime.jsx(JsxRuntime.Fragment, {
|
|
60
|
-
children: tmp
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let make = Messages;
|
|
65
|
-
|
|
66
|
-
export {
|
|
67
|
-
Message,
|
|
68
|
-
Notifications,
|
|
69
|
-
make,
|
|
70
|
-
}
|
|
71
|
-
/* Ink Not a pure module */
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
open Ink
|
|
2
|
-
|
|
3
|
-
let isIndexerFullySynced = (chains: array<TuiData.chain>) => {
|
|
4
|
-
switch chains {
|
|
5
|
-
// A supervised run draws its first frame before any worker has reported, and
|
|
6
|
-
// a run with nothing to report hasn't finished syncing.
|
|
7
|
-
| [] => false
|
|
8
|
-
| chains =>
|
|
9
|
-
chains->Array.every(chain =>
|
|
10
|
-
switch chain.progress {
|
|
11
|
-
| Synced(_) => true
|
|
12
|
-
| _ => false
|
|
13
|
-
}
|
|
14
|
-
)
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let getTotalRemainingBlocks = (chains: array<TuiData.chain>) => {
|
|
19
|
-
chains->Array.reduce(0, (accum, {progress, knownHeight, latestFetchedBlockNumber, endBlock}) => {
|
|
20
|
-
let finalBlock = switch endBlock {
|
|
21
|
-
| Some(endBlock) => endBlock
|
|
22
|
-
| None => knownHeight
|
|
23
|
-
}
|
|
24
|
-
switch progress {
|
|
25
|
-
| Syncing({latestProcessedBlock})
|
|
26
|
-
| Synced({latestProcessedBlock}) =>
|
|
27
|
-
finalBlock - latestProcessedBlock + accum
|
|
28
|
-
| SearchingForEvents => finalBlock - latestFetchedBlockNumber + accum
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let getLatestTimeCaughtUpToHead = (chains: array<TuiData.chain>, indexerStartTime: Date.t) => {
|
|
34
|
-
let latesttimestampCaughtUpToHeadOrEndblockFloat = chains->Array.reduce(0.0, (accum, current) => {
|
|
35
|
-
switch current.progress {
|
|
36
|
-
| Synced({timestampCaughtUpToHeadOrEndblock}) =>
|
|
37
|
-
timestampCaughtUpToHeadOrEndblock->Date.getTime > accum
|
|
38
|
-
? timestampCaughtUpToHeadOrEndblock->Date.getTime
|
|
39
|
-
: accum
|
|
40
|
-
| Syncing(_)
|
|
41
|
-
| SearchingForEvents => accum
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
DateFns.formatDistanceWithOptions(
|
|
46
|
-
indexerStartTime,
|
|
47
|
-
latesttimestampCaughtUpToHeadOrEndblockFloat->Date.fromTime,
|
|
48
|
-
{includeSeconds: true},
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let getTotalBlocksProcessed = (chains: array<TuiData.chain>) => {
|
|
53
|
-
chains->Array.reduce(0, (accum, {progress, latestFetchedBlockNumber}) => {
|
|
54
|
-
switch progress {
|
|
55
|
-
| Syncing({latestProcessedBlock, firstEventBlockNumber})
|
|
56
|
-
| Synced({latestProcessedBlock, firstEventBlockNumber}) =>
|
|
57
|
-
latestProcessedBlock - firstEventBlockNumber + accum
|
|
58
|
-
| SearchingForEvents => latestFetchedBlockNumber + accum
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let useShouldDisplayEta = (~chains: array<TuiData.chain>) => {
|
|
64
|
-
let (shouldDisplayEta, setShouldDisplayEta) = React.useState(_ => false)
|
|
65
|
-
React.useEffect(() => {
|
|
66
|
-
if !shouldDisplayEta {
|
|
67
|
-
// Display ETA once all chains have a known height and some blocks have been fetched
|
|
68
|
-
let allChainsStartedFetching = chains->Array.every(chain => {
|
|
69
|
-
chain.knownHeight > 0 && chain.latestFetchedBlockNumber > 0
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
if allChainsStartedFetching {
|
|
73
|
-
setShouldDisplayEta(_ => true)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
None
|
|
78
|
-
}, [chains])
|
|
79
|
-
|
|
80
|
-
shouldDisplayEta
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let useEta = (~chains, ~indexerStartTime) => {
|
|
84
|
-
let shouldDisplayEta = useShouldDisplayEta(~chains)
|
|
85
|
-
let (secondsToSub, setSecondsToSub) = React.useState(_ => 0.)
|
|
86
|
-
let (timeSinceStart, setTimeSinceStart) = React.useState(_ => 0.)
|
|
87
|
-
|
|
88
|
-
React.useEffect2(() => {
|
|
89
|
-
setTimeSinceStart(_ => Date.now() -. indexerStartTime->Date.getTime)
|
|
90
|
-
setSecondsToSub(_ => 0.)
|
|
91
|
-
|
|
92
|
-
let intervalId = setInterval(() => {
|
|
93
|
-
setSecondsToSub(prev => prev +. 1.)
|
|
94
|
-
}, 1000)
|
|
95
|
-
|
|
96
|
-
Some(() => clearInterval(intervalId))
|
|
97
|
-
}, (chains, indexerStartTime))
|
|
98
|
-
|
|
99
|
-
//blocksProcessed/remainingBlocks = timeSoFar/eta
|
|
100
|
-
//eta = (timeSoFar/blocksProcessed) * remainingBlocks
|
|
101
|
-
|
|
102
|
-
let blocksProcessed = getTotalBlocksProcessed(chains)->Int.toFloat
|
|
103
|
-
if shouldDisplayEta && blocksProcessed > 0. {
|
|
104
|
-
let nowDate = Date.now()
|
|
105
|
-
let remainingBlocks = getTotalRemainingBlocks(chains)->Int.toFloat
|
|
106
|
-
let etaFloat = timeSinceStart /. blocksProcessed *. remainingBlocks
|
|
107
|
-
let millisToSub = secondsToSub *. 1000.
|
|
108
|
-
let etaFloat = Pervasives.max(etaFloat -. millisToSub, 0.0) //template this
|
|
109
|
-
let eta = (etaFloat +. nowDate)->Date.fromTime
|
|
110
|
-
let interval: DateFns.interval = {start: nowDate->Date.fromTime, end: eta}
|
|
111
|
-
let duration = DateFns.intervalToDuration(interval)
|
|
112
|
-
let formattedDuration = DateFns.formatDuration(
|
|
113
|
-
duration,
|
|
114
|
-
{format: ["hours", "minutes", "seconds"]},
|
|
115
|
-
)
|
|
116
|
-
let outputString = switch formattedDuration {
|
|
117
|
-
| "" => "less than 1 second"
|
|
118
|
-
| formattedDuration => formattedDuration
|
|
119
|
-
}
|
|
120
|
-
Some(outputString)
|
|
121
|
-
} else {
|
|
122
|
-
None
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
module Syncing = {
|
|
127
|
-
@react.component
|
|
128
|
-
let make = (~etaStr) => {
|
|
129
|
-
<Text bold=true>
|
|
130
|
-
<Text> {"Sync Time ETA: "->React.string} </Text>
|
|
131
|
-
<Text> {etaStr->React.string} </Text>
|
|
132
|
-
<Text> {" ("->React.string} </Text>
|
|
133
|
-
<Text color=Primary>
|
|
134
|
-
<Spinner />
|
|
135
|
-
</Text>
|
|
136
|
-
<Text color=Secondary> {" in progress"->React.string} </Text>
|
|
137
|
-
<Text> {")"->React.string} </Text>
|
|
138
|
-
</Text>
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
module Synced = {
|
|
143
|
-
@react.component
|
|
144
|
-
let make = (~latestTimeCaughtUpToHeadStr) => {
|
|
145
|
-
<Text bold=true>
|
|
146
|
-
<Text> {"Time Synced: "->React.string} </Text>
|
|
147
|
-
<Text> {`${latestTimeCaughtUpToHeadStr}`->React.string} </Text>
|
|
148
|
-
<Text> {" ("->React.string} </Text>
|
|
149
|
-
<Text color=Success> {"synced"->React.string} </Text>
|
|
150
|
-
<Text> {")"->React.string} </Text>
|
|
151
|
-
</Text>
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
module Calculating = {
|
|
156
|
-
@react.component
|
|
157
|
-
let make = () => {
|
|
158
|
-
<Text>
|
|
159
|
-
<Text color=Primary>
|
|
160
|
-
<Spinner />
|
|
161
|
-
</Text>
|
|
162
|
-
<Text bold=true> {" Calculating ETA..."->React.string} </Text>
|
|
163
|
-
</Text>
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
@react.component
|
|
168
|
-
let make = (~chains, ~indexerStartTime) => {
|
|
169
|
-
let optEta = useEta(~chains, ~indexerStartTime)
|
|
170
|
-
if isIndexerFullySynced(chains) {
|
|
171
|
-
let latestTimeCaughtUpToHeadStr = getLatestTimeCaughtUpToHead(chains, indexerStartTime)
|
|
172
|
-
<Synced latestTimeCaughtUpToHeadStr /> //TODO add real time
|
|
173
|
-
} else {
|
|
174
|
-
switch optEta {
|
|
175
|
-
| Some(etaStr) => <Syncing etaStr />
|
|
176
|
-
| None => <Calculating />
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Ink from "ink";
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
import * as DateFns from "date-fns";
|
|
6
|
-
import InkSpinner from "ink-spinner";
|
|
7
|
-
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
|
-
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
9
|
-
import * as JsxRuntime from "react/jsx-runtime";
|
|
10
|
-
|
|
11
|
-
function isIndexerFullySynced(chains) {
|
|
12
|
-
if (chains.length !== 0) {
|
|
13
|
-
return chains.every(chain => {
|
|
14
|
-
let match = chain.progress;
|
|
15
|
-
if (typeof match !== "object") {
|
|
16
|
-
return false;
|
|
17
|
-
} else {
|
|
18
|
-
return match.TAG !== "Syncing";
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
} else {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function getTotalRemainingBlocks(chains) {
|
|
27
|
-
return Stdlib_Array.reduce(chains, 0, (accum, param) => {
|
|
28
|
-
let progress = param.progress;
|
|
29
|
-
let endBlock = param.endBlock;
|
|
30
|
-
let finalBlock = endBlock !== undefined ? endBlock : param.knownHeight;
|
|
31
|
-
if (typeof progress !== "object") {
|
|
32
|
-
return (finalBlock - param.latestFetchedBlockNumber | 0) + accum | 0;
|
|
33
|
-
}
|
|
34
|
-
progress.TAG === "Syncing";
|
|
35
|
-
return (finalBlock - progress._0.latestProcessedBlock | 0) + accum | 0;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function getLatestTimeCaughtUpToHead(chains, indexerStartTime) {
|
|
40
|
-
let latesttimestampCaughtUpToHeadOrEndblockFloat = Stdlib_Array.reduce(chains, 0.0, (accum, current) => {
|
|
41
|
-
let match = current.progress;
|
|
42
|
-
if (typeof match !== "object") {
|
|
43
|
-
return accum;
|
|
44
|
-
}
|
|
45
|
-
if (match.TAG === "Syncing") {
|
|
46
|
-
return accum;
|
|
47
|
-
}
|
|
48
|
-
let timestampCaughtUpToHeadOrEndblock = match._0.timestampCaughtUpToHeadOrEndblock;
|
|
49
|
-
if (timestampCaughtUpToHeadOrEndblock.getTime() > accum) {
|
|
50
|
-
return timestampCaughtUpToHeadOrEndblock.getTime();
|
|
51
|
-
} else {
|
|
52
|
-
return accum;
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
return DateFns.formatDistance(indexerStartTime, new Date(latesttimestampCaughtUpToHeadOrEndblockFloat), {
|
|
56
|
-
includeSeconds: true
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getTotalBlocksProcessed(chains) {
|
|
61
|
-
return Stdlib_Array.reduce(chains, 0, (accum, param) => {
|
|
62
|
-
let progress = param.progress;
|
|
63
|
-
if (typeof progress !== "object") {
|
|
64
|
-
return param.latestFetchedBlockNumber + accum | 0;
|
|
65
|
-
}
|
|
66
|
-
progress.TAG === "Syncing";
|
|
67
|
-
let match = progress._0;
|
|
68
|
-
return (match.latestProcessedBlock - match.firstEventBlockNumber | 0) + accum | 0;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function useShouldDisplayEta(chains) {
|
|
73
|
-
let match = React.useState(() => false);
|
|
74
|
-
let setShouldDisplayEta = match[1];
|
|
75
|
-
let shouldDisplayEta = match[0];
|
|
76
|
-
React.useEffect(() => {
|
|
77
|
-
if (!shouldDisplayEta) {
|
|
78
|
-
let allChainsStartedFetching = chains.every(chain => {
|
|
79
|
-
if (chain.knownHeight > 0) {
|
|
80
|
-
return chain.latestFetchedBlockNumber > 0;
|
|
81
|
-
} else {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
if (allChainsStartedFetching) {
|
|
86
|
-
setShouldDisplayEta(param => true);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}, [chains]);
|
|
90
|
-
return shouldDisplayEta;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function useEta(chains, indexerStartTime) {
|
|
94
|
-
let shouldDisplayEta = useShouldDisplayEta(chains);
|
|
95
|
-
let match = React.useState(() => 0);
|
|
96
|
-
let setSecondsToSub = match[1];
|
|
97
|
-
let match$1 = React.useState(() => 0);
|
|
98
|
-
let setTimeSinceStart = match$1[1];
|
|
99
|
-
React.useEffect(() => {
|
|
100
|
-
setTimeSinceStart(param => Date.now() - indexerStartTime.getTime());
|
|
101
|
-
setSecondsToSub(param => 0);
|
|
102
|
-
let intervalId = setInterval(() => setSecondsToSub(prev => prev + 1), 1000);
|
|
103
|
-
return () => {
|
|
104
|
-
clearInterval(intervalId);
|
|
105
|
-
};
|
|
106
|
-
}, [
|
|
107
|
-
chains,
|
|
108
|
-
indexerStartTime
|
|
109
|
-
]);
|
|
110
|
-
let blocksProcessed = getTotalBlocksProcessed(chains);
|
|
111
|
-
if (!(shouldDisplayEta && blocksProcessed > 0)) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
let nowDate = Date.now();
|
|
115
|
-
let remainingBlocks = getTotalRemainingBlocks(chains);
|
|
116
|
-
let etaFloat = match$1[0] / blocksProcessed * remainingBlocks;
|
|
117
|
-
let millisToSub = match[0] * 1000;
|
|
118
|
-
let etaFloat$1 = Primitive_float.max(etaFloat - millisToSub, 0.0);
|
|
119
|
-
let eta = new Date(etaFloat$1 + nowDate);
|
|
120
|
-
let interval_start = new Date(nowDate);
|
|
121
|
-
let interval = {
|
|
122
|
-
start: interval_start,
|
|
123
|
-
end: eta
|
|
124
|
-
};
|
|
125
|
-
let duration = DateFns.intervalToDuration(interval);
|
|
126
|
-
let formattedDuration = DateFns.formatDuration(duration, {
|
|
127
|
-
format: [
|
|
128
|
-
"hours",
|
|
129
|
-
"minutes",
|
|
130
|
-
"seconds"
|
|
131
|
-
]
|
|
132
|
-
});
|
|
133
|
-
let tmp = formattedDuration === "" ? "less than 1 second" : formattedDuration;
|
|
134
|
-
return tmp;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function SyncETA$Syncing(props) {
|
|
138
|
-
return JsxRuntime.jsxs(Ink.Text, {
|
|
139
|
-
children: [
|
|
140
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
141
|
-
children: "Sync Time ETA: "
|
|
142
|
-
}),
|
|
143
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
144
|
-
children: props.etaStr
|
|
145
|
-
}),
|
|
146
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
147
|
-
children: " ("
|
|
148
|
-
}),
|
|
149
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
150
|
-
children: JsxRuntime.jsx(InkSpinner, {}),
|
|
151
|
-
color: "#9860E5"
|
|
152
|
-
}),
|
|
153
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
154
|
-
children: " in progress",
|
|
155
|
-
color: "#FFBB2F"
|
|
156
|
-
}),
|
|
157
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
158
|
-
children: ")"
|
|
159
|
-
})
|
|
160
|
-
],
|
|
161
|
-
bold: true
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
let Syncing = {
|
|
166
|
-
make: SyncETA$Syncing
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
function SyncETA$Synced(props) {
|
|
170
|
-
return JsxRuntime.jsxs(Ink.Text, {
|
|
171
|
-
children: [
|
|
172
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
173
|
-
children: "Time Synced: "
|
|
174
|
-
}),
|
|
175
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
176
|
-
children: props.latestTimeCaughtUpToHeadStr
|
|
177
|
-
}),
|
|
178
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
179
|
-
children: " ("
|
|
180
|
-
}),
|
|
181
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
182
|
-
children: "synced",
|
|
183
|
-
color: "#3B8C3D"
|
|
184
|
-
}),
|
|
185
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
186
|
-
children: ")"
|
|
187
|
-
})
|
|
188
|
-
],
|
|
189
|
-
bold: true
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
let Synced = {
|
|
194
|
-
make: SyncETA$Synced
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
function SyncETA$Calculating(props) {
|
|
198
|
-
return JsxRuntime.jsxs(Ink.Text, {
|
|
199
|
-
children: [
|
|
200
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
201
|
-
children: JsxRuntime.jsx(InkSpinner, {}),
|
|
202
|
-
color: "#9860E5"
|
|
203
|
-
}),
|
|
204
|
-
JsxRuntime.jsx(Ink.Text, {
|
|
205
|
-
children: " Calculating ETA...",
|
|
206
|
-
bold: true
|
|
207
|
-
})
|
|
208
|
-
]
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
let Calculating = {
|
|
213
|
-
make: SyncETA$Calculating
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
function SyncETA(props) {
|
|
217
|
-
let indexerStartTime = props.indexerStartTime;
|
|
218
|
-
let chains = props.chains;
|
|
219
|
-
let optEta = useEta(chains, indexerStartTime);
|
|
220
|
-
if (!isIndexerFullySynced(chains)) {
|
|
221
|
-
if (optEta !== undefined) {
|
|
222
|
-
return JsxRuntime.jsx(SyncETA$Syncing, {
|
|
223
|
-
etaStr: optEta
|
|
224
|
-
});
|
|
225
|
-
} else {
|
|
226
|
-
return JsxRuntime.jsx(SyncETA$Calculating, {});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
let latestTimeCaughtUpToHeadStr = getLatestTimeCaughtUpToHead(chains, indexerStartTime);
|
|
230
|
-
return JsxRuntime.jsx(SyncETA$Synced, {
|
|
231
|
-
latestTimeCaughtUpToHeadStr: latestTimeCaughtUpToHeadStr
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
let make = SyncETA;
|
|
236
|
-
|
|
237
|
-
export {
|
|
238
|
-
isIndexerFullySynced,
|
|
239
|
-
getTotalRemainingBlocks,
|
|
240
|
-
getLatestTimeCaughtUpToHead,
|
|
241
|
-
getTotalBlocksProcessed,
|
|
242
|
-
useShouldDisplayEta,
|
|
243
|
-
useEta,
|
|
244
|
-
Syncing,
|
|
245
|
-
Synced,
|
|
246
|
-
Calculating,
|
|
247
|
-
make,
|
|
248
|
-
}
|
|
249
|
-
/* ink Not a pure module */
|