test-keyguard-client
Version:
Nimiq Keyguard client library dev test
200 lines (193 loc) • 9.79 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@nimiq/rpc')) :
typeof define === 'function' && define.amd ? define(['exports', '@nimiq/rpc'], factory) :
(factory((global.KeyguardClient = {}),global.rpc));
}(this, (function (exports,rpc) { 'use strict';
var BehaviorType;
(function (BehaviorType) {
BehaviorType[BehaviorType["REDIRECT"] = 0] = "REDIRECT";
BehaviorType[BehaviorType["IFRAME"] = 1] = "IFRAME";
})(BehaviorType || (BehaviorType = {}));
class RequestBehavior {
static getAllowedOrigin(endpoint) {
const url = new URL(endpoint);
return url.origin;
}
constructor(type) {
this._type = type;
}
async request(endpoint, command, args) {
throw new Error('Not implemented');
}
get type() {
return this._type;
}
}
class RedirectRequestBehavior extends RequestBehavior {
static getRequestUrl(endpoint, command) {
return `${endpoint}/request/${command}/`;
}
constructor(targetUrl, localState) {
super(BehaviorType.REDIRECT);
const location = window.location;
this._targetUrl = targetUrl
|| `${location.protocol}//${location.hostname}:${location.port}${location.pathname}`;
this._localState = localState || {};
// Reject local state with reserved property.
if (typeof this._localState.__command !== 'undefined') {
throw new Error('Invalid localState: Property \'__command\' is reserved');
}
}
async request(endpoint, command, args) {
const url = RedirectRequestBehavior.getRequestUrl(endpoint, command);
const origin = RequestBehavior.getAllowedOrigin(endpoint);
const client = new rpc.RedirectRpcClient(url, origin);
await client.init();
const state = Object.assign({ __command: command }, this._localState);
client.callAndSaveLocalState(this._targetUrl, state, 'request', ...args);
}
}
class IFrameRequestBehavior extends RequestBehavior {
constructor() {
super(BehaviorType.IFRAME);
this._iframe = null;
this._client = null;
}
async request(endpoint, command, args) {
if (this._iframe && this._iframe.src !== `${endpoint}${IFrameRequestBehavior.IFRAME_PATH_SUFFIX}`) {
throw new Error('Accounts Manager iframe is already opened with another endpoint');
}
const origin = RequestBehavior.getAllowedOrigin(endpoint);
if (!this._iframe) {
this._iframe = await this.createIFrame(endpoint);
}
if (!this._iframe.contentWindow) {
throw new Error(`IFrame contentWindow is ${typeof this._iframe.contentWindow}`);
}
if (!this._client) {
this._client = new rpc.PostMessageRpcClient(this._iframe.contentWindow, origin);
await this._client.init();
}
return await this._client.call(command, ...args);
}
async createIFrame(endpoint) {
return new Promise((resolve, reject) => {
const $iframe = document.createElement('iframe');
$iframe.name = 'NimiqKeyguardIFrame';
$iframe.style.display = 'none';
document.body.appendChild($iframe);
$iframe.src = `${endpoint}${IFrameRequestBehavior.IFRAME_PATH_SUFFIX}`;
$iframe.onload = () => resolve($iframe);
$iframe.onerror = reject;
});
}
}
IFrameRequestBehavior.IFRAME_PATH_SUFFIX = '/request/iframe/';
(function (KeyguardCommand) {
KeyguardCommand["CREATE"] = "create";
KeyguardCommand["REMOVE"] = "remove-key";
KeyguardCommand["IMPORT"] = "import";
KeyguardCommand["EXPORT_WORDS"] = "export-words";
KeyguardCommand["EXPORT_FILE"] = "export-file";
KeyguardCommand["EXPORT"] = "export";
KeyguardCommand["CHANGE_PASSPHRASE"] = "change-passphrase";
KeyguardCommand["SIGN_TRANSACTION"] = "sign-transaction";
KeyguardCommand["SIGN_MESSAGE"] = "sign-message";
KeyguardCommand["DERIVE_ADDRESS"] = "derive-address";
// Iframe requests
KeyguardCommand["LIST"] = "list";
KeyguardCommand["MIGRATE_ACCOUNTS_TO_KEYS"] = "migrateAccountsToKeys";
KeyguardCommand["DERIVE_ADDRESSES"] = "deriveAddresses";
KeyguardCommand["RELEASE_KEY"] = "releaseKey";
})(exports.KeyguardCommand || (exports.KeyguardCommand = {}));
class KeyguardClient {
constructor(endpoint = KeyguardClient.DEFAULT_ENDPOINT, defaultBehavior, defaultIframeBehavior) {
this._endpoint = endpoint;
this._defaultBehavior = defaultBehavior || new RedirectRequestBehavior();
this._defaultIframeBehavior = defaultIframeBehavior || new IFrameRequestBehavior();
// Listen for response
this._redirectClient = new rpc.RedirectRpcClient('', RequestBehavior.getAllowedOrigin(this._endpoint));
this._redirectClient.onResponse('request', this._onResolve.bind(this), this._onReject.bind(this));
this._observable = new Nimiq.Observable();
}
init() {
return this._redirectClient.init();
}
on(command, resolve, reject) {
this._observable.on(`${command}-resolve`, resolve);
this._observable.on(`${command}-reject`, reject);
}
/* TOP-LEVEL REQUESTS */
create(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.CREATE, [request]);
}
remove(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.REMOVE, [request]);
}
import(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.IMPORT, [request]);
}
async exportWords(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.EXPORT_WORDS, [request]);
}
async exportFile(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.EXPORT_FILE, [request]);
}
async export(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.EXPORT, [request]);
}
async changePassphrase(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.CHANGE_PASSPHRASE, [request]);
}
async signTransaction(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.SIGN_TRANSACTION, [request]);
}
async signMessage(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.SIGN_MESSAGE, [request]);
}
async deriveAddress(request, requestBehavior = this._defaultBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.DERIVE_ADDRESS, [request]);
}
/* IFRAME REQUESTS */
async list(listFromLegacyStore, requestBehavior = this._defaultIframeBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.LIST, [listFromLegacyStore]);
}
async migrateAccountsToKeys(requestBehavior = this._defaultIframeBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.MIGRATE_ACCOUNTS_TO_KEYS, []);
}
async deriveAddresses(keyId, paths, requestBehavior = this._defaultIframeBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.DERIVE_ADDRESSES, [keyId, paths]);
}
async releaseKey(keyId, requestBehavior = this._defaultIframeBehavior) {
return this._request(requestBehavior, exports.KeyguardCommand.RELEASE_KEY, [keyId]);
}
/* PRIVATE METHODS */
_request(behavior, command, args) {
return behavior.request(this._endpoint, command, args);
}
_onReject(error, id, state) {
const command = state.__command;
if (!command) {
throw new Error('Invalid state after RPC request');
}
delete state.__command;
this._observable.fire(`${command}-reject`, error, state);
}
_onResolve(result, id, state) {
const command = state.__command;
if (!command) {
throw new Error('Invalid state after RPC request');
}
delete state.__command;
this._observable.fire(`${command}-resolve`, result, state);
}
}
KeyguardClient.DEFAULT_ENDPOINT = window.location.origin === 'https://accounts.nimiq.com' ? 'https://keyguard-next.nimiq.com'
: window.location.origin === 'https://accounts.nimiq-testnet.com' ? 'https://keyguard-next.nimiq-testnet.com'
: 'http://localhost:8000/src';
exports.KeyguardClient = KeyguardClient;
exports.RequestBehavior = RequestBehavior;
exports.RedirectRequestBehavior = RedirectRequestBehavior;
exports.IFrameRequestBehavior = IFrameRequestBehavior;
Object.defineProperty(exports, '__esModule', { value: true });
})));