-
- Notifications
You must be signed in to change notification settings - Fork 4
Description
Description
Add configurable retry logic for browser opening and fetch operations in src/index.ts (lines 84-99). Currently, if the browser fails to open or the fetch fails, the operation simply continues or fails silently.
What needs to be done
Implement intelligent retry logic with:
- Configurable retry attempts for browser opening
- Exponential backoff for network requests
- Proper error aggregation and reporting
- Circuit breaker pattern for repeated failures
- Differentiation between retryable and non-retryable errors
Why this matters
Network operations can fail for transient reasons:
- Browser may be slow to start
- Temporary network glitches
- OAuth provider rate limiting
- DNS resolution issues
Without retry logic:
- Users experience unnecessary failures
- Transient issues become permanent errors
- Poor user experience in unstable network conditions
Current code reference
if (openBrowser) { await open(authorizationUrl); } else { // Test mode: trigger mock provider redirect without browser fetch(authorizationUrl) .then(async (response) => { // ... handle response }) .catch(() => { // Ignore - tests may lack mock provider }); }Implementation considerations
-
User experience: How do we balance retry attempts with user wait time? Should we show progress?
-
Alternative approach: Instead of automatic retries, should we expose hooks for users to implement their own retry logic?
-
Browser detection: Should we detect if no browser is available and provide a manual URL instead of retrying?
-
Exponential backoff vs fixed delay: Which retry strategy works best for OAuth flows?
-
Partial failures: What if the browser opens but the user doesn't complete the flow? Should we retry the entire process?
Suggested implementation
interface RetryOptions { maxAttempts?: number; initialDelay?: number; maxDelay?: number; backoffFactor?: number; retryableErrors?: (error: Error) => boolean; onRetry?: (attempt: number, error: Error) => void; } async function withRetry<T>( fn: () => Promise<T>, options: RetryOptions = {} ): Promise<T> { const { maxAttempts = 3, initialDelay = 1000, maxDelay = 30000, backoffFactor = 2, retryableErrors = () => true, onRetry } = options; let lastError: Error; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error as Error; if (attempt === maxAttempts || !retryableErrors(lastError)) { throw new AggregateError( [lastError], `Failed after ${attempt} attempts: ${lastError.message}` ); } const delay = Math.min( initialDelay * Math.pow(backoffFactor, attempt - 1), maxDelay ); onRetry?.(attempt, lastError); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError!; } // Usage in getAuthCode if (openBrowser) { await withRetry( () => open(authorizationUrl), { maxAttempts: 3, onRetry: (attempt, error) => { console.warn(`Failed to open browser (attempt ${attempt}): ${error.message}`); } } ); }Testing requirements
- Test successful retry after transient failure
- Test maximum retry limit
- Test exponential backoff timing
- Test non-retryable error handling
- Test error aggregation
- Test cancellation during retry
Skills required
- TypeScript
- Error handling patterns
- Async/await and promises
- Network programming concepts
- Testing async operations
Difficulty
Medium - Requires understanding of retry patterns and async error handling