Seamless REST/GraphQL API mocking library for browser and Node.js.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
WebSocket handlers provide event-based interception and mocking of WebSocket connections with support for connection lifecycle management, message broadcasting, and client management.
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}`);
});
});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);
});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);
}
});
});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
}));
});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' }));
}
});// 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;
}