UNPKG

@plugnet/rpc-rx

Version:
177 lines (146 loc) 5.53 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _eventemitter = _interopRequireDefault(require("eventemitter3")); var _memoizee = _interopRequireDefault(require("memoizee")); var _rxjs = require("rxjs"); var _rpcCore = _interopRequireDefault(require("@plugnet/rpc-core")); var _operators = require("rxjs/operators"); var _util = require("@plugnet/util"); // Copyright 2017-2019 @polkadot/rpc-rx authors & contributors // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. /** * @name RpcRx * @summary The RxJS API is a wrapper around the API. * @description It allows wrapping API components with observables using RxJS. * * @example * <BR> * * ```javascript * import RpcRx from '@plugnet/rpc-rx'; * import WsProvider from '@plugnet/rpc-provider/ws'; * * const provider = new WsProvider('http://127.0.0.1:9944'); * const api = new RpcRx(provider); * ``` */ class RpcRx { /** * @param {ProviderInterface} provider An API provider using HTTP or WebSocket */ constructor(providerOrRpc) { this._api = void 0; this._eventemitter = void 0; this._isConnected = void 0; this.author = void 0; this.chain = void 0; this.state = void 0; this.system = void 0; this._api = providerOrRpc instanceof _rpcCore.default ? providerOrRpc : new _rpcCore.default(providerOrRpc); this._eventemitter = new _eventemitter.default(); this._isConnected = new _rxjs.BehaviorSubject(this._api._provider.isConnected()); this.initEmitters(this._api._provider); this.author = this.createInterface(this._api.author); this.chain = this.createInterface(this._api.chain); this.state = this.createInterface(this._api.state); this.system = this.createInterface(this._api.system); } isConnected() { return this._isConnected; } on(type, handler) { this._eventemitter.on(type, handler); } emit(type) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } this._eventemitter.emit(type, ...args); } initEmitters(provider) { provider.on('connected', () => { this._isConnected.next(true); this.emit('connected'); }); provider.on('disconnected', () => { this._isConnected.next(false); this.emit('disconnected'); }); } createInterface(section) { return Object.keys(section).filter(name => !['subscribe', 'unsubscribe'].includes(name)).reduce((observables, name) => { observables[name] = this.createObservable(name, section); return observables; }, {}); } createObservable(name, section) { var _this = this; if ((0, _util.isFunction)(section[name].unsubscribe)) { const memoized = (0, _memoizee.default)(function () { for (var _len2 = arguments.length, params = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { params[_key2] = arguments[_key2]; } return _this.createReplay(name, params, section, memoized); }, { length: false, // Normalize args so that different args that should be cached // together are cached together. // E.g.: `query.my.method('abc') === query.my.method(new AccountId('abc'));` normalizer: args => { // `args` is arguments object as accessible in memoized function return JSON.stringify(args); } }); return memoized; } // We voluntarily don't cache the "one-shot" RPC calls. For example, // `getStorage('123')` returns the current value, but this value can change // over time, so we wouldn't want to cache the Observable. return function () { for (var _len3 = arguments.length, params = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { params[_key3] = arguments[_key3]; } return (0, _rxjs.from)(section[name].apply(null, params).catch(error => { console.error(error); })); }; } createReplay(name, params, section, memoized) { return new _rxjs.Observable(observer => { const fn = section[name]; const callback = this.createReplayCallback(observer); const subscribe = fn(...params, callback).catch(error => observer.next(error)); return this.createReplayUnsub(fn, subscribe, params, memoized); }).pipe((0, _operators.map)(value => { if (value instanceof Error) { throw value; } return value; }), (0, _operators.publishReplay)(1), (0, _operators.refCount)()); } createReplayCallback(observer) { let cachedResult; return result => { if ((0, _util.isUndefined)(cachedResult) || !Array.isArray(cachedResult) || !Array.isArray(result) || result.length !== cachedResult.length) { cachedResult = result; } else { cachedResult = cachedResult.map((cachedValue, index) => (0, _util.isUndefined)(result[index]) ? cachedValue : result[index]); } observer.next(cachedResult); }; } createReplayUnsub(fn, subscribe, params, memoized) { return () => { subscribe.then(subscriptionId => fn.unsubscribe(subscriptionId)).then(() => { memoized.delete(...params); }).catch(error => { console.error('Unsubscribe failed', error); }); }; } } exports.default = RpcRx;