Package Exports
- ts-broadcasting
- ts-broadcasting/dist/src/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (ts-broadcasting) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
ts-broadcasting
A high-performance, Laravel-inspired broadcasting system for TypeScript, built on Bun's native WebSocket APIs. Enables real-time communication between your server and clients with minimal overhead and maximum performance.
Features
- 🚀 Built on Bun - Leverages Bun's native WebSocket implementation for maximum performance
- 🔒 Channel Authorization - Flexible authorization system for private and presence channels
- 👥 Presence Channels - Track who's online in real-time
- 📡 Pub/Sub - Efficient broadcasting to multiple subscribers
- 🎯 Type-Safe - Full TypeScript support with comprehensive types
- ⚙️ Configurable - Flexible configuration system using bunfig
- 🛠️ CLI Included - Start and manage your broadcasting server from the command line
- 🔌 Multiple Drivers - Support for Bun, Reverb, Pusher, and Ably
- 💪 Laravel-Compatible - Familiar API for Laravel developers
Installation
bun add ts-broadcastingQuick Start
1. Start the Server
# Using CLI
bunx broadcast start
# Or programmatically
import { BroadcastServer, config } from 'ts-broadcasting'
const server = new BroadcastServer(config)
await server.start()2. Configure Channel Authorization
import { BroadcastServer, config } from 'ts-broadcasting'
const server = new BroadcastServer(config)
// Authorize private channels
server.channels.channel('private-orders.{orderId}', (ws, data) => {
// Check if user owns this order
return ws.data.user?.id === getOrderUserId(orderId)
})
// Authorize presence channels with user info
server.channels.channel('presence-chat.{roomId}', (ws, data) => {
return {
id: ws.data.user?.id,
info: {
name: ws.data.user?.name,
avatar: ws.data.user?.avatar,
},
}
})
await server.start()3. Broadcast Events
import type { BroadcastEvent } from 'ts-broadcasting'
import { AnonymousEvent, Broadcaster, BroadcastServer, config } from 'ts-broadcasting'
// Method 1: Using BroadcastEvent interface
class OrderShipped implements BroadcastEvent {
constructor(private order: Order) {}
shouldBroadcast(): boolean {
return true
}
broadcastOn(): string {
return `private-orders.${this.order.id}`
}
broadcastAs(): string {
return 'OrderShipped'
}
broadcastWith(): Record<string, unknown> {
return {
orderId: this.order.id,
trackingNumber: this.order.trackingNumber,
}
}
}
// Broadcast the event
const server = new BroadcastServer(config)
await server.start()
const broadcaster = new Broadcaster(server, config)
await broadcaster.broadcast(new OrderShipped(order))
// Method 2: Direct broadcast
broadcaster.send('announcements', 'SystemMessage', {
message: 'Scheduled maintenance tonight',
type: 'warning',
})
new AnonymousEvent('notifications')
.as('NewNotification')
.with({ title: 'Welcome!', body: 'Thanks for joining' })
.send(broadcaster)4. Client-Side Usage
<!DOCTYPE html>
<html>
<body>
<script>
const ws = new WebSocket('ws://localhost:6001/ws')
ws.onopen = () => {
console.log('Connected')
// Subscribe to public channel
ws.send(JSON.stringify({
event: 'subscribe',
channel: 'announcements'
}))
// Subscribe to private channel
ws.send(JSON.stringify({
event: 'subscribe',
channel: 'private-user.123'
}))
// Subscribe to presence channel
ws.send(JSON.stringify({
event: 'subscribe',
channel: 'presence-chat.room1',
channel_data: {
user_id: 123,
user_info: { name: 'John' }
}
}))
}
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
switch(message.event) {
case 'connection_established':
console.log('Socket ID:', message.data.socket_id)
break
case 'subscription_succeeded':
console.log('Subscribed to:', message.channel)
break
case 'OrderShipped':
console.log('Order shipped:', message.data)
break
case 'member_added':
console.log('User joined:', message.data)
break
}
}
</script>
</body>
</html>Configuration
Create a broadcast.config.ts or realtime.config.ts file:
import type { BroadcastConfig } from 'ts-broadcasting'
export default {
verbose: true,
driver: 'bun',
default: 'bun',
connections: {
bun: {
driver: 'bun',
host: '0.0.0.0',
port: 6001,
scheme: 'ws',
options: {
idleTimeout: 120,
maxPayloadLength: 16 _ 1024 _ 1024, // 16 MB
backpressureLimit: 1024 * 1024, // 1 MB
sendPings: true,
perMessageDeflate: true,
},
},
reverb: {
driver: 'reverb',
host: process.env.REVERB_HOST || '127.0.0.1',
port: Number.parseInt(process.env.REVERB_PORT || '8080'),
key: process.env.REVERB_APP_KEY,
secret: process.env.REVERB_APP_SECRET,
appId: process.env.REVERB_APP_ID,
},
pusher: {
driver: 'pusher',
key: process.env.PUSHER_APP_KEY,
secret: process.env.PUSHER_APP_SECRET,
appId: process.env.PUSHER_APP_ID,
cluster: process.env.PUSHER_APP_CLUSTER || 'mt1',
useTLS: true,
},
},
} satisfies BroadcastConfigCLI Commands
# Start the server
broadcast start
# Start with custom options
broadcast start --host 0.0.0.0 --port 6001 --verbose
# Show server statistics
broadcast stats
# Watch stats in real-time
broadcast stats --watch --interval 2
# Show configuration
broadcast config
# Show version
broadcast versionChannel Types
Public Channels
Anyone can subscribe without authorization:
// Server
server.broadcast('announcements', 'NewAnnouncement', data)
// Client
ws.send(JSON.stringify({
event: 'subscribe',
channel: 'announcements'
}))Private Channels
Require authorization, prefixed with private-:
// Server - define authorization
server.channels.channel('private-orders.{orderId}', (ws, data) => {
return ws.data.user?.id === getOrderOwnerId(orderId)
})
// Client
ws.send(JSON.stringify({
event: 'subscribe',
channel: 'private-orders.123'
}))Presence Channels
Track online users, prefixed with presence-:
// Server - return user info
server.channels.channel('presence-chat.{roomId}', (ws, data) => {
return {
id: ws.data.user.id,
info: {
name: ws.data.user.name,
status: 'online',
},
}
})
// Client
ws.send(JSON.stringify({
event: 'subscribe',
channel: 'presence-chat.room1',
channel_data: {
user_id: 123,
user_info: { name: 'John' }
}
}))Advanced Usage
Channel Pattern Matching
Use wildcards in channel names:
// Match any order ID
server.channels.channel('private-orders.{orderId}', (ws) => {
// orderId is extracted from channel name
return true
})
// Match any user's private channel
server.channels.channel('private-user.{userId}', (ws) => {
return ws.data.user?.id === userId
})Broadcasting to Others
Exclude the sender from receiving the broadcast:
broadcaster.toOthers(socketId).send(
'presence-chat.room1',
'UserTyping',
{ user: 'John' },
)Client Events (Whisper)
Send messages directly between clients on private channels:
// Client sends
ws.send(JSON.stringify({
event: 'client-typing',
channel: 'presence-chat.room1',
data: { typing: true }
}))
// Other clients receive
ws.onmessage = (event) => {
const msg = JSON.parse(event.data)
if (msg.event === 'client-typing') {
console.log('Someone is typing...')
}
}Compression
Enable per-message compression for large payloads:
const config = {
connections: {
bun: {
options: {
perMessageDeflate: {
compress: true,
decompress: true,
},
},
},
},
}Performance
Built on Bun's native WebSocket implementation, ts-broadcasting delivers exceptional performance:
- 7x faster than Node.js +
wsfor simple chatrooms - Native compression for efficient data transfer
- Zero-copy message passing where possible
- Efficient pub/sub using Bun's built-in publish method
API Reference
BroadcastServer
class BroadcastServer {
constructor(config: BroadcastConfig)
async start(): Promise<void>
async stop(): Promise<void>
broadcast(channel: string, event: string, data: unknown): void
getConnectionCount(): number
getSubscriberCount(channel: string): number
channels: ChannelManager
}ChannelManager
class ChannelManager {
channel(pattern: string, callback: ChannelAuthorizationCallback): this
async subscribe(ws: ServerWebSocket, channelName: string, data?: unknown): Promise<boolean>
unsubscribe(ws: ServerWebSocket, channelName: string): void
getSubscribers(channelName: string): Set<string>
getPresenceMembers(channelName: string): Map<string, PresenceMember>
getSubscriberCount(channelName: string): number
}Broadcaster
class Broadcaster {
async broadcast(event: BroadcastEvent): Promise<void>
send(channels: string | string[], event: string, data: unknown): void
toOthers(socketId: string): BroadcastTo
}Examples
Check the examples/ directory for complete examples:
basic-server.ts- Simple server setupbroadcasting-events.ts- Event broadcasting patternsclient-example.html- Browser client implementation
Contributing
Contributions are welcome! Please read our contributing guidelines.
License
MIT
Credits
Inspired by Laravel's broadcasting system, built with Bun's high-performance APIs.