{"version":3,"file":"use-online.cjs","names":[],"sources":["../../src/hooks/use-online.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useLatestRef } from \"./use-latest-ref\";\n\n/**\n * Options for {@link useOnline}. Omit them for the cheap `navigator.onLine`\n * behaviour; pass `pingUrl` to add a real-reachability probe on top.\n */\nexport interface UseOnlineOptions {\n    /**\n     * URL to probe for real connectivity. `navigator.onLine` reports `true`\n     * behind a captive portal or when the machine has a link but no internet;\n     * a periodic `HEAD` to this URL catches those cases. When set, the result\n     * is `navigator.onLine && lastProbeSucceeded`.\n     */\n    pingUrl?: string;\n    /** How often to probe, in ms. Default `30000`. */\n    intervalMs?: number;\n    /** Abort a probe after this many ms and treat it as offline. Default `5000`. */\n    timeoutMs?: number;\n}\n\n/**\n * Track connectivity and re-render on changes.\n *\n * With no options it mirrors `navigator.onLine` (assuming online during SSR and\n * before the first event). Pass `pingUrl` to additionally probe real\n * reachability — the browser's `onLine` flag lies behind captive portals and\n * dead links, so the probe downgrades the result to `false` when the network is\n * unusable even though `navigator.onLine` is `true`.\n *\n * @param options - Optional reachability-probe configuration.\n * @returns `true` when the device is online (and reachable, when probing).\n *\n * @example\n * const online = useOnline(); // navigator.onLine only\n * const reallyOnline = useOnline({ pingUrl: \"/health\", intervalMs: 15_000 });\n */\nexport function useOnline(options: UseOnlineOptions = {}): boolean {\n    const { pingUrl, intervalMs = 30_000, timeoutMs = 5_000 } = options;\n\n    const [navOnline, setNavOnline] = useState<boolean>(() =>\n        typeof navigator === \"undefined\" ? true : navigator.onLine,\n    );\n    const [reachable, setReachable] = useState<boolean>(true);\n\n    const optionsRef = useLatestRef({ pingUrl, timeoutMs });\n\n    useEffect(() => {\n        if (typeof window === \"undefined\") return;\n        const handleOnline = (): void => setNavOnline(true);\n        const handleOffline = (): void => setNavOnline(false);\n        window.addEventListener(\"online\", handleOnline);\n        window.addEventListener(\"offline\", handleOffline);\n        return () => {\n            window.removeEventListener(\"online\", handleOnline);\n            window.removeEventListener(\"offline\", handleOffline);\n        };\n    }, []);\n\n    useEffect(() => {\n        if (!pingUrl || typeof window === \"undefined\") return;\n\n        let cancelled = false;\n        const probe = async (): Promise<void> => {\n            if (typeof navigator !== \"undefined\" && !navigator.onLine) {\n                if (!cancelled) setReachable(false);\n                return;\n            }\n            const controller = new AbortController();\n            const timer = window.setTimeout(() => controller.abort(), optionsRef.current.timeoutMs);\n            try {\n                await fetch(optionsRef.current.pingUrl!, {\n                    method: \"HEAD\",\n                    cache: \"no-store\",\n                    signal: controller.signal,\n                });\n                if (!cancelled) setReachable(true);\n            } catch {\n                if (!cancelled) setReachable(false);\n            } finally {\n                window.clearTimeout(timer);\n            }\n        };\n\n        void probe();\n        const id = window.setInterval(() => void probe(), intervalMs);\n        const onWake = (): void => void probe();\n        window.addEventListener(\"online\", onWake);\n        document.addEventListener(\"visibilitychange\", onWake);\n        return () => {\n            cancelled = true;\n            window.clearInterval(id);\n            window.removeEventListener(\"online\", onWake);\n            document.removeEventListener(\"visibilitychange\", onWake);\n        };\n    }, [pingUrl, intervalMs, optionsRef]);\n\n    return pingUrl ? navOnline && reachable : navOnline;\n}\n"],"mappings":"+DAqCA,SAAgB,EAAU,EAA4B,CAAC,EAAY,CAC/D,GAAM,CAAE,UAAS,aAAa,IAAQ,YAAY,KAAU,EAEtD,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,KAC9B,OAAO,UAAc,KAAqB,UAAU,MACxD,EACM,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAkB,EAAI,EAElD,EAAa,EAAA,aAAa,CAAE,UAAS,WAAU,CAAC,EAoDtD,OAlDA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,OAAW,IAAa,OACnC,IAAM,MAA2B,EAAa,EAAI,EAC5C,MAA4B,EAAa,EAAK,EAGpD,OAFA,OAAO,iBAAiB,SAAU,CAAY,EAC9C,OAAO,iBAAiB,UAAW,CAAa,MACnC,CACT,OAAO,oBAAoB,SAAU,CAAY,EACjD,OAAO,oBAAoB,UAAW,CAAa,CACvD,CACJ,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,OAAO,OAAW,IAAa,OAE/C,IAAI,EAAY,GACV,EAAQ,SAA2B,CACrC,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,OAAQ,CAClD,GAAW,EAAa,EAAK,EAClC,MACJ,CACA,IAAM,EAAa,IAAI,gBACjB,EAAQ,OAAO,eAAiB,EAAW,MAAM,EAAG,EAAW,QAAQ,SAAS,EACtF,GAAI,CACA,MAAM,MAAM,EAAW,QAAQ,QAAU,CACrC,OAAQ,OACR,MAAO,WACP,OAAQ,EAAW,MACvB,CAAC,EACI,GAAW,EAAa,EAAI,CACrC,MAAQ,CACC,GAAW,EAAa,EAAK,CACtC,QAAU,CACN,OAAO,aAAa,CAAK,CAC7B,CACJ,EAEA,EAAW,EACX,IAAM,EAAK,OAAO,gBAAkB,KAAK,EAAM,EAAG,CAAU,EACtD,MAAqB,KAAK,EAAM,EAGtC,OAFA,OAAO,iBAAiB,SAAU,CAAM,EACxC,SAAS,iBAAiB,mBAAoB,CAAM,MACvC,CACT,EAAY,GACZ,OAAO,cAAc,CAAE,EACvB,OAAO,oBAAoB,SAAU,CAAM,EAC3C,SAAS,oBAAoB,mBAAoB,CAAM,CAC3D,CACJ,EAAG,CAAC,EAAS,EAAY,CAAU,CAAC,EAE7B,EAAU,GAAa,EAAY,CAC9C"}