Help us shape the future of Go! Take the 2026 Go Developer Survey

hub

package
v2.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package hub is a package-level singleton event hub: it broadcasts graph "hit" (search) and "change" (incremental update) events to connected WebSocket clients so the UI can render a live "graph in use" effect.

Ported from an earlier multi-tenant memory hub, trimmed for a single-process local tool: xray-memory has no auth, no tenant/grant filtering, and no multi-replica gossip, so those layers (and the userHash-scoped subscribe variant they required) are left out entirely — every subscriber sees every frame.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClientCount

func ClientCount() int

ClientCount returns the number of currently-connected WebSocket subscribers, so a caller can skip sampling/publishing work when nobody is listening.

func HitCount

func HitCount(qn string) int64

HitCount returns the number of times qn has been returned as a search hit.

func PublishChange

func PublishChange(evt ChangeEvent)

PublishChange broadcasts a graph-update event to all connected subscribers.

func PublishHit

func PublishHit(evt HitEvent)

PublishHit broadcasts a hit event to all connected subscribers and increments each QN's hit counter. A cheap no-op when there are no subscribers AND evt carries no QNs (the common case for HitCount-only callers is still cheap: the map lookups below are O(len(QNs))).

func ResetForTest

func ResetForTest()

ResetForTest restores the hub to its zero state (no clients, no hit counts) so tests don't leak state across each other through this package-level singleton.

func ServeWS

func ServeWS(logger zerolog.Logger) http.HandlerFunc

ServeWS upgrades the connection and streams hub Frame envelopes (`{"type":"hit"|"change","data":...}`), one per WebSocket text message, until the client disconnects. Server -> client push only: the client is never expected to send anything, so the read pump below exists solely to notice a disconnect and answer pong frames.

func Subscribe

func Subscribe() (<-chan Frame, func())

Subscribe registers a new fan-out subscriber and returns a receive-only channel of Frame envelopes plus an unsubscribe func the caller must invoke exactly once (typically via defer) when the connection closes.

The channel is buffered at 8 and every send to it is non-blocking (see broadcast) — a slow reader drops frames rather than stalling a publisher. This is a live "in use" indicator, not a durable event log: a dropped frame just means one search's highlight animation is missed, not a correctness problem, and a publisher (every search request) must never be made to wait on a WebSocket write.

Types

type ChangeEvent

type ChangeEvent struct {
	Op    string   `json:"op"` // "update"
	Paths []string `json:"paths,omitempty"`
}

ChangeEvent is broadcast whenever the graph is updated in place, carrying the paths that were actually re-indexed — never the skipped (CGO-free build, minified) or unowned ones, since those describe no real graph change a client needs to react to.

type Frame

type Frame struct {
	Type FrameType       `json:"type"`
	Data json.RawMessage `json:"data"`
}

Frame is the one wire envelope every subscriber receives over GET /ws: `{"type": "hit"|"change", "data": <payload>}`. json.RawMessage lets broadcast marshal the payload once and every subscriber decode it independently, without a second round of re-marshaling per client.

type FrameType

type FrameType string

FrameType is the WebSocket envelope's explicit discriminator (see Frame). An explicit field beats shape-sniffing the payload: HitEvent and ChangeEvent don't overlap today, but a client should never have to guess which one it received from field presence alone.

const (
	// FrameHit marks a Frame whose Data is a HitEvent.
	FrameHit FrameType = "hit"
	// FrameChange marks a Frame whose Data is a ChangeEvent.
	FrameChange FrameType = "change"
)

type HitEvent

type HitEvent struct {
	QNs   []string `json:"qns"`
	Query string   `json:"query,omitempty"`
	// Kinds is the per-hit node kind, parallel to QNs (same order, same
	// length) — one of "Func" "Test" "Class" "Type" "File" "Doc" "Section"
	// "Manifest". Added so a client can resolve a hit's category even when
	// that node isn't currently drawn on a bounded graph view. Additive
	// (omitempty): a caller that only has QNs still compiles and behaves as
	// before with Kinds left nil. A client that finds Kinds shorter than QNs
	// must fall back to QN-only behavior rather than index out of range.
	Kinds []string `json:"kinds,omitempty"`
}

HitEvent is broadcast whenever a search resolves (over any surface — MCP, HTTP /search, or a direct in-process call, see index.Index.Search's own comment for why that one call site is authoritative).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL