Complete provider waits and lifecycle visibility

Release aborted Real-Debrid, AllDebrid, and BestDebrid web callers even when underlying requests ignore cancellation while retaining terminal rejection observers. Publish provider cooldown deadlines and emit a fresh idle snapshot at the earliest expiry. Abort and visibly drain post-processing before dispatching one pending restart. Surface lifecycle phase, reason, retry countdown, and remaining work in the download controls. Add focused regressions for provider abort races, cooldown expiry, post-processing drain, pending start visibility, and the updated rapid stop contract.
This commit is contained in:
Sucukdeluxe
2026-08-22 10:39:07 +02:00
parent 1f97ce8b42
commit ad29239661
13 changed files with 595 additions and 82 deletions
+43 -7
View File
@@ -42,7 +42,7 @@ function throwIfAborted(signal?: AbortSignal): void {
}
}
async function sleepWithSignal(ms: number, signal?: AbortSignal): Promise<void> {
async function sleepWithSignal(ms: number, signal?: AbortSignal): Promise<void> {
if (!signal) {
await sleep(ms);
return;
@@ -68,8 +68,44 @@ async function sleepWithSignal(ms: number, signal?: AbortSignal): Promise<void>
};
signal.addEventListener("abort", onAbort, { once: true });
});
}
});
}
async function raceWithAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
if (!signal) {
return promise;
}
if (signal.aborted) {
throw abortError();
}
return new Promise<T>((resolve, reject) => {
let settled = false;
const onAbort = (): void => {
if (settled) {
return;
}
settled = true;
signal.removeEventListener("abort", onAbort);
reject(abortError());
};
signal.addEventListener("abort", onAbort, { once: true });
promise.then((value) => {
if (settled) {
return;
}
settled = true;
signal.removeEventListener("abort", onAbort);
resolve(value);
}, (error) => {
if (settled) {
return;
}
settled = true;
signal.removeEventListener("abort", onAbort);
reject(error);
});
});
}
function asRecord(value: unknown): Record<string, unknown> | null {
if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -280,10 +316,10 @@ export class AllDebridWebFallback {
}
return job();
};
const run = this.queue.then(guardedJob, guardedJob);
this.queue = run.then(() => undefined, () => undefined);
return run;
}
const run = this.queue.then(guardedJob, guardedJob);
this.queue = run.then(() => undefined, () => undefined);
return raceWithAbort(run, signal);
}
private async ensureLoginWindow(): Promise<BrowserWindow> {
const partition = this.getPartition();