From c91fcf6f681ab1e0504aecce519f67a3e43d8fe6 Mon Sep 17 00:00:00 2001 From: viveknathani Date: Sun, 22 Feb 2026 15:40:26 +0530 Subject: [PATCH] fix(storage): pass metadata in uploadToSignedUrl --- .../storage-js/src/packages/StorageFileApi.ts | 22 +++++++++- .../storage-js/test/storageFileApi.test.ts | 42 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/packages/core/storage-js/src/packages/StorageFileApi.ts b/packages/core/storage-js/src/packages/StorageFileApi.ts index c5a8e45d0d..fb5523d6cf 100644 --- a/packages/core/storage-js/src/packages/StorageFileApi.ts +++ b/packages/core/storage-js/src/packages/StorageFileApi.ts @@ -248,22 +248,40 @@ export default class StorageFileApi extends BaseApiClient { return this.handleOperation(async () => { let body const options = { upsert: DEFAULT_FILE_OPTIONS.upsert, ...fileOptions } - const headers: Record = { + let headers: Record = { ...this.headers, ...{ 'x-upsert': String(options.upsert as boolean) }, } + const metadata = options.metadata + if (typeof Blob !== 'undefined' && fileBody instanceof Blob) { body = new FormData() body.append('cacheControl', options.cacheControl as string) + if (metadata) { + body.append('metadata', this.encodeMetadata(metadata)) + } body.append('', fileBody) } else if (typeof FormData !== 'undefined' && fileBody instanceof FormData) { body = fileBody - body.append('cacheControl', options.cacheControl as string) + if (!body.has('cacheControl')) { + body.append('cacheControl', options.cacheControl as string) + } + if (metadata && !body.has('metadata')) { + body.append('metadata', this.encodeMetadata(metadata)) + } } else { body = fileBody headers['cache-control'] = `max-age=${options.cacheControl}` headers['content-type'] = options.contentType as string + + if (metadata) { + headers['x-metadata'] = this.toBase64(this.encodeMetadata(metadata)) + } + } + + if (fileOptions?.headers) { + headers = { ...headers, ...fileOptions.headers } } const data = await put(this.fetch, url.toString(), body as object, { headers }) diff --git a/packages/core/storage-js/test/storageFileApi.test.ts b/packages/core/storage-js/test/storageFileApi.test.ts index fa8a0d00d0..08a13e8f90 100644 --- a/packages/core/storage-js/test/storageFileApi.test.ts +++ b/packages/core/storage-js/test/storageFileApi.test.ts @@ -913,6 +913,48 @@ describe('StorageFileApi Edge Cases', () => { expect(body.get('metadata')).toBe(JSON.stringify(metadata)) }) + test('uploadToSignedUrl with Blob and metadata', async () => { + const testBlob = new Blob(['test content'], { type: 'text/plain' }) + const metadata = { custom_id: '12345', author: 'test' } + + await storage + .from('test-bucket') + .uploadToSignedUrl('test-path', 'test-token', testBlob, { metadata }) + + expect(mockPut).toHaveBeenCalled() + const [, , body] = mockPut.mock.calls[0] as [null, null, FormData] + expect(body.constructor.name).toBe('FormData') + expect(body.get('metadata')).toBe(JSON.stringify(metadata)) + }) + + test('uploadToSignedUrl with FormData and metadata', async () => { + const testFormData = new FormData() + testFormData.append('file', 'test content') + const metadata = { custom_id: '12345' } + + await storage + .from('test-bucket') + .uploadToSignedUrl('test-path', 'test-token', testFormData, { metadata }) + + expect(mockPut).toHaveBeenCalled() + const [, , body] = mockPut.mock.calls[0] as [null, null, FormData] + expect(body).toBe(testFormData) + expect(body.get('metadata')).toBe(JSON.stringify(metadata)) + }) + + test('uploadToSignedUrl with binary body and metadata sets x-metadata header', async () => { + const metadata = { custom_id: '12345' } + + await storage + .from('test-bucket') + .uploadToSignedUrl('test-path', 'test-token', 'raw-binary-content', { metadata }) + + expect(mockPut).toHaveBeenCalled() + const [, , , { headers }] = mockPut.mock.calls[0] + const expectedBase64 = Buffer.from(JSON.stringify(metadata)).toString('base64') + expect(headers['x-metadata']).toBe(expectedBase64) + }) + test('upload passes headers', async () => { const testFormData = new FormData() testFormData.append('file', 'test content')