@trezor/connect-web
Version:
High-level javascript interface for Trezor hardware wallet in web environment.
280 lines • 11.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TrezorConnect = exports.CoreInIframe = void 0;
const tslib_1 = require("tslib");
const events_1 = tslib_1.__importDefault(require("events"));
const ERRORS = tslib_1.__importStar(require("@trezor/connect/lib/constants/errors"));
const config_1 = require("@trezor/connect/lib/data/config");
const events_2 = require("@trezor/connect/lib/events");
const factory_1 = require("@trezor/connect/lib/factory");
const debug_1 = require("@trezor/connect/lib/utils/debug");
const createDeferredManager_1 = require("@trezor/utils/lib/createDeferredManager");
const connectSettings_1 = require("../connectSettings");
const iframe = tslib_1.__importStar(require("../iframe"));
const popup = tslib_1.__importStar(require("../popup"));
const button_1 = tslib_1.__importDefault(require("../webusb/button"));
class CoreInIframe {
eventEmitter = new events_1.default();
_settings;
_log;
_popupManager;
_messagePromises;
boundHandleMessage = this.handleMessage.bind(this);
boundDispose = this.dispose.bind(this);
constructor() {
this._settings = (0, connectSettings_1.parseConnectSettings)();
this._log = (0, debug_1.initLog)('@trezor/connect-web');
this._messagePromises = (0, createDeferredManager_1.createDeferredManager)({ initialId: 1 });
}
initPopupManager() {
const pm = new popup.PopupManager(this._settings, { logger: this._log });
pm.on(events_2.POPUP.CLOSED, (error) => {
iframe.postMessage({
type: events_2.POPUP.CLOSED,
payload: error ? { error } : null,
});
});
return pm;
}
manifest(data) {
this._settings = (0, connectSettings_1.parseConnectSettings)({
...this._settings,
manifest: data,
});
}
dispose() {
this.eventEmitter.removeAllListeners();
iframe.dispose();
this._settings = (0, connectSettings_1.parseConnectSettings)();
if (this._popupManager) {
this._popupManager.close();
}
window.removeEventListener('message', this.boundHandleMessage);
window.removeEventListener('unload', this.boundDispose);
return Promise.resolve(undefined);
}
cancel(error) {
if (this._popupManager) {
this._popupManager.emit(events_2.POPUP.CLOSED, error);
}
}
handleMessage(messageEvent) {
if (messageEvent.origin !== iframe.origin)
return;
const message = (0, events_2.parseMessage)(messageEvent.data);
this._log.log('handleMessage', message);
switch (message.event) {
case events_2.RESPONSE_EVENT: {
const { id = 0, success, payload, device } = message;
const resolved = this._messagePromises.resolve(id, {
id,
success,
payload,
device,
});
if (!resolved)
this._log.warn(`Unknown message id ${id}`);
break;
}
case events_2.DEVICE_EVENT:
this.eventEmitter.emit(message.event, message);
this.eventEmitter.emit(message.type, message.payload);
break;
case events_2.TRANSPORT_EVENT:
this.eventEmitter.emit(message.event, message);
this.eventEmitter.emit(message.type, message.payload);
break;
case events_2.BLOCKCHAIN_EVENT:
this.eventEmitter.emit(message.event, message);
this.eventEmitter.emit(message.type, message.payload);
break;
case events_2.UI_EVENT:
if (message.type === events_2.IFRAME.BOOTSTRAP) {
iframe.clearTimeout();
break;
}
if (message.type === events_2.IFRAME.LOADED) {
iframe.initPromise.resolve();
}
if (message.type === events_2.IFRAME.ERROR) {
iframe.initPromise.reject(message.payload.error);
}
this.eventEmitter.emit(message.event, message);
this.eventEmitter.emit(message.type, message.payload);
break;
default:
this._log.log('Undefined message', messageEvent.data);
}
}
async init(settings) {
if (iframe.instance) {
throw ERRORS.TypedError('Init_AlreadyInitialized');
}
this._settings = (0, connectSettings_1.parseConnectSettings)({ ...this._settings, ...settings });
if (!this._settings.manifest) {
throw ERRORS.TypedError('Init_ManifestMissing');
}
if (!this._settings.transports?.length) {
this._settings.transports = ['BridgeTransport', 'WebUsbTransport'];
}
if (!this._settings.coreMode) {
this._settings.coreMode = 'auto';
}
if (this._settings.lazyLoad) {
this._settings.lazyLoad = false;
return;
}
if (!this._popupManager) {
this._popupManager = this.initPopupManager();
}
this._log.enabled = !!this._settings.debug;
window.addEventListener('message', this.boundHandleMessage);
window.addEventListener('unload', this.boundDispose);
await iframe.init(this._settings);
if (this._settings.coreMode === 'auto') {
const { promiseId, promise } = this._messagePromises.create();
this._log.debug('coreMode = auto, Checking bridge availability');
iframe.postMessage({ id: promiseId, type: events_2.TRANSPORT.GET_INFO });
const response = await promise;
this._log.debug('Bridge availability response', response);
if (response.payload === undefined &&
navigator.usb &&
this._settings.transports?.includes('WebUsbTransport')) {
throw ERRORS.TypedError('Transport_Missing');
}
}
if (this._settings.sharedLogger !== false) {
iframe.initIframeLogger();
}
}
setTransports() {
throw new Error('Unsupported right now');
}
async call(params) {
if (!iframe.instance && !iframe.timeout) {
this._settings = (0, connectSettings_1.parseConnectSettings)(this._settings);
if (!this._settings.manifest) {
return (0, events_2.createErrorMessage)(ERRORS.TypedError('Init_ManifestMissing'));
}
if (!this._popupManager) {
this._popupManager = this.initPopupManager();
}
try {
await this.init(this._settings);
}
catch (error) {
if (this._popupManager) {
this._popupManager.clear();
}
return (0, events_2.createErrorMessage)(error);
}
}
if (iframe.timeout) {
await iframe.initPromise.promise;
}
if (iframe.error) {
return (0, events_2.createErrorMessage)(iframe.error);
}
if (this._settings.popup && this._popupManager) {
this._popupManager.request();
}
try {
const { promiseId, promise } = this._messagePromises.create();
iframe.postMessage({ id: promiseId, type: events_2.IFRAME.CALL, payload: params });
const response = await promise;
if (response) {
if (!response.success &&
response.payload.code !== 'Device_CallInProgress' &&
this._popupManager) {
this._popupManager.unlock();
}
return response;
}
if (this._popupManager) {
this._popupManager.unlock();
}
return (0, events_2.createErrorMessage)(ERRORS.TypedError('Method_NoResponse'));
}
catch (error) {
this._log.error('__call error', error);
if (this._popupManager) {
this._popupManager.clear(false);
}
return (0, events_2.createErrorMessage)(error);
}
}
uiResponse(response) {
if (!iframe.instance) {
throw ERRORS.TypedError('Init_NotInitialized');
}
iframe.postMessage(response);
}
renderWebUSBButton(className) {
(0, button_1.default)(className, this._settings.webusbSrc);
}
async requestLogin(params) {
if (typeof params.callback === 'function') {
const { callback } = params;
const loginChallengeListener = async (event) => {
const { data } = event;
if (data && data.type === events_2.UI.LOGIN_CHALLENGE_REQUEST) {
try {
const payload = await callback();
iframe.postMessage({
type: events_2.UI.LOGIN_CHALLENGE_RESPONSE,
payload,
});
}
catch (error) {
iframe.postMessage({
type: events_2.UI.LOGIN_CHALLENGE_RESPONSE,
payload: error.message,
});
}
}
};
window.addEventListener('message', loginChallengeListener, false);
const response = await this.call({
method: 'requestLogin',
...params,
asyncChallenge: true,
callback: null,
});
window.removeEventListener('message', loginChallengeListener);
return response;
}
return this.call({ method: 'requestLogin', ...params });
}
disableWebUSB() {
if (!iframe.instance) {
throw ERRORS.TypedError('Init_NotInitialized');
}
iframe.postMessage({ type: events_2.TRANSPORT.DISABLE_WEBUSB });
}
async requestWebUSBDevice() {
try {
await window.navigator.usb.requestDevice({ filters: config_1.config.webusb });
iframe.postMessage({ type: events_2.TRANSPORT.REQUEST_DEVICE });
}
catch {
}
}
}
exports.CoreInIframe = CoreInIframe;
const impl = new CoreInIframe();
exports.TrezorConnect = (0, factory_1.factory)({
eventEmitter: impl.eventEmitter,
init: impl.init.bind(impl),
call: impl.call.bind(impl),
setTransports: impl.setTransports.bind(impl),
manifest: impl.manifest.bind(impl),
requestLogin: impl.requestLogin.bind(impl),
uiResponse: impl.uiResponse.bind(impl),
cancel: impl.cancel.bind(impl),
dispose: impl.dispose.bind(impl),
}, {
renderWebUSBButton: impl.renderWebUSBButton.bind(impl),
disableWebUSB: impl.disableWebUSB.bind(impl),
requestWebUSBDevice: impl.requestWebUSBDevice.bind(impl),
});
//# sourceMappingURL=core-in-iframe.js.map