@@ -11,7 +11,7 @@ const TRAILING_SLASH_RE = /\/*$/;
11
11
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
12
12
13
13
/** options for each client instance */
14
- interface ClientOptions extends RequestInit {
14
+ interface ClientOptions extends Omit < RequestInit , "headers" > {
15
15
/** set the common root URL for all API requests */
16
16
baseUrl ?: string ;
17
17
/** custom fetch (defaults to globalThis.fetch) */
@@ -20,7 +20,10 @@ interface ClientOptions extends RequestInit {
20
20
querySerializer ?: QuerySerializer < unknown > ;
21
21
/** global bodySerializer */
22
22
bodySerializer ?: BodySerializer < unknown > ;
23
+ // headers override to make typing friendlier
24
+ headers ?: HeadersOptions ;
23
25
}
26
+ export type HeadersOptions = HeadersInit | Record < string , string | number | boolean | null | undefined > ;
24
27
export type QuerySerializer < T > = ( query : T extends { parameters : any } ? NonNullable < T [ "parameters" ] [ "query" ] > : Record < string , unknown > ) => string ;
25
28
export type BodySerializer < T > = ( body : OperationRequestBodyContent < T > ) => any ;
26
29
export type ParseAs = "json" | "text" | "blob" | "arrayBuffer" | "stream" ;
@@ -43,17 +46,12 @@ export type RequestOptions<T> = ParamsOption<T> &
43
46
export default function createClient < Paths extends { } > ( clientOptions : ClientOptions = { } ) {
44
47
const { fetch = globalThis . fetch , querySerializer : globalQuerySerializer , bodySerializer : globalBodySerializer , ...options } = clientOptions ;
45
48
46
- const defaultHeaders = new Headers ( {
47
- ...DEFAULT_HEADERS ,
48
- ...( options . headers ?? { } ) ,
49
- } ) ;
50
-
51
49
async function coreFetch < P extends keyof Paths , M extends HttpMethod > ( url : P , fetchOptions : FetchOptions < M extends keyof Paths [ P ] ? Paths [ P ] [ M ] : never > ) : Promise < FetchResponse < M extends keyof Paths [ P ] ? Paths [ P ] [ M ] : unknown > > {
52
50
const { headers, body : requestBody , params = { } , parseAs = "json" , querySerializer = globalQuerySerializer ?? defaultQuerySerializer , bodySerializer = globalBodySerializer ?? defaultBodySerializer , ...init } = fetchOptions || { } ;
53
51
54
52
// URL
55
53
const finalURL = createFinalURL ( url as string , { baseUrl : options . baseUrl , params, querySerializer } ) ;
56
- const finalHeaders = mergeHeaders ( defaultHeaders as any , headers as any , ( params as any ) . header ) ;
54
+ const finalHeaders = mergeHeaders ( DEFAULT_HEADERS , clientOptions ?. headers , headers , ( params as any ) . header ) ;
57
55
58
56
// fetch!
59
57
const requestInit : RequestInit = { redirect : "follow" , ...options , ...init , headers : finalHeaders } ;
@@ -157,13 +155,15 @@ export function createFinalURL<O>(url: string, options: { baseUrl?: string; para
157
155
}
158
156
159
157
/** merge headers a and b, with b taking priority */
160
- export function mergeHeaders ( ...allHeaders : ( Record < string , unknown > | Headers ) [ ] ) : Headers {
158
+ export function mergeHeaders ( ...allHeaders : ( HeadersOptions | undefined ) [ ] ) : Headers {
161
159
const headers = new Headers ( ) ;
162
160
for ( const headerSet of allHeaders ) {
163
161
if ( ! headerSet || typeof headerSet !== "object" ) continue ;
164
162
const iterator = headerSet instanceof Headers ? headerSet . entries ( ) : Object . entries ( headerSet ) ;
165
163
for ( const [ k , v ] of iterator ) {
166
- if ( v !== undefined && v !== null ) {
164
+ if ( v === null ) {
165
+ headers . delete ( k ) ;
166
+ } else if ( v !== undefined ) {
167
167
headers . set ( k , v as any ) ;
168
168
}
169
169
}
0 commit comments