All files / nodejs/src/daemon socket-line.ts

100% Statements 36/36
100% Branches 8/8
100% Functions 11/11
100% Lines 36/36

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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87                  2x 2x 2x 1x   1x                                   11x 11x     11x 3x 2x 2x       11x 6x 6x 6x 5x 5x       11x 2x 2x     11x 1x 1x     11x 1x 1x             11x 3x   11x 11x 11x 11x     11x 11x 11x 11x      
import type { Socket } from 'node:net';
 
/**
 * Write a newline-delimited line to a socket.
 *
 * @param socket - The socket to write to.
 * @param line - The line to write (without trailing newline).
 */
export async function writeLine(socket: Socket, line: string): Promise<void> {
  return new Promise((resolve, reject) => {
    socket.write(`${line}\n`, (error) => {
      if (error) {
        reject(error);
      } else {
        resolve();
      }
    });
  });
}
 
/**
 * Read a single newline-delimited line from a socket.
 *
 * @param socket - The socket to read from.
 * @param timeoutMs - Optional timeout in milliseconds. When provided, rejects
 * with a timeout error if no complete line is received within the limit.
 * @returns The line read (without trailing newline).
 */
export async function readLine(
  socket: Socket,
  timeoutMs?: number,
): Promise<string> {
  return new Promise((resolve, reject) => {
    let buffer = '';
    let timer: ReturnType<typeof setTimeout> | undefined;
 
    if (timeoutMs !== undefined) {
      timer = setTimeout(() => {
        cleanup();
        reject(new Error('Socket read timed out'));
      }, timeoutMs);
    }
 
    const onData = (data: Buffer): void => {
      buffer += data.toString();
      const idx = buffer.indexOf('\n');
      if (idx !== -1) {
        cleanup();
        resolve(buffer.slice(0, idx));
      }
    };
 
    const onError = (error: Error): void => {
      cleanup();
      reject(error);
    };
 
    const onEnd = (): void => {
      cleanup();
      reject(new Error('Socket closed before response received'));
    };
 
    const onClose = (): void => {
      cleanup();
      reject(new Error('Socket closed before response received'));
    };
 
    /**
     * Remove listeners registered by this call and clear the timeout.
     */
    function cleanup(): void {
      if (timer !== undefined) {
        clearTimeout(timer);
      }
      socket.removeListener('data', onData);
      socket.removeListener('error', onError);
      socket.removeListener('end', onEnd);
      socket.removeListener('close', onClose);
    }
 
    socket.on('data', onData);
    socket.once('error', onError);
    socket.once('end', onEnd);
    socket.once('close', onClose);
  });
}