Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d0a23d4

Browse files
committedNov 29, 2022
added WebWorkerTransport and Serde's
1 parent 09c6626 commit d0a23d4

File tree

3 files changed

+120
-37
lines changed

3 files changed

+120
-37
lines changed
 

‎js/index.js

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
"use strict";
22
Object.defineProperty(exports, "__esModule", { value: true });
3+
const JSONSerDe = {
4+
encode: (data) => JSON.stringify(data),
5+
decode: (obj) => JSON.parse(obj),
6+
};
7+
const NOOPSerDe = {
8+
encode: (data) => data,
9+
decode: (obj) => obj,
10+
};
311
if (typeof WebSocket == 'undefined')
412
globalThis.WebSocket = require('ws');
513
const MAX_BUF_SIZE = 100;
@@ -16,23 +24,23 @@ const NO_METHOD_ERRPR = {
1624
data: 'No such function. ',
1725
};
1826
class WSRPC {
19-
constructor(transport) {
27+
constructor(transport, serde = JSONSerDe) {
2028
this.id = 0;
2129
this.methods = new Map();
2230
this.pending = new Map();
2331
this.transport = transport;
24-
transport.setOnMessage(this.on.bind(this));
32+
this.serde = serde;
33+
transport.setOnMessage((data) => this.on(serde.decode(data)));
2534
}
26-
on(data) {
27-
const msg = JSON.parse(data);
35+
on(msg) {
2836
// call
2937
if ('method' in msg) {
3038
const fn = this.methods.get(msg.method);
3139
if (!fn) {
3240
const err = { ...NO_METHOD_ERRPR };
3341
err.data += `method=${msg.method}`;
3442
if ('id' in msg) {
35-
this.send(JSON.stringify({ id: msg.id, error: err }));
43+
this.send({ id: msg.id, error: err });
3644
}
3745
else {
3846
console.error(`WSRPC: Cant 'notify' unknown method "${msg.method}"`);
@@ -43,11 +51,11 @@ class WSRPC {
4351
if ('id' in msg) {
4452
const resp = { /*jsonrpc: '2.0',*/ id: msg.id };
4553
fn(msg.params)
46-
.then(result => this.send(JSON.stringify({ ...resp, result })))
54+
.then(result => this.send({ ...resp, result }))
4755
.catch(error => {
4856
const msg = { ...resp, error: { ...SERVER_ERROR } };
4957
msg.error.data += `name=${error.name}, message=${error.message}`;
50-
this.send(JSON.stringify(msg));
58+
this.send(msg);
5159
});
5260
return;
5361
}
@@ -75,14 +83,14 @@ class WSRPC {
7583
}
7684
}
7785
send(msg) {
78-
this.transport.send(msg);
86+
this.transport.send(this.serde.encode(msg));
7987
}
8088
register(method, handler) {
8189
this.methods.set(method, handler);
8290
}
8391
call(method, params) {
8492
const id = this.id++;
85-
const msg = JSON.stringify({ /*jsonrpc: '2.0',*/ id, method, params });
93+
const msg = { /*jsonrpc: '2.0',*/ id, method, params };
8694
try {
8795
this.send(msg);
8896
}
@@ -94,7 +102,7 @@ class WSRPC {
94102
});
95103
}
96104
notify(method, params) {
97-
const msg = JSON.stringify({ /*jsonrpc: '2.0',*/ method, params });
105+
const msg = { /*jsonrpc: '2.0',*/ method, params };
98106
this.send(msg);
99107
}
100108
}
@@ -156,15 +164,36 @@ class WSClientTransport {
156164
}
157165
class WSServerTransport {
158166
constructor(ws) {
159-
this.onmessage = (data) => { };
160167
this.ws = ws;
161-
this.ws.onmessage = evt => this.onmessage(evt.data);
162168
}
163169
setOnMessage(fn) {
164-
this.onmessage = fn;
170+
this.ws.onmessage = evt => fn(evt.data);
165171
}
166172
send(msg) {
167173
this.ws.send(msg);
168174
}
169175
}
170-
module.exports = { WSRPC, WSClientTransport, WSServerTransport };
176+
/**
177+
* WebWorkerTransport supports both Worker & SharedWorker on the main thread side
178+
* On the Worker side either pass MessagePort for SharedWorker or
179+
* pass a WorkerLike like object with `onmessage` and `postMessage` methods
180+
*/
181+
class WebWorkerTransport {
182+
constructor(worker) {
183+
this.worker = worker;
184+
}
185+
setOnMessage(fn) {
186+
this.worker.onmessage = evt => fn(evt.data);
187+
}
188+
send(msg) {
189+
this.worker.postMessage(msg);
190+
}
191+
}
192+
module.exports = {
193+
JSONSerDe,
194+
NOOPSerDe,
195+
WSRPC,
196+
WSClientTransport,
197+
WSServerTransport,
198+
WebWorkerTransport,
199+
};

‎js/index.ts

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
type handlerFn = (params: any) => Promise<any>
22
type promisePair = { resolve: Function; reject: Function }
33

4+
type Message = {
5+
method?: string
6+
id?: number
7+
params?: any
8+
result?: any
9+
error?: any
10+
}
11+
12+
export interface SerDe {
13+
encode(data: Message): any
14+
decode(obj: any): Message
15+
}
16+
17+
const JSONSerDe: SerDe = {
18+
encode: (data) => JSON.stringify(data),
19+
decode: (obj) => JSON.parse(obj),
20+
}
21+
22+
const NOOPSerDe: SerDe = {
23+
encode: (data) => data,
24+
decode: (obj) => obj,
25+
}
26+
427
export interface JsonRPC {
528
/** register a method to be called from other side */
629
register: (method: string, handler: handlerFn) => void
@@ -32,26 +55,27 @@ const NO_METHOD_ERRPR = {
3255

3356
class WSRPC implements JsonRPC {
3457
private id = 0
58+
private serde: SerDe
3559
private transport: WSTransport
3660
private methods: Map<string, handlerFn> = new Map()
3761
private pending: Map<number, promisePair> = new Map()
3862

39-
constructor(transport: WSTransport) {
63+
constructor(transport: WSTransport, serde = JSONSerDe) {
4064
this.transport = transport
41-
transport.setOnMessage(this.on.bind(this))
65+
this.serde = serde
66+
transport.setOnMessage((data: any) => this.on(serde.decode(data)))
4267
}
4368

44-
private on(data: string) {
45-
const msg = JSON.parse(data)
69+
private on(msg: Message) {
4670

4771
// call
4872
if ('method' in msg) {
49-
const fn = this.methods.get(msg.method)
73+
const fn = this.methods.get(msg.method!)
5074
if (!fn) {
5175
const err = { ...NO_METHOD_ERRPR }
5276
err.data += `method=${msg.method}`
5377
if ('id' in msg) {
54-
this.send(JSON.stringify({ id: msg.id, error: err }))
78+
this.send({ id: msg.id, error: err })
5579
} else {
5680
console.error(`WSRPC: Cant 'notify' unknown method "${msg.method}"`)
5781
}
@@ -63,11 +87,11 @@ class WSRPC implements JsonRPC {
6387
const resp = { /*jsonrpc: '2.0',*/ id: msg.id }
6488

6589
fn(msg.params)
66-
.then(result => this.send(JSON.stringify({ ...resp, result })))
90+
.then(result => this.send({ ...resp, result }))
6791
.catch(error => {
6892
const msg = { ...resp, error: { ...SERVER_ERROR } }
6993
msg.error.data += `name=${error.name}, message=${error.message}`
70-
this.send(JSON.stringify(msg))
94+
this.send(msg)
7195
})
7296
return
7397
}
@@ -79,27 +103,27 @@ class WSRPC implements JsonRPC {
79103

80104
// resolve
81105
if ('id' in msg) {
82-
if (!this.pending.has(msg.id)) {
106+
if (!this.pending.has(msg.id!)) {
83107
console.error('WSRPC: Cant resolve requestID: ', msg.id)
84108
return
85109
}
86110

87-
const { resolve, reject } = this.pending.get(msg.id)!
111+
const { resolve, reject } = this.pending.get(msg.id!)!
88112
if ('result' in msg) resolve(msg.result)
89113
else if ('error' in msg) reject(msg.error)
90114
else {
91115
console.warn(
92116
`Received msgID=${msg.id} with neither 'result' or 'error'. ` +
93-
`Likely service method is for 'notify' but is called as 'request'`,
117+
`Likely service method is for 'notify' but is called as 'request'`,
94118
)
95119
resolve()
96120
}
97121
return
98122
}
99123
}
100124

101-
private send(msg: string) {
102-
this.transport.send(msg)
125+
private send(msg: Message) {
126+
this.transport.send(this.serde.encode(msg))
103127
}
104128

105129
public register(method: string, handler: handlerFn) {
@@ -108,7 +132,7 @@ class WSRPC implements JsonRPC {
108132

109133
public call(method: string, params?: any): Promise<any> {
110134
const id = this.id++
111-
const msg = JSON.stringify({ /*jsonrpc: '2.0',*/ id, method, params })
135+
const msg = { /*jsonrpc: '2.0',*/ id, method, params }
112136

113137
try {
114138
this.send(msg)
@@ -122,14 +146,19 @@ class WSRPC implements JsonRPC {
122146
}
123147

124148
public notify(method: string, params?: any) {
125-
const msg = JSON.stringify({ /*jsonrpc: '2.0',*/ method, params })
149+
const msg = { /*jsonrpc: '2.0',*/ method, params }
126150
this.send(msg)
127151
}
128152
}
129153

130154
export interface WSTransport {
131155
setOnMessage(fn: (data: any) => void): void
132-
send(msg: string): void
156+
send(msg: any): void
157+
}
158+
159+
export interface WorkerLike {
160+
onmessage: ((this: Worker, ev: MessageEvent) => any) | null;
161+
postMessage(message: any, options?: any): void;
133162
}
134163

135164
/**
@@ -142,9 +171,9 @@ class WSClientTransport implements WSTransport {
142171
private openCB: () => void
143172
private errCB: (e: Error) => void
144173
private sendBuffer: string[] = []
145-
private onmessage = (data: any) => {}
174+
private onmessage = (data: any) => { }
146175

147-
constructor(url: string, openCB = () => {}, errCB = console.error) {
176+
constructor(url: string, openCB = () => { }, errCB = console.error) {
148177
this.url = url
149178
this.openCB = openCB
150179
this.errCB = errCB
@@ -205,20 +234,45 @@ class WSClientTransport implements WSTransport {
205234

206235
class WSServerTransport implements WSTransport {
207236
private ws: WebSocket
208-
private onmessage = (data: any) => {}
209237

210238
constructor(ws: WebSocket) {
211239
this.ws = ws
212-
this.ws.onmessage = evt => this.onmessage(evt.data)
213240
}
214241

215242
public setOnMessage(fn: (data: any) => void) {
216-
this.onmessage = fn
243+
this.ws.onmessage = evt => fn(evt.data)
217244
}
218245

219246
public send(msg: string) {
220247
this.ws.send(msg)
221248
}
222249
}
250+
/**
251+
* WebWorkerTransport supports both Worker & SharedWorker on the main thread side
252+
* On the Worker side either pass MessagePort for SharedWorker or
253+
* pass a WorkerLike like object with `onmessage` and `postMessage` methods
254+
*/
255+
class WebWorkerTransport implements WSTransport {
256+
private worker: Worker | WorkerLike
257+
258+
constructor(worker: Worker | WorkerLike) {
259+
this.worker = worker
260+
}
261+
262+
public setOnMessage(fn: (data: any) => void): void {
263+
this.worker.onmessage = evt => fn(evt.data)
264+
}
223265

224-
module.exports = { WSRPC, WSClientTransport, WSServerTransport }
266+
public send(msg: string): void {
267+
this.worker.postMessage(msg)
268+
}
269+
}
270+
271+
module.exports = {
272+
JSONSerDe,
273+
NOOPSerDe,
274+
WSRPC,
275+
WSClientTransport,
276+
WSServerTransport,
277+
WebWorkerTransport,
278+
}

‎package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsonrpc",
3-
"version": "1.1.2",
3+
"version": "1.2.0",
44
"description": "jsonrpc over websocket for golang &amp; typescript",
55
"main": "./js/index.js",
66
"typings": "./js/index.d.ts",

0 commit comments

Comments
 (0)
Please sign in to comment.