jsonrpc-client-websocket 1.5.5 → 1.6.0
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 +46 -13
- package/lib/get-parameter-names.d.ts +1 -0
- package/lib/get-parameter-names.js +5 -0
- package/lib/jsonrpc-websocket.d.ts +5 -1
- package/lib/jsonrpc-websocket.js +60 -35
- package/package.json +18 -23
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://github.com/fabriciobastian/jsonrpc-client-websocket/actions/workflows/ci.yml) [](https://choosealicense.com/licenses/mit/) [](https://www.npmjs.com/package/jsonrpc-client-websocket)
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
@@ -74,33 +74,66 @@ The defined RPC methods can also be called with both positional and named parame
|
|
|
74
74
|
|
|
75
75
|
### Minification/Obfuscation
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
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:
|
|
77
|
+
For code that will be minified or obfuscated, pass the public parameter names as
|
|
78
|
+
the optional third argument to `on`:
|
|
83
79
|
|
|
84
80
|
```typescript
|
|
85
|
-
websocket.on('
|
|
86
|
-
return params.a + params.b;
|
|
87
|
-
});
|
|
81
|
+
websocket.on('subtract', (x: number, y: number) => x - y, ['a', 'b']);
|
|
88
82
|
```
|
|
89
83
|
|
|
90
|
-
|
|
84
|
+
The list follows the callback's argument order: `a` maps to `x`, and `b` maps to `y`.
|
|
85
|
+
The order of keys in the request object does not matter:
|
|
91
86
|
|
|
92
87
|
```typescript
|
|
93
|
-
websocket.call('
|
|
88
|
+
websocket.call('subtract', { b: 2, a: 5 })
|
|
94
89
|
.then((response) => {
|
|
95
|
-
//
|
|
90
|
+
// response.result is 3
|
|
96
91
|
})
|
|
97
92
|
.catch((error) => {
|
|
98
93
|
// handle error
|
|
99
94
|
});
|
|
100
95
|
```
|
|
101
96
|
|
|
97
|
+
Positional calls such as `websocket.call('subtract', [5, 2])` also work. The explicit
|
|
98
|
+
list defines both the parameter order and the required argument count, even if
|
|
99
|
+
minification renames or removes unused callback parameters. Names must be unique;
|
|
100
|
+
use an empty list for a method that takes no arguments. The list is copied when
|
|
101
|
+
the method is registered.
|
|
102
|
+
|
|
103
|
+
Without the third argument, existing registrations continue to infer named
|
|
104
|
+
parameters from the callback's source. Those names may change during minification
|
|
105
|
+
or obfuscation, so use explicit names for named calls in production bundles.
|
|
106
|
+
If property mangling is enabled, preserve the public keys in request objects too.
|
|
107
|
+
|
|
102
108
|
## Remove RPC method
|
|
103
109
|
|
|
104
110
|
```typescript
|
|
105
111
|
websocket.off('sum');
|
|
106
112
|
```
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
Use the Node.js version in `.nvmrc` and its bundled npm, then run:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm ci
|
|
120
|
+
npm test
|
|
121
|
+
npm run lint
|
|
122
|
+
npm run format:check
|
|
123
|
+
npm run build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
TypeScript checks both the library and tests. ESLint uses native flat configuration
|
|
127
|
+
with TypeScript and Jest rules. TypeScript stays on the latest 6.0 patch supported
|
|
128
|
+
by `typescript-eslint`; upgrading to TypeScript 7 requires its official support.
|
|
129
|
+
Tests live alongside the source and are excluded from build output.
|
|
130
|
+
`npm run update-all` respects peer dependency requirements when selecting updates.
|
|
131
|
+
|
|
132
|
+
These tooling requirements apply to development only. The published library still
|
|
133
|
+
targets ES2020 and has no runtime dependencies.
|
|
134
|
+
|
|
135
|
+
## Publishing
|
|
136
|
+
|
|
137
|
+
Releases are staged through GitHub Actions using
|
|
138
|
+
[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) (OIDC).
|
|
139
|
+
A maintainer approves each staged package on npm before it becomes publicly available.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getParameterNames(callback: (...args: never[]) => unknown): string[];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function getParameterNames(callback) {
|
|
2
|
+
const source = Function.prototype.toString.call(callback).replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, ' ');
|
|
3
|
+
const parameters = source.match(/^(?:async\s+)?([^\s()=]+)\s*=>/)?.[1] ?? source.slice(source.indexOf('(') + 1, source.indexOf(')'));
|
|
4
|
+
return parameters.replace(/=[^,]+/g, '').match(/[^\s,]+/g) ?? [];
|
|
5
|
+
}
|
|
@@ -20,7 +20,11 @@ export declare class JsonRpcWebsocket {
|
|
|
20
20
|
constructor(url: string, requestTimeoutMs: number, onError?: ErrorCallback | undefined);
|
|
21
21
|
open(): Promise<Event>;
|
|
22
22
|
close(): Promise<CloseEvent>;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Registers a callback, optionally declaring its public parameter names in argument order.
|
|
25
|
+
* Explicit names also determine the expected argument count, independently of the callback's signature.
|
|
26
|
+
*/
|
|
27
|
+
on(methodName: string, callback: (...args: any[]) => any, parameterNames?: readonly string[]): void;
|
|
24
28
|
off(methodName: string): void;
|
|
25
29
|
call(method: string, params?: unknown): Promise<JsonRpcResponse>;
|
|
26
30
|
notify(method: string, params?: unknown): void;
|
package/lib/jsonrpc-websocket.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import getParameterNames from 'get-parameter-names';
|
|
1
|
+
import { getParameterNames } from './get-parameter-names';
|
|
2
2
|
import { DeferredPromise } from './deferred-promise';
|
|
3
3
|
import { JsonRpcErrorCodes } from './jsonrpc.model';
|
|
4
4
|
export var WebsocketReadyStates;
|
|
@@ -35,9 +35,20 @@ export class JsonRpcWebsocket {
|
|
|
35
35
|
this.websocket = undefined;
|
|
36
36
|
return this.closeDeferredPromise.asPromise();
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Registers a callback, optionally declaring its public parameter names in argument order.
|
|
40
|
+
* Explicit names also determine the expected argument count, independently of the callback's signature.
|
|
41
|
+
*/
|
|
42
|
+
on(methodName,
|
|
38
43
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
callback, parameterNames) {
|
|
45
|
+
if (parameterNames && new Set(parameterNames).size !== parameterNames.length) {
|
|
46
|
+
throw new Error('Parameter names must be unique');
|
|
47
|
+
}
|
|
48
|
+
this.rpcMethods[methodName.toLowerCase()] = {
|
|
49
|
+
callback,
|
|
50
|
+
parameterNames: parameterNames?.slice(),
|
|
51
|
+
}; // case-insensitive!
|
|
41
52
|
}
|
|
42
53
|
off(methodName) {
|
|
43
54
|
delete this.rpcMethods[methodName.toLowerCase()]; // case-insensitive!
|
|
@@ -77,8 +88,7 @@ export class JsonRpcWebsocket {
|
|
|
77
88
|
this.websocket.send(JSON.stringify(request));
|
|
78
89
|
}
|
|
79
90
|
catch (e) {
|
|
80
|
-
|
|
81
|
-
throw Error(this.getErrorMessage(e));
|
|
91
|
+
throw Error(this.getErrorMessage(e), { cause: e });
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
94
|
createWebsocket() {
|
|
@@ -134,12 +144,11 @@ export class JsonRpcWebsocket {
|
|
|
134
144
|
this.websocket.send(JSON.stringify(response));
|
|
135
145
|
}
|
|
136
146
|
catch (e) {
|
|
137
|
-
|
|
138
|
-
throw Error(this.getErrorMessage(e));
|
|
147
|
+
throw Error(this.getErrorMessage(e), { cause: e });
|
|
139
148
|
}
|
|
140
149
|
}
|
|
141
150
|
handleMessage(msg) {
|
|
142
|
-
let data
|
|
151
|
+
let data;
|
|
143
152
|
try {
|
|
144
153
|
data = JSON.parse(msg);
|
|
145
154
|
}
|
|
@@ -170,7 +179,7 @@ export class JsonRpcWebsocket {
|
|
|
170
179
|
this.handleError(JsonRpcErrorCodes.METHOD_NOT_FOUND, `Method '${request.method}' was not found`, request.id);
|
|
171
180
|
return;
|
|
172
181
|
}
|
|
173
|
-
let requestParams
|
|
182
|
+
let requestParams;
|
|
174
183
|
try {
|
|
175
184
|
requestParams = this.getRequestParams(method, request);
|
|
176
185
|
}
|
|
@@ -179,7 +188,8 @@ export class JsonRpcWebsocket {
|
|
|
179
188
|
return;
|
|
180
189
|
}
|
|
181
190
|
try {
|
|
182
|
-
const
|
|
191
|
+
const { callback } = method;
|
|
192
|
+
const result = callback(...requestParams);
|
|
183
193
|
if (request.id) {
|
|
184
194
|
this.respondOk(request.id, result);
|
|
185
195
|
}
|
|
@@ -190,32 +200,32 @@ export class JsonRpcWebsocket {
|
|
|
190
200
|
}
|
|
191
201
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
192
202
|
getRequestParams(method, request) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
else if (request.params instanceof Object) {
|
|
202
|
-
const parameterNames = getParameterNames(method);
|
|
203
|
-
if (method.length !== Object.keys(request.params).length) {
|
|
204
|
-
throw new Error(`Invalid parameters. Method '${request.method}' expects parameters [${parameterNames}], but got [${Object.keys(request.params)}]`);
|
|
205
|
-
}
|
|
206
|
-
parameterNames.forEach((paramName) => {
|
|
207
|
-
const paramValue = request.params[paramName];
|
|
208
|
-
if (paramValue === undefined) {
|
|
209
|
-
throw new Error(`Invalid parameters. Method '${request.method}' expects parameters [${parameterNames}], but got [${Object.keys(request.params)}]`);
|
|
210
|
-
}
|
|
211
|
-
requestParams.push(paramValue);
|
|
212
|
-
});
|
|
203
|
+
if (!request.params && method.parameterNames === undefined) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
const params = request.params === undefined ? [] : request.params;
|
|
207
|
+
const parameterCount = method.parameterNames?.length ?? method.callback.length;
|
|
208
|
+
if (params instanceof Array) {
|
|
209
|
+
if (parameterCount !== params.length) {
|
|
210
|
+
throw new Error(`Invalid parameters. Method '${request.method}' expects ${parameterCount} parameters, but got ${params.length}`);
|
|
213
211
|
}
|
|
214
|
-
|
|
215
|
-
|
|
212
|
+
return params;
|
|
213
|
+
}
|
|
214
|
+
if (params instanceof Object) {
|
|
215
|
+
const parameterNames = method.parameterNames ?? getParameterNames(method.callback);
|
|
216
|
+
const requestParameterNames = Object.keys(params);
|
|
217
|
+
const invalidParametersMessage = `Invalid parameters. Method '${request.method}' expects parameters [${parameterNames}], but got [${requestParameterNames}]`;
|
|
218
|
+
if (parameterCount !== requestParameterNames.length) {
|
|
219
|
+
throw new Error(invalidParametersMessage);
|
|
216
220
|
}
|
|
221
|
+
return parameterNames.map((paramName) => {
|
|
222
|
+
if (!this.hasProperty(params, paramName)) {
|
|
223
|
+
throw new Error(invalidParametersMessage);
|
|
224
|
+
}
|
|
225
|
+
return params[paramName];
|
|
226
|
+
});
|
|
217
227
|
}
|
|
218
|
-
|
|
228
|
+
throw new Error(`Invalid parameters. Expected array or object, but got ${typeof params}`);
|
|
219
229
|
}
|
|
220
230
|
handleResponse(response) {
|
|
221
231
|
const activeRequest = this.pendingRequests[response.id];
|
|
@@ -293,7 +303,22 @@ export class JsonRpcWebsocket {
|
|
|
293
303
|
return !!Object.prototype.hasOwnProperty.call(object, propertyName);
|
|
294
304
|
}
|
|
295
305
|
getErrorMessage(error) {
|
|
296
|
-
|
|
297
|
-
|
|
306
|
+
try {
|
|
307
|
+
if (typeof error === 'object' && error !== null && 'message' in error) {
|
|
308
|
+
const message = error.message;
|
|
309
|
+
if (typeof message === 'string') {
|
|
310
|
+
return message;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return JSON.stringify(error) ?? String(error);
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
try {
|
|
317
|
+
return String(error);
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return 'Unknown error';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
298
323
|
}
|
|
299
324
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsonrpc-client-websocket",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
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,17 +8,18 @@
|
|
|
8
8
|
"lib/**/*"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "jest",
|
|
12
|
-
"coverage": "
|
|
11
|
+
"test": "tsc --project tsconfig.spec.json && jest",
|
|
12
|
+
"coverage": "npm test -- --coverage",
|
|
13
13
|
"build": "tsc",
|
|
14
14
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
15
|
-
"
|
|
15
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
16
|
+
"lint": "tsc --noEmit && eslint .",
|
|
16
17
|
"prepublishOnly": "npm test && npm run lint",
|
|
17
18
|
"prepare": "npm run build",
|
|
18
19
|
"preversion": "npm run lint",
|
|
19
20
|
"version": "npm run format && git add -A src",
|
|
20
21
|
"postversion": "git push && git push --tags",
|
|
21
|
-
"update-all": "npm-check-updates --upgrade"
|
|
22
|
+
"update-all": "npm-check-updates --upgrade --peer"
|
|
22
23
|
},
|
|
23
24
|
"repository": {
|
|
24
25
|
"type": "git",
|
|
@@ -38,25 +39,19 @@
|
|
|
38
39
|
"author": "Fabricio Bastian",
|
|
39
40
|
"license": "MIT",
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@eslint/
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"eslint-plugin-jest": "^28.8.0",
|
|
49
|
-
"jest": "^29.7.0",
|
|
50
|
-
"jest-environment-jsdom": "^29.7.0",
|
|
42
|
+
"@eslint/js": "^10.0.1",
|
|
43
|
+
"@types/jest": "^30.0.0",
|
|
44
|
+
"eslint": "^10.14.0",
|
|
45
|
+
"eslint-config-prettier": "^10.1.8",
|
|
46
|
+
"eslint-plugin-jest": "^29.17.0",
|
|
47
|
+
"jest": "^30.5.2",
|
|
48
|
+
"jest-environment-jsdom": "^30.5.2",
|
|
51
49
|
"jest-websocket-mock": "^2.5.0",
|
|
52
50
|
"mock-socket": "^9.3.1",
|
|
53
|
-
"npm-check-updates": "^
|
|
54
|
-
"prettier": "^3.
|
|
55
|
-
"ts-jest": "^29.
|
|
56
|
-
"
|
|
57
|
-
"typescript": "^
|
|
58
|
-
},
|
|
59
|
-
"dependencies": {
|
|
60
|
-
"get-parameter-names": "^0.3.0"
|
|
51
|
+
"npm-check-updates": "^23.1.1",
|
|
52
|
+
"prettier": "^3.9.10",
|
|
53
|
+
"ts-jest": "^29.4.15",
|
|
54
|
+
"typescript": "~6.0.3",
|
|
55
|
+
"typescript-eslint": "^8.72.1"
|
|
61
56
|
}
|
|
62
57
|
}
|