@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
150 lines (149 loc) • 4.86 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TransportManager = void 0;
const transport_1 = require("@trezor/transport");
const utils_1 = require("@trezor/utils");
const createOverrideLock = () => {
let promise;
let sequence = 0;
let abort;
let message;
const override = async (abortMessage, action) => {
message = abortMessage;
const seq = ++sequence;
while (promise) {
abort?.abort(new Error(abortMessage));
await promise.catch(() => {});
}
if (seq !== sequence) return Promise.reject(new Error(message));
abort = new AbortController();
promise = action(abort.signal).finally(() => {
abort = undefined;
promise = undefined;
});
return promise;
};
const getPending = () => promise;
return {
override,
getPending
};
};
class TransportManager extends utils_1.TypedEmitter {
lock = createOverrideLock();
transports = [];
activeTransport;
transportReconnect = false;
upgradeTimeout;
startTransport;
constructor(startTransport) {
super();
this.startTransport = startTransport;
}
pending() {
return this.lock.getPending();
}
get() {
return this.activeTransport;
}
init({
transports,
transportReconnect = false,
pendingTransportEvent = false
}) {
this.transports = transports;
this.transportReconnect = transportReconnect;
return this.lock.override('New init', signal => this.createInitPromise(pendingTransportEvent, signal));
}
dispose() {
this.removeAllListeners();
return this.lock.override('Disposing', () => {
const {
activeTransport
} = this;
if (activeTransport) {
clearTimeout(this.upgradeTimeout);
delete this.activeTransport;
activeTransport.stop();
}
return Promise.resolve();
});
}
async selectTransport([transport, ...rest], signal) {
if (signal.aborted) throw new Error(signal.reason);
if (transport === this.activeTransport) return transport;
const result = await transport.init({
signal
});
if (result.success) return transport;else if (rest.length) return this.selectTransport(rest, signal);else throw new Error(result.error);
}
scheduleUpgradeCheck(pendingTransportEvent) {
clearTimeout(this.upgradeTimeout);
this.upgradeTimeout = setTimeout(async () => {
if (!this.activeTransport || this.activeTransport === this.transports[0]) return;
for (const t of this.transports) {
if (t === this.activeTransport) break;
if (await t.ping()) {
this.lock.override('Upgrading', signal => this.createInitPromise(pendingTransportEvent, signal)).catch(() => {});
return;
}
}
this.scheduleUpgradeCheck(pendingTransportEvent);
}, 1000);
}
async createInitPromise(pendingTransportEvent, abortSignal) {
try {
const {
transports,
activeTransport
} = this;
const transport = transports.length ? await this.selectTransport(transports, abortSignal) : undefined;
if (activeTransport !== transport) {
if (activeTransport) {
clearTimeout(this.upgradeTimeout);
delete this.activeTransport;
activeTransport.stop();
}
if (transport) {
try {
await this.startTransport(transport, pendingTransportEvent, abortSignal);
} catch (err) {
transport.stop();
throw err;
}
transport.on(transport_1.TRANSPORT.ERROR, error => {
this.emit(transport_1.TRANSPORT.ERROR, error);
clearTimeout(this.upgradeTimeout);
this.lock.override('Transport error', async signal => {
delete this.activeTransport;
transport.stop();
if (this.transportReconnect) {
await (0, utils_1.resolveAfter)(1000, signal);
await this.createInitPromise(pendingTransportEvent, signal);
}
}).catch(() => {});
});
this.activeTransport = transport;
this.emit(transport_1.TRANSPORT.START, transport);
} else {
this.emit(transport_1.TRANSPORT.ERROR, 'Transport disabled');
}
}
if (transport && transport !== transports[0]) {
this.scheduleUpgradeCheck(pendingTransportEvent);
}
} catch (error) {
this.emit(transport_1.TRANSPORT.ERROR, error?.message);
if (this.transportReconnect && !abortSignal.aborted) {
this.lock.override('Reconnecting', async signal => {
await (0, utils_1.resolveAfter)(1000, signal);
await this.createInitPromise(pendingTransportEvent, signal);
}).catch(() => {});
}
}
}
}
exports.TransportManager = TransportManager;
//# sourceMappingURL=TransportManager.js.map