merge: issue/0011-websocket-sse — WebSocket + SSE (8 fns, 4 tipos)
# Conflicts: # registry.db
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: SSEEvent
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
algebraic: product
|
||||
definition: |
|
||||
type SSEEvent struct {
|
||||
Event string `json:"event"`
|
||||
Data string `json:"data"`
|
||||
ID string `json:"id"`
|
||||
Retry int `json:"retry"`
|
||||
}
|
||||
description: "Evento Server-Sent Events segun la spec W3C. Campos opcionales: si Event esta vacio se envia solo data, si ID esta vacio no se incluye campo id, Retry en ms (0 = omitir y dejar el default del browser ~3000ms)."
|
||||
tags: [sse, event, server-sent-events, infra, realtime]
|
||||
uses_types: []
|
||||
file_path: "functions/infra/sse_event.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
ev := SSEEvent{
|
||||
Event: "metrics_update",
|
||||
ID: "42",
|
||||
Data: `{"cpu": 23.4, "mem": 87.1}`,
|
||||
}
|
||||
SSESend(w, ev)
|
||||
```
|
||||
|
||||
Wire format generado:
|
||||
|
||||
```
|
||||
event: metrics_update
|
||||
id: 42
|
||||
data: {"cpu": 23.4, "mem": 87.1}
|
||||
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Tipo producto con campos opcionales. `Data` puede contener saltos de linea — el formateador los traduce a multiples lineas `data:` segun la spec. `Retry` solo se envia si es > 0; cuando se envia, indica al cliente cuantos ms esperar antes de reconectar.
|
||||
|
||||
Para enviar solo un comentario keepalive (no un evento), no se usa este tipo: se escribe `: keepalive\n\n` directamente al writer.
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: WSClient
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
algebraic: product
|
||||
definition: |
|
||||
type WSClient struct {
|
||||
Hub *WSHub
|
||||
Conn *websocket.Conn
|
||||
Send chan []byte
|
||||
ID string
|
||||
}
|
||||
description: "Conexion WebSocket individual gestionada por un WSHub. Cada cliente tiene su propio canal Send buffereado para entregar mensajes en orden y un ID para identificarlo en broadcasts y handlers."
|
||||
tags: [websocket, client, connection, server, infra, realtime]
|
||||
uses_types: [WSHub_go_infra]
|
||||
file_path: "functions/infra/ws_client.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
client := &WSClient{
|
||||
Hub: hub,
|
||||
Conn: wsConn,
|
||||
Send: make(chan []byte, 64),
|
||||
ID: "user-42",
|
||||
}
|
||||
hub.Register <- client
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Tipo producto. El canal `Send` debe ser buffereado (tipico 64-256) para evitar que un cliente lento bloquee el broadcast del hub. `Conn` usa `nhooyr.io/websocket` que soporta `context.Context` nativamente. `ID` es texto libre — la app que monta el handler decide su semantica (UUID, username, session id, etc.).
|
||||
|
||||
Cada cliente tiene normalmente dos goroutines internas:
|
||||
- readPump: lee del Conn y publica al hub.Broadcast (o procesa con callback)
|
||||
- writePump: consume del Send y escribe al Conn
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: WSHub
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
algebraic: product
|
||||
definition: |
|
||||
type WSHub struct {
|
||||
Clients map[*WSClient]bool
|
||||
Broadcast chan []byte
|
||||
Register chan *WSClient
|
||||
Unregister chan *WSClient
|
||||
}
|
||||
description: "Hub que gestiona el ciclo de vida de conexiones WebSocket. Mantiene un mapa de clientes activos y canales para registro, desregistro y broadcast. Se ejecuta como goroutine via Run() y se compone con ws_handler para servir conexiones."
|
||||
tags: [websocket, hub, server, broadcast, infra, realtime]
|
||||
uses_types: [WSClient_go_infra]
|
||||
file_path: "functions/infra/ws_hub.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
hub := NewWSHub()
|
||||
go hub.Run()
|
||||
defer hub.Stop()
|
||||
|
||||
routes := []Route{
|
||||
{Method: "GET", Path: "/ws", Handler: WSHandler(hub, []string{"*"})},
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Tipo producto. El campo `done` interno (no exportado) controla el cierre limpio. `Broadcast` es buffereado (256) para no bloquear emisores. Cada cliente lento se desconecta automaticamente si su canal `Send` se llena durante un broadcast — esta es la garantia anti-backpressure: un cliente que no consume no afecta a los demas.
|
||||
|
||||
El loop `Run()` es de un solo thread escribiendo al mapa, asi que no hace falta mutex. Para parar limpiamente: `hub.Stop()` cierra `done`, libera todos los `client.Send` y termina el loop.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: WSMessage
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
algebraic: product
|
||||
definition: |
|
||||
type WSMessage struct {
|
||||
Type string `json:"type"`
|
||||
Payload []byte `json:"payload"`
|
||||
SenderID string `json:"sender_id"`
|
||||
Ts int64 `json:"ts"`
|
||||
}
|
||||
description: "Mensaje tipado que viaja por WebSocket entre cliente y servidor. El campo Type permite al receptor decidir como procesar el payload. Incluye SenderID y timestamp para trazabilidad."
|
||||
tags: [websocket, message, protocol, infra, realtime]
|
||||
uses_types: []
|
||||
file_path: "functions/infra/ws_message.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
msg := WSMessage{
|
||||
Type: "chat",
|
||||
Payload: []byte(`{"text":"hola"}`),
|
||||
SenderID: "user-1",
|
||||
Ts: time.Now().UnixMilli(),
|
||||
}
|
||||
data, _ := json.Marshal(msg)
|
||||
WSBroadcast(hub, data)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Tipo producto. Las tags `json:` permiten serializar directamente a JSON para enviar por el wire. `Payload` es `[]byte` para permitir tanto JSON anidado como datos binarios codificados en base64. `Ts` en milisegundos epoch para compatibilidad con `Date.now()` en el browser.
|
||||
|
||||
No es obligatorio usar este tipo — apps que necesiten un protocolo distinto pueden enviar bytes arbitrarios via `WSBroadcast` o `WSSend`. Es solo un convenio recomendado para protocolos chat-like.
|
||||
Reference in New Issue
Block a user