export type WakeStatus = "off" | "waiting" | "requesting" | "active" | "unavailable" | "unsupported"; type Sentinel = Pick; // Browser access is injected so asynchronous acquisition and release can be tested. export function createScreenWakeLock(options: { request?: () => Promise; visible: () => boolean; status: (value: WakeStatus) => void; }) { let desired = false; let disposed = false; let pending = false; let generation = 0; let lock: Sentinel | null = null; const report = (value: WakeStatus) => { if (!disposed) options.status(value); }; const wanted = () => desired && !disposed && options.visible(); function release() { generation++; const old = lock; lock = null; if (old) void old.release().catch(() => {}); } async function sync() { if (!wanted()) { release(); report(desired ? "waiting" : "off"); return; } if (!options.request) { report("unsupported"); return; } if (lock && !lock.released) { report("active"); return; } if (pending) return; pending = true; const version = generation; report("requesting"); try { const acquired = await options.request(); if (version !== generation || !wanted()) { await acquired.release(); return; } lock = acquired; acquired.addEventListener("release", () => { if (lock !== acquired) return; lock = null; // Do not repeatedly fight a system/battery decision to revoke the lock. report(wanted() ? "unavailable" : desired ? "waiting" : "off"); }, { once: true }); report(acquired.released ? "unavailable" : "active"); } catch { if (version === generation && wanted()) report("unavailable"); } finally { pending = false; // An off/on or hidden/visible transition may have invalidated a pending request. if (version !== generation && wanted()) void sync(); } } return { update(enabled: boolean) { desired = enabled; void sync(); }, refresh() { void sync(); }, dispose() { disposed = true; desired = false; release(); }, }; }