67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
import { ENV_CONSTANT } from '@/constants/secrect.constant';
|
|
|
|
export type SseEventHandler<T = unknown> = (
|
|
data: T,
|
|
event: MessageEvent,
|
|
) => void;
|
|
|
|
export type SseEventMap<TEvents extends Record<string, unknown>> = {
|
|
[EventName in keyof TEvents]: SseEventHandler<TEvents[EventName]>;
|
|
};
|
|
|
|
export interface SseConnection {
|
|
source: EventSource;
|
|
close: () => void;
|
|
}
|
|
|
|
function getSseUrl(path: string) {
|
|
if (/^https?:\/\//i.test(path)) return path;
|
|
|
|
const baseUrl = ENV_CONSTANT.BASE_API_URL ?? '';
|
|
const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
|
|
return `${normalizedBase}${normalizedPath}`;
|
|
}
|
|
|
|
function parseEventData(event: MessageEvent) {
|
|
if (!event.data) return null;
|
|
|
|
try {
|
|
return JSON.parse(event.data);
|
|
} catch {
|
|
return event.data;
|
|
}
|
|
}
|
|
|
|
export const sseService = {
|
|
connect: <TEvents extends Record<string, unknown>>(
|
|
path: string,
|
|
events: SseEventMap<TEvents>,
|
|
): SseConnection => {
|
|
const source = new EventSource(getSseUrl(path));
|
|
const cleanups: Array<() => void> = [];
|
|
|
|
Object.entries(events).forEach(([eventName, handler]) => {
|
|
const listener = (event: Event) => {
|
|
const messageEvent = event as MessageEvent;
|
|
(handler as SseEventHandler)(
|
|
parseEventData(messageEvent),
|
|
messageEvent,
|
|
);
|
|
};
|
|
|
|
source.addEventListener(eventName, listener);
|
|
cleanups.push(() => source.removeEventListener(eventName, listener));
|
|
});
|
|
|
|
return {
|
|
source,
|
|
close: () => {
|
|
cleanups.forEach((cleanup) => cleanup());
|
|
source.close();
|
|
},
|
|
};
|
|
},
|
|
};
|