diff options
| author | amankumarsingh77 <amankumarsingh7702@gmail.com> | 2026-07-28 22:08:16 +0530 |
|---|---|---|
| committer | Aman Kumar <145339995+amankumarsingh77@users.noreply.github.com> | 2026-08-02 19:12:46 +0530 |
| commit | b913f2d4af218ae61de7073778feb56da89a15de (patch) | |
| tree | d1c0f6796b3756563284c8edfd8be46a54e4f221 /src/cdp/pending-requests.ts | |
| parent | c88082515dcd9c0df1328b85420e97d7286ad428 (diff) | |
refactor: daemon internals, and fix a leak the merge exposed
IdMultiplexer and EventRouter were the only classes in src/; they are now
createIdMultiplexer/createEventRouter closures like every other factory
here. The multiplexer kept {clientId, localId} per daemon id while the
bridge kept {send, clientId, localId, timer, isAttach} under the same key
— two maps of overlapping data, and mux.resolve()'s return value was
discarded at three of four call sites. One map now holds the union.
That merge fixes a real leak: removeClient cleared the multiplexer but
never the callbacks map, so a disconnected client's timers stayed armed
and later fired a response at a socket that was gone. clearClient now
cancels them, and a test pins it.
handleRequest polled every 250ms for up to 15s per request when Chrome
was down; N queued requests now await one connect signal.
server.ts uses its own protocol's serialize/deserialize instead of
hand-rolled JSON, and broadcast no longer routes through `this` — the
only `this` in the codebase, and the one thing that breaks when the
server object is destructured.
cdp/event-queue.ts held two unrelated concerns; the request/timeout half
moves to cdp/pending-requests.ts. cdp/daemon-transport.ts is the client
half of the daemon protocol, so it moves to daemon/transport.ts.
test/daemon/ needs adding to the test glob to run at all — there was no
daemon test directory before because none of this was reachable without
live Chrome.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'src/cdp/pending-requests.ts')
| -rw-r--r-- | src/cdp/pending-requests.ts | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/src/cdp/pending-requests.ts b/src/cdp/pending-requests.ts new file mode 100644 index 0000000..03b19cd --- /dev/null +++ b/src/cdp/pending-requests.ts @@ -0,0 +1,47 @@ +import { type Result, err } from "../util/result"; +import { type CdpError, cdpError } from "./errors"; + +export type CdpResult = Result<unknown, CdpError>; + +export type Pending = { + readonly resolve: (v: Result<unknown, CdpError>) => void; + readonly timer: ReturnType<typeof setTimeout>; + readonly method: string; +}; + +export const rejectAllPending = (pending: Map<number, Pending>, reason: string): void => { + for (const [, p] of pending) { + clearTimeout(p.timer); + p.resolve(err(cdpError("transport_closed", reason, p.method))); + } + pending.clear(); +}; + +export const sendWithTimeout = ( + pending: Map<number, Pending>, + id: number, + method: string, + timeoutMs: number, + timeoutLabel: string, + send: () => void, +): Promise<CdpResult> => + new Promise((resolve) => { + const timer = setTimeout(() => { + pending.delete(id); + resolve(err(cdpError("timeout", `${timeoutLabel} timeout after ${timeoutMs}ms: ${method}`, method))); + }, timeoutMs); + pending.set(id, { resolve, timer, method }); + try { + send(); + } catch (e) { + clearTimeout(timer); + pending.delete(id); + resolve(err(cdpError("transport_closed", e instanceof Error ? e.message : String(e), method))); + } + }); + +export const makeOnClose = (closeListeners: Set<() => void>) => + (cb: () => void): (() => void) => { + closeListeners.add(cb); + return () => closeListeners.delete(cb); + }; |
