export type FetchHandler = ( req: Request, ) => Response | Promise; export type CapturedRequest = { method: string; url: string; headers: Headers; body: string; }; export type FetchStub = { calls: CapturedRequest[]; restore: () => void; }; export const installFetchStub = (handler: FetchHandler): FetchStub => { const original = globalThis.fetch; const calls: CapturedRequest[] = []; let restored = false; globalThis.fetch = async (input, init) => { const req = new Request(input as RequestInfo, init); const body = req.method === "GET" || req.method === "HEAD" ? "" : await req.clone().text(); calls.push({ method: req.method, url: req.url, headers: new Headers(req.headers), body, }); return await handler(req); }; return { calls, restore: () => { if (restored) throw new Error("FetchStub.restore() called twice"); restored = true; globalThis.fetch = original; }, }; }; export const jsonResponse = ( body: unknown, init: ResponseInit = {}, ): Response => { return new Response(JSON.stringify(body), { status: init.status ?? 200, headers: { "Content-Type": "application/json", ...(init.headers ?? {}) }, }); }; export const errorResponse = (status: number, text = "error"): Response => new Response(text, { status });