diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index dc3edcc236..faa56cfe2d 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -2,15 +2,13 @@ /* tslint:disable */ /** - * Mock Service Worker. + * Mock Service Worker (1.3.3). * @see https://github.com/mswjs/msw * - Please do NOT modify this file. * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.4.8' -const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' const activeClientIds = new Set() self.addEventListener('install', function () { @@ -49,10 +47,7 @@ self.addEventListener('message', async function (event) { case 'INTEGRITY_CHECK_REQUEST': { sendToClient(client, { type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, + payload: INTEGRITY_CHECKSUM, }) break } @@ -91,6 +86,12 @@ self.addEventListener('message', async function (event) { self.addEventListener('fetch', function (event) { const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } // Bypass navigation requests. if (request.mode === 'navigate') { @@ -111,8 +112,29 @@ self.addEventListener('fetch', function (event) { } // Generate unique request ID. - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId)) + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}` + ) + }) + ) }) async function handleRequest(event, requestId) { @@ -124,24 +146,21 @@ async function handleRequest(event, requestId) { // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { ;(async function () { - const responseClone = response.clone() - - sendToClient( - client, - { - type: 'RESPONSE', - payload: { - requestId, - isMockedResponse: IS_MOCKED_RESPONSE in response, - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - body: responseClone.body, - headers: Object.fromEntries(responseClone.headers.entries()), - }, + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, }, - [responseClone.body] - ) + }) })() } @@ -177,20 +196,20 @@ async function resolveMainClient(event) { async function getResponse(event, client, requestId) { const { request } = event - - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const requestClone = request.clone() + const clonedRequest = request.clone() function passthrough() { - const headers = Object.fromEntries(requestClone.headers.entries()) + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) - // Remove internal MSW request header so the passthrough request - // complies with any potential CORS preflight checks on the server. - // Some servers forbid unknown request headers. - delete headers['x-msw-intention'] + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] - return fetch(requestClone, { headers }) + return fetch(clonedRequest, { headers }) } // Bypass mocking when the client is not active. @@ -206,46 +225,57 @@ async function getResponse(event, client, requestId) { return passthrough() } + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + // Notify the client that a request has been intercepted. - const requestBuffer = await request.arrayBuffer() - const clientMessage = await sendToClient( - client, - { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - mode: request.mode, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: requestBuffer, - keepalive: request.keepalive, - }, + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, }, - [requestBuffer] - ) + }) switch (clientMessage.type) { case 'MOCK_RESPONSE': { return respondWithMock(clientMessage.data) } - case 'PASSTHROUGH': { + case 'MOCK_NOT_FOUND': { return passthrough() } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError + } } return passthrough() } -function sendToClient(client, message, transferrables = []) { +function sendToClient(client, message) { return new Promise((resolve, reject) => { const channel = new MessageChannel() @@ -257,28 +287,17 @@ function sendToClient(client, message, transferrables = []) { resolve(event.data) } - client.postMessage( - message, - [channel.port2].concat(transferrables.filter(Boolean)) - ) + client.postMessage(message, [channel.port2]) }) } -async function respondWithMock(response) { - // Setting response status code to 0 is a no-op. - // However, when responding with a "Response.error()", the produced Response - // instance will have status code set to 0. Since it's not possible to create - // a Response instance with status code 0, handle that use-case separately. - if (response.status === 0) { - return Response.error() - } - - const mockedResponse = new Response(response.body, response) - - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs) }) +} - return mockedResponse +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) }