refactor: drop dead query helpers and simplify read deadline (WealthLedger/frontend#141) (#149)
This commit was merged in pull request #149.
This commit is contained in:
+27
-28
@@ -208,51 +208,50 @@ function parseJson(body: string): unknown {
|
||||
}
|
||||
|
||||
async function readAttempt(request: Request): Promise<ReadAttempt> {
|
||||
const controller = new AbortController();
|
||||
const failure: { reason?: "abort" | "timeout" } = {};
|
||||
|
||||
const { promise: deadline, reject: rejectDeadline }
|
||||
= Promise.withResolvers<never>();
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
failure.reason = "timeout";
|
||||
controller.abort();
|
||||
rejectDeadline(abortError());
|
||||
}, GET_DEADLINE_MS);
|
||||
|
||||
const abort = () => {
|
||||
failure.reason = "abort";
|
||||
controller.abort();
|
||||
rejectDeadline(abortError());
|
||||
};
|
||||
|
||||
request.signal.addEventListener("abort", abort, { once: true });
|
||||
const timedRequest = new Request(request, { signal: controller.signal });
|
||||
// Manual timeout (not AbortSignal.timeout) so fake-timer tests can advance it.
|
||||
const timeout = new AbortController();
|
||||
const timer = setTimeout(() => timeout.abort(), GET_DEADLINE_MS);
|
||||
const signal = AbortSignal.any([request.signal, timeout.signal]);
|
||||
|
||||
try {
|
||||
const response = await Promise.race([fetch(timedRequest), deadline]);
|
||||
const body = await Promise.race([response.text(), deadline]);
|
||||
const problem = parseJson(body);
|
||||
// MSW keeps a stalled body readable after abort, so keep the race.
|
||||
const response = await Promise.race([
|
||||
fetch(new Request(request, { signal })),
|
||||
rejectOnAbort(signal),
|
||||
]);
|
||||
|
||||
const body = await Promise.race([response.text(), rejectOnAbort(signal)]);
|
||||
|
||||
return {
|
||||
problem,
|
||||
problem: parseJson(body),
|
||||
reason: "transient",
|
||||
response: new Response(body, response),
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
if (failure.reason === "abort") return { reason: "abort" };
|
||||
if (failure.reason === "timeout" || isNetworkFailure(error))
|
||||
if (request.signal.aborted) return { reason: "abort" };
|
||||
// Timeout or network failure: retryable.
|
||||
if (timeout.signal.aborted || isNetworkFailure(error))
|
||||
return { reason: "transient" };
|
||||
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
clearTimeout(timeout);
|
||||
request.signal.removeEventListener("abort", abort);
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
function rejectOnAbort(signal: AbortSignal): Promise<never> {
|
||||
return new Promise((_resolve, reject) => {
|
||||
// The signal is either already aborted or one of two parents firing once.
|
||||
signal.addEventListener(
|
||||
"abort",
|
||||
() => reject(abortError()),
|
||||
{ once: true },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function retryAfterMs(response: Response): number | undefined {
|
||||
const header = response.headers.get("Retry-After");
|
||||
if (!header) return undefined;
|
||||
|
||||
Reference in New Issue
Block a user