js-rpc2 1.1.1 → 1.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/package.json +1 -1
- package/src/lib.js +26 -7
- package/src/lib.test.js +112 -1
- package/src/server.js +13 -4
- package/src/types.ts +3 -0
package/package.json
CHANGED
package/src/lib.js
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
/**
|
2
|
+
* @import { ExtensionApi } from "./types.js"
|
3
|
+
*/
|
4
|
+
|
1
5
|
const JS_RPC_WITH_CRYPTO = true
|
2
6
|
|
3
7
|
export const sleep = (/** @type {number} */ timeout) => new Promise((resolve) => setTimeout(resolve, timeout))
|
@@ -535,12 +539,14 @@ export function parseRpcItemData(array) {
|
|
535
539
|
}
|
536
540
|
|
537
541
|
/**
|
538
|
-
* @
|
542
|
+
* @template T
|
543
|
+
* @param {ExtensionApi<T>} extension
|
539
544
|
* @param {WritableStreamDefaultWriter<Uint8Array>} writer
|
540
545
|
* @param {Uint8Array} buffer
|
541
|
-
* @param {(msg:string)=>void}
|
546
|
+
* @param {(msg:string)=>void} logger
|
547
|
+
* @param {any} context
|
542
548
|
*/
|
543
|
-
export async function rpcRunServerDecodeBuffer(extension, writer, buffer, logger) {
|
549
|
+
export async function rpcRunServerDecodeBuffer(extension, writer, buffer, logger, context) {
|
544
550
|
/** @type{RPC_DATA} */
|
545
551
|
let box = null
|
546
552
|
let dataId = 0
|
@@ -570,7 +576,17 @@ export async function rpcRunServerDecodeBuffer(extension, writer, buffer, logger
|
|
570
576
|
params.push(p.data)
|
571
577
|
}
|
572
578
|
}
|
573
|
-
let
|
579
|
+
let store = extension.asyncLocalStorage
|
580
|
+
let ret = await new Promise((resolve, reject) => {
|
581
|
+
store.run(context, async () => {
|
582
|
+
try {
|
583
|
+
let ret = await extension[fnName](...params)
|
584
|
+
resolve(ret)
|
585
|
+
} catch (error) {
|
586
|
+
reject(error)
|
587
|
+
}
|
588
|
+
})
|
589
|
+
})
|
574
590
|
if (Array.isArray(ret)) {
|
575
591
|
box = { id: o.id, type: RPC_TYPE_RETURN_ARRAY, data: buildRpcItemData(ret), }
|
576
592
|
} else {
|
@@ -632,11 +648,13 @@ export function createRPCProxy(apiInvoke) {
|
|
632
648
|
*/
|
633
649
|
|
634
650
|
/**
|
651
|
+
* @template T
|
635
652
|
* @param {{
|
636
653
|
* rpcKey: string;
|
637
|
-
* extension:
|
654
|
+
* extension: ExtensionApi<T>;
|
638
655
|
* logger?: (msg:string)=>void;
|
639
656
|
* async?: boolean;
|
657
|
+
* context?:any;
|
640
658
|
* }} param
|
641
659
|
*/
|
642
660
|
export function createRpcServerHelper(param) {
|
@@ -644,12 +662,13 @@ export function createRpcServerHelper(param) {
|
|
644
662
|
const encode = createEncodeStream(rpc_key_iv)
|
645
663
|
const decode = createDecodeStream(rpc_key_iv)
|
646
664
|
let writer = encode.writable.getWriter()
|
665
|
+
let context = param.context
|
647
666
|
decode.readable.pipeTo(new WritableStream({
|
648
667
|
async write(buffer) {
|
649
668
|
if (param.async) {
|
650
|
-
rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger).catch(console.error)
|
669
|
+
rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger, context).catch(console.error)
|
651
670
|
} else {
|
652
|
-
await rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger)
|
671
|
+
await rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger, context)
|
653
672
|
}
|
654
673
|
},
|
655
674
|
async close() {
|
package/src/lib.test.js
CHANGED
@@ -6,6 +6,9 @@ import { WebSocketServer } from 'ws'
|
|
6
6
|
import Koa from 'koa'
|
7
7
|
import Router from 'koa-router'
|
8
8
|
import { createRpcServerKoaRouter, createRpcServerWebSocket } from './server.js'
|
9
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
10
|
+
import { Readable, Transform } from 'node:stream'
|
11
|
+
import { pipeline } from 'node:stream/promises'
|
9
12
|
|
10
13
|
test('basic', async () => {
|
11
14
|
// node --test-name-pattern="^basic$" src/lib.test.js
|
@@ -18,6 +21,8 @@ test('basic', async () => {
|
|
18
21
|
let server = createServer(app.callback()).listen(9000)
|
19
22
|
|
20
23
|
class RpcApi {
|
24
|
+
asyncLocalStorage = new AsyncLocalStorage()
|
25
|
+
|
21
26
|
/**
|
22
27
|
*
|
23
28
|
* @param {string} string
|
@@ -62,7 +67,7 @@ test('basic', async () => {
|
|
62
67
|
url: `http://127.0.0.1:9000/rpc/abc`, // others
|
63
68
|
})
|
64
69
|
|
65
|
-
let ret = await rpc.hello('123', new Uint8Array(3), { q: 2, w: 3, e: 4 }, null, undefined, [1, 2, 3, 4], (message, num) => {
|
70
|
+
let ret = await rpc.hello('123', new Uint8Array(3), { q: 2, w: 3, e: 4 }, null, undefined, [1, 2, 3, 4], async (message, num) => {
|
66
71
|
console.info('callback', message, num)
|
67
72
|
})
|
68
73
|
console.info(ret)
|
@@ -74,6 +79,7 @@ test('测试RPC调用-WebSocket', async () => {
|
|
74
79
|
// node --test-name-pattern="^测试RPC调用-WebSocket$" src/lib.test.js
|
75
80
|
|
76
81
|
const extension = {
|
82
|
+
asyncLocalStorage: new AsyncLocalStorage(),
|
77
83
|
hello: async function (/** @type {string} */ name) {
|
78
84
|
return `hello ${name}`
|
79
85
|
},
|
@@ -168,6 +174,7 @@ test('测试RPC调用-WebSocket', async () => {
|
|
168
174
|
test('测试RPC调用-KoaRouter', async () => {
|
169
175
|
// node --test-name-pattern="^测试RPC调用-KoaRouter$" src/lib.test.js
|
170
176
|
const extension = {
|
177
|
+
asyncLocalStorage: new AsyncLocalStorage(),
|
171
178
|
hello: async function (/** @type {string} */ name) {
|
172
179
|
return `hello ${name}`
|
173
180
|
},
|
@@ -276,6 +283,54 @@ test('测试RPC调用-KoaRouter', async () => {
|
|
276
283
|
console.info('over!')
|
277
284
|
})
|
278
285
|
|
286
|
+
test('测试RPC调用-KoaRouter-AsyncLocalStorage', async () => {
|
287
|
+
// node --test-name-pattern="^测试RPC调用-KoaRouter-AsyncLocalStorage$" src/lib.test.js
|
288
|
+
const extension = {
|
289
|
+
asyncLocalStorage: new AsyncLocalStorage(),
|
290
|
+
hello: async function (/** @type {string} */ name) {
|
291
|
+
console.info('asyncLocalStorage.getStore', this.asyncLocalStorage.getStore())
|
292
|
+
return `hello ${name}`
|
293
|
+
},
|
294
|
+
}
|
295
|
+
|
296
|
+
await runWithAbortController(async (ac) => {
|
297
|
+
let server = createServer()
|
298
|
+
let app = new Koa()
|
299
|
+
let router = new Router()
|
300
|
+
|
301
|
+
ac.signal.addEventListener('abort', () => { server.close() })
|
302
|
+
createRpcServerKoaRouter({
|
303
|
+
path: '/abc',
|
304
|
+
router: router,
|
305
|
+
rpcKey: 'abc',
|
306
|
+
extension: extension,
|
307
|
+
logger(msg) {
|
308
|
+
console.info(msg)
|
309
|
+
},
|
310
|
+
})
|
311
|
+
|
312
|
+
server.addListener('request', app.callback())
|
313
|
+
app.use(router.routes())
|
314
|
+
app.use(router.allowedMethods())
|
315
|
+
|
316
|
+
server.listen(9000)
|
317
|
+
await sleep(100)
|
318
|
+
|
319
|
+
/** @type{typeof extension} */
|
320
|
+
let client = createRpcClientHttp({
|
321
|
+
url: `http://127.0.0.1:9000/abc`,
|
322
|
+
rpcKey: 'abc',
|
323
|
+
signal: ac.signal,
|
324
|
+
})
|
325
|
+
await sleep(100)
|
326
|
+
|
327
|
+
let string = await client.hello('asdfghjkl')
|
328
|
+
console.info(string)
|
329
|
+
strictEqual(string, 'hello asdfghjkl')
|
330
|
+
})
|
331
|
+
console.info('over!')
|
332
|
+
})
|
333
|
+
|
279
334
|
/**
|
280
335
|
* @param {(ac: AbortController) => Promise<void>} func
|
281
336
|
*/
|
@@ -299,6 +354,7 @@ test('error-stack', async () => {
|
|
299
354
|
let server = createServer(app.callback()).listen(9000)
|
300
355
|
|
301
356
|
class RpcApi {
|
357
|
+
asyncLocalStorage = new AsyncLocalStorage()
|
302
358
|
async hello() {
|
303
359
|
new URL('/')
|
304
360
|
}
|
@@ -329,4 +385,59 @@ test('error-stack', async () => {
|
|
329
385
|
}
|
330
386
|
server.close()
|
331
387
|
|
388
|
+
})
|
389
|
+
|
390
|
+
test('async-local-storage', async () => {
|
391
|
+
// node --test-name-pattern="^async-local-storage$" src/lib.test.js
|
392
|
+
let store = new AsyncLocalStorage()
|
393
|
+
|
394
|
+
let p1 = Promise.withResolvers()
|
395
|
+
store.run({ a: 1 }, async () => {
|
396
|
+
console.info('--- 1 1 ', store.getStore())
|
397
|
+
await sleep(100)
|
398
|
+
console.info('--- 1 2 ', store.getStore())
|
399
|
+
p1.resolve()
|
400
|
+
})
|
401
|
+
let p2 = Promise.withResolvers()
|
402
|
+
store.run({ a: 2 }, async () => {
|
403
|
+
console.info('--- 2 1 ', store.getStore())
|
404
|
+
await sleep(100)
|
405
|
+
console.info('--- 2 2 ', store.getStore())
|
406
|
+
p2.resolve()
|
407
|
+
})
|
408
|
+
await Promise.all([p1.promise, p2.promise])
|
409
|
+
})
|
410
|
+
|
411
|
+
test('async-local-storage-stream', async () => {
|
412
|
+
// node --test-name-pattern="^async-local-storage-stream$" src/lib.test.js
|
413
|
+
let store = new AsyncLocalStorage()
|
414
|
+
let p1 = Promise.withResolvers()
|
415
|
+
let readable = new Readable({ read() { } })
|
416
|
+
|
417
|
+
store.run({ a: 1 }, async () => {
|
418
|
+
console.info('--- step 1 ', store.getStore())
|
419
|
+
!(async () => {
|
420
|
+
for (let i = 0; i < 3; i++) {
|
421
|
+
readable.push(`inner data:${i}`)
|
422
|
+
await sleep(10)
|
423
|
+
}
|
424
|
+
})()
|
425
|
+
await pipeline(readable, new Transform({
|
426
|
+
transform(chunk, _, callback) {
|
427
|
+
console.info(`--- step transform ${chunk} :`, store.getStore())
|
428
|
+
callback()
|
429
|
+
}
|
430
|
+
}))
|
431
|
+
console.info('--- step 2 ', store.getStore())
|
432
|
+
p1.resolve()
|
433
|
+
})
|
434
|
+
|
435
|
+
for (let i = 0; i < 3; i++) {
|
436
|
+
readable.push(`data:${i}`)
|
437
|
+
await sleep(10)
|
438
|
+
}
|
439
|
+
readable.push(null)
|
440
|
+
await sleep(100)
|
441
|
+
|
442
|
+
await Promise.all([p1.promise])
|
332
443
|
})
|
package/src/server.js
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
import { Readable } from "node:stream"
|
2
2
|
import { createRpcServerHelper } from "./lib.js"
|
3
|
+
import { AsyncLocalStorage } from "node:async_hooks"
|
3
4
|
|
4
5
|
/**
|
5
6
|
* @import {WebSocketServer} from 'ws'
|
7
|
+
* @import {ExtensionApi} from './types.js'
|
6
8
|
*/
|
7
9
|
|
8
10
|
export { createRpcServerHelper }
|
@@ -12,15 +14,19 @@ export { createRpcServerHelper }
|
|
12
14
|
*/
|
13
15
|
|
14
16
|
/**
|
17
|
+
* @template T
|
15
18
|
* @param {{
|
16
19
|
* path: string;
|
17
20
|
* wss: WebSocketServer;
|
18
21
|
* rpcKey:string;
|
19
|
-
* extension:
|
22
|
+
* extension: ExtensionApi<T>;
|
20
23
|
* logger?:(msg:string)=>void;
|
21
24
|
* }} param
|
22
25
|
*/
|
23
26
|
export function createRpcServerWebSocket(param) {
|
27
|
+
if (!param.extension.asyncLocalStorage) {
|
28
|
+
param.extension.asyncLocalStorage = new AsyncLocalStorage()
|
29
|
+
}
|
24
30
|
param.wss.on('connection', (ws, request) => {
|
25
31
|
let url = request.url
|
26
32
|
if (url != param.path) {
|
@@ -54,18 +60,21 @@ export function createRpcServerWebSocket(param) {
|
|
54
60
|
}
|
55
61
|
|
56
62
|
/**
|
57
|
-
*
|
63
|
+
* @template T
|
58
64
|
* @param {{
|
59
65
|
* path: string;
|
60
66
|
* router: Router<any, {}>;
|
61
67
|
* rpcKey?:string;
|
62
68
|
* logger?:(msg:string)=>void;
|
63
|
-
* extension:
|
69
|
+
* extension: ExtensionApi<T>;
|
64
70
|
* }} param
|
65
71
|
*/
|
66
72
|
export function createRpcServerKoaRouter(param) {
|
73
|
+
if (!param.extension.asyncLocalStorage) {
|
74
|
+
param.extension.asyncLocalStorage = new AsyncLocalStorage()
|
75
|
+
}
|
67
76
|
param.router.post(param.path, async (ctx) => {
|
68
|
-
let helper = createRpcServerHelper({ rpcKey: param.rpcKey, extension: param.extension, logger: param.logger })
|
77
|
+
let helper = createRpcServerHelper({ rpcKey: param.rpcKey, extension: param.extension, logger: param.logger, context: ctx })
|
69
78
|
/** @type{ReadableStream} */
|
70
79
|
let a = Readable.toWeb(ctx.req)
|
71
80
|
await a.pipeTo(helper.writable)
|
package/src/types.ts
ADDED