{"version":3,"file":"use-web-socket.cjs","names":[],"sources":["../../src/ws/use-web-socket.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useLatestRef } from \"@/hooks/use-latest-ref\";\nimport {\n    createWebSocket,\n    type CreateWebSocketOptions,\n    type WebSocketController,\n    type WebSocketMessage,\n    type WebSocketStatus,\n} from \"./create-web-socket\";\n\nexport interface UseWebSocketOptions<T> extends Omit<CreateWebSocketOptions<T>, \"onStatusChange\"> {\n    /** When false, the socket is not opened. Default: true. */\n    enabled?: boolean;\n}\n\nexport interface UseWebSocketResult<T> {\n    status: WebSocketStatus;\n    /**\n     * Last decoded frame received.\n     *\n     * A snapshot, not a stream: two frames arriving in the same tick collapse\n     * into a single render and only the later one is ever visible. One server\n     * action often emits several frames in a row, so anything that must see\n     * every message has to use `onMessage`, which fires once per frame. Read\n     * `lastMessage` for \"what is the current state\" rendering only.\n     */\n    lastMessage: WebSocketMessage<T> | null;\n    /** Send a payload through the active connection. Returns false when not open. */\n    send: (payload: string | Blob | BufferSource) => boolean;\n    /** Force a reconnect, resetting the retry counter. */\n    reconnect: () => void;\n    /**\n     * Change the silence watchdog at runtime, in ms. `0` disables it.\n     *\n     * For a server that announces its own heartbeat interval in the first frame,\n     * so the tolerated silence is not hard-coded on both ends.\n     */\n    setSilenceTimeout: (ms: number) => void;\n}\n\n/**\n * React hook around {@link createWebSocket}. Manages the connection lifecycle\n * for the host component and tears it down on unmount.\n *\n * Every callback is read through a ref, so `onOpen` / `onMessage` / `onClose` /\n * `onError` / `onReconnecting` / `onReconnected` / `onLost` always run the\n * latest closure — an inline arrow function is fine and never reopens the\n * socket. Connection-shaping options (`protocols`, `maxRetries`,\n * `initialBackoff`, `maxBackoff`, `jitter`, `handshakeTimeout`,\n * `silenceTimeout`, `waitForOnline`, `pingInterval`, `queueWhileClosed`) are\n * baked into the connection, so changing one reopens it with the new value\n * rather than being silently ignored.\n *\n * `schema` is read when the socket opens, so declare it outside the component\n * (or memoize it): a schema built inline is a new object on every render, and\n * the one in force is whichever existed at the last open.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Status, last frame, and the `send` / `reconnect` controls.\n */\nexport function useWebSocket<T = unknown>(\n    url: string,\n    options: UseWebSocketOptions<T> = {},\n): UseWebSocketResult<T> {\n    const {\n        enabled = true,\n        protocols,\n        maxRetries,\n        initialBackoff,\n        maxBackoff,\n        jitter,\n        handshakeTimeout,\n        silenceTimeout,\n        waitForOnline,\n        pingInterval,\n        respondToPing,\n        queueWhileClosed,\n        maxQueuedMessages,\n    } = options;\n    const [status, setStatus] = useState<WebSocketStatus>(\"idle\");\n    const [lastMessage, setLastMessage] = useState<WebSocketMessage<T> | null>(null);\n    const controllerRef = useRef<WebSocketController | null>(null);\n\n    const optionsRef = useLatestRef(options);\n\n    const protocolsKey = Array.isArray(protocols) ? protocols.join(\",\") : (protocols ?? \"\");\n\n    useEffect(() => {\n        if (!enabled || !url) {\n            setStatus(\"idle\");\n            return;\n        }\n\n        /*\n         * Presence is read once, when the socket opens, and decides whether the\n         * forwarder is passed at all. A forwarder is always truthy, so wrapping\n         * an absent `parser` — or an absent `onParseError` — would tell\n         * `decodeFrame` the caller had supplied one and silently pick the wrong\n         * branch.\n         */\n        const hasParser = optionsRef.current.parser !== undefined;\n        const hasParseError = optionsRef.current.onParseError !== undefined;\n        const hasValidationError = optionsRef.current.onValidationError !== undefined;\n\n        const controller = createWebSocket<T>(url, {\n            protocols: optionsRef.current.protocols,\n            maxRetries,\n            initialBackoff,\n            maxBackoff,\n            jitter,\n            handshakeTimeout,\n            silenceTimeout,\n            waitForOnline,\n            pingInterval,\n            pingPayload: optionsRef.current.pingPayload,\n            respondToPing,\n            pongPayload: optionsRef.current.pongPayload,\n            queueWhileClosed,\n            maxQueuedMessages,\n            parser: hasParser ? (raw) => optionsRef.current.parser?.(raw) as T : undefined,\n            onParseError: hasParseError\n                ? (error, raw) => optionsRef.current.onParseError?.(error, raw)\n                : undefined,\n            schema: optionsRef.current.schema,\n            onValidationError: hasValidationError\n                ? (issues, raw) => optionsRef.current.onValidationError?.(issues, raw)\n                : undefined,\n            onStatusChange: setStatus,\n            onOpen: (event) => optionsRef.current.onOpen?.(event),\n            onClose: (event) => optionsRef.current.onClose?.(event),\n            onError: (event) => optionsRef.current.onError?.(event),\n            onReconnecting: (attempt, total) => optionsRef.current.onReconnecting?.(attempt, total),\n            onReconnected: () => optionsRef.current.onReconnected?.(),\n            onLost: (reason) => optionsRef.current.onLost?.(reason),\n            onMessage: (message) => {\n                setLastMessage(message);\n                optionsRef.current.onMessage?.(message);\n            },\n        });\n        controllerRef.current = controller;\n\n        return () => {\n            controller.close();\n            controllerRef.current = null;\n        };\n    }, [\n        url,\n        enabled,\n        protocolsKey,\n        maxRetries,\n        initialBackoff,\n        maxBackoff,\n        jitter,\n        handshakeTimeout,\n        silenceTimeout,\n        waitForOnline,\n        pingInterval,\n        respondToPing,\n        queueWhileClosed,\n        maxQueuedMessages,\n        optionsRef,\n    ]);\n\n    const send = useCallback((payload: string | Blob | BufferSource): boolean => {\n        return controllerRef.current?.send(payload) ?? false;\n    }, []);\n\n    const reconnect = useCallback((): void => {\n        controllerRef.current?.reconnect();\n    }, []);\n\n    const setSilenceTimeout = useCallback((ms: number): void => {\n        controllerRef.current?.setSilenceTimeout(ms);\n    }, []);\n\n    return { status, lastMessage, send, reconnect, setSilenceTimeout };\n}\n"],"mappings":"2GA6DA,SAAgB,EACZ,EACA,EAAkC,CAAC,EACd,CACrB,GAAM,CACF,UAAU,GACV,YACA,aACA,iBACA,aACA,SACA,mBACA,iBACA,gBACA,eACA,gBACA,mBACA,qBACA,EACE,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA0B,MAAM,EACtD,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAqC,IAAI,EACzE,GAAA,EAAgB,EAAA,OAAA,CAAmC,IAAI,EAEvD,EAAa,EAAA,aAAa,CAAO,EAEjC,EAAe,MAAM,QAAQ,CAAS,EAAI,EAAU,KAAK,GAAG,EAAK,GAAa,GA0FpF,OAxFA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,CAAC,EAAK,CAClB,EAAU,MAAM,EAChB,MACJ,CASA,IAAM,EAAY,EAAW,QAAQ,SAAW,IAAA,GAC1C,EAAgB,EAAW,QAAQ,eAAiB,IAAA,GACpD,EAAqB,EAAW,QAAQ,oBAAsB,IAAA,GAE9D,EAAa,EAAA,gBAAmB,EAAK,CACvC,UAAW,EAAW,QAAQ,UAC9B,aACA,iBACA,aACA,SACA,mBACA,iBACA,gBACA,eACA,YAAa,EAAW,QAAQ,YAChC,gBACA,YAAa,EAAW,QAAQ,YAChC,mBACA,oBACA,OAAQ,EAAa,GAAQ,EAAW,QAAQ,SAAS,CAAG,EAAS,IAAA,GACrE,aAAc,GACP,EAAO,IAAQ,EAAW,QAAQ,eAAe,EAAO,CAAG,EAC5D,IAAA,GACN,OAAQ,EAAW,QAAQ,OAC3B,kBAAmB,GACZ,EAAQ,IAAQ,EAAW,QAAQ,oBAAoB,EAAQ,CAAG,EACnE,IAAA,GACN,eAAgB,EAChB,OAAS,GAAU,EAAW,QAAQ,SAAS,CAAK,EACpD,QAAU,GAAU,EAAW,QAAQ,UAAU,CAAK,EACtD,QAAU,GAAU,EAAW,QAAQ,UAAU,CAAK,EACtD,gBAAiB,EAAS,IAAU,EAAW,QAAQ,iBAAiB,EAAS,CAAK,EACtF,kBAAqB,EAAW,QAAQ,gBAAgB,EACxD,OAAS,GAAW,EAAW,QAAQ,SAAS,CAAM,EACtD,UAAY,GAAY,CACpB,EAAe,CAAO,EACtB,EAAW,QAAQ,YAAY,CAAO,CAC1C,CACJ,CAAC,EAGD,MAFA,GAAc,QAAU,MAEX,CACT,EAAW,MAAM,EACjB,EAAc,QAAU,IAC5B,CACJ,EAAG,CACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACJ,CAAC,EAcM,CAAE,SAAQ,cAAa,MAAA,EAZjB,EAAA,YAAA,CAAa,GACf,EAAc,SAAS,KAAK,CAAO,GAAK,GAChD,CAAC,CAU0B,EAAM,WAAA,EARlB,EAAA,YAAA,KAAwB,CACtC,EAAc,SAAS,UAAU,CACrC,EAAG,CAAC,CAMgC,EAAW,mBAAA,EAJrB,EAAA,YAAA,CAAa,GAAqB,CACxD,EAAc,SAAS,kBAAkB,CAAE,CAC/C,EAAG,CAAC,CAE2C,CAAkB,CACrE"}