@@ -301,18 +301,7 @@ export abstract class APIClient {
301
301
headers [ this . idempotencyHeader ] = options . idempotencyKey ;
302
302
}
303
303
304
- const reqHeaders : Record < string , string > = {
305
- ...( contentLength && { 'Content-Length' : contentLength } ) ,
306
- ...this . defaultHeaders ( options ) ,
307
- ...headers ,
308
- } ;
309
- // let builtin fetch set the Content-Type for multipart bodies
310
- if ( isMultipartBody ( options . body ) && shimsKind !== 'node' ) {
311
- delete reqHeaders [ 'Content-Type' ] ;
312
- }
313
-
314
- // Strip any headers being explicitly omitted with null
315
- Object . keys ( reqHeaders ) . forEach ( ( key ) => reqHeaders [ key ] === null && delete reqHeaders [ key ] ) ;
304
+ const reqHeaders = this . buildHeaders ( { options, headers, contentLength } ) ;
316
305
317
306
const req : RequestInit = {
318
307
method,
@@ -324,9 +313,35 @@ export abstract class APIClient {
324
313
signal : options . signal ?? null ,
325
314
} ;
326
315
316
+ return { req, url, timeout } ;
317
+ }
318
+
319
+ private buildHeaders ( {
320
+ options,
321
+ headers,
322
+ contentLength,
323
+ } : {
324
+ options : FinalRequestOptions ;
325
+ headers : Record < string , string | null | undefined > ;
326
+ contentLength : string | null | undefined ;
327
+ } ) : Record < string , string > {
328
+ const reqHeaders : Record < string , string > = { } ;
329
+ if ( contentLength ) {
330
+ reqHeaders [ 'content-length' ] = contentLength ;
331
+ }
332
+
333
+ const defaultHeaders = this . defaultHeaders ( options ) ;
334
+ applyHeadersMut ( reqHeaders , defaultHeaders ) ;
335
+ applyHeadersMut ( reqHeaders , headers ) ;
336
+
337
+ // let builtin fetch set the Content-Type for multipart bodies
338
+ if ( isMultipartBody ( options . body ) && shimsKind !== 'node' ) {
339
+ delete reqHeaders [ 'content-type' ] ;
340
+ }
341
+
327
342
this . validateHeaders ( reqHeaders , headers ) ;
328
343
329
- return { req , url , timeout } ;
344
+ return reqHeaders ;
330
345
}
331
346
332
347
/**
@@ -1013,6 +1028,28 @@ export function hasOwn(obj: Object, key: string): boolean {
1013
1028
return Object . prototype . hasOwnProperty . call ( obj , key ) ;
1014
1029
}
1015
1030
1031
+ /**
1032
+ * Copies headers from "newHeaders" onto "targetHeaders",
1033
+ * using lower-case for all properties,
1034
+ * ignoring any keys with undefined values,
1035
+ * and deleting any keys with null values.
1036
+ */
1037
+ function applyHeadersMut ( targetHeaders : Headers , newHeaders : Headers ) : void {
1038
+ for ( const k in newHeaders ) {
1039
+ if ( ! hasOwn ( newHeaders , k ) ) continue ;
1040
+ const lowerKey = k . toLowerCase ( ) ;
1041
+ if ( ! lowerKey ) continue ;
1042
+
1043
+ const val = newHeaders [ k ] ;
1044
+
1045
+ if ( val === null ) {
1046
+ delete targetHeaders [ lowerKey ] ;
1047
+ } else if ( val !== undefined ) {
1048
+ targetHeaders [ lowerKey ] = val ;
1049
+ }
1050
+ }
1051
+ }
1052
+
1016
1053
export function debug ( action : string , ...args : any [ ] ) {
1017
1054
if ( typeof process !== 'undefined' && process . env [ 'DEBUG' ] === 'true' ) {
1018
1055
console . log ( `OpenAI:DEBUG:${ action } ` , ...args ) ;
0 commit comments