js-rpc2 2.0.0 → 2.2.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 CHANGED
@@ -312,4 +312,47 @@ export async function runWithAbortController(func) {
312
312
  await sleep(1000)
313
313
  } finally { ac.abort() }
314
314
  }
315
+ ```
316
+
317
+ ## Electron
318
+ ```js
319
+ // main.js
320
+ class AppApi {
321
+ asyncLocalStorage = new AsyncLocalStorage()
322
+
323
+ async hello(param) {
324
+ return 'wertyuioiuytre ' + param
325
+ }
326
+ }
327
+
328
+ import { ipcMain } from 'electron'
329
+ import { createRpcServerElectronMessagePort } from 'js-rpc2/src/lib.js'
330
+
331
+ ipcMain.on('port', (event) => {
332
+ const port = event.ports[0]
333
+ createRpcServerElectronMessagePort({ port, rpcKey: '', extension: new AppApi() })
334
+ port.start()
335
+ })
336
+
337
+ // preload.js
338
+ import { ipcRenderer } from 'electron/renderer'
339
+
340
+ window.onmessage = (/** @type {MessageEvent} */ event) => {
341
+ if (event.origin == location.origin && event.data != 'port') {
342
+ ipcRenderer.postMessage('port', null, [event.ports[0]])
343
+ }
344
+ }
345
+
346
+ // renderer.js
347
+ import { createRpcClientMessagePort } from 'js-rpc2/src/lib.js'
348
+
349
+ const channel = new MessageChannel();
350
+ window.postMessage("port", location.origin, [channel.port1]);
351
+ let rpc = createRpcClientMessagePort({ port: channel.port2, rpcKey: "" });
352
+
353
+ // uasge.js
354
+ async function postPortMessage() {
355
+ let ret = await rpc.hello('6667');
356
+ console.info("ret from rpc:", ret);
357
+ }
315
358
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-rpc2",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "js web websocket http rpc",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -26,10 +26,10 @@
26
26
  "author": "yuanliwei",
27
27
  "license": "MIT",
28
28
  "devDependencies": {
29
- "@koa/router": "^13.1.1",
30
- "@types/node": "^22.13.14",
31
- "koa": "^2.15.3",
32
- "ws": "^8.18.0"
29
+ "@koa/router": "^14.0.0",
30
+ "@types/node": "^24.2.1",
31
+ "koa": "^3.0.1",
32
+ "ws": "^8.18.3"
33
33
  },
34
34
  "dependencies": {
35
35
  "msgpackr": "^1.11.5"
package/src/lib.js CHANGED
@@ -23,8 +23,8 @@ export function Promise_withResolvers() {
23
23
  }
24
24
 
25
25
  /**
26
- * @param {Promise<[CryptoKey,Uint8Array]>} key_iv
27
- * @returns {TransformStream<Uint8Array, Uint8Array>}
26
+ * @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
27
+ * @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
28
28
  */
29
29
  export function createEncodeStream(key_iv) {
30
30
  let key = null
@@ -41,8 +41,8 @@ export function createEncodeStream(key_iv) {
41
41
  }
42
42
 
43
43
  /**
44
- * @param {Promise<[CryptoKey,Uint8Array]>} key_iv
45
- * @returns {TransformStream<Uint8Array, Uint8Array>}
44
+ * @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
45
+ * @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
46
46
  */
47
47
  export function createDecodeStream(key_iv) {
48
48
  let key = null
@@ -65,10 +65,10 @@ export function createDecodeStream(key_iv) {
65
65
  const HEADER_CHECK = 0xb1f7705f
66
66
 
67
67
  /**
68
- * @param {Uint8Array[]} queue
68
+ * @param {Uint8Array<ArrayBuffer>[]} queue
69
69
  * @param {CryptoKey} key
70
- * @param {Uint8Array} iv
71
- * @returns {Promise<Uint8Array>}
70
+ * @param {Uint8Array<ArrayBuffer>} iv
71
+ * @returns {Promise<Uint8Array<ArrayBuffer>>}
72
72
  */
73
73
  export async function buildBufferData(queue, key, iv) {
74
74
  let buffers = []
@@ -134,7 +134,7 @@ export function processPackets() {
134
134
  }
135
135
 
136
136
  /**
137
- * @param {Uint8Array} buffer
137
+ * @param {Uint8Array<ArrayBuffer>} buffer
138
138
  * @param {number} offset
139
139
  */
140
140
  export function readUInt32LE(buffer, offset) {
@@ -146,7 +146,7 @@ export function readUInt32LE(buffer, offset) {
146
146
  }
147
147
 
148
148
  /**
149
- * @param {Uint8Array} buffer
149
+ * @param {Uint8Array<ArrayBuffer>} buffer
150
150
  * @param {number} value
151
151
  * @param {number} offset
152
152
  */
@@ -159,7 +159,7 @@ export function writeUInt32LE(buffer, value, offset) {
159
159
  }
160
160
 
161
161
  /**
162
- * @param {Uint8Array[]} buffers
162
+ * @param {Uint8Array<ArrayBuffer>[]} buffers
163
163
  */
164
164
  export function Uint8Array_concat(buffers) {
165
165
  const totalLength = buffers.reduce((sum, buffer) => sum + buffer.length, 0)
@@ -196,7 +196,7 @@ export function Uint8Array_from(array, encoding) {
196
196
  }
197
197
 
198
198
  /**
199
- * @param {Uint8Array} buffer
199
+ * @param {Uint8Array<ArrayBuffer>} buffer
200
200
  * @param {'utf-8' | 'hex' | 'base64'} [encoding]
201
201
  */
202
202
  export function Uint8Array_toString(buffer, encoding = 'utf-8') {
@@ -231,7 +231,7 @@ function buildBufferSizeString(string) {
231
231
  }
232
232
 
233
233
  /**
234
- * @param {Uint8Array} buffer
234
+ * @param {Uint8Array<ArrayBuffer>} buffer
235
235
  * @param {number} offset
236
236
  */
237
237
  function readBufferSizeString(buffer, offset) {
@@ -258,7 +258,7 @@ export function guid() {
258
258
  *
259
259
  * @param {string} password
260
260
  * @param {number} iterations
261
- * @returns {Promise<[CryptoKey,Uint8Array]>}
261
+ * @returns {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>}
262
262
  */
263
263
  export async function buildKeyIv(password, iterations) {
264
264
  if (!JS_RPC_WITH_CRYPTO) return [null, null]
@@ -294,9 +294,9 @@ export async function buildKeyIv(password, iterations) {
294
294
 
295
295
  /**
296
296
  *
297
- * @param {Uint8Array} data
297
+ * @param {Uint8Array<ArrayBuffer>} data
298
298
  * @param {CryptoKey} key
299
- * @param {Uint8Array} iv
299
+ * @param {Uint8Array<ArrayBuffer>} iv
300
300
  * @returns
301
301
  */
302
302
  export async function encrypt(data, key, iv) {
@@ -311,7 +311,7 @@ export async function encrypt(data, key, iv) {
311
311
  /**
312
312
  * @param {Uint8Array<ArrayBuffer>} data
313
313
  * @param {CryptoKey} key
314
- * @param {Uint8Array} iv
314
+ * @param {Uint8Array<ArrayBuffer>} iv
315
315
  * @returns
316
316
  */
317
317
  export async function decrypt(data, key, iv) {
@@ -364,16 +364,24 @@ export const RPC_DATA_ARG_TYPE_FUNCTION = 0x7ff45f
364
364
  * @param {RPC_DATA} box
365
365
  */
366
366
  export function buildRpcData(box) {
367
- return Uint8Array.from(packr.pack(box))
367
+ let type = box.type
368
+ let data = box.data
369
+ if (type == RPC_TYPE_CALL || type == RPC_TYPE_CALLBACK) {
370
+ data = data.map(({ type, data }) => [type, data])
371
+ }
372
+ return Uint8Array.from(packr.pack([box.id, type, data]))
368
373
  }
369
374
 
370
375
  /**
371
- * @param {Uint8Array} buffer
376
+ * @param {Uint8Array<ArrayBuffer>} buffer
372
377
  */
373
378
  export function parseRpcData(buffer) {
379
+ let [id, type, data] = packr.unpack(buffer)
380
+ if (type == RPC_TYPE_CALL || type == RPC_TYPE_CALLBACK) {
381
+ data = data.map(([type, data]) => ({ type, data }))
382
+ }
374
383
  /** @type{RPC_DATA} */
375
- let box = packr.unpack(buffer)
376
- return box
384
+ return { id, type, data }
377
385
  }
378
386
 
379
387
  /**
@@ -401,8 +409,8 @@ export function buildRpcItemData(items) {
401
409
  /**
402
410
  * @template T
403
411
  * @param {ExtensionApi<T>} extension
404
- * @param {WritableStreamDefaultWriter<Uint8Array>} writer
405
- * @param {Uint8Array} buffer
412
+ * @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} writer
413
+ * @param {Uint8Array<ArrayBuffer>} buffer
406
414
  * @param {(msg:string)=>void} logger
407
415
  */
408
416
  export async function rpcRunServerDecodeBuffer(extension, writer, buffer, logger) {
@@ -487,8 +495,8 @@ export function createRPCProxy(apiInvoke) {
487
495
 
488
496
  /**
489
497
  * @typedef {{
490
- * writable: WritableStream<Uint8Array>;
491
- * readable: ReadableStream<Uint8Array>;
498
+ * writable: WritableStream<Uint8Array<ArrayBuffer>>;
499
+ * readable: ReadableStream<Uint8Array<ArrayBuffer>>;
492
500
  * }} RPC_HELPER_SERVER
493
501
  */
494
502
 
@@ -529,8 +537,8 @@ export function createRpcServerHelper(param) {
529
537
 
530
538
  /**
531
539
  * @typedef {{
532
- * writable: WritableStream<Uint8Array>;
533
- * readable: ReadableStream<Uint8Array>;
540
+ * writable: WritableStream<Uint8Array<ArrayBuffer>>;
541
+ * readable: ReadableStream<Uint8Array<ArrayBuffer>>;
534
542
  * apiInvoke: (fnName: string, args: object[]) => Promise<object>;
535
543
  * reject: (error:object)=>void;
536
544
  * }} RPC_HELPER_CLIENT
@@ -663,7 +671,7 @@ export function createRpcClientWebSocket(param) {
663
671
  let helper = createRpcClientHelper({ rpcKey: param.rpcKey })
664
672
  let writer = helper.writable.getWriter()
665
673
  let signal = Promise_withResolvers()
666
- /** @type{WritableStreamDefaultWriter<Uint8Array>} */
674
+ /** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
667
675
  let socketWriter = null
668
676
  helper.readable.pipeTo(new WritableStream({
669
677
  async write(chunk) {
@@ -753,3 +761,74 @@ export function createRpcClientHttp(param) {
753
761
  })).catch((err) => helper.reject(err))
754
762
  return createRPCProxy(helper.apiInvoke)
755
763
  }
764
+
765
+ /**
766
+ * @template T
767
+ * @param {{
768
+ * port:MessagePort;
769
+ * rpcKey:string;
770
+ * extension: ExtensionApi<T>;
771
+ * logger?:(msg:string)=>void;
772
+ * }} param
773
+ */
774
+ export function createRpcServerMessagePort(param) {
775
+ const port = param.port
776
+ let helper = createRpcServerHelper({
777
+ rpcKey: '', extension: param.extension, async: true, logger: param.logger,
778
+ })
779
+ let writer = helper.writable.getWriter()
780
+ port.onmessage = async (event) => {
781
+ await writer.write(event.data)
782
+ }
783
+ helper.readable.pipeTo(new WritableStream({
784
+ async write(chunk) {
785
+ port.postMessage(chunk)
786
+ }
787
+ }))
788
+ }
789
+
790
+ /**
791
+ * @import {Electron} from './types.js'
792
+ * @template T
793
+ * @param {{
794
+ * port:Electron.MessagePortMain;
795
+ * rpcKey:string;
796
+ * extension: ExtensionApi<T>;
797
+ * logger?:(msg:string)=>void;
798
+ * }} param
799
+ */
800
+ export function createRpcServerElectronMessagePort(param) {
801
+ const port = param.port
802
+ let helper = createRpcServerHelper({
803
+ rpcKey: '', extension: param.extension, async: true, logger: param.logger,
804
+ })
805
+ let writer = helper.writable.getWriter()
806
+ port.on('message', async (event) => {
807
+ await writer.write(event.data)
808
+ })
809
+ helper.readable.pipeTo(new WritableStream({
810
+ async write(chunk) {
811
+ port.postMessage(chunk)
812
+ }
813
+ }))
814
+ }
815
+
816
+ /**
817
+ * @param {{
818
+ * port:MessagePort;
819
+ * rpcKey:string;
820
+ * }} param
821
+ */
822
+ export function createRpcClientMessagePort(param) {
823
+ let helper = createRpcClientHelper({ rpcKey: param.rpcKey })
824
+ let writer = helper.writable.getWriter()
825
+ helper.readable.pipeTo(new WritableStream({
826
+ async write(chunk) {
827
+ param.port.postMessage(chunk)
828
+ }
829
+ }))
830
+ param.port.onmessage = async (event) => {
831
+ await writer.write(event.data)
832
+ }
833
+ return createRPCProxy(helper.apiInvoke)
834
+ }
package/src/server.js CHANGED
@@ -75,7 +75,6 @@ export function createRpcServerKoaRouter(param) {
75
75
  }
76
76
  param.router.post(param.path, async (ctx) => {
77
77
  let helper = createRpcServerHelper({ rpcKey: param.rpcKey, extension: param.extension, logger: param.logger, context: ctx })
78
- /** @type{ReadableStream} */
79
78
  let a = Readable.toWeb(ctx.req)
80
79
  await a.pipeTo(helper.writable)
81
80
  ctx.status = 200
package/src/types.ts CHANGED
@@ -1,49 +1,91 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
-
3
- export type ExtensionApi<T> = { asyncLocalStorage: AsyncLocalStorage<T> } & object;
4
-
5
-
6
- export type RPC_TYPE_CALL = 0xdf68f4cb
7
- export type RPC_TYPE_RETURN = 0x68b17581
8
- export type RPC_TYPE_CALLBACK = 0x8d65e5cc
9
- export type RPC_TYPE_ERROR = 0xa07c0f84
10
-
11
- export type RPC_DATA_ARG_TYPE_OTHERS = 0xa7f68c
12
- export type RPC_DATA_ARG_TYPE_FUNCTION = 0x7ff45f
13
-
14
-
15
- export type RPC_TYPES = RPC_TYPE_CALL |
16
- RPC_TYPE_RETURN |
17
- RPC_TYPE_CALLBACK |
18
- RPC_TYPE_ERROR;
19
-
20
-
21
- export type PromiseResolvers = {
22
- promise: Promise<any>;
23
- resolve: (value: any | null) => void;
24
- reject: (reason: any | null) => void;
25
- };
26
-
27
- export type CALLBACK_ITEM = {
28
- id: number;
29
- type: RPC_TYPES;
30
- promise?: PromiseResolvers;
31
- callback?: (...data: object[]) => void;
32
- };
33
-
34
- export type RPC_DATA_ARG_ITEM = { type: RPC_DATA_ARG_TYPE; data: object; };
35
- export type RPC_DATA = {
36
- id: number;
37
- type: RPC_TYPE_CALL | RPC_TYPE_CALLBACK;
38
- data: { type: RPC_DATA_ARG_TYPE; data: any; }[];
39
- } | {
40
- id: number;
41
- type: RPC_TYPE_RETURN;
42
- data: any;
43
- } | {
44
- id: number;
45
- type: RPC_TYPE_ERROR;
46
- data: { message: string, stack: string };
47
- };
48
-
49
- export type RPC_DATA_ARG_TYPE = RPC_DATA_ARG_TYPE_OTHERS | RPC_DATA_ARG_TYPE_FUNCTION;
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ export type ExtensionApi<T> = { asyncLocalStorage: AsyncLocalStorage<T> } & object;
4
+
5
+
6
+ export type RPC_TYPE_CALL = 0xdf68f4cb
7
+ export type RPC_TYPE_RETURN = 0x68b17581
8
+ export type RPC_TYPE_CALLBACK = 0x8d65e5cc
9
+ export type RPC_TYPE_ERROR = 0xa07c0f84
10
+
11
+ export type RPC_DATA_ARG_TYPE_OTHERS = 0xa7f68c
12
+ export type RPC_DATA_ARG_TYPE_FUNCTION = 0x7ff45f
13
+
14
+
15
+ export type RPC_TYPES = RPC_TYPE_CALL |
16
+ RPC_TYPE_RETURN |
17
+ RPC_TYPE_CALLBACK |
18
+ RPC_TYPE_ERROR;
19
+
20
+
21
+ export type PromiseResolvers = {
22
+ promise: Promise<any>;
23
+ resolve: (value: any | null) => void;
24
+ reject: (reason: any | null) => void;
25
+ };
26
+
27
+ export type CALLBACK_ITEM = {
28
+ id: number;
29
+ type: RPC_TYPES;
30
+ promise?: PromiseResolvers;
31
+ callback?: (...data: object[]) => void;
32
+ };
33
+
34
+ export type RPC_DATA_ARG_ITEM = { type: RPC_DATA_ARG_TYPE; data: object; };
35
+ export type RPC_DATA = {
36
+ id: number;
37
+ type: RPC_TYPE_CALL | RPC_TYPE_CALLBACK;
38
+ data: { type: RPC_DATA_ARG_TYPE; data: any; }[];
39
+ } | {
40
+ id: number;
41
+ type: RPC_TYPE_RETURN;
42
+ data: any;
43
+ } | {
44
+ id: number;
45
+ type: RPC_TYPE_ERROR;
46
+ data: { message: string, stack: string };
47
+ };
48
+
49
+ export type RPC_DATA_ARG_TYPE = RPC_DATA_ARG_TYPE_OTHERS | RPC_DATA_ARG_TYPE_FUNCTION;
50
+
51
+ export declare namespace Electron {
52
+
53
+ const NodeEventEmitter: typeof import('events').EventEmitter;
54
+
55
+ class MessagePortMain extends NodeEventEmitter {
56
+
57
+ // Docs: https://electronjs.org/docs/api/message-port-main
58
+
59
+ /**
60
+ * Emitted when the remote end of a MessagePortMain object becomes disconnected.
61
+ */
62
+ on(event: 'close', listener: () => void): this;
63
+ off(event: 'close', listener: () => void): this;
64
+ once(event: 'close', listener: () => void): this;
65
+ addListener(event: 'close', listener: () => void): this;
66
+ removeListener(event: 'close', listener: () => void): this;
67
+ /**
68
+ * Emitted when a MessagePortMain object receives a message.
69
+ */
70
+ on(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
71
+ off(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
72
+ once(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
73
+ addListener(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
74
+ removeListener(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
75
+ /**
76
+ * Disconnects the port, so it is no longer active.
77
+ */
78
+ close(): void;
79
+ /**
80
+ * Sends a message from the port, and optionally, transfers ownership of objects to
81
+ * other browsing contexts.
82
+ */
83
+ postMessage(message: any, transfer?: MessagePortMain[]): void;
84
+ /**
85
+ * Starts the sending of messages queued on the port. Messages will be queued until
86
+ * this method is called.
87
+ */
88
+ start(): void;
89
+ }
90
+
91
+ }