//@ts-check import { deferredPromise } from "./deferredPromise.mjs"; /** * Creates a response object that can dispatch progress. * @param {(received: number) => void} onProgress * @param {AbortSignal} [signal] * @param {number} [maxPackets] a maximum amount of packets to receive */ export const createTrackedResponse = ( onProgress, signal, maxPackets = 99999 ) => { /** @type {DeferredPromise>} */ let readerPromise = deferredPromise(); /** @type {DeferredPromise} */ const successPromise = deferredPromise(); let received = 0; let started = false; let failed = false; let success = false; const response = new Response( new ReadableStream({ async start(controller) { const onError = (/** @type {Error} */ error) => { failed = true; success = false; controller.close(); controller.error(error); successPromise.reject(error); }; const onSuccess = () => { failed = false; success = true; successPromise.resolve(response); }; signal && signal.addEventListener("abort", () => onError(new Error(`Stream aborted`)) ); try { const reader = await readerPromise; started = true; try { while (true && maxPackets-- > 0) { const { done, value } = await reader.read(); if (done) { controller.close(); onProgress(received); break; } received += value.byteLength; controller.enqueue(value); onProgress(received); } onSuccess(); } catch (error) { onError(error); } } catch (readerError) { onError(readerError); } }, }) ); const start = readerPromise.resolve; return Object.assign(successPromise, { start, get response() { return response; }, get received() { return received; }, get isStarted() { return started; }, get isFailed() { return failed; }, get isSuccess() { return success; }, get isUnknown() { return success === false && failed === false; }, }); };