JSPM

ts-broadcasting

0.0.5
    • ESM via JSPM
    • ES Module Entrypoint
    • Export Map
    • Keywords
    • License
    • Repository URL
    • TypeScript Types
    • README
    • Created
    • Published
    • Downloads 55517
    • Score
      100M100P100Q156850F
    • License MIT

    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-broadcasting

    Quick 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 BroadcastConfig

    CLI 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 version

    Channel 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 + ws for 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 setup
    • broadcasting-events.ts - Event broadcasting patterns
    • client-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.