1export const WEBVIEW_CRASH_RECOVERY_WINDOW_MS = 30_000;
2const WEBVIEW_CRASH_RECOVERY_MAX_ATTEMPTS = 3;
3const WEBVIEW_CRASH_RECOVERY_BASE_DELAY_MS = 250;
4
5export interface WebviewCrashRecoveryState {
6 readonly attempts: number;
7 readonly windowStartedAt: number | null;
8}
9
10export interface WebviewCrashRecoveryPlan {
11 readonly delayMs: number;
12 readonly state: WebviewCrashRecoveryState;
13}
14
15export const INITIAL_WEBVIEW_CRASH_RECOVERY_STATE: WebviewCrashRecoveryState = {
16 attempts: 0,
17 windowStartedAt: null,
18};
19
20export function planWebviewCrashRecovery(
21 state: WebviewCrashRecoveryState,
22 now: number,
23): WebviewCrashRecoveryPlan | null {
24 const startsNewWindow =
25 state.windowStartedAt === null ||
26 now - state.windowStartedAt >= WEBVIEW_CRASH_RECOVERY_WINDOW_MS;
27 const attempts = startsNewWindow ? 0 : state.attempts;
28 if (attempts >= WEBVIEW_CRASH_RECOVERY_MAX_ATTEMPTS) return null;
29
30 const nextAttempts = attempts + 1;
31 return {
32 delayMs: WEBVIEW_CRASH_RECOVERY_BASE_DELAY_MS * 2 ** attempts,
33 state: {
34 attempts: nextAttempts,
35 windowStartedAt: startsNewWindow ? now : state.windowStartedAt,
36 },
37 };
38}