Documentation
¶
Overview ¶
Package notify is a Laravel-notifications-style abstraction: a Notification declares which channels it goes out on (Via) and, per channel, its content via a small interface the channel type-asserts for (Mailable, Databasable, Broadcastable in notify/broadcast). A Hub fans a notification out to its channels.
This is a deliberate exception to "centralize, don't abstract": it is a first-party system (not a wrapper around a single third-party API), meant to make building products faster, with room to add drivers for third-party notification services later. Keep it thin — no reflection in the hot path, one type assertion per channel per send.
Sending stays synchronous. For asynchronous delivery, dispatch an event and call Hub.Send from a listener — the events package already provides at-least-once delivery and retries; notify does not duplicate that.
Index ¶
- Constants
- Variables
- func ContextWithID(ctx context.Context, id uuid.UUID) context.Context
- func IDFromContext(ctx context.Context) (uuid.UUID, bool)
- type BroadcastChannel
- type Broadcastable
- type Channel
- type Databasable
- type Delivery
- type Hub
- type MailChannel
- type Mailable
- type Memory
- type Notification
- type Recipient
- type Sender
Constants ¶
const ( ChannelMail = "mail" ChannelDatabase = "database" ChannelBroadcast = "broadcast" )
Channel names recognized by the notifications generated by the CLI and the channels the kit ships. Third-party drivers may define their own.
Variables ¶
var ErrNoContent = errors.New("notify: notification has no content for channel")
ErrNoContent is returned by a Channel when a notification's Via names it but the notification does not implement that channel's content interface (e.g. Via returns ChannelMail but the type has no ToMail method).
Functions ¶
func ContextWithID ¶
ContextWithID attaches a delivery ID to ctx. Hub.Send calls this once per Send; channels read it with IDFromContext to correlate a database row with its broadcast envelope.
Types ¶
type BroadcastChannel ¶
type BroadcastChannel struct {
// contains filtered or unexported fields
}
BroadcastChannel delivers notifications through a broadcast.Broadcaster to the recipient's own channel (broadcast.UserChannel). Its envelope carries the delivery ID Hub.Send stamped into ctx — the same ID the database channel uses as its row's primary key, when both channels are wired for a send — so a client can reconcile a live push with markAsRead against the database channel's REST surface without refetching.
func NewBroadcastChannel ¶
func NewBroadcastChannel(pub broadcast.Broadcaster, channelPrefix string) *BroadcastChannel
NewBroadcastChannel returns a Channel named ChannelBroadcast that publishes through pub, using channelPrefix to namespace per-user channels (the same prefix the realtime gateway is configured with).
func (*BroadcastChannel) Name ¶
func (c *BroadcastChannel) Name() string
Name returns ChannelBroadcast.
func (*BroadcastChannel) Send ¶
func (c *BroadcastChannel) Send(ctx context.Context, r Recipient, n Notification) error
Send requires r.ID (there is no on-demand broadcast target), builds the payload from Broadcastable or, failing that, Databasable, and publishes it to the recipient's user channel.
type Broadcastable ¶
Broadcastable is implemented by notifications that build a distinct payload for the broadcast channel (notify.BroadcastChannel). It is optional: a notification with no ToBroadcast falls back to Databasable's payload, so a notification wired for database+broadcast does not need to duplicate its payload logic.
type Channel ¶
type Channel interface {
// Name identifies the channel; it must match the values Via returns.
Name() string
Send(ctx context.Context, r Recipient, n Notification) error
}
Channel delivers one notification to one recipient. Implementations type-assert n against their own content interface and return ErrNoContent when it is absent.
type Databasable ¶
Databasable is implemented by notifications deliverable on the database channel (notify/database.Channel). The returned value is JSON-marshaled into the stored row's payload; returning the notification itself is the common case.
Databasable lives in the root package, not notify/database, so a notification's content package only needs to import notify and mail, not the database channel's driver.
type Delivery ¶
type Delivery struct {
Recipient Recipient
Notification Notification
}
Delivery is one captured send, recorded by Memory.
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub fans a notification out to its Via channels. Build it once at startup with the channels the project has wired; it is read-only and safe for concurrent use afterwards.
func NewHub ¶
NewHub registers channels by Name. NewHub panics if two channels share a name — that is a boot-time wiring mistake, not a runtime condition.
func (*Hub) Send ¶
Send stamps a delivery ID into ctx (retrievable with IDFromContext, e.g. by the database channel as its row's primary key and by the broadcast channel for its envelope), then attempts every channel n.Via names, in order. A channel name with no registered Channel is silently skipped (discard semantics, mirroring an unconfigured mail transport). Every named channel is attempted regardless of earlier failures; errors are joined, each wrapped with the channel and notification name for diagnosis.
type MailChannel ¶
type MailChannel struct {
// contains filtered or unexported fields
}
MailChannel delivers Mailable notifications through a kit mail.Mailer — the same transport add mail wires, so a project's mail-sending configuration (SMTP, discard, memory) is shared between direct mail.Mailer use and notifications.
func NewMailChannel ¶
func NewMailChannel(m mail.Mailer) *MailChannel
NewMailChannel returns a Channel named ChannelMail that sends through m.
func (*MailChannel) Send ¶
func (c *MailChannel) Send(ctx context.Context, r Recipient, n Notification) error
Send type-asserts n against Mailable, builds the message, and routes it to r when the notification did not set an explicit recipient.
type Mailable ¶
Mailable is implemented by notifications deliverable on the mail channel. If the returned message has no To recipient set, MailChannel routes it to r.Email/r.Name automatically, so ToMail only needs to build subject/body.
type Memory ¶
type Memory struct {
// contains filtered or unexported fields
}
Memory is a Channel that captures every delivery instead of sending it — for tests. Register it under whatever channel name you want to observe, e.g. NewMemory(notify.ChannelDatabase) to assert on ToDatabase-eligible sends without a real store.
func (*Memory) Deliveries ¶
Deliveries returns everything captured so far.
type Notification ¶
type Notification interface {
// NotificationName is a stable, dotted identifier (e.g.
// "contact.admin_notification"); the database channel persists it and
// spans/logs carry it. Must work on the zero value, like events.Event.
NotificationName() string
// Via lists the channel names this notification goes out on for r.
Via(ctx context.Context, r Recipient) []string
}
Notification declares content deliverable on one or more channels. Via is evaluated per send, so the same notification type may go out on different channels for different recipients.
type Recipient ¶
Recipient is who a notification goes to. ID is the recipient's stable identifier (an rbac.Identity.Subject in kit-issued JWTs); it is required by the database and broadcast channels. Email/Name route the mail channel. An on-demand send (no ID, e.g. a contact-form submitter) should only name ChannelMail in Via.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package broadcast is the publish-side of the kit's realtime channel: a small Broadcaster interface plus the per-user/topic channel naming convention, backed by an embedded centrifuge.Node publishing through a Valkey broker.
|
Package broadcast is the publish-side of the kit's realtime channel: a small Broadcaster interface plus the per-user/topic channel naming convention, backed by an embedded centrifuge.Node publishing through a Valkey broker. |
|
Package database implements the notify database channel: it persists Databasable notifications into a notifications table and exposes the minimal read side (list, unread count, mark read) a project's own HTTP domain wraps with its own DTOs and rbac guard.
|
Package database implements the notify database channel: it persists Databasable notifications into a notifications table and exposes the minimal read side (list, unread count, mark read) a project's own HTTP domain wraps with its own DTOs and rbac guard. |
|
bunx
Package bunx is the bun adapter for the notify database channel, mirroring the dbx/bunx split and events/outbox/bunx: projects that wire the bun transactor use it so notification inserts/updates join the bun transaction opened by WithinTransaction.
|
Package bunx is the bun adapter for the notify database channel, mirroring the dbx/bunx split and events/outbox/bunx: projects that wire the bun transactor use it so notification inserts/updates join the bun transaction opened by WithinTransaction. |