events

package
v4.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package events provides a typed in-app publish/subscribe fan-out bus with component-lifecycle-tied subscriptions for the GoWebComponents framework.

Every topic is a plain string key. Subscribers are typed via generics; a subscriber registered for type T on topic "foo" only receives publications of type T on that same topic — mismatched types are silently skipped.

The package works without a running render loop (unit-testable on native Go) and compiles for both native and GOOS=js GOARCH=wasm targets.

Package events — typed_publish.go adds a topic-type codec registry that lets an AI agent bridge (or any JSON source) publish events that reach typed subscribers registered with Subscribe[T], INCLUDING composite types.

Background: a plain json.Unmarshal into `any` yields Go primitives for JSON primitives (string, float64, bool, nil) but a map[string]any for JSON objects. So an untyped Publish[any] DOES reach a Subscribe[T] handler when T is the matching primitive — the value's dynamic type satisfies the parseValue.(T) assertion in the deliver wrapper — but it NEVER reaches a Subscribe[SomeStruct] handler, because the value is a map[string]any, not the struct. The codec registry closes that gap: RegisterTopic[T] decodes the JSON into the concrete T and calls Publish[T], so subscribers of ANY type T — composite types included — receive the value.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Publish

func Publish[T any](parseTopic string, parseValue T)

Publish delivers parseValue to every subscriber currently registered on parseTopic whose handler type matches T. Delivery order matches registration order. A panicking subscriber is contained; other subscribers still receive the value. Publish is goroutine-safe.

Example

ExamplePublish shows the non-hook core: subscribe to a typed topic, publish to it, and unsubscribe when done. Inside a component prefer events.UseTopic, which ties the subscription to the component lifecycle.

package main

import (
	"fmt"

	"github.com/monstercameron/GoWebComponents/v4/events"
)

func main() {
	parseUnsubscribe := events.Subscribe("greeting", func(parseValue string) {
		fmt.Println("received:", parseValue)
	})
	defer parseUnsubscribe()

	events.Publish("greeting", "hello")
	events.Publish("greeting", "world")
}
Output:
received: hello
received: world

func PublishJSON

func PublishJSON(parseTopic string, parseRaw json.RawMessage) error

PublishJSON decodes parseRaw and publishes the result on parseTopic.

If a codec has been registered for the topic via RegisterTopic[T], the JSON is unmarshalled into T and Publish[T] is called — this reaches every Subscribe[T] handler on that topic.

If NO codec is registered, the JSON is decoded into a generic any value (numbers become float64, objects become map[string]any, etc.) and Publish[any] is called. This reaches any-typed subscribers AND concretely-typed subscribers whose T matches the decoded primitive (Subscribe[string]/[float64]/[bool] receive a JSON string/number/bool), but does NOT reach composite-typed subscribers — a Subscribe[SomeStruct] sees a map[string]any, not its struct, so register a codec for those topics.

An error is returned when the registered codec's json.Unmarshal step fails (wrong JSON shape for T). JSON decode failures in the untyped fallback path are also returned. A missing topic codec is not an error.

func RegisterTopic

func RegisterTopic[T any](parseTopic string)

RegisterTopic registers a JSON codec for parseTopic so that PublishJSON can decode an incoming JSON payload into the concrete Go type T and deliver it to every Subscribe[T] handler on that topic.

Applications call RegisterTopic once per typed topic they want an agent bridge (or any JSON source) to drive. Re-registration replaces the previous codec for the topic.

Example:

events.RegisterTopic[string]("chat.message")
events.RegisterTopic[MyStruct]("app.state")

func RegisteredTopicCodecs

func RegisteredTopicCodecs() []string

RegisteredTopicCodecs returns a sorted list of topic strings that have a registered codec. Agent bridges and devtools use this to discover which topics accept a typed JSON publish.

func Subscribe

func Subscribe[T any](parseTopic string, parseHandler func(T)) (parseUnsubscribe func())

Subscribe registers parseHandler to receive every future value published on parseTopic whose type matches T. It returns an unsubscribe function; calling it removes the handler and, when the last subscriber leaves, clears the internal registry entry for that topic.

Subscribe is goroutine-safe. The handler is invoked synchronously from Publish; a panicking handler is contained so it cannot stop delivery to sibling subscribers.

func SubscriberCount

func SubscriberCount(parseTopic string) int

SubscriberCount returns the number of handlers currently registered on a topic, across all element types. It is the exported counterpart of the package-internal test helper.

Types

type TopicHandle

type TopicHandle[T any] struct {
	// contains filtered or unexported fields
}

TopicHandle is returned by UseTopic and lets the caller publish values on the topic it was created for.

func UseTopic

func UseTopic[T any](parseTopic string, parseHandler func(T), parseOptions ...TopicOption) TopicHandle[T]

UseTopic wires a typed pub/sub subscription to a component's lifetime via UseEffect. When parseHandler is non-nil the subscription is established during the effect phase and automatically removed when the component unmounts or when parseTopic changes. Pass nil for a publish-only component. Zero or more TopicOption values fine-tune subscription behaviour.

UseTopic returns a TopicHandle whose Publish method sends values on the topic. Because UseEffect is a no-op on native (non-wasm) builds, UseTopic's subscription side is also a no-op on native; use the core Subscribe/Publish functions directly in unit tests.

func (TopicHandle[T]) Publish

func (parseH TopicHandle[T]) Publish(parseValue T)

Publish delivers parseValue to all subscribers currently registered on the topic this handle was created for.

func (TopicHandle[T]) Topic

func (parseH TopicHandle[T]) Topic() string

Topic returns the topic name this handle publishes to, for logging and comparison.

type TopicInfo

type TopicInfo struct {
	// Topic is the topic string.
	Topic string `json:"topic"`
	// Subscribers is the number of handlers currently registered on the topic.
	// Note this counts handlers of every element type T; a Publish[T] only
	// delivers to handlers whose registered T matches, so a non-zero count is
	// an upper bound on who will actually receive a given typed publish.
	Subscribers int `json:"subscribers"`
}

TopicInfo describes one live pub/sub topic for agent/devtools introspection.

func Topics

func Topics() []TopicInfo

Topics returns every topic the registry currently knows about, sorted by topic string. A topic appears once it has been published to or subscribed on at least once. This is the discovery surface an agent bridge uses to learn the in-app event vocabulary instead of guessing topic names.

type TopicOption

type TopicOption struct {
	// contains filtered or unexported fields
}

TopicOption is a functional option accepted by UseTopic.

func WithReplayLast

func WithReplayLast() TopicOption

WithReplayLast returns a TopicOption that causes a new subscriber to immediately receive the most recently published value on its topic, if any value has been published since the program started.

Jump to

Keyboard shortcuts

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