39 lines
1.1 KiB
TypeScript
39 lines
1.1 KiB
TypeScript
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<string, (resp: any) => void>();
|
|
private listeners = new Set<Listener>();
|
|
|
|
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<any> {
|
|
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); }
|
|
}
|