diff --git a/packages/better-fetch/src/fetch.ts b/packages/better-fetch/src/fetch.ts index e825b57..c894ad8 100644 --- a/packages/better-fetch/src/fetch.ts +++ b/packages/better-fetch/src/fetch.ts @@ -44,7 +44,9 @@ export const betterFetch = async < const headers = await getHeaders(opts); const body = getBody(opts, headers); const method = getMethod(__url, opts); - let context = { + // one stable object for the whole request so per-request state keyed on + // its identity survives hooks that return a replacement context + const context = { ...opts, url: _url, headers, @@ -59,7 +61,7 @@ export const betterFetch = async < if (onRequest) { const res = await onRequest(context); if (typeof res === "object" && res !== null) { - context = res; + Object.assign(context, res); } } } diff --git a/packages/better-fetch/src/test/create.test.ts b/packages/better-fetch/src/test/create.test.ts index 6de9b04..9659002 100644 --- a/packages/better-fetch/src/test/create.test.ts +++ b/packages/better-fetch/src/test/create.test.ts @@ -253,6 +253,37 @@ describe("create-fetch-runtime-test", () => { method: "PUT", }); }); + + it("keeps the request-context identity stable across replacing hooks", async () => { + const seen: object[] = []; + const capture = (id: string): BetterFetchPlugin => ({ + id, + name: id, + hooks: { + // returning a replacement must not break per-request identity + onRequest(context) { + seen.push(context); + return { ...context }; + }, + }, + }); + const $fetch = createFetch({ + baseURL: "http://localhost:4001", + customFetchImpl: async () => new Response(null, { status: 200 }), + plugins: [capture("a"), capture("b")], + }); + + let successRequest: object | undefined; + await $fetch("/", { + onSuccess(context) { + successRequest = context.request; + }, + }); + + expect(seen).toHaveLength(2); + expect(seen[1]).toBe(seen[0]); + expect(successRequest).toBe(seen[0]); + }); }); describe("create-fetch-type-test", () => { @@ -270,10 +301,8 @@ describe("create-fetch-type-test", () => { const res = await $fetch("/", { throw: true, }); - expectTypeOf(res).toMatchTypeOf< - { message: string } - >(); - }) + expectTypeOf(res).toMatchTypeOf<{ message: string }>(); + }); it("should return unknown if no output is defined", () => { const res = $fetch("/");