( operation: () => Promise<T>, timeoutMs: number = EMAIL_REQUEST_TIMEOUT_MS )
| 146 | * treats it as transient and retries, mirroring an aborted fetch attempt. |
| 147 | */ |
| 148 | export const withEmailTimeout = async <T>( |
| 149 | operation: () => Promise<T>, |
| 150 | timeoutMs: number = EMAIL_REQUEST_TIMEOUT_MS |
| 151 | ): Promise<T> => { |
| 152 | let timer: ReturnType<typeof setTimeout> | undefined; |
| 153 | |
| 154 | const guard = new Promise<never>((_resolve, reject) => { |
| 155 | timer = setTimeout(() => { |
| 156 | reject( |
| 157 | ApiErrors.externalService( |
| 158 | `Email provider timeout after ${String(timeoutMs)}ms` |
| 159 | ) |
| 160 | ); |
| 161 | }, timeoutMs); |
| 162 | }); |
| 163 | |
| 164 | try { |
| 165 | return await Promise.race([operation(), guard]); |
| 166 | } finally { |
| 167 | if (timer !== undefined) { |
| 168 | clearTimeout(timer); |
| 169 | } |
| 170 | } |
| 171 | }; |
| 172 | |
| 173 | /** |
| 174 | * Retry an async function with exponential backoff. Only transient errors |
no outgoing calls
no test coverage detected