| 79 | } |
| 80 | |
| 81 | async function buildClients( |
| 82 | config: AzureBlobDestinationConfig, |
| 83 | credentials: AzureBlobDestinationCredentials |
| 84 | ): Promise<BlobClients> { |
| 85 | const { BlobServiceClient, StorageSharedKeyCredential } = await import('@azure/storage-blob') |
| 86 | const sharedKeyCredential = new StorageSharedKeyCredential( |
| 87 | config.accountName, |
| 88 | credentials.accountKey |
| 89 | ) |
| 90 | const suffix = config.endpointSuffix ?? DEFAULT_ENDPOINT_SUFFIX |
| 91 | /** |
| 92 | * Bound per-attempt timeout. SDK default `tryTimeoutInMs` is "infinite" / OS |
| 93 | * connection-idle limits, so a hung receiver could pin a delivery indefinitely. |
| 94 | * 30s per try + 5 tries (~ exponential 0.5s → 30s) caps the worst-case wall time. |
| 95 | */ |
| 96 | const blobServiceClient = new BlobServiceClient( |
| 97 | `https://${config.accountName}.${suffix}`, |
| 98 | sharedKeyCredential, |
| 99 | { |
| 100 | retryOptions: { |
| 101 | tryTimeoutInMs: 30_000, |
| 102 | maxTries: 5, |
| 103 | retryDelayInMs: 500, |
| 104 | maxRetryDelayInMs: 30_000, |
| 105 | }, |
| 106 | } |
| 107 | ) |
| 108 | return { containerClient: blobServiceClient.getContainerClient(config.containerName) } |
| 109 | } |
| 110 | |
| 111 | interface AzureRestErrorLike { |
| 112 | statusCode?: number |