@zimic/interceptor 1.2.3-canary.7 → 1.2.4-canary.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/dist/{chunk-ZU6IGW27.js → chunk-IPL73BDI.js} +99 -73
- package/dist/chunk-IPL73BDI.js.map +1 -0
- package/dist/{chunk-XCYZ5L2M.mjs → chunk-OTZ5Z633.mjs} +99 -73
- package/dist/chunk-OTZ5Z633.mjs.map +1 -0
- package/dist/cli.js +18 -18
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/http.js +101 -99
- package/dist/http.js.map +1 -1
- package/dist/http.mjs +101 -99
- package/dist/http.mjs.map +1 -1
- package/dist/server.js +6 -6
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/http/interceptor/HttpInterceptorClient.ts +1 -1
- package/src/http/interceptor/HttpInterceptorStore.ts +6 -3
- package/src/http/interceptor/RemoteHttpInterceptor.ts +1 -1
- package/src/http/interceptorWorker/HttpInterceptorWorker.ts +3 -5
- package/src/http/interceptorWorker/LocalHttpInterceptorWorker.ts +16 -12
- package/src/http/interceptorWorker/RemoteHttpInterceptorWorker.ts +20 -43
- package/src/server/InterceptorServer.ts +39 -21
- package/src/server/types/schema.ts +1 -1
- package/src/webSocket/WebSocketClient.ts +4 -4
- package/src/webSocket/WebSocketHandler.ts +115 -65
- package/src/webSocket/WebSocketServer.ts +2 -3
- package/src/webSocket/types.ts +9 -14
- package/dist/chunk-XCYZ5L2M.mjs.map +0 -1
- package/dist/chunk-ZU6IGW27.js.map +0 -1
package/dist/server.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkIPL73BDI_js = require('./chunk-IPL73BDI.js');
|
|
4
4
|
require('./chunk-DGUM43GV.js');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
Object.defineProperty(exports, "DEFAULT_ACCESS_CONTROL_HEADERS", {
|
|
9
9
|
enumerable: true,
|
|
10
|
-
get: function () { return
|
|
10
|
+
get: function () { return chunkIPL73BDI_js.DEFAULT_ACCESS_CONTROL_HEADERS; }
|
|
11
11
|
});
|
|
12
12
|
Object.defineProperty(exports, "DEFAULT_PREFLIGHT_STATUS_CODE", {
|
|
13
13
|
enumerable: true,
|
|
14
|
-
get: function () { return
|
|
14
|
+
get: function () { return chunkIPL73BDI_js.DEFAULT_PREFLIGHT_STATUS_CODE; }
|
|
15
15
|
});
|
|
16
16
|
Object.defineProperty(exports, "NotRunningInterceptorServerError", {
|
|
17
17
|
enumerable: true,
|
|
18
|
-
get: function () { return
|
|
18
|
+
get: function () { return chunkIPL73BDI_js.NotRunningInterceptorServerError_default; }
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(exports, "RunningInterceptorServerError", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunkIPL73BDI_js.RunningInterceptorServerError_default; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "createInterceptorServer", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunkIPL73BDI_js.createInterceptorServer; }
|
|
27
27
|
});
|
|
28
28
|
//# sourceMappingURL=server.js.map
|
|
29
29
|
//# sourceMappingURL=server.js.map
|
package/dist/server.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default as NotRunningInterceptorServerError, RunningInterceptorServerError_default as RunningInterceptorServerError, createInterceptorServer } from './chunk-
|
|
1
|
+
export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default as NotRunningInterceptorServerError, RunningInterceptorServerError_default as RunningInterceptorServerError, createInterceptorServer } from './chunk-OTZ5Z633.mjs';
|
|
2
2
|
import './chunk-BJTO5JO5.mjs';
|
|
3
3
|
//# sourceMappingURL=server.mjs.map
|
|
4
4
|
//# sourceMappingURL=server.mjs.map
|
package/package.json
CHANGED
|
@@ -376,7 +376,7 @@ class HttpInterceptorClient<
|
|
|
376
376
|
|
|
377
377
|
clear() {
|
|
378
378
|
const clearPromises: Promise<AnyHttpRequestHandlerClient | void>[] = [
|
|
379
|
-
Promise.resolve(this.workerOrThrow.
|
|
379
|
+
Promise.resolve(this.workerOrThrow.clearHandlers({ interceptor: this })),
|
|
380
380
|
];
|
|
381
381
|
|
|
382
382
|
for (const method of HTTP_METHODS) {
|
|
@@ -25,10 +25,13 @@ class HttpInterceptorStore {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
private getRemoteWorkerKey(baseURL: URL, options: RemoteWorkerKeyOptions) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const key = [`${baseURL.origin}${baseURL.pathname}`];
|
|
29
|
+
|
|
30
|
+
if (options.auth) {
|
|
31
|
+
key.push(options.auth.token);
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
|
|
34
|
+
return key.join(':');
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
remoteWorker(baseURL: URL, options: RemoteWorkerKeyOptions) {
|
|
@@ -24,7 +24,7 @@ class RemoteHttpInterceptor<Schema extends HttpSchema> implements PublicRemoteHt
|
|
|
24
24
|
baseURL,
|
|
25
25
|
createWorker: () => {
|
|
26
26
|
return this.store.getOrCreateRemoteWorker({
|
|
27
|
-
serverURL:
|
|
27
|
+
serverURL: baseURL,
|
|
28
28
|
auth: this._auth,
|
|
29
29
|
});
|
|
30
30
|
},
|
|
@@ -183,11 +183,9 @@ abstract class HttpInterceptorWorker {
|
|
|
183
183
|
return interceptor.onUnhandledRequest;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
abstract clearHandlers(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
interceptor: HttpInterceptorClient<Schema>,
|
|
190
|
-
): PossiblePromise<void>;
|
|
186
|
+
abstract clearHandlers<Schema extends HttpSchema>(options?: {
|
|
187
|
+
interceptor?: HttpInterceptorClient<Schema>;
|
|
188
|
+
}): PossiblePromise<void>;
|
|
191
189
|
|
|
192
190
|
abstract get interceptorsWithHandlers(): AnyHttpInterceptorClient[];
|
|
193
191
|
|
|
@@ -50,6 +50,8 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
get internalWorkerOrThrow() {
|
|
53
|
+
/* istanbul ignore if -- @preserve
|
|
54
|
+
* Trying to access the internal worker when it does not exist should not happen. */
|
|
53
55
|
if (!this.internalWorker) {
|
|
54
56
|
throw new NotRunningHttpInterceptorError();
|
|
55
57
|
}
|
|
@@ -248,22 +250,24 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
252
|
|
|
251
|
-
clearHandlers(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
clearInterceptorHandlers<Schema extends HttpSchema>(interceptor: HttpInterceptorClient<Schema>) {
|
|
253
|
+
clearHandlers<Schema extends HttpSchema>(
|
|
254
|
+
options: {
|
|
255
|
+
interceptor?: HttpInterceptorClient<Schema>;
|
|
256
|
+
} = {},
|
|
257
|
+
) {
|
|
260
258
|
if (!this.isRunning) {
|
|
261
259
|
throw new NotRunningHttpInterceptorError();
|
|
262
260
|
}
|
|
263
261
|
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
|
|
262
|
+
if (options.interceptor === undefined) {
|
|
263
|
+
for (const handlers of Object.values(this.httpHandlersByMethod)) {
|
|
264
|
+
handlers.length = 0;
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
for (const methodHandlers of Object.values(this.httpHandlersByMethod)) {
|
|
268
|
+
const groupToRemoveIndex = methodHandlers.findIndex((group) => group.interceptor === options.interceptor);
|
|
269
|
+
removeArrayIndex(methodHandlers, groupToRemoveIndex);
|
|
270
|
+
}
|
|
267
271
|
}
|
|
268
272
|
}
|
|
269
273
|
|
|
@@ -55,14 +55,14 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
55
55
|
|
|
56
56
|
async start() {
|
|
57
57
|
await super.sharedStart(async () => {
|
|
58
|
+
this.webSocketClient.onChannel('event', 'interceptors/responses/create', this.createResponse);
|
|
59
|
+
this.webSocketClient.onChannel('event', 'interceptors/responses/unhandled', this.handleUnhandledServerRequest);
|
|
60
|
+
|
|
58
61
|
await this.webSocketClient.start({
|
|
59
62
|
parameters: this.auth ? { token: this.auth.token } : undefined,
|
|
60
63
|
waitForAuthentication: true,
|
|
61
64
|
});
|
|
62
65
|
|
|
63
|
-
this.webSocketClient.onEvent('interceptors/responses/create', this.createResponse);
|
|
64
|
-
this.webSocketClient.onEvent('interceptors/responses/unhandled', this.handleUnhandledServerRequest);
|
|
65
|
-
|
|
66
66
|
this.platform = this.readPlatform();
|
|
67
67
|
this.isRunning = true;
|
|
68
68
|
});
|
|
@@ -120,10 +120,10 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
120
120
|
|
|
121
121
|
async stop() {
|
|
122
122
|
await super.sharedStop(async () => {
|
|
123
|
-
|
|
123
|
+
this.webSocketClient.offChannel('event', 'interceptors/responses/create', this.createResponse);
|
|
124
|
+
this.webSocketClient.offChannel('event', 'interceptors/responses/unhandled', this.handleUnhandledServerRequest);
|
|
124
125
|
|
|
125
|
-
this.
|
|
126
|
-
this.webSocketClient.offEvent('interceptors/responses/unhandled', this.handleUnhandledServerRequest);
|
|
126
|
+
await this.clearHandlers();
|
|
127
127
|
|
|
128
128
|
await this.webSocketClient.stop();
|
|
129
129
|
|
|
@@ -167,45 +167,22 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
async clearHandlers(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
this.httpHandlers.clear();
|
|
176
|
-
|
|
177
|
-
if (!this.webSocketClient.isRunning) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
await this.webSocketClient.request('interceptors/workers/reset', undefined);
|
|
183
|
-
} catch (error) {
|
|
184
|
-
/* istanbul ignore next -- @preserve
|
|
185
|
-
*
|
|
186
|
-
* If the socket is closed before receiving a response, the message is aborted with an error. This can happen if
|
|
187
|
-
* we send a request message and the interceptor server closes the socket before sending a response. In this case,
|
|
188
|
-
* we can safely ignore the error because we know that the server is shutting down and resetting is no longer
|
|
189
|
-
* necessary.
|
|
190
|
-
*
|
|
191
|
-
* Due to the rare nature of this edge case, we can't reliably reproduce it in tests. */
|
|
192
|
-
const isMessageAbortError = error instanceof WebSocketMessageAbortError;
|
|
193
|
-
|
|
194
|
-
/* istanbul ignore next -- @preserve */
|
|
195
|
-
if (!isMessageAbortError) {
|
|
196
|
-
throw error;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async clearInterceptorHandlers<Schema extends HttpSchema>(interceptor: HttpInterceptorClient<Schema>) {
|
|
170
|
+
async clearHandlers<Schema extends HttpSchema>(
|
|
171
|
+
options: {
|
|
172
|
+
interceptor?: HttpInterceptorClient<Schema>;
|
|
173
|
+
} = {},
|
|
174
|
+
) {
|
|
202
175
|
if (!this.isRunning) {
|
|
203
176
|
throw new NotRunningHttpInterceptorError();
|
|
204
177
|
}
|
|
205
178
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
179
|
+
if (options.interceptor === undefined) {
|
|
180
|
+
this.httpHandlers.clear();
|
|
181
|
+
} else {
|
|
182
|
+
for (const handler of this.httpHandlers.values()) {
|
|
183
|
+
if (handler.interceptor === options.interceptor) {
|
|
184
|
+
this.httpHandlers.delete(handler.id);
|
|
185
|
+
}
|
|
209
186
|
}
|
|
210
187
|
}
|
|
211
188
|
|
|
@@ -213,7 +190,7 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
213
190
|
return;
|
|
214
191
|
}
|
|
215
192
|
|
|
216
|
-
const
|
|
193
|
+
const handlersToRecommit = Array.from<HttpHandler, HttpHandlerCommit>(this.httpHandlers.values(), (handler) => ({
|
|
217
194
|
id: handler.id,
|
|
218
195
|
baseURL: handler.baseURL,
|
|
219
196
|
method: handler.method,
|
|
@@ -221,7 +198,7 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
221
198
|
}));
|
|
222
199
|
|
|
223
200
|
try {
|
|
224
|
-
await this.webSocketClient.request('interceptors/workers/reset',
|
|
201
|
+
await this.webSocketClient.request('interceptors/workers/reset', handlersToRecommit);
|
|
225
202
|
} catch (error) {
|
|
226
203
|
/* istanbul ignore next -- @preserve
|
|
227
204
|
*
|
|
@@ -164,20 +164,21 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
private async startHttpServer() {
|
|
167
|
+
this.httpServerOrThrow.on('request', this.handleHttpRequest);
|
|
168
|
+
|
|
167
169
|
await startHttpServer(this.httpServerOrThrow, {
|
|
168
170
|
hostname: this.hostname,
|
|
169
171
|
port: this.port,
|
|
170
172
|
});
|
|
171
|
-
this.port = getHttpServerPort(this.httpServerOrThrow);
|
|
172
173
|
|
|
173
|
-
this.
|
|
174
|
+
this.port = getHttpServerPort(this.httpServerOrThrow);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
private startWebSocketServer() {
|
|
177
|
-
this.webSocketServerOrThrow.
|
|
178
|
+
this.webSocketServerOrThrow.onChannel('event', 'interceptors/workers/commit', this.commitWorker);
|
|
179
|
+
this.webSocketServerOrThrow.onChannel('event', 'interceptors/workers/reset', this.resetWorker);
|
|
178
180
|
|
|
179
|
-
this.webSocketServerOrThrow.
|
|
180
|
-
this.webSocketServerOrThrow.onEvent('interceptors/workers/reset', this.resetWorker);
|
|
181
|
+
this.webSocketServerOrThrow.start();
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
private commitWorker = (
|
|
@@ -193,25 +194,42 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
193
194
|
};
|
|
194
195
|
|
|
195
196
|
private resetWorker = (
|
|
196
|
-
|
|
197
|
+
{ data: handlersToRecommit }: WebSocketEventMessage<InterceptorServerWebSocketSchema, 'interceptors/workers/reset'>,
|
|
197
198
|
socket: Socket,
|
|
198
199
|
) => {
|
|
199
|
-
this.
|
|
200
|
+
this.registerWorkerSocketIfUnknown(socket);
|
|
201
|
+
|
|
202
|
+
this.webSocketServerOrThrow.emitSocket('abortRequests', socket, {
|
|
203
|
+
shouldAbortRequest: (request) => {
|
|
204
|
+
const isResponseCreationRequest = this.webSocketServerOrThrow.isChannelEvent(
|
|
205
|
+
request,
|
|
206
|
+
'interceptors/responses/create',
|
|
207
|
+
);
|
|
200
208
|
|
|
201
|
-
|
|
202
|
-
|
|
209
|
+
/* istanbul ignore if -- @preserve
|
|
210
|
+
* While resetting a worker, there could be other types of requests in progress. These are not guaranteed to
|
|
211
|
+
* exist and are not related to handler resets, so we let them continue. */
|
|
212
|
+
if (!isResponseCreationRequest) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
203
215
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
// TODO: create a test with two interceptors, one for each path,, and reset only one of them.
|
|
217
|
+
const isHandlerStillCommitted = handlersToRecommit.some(
|
|
218
|
+
/* istanbul ignore next -- @preserve
|
|
219
|
+
* Ensuring this function is called in tests is difficult because it requires clearing or stopping a worker
|
|
220
|
+
* at the exact moment a request is being handled, in a scenario when there are other handlers still
|
|
221
|
+
* committed. */
|
|
222
|
+
(handler) => request.data.handlerId === handler.id,
|
|
223
|
+
);
|
|
224
|
+
return !isHandlerStillCommitted;
|
|
225
|
+
},
|
|
226
|
+
});
|
|
213
227
|
|
|
214
|
-
this.
|
|
228
|
+
this.removeHttpHandlersBySocket(socket);
|
|
229
|
+
|
|
230
|
+
for (const handler of handlersToRecommit) {
|
|
231
|
+
this.registerHttpHandler(handler, socket);
|
|
232
|
+
}
|
|
215
233
|
|
|
216
234
|
return {};
|
|
217
235
|
};
|
|
@@ -263,8 +281,8 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
263
281
|
}
|
|
264
282
|
|
|
265
283
|
private async stopWebSocketServer() {
|
|
266
|
-
this.webSocketServerOrThrow.
|
|
267
|
-
this.webSocketServerOrThrow.
|
|
284
|
+
this.webSocketServerOrThrow.offChannel('event', 'interceptors/workers/commit', this.commitWorker);
|
|
285
|
+
this.webSocketServerOrThrow.offChannel('event', 'interceptors/workers/reset', this.resetWorker);
|
|
268
286
|
|
|
269
287
|
await this.webSocketServerOrThrow.stop();
|
|
270
288
|
|
|
@@ -49,11 +49,11 @@ class WebSocketClient<Schema extends WebSocketSchema> extends WebSocketHandler<S
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async stop() {
|
|
52
|
-
super.
|
|
52
|
+
super.offAny();
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (this.socket) {
|
|
55
|
+
await super.closeClientSockets([this.socket]);
|
|
56
|
+
}
|
|
57
57
|
|
|
58
58
|
this.socket = undefined;
|
|
59
59
|
}
|
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
WebSocketMessage,
|
|
27
27
|
} from './types';
|
|
28
28
|
|
|
29
|
+
interface WebSocketRequestAbortOptions<Schema extends WebSocketSchema> {
|
|
30
|
+
shouldAbortRequest?: (request: WebSocketEventMessage<Schema>) => boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
30
34
|
private sockets = new Set<ClientSocket>();
|
|
31
35
|
|
|
@@ -40,7 +44,7 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
40
44
|
} = {};
|
|
41
45
|
|
|
42
46
|
private socketListeners = {
|
|
43
|
-
|
|
47
|
+
abortRequests: new Map<ClientSocket, Set<(options: WebSocketRequestAbortOptions<Schema>) => void>>(),
|
|
44
48
|
};
|
|
45
49
|
|
|
46
50
|
protected constructor(options: { socketTimeout?: number; messageTimeout?: number }) {
|
|
@@ -71,11 +75,14 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
71
75
|
socket.addEventListener('error', handleSocketError);
|
|
72
76
|
|
|
73
77
|
const handleSocketClose = () => {
|
|
78
|
+
this.sockets.delete(socket);
|
|
79
|
+
|
|
80
|
+
this.emitSocket('abortRequests', socket);
|
|
81
|
+
this.socketListeners.abortRequests.delete(socket);
|
|
82
|
+
|
|
74
83
|
socket.removeEventListener('message', handleSocketMessage);
|
|
75
84
|
socket.removeEventListener('close', handleSocketClose);
|
|
76
85
|
socket.removeEventListener('error', handleSocketError);
|
|
77
|
-
|
|
78
|
-
this.removeSocket(socket);
|
|
79
86
|
};
|
|
80
87
|
|
|
81
88
|
socket.addEventListener('close', handleSocketClose);
|
|
@@ -154,6 +161,13 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
154
161
|
);
|
|
155
162
|
}
|
|
156
163
|
|
|
164
|
+
isChannelEvent<Channel extends WebSocketChannel<Schema>>(
|
|
165
|
+
event: WebSocketEventMessage<Schema>,
|
|
166
|
+
channel: Channel,
|
|
167
|
+
): event is WebSocketEventMessage<Schema, Channel> {
|
|
168
|
+
return event.channel === channel;
|
|
169
|
+
}
|
|
170
|
+
|
|
157
171
|
private async notifyListeners(message: WebSocketMessage<Schema>, socket: ClientSocket) {
|
|
158
172
|
if (this.isReplyMessage(message)) {
|
|
159
173
|
await this.notifyReplyListeners(message, socket);
|
|
@@ -193,11 +207,6 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
193
207
|
await Promise.all(closingPromises);
|
|
194
208
|
}
|
|
195
209
|
|
|
196
|
-
private removeSocket(socket: ClientSocket) {
|
|
197
|
-
this.abortSocketMessages([socket]);
|
|
198
|
-
this.sockets.delete(socket);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
210
|
private async createEventMessage<Channel extends WebSocketChannel<Schema>>(
|
|
202
211
|
channel: Channel,
|
|
203
212
|
eventData: WebSocketEventMessage<Schema, Channel>['data'],
|
|
@@ -233,43 +242,67 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
233
242
|
const request = await this.createEventMessage(channel, requestData);
|
|
234
243
|
this.sendMessage(request, options.sockets);
|
|
235
244
|
|
|
236
|
-
const response = await this.waitForReply(channel, request
|
|
245
|
+
const response = await this.waitForReply(channel, request, options.sockets);
|
|
237
246
|
return response.data;
|
|
238
247
|
}
|
|
239
248
|
|
|
240
249
|
async waitForReply<Channel extends WebSocketChannelWithReply<Schema>>(
|
|
241
250
|
channel: Channel,
|
|
242
|
-
|
|
251
|
+
request: WebSocketEventMessage<Schema, Channel>,
|
|
243
252
|
sockets: Collection<ClientSocket> = this.sockets,
|
|
244
253
|
) {
|
|
245
254
|
return new Promise<WebSocketReplyMessage<Schema, Channel>>((resolve, reject) => {
|
|
246
255
|
const replyTimeout = setTimeout(() => {
|
|
247
|
-
this.
|
|
248
|
-
|
|
256
|
+
this.offChannel('reply', channel, replyListener); // eslint-disable-line @typescript-eslint/no-use-before-define
|
|
257
|
+
|
|
258
|
+
for (const socket of sockets) {
|
|
259
|
+
this.offSocket('abortRequests', socket, abortRequestsHandler); // eslint-disable-line @typescript-eslint/no-use-before-define
|
|
260
|
+
}
|
|
249
261
|
|
|
250
262
|
const timeoutError = new WebSocketMessageTimeoutError(this.messageTimeout);
|
|
251
263
|
reject(timeoutError);
|
|
252
264
|
}, this.messageTimeout);
|
|
253
265
|
|
|
254
|
-
const
|
|
266
|
+
const replyListener = this.onChannel('reply', channel, (message) => {
|
|
267
|
+
if (message.requestId !== request.id) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
255
271
|
clearTimeout(replyTimeout);
|
|
256
272
|
|
|
257
|
-
this.
|
|
258
|
-
|
|
273
|
+
this.offChannel('reply', channel, replyListener);
|
|
274
|
+
|
|
275
|
+
for (const socket of sockets) {
|
|
276
|
+
this.offSocket('abortRequests', socket, abortRequestsHandler); // eslint-disable-line @typescript-eslint/no-use-before-define
|
|
277
|
+
}
|
|
259
278
|
|
|
260
|
-
|
|
279
|
+
resolve(message);
|
|
261
280
|
});
|
|
262
281
|
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
282
|
+
const abortRequestsHandler = (options: WebSocketRequestAbortOptions<Schema>) => {
|
|
283
|
+
const shouldAbortRequest = options.shouldAbortRequest === undefined || options.shouldAbortRequest(request);
|
|
284
|
+
|
|
285
|
+
/* istanbul ignore if -- @preserve
|
|
286
|
+
* Aborting requests is highly non-deterministic because it depends on specific timing of socket events. */
|
|
287
|
+
if (!shouldAbortRequest) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
clearTimeout(replyTimeout);
|
|
266
292
|
|
|
267
|
-
|
|
268
|
-
this.offAbortSocketMessages(sockets, abortListener);
|
|
293
|
+
this.offChannel('reply', channel, replyListener);
|
|
269
294
|
|
|
270
|
-
|
|
295
|
+
for (const socket of sockets) {
|
|
296
|
+
this.offSocket('abortRequests', socket, abortRequestsHandler);
|
|
271
297
|
}
|
|
272
|
-
|
|
298
|
+
|
|
299
|
+
const abortError = new WebSocketMessageAbortError();
|
|
300
|
+
reject(abortError);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
for (const socket of sockets) {
|
|
304
|
+
this.onSocket('abortRequests', socket, abortRequestsHandler);
|
|
305
|
+
}
|
|
273
306
|
});
|
|
274
307
|
}
|
|
275
308
|
|
|
@@ -322,12 +355,22 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
322
355
|
}
|
|
323
356
|
}
|
|
324
357
|
|
|
325
|
-
|
|
358
|
+
onChannel<Channel extends WebSocketChannel<Schema>, Listener extends WebSocketEventMessageListener<Schema, Channel>>(
|
|
359
|
+
type: 'event',
|
|
326
360
|
channel: Channel,
|
|
327
|
-
|
|
328
|
-
): Listener
|
|
361
|
+
eventListener: Listener,
|
|
362
|
+
): Listener;
|
|
363
|
+
onChannel<Channel extends WebSocketChannel<Schema>, Listener extends WebSocketReplyMessageListener<Schema, Channel>>(
|
|
364
|
+
type: 'reply',
|
|
365
|
+
channel: Channel,
|
|
366
|
+
replyListener: Listener,
|
|
367
|
+
): Listener;
|
|
368
|
+
onChannel<
|
|
369
|
+
Channel extends WebSocketChannel<Schema>,
|
|
370
|
+
Listener extends WebSocketEventMessageListener<Schema, Channel> & WebSocketReplyMessageListener<Schema, Channel>,
|
|
371
|
+
>(type: 'event' | 'reply', channel: Channel, listener: Listener): Listener {
|
|
329
372
|
const listeners = this.getOrCreateChannelListeners<Channel>(channel);
|
|
330
|
-
listeners.
|
|
373
|
+
listeners[type].add(listener);
|
|
331
374
|
return listener;
|
|
332
375
|
}
|
|
333
376
|
|
|
@@ -344,59 +387,66 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
344
387
|
return listeners;
|
|
345
388
|
}
|
|
346
389
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
Listener extends WebSocketReplyMessageListener<Schema, Channel>,
|
|
350
|
-
>(channel: Channel, listener: Listener): Listener {
|
|
351
|
-
const listeners = this.getOrCreateChannelListeners<Channel>(channel);
|
|
352
|
-
listeners.reply.add(listener);
|
|
353
|
-
return listener;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
offEvent<Channel extends WebSocketChannel<Schema>>(
|
|
390
|
+
offChannel<Channel extends WebSocketChannel<Schema>>(
|
|
391
|
+
type: 'event',
|
|
357
392
|
channel: Channel,
|
|
358
|
-
|
|
359
|
-
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
393
|
+
eventListener: WebSocketEventMessageListener<Schema, Channel>,
|
|
394
|
+
): void;
|
|
395
|
+
offChannel<Channel extends WebSocketChannel<Schema>>(
|
|
396
|
+
type: 'reply',
|
|
397
|
+
channel: Channel,
|
|
398
|
+
replyListener: WebSocketReplyMessageListener<Schema, Channel>,
|
|
399
|
+
): void;
|
|
400
|
+
offChannel<Channel extends WebSocketChannel<Schema>>(
|
|
401
|
+
type: 'event' | 'reply',
|
|
364
402
|
channel: Channel,
|
|
365
|
-
listener: WebSocketReplyMessageListener<Schema, Channel>,
|
|
403
|
+
listener: WebSocketEventMessageListener<Schema, Channel> & WebSocketReplyMessageListener<Schema, Channel>,
|
|
366
404
|
) {
|
|
367
|
-
this.channelListeners[channel]
|
|
405
|
+
const listeners = this.channelListeners[channel];
|
|
406
|
+
listeners?.[type].delete(listener);
|
|
368
407
|
}
|
|
369
408
|
|
|
370
|
-
|
|
371
|
-
|
|
409
|
+
onSocket<Listener extends (options: WebSocketRequestAbortOptions<Schema>) => void>(
|
|
410
|
+
type: 'abortRequests',
|
|
411
|
+
socket: ClientSocket,
|
|
412
|
+
listener: Listener,
|
|
413
|
+
): Listener {
|
|
414
|
+
const listeners = this.getOrCreateSocketListeners(type, socket);
|
|
415
|
+
listeners.add(listener);
|
|
416
|
+
return listener;
|
|
372
417
|
}
|
|
373
418
|
|
|
374
|
-
private
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
this.socketListeners.messageAbort.set(socket, listeners);
|
|
380
|
-
}
|
|
381
|
-
listeners.add(listener);
|
|
419
|
+
private getOrCreateSocketListeners(type: 'abortRequests', socket: ClientSocket) {
|
|
420
|
+
const listeners = this.socketListeners[type].get(socket) ?? new Set();
|
|
421
|
+
|
|
422
|
+
if (!this.socketListeners[type].has(socket)) {
|
|
423
|
+
this.socketListeners[type].set(socket, listeners);
|
|
382
424
|
}
|
|
383
425
|
|
|
384
|
-
return
|
|
426
|
+
return listeners;
|
|
385
427
|
}
|
|
386
428
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
429
|
+
offSocket<Listener extends (options: WebSocketRequestAbortOptions<Schema>) => void>(
|
|
430
|
+
type: 'abortRequests',
|
|
431
|
+
socket: ClientSocket,
|
|
432
|
+
listener: Listener,
|
|
433
|
+
) {
|
|
434
|
+
const listeners = this.socketListeners[type].get(socket);
|
|
435
|
+
listeners?.delete(listener);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
emitSocket(type: 'abortRequests', socket: ClientSocket, options: WebSocketRequestAbortOptions<Schema> = {}) {
|
|
439
|
+
for (const listener of this.socketListeners[type].get(socket) ?? []) {
|
|
440
|
+
listener(options);
|
|
390
441
|
}
|
|
391
442
|
}
|
|
392
443
|
|
|
393
|
-
|
|
394
|
-
|
|
444
|
+
offAny() {
|
|
445
|
+
this.channelListeners = {};
|
|
395
446
|
|
|
396
|
-
for (const
|
|
397
|
-
const listeners
|
|
398
|
-
|
|
399
|
-
listener(abortError);
|
|
447
|
+
for (const listenersBySocket of Object.values(this.socketListeners)) {
|
|
448
|
+
for (const listeners of listenersBySocket.values()) {
|
|
449
|
+
listeners.clear();
|
|
400
450
|
}
|
|
401
451
|
}
|
|
402
452
|
}
|