Application-level ping/pong
RFC ping/pong (opcode 0x9/0xA) is invisible from the browser API — the browser handles it but does not let you trigger it. So you build your own. Send {type: 'ping'} every 30 seconds; expect {type: 'pong'} within a few seconds; if you do not get one, close the connection with code 4000 and let your reconnection logic take over. This catches silently-dead connections (NAT timeout, laptop lid) that the browser does not notice.
Message router by type
Once your messages have a type field, route them. A flat switch is fine for a few types; a registry (Map<type, handler>) scales better. The router is the natural place to add cross-cutting concerns: logging, validation, error handling.
Compose, don't inherit
Reconnection, heartbeat, and routing are independent concerns. Build them as small composable wrappers around a base socket, rather than one giant class. cwkPippa's adapters follow exactly this shape — narrow boundary, narrow responsibilities.