@trilitech-umami/umami-embed
Version:
[WIP - not ready for production use] A simple embeddable Umami wallet
139 lines (138 loc) • 5.18 kB
JavaScript
import { UmamiIframe } from "./UmamiIframe";
import { isFailedResponse, withTimeout } from "./utils";
import { Mutex, tryAcquire } from "async-mutex";
import { getIframeUrl } from "./iframeUrls";
const TIMEOUT = 50000;
export class Messages {
constructor(window, logger, useLocalEmbedIframe, network) {
Object.defineProperty(this, "window", {
enumerable: true,
configurable: true,
writable: true,
value: window
});
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: logger
});
Object.defineProperty(this, "useLocalEmbedIframe", {
enumerable: true,
configurable: true,
writable: true,
value: useLocalEmbedIframe
});
Object.defineProperty(this, "network", {
enumerable: true,
configurable: true,
writable: true,
value: network
});
Object.defineProperty(this, "iframe", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "actionMutex", {
enumerable: true,
configurable: true,
writable: true,
value: new Mutex()
});
Object.defineProperty(this, "embedUrl", {
enumerable: true,
configurable: true,
writable: true,
value: ""
});
this.iframe = new UmamiIframe();
this.embedUrl = getIframeUrl(this.useLocalEmbedIframe, this.network);
}
get isInitialized() {
return this.iframe.isInitialized;
}
init(iframeParent) {
return tryAcquire(this.actionMutex).runExclusive(async () => {
this.iframe.init(iframeParent, this.embedUrl);
return this.handleIFrameResponse("init_complete");
});
}
destroy() {
this.actionMutex.cancel();
this.iframe.destroy();
}
setConfig(config) {
return tryAcquire(this.actionMutex).runExclusive(async () => {
this.iframe.request({ type: "config_request", config });
return this.handleIFrameResponse("config_response");
});
}
login() {
return tryAcquire(this.actionMutex).runExclusive(async () => {
this.iframe.request({ type: "login_request" });
this.iframe.show();
return this.handleIFrameResponse("login_response");
});
}
logout() {
return tryAcquire(this.actionMutex).runExclusive(async () => {
this.iframe.request({ type: "logout_request" });
return this.handleIFrameResponse("logout_response");
});
}
send(operations) {
return tryAcquire(this.actionMutex).runExclusive(async () => {
this.iframe.request({ type: "operation_request", operations });
await this.handleIFrameResponse("computation_completed_response");
this.iframe.show();
return this.handleIFrameResponse("operation_response");
});
}
sign(signingType, payload) {
return tryAcquire(this.actionMutex).runExclusive(async () => {
this.iframe.request({
type: "sign_request",
signingType,
payload,
});
this.iframe.show();
return this.handleIFrameResponse("sign_response");
});
}
// The promise is rejected on timeout or if the response is failed.
// Response is only returned on success.
handleIFrameResponse(expectedType) {
let webResponseMessageListener;
return withTimeout(() => new Promise((resolve, reject) => {
webResponseMessageListener = (event) => {
if (event.origin !== this.embedUrl) {
return;
}
try {
const data = JSON.parse(event.data);
this.logger.info(`Received ${event.data} from ${event.origin}`);
// Only process the response if it matches the awaited response type
if (data.type === expectedType) {
// TODO: remove secure data from logs.
this.logger.info(`Processing ${event.data} from ${event.origin}`);
if (isFailedResponse(data)) {
reject(new Error(`${data.error}: ${data.errorMessage}`));
}
else {
resolve(data);
}
}
}
catch (e) {
reject(`Error processing message from Umami iFrame: ${e}`);
}
};
this.window.addEventListener("message", webResponseMessageListener);
}), TIMEOUT, `Wait for ${expectedType} has timed out`).finally(() => {
this.iframe.hide();
this.window.removeEventListener("message", webResponseMessageListener);
});
}
}