CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-msw

Seamless REST/GraphQL API mocking library for browser and Node.js.

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

websocket-handlers.mddocs/

WebSocket Handlers

WebSocket handlers provide event-based interception and mocking of WebSocket connections with support for connection lifecycle management, message broadcasting, and client management.

Capabilities

WebSocket Link Creation

Create WebSocket handlers for specific WebSocket server URLs.

/**
 * Creates a WebSocket link handler for intercepting connections to a specific URL
 * @param url - WebSocket server URL to intercept (string, RegExp, or path pattern)
 * @returns WebSocketLink instance with event handling and broadcast capabilities
 */
function link(url: string | RegExp): WebSocketLink;

interface WebSocketLink {
  /** Set of all WebSocket clients connected to this link */
  clients: Set<WebSocketClientConnectionProtocol>;
  
  /** Add event listener for WebSocket connection events */
  addEventListener<EventType extends keyof WebSocketHandlerEventMap>(
    event: EventType,
    listener: WebSocketEventListener<EventType>
  ): WebSocketHandler;
  
  /** Broadcast data to all connected WebSocket clients */
  broadcast(data: WebSocketData): void;
  
  /** Broadcast data to all clients except specified ones */
  broadcastExcept(
    clients: WebSocketClientConnectionProtocol | WebSocketClientConnectionProtocol[],
    data: WebSocketData
  ): void;
}

const ws: {
  link: typeof link;
};

Usage Examples:

import { ws } from "msw";

// Create WebSocket link for chat application
const chatServer = ws.link('wss://chat.example.com');

// Handle new connections
chatServer.addEventListener('connection', ({ client }) => {
  console.log('Client connected:', client.id);
  
  // Send welcome message to new client
  client.send(JSON.stringify({
    type: 'welcome',
    message: 'Welcome to the chat!'
  }));
  
  // Notify other clients about new user
  chatServer.broadcastExcept(client, JSON.stringify({
    type: 'user_joined',
    userId: client.id
  }));
});

// Create WebSocket link with RegExp pattern
const gameServer = ws.link(/wss:\/\/game\.example\.com\/room\/\d+/);

gameServer.addEventListener('connection', ({ client }) => {
  // Extract room ID from URL
  const roomId = client.url.match(/\/room\/(\d+)/)?.[1];
  
  client.send(JSON.stringify({
    type: 'room_joined',
    roomId
  }));
});

// Development WebSocket server
const devServer = ws.link('ws://localhost:8080');

devServer.addEventListener('connection', ({ client }) => {
  // Echo server - send back any received message
  client.addEventListener('message', (event) => {
    client.send(`Echo: ${event.data}`);
  });
});

Event Handling

Handle WebSocket connection lifecycle events.

type WebSocketEventListener<EventType extends keyof WebSocketHandlerEventMap> = 
  (...args: WebSocketHandlerEventMap[EventType]) => void;

interface WebSocketHandlerEventMap {
  /** Fired when a client connects to the WebSocket server */
  connection: [event: { client: WebSocketClientConnectionProtocol; server: WebSocketLink }];
  /** Fired when a client disconnects from the WebSocket server */
  disconnect: [event: { client: WebSocketClientConnectionProtocol; server: WebSocketLink }];
}

interface WebSocketClientConnectionProtocol {
  /** Unique client identifier */
  id: string;
  /** WebSocket URL the client connected to */
  url: string;
  /** Connection protocol (if specified) */
  protocol?: string;
  /** Send data to this specific client */
  send(data: WebSocketData): void;
  /** Close the connection to this client */
  close(code?: number, reason?: string): void;
  /** Add event listener for client-specific events */
  addEventListener(event: string, listener: (event: any) => void): void;
  /** Remove event listener from client */
  removeEventListener(event: string, listener: (event: any) => void): void;
}

Usage Examples:

// Handle connection events
const server = ws.link('wss://api.example.com');

server.addEventListener('connection', ({ client, server }) => {
  console.log(`Client ${client.id} connected to ${client.url}`);
  
  // Set up client-specific message handler
  client.addEventListener('message', (event) => {
    console.log(`Message from ${client.id}:`, event.data);
    
    // Parse and handle different message types
    try {
      const message = JSON.parse(event.data);
      
      switch (message.type) {
        case 'chat':
          // Broadcast chat message to all clients
          server.broadcast(JSON.stringify({
            type: 'chat',
            from: client.id,
            message: message.text,
            timestamp: Date.now()
          }));
          break;
          
        case 'ping':
          // Respond with pong
          client.send(JSON.stringify({ type: 'pong' }));
          break;
          
        case 'join_room':
          // Handle room joining logic
          client.send(JSON.stringify({
            type: 'room_joined',
            room: message.room
          }));
          break;
      }
    } catch (error) {
      client.send(JSON.stringify({
        type: 'error',
        message: 'Invalid message format'
      }));
    }
  });
  
  // Handle client errors
  client.addEventListener('error', (event) => {
    console.error(`Client ${client.id} error:`, event);
  });
  
  // Handle client disconnect
  client.addEventListener('close', (event) => {
    console.log(`Client ${client.id} disconnected:`, event.code, event.reason);
    
    // Notify other clients
    server.broadcastExcept(client, JSON.stringify({
      type: 'user_left',
      userId: client.id
    }));
  });
});

// Handle disconnect events at server level
server.addEventListener('disconnect', ({ client }) => {
  console.log(`Server: Client ${client.id} disconnected`);
  
  // Clean up any server-side state associated with this client
  // removeUserFromRooms(client.id);
});

Broadcasting Messages

Send messages to multiple clients with flexible targeting.

/**
 * Broadcast data to all connected WebSocket clients
 * @param data - Data to send (string, ArrayBuffer, or Blob)
 */
broadcast(data: WebSocketData): void;

/**
 * Broadcast data to all clients except specified ones
 * @param clients - Client or array of clients to exclude from broadcast
 * @param data - Data to send (string, ArrayBuffer, or Blob)  
 */
broadcastExcept(
  clients: WebSocketClientConnectionProtocol | WebSocketClientConnectionProtocol[],
  data: WebSocketData
): void;

type WebSocketData = string | ArrayBuffer | Blob;

Usage Examples:

const chatServer = ws.link('wss://chat.example.com');

chatServer.addEventListener('connection', ({ client, server }) => {
  client.addEventListener('message', (event) => {
    const message = JSON.parse(event.data);
    
    switch (message.type) {
      case 'broadcast_message':
        // Send message to all connected clients
        server.broadcast(JSON.stringify({
          type: 'message',
          from: client.id,
          text: message.text,
          timestamp: Date.now()
        }));
        break;
        
      case 'private_message':
        // Send message to all clients except sender
        server.broadcastExcept(client, JSON.stringify({
          type: 'message',
          from: client.id,
          text: message.text,
          timestamp: Date.now()
        }));
        break;
        
      case 'admin_announcement':
        // Send to all clients except specific ones
        const excludedClients = Array.from(server.clients).filter(
          c => c.id.startsWith('guest_')
        );
        
        server.broadcastExcept(excludedClients, JSON.stringify({
          type: 'announcement',
          text: message.text,
          priority: 'high'
        }));
        break;
        
      case 'server_stats':
        // Broadcast server statistics
        server.broadcast(JSON.stringify({
          type: 'stats',
          connectedClients: server.clients.size,
          uptime: process.uptime(),
          timestamp: Date.now()
        }));
        break;
    }
  });
});

// Periodic broadcasts
const gameServer = ws.link('wss://game.example.com');

gameServer.addEventListener('connection', ({ server }) => {
  // Send game state updates every second
  setInterval(() => {
    if (server.clients.size > 0) {
      server.broadcast(JSON.stringify({
        type: 'game_state',
        players: server.clients.size,
        timestamp: Date.now()
      }));
    }
  }, 1000);
});

// Binary data broadcasting
const dataServer = ws.link('wss://data.example.com');

dataServer.addEventListener('connection', ({ client, server }) => {
  client.addEventListener('message', (event) => {
    if (event.data instanceof ArrayBuffer) {
      // Echo binary data to all other clients
      server.broadcastExcept(client, event.data);
    }
  });
});

Client Management

Access and manage connected WebSocket clients.

interface WebSocketLink {
  /** Set of all WebSocket clients connected to this link */
  clients: Set<WebSocketClientConnectionProtocol>;
}

Usage Examples:

const server = ws.link('wss://monitoring.example.com');

server.addEventListener('connection', ({ client, server }) => {
  console.log(`Total clients: ${server.clients.size}`);
  
  // Send client list to new client
  client.send(JSON.stringify({
    type: 'client_list',
    clients: Array.from(server.clients).map(c => ({
      id: c.id,
      url: c.url,
      protocol: c.protocol
    }))
  }));
  
  client.addEventListener('message', (event) => {
    const message = JSON.parse(event.data);
    
    switch (message.type) {
      case 'get_stats':
        client.send(JSON.stringify({
          type: 'stats',
          totalClients: server.clients.size,
          clientIds: Array.from(server.clients).map(c => c.id)
        }));
        break;
        
      case 'kick_client':
        // Find and disconnect specific client
        const targetClient = Array.from(server.clients).find(
          c => c.id === message.clientId
        );
        
        if (targetClient) {
          targetClient.close(1000, 'Kicked by admin');
        }
        break;
        
      case 'message_client':
        // Send message to specific client
        const recipient = Array.from(server.clients).find(
          c => c.id === message.targetId
        );
        
        if (recipient) {
          recipient.send(JSON.stringify({
            type: 'private_message',
            from: client.id,
            text: message.text
          }));
        }
        break;
    }
  });
});

// Connection limiting
const limitedServer = ws.link('wss://limited.example.com');

limitedServer.addEventListener('connection', ({ client, server }) => {
  if (server.clients.size > 10) {
    client.close(1013, 'Server full');
    return;
  }
  
  client.send(JSON.stringify({
    type: 'welcome',
    position: server.clients.size,
    maxClients: 10
  }));
});

Advanced WebSocket Patterns

Handle complex WebSocket scenarios and patterns.

// Room-based messaging
const roomServer = ws.link('wss://rooms.example.com');
const rooms = new Map<string, Set<WebSocketClientConnectionProtocol>>();

roomServer.addEventListener('connection', ({ client, server }) => {
  let currentRoom: string | null = null;
  
  client.addEventListener('message', (event) => {
    const message = JSON.parse(event.data);
    
    switch (message.type) {
      case 'join_room':
        // Leave current room
        if (currentRoom && rooms.has(currentRoom)) {
          rooms.get(currentRoom)!.delete(client);
        }
        
        // Join new room
        currentRoom = message.room;
        if (!rooms.has(currentRoom)) {
          rooms.set(currentRoom, new Set());
        }
        rooms.get(currentRoom)!.add(client);
        
        // Notify room members
        rooms.get(currentRoom)!.forEach(roomClient => {
          if (roomClient !== client) {
            roomClient.send(JSON.stringify({
              type: 'user_joined_room',
              userId: client.id,
              room: currentRoom
            }));
          }
        });
        break;
        
      case 'room_message':
        // Send message to all clients in current room
        if (currentRoom && rooms.has(currentRoom)) {
          rooms.get(currentRoom)!.forEach(roomClient => {
            roomClient.send(JSON.stringify({
              type: 'room_message',
              from: client.id,
              room: currentRoom,
              text: message.text
            }));
          });
        }
        break;
    }
  });
  
  client.addEventListener('close', () => {
    // Clean up room membership
    if (currentRoom && rooms.has(currentRoom)) {
      rooms.get(currentRoom)!.delete(client);
      if (rooms.get(currentRoom)!.size === 0) {
        rooms.delete(currentRoom);
      }
    }
  });
});

// Protocol-specific handling
const multiProtocolServer = ws.link('wss://protocols.example.com');

multiProtocolServer.addEventListener('connection', ({ client }) => {
  switch (client.protocol) {
    case 'chat':
      client.send(JSON.stringify({ type: 'chat_ready' }));
      break;
    case 'game':
      client.send(JSON.stringify({ type: 'game_ready' }));
      break;
    default:
      client.send(JSON.stringify({ type: 'generic_ready' }));
  }
});

Types

// Handler types
interface WebSocketHandler {
  url: string | RegExp;
  eventHandlers: Map<string, Function[]>;
}

// Client types
interface WebSocketClientConnectionProtocol {
  id: string;
  url: string;
  protocol?: string;
  send(data: WebSocketData): void;
  close(code?: number, reason?: string): void;
  addEventListener(event: string, listener: (event: any) => void): void;
  removeEventListener(event: string, listener: (event: any) => void): void;
}

// Data types
type WebSocketData = string | ArrayBuffer | Blob;

// Event types
interface WebSocketHandlerEventMap {
  connection: [event: { 
    client: WebSocketClientConnectionProtocol; 
    server: WebSocketLink 
  }];
  disconnect: [event: { 
    client: WebSocketClientConnectionProtocol; 
    server: WebSocketLink 
  }];
}

type WebSocketEventListener<EventType extends keyof WebSocketHandlerEventMap> = 
  (...args: WebSocketHandlerEventMap[EventType]) => void;

// Connection types
interface WebSocketHandlerConnection {
  client: WebSocketClientConnectionProtocol;
  server: WebSocketLink;
}

docs

browser-setup.md

cli.md

graphql-handlers.md

http-handlers.md

index.md

nodejs-setup.md

react-native-setup.md

response-creation.md

utilities.md

websocket-handlers.md

tile.json