UNPKG

up-fetch

Version:

Advanced fetch client builder for typescript.

62 lines (55 loc) 2.08 kB
import type { StreamingEvent } from './types' /** * Safari does not support for await...of iteration on response/request bodies, * so we use the ReadableStream reader API directly */ const isWebkit = typeof window !== 'undefined' && /AppleWebKit/i.test(navigator.userAgent) && !/Chrome/i.test(navigator.userAgent) export async function toStreamable<R extends Request | Response>( reqOrRes: R, onChunk?: (event: StreamingEvent, reqOrRes: R) => void, ): Promise<R> { const isResponse = 'ok' in reqOrRes const isNotSupported = isWebkit && !isResponse // clone reqOrRes here to support IOS & Safari 14, otherwise support 15+ if (isNotSupported || !onChunk || !reqOrRes.clone().body) return reqOrRes const contentLength = reqOrRes.headers.get('content-length') let totalBytes: number = +(contentLength || 0) // For the Request, when no "Content-Length" header is present, we read the total bytes from the body if (!isResponse && !contentLength) { const reader = reqOrRes.clone().body!.getReader() while (true) { const { value, done } = await reader.read() if (done) break totalBytes += value.byteLength } } let transferredBytes = 0 await onChunk( { totalBytes, transferredBytes, chunk: new Uint8Array() }, reqOrRes, ) const stream = new ReadableStream({ async start(controller) { const reader = reqOrRes.body!.getReader() while (true) { const { value, done } = await reader.read() if (done) break transferredBytes += value.byteLength totalBytes = Math.max(totalBytes, transferredBytes) await onChunk( { totalBytes, transferredBytes, chunk: value }, reqOrRes, ) controller.enqueue(value) } controller.close() }, }) return isResponse ? (new Response(stream, reqOrRes) as R) : // @ts-expect-error outdated ts types (new Request(reqOrRes, { body: stream, duplex: 'half' }) as R) }