@@ -83,6 +83,53 @@ const resolveDefaultDispatchers = <Paths extends object>(): DispatchersFor<Paths
8383 return asDispatchersFor < DispatchersFor < Paths > > ( defaultDispatchers )
8484}
8585
86+ const applyPathParams = ( path : string , params ?: Record < string , ParamValue > ) : string => {
87+ if ( params === undefined ) {
88+ return path
89+ }
90+
91+ let url = path
92+ for ( const [ key , value ] of Object . entries ( params ) ) {
93+ url = url . replace ( "{" + key + "}" , encodeURIComponent ( String ( value ) ) )
94+ }
95+ return url
96+ }
97+
98+ const buildQueryString = ( query ?: Record < string , QueryValue > ) : string => {
99+ if ( query === undefined ) {
100+ return ""
101+ }
102+
103+ const searchParams = new URLSearchParams ( )
104+ for ( const [ key , value ] of Object . entries ( query ) ) {
105+ if ( Array . isArray ( value ) ) {
106+ for ( const item of value ) {
107+ searchParams . append ( key , String ( item ) )
108+ }
109+ continue
110+ }
111+ searchParams . set ( key , String ( value ) )
112+ }
113+ return searchParams . toString ( )
114+ }
115+
116+ const appendQueryString = ( url : string , queryString : string ) : string => {
117+ if ( queryString . length === 0 ) {
118+ return url
119+ }
120+ return url . includes ( "?" ) ? url + "&" + queryString : url + "?" + queryString
121+ }
122+
123+ const withBaseUrl = ( baseUrl : string | undefined , url : string ) : string => {
124+ // If baseUrl is not provided, keep a relative URL (browser-friendly)
125+ if ( baseUrl === undefined || baseUrl === "" ) {
126+ return url
127+ }
128+
129+ // Construct full URL
130+ return new URL ( url , baseUrl ) . toString ( )
131+ }
132+
86133/**
87134 * Build URL with path parameters and query string
88135 *
@@ -96,36 +143,15 @@ const resolveDefaultDispatchers = <Paths extends object>(): DispatchersFor<Paths
96143 * @complexity O(n + m) where n = |params|, m = |query|
97144 */
98145const buildUrl = (
99- baseUrl : string ,
146+ baseUrl : string | undefined ,
100147 path : string ,
101148 params ?: Record < string , ParamValue > ,
102149 query ?: Record < string , QueryValue >
103150) : string => {
104- // Replace path parameters
105- let url = path
106- if ( params ) {
107- for ( const [ key , value ] of Object . entries ( params ) ) {
108- url = url . replace ( `{${ key } }` , encodeURIComponent ( String ( value ) ) )
109- }
110- }
111-
112- // Construct full URL
113- const fullUrl = new URL ( url , baseUrl )
114-
115- // Add query parameters
116- if ( query ) {
117- for ( const [ key , value ] of Object . entries ( query ) ) {
118- if ( Array . isArray ( value ) ) {
119- for ( const item of value ) {
120- fullUrl . searchParams . append ( key , String ( item ) )
121- }
122- } else {
123- fullUrl . searchParams . set ( key , String ( value ) )
124- }
125- }
126- }
127-
128- return fullUrl . toString ( )
151+ const urlWithParams = applyPathParams ( path , params )
152+ const queryString = buildQueryString ( query )
153+ const urlWithQuery = appendQueryString ( urlWithParams , queryString )
154+ return withBaseUrl ( baseUrl , urlWithQuery )
129155}
130156
131157/**
@@ -172,22 +198,63 @@ const needsJsonContentType = (body: BodyInit | object | undefined): boolean =>
172198 && ! ( body instanceof Blob )
173199 && ! ( body instanceof FormData )
174200
201+ const toHeadersFromRecord = (
202+ headersInit : Record <
203+ string ,
204+ | string
205+ | number
206+ | boolean
207+ | ReadonlyArray < string | number | boolean >
208+ | null
209+ | undefined
210+ >
211+ ) : Headers => {
212+ const headers = new Headers ( )
213+
214+ for ( const [ key , value ] of Object . entries ( headersInit ) ) {
215+ if ( value === null || value === undefined ) {
216+ continue
217+ }
218+ if ( Array . isArray ( value ) ) {
219+ headers . set ( key , value . map ( String ) . join ( "," ) )
220+ continue
221+ }
222+ headers . set ( key , String ( value ) )
223+ }
224+
225+ return headers
226+ }
227+
175228/**
176229 * Merge headers from client options and request options
177230 *
178231 * @pure true
179232 * @complexity O(n) where n = number of headers
180233 */
234+ const toHeaders = ( headersInit : ClientOptions [ "headers" ] | undefined ) : Headers => {
235+ if ( headersInit === undefined ) {
236+ return new Headers ( )
237+ }
238+
239+ if ( headersInit instanceof Headers ) {
240+ return new Headers ( headersInit )
241+ }
242+
243+ if ( Array . isArray ( headersInit ) ) {
244+ return new Headers ( headersInit )
245+ }
246+
247+ return toHeadersFromRecord ( headersInit )
248+ }
249+
181250const mergeHeaders = (
182- clientHeaders : HeadersInit | undefined ,
183- requestHeaders : HeadersInit | undefined
251+ clientHeaders : ClientOptions [ "headers" ] | undefined ,
252+ requestHeaders : ClientOptions [ "headers" ] | undefined
184253) : Headers => {
185- const headers = new Headers ( clientHeaders )
186- if ( requestHeaders ) {
187- const optHeaders = new Headers ( requestHeaders )
188- for ( const [ key , value ] of optHeaders . entries ( ) ) {
189- headers . set ( key , value )
190- }
254+ const headers = toHeaders ( clientHeaders )
255+ const optHeaders = toHeaders ( requestHeaders )
256+ for ( const [ key , value ] of optHeaders . entries ( ) ) {
257+ headers . set ( key , value )
191258 }
192259 return headers
193260}
@@ -201,7 +268,7 @@ type MethodHandlerOptions = {
201268 params ?: Record < string , ParamValue > | undefined
202269 query ?: Record < string , QueryValue > | undefined
203270 body ?: BodyInit | object | undefined
204- headers ?: HeadersInit | undefined
271+ headers ?: ClientOptions [ "headers" ] | undefined
205272 signal ?: AbortSignal | undefined
206273}
207274
0 commit comments