()
| 64 | } |
| 65 | |
| 66 | export async function performRefresh(): Promise<boolean> { |
| 67 | inFlightRefresh ??= (async (): Promise<boolean> => { |
| 68 | try { |
| 69 | const response = await fetch(`${BASE_URL}/api/v1/auth/refresh`, { |
| 70 | method: "POST", |
| 71 | credentials: "include" |
| 72 | }); |
| 73 | |
| 74 | if (!response.ok) { |
| 75 | logger.info({ event: "auth.refresh_attempt", success: false }); |
| 76 | |
| 77 | return false; |
| 78 | } |
| 79 | |
| 80 | const userId = await readSessionUserId(response); |
| 81 | const success = userId !== null; |
| 82 | |
| 83 | logger.info({ event: "auth.refresh_attempt", success }); |
| 84 | |
| 85 | return success; |
| 86 | } catch (cause) { |
| 87 | logger.warn({ |
| 88 | event: "auth.refresh_failed", |
| 89 | error: getErrorMessage(cause) |
| 90 | }); |
| 91 | |
| 92 | return false; |
| 93 | } finally { |
| 94 | /* |
| 95 | * Reset on the next microtask so concurrent callers all see the same |
| 96 | * promise but a fresh refresh fires on the next 401 wave. |
| 97 | */ |
| 98 | queueMicrotask(() => { |
| 99 | inFlightRefresh = null; |
| 100 | }); |
| 101 | } |
| 102 | })(); |
| 103 | |
| 104 | return inFlightRefresh; |
| 105 | } |
| 106 | |
| 107 | function isRefreshableEndpoint(url: string): boolean { |
| 108 | return !url.includes("/auth/refresh") && !url.includes("/auth/login"); |
no test coverage detected