import { EventEmitter } from "eventemitter3";
/**
* Simple WebSocket client with event emitter
*/
export class WebSocketClient extends EventEmitter {
ws;
connectionId;
userId;
investigationId;
logger;
/**
* Create a WebSocket client
* @param {WebSocket} ws - WebSocket instance
* @param {string} connectionId - Connection ID
* @param {string} userId - User ID
* @param {string | null} investigationId - Investigation ID
* @param {Logger} logger - Logger instance
*/
constructor(ws, connectionId, userId, investigationId, logger) {
super();
this.ws = ws;
this.connectionId = connectionId;
this.userId = userId;
this.investigationId = investigationId;
this.logger = logger;
ws.on("close", (code, reason) => {
this.logger.info({ code, reason: reason?.toString("utf8") }, "WebSocket client closed");
});
ws.on("error", (error) => {
this.logger.error({ error }, "WebSocket client error");
});
// Listen for incoming messages
ws.on("message", (rawData) => {
try {
let text;
if (Buffer.isBuffer(rawData)) {
text = rawData.toString("utf8");
}
else if (Array.isArray(rawData)) {
text = Buffer.concat(rawData).toString("utf8");
}
else if (rawData instanceof ArrayBuffer) {
text = Buffer.from(rawData).toString("utf8");
}
else {
text = String(rawData);
}
const parsed = JSON.parse(text);
const message = parsed;
if (typeof message === "object" &&
message !== null &&
typeof message.type === "string" &&
message.data !== undefined) {
this.emit(message.type, message.data);
}
}
catch (error) {
logger.error({ error }, "Failed to parse message");
this.send("error", { message: "Invalid JSON", code: "PARSE_ERROR" });
}
});
}
/**
* Send an event to the client
* @param {string} event - Event name
* @param {unknown} data - Event data
*/
send(event, data) {
if (this.ws.readyState === 1) {
try {
this.ws.send(JSON.stringify({ type: event, data }), (error) => {
if (error) {
this.logger.error({ error, event }, "Failed to send websocket message");
}
});
}
catch (error) {
this.logger.error({ error, event }, "Websocket send threw an error");
}
}
}
}
Source