msw 2.3.0-ws.rc-1 → 2.3.0-ws.rc-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 (178) hide show
  1. package/README.md +8 -3
  2. package/cli/init.js +1 -1
  3. package/lib/browser/index.d.mts +4 -4
  4. package/lib/browser/index.js +64 -59
  5. package/lib/browser/index.js.map +1 -1
  6. package/lib/browser/index.mjs +64 -59
  7. package/lib/browser/index.mjs.map +1 -1
  8. package/lib/core/{GraphQLHandler-Cbu12sb0.d.ts → GraphQLHandler-3gvpA65n.d.ts} +3 -3
  9. package/lib/core/{GraphQLHandler-QGQY_9Rc.d.mts → GraphQLHandler-4DPdxG0R.d.mts} +3 -3
  10. package/lib/core/{HttpResponse-BWB1yDNM.d.mts → HttpResponse-aJY-D0oG.d.ts} +3 -3
  11. package/lib/core/{HttpResponse-DeJBWGN5.d.ts → HttpResponse-xuSipbNt.d.mts} +3 -3
  12. package/lib/core/HttpResponse.d.mts +1 -1
  13. package/lib/core/HttpResponse.d.ts +1 -1
  14. package/lib/core/HttpResponse.js.map +1 -1
  15. package/lib/core/HttpResponse.mjs.map +1 -1
  16. package/lib/core/SetupApi.d.mts +1 -1
  17. package/lib/core/SetupApi.d.ts +1 -1
  18. package/lib/core/bypass.js +6 -1
  19. package/lib/core/bypass.js.map +1 -1
  20. package/lib/core/bypass.mjs +6 -1
  21. package/lib/core/bypass.mjs.map +1 -1
  22. package/lib/core/getResponse.d.mts +1 -1
  23. package/lib/core/getResponse.d.ts +1 -1
  24. package/lib/core/getResponse.js +2 -2
  25. package/lib/core/getResponse.js.map +1 -1
  26. package/lib/core/getResponse.mjs +2 -2
  27. package/lib/core/getResponse.mjs.map +1 -1
  28. package/lib/core/graphql.d.mts +8 -6
  29. package/lib/core/graphql.d.ts +8 -6
  30. package/lib/core/graphql.js.map +1 -1
  31. package/lib/core/graphql.mjs.map +1 -1
  32. package/lib/core/handlers/GraphQLHandler.d.mts +2 -2
  33. package/lib/core/handlers/GraphQLHandler.d.ts +2 -2
  34. package/lib/core/handlers/GraphQLHandler.js.map +1 -1
  35. package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
  36. package/lib/core/handlers/HttpHandler.d.mts +1 -1
  37. package/lib/core/handlers/HttpHandler.d.ts +1 -1
  38. package/lib/core/handlers/HttpHandler.js +1 -1
  39. package/lib/core/handlers/HttpHandler.js.map +1 -1
  40. package/lib/core/handlers/HttpHandler.mjs +1 -1
  41. package/lib/core/handlers/HttpHandler.mjs.map +1 -1
  42. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  43. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  44. package/lib/core/handlers/WebSocketHandler.d.mts +11 -17
  45. package/lib/core/handlers/WebSocketHandler.d.ts +11 -17
  46. package/lib/core/handlers/WebSocketHandler.js +9 -12
  47. package/lib/core/handlers/WebSocketHandler.js.map +1 -1
  48. package/lib/core/handlers/WebSocketHandler.mjs +9 -12
  49. package/lib/core/handlers/WebSocketHandler.mjs.map +1 -1
  50. package/lib/core/http.d.mts +1 -1
  51. package/lib/core/http.d.ts +1 -1
  52. package/lib/core/index.d.mts +4 -4
  53. package/lib/core/index.d.ts +4 -4
  54. package/lib/core/index.js +2 -0
  55. package/lib/core/index.js.map +1 -1
  56. package/lib/core/index.mjs +4 -0
  57. package/lib/core/index.mjs.map +1 -1
  58. package/lib/core/passthrough.d.mts +1 -1
  59. package/lib/core/passthrough.d.ts +1 -1
  60. package/lib/core/typeUtils.d.mts +6 -1
  61. package/lib/core/typeUtils.d.ts +6 -1
  62. package/lib/core/typeUtils.js.map +1 -1
  63. package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
  64. package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
  65. package/lib/core/utils/HttpResponse/decorators.js +4 -1
  66. package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
  67. package/lib/core/utils/HttpResponse/decorators.mjs +4 -1
  68. package/lib/core/utils/HttpResponse/decorators.mjs.map +1 -1
  69. package/lib/core/utils/executeHandlers.d.mts +1 -1
  70. package/lib/core/utils/executeHandlers.d.ts +1 -1
  71. package/lib/core/utils/handleRequest.d.mts +1 -1
  72. package/lib/core/utils/handleRequest.d.ts +1 -1
  73. package/lib/core/utils/handleRequest.js.map +1 -1
  74. package/lib/core/utils/handleRequest.mjs.map +1 -1
  75. package/lib/core/utils/handleWebSocketEvent.d.mts +8 -2
  76. package/lib/core/utils/handleWebSocketEvent.d.ts +8 -2
  77. package/lib/core/utils/handleWebSocketEvent.js +20 -17
  78. package/lib/core/utils/handleWebSocketEvent.js.map +1 -1
  79. package/lib/core/utils/handleWebSocketEvent.mjs +21 -22
  80. package/lib/core/utils/handleWebSocketEvent.mjs.map +1 -1
  81. package/lib/core/utils/internal/mergeRight.js +15 -12
  82. package/lib/core/utils/internal/mergeRight.js.map +1 -1
  83. package/lib/core/utils/internal/mergeRight.mjs +15 -12
  84. package/lib/core/utils/internal/mergeRight.mjs.map +1 -1
  85. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +2 -2
  86. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +2 -2
  87. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  88. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  89. package/lib/core/utils/internal/requestHandlerUtils.d.mts +1 -1
  90. package/lib/core/utils/internal/requestHandlerUtils.d.ts +1 -1
  91. package/lib/core/utils/logging/getTimestamp.d.mts +4 -1
  92. package/lib/core/utils/logging/getTimestamp.d.ts +4 -1
  93. package/lib/core/utils/logging/getTimestamp.js +6 -2
  94. package/lib/core/utils/logging/getTimestamp.js.map +1 -1
  95. package/lib/core/utils/logging/getTimestamp.mjs +6 -2
  96. package/lib/core/utils/logging/getTimestamp.mjs.map +1 -1
  97. package/lib/core/ws/utils/attachWebSocketLogger.d.mts +34 -0
  98. package/lib/core/ws/utils/attachWebSocketLogger.d.ts +34 -0
  99. package/lib/core/ws/utils/attachWebSocketLogger.js +211 -0
  100. package/lib/core/ws/utils/attachWebSocketLogger.js.map +1 -0
  101. package/lib/core/ws/utils/attachWebSocketLogger.mjs +191 -0
  102. package/lib/core/ws/utils/attachWebSocketLogger.mjs.map +1 -0
  103. package/lib/core/ws/utils/getMessageLength.d.mts +11 -0
  104. package/lib/core/ws/utils/getMessageLength.d.ts +11 -0
  105. package/lib/core/ws/utils/getMessageLength.js +33 -0
  106. package/lib/core/ws/utils/getMessageLength.js.map +1 -0
  107. package/lib/core/ws/utils/getMessageLength.mjs +13 -0
  108. package/lib/core/ws/utils/getMessageLength.mjs.map +1 -0
  109. package/lib/core/ws/utils/getPublicData.d.mts +5 -0
  110. package/lib/core/ws/utils/getPublicData.d.ts +5 -0
  111. package/lib/core/ws/utils/getPublicData.js +36 -0
  112. package/lib/core/ws/utils/getPublicData.js.map +1 -0
  113. package/lib/core/ws/utils/getPublicData.mjs +16 -0
  114. package/lib/core/ws/utils/getPublicData.mjs.map +1 -0
  115. package/lib/core/ws/utils/truncateMessage.d.mts +3 -0
  116. package/lib/core/ws/utils/truncateMessage.d.ts +3 -0
  117. package/lib/core/{utils/internal/randomId.js → ws/utils/truncateMessage.js} +11 -7
  118. package/lib/core/ws/utils/truncateMessage.js.map +1 -0
  119. package/lib/core/ws/utils/truncateMessage.mjs +11 -0
  120. package/lib/core/ws/utils/truncateMessage.mjs.map +1 -0
  121. package/lib/iife/index.js +508 -231
  122. package/lib/iife/index.js.map +1 -1
  123. package/lib/mockServiceWorker.js +8 -11
  124. package/lib/native/index.d.mts +4 -4
  125. package/lib/native/index.js +8 -2
  126. package/lib/native/index.js.map +1 -1
  127. package/lib/native/index.mjs +8 -2
  128. package/lib/native/index.mjs.map +1 -1
  129. package/lib/node/index.d.mts +6 -6
  130. package/lib/node/index.d.ts +2 -2
  131. package/lib/node/index.js +8 -2
  132. package/lib/node/index.js.map +1 -1
  133. package/lib/node/index.mjs +8 -2
  134. package/lib/node/index.mjs.map +1 -1
  135. package/package.json +8 -11
  136. package/src/browser/setupWorker/glossary.ts +4 -1
  137. package/src/browser/setupWorker/setupWorker.ts +19 -2
  138. package/src/browser/setupWorker/start/createRequestListener.ts +1 -1
  139. package/src/browser/setupWorker/start/createStartHandler.ts +9 -19
  140. package/src/browser/setupWorker/start/utils/createMessageChannel.ts +1 -1
  141. package/src/browser/utils/checkWorkerIntegrity.ts +34 -0
  142. package/src/core/HttpResponse.ts +3 -2
  143. package/src/core/bypass.test.ts +22 -0
  144. package/src/core/bypass.ts +9 -1
  145. package/src/core/getResponse.ts +2 -2
  146. package/src/core/graphql.ts +6 -3
  147. package/src/core/handlers/GraphQLHandler.test.ts +4 -5
  148. package/src/core/handlers/GraphQLHandler.ts +7 -4
  149. package/src/core/handlers/HttpHandler.test.ts +5 -5
  150. package/src/core/handlers/HttpHandler.ts +1 -1
  151. package/src/core/handlers/WebSocketHandler.ts +21 -39
  152. package/src/core/index.ts +6 -2
  153. package/src/core/typeUtils.ts +16 -10
  154. package/src/core/utils/HttpResponse/decorators.ts +8 -4
  155. package/src/core/utils/handleRequest.test.ts +14 -14
  156. package/src/core/utils/handleRequest.ts +1 -1
  157. package/src/core/utils/handleWebSocketEvent.ts +39 -29
  158. package/src/core/utils/internal/mergeRight.ts +16 -13
  159. package/src/core/utils/logging/getTimestamp.test.ts +20 -6
  160. package/src/core/utils/logging/getTimestamp.ts +11 -6
  161. package/src/core/ws/utils/attachWebSocketLogger.ts +262 -0
  162. package/src/core/ws/utils/getMessageLength.test.ts +16 -0
  163. package/src/core/ws/utils/getMessageLength.ts +19 -0
  164. package/src/core/ws/utils/getPublicData.test.ts +38 -0
  165. package/src/core/ws/utils/getPublicData.ts +17 -0
  166. package/src/core/ws/utils/truncateMessage.test.ts +12 -0
  167. package/src/core/ws/utils/truncateMessage.ts +9 -0
  168. package/src/mockServiceWorker.js +7 -10
  169. package/src/node/SetupServerApi.ts +4 -4
  170. package/src/node/SetupServerCommonApi.ts +6 -2
  171. package/src/node/glossary.ts +3 -3
  172. package/lib/core/utils/internal/randomId.d.mts +0 -3
  173. package/lib/core/utils/internal/randomId.d.ts +0 -3
  174. package/lib/core/utils/internal/randomId.js.map +0 -1
  175. package/lib/core/utils/internal/randomId.mjs +0 -7
  176. package/lib/core/utils/internal/randomId.mjs.map +0 -1
  177. package/src/browser/utils/requestIntegrityCheck.ts +0 -23
  178. package/src/core/utils/internal/randomId.ts +0 -3
@@ -45,3 +45,25 @@ it('returns bypassed request given request instance', async () => {
45
45
  'x-msw-intention': 'bypass',
46
46
  })
47
47
  })
48
+
49
+ it('allows modifying the bypassed request instance', async () => {
50
+ const original = new Request('http://localhost/resource', {
51
+ method: 'POST',
52
+ body: 'hello world',
53
+ })
54
+ const request = bypass(original, {
55
+ method: 'PUT',
56
+ headers: { 'x-modified-header': 'yes' },
57
+ })
58
+
59
+ expect(request.method).toBe('PUT')
60
+ expect(Object.fromEntries(request.headers.entries())).toEqual({
61
+ 'x-msw-intention': 'bypass',
62
+ 'x-modified-header': 'yes',
63
+ })
64
+ expect(original.bodyUsed).toBe(false)
65
+ expect(request.bodyUsed).toBe(false)
66
+
67
+ expect(await request.text()).toBe('hello world')
68
+ expect(original.bodyUsed).toBe(false)
69
+ })
@@ -15,7 +15,15 @@ export type BypassRequestInput = string | URL | Request
15
15
  * @see {@link https://mswjs.io/docs/api/bypass `bypass()` API reference}
16
16
  */
17
17
  export function bypass(input: BypassRequestInput, init?: RequestInit): Request {
18
- const request = input instanceof Request ? input : new Request(input, init)
18
+ // Always create a new Request instance.
19
+ // This way, the "init" modifications will propagate
20
+ // to the bypass request instance automatically.
21
+ const request = new Request(
22
+ // If given a Request instance, clone it not to exhaust
23
+ // the original request's body.
24
+ input instanceof Request ? input.clone() : input,
25
+ init,
26
+ )
19
27
 
20
28
  invariant(
21
29
  !request.bodyUsed,
@@ -1,6 +1,6 @@
1
+ import { createRequestId } from '@mswjs/interceptors'
1
2
  import type { RequestHandler } from './handlers/RequestHandler'
2
3
  import { executeHandlers } from './utils/executeHandlers'
3
- import { randomId } from './utils/internal/randomId'
4
4
 
5
5
  /**
6
6
  * Finds a response for the given request instance
@@ -15,7 +15,7 @@ export const getResponse = async (
15
15
  ): Promise<Response | undefined> => {
16
16
  const result = await executeHandlers({
17
17
  request,
18
- requestId: randomId(),
18
+ requestId: createRequestId(),
19
19
  handlers,
20
20
  })
21
21
 
@@ -31,7 +31,10 @@ export type GraphQLRequestHandler = <
31
31
  | GraphQLHandlerNameSelector
32
32
  | DocumentNode
33
33
  | TypedDocumentNode<Query, Variables>,
34
- resolver: GraphQLResponseResolver<Query, Variables>,
34
+ resolver: GraphQLResponseResolver<
35
+ [Query] extends [never] ? GraphQLQuery : Query,
36
+ Variables
37
+ >,
35
38
  options?: RequestHandlerOptions,
36
39
  ) => GraphQLHandler
37
40
 
@@ -41,7 +44,7 @@ export type GraphQLResponseResolver<
41
44
  > = ResponseResolver<
42
45
  GraphQLResolverExtras<Variables>,
43
46
  null,
44
- GraphQLResponseBody<Query>
47
+ GraphQLResponseBody<[Query] extends [never] ? GraphQLQuery : Query>
45
48
  >
46
49
 
47
50
  function createScopedGraphQLHandler(
@@ -61,7 +64,7 @@ function createScopedGraphQLHandler(
61
64
 
62
65
  function createGraphQLOperationHandler(url: Path) {
63
66
  return <
64
- Query extends Record<string, any>,
67
+ Query extends GraphQLQuery = GraphQLQuery,
65
68
  Variables extends GraphQLVariables = GraphQLVariables,
66
69
  >(
67
70
  resolver: ResponseResolver<
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @vitest-environment jsdom
3
3
  */
4
- import { encodeBuffer } from '@mswjs/interceptors'
4
+ import { createRequestId, encodeBuffer } from '@mswjs/interceptors'
5
5
  import { OperationTypeNode, parse } from 'graphql'
6
6
  import {
7
7
  GraphQLHandler,
@@ -9,7 +9,6 @@ import {
9
9
  GraphQLResolverExtras,
10
10
  isDocumentNode,
11
11
  } from './GraphQLHandler'
12
- import { randomId } from '../utils/internal/randomId'
13
12
  import { HttpResponse } from '../HttpResponse'
14
13
  import { ResponseResolver } from './RequestHandler'
15
14
 
@@ -737,7 +736,7 @@ describe('run', () => {
737
736
  userId: 'abc-123',
738
737
  },
739
738
  })
740
- const requestId = randomId()
739
+ const requestId = createRequestId()
741
740
  const result = await handler.run({ request, requestId })
742
741
 
743
742
  expect(result!.handler).toEqual(handler)
@@ -779,7 +778,7 @@ describe('run', () => {
779
778
  const request = createPostGraphQLRequest({
780
779
  query: LOGIN,
781
780
  })
782
- const requestId = randomId()
781
+ const requestId = createRequestId()
783
782
  const result = await handler.run({ request, requestId })
784
783
 
785
784
  expect(result).toBeNull()
@@ -827,7 +826,7 @@ describe('request', () => {
827
826
  `,
828
827
  })
829
828
 
830
- const requestId = randomId()
829
+ const requestId = createRequestId()
831
830
  await handler.run({ request, requestId })
832
831
 
833
832
  expect(matchAllResolver).toHaveBeenCalledTimes(1)
@@ -68,10 +68,13 @@ export interface GraphQLJsonRequestBody<Variables extends GraphQLVariables> {
68
68
  variables?: Variables
69
69
  }
70
70
 
71
- export interface GraphQLResponseBody<BodyType extends DefaultBodyType> {
72
- data?: BodyType | null
73
- errors?: readonly Partial<GraphQLError>[] | null
74
- }
71
+ export type GraphQLResponseBody<BodyType extends DefaultBodyType> =
72
+ | {
73
+ data?: BodyType | null
74
+ errors?: readonly Partial<GraphQLError>[] | null
75
+ }
76
+ | null
77
+ | undefined
75
78
 
76
79
  export function isDocumentNode(
77
80
  value: DocumentNode | any,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @vitest-environment jsdom
3
3
  */
4
- import { randomId } from '../utils/internal/randomId'
4
+ import { createRequestId } from '@mswjs/interceptors'
5
5
  import { HttpHandler, HttpRequestResolverExtras } from './HttpHandler'
6
6
  import { HttpResponse } from '..'
7
7
  import { ResponseResolver } from './RequestHandler'
@@ -152,7 +152,7 @@ describe('run', () => {
152
152
  test('returns a mocked response given a matching request', async () => {
153
153
  const handler = new HttpHandler('GET', '/user/:userId', resolver)
154
154
  const request = new Request(new URL('/user/abc-123', location.href))
155
- const requestId = randomId()
155
+ const requestId = createRequestId()
156
156
  const result = await handler.run({ request, requestId })
157
157
 
158
158
  expect(result!.handler).toEqual(handler)
@@ -176,7 +176,7 @@ describe('run', () => {
176
176
  const handler = new HttpHandler('POST', '/login', resolver)
177
177
  const result = await handler.run({
178
178
  request: new Request(new URL('/users', location.href)),
179
- requestId: randomId(),
179
+ requestId: createRequestId(),
180
180
  })
181
181
 
182
182
  expect(result).toBeNull()
@@ -186,7 +186,7 @@ describe('run', () => {
186
186
  const handler = new HttpHandler('GET', '/users', resolver)
187
187
  const result = await handler.run({
188
188
  request: new Request(new URL('/users', location.href)),
189
- requestId: randomId(),
189
+ requestId: createRequestId(),
190
190
  })
191
191
 
192
192
  expect(result?.parsedResult?.match?.params).toEqual({})
@@ -207,7 +207,7 @@ describe('run', () => {
207
207
  const run = async () => {
208
208
  const result = await handler.run({
209
209
  request: new Request(new URL('/users', location.href)),
210
- requestId: randomId(),
210
+ requestId: createRequestId(),
211
211
  })
212
212
  return result?.response?.text()
213
213
  }
@@ -102,7 +102,7 @@ export class HttpHandler extends RequestHandler<
102
102
  })
103
103
 
104
104
  devUtils.warn(
105
- `Found a redundant usage of query parameters in the request handler URL for "${method} ${path}". Please match against a path instead and access query parameters in the response resolver function using "req.url.searchParams".`,
105
+ `Found a redundant usage of query parameters in the request handler URL for "${method} ${path}". Please match against a path instead and access query parameters using "new URL(request.url).searchParams" instead. Learn more: https://mswjs.io/docs/recipes/query-parameters`,
106
106
  )
107
107
  }
108
108
 
@@ -1,47 +1,41 @@
1
1
  import { Emitter } from 'strict-event-emitter'
2
- import type {
3
- WebSocketClientConnection,
4
- WebSocketServerConnection,
5
- } from '@mswjs/interceptors/WebSocket'
2
+ import type { WebSocketConnectionData } from '@mswjs/interceptors/WebSocket'
6
3
  import {
7
4
  type Match,
8
5
  type Path,
9
6
  type PathParams,
10
7
  matchRequestUrl,
11
8
  } from '../utils/matching/matchRequestUrl'
9
+ import { getCallFrame } from '../utils/internal/getCallFrame'
12
10
 
13
11
  type WebSocketHandlerParsedResult = {
14
12
  match: Match
15
13
  }
16
14
 
17
15
  export type WebSocketHandlerEventMap = {
18
- connection: [
19
- args: {
20
- client: WebSocketClientConnection
21
- server: WebSocketServerConnection
22
- params: PathParams
23
- },
24
- ]
16
+ connection: [args: WebSocketHandlerConnection]
25
17
  }
26
18
 
27
- type WebSocketHandlerIncomingEvent = MessageEvent<{
28
- client: WebSocketClientConnection
29
- server: WebSocketServerConnection
30
- }>
19
+ interface WebSocketHandlerConnection extends WebSocketConnectionData {
20
+ params: PathParams
21
+ }
31
22
 
32
23
  export const kEmitter = Symbol('kEmitter')
33
24
  export const kDispatchEvent = Symbol('kDispatchEvent')
34
- export const kDefaultPrevented = Symbol('kDefaultPrevented')
25
+ export const kSender = Symbol('kSender')
35
26
 
36
27
  export class WebSocketHandler {
28
+ public callFrame?: string
29
+
37
30
  protected [kEmitter]: Emitter<WebSocketHandlerEventMap>
38
31
 
39
32
  constructor(private readonly url: Path) {
40
33
  this[kEmitter] = new Emitter()
34
+ this.callFrame = getCallFrame(new Error())
41
35
  }
42
36
 
43
37
  public parse(args: {
44
- event: WebSocketHandlerIncomingEvent
38
+ event: MessageEvent<WebSocketConnectionData>
45
39
  }): WebSocketHandlerParsedResult {
46
40
  const connection = args.event.data
47
41
  const match = matchRequestUrl(connection.client.url, this.url)
@@ -52,38 +46,26 @@ export class WebSocketHandler {
52
46
  }
53
47
 
54
48
  public predicate(args: {
55
- event: WebSocketHandlerIncomingEvent
49
+ event: MessageEvent<WebSocketConnectionData>
56
50
  parsedResult: WebSocketHandlerParsedResult
57
51
  }): boolean {
58
52
  return args.parsedResult.match.matches
59
53
  }
60
54
 
61
- async [kDispatchEvent](event: MessageEvent<any>): Promise<void> {
55
+ async [kDispatchEvent](
56
+ event: MessageEvent<WebSocketConnectionData>,
57
+ ): Promise<void> {
62
58
  const parsedResult = this.parse({ event })
63
- const shouldIntercept = this.predicate({ event, parsedResult })
64
-
65
- if (!shouldIntercept) {
66
- return
67
- }
68
-
69
- // Account for other matching event handlers that've already prevented this event.
70
- if (!Reflect.get(event, kDefaultPrevented)) {
71
- // At this point, the WebSocket connection URL has matched the handler.
72
- // Prevent the default behavior of establishing the connection as-is.
73
- // Use internal symbol because we aren't actually dispatching this
74
- // event. Events can only marked as cancelable and can be prevented
75
- // when dispatched on an EventTarget.
76
- Reflect.set(event, kDefaultPrevented, true)
77
- }
78
-
79
59
  const connection = event.data
80
60
 
81
- // Emit the connection event on the handler.
82
- // This is what the developer adds listeners for.
83
- this[kEmitter].emit('connection', {
61
+ const resolvedConnection: WebSocketHandlerConnection = {
84
62
  client: connection.client,
85
63
  server: connection.server,
86
64
  params: parsedResult.match.params || {},
87
- })
65
+ }
66
+
67
+ // Emit the connection event on the handler.
68
+ // This is what the developer adds listeners for.
69
+ this[kEmitter].emit('connection', resolvedConnection)
88
70
  }
89
71
  }
package/src/core/index.ts CHANGED
@@ -2,15 +2,19 @@ import { checkGlobals } from './utils/internal/checkGlobals'
2
2
 
3
3
  export { SetupApi } from './SetupApi'
4
4
 
5
- /* Request handlers */
5
+ /* HTTP handlers */
6
6
  export { RequestHandler } from './handlers/RequestHandler'
7
7
  export { http } from './http'
8
8
  export { HttpHandler, HttpMethods } from './handlers/HttpHandler'
9
9
  export { graphql } from './graphql'
10
10
  export { GraphQLHandler } from './handlers/GraphQLHandler'
11
11
 
12
- /* WebSocket */
12
+ /* WebSocket handler */
13
13
  export { ws } from './ws/ws'
14
+ export {
15
+ WebSocketHandler,
16
+ type WebSocketHandlerEventMap,
17
+ } from './handlers/WebSocketHandler'
14
18
 
15
19
  /* Utils */
16
20
  export { matchRequestUrl } from './utils/matching/matchRequestUrl'
@@ -8,13 +8,19 @@ export type RequiredDeep<
8
8
  > = Type extends Fn
9
9
  ? Type
10
10
  : /**
11
- * @note The "Fn" type satisfies the predicate below.
12
- * It must always come first, before the Record check.
13
- */
14
- Type extends Record<string, any>
15
- ? {
16
- [Key in keyof Type]-?: NonNullable<Type[Key]> extends NonNullable<U>
17
- ? NonNullable<Type[Key]>
18
- : RequiredDeep<NonNullable<Type[Key]>, U>
19
- }
20
- : Type
11
+ * @note The "Fn" type satisfies the predicate below.
12
+ * It must always come first, before the Record check.
13
+ */
14
+ Type extends Record<string, any>
15
+ ? {
16
+ [Key in keyof Type]-?: NonNullable<Type[Key]> extends NonNullable<U>
17
+ ? NonNullable<Type[Key]>
18
+ : RequiredDeep<NonNullable<Type[Key]>, U>
19
+ }
20
+ : Type
21
+
22
+ /**
23
+ * @fixme Remove this once TS 5.4 is the lowest supported version.
24
+ * Because "NoInfer" is a built-in type utility there.
25
+ */
26
+ export type NoInfer<T> = [T][T extends any ? 0 : never]
@@ -1,5 +1,6 @@
1
1
  import statuses from '@bundled-es-modules/statuses'
2
2
  import type { HttpResponseInit } from '../../HttpResponse'
3
+ import { Headers as HeadersPolyfill } from 'headers-polyfill'
3
4
 
4
5
  const { message } = statuses
5
6
 
@@ -40,10 +41,13 @@ export function decorateResponse(
40
41
  // Cookie forwarding is only relevant in the browser.
41
42
  if (typeof document !== 'undefined') {
42
43
  // Write the mocked response cookies to the document.
43
- // Note that Fetch API Headers will concatenate multiple "Set-Cookie"
44
- // headers into a single comma-separated string, just as it does
45
- // with any other multi-value headers.
46
- const responseCookies = init.headers.get('Set-Cookie')?.split(',') || []
44
+ // Use `headers-polyfill` to get the Set-Cookie header value correctly.
45
+ // This is an alternative until TypeScript 5.2
46
+ // and Node.js v20 become the minimum supported version
47
+ // and getSetCookie in Headers can be used directly.
48
+ const responseCookies = HeadersPolyfill.prototype.getSetCookie.call(
49
+ init.headers,
50
+ )
47
51
 
48
52
  for (const cookieString of responseCookies) {
49
53
  // No need to parse the cookie headers because it's defined
@@ -2,12 +2,12 @@
2
2
  * @vitest-environment jsdom
3
3
  */
4
4
  import { Emitter } from 'strict-event-emitter'
5
+ import { createRequestId } from '@mswjs/interceptors'
5
6
  import { LifeCycleEventsMap, SharedOptions } from '../sharedOptions'
6
7
  import { RequestHandler } from '../handlers/RequestHandler'
7
8
  import { http } from '../http'
8
9
  import { handleRequest, HandleRequestOptions } from './handleRequest'
9
10
  import { RequiredDeep } from '../typeUtils'
10
- import { randomId } from './internal/randomId'
11
11
  import { HttpResponse } from '../HttpResponse'
12
12
  import { passthrough } from '../passthrough'
13
13
 
@@ -51,7 +51,7 @@ afterEach(() => {
51
51
  test('returns undefined for a request with the "x-msw-intention" header equal to "bypass"', async () => {
52
52
  const { emitter, events } = setup()
53
53
 
54
- const requestId = randomId()
54
+ const requestId = createRequestId()
55
55
  const request = new Request(new URL('http://localhost/user'), {
56
56
  headers: new Headers({
57
57
  'x-msw-intention': 'bypass',
@@ -97,7 +97,7 @@ test('does not bypass a request with "x-msw-intention" header set to arbitrary v
97
97
 
98
98
  const result = await handleRequest(
99
99
  request,
100
- randomId(),
100
+ createRequestId(),
101
101
  handlers,
102
102
  options,
103
103
  emitter,
@@ -112,7 +112,7 @@ test('does not bypass a request with "x-msw-intention" header set to arbitrary v
112
112
  test('reports request as unhandled when it has no matching request handlers', async () => {
113
113
  const { emitter, events } = setup()
114
114
 
115
- const requestId = randomId()
115
+ const requestId = createRequestId()
116
116
  const request = new Request(new URL('http://localhost/user'))
117
117
  const handlers: Array<RequestHandler> = []
118
118
 
@@ -145,7 +145,7 @@ test('reports request as unhandled when it has no matching request handlers', as
145
145
  test('returns undefined on a request handler that returns no response', async () => {
146
146
  const { emitter, events } = setup()
147
147
 
148
- const requestId = randomId()
148
+ const requestId = createRequestId()
149
149
  const request = new Request(new URL('http://localhost/user'))
150
150
  const handlers: Array<RequestHandler> = [
151
151
  http.get('/user', () => {
@@ -184,7 +184,7 @@ test('returns undefined on a request handler that returns no response', async ()
184
184
  test('returns the mocked response for a request with a matching request handler', async () => {
185
185
  const { emitter, events } = setup()
186
186
 
187
- const requestId = randomId()
187
+ const requestId = createRequestId()
188
188
  const request = new Request(new URL('http://localhost/user'))
189
189
  const mockedResponse = HttpResponse.json({ firstName: 'John' })
190
190
  const handlers: Array<RequestHandler> = [
@@ -242,7 +242,7 @@ test('returns the mocked response for a request with a matching request handler'
242
242
  test('returns a transformed response if the "transformResponse" option is provided', async () => {
243
243
  const { emitter, events } = setup()
244
244
 
245
- const requestId = randomId()
245
+ const requestId = createRequestId()
246
246
  const request = new Request(new URL('http://localhost/user'))
247
247
  const mockedResponse = HttpResponse.json({ firstName: 'John' })
248
248
  const handlers: Array<RequestHandler> = [
@@ -325,7 +325,7 @@ test('returns a transformed response if the "transformResponse" option is provid
325
325
  it('returns undefined without warning on a passthrough request', async () => {
326
326
  const { emitter, events } = setup()
327
327
 
328
- const requestId = randomId()
328
+ const requestId = createRequestId()
329
329
  const request = new Request(new URL('http://localhost/user'))
330
330
  const handlers: Array<RequestHandler> = [
331
331
  http.get('/user', () => {
@@ -358,7 +358,7 @@ it('returns undefined without warning on a passthrough request', async () => {
358
358
  it('calls the handler with the requestId', async () => {
359
359
  const { emitter } = setup()
360
360
 
361
- const requestId = randomId()
361
+ const requestId = createRequestId()
362
362
  const request = new Request(new URL('http://localhost/user'))
363
363
  const handlerFn = vi.fn()
364
364
  const handlers: Array<RequestHandler> = [http.get('/user', handlerFn)]
@@ -390,7 +390,7 @@ it('marks the first matching one-time handler as used', async () => {
390
390
  })
391
391
  const handlers: Array<RequestHandler> = [oneTimeHandler, anotherHandler]
392
392
 
393
- const requestId = randomId()
393
+ const requestId = createRequestId()
394
394
  const request = new Request('http://localhost/resource')
395
395
  const firstResult = await handleRequest(
396
396
  request,
@@ -438,7 +438,7 @@ it('does not mark non-matching one-time handlers as used', async () => {
438
438
  )
439
439
  const handlers: Array<RequestHandler> = [oneTimeHandler, anotherHandler]
440
440
 
441
- const requestId = randomId()
441
+ const requestId = createRequestId()
442
442
  const firstResult = await handleRequest(
443
443
  new Request('http://localhost/another'),
444
444
  requestId,
@@ -481,7 +481,7 @@ it('handles parallel requests with one-time handlers', async () => {
481
481
  })
482
482
  const handlers: Array<RequestHandler> = [oneTimeHandler, anotherHandler]
483
483
 
484
- const requestId = randomId()
484
+ const requestId = createRequestId()
485
485
  const request = new Request('http://localhost/resource')
486
486
  const firstResultPromise = handleRequest(
487
487
  request,
@@ -526,7 +526,7 @@ describe('[Private] - resolutionContext - used for extensions', () => {
526
526
 
527
527
  const handlers: Array<RequestHandler> = [handler]
528
528
 
529
- const requestId = randomId()
529
+ const requestId = createRequestId()
530
530
  const request = new Request(new URL('/resource', baseUrl))
531
531
  const response = await handleRequest(
532
532
  request,
@@ -555,7 +555,7 @@ describe('[Private] - resolutionContext - used for extensions', () => {
555
555
 
556
556
  const handlers: Array<RequestHandler> = [handler]
557
557
 
558
- const requestId = randomId()
558
+ const requestId = createRequestId()
559
559
  const request = new Request(
560
560
  new URL('/resource', `http://not-the-base-url.com`),
561
561
  )
@@ -51,7 +51,7 @@ export async function handleRequest(
51
51
  ): Promise<Response | undefined> {
52
52
  emitter.emit('request:start', { request, requestId })
53
53
 
54
- // Perform bypassed requests (i.e. issued via "ctx.fetch") as-is.
54
+ // Perform bypassed requests (i.e. wrapped in "bypass()") as-is.
55
55
  if (request.headers.get('x-msw-intention') === 'bypass') {
56
56
  emitter.emit('request:end', { request, requestId })
57
57
  handleRequestOptions?.onPassthroughResponse?.(request)
@@ -1,45 +1,55 @@
1
+ import type { WebSocketConnectionData } from '@mswjs/interceptors/lib/browser/interceptors/WebSocket'
1
2
  import { RequestHandler } from '../handlers/RequestHandler'
2
- import {
3
- WebSocketHandler,
4
- kDefaultPrevented,
5
- kDispatchEvent,
6
- } from '../handlers/WebSocketHandler'
3
+ import { WebSocketHandler, kDispatchEvent } from '../handlers/WebSocketHandler'
7
4
  import { webSocketInterceptor } from '../ws/webSocketInterceptor'
8
5
 
9
- export function handleWebSocketEvent(
10
- getCurrentHandlers: () => Array<RequestHandler | WebSocketHandler>,
11
- ) {
6
+ interface HandleWebSocketEventOptions {
7
+ getHandlers: () => Array<RequestHandler | WebSocketHandler>
8
+ onMockedConnection: (connection: WebSocketConnectionData) => void
9
+ onPassthroughConnection: (onnection: WebSocketConnectionData) => void
10
+ }
11
+
12
+ export function handleWebSocketEvent(options: HandleWebSocketEventOptions) {
12
13
  webSocketInterceptor.on('connection', (connection) => {
13
- const handlers = getCurrentHandlers()
14
+ const handlers = options.getHandlers()
14
15
 
15
16
  const connectionEvent = new MessageEvent('connection', {
16
17
  data: connection,
17
- /**
18
- * @note This message event should be marked as "cancelable"
19
- * to have its default prevented using "event.preventDefault()".
20
- * There's a bug in Node.js that breaks the "cancelable" flag.
21
- * @see https://github.com/nodejs/node/issues/51767
22
- */
23
18
  })
24
19
 
25
- Object.defineProperty(connectionEvent, kDefaultPrevented, {
26
- enumerable: false,
27
- writable: true,
28
- value: false,
29
- })
20
+ // First, filter only those WebSocket handlers that
21
+ // match the "ws.link()" endpoint predicate. Don't dispatch
22
+ // anything yet so the logger can be attached to the connection
23
+ // before it potentially sends events.
24
+ const matchingHandlers = handlers.filter<WebSocketHandler>(
25
+ (handler): handler is WebSocketHandler => {
26
+ if (handler instanceof WebSocketHandler) {
27
+ return handler.predicate({
28
+ event: connectionEvent,
29
+ parsedResult: handler.parse({
30
+ event: connectionEvent,
31
+ }),
32
+ })
33
+ }
30
34
 
31
- // Iterate over the handlers and forward the connection
32
- // event to WebSocket event handlers. This is equivalent
33
- // to dispatching that event onto multiple listeners.
34
- for (const handler of handlers) {
35
- if (handler instanceof WebSocketHandler) {
35
+ return false
36
+ },
37
+ )
38
+
39
+ if (matchingHandlers.length > 0) {
40
+ options?.onMockedConnection(connection)
41
+
42
+ // Iterate over the handlers and forward the connection
43
+ // event to WebSocket event handlers. This is equivalent
44
+ // to dispatching that event onto multiple listeners.
45
+ for (const handler of matchingHandlers) {
36
46
  handler[kDispatchEvent](connectionEvent)
37
47
  }
38
- }
48
+ } else {
49
+ options?.onPassthroughConnection(connection)
39
50
 
40
- // If none of the "ws" handlers matched,
41
- // establish the WebSocket connection as-is.
42
- if (!Reflect.get(connectionEvent, kDefaultPrevented)) {
51
+ // If none of the "ws" handlers matched,
52
+ // establish the WebSocket connection as-is.
43
53
  connection.server.connect()
44
54
  connection.client.addEventListener('message', (event) => {
45
55
  connection.server.send(event.data)
@@ -8,20 +8,23 @@ export function mergeRight(
8
8
  left: Record<string, any>,
9
9
  right: Record<string, any>,
10
10
  ) {
11
- return Object.entries(right).reduce((result, [key, rightValue]) => {
12
- const leftValue = result[key]
11
+ return Object.entries(right).reduce(
12
+ (result, [key, rightValue]) => {
13
+ const leftValue = result[key]
13
14
 
14
- if (Array.isArray(leftValue) && Array.isArray(rightValue)) {
15
- result[key] = leftValue.concat(rightValue)
16
- return result
17
- }
15
+ if (Array.isArray(leftValue) && Array.isArray(rightValue)) {
16
+ result[key] = leftValue.concat(rightValue)
17
+ return result
18
+ }
18
19
 
19
- if (isObject(leftValue) && isObject(rightValue)) {
20
- result[key] = mergeRight(leftValue, rightValue)
21
- return result
22
- }
20
+ if (isObject(leftValue) && isObject(rightValue)) {
21
+ result[key] = mergeRight(leftValue, rightValue)
22
+ return result
23
+ }
23
24
 
24
- result[key] = rightValue
25
- return result
26
- }, Object.assign({}, left))
25
+ result[key] = rightValue
26
+ return result
27
+ },
28
+ Object.assign({}, left),
29
+ )
27
30
  }