Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions driver_patches/framesPatch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
import { type Project, SyntaxKind } from "ts-morph";
import { assertDefined } from "./utils.ts";

// URL patterns excluded from networkidle calculations.
// These are captcha providers, analytics, tracking, fraud detection, and session
// heartbeat endpoints that poll continuously and would prevent networkidle from firing.
const NETWORKIDLE_EXCLUDED_URL_PATTERNS = [
// -- Captcha providers --
'challenges.cloudflare.com',
'google.com/recaptcha',
'www.gstatic.com/recaptcha',
'hcaptcha.com',
'api.funcaptcha.com',
'client-api.arkoselabs.com',
// -- Analytics & tracking --
'google-analytics.com',
'googletagmanager.com',
'analytics.google.com',
// -- Session recording & heatmaps --
'hotjar.com',
'fullstory.com',
'logrocket.com',
'mouseflow.com',
'clarity.ms',
// -- Telemetry & monitoring --
'browser-intake-datadoghq.com',
'sentry.io',
'newrelic.com',
'nr-data.net',
// -- Fraud detection --
'forter.com',
// -- Common polling/heartbeat patterns --
'/heartbeat',
'/keepalive',
'/keep-alive',
'/beacon',
];

// ----------------
// server/frames.ts
// ----------------
Expand Down Expand Up @@ -29,6 +64,30 @@ export function patchFrames(project: Project) {
"frame._isolatedWorld = undefined;"
]);

// -- _inflightRequestStarted Method (captcha networkidle exclusion) --
const inflightStartedMethod = frameManagerClass.getMethodOrThrow("_inflightRequestStarted");
const inflightStartedBody = inflightStartedMethod.getBodyOrThrow().asKindOrThrow(SyntaxKind.Block);
const faviconCheckStart = assertDefined(
inflightStartedBody.getStatements().find(s => s.getText().includes('request._isFavicon')),
'_isFavicon check in _inflightRequestStarted'
);
inflightStartedBody.insertStatements(faviconCheckStart.getChildIndex() + 1, [
`const _reqUrl = request.url();`,
`if (${JSON.stringify(NETWORKIDLE_EXCLUDED_URL_PATTERNS)}.some(p => _reqUrl.includes(p))) return;`,
]);

// -- _inflightRequestFinished Method (captcha networkidle exclusion) --
const inflightFinishedMethod = frameManagerClass.getMethodOrThrow("_inflightRequestFinished");
const inflightFinishedBody = inflightFinishedMethod.getBodyOrThrow().asKindOrThrow(SyntaxKind.Block);
const faviconCheckFinish = assertDefined(
inflightFinishedBody.getStatements().find(s => s.getText().includes('request._isFavicon')),
'_isFavicon check in _inflightRequestFinished'
);
inflightFinishedBody.insertStatements(faviconCheckFinish.getChildIndex() + 1, [
`const _reqUrl = request.url();`,
`if (${JSON.stringify(NETWORKIDLE_EXCLUDED_URL_PATTERNS)}.some(p => _reqUrl.includes(p))) return;`,
]);

// ------- Frame Class -------
const frameClass = framesSourceFile.getClassOrThrow("Frame");
// Add Properties to the Frame Class
Expand Down