├── .prettierrc ├── src ├── index.ts ├── interfaces │ ├── index.ts │ ├── rpc-response-error.interface.ts │ ├── rpc-request.interface.ts │ ├── rpc-client-options.interface.ts │ └── rpc-response.interface.ts ├── rpc-error.ts ├── rpc-error-codes.enum.ts └── rpc-client.ts ├── .travis.yml ├── scripts └── build.sh ├── jest.json ├── tsconfig.json ├── tslint.json ├── LICENSE ├── package.json ├── .gitignore ├── README.md └── tests └── rpc-client.spec.ts /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './rpc-client'; 2 | export * from './rpc-error'; 3 | export * from './rpc-error-codes.enum'; 4 | export * from './interfaces'; -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './rpc-client-options.interface'; 2 | export * from './rpc-request.interface'; 3 | export * from './rpc-response-error.interface'; 4 | export * from './rpc-response.interface'; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | - '9' 5 | - '10' 6 | install: 7 | - npm install 8 | script: 9 | - npm test 10 | after_success: 11 | - npm run test:coverage 12 | - 'npm install coveralls && cat ./coverage/lcov.info | coveralls' 13 | -------------------------------------------------------------------------------- /src/rpc-error.ts: -------------------------------------------------------------------------------- 1 | import { RpcResponseError } from './interfaces'; 2 | import { RpcErrorCode } from './rpc-error-codes.enum'; 3 | 4 | export class RpcError extends Error { 5 | constructor(private readonly err: RpcResponseError) { 6 | super(); 7 | super.message = err.message; 8 | } 9 | 10 | public getCode(): RpcErrorCode | number { 11 | return this.err.code; 12 | } 13 | 14 | public getData(): TError | undefined { 15 | return this.err.data; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A basic script to build and compile the typescript files using tsc 3 | 4 | # Set an error handler 5 | trap onExit EXIT 6 | 7 | # printing the simple stack trace 8 | onExit() { 9 | while caller $((n++)); 10 | do :; 11 | done; 12 | } 13 | 14 | build() { 15 | echo 'Start building..' 16 | # Run tsc 17 | tsc 18 | echo 'tsc exist with status code:' $? 19 | echo 'Copying Other files..' 20 | cp -rf package.json dist 21 | cp -rf README.md dist 22 | echo 'Done.' 23 | echo '--------' 24 | } 25 | 26 | build -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testRegex": ".spec.ts$", 5 | "transform": { 6 | "^.+\\.ts?$": "ts-jest" 7 | }, 8 | "globals": { 9 | "ts-jest": { 10 | "diagnostics": false 11 | } 12 | }, 13 | "collectCoverageFrom": [ 14 | "src/**/*.ts", 15 | "!src/index.ts", 16 | "!src/**/index.ts", 17 | "!src/**/*.interface.ts", 18 | "!src/**/*.enum.ts", 19 | "!**/node_modules/**", 20 | "!**/vendor/**" 21 | ], 22 | "coverageReporters": [ 23 | "json", 24 | "lcov" 25 | ], 26 | "coverageDirectory": "./coverage", 27 | "testEnvironment": "node" 28 | } 29 | -------------------------------------------------------------------------------- /src/rpc-error-codes.enum.ts: -------------------------------------------------------------------------------- 1 | export enum RpcErrorCode { 2 | /** 3 | * Invalid JSON was received by the server. 4 | * or An error occurred on the server while parsing the JSON text. 5 | */ 6 | PARSE_ERROR = -32700, 7 | /** 8 | * The JSON sent is not a valid Request object. 9 | */ 10 | INVALID_REQUEST = -32600, 11 | /** 12 | * The method does not exist / is not available. 13 | */ 14 | METHOD_NOT_FOUND = -32601, 15 | /** 16 | * Invalid method parameter(s). 17 | */ 18 | INVALID_PARAMS = -32602, 19 | /** 20 | * Internal JSON-RPC error. 21 | */ 22 | INTERNAL_ERROR = -32603, 23 | /** 24 | * Reserved for implementation-defined server-errors. 25 | */ 26 | SERVER_ERROR = -32000, 27 | } 28 | -------------------------------------------------------------------------------- /src/interfaces/rpc-response-error.interface.ts: -------------------------------------------------------------------------------- 1 | import { RpcErrorCode } from '../rpc-error-codes.enum'; 2 | 3 | /** 4 | * When a rpc call encounters an error, 5 | * the Response Object MUST contain the error member with a value that is that object 6 | */ 7 | export interface RpcResponseError { 8 | /** 9 | * A Number that indicates the error type that occurred. 10 | */ 11 | code: number | RpcErrorCode; 12 | /** 13 | * A String providing a short description of the error. 14 | */ 15 | message: string; 16 | /** 17 | * A Primitive or Structured value that contains additional information about the error. 18 | * The value of this member is defined by the Server 19 | * (e.g. detailed error information, nested errors etc.). 20 | */ 21 | data?: TErrorData; 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": true, 6 | "noImplicitThis": true, 7 | "alwaysStrict": true, 8 | "noImplicitReturns": true, 9 | "skipLibCheck": true, 10 | "pretty": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "strictNullChecks": true, 14 | "removeComments": false, 15 | "noLib": false, 16 | "emitDecoratorMetadata": true, 17 | "experimentalDecorators": true, 18 | "target": "es2016", 19 | "inlineSourceMap": false, 20 | "inlineSources": false, 21 | "sourceMap": true, 22 | "allowJs": false, 23 | "lib": ["esnext"], 24 | "outDir": "./dist", 25 | "baseUrl": "./src" 26 | }, 27 | "include": ["src/**/*"], 28 | "exclude": ["node_modules", "**/*.spec.ts"] 29 | } 30 | -------------------------------------------------------------------------------- /src/interfaces/rpc-request.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A rpc call is represented by sending a Request object to a Server. 3 | */ 4 | export interface RpcRequest { 5 | /** 6 | * A String specifying the version of the JSON-RPC protocol. **MUST** be exactly "2.0". 7 | */ 8 | jsonrpc: '2.0'; 9 | 10 | /** 11 | * A String containing the name of the method to be invoked. 12 | */ 13 | method: TMethod; 14 | 15 | /** 16 | * A Structured value that holds the parameter values 17 | * to be used during the invocation of the method. 18 | */ 19 | params?: TParam; 20 | 21 | /** 22 | * An identifier established by the Client that **MUST** contain a `String`, `Number`, 23 | * or `NULL` value if included. 24 | * If it is not included it is assumed to be a notification. 25 | * The value **SHOULD** normally not be Null and Numbers **SHOULD NOT** contain fractional parts 26 | */ 27 | id?: string | number | null; 28 | } 29 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "linterOptions": { 8 | "exclude": ["src/generated/**/*.ts"] 9 | }, 10 | "rules": { 11 | "eofline": false, 12 | "quotemark": [true, "single"], 13 | "indent": true, 14 | "member-access": [true], 15 | "ordered-imports": [true], 16 | "max-line-length": [true, 100], 17 | "member-ordering": [true], 18 | "curly": false, 19 | "interface-name": [false], 20 | "no-empty-interface": true, 21 | "no-empty": false, 22 | "arrow-parens": false, 23 | "object-literal-sort-keys": false, 24 | "no-unused-expression": true, 25 | "max-classes-per-file": [false, 10], 26 | "variable-name": [true], 27 | "one-line": [false], 28 | "array-type": [true, "generic"], 29 | "object-literal-key-quotes": false, 30 | "one-variable-per-declaration": [false] 31 | }, 32 | "rulesDirectory": [] 33 | } 34 | -------------------------------------------------------------------------------- /src/interfaces/rpc-client-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from 'axios'; 2 | 3 | export interface RpcClientOptions extends AxiosRequestConfig { 4 | /** 5 | * `url` is the server URL that will be used for the request 6 | */ 7 | url: string; 8 | /** 9 | * `auth` indicates that HTTP Basic auth should be used, and supplies credentials. 10 | * This will set an `Authorization` header, overwriting any existing 11 | * `Authorization` custom headers you have set using `headers`. 12 | */ 13 | auth?: { 14 | username: string; 15 | password: string; 16 | }; 17 | /** 18 | * Extend the headers sent by the client 19 | */ 20 | headers?: any; 21 | /** 22 | * `timeout` specifies the number of milliseconds before the request times out, 23 | * Automatically causes the request to abort 24 | */ 25 | timeout?: number; 26 | /** 27 | * `responseEncoding` indicates encoding to use for decoding responses. 28 | */ 29 | responseEncoding?: string | 'utf8'; 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Shady Khalifa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonrpc-ts", 3 | "version": "0.3.0", 4 | "description": "Strongly Typed Fast and lightweight JSON RPC 2.0 Client for Nodejs", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --config jest.json", 8 | "test:watch": "jest --watch --config=jest.json", 9 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 10 | "build": "./scripts/build.sh", 11 | "npm:publish": "cd dist && npm publish" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/shekohex/jsonrpc-ts.git" 16 | }, 17 | "author": "Shady Khalifa ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/shekohex/jsonrpc-ts/issues" 21 | }, 22 | "homepage": "https://github.com/shekohex/jsonrpc-ts#readme", 23 | "devDependencies": { 24 | "@types/jest": "^24.9.1", 25 | "@types/node": "^12.12.48", 26 | "coveralls": "^3.1.0", 27 | "http-jsonrpc-server": "^1.1.0", 28 | "jest": "^29.7.0", 29 | "ts-jest": "^29.1.1" 30 | }, 31 | "dependencies": { 32 | "axios": "^1.5.1", 33 | "typescript": "^5.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | dist -------------------------------------------------------------------------------- /src/interfaces/rpc-response.interface.ts: -------------------------------------------------------------------------------- 1 | import { RpcResponseError } from './rpc-response-error.interface'; 2 | 3 | /** 4 | * When a rpc call is made, the Server **MUST** reply with a Response 5 | * except for in the case of Notifications. 6 | * The Response is expressed as a single JSON Object 7 | */ 8 | export interface RpcResponse { 9 | /** 10 | * A String specifying the version of the JSON-RPC protocol. 11 | * **MUST** be exactly "2.0". 12 | */ 13 | jsonrpc: '2.0'; 14 | 15 | /** 16 | * This member is **REQUIRED** on success. 17 | * This member **MUST NOT** exist if there was an error invoking the method. 18 | * The value of this member is determined by the method invoked on the Server. 19 | */ 20 | result?: TResult; 21 | 22 | /** 23 | * This member is REQUIRED on error. 24 | * This member MUST NOT exist if there was no error triggered during invocation. 25 | * The value for this member MUST be an Object of Type `RpcResponseError`. 26 | */ 27 | error?: RpcResponseError; 28 | 29 | /** 30 | * An identifier established by the Client that **MUST** contain a `String`, `Number`, 31 | * or `NULL` value if included. 32 | * It **MUST** be the same as the value of the id member in the Request Object. 33 | * If there was an error 34 | * in detecting the `id` in the Request object (e.g. `Parse error`/`Invalid Request`) 35 | * it **MUST** be `Null`. 36 | */ 37 | id: string | number | null; 38 | } 39 | -------------------------------------------------------------------------------- /src/rpc-client.ts: -------------------------------------------------------------------------------- 1 | import Axios, { AxiosInstance, AxiosResponse } from 'axios'; 2 | import { RpcClientOptions, RpcRequest, RpcResponse } from './interfaces'; 3 | import { RpcError } from './rpc-error'; 4 | 5 | type RpcBatchRequest = Array>; 6 | type RpcBatchResponse = Array>; 7 | type ReturnTypeOfMethod = T extends (...args: Array) => any ? ReturnType : any; 8 | type ReturnTypeOfMethodIfExists = S extends keyof T ? ReturnTypeOfMethod : any; 9 | type MethodParams = T extends (...args: infer P) => any ? P[0] : T; 10 | type MethodParamsIfExists = S extends keyof T ? MethodParams : S; 11 | export class RpcClient { 12 | private readonly client: AxiosInstance = Axios; 13 | 14 | constructor(private readonly options: RpcClientOptions) {} 15 | /** 16 | * Make JSON RPC Batch Request of the same method 17 | * @throws {AxiosError | RpcError} http/rpc error 18 | */ 19 | public async makeBatchRequest( 20 | requests: RpcBatchRequest, 21 | ): Promise>> { 22 | const response = await this.client.post>( 23 | this.options.url, 24 | requests, 25 | this.options, 26 | ); 27 | // TODO: Handle Batch Request errors ! 28 | // if (response.data.error) { 29 | // throw new RpcError(response.data.error); 30 | // } 31 | return response; 32 | } 33 | 34 | /** 35 | * Make JSON RPC Request 36 | * @throws {AxiosError | RpcError} http/rpc error 37 | */ 38 | public async makeRequest( 39 | request: RpcRequest>, 40 | ): Promise, TError>>> { 41 | const response = await this.client.post< 42 | RpcResponse, TError> 43 | >(this.options.url, request, this.options); 44 | if (response.data.error) { 45 | throw new RpcError(response.data.error); 46 | } 47 | return response; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSONRPC Typescript 2 | 3 | [![Build Status](https://travis-ci.org/shekohex/jsonrpc-ts.svg?branch=master)](https://travis-ci.org/shekohex/jsonrpc-ts) [![Greenkeeper badge](https://badges.greenkeeper.io/shekohex/jsonrpc-ts.svg)](https://greenkeeper.io/) 4 | [![Coverage Status](https://coveralls.io/repos/github/shekohex/jsonrpc-ts/badge.svg?branch=master)](https://coveralls.io/github/shekohex/jsonrpc-ts?branch=master) 5 | [![npm version](https://badge.fury.io/js/jsonrpc-ts.svg)](https://badge.fury.io/js/jsonrpc-ts) 6 | 7 | Strongly 💪 Typed JSON RPC 2.0 Client for Nodejs 8 | 9 | Fully tested to comply with the [official JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification) 10 | 11 | ## Quick Overview 12 | 13 | By Declaring events using a simple interface mapping methods names to their parameters to get Strongly Typed, Fast and Modern Rpc client for your service. 14 | 15 | ## Install 16 | 17 | ``` 18 | npm i jsonrpc-ts 19 | ``` 20 | 21 | ## Usage 22 | 23 | First you need to define your Methods, that could be achieved by creating an interface of methods name and there parameters. 24 | 25 | ```ts 26 | // we have a service that can do math, 27 | // and it has this methods 28 | interface MathService { 29 | // a method called sum that accepts 2 args of type number 30 | sum: [number, number]; 31 | // methods can have named paramerter too. 32 | sub: { left: number; right: number }; 33 | // or if you need return type, you can have that too :) 34 | sumWithReturnType: ({ x, y }: { x: number; y: number }) => number; 35 | } 36 | ``` 37 | 38 | then import `RpcClient` and start making requests 39 | 40 | ```ts 41 | import { RpcClient } from 'jsonrpc-ts'; 42 | 43 | const rpcClient = new RpcClient({ url: '...' }); 44 | // now you have a strongly typed methods. 45 | // try to change [3, 2] to ['3', '2'] and the typescript compiler will catch you ! 46 | const response = await rpcClient.makeRequest({ 47 | method: 'sum', 48 | params: [3, 2], 49 | id: 1, 50 | jsonrpc: '2.0', 51 | }); 52 | // response.data.result === 5 53 | 54 | // response2.data.result has type of number :) 55 | const response2 = await rpcClient.makeRequest({ 56 | method: 'sumWithReturnType', 57 | params: { x: 3, y: 2 }, 58 | id: 2, 59 | jsonrpc: '2.0', 60 | }); 61 | // response2.data.result === 5 62 | ``` 63 | -------------------------------------------------------------------------------- /tests/rpc-client.spec.ts: -------------------------------------------------------------------------------- 1 | import * as RpcServer from 'http-jsonrpc-server'; 2 | import { RpcClientOptions } from '../src/interfaces'; 3 | import { RpcClient } from '../src/rpc-client'; 4 | import { RpcError } from '../src/rpc-error'; 5 | import { RpcResponseError } from '../src/interfaces'; 6 | import { RpcErrorCode } from '../src/rpc-error-codes.enum'; 7 | 8 | describe('RpcClient', () => { 9 | // A scheme describe our rpc. 10 | interface MyRpcMethods { 11 | // sum is a rpc method that accepts 2 args both of type number. 12 | sum: [number, number]; 13 | sumWithReturnType: ({ x, y }: { x: number; y: number }) => number; 14 | sumNamedParams: { 15 | x: number; 16 | y: number; 17 | }; 18 | noMethod: unknown; 19 | invalidParms: string; 20 | serverError: undefined; 21 | } 22 | let rpcClient: RpcClient; 23 | let rpcServer: any; 24 | 25 | beforeAll(async () => { 26 | const sum = ([x, y]: [number, number]) => x + y; 27 | const sumNamedParams = ({ x, y }: { x: number; y: number }) => x + y; 28 | const sumWithReturnType = sumNamedParams; 29 | const invalidParms = s => s.length; 30 | const serverError = () => { 31 | throw new Error('Server Error'); 32 | }; 33 | 34 | rpcServer = new RpcServer({ 35 | methods: { 36 | sum, 37 | sumNamedParams, 38 | sumWithReturnType, 39 | invalidParms, 40 | serverError 41 | }, 42 | }); 43 | await rpcServer.listen(9090, 'localhost'); 44 | }); 45 | 46 | beforeEach(async () => { 47 | const opts: RpcClientOptions = { 48 | url: 'http://localhost:9090/', 49 | headers: { 50 | 'content-type': 'application/json', 51 | accept: 'application/json', 52 | }, 53 | }; 54 | rpcClient = new RpcClient(opts); 55 | }); 56 | 57 | afterAll(async () => { 58 | // Close the server. 59 | await rpcServer.close(); 60 | }); 61 | 62 | it('should make request', async () => { 63 | const { 64 | data: { result }, 65 | } = await rpcClient.makeRequest({ 66 | method: 'sumWithReturnType', 67 | params: { x: 3, y: 2 }, 68 | id: 123, 69 | jsonrpc: '2.0', 70 | }); 71 | expect(result).toEqual(5); 72 | }); 73 | 74 | it('should make request with named params', async () => { 75 | const { 76 | data: { result }, 77 | } = await rpcClient.makeRequest({ 78 | method: 'sumNamedParams', 79 | params: { x: 1, y: 4 }, 80 | id: 123, 81 | jsonrpc: '2.0', 82 | }); 83 | expect(result).toEqual(5); 84 | }); 85 | 86 | it('should make request', async () => { 87 | const { 88 | data: { result }, 89 | } = await rpcClient.makeRequest({ 90 | method: 'sum', 91 | params: [3, 2], 92 | id: 123, 93 | jsonrpc: '2.0', 94 | }); 95 | expect(result).toEqual(5); 96 | }); 97 | 98 | it('should throw error `method not found`', async () => { 99 | const call = rpcClient.makeRequest({ 100 | method: 'noMethod', 101 | id: 1, 102 | jsonrpc: '2.0', 103 | }); 104 | await expect(call).rejects.toThrow(); 105 | await expect(call).rejects.toBeInstanceOf(RpcError); 106 | await expect(call).rejects.toHaveProperty('err', { 107 | code: RpcErrorCode.METHOD_NOT_FOUND, 108 | }); 109 | }); 110 | 111 | // I'm not sure of this test, althogth it passes ! 112 | it('should throw error `invalid params`', async () => { 113 | const call = rpcClient.makeRequest({ 114 | method: 'invalidParms', 115 | params: 1 as any, 116 | id: 12, 117 | jsonrpc: '2.0', 118 | }); 119 | await expect(call).rejects.toThrow(); 120 | await expect(call).rejects.toBeInstanceOf(RpcError); 121 | await expect(call).rejects.toHaveProperty('err', { 122 | code: RpcErrorCode.INVALID_PARAMS, 123 | }); 124 | }); 125 | 126 | it('should throw error `server error` with message', async () => { 127 | const call = rpcClient.makeRequest({ 128 | method: 'serverError', 129 | id: 1232, 130 | jsonrpc: '2.0', 131 | }); 132 | await expect(call).rejects.toThrow(); 133 | await expect(call).rejects.toBeInstanceOf(RpcError); 134 | await expect(call).rejects.toHaveProperty('err', { 135 | code: RpcErrorCode.SERVER_ERROR, 136 | message: 'Server Error', 137 | }); 138 | try { 139 | await call; 140 | } catch (error) { 141 | expect(error.getCode()).toEqual(RpcErrorCode.SERVER_ERROR); 142 | } 143 | }); 144 | 145 | // For this particular test we can't use the RpcServer instance because 146 | // it doesn't expose a way to provoke an rpcError with data. 147 | it('should expose a data property on RpcError', async () => { 148 | const data = {test: "test"} 149 | const rpcError: RpcResponseError = { 150 | code: RpcErrorCode.SERVER_ERROR, 151 | message: 'Server Error', 152 | data: data 153 | } 154 | const error = new RpcError(rpcError) 155 | expect(error.getData()).toEqual(data); 156 | }); 157 | 158 | it('should make batch requests', async () => { 159 | const { data } = await rpcClient.makeBatchRequest([ 160 | { 161 | method: 'sum', 162 | params: [3, 1], 163 | id: 123, 164 | jsonrpc: '2.0', 165 | }, 166 | { 167 | method: 'sum', 168 | params: [3, 2], 169 | id: 124, 170 | jsonrpc: '2.0', 171 | }, 172 | ]); 173 | const [res1, res2] = data; 174 | expect(res1.result).toEqual(4); 175 | expect(res2.result).toEqual(5); 176 | }); 177 | }); 178 | --------------------------------------------------------------------------------