Skip to content

Commit

Permalink
feat: always clone and normalize options.headers and options.query (
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Aug 28, 2024
1 parent 6aff08e commit 0d84f7b
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 38 deletions.
27 changes: 16 additions & 11 deletions src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import {
isPayloadMethod,
isJSONSerializable,
detectResponseType,
mergeFetchOptions,
resolveFetchOptions,
callHooks,
} from "./utils";
import type {
CreateFetchOptions,
FetchResponse,
ResponseType,
FetchContext,
$Fetch,
FetchRequest,
FetchOptions,
} from "./types";

// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
Expand Down Expand Up @@ -88,13 +91,18 @@ export function createFetch(globalOptions: CreateFetchOptions = {}): $Fetch {
throw error;
}

const $fetchRaw: $Fetch["raw"] = async function $fetchRaw(
_request,
_options = {}
) {
const $fetchRaw: $Fetch["raw"] = async function $fetchRaw<
T = any,
R extends ResponseType = "json",
>(_request: FetchRequest, _options: FetchOptions<R> = {}) {
const context: FetchContext = {
request: _request,
options: mergeFetchOptions(_options, globalOptions.defaults, Headers),
options: resolveFetchOptions<R, T>(
_request,
_options,
globalOptions.defaults as unknown as FetchOptions<R, T>,
Headers
),
response: undefined,
error: undefined,
};
Expand All @@ -110,11 +118,8 @@ export function createFetch(globalOptions: CreateFetchOptions = {}): $Fetch {
if (context.options.baseURL) {
context.request = withBase(context.request, context.options.baseURL);
}
if (context.options.query || context.options.params) {
context.request = withQuery(context.request, {
...context.options.params,
...context.options.query,
});
if (context.options.query) {
context.request = withQuery(context.request, context.options.query);
}
}

Expand Down
15 changes: 14 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ export interface FetchOptions<R extends ResponseType = ResponseType, T = any>
extends Omit<RequestInit, "body">,
FetchHooks<T, R> {
baseURL?: string;

body?: RequestInit["body"] | Record<string, any>;

ignoreResponseError?: boolean;

params?: Record<string, any>;

query?: Record<string, any>;

parseResponse?: (responseText: string) => any;

responseType?: R;

/**
Expand Down Expand Up @@ -61,6 +67,13 @@ export interface FetchOptions<R extends ResponseType = ResponseType, T = any>
retryStatusCodes?: number[];
}

export interface ResolvedFetchOptions<
R extends ResponseType = ResponseType,
T = any,
> extends FetchOptions<R, T> {
headers: Headers;
}

export interface CreateFetchOptions {
defaults?: FetchOptions;
fetch?: Fetch;
Expand All @@ -79,7 +92,7 @@ export type GlobalOptions = Pick<

export interface FetchContext<T = any, R extends ResponseType = ResponseType> {
request: FetchRequest;
options: FetchOptions<R>;
options: ResolvedFetchOptions<R>;
response?: FetchResponse<T>;
error?: Error;
}
Expand Down
71 changes: 46 additions & 25 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type {
FetchContext,
FetchHook,
FetchOptions,
FetchRequest,
ResolvedFetchOptions,
ResponseType,
} from "./types";

Expand Down Expand Up @@ -69,40 +71,59 @@ export function detectResponseType(_contentType = ""): ResponseType {
return "blob";
}

// Merging of fetch option objects.
export function mergeFetchOptions(
input: FetchOptions | undefined,
defaults: FetchOptions | undefined,
Headers = globalThis.Headers
): FetchOptions {
const merged: FetchOptions = {
...defaults,
...input,
};
export function resolveFetchOptions<
R extends ResponseType = ResponseType,
T = any,
>(
request: FetchRequest,
input: FetchOptions<R, T> | undefined,
defaults: FetchOptions<R, T> | undefined,
Headers: typeof globalThis.Headers
): ResolvedFetchOptions<R, T> {
// Merge headers
const headers = mergeHeaders(
input?.headers ?? (request as Request)?.headers,
defaults?.headers,
Headers
);

// Merge params and query
if (defaults?.params && input?.params) {
merged.params = {
// Merge query/params
let query: Record<string, any> | undefined;
if (defaults?.query || defaults?.params || input?.params || input?.query) {
query = {
...defaults?.params,
...input?.params,
};
}
if (defaults?.query && input?.query) {
merged.query = {
...defaults?.query,
...input?.params,
...input?.query,
};
}

// Merge headers
if (defaults?.headers && input?.headers) {
merged.headers = new Headers(defaults?.headers || {});
for (const [key, value] of new Headers(input?.headers || {})) {
merged.headers.set(key, value);
return {
...defaults,
...input,
query,
params: query,
headers,
};
}

function mergeHeaders(
input: HeadersInit | undefined,
defaults: HeadersInit | undefined,
Headers: typeof globalThis.Headers
): Headers {
if (!defaults) {
return new Headers(input);
}
const headers = new Headers(defaults);
if (input) {
for (const [key, value] of Symbol.iterator in input || Array.isArray(input)
? input
: new Headers(input)) {
headers.set(key, value);
}
}

return merged;
return headers;
}

export async function callHooks<C extends FetchContext = FetchContext>(
Expand Down
19 changes: 18 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,24 @@ describe("ofetch", () => {
"x-header-c": "3",
});

expect(path).to.eq("?b=2&c=3&a=1");
const parseParams = (str: string) =>
Object.fromEntries(new URLSearchParams(str).entries());
expect(parseParams(path)).toMatchObject(parseParams("?b=2&c=3&a=1"));
});

it("uses request headers", async () => {
expect(
await $fetch(
new Request(getURL("echo"), { headers: { foo: "1" } }),
{}
).then((r) => r.headers)
).toMatchObject({ foo: "1" });

expect(
await $fetch(new Request(getURL("echo"), { headers: { foo: "1" } }), {
headers: { foo: "2", bar: "3" },
}).then((r) => r.headers)
).toMatchObject({ foo: "2", bar: "3" });
});

it("calls hooks", async () => {
Expand Down

0 comments on commit 0d84f7b

Please sign in to comment.