fix(s3): replace DeleteObjectsCommand with per-key deletes for CF Workers compat

DeleteObjectsCommand returns XML that requires DOMParser to parse, but
Cloudflare Workers doesn't have DOMParser. Use parallel deleteObject calls instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
saltbo
2026-04-11 23:52:12 -04:00
co-authored by Claude Opus 4.6
parent bce4da3ebf
commit e2ab579773
2 changed files with 7 additions and 18 deletions
+4 -10
View File
@@ -204,17 +204,11 @@ describe('S3Service', () => {
})
describe('deleteObjects', () => {
it('sends DeleteObjectsCommand with keys', async () => {
mockSend.mockResolvedValueOnce({ $metadata: {} })
it('sends individual DeleteObjectCommand for each key', async () => {
mockSend.mockResolvedValue({ $metadata: {} })
await service.deleteObjects(storage, ['a.jpg', 'b.jpg'])
expect(mockSend).toHaveBeenCalledWith(
expect.objectContaining({
input: {
Bucket: 'my-bucket',
Delete: { Objects: [{ Key: 'a.jpg' }, { Key: 'b.jpg' }] },
},
}),
)
expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({ input: { Bucket: 'my-bucket', Key: 'a.jpg' } }))
expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({ input: { Bucket: 'my-bucket', Key: 'b.jpg' } }))
})
it('skips API call for empty keys array', async () => {
+3 -8
View File
@@ -1,7 +1,6 @@
import {
CopyObjectCommand,
DeleteObjectCommand,
DeleteObjectsCommand,
GetObjectCommand,
HeadObjectCommand,
PutObjectCommand,
@@ -89,12 +88,8 @@ export class S3Service {
async deleteObjects(storage: Storage, keys: string[]): Promise<void> {
if (keys.length === 0) return
const client = this.createClient(storage)
await client.send(
new DeleteObjectsCommand({
Bucket: storage.bucket,
Delete: { Objects: keys.map((Key) => ({ Key })) },
}),
)
// DeleteObjectsCommand returns XML which requires DOMParser to parse.
// Cloudflare Workers doesn't have DOMParser, so we delete one-by-one.
await Promise.all(keys.map((key) => this.deleteObject(storage, key)))
}
}