or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

eventsource.mdindex.mdwebsocket-client.mdwebsocket-server.md
tile.json

eventsource.mddocs/

EventSource (Server-Sent Events)

Server-sent events implementation for unidirectional streaming from server to client with automatic reconnection support and standards compliance.

Capabilities

EventSource Constructor

Creates a new EventSource connection for streaming server-sent events to clients.

/**
 * Creates an EventSource connection for server-sent events
 * @param {Object} request - HTTP request object
 * @param {Object} response - HTTP response object
 * @param {Object} [options] - Configuration options
 */
const WebSocket = require('faye-websocket');
const EventSource = WebSocket.EventSource;
const es = new EventSource(request, response, options);

Usage Example:

const WebSocket = require('faye-websocket');
const http = require('http');

const server = http.createServer();

server.on('request', function(request, response) {
  if (EventSource.isEventSource(request)) {
    const es = new EventSource(request, response, {
      retry: 10, // Client should retry every 10 seconds
      ping: 30   // Send ping every 30 seconds
    });
    
    console.log('EventSource connected:', es.url);
    console.log('Last event ID:', es.lastEventId);
    
    // Send periodic updates
    const interval = setInterval(function() {
      es.send('Current time: ' + new Date().toISOString());
    }, 1000);
    
    es.on('close', function() {
      clearInterval(interval);
      console.log('EventSource closed');
    });
  }
});

server.listen(8000);

Static Methods

isEventSource

Checks if an HTTP request accepts EventSource (text/event-stream).

/**
 * Checks if HTTP request accepts server-sent events
 * @param {Object} request - HTTP request object
 * @returns {boolean} True if request accepts text/event-stream
 */
EventSource.isEventSource(request);

Usage Example:

server.on('request', function(request, response) {
  if (EventSource.isEventSource(request)) {
    // Handle as EventSource
    const es = new EventSource(request, response);
  } else {
    // Handle as regular HTTP request
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.end('<h1>Not an EventSource request</h1>');
  }
});

Instance Properties

Connection Information

es.url;         // String - The URL from the client request
es.lastEventId; // String - Last event ID sent by client (for reconnection)
es.readyState;  // Number - Connection state (CONNECTING=0, OPEN=1, CLOSED=3)
es.writable;    // Boolean - Stream writable state

Default Constants

EventSource.prototype.DEFAULT_PING;  // 10 - Default ping interval (seconds)
EventSource.prototype.DEFAULT_RETRY; // 5 - Default retry interval (seconds)

Instance Methods

send

Sends a server-sent event to the client.

/**
 * Sends a server-sent event to the client
 * @param {string} message - Event data
 * @param {Object} [options] - Event options
 * @param {string} [options.event] - Event type (default: 'message')
 * @param {string} [options.id] - Event ID for client tracking
 * @returns {boolean} True if message was sent successfully
 */
es.send(message, options);

Usage Examples:

// Simple message
es.send('Hello, client!');

// Typed event with ID
es.send('User logged in', {
  event: 'user-login',
  id: '12345'
});

// JSON data
es.send(JSON.stringify({
  user: 'alice',
  action: 'login',
  timestamp: Date.now()
}), {
  event: 'user-event',
  id: 'evt-' + Date.now()
});

ping

Sends a keep-alive ping to maintain the connection.

/**
 * Sends a keep-alive ping to the client
 * @returns {boolean} True if ping was sent
 */
es.ping();

close

Closes the EventSource connection.

/**
 * Closes the EventSource connection
 * @returns {boolean} True if connection was closed
 */
es.close();

Stream Methods

// Stream interface methods
es.write(message);  // Alias for send()
es.end(message);    // Send final message and close

Events

open

Fired when the EventSource connection is established (internal event).

es.on('open', function(event) {
  console.log('EventSource connection opened');
});

close

Fired when the connection is closed.

es.on('close', function(event) {
  console.log('EventSource connection closed');
});

drain

Fired when the underlying stream is ready for more data.

es.on('drain', function() {
  console.log('Ready for more data');
});

Configuration Options

// EventSource configuration options
{
  headers: Object,  // Custom HTTP response headers
  retry: number,    // Client reconnect interval in seconds (default: 5)
  ping: number     // Server ping interval in seconds (default: 10)
}

Usage Example:

const es = new EventSource(request, response, {
  headers: {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Credentials': 'true',
    'X-Custom-Header': 'streaming-api'
  },
  retry: 15, // Client retries every 15 seconds
  ping: 45   // Server pings every 45 seconds
});

Complete EventSource Server Example

const WebSocket = require('faye-websocket');
const http = require('http');

const server = http.createServer();

server.on('request', function(request, response) {
  if (EventSource.isEventSource(request)) {
    const es = new EventSource(request, response, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Cache-Control': 'no-cache'
      },
      retry: 5,
      ping: 30
    });
    
    console.log('Client connected:', es.url);
    
    // Handle reconnection by checking last event ID
    let lastId = parseInt(es.lastEventId) || 0;
    console.log('Resuming from event ID:', lastId);
    
    // Send missed events if reconnecting
    if (lastId > 0) {
      es.send('Reconnected successfully', {
        event: 'reconnect',
        id: (++lastId).toString()
      });
    }
    
    // Send periodic updates
    const timer = setInterval(function() {
      const eventData = {
        timestamp: new Date().toISOString(),
        counter: ++lastId,
        message: 'Server update #' + lastId
      };
      
      es.send(JSON.stringify(eventData), {
        event: 'update',
        id: lastId.toString()
      });
    }, 2000);
    
    // Send different event types
    setTimeout(function() {
      es.send('Special announcement!', {
        event: 'announcement',
        id: (++lastId).toString()
      });
    }, 5000);
    
    // Handle client disconnect
    es.on('close', function() {
      console.log('Client disconnected from:', es.url);
      clearInterval(timer);
    });
    
    // Handle connection errors
    ['error', 'end'].forEach(function(event) {
      es.on(event, function() {
        console.log('EventSource', event);
        clearInterval(timer);
      });
    });
    
  } else {
    // Serve client test page
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.end(`
      <!DOCTYPE html>
      <html>
      <head><title>EventSource Test</title></head>
      <body>
        <div id="messages"></div>
        <script>
          const es = new EventSource('/');
          const messages = document.getElementById('messages');
          
          es.onmessage = function(event) {
            messages.innerHTML += '<p>Message: ' + event.data + '</p>';
          };
          
          es.addEventListener('update', function(event) {
            messages.innerHTML += '<p>Update: ' + event.data + '</p>';
          });
          
          es.addEventListener('announcement', function(event) {
            messages.innerHTML += '<p><strong>Announcement:</strong> ' + event.data + '</p>';
          });
          
          es.onerror = function(event) {
            messages.innerHTML += '<p style="color:red">Error occurred</p>';
          };
        </script>
      </body>
      </html>
    `);
  }
});

server.listen(8000, function() {
  console.log('Server listening on http://localhost:8000');
});

Event Message Format

Server-sent events follow a specific format:

// Basic message
es.send('Hello World');
// Sends: "data: Hello World\r\n\r\n"

// Message with event type
es.send('User data', { event: 'user-update' });
// Sends: "event: user-update\r\ndata: User data\r\n\r\n"

// Message with ID
es.send('Important data', { id: '123' });
// Sends: "id: 123\r\ndata: Important data\r\n\r\n"

// Complete message
es.send('Full event', { event: 'custom', id: '456' });
// Sends: "event: custom\r\nid: 456\r\ndata: Full event\r\n\r\n"

Multiline Messages

// Multiline messages are automatically formatted
es.send('Line 1\nLine 2\nLine 3');
// Sends: "data: Line 1\r\ndata: Line 2\r\ndata: Line 3\r\n\r\n"

CORS Support

const es = new EventSource(request, response, {
  headers: {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Headers': 'Cache-Control',
    'Access-Control-Allow-Credentials': 'true'
  }
});

Connection Management

// Check connection state
if (es.readyState === 1) { // OPEN
  es.send('Connection is active');
}

// Graceful shutdown
process.on('SIGTERM', function() {
  es.close();
});