@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
154 lines • 5.89 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