jsonrpc-client-websocket 1.4.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,20 @@
1
- [![Travis build status](https://travis-ci.com/fabriciobastian/jsonrpc-client-websocket.svg?branch=master)](https://travis-ci.com/fabriciobastian/jsonrpc-client-websocket) [![License](https://img.shields.io/github/license/fabriciobastian/jsonrpc-client-websocket)](https://choosealicense.com/licenses/mit/) [![NPM downloads](https://img.shields.io/npm/dt/jsonrpc-client-websocket.svg)](https://www.npmjs.com/package/jsonrpc-client-websocket)
1
+ [![Travis build status](https://travis-ci.com/fabriciobastian/jsonrpc-client-websocket.svg?branch=master)](https://travis-ci.com/fabriciobastian/jsonrpc-client-websocket) [![License](https://img.shields.io/github/license/fabriciobastian/jsonrpc-client-websocket)](https://choosealicense.com/licenses/mit/) [![NPM downloads](https://img.shields.io/npm/dt/jsonrpc-client-websocket.svg)](https://www.npmjs.com/package/jsonrpc-client-websocket)
2
2
 
3
- # jsonrpc-client-websocket
3
+ ![jsonrpc-client-websocket](https://github.com/fabriciobastian/jsonrpc-client-websocket/blob/master/assets/jsonrpc-client-websocket-banner-light-small.png?raw=true)
4
4
 
5
5
  A simple JSON RPC 2.0 client over websockets
6
6
 
7
7
  ## Open connection
8
8
 
9
9
  ```typescript
10
- const websocketUrl = "ws://mywebsocketurl:port"
10
+ const websocketUrl = 'ws://mywebsocketurl:port';
11
11
  const requestTimeoutMs = 2000;
12
- const websocket = new JsonRpcWebsocket(
13
- websocketUrl,
14
- requestTimeoutMs,
15
- (error: JsonRpcError) => { /* handle error */ });
12
+ const websocket = new JsonRpcWebsocket(websocketUrl, requestTimeoutMs, (error: JsonRpcError) => {
13
+ /* handle error */
14
+ });
16
15
  await websocket.open();
17
16
  ```
17
+
18
18
  Requests that do not receive a response within the specified timeout will fail with a REQUEST_TIMEOUT code.
19
19
  The callback (optional) is used for eventual errors, such as receiving a response that does not match any request id and
20
20
  connection errors. Furthermore, all errors that are sent to an eventual caller are also reported on the callback, e.g.
@@ -33,25 +33,25 @@ Considering that the server has a method `sum(a: int, b: int)`
33
33
  ### with positional parameters
34
34
 
35
35
  ```typescript
36
- websocket.call('sum', [1,2])
37
- .then((response) => {
38
- // handle response
39
- })
40
- .catch((error) => {
41
- // handle error
42
- });
36
+ websocket.call('sum', [1, 2])
37
+ .then((response) => {
38
+ // handle response
39
+ })
40
+ .catch((error) => {
41
+ // handle error
42
+ });
43
43
  ```
44
44
 
45
45
  ### with named parameters
46
46
 
47
47
  ```typescript
48
- websocket.call('sum', {b: 1, a: 2})
49
- .then((response) => {
50
- // handle response
51
- })
52
- .catch((error) => {
53
- // handle error
54
- });
48
+ websocket.call('sum', { b: 1, a: 2 })
49
+ .then((response) => {
50
+ // handle response
51
+ })
52
+ .catch((error) => {
53
+ // handle error
54
+ });
55
55
  ```
56
56
 
57
57
  ## Send notification
@@ -66,12 +66,41 @@ websocket.notify('log', ['a log message']);
66
66
 
67
67
  ```typescript
68
68
  websocket.on('sum', (a: number, b: number) => {
69
- return a + b;
69
+ return a + b;
70
70
  });
71
71
  ```
72
+
72
73
  The defined RPC methods can also be called with both positional and named parameters.
73
74
 
75
+ ### Minification/Obfuscation
76
+
77
+ When using code minification/obfuscation the parameter names may change, which can
78
+ cause calls using named parameters to fail. To mitigate this you can do one or a
79
+ combination of the following:
80
+ - do not minify/obfuscate the code where `on` is used, so the function registration does not get modified
81
+ - use positional parameters
82
+ - register functions as following:
83
+
84
+ ```typescript
85
+ websocket.on('sum', (params: { a: number; b: number }) => {
86
+ return params.a + params.b;
87
+ });
88
+ ```
89
+
90
+ and then call the function using positional parameters using the object:
91
+
92
+ ```typescript
93
+ websocket.call('sum', [{ a: 1, b: 2 }])
94
+ .then((response) => {
95
+ // handle response
96
+ })
97
+ .catch((error) => {
98
+ // handle error
99
+ });
100
+ ```
101
+
74
102
  ## Remove RPC method
103
+
75
104
  ```typescript
76
105
  websocket.off('sum');
77
106
  ```
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DeferredPromise = void 0;
4
- class DeferredPromise {
1
+ export class DeferredPromise {
5
2
  constructor() {
6
3
  this.promise = new Promise((resolve, reject) => {
7
4
  this.deferResolve = resolve;
@@ -18,4 +15,3 @@ class DeferredPromise {
18
15
  this.deferReject(error);
19
16
  }
20
17
  }
21
- exports.DeferredPromise = DeferredPromise;
package/lib/index.js CHANGED
@@ -1,14 +1,2 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- __exportStar(require("./jsonrpc-websocket"), exports);
14
- __exportStar(require("./jsonrpc.model"), exports);
1
+ export * from './jsonrpc-websocket';
2
+ export * from './jsonrpc.model';
@@ -22,8 +22,8 @@ export declare class JsonRpcWebsocket {
22
22
  close(): Promise<CloseEvent>;
23
23
  on(methodName: string, callback: (...args: any[]) => any): void;
24
24
  off(methodName: string): void;
25
- call(method: string, params?: any): Promise<JsonRpcResponse>;
26
- notify(method: string, params?: any): void;
25
+ call(method: string, params?: unknown): Promise<JsonRpcResponse>;
26
+ notify(method: string, params?: unknown): void;
27
27
  private createWebsocket;
28
28
  private respondOk;
29
29
  private respondError;
@@ -1,17 +1,14 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JsonRpcWebsocket = exports.WebsocketReadyStates = void 0;
4
- const getParameterNames = require("get-parameter-names");
5
- const deferred_promise_1 = require("./deferred-promise");
6
- const jsonrpc_model_1 = require("./jsonrpc.model");
7
- var WebsocketReadyStates;
1
+ import * as getParameterNames from 'get-parameter-names';
2
+ import { DeferredPromise } from './deferred-promise';
3
+ import { JsonRpcErrorCodes } from './jsonrpc.model';
4
+ export var WebsocketReadyStates;
8
5
  (function (WebsocketReadyStates) {
9
6
  WebsocketReadyStates[WebsocketReadyStates["CONNECTING"] = 0] = "CONNECTING";
10
7
  WebsocketReadyStates[WebsocketReadyStates["OPEN"] = 1] = "OPEN";
11
8
  WebsocketReadyStates[WebsocketReadyStates["CLOSING"] = 2] = "CLOSING";
12
9
  WebsocketReadyStates[WebsocketReadyStates["CLOSED"] = 3] = "CLOSED";
13
- })(WebsocketReadyStates = exports.WebsocketReadyStates || (exports.WebsocketReadyStates = {}));
14
- class JsonRpcWebsocket {
10
+ })(WebsocketReadyStates || (WebsocketReadyStates = {}));
11
+ export class JsonRpcWebsocket {
15
12
  constructor(url, requestTimeoutMs, onError) {
16
13
  this.url = url;
17
14
  this.requestTimeoutMs = requestTimeoutMs;
@@ -45,10 +42,9 @@ class JsonRpcWebsocket {
45
42
  off(methodName) {
46
43
  delete this.rpcMethods[methodName.toLowerCase()]; // case-insensitive!
47
44
  }
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
45
  call(method, params) {
50
46
  if (!this.websocket || this.state !== WebsocketReadyStates.OPEN) {
51
- return Promise.reject({ code: jsonrpc_model_1.JsonRpcErrorCodes.INTERNAL_ERROR, message: 'The websocket is not opened' });
47
+ return Promise.reject({ code: JsonRpcErrorCodes.INTERNAL_ERROR, message: 'The websocket is not opened' });
52
48
  }
53
49
  const request = {
54
50
  id: this.getRequestId(),
@@ -61,11 +57,10 @@ class JsonRpcWebsocket {
61
57
  }
62
58
  catch (e) {
63
59
  // istanbul ignore next
64
- return Promise.reject({ code: jsonrpc_model_1.JsonRpcErrorCodes.INTERNAL_ERROR, message: `Internal error. ${e}` });
60
+ return Promise.reject({ code: JsonRpcErrorCodes.INTERNAL_ERROR, message: `Internal error. ${e}` });
65
61
  }
66
62
  return this.createPendingRequest(request);
67
63
  }
68
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
64
  notify(method, params) {
70
65
  if (!this.websocket || this.state !== WebsocketReadyStates.OPEN) {
71
66
  throw new Error('The websocket is not opened');
@@ -87,8 +82,8 @@ class JsonRpcWebsocket {
87
82
  // See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent for close event codes
88
83
  // this.websocket = new WebSocket(this.url, ['jsonrpc-2.0']);
89
84
  this.websocket = new WebSocket(this.url);
90
- const openDeferredPromise = new deferred_promise_1.DeferredPromise();
91
- this.closeDeferredPromise = new deferred_promise_1.DeferredPromise();
85
+ const openDeferredPromise = new DeferredPromise();
86
+ this.closeDeferredPromise = new DeferredPromise();
92
87
  this.websocket.onopen = (event) => {
93
88
  openDeferredPromise.resolve(event);
94
89
  };
@@ -111,14 +106,12 @@ class JsonRpcWebsocket {
111
106
  this.websocket.onmessage = (message) => this.handleMessage(message.data);
112
107
  return openDeferredPromise.asPromise();
113
108
  }
114
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
109
  respondOk(id, result) {
116
110
  this.respond(id, result);
117
111
  }
118
112
  respondError(id, error) {
119
113
  this.respond(id, undefined, error);
120
114
  }
121
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
115
  respond(id, result, error) {
123
116
  // istanbul ignore if
124
117
  if (!this.websocket || this.state !== WebsocketReadyStates.OPEN) {
@@ -132,7 +125,7 @@ class JsonRpcWebsocket {
132
125
  jsonrpc: this.jsonRpcVersion,
133
126
  error: error,
134
127
  id: id,
135
- result: result
128
+ result: result,
136
129
  };
137
130
  try {
138
131
  this.websocket.send(JSON.stringify(response));
@@ -148,14 +141,14 @@ class JsonRpcWebsocket {
148
141
  data = JSON.parse(msg);
149
142
  }
150
143
  catch (e) /* istanbul ignore next */ {
151
- this.handleError(jsonrpc_model_1.JsonRpcErrorCodes.PARSE_ERROR, `Invalid JSON was received. ${e}`);
144
+ this.handleError(JsonRpcErrorCodes.PARSE_ERROR, `Invalid JSON was received. ${e}`);
152
145
  return;
153
146
  }
154
147
  const isResponse = !!data && this.hasProperty(data, 'id') && (this.hasProperty(data, 'result') || this.hasProperty(data, 'error'));
155
148
  const isRequest = !!data && this.hasProperty(data, 'method');
156
149
  const requestId = isRequest && data.id ? data.id : undefined;
157
150
  if (!data.jsonrpc || data.jsonrpc !== this.jsonRpcVersion) {
158
- this.handleError(jsonrpc_model_1.JsonRpcErrorCodes.INVALID_REQUEST, `Invalid JSON RPC protocol version. Expecting ${this.jsonRpcVersion}, but got ${data.jsonrpc}`, requestId);
151
+ this.handleError(JsonRpcErrorCodes.INVALID_REQUEST, `Invalid JSON RPC protocol version. Expecting ${this.jsonRpcVersion}, but got ${data.jsonrpc}`, requestId);
159
152
  return;
160
153
  }
161
154
  if (isResponse) {
@@ -165,13 +158,13 @@ class JsonRpcWebsocket {
165
158
  this.handleRequest(data);
166
159
  }
167
160
  else {
168
- this.handleError(jsonrpc_model_1.JsonRpcErrorCodes.INVALID_REQUEST, `Received unknown data: ${JSON.stringify(data)}`, requestId);
161
+ this.handleError(JsonRpcErrorCodes.INVALID_REQUEST, `Received unknown data: ${JSON.stringify(data)}`, requestId);
169
162
  }
170
163
  }
171
164
  handleRequest(request) {
172
165
  const method = this.rpcMethods[request.method.toLowerCase()]; // case-insensitive!
173
166
  if (!method) {
174
- this.handleError(jsonrpc_model_1.JsonRpcErrorCodes.METHOD_NOT_FOUND, `Method '${request.method}' was not found`, request.id);
167
+ this.handleError(JsonRpcErrorCodes.METHOD_NOT_FOUND, `Method '${request.method}' was not found`, request.id);
175
168
  return;
176
169
  }
177
170
  let requestParams = [];
@@ -179,12 +172,17 @@ class JsonRpcWebsocket {
179
172
  requestParams = this.getRequestParams(method, request);
180
173
  }
181
174
  catch (error) {
182
- this.handleError(jsonrpc_model_1.JsonRpcErrorCodes.INVALID_PARAMS, error.message, request.id);
175
+ this.handleError(JsonRpcErrorCodes.INVALID_PARAMS, error.message, request.id);
183
176
  return;
184
177
  }
185
- const result = method(...requestParams);
186
- if (request.id) {
187
- this.respondOk(request.id, result);
178
+ try {
179
+ const result = method(...requestParams);
180
+ if (request.id) {
181
+ this.respondOk(request.id, result);
182
+ }
183
+ }
184
+ catch (error) {
185
+ this.handleError(JsonRpcErrorCodes.REQUEST_FAILED, `Method '${request.method}' has thrown: '${error.message}'`, request.id);
188
186
  }
189
187
  }
190
188
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -202,7 +200,7 @@ class JsonRpcWebsocket {
202
200
  if (method.length !== Object.keys(request.params).length) {
203
201
  throw new Error(`Invalid parameters. Method '${request.method}' expects parameters [${parameterNames}], but got [${Object.keys(request.params)}]`);
204
202
  }
205
- parameterNames.forEach(paramName => {
203
+ parameterNames.forEach((paramName) => {
206
204
  const paramValue = request.params[paramName];
207
205
  if (paramValue === undefined) {
208
206
  throw new Error(`Invalid parameters. Method '${request.method}' expects parameters [${parameterNames}], but got [${Object.keys(request.params)}]`);
@@ -220,7 +218,7 @@ class JsonRpcWebsocket {
220
218
  const activeRequest = this.pendingRequests[response.id];
221
219
  if (activeRequest === undefined) {
222
220
  this.callOnError({
223
- code: jsonrpc_model_1.JsonRpcErrorCodes.INTERNAL_ERROR,
221
+ code: JsonRpcErrorCodes.INTERNAL_ERROR,
224
222
  message: `Received a response with id ${response.id}, which does not match any requests made by this client`,
225
223
  });
226
224
  return;
@@ -229,7 +227,7 @@ class JsonRpcWebsocket {
229
227
  if (this.hasProperty(response, 'result') && this.hasProperty(response, 'error')) {
230
228
  const errorResponse = {
231
229
  error: {
232
- code: jsonrpc_model_1.JsonRpcErrorCodes.INVALID_RESPONSE,
230
+ code: JsonRpcErrorCodes.INVALID_RESPONSE,
233
231
  message: `Invalid response. Either result or error must be set, but not both. ${JSON.stringify(response)}`,
234
232
  },
235
233
  id: activeRequest.request.id,
@@ -253,7 +251,7 @@ class JsonRpcWebsocket {
253
251
  }
254
252
  }
255
253
  createPendingRequest(request) {
256
- const response = new deferred_promise_1.DeferredPromise();
254
+ const response = new DeferredPromise();
257
255
  this.pendingRequests[request.id] = {
258
256
  request,
259
257
  response,
@@ -270,7 +268,7 @@ class JsonRpcWebsocket {
270
268
  }
271
269
  const response = {
272
270
  error: {
273
- code: jsonrpc_model_1.JsonRpcErrorCodes.REQUEST_TIMEOUT,
271
+ code: JsonRpcErrorCodes.REQUEST_TIMEOUT,
274
272
  message: `Request ${activeRequest.request.id} exceeded the maximum time of ${this.requestTimeoutMs}ms and was aborted`,
275
273
  },
276
274
  id: activeRequest.request.id,
@@ -288,9 +286,7 @@ class JsonRpcWebsocket {
288
286
  getRequestId() {
289
287
  return ++this.requestId;
290
288
  }
291
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
292
289
  hasProperty(object, propertyName) {
293
290
  return !!Object.prototype.hasOwnProperty.call(object, propertyName);
294
291
  }
295
292
  }
296
- exports.JsonRpcWebsocket = JsonRpcWebsocket;
@@ -5,7 +5,8 @@ export declare enum JsonRpcErrorCodes {
5
5
  INVALID_PARAMS = -32602,
6
6
  INTERNAL_ERROR = -32603,
7
7
  INVALID_RESPONSE = -32001,
8
- REQUEST_TIMEOUT = -32002
8
+ REQUEST_TIMEOUT = -32002,
9
+ REQUEST_FAILED = -32003
9
10
  }
10
11
  export declare class JsonRpcError {
11
12
  code: number;
@@ -1,9 +1,6 @@
1
- "use strict";
2
1
  /* tslint:disable:max-classes-per-file */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.JsonRpcRequest = exports.JsonRpcResponse = exports.JsonRpcError = exports.JsonRpcErrorCodes = void 0;
5
2
  // See https://www.jsonrpc.org/specification for the protocol details
6
- var JsonRpcErrorCodes;
3
+ export var JsonRpcErrorCodes;
7
4
  (function (JsonRpcErrorCodes) {
8
5
  JsonRpcErrorCodes[JsonRpcErrorCodes["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
9
6
  JsonRpcErrorCodes[JsonRpcErrorCodes["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
@@ -13,13 +10,11 @@ var JsonRpcErrorCodes;
13
10
  // App specific
14
11
  JsonRpcErrorCodes[JsonRpcErrorCodes["INVALID_RESPONSE"] = -32001] = "INVALID_RESPONSE";
15
12
  JsonRpcErrorCodes[JsonRpcErrorCodes["REQUEST_TIMEOUT"] = -32002] = "REQUEST_TIMEOUT";
16
- })(JsonRpcErrorCodes = exports.JsonRpcErrorCodes || (exports.JsonRpcErrorCodes = {}));
17
- class JsonRpcError {
13
+ JsonRpcErrorCodes[JsonRpcErrorCodes["REQUEST_FAILED"] = -32003] = "REQUEST_FAILED";
14
+ })(JsonRpcErrorCodes || (JsonRpcErrorCodes = {}));
15
+ export class JsonRpcError {
18
16
  }
19
- exports.JsonRpcError = JsonRpcError;
20
- class JsonRpcResponse {
17
+ export class JsonRpcResponse {
21
18
  }
22
- exports.JsonRpcResponse = JsonRpcResponse;
23
- class JsonRpcRequest {
19
+ export class JsonRpcRequest {
24
20
  }
25
- exports.JsonRpcRequest = JsonRpcRequest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsonrpc-client-websocket",
3
- "version": "1.4.0",
3
+ "version": "1.5.2",
4
4
  "description": "A simple JSON RPC 2.0 websocket client",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -8,16 +8,17 @@
8
8
  "lib/**/*"
9
9
  ],
10
10
  "scripts": {
11
- "test": "jest --config jestconfig.json",
12
- "coverage": "jest --config jestconfig.json --coverage",
11
+ "test": "jest",
12
+ "coverage": "jest --config jest.config.ts --coverage",
13
13
  "build": "tsc",
14
- "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
14
+ "format": "prettier --write \"src/**/*.ts\"",
15
15
  "lint": "tsc --noEmit && eslint . --ext .js,.jsx,.ts,.tsx --fix",
16
16
  "prepublishOnly": "npm test && npm run lint",
17
17
  "prepare": "npm run build",
18
18
  "preversion": "npm run lint",
19
19
  "version": "npm run format && git add -A src",
20
- "postversion": "git push && git push --tags"
20
+ "postversion": "git push && git push --tags",
21
+ "updateAll": "npm-check-updates --upgrade"
21
22
  },
22
23
  "repository": {
23
24
  "type": "git",
@@ -37,18 +38,21 @@
37
38
  "author": "Fabricio Bastian",
38
39
  "license": "MIT",
39
40
  "devDependencies": {
40
- "@types/jest": "^24.9.1",
41
- "@typescript-eslint/eslint-plugin": "^2.34.0",
42
- "@typescript-eslint/parser": "^2.34.0",
43
- "eslint": "^6.8.0",
44
- "eslint-config-prettier": "^6.11.0",
45
- "eslint-plugin-jest": "^23.20.0",
46
- "jest": "^24.9.0",
47
- "jest-websocket-mock": "^1.5.1",
48
- "mock-socket": "^8.1.1",
49
- "prettier": "^1.19.1",
50
- "ts-jest": "^24.3.0",
51
- "typescript": "^3.9.7"
41
+ "@types/jest": "^28.1.6",
42
+ "@typescript-eslint/eslint-plugin": "^5.31.0",
43
+ "@typescript-eslint/parser": "^5.31.0",
44
+ "eslint": "^8.20.0",
45
+ "eslint-config-prettier": "^8.5.0",
46
+ "eslint-plugin-jest": "^26.6.0",
47
+ "jest": "^28.1.3",
48
+ "jest-environment-jsdom": "^28.1.3",
49
+ "jest-websocket-mock": "^2.3.0",
50
+ "mock-socket": "^9.1.5",
51
+ "npm-check-updates": "^16.0.1",
52
+ "prettier": "^2.7.1",
53
+ "ts-jest": "^28.0.7",
54
+ "ts-node": "^10.9.1",
55
+ "typescript": "^4.7.4"
52
56
  },
53
57
  "dependencies": {
54
58
  "get-parameter-names": "^0.3.0"