@delight-rpc/child-process 0.7.1 → 0.7.3

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/lib/client.js CHANGED
@@ -3,41 +3,50 @@ import { Deferred } from 'extra-promise';
3
3
  import { CustomError } from '@blackglory/errors';
4
4
  import { raceAbortSignals, timeoutSignal, withAbortSignal } from 'extra-abort';
5
5
  import { isntUndefined } from '@blackglory/prelude';
6
+ import { SyncDestructor } from 'extra-defer';
6
7
  export function createClient(process, { parameterValidators, expectedVersion, channel, timeout } = {}) {
8
+ const destructor = new SyncDestructor();
7
9
  const pendings = new Map();
8
- process.on('message', handler);
10
+ destructor.defer(abortAllPendings);
11
+ process.on('message', receive);
12
+ destructor.defer(() => process.off('message', receive));
13
+ process.on('disconnect', abortAllPendings);
14
+ destructor.defer(() => process.off('disconnect', abortAllPendings));
9
15
  const client = DelightRPC.createClient(async function send(request, signal) {
16
+ const destructor = new SyncDestructor();
10
17
  const res = new Deferred();
11
18
  pendings.set(request.id, res);
19
+ destructor.defer(() => pendings.delete(request.id));
12
20
  try {
13
21
  process.send(request);
14
22
  const mergedSignal = raceAbortSignals([
15
23
  isntUndefined(timeout) && timeoutSignal(timeout),
16
24
  signal
17
25
  ]);
18
- mergedSignal.addEventListener('abort', () => {
19
- const abort = DelightRPC.createAbort(request.id, channel);
20
- process.send(abort);
21
- });
26
+ mergedSignal.addEventListener('abort', sendAbort);
27
+ destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort));
22
28
  return await withAbortSignal(mergedSignal, () => res);
23
29
  }
24
30
  finally {
25
- pendings.delete(request.id);
31
+ destructor.execute();
32
+ }
33
+ function sendAbort() {
34
+ const abort = DelightRPC.createAbort(request.id, channel);
35
+ process.send(abort);
26
36
  }
27
37
  }, {
28
38
  parameterValidators,
29
39
  expectedVersion,
30
40
  channel
31
41
  });
32
- return [client, close];
33
- function close() {
34
- process.off('message', handler);
35
- for (const [key, deferred] of pendings.entries()) {
42
+ return [client, () => destructor.execute()];
43
+ function abortAllPendings() {
44
+ for (const deferred of pendings.values()) {
36
45
  deferred.reject(new ClientClosed());
37
- pendings.delete(key);
38
46
  }
47
+ pendings.clear();
39
48
  }
40
- function handler(res) {
49
+ function receive(res) {
41
50
  var _a;
42
51
  if (DelightRPC.isResult(res) || DelightRPC.isError(res)) {
43
52
  (_a = pendings.get(res.id)) === null || _a === void 0 ? void 0 : _a.resolve(res);
@@ -45,38 +54,46 @@ export function createClient(process, { parameterValidators, expectedVersion, ch
45
54
  }
46
55
  }
47
56
  export function createBatchClient(process, { expectedVersion, channel, timeout } = {}) {
57
+ const destructor = new SyncDestructor();
48
58
  const pendings = new Map();
49
- process.on('message', handler);
59
+ destructor.defer(abortAllPendings);
60
+ process.on('message', receive);
61
+ destructor.defer(() => process.off('message', receive));
62
+ process.on('disconnect', abortAllPendings);
63
+ destructor.defer(() => process.off('disconnect', abortAllPendings));
50
64
  const client = new DelightRPC.BatchClient(async function send(request) {
65
+ const destructor = new SyncDestructor();
51
66
  const res = new Deferred();
52
67
  pendings.set(request.id, res);
68
+ destructor.defer(() => pendings.delete(request.id));
53
69
  try {
54
70
  process.send(request);
55
71
  const mergedSignal = raceAbortSignals([
56
72
  isntUndefined(timeout) && timeoutSignal(timeout)
57
73
  ]);
58
- mergedSignal.addEventListener('abort', () => {
59
- const abort = DelightRPC.createAbort(request.id, channel);
60
- process.send(abort);
61
- });
74
+ mergedSignal.addEventListener('abort', sendAbort);
75
+ destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort));
62
76
  return await withAbortSignal(mergedSignal, () => res);
63
77
  }
64
78
  finally {
65
- pendings.delete(request.id);
79
+ destructor.execute();
80
+ }
81
+ function sendAbort() {
82
+ const abort = DelightRPC.createAbort(request.id, channel);
83
+ process.send(abort);
66
84
  }
67
85
  }, {
68
86
  expectedVersion,
69
87
  channel
70
88
  });
71
- return [client, close];
72
- function close() {
73
- process.off('message', handler);
74
- for (const [key, deferred] of pendings.entries()) {
89
+ return [client, () => destructor.execute()];
90
+ function abortAllPendings() {
91
+ for (const deferred of pendings.values()) {
75
92
  deferred.reject(new ClientClosed());
76
- pendings.delete(key);
77
93
  }
94
+ pendings.clear();
78
95
  }
79
- function handler(res) {
96
+ function receive(res) {
80
97
  var _a;
81
98
  if (DelightRPC.isError(res) || DelightRPC.isBatchResponse(res)) {
82
99
  (_a = pendings.get(res.id)) === null || _a === void 0 ? void 0 : _a.resolve(res);
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/lib/server.js CHANGED
@@ -2,26 +2,29 @@ import * as DelightRPC from 'delight-rpc';
2
2
  import { isntNull, pass } from '@blackglory/prelude';
3
3
  import { AbortController } from 'extra-abort';
4
4
  import { HashMap } from '@blackglory/structures';
5
+ import { SyncDestructor } from 'extra-defer';
5
6
  export function createServer(api, process, { parameterValidators, version, channel, ownPropsOnly } = {}) {
7
+ const destructor = new SyncDestructor();
6
8
  const channelIdToController = new HashMap(({ channel, id }) => JSON.stringify([channel, id]));
7
- process.on('message', handleMessage);
9
+ destructor.defer(abortAllPendings);
10
+ process.on('message', receive);
11
+ destructor.defer(() => process.off('message', receive));
8
12
  process.on('disconnect', abortAllPendings);
9
- return () => {
10
- process.off('message', handleMessage);
11
- process.off('disconnect', abortAllPendings);
12
- abortAllPendings();
13
- };
13
+ destructor.defer(() => process.off('disconnect', abortAllPendings));
14
+ return () => destructor.execute();
14
15
  function abortAllPendings() {
15
16
  for (const controller of channelIdToController.values()) {
16
17
  controller.abort();
17
18
  }
18
19
  channelIdToController.clear();
19
20
  }
20
- async function handleMessage(message) {
21
+ async function receive(message) {
21
22
  var _a;
22
23
  if (DelightRPC.isRequest(message) || DelightRPC.isBatchRequest(message)) {
24
+ const destructor = new SyncDestructor();
23
25
  const controller = new AbortController();
24
26
  channelIdToController.set(message, controller);
27
+ destructor.defer(() => channelIdToController.delete(message));
25
28
  try {
26
29
  const result = await DelightRPC.createResponse(api, message, {
27
30
  parameterValidators,
@@ -44,7 +47,7 @@ export function createServer(api, process, { parameterValidators, version, chann
44
47
  }
45
48
  }
46
49
  finally {
47
- channelIdToController.delete(message);
50
+ destructor.execute();
48
51
  }
49
52
  }
50
53
  else if (DelightRPC.isAbort(message)) {
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;AAEzC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAEhD,MAAM,UAAU,YAAY,CAC1B,GAAsC,EACtC,OAAsC,EACtC,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,KAKjD,EAAE;IAEN,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;IAEnE,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACpC,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAA;IAC1C,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAA;QAC3C,gBAAgB,EAAE,CAAA;IACpB,CAAC,CAAA;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,aAAa,CAAC,OAAgB;;QAC3C,IAAI,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YACxE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;YACxC,qBAAqB,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;YAE9C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,cAAc,CAC5C,GAAG,EACH,OAAO,EACP;oBACE,mBAAmB;oBACnB,OAAO;oBACP,OAAO;oBACP,YAAY;oBACZ,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CACF,CAAA;gBAED,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBAErB,OAAO,CAAC,IAAK,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;wBAC1B,IAAI,GAAG,EAAE,CAAC;4BACR,IAAK,GAA6B,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;gCACrE,IAAI,EAAE,CAAA;4BACR,CAAC;iCAAM,CAAC;gCACN,MAAM,GAAG,CAAA;4BACX,CAAC;wBACH,CAAC;oBACH,CAAC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,qBAAqB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YACvC,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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delight-rpc/child-process",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "files": [
@@ -58,6 +58,7 @@
58
58
  "@blackglory/structures": "^0.14.13",
59
59
  "@delight-rpc/protocol": "^4.0.0",
60
60
  "extra-abort": "^0.4.1",
61
+ "extra-defer": "^0.3.1",
61
62
  "extra-promise": "^7.1.1"
62
63
  },
63
64
  "peerDependencies": {
package/src/client.ts CHANGED
@@ -5,6 +5,7 @@ import { CustomError } from '@blackglory/errors'
5
5
  import { IResponse, IError, IBatchRequest, IBatchResponse } from '@delight-rpc/protocol'
6
6
  import { raceAbortSignals, timeoutSignal, withAbortSignal } from 'extra-abort'
7
7
  import { isntUndefined } from '@blackglory/prelude'
8
+ import { SyncDestructor } from 'extra-defer'
8
9
 
9
10
  export function createClient<IAPI extends object>(
10
11
  process: ChildProcess | NodeJS.Process
@@ -15,14 +16,25 @@ export function createClient<IAPI extends object>(
15
16
  timeout?: number
16
17
  } = {}
17
18
  ): [client: DelightRPC.ClientProxy<IAPI>, close: () => void] {
19
+ const destructor = new SyncDestructor()
20
+
18
21
  const pendings: Map<string, Deferred<IResponse<unknown>>> = new Map()
22
+ destructor.defer(abortAllPendings)
23
+
24
+ process.on('message', receive)
25
+ destructor.defer(() => process.off('message', receive))
19
26
 
20
- process.on('message', handler)
27
+ process.on('disconnect', abortAllPendings)
28
+ destructor.defer(() => process.off('disconnect', abortAllPendings))
21
29
 
22
30
  const client = DelightRPC.createClient<IAPI>(
23
31
  async function send(request, signal) {
32
+ const destructor = new SyncDestructor()
33
+
24
34
  const res = new Deferred<IResponse<unknown>>()
25
35
  pendings.set(request.id, res)
36
+ destructor.defer(() => pendings.delete(request.id))
37
+
26
38
  try {
27
39
  process.send!(request)
28
40
 
@@ -30,14 +42,17 @@ export function createClient<IAPI extends object>(
30
42
  isntUndefined(timeout) && timeoutSignal(timeout)
31
43
  , signal
32
44
  ])
33
- mergedSignal.addEventListener('abort', () => {
34
- const abort = DelightRPC.createAbort(request.id, channel)
35
- process.send!(abort)
36
- })
45
+ mergedSignal.addEventListener('abort', sendAbort)
46
+ destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort))
37
47
 
38
48
  return await withAbortSignal(mergedSignal, () => res)
39
49
  } finally {
40
- pendings.delete(request.id)
50
+ destructor.execute()
51
+ }
52
+
53
+ function sendAbort(): void {
54
+ const abort = DelightRPC.createAbort(request.id, channel)
55
+ process.send!(abort)
41
56
  }
42
57
  }
43
58
  , {
@@ -47,18 +62,17 @@ export function createClient<IAPI extends object>(
47
62
  }
48
63
  )
49
64
 
50
- return [client, close]
65
+ return [client, () => destructor.execute()]
51
66
 
52
- function close() {
53
- process.off('message', handler)
54
-
55
- for (const [key, deferred] of pendings.entries()) {
67
+ function abortAllPendings(): void {
68
+ for (const deferred of pendings.values()) {
56
69
  deferred.reject(new ClientClosed())
57
- pendings.delete(key)
58
70
  }
71
+
72
+ pendings.clear()
59
73
  }
60
74
 
61
- function handler(res: unknown): void {
75
+ function receive(res: unknown): void {
62
76
  if (DelightRPC.isResult(res) || DelightRPC.isError(res)) {
63
77
  pendings.get(res.id)?.resolve(res)
64
78
  }
@@ -73,34 +87,42 @@ export function createBatchClient<DataType>(
73
87
  timeout?: number
74
88
  } = {}
75
89
  ): [client: DelightRPC.BatchClient<DataType>, close: () => void] {
76
- const pendings: Map<
77
- string
78
- , Deferred<IError | IBatchResponse<unknown>>
79
- > = new Map()
90
+ const destructor = new SyncDestructor()
80
91
 
81
- process.on('message', handler)
92
+ const pendings: Map<string, Deferred<IError | IBatchResponse<unknown>>> = new Map()
93
+ destructor.defer(abortAllPendings)
94
+
95
+ process.on('message', receive)
96
+ destructor.defer(() => process.off('message', receive))
97
+
98
+ process.on('disconnect', abortAllPendings)
99
+ destructor.defer(() => process.off('disconnect', abortAllPendings))
82
100
 
83
101
  const client = new DelightRPC.BatchClient(
84
102
  async function send(request: IBatchRequest<unknown>) {
85
- const res = new Deferred<
86
- | IError
87
- | IBatchResponse<unknown>
88
- >()
103
+ const destructor = new SyncDestructor()
104
+
105
+ const res = new Deferred<IError | IBatchResponse<unknown>>()
89
106
  pendings.set(request.id, res)
107
+ destructor.defer(() => pendings.delete(request.id))
108
+
90
109
  try {
91
110
  process.send!(request)
92
111
 
93
112
  const mergedSignal = raceAbortSignals([
94
113
  isntUndefined(timeout) && timeoutSignal(timeout)
95
114
  ])
96
- mergedSignal.addEventListener('abort', () => {
97
- const abort = DelightRPC.createAbort(request.id, channel)
98
- process.send!(abort)
99
- })
115
+ mergedSignal.addEventListener('abort', sendAbort)
116
+ destructor.defer(() => mergedSignal.removeEventListener('abort', sendAbort))
100
117
 
101
118
  return await withAbortSignal(mergedSignal, () => res)
102
119
  } finally {
103
- pendings.delete(request.id)
120
+ destructor.execute()
121
+ }
122
+
123
+ function sendAbort(): void {
124
+ const abort = DelightRPC.createAbort(request.id, channel)
125
+ process.send!(abort)
104
126
  }
105
127
  }
106
128
  , {
@@ -109,18 +131,17 @@ export function createBatchClient<DataType>(
109
131
  }
110
132
  )
111
133
 
112
- return [client, close]
134
+ return [client, () => destructor.execute()]
113
135
 
114
- function close() {
115
- process.off('message', handler)
116
-
117
- for (const [key, deferred] of pendings.entries()) {
136
+ function abortAllPendings(): void {
137
+ for (const deferred of pendings.values()) {
118
138
  deferred.reject(new ClientClosed())
119
- pendings.delete(key)
120
139
  }
140
+
141
+ pendings.clear()
121
142
  }
122
143
 
123
- function handler(res: unknown): void {
144
+ function receive(res: unknown): void {
124
145
  if (DelightRPC.isError(res) || DelightRPC.isBatchResponse(res)) {
125
146
  pendings.get(res.id)?.resolve(res)
126
147
  }
package/src/server.ts CHANGED
@@ -3,6 +3,7 @@ import { ChildProcess } from 'child_process'
3
3
  import { isntNull, pass } from '@blackglory/prelude'
4
4
  import { AbortController } from 'extra-abort'
5
5
  import { HashMap } from '@blackglory/structures'
6
+ import { SyncDestructor } from 'extra-defer'
6
7
 
7
8
  export function createServer<IAPI extends object>(
8
9
  api: DelightRPC.ImplementationOf<IAPI>
@@ -14,6 +15,8 @@ export function createServer<IAPI extends object>(
14
15
  ownPropsOnly?: boolean
15
16
  } = {}
16
17
  ): () => void {
18
+ const destructor = new SyncDestructor()
19
+
17
20
  const channelIdToController: HashMap<
18
21
  {
19
22
  channel?: string
@@ -21,14 +24,15 @@ export function createServer<IAPI extends object>(
21
24
  }
22
25
  , AbortController
23
26
  > = new HashMap(({ channel, id }) => JSON.stringify([channel, id]))
27
+ destructor.defer(abortAllPendings)
28
+
29
+ process.on('message', receive)
30
+ destructor.defer(() => process.off('message', receive))
24
31
 
25
- process.on('message', handleMessage)
26
32
  process.on('disconnect', abortAllPendings)
27
- return () => {
28
- process.off('message', handleMessage)
29
- process.off('disconnect', abortAllPendings)
30
- abortAllPendings()
31
- }
33
+ destructor.defer(() => process.off('disconnect', abortAllPendings))
34
+
35
+ return () => destructor.execute()
32
36
 
33
37
  function abortAllPendings(): void {
34
38
  for (const controller of channelIdToController.values()) {
@@ -38,10 +42,13 @@ export function createServer<IAPI extends object>(
38
42
  channelIdToController.clear()
39
43
  }
40
44
 
41
- async function handleMessage(message: unknown): Promise<void> {
45
+ async function receive(message: unknown): Promise<void> {
42
46
  if (DelightRPC.isRequest(message) || DelightRPC.isBatchRequest(message)) {
47
+ const destructor = new SyncDestructor()
48
+
43
49
  const controller = new AbortController()
44
50
  channelIdToController.set(message, controller)
51
+ destructor.defer(() => channelIdToController.delete(message))
45
52
 
46
53
  try {
47
54
  const result = await DelightRPC.createResponse(
@@ -69,7 +76,7 @@ export function createServer<IAPI extends object>(
69
76
  })
70
77
  }
71
78
  } finally {
72
- channelIdToController.delete(message)
79
+ destructor.execute()
73
80
  }
74
81
  } else if (DelightRPC.isAbort(message)) {
75
82
  if (DelightRPC.matchChannel(message, channel)) {