Ego-Lite Page Facade API and Fluent Locator Interface: A Complete Guide

Ego‑Lite provides a Playwright‑style page façade injected as page into agent scripts, offering browser automation methods paired with a strict, auto‑waiting fluent locator API for resilient element interaction.

The ego‑lite repository (citrolabs/ego-lite) exposes a high‑level browser automation layer designed for AI agents. The page façade API wraps Chrome DevTools Protocol (CDP) calls into familiar Playwright‑like methods, while the fluent locator interface eliminates timing bugs through automatic waiting and strict single‑element matching. This architecture lets agent scripts remain concise yet robust against dynamic page changes.


Understanding the Page Facade API

The page façade is registered in the runtime's helper context under the name page. According to the FACADE_HELP documentation in src/helpers.ts, it provides navigation, element location, waiting utilities, and direct interaction methods. The full reference displays when calling help('page') from within an agent script.

Core Navigation Methods

Method Description
await page.goto(url) Navigate to a given URL
await page.waitForURL(url, options) Wait until the page reaches a specific URL pattern
await page.url() Returns the current page URL asynchronously

Element Location Methods

Ego‑lite supports multiple strategies for finding DOM elements, all returning locator objects:

  • page.locator(selector) – CSS selector or XPath
  • page.getByText(text) – Semantic text matching
  • page.getByLabel(text) – Find by associated label
  • page.getByPlaceholder(text) – Find by placeholder attribute
  • page.getByTestId(testId) – Find by data-testid attribute

These methods are defined in src/helpers.ts within the FACADE_HELP structure at lines 810‑820.

Waiting and Interaction Utilities

// Wait for page events and states
await page.waitForEvent(event);
await page.waitForLoadState(state, options);
await page.waitForRequest(predicate);
await page.waitForResponse(predicate);

// Direct page-level interactions
await page.evaluate(expression);
await page.screenshot(options);
await page.screencast.start(options);
await page.screencast.stop();

// Input device simulation
await page.keyboard.press(key);
await page.keyboard.type(text);
await page.mouse.click(x, y);

How Locators Work as a Fluent Interface

Locators in ego‑lite are strict, auto‑waiting façades that enable method chaining. The implementation lives in src/driver/locator.ts, where each call returns either the same locator instance or a derived one with refined scope.

Locator Creation and Resolution Pipeline

The resolution process follows four stages as implemented in the source:

  1. Resolutionpage.locator(selector) instantiates a Locator object that delegates to src/element-resolver.ts for mapping selectors to unique backendNodeId values.
  2. Strictness – Each locator enforces a single DOM match. Multiple matches trigger an error until disambiguated with first(), nth(index), or last().
  3. Auto‑waiting – Before any action, the façade internally calls await locator.waitFor({ state: "visible" }) (or method‑appropriate state).
  4. Fluent chaining – Method calls compile into optimized CDP command sequences, minimizing round‑trips to the browser.

Chainable Locator Methods

Category Methods
Refinement locator(selector), filter({ hasText, has }), getByRole(), getByText()
Position selection first(), nth(index), last()
Interactions click(), hover(), dragTo(target), fill(value), clear(), press(key), check(), selectOption(value)
Property retrieval textContent(), innerText(), innerHTML(), isVisible(), isEnabled(), getAttribute(name)
Utilities screenshot(), count(), evaluate(fn, arg), evaluateAll(fn, arg), waitFor(options)

The chaining logic is documented under the "locator" entry of FACADE_HELP in src/helpers.ts at lines 811‑822.


Practical Code Examples

Basic Navigation and Element Interaction

// Navigate and verify URL
await page.goto('https://example.com');
console.log('Current URL:', await page.url());

// Fill a form using semantic locators
await page.locator('form#login')
          .getByLabel('Email')
          .fill('user@example.com');

await page.locator('form#login')
          .getByLabel('Password')
          .type('s3cr3t');

Fluent Filtering and Position Selection

// Click first button with exact "Submit" text
await page.locator('button')
          .filter({ hasText: 'Submit' })
          .first()
          .click();

// Use nth() for zero-based index selection
await page.locator('.item')
          .nth(2)  // third item
          .hover();

Event Waiting with Locator Actions

// Wait for download after clicking a link
const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.locator('a[href$=".pdf"]').click(),
]);
await download.saveAs('/tmp/report.pdf');

Key Implementation Files

File Purpose
src/helpers.ts Defines page façade and FACADE_HELP documentation
src/driver/locator.ts Implements locator chaining, auto‑waiting, and action execution
src/element-resolver.ts Maps selectors to backendNodeId and handles resolution errors
src/browser-runtime.ts Provides CDP transport layer for all façade operations

These files together create the high‑level API surface while maintaining efficient, reliable CDP communication.


Summary

  • Page façade API – Injected as page, provides Playwright‑compatible navigation, location, waiting, and interaction methods documented in src/helpers.ts.
  • Fluent locator interface – Returned by page.locator() and semantic getters, offering chainable refinement, strict single‑element matching, and automatic waiting.
  • Auto‑waiting behavior – Every locator action internally waits for element stability, eliminating explicit sleep calls.
  • Code efficiency – Method chains compile to optimized CDP sequences, reducing browser round‑trips.
  • Strictness by default – Forces explicit disambiguation when selectors match multiple elements.

Frequently Asked Questions

What is the difference between page.locator() and page.getByText()?

Both return locator objects, but page.locator() accepts raw CSS selectors or XPath expressions, while page.getByText() uses semantic text matching. Internally, getByText constructs a more resilient selector that handles visibility and exact/partial matching rules. Choose locator() for precision and getByText() for maintainability when text content is stable.

Why does my locator throw "strict mode violation"?

Ego‑lite locators enforce strict single‑element matching as implemented in src/driver/locator.ts. If your selector matches multiple DOM nodes, you must disambiguate using first(), nth(index), or last(), or refine with filter(). This design prevents accidental interactions with the wrong element and surfaces ambiguous selections immediately.

How does auto‑waiting work under the hood?

Before executing any action, the locator calls waitFor({ state: "visible" }) or a method‑appropriate state. This delegates to src/element-resolver.ts to poll the element's presence and stability via CDP. The polling continues until the condition is met or a timeout occurs, making scripts resilient to network delays and JavaScript‑driven rendering without explicit waits.

Can I use async operations inside locator.evaluate()?

Yes. The evaluate and evaluateAll methods accept functions that may return Promises. The locator awaits resolution before returning, allowing complex browser‑side logic with asynchronous dependencies. Note that the function runs in the page context, not the Node.js runtime, so it cannot access variables from your script scope without explicit argument passing.

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 →