From a17808ad7cd92e68b445b458c83af86b71b2b4ba Mon Sep 17 00:00:00 2001 From: Ivan Date: Wed, 26 Nov 2025 05:18:44 +0300 Subject: [PATCH] :bug: fix: properly await async headers functions in treaty The `headers` config option was not awaiting async functions, causing Promise objects to be passed through instead of resolved header values. - Make `processHeaders` async and await function results - Move config header processing inside async IIFE - Update type to allow `MaybePromise` return from header functions - Add test for async headers configuration --- src/treaty2/index.ts | 24 ++++++++++++------------ src/treaty2/types.ts | 2 +- test/treaty2.test.ts | 31 +++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/treaty2/index.ts b/src/treaty2/index.ts index d479411..b2a1f87 100644 --- a/src/treaty2/index.ts +++ b/src/treaty2/index.ts @@ -64,16 +64,16 @@ const createNewFile = (v: File) => reader.readAsArrayBuffer(v) }) -const processHeaders = ( +const processHeaders = async ( h: Treaty.Config['headers'], path: string, options: RequestInit = {}, headers: Record = {} -): Record => { +): Promise> => { if (Array.isArray(h)) { for (const value of h) if (!Array.isArray(value)) - headers = processHeaders(value, path, options, headers) + headers = await processHeaders(value, path, options, headers) else { const key = value[0] if (typeof key === 'string') @@ -93,7 +93,7 @@ const processHeaders = ( if (h instanceof Headers) return processHeaders(h, path, options, headers) - const v = h(path, options) + const v = await h(path, options) if (v) return processHeaders(v, path, options, headers) return headers @@ -204,8 +204,6 @@ const createProxy = ( method === 'head' || method === 'subscribe' - headers = processHeaders(headers, path, options) - const query = isGetOrHead ? (body as Record) ?.query @@ -260,6 +258,8 @@ const createProxy = ( } return (async () => { + headers = await processHeaders(headers, path, options) + let fetchInit = { method: method?.toUpperCase(), body, @@ -269,12 +269,12 @@ const createProxy = ( fetchInit.headers = { ...headers, - ...processHeaders( + ...(await processHeaders( // For GET and HEAD, options is moved to body (1st param) isGetOrHead ? body?.headers : options?.headers, path, fetchInit - ) + )) } const fetchOpts = @@ -301,11 +301,11 @@ const createProxy = ( ...temp, headers: { ...fetchInit.headers, - ...processHeaders( + ...(await processHeaders( temp.headers, path, fetchInit - ) + )) } } } @@ -396,11 +396,11 @@ const createProxy = ( ...temp, headers: { ...fetchInit.headers, - ...processHeaders( + ...(await processHeaders( temp.headers, path, fetchInit - ) + )) } as Record } } diff --git a/src/treaty2/types.ts b/src/treaty2/types.ts index bd21178..4c09f27 100644 --- a/src/treaty2/types.ts +++ b/src/treaty2/types.ts @@ -180,7 +180,7 @@ export namespace Treaty { | (( path: string, options: RequestInit - ) => RequestInit['headers'] | void) + ) => MaybePromise) > onRequest?: MaybeArray< ( diff --git a/test/treaty2.test.ts b/test/treaty2.test.ts index 4475c6b..22cb05f 100644 --- a/test/treaty2.test.ts +++ b/test/treaty2.test.ts @@ -572,6 +572,37 @@ describe('Treaty2', () => { }) }) + it('accept async headers configuration', async () => { + const client = treaty(app, { + async headers(path) { + // Simulate async operation (e.g., fetching token) + await new Promise((r) => setTimeout(r, 10)) + if (path === '/headers-custom') + return { + 'x-custom': 'custom' + } + }, + async onResponse(response) { + return { intercepted: true, data: await response.json() } + } + }) + + const headers = { username: 'a', alias: 'Kristen' } as const + + const { data } = await client['headers-custom'].get({ + headers + }) + + expect(data).toEqual({ + // @ts-expect-error + intercepted: true, + data: { + ...headers, + 'x-custom': 'custom' + } + }) + }) + it('accept headers configuration array', async () => { const client = treaty(app, { headers: [