* Creates a fetch function with a fresh 30-second timeout for each OAuth request. * Used by ClaudeAuthProvider for metadata discovery and token refresh. * Prevents stale timeout signals from affecting auth operations.
()
| 196 | * Prevents stale timeout signals from affecting auth operations. |
| 197 | */ |
| 198 | function createAuthFetch(): FetchLike { |
| 199 | return async (url: string | URL, init?: RequestInit) => { |
| 200 | const timeoutSignal = AbortSignal.timeout(AUTH_REQUEST_TIMEOUT_MS) |
| 201 | const isPost = init?.method?.toUpperCase() === 'POST' |
| 202 | |
| 203 | // No existing signal - just use timeout |
| 204 | if (!init?.signal) { |
| 205 | // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins |
| 206 | const response = await fetch(url, { ...init, signal: timeoutSignal }) |
| 207 | return isPost ? normalizeOAuthErrorBody(response) : response |
| 208 | } |
| 209 | |
| 210 | // Combine signals: abort when either fires |
| 211 | const controller = new AbortController() |
| 212 | const abort = () => controller.abort() |
| 213 | |
| 214 | init.signal.addEventListener('abort', abort) |
| 215 | timeoutSignal.addEventListener('abort', abort) |
| 216 | |
| 217 | // Cleanup to prevent event listener leaks after fetch completes |
| 218 | const cleanup = () => { |
| 219 | init.signal?.removeEventListener('abort', abort) |
| 220 | timeoutSignal.removeEventListener('abort', abort) |
| 221 | } |
| 222 | |
| 223 | if (init.signal.aborted) { |
| 224 | controller.abort() |
| 225 | } |
| 226 | |
| 227 | try { |
| 228 | // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins |
| 229 | const response = await fetch(url, { ...init, signal: controller.signal }) |
| 230 | cleanup() |
| 231 | return isPost ? normalizeOAuthErrorBody(response) : response |
| 232 | } catch (error) { |
| 233 | cleanup() |
| 234 | throw error |
| 235 | } |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Fetches authorization server metadata, using a configured metadata URL if available, |
no test coverage detected