// Package events fans out "something changed" signals from the agent hub to // open browser streams. package events import "sync" // Topic names what changed. Subscribers refetch that part of the UI. type Topic string // Topics. const ( Services Topic = "services" Agents Topic = "agents" ) // Broker is an in-process publish/subscribe hub. type Broker struct { mu sync.Mutex subs map[chan Topic]struct{} } // NewBroker returns an empty Broker. func NewBroker() *Broker { return &Broker{subs: make(map[chan Topic]struct{})} } // Subscribe returns a channel of topics and a cancel function that must be // called when the subscriber goes away. Cancel is safe to call twice. func (b *Broker) Subscribe() (<-chan Topic, func()) { ch := make(chan Topic, 16) b.mu.Lock() b.subs[ch] = struct{}{} b.mu.Unlock() var once sync.Once return ch, func() { once.Do(func() { b.mu.Lock() delete(b.subs, ch) b.mu.Unlock() }) } } // Publish signals t to every subscriber. A subscriber whose buffer is full is // skipped rather than blocking the agent hub; it already has signals queued // and will refetch current state when it processes them. func (b *Broker) Publish(t Topic) { b.mu.Lock() defer b.mu.Unlock() for ch := range b.subs { select { case ch <- t: default: } } }