Skip to content

Commit 933e754

Browse files
DennisBetawerkpi0
andauthored
fix: deep merge fetch options (#243)
Co-authored-by: Pooya Parsa <pooya@pi0.io>
1 parent 253707a commit 933e754

File tree

3 files changed

+87
-5
lines changed

3 files changed

+87
-5
lines changed

src/fetch.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
detectResponseType,
99
ResponseType,
1010
MappedType,
11+
mergeFetchOptions,
1112
} from "./utils";
1213

1314
export interface CreateFetchOptions {
@@ -129,7 +130,7 @@ export function createFetch(globalOptions: CreateFetchOptions): $Fetch {
129130
) {
130131
const context: FetchContext = {
131132
request: _request,
132-
options: { ...globalOptions.defaults, ..._options },
133+
options: mergeFetchOptions(_options, globalOptions.defaults, Headers),
133134
response: undefined,
134135
error: undefined,
135136
};

src/utils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { FetchOptions } from "./fetch";
2+
13
const payloadMethods = new Set(
24
Object.freeze(["PATCH", "POST", "PUT", "DELETE"])
35
);
@@ -71,3 +73,39 @@ export function detectResponseType(_contentType = ""): ResponseType {
7173

7274
return "blob";
7375
}
76+
77+
// Merging of fetch option objects.
78+
export function mergeFetchOptions(
79+
input: FetchOptions | undefined,
80+
defaults: FetchOptions | undefined,
81+
Headers = globalThis.Headers
82+
): FetchOptions {
83+
const merged: FetchOptions = {
84+
...defaults,
85+
...input,
86+
};
87+
88+
// Merge params and query
89+
if (defaults?.params && input?.params) {
90+
merged.params = {
91+
...defaults?.params,
92+
...input?.params,
93+
};
94+
}
95+
if (defaults?.query && input?.query) {
96+
merged.query = {
97+
...defaults?.query,
98+
...input?.query,
99+
};
100+
}
101+
102+
// Merge headers
103+
if (defaults?.headers && input?.headers) {
104+
merged.headers = new Headers(defaults?.headers || {});
105+
for (const [key, value] of new Headers(input?.headers || {})) {
106+
merged.headers.set(key, value);
107+
}
108+
}
109+
110+
return merged;
111+
}

test/index.test.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ describe("ofetch", () => {
3131
"/url",
3232
eventHandler((event) => event.node.req.url)
3333
)
34+
.use(
35+
"/echo",
36+
eventHandler(async (event) => ({
37+
path: event.path,
38+
body:
39+
event.node.req.method === "POST"
40+
? await readRawBody(event)
41+
: undefined,
42+
headers: event.node.req.headers,
43+
}))
44+
)
3445
.use(
3546
"/post",
3647
eventHandler(async (event) => ({
@@ -45,10 +56,6 @@ describe("ofetch", () => {
4556
return new Blob(["binary"]);
4657
})
4758
)
48-
.use(
49-
"/echo",
50-
eventHandler(async (event) => ({ body: await readRawBody(event) }))
51-
)
5259
.use(
5360
"/403",
5461
eventHandler(() =>
@@ -202,4 +209,40 @@ describe("ofetch", () => {
202209
}
203210
expect(abortHandle()).rejects.toThrow(/aborted/);
204211
});
212+
213+
it("deep merges defaultOptions", async () => {
214+
const _customFetch = $fetch.create({
215+
query: {
216+
a: 0,
217+
},
218+
params: {
219+
b: 2,
220+
},
221+
headers: {
222+
"x-header-a": "0",
223+
"x-header-b": "2",
224+
},
225+
});
226+
const { headers, path } = await _customFetch(getURL("echo"), {
227+
query: {
228+
a: 1,
229+
},
230+
params: {
231+
c: 3,
232+
},
233+
headers: {
234+
"Content-Type": "text/plain",
235+
"x-header-a": "1",
236+
"x-header-c": "3",
237+
},
238+
});
239+
240+
expect(headers).to.include({
241+
"x-header-a": "1",
242+
"x-header-b": "2",
243+
"x-header-c": "3",
244+
});
245+
246+
expect(path).to.eq("?b=2&c=3&a=1");
247+
});
205248
});

0 commit comments

Comments
 (0)