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
package/src/tui/Tui.res
CHANGED
|
@@ -1,252 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
~startBlock,
|
|
13
|
-
~endBlock,
|
|
14
|
-
~poweredByHyperSync,
|
|
15
|
-
~eventsProcessed,
|
|
16
|
-
~blockUnit: string,
|
|
17
|
-
) => {
|
|
18
|
-
let chainsWidth = Pervasives.min(stdoutColumns - 2, 60)
|
|
19
|
-
let headerWidth = maxChainIdLength + 10 // 10 for additional text
|
|
20
|
-
|
|
21
|
-
let progressBlockStr = progressBlock->TuiData.formatLocaleString
|
|
22
|
-
let toBlockStr = toBlock->TuiData.formatLocaleString
|
|
23
|
-
let eventsStr = eventsProcessed->TuiData.formatFloatLocaleString
|
|
24
|
-
|
|
25
|
-
let endLabel = ` (End ${blockUnit})`
|
|
26
|
-
let blocksText =
|
|
27
|
-
`${blockUnit}s: ${progressBlockStr} / ${toBlockStr}` ++
|
|
28
|
-
(endBlock->Option.isSome ? endLabel : "") ++ ` `
|
|
29
|
-
let eventsText = `Events: ${eventsStr}`
|
|
30
|
-
|
|
31
|
-
let fitsSameLine = blocksText->String.length + eventsText->String.length <= chainsWidth
|
|
32
|
-
|
|
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}
|
|
40
|
-
</Box>
|
|
41
|
-
<BufferedProgressBar
|
|
42
|
-
barWidth={chainsWidth - headerWidth}
|
|
43
|
-
loaded={progressBlock - startBlock}
|
|
44
|
-
buffered={bufferBlock - startBlock}
|
|
45
|
-
outOf={toBlock - startBlock}
|
|
46
|
-
loadingColor={Secondary}
|
|
47
|
-
/>
|
|
48
|
-
</Box>
|
|
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>
|
|
60
|
-
}
|
|
1
|
+
type t
|
|
2
|
+
|
|
3
|
+
type info = {
|
|
4
|
+
ecosystem: string,
|
|
5
|
+
version: string,
|
|
6
|
+
projectDir: string,
|
|
7
|
+
startTime: float,
|
|
8
|
+
graphqlUrl: string,
|
|
9
|
+
graphqlPassword: option<string>,
|
|
10
|
+
devConsoleUrl: option<string>,
|
|
11
|
+
clickhouseUrl: option<string>,
|
|
61
12
|
}
|
|
62
13
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
let (samples, setSamples) = React.useState((): array<sample> => [])
|
|
79
|
-
|
|
80
|
-
React.useEffect1(() => {
|
|
81
|
-
let now = Date.now()
|
|
82
|
-
let cutoff = now -. windowMs
|
|
83
|
-
setSamples(prev => {
|
|
84
|
-
let kept = prev->Array.filter(s => s.time >= cutoff)
|
|
85
|
-
kept->Array.concat([{time: now, events: totalEventsProcessed}])
|
|
86
|
-
})
|
|
87
|
-
None
|
|
88
|
-
}, [tick])
|
|
89
|
-
|
|
90
|
-
computeEps(samples)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module TotalEventsProcessed = {
|
|
95
|
-
@react.component
|
|
96
|
-
let make = (~totalEventsProcessed, ~eventsPerSecond: option<float>) => {
|
|
97
|
-
<Text>
|
|
98
|
-
<Text bold=true> {"Total Events: "->React.string} </Text>
|
|
99
|
-
<Text color={Secondary}>
|
|
100
|
-
{`${totalEventsProcessed->TuiData.formatFloatLocaleString}`->React.string}
|
|
101
|
-
</Text>
|
|
102
|
-
{switch eventsPerSecond {
|
|
103
|
-
| Some(eps) =>
|
|
104
|
-
<Text color={Gray}>
|
|
105
|
-
{` (${Math.round(eps)->TuiData.formatFloatLocaleString} events/sec)`->React.string}
|
|
106
|
-
</Text>
|
|
107
|
-
| None => React.null
|
|
108
|
-
}}
|
|
109
|
-
</Text>
|
|
110
|
-
}
|
|
14
|
+
type chain = {
|
|
15
|
+
chainId: string,
|
|
16
|
+
poweredByHyperSync: bool,
|
|
17
|
+
startBlock: int,
|
|
18
|
+
endBlock: option<int>,
|
|
19
|
+
firstEventBlockNumber: option<int>,
|
|
20
|
+
progressBlockNumber: int,
|
|
21
|
+
latestFetchedBlockNumber: int,
|
|
22
|
+
knownHeight: int,
|
|
23
|
+
sourceBlockNumber: int,
|
|
24
|
+
timestampCaughtUpToHeadOrEndblock: option<float>,
|
|
25
|
+
numEventsProcessed: float,
|
|
26
|
+
rateLimitTimeMs: float,
|
|
27
|
+
rateLimitResetInMs: option<float>,
|
|
28
|
+
processedToEndblock: bool,
|
|
111
29
|
}
|
|
112
30
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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.
|
|
120
|
-
let (tick, setTick) = React.useState(() => 0)
|
|
121
|
-
let metrics: Metrics.t = getMetrics()
|
|
122
|
-
|
|
123
|
-
React.useEffect(() => {
|
|
124
|
-
let intervalId = setInterval(() => {
|
|
125
|
-
setTick(t => t + 1)
|
|
126
|
-
}, 500)
|
|
127
|
-
|
|
128
|
-
Some(
|
|
129
|
-
() => {
|
|
130
|
-
clearInterval(intervalId)
|
|
131
|
-
},
|
|
132
|
-
)
|
|
133
|
-
}, [getMetrics])
|
|
134
|
-
|
|
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))
|
|
140
|
-
|
|
141
|
-
let totalEventsProcessed = chains->Array.reduce(0., (acc, chain) => {
|
|
142
|
-
acc +. chain.eventsProcessed
|
|
143
|
-
})
|
|
144
|
-
let maxChainIdLength = chains->Array.reduce(0, (acc, chain) => {
|
|
145
|
-
let chainIdLength = chain.chainId->String.length
|
|
146
|
-
if chainIdLength > acc {
|
|
147
|
-
chainIdLength
|
|
148
|
-
} else {
|
|
149
|
-
acc
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
let eventsPerSecond = EventsPerSecond.use(~totalEventsProcessed, ~tick)
|
|
153
|
-
|
|
154
|
-
<Box flexDirection={Column}>
|
|
155
|
-
<BigText
|
|
156
|
-
text="envio"
|
|
157
|
-
colors=[Secondary, Primary]
|
|
158
|
-
font={chains->Array.length > 5 ? Tiny : Block}
|
|
159
|
-
space=false
|
|
160
|
-
/>
|
|
161
|
-
<Newline />
|
|
162
|
-
{chains
|
|
163
|
-
->Array.mapWithIndex((chainData, i) => {
|
|
164
|
-
<ChainLine
|
|
165
|
-
key={i->Int.toString}
|
|
166
|
-
chainId={chainData.chainId}
|
|
167
|
-
maxChainIdLength={maxChainIdLength}
|
|
168
|
-
progressBlock={chainData.progressBlock}
|
|
169
|
-
bufferBlock={chainData.bufferBlock}
|
|
170
|
-
toBlock={chainData.toBlock}
|
|
171
|
-
startBlock={chainData.startBlock}
|
|
172
|
-
endBlock={chainData.endBlock}
|
|
173
|
-
stdoutColumns={stdoutColumns}
|
|
174
|
-
poweredByHyperSync={chainData.poweredByHyperSync}
|
|
175
|
-
eventsProcessed={chainData.eventsProcessed}
|
|
176
|
-
blockUnit={chainData.blockUnit}
|
|
177
|
-
/>
|
|
178
|
-
})
|
|
179
|
-
->React.array}
|
|
180
|
-
<TotalEventsProcessed
|
|
181
|
-
totalEventsProcessed
|
|
182
|
-
eventsPerSecond={SyncETA.isIndexerFullySynced(chains) ? None : eventsPerSecond}
|
|
183
|
-
/>
|
|
184
|
-
<SyncETA chains indexerStartTime={metrics.startTime} />
|
|
185
|
-
{
|
|
186
|
-
let maxRateLimitTimeMs =
|
|
187
|
-
chains->Array.reduce(0., (acc, chain) => Pervasives.max(acc, chain.rateLimitTimeMs))
|
|
188
|
-
let maxResetInMs =
|
|
189
|
-
chains->Array.reduce(0.0, (acc, chain) =>
|
|
190
|
-
Pervasives.max(acc, chain.rateLimitResetInMs->Option.getOr(0.0))
|
|
191
|
-
)
|
|
192
|
-
maxRateLimitTimeMs > 1000.
|
|
193
|
-
? {
|
|
194
|
-
let rateLimitSecs = Math.round(maxRateLimitTimeMs /. 1000.)
|
|
195
|
-
let activeSuffix = if maxResetInMs > 0.0 {
|
|
196
|
-
let resetSecs = Pervasives.max(1.0, Math.ceil(maxResetInMs /. 1000.))
|
|
197
|
-
` (⏳ ${resetSecs->TuiData.formatFloatLocaleString}s until reset)`
|
|
198
|
-
} else {
|
|
199
|
-
""
|
|
200
|
-
}
|
|
201
|
-
<Box flexDirection={Column}>
|
|
202
|
-
<Newline />
|
|
203
|
-
<Text color={Danger}>
|
|
204
|
-
{`Backfill ${rateLimitSecs->TuiData.formatFloatLocaleString}s slower due to your plan's rate limit${activeSuffix}`->React.string}
|
|
205
|
-
</Text>
|
|
206
|
-
<Text color={Danger}>
|
|
207
|
-
<Text color={Danger}> {"Upgrade at "->React.string} </Text>
|
|
208
|
-
<Text color={Danger} underline=true>
|
|
209
|
-
{"https://envio.dev/app/api-tokens"->React.string}
|
|
210
|
-
</Text>
|
|
211
|
-
<Text color={Danger}> {" for higher rate limits."->React.string} </Text>
|
|
212
|
-
</Text>
|
|
213
|
-
</Box>
|
|
214
|
-
}
|
|
215
|
-
: React.null
|
|
216
|
-
}
|
|
217
|
-
<Newline />
|
|
218
|
-
<Box flexDirection={Row}>
|
|
219
|
-
<Text> {"GraphQL: "->React.string} </Text>
|
|
220
|
-
<Text color={Info} underline=true> {Env.Hasura.url->React.string} </Text>
|
|
221
|
-
{
|
|
222
|
-
let defaultPassword = "testing"
|
|
223
|
-
if Env.Hasura.secret == defaultPassword {
|
|
224
|
-
<Text color={Gray}> {` (password: ${defaultPassword})`->React.string} </Text>
|
|
225
|
-
} else {
|
|
226
|
-
React.null
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
</Box>
|
|
230
|
-
{if config.isDev {
|
|
231
|
-
<Box flexDirection={Row}>
|
|
232
|
-
<Text> {"Dev Console: "->React.string} </Text>
|
|
233
|
-
<Text color={Info} underline=true> {`${Env.envioAppUrl}/console`->React.string} </Text>
|
|
234
|
-
</Box>
|
|
235
|
-
} else {
|
|
236
|
-
React.null
|
|
237
|
-
}}
|
|
238
|
-
{switch (config.storage.clickhouse, Env.ClickHouse.host()) {
|
|
239
|
-
| (true, Some(host)) =>
|
|
240
|
-
<Box flexDirection={Row}>
|
|
241
|
-
<Text> {"ClickHouse: "->React.string} </Text>
|
|
242
|
-
<Text color={Info} underline=true> {`${host}/play`->React.string} </Text>
|
|
243
|
-
</Box>
|
|
244
|
-
| _ => React.null
|
|
245
|
-
}}
|
|
246
|
-
<Messages config />
|
|
247
|
-
</Box>
|
|
248
|
-
}
|
|
249
|
-
}
|
|
31
|
+
@send external make: (Core.tuiCtor, info) => t = "start"
|
|
32
|
+
@send external update: (t, array<chain>) => unit = "update"
|
|
33
|
+
@send external setMessages: (t, Null.t<array<InitApi.message>>) => unit = "setMessages"
|
|
34
|
+
@send external print: (t, string) => bool = "print"
|
|
35
|
+
@send external stop: t => unit = "stop"
|
|
250
36
|
|
|
251
37
|
// Whether this process draws the progress display: `ENVIO_TUI` first, then
|
|
252
38
|
// whether anything is watching. A supervisor asks the same question its
|
|
@@ -258,9 +44,116 @@ let shouldUse = (~suppressed=false, ~explicitTui=Env.tuiEnvVar) =>
|
|
|
258
44
|
| (_, None) => !Envio.isNonInteractive()
|
|
259
45
|
}
|
|
260
46
|
|
|
261
|
-
let
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
47
|
+
let toChain = (m: Metrics.chainMetrics): chain => {
|
|
48
|
+
chainId: m.chainId->ChainId.toString,
|
|
49
|
+
poweredByHyperSync: m.poweredByHyperSync,
|
|
50
|
+
startBlock: m.startBlock,
|
|
51
|
+
endBlock: m.endBlock,
|
|
52
|
+
firstEventBlockNumber: m.firstEventBlockNumber,
|
|
53
|
+
progressBlockNumber: m.progressBlockNumber,
|
|
54
|
+
latestFetchedBlockNumber: m.latestFetchedBlockNumber,
|
|
55
|
+
knownHeight: m.knownHeight,
|
|
56
|
+
sourceBlockNumber: m.sourceBlockNumber,
|
|
57
|
+
timestampCaughtUpToHeadOrEndblock: m.timestampCaughtUpToHeadOrEndblock->Option.map(Date.getTime),
|
|
58
|
+
numEventsProcessed: m.numEventsProcessed,
|
|
59
|
+
rateLimitTimeMs: m.rateLimitTimeMs,
|
|
60
|
+
rateLimitResetInMs: m.rateLimitResetInMs,
|
|
61
|
+
processedToEndblock: m->Metrics.hasProcessedToEndblock,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type consoleClass
|
|
65
|
+
type writableClass
|
|
66
|
+
@module("node:console") external consoleClass: consoleClass = "Console"
|
|
67
|
+
@module("node:stream") external writableClass: writableClass = "Writable"
|
|
68
|
+
|
|
69
|
+
// Sends everything written through `console` above the display, the way the
|
|
70
|
+
// terminal would have shown it, and returns the function that restores it.
|
|
71
|
+
// Routed through a `Console` of our own so every method formats as Node's
|
|
72
|
+
// would; stderr stays where it is when it isn't the terminal, and once the
|
|
73
|
+
// display is gone, output goes back to where it came from.
|
|
74
|
+
let redirectConsole: (
|
|
75
|
+
consoleClass,
|
|
76
|
+
writableClass,
|
|
77
|
+
string => bool,
|
|
78
|
+
) => unit => unit = %raw(`(Console, Writable, print) => {
|
|
79
|
+
let original = [];
|
|
80
|
+
const restore = () => original.forEach(([name, method]) => { console[name] = method; });
|
|
81
|
+
const toDisplay = (stream) => new Writable({
|
|
82
|
+
write(chunk, _encoding, callback) {
|
|
83
|
+
// Each call ends its output with a newline, which printing a line adds.
|
|
84
|
+
if (!print(chunk.toString().replace(/\n$/, ""))) {
|
|
85
|
+
restore();
|
|
86
|
+
stream.write(chunk);
|
|
87
|
+
}
|
|
88
|
+
callback();
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
const redirected = new Console({
|
|
92
|
+
stdout: toDisplay(process.stdout),
|
|
93
|
+
stderr: process.stderr.isTTY ? toDisplay(process.stderr) : process.stderr,
|
|
94
|
+
colorMode: process.stdout.hasColors(),
|
|
95
|
+
});
|
|
96
|
+
const methods = Object.keys(console).filter((name) => typeof redirected[name] === "function");
|
|
97
|
+
original = methods.map((name) => [name, console[name]]);
|
|
98
|
+
methods.forEach((name) => { console[name] = redirected[name].bind(redirected); });
|
|
99
|
+
return restore;
|
|
100
|
+
}`)
|
|
101
|
+
|
|
102
|
+
let start = (~config: Config.t, ~getMetrics: unit => Metrics.t) => {
|
|
103
|
+
let metrics = getMetrics()
|
|
104
|
+
let info = {
|
|
105
|
+
ecosystem: (config.ecosystem.name :> string),
|
|
106
|
+
version: Utils.EnvioPackage.value.version,
|
|
107
|
+
projectDir: NodeJs.Process.cwd(),
|
|
108
|
+
startTime: metrics.startTime->Date.getTime,
|
|
109
|
+
graphqlUrl: Env.Hasura.url,
|
|
110
|
+
graphqlPassword: Env.Hasura.secret === "testing" ? Some("testing") : None,
|
|
111
|
+
devConsoleUrl: config.isDev ? Some(`${Env.envioAppUrl}/console`) : None,
|
|
112
|
+
clickhouseUrl: switch (config.storage.clickhouse, Env.ClickHouse.host()) {
|
|
113
|
+
| (true, Some(host)) => Some(`${host}/play`)
|
|
114
|
+
| _ => None
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
switch Core.getAddon().tui->make(info) {
|
|
118
|
+
| exception exn =>
|
|
119
|
+
Logging.warn({
|
|
120
|
+
"msg": "Failed to start the TUI. Continuing without it.",
|
|
121
|
+
"err": exn->Utils.prettifyExn,
|
|
122
|
+
})
|
|
123
|
+
| tui =>
|
|
124
|
+
let restoreConsole = redirectConsole(consoleClass, writableClass, text => tui->print(text))
|
|
125
|
+
let update = () => tui->update(getMetrics().chains->Array.map(toChain))
|
|
126
|
+
update()
|
|
127
|
+
let _ = setInterval(update, 500)
|
|
128
|
+
let finish = () => {
|
|
129
|
+
tui->stop
|
|
130
|
+
restoreConsole()
|
|
131
|
+
}
|
|
132
|
+
NodeJs.Process.onExit(finish)
|
|
133
|
+
// A signal nothing else listens for ends the process without an `exit`
|
|
134
|
+
// event, so the display finishes first and the signal is raised again
|
|
135
|
+
// once its default action is back.
|
|
136
|
+
["SIGINT", "SIGTERM", "SIGHUP"]->Array.forEach(signal => {
|
|
137
|
+
let rec onSignal = () =>
|
|
138
|
+
if NodeJs.Process.listenerCount(signal) === 1 {
|
|
139
|
+
finish()
|
|
140
|
+
NodeJs.Process.offSignal(signal, onSignal)
|
|
141
|
+
NodeJs.Process.kill(NodeJs.Process.pid, signal)->ignore
|
|
142
|
+
}
|
|
143
|
+
NodeJs.Process.onSignal(signal, onSignal)
|
|
144
|
+
})
|
|
145
|
+
InitApi.getMessages(~config)
|
|
146
|
+
->Promise.thenResolve(result =>
|
|
147
|
+
switch result {
|
|
148
|
+
| Ok(messages) => tui->setMessages(Null.Value(messages))
|
|
149
|
+
| Error(exn) =>
|
|
150
|
+
Logging.error({
|
|
151
|
+
"msg": "Failed to load messages from envio server",
|
|
152
|
+
"err": exn->Utils.prettifyExn,
|
|
153
|
+
})
|
|
154
|
+
tui->setMessages(Null.Null)
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
->ignore
|
|
265
158
|
}
|
|
266
159
|
}
|