2023-06-06 22:45:16 +00:00
|
|
|
//@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<ReadableStreamDefaultReader<Uint8Array>>} */
|
|
|
|
let readerPromise = deferredPromise();
|
|
|
|
/** @type {DeferredPromise<Response>} */
|
|
|
|
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;
|
|
|
|
|
2023-06-07 16:51:18 +00:00
|
|
|
/** @type {Promise<Response>} */
|
|
|
|
return {
|
2023-06-06 22:45:16 +00:00
|
|
|
start,
|
2023-06-07 16:51:18 +00:00
|
|
|
/** @type {Promise<Response>["then"]} */
|
|
|
|
then: successPromise.then.bind(successPromise),
|
|
|
|
/** @type {Promise["catch"]} */
|
|
|
|
catch: successPromise.catch.bind(successPromise),
|
|
|
|
/** @type {Promise["finally"]} */
|
|
|
|
finally: successPromise.finally.bind(successPromise),
|
2023-06-06 22:45:16 +00:00
|
|
|
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;
|
|
|
|
},
|
2023-06-07 16:51:18 +00:00
|
|
|
};
|
2023-06-06 22:45:16 +00:00
|
|
|
};
|
2023-06-07 16:51:18 +00:00
|
|
|
|
|
|
|
export default createTrackedResponse;
|