Picks the poll delay after a successful tick. Returns null when the state * is terminal (no further polling).
(next: FreebuffSessionResponse)
| 136 | /** Picks the poll delay after a successful tick. Returns null when the state |
| 137 | * is terminal (no further polling). */ |
| 138 | function nextDelayMs(next: FreebuffSessionResponse): number | null { |
| 139 | switch (next.status) { |
| 140 | case 'queued': |
| 141 | return POLL_INTERVAL_QUEUED_MS |
| 142 | case 'active': |
| 143 | // Poll at the normal cadence, but ensure we land just after |
| 144 | // `expires_at` so the transition shows up promptly instead of leaving |
| 145 | // the countdown stuck at 0 for up to a full interval. |
| 146 | return Math.max( |
| 147 | 1_000, |
| 148 | Math.min(POLL_INTERVAL_ACTIVE_MS, next.remainingMs + 1_000), |
| 149 | ) |
| 150 | case 'ended': |
| 151 | // Inside the grace window we keep checking so the post-grace transition |
| 152 | // (server returns `none`, we synthesize ended-no-instanceId) is prompt. |
| 153 | return next.instanceId ? POLL_INTERVAL_ACTIVE_MS : null |
| 154 | case 'none': |
| 155 | case 'disabled': |
| 156 | case 'superseded': |
| 157 | case 'takeover_prompt': |
| 158 | case 'country_blocked': |
| 159 | case 'banned': |
| 160 | case 'model_locked': |
| 161 | case 'rate_limited': |
| 162 | case 'model_unavailable': |
| 163 | return null |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | // --- Poll-loop control surface --------------------------------------------- |
| 168 | // |