diff --git a/README.md b/README.md index 4406201..150823d 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,14 @@ Obscura embeds V8 directly. Use `--v8-flags` to pass raw flags through to V8, sa obscura --v8-flags "--max-old-space-size=4096" fetch ``` +### Heavy SPAs (script execution budget) + +Obscura caps the page's script-execution phase so one slow or hung page cannot stall a worker. The default budget is 30s; pages that finish sooner return immediately, so the cap only affects pages that keep running. A very heavy React/Vue/Angular SPA on a slow network can need more time to boot before it fires its data requests. Raise the budget with `OBSCURA_SCRIPT_DEADLINE_MS` (milliseconds), and pair it with a matching navigation timeout in your CDP client: + +```bash +OBSCURA_SCRIPT_DEADLINE_MS=60000 obscura serve --port 9222 +``` + ### `obscura serve` Start a CDP WebSocket server. diff --git a/crates/obscura-browser/src/page.rs b/crates/obscura-browser/src/page.rs index 1735562..671241d 100644 --- a/crates/obscura-browser/src/page.rs +++ b/crates/obscura-browser/src/page.rs @@ -369,12 +369,21 @@ impl Page { let document_base = self.resolve_base_url(); // Soft deadline on the entire script-execution phase. Heavy SPAs // (GitHub, Linear, CodeSandbox) ship 50+ scripts and our serial - // fetch + execute loop can blow past a 25s Puppeteer goto timeout. - // Override via OBSCURA_SCRIPT_DEADLINE_MS for slow networks. + // fetch + execute loop can blow past a Puppeteer/Playwright goto + // timeout. The old 10s default was too tight: a heavy React/Vue/Angular + // SPA had its remaining scripts skipped before the app booted, so it + // never fired its XHR/fetch calls and page.on('response') saw nothing + // (issue #361). Only pages that actually run past the deadline are + // affected; fast pages finish and return well before it, so a larger + // budget costs them nothing. 30s gives an app room to initialize while + // the per-phase watchdog (armed at this + 1s) still bounds a real + // synchronous hang. Raise it further with OBSCURA_SCRIPT_DEADLINE_MS= + // for very heavy SPAs on slow networks (pair it with a matching client + // navigation timeout). let script_deadline_ms: u64 = std::env::var("OBSCURA_SCRIPT_DEADLINE_MS") .ok() .and_then(|s| s.parse().ok()) - .unwrap_or(10_000); + .unwrap_or(30_000); let script_deadline = tokio::time::Instant::now() + tokio::time::Duration::from_millis(script_deadline_ms);