const encoder = new TextEncoder();
const decoder = new TextDecoder();
export const FLUSH_PKT = encoder.encode("0000");
const LENGTH_BYTES = 4;
const MAX_PKT_DATA = 65516;
export function pktLine(data: string | Uint8Array): Uint8Array<ArrayBuffer> {
const payload = typeof data === "string" ? encoder.encode(data) : data;
if (payload.byteLength > MAX_PKT_DATA) {
throw new Error("pkt-line payload too large");
}
const header = encoder.encode((payload.byteLength + LENGTH_BYTES).toString(16).padStart(LENGTH_BYTES, "0"));
return concatBytes([header, payload]);
}
export function concatBytes(chunks: Uint8Array[]): Uint8Array<ArrayBuffer> {
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
const out = new Uint8Array(total);
let offset = 0;
for (const chunk of chunks) {
out.set(chunk, offset);
offset += chunk.byteLength;
}
return out;
}
export type Packet = { kind: "data"; data: Uint8Array } | { kind: "flush" } | { kind: "delim" } | { kind: "end" };
export function parsePacket(buffer: Uint8Array, offset: number): { packet: Packet; next: number } | null {
if (buffer.byteLength - offset < LENGTH_BYTES) return null;
const length = Number.parseInt(decoder.decode(buffer.subarray(offset, offset + LENGTH_BYTES)), 16);
if (Number.isNaN(length)) throw new Error("Invalid pkt-line length");
if (length === 0) return { packet: { kind: "flush" }, next: offset + LENGTH_BYTES };
if (length === 1) return { packet: { kind: "delim" }, next: offset + LENGTH_BYTES };
if (length === 2) return { packet: { kind: "end" }, next: offset + LENGTH_BYTES };
if (buffer.byteLength - offset < length) return null;
return {
packet: { kind: "data", data: buffer.subarray(offset + LENGTH_BYTES, offset + length) },
next: offset + length,