@zimic/interceptor 1.2.3 → 1.2.4-canary.1
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-XCYZ5L2M.mjs → chunk-EIYQEPK2.mjs} +111 -85
- package/dist/chunk-EIYQEPK2.mjs.map +1 -0
- package/dist/{chunk-ZU6IGW27.js → chunk-OKZEX5DQ.js} +111 -85
- package/dist/chunk-OKZEX5DQ.js.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 +112 -110
- package/dist/http.js.map +1 -1
- package/dist/http.mjs +112 -110
- 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/interceptor/errors/UnknownHttpInterceptorPlatformError.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/utils/http.ts +2 -2
- package/src/utils/webSocket.ts +3 -3
- package/src/webSocket/WebSocketClient.ts +4 -4
- package/src/webSocket/WebSocketHandler.ts +119 -69
- package/src/webSocket/WebSocketServer.ts +2 -3
- package/src/webSocket/errors/InvalidWebSocketMessageError.ts +8 -0
- package/src/webSocket/types.ts +9 -14
- package/dist/chunk-XCYZ5L2M.mjs.map +0 -1
- package/dist/chunk-ZU6IGW27.js.map +0 -1
- package/src/webSocket/errors/InvalidWebSocketMessage.ts +0 -8
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from '@/utils/webSocket';
|
|
13
13
|
|
|
14
14
|
import { WEB_SOCKET_CONTROL_MESSAGES, WebSocketControlMessage } from './constants';
|
|
15
|
-
import
|
|
15
|
+
import InvalidWebSocketMessageError from './errors/InvalidWebSocketMessageError';
|
|
16
16
|
import NotRunningWebSocketHandlerError from './errors/NotRunningWebSocketHandlerError';
|
|
17
17
|
import {
|
|
18
18
|
WebSocketEventMessageListener,
|
|
@@ -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);
|
|
@@ -109,7 +116,7 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
109
116
|
if (typeof data === 'string') {
|
|
110
117
|
return data;
|
|
111
118
|
} else {
|
|
112
|
-
throw new
|
|
119
|
+
throw new InvalidWebSocketMessageError(data);
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
|
|
@@ -119,11 +126,11 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
|
|
|
119
126
|
try {
|
|
120
127
|
parsedMessage = JSON.parse(stringifiedMessage) as unknown;
|
|
121
128
|
} catch {
|
|
122
|
-
throw new
|
|
129
|
+
throw new InvalidWebSocketMessageError(stringifiedMessage);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
if (!this.isMessage(parsedMessage)) {
|
|
126
|
-
throw new
|
|
133
|
+
throw new InvalidWebSocketMessageError(stringifiedMessage);
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
if (this.isReplyMessage(parsedMessage)) {
|
|
@@ -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
|
}
|
|
@@ -79,12 +79,11 @@ class WebSocketServer<Schema extends WebSocketSchema> extends WebSocketHandler<S
|
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
super.
|
|
83
|
-
super.abortSocketMessages();
|
|
82
|
+
super.offAny();
|
|
84
83
|
await super.closeClientSockets();
|
|
85
84
|
|
|
86
85
|
await closeServerSocket(this.webSocketServer, { timeout: this.socketTimeout });
|
|
87
|
-
|
|
86
|
+
|
|
88
87
|
this.webSocketServer = undefined;
|
|
89
88
|
}
|
|
90
89
|
}
|
package/src/webSocket/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JSONValue } from '@zimic/http';
|
|
2
2
|
import { PossiblePromise } from '@zimic/utils/types';
|
|
3
3
|
import type { WebSocket as ClientSocket } from 'isomorphic-ws';
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface WebSocketChannelFrame<Channel extends string> {
|
|
6
6
|
id: string;
|
|
7
7
|
channel: Channel;
|
|
8
8
|
}
|
|
@@ -10,14 +10,14 @@ export interface WebSocketChannelData<Channel extends string> {
|
|
|
10
10
|
export interface WebSocketEventMessage<
|
|
11
11
|
Schema extends WebSocketSchema,
|
|
12
12
|
Channel extends WebSocketChannel<Schema> = WebSocketChannel<Schema>,
|
|
13
|
-
> extends
|
|
13
|
+
> extends WebSocketChannelFrame<Channel> {
|
|
14
14
|
data: Schema[Channel]['event'];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface WebSocketReplyMessage<
|
|
18
18
|
Schema extends WebSocketSchema,
|
|
19
19
|
Channel extends WebSocketChannel<Schema> = WebSocketChannel<Schema>,
|
|
20
|
-
> extends
|
|
20
|
+
> extends WebSocketChannelFrame<Channel> {
|
|
21
21
|
data: Schema[Channel]['reply'];
|
|
22
22
|
requestId: string;
|
|
23
23
|
}
|
|
@@ -34,16 +34,7 @@ interface BaseWebSocketSchema {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export type WebSocketSchema<Schema extends BaseWebSocketSchema = BaseWebSocketSchema> =
|
|
38
|
-
WebSocketSchema.ConvertToStrict<Schema>;
|
|
39
|
-
|
|
40
|
-
export namespace WebSocketSchema {
|
|
41
|
-
export type ConvertToStrict<Schema extends BaseWebSocketSchema> = {
|
|
42
|
-
[Channel in keyof Schema]: {
|
|
43
|
-
[Key in keyof Schema[Channel]]: JSONSerialized<Schema[Channel][Key]>;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
}
|
|
37
|
+
export type WebSocketSchema<Schema extends BaseWebSocketSchema = BaseWebSocketSchema> = Schema;
|
|
47
38
|
|
|
48
39
|
export type WebSocketChannel<Schema extends WebSocketSchema> = keyof Schema & string;
|
|
49
40
|
|
|
@@ -65,3 +56,7 @@ export type WebSocketReplyMessageListener<Schema extends WebSocketSchema, Channe
|
|
|
65
56
|
message: WebSocketReplyMessage<Schema, Channel>,
|
|
66
57
|
socket: ClientSocket,
|
|
67
58
|
) => PossiblePromise<void>;
|
|
59
|
+
|
|
60
|
+
export type WebSocketMessageListener<Schema extends WebSocketSchema, Channel extends WebSocketChannel<Schema>> =
|
|
61
|
+
| WebSocketEventMessageListener<Schema, Channel>
|
|
62
|
+
| WebSocketReplyMessageListener<Schema, Channel>;
|