export const API = "http://127.0.0.1:8000"; export const POLL = 15_000; export const POLL_HEAVY = 60_000; export const FETCH_TIMEOUT = 12_000; export function pollDelay({ok = true, base = POLL, failures = 0} = {}) { return ok ? base : Math.min(base * 2 ** Math.min(failures, 4), 300_000); } export function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Waits `ms` if the tab is visible; pauses until the tab becomes visible if hidden, // then returns immediately so the next poll fires as soon as the user returns. export async function waitForVisible(ms) { if (typeof document === "undefined") return sleep(ms); if (document.visibilityState === "visible") return sleep(ms); return new Promise(resolve => { const handler = () => { document.removeEventListener("visibilitychange", handler); resolve(); }; document.addEventListener("visibilitychange", handler); }); } export async function apiFetch(path, options = {}) { const url = path.startsWith("http") ? path : `${API}${path}`; const timeout = options.timeout ?? FETCH_TIMEOUT; const {timeout: _timeout, ...fetchOptions} = options; const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), timeout); try { return await fetch(url, {cache: "no-store", ...fetchOptions, signal: ctrl.signal}); } finally { clearTimeout(timer); } }