Files
stack/apps/web/src/lib/socket.ts
T
2026-08-10 00:24:20 -05:00

37 lines
1.3 KiB
TypeScript

import { io } from 'socket.io-client';
import type { ChatSocket } from './chat-contract';
let socket: ChatSocket | null = null;
export function getSocket(): ChatSocket {
if (!socket) {
// socket.io-client 4.8.3's `io()` factory declaration always returns the
// default unparameterized Socket (it accepts no <ListenEvents, EmitEvents>
// generics), so this one cast is the unavoidable boundary between that and the
// typed `/chat` contract. Every other call site uses the resulting ChatSocket
// with no further assertions.
socket = io('/chat', {
withCredentials: true,
autoConnect: false,
transports: ['websocket', 'polling'],
}) as unknown as ChatSocket;
// A transient `disconnect` (network blip, server restart) must NOT null
// the singleton: socket.io-client auto-reconnects this same instance,
// and its listeners stay registered across that reconnect. Nulling here
// previously orphaned those listeners on the next getSocket() call by
// handing back a brand-new, unconnected instance. Only destroySocket()
// (an explicit, intentional teardown) may reset the singleton.
}
return socket;
}
/** Tear down the singleton socket and reset the reference. */
export function destroySocket(): void {
if (socket) {
socket.offAny();
socket.disconnect();
socket = null;
}
}