--- name: ws_hub kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func NewWSHub() *WSHub" description: "Crea un WSHub con canales Broadcast (buffereado, capacidad 256), Register y Unregister. El metodo Run() ejecuta el loop principal: select sobre Register/Unregister/Broadcast. Stop() cierra el loop y limpia todos los clientes. Patron hub clasico de Go: un solo writer al mapa de clientes, sin mutex." tags: [websocket, hub, server, broadcast, infra, realtime] uses_functions: [] uses_types: [WSHub_go_infra, WSClient_go_infra] returns: [WSHub_go_infra] returns_optional: false error_type: "error_go_core" imports: [] params: [] output: "*WSHub listo para usar. Llamar a Run() en una goroutine para activar el loop. Llamar a Stop() para terminar limpiamente." tested: true tests: ["registra y desregistra clientes", "broadcast envia mensaje a todos los clientes", "cliente lento es desconectado del hub", "Stop cierra todos los clientes"] test_file_path: "functions/infra/ws_test.go" file_path: "functions/infra/ws_hub.go" --- ## Ejemplo ```go hub := NewWSHub() go hub.Run() defer hub.Stop() // Cualquier goroutine puede broadcastear WSBroadcast(hub, []byte("evento")) ``` ## Notas El loop `Run()` es bloqueante y esta diseñado para correr como goroutine durante toda la vida del proceso. La unica forma de pararlo es llamar a `Stop()`. Un cliente lento (cuyo canal `Send` esta lleno) es desconectado automaticamente del hub durante el broadcast — esta es la garantia anti-backpressure: ningun cliente puede bloquear el broadcast a los demas. El hub no autentica ni autoriza — es solo un fan-out. La auth se hace en el handler antes de registrar el cliente.