msw 2.8.7 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/index.js +13 -17
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/index.mjs +13 -17
- package/lib/browser/index.mjs.map +1 -1
- package/lib/iife/index.js +13 -17
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +91 -54
- package/package.json +3 -2
- package/src/browser/setupWorker/glossary.ts +7 -7
- package/src/browser/setupWorker/setupWorker.ts +0 -1
- package/src/browser/setupWorker/start/createRequestListener.ts +3 -4
- package/src/browser/setupWorker/start/createResponseListener.ts +8 -12
- package/src/browser/utils/{parseWorkerRequest.ts → deserializeRequest.ts} +5 -5
- package/src/mockServiceWorker.js +89 -52
- package/src/tsconfig.worker.json +13 -0
package/lib/mockServiceWorker.js
CHANGED
|
@@ -5,24 +5,23 @@
|
|
|
5
5
|
* Mock Service Worker.
|
|
6
6
|
* @see https://github.com/mswjs/msw
|
|
7
7
|
* - Please do NOT modify this file.
|
|
8
|
-
* - Please do NOT serve this file on production.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
const PACKAGE_VERSION = '2.
|
|
12
|
-
const INTEGRITY_CHECKSUM = '
|
|
10
|
+
const PACKAGE_VERSION = '2.9.0'
|
|
11
|
+
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
|
|
13
12
|
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
14
13
|
const activeClientIds = new Set()
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
addEventListener('install', function () {
|
|
17
16
|
self.skipWaiting()
|
|
18
17
|
})
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
addEventListener('activate', function (event) {
|
|
21
20
|
event.waitUntil(self.clients.claim())
|
|
22
21
|
})
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
const clientId = event.source
|
|
23
|
+
addEventListener('message', async function (event) {
|
|
24
|
+
const clientId = Reflect.get(event.source || {}, 'id')
|
|
26
25
|
|
|
27
26
|
if (!clientId || !self.clients) {
|
|
28
27
|
return
|
|
@@ -94,17 +93,18 @@ self.addEventListener('message', async function (event) {
|
|
|
94
93
|
}
|
|
95
94
|
})
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
const { request } = event
|
|
99
|
-
|
|
96
|
+
addEventListener('fetch', function (event) {
|
|
100
97
|
// Bypass navigation requests.
|
|
101
|
-
if (request.mode === 'navigate') {
|
|
98
|
+
if (event.request.mode === 'navigate') {
|
|
102
99
|
return
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
// Opening the DevTools triggers the "only-if-cached" request
|
|
106
103
|
// that cannot be handled by the worker. Bypass such requests.
|
|
107
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
event.request.cache === 'only-if-cached' &&
|
|
106
|
+
event.request.mode !== 'same-origin'
|
|
107
|
+
) {
|
|
108
108
|
return
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -115,48 +115,62 @@ self.addEventListener('fetch', function (event) {
|
|
|
115
115
|
return
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// Generate unique request ID.
|
|
119
118
|
const requestId = crypto.randomUUID()
|
|
120
119
|
event.respondWith(handleRequest(event, requestId))
|
|
121
120
|
})
|
|
122
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {FetchEvent} event
|
|
124
|
+
* @param {string} requestId
|
|
125
|
+
*/
|
|
123
126
|
async function handleRequest(event, requestId) {
|
|
124
127
|
const client = await resolveMainClient(event)
|
|
128
|
+
const requestCloneForEvents = event.request.clone()
|
|
125
129
|
const response = await getResponse(event, client, requestId)
|
|
126
130
|
|
|
127
131
|
// Send back the response clone for the "response:*" life-cycle events.
|
|
128
132
|
// Ensure MSW is active and ready to handle the message, otherwise
|
|
129
133
|
// this message will pend indefinitely.
|
|
130
134
|
if (client && activeClientIds.has(client.id)) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
|
136
|
+
|
|
137
|
+
// Clone the response so both the client and the library could consume it.
|
|
138
|
+
const responseClone = response.clone()
|
|
139
|
+
|
|
140
|
+
sendToClient(
|
|
141
|
+
client,
|
|
142
|
+
{
|
|
143
|
+
type: 'RESPONSE',
|
|
144
|
+
payload: {
|
|
145
|
+
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
|
146
|
+
request: {
|
|
147
|
+
id: requestId,
|
|
148
|
+
...serializedRequest,
|
|
149
|
+
},
|
|
150
|
+
response: {
|
|
141
151
|
type: responseClone.type,
|
|
142
152
|
status: responseClone.status,
|
|
143
153
|
statusText: responseClone.statusText,
|
|
144
|
-
body: responseClone.body,
|
|
145
154
|
headers: Object.fromEntries(responseClone.headers.entries()),
|
|
155
|
+
body: responseClone.body,
|
|
146
156
|
},
|
|
147
157
|
},
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
},
|
|
159
|
+
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
|
160
|
+
)
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
return response
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the main client for the given event.
|
|
168
|
+
* Client that issues a request doesn't necessarily equal the client
|
|
169
|
+
* that registered the worker. It's with the latter the worker should
|
|
170
|
+
* communicate with during the response resolving phase.
|
|
171
|
+
* @param {FetchEvent} event
|
|
172
|
+
* @returns {Promise<Client | undefined>}
|
|
173
|
+
*/
|
|
160
174
|
async function resolveMainClient(event) {
|
|
161
175
|
const client = await self.clients.get(event.clientId)
|
|
162
176
|
|
|
@@ -184,12 +198,16 @@ async function resolveMainClient(event) {
|
|
|
184
198
|
})
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @param {FetchEvent} event
|
|
203
|
+
* @param {Client | undefined} client
|
|
204
|
+
* @param {string} requestId
|
|
205
|
+
* @returns {Promise<Response>}
|
|
206
|
+
*/
|
|
187
207
|
async function getResponse(event, client, requestId) {
|
|
188
|
-
const { request } = event
|
|
189
|
-
|
|
190
208
|
// Clone the request because it might've been already used
|
|
191
209
|
// (i.e. its body has been read and sent to the client).
|
|
192
|
-
const requestClone = request.clone()
|
|
210
|
+
const requestClone = event.request.clone()
|
|
193
211
|
|
|
194
212
|
function passthrough() {
|
|
195
213
|
// Cast the request headers to a new Headers instance
|
|
@@ -230,29 +248,17 @@ async function getResponse(event, client, requestId) {
|
|
|
230
248
|
}
|
|
231
249
|
|
|
232
250
|
// Notify the client that a request has been intercepted.
|
|
233
|
-
const
|
|
251
|
+
const serializedRequest = await serializeRequest(event.request)
|
|
234
252
|
const clientMessage = await sendToClient(
|
|
235
253
|
client,
|
|
236
254
|
{
|
|
237
255
|
type: 'REQUEST',
|
|
238
256
|
payload: {
|
|
239
257
|
id: requestId,
|
|
240
|
-
|
|
241
|
-
mode: request.mode,
|
|
242
|
-
method: request.method,
|
|
243
|
-
headers: Object.fromEntries(request.headers.entries()),
|
|
244
|
-
cache: request.cache,
|
|
245
|
-
credentials: request.credentials,
|
|
246
|
-
destination: request.destination,
|
|
247
|
-
integrity: request.integrity,
|
|
248
|
-
redirect: request.redirect,
|
|
249
|
-
referrer: request.referrer,
|
|
250
|
-
referrerPolicy: request.referrerPolicy,
|
|
251
|
-
body: requestBuffer,
|
|
252
|
-
keepalive: request.keepalive,
|
|
258
|
+
...serializedRequest,
|
|
253
259
|
},
|
|
254
260
|
},
|
|
255
|
-
[
|
|
261
|
+
[serializedRequest.body],
|
|
256
262
|
)
|
|
257
263
|
|
|
258
264
|
switch (clientMessage.type) {
|
|
@@ -268,6 +274,12 @@ async function getResponse(event, client, requestId) {
|
|
|
268
274
|
return passthrough()
|
|
269
275
|
}
|
|
270
276
|
|
|
277
|
+
/**
|
|
278
|
+
* @param {Client} client
|
|
279
|
+
* @param {any} message
|
|
280
|
+
* @param {Array<Transferable>} transferrables
|
|
281
|
+
* @returns {Promise<any>}
|
|
282
|
+
*/
|
|
271
283
|
function sendToClient(client, message, transferrables = []) {
|
|
272
284
|
return new Promise((resolve, reject) => {
|
|
273
285
|
const channel = new MessageChannel()
|
|
@@ -280,14 +292,18 @@ function sendToClient(client, message, transferrables = []) {
|
|
|
280
292
|
resolve(event.data)
|
|
281
293
|
}
|
|
282
294
|
|
|
283
|
-
client.postMessage(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
)
|
|
295
|
+
client.postMessage(message, [
|
|
296
|
+
channel.port2,
|
|
297
|
+
...transferrables.filter(Boolean),
|
|
298
|
+
])
|
|
287
299
|
})
|
|
288
300
|
}
|
|
289
301
|
|
|
290
|
-
|
|
302
|
+
/**
|
|
303
|
+
* @param {Response} response
|
|
304
|
+
* @returns {Response}
|
|
305
|
+
*/
|
|
306
|
+
function respondWithMock(response) {
|
|
291
307
|
// Setting response status code to 0 is a no-op.
|
|
292
308
|
// However, when responding with a "Response.error()", the produced Response
|
|
293
309
|
// instance will have status code set to 0. Since it's not possible to create
|
|
@@ -305,3 +321,24 @@ async function respondWithMock(response) {
|
|
|
305
321
|
|
|
306
322
|
return mockedResponse
|
|
307
323
|
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* @param {Request} request
|
|
327
|
+
*/
|
|
328
|
+
async function serializeRequest(request) {
|
|
329
|
+
return {
|
|
330
|
+
url: request.url,
|
|
331
|
+
mode: request.mode,
|
|
332
|
+
method: request.method,
|
|
333
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
334
|
+
cache: request.cache,
|
|
335
|
+
credentials: request.credentials,
|
|
336
|
+
destination: request.destination,
|
|
337
|
+
integrity: request.integrity,
|
|
338
|
+
redirect: request.redirect,
|
|
339
|
+
referrer: request.referrer,
|
|
340
|
+
referrerPolicy: request.referrerPolicy,
|
|
341
|
+
body: await request.arrayBuffer(),
|
|
342
|
+
keepalive: request.keepalive,
|
|
343
|
+
}
|
|
344
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "msw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./lib/core/index.js",
|
|
@@ -243,6 +243,7 @@
|
|
|
243
243
|
"@types/express": "^4.17.21",
|
|
244
244
|
"@types/json-bigint": "^1.0.4",
|
|
245
245
|
"@types/node": "~18.19.28",
|
|
246
|
+
"@types/serviceworker": "^0.0.136",
|
|
246
247
|
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
|
247
248
|
"@typescript-eslint/parser": "^8.8.1",
|
|
248
249
|
"@web/dev-server": "^0.4.6",
|
|
@@ -279,7 +280,7 @@
|
|
|
279
280
|
"vitest-environment-miniflare": "^2.14.4",
|
|
280
281
|
"webpack": "^5.95.0",
|
|
281
282
|
"webpack-http-server": "^0.5.0",
|
|
282
|
-
"msw": "2.
|
|
283
|
+
"msw": "2.9.0"
|
|
283
284
|
},
|
|
284
285
|
"peerDependencies": {
|
|
285
286
|
"typescript": ">= 4.8.x"
|
|
@@ -36,12 +36,13 @@ export interface ServiceWorkerIncomingRequest extends RequestWithoutMethods {
|
|
|
36
36
|
body?: ArrayBuffer | null
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
type ServiceWorkerIncomingResponse =
|
|
40
|
-
Response,
|
|
41
|
-
'type' | 'ok' | 'status' | 'statusText' | 'body' | 'headers' | 'redirected'
|
|
42
|
-
> & {
|
|
43
|
-
requestId: string
|
|
39
|
+
type ServiceWorkerIncomingResponse = {
|
|
44
40
|
isMockedResponse: boolean
|
|
41
|
+
request: ServiceWorkerIncomingRequest
|
|
42
|
+
response: Pick<
|
|
43
|
+
Response,
|
|
44
|
+
'type' | 'ok' | 'status' | 'statusText' | 'body' | 'headers' | 'redirected'
|
|
45
|
+
>
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
/**
|
|
@@ -87,8 +88,7 @@ export interface SetupWorkerInternalContext {
|
|
|
87
88
|
startOptions: RequiredDeep<StartOptions>
|
|
88
89
|
worker: ServiceWorker | null
|
|
89
90
|
registration: ServiceWorkerRegistration | null
|
|
90
|
-
getRequestHandlers()
|
|
91
|
-
requests: Map<string, Request>
|
|
91
|
+
getRequestHandlers: () => Array<RequestHandler | WebSocketHandler>
|
|
92
92
|
emitter: Emitter<LifeCycleEventsMap>
|
|
93
93
|
keepAliveInterval?: number
|
|
94
94
|
workerChannel: {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ServiceWorkerMessage,
|
|
8
8
|
WorkerChannel,
|
|
9
9
|
} from './utils/createMessageChannel'
|
|
10
|
-
import {
|
|
10
|
+
import { deserializeRequest } from '../../utils/deserializeRequest'
|
|
11
11
|
import { RequestHandler } from '~/core/handlers/RequestHandler'
|
|
12
12
|
import { handleRequest } from '~/core/utils/handleRequest'
|
|
13
13
|
import { RequiredDeep } from '~/core/typeUtils'
|
|
@@ -29,16 +29,15 @@ export const createRequestListener = (
|
|
|
29
29
|
const messageChannel = new WorkerChannel(event.ports[0])
|
|
30
30
|
|
|
31
31
|
const requestId = message.payload.id
|
|
32
|
-
const request =
|
|
32
|
+
const request = deserializeRequest(message.payload)
|
|
33
33
|
const requestCloneForLogs = request.clone()
|
|
34
34
|
|
|
35
|
-
// Make this the first
|
|
35
|
+
// Make this the first request clone before the
|
|
36
36
|
// request resolution pipeline even starts.
|
|
37
37
|
// Store the clone in cache so the first matching
|
|
38
38
|
// request handler would skip the cloning phase.
|
|
39
39
|
const requestClone = request.clone()
|
|
40
40
|
RequestHandler.cache.set(request, requestClone)
|
|
41
|
-
context.requests.set(requestId, requestClone)
|
|
42
41
|
|
|
43
42
|
try {
|
|
44
43
|
await handleRequest(
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
SetupWorkerInternalContext,
|
|
5
5
|
} from '../glossary'
|
|
6
6
|
import type { ServiceWorkerMessage } from './utils/createMessageChannel'
|
|
7
|
+
import { deserializeRequest } from '../../utils/deserializeRequest'
|
|
7
8
|
|
|
8
9
|
export function createResponseListener(context: SetupWorkerInternalContext) {
|
|
9
10
|
return (
|
|
@@ -14,12 +15,7 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
|
|
|
14
15
|
>,
|
|
15
16
|
) => {
|
|
16
17
|
const { payload: responseJson } = message
|
|
17
|
-
|
|
18
|
-
// Get the Request instance reference stored in the
|
|
19
|
-
// request listener.
|
|
20
|
-
const { requestId } = responseJson
|
|
21
|
-
const request = context.requests.get(requestId)!
|
|
22
|
-
context.requests.delete(requestId)
|
|
18
|
+
const request = deserializeRequest(responseJson.request)
|
|
23
19
|
|
|
24
20
|
/**
|
|
25
21
|
* CORS requests with `mode: "no-cors"` result in "opaque" responses.
|
|
@@ -28,12 +24,12 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
|
|
|
28
24
|
* @see https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
|
|
29
25
|
* @see https://github.com/mswjs/msw/issues/529
|
|
30
26
|
*/
|
|
31
|
-
if (responseJson.type?.includes('opaque')) {
|
|
27
|
+
if (responseJson.response.type?.includes('opaque')) {
|
|
32
28
|
return
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
const response =
|
|
36
|
-
responseJson.status === 0
|
|
32
|
+
responseJson.response.status === 0
|
|
37
33
|
? Response.error()
|
|
38
34
|
: new FetchResponse(
|
|
39
35
|
/**
|
|
@@ -42,8 +38,8 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
|
|
|
42
38
|
* throw when passed a non-null body, so ensure it's null here
|
|
43
39
|
* for those codes
|
|
44
40
|
*/
|
|
45
|
-
FetchResponse.isResponseWithBody(responseJson.status)
|
|
46
|
-
? responseJson.body
|
|
41
|
+
FetchResponse.isResponseWithBody(responseJson.response.status)
|
|
42
|
+
? responseJson.response.body
|
|
47
43
|
: null,
|
|
48
44
|
{
|
|
49
45
|
...responseJson,
|
|
@@ -59,9 +55,9 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
|
|
|
59
55
|
context.emitter.emit(
|
|
60
56
|
responseJson.isMockedResponse ? 'response:mocked' : 'response:bypass',
|
|
61
57
|
{
|
|
62
|
-
|
|
58
|
+
requestId: responseJson.request.id,
|
|
63
59
|
request,
|
|
64
|
-
|
|
60
|
+
response,
|
|
65
61
|
},
|
|
66
62
|
)
|
|
67
63
|
}
|
|
@@ -5,11 +5,11 @@ import type { ServiceWorkerIncomingRequest } from '../setupWorker/glossary'
|
|
|
5
5
|
* Converts a given request received from the Service Worker
|
|
6
6
|
* into a Fetch `Request` instance.
|
|
7
7
|
*/
|
|
8
|
-
export function
|
|
9
|
-
|
|
8
|
+
export function deserializeRequest(
|
|
9
|
+
serializedRequest: ServiceWorkerIncomingRequest,
|
|
10
10
|
): Request {
|
|
11
|
-
return new Request(
|
|
12
|
-
...
|
|
13
|
-
body: pruneGetRequestBody(
|
|
11
|
+
return new Request(serializedRequest.url, {
|
|
12
|
+
...serializedRequest,
|
|
13
|
+
body: pruneGetRequestBody(serializedRequest),
|
|
14
14
|
})
|
|
15
15
|
}
|
package/src/mockServiceWorker.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* Mock Service Worker.
|
|
6
6
|
* @see https://github.com/mswjs/msw
|
|
7
7
|
* - Please do NOT modify this file.
|
|
8
|
-
* - Please do NOT serve this file on production.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
const PACKAGE_VERSION = '<PACKAGE_VERSION>'
|
|
@@ -13,16 +12,16 @@ const INTEGRITY_CHECKSUM = '<INTEGRITY_CHECKSUM>'
|
|
|
13
12
|
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
14
13
|
const activeClientIds = new Set()
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
addEventListener('install', function () {
|
|
17
16
|
self.skipWaiting()
|
|
18
17
|
})
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
addEventListener('activate', function (event) {
|
|
21
20
|
event.waitUntil(self.clients.claim())
|
|
22
21
|
})
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
const clientId = event.source
|
|
23
|
+
addEventListener('message', async function (event) {
|
|
24
|
+
const clientId = Reflect.get(event.source || {}, 'id')
|
|
26
25
|
|
|
27
26
|
if (!clientId || !self.clients) {
|
|
28
27
|
return
|
|
@@ -94,17 +93,18 @@ self.addEventListener('message', async function (event) {
|
|
|
94
93
|
}
|
|
95
94
|
})
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
const { request } = event
|
|
99
|
-
|
|
96
|
+
addEventListener('fetch', function (event) {
|
|
100
97
|
// Bypass navigation requests.
|
|
101
|
-
if (request.mode === 'navigate') {
|
|
98
|
+
if (event.request.mode === 'navigate') {
|
|
102
99
|
return
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
// Opening the DevTools triggers the "only-if-cached" request
|
|
106
103
|
// that cannot be handled by the worker. Bypass such requests.
|
|
107
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
event.request.cache === 'only-if-cached' &&
|
|
106
|
+
event.request.mode !== 'same-origin'
|
|
107
|
+
) {
|
|
108
108
|
return
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -115,48 +115,62 @@ self.addEventListener('fetch', function (event) {
|
|
|
115
115
|
return
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// Generate unique request ID.
|
|
119
118
|
const requestId = crypto.randomUUID()
|
|
120
119
|
event.respondWith(handleRequest(event, requestId))
|
|
121
120
|
})
|
|
122
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {FetchEvent} event
|
|
124
|
+
* @param {string} requestId
|
|
125
|
+
*/
|
|
123
126
|
async function handleRequest(event, requestId) {
|
|
124
127
|
const client = await resolveMainClient(event)
|
|
128
|
+
const requestCloneForEvents = event.request.clone()
|
|
125
129
|
const response = await getResponse(event, client, requestId)
|
|
126
130
|
|
|
127
131
|
// Send back the response clone for the "response:*" life-cycle events.
|
|
128
132
|
// Ensure MSW is active and ready to handle the message, otherwise
|
|
129
133
|
// this message will pend indefinitely.
|
|
130
134
|
if (client && activeClientIds.has(client.id)) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
|
136
|
+
|
|
137
|
+
// Clone the response so both the client and the library could consume it.
|
|
138
|
+
const responseClone = response.clone()
|
|
139
|
+
|
|
140
|
+
sendToClient(
|
|
141
|
+
client,
|
|
142
|
+
{
|
|
143
|
+
type: 'RESPONSE',
|
|
144
|
+
payload: {
|
|
145
|
+
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
|
146
|
+
request: {
|
|
147
|
+
id: requestId,
|
|
148
|
+
...serializedRequest,
|
|
149
|
+
},
|
|
150
|
+
response: {
|
|
141
151
|
type: responseClone.type,
|
|
142
152
|
status: responseClone.status,
|
|
143
153
|
statusText: responseClone.statusText,
|
|
144
|
-
body: responseClone.body,
|
|
145
154
|
headers: Object.fromEntries(responseClone.headers.entries()),
|
|
155
|
+
body: responseClone.body,
|
|
146
156
|
},
|
|
147
157
|
},
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
},
|
|
159
|
+
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
|
160
|
+
)
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
return response
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the main client for the given event.
|
|
168
|
+
* Client that issues a request doesn't necessarily equal the client
|
|
169
|
+
* that registered the worker. It's with the latter the worker should
|
|
170
|
+
* communicate with during the response resolving phase.
|
|
171
|
+
* @param {FetchEvent} event
|
|
172
|
+
* @returns {Promise<Client | undefined>}
|
|
173
|
+
*/
|
|
160
174
|
async function resolveMainClient(event) {
|
|
161
175
|
const client = await self.clients.get(event.clientId)
|
|
162
176
|
|
|
@@ -184,12 +198,16 @@ async function resolveMainClient(event) {
|
|
|
184
198
|
})
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @param {FetchEvent} event
|
|
203
|
+
* @param {Client | undefined} client
|
|
204
|
+
* @param {string} requestId
|
|
205
|
+
* @returns {Promise<Response>}
|
|
206
|
+
*/
|
|
187
207
|
async function getResponse(event, client, requestId) {
|
|
188
|
-
const { request } = event
|
|
189
|
-
|
|
190
208
|
// Clone the request because it might've been already used
|
|
191
209
|
// (i.e. its body has been read and sent to the client).
|
|
192
|
-
const requestClone = request.clone()
|
|
210
|
+
const requestClone = event.request.clone()
|
|
193
211
|
|
|
194
212
|
function passthrough() {
|
|
195
213
|
// Cast the request headers to a new Headers instance
|
|
@@ -230,29 +248,17 @@ async function getResponse(event, client, requestId) {
|
|
|
230
248
|
}
|
|
231
249
|
|
|
232
250
|
// Notify the client that a request has been intercepted.
|
|
233
|
-
const
|
|
251
|
+
const serializedRequest = await serializeRequest(event.request)
|
|
234
252
|
const clientMessage = await sendToClient(
|
|
235
253
|
client,
|
|
236
254
|
{
|
|
237
255
|
type: 'REQUEST',
|
|
238
256
|
payload: {
|
|
239
257
|
id: requestId,
|
|
240
|
-
|
|
241
|
-
mode: request.mode,
|
|
242
|
-
method: request.method,
|
|
243
|
-
headers: Object.fromEntries(request.headers.entries()),
|
|
244
|
-
cache: request.cache,
|
|
245
|
-
credentials: request.credentials,
|
|
246
|
-
destination: request.destination,
|
|
247
|
-
integrity: request.integrity,
|
|
248
|
-
redirect: request.redirect,
|
|
249
|
-
referrer: request.referrer,
|
|
250
|
-
referrerPolicy: request.referrerPolicy,
|
|
251
|
-
body: requestBuffer,
|
|
252
|
-
keepalive: request.keepalive,
|
|
258
|
+
...serializedRequest,
|
|
253
259
|
},
|
|
254
260
|
},
|
|
255
|
-
[
|
|
261
|
+
[serializedRequest.body],
|
|
256
262
|
)
|
|
257
263
|
|
|
258
264
|
switch (clientMessage.type) {
|
|
@@ -268,6 +274,12 @@ async function getResponse(event, client, requestId) {
|
|
|
268
274
|
return passthrough()
|
|
269
275
|
}
|
|
270
276
|
|
|
277
|
+
/**
|
|
278
|
+
* @param {Client} client
|
|
279
|
+
* @param {any} message
|
|
280
|
+
* @param {Array<Transferable>} transferrables
|
|
281
|
+
* @returns {Promise<any>}
|
|
282
|
+
*/
|
|
271
283
|
function sendToClient(client, message, transferrables = []) {
|
|
272
284
|
return new Promise((resolve, reject) => {
|
|
273
285
|
const channel = new MessageChannel()
|
|
@@ -280,14 +292,18 @@ function sendToClient(client, message, transferrables = []) {
|
|
|
280
292
|
resolve(event.data)
|
|
281
293
|
}
|
|
282
294
|
|
|
283
|
-
client.postMessage(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
)
|
|
295
|
+
client.postMessage(message, [
|
|
296
|
+
channel.port2,
|
|
297
|
+
...transferrables.filter(Boolean),
|
|
298
|
+
])
|
|
287
299
|
})
|
|
288
300
|
}
|
|
289
301
|
|
|
290
|
-
|
|
302
|
+
/**
|
|
303
|
+
* @param {Response} response
|
|
304
|
+
* @returns {Response}
|
|
305
|
+
*/
|
|
306
|
+
function respondWithMock(response) {
|
|
291
307
|
// Setting response status code to 0 is a no-op.
|
|
292
308
|
// However, when responding with a "Response.error()", the produced Response
|
|
293
309
|
// instance will have status code set to 0. Since it's not possible to create
|
|
@@ -305,3 +321,24 @@ async function respondWithMock(response) {
|
|
|
305
321
|
|
|
306
322
|
return mockedResponse
|
|
307
323
|
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* @param {Request} request
|
|
327
|
+
*/
|
|
328
|
+
async function serializeRequest(request) {
|
|
329
|
+
return {
|
|
330
|
+
url: request.url,
|
|
331
|
+
mode: request.mode,
|
|
332
|
+
method: request.method,
|
|
333
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
334
|
+
cache: request.cache,
|
|
335
|
+
credentials: request.credentials,
|
|
336
|
+
destination: request.destination,
|
|
337
|
+
integrity: request.integrity,
|
|
338
|
+
redirect: request.redirect,
|
|
339
|
+
referrer: request.referrer,
|
|
340
|
+
referrerPolicy: request.referrerPolicy,
|
|
341
|
+
body: await request.arrayBuffer(),
|
|
342
|
+
keepalive: request.keepalive,
|
|
343
|
+
}
|
|
344
|
+
}
|