(deps: UseAuthQueryDeps = {})
| 126 | * CHANGE: Now accepts optional dependencies for testing via dependency injection |
| 127 | */ |
| 128 | export function useAuthQuery(deps: UseAuthQueryDeps = {}) { |
| 129 | const { |
| 130 | getUserCredentials = defaultGetUserCredentials, |
| 131 | getUserInfoFromApiKey = defaultGetUserInfoFromApiKey, |
| 132 | logger = defaultLogger, |
| 133 | } = deps |
| 134 | |
| 135 | const userCredentials = getUserCredentials() |
| 136 | const apiKey = userCredentials?.authToken || getCiEnv().CODEBUFF_API_KEY || '' |
| 137 | |
| 138 | return useQuery({ |
| 139 | queryKey: authQueryKeys.validation(apiKey), |
| 140 | queryFn: () => validateApiKey({ apiKey, getUserInfoFromApiKey, logger }), |
| 141 | enabled: !!apiKey, |
| 142 | staleTime: 5 * 60 * 1000, // 5 minutes |
| 143 | gcTime: 10 * 60 * 1000, // 10 minutes |
| 144 | // Retry only for retryable network errors (5xx, timeouts, etc.) |
| 145 | // Don't retry authentication errors (invalid credentials) |
| 146 | retry: (failureCount, error) => { |
| 147 | const statusCode = getErrorStatusCode(error) |
| 148 | // Don't retry authentication errors - user needs to update credentials |
| 149 | if (isAuthenticationError(error)) { |
| 150 | return false |
| 151 | } |
| 152 | // Retry network errors if they're retryable and we haven't exceeded max retries |
| 153 | if (statusCode !== undefined && isRetryableStatusCode(statusCode)) { |
| 154 | return failureCount < MAX_RETRIES_PER_MESSAGE |
| 155 | } |
| 156 | // Don't retry other errors |
| 157 | return false |
| 158 | }, |
| 159 | retryDelay: (attemptIndex) => { |
| 160 | // Exponential backoff: 1s, 2s, 4s |
| 161 | return Math.min( |
| 162 | RETRY_BACKOFF_BASE_DELAY_MS * Math.pow(2, attemptIndex), |
| 163 | 8000, // Cap at 8 seconds |
| 164 | ) |
| 165 | }, |
| 166 | }) |
| 167 | } |
| 168 | |
| 169 | export interface UseLoginMutationDeps { |
| 170 | saveUserCredentials?: (user: User) => void |
no test coverage detected