All files / server/routes/simulation ws-output-buffer.ts

100% Statements 23/23
85.71% Branches 12/14
100% Functions 6/6
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65                        464x 457x         22x             72x 72x 72x   72x 72x 31x         58x 58x 26x     32x 32x 70x   32x 32x 58x   58x               76x 76x 1x   76x      
import { WebSocket } from "ws";
import { WSMessageType, type ServerToClientWSMessage } from "@shared/schema";
 
type SerialBufferState = {
  lines: Array<{ data: string; isComplete: boolean }>;
  flushTimer: NodeJS.Timeout | null;
};
 
export function sendMessageToClient(
  ws: WebSocket,
  message: ServerToClientWSMessage,
): void {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(message));
  }
}
 
export class WsOutputBuffer {
  private readonly clientSerialBuffers = new Map<WebSocket, SerialBufferState>();
 
  sendSerialOutputBatched(
    ws: WebSocket,
    line: string,
    isComplete?: boolean,
  ): void {
    let bufferState = this.clientSerialBuffers.get(ws);
    bufferState ??= { lines: [], flushTimer: null };
    this.clientSerialBuffers.set(ws, bufferState);
 
    bufferState.lines.push({ data: line, isComplete: isComplete ?? true });
    bufferState.flushTimer ??= setTimeout(() => {
      this.flushSerialOutputBuffer(ws);
    }, 50);
  }
 
  flushSerialOutputBuffer(ws: WebSocket): void {
    const bufferState = this.clientSerialBuffers.get(ws);
    if (!bufferState || bufferState.lines.length === 0) {
      return;
    }
 
    bufferState.flushTimer = null;
    const combinedData = bufferState.lines
      .map((lineObj) => (lineObj.isComplete ? `${lineObj.data}\n` : lineObj.data))
      .join("");
    const lastLine = bufferState.lines.at(-1);
    const finalIsComplete = lastLine?.isComplete ?? true;
    bufferState.lines = [];
 
    sendMessageToClient(ws, {
      type: WSMessageType.SERIAL_OUTPUT,
      data: combinedData,
      isComplete: finalIsComplete,
    });
  }
 
  clearClient(ws: WebSocket): void {
    const bufferState = this.clientSerialBuffers.get(ws);
    if (bufferState?.flushTimer) {
      clearTimeout(bufferState.flushTimer);
    }
    this.clientSerialBuffers.delete(ws);
  }
}