From 92c7ecc0ade184e40fc2aca940f4847d68d5d0df Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Sun, 15 Oct 2023 13:23:29 +0200 Subject: [PATCH] fix: transfer response buffer on safari --- src/browser/setupWorker/glossary.ts | 2 +- .../start/createRequestListener.ts | 32 +++++++++++++------ .../utils/supportsReadableStreamTransfer.ts | 17 ++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 src/browser/utils/supportsReadableStreamTransfer.ts diff --git a/src/browser/setupWorker/glossary.ts b/src/browser/setupWorker/glossary.ts index 084f3cc65..dfeab0ce7 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 } /** diff --git a/src/browser/setupWorker/start/createRequestListener.ts b/src/browser/setupWorker/start/createRequestListener.ts index dccd989de..72e64344d 100644 --- a/src/browser/setupWorker/start/createRequestListener.ts +++ b/src/browser/setupWorker/start/createRequestListener.ts @@ -12,6 +12,7 @@ import { handleRequest } from '~/core/utils/handleRequest' import { RequiredDeep } from '~/core/typeUtils' import { devUtils } from '~/core/utils/internal/devUtils' import { toResponseInit } from '~/core/utils/toResponseInit' +import { supportsReadableStreamTransfer } from '../../utils/supportsReadableStreamTransfer' export const createRequestListener = ( context: SetupWorkerInternalContext, @@ -47,18 +48,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 (supportsReadableStreamTransfer()) { + 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 + } +}