npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bdky/websocket-client

v1.0.2

Published

轻量级 WebSocket 客户端 SDK,基于事件驱动架构,支持自动重连与消息队列管理

Downloads

234

Readme

@bdky/websocket-client

功能丰富的浏览器端 WebSocket 客户端,内置心跳检测指数退避自动重连消息缓冲ACK/NACK 投递确认。

特性

  • 心跳检测 (Ping/Pong) — 可配置的 keep-alive 机制,自动识别超时断线
  • 自动重连 — 指数退避策略,支持最大重试次数、首次立即重连
  • 消息缓冲 — 断线期间发送的消息自动入队,重连后统一刷新发送
  • ACK/NACK — 可选的应用层投递确认,超时未 ACK 自动触发 NACK 事件
  • 连接超时 — 自动终止长时间无响应的连接尝试
  • 类型安全 — 完整 TypeScript 支持,基于 Emittery 的强类型事件系统
  • 轻量级 — 唯一外部依赖为 emittery,支持 ESM + CJS 双格式 tree-shaking

安装

npm install @bdky/websocket-client
# 或
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('已连接');
    client.send('Hello, server!');
});

client.emitter.on('message', ({data}) => {
    console.log('收到消息:', data);
});

client.emitter.on('close', ({code, reason}) => {
    console.log(`连接关闭: ${code} ${reason}`);
});

client.connect();

API

new WebSocketClient(options)

创建一个新的 WebSocket 客户端实例。

配置项

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | url | string | 必填 | WebSocket 服务端地址 | | protocols | string \| string[] | undefined | 子协议 | | connectionTimeout | number | 10000 | 连接超时时间 (ms) | | backoff | Partial<IBackoffOptions> | 见下文 | 重连退避策略 | | heartbeat | Partial<IHeartbeatOptions> \| null | 见下文 | 心跳配置,传 null 可禁用 | | ack | Partial<IAckOptions> | 见下文 | ACK/NACK 配置 | | binaryType | BinaryType | 'blob' | 二进制数据类型 |

属性

| 属性 | 类型 | 说明 | |------|------|------| | readyState | WebSocketState | 当前连接状态:'idle' | 'connecting' | 'open' | 'closing' | 'closed' | 'reconnecting' | | url | string | WebSocket 地址 | | bufferedAmount | number | 发送缓冲队列中待发送的消息数 | | emitter | Emittery<IWebSocketEvents> | 强类型事件发射器 |

方法

| 方法 | 说明 | |------|------| | connect() | 发起 WebSocket 连接 | | send(data) | 发送数据;未连接时自动缓冲 | | close(code?, reason?) | 主动关闭连接(不触发重连) | | dispose() | 销毁实例,释放所有资源 |

事件

通过 client.emitter.on(event, handler) 订阅:

| 事件 | 载荷 | 说明 | |------|------|------| | open | {event} | 连接建立 | | close | {code, reason, wasClean} | 连接关闭 | | error | {event} | 发生 WebSocket 错误 | | message | {data, event} | 收到消息(已排除 pong 和 ack) | | reconnecting | {attempt, delay} | 即将重连 | | reconnected | {attempt} | 重连成功 | | heartbeat_timeout | undefined | 心跳超时,未收到 pong | | ack | {messageId} | 服务端确认收到消息 | | nack | {messageId} | ACK 超时,消息未被确认 |

心跳检测

自动发送 ping 并等待 pong 响应,及早发现死连接。

const client = new WebSocketClient({
    url: 'wss://example.com/ws',
    heartbeat: {
        interval: 30_000,     // 每 30 秒发送 ping
        timeout: 5_000,       // 等待 pong 的超时时间
        pingMessage: 'ping',  // ping 消息内容(字符串或 () => string)
        isPong: (data) => data === 'pong',  // 判断是否为 pong 响应
    },
});

传入 heartbeat: null 可完全禁用心跳。

默认值:

| 参数 | 默认值 | |------|--------| | interval | 30000 | | timeout | 5000 | | pingMessage | 'ping' | | isPong | (data) => data === 'pong' |

自动重连

指数退避重连策略,连接断开后自动尝试恢复。

const client = new WebSocketClient({
    url: 'wss://example.com/ws',
    backoff: {
        initialDelay: 1_000,      // 首次重连延迟 1 秒
        maxDelay: 30_000,         // 最大延迟上限 30 秒
        maxRetries: 10,           // 最多重试 10 次(0 = 无限重试)
        instantReconnect: false,  // 首次重连是否跳过延迟
    },
});

client.emitter.on('reconnecting', ({attempt, delay}) => {
    console.log(`第 ${attempt} 次重连,延迟 ${delay}ms`);
});

client.emitter.on('reconnected', ({attempt}) => {
    console.log(`重连成功,共尝试 ${attempt} 次`);
});

默认值:

| 参数 | 默认值 | |------|--------| | initialDelay | 1000 | | maxDelay | 30000 | | maxRetries | 0(无限重试) | | instantReconnect | false |

延迟计算公式:min(initialDelay * 2^attempt, maxDelay)

消息缓冲

断线期间发送的消息不会丢失,而是自动入队。连接(重新)建立后,缓冲区中的消息会按顺序发出。

const client = new WebSocketClient({url: 'wss://example.com/ws'});

// 这些消息会被缓冲,不会丢失
client.send('message-1');
client.send('message-2');

console.log(client.bufferedAmount); // 2

// 连接建立后自动发送缓冲消息
client.connect();

ACK / NACK

应用层消息投递确认机制。发送消息后,在指定时间内未收到服务端 ACK 将触发 NACK 事件。

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(`消息 ${messageId} 已确认`);
});

client.emitter.on('nack', ({messageId}) => {
    console.log(`消息 ${messageId} 超时未确认`);
});

JSON 协议示例

以下是一个贴近实际业务的完整示例,展示心跳 + 重连 + 消息处理的组合用法:

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('服务端消息:', msg);
    }
});

client.emitter.on('error', () => {
    console.error('连接异常');
});

client.connect();

导出一览

// 类
export {WebSocketClient, ExponentialBackoff, ArrayQueue};

// 类型
export type {
    IWebSocketClientOptions,
    IBackoffOptions,
    IHeartbeatOptions,
    IAckOptions,
    IWebSocketEvents,
    IResolvedOptions,
    WebSocketState,
    WebSocketEmitter,
    BufferData,
};

// 默认值常量
export {
    DEFAULT_CONNECTION_TIMEOUT,
    DEFAULT_BACKOFF,
    DEFAULT_HEARTBEAT,
    DEFAULT_ACK,
};

许可证

MIT