xumm
Version:
One Xumm SDK to rule them all (browser, backend, xApp)
550 lines (549 loc) • 27.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Xumm = void 0;
const xumm_oauth2_pkce_1 = require("xumm-oauth2-pkce");
const buffer_1 = require("buffer");
const events_1 = require("events");
const fromBinary = (encoded) => {
// Fix browser atob UTF8 incompat
return buffer_1.Buffer.from(encoded, "base64").toString("utf8");
};
/**
* Development?
* npm run watch (tsc in watch mode)
* npm run browserify-watch (watch, browserify tsc output)
* npm run serve (webserver, :3001)
* npm run dev (watch, nodemon dist/samples/index.js)
* ngrok start dev (local :3001 to https:// dev URL)
* > https://.../sample/
*/
var Runtimes;
(function (Runtimes) {
Runtimes["cli"] = "cli";
Runtimes["browser"] = "browser";
Runtimes["xapp"] = "xapp";
})(Runtimes || (Runtimes = {}));
const _runtime = {
cli: false,
browser: false,
xapp: false,
};
const uuidv4re = new RegExp("^[0-9(a-f|A-F)]{8}-[0-9(a-f|A-F)]{4}-4[0-9(a-f|A-F)]{3}-[89ab][0-9(a-f|A-F)]{3}-[0-9(a-f|A-F)]{12}$");
const _classes = {};
const _env = typeof process === "object" && process ? (process === null || process === void 0 ? void 0 : process.env) || {} : {};
Object.assign(_runtime, {
cli: Object.keys(_env).indexOf("NODE") > -1 ||
Object.keys(_env).indexOf("SHELL") > -1 ||
Object.keys(_env).indexOf("TERM") > -1 ||
Object.keys(_env).indexOf("PATH") > -1,
});
Object.assign(_runtime, {
browser: (typeof process === "object" && process && (process === null || process === void 0 ? void 0 : process.browser)) ||
(typeof document === "object" &&
document &&
(document === null || document === void 0 ? void 0 : document.location)),
});
Object.assign(_runtime, {
xapp: _runtime.browser &&
(!!navigator.userAgent.match(/xumm\/xapp/i) ||
!!navigator.userAgent.match(/xAppBuilder/i)),
});
const runtime = Object.keys(_runtime).filter((r) => _runtime[r]);
const readyPromises = [];
/**
* Wrap a function into an awaiter, till the entire object is ready
*/
const Asyncify = (ReturnValue) => __awaiter(void 0, void 0, void 0, function* () {
yield Promise.all(readyPromises);
return yield ReturnValue();
});
/**
* Take an entire constructed class & wrap all responses
* in a promise (Asyncify) that waits for this entire class
* to be ready.
*/
const Proxify = (ObjectToProxy) => {
return new Proxy(ObjectToProxy, {
get(obj, prop) {
if (["on", "off"].indexOf(String(prop)) < 0 && // Events
(typeof obj[prop] === "function" ||
obj.constructor.name === "Promise")) {
return function () {
if (obj.constructor.name === "Promise") {
return obj.then((res) => Asyncify(() => {
return res[prop](...arguments);
}));
}
return Asyncify(() => {
return obj[prop](...arguments);
});
};
}
return obj[prop];
},
});
};
const _initialized = {
XummSdk: false,
XummSdkJwt: false,
XummPkce: false,
xApp: false,
};
let _ott;
let _jwt = "";
let _jwtData = {};
let _me = {};
let instance = 0;
class UnifiedUserData {
constructor() {
this.account = Asyncify(() => { var _a, _b, _c; return (_b = (_a = _jwtData === null || _jwtData === void 0 ? void 0 : _jwtData.sub) !== null && _a !== void 0 ? _a : _me === null || _me === void 0 ? void 0 : _me.sub) !== null && _b !== void 0 ? _b : (_c = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _c === void 0 ? void 0 : _c.account; });
this.picture = Asyncify(() => {
var _a, _b, _c, _d, _e, _f, _g;
return (_a = _me === null || _me === void 0 ? void 0 : _me.picture) !== null && _a !== void 0 ? _a : (((_c = (_b = _jwtData === null || _jwtData === void 0 ? void 0 : _jwtData.sub) !== null && _b !== void 0 ? _b : _me === null || _me === void 0 ? void 0 : _me.sub) !== null && _c !== void 0 ? _c : (_d = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _d === void 0 ? void 0 : _d.account)
? `https://xumm.app/avatar/${(_f = (_e = _jwtData === null || _jwtData === void 0 ? void 0 : _jwtData.sub) !== null && _e !== void 0 ? _e : _me === null || _me === void 0 ? void 0 : _me.sub) !== null && _f !== void 0 ? _f : (_g = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _g === void 0 ? void 0 : _g.account}.png`
: undefined);
});
this.name = Asyncify(() => { var _a, _b; return (_a = _me === null || _me === void 0 ? void 0 : _me.name) !== null && _a !== void 0 ? _a : (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.name; });
this.domain = Asyncify(() => { var _a, _b; return (_a = _me === null || _me === void 0 ? void 0 : _me.domain) !== null && _a !== void 0 ? _a : (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.domain; });
this.source = Asyncify(() => { var _a, _b; return (_a = _me === null || _me === void 0 ? void 0 : _me.source) !== null && _a !== void 0 ? _a : (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.source; });
this.networkType = Asyncify(() => {
var _a, _b;
return (_b = (_a = _me === null || _me === void 0 ? void 0 : _me.networkType) !== null && _a !== void 0 ? _a : _jwtData.network_type) !== null && _b !== void 0 ? _b : _ott === null || _ott === void 0 ? void 0 : _ott.nodetype;
});
this.networkEndpoint = Asyncify(() => {
var _a, _b;
return (_b = (_a = _me === null || _me === void 0 ? void 0 : _me.networkEndpoint) !== null && _a !== void 0 ? _a : _jwtData.network_endpoint) !== null && _b !== void 0 ? _b : _ott === null || _ott === void 0 ? void 0 : _ott.nodewss;
});
this.networkId = Asyncify(() => {
var _a, _b;
return (_b = (_a = _me === null || _me === void 0 ? void 0 : _me.networkId) !== null && _a !== void 0 ? _a : _jwtData.network_id) !== null && _b !== void 0 ? _b : _ott === null || _ott === void 0 ? void 0 : _ott.networkid;
});
this.blocked = Asyncify(() => { var _a, _b; return (_a = _me === null || _me === void 0 ? void 0 : _me.blocked) !== null && _a !== void 0 ? _a : (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.blocked; });
this.kycApproved = Asyncify(() => { var _a, _b; return (_a = _me === null || _me === void 0 ? void 0 : _me.kycApproved) !== null && _a !== void 0 ? _a : (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.kycApproved; });
this.proSubscription = Asyncify(() => { var _a, _b; return (_a = _me === null || _me === void 0 ? void 0 : _me.proSubscription) !== null && _a !== void 0 ? _a : (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.proSubscription; });
this.profile = Asyncify(() => {
var _a, _b, _c;
return (_a = _me === null || _me === void 0 ? void 0 : _me.profile) !== null && _a !== void 0 ? _a : (((_c = (_b = _ott === null || _ott === void 0 ? void 0 : _ott.account_info) === null || _b === void 0 ? void 0 : _b.profile) === null || _c === void 0 ? void 0 : _c.slug)
? _ott.account_info.profile
: undefined);
});
this.token = Asyncify(() => { var _a; return (_a = _jwtData === null || _jwtData === void 0 ? void 0 : _jwtData.usertoken_uuidv4) !== null && _a !== void 0 ? _a : null; });
}
}
/**
* This is where the magic happens
*/
class Xumm extends events_1.EventEmitter {
constructor(apiKeyOrJwt, apiSecretOrOtt) {
var _a, _b, _c, _d;
super();
this.instance = "0";
this.jwtCredential = false;
this.runtime = _runtime;
this.apiKeyOrJwt = "";
this.apiKeyOrJwt = apiKeyOrJwt;
this.apiSecretOrOtt = apiSecretOrOtt;
instance++;
this.instance = String(instance);
if (typeof (console === null || console === void 0 ? void 0 : console.log) !== "undefined") {
if (!_runtime.cli)
console.log("Constructed Xumm", { runtime });
}
let jwtExpired = false;
if (typeof this.apiKeyOrJwt === "string" &&
this.apiKeyOrJwt.split(".").length === 3) {
let _testJwtData;
try {
// Check validity
_testJwtData = JSON.parse(fromBinary((_a = this.apiKeyOrJwt.split(".")) === null || _a === void 0 ? void 0 : _a[1]));
}
catch (e) {
// e
// Parse error
}
if (Date.now() >= _testJwtData.exp * 1000) {
jwtExpired = true;
const appId = (_d = (_c = (_b = _testJwtData === null || _testJwtData === void 0 ? void 0 : _testJwtData.app_uuidv4) !== null && _b !== void 0 ? _b : _testJwtData === null || _testJwtData === void 0 ? void 0 : _testJwtData.client_id) !== null && _c !== void 0 ? _c : _testJwtData === null || _testJwtData === void 0 ? void 0 : _testJwtData.aud) !== null && _d !== void 0 ? _d : "";
this.apiKeyOrJwt = appId;
if (!_runtime.cli)
console.log("JWT expired, falling back to API KEY: " + appId);
if (_runtime.cli || _runtime.xapp) {
const error = new Error("JWT Expired, cannot fall back to API credential: in CLI/xApp environment");
this.emit("error", error);
throw error;
}
}
if (!jwtExpired) {
this.jwtCredential = true;
_jwt = this.apiKeyOrJwt;
}
}
this.initialize();
/**
* Bootstrap class properties
*/
this.user = new UnifiedUserData();
this.environment = {
jwt: Asyncify(() => _jwtData),
ott: Asyncify(() => _ott),
openid: Asyncify(() => _me),
bearer: Asyncify(() => _jwt),
ready: new Promise((resolve) => this.on("ready", () => resolve(undefined))),
success: new Promise((resolve) => this.on("success", () => resolve(undefined))),
retrieved: new Promise((resolve) => this.on("retrieved", () => resolve(undefined))),
retrieving: new Promise((resolve) => this.on("retrieving", () => resolve(undefined))),
};
this.state = {
account: '',
signedIn: false,
};
/**
* Xumm SDK mapped
*/
this.helpers = Proxify(Asyncify(() => (_classes.XummSdk || _classes.XummSdkJwt)));
this.push = Proxify(Asyncify(() => (_classes.XummSdk || _classes.XummSdkJwt)
.Push));
this.payload = Proxify(Asyncify(() => (_classes.XummSdk || _classes.XummSdkJwt)
.payload));
this.userstore = Proxify(Asyncify(() => (_classes.XummSdk || _classes.XummSdkJwt)
.jwtUserdata));
this.backendstore = Proxify(Asyncify(() => (_classes.XummSdk || _classes.XummSdkJwt)
.storage));
/**
* Xumm xApp SDK: UI lib.
*/
const xapp = _classes === null || _classes === void 0 ? void 0 : _classes.xApp;
if (xapp)
this.xapp = xapp;
setTimeout(() => Promise.all([
...readyPromises.filter((p) => (p === null || p === void 0 ? void 0 : p.promiseType) !==
"pkceRetrieverResolver"),
/**
* If PKCE flow: wait for `ready` till account is known
*/
_runtime.xapp
? Promise.resolve()
: new Promise((resolve) => {
var _a;
if (_classes === null || _classes === void 0 ? void 0 : _classes.XummPkce) {
this.user.account.then(() => resolve());
(_a = _classes.XummPkce) === null || _a === void 0 ? void 0 : _a.on("loggedout", () => resolve());
}
else {
resolve();
}
}),
]).then(() => this.emit("ready")), // Constructor ready
0);
}
initialize() {
_ott = undefined;
if (typeof this.apiKeyOrJwt === "string" &&
this.apiKeyOrJwt.split(".").length === 3 &&
_jwt === this.apiKeyOrJwt) {
// Keep JWT data, constructed with JWT
}
else {
_jwt = "";
_jwtData = {};
}
_me = {};
_initialized.XummSdkJwt = false;
// _initialized.XummPkce = false
/**
* Handlers (setup)
*/
const handleXappEvents = () => __awaiter(this, void 0, void 0, function* () {
// Always attach event listeners
// So no: && !_initialized.xApp
if (_classes === null || _classes === void 0 ? void 0 : _classes.xApp) {
_initialized.xApp = true;
_classes.xApp.on("qr", (data) => {
this.emit("qr", data, this.instance);
});
_classes.xApp.on("payload", (data) => {
this.emit("payload", data, this.instance);
});
_classes.xApp.on("networkswitch", (data) => {
this.emit("networkswitch", data, this.instance);
});
_classes.xApp.on("destination", (data) => {
this.emit("destination", data, this.instance);
});
}
});
this.handlePkceEvents();
/**
* Xumm SDK
*/
const handleOttJwt = () => __awaiter(this, void 0, void 0, function* () {
var _a;
if ((_classes === null || _classes === void 0 ? void 0 : _classes.XummSdkJwt) && !_initialized.XummSdkJwt) {
_initialized.XummSdkJwt = true;
const doNotFetchJwtOtt = this.jwtCredential || _jwt !== "";
if (!doNotFetchJwtOtt) {
readyPromises.push(_classes.XummSdkJwt.getOttData());
readyPromises.push(_classes.XummSdkJwt.getJwt());
}
const ott = !doNotFetchJwtOtt
? yield _classes.XummSdkJwt.getOttData()
: null;
const jwt = !doNotFetchJwtOtt
? yield _classes.XummSdkJwt.getJwt()
: _jwt;
if (ott) {
_ott = ott;
if (_ott === null || _ott === void 0 ? void 0 : _ott.account) {
this.state.account = _ott.account;
this.state.signedIn = true;
}
// Mock, so browser code works in xApp as well
this.emit("retrieved");
this.emit("success");
}
if (jwt) {
_jwt = jwt;
try {
_jwtData = JSON.parse(fromBinary((_a = _jwt.split(".")) === null || _a === void 0 ? void 0 : _a[1]));
if (doNotFetchJwtOtt && this.jwtCredential) {
// Mock, so success & retrieved events are fired as well.
setTimeout(() => this.emit("retrieved"), 0);
setTimeout(() => this.emit("success"), 0);
}
}
catch (e) {
if (typeof (console === null || console === void 0 ? void 0 : console.log) !== "undefined") {
if (!_runtime.cli)
console.log("Error decoding JWT", (e === null || e === void 0 ? void 0 : e.message) || "");
}
}
}
if (typeof (console === null || console === void 0 ? void 0 : console.log) !== "undefined") {
// console.log({ ott, jwt });
}
}
});
const initOttJwtRuntime = () => {
var _a, _b, _c;
if (!(_classes === null || _classes === void 0 ? void 0 : _classes.XummSdkJwt)) {
Object.assign(_classes, {
XummSdkJwt: new (require("xumm-sdk").XummSdkJwt)(this.apiKeyOrJwt,
// Get OTT from UA if present, otherwise fall back to default behaviour
typeof ((_a = _classes === null || _classes === void 0 ? void 0 : _classes.xApp) === null || _a === void 0 ? void 0 : _a.getEnvironment) !== "undefined"
? ((_c = (_b = _classes === null || _classes === void 0 ? void 0 : _classes.xApp) === null || _b === void 0 ? void 0 : _b.getEnvironment()) === null || _c === void 0 ? void 0 : _c.ott) || undefined // Required as null breaks
: undefined),
});
readyPromises.push(handleOttJwt());
}
};
if (_runtime.xapp) {
/**
* xApp
*/
if (typeof this.apiKeyOrJwt !== "string" ||
!(uuidv4re.test(this.apiKeyOrJwt) || this.jwtCredential)) {
throw new Error("Running in xApp, constructor requires first param. to be Xumm API Key or JWT");
}
if (!(_classes === null || _classes === void 0 ? void 0 : _classes.xApp)) {
Object.assign(_classes, {
xApp: new (require("xumm-xapp-sdk").xApp)(),
});
}
readyPromises.push(handleXappEvents());
initOttJwtRuntime();
}
else if (_runtime.browser) {
/**
* Browser (JWT, PKCE?)
*/
if (typeof this.apiKeyOrJwt !== "string" ||
!(uuidv4re.test(this.apiKeyOrJwt) || this.jwtCredential)) {
throw new Error("Running in browser, constructor requires first param. to be Xumm API Key or JWT");
}
if (!(_classes === null || _classes === void 0 ? void 0 : _classes.XummPkce) && !_runtime.xapp) {
Object.assign(_classes, {
XummPkce: new xumm_oauth2_pkce_1.XummPkce(this.apiKeyOrJwt, {
implicit: true,
}),
});
if (_classes.XummPkce) {
if (this.jwtCredential) {
initOttJwtRuntime();
}
else {
setTimeout(() => this.emit("retrieving"), 0);
const handlePkceState = (resolve) => {
var _a, _b;
(_b = (_a = _classes.XummPkce) === null || _a === void 0 ? void 0 : _a.state()) === null || _b === void 0 ? void 0 : _b.then((state) => {
var _a;
// state: jwt, me, sdk
if ((state === null || state === void 0 ? void 0 : state.sdk) && !(_classes === null || _classes === void 0 ? void 0 : _classes.XummSdkJwt)) {
Object.assign(_classes, { XummSdkJwt: state.sdk });
Object.assign(_me, Object.assign({}, ((state === null || state === void 0 ? void 0 : state.me) || {})));
if ((_a = state === null || state === void 0 ? void 0 : state.me) === null || _a === void 0 ? void 0 : _a.account) {
this.state.account = state.me.account;
this.state.signedIn = true;
}
}
if ((state === null || state === void 0 ? void 0 : state.jwt) && _jwt === "") {
_jwt = state.jwt;
handleOttJwt();
}
resolve(state);
});
};
readyPromises.push(this.handlePkceEvents());
// The promise below does not count against the "ready" event
const pkceRetrieverResolver = new Promise((resolve) => {
var _a, _b;
(_a = _classes.XummPkce) === null || _a === void 0 ? void 0 : _a.on("retrieved", () => {
handlePkceState(resolve);
});
(_b = _classes.XummPkce) === null || _b === void 0 ? void 0 : _b.on("success", () => {
handlePkceState(resolve);
});
});
readyPromises.push(Object.assign(pkceRetrieverResolver, {
promiseType: "pkceRetrieverResolver",
}));
}
}
}
}
else if (_runtime.cli) {
/**
* CLI, Node / ...
*/
if (typeof this.apiKeyOrJwt !== "string") {
throw new Error("Running CLI, constructor needs first param. to be Xumm API Key / raw JWT");
}
else {
if (uuidv4re.test(this.apiKeyOrJwt)) {
// API Key
if (typeof this.apiSecretOrOtt !== "string" ||
!uuidv4re.test(this.apiSecretOrOtt)) {
throw new Error("Running CLI, constructor first param. is API Key, but second param. isn't a valid API Secret");
}
}
if (!uuidv4re.test(this.apiKeyOrJwt) &&
this.apiKeyOrJwt.split(".").length !== 3) {
throw new Error("Running CLI, constructor first param. not a valid JWT, nor a valid API Key");
}
// All fine
if (this.jwtCredential) {
initOttJwtRuntime();
}
else {
if (!(_classes === null || _classes === void 0 ? void 0 : _classes.XummSdk)) {
Object.assign(_classes, {
XummSdk: new (require("xumm-sdk").XummSdk)(this.apiKeyOrJwt, this.apiSecretOrOtt),
});
}
}
}
}
}
/**
* PKCE
*/
authorize() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// console.log("Authorize");
try {
return yield ((_a = _classes === null || _classes === void 0 ? void 0 : _classes.XummPkce) === null || _a === void 0 ? void 0 : _a.authorize());
}
catch (e) {
return e;
}
});
}
/**
* TODO: CHECK IF EVENTS ARE NOT REGISTERED AND FIRING TWICE
*/
handlePkceEvents() {
return __awaiter(this, void 0, void 0, function* () {
// Always attach event listeners
// So no: && !_initialized.XummPkce
if ((_classes === null || _classes === void 0 ? void 0 : _classes.XummPkce) && !_initialized.XummPkce) {
_initialized.XummPkce = true;
const retrievedHandler = () => this.emit("retrieved");
const successHandler = () => this.emit("success");
const errorHandler = (data) => this.emit("error", data);
_classes.XummPkce.on("retrieved", retrievedHandler);
_classes.XummPkce.on("success", successHandler);
_classes.XummPkce.on("error", errorHandler);
}
return;
});
}
logout() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (_runtime.xapp) {
return;
}
let downgradeJwtLogin = false;
this.state.account = '';
this.state.signedIn = false;
if (typeof this.apiKeyOrJwt === "string" &&
this.apiKeyOrJwt.split(".").length === 3 &&
(_jwtData === null || _jwtData === void 0 ? void 0 : _jwtData.app_uuidv4) &&
this.jwtCredential) {
// Constructed with JWT, reset to application UUID for new auth
this.apiKeyOrJwt = _jwtData.app_uuidv4;
this.jwtCredential = false;
downgradeJwtLogin = true;
// Remove PKCE Thread for full re-init
window._XummPkce = undefined;
}
if (_runtime.browser && ((_me === null || _me === void 0 ? void 0 : _me.sub) || downgradeJwtLogin)) {
(_a = _classes === null || _classes === void 0 ? void 0 : _classes.XummPkce) === null || _a === void 0 ? void 0 : _a.logout();
Object.assign(_classes, {
XummSdk: undefined,
XummSdkJwt: undefined,
XummPkce: undefined,
});
readyPromises.length = 0;
this.jwtCredential = false;
this.initialize();
/**
* RESET: Bootstrap class properties
*/
this.user = new UnifiedUserData();
this.environment = {
jwt: Asyncify(() => _jwtData),
ott: Asyncify(() => _ott),
openid: Asyncify(() => _me),
bearer: Asyncify(() => _jwt),
ready: new Promise((resolve) => this.on("ready", () => resolve(undefined))),
success: new Promise((resolve) => this.on("success", () => resolve(undefined))),
retrieved: new Promise((resolve) => this.on("retrieved", () => resolve(undefined))),
retrieving: new Promise((resolve) => this.on("retrieving", () => resolve(undefined))),
};
this.emit("logout");
}
});
}
/**
* SDK
*/
ping() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// TODO: What if not JWT but regular?
yield Promise.all(readyPromises);
return (_a = ((_classes === null || _classes === void 0 ? void 0 : _classes.XummSdkJwt) || (_classes === null || _classes === void 0 ? void 0 : _classes.XummSdk))) === null || _a === void 0 ? void 0 : _a.ping();
});
}
}
exports.Xumm = Xumm;