UNPKG

@ledgerhq/live-common

Version:
83 lines (75 loc) 3.16 kB
import type { SocketEvent } from "@ledgerhq/types-live"; import { from, Observable } from "rxjs"; import { mergeMap, retryWhen } from "rxjs/operators"; import { LockedDeviceError } from "@ledgerhq/errors"; import getDeviceInfo from "./getDeviceInfo"; import { retryWhileErrors, withDevice } from "./deviceAccess"; import genuineCheck from "./genuineCheck"; export type GetGenuineCheckFromDeviceIdArgs = { deviceId: string; deviceName: string | null; lockedDeviceTimeoutMs?: number; }; export type GetGenuineCheckFromDeviceIdResult = { socketEvent: SocketEvent | null; lockedDevice: boolean; }; export type GetGenuineCheckFromDeviceIdOutput = Observable<GetGenuineCheckFromDeviceIdResult>; /** * Get a genuine check for a device only from its id * @param deviceId A device id, or an empty string if device is usb plugged * @param lockedDeviceTimeoutMs Time of no response from device after which the device is considered locked, in ms. Default 1000ms. * @returns An Observable pushing objects containing: * - socketEvent: a SocketEvent giving the current status of the genuine check, * null if the genuine check process did not reach any state yet * - lockedDevice: a boolean set to true if the device is currently locked, false otherwise */ export const getGenuineCheckFromDeviceId = ({ deviceId, deviceName, lockedDeviceTimeoutMs = 1000, }: GetGenuineCheckFromDeviceIdArgs): GetGenuineCheckFromDeviceIdOutput => { return new Observable(subscriber => { // In order to know if a device is locked or not. // As we're not timing out inside the genuineCheckObservable flow (with rxjs timeout for ex) // once the device is unlock, getDeviceInfo should return the device info and // the flow will continue. No need to handle a retry strategy const lockedDeviceTimeout = setTimeout(() => { subscriber.next({ socketEvent: null, lockedDevice: true }); }, lockedDeviceTimeoutMs); // Returns a Subscription that can be unsubscribed/cleaned return ( withDevice( deviceId, deviceName ? { matchDeviceByName: deviceName } : undefined, )(t => from(getDeviceInfo(t)).pipe( mergeMap(deviceInfo => { clearTimeout(lockedDeviceTimeout); subscriber.next({ socketEvent: null, lockedDevice: false }); return genuineCheck(t, deviceInfo); }), ), ) // Needs to retry with withDevice .pipe( retryWhen( retryWhileErrors((e: Error) => { // Cancels the locked-device unresponsive/timeout strategy if received any response/error clearTimeout(lockedDeviceTimeout); if (e instanceof LockedDeviceError) { subscriber.next({ socketEvent: null, lockedDevice: true }); return true; } return false; }), ), ) .subscribe({ next: (socketEvent: SocketEvent) => subscriber.next({ socketEvent, lockedDevice: false }), error: e => subscriber.error(e), complete: () => subscriber.complete(), }) ); }); };