js-rpc2 1.1.0 → 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 +28 -8
- package/src/lib.test.js +119 -5
- 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 {
|
@@ -589,7 +605,8 @@ export async function rpcRunServerDecodeBuffer(extension, writer, buffer, logger
|
|
589
605
|
}
|
590
606
|
if (logger) {
|
591
607
|
logger(`time: ${Date.now() - time}ms ${fnName}(${params.map(o => {
|
592
|
-
if (typeof o == 'function') { return Function }
|
608
|
+
if (typeof o == 'function') { return `Function()` }
|
609
|
+
if (o instanceof Uint8Array) { return `Uint8Array(${o.length})` }
|
593
610
|
return o
|
594
611
|
}).join(', ')})`)
|
595
612
|
}
|
@@ -631,11 +648,13 @@ export function createRPCProxy(apiInvoke) {
|
|
631
648
|
*/
|
632
649
|
|
633
650
|
/**
|
651
|
+
* @template T
|
634
652
|
* @param {{
|
635
653
|
* rpcKey: string;
|
636
|
-
* extension:
|
654
|
+
* extension: ExtensionApi<T>;
|
637
655
|
* logger?: (msg:string)=>void;
|
638
656
|
* async?: boolean;
|
657
|
+
* context?:any;
|
639
658
|
* }} param
|
640
659
|
*/
|
641
660
|
export function createRpcServerHelper(param) {
|
@@ -643,12 +662,13 @@ export function createRpcServerHelper(param) {
|
|
643
662
|
const encode = createEncodeStream(rpc_key_iv)
|
644
663
|
const decode = createDecodeStream(rpc_key_iv)
|
645
664
|
let writer = encode.writable.getWriter()
|
665
|
+
let context = param.context
|
646
666
|
decode.readable.pipeTo(new WritableStream({
|
647
667
|
async write(buffer) {
|
648
668
|
if (param.async) {
|
649
|
-
rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger).catch(console.error)
|
669
|
+
rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger, context).catch(console.error)
|
650
670
|
} else {
|
651
|
-
await rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger)
|
671
|
+
await rpcRunServerDecodeBuffer(param.extension, writer, buffer, param.logger, context)
|
652
672
|
}
|
653
673
|
},
|
654
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
|
},
|
@@ -133,13 +139,13 @@ test('测试RPC调用-WebSocket', async () => {
|
|
133
139
|
console.info(string)
|
134
140
|
strictEqual(string, 'hello asdfghjkl')
|
135
141
|
|
136
|
-
let stringcallback = await client.callback('asdfghjkl', (progress) => {
|
142
|
+
let stringcallback = await client.callback('asdfghjkl', async (progress) => {
|
137
143
|
console.info(`client : ${progress}`)
|
138
144
|
})
|
139
145
|
strictEqual(stringcallback, 'hello callback asdfghjkl')
|
140
146
|
|
141
147
|
let callbackCount = 0
|
142
|
-
let stringcallback2 = await client.callback2('asdfghjkl', (progress, buffer) => {
|
148
|
+
let stringcallback2 = await client.callback2('asdfghjkl', async (progress, buffer) => {
|
143
149
|
console.info(`client : ${progress}`, buffer)
|
144
150
|
callbackCount++
|
145
151
|
})
|
@@ -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
|
},
|
@@ -211,6 +218,9 @@ test('测试RPC调用-KoaRouter', async () => {
|
|
211
218
|
router: router,
|
212
219
|
rpcKey: '11474f3dfbb861700cb6c3864b328311',
|
213
220
|
extension: extension,
|
221
|
+
logger(msg) {
|
222
|
+
console.info(msg)
|
223
|
+
},
|
214
224
|
})
|
215
225
|
|
216
226
|
server.addListener('request', app.callback())
|
@@ -233,7 +243,7 @@ test('测试RPC调用-KoaRouter', async () => {
|
|
233
243
|
strictEqual(string, 'hello asdfghjkl')
|
234
244
|
|
235
245
|
let callbackCount = 0
|
236
|
-
let stringcallback = await client.callback('asdfghjkl', (progress) => {
|
246
|
+
let stringcallback = await client.callback('asdfghjkl', async (progress) => {
|
237
247
|
console.info(`client : ${progress}`)
|
238
248
|
callbackCount++
|
239
249
|
})
|
@@ -241,7 +251,7 @@ test('测试RPC调用-KoaRouter', async () => {
|
|
241
251
|
strictEqual(3, callbackCount)
|
242
252
|
strictEqual(stringcallback, 'hello callback asdfghjkl')
|
243
253
|
|
244
|
-
let stringcallback2 = await client.callback2('asdfghjkl', (progress, buffer) => {
|
254
|
+
let stringcallback2 = await client.callback2('asdfghjkl', async (progress, buffer) => {
|
245
255
|
console.info(`client : ${progress}`, buffer)
|
246
256
|
})
|
247
257
|
strictEqual(stringcallback2, 'hello callback asdfghjkl')
|
@@ -273,6 +283,54 @@ test('测试RPC调用-KoaRouter', async () => {
|
|
273
283
|
console.info('over!')
|
274
284
|
})
|
275
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
|
+
|
276
334
|
/**
|
277
335
|
* @param {(ac: AbortController) => Promise<void>} func
|
278
336
|
*/
|
@@ -296,6 +354,7 @@ test('error-stack', async () => {
|
|
296
354
|
let server = createServer(app.callback()).listen(9000)
|
297
355
|
|
298
356
|
class RpcApi {
|
357
|
+
asyncLocalStorage = new AsyncLocalStorage()
|
299
358
|
async hello() {
|
300
359
|
new URL('/')
|
301
360
|
}
|
@@ -326,4 +385,59 @@ test('error-stack', async () => {
|
|
326
385
|
}
|
327
386
|
server.close()
|
328
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])
|
329
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