@delight-rpc/piscina 0.5.6 → 0.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 +9 -5
- package/lib/client.d.ts +7 -5
- package/lib/client.js +64 -9
- package/lib/client.js.map +1 -1
- package/lib/server.d.ts +1 -1
- package/lib/server.js +42 -11
- package/lib/server.js.map +1 -1
- package/package.json +26 -27
- package/src/client.ts +80 -15
- package/src/server.ts +57 -15
package/README.md
CHANGED
|
@@ -22,7 +22,9 @@ const api: IAPI = {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const [handler] = createServer(api)
|
|
26
|
+
|
|
27
|
+
export default handler
|
|
26
28
|
|
|
27
29
|
// main.ts
|
|
28
30
|
import { createClient } from '@delight-rpc/piscina'
|
|
@@ -30,7 +32,7 @@ import { createClient } from '@delight-rpc/piscina'
|
|
|
30
32
|
const piscina = new Piscina({
|
|
31
33
|
filename: new URL('./worker.js', import.meta.url).href
|
|
32
34
|
})
|
|
33
|
-
const client = createClient<IAPI>(piscina)
|
|
35
|
+
const [client] = createClient<IAPI>(piscina)
|
|
34
36
|
|
|
35
37
|
await client.echo('hello world')
|
|
36
38
|
```
|
|
@@ -44,8 +46,9 @@ function createClient<IAPI extends object>(
|
|
|
44
46
|
parameterValidators?: DelightRPC.ParameterValidators<IAPI>
|
|
45
47
|
expectedVersion?: string
|
|
46
48
|
channel?: string
|
|
49
|
+
timeout?: number
|
|
47
50
|
}
|
|
48
|
-
): DelightRPC.ClientProxy<IAPI
|
|
51
|
+
): [client: DelightRPC.ClientProxy<IAPI>, close: () => void]
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
### createBatchClient
|
|
@@ -55,8 +58,9 @@ function createBatchClient<DataType>(
|
|
|
55
58
|
, options?: {
|
|
56
59
|
expectedVersion?: string
|
|
57
60
|
channel?: string
|
|
61
|
+
timeout?: number
|
|
58
62
|
}
|
|
59
|
-
): DelightRPC.BatchClient<DataType
|
|
63
|
+
): [client: DelightRPC.BatchClient<DataType>, close: () => void]
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
### createServer
|
|
@@ -70,5 +74,5 @@ function createServer<IAPI extends object>(
|
|
|
70
74
|
ownPropsOnly?: boolean
|
|
71
75
|
channel?: string | RegExp | AnyChannel
|
|
72
76
|
}
|
|
73
|
-
): (
|
|
77
|
+
): [handler: (message: unknown) => Promise<unknown>, close: () => void]
|
|
74
78
|
```
|
package/lib/client.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as DelightRPC from 'delight-rpc';
|
|
2
|
-
import Piscina from 'piscina';
|
|
3
|
-
export declare function createClient<IAPI extends object>(piscina: Piscina, { parameterValidators, expectedVersion, channel }?: {
|
|
2
|
+
import { Piscina } from 'piscina';
|
|
3
|
+
export declare function createClient<IAPI extends object>(piscina: Piscina, { parameterValidators, expectedVersion, channel, timeout }?: {
|
|
4
4
|
parameterValidators?: DelightRPC.ParameterValidators<IAPI>;
|
|
5
5
|
expectedVersion?: string;
|
|
6
6
|
channel?: string;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}): [client: DelightRPC.ClientProxy<IAPI>, close: () => void];
|
|
9
|
+
export declare function createBatchClient<DataType>(piscina: Piscina, { expectedVersion, channel, timeout }?: {
|
|
9
10
|
expectedVersion?: string;
|
|
10
11
|
channel?: string;
|
|
11
|
-
|
|
12
|
+
timeout?: number;
|
|
13
|
+
}): [client: DelightRPC.BatchClient<DataType>, close: () => void];
|
package/lib/client.js
CHANGED
|
@@ -1,20 +1,75 @@
|
|
|
1
1
|
import * as DelightRPC from 'delight-rpc';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { SyncDestructor } from 'extra-defer';
|
|
3
|
+
import { AbortController, raceAbortSignals, timeoutSignal } from 'extra-abort';
|
|
4
|
+
import { isntUndefined, pass } from '@blackglory/prelude';
|
|
5
|
+
export function createClient(piscina, { parameterValidators, expectedVersion, channel, timeout } = {}) {
|
|
6
|
+
const destructor = new SyncDestructor();
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
destructor.defer(abortAllPendings);
|
|
9
|
+
const client = DelightRPC.createClient(async function send(request, signal) {
|
|
10
|
+
const destructor = new SyncDestructor();
|
|
11
|
+
try {
|
|
12
|
+
const mergedSignal = raceAbortSignals([
|
|
13
|
+
isntUndefined(timeout) && timeoutSignal(timeout),
|
|
14
|
+
signal,
|
|
15
|
+
controller.signal
|
|
16
|
+
]);
|
|
17
|
+
mergedSignal.addEventListener('abort', sendAbort);
|
|
18
|
+
destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort));
|
|
19
|
+
return await piscina.run(request, { signal: mergedSignal });
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
destructor.execute();
|
|
23
|
+
}
|
|
24
|
+
async function sendAbort() {
|
|
25
|
+
const abort = DelightRPC.createAbort(request.id, channel);
|
|
26
|
+
await piscina.run(abort).catch(pass);
|
|
27
|
+
}
|
|
28
|
+
}, {
|
|
4
29
|
parameterValidators,
|
|
5
30
|
expectedVersion,
|
|
6
31
|
channel
|
|
7
32
|
});
|
|
8
|
-
return client;
|
|
33
|
+
return [client, close];
|
|
34
|
+
function close() {
|
|
35
|
+
destructor.execute();
|
|
36
|
+
}
|
|
37
|
+
function abortAllPendings() {
|
|
38
|
+
controller.abort();
|
|
39
|
+
}
|
|
9
40
|
}
|
|
10
|
-
export function createBatchClient(piscina, { expectedVersion, channel } = {}) {
|
|
11
|
-
const
|
|
41
|
+
export function createBatchClient(piscina, { expectedVersion, channel, timeout } = {}) {
|
|
42
|
+
const destructor = new SyncDestructor();
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
destructor.defer(abortAllPendings);
|
|
45
|
+
const client = new DelightRPC.BatchClient(async function send(request) {
|
|
46
|
+
const destructor = new SyncDestructor();
|
|
47
|
+
try {
|
|
48
|
+
const mergedSignal = raceAbortSignals([
|
|
49
|
+
isntUndefined(timeout) && timeoutSignal(timeout),
|
|
50
|
+
controller.signal
|
|
51
|
+
]);
|
|
52
|
+
mergedSignal.addEventListener('abort', sendAbort);
|
|
53
|
+
destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort));
|
|
54
|
+
return await piscina.run(request, { signal: mergedSignal });
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
destructor.execute();
|
|
58
|
+
}
|
|
59
|
+
async function sendAbort() {
|
|
60
|
+
const abort = DelightRPC.createAbort(request.id, channel);
|
|
61
|
+
await piscina.run(abort).catch(pass);
|
|
62
|
+
}
|
|
63
|
+
}, {
|
|
12
64
|
expectedVersion,
|
|
13
65
|
channel
|
|
14
66
|
});
|
|
15
|
-
return client;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
67
|
+
return [client, close];
|
|
68
|
+
function close() {
|
|
69
|
+
destructor.execute();
|
|
70
|
+
}
|
|
71
|
+
function abortAllPendings() {
|
|
72
|
+
controller.abort();
|
|
73
|
+
}
|
|
19
74
|
}
|
|
20
75
|
//# sourceMappingURL=client.js.map
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/lib/server.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export declare function createServer<IAPI extends object>(api: DelightRPC.Implem
|
|
|
4
4
|
version?: `${number}.${number}.${number}`;
|
|
5
5
|
channel?: string | RegExp | typeof DelightRPC.AnyChannel;
|
|
6
6
|
ownPropsOnly?: boolean;
|
|
7
|
-
}): (req: unknown) => Promise<unknown
|
|
7
|
+
}): [handler: (req: unknown) => Promise<unknown>, close: () => void];
|
package/lib/server.js
CHANGED
|
@@ -1,18 +1,49 @@
|
|
|
1
1
|
import * as DelightRPC from 'delight-rpc';
|
|
2
2
|
import { isntNull } from '@blackglory/prelude';
|
|
3
|
+
import { SyncDestructor } from 'extra-defer';
|
|
4
|
+
import { HashMap } from '@blackglory/structures';
|
|
3
5
|
export function createServer(api, { parameterValidators, version, channel, ownPropsOnly } = {}) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
const destructor = new SyncDestructor();
|
|
7
|
+
const channelIdToController = new HashMap(({ channel, id }) => JSON.stringify([channel, id]));
|
|
8
|
+
destructor.defer(abortAllPendings);
|
|
9
|
+
return [handler, close];
|
|
10
|
+
function close() {
|
|
11
|
+
destructor.execute();
|
|
12
|
+
}
|
|
13
|
+
function abortAllPendings() {
|
|
14
|
+
for (const controller of channelIdToController.values()) {
|
|
15
|
+
controller.abort();
|
|
16
|
+
}
|
|
17
|
+
channelIdToController.clear();
|
|
18
|
+
}
|
|
19
|
+
async function handler(message) {
|
|
20
|
+
var _a;
|
|
21
|
+
if (DelightRPC.isRequest(message) || DelightRPC.isBatchRequest(message)) {
|
|
22
|
+
const destructor = new SyncDestructor();
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
channelIdToController.set(message, controller);
|
|
25
|
+
destructor.defer(() => channelIdToController.delete(message));
|
|
26
|
+
try {
|
|
27
|
+
const response = await DelightRPC.createResponse(api, message, {
|
|
28
|
+
parameterValidators,
|
|
29
|
+
version,
|
|
30
|
+
channel,
|
|
31
|
+
ownPropsOnly
|
|
32
|
+
});
|
|
33
|
+
if (isntNull(response)) {
|
|
34
|
+
return response;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
destructor.execute();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (DelightRPC.isAbort(message)) {
|
|
42
|
+
if (DelightRPC.matchChannel(message, channel)) {
|
|
43
|
+
(_a = channelIdToController.get(message)) === null || _a === void 0 ? void 0 : _a.abort();
|
|
44
|
+
channelIdToController.delete(message);
|
|
14
45
|
}
|
|
15
46
|
}
|
|
16
|
-
}
|
|
47
|
+
}
|
|
17
48
|
}
|
|
18
49
|
//# sourceMappingURL=server.js.map
|
package/lib/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAEhD,MAAM,UAAU,YAAY,CAC1B,GAAsC,EACtC,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,KAKjD,EAAE;IAEN,MAAM,UAAU,GAAG,IAAI,cAAc,EAAE,CAAA;IAEvC,MAAM,qBAAqB,GAMvB,IAAI,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IACnE,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;IAElC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAEvB,SAAS,KAAK;QACZ,UAAU,CAAC,OAAO,EAAE,CAAA;IACtB,CAAC;IAED,SAAS,gBAAgB;QACvB,KAAK,MAAM,UAAU,IAAI,qBAAqB,CAAC,MAAM,EAAE,EAAE,CAAC;YACxD,UAAU,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;QAED,qBAAqB,CAAC,KAAK,EAAE,CAAA;IAC/B,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,OAAgB;;QACrC,IAAI,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YACxE,MAAM,UAAU,GAAG,IAAI,cAAc,EAAE,CAAA;YAEvC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;YACxC,qBAAqB,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;YAC9C,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;YAE7D,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,cAAc,CAC9C,GAAG,EACH,OAAO,EACP;oBACE,mBAAmB;oBACnB,OAAO;oBACP,OAAO;oBACP,YAAY;iBACb,CACF,CAAA;gBAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACvB,OAAO,QAAQ,CAAA;gBACjB,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,UAAU,CAAC,OAAO,EAAE,CAAA;YACtB,CAAC;QACH,CAAC;aAAM,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,IAAI,UAAU,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC9C,MAAA,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,0CAAE,KAAK,EAAE,CAAA;gBAC3C,qBAAqB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YACvC,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delight-rpc/piscina",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"files": [
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"types": "lib/index.d.ts",
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
15
|
+
"node": ">=22"
|
|
16
16
|
},
|
|
17
17
|
"repository": "git@github.com:delight-rpc/piscina.git",
|
|
18
18
|
"author": "BlackGlory <woshenmedoubuzhidao@blackglory.me>",
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"prepare": "ts-patch install -s",
|
|
22
22
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
|
|
23
|
-
"test": "
|
|
24
|
-
"test:debug": "cross-env NODE_OPTIONS='--experimental-vm-modules --loader ts-node/esm' node --inspect-brk node_modules/.bin/jest --runInBand --config jest.config.cjs",
|
|
23
|
+
"test": "vitest run",
|
|
25
24
|
"prepublishOnly": "run-s prepare clean build",
|
|
26
25
|
"clean": "rimraf lib",
|
|
27
26
|
"build": "tsc --project tsconfig.build.json",
|
|
@@ -34,37 +33,37 @@
|
|
|
34
33
|
}
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
|
-
"@
|
|
38
|
-
"@commitlint/
|
|
39
|
-
"@
|
|
40
|
-
"@types/
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"delight-rpc": "^6.0.0",
|
|
46
|
-
"eslint": "8.36.0",
|
|
36
|
+
"@commitlint/cli": "^20.4.2",
|
|
37
|
+
"@commitlint/config-conventional": "^20.4.2",
|
|
38
|
+
"@eslint/js": "^10.0.1",
|
|
39
|
+
"@types/node": "22",
|
|
40
|
+
"cross-env": "^10.1.0",
|
|
41
|
+
"delight-rpc": "^7.0.0",
|
|
42
|
+
"eslint": "10.0.2",
|
|
43
|
+
"extra-promise": "^7.1.1",
|
|
47
44
|
"husky": "4",
|
|
48
|
-
"jest": "^29.5.0",
|
|
49
|
-
"jest-environment-jsdom": "^29.5.0",
|
|
50
|
-
"jest-resolve": "^29.5.0",
|
|
51
45
|
"npm-run-all": "^4.1.5",
|
|
52
46
|
"piscina": "^4.2.0",
|
|
53
|
-
"return-style": "^
|
|
54
|
-
"rimraf": "^
|
|
47
|
+
"return-style": "^4.0.0",
|
|
48
|
+
"rimraf": "^6.1.3",
|
|
55
49
|
"standard-version": "^9.5.0",
|
|
56
|
-
"ts-
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"typescript": "^
|
|
60
|
-
"
|
|
50
|
+
"ts-patch": "^3.3.0",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"typescript-eslint": "^8.56.1",
|
|
53
|
+
"typescript-transform-paths": "^3.5.6",
|
|
54
|
+
"vite": "^7.3.1",
|
|
55
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
56
|
+
"vitest": "^4.0.18"
|
|
61
57
|
},
|
|
62
58
|
"dependencies": {
|
|
63
|
-
"@blackglory/prelude": "^0.
|
|
64
|
-
"@
|
|
59
|
+
"@blackglory/prelude": "^0.4.0",
|
|
60
|
+
"@blackglory/structures": "^0.14.13",
|
|
61
|
+
"@delight-rpc/protocol": "^4.1.1",
|
|
62
|
+
"extra-abort": "^0.4.1",
|
|
63
|
+
"extra-defer": "^0.3.1"
|
|
65
64
|
},
|
|
66
65
|
"peerDependencies": {
|
|
67
|
-
"delight-rpc": "^5.0.0 || ^6.0.0",
|
|
66
|
+
"delight-rpc": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
68
67
|
"piscina": "^3.2.0 || ^4.0.0"
|
|
69
68
|
}
|
|
70
69
|
}
|
package/src/client.ts
CHANGED
|
@@ -1,17 +1,46 @@
|
|
|
1
1
|
import * as DelightRPC from 'delight-rpc'
|
|
2
|
-
import Piscina from 'piscina'
|
|
3
|
-
import {
|
|
2
|
+
import { Piscina } from 'piscina'
|
|
3
|
+
import { SyncDestructor } from 'extra-defer'
|
|
4
|
+
import { AbortController, raceAbortSignals, timeoutSignal } from 'extra-abort'
|
|
5
|
+
import { isntUndefined, pass } from '@blackglory/prelude'
|
|
4
6
|
|
|
5
7
|
export function createClient<IAPI extends object>(
|
|
6
8
|
piscina: Piscina
|
|
7
|
-
, { parameterValidators, expectedVersion, channel }: {
|
|
9
|
+
, { parameterValidators, expectedVersion, channel, timeout }: {
|
|
8
10
|
parameterValidators?: DelightRPC.ParameterValidators<IAPI>
|
|
9
11
|
expectedVersion?: string
|
|
10
12
|
channel?: string
|
|
13
|
+
timeout?: number
|
|
11
14
|
} = {}
|
|
12
|
-
): DelightRPC.ClientProxy<IAPI
|
|
15
|
+
): [client: DelightRPC.ClientProxy<IAPI>, close: () => void] {
|
|
16
|
+
const destructor = new SyncDestructor()
|
|
17
|
+
|
|
18
|
+
const controller = new AbortController()
|
|
19
|
+
destructor.defer(abortAllPendings)
|
|
20
|
+
|
|
13
21
|
const client = DelightRPC.createClient<IAPI>(
|
|
14
|
-
|
|
22
|
+
async function send(request, signal) {
|
|
23
|
+
const destructor = new SyncDestructor()
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const mergedSignal = raceAbortSignals([
|
|
27
|
+
isntUndefined(timeout) && timeoutSignal(timeout)
|
|
28
|
+
, signal
|
|
29
|
+
, controller.signal
|
|
30
|
+
])
|
|
31
|
+
mergedSignal.addEventListener('abort', sendAbort)
|
|
32
|
+
destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort))
|
|
33
|
+
|
|
34
|
+
return await piscina.run(request, { signal: mergedSignal })
|
|
35
|
+
} finally {
|
|
36
|
+
destructor.execute()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function sendAbort(): Promise<void> {
|
|
40
|
+
const abort = DelightRPC.createAbort(request.id, channel)
|
|
41
|
+
await piscina.run(abort).catch(pass)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
15
44
|
, {
|
|
16
45
|
parameterValidators
|
|
17
46
|
, expectedVersion
|
|
@@ -19,29 +48,65 @@ export function createClient<IAPI extends object>(
|
|
|
19
48
|
}
|
|
20
49
|
)
|
|
21
50
|
|
|
22
|
-
return client
|
|
51
|
+
return [client, close]
|
|
52
|
+
|
|
53
|
+
function close(): void {
|
|
54
|
+
destructor.execute()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function abortAllPendings(): void {
|
|
58
|
+
controller.abort()
|
|
59
|
+
}
|
|
23
60
|
}
|
|
24
61
|
|
|
25
62
|
export function createBatchClient<DataType>(
|
|
26
63
|
piscina: Piscina
|
|
27
|
-
, { expectedVersion, channel }: {
|
|
64
|
+
, { expectedVersion, channel, timeout }: {
|
|
28
65
|
expectedVersion?: string
|
|
29
66
|
channel?: string
|
|
67
|
+
timeout?: number
|
|
30
68
|
} = {}
|
|
31
|
-
): DelightRPC.BatchClient<DataType
|
|
69
|
+
): [client: DelightRPC.BatchClient<DataType>, close: () => void] {
|
|
70
|
+
const destructor = new SyncDestructor()
|
|
71
|
+
|
|
72
|
+
const controller = new AbortController()
|
|
73
|
+
destructor.defer(abortAllPendings)
|
|
74
|
+
|
|
32
75
|
const client = new DelightRPC.BatchClient<DataType>(
|
|
33
|
-
|
|
76
|
+
async function send(request) {
|
|
77
|
+
const destructor = new SyncDestructor()
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const mergedSignal = raceAbortSignals([
|
|
81
|
+
isntUndefined(timeout) && timeoutSignal(timeout)
|
|
82
|
+
, controller.signal
|
|
83
|
+
])
|
|
84
|
+
mergedSignal.addEventListener('abort', sendAbort)
|
|
85
|
+
destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort))
|
|
86
|
+
|
|
87
|
+
return await piscina.run(request, { signal: mergedSignal })
|
|
88
|
+
} finally {
|
|
89
|
+
destructor.execute()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function sendAbort(): Promise<void> {
|
|
93
|
+
const abort = DelightRPC.createAbort(request.id, channel)
|
|
94
|
+
await piscina.run(abort).catch(pass)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
34
97
|
, {
|
|
35
98
|
expectedVersion
|
|
36
99
|
, channel
|
|
37
100
|
}
|
|
38
101
|
)
|
|
39
102
|
|
|
40
|
-
return client
|
|
41
|
-
}
|
|
103
|
+
return [client, close]
|
|
42
104
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
105
|
+
function close(): void {
|
|
106
|
+
destructor.execute()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function abortAllPendings(): void {
|
|
110
|
+
controller.abort()
|
|
111
|
+
}
|
|
47
112
|
}
|
package/src/server.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as DelightRPC from 'delight-rpc'
|
|
2
2
|
import { isntNull } from '@blackglory/prelude'
|
|
3
|
+
import { SyncDestructor } from 'extra-defer'
|
|
4
|
+
import { HashMap } from '@blackglory/structures'
|
|
3
5
|
|
|
4
6
|
export function createServer<IAPI extends object>(
|
|
5
7
|
api: DelightRPC.ImplementationOf<IAPI>
|
|
@@ -9,22 +11,62 @@ export function createServer<IAPI extends object>(
|
|
|
9
11
|
channel?: string | RegExp | typeof DelightRPC.AnyChannel
|
|
10
12
|
ownPropsOnly?: boolean
|
|
11
13
|
} = {}
|
|
12
|
-
): (req: unknown) => Promise<unknown
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
): [handler: (req: unknown) => Promise<unknown>, close: () => void] {
|
|
15
|
+
const destructor = new SyncDestructor()
|
|
16
|
+
|
|
17
|
+
const channelIdToController: HashMap<
|
|
18
|
+
{
|
|
19
|
+
channel?: string
|
|
20
|
+
, id: string
|
|
21
|
+
}
|
|
22
|
+
, AbortController
|
|
23
|
+
> = new HashMap(({ channel, id }) => JSON.stringify([channel, id]))
|
|
24
|
+
destructor.defer(abortAllPendings)
|
|
25
|
+
|
|
26
|
+
return [handler, close]
|
|
27
|
+
|
|
28
|
+
function close(): void {
|
|
29
|
+
destructor.execute()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function abortAllPendings(): void {
|
|
33
|
+
for (const controller of channelIdToController.values()) {
|
|
34
|
+
controller.abort()
|
|
35
|
+
}
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
channelIdToController.clear()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function handler(message: unknown): Promise<unknown> {
|
|
41
|
+
if (DelightRPC.isRequest(message) || DelightRPC.isBatchRequest(message)) {
|
|
42
|
+
const destructor = new SyncDestructor()
|
|
43
|
+
|
|
44
|
+
const controller = new AbortController()
|
|
45
|
+
channelIdToController.set(message, controller)
|
|
46
|
+
destructor.defer(() => channelIdToController.delete(message))
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const response = await DelightRPC.createResponse(
|
|
50
|
+
api
|
|
51
|
+
, message
|
|
52
|
+
, {
|
|
53
|
+
parameterValidators
|
|
54
|
+
, version
|
|
55
|
+
, channel
|
|
56
|
+
, ownPropsOnly
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if (isntNull(response)) {
|
|
61
|
+
return response
|
|
62
|
+
}
|
|
63
|
+
} finally {
|
|
64
|
+
destructor.execute()
|
|
65
|
+
}
|
|
66
|
+
} else if (DelightRPC.isAbort(message)) {
|
|
67
|
+
if (DelightRPC.matchChannel(message, channel)) {
|
|
68
|
+
channelIdToController.get(message)?.abort()
|
|
69
|
+
channelIdToController.delete(message)
|
|
28
70
|
}
|
|
29
71
|
}
|
|
30
72
|
}
|