@roostjs/broadcast
v0.2.0
Published
Real-time broadcasting via Durable Objects and WebSocket hibernation.
Readme
@roostjs/broadcast
Real-time broadcasting via Durable Objects and WebSocket hibernation.
Part of Roost — the Laravel of Cloudflare Workers.
Installation
bun add @roostjs/broadcastQuick Start
// Server: define a broadcastable event
import { Event } from '@roostjs/events';
import { Channel, PrivateChannel } from '@roostjs/broadcast';
import type { BroadcastableEvent } from '@roostjs/broadcast';
class OrderShipped extends Event implements BroadcastableEvent {
constructor(public orderId: string, public userId: string) { super(); }
broadcastOn() {
return [new PrivateChannel(`orders.${this.userId}`)];
}
broadcastWith() {
return { orderId: this.orderId };
}
}
// Dispatching the event auto-broadcasts when @roostjs/broadcast is registered
await OrderShipped.dispatch(new OrderShipped('ord_1', 'usr_42'));
// Client (browser/edge)
import { createBroadcastClient } from '@roostjs/broadcast/client';
const client = createBroadcastClient('/ws/channel');
const unsub = client.subscribe('orders.usr_42', (event, data) => {
console.log(event, data);
});Features
Channel,PrivateChannel,PresenceChannel— same API as Laravel BroadcastingChannelDODurable Object with WebSocket hibernation — zero cost at idle- Private and presence channel authorization hooks built in
- Presence member tracking:
presence:join/presence:leaveevents - Whisper support for client-to-client ephemeral messages
createBroadcastClientwith exponential backoff auto-reconnectBroadcastManager.fake()/assertBroadcast()/assertBroadcastOn()for testing
Setup
Add the Durable Object binding to wrangler.jsonc and export ChannelDO:
// worker.ts
export { ChannelDO } from '@roostjs/broadcast';Register the provider in your app bootstrap:
import { BroadcastServiceProvider } from '@roostjs/broadcast';
app.register(BroadcastServiceProvider);The provider expects a Durable Object binding named BROADCAST_DO by default.
API
// Channels
class Channel { constructor(name: string) }
class PrivateChannel extends Channel {}
class PresenceChannel extends Channel {}
// Event contract
interface BroadcastableEvent {
broadcastOn(): Channel[]
broadcastWith(): Record<string, unknown>
broadcastAs?(): string
}
// Manager
class BroadcastManager {
static get(): BroadcastManager
static fake(): void
static restore(): void
static assertBroadcast(eventClass: new (...args) => BroadcastableEvent): void
static assertBroadcastOn(channel: string): void
broadcast(event: BroadcastableEvent): Promise<void>
}
// Client (import from @roostjs/broadcast/client)
function createBroadcastClient(
url: string | (() => string),
options?: BroadcastClientOptions
): BroadcastClient
interface BroadcastClient {
subscribe(channel: string, handler: (event: string, data: unknown) => void): () => void
unsubscribe(channel: string): void
whisper(channel: string, event: string, data?: unknown): void
close(): void
}Override authorize() on ChannelDO to validate private/presence channel access with real JWT or session checks — the default accepts any bearer token.
Documentation
Full documentation at roost.birdcar.dev/docs/reference/broadcast
License
MIT
