import type { ChildProcessWithoutNullStreams } from "node:child_process"; import { attachJsonlReader } from "./line-splitter.js"; type Listener = (evt: any) => void; export class RpcClient { private seq = 0; private pending = new Map void>(); private listeners = new Set(); constructor(private child: ChildProcessWithoutNullStreams) { attachJsonlReader(child.stdout, (line) => { if (!line) return; let msg: any; try { msg = JSON.parse(line); } catch { return; } if (msg.type === "response" && msg.id && this.pending.has(msg.id)) { this.pending.get(msg.id)!(msg); this.pending.delete(msg.id); return; } for (const l of this.listeners) l(msg); }); } nextId(): string { return `c${++this.seq}`; } send(cmd: object): void { this.child.stdin.write(JSON.stringify(cmd) + "\n"); } request(cmd: object & { type: string }): Promise { const id = this.nextId(); return new Promise((resolve) => { this.pending.set(id, resolve); this.send({ ...cmd, id }); }); } on(_event: "event", cb: Listener): void { this.listeners.add(cb); } }