@trezor/transport
Version:
Low level library facilitating protocol buffers based communication with Trezor devices
149 lines (148 loc) • 4.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.AbstractTransport = exports.isTransportInstance = void 0;
const tslib_1 = require("tslib");
const protobuf_1 = require("@trezor/protobuf");
const utils_1 = require("@trezor/utils");
const constants_1 = require("../constants");
const ERRORS = tslib_1.__importStar(require("../errors"));
const result_1 = require("../utils/result");
const isTransportInstance = transport => {
const requiredMethods = ['init', 'enumerate', 'listen', 'acquire', 'release', 'send', 'receive', 'call'];
if (transport && typeof transport === 'object') {
return !requiredMethods.some(m => typeof transport[m] !== 'function');
}
return false;
};
exports.isTransportInstance = isTransportInstance;
const getKey = ({
path,
product
}) => `${path}${product}`;
class AbstractTransport extends utils_1.TypedEmitter {
isOutdated = false;
version = '';
stopped = true;
listening = false;
messages;
descriptors;
abortController;
logger;
id;
deviceEvents;
constructor({
messages,
logger,
id
}) {
super();
this.descriptors = [];
this.messages = (0, protobuf_1.parseConfigure)(messages);
this.abortController = new AbortController();
this.logger = logger;
this.id = id;
this.deviceEvents = new utils_1.TypedEmitter();
}
get apiType() {
return 'usb';
}
ping(_params) {
return Promise.resolve(false);
}
subscribe(_params) {
return Promise.reject(new Error(`${this.name} does not support the 'subscribe' method.`));
}
stop() {
this.emit(constants_1.TRANSPORT.STOPPED);
this.removeAllListeners();
this.deviceEvents.removeAllListeners();
this.stopped = true;
this.listening = false;
this.abortController.abort();
this.abortController = new AbortController();
this.descriptors = [];
}
handleDescriptorsChange(nextDescriptors) {
if (this.stopped) {
return;
}
const oldDescriptors = new Map(this.descriptors.map(d => [getKey(d), d]));
const newDescriptors = new Map(nextDescriptors.map(d => [getKey(d), d]));
this.descriptors.filter(d => !newDescriptors.has(getKey(d))).forEach(descriptor => this.deviceEvents.emit(descriptor.path, {
type: constants_1.TRANSPORT.DEVICE_DISCONNECTED
}));
nextDescriptors.forEach(descriptor => {
const prevDescriptor = oldDescriptors.get(getKey(descriptor));
if (!prevDescriptor) {
this.emit(constants_1.TRANSPORT.DEVICE_CONNECTED, descriptor);
} else if (prevDescriptor.session !== descriptor.session) {
this.deviceEvents.emit(descriptor.path, {
type: constants_1.TRANSPORT.DEVICE_SESSION_CHANGED,
descriptor
});
}
});
this.descriptors = nextDescriptors;
}
getDescriptor(path) {
return this.descriptors.find(d => d.path === path);
}
getMessage(message = 'GetFeatures') {
return !!this.messages.get(message);
}
getMessages() {
return this.messages;
}
updateMessages(messages) {
this.messages = (0, protobuf_1.parseConfigure)(messages);
}
loadMessages(packageName, packageLoader) {
return (0, protobuf_1.loadDefinitions)(this.messages, packageName, packageLoader);
}
success(payload) {
return (0, result_1.success)(payload);
}
error(payload) {
return (0, result_1.error)(payload);
}
unknownError = (err, expectedErrors = []) => {
this.logger?.error(this.name, 'unexpected error: ', err);
return (0, result_1.unknownError)(typeof err !== 'string' ? err : new Error(err), expectedErrors);
};
mergeAbort(signal) {
if (!signal) {
return {
signal: this.abortController.signal,
clear: () => {}
};
}
const controller = new AbortController();
const onAbort = () => controller.abort();
signal.addEventListener('abort', onAbort);
if (signal.aborted) controller.abort(signal.reason);
this.abortController.signal.addEventListener('abort', onAbort);
const clear = () => {
signal.removeEventListener('abort', onAbort);
this.abortController.signal.removeEventListener('abort', onAbort);
};
return {
signal: controller.signal,
clear
};
}
scheduleAction = (action, params, errors = []) => {
const {
signal,
clear
} = this.mergeAbort(params?.signal);
return (0, utils_1.scheduleAction)(action, {
timeout: constants_1.ACTION_TIMEOUT,
...params,
signal
}).catch(err => (0, result_1.unknownError)(err, [ERRORS.ABORTED_BY_TIMEOUT, ERRORS.ABORTED_BY_SIGNAL, ...errors])).finally(clear);
};
}
exports.AbstractTransport = AbstractTransport;
//# sourceMappingURL=abstract.js.map