Documentation
¶
Index ¶
- Constants
- Variables
- func IsTransportKind(kind string) bool
- func NewLocalRoom(opts RoomOptions) *local.LocalRoom
- func NewMember(opts streaming.MemberOptions) streaming.Member
- func NewSSEConnection(stream forge.Stream, remoteAddr, localAddr string) forge.Connection
- func NewSubscription(opts streaming.SubscriptionOptions) streaming.Subscription
- func TransportKinds() []string
- type ActivityInfo
- type AnalyticsEvent
- type AnalyticsQuery
- type AnalyticsResult
- type Availability
- type BackendError
- type BinaryCodec
- type Channel
- type ChannelStore
- type Codec
- type CodecRegistry
- func (r *CodecRegistry) Decode(data []byte, msg *streaming.Message) error
- func (r *CodecRegistry) DecodeWithType(contentType string, data []byte, msg *streaming.Message) error
- func (r *CodecRegistry) Default() Codec
- func (r *CodecRegistry) Encode(msg *streaming.Message) ([]byte, error)
- func (r *CodecRegistry) EncodeWithType(contentType string, msg *streaming.Message) ([]byte, error)
- func (r *CodecRegistry) Get(contentType string) (Codec, bool)
- func (r *CodecRegistry) Register(codec Codec)
- func (r *CodecRegistry) SetDefault(contentType string) error
- type Config
- type ConfigOption
- func WithAuthentication(username, password string) ConfigOption
- func WithBackend(backend string) ConfigOption
- func WithBackendURLs(urls ...string) ConfigOption
- func WithBufferSizes(read, write int) ConfigOption
- func WithConfig(config Config) ConfigOption
- func WithConnectionLimits(perUser, roomsPerUser, channelsPerUser int) ConfigOption
- func WithFeatures(rooms, channels, presence, typing, history bool) ConfigOption
- func WithLoadBalancer(strategy string) ConfigOption
- func WithLocalBackend() ConfigOption
- func WithMessageLimits(maxSize, maxPerSecond int) ConfigOption
- func WithMessageRetention(retention time.Duration) ConfigOption
- func WithNATSBackend(urls ...string) ConfigOption
- func WithNodeID(nodeID string) ConfigOption
- func WithPresenceTimeout(timeout time.Duration) ConfigOption
- func WithRedisBackend(url string) ConfigOption
- func WithRequireConfig(require bool) ConfigOption
- func WithSessionResumption(ttl time.Duration) ConfigOption
- func WithTLS(certFile, keyFile, caFile string) ConfigOption
- func WithTimeouts(ping, pong, write time.Duration) ConfigOption
- func WithTypingTimeout(timeout time.Duration) ConfigOption
- type ConnOption
- type Connection
- type ConnectionHook
- type ConnectionInfo
- type DeviceInfo
- type DistributedBackend
- type DistributedBackendOptions
- type ErrorHook
- type Extension
- func (e *Extension) AsyncAPISpec() *forge.AsyncAPISpec
- func (e *Extension) Codecs() *CodecRegistry
- func (e *Extension) Health(ctx context.Context) error
- func (e *Extension) Hooks() *HookRegistry
- func (e *Extension) Manager() Manager
- func (e *Extension) Register(app forge.App) error
- func (e *Extension) RegisterCodec(codec Codec)
- func (e *Extension) RegisterContractContributor(disp *dispatcher.Dispatcher, reg dashcontract.Registry, ...) error
- func (e *Extension) RegisterHook(hook StreamingHook)
- func (e *Extension) RegisterRoutes(router forge.Router, wsPath, ssePath string) error
- func (e *Extension) Start(ctx context.Context) error
- func (e *Extension) Stop(ctx context.Context) error
- func (e *Extension) UnregisterHook(name string)
- type FileInfo
- type FileQuery
- type FileUpload
- type HistoryQuery
- type HookRegistry
- func (r *HookRegistry) Add(hook StreamingHook)
- func (r *HookRegistry) Close() error
- func (r *HookRegistry) DroppedDeliveries() uint64
- func (r *HookRegistry) FireOnConnect(ctx context.Context, conn streaming.EnhancedConnection) error
- func (r *HookRegistry) FireOnDisconnect(ctx context.Context, conn streaming.EnhancedConnection)
- func (r *HookRegistry) FireOnError(ctx context.Context, conn streaming.EnhancedConnection, err error)
- func (r *HookRegistry) FireOnMessageDelivered(ctx context.Context, conn streaming.EnhancedConnection, msg *streaming.Message)
- func (r *HookRegistry) FireOnMessageReceived(ctx context.Context, conn streaming.EnhancedConnection, msg *streaming.Message) (*streaming.Message, error)
- func (r *HookRegistry) FireOnPresenceChange(ctx context.Context, userID, oldStatus, newStatus string)
- func (r *HookRegistry) FireOnRawMessage(ctx context.Context, conn streaming.EnhancedConnection, data []byte) ([]byte, error)
- func (r *HookRegistry) FireOnRoomCreate(ctx context.Context, room streaming.Room) error
- func (r *HookRegistry) FireOnRoomDelete(ctx context.Context, roomID string)
- func (r *HookRegistry) FireOnRoomJoin(ctx context.Context, conn streaming.EnhancedConnection, roomID string) error
- func (r *HookRegistry) FireOnRoomLeave(ctx context.Context, conn streaming.EnhancedConnection, roomID string)
- func (r *HookRegistry) List() []StreamingHook
- func (r *HookRegistry) Remove(name string)
- type Invite
- type InviteOptions
- type JSONCodec
- type LocalRoom
- type Lock
- type Manager
- type ManagerOption
- func WithCodecRegistry(cr *CodecRegistry) ManagerOption
- func WithCoordinator(c coordinator.StreamCoordinator) ManagerOption
- func WithFilterChain(fc filters.FilterChain) ManagerOption
- func WithHookRegistry(hr *HookRegistry) ManagerOption
- func WithManagerHealthChecker(hc lb.HealthChecker) ManagerOption
- func WithManagerLoadBalancer(l lb.LoadBalancer) ManagerOption
- func WithManagerNodeID(id string) ManagerOption
- func WithMessageAuthorizer(ma streamauth.MessageAuthorizer) ManagerOption
- func WithRateLimiter(rl ratelimit.RateLimiter) ManagerOption
- func WithRoomAuthorizer(ra streamauth.RoomAuthorizer) ManagerOption
- func WithSessionStore(ss SessionStore) ManagerOption
- func WithValidator(v validation.MessageValidator) ManagerOption
- type ManagerStats
- type Member
- type MemberOptions
- type Message
- type MessageEdit
- type MessageHandler
- type MessageHook
- type MessageReaction
- type MessageSearchQuery
- type MessageStore
- type ModerationEvent
- type ModerationStatus
- type NodeChangeEvent
- type NodeChangeHandler
- type NodeInfo
- type OnlineStats
- type OutboundFrame
- type PresenceEvent
- type PresenceFilters
- type PresenceHook
- type PresenceOptions
- type PresenceStore
- type PresenceTracker
- type RateLimitStatus
- type RawMessageHook
- type Room
- type RoomBan
- type RoomEvent
- type RoomHook
- type RoomOptions
- type RoomStats
- type RoomStore
- type SendQueueStats
- type SessionSnapshot
- type SessionStore
- type StreamingHook
- type TextCodec
- type TypingOptions
- type TypingStore
- type TypingTracker
- type UserPresence
- type UserPresenceStats
- type UserStats
- type WebhookConfig
Constants ¶
const ( TransportWebSocket = "websocket" TransportSSE = "sse" )
Transport type constants.
const ( StatusOnline = internal.StatusOnline StatusAway = internal.StatusAway StatusBusy = internal.StatusBusy StatusOffline = internal.StatusOffline )
Status constants.
const ( MessageTypeMessage = internal.MessageTypeMessage MessageTypeJoin = internal.MessageTypeJoin MessageTypeLeave = internal.MessageTypeLeave MessageTypeTyping = internal.MessageTypeTyping MessageTypePresence = internal.MessageTypePresence MessageTypeSystem = internal.MessageTypeSystem MessageTypeError = internal.MessageTypeError )
Message type constants.
All seven kinds `internal` declares, deliberately: `TransportKinds` needs the complete set, and a reserved set that silently listed only some of them would be wrong in the direction that matters. A kind missing from it is a frame the client reports as an unknown message forever. Exporting them all also spares a caller outside this package spelling any value of `Message.Type` as a string literal.
const ( ContentTypeJSON = internal.ContentTypeJSON ContentTypeBinary = internal.ContentTypeBinary ContentTypeText = internal.ContentTypeText ContentTypeMsgPack = internal.ContentTypeMsgPack ContentTypeProtobuf = internal.ContentTypeProtobuf )
Content type constants.
const ( // DefaultSendQueueCapacity is the number of frames a connection may have // queued for the socket before the overflow policy kicks in. DefaultSendQueueCapacity = 256 // DefaultSendQueueFlushTimeout bounds how long Close waits for already // queued frames to reach the socket before the connection is torn down. // It exists so a final frame (a kick notice, a close reason) is not // discarded, without letting one slow consumer stall connection cleanup. DefaultSendQueueFlushTimeout = time.Second // DefaultSendQueueExitTimeout bounds how long Close waits for the writer // goroutine to exit after the underlying connection has been closed. DefaultSendQueueExitTimeout = 5 * time.Second )
Send queue defaults.
const LifecycleMetadataKey = "lifecycle"
LifecycleMetadataKey is the Metadata key under which NewLifecycleMessage records the lifecycle name.
Exported so neither the client nor any other consumer has to match a bare string literal against an envelope this package owns. A literal spelled in three places is a rename away from a reader that silently finds nothing, and a lifecycle frame whose name cannot be read is indistinguishable from one that never carried a name at all.
const (
// ManagerKey is the DI key for the streaming manager.
ManagerKey = "streaming"
)
DI container keys for streaming extension services.
Variables ¶
var ( ErrInvalidConfig = internal.ErrInvalidConfig ErrBackendNotFound = internal.ErrBackendNotConnected ErrBackendTimeout = internal.ErrBackendTimeout ErrLockAcquisitionFailed = internal.ErrLockAcquisitionFailed ErrLockNotHeld = internal.ErrLockNotHeld ErrNodeNotFound = internal.ErrNodeNotFound ErrInvalidChannel = internal.ErrInvalidChannel ErrInvalidRoom = internal.ErrInvalidRoom ErrInvalidMessage = internal.ErrInvalidMessage ErrPresenceNotFound = internal.ErrPresenceNotFound ErrInvalidStatus = internal.ErrInvalidStatus ErrInvalidPermission = internal.ErrInvalidPermission ErrInsufficientRole = internal.ErrInsufficientRole ErrMessageTooLarge = internal.ErrMessageTooLarge ErrMessageNotFound = internal.ErrMessageNotFound )
Backend, validation and lookup errors.
var ( ErrConnectionNotFound = internal.ErrConnectionNotFound ErrConnectionClosed = internal.ErrConnectionClosed ErrConnectionLimitReached = internal.ErrConnectionLimitReached ErrInvalidConnection = internal.ErrInvalidConnection )
Connection errors.
var ( ErrRoomNotFound = internal.ErrRoomNotFound ErrRoomAlreadyExists = internal.ErrRoomAlreadyExists ErrRoomFull = internal.ErrRoomFull ErrNotRoomMember = internal.ErrNotRoomMember ErrAlreadyRoomMember = internal.ErrAlreadyRoomMember ErrRoomLimitReached = internal.ErrRoomLimitReached )
Room errors.
var ( ErrChannelNotFound = internal.ErrChannelNotFound ErrChannelAlreadyExists = internal.ErrChannelAlreadyExists ErrNotSubscribed = internal.ErrNotSubscribed ErrAlreadySubscribed = internal.ErrAlreadySubscribed ErrChannelLimitReached = internal.ErrChannelLimitReached )
Channel errors.
var ( ErrRoomAccessDenied = internal.ErrRoomAccessDenied ErrChannelAccessDenied = internal.ErrChannelAccessDenied ErrSendDenied = internal.ErrSendDenied ErrUserMuted = internal.ErrUserMuted ErrUserBanned = internal.ErrUserBanned ErrSessionNotOwned = internal.ErrSessionNotOwned )
Authorization errors.
var ( ErrRoomsDisabled = internal.ErrRoomsDisabled ErrChannelsDisabled = internal.ErrChannelsDisabled ErrHistoryDisabled = internal.ErrHistoryDisabled ErrPresenceDisabled = internal.ErrPresenceDisabled ErrTypingDisabled = internal.ErrTypingDisabled )
Feature-flag errors.
var ( ErrInviteNotFound = internal.ErrInviteNotFound ErrInviteExpired = internal.ErrInviteExpired )
Invite errors.
var ( NewConnectionError = internal.NewConnectionError NewRoomError = internal.NewRoomError NewChannelError = internal.NewChannelError NewMessageError = internal.NewMessageError NewBackendError = internal.NewBackendError )
Error constructors.
var ( DefaultPresenceOptions = internal.DefaultPresenceOptions DefaultTypingOptions = internal.DefaultTypingOptions )
Default option functions.
var ( // ErrSendQueueClosed is returned when a frame is enqueued on a connection // whose writer has already stopped. ErrSendQueueClosed = errors.New("streaming: connection send queue closed") // ErrSendQueueOverflow is returned when a non-droppable frame arrives on a // full queue. The connection is disconnected; the client is expected to // reconnect and resynchronise. ErrSendQueueOverflow = errors.New("streaming: connection send queue overflow") )
Send queue errors.
var ErrPermissionDenied = internal.ErrPermissionDenied
ErrPermissionDenied is returned when the caller may not perform an action.
var ErrRateLimitExceeded = internal.ErrRateLimitExceeded
ErrRateLimitExceeded is returned when a connection exceeds its rate limit.
Functions ¶
func IsTransportKind ¶ added in v1.9.5
IsTransportKind reports whether kind is one of the reserved transport kinds.
Useful to a producer choosing an Event name: a domain event that collides with a reserved kind is unbindable on the client, because a frame naming it cannot be told apart from the transport frame of the same name.
func NewLocalRoom ¶
func NewLocalRoom(opts RoomOptions) *local.LocalRoom
NewLocalRoom creates a room backed by the in-process local store.
func NewMember ¶ added in v1.9.5
func NewMember(opts streaming.MemberOptions) streaming.Member
NewMember creates a Member suitable for handing to any RoomStore.
func NewSSEConnection ¶ added in v0.10.0
func NewSSEConnection(stream forge.Stream, remoteAddr, localAddr string) forge.Connection
NewSSEConnection creates a new SSE connection adapter.
func NewSubscription ¶ added in v1.9.5
func NewSubscription(opts streaming.SubscriptionOptions) streaming.Subscription
NewSubscription creates a Subscription suitable for handing to any ChannelStore.
func TransportKinds ¶ added in v1.9.5
func TransportKinds() []string
TransportKinds returns the reserved values of Message.Type, in the order they are declared.
Mirrored by TRANSPORT_KINDS in packages/client-core/src/streaming.ts, which drops a frame whose name resolves to one of these through the Type fallback. The mirror is asserted in frame_test.go rather than trusted: an eighth kind added to this package and not to that set would arrive at the client as a frame name no binding can claim, and be reported as an unknown message on every channel for as long as it existed.
A fresh slice per call, so a caller ranging over it cannot reorder the set every other consumer reads.
Types ¶
type BackendError ¶
type BackendError = internal.BackendError
BackendError wraps backend-related errors with context.
type BinaryCodec ¶ added in v1.3.0
type BinaryCodec struct{}
BinaryCodec handles raw binary data. On decode, it stores the raw bytes in msg.RawData and sets the content type. On encode, it returns msg.RawData directly.
func (*BinaryCodec) ContentType ¶ added in v1.3.0
func (c *BinaryCodec) ContentType() string
type Codec ¶ added in v1.3.0
type Codec interface {
// ContentType returns the MIME type this codec handles (e.g. "application/json").
ContentType() string
// Encode serializes a message to bytes.
Encode(msg *streaming.Message) ([]byte, error)
// Decode deserializes bytes into a message.
Decode(data []byte, msg *streaming.Message) error
}
Codec handles encoding and decoding messages for a specific content type.
type CodecRegistry ¶ added in v1.3.0
type CodecRegistry struct {
// contains filtered or unexported fields
}
CodecRegistry manages codecs by content type and provides encode/decode dispatch based on message content type.
func NewCodecRegistry ¶ added in v1.3.0
func NewCodecRegistry() *CodecRegistry
NewCodecRegistry creates a new codec registry pre-loaded with a JSON codec as default.
func (*CodecRegistry) Decode ¶ added in v1.3.0
func (r *CodecRegistry) Decode(data []byte, msg *streaming.Message) error
Decode deserializes bytes into a message using the default codec. If decoding fails with the default codec, it returns the error.
func (*CodecRegistry) DecodeWithType ¶ added in v1.3.0
func (r *CodecRegistry) DecodeWithType(contentType string, data []byte, msg *streaming.Message) error
DecodeWithType deserializes bytes into a message using the codec for the given content type.
func (*CodecRegistry) Default ¶ added in v1.3.0
func (r *CodecRegistry) Default() Codec
Default returns the default codec (JSON).
func (*CodecRegistry) Encode ¶ added in v1.3.0
func (r *CodecRegistry) Encode(msg *streaming.Message) ([]byte, error)
Encode serializes a message using the codec matching msg.ContentType, or the default codec if ContentType is empty.
Callers that resolve a content type from somewhere other than the message — notably a connection's SetContentType preference — must use EncodeWithType instead, or the resolved type is discarded here.
func (*CodecRegistry) EncodeWithType ¶ added in v1.9.5
EncodeWithType serializes a message using the codec for the given content type, falling back to the default codec when contentType is empty. It mirrors DecodeWithType and lets the caller supply a type resolved from the connection rather than from the message itself.
func (*CodecRegistry) Get ¶ added in v1.3.0
func (r *CodecRegistry) Get(contentType string) (Codec, bool)
Get returns the codec for the given content type.
func (*CodecRegistry) Register ¶ added in v1.3.0
func (r *CodecRegistry) Register(codec Codec)
Register adds a codec. If a codec for the same content type already exists, it is replaced.
func (*CodecRegistry) SetDefault ¶ added in v1.3.0
func (r *CodecRegistry) SetDefault(contentType string) error
SetDefault changes the default codec to the one registered for the given content type.
type ConfigOption ¶
type ConfigOption = internal.ConfigOption
Configuration.
func WithAuthentication ¶
func WithAuthentication(username, password string) ConfigOption
func WithBackend ¶
func WithBackend(backend string) ConfigOption
func WithBackendURLs ¶
func WithBackendURLs(urls ...string) ConfigOption
func WithBufferSizes ¶
func WithBufferSizes(read, write int) ConfigOption
func WithConfig ¶
func WithConfig(config Config) ConfigOption
func WithConnectionLimits ¶
func WithConnectionLimits(perUser, roomsPerUser, channelsPerUser int) ConfigOption
func WithFeatures ¶
func WithFeatures(rooms, channels, presence, typing, history bool) ConfigOption
func WithLoadBalancer ¶ added in v1.9.5
func WithLoadBalancer(strategy string) ConfigOption
func WithLocalBackend ¶
func WithLocalBackend() ConfigOption
func WithMessageLimits ¶
func WithMessageLimits(maxSize, maxPerSecond int) ConfigOption
func WithMessageRetention ¶
func WithMessageRetention(retention time.Duration) ConfigOption
func WithNATSBackend ¶
func WithNATSBackend(urls ...string) ConfigOption
func WithNodeID ¶
func WithNodeID(nodeID string) ConfigOption
func WithPresenceTimeout ¶
func WithPresenceTimeout(timeout time.Duration) ConfigOption
func WithRedisBackend ¶
func WithRedisBackend(url string) ConfigOption
func WithRequireConfig ¶
func WithRequireConfig(require bool) ConfigOption
func WithSessionResumption ¶ added in v1.9.5
func WithSessionResumption(ttl time.Duration) ConfigOption
func WithTLS ¶
func WithTLS(certFile, keyFile, caFile string) ConfigOption
func WithTimeouts ¶
func WithTimeouts(ping, pong, write time.Duration) ConfigOption
func WithTypingTimeout ¶
func WithTypingTimeout(timeout time.Duration) ConfigOption
type ConnOption ¶ added in v1.9.5
type ConnOption func(*connOptions)
ConnOption configures an enhanced connection.
func WithConnectionLogger ¶ added in v1.9.5
func WithConnectionLogger(logger forge.Logger) ConnOption
WithConnectionLogger gives the connection a logger for writer-goroutine teardown reasons.
func WithConnectionMetrics ¶ added in v1.9.5
func WithConnectionMetrics(metrics forge.Metrics) ConnOption
WithConnectionMetrics gives the connection a metrics handle for send queue depth, drops, and overflow disconnects. Without one the same numbers remain readable through SendQueueStats.
func WithSendQueueCapacity ¶ added in v1.9.5
func WithSendQueueCapacity(capacity int) ConnOption
WithSendQueueCapacity sets how many frames may be queued for the socket before the per-message-type overflow policy applies. Zero or negative selects DefaultSendQueueCapacity.
func WithSendQueueTimeouts ¶ added in v1.9.5
func WithSendQueueTimeouts(flush, exit time.Duration) ConnOption
WithSendQueueTimeouts bounds connection teardown: flush is how long Close waits for queued frames to reach the socket, exit is how long it then waits for the writer goroutine to unwind after the socket has been closed.
type Connection ¶
type Connection = internal.EnhancedConnection
Core interfaces.
func NewConnection ¶
func NewConnection(conn forge.Connection, opts ...ConnOption) Connection
NewConnection creates a new enhanced connection with default transport "websocket".
func NewConnectionWithTransport ¶ added in v1.3.0
func NewConnectionWithTransport(conn forge.Connection, transport string, opts ...ConnOption) Connection
NewConnectionWithTransport creates a new enhanced connection with a specified transport type.
type ConnectionHook ¶ added in v1.3.0
type ConnectionHook interface {
StreamingHook
// OnConnect is called before registration. Return error to reject connection.
OnConnect(ctx context.Context, conn Connection) error
// OnDisconnect is called after unregistration.
OnDisconnect(ctx context.Context, conn Connection)
}
ConnectionHook fires on connection lifecycle events.
type ConnectionInfo ¶
type ConnectionInfo = internal.ConnectionInfo
ConnectionInfo provides detailed information about a connection.
type DistributedBackend ¶
type DistributedBackend = internal.DistributedBackend
Distributed backend.
type DistributedBackendOptions ¶
type DistributedBackendOptions = internal.DistributedBackendOptions
Configuration.
type ErrorHook ¶ added in v1.3.0
type ErrorHook interface {
StreamingHook
// OnError is called when a message handling error occurs.
OnError(ctx context.Context, conn Connection, err error)
}
ErrorHook fires on message handling errors.
type Extension ¶
type Extension struct {
*forge.BaseExtension
// contains filtered or unexported fields
}
Extension implements forge.Extension for streaming functionality.
func NewExtension ¶
func NewExtension(opts ...ConfigOption) *Extension
NewExtension creates a new streaming extension with functional options.
Returns the concrete *Extension rather than forge.Extension because the type carries surface the interface does not — RegisterRoutes, RegisterHook, RegisterCodec, Manager — and every one of those is needed in ordinary use. Returning the interface made the documented quick-start unable to compile.
This widens the return type rather than narrowing it: *Extension satisfies forge.Extension, so `var e forge.Extension = streaming.NewExtension(...)` still works.
func NewExtensionWithConfig ¶
NewExtensionWithConfig creates a new streaming extension with a complete config.
func (*Extension) AsyncAPISpec ¶
func (e *Extension) AsyncAPISpec() *forge.AsyncAPISpec
AsyncAPISpec generates AsyncAPI 3.0.0 specification for the streaming extension This documents all streaming channels, operations, and message types.
func (*Extension) Codecs ¶ added in v1.3.0
func (e *Extension) Codecs() *CodecRegistry
Codecs returns the codec registry for direct access.
func (*Extension) Hooks ¶ added in v1.3.0
func (e *Extension) Hooks() *HookRegistry
Hooks returns the hook registry for direct access.
func (*Extension) RegisterCodec ¶ added in v1.3.0
RegisterCodec adds a message codec for a specific content type.
func (*Extension) RegisterContractContributor ¶ added in v1.6.4
func (e *Extension) RegisterContractContributor( disp *dispatcher.Dispatcher, reg dashcontract.Registry, wreg dashcontract.WardenRegistry, ) error
RegisterContractContributor implements dashboard.ContractContributorAware. Wires the streaming contributor handlers into the dashboard's contract dispatcher and registers the embedded YAML manifest, serving /dashboard/contract/streaming/*. This is the only dashboard surface streaming exposes: the server-rendered contributor that used to sit beside it on /dashboard/ext/streaming/* is gone, and the UI is now the @forge-go/dashboard-plugin-streaming React package, which joins to this contributor by its manifest name.
func (*Extension) RegisterHook ¶ added in v1.3.0
func (e *Extension) RegisterHook(hook StreamingHook)
RegisterHook adds a streaming hook for lifecycle events. Hooks can implement one or more hook interfaces (ConnectionHook, MessageHook, RawMessageHook, RoomHook, PresenceHook, ErrorHook).
func (*Extension) RegisterRoutes ¶
RegisterRoutes is a helper to register WebSocket and SSE routes with the router.
func (*Extension) Stop ¶
Stop stops the streaming extension.
Connections are drained before the manager is torn down. Drain was implemented but never called from anywhere, so shutdown dropped every live socket without notice — clients saw an abrupt close and could not tell a deploy from a crash, which matters because the two deserve different reconnect behaviour.
func (*Extension) UnregisterHook ¶ added in v1.3.0
UnregisterHook removes a streaming hook by name.
type HookRegistry ¶ added in v1.3.0
type HookRegistry struct {
// contains filtered or unexported fields
}
HookRegistry manages streaming hooks and dispatches events.
Hooks fire in registration order. Several of the fire methods are a pipeline — FireOnMessageReceived threads the message through each hook in turn, and FireOnConnect stops at the first rejection — so the order has to be defined and stable, not whatever a map range happens to produce.
func NewHookRegistry ¶ added in v1.3.0
func NewHookRegistry() *HookRegistry
NewHookRegistry creates a new hook registry.
func (*HookRegistry) Add ¶ added in v1.3.0
func (r *HookRegistry) Add(hook StreamingHook)
Add registers a hook. The hook is type-asserted to categorize it into the appropriate dispatch lists.
Registering a name that already exists replaces the hook in place, keeping its original position in the dispatch order.
func (*HookRegistry) Close ¶ added in v1.9.5
func (r *HookRegistry) Close() error
Close shuts down the delivery worker pool and waits for in-flight delivery hooks to finish. It is safe to call concurrently and more than once; calls after the first are no-ops. Deliveries fired after Close are dropped.
func (*HookRegistry) DroppedDeliveries ¶ added in v1.9.5
func (r *HookRegistry) DroppedDeliveries() uint64
DroppedDeliveries returns the number of delivery-hook batches that were never run, either because the pool's queue was saturated or because the registry was already closed. Delivery hooks are non-blocking by contract, so overload is shed here rather than pushed back onto the delivery path — this counter is how that shedding becomes visible. A steadily climbing value means hooks are too slow for the message rate, and any hook doing billing or audit work is losing events.
func (*HookRegistry) FireOnConnect ¶ added in v1.3.0
func (r *HookRegistry) FireOnConnect(ctx context.Context, conn streaming.EnhancedConnection) error
FireOnConnect fires ConnectionHook.OnConnect for all registered hooks. Returns the first error encountered, which should be used to reject the connection.
func (*HookRegistry) FireOnDisconnect ¶ added in v1.3.0
func (r *HookRegistry) FireOnDisconnect(ctx context.Context, conn streaming.EnhancedConnection)
FireOnDisconnect fires ConnectionHook.OnDisconnect for all registered hooks. Errors are ignored (post-hook).
func (*HookRegistry) FireOnError ¶ added in v1.3.0
func (r *HookRegistry) FireOnError(ctx context.Context, conn streaming.EnhancedConnection, err error)
FireOnError fires ErrorHook.OnError for all hooks (post-hook).
func (*HookRegistry) FireOnMessageDelivered ¶ added in v1.3.0
func (r *HookRegistry) FireOnMessageDelivered(ctx context.Context, conn streaming.EnhancedConnection, msg *streaming.Message)
FireOnMessageDelivered fires MessageHook.OnMessageDelivered on a bounded worker pool. This is non-blocking to avoid slowing down message delivery: the call is made once per recipient, so a broadcast to a large room reaches this path thousands of times for a single message.
If the pool's queue is saturated the batch is dropped rather than queued or awaited, because blocking here would stall delivery itself.
func (*HookRegistry) FireOnMessageReceived ¶ added in v1.3.0
func (r *HookRegistry) FireOnMessageReceived(ctx context.Context, conn streaming.EnhancedConnection, msg *streaming.Message) (*streaming.Message, error)
FireOnMessageReceived fires MessageHook.OnMessageReceived for all hooks in sequence. Each hook can transform or block (return nil) the message.
func (*HookRegistry) FireOnPresenceChange ¶ added in v1.3.0
func (r *HookRegistry) FireOnPresenceChange(ctx context.Context, userID, oldStatus, newStatus string)
FireOnPresenceChange fires PresenceHook.OnPresenceChange for all hooks (post-hook).
func (*HookRegistry) FireOnRawMessage ¶ added in v1.3.0
func (r *HookRegistry) FireOnRawMessage(ctx context.Context, conn streaming.EnhancedConnection, data []byte) ([]byte, error)
FireOnRawMessage fires RawMessageHook.OnRawMessage for all hooks in sequence. Each hook can transform the bytes or block (return error) the message.
func (*HookRegistry) FireOnRoomCreate ¶ added in v1.3.0
FireOnRoomCreate fires RoomHook.OnRoomCreate for all hooks. Returns the first error encountered, which should be used to reject creation.
func (*HookRegistry) FireOnRoomDelete ¶ added in v1.3.0
func (r *HookRegistry) FireOnRoomDelete(ctx context.Context, roomID string)
FireOnRoomDelete fires RoomHook.OnRoomDelete for all hooks (post-hook).
func (*HookRegistry) FireOnRoomJoin ¶ added in v1.3.0
func (r *HookRegistry) FireOnRoomJoin(ctx context.Context, conn streaming.EnhancedConnection, roomID string) error
FireOnRoomJoin fires RoomHook.OnRoomJoin for all hooks. Returns the first error encountered, which should be used to reject the join.
func (*HookRegistry) FireOnRoomLeave ¶ added in v1.3.0
func (r *HookRegistry) FireOnRoomLeave(ctx context.Context, conn streaming.EnhancedConnection, roomID string)
FireOnRoomLeave fires RoomHook.OnRoomLeave for all hooks (post-hook).
func (*HookRegistry) List ¶ added in v1.3.0
func (r *HookRegistry) List() []StreamingHook
List returns all registered hooks in registration order.
func (*HookRegistry) Remove ¶ added in v1.3.0
func (r *HookRegistry) Remove(name string)
Remove unregisters a hook by name.
type JSONCodec ¶ added in v1.3.0
type JSONCodec struct{}
JSONCodec encodes/decodes messages as JSON. This is the default codec.
func (*JSONCodec) ContentType ¶ added in v1.3.0
type Manager ¶
Core interfaces.
func GetManager ¶
GetManager retrieves the streaming Manager from the container. Returns error if not found or type assertion fails.
func GetManagerFromApp ¶
GetManagerFromApp retrieves the streaming Manager from the app. Returns error if not found or type assertion fails.
func MustGetManager ¶
MustGetManager retrieves the streaming Manager from the container. Panics if not found or type assertion fails.
func MustGetManagerFromApp ¶
MustGetManagerFromApp retrieves the streaming Manager from the app. Panics if not found or type assertion fails.
func NewManager ¶
func NewManager( config Config, roomStore RoomStore, channelStore ChannelStore, messageStore MessageStore, presenceTracker PresenceTracker, typingTracker TypingTracker, distributed DistributedBackend, logger forge.Logger, metrics forge.Metrics, opts ...ManagerOption, ) Manager
NewManager creates a new streaming manager.
type ManagerOption ¶ added in v0.10.0
type ManagerOption func(*manager)
ManagerOption configures the manager.
func WithCodecRegistry ¶ added in v1.3.0
func WithCodecRegistry(cr *CodecRegistry) ManagerOption
WithCodecRegistry sets the codec registry for message encoding/decoding.
func WithCoordinator ¶ added in v0.10.0
func WithCoordinator(c coordinator.StreamCoordinator) ManagerOption
WithCoordinator sets the distributed coordinator.
func WithFilterChain ¶ added in v0.10.0
func WithFilterChain(fc filters.FilterChain) ManagerOption
WithFilterChain sets the message filter chain.
func WithHookRegistry ¶ added in v1.3.0
func WithHookRegistry(hr *HookRegistry) ManagerOption
WithHookRegistry sets the hook registry for lifecycle hooks.
func WithManagerHealthChecker ¶ added in v0.10.0
func WithManagerHealthChecker(hc lb.HealthChecker) ManagerOption
WithManagerHealthChecker sets the health checker.
func WithManagerLoadBalancer ¶ added in v0.10.0
func WithManagerLoadBalancer(l lb.LoadBalancer) ManagerOption
WithManagerLoadBalancer sets the load balancer.
func WithManagerNodeID ¶ added in v0.10.0
func WithManagerNodeID(id string) ManagerOption
WithManagerNodeID sets the node ID for distributed mode.
func WithMessageAuthorizer ¶ added in v1.9.5
func WithMessageAuthorizer(ma streamauth.MessageAuthorizer) ManagerOption
WithMessageAuthorizer sets the message authorizer. Sends, edits, deletes and reactions are checked against it.
func WithRateLimiter ¶ added in v0.10.0
func WithRateLimiter(rl ratelimit.RateLimiter) ManagerOption
WithRateLimiter sets the rate limiter.
func WithRoomAuthorizer ¶ added in v1.9.5
func WithRoomAuthorizer(ra streamauth.RoomAuthorizer) ManagerOption
WithRoomAuthorizer sets the room authorizer. Join, leave and room-targeted sends are checked against it.
func WithSessionStore ¶ added in v0.10.0
func WithSessionStore(ss SessionStore) ManagerOption
WithSessionStore sets the session store for session resumption.
func WithValidator ¶ added in v0.10.0
func WithValidator(v validation.MessageValidator) ManagerOption
WithValidator sets the message validator.
type ManagerStats ¶
type ManagerStats = internal.ManagerStats
Room creation (local backend) and statistics types.
type Message ¶
Message types.
func NewEventMessage ¶ added in v1.9.5
NewEventMessage builds a frame carrying a domain event, named so the generated client can bind it.
Type is MessageTypeMessage and Event is the domain name -- never the reverse, which is the mistake this constructor exists to make unavailable. Callers wanting a different transport kind may set Type afterwards; the client honours Event regardless of the kind it rides on, which is what makes the existing system-kind events in manager.go (message.deleted, message.edited) bindable without being reclassified.
ID, UserID and the routing fields are deliberately left alone. They are the producer's, and identity in particular has semantics -- deduplication, history -- that a wire-contract helper has no business inventing. Timestamp is set because a frame without one marshals as the zero time, which is a wrong answer rather than an absent one.
An event whose name collides with a reserved transport kind is accepted and is a mistake: the client will look for a binding named "presence" and find none. It is not rejected here because the failure is visible -- an event name always takes the client's event branch, so the frame is reported rather than dropped -- and because a constructor that can fail is a worse trade than a caller running IsTransportKind when the name is not a literal.
func NewLifecycleMessage ¶ added in v1.9.5
NewLifecycleMessage builds a transport frame announcing a connection lifecycle moment -- a ping, a kick, a shutdown -- named in Metadata rather than in Event.
Event is left empty, and that is the entire point of this constructor. Event is the binding key: it is what the generated client's manifest is keyed on, one row per AsyncAPI domain message, and what forgeStreamingDecoder reads first. A lifecycle name sitting in Event is therefore a name no manifest binds, and worse, it is non-empty, so the decoder takes its event branch and never reaches the reserved-kind filter that exists to drop transport frames quietly. The frame is passed through to the runtime, no slot matches it, and it surfaces through onUnknown as an unknown message on a channel that is working exactly as designed. For a heartbeat firing on a ticker for every connection that is a permanent recurring false signal -- into an application's own onUnknown, which is typically wired to metrics or Sentry.
The alternatives were both worse. Leaving the name in Event costs every heartbeat a spurious unknown-message report and cannot be fixed on the decoder side, because a lifecycle name and a genuine system-kind domain event (message.deleted, message.edited in manager.go) have the same shape on the wire and no decoder-side rule can separate them. Dropping the name entirely would make the frames clean but would throw away information a client may legitimately want -- a client that wants to show "you were removed by a moderator" needs to know a kicked frame from an idle_cleanup one. Metadata carries the name where a curious client can still read it, while the frame stays a pure transport frame by shape: it sets only Type, so the client drops it silently, which is the correct outcome for a frame no manifest was ever going to bind.
This is a deliberate wire change. A consumer previously reading event: "kicked" must now read metadata.lifecycle.
ID, UserID, Data and the routing fields are left to the caller for the same reason NewEventMessage leaves them: they are the producer's, and identity in particular carries deduplication and history semantics a wire-contract helper has no business inventing. Timestamp is set because a frame without one marshals as the zero time, which is a wrong answer rather than an absent one.
type MessageHook ¶ added in v1.3.0
type MessageHook interface {
StreamingHook
// OnMessageReceived is called before message processing. Can transform or block (return nil).
OnMessageReceived(ctx context.Context, conn Connection, msg *Message) (*Message, error)
// OnMessageDelivered is called after delivery (non-blocking, runs async).
OnMessageDelivered(ctx context.Context, conn Connection, msg *Message)
}
MessageHook fires on message events.
type ModerationStatus ¶
type ModerationStatus = internal.ModerationStatus
ModerationStatus represents the status of a moderation action.
type OnlineStats ¶
type OnlineStats = internal.OnlineStats
Room creation (local backend) and statistics types.
type OutboundFrame ¶ added in v1.9.5
type OutboundFrame struct {
// Data is the encoded payload. The queue retains it until it is written, so
// callers must not reuse the slice.
Data []byte
// Type is the streaming message type, one of the MessageType* constants. It
// selects the overflow policy; empty means unknown, which is treated
// conservatively.
Type string
// Binary requests a binary frame. A text frame promises valid UTF-8, and a
// browser fails the connection with close code 1007 when that is broken.
Binary bool
}
OutboundFrame is a pre-encoded frame plus the two things its bytes no longer carry: the streaming message type it was encoded from, and whether the wire needs a binary frame rather than a text one.
type PresenceHook ¶ added in v1.3.0
type PresenceHook interface {
StreamingHook
// OnPresenceChange is called after a user's presence status changes.
OnPresenceChange(ctx context.Context, userID, oldStatus, newStatus string)
}
PresenceHook fires on presence changes.
type RateLimitStatus ¶
type RateLimitStatus = internal.RateLimitStatus
RateLimitStatus represents rate limiting status for a user.
type RawMessageHook ¶ added in v1.3.0
type RawMessageHook interface {
StreamingHook
// OnRawMessage processes raw bytes before decoding. Return error to drop the message.
OnRawMessage(ctx context.Context, conn Connection, data []byte) ([]byte, error)
}
RawMessageHook fires before deserialization on raw bytes from the connection.
type RoomHook ¶ added in v1.3.0
type RoomHook interface {
StreamingHook
// OnRoomJoin is called before join. Return error to reject.
OnRoomJoin(ctx context.Context, conn Connection, roomID string) error
// OnRoomLeave is called after leave.
OnRoomLeave(ctx context.Context, conn Connection, roomID string)
// OnRoomCreate is called before room creation. Return error to reject.
OnRoomCreate(ctx context.Context, room Room) error
// OnRoomDelete is called after room deletion.
OnRoomDelete(ctx context.Context, roomID string)
}
RoomHook fires on room lifecycle events.
type SendQueueStats ¶ added in v1.9.5
type SendQueueStats struct {
Capacity int
Depth int
Enqueued uint64
Written uint64
Dropped uint64
OverflowDisconnects uint64
WriteErrors uint64
Closed bool
}
SendQueueStats is a snapshot of one connection's outbound queue. The manager can read it off a connection with a type assertion:
if s, ok := conn.(interface{ SendQueueStats() streaming.SendQueueStats }); ok {
depth := s.SendQueueStats().Depth
}
type SessionSnapshot ¶ added in v0.10.0
type SessionSnapshot struct {
SessionID string `json:"session_id"`
UserID string `json:"user_id"`
Rooms []string `json:"rooms"`
Channels []string `json:"channels"`
Metadata map[string]string `json:"metadata,omitempty"`
DisconnectedAt time.Time `json:"disconnected_at"`
// LastEventIDs is the position each channel had reached when the session
// dropped, so a resumption can ask for the gap instead of resynchronising.
LastEventIDs map[string]string `json:"last_event_ids,omitempty"`
}
SessionSnapshot captures the state of a connection for resumption.
type SessionStore ¶ added in v0.10.0
type SessionStore interface {
// Save stores a session snapshot with a TTL.
Save(ctx context.Context, snapshot *SessionSnapshot, ttl time.Duration) error
// Get retrieves a session snapshot by session ID.
Get(ctx context.Context, sessionID string) (*SessionSnapshot, error)
// Delete removes a session snapshot.
Delete(ctx context.Context, sessionID string) error
// Close releases any background resources held by the store. It must be
// safe to call more than once.
Close() error
}
SessionStore stores session snapshots for resumption.
func NewInMemorySessionStore ¶ added in v0.10.0
func NewInMemorySessionStore() SessionStore
NewInMemorySessionStore creates an in-memory session store.
type StreamingHook ¶ added in v1.3.0
type StreamingHook interface {
Name() string
}
StreamingHook is the base interface for all streaming hooks. Hooks implement one or more of the optional hook interfaces below.
type TextCodec ¶ added in v1.3.0
type TextCodec struct{}
TextCodec handles plain text data. On decode, it stores the text in msg.Data as a string. On encode, it converts msg.Data to a string and returns bytes.
func (*TextCodec) ContentType ¶ added in v1.3.0
type TypingOptions ¶
type TypingOptions = internal.TypingOptions
TypingOptions contains configuration for typing indicators.
type UserPresenceStats ¶
type UserPresenceStats = internal.UserPresenceStats
Room creation (local backend) and statistics types.
type WebhookConfig ¶
type WebhookConfig = internal.WebhookConfig
WebhookConfig represents webhook configuration.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
storetest
Package storetest holds the behavioural contract every MessageStore backend must satisfy, so the backends can be proven to agree rather than merely to each work.
|
Package storetest holds the behavioural contract every MessageStore backend must satisfy, so the backends can be proven to agree rather than merely to each work. |
|
Package contract is the streaming extension's dashboard contract contributor.
|
Package contract is the streaming extension's dashboard contract contributor. |
|
examples
|
|
|
chat
command
|
|