Skip to content

Commit 3570113

Browse files
authored
fix(cf): add slow fallback token poll for managed/invisible widgets (#117)
Managed/invisible Turnstile widgets (e.g. Ahrefs) auto-solve after 30-60s but the fast 20ms hook poll stops once hooks are installed. The render() callback handles most cases, but managed widgets can solve after the fast poll exits — leaving only auto_navigation (page navigates) to resolve, causing 50-60s ghost traces in cf.resolutionRace. Add a 1s getResponse() fallback poll (90s lifetime) that catches tokens from widgets solved after the fast poll stops. This should eliminate the remaining ghost traces not fixed by PR #116's bridge event buffering.
1 parent befb49d commit 3570113

1 file changed

Lines changed: 20 additions & 1 deletion

File tree

src/browser/turnstile-hooks.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export function setupTurnstileHooks(emit: Emit): void {
119119
// Poll for turnstile creation — CF's api.js loads async and creates
120120
// window.turnstile. Fast poll (20ms) to hook render() before first call.
121121
// Abort immediately if _cf_chl_opt detected (CF challenge page).
122+
let hooksInstalled = false;
122123
const pollId = setInterval(() => {
123124
if (isCFChallengePage()) { clearInterval(pollId); return; }
124125
if (window.turnstile) {
@@ -127,11 +128,29 @@ export function setupTurnstileHooks(emit: Emit): void {
127128
emit({ type: 'timing', event: 'api_loaded', ts: Date.now() });
128129
}
129130
hookAndCheck(window.turnstile, emit);
130-
if (window.turnstile.__cbHooked && window.turnstile.__grHooked) clearInterval(pollId);
131+
if (window.turnstile.__cbHooked && window.turnstile.__grHooked) {
132+
hooksInstalled = true;
133+
clearInterval(pollId);
134+
}
131135
}
132136
}, 20);
133137
setTimeout(() => {
134138
clearInterval(pollId);
135139
document.removeEventListener('load', captureLoadHandler, true);
136140
}, 30000);
141+
142+
// Slow fallback poll — catches tokens from managed/invisible widgets that
143+
// auto-solve AFTER the fast poll stops. The render() callback wrapper handles
144+
// most cases, but managed widgets can solve after 30-60s. Without this poll,
145+
// the token is only detected when the page navigates (auto_navigation),
146+
// causing 50-60s ghost traces in cf.resolutionRace.
147+
const tokenPollId = setInterval(() => {
148+
if (tokenReported || isCFChallengePage()) { clearInterval(tokenPollId); return; }
149+
if (!hooksInstalled) return; // wait for hooks first
150+
try {
151+
const token = window.turnstile?.getResponse?.();
152+
if (token) reportSolved(emit, token);
153+
} catch (_) {}
154+
}, 1000);
155+
setTimeout(() => clearInterval(tokenPollId), 90000);
137156
}

0 commit comments

Comments
 (0)