diff --git a/src/browser/setupWorker/glossary.ts b/src/browser/setupWorker/glossary.ts index 084f3cc65..23017c6ec 100644 --- a/src/browser/setupWorker/glossary.ts +++ b/src/browser/setupWorker/glossary.ts @@ -72,7 +72,7 @@ export type ServiceWorkerOutgoingEventTypes = | 'CLIENT_CLOSED' export interface StringifiedResponse extends ResponseInit { - body: string | ReadableStream | null + body: string | ArrayBuffer | ReadableStream | null } /** @@ -146,7 +146,10 @@ export interface SetupWorkerInternalContext { ServiceWorkerMessage > } - useFallbackMode: boolean + supports: { + serviceWorkerApi: boolean + readableStreamTransfer: boolean + } fallbackInterceptor?: Interceptor } diff --git a/src/browser/setupWorker/setupWorker.ts b/src/browser/setupWorker/setupWorker.ts index 301e4e520..b89376641 100644 --- a/src/browser/setupWorker/setupWorker.ts +++ b/src/browser/setupWorker/setupWorker.ts @@ -20,6 +20,7 @@ import { SetupApi } from '~/core/SetupApi' import { mergeRight } from '~/core/utils/internal/mergeRight' import { LifeCycleEventsMap } from '~/core/sharedOptions' import { SetupWorker } from './glossary' +import { supportsReadableStreamTransfer } from '../utils/supportsReadableStreamTransfer' interface Listener { target: EventTarget @@ -142,8 +143,11 @@ export class SetupWorkerApi }) }, }, - useFallbackMode: - !('serviceWorker' in navigator) || location.protocol === 'file:', + supports: { + serviceWorkerApi: + !('serviceWorker' in navigator) || location.protocol === 'file:', + readableStreamTransfer: supportsReadableStreamTransfer(), + }, } /** @@ -156,11 +160,11 @@ export class SetupWorkerApi }, }) - this.startHandler = context.useFallbackMode + this.startHandler = context.supports.serviceWorkerApi ? createFallbackStart(context) : createStartHandler(context) - this.stopHandler = context.useFallbackMode + this.stopHandler = context.supports.serviceWorkerApi ? createFallbackStop(context) : createStop(context) diff --git a/src/browser/setupWorker/start/createRequestListener.ts b/src/browser/setupWorker/start/createRequestListener.ts index dccd989de..b6ee1e56a 100644 --- a/src/browser/setupWorker/start/createRequestListener.ts +++ b/src/browser/setupWorker/start/createRequestListener.ts @@ -47,18 +47,29 @@ export const createRequestListener = ( // ".log()" method of the request handler. const responseClone = response.clone() const responseInit = toResponseInit(response) - const responseStream = responseClone.body - messageChannel.postMessage( - 'MOCK_RESPONSE', - { + /** + * @note Safari doesn't support transferring a "ReadableStream". + * Check that the browser supports that before sending it to the worker. + */ + if (context.supports.readableStreamTransfer) { + const responseStream = response.body + messageChannel.postMessage( + 'MOCK_RESPONSE', + { + ...responseInit, + body: responseStream, + }, + responseStream ? [responseStream] : undefined, + ) + } else { + // As a fallback, send the response body buffer to the worker. + const responseBuffer = await responseClone.arrayBuffer() + messageChannel.postMessage('MOCK_RESPONSE', { ...responseInit, - body: responseStream, - }, - // Transfer response's buffer so it could - // be sent over to the worker. - responseStream ? [responseStream] : undefined, - ) + body: responseBuffer, + }) + } if (!options.quiet) { context.emitter.once('response:mocked', ({ response }) => { diff --git a/src/browser/utils/supportsReadableStreamTransfer.ts b/src/browser/utils/supportsReadableStreamTransfer.ts new file mode 100644 index 000000000..b1c5dc295 --- /dev/null +++ b/src/browser/utils/supportsReadableStreamTransfer.ts @@ -0,0 +1,17 @@ +/** + * Returns a boolean indicating whether the current browser + * supports `ReadableStream` as a `Transferable` when posting + * messages. + */ +export function supportsReadableStreamTransfer() { + try { + const stream = new ReadableStream({ + start: (controller) => controller.close(), + }) + const message = new MessageChannel() + message.port1.postMessage(stream, [stream]) + return true + } catch (error) { + return false + } +}