UNPKG

xumm

Version:

One Xumm SDK to rule them all (browser, backend, xApp)

550 lines (549 loc) 27.7 kB
"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;