CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-faye-websocket

Standards-compliant WebSocket server and client implementation for Node.js with EventSource support

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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();
});

docs

eventsource.md

index.md

websocket-client.md

websocket-server.md

tile.json