/** * Shared single-flight wrapper for OAuth token refresh. * * OAuth providers all share the same race: when N concurrent requests arrive * near expiry (or after a 401), each requester runs its own full refresh path * — token endpoint call, in-memory state mutation, and `onRefresh` callback * for persistence. With N concurrent callers, the upstream endpoint is hit N * times, `onRefresh` fires N times, and state mutations race with stale-wins. * * `createSingleFlightRefresh` collapses concurrent callers onto one in-flight * promise. After the refresh resolves (or rejects), the slot is cleared so * the next refresh can fire normally. The work function passed in must * include every side effect (state mutation + persistence callback), because * it is the boundary the helper enforces single-flight across. * * Contract: * - `refresh()` called while no refresh is in flight → starts one. * - `refresh()` called while a refresh IS in flight → awaits the same promise. * - On rejection, every awaiter sees the same error; the slot is cleared so * a later refresh can be retried. * - NO caller's `signal` drives the shared refresh. The token exchange (and * its state-mutation + persistence side effects) always runs to completion, * bounded only by each `refreshFn`'s own internal timeout. A caller's signal * cancels only THAT caller's wait — it never cancels the shared work. */ export interface SingleFlightRefresh { /** * Trigger (or join) a refresh. Returns the new value once complete. * `signal` aborts only the calling awaiter's wait; the shared refresh keeps * running so other awaiters — and the persistence callback — are unaffected. */ refresh(signal?: AbortSignal): Promise; /** True iff a refresh is currently in flight. */ get inFlight(): boolean; } export declare function createSingleFlightRefresh(refreshFn: (signal: AbortSignal | undefined) => Promise): SingleFlightRefresh; //# sourceMappingURL=oauth-refresh.d.ts.map