Adds `RemoveSource` to stop streaming from a previously registered
source. Removing a source cancels its connection loop and closes any
in-flight websocket so the loop exits promptly instead of waiting for
the next reconnect tick.
Per-source runtime state (the loop's cancel func and the active
websocket conn) was previously split across two sync.Maps and a
separate map of registered sources, each with its own locking. That
left several races, e.g. the conn was stored in connMap only after a successful
dial, so a remove during the dial would miss it.
The new design collapses everything into a single
`sources map[Source]*sourceState` guarded by `sourcesMu`. The lock is only
held for short, non-blocking map mutations and is always released before
any side effect possibly-blocking calls.
`runConnection` now re-checks under the lock that the source is still
registered (and the ctx not cancelled) before installing a freshly
dialed conn, and its deferred cleanup only clears the conn slot if the
entry still points at the conn it installed. This makes 'remove
during dial' and 'remove during read' both deterministic: the cancel
cuts the loop, the close breaks ReadMessage, and a concurrent
runConnection cannot resurrect state for a removed source.