(
url: string,
init: RequestInit,
attempt = 0
)
| 116 | } |
| 117 | |
| 118 | private async fetchWithRetry( |
| 119 | url: string, |
| 120 | init: RequestInit, |
| 121 | attempt = 0 |
| 122 | ): Promise<Response> { |
| 123 | try { |
| 124 | if (attempt > 0) { |
| 125 | console.debug(`[api] retry ${attempt}/${MAX_RETRY_ATTEMPTS - 1} ${init.method ?? "GET"} ${url}`); |
| 126 | } else { |
| 127 | console.debug(`[api] → ${init.method ?? "GET"} ${url}`); |
| 128 | } |
| 129 | const res = await fetch(url, init); |
| 130 | console.debug(`[api] ← ${res.status} ${url}`); |
| 131 | |
| 132 | if (res.status >= 500 && attempt < MAX_RETRY_ATTEMPTS - 1 && !init.signal?.aborted) { |
| 133 | await sleep(backoffMs(attempt)); |
| 134 | return this.fetchWithRetry(url, init, attempt + 1); |
| 135 | } |
| 136 | return res; |
| 137 | } catch (err) { |
| 138 | if (!isAbortError(err) && attempt < MAX_RETRY_ATTEMPTS - 1) { |
| 139 | await sleep(backoffMs(attempt)); |
| 140 | return this.fetchWithRetry(url, init, attempt + 1); |
| 141 | } |
| 142 | throw err; |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Core fetch method. Applies auth headers, timeout, and retry logic. |
no test coverage detected