mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 22:11:23 +00:00
refactor(infra): make fetch wrapping idempotent
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import { wrapFetchWithAbortSignal } from "./fetch.js";
|
import { resolveFetch, wrapFetchWithAbortSignal } from "./fetch.js";
|
||||||
|
|
||||||
describe("wrapFetchWithAbortSignal", () => {
|
describe("wrapFetchWithAbortSignal", () => {
|
||||||
it("adds duplex for requests with a body", async () => {
|
it("adds duplex for requests with a body", async () => {
|
||||||
@@ -182,4 +182,31 @@ describe("wrapFetchWithAbortSignal", () => {
|
|||||||
expect(addEventListener).not.toHaveBeenCalled();
|
expect(addEventListener).not.toHaveBeenCalled();
|
||||||
expect(removeEventListener).not.toHaveBeenCalled();
|
expect(removeEventListener).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns the same function when called with an already wrapped fetch", () => {
|
||||||
|
const fetchImpl = vi.fn(async () => ({ ok: true }) as Response);
|
||||||
|
const wrapped = wrapFetchWithAbortSignal(fetchImpl);
|
||||||
|
|
||||||
|
expect(wrapFetchWithAbortSignal(wrapped)).toBe(wrapped);
|
||||||
|
expect(resolveFetch(wrapped)).toBe(wrapped);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps preconnect bound to the original fetch implementation", () => {
|
||||||
|
const preconnectSpy = vi.fn(function (this: unknown) {
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
const fetchImpl = vi.fn(async () => ({ ok: true }) as Response) as typeof fetch & {
|
||||||
|
preconnect: (url: string, init?: { credentials?: RequestCredentials }) => unknown;
|
||||||
|
};
|
||||||
|
fetchImpl.preconnect = preconnectSpy;
|
||||||
|
|
||||||
|
const wrapped = wrapFetchWithAbortSignal(fetchImpl) as typeof fetch & {
|
||||||
|
preconnect: (url: string, init?: { credentials?: RequestCredentials }) => unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
const seenThis = wrapped.preconnect("https://example.com");
|
||||||
|
|
||||||
|
expect(preconnectSpy).toHaveBeenCalledOnce();
|
||||||
|
expect(seenThis).toBe(fetchImpl);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ type FetchWithPreconnect = typeof fetch & {
|
|||||||
|
|
||||||
type RequestInitWithDuplex = RequestInit & { duplex?: "half" };
|
type RequestInitWithDuplex = RequestInit & { duplex?: "half" };
|
||||||
|
|
||||||
|
const wrapFetchWithAbortSignalMarker = Symbol.for("openclaw.fetch.abort-signal-wrapped");
|
||||||
|
|
||||||
|
type FetchWithAbortSignalMarker = typeof fetch & {
|
||||||
|
[wrapFetchWithAbortSignalMarker]?: true;
|
||||||
|
};
|
||||||
|
|
||||||
function withDuplex(
|
function withDuplex(
|
||||||
init: RequestInit | undefined,
|
init: RequestInit | undefined,
|
||||||
input: RequestInfo | URL,
|
input: RequestInfo | URL,
|
||||||
@@ -28,6 +34,10 @@ function withDuplex(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function wrapFetchWithAbortSignal(fetchImpl: typeof fetch): typeof fetch {
|
export function wrapFetchWithAbortSignal(fetchImpl: typeof fetch): typeof fetch {
|
||||||
|
if ((fetchImpl as FetchWithAbortSignalMarker)[wrapFetchWithAbortSignalMarker]) {
|
||||||
|
return fetchImpl;
|
||||||
|
}
|
||||||
|
|
||||||
const wrapped = ((input: RequestInfo | URL, init?: RequestInit) => {
|
const wrapped = ((input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
const patchedInit = withDuplex(init, input);
|
const patchedInit = withDuplex(init, input);
|
||||||
const signal = patchedInit?.signal;
|
const signal = patchedInit?.signal;
|
||||||
@@ -73,13 +83,21 @@ export function wrapFetchWithAbortSignal(fetchImpl: typeof fetch): typeof fetch
|
|||||||
}
|
}
|
||||||
}) as FetchWithPreconnect;
|
}) as FetchWithPreconnect;
|
||||||
|
|
||||||
|
const wrappedFetch = Object.assign(wrapped, fetchImpl) as FetchWithPreconnect;
|
||||||
const fetchWithPreconnect = fetchImpl as FetchWithPreconnect;
|
const fetchWithPreconnect = fetchImpl as FetchWithPreconnect;
|
||||||
wrapped.preconnect =
|
wrappedFetch.preconnect =
|
||||||
typeof fetchWithPreconnect.preconnect === "function"
|
typeof fetchWithPreconnect.preconnect === "function"
|
||||||
? fetchWithPreconnect.preconnect.bind(fetchWithPreconnect)
|
? fetchWithPreconnect.preconnect.bind(fetchWithPreconnect)
|
||||||
: () => {};
|
: () => {};
|
||||||
|
|
||||||
return Object.assign(wrapped, fetchImpl);
|
Object.defineProperty(wrappedFetch, wrapFetchWithAbortSignalMarker, {
|
||||||
|
value: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: false,
|
||||||
|
writable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return wrappedFetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveFetch(fetchImpl?: typeof fetch): typeof fetch | undefined {
|
export function resolveFetch(fetchImpl?: typeof fetch): typeof fetch | undefined {
|
||||||
|
|||||||
Reference in New Issue
Block a user