How ego-lite Handles Browser Session Readiness Before Executing Helpers

ego-lite guarantees a ready browser session by automatically creating CDP sessions via browserCdp() in src/state.ts, enabling required domains on-demand, and polling document.readyState until the page reaches a stable state before executing any helper.

When automating browser interactions with citrolabs/ego-lite, the framework must ensure that the Chrome DevTools Protocol (CDP) session is active and the page is in a responsive state before running high-level helpers like page.goto() or locator.click(). The library achieves this through a layered architecture that manages session lifecycle, domain enablement, and readiness polling transparently.

The State Management Layer

At the core of ego-lite's readiness guarantee is a singleton runtime state defined in src/state.ts. This module maintains the sessionId and sessionTargetId variables that track the current CDP session attachment.

When any helper invokes a CDP command, the request flows through state.send, which delegates to browserCdp() imported from src/browser-runtime.ts. If state.sessionId is null, browserCdp() automatically opens a fresh CDP session, stores its ID in state.sessionId, and records the target ID in state.sessionTargetId. This ensures that every subsequent command runs against an attached session without requiring explicit session management from the user.

Automatic Session Attachment and CDP Evaluation

The generic CDP helper cdp() exported from src/cdp-eval.ts serves as the gateway for all browser communication. This function forwards requests to state.send, which triggers the automatic session creation described above.

All high-level helpers in src/helpers.ts ultimately rely on this pipeline. Whether navigating to a URL or clicking an element, the helper calls funnel through cdp(), ensuring the session exists before the actual CDP command is transmitted.

Domain Enablement for Network and Page Events

Before network-related helpers like waitForRequest or waitForResponse execute, the driver layer must enable the appropriate CDP domains. In src/driver/waits.ts, the acquireNetworkEvents() function checks state.networkDomainEnabled and sends the Network.enable command if the domain is not yet active.

The same pattern applies to Page events when loading URLs through waitForLoadState(). The first call to these helpers enables the necessary domains and caches the enabled state in the runtime state object, preventing redundant enablement calls on subsequent operations.

Readiness Checks Before Execution

Helpers that interact with page content implement explicit readiness loops to ensure the DOM is in a stable state.

Document Load Polling

The waitForLoadState() function in src/driver/waits.ts calls waitForDocumentLoad(), which repeatedly evaluates document.readyState via Runtime.evaluate until it reaches "complete" (or the requested state). This polling mechanism guarantees that navigation helpers do not interact with the page until the browser has finished parsing the document and executing deferred scripts.

Selector Resolution Retries

For element-specific operations, waitForSelector() attempts to resolve the selector through resolveHandle(). If the element is not present, the function catches transient resolution errors, invokes state.sleep() to pause execution, and retries the resolution. This loop continues until the element appears or the timeout expires, treating resolution failures as "not ready yet" rather than hard errors.

Consistent Timeout Handling

Every readiness check respects the state.defaultTimeout value, which defaults to 10 seconds. Individual helpers can override this default per-call, ensuring that polling loops for document readiness or selector presence never hang indefinitely. This timeout is applied consistently across the driver modules (src/driver/nav.ts, src/driver/pointer.ts, etc.), providing predictable error boundaries for all automation scripts.

Practical Usage Examples

The following snippets demonstrate how ego-lite automatically handles session readiness without explicit setup:

// Navigate and wait for full page load
await page.goto('https://example.com');          // → nav.goto → cdp("Page.navigate")
await page.waitForLoadState('networkidle');      // → waits.waitForLoadState → waitForNetworkIdle
// Interact with an element only after it appears
await page.locator('#submit').waitFor({ timeout: 5000 }); // → waits.waitForSelector
await page.locator('#submit').click();                     // → pointer.click (uses existing session)

Both examples rely on the automatic session creation in browserCdp() and the readiness polling in waitForDocumentLoad() and waitForSelector().

Summary

  • Automatic Session Creation: state.send delegates to browserCdp() in src/browser-runtime.ts, which opens a new CDP session if state.sessionId is null.
  • On-Demand Domain Enablement: acquireNetworkEvents() in src/driver/waits.ts enables the Network domain when first needed; Page domains are enabled similarly during load state checks.
  • Document Readiness Polling: waitForDocumentLoad() polls document.readyState via Runtime.evaluate until the page reaches "complete".
  • Element Selection Retries: waitForSelector() uses resolveHandle() with exponential backoff via state.sleep() to wait for DOM elements.
  • Configurable Timeouts: All helpers respect state.defaultTimeout (10s default) to prevent indefinite blocking.

Frequently Asked Questions

How does ego-lite create a browser session if one doesn't exist?

When a helper invokes cdp(), the call flows through state.send to browserCdp() in src/browser-runtime.ts. If state.sessionId is null, the function automatically opens a fresh CDP session, stores the session ID in state.sessionId, and records the target ID in state.sessionTargetId before executing the requested command.

What happens if the page hasn't finished loading when a helper is called?

Helpers that require page content, such as waitForLoadState(), call waitForDocumentLoad() which polls document.readyState via Runtime.evaluate until it returns "complete". Similarly, waitForSelector() retries element resolution until the selector appears or the timeout expires, ensuring operations only proceed against stable DOM states.

Which CDP domains does ego-lite enable automatically?

The framework enables the Network domain via Network.enable when acquireNetworkEvents() is first called from network-related wait helpers, and it enables Page domains during navigation and load state monitoring. The enabled state is cached in state.networkDomainEnabled and similar flags to prevent redundant enablement calls.

Can I customize the timeout for readiness checks?

Yes. While all helpers default to state.defaultTimeout (10 seconds), you can override this on a per-call basis by passing a timeout option, as shown in page.locator('#submit').waitFor({ timeout: 5000 }). This ensures that both session creation and readiness polling respect your specified maximum wait time.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →