diff options
| author | amankumarsingh77 <amankumarsingh7702@gmal.com> | 2026-05-02 15:55:44 +0530 |
|---|---|---|
| committer | amankumarsingh77 <amankumarsingh7702@gmal.com> | 2026-05-02 15:55:44 +0530 |
| commit | d3d86f9de1cd3465cd1ec1e9a34c36b2f4878694 (patch) | |
| tree | 80c8c96f6645c03f41219a304be70b2eb76a4dce /src/cdp | |
| parent | 71228dea254b5e817477b7c0f6a95213a3b84ad3 (diff) | |
fix(cdp): serialize event consumer; persist dialog until taken; validate page info shape
Diffstat (limited to 'src/cdp')
| -rw-r--r-- | src/cdp/session.ts | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/src/cdp/session.ts b/src/cdp/session.ts index 71f7a76..fffa241 100644 --- a/src/cdp/session.ts +++ b/src/cdp/session.ts @@ -21,6 +21,8 @@ export const createCdpSession = (transport: CdpTransport): CdpSession => { let dialog: DialogInfo | null = null; let pageInfoDirty = false; + let activeConsumer: Promise<void> = Promise.resolve(); + const consumeEvents = async (): Promise<void> => { for await (const ev of transport.events()) { if (ev.method === "Page.javascriptDialogOpening") { @@ -32,25 +34,33 @@ export const createCdpSession = (transport: CdpTransport): CdpSession => { }; continue; } - if (ev.method === "Page.javascriptDialogClosed") { - dialog = null; - continue; - } + // Page.javascriptDialogClosed is intentionally NOT cleared here — + // the dialog stays in the buffer until takeDialog() is called. + // This prevents fast dismiss flows from dropping a dialog the agent + // was about to read. (Fix for spec §7 predictability bug #2.) if (ev.method === "Page.frameNavigated" || ev.method === "Page.loadEventFired") { pageInfoDirty = true; } } }; - void consumeEvents(); + const restartConsumer = (): void => { + activeConsumer = activeConsumer.then(() => consumeEvents()).catch(() => undefined); + }; + + restartConsumer(); transport.onClose(() => { sessionId = null; targetId = null; - dialog = null; pageInfoDirty = false; - void consumeEvents(); + // Do NOT clear `dialog` here — same rationale as inside consumeEvents: + // the agent may have a pending takeDialog() call that should still see it. + restartConsumer(); }); + // TODO(perf): the four enable calls are sequential here for predictability. + // Switching to Promise.all over a single WS pipelines the round-trips and + // saves ~3× on tab-switch latency. Defer until session.ts has tests. const enableDomains = async (sid: string): Promise<void> => { for (const d of ["Page", "DOM", "Runtime", "Network"]) { await transport.request(`${d}.enable`, {}, { sessionId: sid }); |
