UNPKG

opendb_test_rpc

Version:

general purpose library for OpenDB blockchain

285 lines 17.8 kB
import { v1 } from 'uuid'; import WebSocket from 'isomorphic-ws'; export var RpcVersions; (function (RpcVersions) { RpcVersions["RPC_VERSION"] = "2.0"; })(RpcVersions || (RpcVersions = {})); export class RpcWebSocketClient { // constructor /** * Does not start WebSocket connection! * You need to call connect() method first. * @memberof RpcWebSocketClient */ constructor() { // TSES-lint: (any - unknown) Unexpected any. Specify a different type this.idAwaiter = {}; this.onOpenHandlers = []; this.onAnyMessageHandlers = []; this.onNotification = []; this.onRequest = []; this.onSuccessResponse = []; this.onErrorResponse = []; this.onErrorHandlers = []; this.onCloseHandlers = []; this.config = { responseTimeout: 10000, }; this.ws = undefined; } // public /** * Starts WebSocket connection. Returns Promise when connection is established. * @param {string} url * @param {(string | string[])} [protocols] * @memberof RpcWebSocketClient */ async connect(url, protocols) { this.ws = new WebSocket(url, protocols); await this.listen(); } // events onOpen(fn) { this.onOpenHandlers.push(fn); } /** * Native onMessage event. DO NOT USE THIS unless you really have to or for debugging purposes. * Proper RPC events are onRequest, onNotification, onSuccessResponse and onErrorResponse (or just awaiting response). * @param {RpcMessageEventFunction} fn * @memberof RpcWebSocketClient */ onAnyMessage(fn) { this.onAnyMessageHandlers.push(fn); } onError(fn) { this.onErrorHandlers.push(fn); } onClose(fn) { this.onCloseHandlers.push(fn); } /** * Appends onmessage listener on native websocket with RPC handlers. * If onmessage function was already there, it will call it on beggining. * Useful if you want to use RPC WebSocket Client on already established WebSocket along with function changeSocket(). * @memberof RpcWebSocketClient */ listenMessages() { let previousOnMessage; if (this.ws.onmessage) { previousOnMessage = this.ws.onmessage.bind(this.ws); } this.ws.onmessage = (e) => { if (previousOnMessage) { previousOnMessage(e); } for (const handler of this.onAnyMessageHandlers) { handler(e); } const data = JSON.parse(e.data); if (this.isNotification(data)) { // notification for (const handler of this.onNotification) { handler(data); } } else if (this.isRequest(data)) { // request for (const handler of this.onRequest) { handler(data); } // responses } else if (this.isSuccessResponse(data)) { // success for (const handler of this.onSuccessResponse) { handler(data); } // resolve awaiting function this.idAwaiter[data.id](data.result); } else if (this.isErrorResponse(data)) { // error for (const handler of this.onErrorResponse) { handler(data); } // resolve awaiting function this.idAwaiter[data.id](data.error); } }; } // communication /** * Creates and sends RPC request. Resolves when appropirate response is returned from server or after config.responseTimeout. * @param {string} method * @param {*} [params] * @returns * @memberof RpcWebSocketClient */ // TSES-lint: (any - unknown) Unexpected any. Specify a different type call(method, params) { return new Promise((resolve, reject) => { const data = this.buildRequest(method, params); // give limited time for response let timeout; if (this.config.responseTimeout) { timeout = setTimeout(() => { // stop waiting for response delete this.idAwaiter[data.id]; reject(`Awaiting response to: ${method} with id: ${data.id} timed out.`); }, this.config.responseTimeout); } // TSES-lint: (any - unknown) Unexpected any. Specify a different type // expect response this.idAwaiter[data.id] = (responseData) => { // stop timeout clearInterval(timeout); // stop waiting for response delete this.idAwaiter[data.id]; if (this.isRpcError(responseData)) { reject(responseData); return; } resolve(responseData); }; this.ws.send(JSON.stringify(data)); }); } // -------TODO: Unused Function Notify /** * Creates and sends RPC Notification. * @param {string} method * @param {*} [params] * @memberof RpcWebSocketClient */ // public notify(method: string, params?: any) { // this.ws.send(JSON.stringify(this.buildNotification(method, params))) // } // setup /** * You can provide custom id generation function to replace default uuid/v1. * @param {() => string} idFn * @memberof RpcWebSocketClient */ customId(idFn) { this.idFn = idFn; } /** * Allows modifying configuration. * @param {RpcWebSocketConfig} options * @memberof RpcWebSocketClient */ // TSES-lint: (any - unknown) Unexpected any. Specify a different type configure(options) { Object.assign(this.config, options); } /** * Allows you to change used native WebSocket client to another one. * If you have already-connected WebSocket, use this with listenMessages(). * @param {WebSocket} ws * @memberof RpcWebSocketClient */ changeSocket(ws) { this.ws = ws; } // private // events listen() { return new Promise((resolve, reject) => { this.ws.onopen = (e) => { for (const handler of this.onOpenHandlers) { handler(e); } resolve(void 0); }; // listen for messages this.listenMessages(); // called before onclose this.ws.onerror = (e) => { for (const handler of this.onErrorHandlers) { handler(e); } }; this.ws.onclose = (e) => { for (const handler of this.onCloseHandlers) { handler(e); } reject(); }; }); } // TSES-lint: (any - unknown) Unexpected any. Specify a different type // request buildRequest(method, params) { const data = this.buildRequestBase(method, params); data.jsonrpc = RpcVersions.RPC_VERSION; return data; } // TSES-lint: (any - unknown) Unexpected any. Specify a different type // private buildRequestBase(method: string, params?: unknown): IRpcRequest { // const data: IRpcRequest = {} as unknown // data.id = this.idFn() // data.method = method // if (params) { // data.params = params // } // return data // } buildRequestBase(method, params) { const data = { id: this.idFn(), jsonrpc: RpcVersions.RPC_VERSION, method: method, }; if (params) { data.params = params; } return data; } // -------TODO: Unused Function Notify // notification // private buildNotification(method: string, params?: any): IRpcNotification { // const data = this.buildNotificationBase(method, params) // data.jsonrpc = RpcVersions.RPC_VERSION // return data // } // private buildNotificationBase( // method: string, // params?: any // ): IRpcNotification { // const data: IRpcNotification = {} as any // data.method = method // if (params) { // data.params = params // } // return data // } idFn() { return v1(); } // tests isNotification(data) { return !data.id; // eslint-disable-line @typescript-eslint/no-explicit-any } isRequest(data) { // TSES-lint: (any - unknown) Unexpected any. Specify a different type return data.method !== undefined; // return (data as any).method } isSuccessResponse(data) { // TSES_lint : error Do not access Object.prototype method 'hasOwnProperty' from target object // return data.hasOwnProperty(`result`) return Object.prototype.hasOwnProperty.call(data, 'result'); } isErrorResponse(data) { // TSES_lint : error Do not access Object.prototype method 'hasOwnProperty' from target object // return data.hasOwnProperty(`error`) return Object.prototype.hasOwnProperty.call(data, 'error'); } // TSES-lint: (any - unknown) Unexpected any. Specify a different type isRpcError(data) { // return typeof (data as any).code !== 'undefined' return typeof data.code !== 'undefined'; } } export default RpcWebSocketClient; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnBjLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NsaWVudC9ycGMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQTtBQUN6QixPQUFPLFNBQVMsTUFBTSxlQUFlLENBQUE7QUFXckMsTUFBTSxDQUFOLElBQVksV0FFWDtBQUZELFdBQVksV0FBVztJQUNyQixrQ0FBbUIsQ0FBQTtBQUNyQixDQUFDLEVBRlcsV0FBVyxLQUFYLFdBQVcsUUFFdEI7QUFtREQsTUFBTSxPQUFPLGtCQUFrQjtJQXdCN0IsY0FBYztJQUNkOzs7O09BSUc7SUFDSDtRQUNFLHNFQUFzRTtRQTNCaEUsY0FBUyxHQUdiLEVBQUUsQ0FBQTtRQUVFLG1CQUFjLEdBQXVCLEVBQUUsQ0FBQTtRQUN2Qyx5QkFBb0IsR0FBOEIsRUFBRSxDQUFBO1FBRXBELG1CQUFjLEdBQTJCLEVBQUUsQ0FBQTtRQUMzQyxjQUFTLEdBQXNCLEVBQUUsQ0FBQTtRQUNqQyxzQkFBaUIsR0FBOEIsRUFBRSxDQUFBO1FBQ2pELG9CQUFlLEdBQTRCLEVBQUUsQ0FBQTtRQUU3QyxvQkFBZSxHQUF1QixFQUFFLENBQUE7UUFDeEMsb0JBQWUsR0FBNEIsRUFBRSxDQUFBO1FBRTdDLFdBQU0sR0FBRztZQUNmLGVBQWUsRUFBRSxLQUFLO1NBQ3ZCLENBQUE7UUFXQyxJQUFJLENBQUMsRUFBRSxHQUFHLFNBQW9CLENBQUE7SUFDaEMsQ0FBQztJQUVELFNBQVM7SUFDVDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBVyxFQUFFLFNBQTZCO1FBQzdELElBQUksQ0FBQyxFQUFFLEdBQUcsSUFBSSxTQUFTLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxDQUFBO1FBQ3ZDLE1BQU0sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFBO0lBQ3JCLENBQUM7SUFFRCxTQUFTO0lBQ0YsTUFBTSxDQUFDLEVBQW9CO1FBQ2hD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