import { cancellableRace, combineRaceCancellation } from "race-cancellation"; import { AttachProtocolTransport, AttachSession, DetachSession, RaceCancellation, } from "../types"; export type DispatchEvent = ( sessionId: SessionId, event: string, params?: object, ) => void; interface Session { onEvent: (event: string, params?: object) => void; onError: (error: Error) => void; onDetach: () => void; } export type Send = < Method extends string, Params extends object, Result extends object, >( method: Method, params?: Params, raceCancellation?: RaceCancellation, sessionId?: SessionId, ) => Promise; export default function newSessions( send: Send, raceClose: RaceCancellation, ): [ AttachSession, DetachSession, DispatchEvent, ] { let sessions: Map | undefined; return [attachSession, detachSession, dispatchEvent]; function attachSession( sessionId: SessionId, ): AttachProtocolTransport { return ( onEvent: (event: string, params?: object) => void, onError: (err: Error) => void, onDetach: () => void, ) => { if (sessions === undefined) { sessions = new Map(); } const [raceDetach, cancel] = cancellableRace(); sessions.set(sessionId, { onDetach() { cancel(`session detached ${String(sessionId)}`); onDetach(); }, onError, onEvent, }); return [ attachSession, detachSession, (method, params, raceCancellation) => send( method, params, // send is already raced against close combineRaceCancellation(raceDetach, raceCancellation), sessionId, ), combineRaceCancellation(raceClose, raceDetach), ]; }; } function detachSession(sessionId: SessionId): void { if (sessions === undefined) { return; } const session = sessions.get(sessionId); if (session !== undefined) { sessions.delete(sessionId); session.onDetach(); } } function dispatchEvent( sessionId: SessionId, event: string, params?: object, ): void { if (sessions === undefined) { return; } const session = sessions.get(sessionId); if (session !== undefined) { try { session.onEvent(event, params); } catch (e) { let err: Error & { cause?: unknown }; if (e instanceof Error) { err = e; } else { err = new Error("error dispatching event"); err.cause = e; } session.onError(err); } } } }