58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
import type { Socket } from "net";
|
|
|
|
/**
|
|
* Write a length-prefixed message to a socket.
|
|
* Format: 4-byte big-endian length + JSON payload
|
|
*/
|
|
export function writeMessage(socket: Socket, msg: unknown): void {
|
|
const json = JSON.stringify(msg);
|
|
const payload = Buffer.from(json, "utf-8");
|
|
const header = Buffer.alloc(4);
|
|
header.writeUInt32BE(payload.length, 0);
|
|
socket.write(Buffer.concat([header, payload]));
|
|
}
|
|
|
|
/**
|
|
* Create a message reader that handles partial reads.
|
|
* Calls onMessage for each complete message received.
|
|
* Protocol or handler errors are reported to onError so the caller can close the socket.
|
|
*/
|
|
export function createMessageReader(
|
|
onMessage: (msg: unknown) => void,
|
|
onError: (error: Error) => void,
|
|
) {
|
|
let buffer = Buffer.alloc(0);
|
|
|
|
return (data: Buffer) => {
|
|
buffer = Buffer.concat([buffer, data]);
|
|
|
|
while (buffer.length >= 4) {
|
|
const length = buffer.readUInt32BE(0);
|
|
|
|
if (buffer.length < 4 + length) {
|
|
break;
|
|
}
|
|
|
|
const payload = buffer.subarray(4, 4 + length);
|
|
buffer = buffer.subarray(4 + length);
|
|
|
|
let msg: unknown;
|
|
try {
|
|
msg = JSON.parse(payload.toString("utf-8"));
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
onError(new Error(`Failed to parse intercom message: ${message}`, { cause: error }));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
onMessage(msg);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
onError(new Error(`Failed to handle intercom message: ${message}`, { cause: error }));
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
}
|