fix(browser): raise script-execution budget so heavy SPAs boot (#371)

The script-execution phase had a 10s soft deadline. On a heavy SPA the
remaining scripts were skipped before the app finished booting, so it
never fired its XHR/fetch calls and page.on('response') captured zero API
responses (issue #361). The knob to extend it, OBSCURA_SCRIPT_DEADLINE_MS,
existed but was undocumented, so it was undiscoverable.

Raise the default to 30s and document the env var. Only pages that keep
running past the deadline are affected; fast pages finish and return well
before it, so the larger budget costs them nothing, and the per-phase
watchdog (armed at deadline + 1s) still bounds a real synchronous hang.
Very heavy SPAs on slow networks can raise it further with
OBSCURA_SCRIPT_DEADLINE_MS, paired with a matching client nav timeout.

Fixes #361.
This commit is contained in:
SGavrl authored and GitHub committed 2026-07-09 11:07:34 +02:00
1 parent f5d7e11692
commit fa9ba7f13d
2 files changed
+20 -3

No files matched your search

+8
View File
@@ -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 <url>
```
### 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.
+12 -3
View File
@@ -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=<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);