( keys: string[], customConfig?: S3Config )
| 297 | * against the per-prefix DELETE rate limit (3500/sec). |
| 298 | */ |
| 299 | export async function deleteManyFromS3( |
| 300 | keys: string[], |
| 301 | customConfig?: S3Config |
| 302 | ): Promise<{ failed: Array<{ key: string; error: string }> }> { |
| 303 | const failed: Array<{ key: string; error: string }> = [] |
| 304 | if (keys.length === 0) return { failed } |
| 305 | |
| 306 | const config = customConfig || { bucket: S3_CONFIG.bucket, region: S3_CONFIG.region } |
| 307 | const s3Client = getS3Client() |
| 308 | |
| 309 | for (let i = 0; i < keys.length; i += S3_DELETE_OBJECTS_MAX_KEYS) { |
| 310 | const chunk = keys.slice(i, i + S3_DELETE_OBJECTS_MAX_KEYS) |
| 311 | try { |
| 312 | const response = await s3Client.send( |
| 313 | new DeleteObjectsCommand({ |
| 314 | Bucket: config.bucket, |
| 315 | Delete: { |
| 316 | Objects: chunk.map((Key) => ({ Key })), |
| 317 | Quiet: true, |
| 318 | }, |
| 319 | }) |
| 320 | ) |
| 321 | for (const error of response.Errors ?? []) { |
| 322 | if (error.Key) { |
| 323 | failed.push({ |
| 324 | key: error.Key, |
| 325 | error: error.Message ?? error.Code ?? 'unknown', |
| 326 | }) |
| 327 | } |
| 328 | } |
| 329 | } catch (error) { |
| 330 | const message = getErrorMessage(error) |
| 331 | for (const Key of chunk) failed.push({ key: Key, error: message }) |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | return { failed } |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Initiate a multipart upload for S3 |
no test coverage detected