* Surfaces actionable S3 SDK error codes (`AccessDenied`, `NoSuchBucket`, * `InvalidAccessKeyId`, `SignatureDoesNotMatch`, ...) and preserves the * original error as `cause` so callers can still branch on `code`/`$metadata`.
(action: string, fn: () => Promise<T>)
| 140 | * original error as `cause` so callers can still branch on `code`/`$metadata`. |
| 141 | */ |
| 142 | async function withS3ErrorContext<T>(action: string, fn: () => Promise<T>): Promise<T> { |
| 143 | try { |
| 144 | return await fn() |
| 145 | } catch (error) { |
| 146 | if (isS3ServiceException(error)) { |
| 147 | const code = error.name |
| 148 | const status = error.$metadata?.httpStatusCode |
| 149 | const requestId = error.$metadata?.requestId |
| 150 | logger.warn('S3 operation failed', { action, code, status, requestId }) |
| 151 | /** Preserve SDK error as `cause` so callers can still branch on `code` / `$metadata`. */ |
| 152 | throw new Error( |
| 153 | `S3 ${action} failed (${code}${status ? ` ${status}` : ''}): ${error.message}`, |
| 154 | { cause: error } |
| 155 | ) |
| 156 | } |
| 157 | throw error |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | export const s3Destination: DrainDestination<S3DestinationConfig, S3DestinationCredentials> = { |
| 162 | type: 's3', |