forked from LedgerHQ/ledger-live-common
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrunner.js
105 lines (99 loc) · 3 KB
/
runner.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// @flow
import { Observable, from, of, defer, concat } from "rxjs";
import {
map,
materialize,
reduce,
ignoreElements,
throttleTime,
scan,
mergeMap,
distinctUntilChanged,
} from "rxjs/operators";
import type { Exec, State, AppOp, RunnerEvent } from "./types";
import { reducer, getActionPlan, getNextAppOp } from "./logic";
import { delay } from "../promise";
import { getEnv } from "../env";
export const runAppOp = (
{ appByName, deviceInfo }: State,
appOp: AppOp,
exec: Exec
): Observable<RunnerEvent> => {
const app = appByName[appOp.name];
if (!app) {
// app not in list, we skip it.
return from([
{ type: "runStart", appOp },
{ type: "runSuccess", appOp },
]);
}
return concat(
of({ type: "runStart", appOp }),
// we need to allow a 1s delay for the action to be achieved without glitch (bug in old firmware when you do things too closely)
defer(() => delay(getEnv("MANAGER_INSTALL_DELAY"))).pipe(ignoreElements()),
defer(() => exec(appOp, deviceInfo.targetId, app)).pipe(
throttleTime(100),
materialize(),
map((n) => {
switch (n.kind) {
case "N":
return { type: "runProgress", appOp, progress: n.value.progress };
case "E":
return { type: "runError", appOp, error: n.error };
case "C":
return { type: "runSuccess", appOp };
default:
throw new Error("invalid notification of kind=" + n.kind);
}
})
)
);
};
export const runAllWithProgress = (
state: State,
exec: Exec,
precision: number = 100
): Observable<number> => {
const total = state.uninstallQueue.length + state.installQueue.length;
function globalProgress(s, localProgress) {
let p =
1 -
(s.uninstallQueue.length + s.installQueue.length - localProgress) / total;
p = Math.round(p * precision) / precision;
return p;
}
return concat(
...getActionPlan(state).map((appOp) => runAppOp(state, appOp, exec))
).pipe(
map((event) => ({ type: "onRunnerEvent", event })),
scan(reducer, state),
mergeMap((s) => {
const { currentProgressSubject } = s;
if (!currentProgressSubject) return of(globalProgress(s, 0));
return currentProgressSubject.pipe(map((v) => globalProgress(s, v)));
}),
distinctUntilChanged()
);
};
// use for CLI, no change of the state over time
export const runAll = (state: State, exec: Exec): Observable<State> =>
concat(
...getActionPlan(state).map((appOp) => runAppOp(state, appOp, exec))
).pipe(
map((event) => ({ type: "onRunnerEvent", event })),
reduce(reducer, state)
);
export const runOneAppOp = (
state: State,
appOp: AppOp,
exec: Exec
): Observable<State> =>
runAppOp(state, appOp, exec).pipe(
map((event) => ({ type: "onRunnerEvent", event })),
reduce(reducer, state)
);
export const runOne = (state: State, exec: Exec): Observable<State> => {
const next = getNextAppOp(state);
if (!next) return of(state);
return runOneAppOp(state, next, exec);
};