@ledgerhq/live-common 34.35.1-hotfix.0 → 34.35.2-nightly.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/lib/apps/support.d.ts +1 -0
- package/lib/apps/support.d.ts.map +1 -1
- package/lib/apps/support.js +10 -1
- package/lib/apps/support.js.map +1 -1
- package/lib/e2e/enum/Account.d.ts +64 -58
- package/lib/e2e/enum/Account.d.ts.map +1 -1
- package/lib/e2e/enum/Account.js +80 -59
- package/lib/e2e/enum/Account.js.map +1 -1
- package/lib/e2e/enum/Device.d.ts +8 -0
- package/lib/e2e/enum/Device.d.ts.map +1 -0
- package/lib/e2e/enum/Device.js +14 -0
- package/lib/e2e/enum/Device.js.map +1 -0
- package/lib/e2e/enum/Swap.d.ts +2 -1
- package/lib/e2e/enum/Swap.d.ts.map +1 -1
- package/lib/e2e/enum/Swap.js +12 -10
- package/lib/e2e/enum/Swap.js.map +1 -1
- package/lib/e2e/families/cardano.d.ts.map +1 -1
- package/lib/e2e/families/cardano.js +55 -26
- package/lib/e2e/families/cardano.js.map +1 -1
- package/lib/e2e/families/evm.d.ts.map +1 -1
- package/lib/e2e/families/evm.js +2 -1
- package/lib/e2e/families/evm.js.map +1 -1
- package/lib/e2e/families/solana.d.ts.map +1 -1
- package/lib/e2e/families/solana.js +5 -2
- package/lib/e2e/families/solana.js.map +1 -1
- package/lib/e2e/index.d.ts +2 -0
- package/lib/e2e/index.d.ts.map +1 -1
- package/lib/e2e/models/BuySell.d.ts +11 -0
- package/lib/e2e/models/BuySell.d.ts.map +1 -0
- package/lib/{index.js → e2e/models/BuySell.js} +1 -1
- package/lib/e2e/models/BuySell.js.map +1 -0
- package/lib/e2e/models/Transaction.d.ts +5 -5
- package/lib/e2e/models/Transaction.d.ts.map +1 -1
- package/lib/e2e/models/Transaction.js.map +1 -1
- package/lib/e2e/speculos.d.ts +6 -1
- package/lib/e2e/speculos.d.ts.map +1 -1
- package/lib/e2e/speculos.js +72 -40
- package/lib/e2e/speculos.js.map +1 -1
- package/lib/e2e/speculosCI.d.ts +5 -0
- package/lib/e2e/speculosCI.d.ts.map +1 -0
- package/lib/e2e/speculosCI.js +129 -0
- package/lib/e2e/speculosCI.js.map +1 -0
- package/lib/families/hedera/logic.d.ts +2 -0
- package/lib/families/hedera/logic.d.ts.map +1 -0
- package/lib/families/hedera/logic.js +19 -0
- package/lib/families/hedera/logic.js.map +1 -0
- package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib/featureFlags/defaultFeatures.js +1 -0
- package/lib/featureFlags/defaultFeatures.js.map +1 -1
- package/lib/featureFlags/useFeature.d.ts +1 -1
- package/lib/featureFlags/useFeature.d.ts.map +1 -1
- package/lib/hw/actions/manager.d.ts +1 -1
- package/lib/hw/actions/manager.d.ts.map +1 -1
- package/lib/hw/connectApp.d.ts.map +1 -1
- package/lib/hw/connectApp.js +56 -5
- package/lib/hw/connectApp.js.map +1 -1
- package/lib/hw/connectAppEventMapper.d.ts +29 -0
- package/lib/hw/connectAppEventMapper.d.ts.map +1 -0
- package/lib/hw/connectAppEventMapper.js +300 -0
- package/lib/hw/connectAppEventMapper.js.map +1 -0
- package/lib/hw/connectManager.d.ts +3 -2
- package/lib/hw/connectManager.d.ts.map +1 -1
- package/lib/hw/connectManager.js +34 -3
- package/lib/hw/connectManager.js.map +1 -1
- package/lib/hw/connectManagerEventMapper.d.ts +16 -0
- package/lib/hw/connectManagerEventMapper.d.ts.map +1 -0
- package/lib/hw/connectManagerEventMapper.js +78 -0
- package/lib/hw/connectManagerEventMapper.js.map +1 -0
- package/lib/market/hooks/useLargeMoverChartData.d.ts +18 -0
- package/lib/market/hooks/useLargeMoverChartData.d.ts.map +1 -0
- package/lib/market/hooks/useLargeMoverChartData.js +28 -0
- package/lib/market/hooks/useLargeMoverChartData.js.map +1 -0
- package/lib/market/hooks/useLargeMoverCurrencies.d.ts +13 -0
- package/lib/market/hooks/useLargeMoverCurrencies.d.ts.map +1 -0
- package/lib/market/hooks/useLargeMoverCurrencies.js +29 -0
- package/lib/market/hooks/useLargeMoverCurrencies.js.map +1 -0
- package/lib/notifications/AnnouncementProvider/index.d.ts.map +1 -1
- package/lib/notifications/AnnouncementProvider/index.js +12 -10
- package/lib/notifications/AnnouncementProvider/index.js.map +1 -1
- package/lib/notifications/AnnouncementProvider/machine.d.ts +1 -4
- package/lib/notifications/AnnouncementProvider/machine.d.ts.map +1 -1
- package/lib/notifications/AnnouncementProvider/machine.js +9 -8
- package/lib/notifications/AnnouncementProvider/machine.js.map +1 -1
- package/lib/notifications/ServiceStatusProvider/index.d.ts.map +1 -1
- package/lib/notifications/ServiceStatusProvider/index.js +5 -4
- package/lib/notifications/ServiceStatusProvider/index.js.map +1 -1
- package/lib/notifications/ServiceStatusProvider/machine.d.ts +1 -4
- package/lib/notifications/ServiceStatusProvider/machine.d.ts.map +1 -1
- package/lib/notifications/ServiceStatusProvider/machine.js +4 -4
- package/lib/notifications/ServiceStatusProvider/machine.js.map +1 -1
- package/lib/wallet-api/ModularDrawer/types.d.ts +63 -0
- package/lib/wallet-api/ModularDrawer/types.d.ts.map +1 -0
- package/lib/wallet-api/ModularDrawer/types.js +25 -0
- package/lib/wallet-api/ModularDrawer/types.js.map +1 -0
- package/lib/wallet-api/ModularDrawer/utils.d.ts +5 -0
- package/lib/wallet-api/ModularDrawer/utils.d.ts.map +1 -0
- package/lib/wallet-api/ModularDrawer/utils.js +30 -0
- package/lib/wallet-api/ModularDrawer/utils.js.map +1 -0
- package/lib/wallet-api/react.d.ts +2 -0
- package/lib/wallet-api/react.d.ts.map +1 -1
- package/lib/wallet-api/react.js +2 -1
- package/lib/wallet-api/react.js.map +1 -1
- package/lib-es/apps/support.d.ts +1 -0
- package/lib-es/apps/support.d.ts.map +1 -1
- package/lib-es/apps/support.js +8 -0
- package/lib-es/apps/support.js.map +1 -1
- package/lib-es/e2e/enum/Account.d.ts +64 -58
- package/lib-es/e2e/enum/Account.d.ts.map +1 -1
- package/lib-es/e2e/enum/Account.js +76 -58
- package/lib-es/e2e/enum/Account.js.map +1 -1
- package/lib-es/e2e/enum/Device.d.ts +8 -0
- package/lib-es/e2e/enum/Device.d.ts.map +1 -0
- package/lib-es/e2e/enum/Device.js +10 -0
- package/lib-es/e2e/enum/Device.js.map +1 -0
- package/lib-es/e2e/enum/Swap.d.ts +2 -1
- package/lib-es/e2e/enum/Swap.d.ts.map +1 -1
- package/lib-es/e2e/enum/Swap.js +12 -10
- package/lib-es/e2e/enum/Swap.js.map +1 -1
- package/lib-es/e2e/families/cardano.d.ts.map +1 -1
- package/lib-es/e2e/families/cardano.js +56 -27
- package/lib-es/e2e/families/cardano.js.map +1 -1
- package/lib-es/e2e/families/evm.d.ts.map +1 -1
- package/lib-es/e2e/families/evm.js +2 -1
- package/lib-es/e2e/families/evm.js.map +1 -1
- package/lib-es/e2e/families/solana.d.ts.map +1 -1
- package/lib-es/e2e/families/solana.js +5 -2
- package/lib-es/e2e/families/solana.js.map +1 -1
- package/lib-es/e2e/index.d.ts +2 -0
- package/lib-es/e2e/index.d.ts.map +1 -1
- package/lib-es/e2e/models/BuySell.d.ts +11 -0
- package/lib-es/e2e/models/BuySell.d.ts.map +1 -0
- package/lib-es/e2e/models/BuySell.js +2 -0
- package/lib-es/e2e/models/BuySell.js.map +1 -0
- package/lib-es/e2e/models/Transaction.d.ts +5 -5
- package/lib-es/e2e/models/Transaction.d.ts.map +1 -1
- package/lib-es/e2e/models/Transaction.js.map +1 -1
- package/lib-es/e2e/speculos.d.ts +6 -1
- package/lib-es/e2e/speculos.d.ts.map +1 -1
- package/lib-es/e2e/speculos.js +69 -39
- package/lib-es/e2e/speculos.js.map +1 -1
- package/lib-es/e2e/speculosCI.d.ts +5 -0
- package/lib-es/e2e/speculosCI.d.ts.map +1 -0
- package/lib-es/e2e/speculosCI.js +121 -0
- package/lib-es/e2e/speculosCI.js.map +1 -0
- package/lib-es/families/hedera/logic.d.ts +2 -0
- package/lib-es/families/hedera/logic.d.ts.map +1 -0
- package/lib-es/families/hedera/logic.js +3 -0
- package/lib-es/families/hedera/logic.js.map +1 -0
- package/lib-es/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib-es/featureFlags/defaultFeatures.js +1 -0
- package/lib-es/featureFlags/defaultFeatures.js.map +1 -1
- package/lib-es/featureFlags/useFeature.d.ts +1 -1
- package/lib-es/featureFlags/useFeature.d.ts.map +1 -1
- package/lib-es/hw/actions/manager.d.ts +1 -1
- package/lib-es/hw/actions/manager.d.ts.map +1 -1
- package/lib-es/hw/connectApp.d.ts.map +1 -1
- package/lib-es/hw/connectApp.js +57 -6
- package/lib-es/hw/connectApp.js.map +1 -1
- package/lib-es/hw/connectAppEventMapper.d.ts +29 -0
- package/lib-es/hw/connectAppEventMapper.d.ts.map +1 -0
- package/lib-es/hw/connectAppEventMapper.js +296 -0
- package/lib-es/hw/connectAppEventMapper.js.map +1 -0
- package/lib-es/hw/connectManager.d.ts +3 -2
- package/lib-es/hw/connectManager.d.ts.map +1 -1
- package/lib-es/hw/connectManager.js +34 -4
- package/lib-es/hw/connectManager.js.map +1 -1
- package/lib-es/hw/connectManagerEventMapper.d.ts +16 -0
- package/lib-es/hw/connectManagerEventMapper.d.ts.map +1 -0
- package/lib-es/hw/connectManagerEventMapper.js +74 -0
- package/lib-es/hw/connectManagerEventMapper.js.map +1 -0
- package/lib-es/market/hooks/useLargeMoverChartData.d.ts +18 -0
- package/lib-es/market/hooks/useLargeMoverChartData.d.ts.map +1 -0
- package/lib-es/market/hooks/useLargeMoverChartData.js +24 -0
- package/lib-es/market/hooks/useLargeMoverChartData.js.map +1 -0
- package/lib-es/market/hooks/useLargeMoverCurrencies.d.ts +13 -0
- package/lib-es/market/hooks/useLargeMoverCurrencies.d.ts.map +1 -0
- package/lib-es/market/hooks/useLargeMoverCurrencies.js +25 -0
- package/lib-es/market/hooks/useLargeMoverCurrencies.js.map +1 -0
- package/lib-es/notifications/AnnouncementProvider/index.d.ts.map +1 -1
- package/lib-es/notifications/AnnouncementProvider/index.js +12 -10
- package/lib-es/notifications/AnnouncementProvider/index.js.map +1 -1
- package/lib-es/notifications/AnnouncementProvider/machine.d.ts +1 -4
- package/lib-es/notifications/AnnouncementProvider/machine.d.ts.map +1 -1
- package/lib-es/notifications/AnnouncementProvider/machine.js +9 -8
- package/lib-es/notifications/AnnouncementProvider/machine.js.map +1 -1
- package/lib-es/notifications/ServiceStatusProvider/index.d.ts.map +1 -1
- package/lib-es/notifications/ServiceStatusProvider/index.js +5 -4
- package/lib-es/notifications/ServiceStatusProvider/index.js.map +1 -1
- package/lib-es/notifications/ServiceStatusProvider/machine.d.ts +1 -4
- package/lib-es/notifications/ServiceStatusProvider/machine.d.ts.map +1 -1
- package/lib-es/notifications/ServiceStatusProvider/machine.js +4 -4
- package/lib-es/notifications/ServiceStatusProvider/machine.js.map +1 -1
- package/lib-es/wallet-api/ModularDrawer/types.d.ts +63 -0
- package/lib-es/wallet-api/ModularDrawer/types.d.ts.map +1 -0
- package/lib-es/wallet-api/ModularDrawer/types.js +22 -0
- package/lib-es/wallet-api/ModularDrawer/types.js.map +1 -0
- package/lib-es/wallet-api/ModularDrawer/utils.d.ts +5 -0
- package/lib-es/wallet-api/ModularDrawer/utils.d.ts.map +1 -0
- package/lib-es/wallet-api/ModularDrawer/utils.js +25 -0
- package/lib-es/wallet-api/ModularDrawer/utils.js.map +1 -0
- package/lib-es/wallet-api/react.d.ts +2 -0
- package/lib-es/wallet-api/react.d.ts.map +1 -1
- package/lib-es/wallet-api/react.js +2 -1
- package/lib-es/wallet-api/react.js.map +1 -1
- package/package.json +53 -52
- package/src/apps/support.ts +11 -0
- package/src/e2e/enum/Account.ts +363 -362
- package/src/e2e/enum/Device.ts +7 -0
- package/src/e2e/enum/Swap.ts +10 -9
- package/src/e2e/families/cardano.ts +63 -28
- package/src/e2e/families/evm.ts +2 -1
- package/src/e2e/families/solana.ts +5 -2
- package/src/e2e/models/BuySell.ts +12 -0
- package/src/e2e/models/Transaction.ts +5 -5
- package/src/e2e/speculos.ts +75 -41
- package/src/e2e/speculosCI.ts +161 -0
- package/src/families/hedera/logic.ts +2 -0
- package/src/featureFlags/defaultFeatures.ts +1 -0
- package/src/hw/actions/manager.ts +1 -1
- package/src/hw/connectApp.ts +245 -178
- package/src/hw/connectAppEventMapper.ts +364 -0
- package/src/hw/connectManager.ts +116 -74
- package/src/hw/connectManagerEventMapper.ts +109 -0
- package/src/market/hooks/useLargeMoverChartData.ts +38 -0
- package/src/market/hooks/useLargeMoverCurrencies.ts +36 -0
- package/src/notifications/AnnouncementProvider/index.tsx +21 -17
- package/src/notifications/AnnouncementProvider/machine.ts +9 -8
- package/src/notifications/ServiceStatusProvider/index.tsx +11 -8
- package/src/notifications/ServiceStatusProvider/machine.ts +4 -4
- package/src/wallet-api/ModularDrawer/types.ts +45 -0
- package/src/wallet-api/ModularDrawer/utils.ts +37 -0
- package/src/wallet-api/react.ts +37 -31
- package/lib/index.d.ts +0 -2
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/market/hooks/useLargeMoverDataProvider.d.ts +0 -17
- package/lib/market/hooks/useLargeMoverDataProvider.d.ts.map +0 -1
- package/lib/market/hooks/useLargeMoverDataProvider.js +0 -48
- package/lib/market/hooks/useLargeMoverDataProvider.js.map +0 -1
- package/lib-es/index.d.ts +0 -1
- package/lib-es/index.d.ts.map +0 -1
- package/lib-es/index.js +0 -2
- package/lib-es/index.js.map +0 -1
- package/lib-es/market/hooks/useLargeMoverDataProvider.d.ts +0 -17
- package/lib-es/market/hooks/useLargeMoverDataProvider.d.ts.map +0 -1
- package/lib-es/market/hooks/useLargeMoverDataProvider.js +0 -44
- package/lib-es/market/hooks/useLargeMoverDataProvider.js.map +0 -1
- package/src/index.ts +0 -0
- package/src/market/hooks/useLargeMoverDataProvider.ts +0 -70
@@ -0,0 +1,364 @@
|
|
1
|
+
import { Observable, Subject, of, share, takeUntil } from "rxjs";
|
2
|
+
import { catchError, tap, withLatestFrom } from "rxjs/operators";
|
3
|
+
import type {
|
4
|
+
DeviceManagementKit,
|
5
|
+
DeviceActionState,
|
6
|
+
InstallPlan,
|
7
|
+
ExecuteDeviceActionReturnType,
|
8
|
+
DeviceSessionState,
|
9
|
+
GetOsVersionResponse,
|
10
|
+
FirmwareUpdateContext as DmkFirmwareUpdateContext,
|
11
|
+
} from "@ledgerhq/device-management-kit";
|
12
|
+
import {
|
13
|
+
DeviceActionStatus,
|
14
|
+
DeviceLockedError,
|
15
|
+
DeviceSessionStateType,
|
16
|
+
DeviceStatus,
|
17
|
+
UserInteractionRequired,
|
18
|
+
OutOfMemoryDAError,
|
19
|
+
SecureChannelError,
|
20
|
+
UnsupportedFirmwareDAError,
|
21
|
+
} from "@ledgerhq/device-management-kit";
|
22
|
+
import type {
|
23
|
+
ConnectAppDAOutput,
|
24
|
+
ConnectAppDAError,
|
25
|
+
ConnectAppDAIntermediateValue,
|
26
|
+
} from "@ledgerhq/live-dmk-shared";
|
27
|
+
import { DeviceInfo, FirmwareUpdateContext } from "@ledgerhq/types-live";
|
28
|
+
import {
|
29
|
+
UserRefusedAllowManager,
|
30
|
+
UserRefusedOnDevice,
|
31
|
+
LatestFirmwareVersionRequired,
|
32
|
+
} from "@ledgerhq/errors";
|
33
|
+
|
34
|
+
import type { SkippedAppOp } from "../apps/types";
|
35
|
+
import { SkipReason } from "../apps/types";
|
36
|
+
import { parseDeviceInfo } from "../deviceSDK/tasks/getDeviceInfo";
|
37
|
+
import { ConnectAppEvent } from "./connectApp";
|
38
|
+
|
39
|
+
export class ConnectAppEventMapper {
|
40
|
+
private openAppRequested: boolean = false;
|
41
|
+
private permissionRequested: boolean = false;
|
42
|
+
private lastSeenDeviceSent: boolean = false;
|
43
|
+
private installPlan: InstallPlan | null = null;
|
44
|
+
private eventSubject = new Subject<ConnectAppEvent>();
|
45
|
+
|
46
|
+
constructor(
|
47
|
+
private dmk: DeviceManagementKit,
|
48
|
+
private sessionId: string,
|
49
|
+
private appName: string,
|
50
|
+
private events: ExecuteDeviceActionReturnType<
|
51
|
+
ConnectAppDAOutput,
|
52
|
+
ConnectAppDAError,
|
53
|
+
ConnectAppDAIntermediateValue
|
54
|
+
>,
|
55
|
+
) {}
|
56
|
+
|
57
|
+
map(): Observable<ConnectAppEvent> {
|
58
|
+
const cancelAction = this.events.cancel;
|
59
|
+
const unsubscribe = new Subject<void>();
|
60
|
+
|
61
|
+
// Create a shared observable for device session state
|
62
|
+
const deviceSessionState = this.dmk
|
63
|
+
.getDeviceSessionState({ sessionId: this.sessionId })
|
64
|
+
.pipe(share());
|
65
|
+
|
66
|
+
// Subscribe to device action events
|
67
|
+
this.events.observable
|
68
|
+
.pipe(
|
69
|
+
withLatestFrom(deviceSessionState),
|
70
|
+
tap(([event, deviceState]) => this.handleEvent(event, deviceState)),
|
71
|
+
takeUntil(unsubscribe),
|
72
|
+
catchError(error => this.handleError(error)),
|
73
|
+
)
|
74
|
+
.subscribe();
|
75
|
+
|
76
|
+
// Subscribe to device session state events
|
77
|
+
deviceSessionState
|
78
|
+
.pipe(
|
79
|
+
tap(deviceState => this.handleDeviceState(deviceState)),
|
80
|
+
takeUntil(unsubscribe),
|
81
|
+
)
|
82
|
+
.subscribe();
|
83
|
+
|
84
|
+
return new Observable<ConnectAppEvent>(observer => {
|
85
|
+
const sub = this.eventSubject.subscribe(observer);
|
86
|
+
return () => {
|
87
|
+
sub.unsubscribe();
|
88
|
+
cancelAction();
|
89
|
+
unsubscribe.next();
|
90
|
+
};
|
91
|
+
});
|
92
|
+
}
|
93
|
+
|
94
|
+
private handleDeviceState(deviceState: DeviceSessionState): void {
|
95
|
+
if (deviceState.sessionStateType === DeviceSessionStateType.Connected) {
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
|
99
|
+
if (deviceState.deviceStatus === DeviceStatus.NOT_CONNECTED) {
|
100
|
+
this.eventSubject.next({ type: "disconnected", expected: false });
|
101
|
+
} else if (
|
102
|
+
deviceState.firmwareVersion?.metadata &&
|
103
|
+
deviceState.firmwareUpdateContext &&
|
104
|
+
!this.lastSeenDeviceSent
|
105
|
+
) {
|
106
|
+
this.lastSeenDeviceSent = true;
|
107
|
+
this.eventSubject.next({
|
108
|
+
type: "device-update-last-seen",
|
109
|
+
deviceInfo: this.mapDeviceInfo(deviceState.firmwareVersion.metadata),
|
110
|
+
latestFirmware: this.mapLatestFirmware(deviceState.firmwareUpdateContext),
|
111
|
+
});
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
private handleEvent(
|
116
|
+
event: DeviceActionState<ConnectAppDAOutput, ConnectAppDAError, ConnectAppDAIntermediateValue>,
|
117
|
+
deviceState: DeviceSessionState,
|
118
|
+
): void {
|
119
|
+
switch (event.status) {
|
120
|
+
case DeviceActionStatus.Pending:
|
121
|
+
this.handlePendingEvent(event.intermediateValue);
|
122
|
+
break;
|
123
|
+
case DeviceActionStatus.Completed:
|
124
|
+
this.handleCompletedEvent(event.output, deviceState);
|
125
|
+
break;
|
126
|
+
case DeviceActionStatus.Error:
|
127
|
+
this.handleErrorEvent(event.error, deviceState);
|
128
|
+
break;
|
129
|
+
case DeviceActionStatus.NotStarted:
|
130
|
+
case DeviceActionStatus.Stopped:
|
131
|
+
this.eventSubject.error(new Error("Unexpected device action status"));
|
132
|
+
break;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
private handlePendingEvent(intermediateValue: ConnectAppDAIntermediateValue): void {
|
137
|
+
switch (intermediateValue.requiredUserInteraction) {
|
138
|
+
case UserInteractionRequired.ConfirmOpenApp:
|
139
|
+
if (!this.openAppRequested) {
|
140
|
+
this.openAppRequested = true;
|
141
|
+
this.eventSubject.next({ type: "ask-open-app", appName: this.appName });
|
142
|
+
}
|
143
|
+
break;
|
144
|
+
case UserInteractionRequired.AllowSecureConnection:
|
145
|
+
if (!this.permissionRequested) {
|
146
|
+
this.permissionRequested = true;
|
147
|
+
this.eventSubject.next({ type: "device-permission-requested" });
|
148
|
+
}
|
149
|
+
break;
|
150
|
+
case UserInteractionRequired.UnlockDevice:
|
151
|
+
this.eventSubject.next({ type: "lockedDevice" });
|
152
|
+
break;
|
153
|
+
case UserInteractionRequired.None:
|
154
|
+
if (this.openAppRequested) {
|
155
|
+
this.openAppRequested = false;
|
156
|
+
this.eventSubject.next({ type: "device-permission-granted" });
|
157
|
+
}
|
158
|
+
if (this.permissionRequested) {
|
159
|
+
this.permissionRequested = false;
|
160
|
+
this.eventSubject.next({ type: "device-permission-granted" });
|
161
|
+
|
162
|
+
// Simulate apps listing (not systematic step in LDMK)
|
163
|
+
this.eventSubject.next({ type: "listing-apps" });
|
164
|
+
}
|
165
|
+
if (intermediateValue.installPlan !== null) {
|
166
|
+
this.handleInstallPlan(intermediateValue.installPlan);
|
167
|
+
}
|
168
|
+
break;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
private handleInstallPlan(installPlan: InstallPlan): void {
|
173
|
+
// Handle install plan resolved events
|
174
|
+
if (this.installPlan === null) {
|
175
|
+
// Skipped applications
|
176
|
+
const alreadyInstalled = this.mapSkippedApps(
|
177
|
+
installPlan.alreadyInstalled,
|
178
|
+
SkipReason.AppAlreadyInstalled,
|
179
|
+
);
|
180
|
+
const missing = this.mapSkippedApps(
|
181
|
+
installPlan.missingApplications,
|
182
|
+
SkipReason.NoSuchAppOnProvider,
|
183
|
+
);
|
184
|
+
const skippedAppOps = [...alreadyInstalled, ...missing];
|
185
|
+
if (skippedAppOps.length > 0) {
|
186
|
+
this.eventSubject.next({
|
187
|
+
type: "some-apps-skipped",
|
188
|
+
skippedAppOps,
|
189
|
+
});
|
190
|
+
}
|
191
|
+
|
192
|
+
// Install queue content
|
193
|
+
this.eventSubject.next({
|
194
|
+
type: "listed-apps",
|
195
|
+
installQueue: installPlan.installPlan.map(app => app.versionName),
|
196
|
+
});
|
197
|
+
}
|
198
|
+
|
199
|
+
// Handle ongoing install events
|
200
|
+
this.eventSubject.next({
|
201
|
+
type: "inline-install",
|
202
|
+
progress: installPlan.currentProgress,
|
203
|
+
itemProgress: installPlan.currentIndex,
|
204
|
+
currentAppOp: {
|
205
|
+
type: "install",
|
206
|
+
name: installPlan.installPlan[installPlan.currentIndex]!.versionName,
|
207
|
+
},
|
208
|
+
installQueue: installPlan.installPlan.map(app => app.versionName),
|
209
|
+
});
|
210
|
+
this.installPlan = installPlan;
|
211
|
+
}
|
212
|
+
|
213
|
+
private handleCompletedEvent(output: ConnectAppDAOutput, deviceState: DeviceSessionState): void {
|
214
|
+
if (deviceState.sessionStateType !== DeviceSessionStateType.Connected) {
|
215
|
+
// Handle opened app
|
216
|
+
const currentApp = deviceState.currentApp;
|
217
|
+
let flags: number | Buffer = 0;
|
218
|
+
if (typeof currentApp.flags === "number") {
|
219
|
+
flags = currentApp.flags;
|
220
|
+
} else if (currentApp.flags !== undefined) {
|
221
|
+
flags = Buffer.from(currentApp.flags);
|
222
|
+
}
|
223
|
+
this.eventSubject.next({
|
224
|
+
type: "opened",
|
225
|
+
app: {
|
226
|
+
name: currentApp.name,
|
227
|
+
version: currentApp.version,
|
228
|
+
flags,
|
229
|
+
},
|
230
|
+
derivation: output.derivation ? { address: output.derivation } : undefined,
|
231
|
+
});
|
232
|
+
}
|
233
|
+
this.eventSubject.complete();
|
234
|
+
}
|
235
|
+
|
236
|
+
private handleErrorEvent(error: ConnectAppDAError, deviceState: DeviceSessionState): void {
|
237
|
+
if (error instanceof OutOfMemoryDAError && this.installPlan !== null) {
|
238
|
+
const appNames = this.installPlan.installPlan.map(app => app.versionName);
|
239
|
+
this.eventSubject.next({
|
240
|
+
type: "app-not-installed",
|
241
|
+
appNames,
|
242
|
+
appName: appNames[0]!,
|
243
|
+
});
|
244
|
+
this.eventSubject.complete();
|
245
|
+
} else if (
|
246
|
+
error instanceof UnsupportedFirmwareDAError &&
|
247
|
+
deviceState.sessionStateType !== DeviceSessionStateType.Connected
|
248
|
+
) {
|
249
|
+
this.eventSubject.error(
|
250
|
+
new LatestFirmwareVersionRequired("LatestFirmwareVersionRequired", {
|
251
|
+
current: deviceState.firmwareUpdateContext!.currentFirmware.version,
|
252
|
+
latest:
|
253
|
+
deviceState.firmwareUpdateContext?.availableUpdate?.finalFirmware.version ||
|
254
|
+
deviceState.firmwareUpdateContext!.currentFirmware.version,
|
255
|
+
}),
|
256
|
+
);
|
257
|
+
} else if (error instanceof DeviceLockedError) {
|
258
|
+
this.eventSubject.next({ type: "lockedDevice" });
|
259
|
+
this.eventSubject.complete();
|
260
|
+
} else if (error instanceof SecureChannelError) {
|
261
|
+
this.eventSubject.error(new UserRefusedAllowManager());
|
262
|
+
} else if ("_tag" in error && error._tag === "WebHidSendReportError") {
|
263
|
+
this.eventSubject.next({ type: "disconnected", expected: false });
|
264
|
+
this.eventSubject.complete();
|
265
|
+
} else if ("errorCode" in error && typeof error.errorCode === "string" && "_tag" in error) {
|
266
|
+
this.eventSubject.error(this.mapDeviceError(error.errorCode, error._tag));
|
267
|
+
} else {
|
268
|
+
this.eventSubject.error(error);
|
269
|
+
}
|
270
|
+
}
|
271
|
+
|
272
|
+
private handleError(error: Error): Observable<never> {
|
273
|
+
this.eventSubject.error(error);
|
274
|
+
return of();
|
275
|
+
}
|
276
|
+
|
277
|
+
private mapDeviceError(errorCode: string, defaultMessage: string): Error {
|
278
|
+
switch (errorCode) {
|
279
|
+
case "5501":
|
280
|
+
return new UserRefusedOnDevice();
|
281
|
+
default:
|
282
|
+
return new Error(defaultMessage);
|
283
|
+
}
|
284
|
+
}
|
285
|
+
|
286
|
+
private mapSkippedApps(appNames: string[], reason: SkipReason): SkippedAppOp[] {
|
287
|
+
return appNames.map(name => ({
|
288
|
+
reason,
|
289
|
+
appOp: {
|
290
|
+
type: "install",
|
291
|
+
name,
|
292
|
+
},
|
293
|
+
}));
|
294
|
+
}
|
295
|
+
|
296
|
+
private mapDeviceInfo(osVersion: GetOsVersionResponse): DeviceInfo {
|
297
|
+
return parseDeviceInfo({
|
298
|
+
isBootloader: osVersion.isBootloader,
|
299
|
+
rawVersion: osVersion.seVersion, // Always the SE version since at this step we cannot be in bootloader mode
|
300
|
+
targetId: osVersion.targetId,
|
301
|
+
seVersion: osVersion.seVersion,
|
302
|
+
seTargetId: osVersion.seTargetId,
|
303
|
+
mcuBlVersion: undefined, // We cannot be in bootloader mode at this step
|
304
|
+
mcuVersion: osVersion.mcuSephVersion,
|
305
|
+
mcuTargetId: osVersion.mcuTargetId,
|
306
|
+
flags: Buffer.from(osVersion.seFlags),
|
307
|
+
bootloaderVersion: osVersion.mcuBootloaderVersion,
|
308
|
+
hardwareVersion: parseInt(osVersion.hwVersion, 16),
|
309
|
+
languageId: osVersion.langId,
|
310
|
+
});
|
311
|
+
}
|
312
|
+
|
313
|
+
private mapLatestFirmware(updateContext: DmkFirmwareUpdateContext): FirmwareUpdateContext | null {
|
314
|
+
if (!updateContext.availableUpdate) {
|
315
|
+
return null;
|
316
|
+
}
|
317
|
+
const availableUpdate = updateContext.availableUpdate;
|
318
|
+
const osu = availableUpdate.osuFirmware;
|
319
|
+
const final = availableUpdate.finalFirmware;
|
320
|
+
return {
|
321
|
+
osu: {
|
322
|
+
id: osu.id,
|
323
|
+
perso: osu.perso,
|
324
|
+
firmware: osu.firmware,
|
325
|
+
firmware_key: osu.firmwareKey,
|
326
|
+
hash: osu.hash || "",
|
327
|
+
next_se_firmware_final_version: osu.nextFinalFirmware,
|
328
|
+
// Following fields are inherited from dto, but unused
|
329
|
+
description: undefined,
|
330
|
+
display_name: undefined,
|
331
|
+
notes: undefined,
|
332
|
+
name: "",
|
333
|
+
date_creation: "",
|
334
|
+
date_last_modified: "",
|
335
|
+
device_versions: [],
|
336
|
+
providers: [],
|
337
|
+
previous_se_firmware_final_version: [],
|
338
|
+
},
|
339
|
+
final: {
|
340
|
+
id: final.id,
|
341
|
+
name: final.version,
|
342
|
+
version: final.version,
|
343
|
+
perso: final.perso,
|
344
|
+
firmware: final.firmware || "",
|
345
|
+
firmware_key: final.firmwareKey || "",
|
346
|
+
hash: final.hash || "",
|
347
|
+
bytes: final.bytes || 0,
|
348
|
+
mcu_versions: final.mcuVersions,
|
349
|
+
// Following fields are inherited from dto, but unused
|
350
|
+
description: undefined,
|
351
|
+
display_name: undefined,
|
352
|
+
notes: undefined,
|
353
|
+
se_firmware: 0,
|
354
|
+
date_creation: "",
|
355
|
+
date_last_modified: "",
|
356
|
+
device_versions: [],
|
357
|
+
application_versions: [],
|
358
|
+
osu_versions: [],
|
359
|
+
providers: [],
|
360
|
+
},
|
361
|
+
shouldFlashMCU: availableUpdate.mcuUpdateRequired,
|
362
|
+
};
|
363
|
+
}
|
364
|
+
}
|
package/src/hw/connectManager.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Observable, concat, from, of, throwError } from "rxjs";
|
1
|
+
import { Observable, concat, concatWith, from, of, throwError } from "rxjs";
|
2
2
|
import { concatMap, catchError, delay } from "rxjs/operators";
|
3
3
|
import {
|
4
4
|
TransportStatusError,
|
@@ -7,6 +7,9 @@ import {
|
|
7
7
|
LockedDeviceError,
|
8
8
|
} from "@ledgerhq/errors";
|
9
9
|
import { DeviceInfo } from "@ledgerhq/types-live";
|
10
|
+
import type Transport from "@ledgerhq/hw-transport";
|
11
|
+
import type { DeviceManagementKit } from "@ledgerhq/device-management-kit";
|
12
|
+
import { PrepareConnectManagerDeviceAction } from "@ledgerhq/live-dmk-shared";
|
10
13
|
import type { ListAppsEvent } from "../apps";
|
11
14
|
import { listAppsUseCase } from "../device/use-cases/listAppsUseCase";
|
12
15
|
import { withDevice } from "./deviceAccess";
|
@@ -17,6 +20,7 @@ import { DeviceNotOnboarded } from "../errors";
|
|
17
20
|
import attemptToQuitApp, { AttemptToQuitAppEvent } from "./attemptToQuitApp";
|
18
21
|
import { LockedDeviceEvent } from "./actions/types";
|
19
22
|
import { ManagerRequest } from "./actions/manager";
|
23
|
+
import { PrepareConnectManagerEventMapper } from "./connectManagerEventMapper";
|
20
24
|
|
21
25
|
export type Input = {
|
22
26
|
deviceId: string;
|
@@ -40,85 +44,123 @@ export type ConnectManagerEvent =
|
|
40
44
|
| ListAppsEvent
|
41
45
|
| LockedDeviceEvent;
|
42
46
|
|
43
|
-
const cmd = (
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
.pipe(delay(1000))
|
51
|
-
.subscribe(e => o.next(e));
|
47
|
+
const cmd = (transport: Transport, { request }: Input): Observable<ConnectManagerEvent> =>
|
48
|
+
new Observable(o => {
|
49
|
+
const timeoutSub = of({
|
50
|
+
type: "unresponsiveDevice",
|
51
|
+
} as ConnectManagerEvent)
|
52
|
+
.pipe(delay(1000))
|
53
|
+
.subscribe(e => o.next(e));
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
55
|
+
const sub = from(getDeviceInfo(transport))
|
56
|
+
.pipe(
|
57
|
+
concatMap(deviceInfo => {
|
58
|
+
timeoutSub.unsubscribe();
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
if (!deviceInfo.onboarded && !deviceInfo.isRecoveryMode) {
|
61
|
+
throw new DeviceNotOnboarded();
|
62
|
+
}
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
if (deviceInfo.isBootloader) {
|
65
|
+
return of({
|
66
|
+
type: "bootloader",
|
67
|
+
deviceInfo,
|
68
|
+
} as ConnectManagerEvent);
|
69
|
+
}
|
68
70
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
if (deviceInfo.isOSU) {
|
72
|
+
return of({
|
73
|
+
type: "osu",
|
74
|
+
deviceInfo,
|
75
|
+
} as ConnectManagerEvent);
|
76
|
+
}
|
75
77
|
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
78
|
+
return concat(
|
79
|
+
of({
|
80
|
+
type: "listingApps",
|
81
|
+
deviceInfo,
|
82
|
+
} as ConnectManagerEvent),
|
83
|
+
listAppsUseCase(transport, deviceInfo),
|
84
|
+
);
|
85
|
+
}),
|
86
|
+
catchError((e: unknown) => {
|
87
|
+
if (e instanceof LockedDeviceError) {
|
88
|
+
return of({
|
89
|
+
type: "lockedDevice",
|
90
|
+
} as ConnectManagerEvent);
|
91
|
+
} else if (
|
92
|
+
e instanceof DeviceOnDashboardExpected ||
|
93
|
+
(e &&
|
94
|
+
e instanceof TransportStatusError &&
|
95
|
+
[
|
96
|
+
StatusCodes.CLA_NOT_SUPPORTED,
|
97
|
+
StatusCodes.INS_NOT_SUPPORTED,
|
98
|
+
StatusCodes.UNKNOWN_APDU,
|
99
|
+
0x6e01, // No StatusCodes definition
|
100
|
+
0x6d01, // No StatusCodes definition
|
101
|
+
].includes(e.statusCode))
|
102
|
+
) {
|
103
|
+
return from(getAppAndVersion(transport)).pipe(
|
104
|
+
concatMap(appAndVersion => {
|
105
|
+
return !request?.autoQuitAppDisabled && !isDashboardName(appAndVersion.name)
|
106
|
+
? attemptToQuitApp(transport, appAndVersion)
|
107
|
+
: of({
|
108
|
+
type: "appDetected",
|
109
|
+
} as ConnectManagerEvent);
|
110
|
+
}),
|
111
|
+
);
|
112
|
+
}
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
return throwError(() => e);
|
115
|
+
}),
|
116
|
+
)
|
117
|
+
.subscribe(o);
|
116
118
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
119
|
+
return () => {
|
120
|
+
timeoutSub.unsubscribe();
|
121
|
+
sub.unsubscribe();
|
122
|
+
};
|
123
|
+
});
|
124
|
+
|
125
|
+
const isDmkTransport = (
|
126
|
+
transport: Transport,
|
127
|
+
): transport is Transport & { dmk: DeviceManagementKit; sessionId: string } => {
|
128
|
+
return (
|
129
|
+
"dmk" in transport &&
|
130
|
+
transport.dmk !== undefined &&
|
131
|
+
"sessionId" in transport &&
|
132
|
+
transport.sessionId !== undefined
|
122
133
|
);
|
134
|
+
};
|
123
135
|
|
124
|
-
export default
|
136
|
+
export default function connectManagerFactory(
|
137
|
+
{
|
138
|
+
isLdmkConnectAppEnabled,
|
139
|
+
}: {
|
140
|
+
isLdmkConnectAppEnabled: boolean;
|
141
|
+
} = { isLdmkConnectAppEnabled: false },
|
142
|
+
) {
|
143
|
+
if (!isLdmkConnectAppEnabled) {
|
144
|
+
return ({ deviceId, request }: Input): Observable<ConnectManagerEvent> =>
|
145
|
+
withDevice(deviceId)(transport => cmd(transport, { deviceId, request }));
|
146
|
+
}
|
147
|
+
return ({ deviceId, request }: Input): Observable<ConnectManagerEvent> =>
|
148
|
+
withDevice(deviceId)(transport => {
|
149
|
+
if (!isDmkTransport(transport)) {
|
150
|
+
return cmd(transport, { deviceId, request });
|
151
|
+
}
|
152
|
+
const { dmk, sessionId } = transport;
|
153
|
+
const deviceAction = new PrepareConnectManagerDeviceAction({
|
154
|
+
input: {
|
155
|
+
unlockTimeout: 0, // Expect to fail immediately when device is locked
|
156
|
+
},
|
157
|
+
});
|
158
|
+
const observable = dmk.executeDeviceAction({
|
159
|
+
sessionId,
|
160
|
+
deviceAction,
|
161
|
+
});
|
162
|
+
return new PrepareConnectManagerEventMapper(observable)
|
163
|
+
.map()
|
164
|
+
.pipe(concatWith(cmd(transport, { deviceId, request })));
|
165
|
+
});
|
166
|
+
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import { Observable, Subject, of, takeUntil } from "rxjs";
|
2
|
+
import { catchError, tap } from "rxjs/operators";
|
3
|
+
import type {
|
4
|
+
DeviceActionState,
|
5
|
+
ExecuteDeviceActionReturnType,
|
6
|
+
} from "@ledgerhq/device-management-kit";
|
7
|
+
import {
|
8
|
+
DeviceActionStatus,
|
9
|
+
DeviceLockedError,
|
10
|
+
UserInteractionRequired,
|
11
|
+
} from "@ledgerhq/device-management-kit";
|
12
|
+
import type {
|
13
|
+
PrepareConnectManagerDAOutput,
|
14
|
+
PrepareConnectManagerDAError,
|
15
|
+
PrepareConnectManagerDAIntermediateValue,
|
16
|
+
} from "@ledgerhq/live-dmk-shared";
|
17
|
+
import { DisconnectedDevice } from "@ledgerhq/errors";
|
18
|
+
|
19
|
+
import { LockedDeviceEvent } from "./actions/types";
|
20
|
+
|
21
|
+
export class PrepareConnectManagerEventMapper {
|
22
|
+
private eventSubject = new Subject<LockedDeviceEvent>();
|
23
|
+
|
24
|
+
constructor(
|
25
|
+
private events: ExecuteDeviceActionReturnType<
|
26
|
+
PrepareConnectManagerDAOutput,
|
27
|
+
PrepareConnectManagerDAError,
|
28
|
+
PrepareConnectManagerDAIntermediateValue
|
29
|
+
>,
|
30
|
+
) {}
|
31
|
+
|
32
|
+
map(): Observable<LockedDeviceEvent> {
|
33
|
+
const cancelAction = this.events.cancel;
|
34
|
+
const unsubscribe = new Subject<void>();
|
35
|
+
|
36
|
+
// Subscribe to device action events
|
37
|
+
this.events.observable
|
38
|
+
.pipe(
|
39
|
+
tap(event => this.handleEvent(event)),
|
40
|
+
takeUntil(unsubscribe),
|
41
|
+
catchError(error => this.handleError(error)),
|
42
|
+
)
|
43
|
+
.subscribe();
|
44
|
+
|
45
|
+
return new Observable<LockedDeviceEvent>(observer => {
|
46
|
+
const sub = this.eventSubject.subscribe(observer);
|
47
|
+
return () => {
|
48
|
+
sub.unsubscribe();
|
49
|
+
cancelAction();
|
50
|
+
unsubscribe.next();
|
51
|
+
};
|
52
|
+
});
|
53
|
+
}
|
54
|
+
|
55
|
+
private handleEvent(
|
56
|
+
event: DeviceActionState<
|
57
|
+
PrepareConnectManagerDAOutput,
|
58
|
+
PrepareConnectManagerDAError,
|
59
|
+
PrepareConnectManagerDAIntermediateValue
|
60
|
+
>,
|
61
|
+
): void {
|
62
|
+
switch (event.status) {
|
63
|
+
case DeviceActionStatus.Pending:
|
64
|
+
this.handlePendingEvent(event.intermediateValue);
|
65
|
+
break;
|
66
|
+
case DeviceActionStatus.Completed:
|
67
|
+
this.handleCompletedEvent(event.output);
|
68
|
+
break;
|
69
|
+
case DeviceActionStatus.Error:
|
70
|
+
this.handleErrorEvent(event.error);
|
71
|
+
break;
|
72
|
+
case DeviceActionStatus.NotStarted:
|
73
|
+
case DeviceActionStatus.Stopped:
|
74
|
+
this.eventSubject.error(new Error("Unexpected device action status"));
|
75
|
+
break;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
private handlePendingEvent(intermediateValue: PrepareConnectManagerDAIntermediateValue): void {
|
80
|
+
switch (intermediateValue.requiredUserInteraction) {
|
81
|
+
case UserInteractionRequired.UnlockDevice:
|
82
|
+
this.eventSubject.next({ type: "lockedDevice" });
|
83
|
+
break;
|
84
|
+
case UserInteractionRequired.None:
|
85
|
+
// Nothing to do
|
86
|
+
break;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
private handleCompletedEvent(_output: PrepareConnectManagerDAOutput): void {
|
91
|
+
this.eventSubject.complete();
|
92
|
+
}
|
93
|
+
|
94
|
+
private handleErrorEvent(error: PrepareConnectManagerDAError): void {
|
95
|
+
if (error instanceof DeviceLockedError) {
|
96
|
+
this.eventSubject.next({ type: "lockedDevice" });
|
97
|
+
this.eventSubject.complete();
|
98
|
+
} else if ("_tag" in error && error._tag === "WebHidSendReportError") {
|
99
|
+
this.eventSubject.error(new DisconnectedDevice());
|
100
|
+
} else {
|
101
|
+
this.eventSubject.error(error);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
private handleError(error: Error): Observable<never> {
|
106
|
+
this.eventSubject.error(error);
|
107
|
+
return of();
|
108
|
+
}
|
109
|
+
}
|