feat: 更新WebSocket Keep-Alive客户端库以支持Bun和Node.js环境,添加统一的消息处理方法,并创建keep.ts文件以初始化KeepAlive

This commit is contained in:
2026-02-15 20:48:45 +08:00
parent d231f3748a
commit 47229c6db9
5 changed files with 120 additions and 47 deletions

View File

@@ -1,5 +1,16 @@
// WebSocket Keep-Alive Client Library
import WebSocket from "ws";
// 运行时检测Bun 使用原生 WebSocketNode.js 使用 ws 库
let WebSocketModule: any;
if (typeof Bun !== 'undefined') {
// Bun 环境:使用原生 WebSocket
WebSocketModule = { WebSocket: globalThis.WebSocket };
} else {
// Node.js 环境:使用 ws 库
WebSocketModule = await import('ws');
}
const WebSocket = WebSocketModule.WebSocket;
export interface KeepAliveConfig {
wsUrl: string;
@@ -31,6 +42,7 @@ export class WSKeepAlive {
private pingTimer: NodeJS.Timeout | null = null;
private messageHandlers: Set<MessageHandler> = new Set();
private url: URL;
private readonly isBun: boolean;
constructor(config: KeepAliveConfig) {
this.config = {
@@ -48,6 +60,7 @@ export class WSKeepAlive {
debug: config.debug ?? false,
};
this.url = new URL(this.config.wsUrl);
this.isBun = typeof Bun !== 'undefined';
}
private log(message: string) {
@@ -107,44 +120,91 @@ export class WSKeepAlive {
}
});
this.ws.on("open", () => {
debug && this.log("Connected!");
this.reconnectAttempts = 0;
this.config.onConnect();
this.startPing();
});
if (this.isBun) {
// Bun 环境:使用标准 Web API
const ws = this.ws as any;
ws.onopen = () => {
debug && this.log("Connected!");
this.reconnectAttempts = 0;
this.config.onConnect();
this.startPing();
};
this.ws.on("message", (data: any) => {
if (Buffer.isBuffer(data)) {
const parsed = this.parseMessage(data);
this.config.onMessage(parsed?.raw ?? data);
ws.onmessage = async (event: MessageEvent) => {
let data: Buffer | string;
this.messageHandlers.forEach(handler => {
if (parsed) handler(parsed);
});
} else {
this.config.onMessage(data);
}
});
if (event.data instanceof Blob) {
data = Buffer.from(await event.data.arrayBuffer());
} else if (event.data instanceof ArrayBuffer) {
data = Buffer.from(event.data);
} else if (typeof event.data === 'string') {
data = event.data;
} else {
data = Buffer.from(event.data);
}
this.ws.on("close", (code: number) => {
debug && this.log(`Disconnected (code: ${code})`);
this.stopPing();
this.config.onDisconnect(code);
this.handleReconnect();
});
this.handleMessage(data);
};
this.ws.on("error", (err: Error) => {
debug && this.log(`Error: ${err.message}`);
this.config.onError(err);
});
ws.onclose = (event: CloseEvent) => {
debug && this.log(`Disconnected (code: ${event.code})`);
this.stopPing();
this.config.onDisconnect(event.code);
this.handleReconnect();
};
ws.onerror = (event: Event) => {
debug && this.log(`Error: ${event}`);
this.config.onError(new Error("WebSocket error"));
};
} else {
// Node.js (ws 库):使用 EventEmitter 模式
const ws = this.ws as any;
ws.on("open", () => {
debug && this.log("Connected!");
this.reconnectAttempts = 0;
this.config.onConnect();
this.startPing();
});
ws.on("message", (data: any) => {
this.handleMessage(data);
});
ws.on("close", (code: number) => {
debug && this.log(`Disconnected (code: ${code})`);
this.stopPing();
this.config.onDisconnect(code);
this.handleReconnect();
});
ws.on("error", (err: Error) => {
debug && this.log(`Error: ${err.message}`);
this.config.onError(err);
});
}
}
// 统一的消息处理方法
private handleMessage(data: Buffer | string) {
if (Buffer.isBuffer(data)) {
const parsed = this.parseMessage(data);
this.config.onMessage(parsed?.raw ?? data);
this.messageHandlers.forEach(handler => {
if (parsed) handler(parsed);
});
} else {
this.config.onMessage(data);
}
}
private startPing() {
this.stopPing();
this.pingTimer = setInterval(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.ping();
// 使用 JSON 格式的 ping 消息,兼容 Bun 和 Node.js
this.ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
this.log("Sent ping");
}
}, this.config.pingInterval);