---
name: ws_broadcast
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func WSBroadcast(hub *WSHub, msg []byte) error"
description: "Envia bytes al canal Broadcast del hub para que se entreguen a todos los clientes WebSocket conectados. La entrega real la hace el loop Run() del hub. Bloqueante hasta que el canal Broadcast tenga espacio (capacidad 256 por defecto). Retorna error si el hub es nil."
tags: [websocket, broadcast, server, fanout, infra, realtime]
uses_functions: []
uses_types: [WSHub_go_infra]
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [fmt]
params:
  - name: hub
    desc: "*WSHub donde estan registrados los clientes. Si es nil retorna error."
  - name: msg
    desc: "bytes a entregar a todos los clientes. Tipicamente JSON serializado de un WSMessage."
output: "error si hub es nil. Nil si el mensaje se encolo en el canal Broadcast (la entrega es asincrona)."
tested: true
tests: ["envia mensaje al canal Broadcast del hub", "retorna error si hub es nil", "el hub entrega el mensaje a todos los clientes registrados"]
test_file_path: "functions/infra/ws_test.go"
file_path: "functions/infra/ws_broadcast.go"
---

## Ejemplo

```go
hub := NewWSHub()
go hub.Run()
defer hub.Stop()

// Notificar a todos los clientes conectados
msg, _ := json.Marshal(WSMessage{
    Type:     "step_complete",
    Payload:  []byte(`{"step":"build","status":"ok"}`),
    SenderID: "pipeline_runner",
    Ts:       time.Now().UnixMilli(),
})
WSBroadcast(hub, msg)
```

## Notas

Bloqueante con backpressure controlado: si el canal `Broadcast` se llena (256 mensajes pendientes), la llamada se bloquea hasta que el hub procese alguno. Esto da feedback natural al productor cuando el sistema esta saturado.

La entrega a clientes individuales es no bloqueante (clientes lentos se desconectan automaticamente). Si necesitas semantica at-least-once con retry, montar la logica encima.
