@zimic/interceptor 1.5.2-canary.0 → 1.5.2-canary.2

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.
Files changed (44) hide show
  1. package/dist/{chunk-3WCEMV5U.js → chunk-UJTX22M5.js} +71 -20
  2. package/dist/chunk-UJTX22M5.js.map +1 -0
  3. package/dist/{chunk-YQNDAJTN.mjs → chunk-V5VFQ6WK.mjs} +71 -20
  4. package/dist/chunk-V5VFQ6WK.mjs.map +1 -0
  5. package/dist/cli.js +16 -16
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cli.mjs +2 -2
  8. package/dist/cli.mjs.map +1 -1
  9. package/dist/http.d.ts +17 -5
  10. package/dist/http.js +366 -293
  11. package/dist/http.js.map +1 -1
  12. package/dist/http.mjs +365 -292
  13. package/dist/http.mjs.map +1 -1
  14. package/dist/server.js +6 -6
  15. package/dist/server.mjs +1 -1
  16. package/package.json +1 -1
  17. package/src/http/errors/HttpTimesCheckError.ts +9 -9
  18. package/src/http/index.ts +6 -1
  19. package/src/http/interceptor/HttpInterceptorImplementation.ts +74 -21
  20. package/src/http/interceptor/HttpInterceptorStore.ts +11 -13
  21. package/src/http/interceptor/LocalHttpInterceptor.ts +1 -10
  22. package/src/http/interceptor/RemoteHttpInterceptor.ts +16 -22
  23. package/src/http/interceptor/types/options.ts +1 -1
  24. package/src/http/interceptorWorker/HttpInterceptorWorker.ts +14 -3
  25. package/src/http/interceptorWorker/LocalHttpInterceptorWorker.ts +25 -93
  26. package/src/http/requestHandler/HttpRequestHandlerImplementation.ts +5 -9
  27. package/src/http/requestHandler/LocalHttpRequestHandler.ts +2 -5
  28. package/src/http/requestHandler/RemoteHttpRequestHandler.ts +17 -20
  29. package/src/http/requestHandler/types/public.ts +3 -8
  30. package/src/http/requestHandler/types/requests.ts +31 -3
  31. package/src/http/utils/logging.ts +54 -0
  32. package/src/interceptor/LocalMSWWorkerStore.ts +123 -0
  33. package/src/{http/interceptorWorker → interceptor}/types/msw.ts +1 -2
  34. package/src/server/utils/auth.ts +1 -1
  35. package/src/utils/fetch.ts +2 -2
  36. package/src/utils/logging.ts +0 -54
  37. package/src/utils/webSocket/WebSocketHandler.ts +9 -2
  38. package/src/utils/webSocket/WebSocketServer.ts +58 -12
  39. package/src/utils/webSocket/constants.ts +7 -0
  40. package/src/utils/webSocket/types.ts +1 -2
  41. package/src/utils/webSocket.ts +2 -2
  42. package/dist/chunk-3WCEMV5U.js.map +0 -1
  43. package/dist/chunk-YQNDAJTN.mjs.map +0 -1
  44. /package/src/{http/interceptorWorker → interceptor}/errors/UnregisteredBrowserServiceWorkerError.ts +0 -0
@@ -1,4 +1,3 @@
1
- import { HttpFormData, HttpHeaders, HttpSearchParams } from '@zimic/http';
2
1
  import { createCachedDynamicImport } from '@zimic/utils/import';
3
2
  import { Logger } from '@zimic/utils/logging';
4
3
  import color from 'picocolors';
@@ -9,59 +8,6 @@ export const logger = new Logger({
9
8
  prefix: color.cyan('[@zimic/interceptor]'),
10
9
  });
11
10
 
12
- function stringifyJSONToLog(value: unknown): string {
13
- return JSON.stringify(
14
- value,
15
- (_key, value) => {
16
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
17
- return stringifyValueToLog(value, {
18
- fallback: (value) => value as string,
19
- });
20
- },
21
- 2,
22
- )
23
- .replace(/\n\s*/g, ' ')
24
- .replace(/"(File { name: '.*?', type: '.*?', size: \d*? })"/g, '$1')
25
- .replace(/"(Blob { type: '.*?', size: \d*? })"/g, '$1');
26
- }
27
-
28
- export function stringifyValueToLog(
29
- value: unknown,
30
- options: {
31
- fallback?: (value: unknown) => string;
32
- includeClassName?: { searchParams?: boolean };
33
- } = {},
34
- ): string {
35
- const { fallback = stringifyJSONToLog, includeClassName } = options;
36
-
37
- if (value === null || value === undefined || typeof value !== 'object') {
38
- return String(value);
39
- }
40
-
41
- if (value instanceof HttpHeaders) {
42
- return stringifyValueToLog(value.toObject());
43
- }
44
-
45
- if (value instanceof HttpHeaders || value instanceof HttpSearchParams) {
46
- const prefix = (includeClassName?.searchParams ?? false) ? 'URLSearchParams ' : '';
47
- return `${prefix}${stringifyValueToLog(value.toObject())}`;
48
- }
49
-
50
- if (value instanceof HttpFormData) {
51
- return `FormData ${stringifyValueToLog(value.toObject())}`;
52
- }
53
-
54
- if (value instanceof File) {
55
- return `File { name: '${value.name}', type: '${value.type}', size: ${value.size} }`;
56
- }
57
-
58
- if (value instanceof Blob) {
59
- return `Blob { type: '${value.type}', size: ${value.size} }`;
60
- }
61
-
62
- return fallback(value);
63
- }
64
-
65
11
  const importUtil = createCachedDynamicImport(() => import('util'));
66
12
 
67
13
  export async function formatValueToLog(value: unknown, options: { colors?: boolean } = {}) {
@@ -64,7 +64,12 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
64
64
  };
65
65
  socket.addEventListener('message', handleSocketMessage);
66
66
 
67
- await openPromise;
67
+ try {
68
+ await openPromise;
69
+ } catch (error) {
70
+ socket.removeEventListener('message', handleSocketMessage);
71
+ throw error;
72
+ }
68
73
 
69
74
  /* istanbul ignore next -- @preserve
70
75
  * It is difficult to reliably simulate socket errors in tests. */
@@ -87,6 +92,8 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
87
92
  socket.addEventListener('close', handleSocketClose);
88
93
 
89
94
  this.sockets.add(socket);
95
+
96
+ return handleSocketClose;
90
97
  }
91
98
 
92
99
  private handleSocketMessage = async (socket: ClientSocket, rawMessage: ClientSocket.MessageEvent) => {
@@ -123,7 +130,7 @@ abstract class WebSocketHandler<Schema extends WebSocketSchema> {
123
130
  let parsedMessage: unknown;
124
131
 
125
132
  try {
126
- parsedMessage = JSON.parse(stringifiedMessage) as unknown;
133
+ parsedMessage = JSON.parse(stringifiedMessage);
127
134
  } catch {
128
135
  throw new InvalidWebSocketMessageError(stringifiedMessage);
129
136
  }
@@ -4,29 +4,41 @@ import ClientSocket from 'isomorphic-ws';
4
4
 
5
5
  import { closeServerSocket } from '@/utils/webSocket';
6
6
 
7
- import { WebSocketControlMessage } from './constants';
7
+ import { WEB_SOCKET_CLOSE_CODES, WebSocketControlMessage } from './constants';
8
8
  import { WebSocketSchema } from './types';
9
9
  import WebSocketHandler from './WebSocketHandler';
10
10
 
11
11
  const { WebSocketServer: ServerSocket } = ClientSocket;
12
12
 
13
+ function isSocketOpen(socket: ClientSocket) {
14
+ return socket.readyState === socket.OPEN;
15
+ }
16
+
13
17
  export type WebSocketServerAuthenticate = (
14
18
  socket: ClientSocket,
15
19
  request: IncomingMessage,
16
20
  ) => PossiblePromise<{ isValid: true } | { isValid: false; message: string }>;
17
21
 
22
+ export type WebSocketServerConnectionHandler = (
23
+ socket: ClientSocket,
24
+ request: IncomingMessage,
25
+ ) => PossiblePromise<{ handled: boolean }>;
26
+
18
27
  interface WebSocketServerOptions {
19
28
  httpServer: HttpServer;
20
29
  socketTimeout?: number;
21
30
  messageTimeout?: number;
22
31
  authenticate?: WebSocketServerAuthenticate;
32
+ handleConnection?: WebSocketServerConnectionHandler;
23
33
  }
24
34
 
25
35
  class WebSocketServer<Schema extends WebSocketSchema> extends WebSocketHandler<Schema> {
26
36
  private webSocketServer?: InstanceType<typeof ServerSocket>;
37
+ private isStopping = false;
27
38
 
28
39
  private httpServer: HttpServer;
29
40
  private authenticate?: WebSocketServerOptions['authenticate'];
41
+ private handleConnection?: WebSocketServerOptions['handleConnection'];
30
42
 
31
43
  constructor(options: WebSocketServerOptions) {
32
44
  super({
@@ -36,6 +48,7 @@ class WebSocketServer<Schema extends WebSocketSchema> extends WebSocketHandler<S
36
48
 
37
49
  this.httpServer = options.httpServer;
38
50
  this.authenticate = options.authenticate;
51
+ this.handleConnection = options.handleConnection;
39
52
  }
40
53
 
41
54
  get isRunning() {
@@ -54,19 +67,46 @@ class WebSocketServer<Schema extends WebSocketSchema> extends WebSocketHandler<S
54
67
  });
55
68
 
56
69
  webSocketServer.on('connection', async (socket, request) => {
57
- if (this.authenticate) {
58
- const result = await this.authenticate(socket, request);
70
+ socket.pause();
59
71
 
60
- if (!result.isValid) {
61
- socket.close(1008, result.message);
72
+ try {
73
+ if (this.authenticate) {
74
+ const result = await this.authenticate(socket, request);
75
+
76
+ if (!isSocketOpen(socket)) {
77
+ return;
78
+ }
79
+
80
+ if (!result.isValid) {
81
+ socket.resume();
82
+ socket.close(WEB_SOCKET_CLOSE_CODES.POLICY_VIOLATION, result.message);
83
+ return;
84
+ }
85
+ }
86
+
87
+ const connectionResult = await this.handleConnection?.(socket, request);
88
+
89
+ if (!isSocketOpen(socket)) {
62
90
  return;
63
91
  }
64
- }
65
92
 
66
- try {
67
- await super.registerSocket(socket);
93
+ if (connectionResult?.handled) {
94
+ return;
95
+ }
96
+
97
+ const unregisterSocket = await super.registerSocket(socket);
98
+
99
+ if (this.isStopping || !isSocketOpen(socket)) {
100
+ unregisterSocket();
101
+ return;
102
+ }
103
+
104
+ socket.resume();
68
105
  socket.send('socket:auth:valid' satisfies WebSocketControlMessage);
69
106
  } catch (error) {
107
+ socket.resume();
108
+ socket.close(WEB_SOCKET_CLOSE_CODES.INTERNAL_ERROR);
109
+
70
110
  webSocketServer.emit('error', error);
71
111
  }
72
112
  });
@@ -79,12 +119,18 @@ class WebSocketServer<Schema extends WebSocketSchema> extends WebSocketHandler<S
79
119
  return;
80
120
  }
81
121
 
82
- super.offAny();
83
- await super.closeClientSockets();
122
+ this.isStopping = true;
84
123
 
85
- await closeServerSocket(this.webSocketServer, { timeout: this.socketTimeout });
124
+ try {
125
+ super.offAny();
126
+ await super.closeClientSockets();
86
127
 
87
- this.webSocketServer = undefined;
128
+ await closeServerSocket(this.webSocketServer, { timeout: this.socketTimeout });
129
+
130
+ this.webSocketServer = undefined;
131
+ } finally {
132
+ this.isStopping = false;
133
+ }
88
134
  }
89
135
  }
90
136
 
@@ -1,2 +1,9 @@
1
1
  export const WEB_SOCKET_CONTROL_MESSAGES = Object.freeze(['socket:auth:valid'] as const);
2
2
  export type WebSocketControlMessage = (typeof WEB_SOCKET_CONTROL_MESSAGES)[number];
3
+
4
+ export const WEB_SOCKET_CLOSE_CODES = Object.freeze({
5
+ DEFAULT: 1000,
6
+ PROTOCOL_ERROR: 1002,
7
+ POLICY_VIOLATION: 1008,
8
+ INTERNAL_ERROR: 1011,
9
+ } as const);
@@ -1,5 +1,4 @@
1
- import { JSONValue } from '@zimic/http';
2
- import { PossiblePromise } from '@zimic/utils/types';
1
+ import type { JSONValue, PossiblePromise } from '@zimic/utils/types';
3
2
  import type { WebSocket as ClientSocket } from 'isomorphic-ws';
4
3
 
5
4
  export interface WebSocketChannelFrame<Channel extends string> {
@@ -1,6 +1,6 @@
1
1
  import ClientSocket, { type WebSocketServer as ServerSocket } from 'isomorphic-ws';
2
2
 
3
- import { WebSocketControlMessage } from '@/utils/webSocket/constants';
3
+ import { WEB_SOCKET_CLOSE_CODES, WebSocketControlMessage } from '@/utils/webSocket/constants';
4
4
  import UnauthorizedWebSocketConnectionError from '@/utils/webSocket/errors/UnauthorizedWebSocketConnectionError';
5
5
 
6
6
  class WebSocketTimeoutError extends Error {}
@@ -65,7 +65,7 @@ export async function waitForOpenClientSocket(
65
65
  }
66
66
 
67
67
  function handleClose(event: ClientSocket.CloseEvent) {
68
- const isUnauthorized = event.code === 1008;
68
+ const isUnauthorized = event.code === WEB_SOCKET_CLOSE_CODES.POLICY_VIOLATION;
69
69
 
70
70
  /* istanbul ignore else -- @preserve
71
71
  * An unauthorized close event is the only one we expect to happen here. */