aboutsummaryrefslogtreecommitdiff
path: root/src/cdp
diff options
context:
space:
mode:
authoramankumarsingh77 <amankumarsingh7702@gmal.com>2026-05-02 15:55:44 +0530
committeramankumarsingh77 <amankumarsingh7702@gmal.com>2026-05-02 15:55:44 +0530
commitd3d86f9de1cd3465cd1ec1e9a34c36b2f4878694 (patch)
tree80c8c96f6645c03f41219a304be70b2eb76a4dce /src/cdp
parent71228dea254b5e817477b7c0f6a95213a3b84ad3 (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.ts24
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 });