feat(master): in-process broker for live UI change signals

This commit is contained in:
2026-10-12 14:48:03 +02:00
parent 8a1fb45971
commit 5d75f9a7da
2 changed files with 104 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
// 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:
}
}
}