msw 2.11.0 → 2.11.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 (136) hide show
  1. package/lib/browser/index.d.mts +0 -3
  2. package/lib/browser/index.d.ts +0 -3
  3. package/lib/browser/index.js +503 -294
  4. package/lib/browser/index.js.map +1 -1
  5. package/lib/browser/index.mjs +503 -294
  6. package/lib/browser/index.mjs.map +1 -1
  7. package/lib/core/{HttpResponse-DiuKTgC7.d.mts → HttpResponse-B4YmE-GJ.d.mts} +1 -1
  8. package/lib/core/{HttpResponse-DlQEvD4q.d.ts → HttpResponse-BbwAqLE_.d.ts} +1 -1
  9. package/lib/core/HttpResponse.d.mts +1 -1
  10. package/lib/core/HttpResponse.d.ts +1 -1
  11. package/lib/core/HttpResponse.js.map +1 -1
  12. package/lib/core/HttpResponse.mjs.map +1 -1
  13. package/lib/core/SetupApi.d.mts +1 -1
  14. package/lib/core/SetupApi.d.ts +1 -1
  15. package/lib/core/getResponse.d.mts +1 -1
  16. package/lib/core/getResponse.d.ts +1 -1
  17. package/lib/core/graphql.d.mts +1 -1
  18. package/lib/core/graphql.d.ts +1 -1
  19. package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
  20. package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
  21. package/lib/core/handlers/GraphQLHandler.js.map +1 -1
  22. package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
  23. package/lib/core/handlers/HttpHandler.d.mts +1 -1
  24. package/lib/core/handlers/HttpHandler.d.ts +1 -1
  25. package/lib/core/handlers/HttpHandler.js.map +1 -1
  26. package/lib/core/handlers/HttpHandler.mjs.map +1 -1
  27. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  28. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  29. package/lib/core/http.d.mts +1 -1
  30. package/lib/core/http.d.ts +1 -1
  31. package/lib/core/index.d.mts +1 -1
  32. package/lib/core/index.d.ts +1 -1
  33. package/lib/core/passthrough.d.mts +1 -1
  34. package/lib/core/passthrough.d.ts +1 -1
  35. package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
  36. package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
  37. package/lib/core/utils/cookieStore.d.mts +10 -2
  38. package/lib/core/utils/cookieStore.d.ts +10 -2
  39. package/lib/core/utils/cookieStore.js +49 -146
  40. package/lib/core/utils/cookieStore.js.map +1 -1
  41. package/lib/core/utils/cookieStore.mjs +53 -136
  42. package/lib/core/utils/cookieStore.mjs.map +1 -1
  43. package/lib/core/utils/executeHandlers.d.mts +1 -1
  44. package/lib/core/utils/executeHandlers.d.ts +1 -1
  45. package/lib/core/utils/handleRequest.d.mts +1 -1
  46. package/lib/core/utils/handleRequest.d.ts +1 -1
  47. package/lib/core/utils/handleRequest.js +1 -1
  48. package/lib/core/utils/handleRequest.js.map +1 -1
  49. package/lib/core/utils/handleRequest.mjs +1 -1
  50. package/lib/core/utils/handleRequest.mjs.map +1 -1
  51. package/lib/core/utils/internal/devUtils.js.map +1 -1
  52. package/lib/core/utils/internal/devUtils.mjs.map +1 -1
  53. package/lib/core/utils/internal/getCallFrame.js +2 -2
  54. package/lib/core/utils/internal/getCallFrame.js.map +1 -1
  55. package/lib/core/utils/internal/getCallFrame.mjs +2 -2
  56. package/lib/core/utils/internal/getCallFrame.mjs.map +1 -1
  57. package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
  58. package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
  59. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
  60. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
  61. package/lib/core/utils/internal/parseGraphQLRequest.js +1 -0
  62. package/lib/core/utils/internal/parseGraphQLRequest.js.map +1 -1
  63. package/lib/core/utils/internal/parseGraphQLRequest.mjs +1 -0
  64. package/lib/core/utils/internal/parseGraphQLRequest.mjs.map +1 -1
  65. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  66. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  67. package/lib/core/utils/internal/requestHandlerUtils.d.mts +1 -1
  68. package/lib/core/utils/internal/requestHandlerUtils.d.ts +1 -1
  69. package/lib/core/utils/matching/matchRequestUrl.js +1 -1
  70. package/lib/core/utils/matching/matchRequestUrl.js.map +1 -1
  71. package/lib/core/utils/matching/matchRequestUrl.mjs +1 -1
  72. package/lib/core/utils/matching/matchRequestUrl.mjs.map +1 -1
  73. package/lib/core/utils/request/getRequestCookies.js +1 -1
  74. package/lib/core/utils/request/getRequestCookies.js.map +1 -1
  75. package/lib/core/utils/request/getRequestCookies.mjs +1 -1
  76. package/lib/core/utils/request/getRequestCookies.mjs.map +1 -1
  77. package/lib/core/utils/request/storeResponseCookies.d.mts +1 -1
  78. package/lib/core/utils/request/storeResponseCookies.d.ts +1 -1
  79. package/lib/core/utils/request/storeResponseCookies.js +2 -2
  80. package/lib/core/utils/request/storeResponseCookies.js.map +1 -1
  81. package/lib/core/utils/request/storeResponseCookies.mjs +2 -2
  82. package/lib/core/utils/request/storeResponseCookies.mjs.map +1 -1
  83. package/lib/core/utils/url/cleanUrl.js +1 -1
  84. package/lib/core/utils/url/cleanUrl.js.map +1 -1
  85. package/lib/core/utils/url/cleanUrl.mjs +1 -1
  86. package/lib/core/utils/url/cleanUrl.mjs.map +1 -1
  87. package/lib/core/utils/url/isAbsoluteUrl.js +1 -1
  88. package/lib/core/utils/url/isAbsoluteUrl.js.map +1 -1
  89. package/lib/core/utils/url/isAbsoluteUrl.mjs +1 -1
  90. package/lib/core/utils/url/isAbsoluteUrl.mjs.map +1 -1
  91. package/lib/core/ws/WebSocketIndexedDBClientStore.js.map +1 -1
  92. package/lib/core/ws/WebSocketIndexedDBClientStore.mjs.map +1 -1
  93. package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
  94. package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
  95. package/lib/core/ws/utils/attachWebSocketLogger.js.map +1 -1
  96. package/lib/core/ws/utils/attachWebSocketLogger.mjs.map +1 -1
  97. package/lib/iife/index.js +6885 -16153
  98. package/lib/iife/index.js.map +1 -1
  99. package/lib/mockServiceWorker.js +16 -12
  100. package/lib/node/index.js.map +1 -1
  101. package/lib/node/index.mjs.map +1 -1
  102. package/package.json +22 -15
  103. package/src/browser/setupWorker/glossary.ts +9 -114
  104. package/src/browser/setupWorker/setupWorker.ts +83 -120
  105. package/src/browser/setupWorker/start/createRequestListener.ts +20 -24
  106. package/src/browser/setupWorker/start/createResponseListener.ts +15 -22
  107. package/src/browser/setupWorker/start/createStartHandler.ts +24 -18
  108. package/src/browser/setupWorker/start/utils/enableMocking.ts +18 -21
  109. package/src/browser/setupWorker/start/utils/printStartMessage.ts +0 -2
  110. package/src/browser/utils/checkWorkerIntegrity.ts +22 -14
  111. package/src/browser/utils/workerChannel.ts +146 -0
  112. package/src/core/HttpResponse.ts +3 -3
  113. package/src/core/handlers/GraphQLHandler.test.ts +1 -1
  114. package/src/core/handlers/GraphQLHandler.ts +0 -3
  115. package/src/core/handlers/HttpHandler.ts +0 -2
  116. package/src/core/utils/cookieStore.ts +56 -198
  117. package/src/core/utils/handleRequest.ts +1 -1
  118. package/src/core/utils/internal/devUtils.ts +0 -2
  119. package/src/core/utils/internal/getCallFrame.ts +2 -2
  120. package/src/core/utils/internal/parseGraphQLRequest.ts +1 -0
  121. package/src/core/utils/matching/matchRequestUrl.ts +2 -2
  122. package/src/core/utils/request/getRequestCookies.ts +1 -1
  123. package/src/core/utils/request/onUnhandledRequest.test.ts +1 -1
  124. package/src/core/utils/request/storeResponseCookies.ts +3 -3
  125. package/src/core/utils/request/toPublicUrl.test.ts +1 -1
  126. package/src/core/utils/url/cleanUrl.ts +1 -1
  127. package/src/core/utils/url/isAbsoluteUrl.ts +1 -1
  128. package/src/core/ws/WebSocketClientManager.test.ts +1 -1
  129. package/src/core/ws/WebSocketIndexedDBClientStore.ts +0 -4
  130. package/src/core/ws/utils/attachWebSocketLogger.ts +0 -14
  131. package/src/mockServiceWorker.js +14 -10
  132. package/src/node/SetupServerApi.ts +1 -1
  133. package/src/browser/setupWorker/start/createFallbackStart.ts +0 -21
  134. package/src/browser/setupWorker/start/utils/createMessageChannel.ts +0 -32
  135. package/src/browser/setupWorker/stop/createFallbackStop.ts +0 -11
  136. package/src/browser/setupWorker/stop/createStop.ts +0 -35
@@ -1,45 +1,34 @@
1
1
  import { invariant } from 'outvariant'
2
2
  import { isNodeProcess } from 'is-node-process'
3
- import {
3
+ import type {
4
4
  SetupWorkerInternalContext,
5
- ServiceWorkerIncomingEventsMap,
6
5
  StartReturnType,
7
- StopHandler,
8
- StartHandler,
9
6
  StartOptions,
7
+ SetupWorker,
10
8
  } from './glossary'
11
- import { createStartHandler } from './start/createStartHandler'
12
- import { createStop } from './stop/createStop'
13
- import { ServiceWorkerMessage } from './start/utils/createMessageChannel'
14
9
  import { RequestHandler } from '~/core/handlers/RequestHandler'
15
10
  import { DEFAULT_START_OPTIONS } from './start/utils/prepareStartHandler'
16
- import { createFallbackStart } from './start/createFallbackStart'
17
- import { createFallbackStop } from './stop/createFallbackStop'
11
+ import { createStartHandler } from './start/createStartHandler'
18
12
  import { devUtils } from '~/core/utils/internal/devUtils'
19
13
  import { SetupApi } from '~/core/SetupApi'
20
14
  import { mergeRight } from '~/core/utils/internal/mergeRight'
21
15
  import type { LifeCycleEventsMap } from '~/core/sharedOptions'
22
16
  import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'
23
- import { SetupWorker } from './glossary'
24
17
  import { supportsReadableStreamTransfer } from '../utils/supportsReadableStreamTransfer'
25
18
  import { webSocketInterceptor } from '~/core/ws/webSocketInterceptor'
26
19
  import { handleWebSocketEvent } from '~/core/ws/handleWebSocketEvent'
27
20
  import { attachWebSocketLogger } from '~/core/ws/utils/attachWebSocketLogger'
28
-
29
- interface Listener {
30
- target: EventTarget
31
- eventType: string
32
- callback: EventListenerOrEventListenerObject
33
- }
21
+ import { WorkerChannel } from '../utils/workerChannel'
22
+ import { DeferredPromise } from '@open-draft/deferred-promise'
23
+ import { createFallbackRequestListener } from './start/createFallbackRequestListener'
24
+ import { printStartMessage } from './start/utils/printStartMessage'
25
+ import { printStopMessage } from './stop/utils/printStopMessage'
34
26
 
35
27
  export class SetupWorkerApi
36
28
  extends SetupApi<LifeCycleEventsMap>
37
29
  implements SetupWorker
38
30
  {
39
31
  private context: SetupWorkerInternalContext
40
- private startHandler: StartHandler = null as any
41
- private stopHandler: StopHandler = null as any
42
- private listeners: Array<Listener>
43
32
 
44
33
  constructor(...handlers: Array<RequestHandler | WebSocketHandler>) {
45
34
  super(...handlers)
@@ -51,129 +40,53 @@ export class SetupWorkerApi
51
40
  ),
52
41
  )
53
42
 
54
- this.listeners = []
55
43
  this.context = this.createWorkerContext()
56
44
  }
57
45
 
58
46
  private createWorkerContext(): SetupWorkerInternalContext {
59
- const context: SetupWorkerInternalContext = {
47
+ const workerPromise = new DeferredPromise<ServiceWorker>()
48
+
49
+ return {
60
50
  // Mocking is not considered enabled until the worker
61
51
  // signals back the successful activation event.
62
52
  isMockingEnabled: false,
63
53
  startOptions: null as any,
64
- worker: null,
54
+ workerPromise,
55
+ registration: undefined,
65
56
  getRequestHandlers: () => {
66
57
  return this.handlersController.currentHandlers()
67
58
  },
68
- registration: null,
69
59
  emitter: this.emitter,
70
- workerChannel: {
71
- on: (eventType, callback) => {
72
- this.context.events.addListener<
73
- MessageEvent<ServiceWorkerMessage<typeof eventType, any>>
74
- >(navigator.serviceWorker, 'message', (event) => {
75
- // Avoid messages broadcasted from unrelated workers.
76
- if (event.source !== this.context.worker) {
77
- return
78
- }
79
-
80
- const message = event.data
81
-
82
- if (!message) {
83
- return
84
- }
85
-
86
- if (message.type === eventType) {
87
- callback(event, message)
88
- }
89
- })
90
- },
91
- send: (type) => {
92
- this.context.worker?.postMessage(type)
93
- },
94
- },
95
- events: {
96
- addListener: (target, eventType, callback) => {
97
- target.addEventListener(eventType, callback as EventListener)
98
- this.listeners.push({
99
- eventType,
100
- target,
101
- callback: callback as EventListener,
102
- })
103
-
104
- return () => {
105
- target.removeEventListener(eventType, callback as EventListener)
106
- }
107
- },
108
- removeAllListeners: () => {
109
- for (const { target, eventType, callback } of this.listeners) {
110
- target.removeEventListener(eventType, callback)
111
- }
112
- this.listeners = []
113
- },
114
- once: (eventType) => {
115
- const bindings: Array<() => void> = []
116
-
117
- return new Promise<
118
- ServiceWorkerMessage<
119
- typeof eventType,
120
- ServiceWorkerIncomingEventsMap[typeof eventType]
121
- >
122
- >((resolve, reject) => {
123
- const handleIncomingMessage = (event: MessageEvent) => {
124
- try {
125
- const message = event.data
126
-
127
- if (message.type === eventType) {
128
- resolve(message)
129
- }
130
- } catch (error) {
131
- reject(error)
132
- }
133
- }
134
-
135
- bindings.push(
136
- this.context.events.addListener(
137
- navigator.serviceWorker,
138
- 'message',
139
- handleIncomingMessage,
140
- ),
141
- this.context.events.addListener(
142
- navigator.serviceWorker,
143
- 'messageerror',
144
- reject,
145
- ),
146
- )
147
- }).finally(() => {
148
- bindings.forEach((unbind) => unbind())
149
- })
150
- },
151
- },
60
+ workerChannel: new WorkerChannel({
61
+ worker: workerPromise,
62
+ }),
152
63
  supports: {
153
64
  serviceWorkerApi:
154
- !('serviceWorker' in navigator) || location.protocol === 'file:',
65
+ 'serviceWorker' in navigator && location.protocol !== 'file:',
155
66
  readableStreamTransfer: supportsReadableStreamTransfer(),
156
67
  },
157
68
  }
158
-
159
- this.startHandler = context.supports.serviceWorkerApi
160
- ? createFallbackStart(context)
161
- : createStartHandler(context)
162
-
163
- this.stopHandler = context.supports.serviceWorkerApi
164
- ? createFallbackStop(context)
165
- : createStop(context)
166
-
167
- return context
168
69
  }
169
70
 
170
71
  public async start(options: StartOptions = {}): StartReturnType {
171
- if (options.waitUntilReady === true) {
72
+ if ('waitUntilReady' in options) {
172
73
  devUtils.warn(
173
74
  'The "waitUntilReady" option has been deprecated. Please remove it from this "worker.start()" call. Follow the recommended Browser integration (https://mswjs.io/docs/integrations/browser) to eliminate any race conditions between the Service Worker registration and any requests made by your application on initial render.',
174
75
  )
175
76
  }
176
77
 
78
+ // Warn the developer on multiple "worker.start()" calls.
79
+ // While this will not affect the worker in any way,
80
+ // it likely indicates an issue with the developer's code.
81
+ if (this.context.isMockingEnabled) {
82
+ devUtils.warn(
83
+ `Found a redundant "worker.start()" call. Note that starting the worker while mocking is already enabled will have no effect. Consider removing this "worker.start()" call.`,
84
+ )
85
+ return this.context.registration
86
+ }
87
+
88
+ this.context.workerStoppedAt = undefined
89
+
177
90
  this.context.startOptions = mergeRight(
178
91
  DEFAULT_START_OPTIONS,
179
92
  options,
@@ -202,14 +115,64 @@ export class SetupWorkerApi
202
115
  webSocketInterceptor.dispose()
203
116
  })
204
117
 
205
- return await this.startHandler(this.context.startOptions, options)
118
+ // Use a fallback interception algorithm in the environments
119
+ // where the Service Worker API isn't supported.
120
+ if (!this.context.supports.serviceWorkerApi) {
121
+ const fallbackInterceptor = createFallbackRequestListener(
122
+ this.context,
123
+ this.context.startOptions,
124
+ )
125
+
126
+ this.subscriptions.push(() => {
127
+ fallbackInterceptor.dispose()
128
+ })
129
+
130
+ this.context.isMockingEnabled = true
131
+
132
+ printStartMessage({
133
+ message: 'Mocking enabled (fallback mode).',
134
+ quiet: this.context.startOptions.quiet,
135
+ })
136
+
137
+ return undefined
138
+ }
139
+
140
+ const startHandler = createStartHandler(this.context)
141
+ const registration = await startHandler(this.context.startOptions, options)
142
+
143
+ this.context.isMockingEnabled = true
144
+
145
+ return registration
206
146
  }
207
147
 
208
148
  public stop(): void {
209
149
  super.dispose()
210
- this.context.events.removeAllListeners()
150
+
151
+ if (!this.context.isMockingEnabled) {
152
+ devUtils.warn(
153
+ 'Found a redundant "worker.stop()" call. Notice that stopping the worker after it has already been stopped has no effect. Consider removing this "worker.stop()" call.',
154
+ )
155
+ return
156
+ }
157
+
158
+ this.context.isMockingEnabled = false
159
+ this.context.workerStoppedAt = Date.now()
211
160
  this.context.emitter.removeAllListeners()
212
- this.stopHandler()
161
+
162
+ if (this.context.supports.serviceWorkerApi) {
163
+ this.context.workerChannel.removeAllListeners('RESPONSE')
164
+ window.clearInterval(this.context.keepAliveInterval)
165
+ }
166
+
167
+ // Post the internal stop message on the window
168
+ // to let any logic know when the worker has stopped.
169
+ // E.g. the WebSocket client manager needs this to know
170
+ // when to clear its in-memory clients list.
171
+ window.postMessage({ type: 'msw/worker:stop' })
172
+
173
+ printStopMessage({
174
+ quiet: this.context.startOptions?.quiet,
175
+ })
213
176
  }
214
177
  }
215
178
 
@@ -1,12 +1,5 @@
1
- import {
2
- StartOptions,
3
- SetupWorkerInternalContext,
4
- ServiceWorkerIncomingEventsMap,
5
- } from '../glossary'
6
- import {
7
- ServiceWorkerMessage,
8
- WorkerChannel,
9
- } from './utils/createMessageChannel'
1
+ import { Emitter } from 'rettime'
2
+ import { StartOptions, SetupWorkerInternalContext } from '../glossary'
10
3
  import { deserializeRequest } from '../../utils/deserializeRequest'
11
4
  import { RequestHandler } from '~/core/handlers/RequestHandler'
12
5
  import { handleRequest } from '~/core/utils/handleRequest'
@@ -18,18 +11,21 @@ import { isHandlerKind } from '~/core/utils/internal/isHandlerKind'
18
11
  export const createRequestListener = (
19
12
  context: SetupWorkerInternalContext,
20
13
  options: RequiredDeep<StartOptions>,
21
- ) => {
22
- return async (
23
- event: MessageEvent,
24
- message: ServiceWorkerMessage<
25
- 'REQUEST',
26
- ServiceWorkerIncomingEventsMap['REQUEST']
27
- >,
28
- ) => {
29
- const messageChannel = new WorkerChannel(event.ports[0])
14
+ ): Emitter.ListenerType<typeof context.workerChannel, 'REQUEST'> => {
15
+ return async (event) => {
16
+ // Treat any incoming requests from the worker as passthrough
17
+ // if `worker.stop()` has been called for this client.
18
+ if (
19
+ !context.isMockingEnabled &&
20
+ context.workerStoppedAt &&
21
+ event.data.interceptedAt > context.workerStoppedAt
22
+ ) {
23
+ event.postMessage('PASSTHROUGH')
24
+ return
25
+ }
30
26
 
31
- const requestId = message.payload.id
32
- const request = deserializeRequest(message.payload)
27
+ const requestId = event.data.id
28
+ const request = deserializeRequest(event.data)
33
29
  const requestCloneForLogs = request.clone()
34
30
 
35
31
  // Make this the first request clone before the
@@ -48,7 +44,7 @@ export const createRequestListener = (
48
44
  context.emitter,
49
45
  {
50
46
  onPassthroughResponse() {
51
- messageChannel.postMessage('PASSTHROUGH')
47
+ event.postMessage('PASSTHROUGH')
52
48
  },
53
49
  async onMockedResponse(response, { handler, parsedResult }) {
54
50
  // Clone the mocked response so its body could be read
@@ -65,7 +61,7 @@ export const createRequestListener = (
65
61
  if (context.supports.readableStreamTransfer) {
66
62
  const responseStreamOrNull = response.body
67
63
 
68
- messageChannel.postMessage(
64
+ event.postMessage(
69
65
  'MOCK_RESPONSE',
70
66
  {
71
67
  ...responseInit,
@@ -85,7 +81,7 @@ export const createRequestListener = (
85
81
  ? null
86
82
  : await responseClone.arrayBuffer()
87
83
 
88
- messageChannel.postMessage('MOCK_RESPONSE', {
84
+ event.postMessage('MOCK_RESPONSE', {
89
85
  ...responseInit,
90
86
  body: responseBufferOrNull,
91
87
  })
@@ -118,7 +114,7 @@ This exception has been gracefully handled as a 500 response, however, it's stro
118
114
 
119
115
  // Treat all other exceptions in a request handler as unintended,
120
116
  // alerting that there is a problem that needs fixing.
121
- messageChannel.postMessage('MOCK_RESPONSE', {
117
+ event.postMessage('MOCK_RESPONSE', {
122
118
  status: 500,
123
119
  statusText: 'Request Handler Error',
124
120
  headers: {
@@ -1,21 +1,14 @@
1
1
  import { FetchResponse } from '@mswjs/interceptors'
2
- import type {
3
- ServiceWorkerIncomingEventsMap,
4
- SetupWorkerInternalContext,
5
- } from '../glossary'
6
- import type { ServiceWorkerMessage } from './utils/createMessageChannel'
2
+ import type { Emitter } from 'rettime'
3
+ import type { SetupWorkerInternalContext } from '../glossary'
7
4
  import { deserializeRequest } from '../../utils/deserializeRequest'
8
5
 
9
- export function createResponseListener(context: SetupWorkerInternalContext) {
10
- return (
11
- _: MessageEvent,
12
- message: ServiceWorkerMessage<
13
- 'RESPONSE',
14
- ServiceWorkerIncomingEventsMap['RESPONSE']
15
- >,
16
- ) => {
17
- const { payload: responseJson } = message
18
- const request = deserializeRequest(responseJson.request)
6
+ export function createResponseListener(
7
+ context: SetupWorkerInternalContext,
8
+ ): Emitter.ListenerType<typeof context.workerChannel, 'RESPONSE'> {
9
+ return (event) => {
10
+ const responseMessage = event.data
11
+ const request = deserializeRequest(responseMessage.request)
19
12
 
20
13
  /**
21
14
  * CORS requests with `mode: "no-cors"` result in "opaque" responses.
@@ -24,12 +17,12 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
24
17
  * @see https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
25
18
  * @see https://github.com/mswjs/msw/issues/529
26
19
  */
27
- if (responseJson.response.type?.includes('opaque')) {
20
+ if (responseMessage.response.type?.includes('opaque')) {
28
21
  return
29
22
  }
30
23
 
31
24
  const response =
32
- responseJson.response.status === 0
25
+ responseMessage.response.status === 0
33
26
  ? Response.error()
34
27
  : new FetchResponse(
35
28
  /**
@@ -38,11 +31,11 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
38
31
  * throw when passed a non-null body, so ensure it's null here
39
32
  * for those codes
40
33
  */
41
- FetchResponse.isResponseWithBody(responseJson.response.status)
42
- ? responseJson.response.body
34
+ FetchResponse.isResponseWithBody(responseMessage.response.status)
35
+ ? responseMessage.response.body
43
36
  : null,
44
37
  {
45
- ...responseJson,
38
+ ...responseMessage,
46
39
  /**
47
40
  * Set response URL if it's not set already.
48
41
  * @see https://github.com/mswjs/msw/issues/2030
@@ -53,9 +46,9 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
53
46
  )
54
47
 
55
48
  context.emitter.emit(
56
- responseJson.isMockedResponse ? 'response:mocked' : 'response:bypass',
49
+ responseMessage.isMockedResponse ? 'response:mocked' : 'response:bypass',
57
50
  {
58
- requestId: responseJson.request.id,
51
+ requestId: responseMessage.request.id,
59
52
  request,
60
53
  response,
61
54
  },
@@ -1,11 +1,12 @@
1
1
  import { devUtils } from '~/core/utils/internal/devUtils'
2
2
  import { getWorkerInstance } from './utils/getWorkerInstance'
3
3
  import { enableMocking } from './utils/enableMocking'
4
- import { SetupWorkerInternalContext, StartHandler } from '../glossary'
4
+ import type { SetupWorkerInternalContext, StartHandler } from '../glossary'
5
5
  import { createRequestListener } from './createRequestListener'
6
6
  import { checkWorkerIntegrity } from '../../utils/checkWorkerIntegrity'
7
7
  import { createResponseListener } from './createResponseListener'
8
8
  import { validateWorkerScope } from './utils/validateWorkerScope'
9
+ import { DeferredPromise } from '@open-draft/deferred-promise'
9
10
 
10
11
  export const createStartHandler = (
11
12
  context: SetupWorkerInternalContext,
@@ -15,7 +16,7 @@ export const createStartHandler = (
15
16
  // Remove all previously existing event listeners.
16
17
  // This way none of the listeners persists between Fast refresh
17
18
  // of the application's code.
18
- context.events.removeAllListeners()
19
+ context.workerChannel.removeAllListeners()
19
20
 
20
21
  // Handle requests signaled by the worker.
21
22
  context.workerChannel.on(
@@ -57,17 +58,18 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
57
58
  throw new Error(missingWorkerMessage)
58
59
  }
59
60
 
60
- context.worker = worker
61
+ context.workerPromise.resolve(worker)
61
62
  context.registration = registration
62
63
 
63
- context.events.addListener(window, 'beforeunload', () => {
64
+ window.addEventListener('beforeunload', () => {
64
65
  if (worker.state !== 'redundant') {
65
66
  // Notify the Service Worker that this client has closed.
66
67
  // Internally, it's similar to disabling the mocking, only
67
68
  // client close event has a handler that self-terminates
68
69
  // the Service Worker when there are no open clients.
69
- context.workerChannel.send('CLIENT_CLOSED')
70
+ context.workerChannel.postMessage('CLIENT_CLOSED')
70
71
  }
72
+
71
73
  // Make sure we're always clearing the interval - there are reports that not doing this can
72
74
  // cause memory leaks in headless browser environments.
73
75
  window.clearInterval(context.keepAliveInterval)
@@ -82,14 +84,13 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
82
84
  // by the currently installed version of MSW.
83
85
  await checkWorkerIntegrity(context).catch((error) => {
84
86
  devUtils.error(
85
- 'Error while checking the worker script integrity. Please report this on GitHub (https://github.com/mswjs/msw/issues), including the original error below.',
87
+ 'Error while checking the worker script integrity. Please report this on GitHub (https://github.com/mswjs/msw/issues) and include the original error below.',
86
88
  )
87
- // eslint-disable-next-line no-console
88
89
  console.error(error)
89
90
  })
90
91
 
91
92
  context.keepAliveInterval = window.setInterval(
92
- () => context.workerChannel.send('KEEPALIVE_REQUEST'),
93
+ () => context.workerChannel.postMessage('KEEPALIVE_REQUEST'),
93
94
  5000,
94
95
  )
95
96
 
@@ -104,22 +105,27 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
104
105
  async (registration) => {
105
106
  const pendingInstance = registration.installing || registration.waiting
106
107
 
107
- // Wait until the worker is activated.
108
- // Assume the worker is already activated if there's no pending registration
109
- // (i.e. when reloading the page after a successful activation).
110
108
  if (pendingInstance) {
111
- await new Promise<void>((resolve) => {
112
- pendingInstance.addEventListener('statechange', () => {
113
- if (pendingInstance.state === 'activated') {
114
- return resolve()
115
- }
116
- })
109
+ const activationPromise = new DeferredPromise<void>()
110
+
111
+ pendingInstance.addEventListener('statechange', () => {
112
+ if (pendingInstance.state === 'activated') {
113
+ activationPromise.resolve()
114
+ }
117
115
  })
116
+
117
+ // Wait until the worker is activated.
118
+ // Assume the worker is already activated if there's no pending registration
119
+ // (i.e. when reloading the page after a successful activation).
120
+ await activationPromise
118
121
  }
119
122
 
120
123
  // Print the activation message only after the worker has been activated.
121
124
  await enableMocking(context, options).catch((error) => {
122
- throw new Error(`Failed to enable mocking: ${error?.message}`)
125
+ devUtils.error(
126
+ 'Failed to enable mocking. Please report this on GitHub (https://github.com/mswjs/msw/issues) and include the original error below.',
127
+ )
128
+ throw error
123
129
  })
124
130
 
125
131
  return registration
@@ -1,33 +1,30 @@
1
- import { devUtils } from '~/core/utils/internal/devUtils'
2
- import { StartOptions, SetupWorkerInternalContext } from '../../glossary'
1
+ import { DeferredPromise } from '@open-draft/deferred-promise'
2
+ import type { StartOptions, SetupWorkerInternalContext } from '../../glossary'
3
3
  import { printStartMessage } from './printStartMessage'
4
4
 
5
5
  /**
6
6
  * Signals the worker to enable the interception of requests.
7
7
  */
8
- export async function enableMocking(
8
+ export function enableMocking(
9
9
  context: SetupWorkerInternalContext,
10
10
  options: StartOptions,
11
- ) {
12
- context.workerChannel.send('MOCK_ACTIVATE')
13
- const { payload } = await context.events.once('MOCKING_ENABLED')
11
+ ): Promise<boolean> {
12
+ const mockingEnabledPromise = new DeferredPromise<boolean>()
14
13
 
15
- // Warn the developer on multiple "worker.start()" calls.
16
- // While this will not affect the worker in any way,
17
- // it likely indicates an issue with the developer's code.
18
- if (context.isMockingEnabled) {
19
- devUtils.warn(
20
- `Found a redundant "worker.start()" call. Note that starting the worker while mocking is already enabled will have no effect. Consider removing this "worker.start()" call.`,
21
- )
22
- return
23
- }
14
+ context.workerChannel.postMessage('MOCK_ACTIVATE')
15
+ context.workerChannel.once('MOCKING_ENABLED', async (event) => {
16
+ context.isMockingEnabled = true
17
+ const worker = await context.workerPromise
24
18
 
25
- context.isMockingEnabled = true
19
+ printStartMessage({
20
+ quiet: options.quiet,
21
+ workerScope: context.registration?.scope,
22
+ workerUrl: worker.scriptURL,
23
+ client: event.data.client,
24
+ })
26
25
 
27
- printStartMessage({
28
- quiet: options.quiet,
29
- workerScope: context.registration?.scope,
30
- workerUrl: context.worker?.scriptURL,
31
- client: payload.client,
26
+ mockingEnabledPromise.resolve(true)
32
27
  })
28
+
29
+ return mockingEnabledPromise
33
30
  }
@@ -19,7 +19,6 @@ export function printStartMessage(args: PrintStartMessageArgs = {}) {
19
19
 
20
20
  const message = args.message || 'Mocking enabled.'
21
21
 
22
- // eslint-disable-next-line no-console
23
22
  console.groupCollapsed(
24
23
  `%c${devUtils.formatMessage(message)}`,
25
24
  'color:orangered;font-weight:bold;',
@@ -48,6 +47,5 @@ export function printStartMessage(args: PrintStartMessageArgs = {}) {
48
47
  console.log('Client ID: %s (%s)', args.client.id, args.client.frameType)
49
48
  }
50
49
 
51
- // eslint-disable-next-line no-console
52
50
  console.groupEnd()
53
51
  }
@@ -1,34 +1,42 @@
1
1
  import { devUtils } from '~/core/utils/internal/devUtils'
2
2
  import type { SetupWorkerInternalContext } from '../setupWorker/glossary'
3
+ import { DeferredPromise } from '@open-draft/deferred-promise'
3
4
 
4
5
  /**
5
6
  * Check whether the registered Service Worker has been
6
7
  * generated by the installed version of the library.
7
8
  * Prints a warning message if the worker scripts mismatch.
8
9
  */
9
- export async function checkWorkerIntegrity(
10
+ export function checkWorkerIntegrity(
10
11
  context: SetupWorkerInternalContext,
11
12
  ): Promise<void> {
12
- // Request the integrity checksum from the registered worker.
13
- context.workerChannel.send('INTEGRITY_CHECK_REQUEST')
13
+ const integrityCheckPromise = new DeferredPromise<void>()
14
14
 
15
- const { payload } = await context.events.once('INTEGRITY_CHECK_RESPONSE')
15
+ // Request the integrity checksum from the registered worker.
16
+ context.workerChannel.postMessage('INTEGRITY_CHECK_REQUEST')
17
+ context.workerChannel.once('INTEGRITY_CHECK_RESPONSE', (event) => {
18
+ const { checksum, packageVersion } = event.data
16
19
 
17
- // Compare the response from the Service Worker and the
18
- // global variable set during the build.
20
+ // Compare the response from the Service Worker and the
21
+ // global variable set during the build.
19
22
 
20
- // The integrity is validated based on the worker script's checksum
21
- // that's derived from its minified content during the build.
22
- // The "SERVICE_WORKER_CHECKSUM" global variable is injected by the build.
23
- if (payload.checksum !== SERVICE_WORKER_CHECKSUM) {
24
- devUtils.warn(
25
- `The currently registered Service Worker has been generated by a different version of MSW (${payload.packageVersion}) and may not be fully compatible with the installed version.
23
+ // The integrity is validated based on the worker script's checksum
24
+ // that's derived from its minified content during the build.
25
+ // The "SERVICE_WORKER_CHECKSUM" global variable is injected by the build.
26
+ if (checksum !== SERVICE_WORKER_CHECKSUM) {
27
+ devUtils.warn(
28
+ `The currently registered Service Worker has been generated by a different version of MSW (${packageVersion}) and may not be fully compatible with the installed version.
26
29
 
27
30
  It's recommended you update your worker script by running this command:
28
31
 
29
32
  \u2022 npx msw init <PUBLIC_DIR>
30
33
 
31
34
  You can also automate this process and make the worker script update automatically upon the library installations. Read more: https://mswjs.io/docs/cli/init.`,
32
- )
33
- }
35
+ )
36
+ }
37
+
38
+ integrityCheckPromise.resolve()
39
+ })
40
+
41
+ return integrityCheckPromise
34
42
  }