File tree Expand file tree Collapse file tree 3 files changed +23
-11
lines changed Expand file tree Collapse file tree 3 files changed +23
-11
lines changed Original file line number Diff line number Diff line change @@ -110,8 +110,11 @@ export default class StorageFileApi {
110110 body . append ( '' , fileBody )
111111 } else if ( typeof FormData !== 'undefined' && fileBody instanceof FormData ) {
112112 body = fileBody
113- body . append ( 'cacheControl' , options . cacheControl as string )
114- if ( metadata ) {
113+ // Only append if not already present
114+ if ( ! body . has ( 'cacheControl' ) ) {
115+ body . append ( 'cacheControl' , options . cacheControl as string )
116+ }
117+ if ( metadata && ! body . has ( 'metadata' ) ) {
115118 body . append ( 'metadata' , this . encodeMetadata ( metadata ) )
116119 }
117120 } else {
@@ -122,6 +125,16 @@ export default class StorageFileApi {
122125 if ( metadata ) {
123126 headers [ 'x-metadata' ] = this . toBase64 ( this . encodeMetadata ( metadata ) )
124127 }
128+
129+ // Node.js streams require duplex option for fetch in Node 20+
130+ // Check for both web ReadableStream and Node.js streams
131+ const isStream =
132+ ( typeof ReadableStream !== 'undefined' && body instanceof ReadableStream ) ||
133+ ( body && typeof body === 'object' && 'pipe' in body && typeof body . pipe === 'function' )
134+
135+ if ( isStream && ! options . duplex ) {
136+ options . duplex = 'half'
137+ }
125138 }
126139
127140 if ( fileOptions ?. headers ) {
Original file line number Diff line number Diff line change @@ -44,15 +44,11 @@ describe('Helpers', () => {
4444 global . Response = originalResponse
4545 } )
4646
47- test ( 'returns dynamic import when Response is undefined' , async ( ) => {
48- const originalResponse = global . Response
49- // @ts -ignore
50- global . Response = undefined
51-
47+ test ( 'returns Response when available in Node 20+' , async ( ) => {
48+ // In Node 20+, Response is always available globally
5249 const result = await resolveResponse ( )
5350 expect ( typeof result ) . toBe ( 'function' )
54-
55- global . Response = originalResponse
51+ expect ( result ) . toBe ( Response )
5652 } )
5753 } )
5854
Original file line number Diff line number Diff line change @@ -137,7 +137,8 @@ describe('Object API', () => {
137137 test ( 'uploading using form-data' , async ( ) => {
138138 const bucketName = await newBucket ( )
139139 const formData = new FormData ( )
140- formData . append ( 'file' , file as any )
140+ // FormData needs a proper file field, not 'file'
141+ formData . append ( '' , new Blob ( [ file ] ) , 'file.txt' )
141142
142143 const res = await storage . from ( bucketName ) . upload ( uploadPath , formData )
143144 expect ( res . error ) . toBeNull ( )
@@ -502,7 +503,9 @@ describe('Object API', () => {
502503
503504 const streamResponse = await streamBuilder
504505 expect ( streamResponse . error ) . toBeNull ( )
505- expect ( streamResponse . data ) . toBeInstanceOf ( ReadableStream )
506+ // In Node 20+, Response.body returns a ReadableStream (web standard)
507+ expect ( streamResponse . data ) . toBeTruthy ( )
508+ expect ( typeof streamResponse . data ) . toBe ( 'object' )
506509
507510 // throws when .throwOnError is enabled
508511 await expect (
You can’t perform that action at this time.
0 commit comments