@an-epiphany/websocket-json-stream

中文文档
1.2.0发布于 2026/01/08MITTypeScriptESM + CJS零依赖近 30 天下载 5758

一个用于WebSocket的TypeScript Dupplex流包装器,带有自动JSON序列. 支持 ws, SockJS, 和 Socket. 木卫一.

中文使用文档
文档目录29 节
  1. 特性
  2. 安装
  3. 快速开始
  4. 服务端
  5. 客户端(原生 WebSocket)
  6. 类型安全消息
  7. 自定义序列化器
  8. 使用选项对象
  9. MessagePack 示例
  10. Base64 编码示例
  11. 默认 JSON 序列化器
  12. SockJS 支持
  13. 服务端 (sockjs-node)
  14. 客户端 (sockjs-client)
  15. 为什么选择 SockJS?
  16. Socket.IO 支持
  17. 服务端 (socket.io)
  18. 客户端 (socket.io-client)
  19. 为什么选择 Socket.IO?
  20. API 参考
  21. 构造函数
  22. 事件
  23. 方法
  24. 关闭连接
  25. 错误处理
  26. 高级:适配器工具
  27. 类型
  28. 许可证
  29. 致谢

websocket-json-stream

license typescript node

Node.js Duplex 流封装,用于 WebSocket 连接的自动 JSON 序列化。

支持 Node.js WebSockets (ws)、SockJS 和 Socket.IO。

English | 中文


特性

  • TypeScript 优先 - 完整的类型定义和泛型支持
  • 双包支持 - 同时支持 ESM 和 CommonJS
  • 自定义序列化器 - 可插拔的序列化方案(JSON、MessagePack 等)
  • SockJS 适配器 - 内置 SockJS 支持,带 HTTP 降级
  • Socket.IO 适配器 - 内置 Socket.IO 支持,带自动重连
  • 零依赖 - 仅需 WebSocket 库作为对等依赖
  • 类型安全消息 - 泛型类型实现编译时消息校验

安装

npm install @an-epiphany/websocket-json-stream
# 或
pnpm add @an-epiphany/websocket-json-stream
# 或
yarn add @an-epiphany/websocket-json-stream

快速开始

服务端

import { WebSocketJSONStream } from '@an-epiphany/websocket-json-stream'
import { WebSocketServer } from 'ws'

const wss = new WebSocketServer({ port: 8080 })

wss.on('connection', (ws) => {
  const stream = new WebSocketJSONStream(ws)

  stream.on('data', (data) => {
    console.log('收到:', data)
    stream.write({ echo: data })
  })
})

客户端(原生 WebSocket)

import { WebSocket } from 'ws'

const ws = new WebSocket('ws://localhost:8080')

ws.on('open', () => {
  ws.send(JSON.stringify({ message: '你好!' }))
})

ws.on('message', (data) => {
  const message = JSON.parse(data.toString())
  console.log('收到:', message)
})

类型安全消息

interface ChatMessage {
  type: 'message' | 'join' | 'leave'
  user: string
  content?: string
}

const stream = new WebSocketJSONStream<ChatMessage>(ws)

stream.on('data', (msg) => {
  // msg 被类型化为 ChatMessage
  switch (msg.type) {
    case 'message':
      console.log(`${msg.user}: ${msg.content}`)
      break
    case 'join':
      console.log(`${msg.user} 加入了`)
      break
  }
})

stream.write({ type: 'message', user: 'Alice', content: '你好!' })

自定义序列化器

默认情况下,流使用 JSON 进行序列化。你可以提供自定义序列化器以获得更好的性能或使用不同的格式。

使用选项对象

import { WebSocketJSONStream, type Serializer } from '@an-epiphany/websocket-json-stream'

// 带前缀的自定义序列化器(示例)
const customSerializer: Serializer<MyData> = {
  serialize: (value) => `PREFIX:${JSON.stringify(value)}`,
  deserialize: (data) => JSON.parse(data.replace('PREFIX:', '')),
}

const stream = new WebSocketJSONStream(ws, {
  adapterType: 'ws',
  serializer: customSerializer,
})

MessagePack 示例

MessagePack 是一种二进制格式,比 JSON 更快更小。

import { WebSocketJSONStream, type Serializer } from '@an-epiphany/websocket-json-stream'
import { encode, decode } from '@msgpack/msgpack'

const msgpackSerializer: Serializer<MyData> = {
  serialize: (value) => Buffer.from(encode(value)).toString('base64'),
  deserialize: (data) => decode(Buffer.from(data, 'base64')) as MyData,
}

const stream = new WebSocketJSONStream(ws, {
  serializer: msgpackSerializer,
})

Base64 编码示例

const base64Serializer: Serializer<unknown> = {
  serialize: (value) => Buffer.from(JSON.stringify(value)).toString('base64'),
  deserialize: (data) => JSON.parse(Buffer.from(data, 'base64').toString('utf-8')),
}

const stream = new WebSocketJSONStream(ws, {
  serializer: base64Serializer,
})

默认 JSON 序列化器

你也可以导入默认序列化器用于参考或扩展:

import { jsonSerializer } from '@an-epiphany/websocket-json-stream'

// jsonSerializer.serialize(value) - 转换为 JSON 字符串
// jsonSerializer.deserialize(data) - 解析 JSON 字符串

SockJS 支持

SockJS 提供类似 WebSocket 的 API,当 WebSocket 不可用时自动降级到 HTTP 传输。

服务端 (sockjs-node)

import { WebSocketJSONStream } from '@an-epiphany/websocket-json-stream'
import sockjs from 'sockjs'
import http from 'http'

const server = sockjs.createServer()

server.on('connection', (conn) => {
  // 服务端连接使用 'sockjs-node' 适配器
  const stream = new WebSocketJSONStream(conn, 'sockjs-node')

  stream.on('data', (data) => {
    stream.write({ echo: data })
  })
})

const httpServer = http.createServer()
server.installHandlers(httpServer, { prefix: '/sockjs' })
httpServer.listen(8080)

客户端 (sockjs-client)

import SockJS from 'sockjs-client'

const sock = new SockJS('http://localhost:8080/sockjs')

sock.onopen = () => {
  sock.send(JSON.stringify({ message: '通过 SockJS 发送!' }))
}

sock.onmessage = (e) => {
  const message = JSON.parse(e.data)
  console.log('收到:', message)
}

为什么选择 SockJS?

场景解决方案
WebSocket 被防火墙/代理阻止自动降级到 XHR streaming
企业网络环境降级到长轮询
WebSocket 连接不稳定多种传输选项

Socket.IO 支持

Socket.IO 提供实时双向事件通信,支持自动重连和 HTTP 降级。

服务端 (socket.io)

import { WebSocketJSONStream } from '@an-epiphany/websocket-json-stream'
import { Server as SocketIOServer } from 'socket.io'
import http from 'http'

const httpServer = http.createServer()
const io = new SocketIOServer(httpServer)

io.on('connection', (socket) => {
  // 使用 'socketio' 适配器
  const stream = new WebSocketJSONStream(socket, 'socketio')

  stream.on('data', (data) => {
    stream.write({ echo: data })
  })
})

httpServer.listen(8080)

客户端 (socket.io-client)

import { io } from 'socket.io-client'

const socket = io('http://localhost:8080')

socket.on('connect', () => {
  // 通过 'message' 事件发送 JSON(对应服务端的 WebSocketJSONStream)
  socket.emit('message', JSON.stringify({ message: '通过 Socket.IO 发送!' }))
})

socket.on('message', (data: string) => {
  const message = JSON.parse(data)
  console.log('收到:', message)
})

为什么选择 Socket.IO?

场景解决方案
需要自动重连内置重连和退避机制
WebSocket 不可用自动降级到 HTTP 长轮询
需要房间/命名空间支持原生房间和命名空间
跨浏览器兼容性包含 polyfill 和降级方案

API 参考

构造函数

// 新的选项对象 API(推荐)
new WebSocketJSONStream<T>(ws: AdaptableWebSocket, options?: WebSocketJSONStreamOptions<T>)

// 旧版 API(仍然支持)
new WebSocketJSONStream<T>(ws: AdaptableWebSocket, adapterType?: AdapterType)

选项对象

属性类型默认值描述
adapterTypeAdapterType'ws'WebSocket 实现的适配器类型
serializerSerializer<T>jsonSerializer自定义序列化器

参数

参数类型默认值描述
wsAdaptableWebSocket-WebSocket、SockJS 或 Socket.IO 连接
T泛型unknown消息类型

事件

事件载荷描述
dataT收到 JSON 消息
errorError解析/写入错误
close-流已关闭
finish-写入端已结束

方法

方法描述
write(data: T)发送 JSON 消息
end()以码 1000 关闭
destroy(error?)强制关闭

关闭连接

// 正常关闭 (码: 1000)
stream.end()

// 无状态码关闭 (码: 1005)
stream.destroy()

// 带错误关闭 (码: 1011)
stream.destroy(new Error('出错了'))

// 自定义关闭码 (3000-4999)
const error = new Error('自定义') as StreamError
error.closeCode = 4000
error.closeReason = '自定义原因'
stream.destroy(error)

错误处理

// 处理 WebSocket 错误(流不处理)
ws.on('error', (error) => {
  console.error('WebSocket 错误:', error)
})

// 处理流错误
stream.on('error', (error) => {
  console.error('流错误:', error)
})

高级:适配器工具

import {
  adaptWebSocket,
  isWebSocketLike,
  isSockJSNodeConnection,
  isSocketIOSocket,
  SockJSNodeAdapter,
  SocketIOAdapter,
} from '@an-epiphany/websocket-json-stream'

// 类型检查
if (isSockJSNodeConnection(conn)) {
  console.log('SockJS Node 连接')
}

if (isSocketIOSocket(socket)) {
  console.log('Socket.IO 连接')
}

// 手动适配
const adapted = adaptWebSocket(conn, 'auto')

类型

interface Serializer<T = unknown> {
  serialize(value: T): string
  deserialize(data: string): T
}

interface WebSocketJSONStreamOptions<T = unknown> {
  adapterType?: AdapterType
  serializer?: Serializer<T>
}

interface WebSocketLike {
  readonly readyState: number
  send(data: string, callback?: (error?: Error) => void): void
  close(code?: number, reason?: string): void
  addEventListener(type: string, listener: Function): void
  removeEventListener(type: string, listener: Function): void
}

interface SockJSNodeConnection {
  readonly readyState: number
  write(data: string): boolean
  close(code?: number, reason?: string): void
  on(event: 'data' | 'close', listener: Function): this
  off(event: 'data' | 'close', listener: Function): this
}

interface SocketIOSocket {
  readonly id: string
  readonly connected: boolean
  emit(event: string, ...args: unknown[]): this
  on(event: string, listener: Function): this
  off(event: string, listener: Function): this
  disconnect(close?: boolean): this
}

interface StreamError extends Error {
  closeCode?: number
  closeReason?: string
}

type AdaptableWebSocket = WebSocketLike | SockJSNodeConnection | SocketIOSocket
type AdapterType = 'ws' | 'sockjs-node' | 'socketio' | 'auto'

许可证

MIT

致谢

基于 Greg Kubisa 的 @teamwork/websocket-json-stream 重写的 TypeScript 版本。

版本与发布记录

@an-epiphany/websocket-json-stream 共发布 2 个版本,首次发布于 2026/01/08;过去 12 个月发布了 2 个版本。

版本发布日期距今
1.2.0latest2026/01/089 个月前
1.0.02026/01/079 个月前

关于 @an-epiphany/websocket-json-stream 的常见问题

@an-epiphany/websocket-json-stream 是什么?

一个用于WebSocket的TypeScript Dupplex流包装器,带有自动JSON序列. 支持 ws, SockJS, 和 Socket. 木卫一.

如何安装 @an-epiphany/websocket-json-stream?

在项目目录运行 npm install @an-epiphany/websocket-json-stream,或使用 pnpm add / yarn add / bun add。

@an-epiphany/websocket-json-stream 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@an-epiphany/websocket-json-stream 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@an-epiphany/websocket-json-stream 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@an-epiphany/websocket-json-stream 可以商用吗?

@an-epiphany/websocket-json-stream 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@an-epiphany/websocket-json-stream 的下载量有多少?

根据 npm 官方下载统计,@an-epiphany/websocket-json-stream 在 2026/10/06 – 2026/11/04 这 30 天内被下载 5,758 次,日均约 192 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@an-epiphany/websocket-json-stream 还在维护吗?

最新版本 1.2.0 发布于 2026/01/08(9 个月前);至今共发布 2 个版本,其中过去 12 个月发布了 2 个;首次发布于 2026/01/08。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。