{"version":3,"file":"use-event-stream.cjs","names":[],"sources":["../../src/sse/use-event-stream.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { useLatestRef } from \"@/hooks/use-latest-ref\";\nimport {\n    createEventStream,\n    type CreateEventStreamOptions,\n    type EventStreamMessage,\n    type EventStreamStatus,\n} from \"./create-event-stream\";\n\nexport interface UseEventStreamOptions<T> extends Omit<\n    CreateEventStreamOptions<T>,\n    \"onStatusChange\"\n> {\n    /** When false, the stream is not opened. Useful for \"wait for auth\". Default: true. */\n    enabled?: boolean;\n}\n\nexport interface UseEventStreamResult<T> {\n    status: EventStreamStatus;\n    /** Last message received (excluding heartbeats). */\n    lastMessage: EventStreamMessage<T> | null;\n    /** Force a reconnect. */\n    reconnect: () => void;\n}\n\n/**\n * React hook wrapper around {@link createEventStream}. Connection lifecycle is\n * tied to the component (and the `url`/`enabled` dependencies); the stream\n * closes on unmount.\n *\n * Pass `schema` to have every frame validated before it reaches `onMessage`,\n * instead of trusting the type argument: without it `data` is announced as `T`\n * on the strength of the generic alone, which is a promise about the server\n * that TypeScript cannot keep. It is read when the stream opens, so declare it\n * outside the component rather than building one inline per render.\n *\n * @example\n * useEventStream<Notification>(`${API}/notifications/stream`, {\n *     enabled: !!user,\n *     withCredentials: true,\n *     onMessage: ({ data }) => addNotification(data),\n * });\n *\n * @example\n * // Validated: a frame that does not match never reaches `onMessage`\n * const notificationSchema = z.object({ id: z.string(), message: z.string() });\n *\n * useEventStream<Notification>(`${API}/notifications/stream`, {\n *     schema: notificationSchema,\n *     onValidationError: (issues) => logger.warn(\"stream drift\", { issues }),\n *     onMessage: ({ data }) => addNotification(data),\n * });\n */\nexport function useEventStream<T = unknown>(\n    url: string,\n    options: UseEventStreamOptions<T> = {},\n): UseEventStreamResult<T> {\n    const { enabled = true, onMessage, ...rest } = options;\n    const [status, setStatus] = useState<EventStreamStatus>(\"idle\");\n    const [lastMessage, setLastMessage] = useState<EventStreamMessage<T> | null>(null);\n    const reconnectRef = useRef<(() => void) | null>(null);\n\n    const onMessageRef = useLatestRef(onMessage);\n\n    useEffect(() => {\n        if (!enabled || !url) {\n            setStatus(\"idle\");\n            return;\n        }\n\n        const controller = createEventStream<T>(url, {\n            ...rest,\n            onStatusChange: setStatus,\n            onMessage: (message) => {\n                setLastMessage(message);\n                onMessageRef.current?.(message);\n            },\n        });\n        reconnectRef.current = controller.reconnect;\n\n        return () => {\n            controller.close();\n            reconnectRef.current = null;\n        };\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [url, enabled]);\n\n    return {\n        status,\n        lastMessage,\n        reconnect: () => reconnectRef.current?.(),\n    };\n}\n"],"mappings":"6GAqDA,SAAgB,EACZ,EACA,EAAoC,CAAC,EACd,CACvB,GAAM,CAAE,UAAU,GAAM,YAAW,GAAG,GAAS,EACzC,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA4B,MAAM,EACxD,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAuC,IAAI,EAC3E,GAAA,EAAe,EAAA,OAAA,CAA4B,IAAI,EAE/C,EAAe,EAAA,aAAa,CAAS,EAyB3C,OAvBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,CAAC,EAAK,CAClB,EAAU,MAAM,EAChB,MACJ,CAEA,IAAM,EAAa,EAAA,kBAAqB,EAAK,CACzC,GAAG,EACH,eAAgB,EAChB,UAAY,GAAY,CACpB,EAAe,CAAO,EACtB,EAAa,UAAU,CAAO,CAClC,CACJ,CAAC,EAGD,MAFA,GAAa,QAAU,EAAW,cAErB,CACT,EAAW,MAAM,EACjB,EAAa,QAAU,IAC3B,CAEJ,EAAG,CAAC,EAAK,CAAO,CAAC,EAEV,CACH,SACA,cACA,cAAiB,EAAa,UAAU,CAC5C,CACJ"}