
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
@bdky/websocket-client
Advanced tools
A robust, feature-rich WebSocket client for the browser with built-in heartbeat, automatic reconnection with exponential backoff, message buffering, and ACK/NACK support.
npm install @bdky/websocket-client
# or
yarn add @bdky/websocket-client
import {WebSocketClient} from '@bdky/websocket-client';
const client = new WebSocketClient({
url: 'wss://example.com/ws',
});
client.emitter.on('open', () => {
console.log('Connected');
client.send('Hello, server!');
});
client.emitter.on('message', ({data}) => {
console.log('Received:', data);
});
client.emitter.on('close', ({code, reason}) => {
console.log(`Closed: ${code} ${reason}`);
});
client.connect();
new WebSocketClient(options)Creates a new WebSocket client instance.
| Option | Type | Default | Description |
|---|---|---|---|
url | string | required | WebSocket server URL |
protocols | string | string[] | undefined | Subprotocol(s) |
connectionTimeout | number | 10000 | Connection timeout in ms |
backoff | Partial<IBackoffOptions> | see below | Reconnection backoff strategy |
heartbeat | Partial<IHeartbeatOptions> | null | see below | Heartbeat config, null to disable |
ack | Partial<IAckOptions> | see below | ACK/NACK config |
binaryType | BinaryType | 'blob' | Binary data type |
| Property | Type | Description |
|---|---|---|
readyState | WebSocketState | Current connection state: 'idle' | 'connecting' | 'open' | 'closing' | 'closed' | 'reconnecting' |
url | string | The WebSocket URL |
bufferedAmount | number | Number of messages waiting in the send buffer |
emitter | Emittery<IWebSocketEvents> | Typed event emitter |
| Method | Description |
|---|---|
connect() | Initiate the WebSocket connection |
send(data) | Send data. Auto-buffers if not connected |
close(code?, reason?) | Gracefully close the connection (no reconnect) |
dispose() | Destroy the instance, release all resources |
Subscribe via client.emitter.on(event, handler):
| Event | Payload | Description |
|---|---|---|
open | {event} | Connection established |
close | {code, reason, wasClean} | Connection closed |
error | {event} | WebSocket error occurred |
message | {data, event} | Message received (excludes pong & ack) |
reconnecting | {attempt, delay} | About to reconnect |
reconnected | {attempt} | Successfully reconnected |
heartbeat_timeout | undefined | Pong not received within timeout |
ack | {messageId} | Server acknowledged a message |
nack | {messageId} | ACK timeout, message not confirmed |
Automatic ping/pong keep-alive to detect dead connections early.
const client = new WebSocketClient({
url: 'wss://example.com/ws',
heartbeat: {
interval: 30_000, // Send ping every 30s
timeout: 5_000, // Wait 5s for pong
pingMessage: 'ping', // Ping payload (string or () => string)
isPong: (data) => data === 'pong', // Identify pong responses
},
});
Set heartbeat: null to disable heartbeat entirely.
Default values:
| Option | Default |
|---|---|
interval | 30000 |
timeout | 5000 |
pingMessage | 'ping' |
isPong | (data) => data === 'pong' |
Exponential backoff reconnection with configurable limits.
const client = new WebSocketClient({
url: 'wss://example.com/ws',
backoff: {
initialDelay: 1_000, // Start with 1s delay
maxDelay: 30_000, // Cap at 30s
maxRetries: 10, // Give up after 10 attempts (0 = infinite)
instantReconnect: false, // Skip delay on first retry
},
});
client.emitter.on('reconnecting', ({attempt, delay}) => {
console.log(`Reconnect #${attempt} in ${delay}ms`);
});
client.emitter.on('reconnected', ({attempt}) => {
console.log(`Reconnected after ${attempt} attempts`);
});
Default values:
| Option | Default |
|---|---|
initialDelay | 1000 |
maxDelay | 30000 |
maxRetries | 0 (infinite) |
instantReconnect | false |
The delay formula is: min(initialDelay * 2^attempt, maxDelay)
Messages sent while disconnected are automatically buffered and flushed when the connection is (re)established.
const client = new WebSocketClient({url: 'wss://example.com/ws'});
// These messages are queued, not lost
client.send('message-1');
client.send('message-2');
console.log(client.bufferedAmount); // 2
// On connect, buffered messages are sent automatically
client.connect();
Application-level message delivery confirmation.
const client = new WebSocketClient({
url: 'wss://example.com/ws',
ack: {
enabled: true,
timeout: 5_000,
getMessageId: (data) => {
const msg = JSON.parse(data as string);
return msg.id;
},
parseAck: (data) => {
const msg = JSON.parse(data as string);
return msg.type === 'ack' ? msg.id : null;
},
},
});
client.emitter.on('ack', ({messageId}) => {
console.log(`Message ${messageId} confirmed`);
});
client.emitter.on('nack', ({messageId}) => {
console.log(`Message ${messageId} timed out — no ACK received`);
});
import {WebSocketClient} from '@bdky/websocket-client';
interface ServerMessage {
type: 'data' | 'pong' | 'ack';
payload?: unknown;
id?: string;
}
const client = new WebSocketClient({
url: 'wss://api.example.com/ws',
heartbeat: {
interval: 25_000,
timeout: 5_000,
pingMessage: () => JSON.stringify({type: 'ping'}),
isPong: (data) => {
if (typeof data !== 'string') {
return false;
}
try {
const msg: ServerMessage = JSON.parse(data);
return msg.type === 'pong';
}
catch {
return false;
}
},
},
backoff: {
initialDelay: 1_000,
maxDelay: 30_000,
maxRetries: 5,
},
});
client.emitter.on('message', ({data}) => {
if (typeof data === 'string') {
const msg: ServerMessage = JSON.parse(data);
console.log('Server says:', msg);
}
});
client.emitter.on('error', () => {
console.error('Connection error');
});
client.connect();
// Classes
export {WebSocketClient, ExponentialBackoff, ArrayQueue};
// Types
export type {
IWebSocketClientOptions,
IBackoffOptions,
IHeartbeatOptions,
IAckOptions,
IWebSocketEvents,
IResolvedOptions,
WebSocketState,
WebSocketEmitter,
BufferData,
};
// Default constants
export {
DEFAULT_CONNECTION_TIMEOUT,
DEFAULT_BACKOFF,
DEFAULT_HEARTBEAT,
DEFAULT_ACK,
};
MIT
FAQs
轻量级 WebSocket 客户端 SDK,基于事件驱动架构,支持自动重连与消息队列管理
The npm package @bdky/websocket-client receives a total of 10 weekly downloads. As such, @bdky/websocket-client popularity was classified as not popular.
We found that @bdky/websocket-client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.