mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 10:06:21 +00:00
* fix(capture): bound scroll/evaluate timeouts so heavy pages keep capturing Fonts/Vercel-class sites were failing after navigation when a single in-page scroll/evaluate hung until protocolTimeout. Drive lazy scroll from Node, degrade on evaluate timeouts, and stop labeling those failures as bot blocks. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(capture): enforce stage budgets and surface evaluate degradation Bound each scroll/content evaluate with remaining stage time so a wedged page.evaluate cannot outlive the advertised 15s/8s budgets, skip recovery CDP calls after expiry, and return/propagate timed-out animation and screenshot work so caller warnings are reachable. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(capture): prefer TimeoutError instanceof for timeout classification Use puppeteer-core TimeoutError as the primary signal for navigation vs evaluate/protocol timeouts, with message checks only as a fallback for string formatting and non-TimeoutError cases. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { isNavigationTimeoutError } from "./captureTimeout.js";
|
|
|
|
export { isNavigationTimeoutError };
|
|
|
|
export const NETWORK_IDLE_ATTEMPT_MS = 30_000;
|
|
|
|
export type CaptureGotoWaitUntil = "networkidle2" | "domcontentloaded";
|
|
|
|
export interface CaptureGotoOptions {
|
|
waitUntil: CaptureGotoWaitUntil;
|
|
timeout: number;
|
|
}
|
|
|
|
export interface CaptureGotoPage<TResponse = unknown> {
|
|
goto(url: string, options: CaptureGotoOptions): Promise<TResponse>;
|
|
}
|
|
|
|
export interface NavigateForCaptureResult<TResponse = unknown> {
|
|
response: TResponse;
|
|
waitUntil: CaptureGotoWaitUntil;
|
|
networkIdleTimeoutMs: number;
|
|
fellBackFromNetworkIdle: boolean;
|
|
}
|
|
|
|
export function networkIdleAttemptTimeoutMs(totalTimeoutMs: number): number {
|
|
return Math.min(NETWORK_IDLE_ATTEMPT_MS, Math.max(0, totalTimeoutMs));
|
|
}
|
|
|
|
export async function navigateForCapture<TResponse>(
|
|
page: CaptureGotoPage<TResponse>,
|
|
url: string,
|
|
totalTimeoutMs: number,
|
|
): Promise<NavigateForCaptureResult<TResponse>> {
|
|
const networkIdleTimeoutMs = networkIdleAttemptTimeoutMs(totalTimeoutMs);
|
|
try {
|
|
const response = await page.goto(url, {
|
|
waitUntil: "networkidle2",
|
|
timeout: networkIdleTimeoutMs,
|
|
});
|
|
return {
|
|
response,
|
|
waitUntil: "networkidle2",
|
|
networkIdleTimeoutMs,
|
|
fellBackFromNetworkIdle: false,
|
|
};
|
|
} catch (err) {
|
|
if (!isNavigationTimeoutError(err) || networkIdleTimeoutMs >= totalTimeoutMs) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
const fallbackTimeoutMs = totalTimeoutMs - networkIdleTimeoutMs;
|
|
const response = await page.goto(url, {
|
|
waitUntil: "domcontentloaded",
|
|
timeout: fallbackTimeoutMs,
|
|
});
|
|
return {
|
|
response,
|
|
waitUntil: "domcontentloaded",
|
|
networkIdleTimeoutMs,
|
|
fellBackFromNetworkIdle: true,
|
|
};
|
|
}
|