package realtime import ( "encoding/json" "sync" ) type Event struct { Topic string `json:"topic"` Data json.RawMessage `json:"data"` } type Hub struct { mu sync.RWMutex subs map[string]map[chan Event]struct{} } func New() *Hub { return &Hub{subs: make(map[string]map[chan Event]struct{})} } func (h *Hub) Publish(topic string, value any) { data, err := json.Marshal(value) if err != nil { return } h.mu.RLock() defer h.mu.RUnlock() for _, key := range []string{"*", topic} { for ch := range h.subs[key] { select { case ch <- Event{Topic: topic, Data: data}: default: } } } } func (h *Hub) Subscribe(topic string) (<-chan Event, func()) { ch := make(chan Event, 16) h.mu.Lock() if h.subs[topic] == nil { h.subs[topic] = make(map[chan Event]struct{}) } h.subs[topic][ch] = struct{}{} h.mu.Unlock() return ch, func() { h.mu.Lock() delete(h.subs[topic], ch) close(ch) h.mu.Unlock() } }