mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
fix(browser): observe fetch settlement after synchronous abort
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import process from "node:process";
|
||||
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { fetchWebSourceMaps } from "./WebSourceMapFetcher.js";
|
||||
@@ -9,6 +11,11 @@ const input = analyzeWebBundleInputSchema.parse({
|
||||
target_id: "page-1",
|
||||
fetch_source_maps: true,
|
||||
});
|
||||
const request = {
|
||||
scriptKey: `scr_${"1".repeat(64)}`,
|
||||
declaredUrl: "https://app.example.test/app.js.map",
|
||||
fetchUrl: "https://app.example.test/app.js.map",
|
||||
};
|
||||
|
||||
it("reports an empty selected request set without starting fetch work", async () => {
|
||||
const result = await fetchWebSourceMaps([], input, undefined, {
|
||||
@@ -34,3 +41,76 @@ it("honors cancellation before returning an empty request set", async () => {
|
||||
reason,
|
||||
);
|
||||
});
|
||||
|
||||
it("cancels a response body that arrives after its fetch host aborts synchronously", async () => {
|
||||
const controller = new AbortController();
|
||||
const reason = new Error("fetch host aborted during admission");
|
||||
let resolveFetch!: (response: Response) => void;
|
||||
const fetchPromise = new Promise<Response>((resolve) => {
|
||||
resolveFetch = resolve;
|
||||
});
|
||||
let markCancelled!: () => void;
|
||||
const cancelled = new Promise<void>((resolve) => {
|
||||
markCancelled = resolve;
|
||||
});
|
||||
let cancellationReason: unknown;
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
cancel: (cause) => {
|
||||
cancellationReason = cause;
|
||||
markCancelled();
|
||||
},
|
||||
});
|
||||
const fetchInput = analyzeWebBundleInputSchema.parse({
|
||||
cdp_endpoint: "http://127.0.0.1:9222",
|
||||
allowed_origins: [new URL(request.fetchUrl).origin],
|
||||
target_id: "page-1",
|
||||
fetch_source_maps: true,
|
||||
});
|
||||
const pending = fetchWebSourceMaps([request], fetchInput, controller.signal, {
|
||||
fetch: () => {
|
||||
controller.abort(reason);
|
||||
return fetchPromise;
|
||||
},
|
||||
});
|
||||
|
||||
await expect(pending).rejects.toBe(reason);
|
||||
resolveFetch(new Response(body));
|
||||
await cancelled;
|
||||
expect(cancellationReason).toBe(reason);
|
||||
});
|
||||
|
||||
it("observes a fetch rejection that arrives after its host aborts synchronously", async () => {
|
||||
const controller = new AbortController();
|
||||
const reason = new Error("fetch host aborted during admission");
|
||||
const lateFailure = new Error("late fetch failure");
|
||||
let rejectFetch!: (cause: unknown) => void;
|
||||
const fetchPromise = new Promise<Response>((_resolve, reject) => {
|
||||
rejectFetch = reject;
|
||||
});
|
||||
const fetchInput = analyzeWebBundleInputSchema.parse({
|
||||
cdp_endpoint: "http://127.0.0.1:9222",
|
||||
allowed_origins: [new URL(request.fetchUrl).origin],
|
||||
target_id: "page-1",
|
||||
fetch_source_maps: true,
|
||||
});
|
||||
const pending = fetchWebSourceMaps([request], fetchInput, controller.signal, {
|
||||
fetch: () => {
|
||||
controller.abort(reason);
|
||||
return fetchPromise;
|
||||
},
|
||||
});
|
||||
let unhandled: unknown;
|
||||
const onUnhandled = (cause: unknown): void => {
|
||||
unhandled = cause;
|
||||
};
|
||||
process.on("unhandledRejection", onUnhandled);
|
||||
|
||||
try {
|
||||
await expect(pending).rejects.toBe(reason);
|
||||
rejectFetch(lateFailure);
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
expect(unhandled).toBeUndefined();
|
||||
} finally {
|
||||
process.off("unhandledRejection", onUnhandled);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -522,7 +522,6 @@ const promiseWithAbort = (
|
||||
signal: AbortSignal | undefined,
|
||||
): Promise<Response> => {
|
||||
if (signal === undefined) return promise;
|
||||
if (signal.aborted) return Promise.reject(signal.reason);
|
||||
return new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
const cleanup = (): void => signal.removeEventListener("abort", abort);
|
||||
@@ -555,6 +554,7 @@ const promiseWithAbort = (
|
||||
reject(cause);
|
||||
},
|
||||
);
|
||||
if (signal.aborted) abort();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user