interface PendingResponse { resolve: (frame: TResponse) => void; reject: (error: Error) => void; } export class PendingResponseRegistry { private readonly pending = new Map>(); // Deliberately no per-request timer: local framed stdio never loses frames, // so a response is bounded by the transport's silence watchdog (a dead or // wedged sidecar rejects all pending requests through `rejectAll`) rather // than by guessing how long any one request should take. waitForResponse(requestId: number): Promise { if (this.pending.has(requestId)) { throw new Error( `response waiter already registered for request ${requestId}`, ); } return new Promise((resolve, reject) => { this.pending.set(requestId, { resolve: (frame: TResponse) => { this.pending.delete(requestId); resolve(frame); }, reject: (error: Error) => { this.pending.delete(requestId); reject(error); }, }); }); } resolve(requestId: number, frame: TResponse): boolean { const pending = this.pending.get(requestId); if (!pending) { return false; } pending.resolve(frame); return true; } reject(requestId: number, error: Error): boolean { const pending = this.pending.get(requestId); if (!pending) { return false; } pending.reject(error); return true; } rejectAll(error: Error): void { for (const pending of this.pending.values()) { pending.reject(error); } this.pending.clear(); } }