UNPKG

@ledgerhq/live-common

Version:
128 lines 6.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isAllowedOnboardingStatePollingError = exports.getOnboardingStatePolling = void 0; const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const getVersionUseCase_1 = require("../device/use-cases/getVersionUseCase"); const deviceAccess_1 = require("./deviceAccess"); const errors_1 = require("@ledgerhq/errors"); const extractOnboardingState_1 = require("./extractOnboardingState"); const device_management_kit_1 = require("@ledgerhq/device-management-kit"); const quitApp_1 = require("../deviceSDK/commands/quitApp"); /** * Polls the device onboarding state at a given frequency * * @param deviceId A device id * @param pollingPeriodMs The period in ms after which the device onboarding state is fetched again * @param transportAbortTimeoutMs Depending on the transport implementation, an "abort timeout" will be set (and throw an error) when: * - opening a transport instance * - on called commands (where `getVersion`) * Default to (pollingPeriodMs - 100) ms * @param safeGuardTimeoutMs For Transport implementations not implementing an "abort timeout", a timeout will be triggered (and throw an error) at this function call level * @returns An Observable that polls the device onboarding state and pushes an object containing: * - onboardingState: the device state during the onboarding * - allowedError: any error that is allowed and does not stop the polling * - lockedDevice: a boolean set to true if the device is currently locked, false otherwise */ const getOnboardingStatePolling = ({ deviceId, deviceName, pollingPeriodMs, transportAbortTimeoutMs = pollingPeriodMs - 100, safeGuardTimeoutMs = pollingPeriodMs * 10, // Nb Empirical value allowedErrorChecks = [], }) => { let hasQuitAppAlreadyRun = false; const getOnboardingStateOnce = () => { return (0, deviceAccess_1.withDevice)(deviceId, { openTimeoutMs: transportAbortTimeoutMs, matchDeviceByName: deviceName ?? undefined, })(t => { const getVersionObs = (0, rxjs_1.defer)(() => (0, rxjs_1.from)((0, getVersionUseCase_1.getVersion)(t, { abortTimeoutMs: transportAbortTimeoutMs }))); return getVersionObs.pipe((0, operators_1.catchError)((error) => { const isApduNotSupported = error instanceof errors_1.TransportStatusError && [errors_1.StatusCodes.CLA_NOT_SUPPORTED, errors_1.StatusCodes.INS_NOT_SUPPORTED].includes(error.statusCode); if (isApduNotSupported && !hasQuitAppAlreadyRun) { hasQuitAppAlreadyRun = true; return (0, quitApp_1.quitApp)(t).pipe((0, operators_1.switchMap)(() => getVersionObs)); } return (0, rxjs_1.throwError)(() => error); })); }).pipe((0, operators_1.timeout)(safeGuardTimeoutMs), // Throws a TimeoutError (0, operators_1.first)(), (0, operators_1.catchError)((error) => { if ((0, exports.isAllowedOnboardingStatePollingError)(error) || allowedErrorChecks?.some(fn => fn(error))) { // Pushes the error to the next step to be processed (no retry from the beginning) return (0, rxjs_1.of)(error); } return (0, rxjs_1.throwError)(() => error); }), (0, operators_1.map)((event) => { if ("flags" in event) { const firmwareInfo = event; let onboardingState = null; if (firmwareInfo.isBootloader) { // Throws so it will be considered a fatal error throw new errors_1.UnexpectedBootloader("Device in bootloader during the polling"); } try { onboardingState = (0, extractOnboardingState_1.extractOnboardingState)(firmwareInfo.flags, firmwareInfo.charonState); } catch (error) { if (error instanceof errors_1.DeviceExtractOnboardingStateError) { return { onboardingState: null, allowedError: error, lockedDevice: false, }; } else { let errorMessage = ""; if (error instanceof Error) { errorMessage = `${error.name}: ${error.message}`; } else { errorMessage = `${error}`; } return { onboardingState: null, allowedError: new errors_1.DeviceOnboardingStatePollingError(`SyncOnboarding: Unknown error while extracting the onboarding state ${errorMessage}`), lockedDevice: false, }; } } return { onboardingState, allowedError: null, lockedDevice: false, }; } else { // If an error is caught previously, and this error is "allowed", // the value from the observable is not a FirmwareInfo but an Error const allowedError = event; return { onboardingState: null, allowedError: allowedError, lockedDevice: allowedError instanceof errors_1.LockedDeviceError, }; } })); }; return getOnboardingStateOnce().pipe((0, operators_1.repeat)({ delay: _count => (0, rxjs_1.timer)(pollingPeriodMs), })); }; exports.getOnboardingStatePolling = getOnboardingStatePolling; const isAllowedOnboardingStatePollingError = (error) => { if (error && // Timeout error is thrown by rxjs's timeout (error instanceof rxjs_1.TimeoutError || error instanceof errors_1.TransportExchangeTimeoutError || error instanceof errors_1.DisconnectedDevice || error instanceof errors_1.DisconnectedDeviceDuringOperation || error instanceof device_management_kit_1.DeviceDisconnectedWhileSendingError || error instanceof errors_1.CantOpenDevice || error instanceof errors_1.TransportRaceCondition || error instanceof errors_1.TransportStatusError || // A locked device is handled as an allowed error error instanceof errors_1.LockedDeviceError)) { return true; } return false; }; exports.isAllowedOnboardingStatePollingError = isAllowedOnboardingStatePollingError; //# sourceMappingURL=getOnboardingStatePolling.js.map