UNPKG

use-next-sse

Version:

A lightweight Server-Sent Events (SSE) library for Next.js, enabling real-time, unidirectional data streaming from server to client

104 lines (103 loc) 5.16 kB
"use strict"; 'use client'; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSSE = void 0; const react_1 = require("react"); const sse_manager_1 = require("./sse-manager"); /** * Hook to manage Server-Sent Events (SSE) connections. * * @template T - The type of the data expected from the SSE. * @param {SSEOptions} options - The options for the SSE connection. {@link SSEOptions} * @param {string} options.url - The URL to connect to for SSE. * @param {string} [options.eventName='message'] - The name of the event to listen for. * @param {boolean | { interval?: number, maxAttempts?: number }} [options.reconnect] - Whether to automatically reconnect if the connection is lost. If an object, the interval and maxAttempts can be specified. Default `false`. * @param {number} [options.reconnect.interval] - The interval in milliseconds to wait before reconnecting. Default `1000`ms. * @param {number} [options.reconnect.maxAttempts] - The maximum number of reconnection attempts. Default `5`. * @param {boolean} [options.withCredentials=false] - Whether to include credentials in the request. Default `false`. * @returns {SSEResult<T>} The result of the SSE connection, including data, error, last event ID, and a close function. {@link SSEResult} * * @example * const { data, error, lastEventId, close } = useSSE<{ message: string }>({ url: 'https://example.com/sse' }); * * useEffect(() => { * if (data) { * console.log(data.message); * } * }, [data]); */ function useSSE({ url, eventName = 'message', reconnect = false, withCredentials = false, }) { const [data, setData] = (0, react_1.useState)(null); const [error, setError] = (0, react_1.useState)(null); const [lastEventId, setLastEventId] = (0, react_1.useState)(null); const [connectionState, setConnectionState] = (0, react_1.useState)('connecting'); const reconnectAttempts = (0, react_1.useRef)(0); const reconnectTimeout = (0, react_1.useRef)(null); const cleanupRef = (0, react_1.useRef)(() => { }); const close = (0, react_1.useCallback)(() => { if (reconnectTimeout.current) { clearTimeout(reconnectTimeout.current); } cleanupRef.current(); setConnectionState('closed'); }, [cleanupRef.current]); (0, react_1.useEffect)(() => { const connect = () => { setConnectionState('connecting'); const source = sse_manager_1.sseManager.getConnection(url, { withCredentials }); const handleOpen = () => { setConnectionState('open'); reconnectAttempts.current = 0; }; const handleMessage = (event) => { try { const parsedData = JSON.parse(event.data); setData(parsedData); setLastEventId(event.lastEventId); setError(null); } catch (err) { setError(new Error('Failed to parse event data')); } }; const handleError = (event) => { setConnectionState('closed'); const maxAttempts = typeof reconnect === 'object' && reconnect.maxAttempts ? reconnect.maxAttempts : 5; if (reconnect && reconnectAttempts.current < maxAttempts) { const interval = typeof reconnect === 'object' && reconnect.interval ? reconnect.interval : 1000; reconnectAttempts.current += 1; reconnectTimeout.current = window.setTimeout(() => { sse_manager_1.sseManager.removeEventListener(url, eventName, handleMessage); source.removeEventListener('open', handleOpen); source.removeEventListener('error', handleError); connect(); }, interval); } else { destructor(); } setError(new Error(`EventSource failed\n\nsource state:${source.readyState}\nCLOSED: ${source.CLOSED}\nCONNECTING: ${source.CONNECTING}\nOPEN: ${source.OPEN}`)); }; const destructor = () => { sse_manager_1.sseManager.removeEventListener(url, eventName, handleMessage); source.removeEventListener('open', handleOpen); source.removeEventListener('error', handleError); sse_manager_1.sseManager.releaseConnection(url); }; sse_manager_1.sseManager.addEventListener(url, eventName, handleMessage); source.addEventListener('open', handleOpen); source.addEventListener('error', handleError); return destructor; }; const cleanup = connect(); cleanupRef.current = cleanup; return () => { if (reconnectTimeout.current) { clearTimeout(reconnectTimeout.current); } cleanup(); }; }, [url, eventName, reconnect]); return { data, error, lastEventId, close, connectionState }; } exports.useSSE = useSSE;