# Understanding the Element Resolution Pipeline in Ego-Lite

> Discover Ego-Lite's element resolution pipeline. Learn how it transforms selectors into coordinates via parsing snapshot mapping and CDP commands for efficient browser automation.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-24

---

**Ego-Lite's element resolution pipeline converts high-level selectors, references, and locators into concrete browser coordinates or object IDs through a multi-stage process involving parsing, snapshot mapping, and Chrome DevTools Protocol (CDP) command execution.**

The **element resolution pipeline** is the core mechanism in [citrolabs/ego-lite](https://github.com/citrolabs/ego-lite) that bridges high-level user interactions with low-level browser internals. Located primarily in [`package/ego-browser/src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts), this pipeline translates CSS selectors, XPath expressions, accessibility role locators, and numeric references into actionable targets that the browser runtime can interact with via CDP.

## How the Element Resolution Pipeline Works

The pipeline operates through five distinct stages, each handling specific aspects of target identification and validation.

### Stage 1: Input Parsing and Locator Classification

The resolution process begins by analyzing the raw input string to determine its type. The `parseRef` function checks if the argument follows the reference syntax (e.g., `@23`), while `parseLocator` handles standard selector strings.

For non-ref inputs, `parseLocator` analyzes the string and returns a **locator** object containing:

- **kind**: The selector type (`css`, `xpath`, `role`, `text`, `label`, `query`, etc.)
- **Indexing parameters**: Optional `nth` or `last` modifiers for disambiguation

This parsing logic is implemented at the start of `parseLocator` in [`element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/element-resolver.ts) (lines 14-97).

### Stage 2: Reference Resolution via Snapshot Mapping

When the input is a numeric reference (e.g., `@42`), the pipeline queries the **ref map**—a data structure built from the most recent DOM snapshot that maps reference IDs to backend node IDs.

The resolution flow for references:

1. Look up the ref ID using `refMap.get(refId)`
2. If a `backendNodeId` exists, execute `DOM.getBoxModel` (for coordinates) or `DOM.resolveNode` (for object IDs)
3. If the node is stale or lacks a box model, fall back to role/name lookup via the Accessibility tree

This logic is handled in `resolveElementCenter` (lines 70-103) and `resolveElementObjectId` (lines 56-86).

### Stage 3: Locator Strategy Implementation

For direct selectors (non-ref inputs), the pipeline branches based on the locator's `kind`:

**Role-based locators** leverage the Accessibility tree via `Accessibility.getFullAXTree`, using helper functions like `findBackendNodeIdByRoleName` and `findUniqueBackendNodeIdByRoleName` to locate the matching backend node.

**CSS, XPath, text, and query locators** generate optimized JavaScript snippets through `buildLocatorCenterJs`, `buildLocatorFindJs`, or `buildLocatorCountJs`. These snippets execute inside the target page via `Runtime.evaluate`, returning element coordinates or handles without requiring full DOM serialization.

Locator resolution functions reside in `resolveLocatorCenter` (lines 50-75) and `resolveLocatorObjectId` (lines 33-55).

### Stage 4: Box Model Extraction and Coordinate Calculation

Once a backend node ID is identified, the pipeline retrieves the element's geometry using `DOM.getBoxModel`, which returns the eight coordinates defining the element's border box. The `boxModelCenter` function (lines 53-68) calculates the precise center point from these coordinates.

If the box model is unavailable (element not yet rendered or detached), the function throws a **transient** error, signaling to calling helpers like `waitForSelector` that a retry attempt should be made.

### Stage 5: Error Handling and Retry Classification

All resolution paths wrap failures in **`ElementResolutionError`**, which includes a `kind` property distinguishing between:

- **`transient`**: Retryable errors (element not rendered, timing issues)
- **`permanent`**: Non-retryable errors (selector matched multiple elements, invalid syntax)

Helpers interpret these classifications to determine polling behavior. Error construction utilities include `selectorResolutionError` (lines 52-61) and `matchCountKind` (lines 46-50).

## Core Implementation Files

The element resolution pipeline spans several specialized modules:

- **[`package/ego-browser/src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts)**: Core implementation containing parsing logic, resolution strategies, and error handling.
- **[`ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/ref-map.ts)**: Constructs the reference-to-backendNode mapping from DOM snapshots.
- **[`ref-state.ts`](https://github.com/citrolabs/ego-lite/blob/main/ref-state.ts)**: Maintains mutable snapshot state powering the reference map.
- **[`cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/cdp-eval.ts)**: Abstraction layer for CDP commands (`Runtime.evaluate`, `DOM.getBoxModel`, `DOM.resolveNode`).
- **[`locator-query.ts`](https://github.com/citrolabs/ego-lite/blob/main/locator-query.ts)**: Generates DOM query expressions for CSS and text-based locators.
- **[`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts)**: Public API wrappers (`click`, `hover`, `waitForSelector`) that invoke the resolver.

## Practical Code Examples

### Resolving Center Coordinates from CSS Selectors

```typescript
const {x, y, sessionId} = await resolveElementCenter(
  cdp,                    // CDP connection instance
  mainSessionId,         // Current session identifier
  refMap,                // Snapshot reference map
  'css:#submit-button',  // CSS selector string
);
// x and y represent the clickable center of the element

```

### Resolving Object IDs from Numeric References

```typescript
const {objectId, sessionId} = await resolveElementObjectId(
  cdp,
  mainSessionId,
  refMap,
  '@42',  // Numeric reference from previous snapshot
);
// objectId is suitable for CDP methods like DOM.focus or Runtime.callFunctionOn

```

### Using Role-Based Locators with Indexing

```typescript
const {x, y} = await resolveElementCenter(
  cdp,
  sessionId,
  refMap,
  'role:button[name="Next"]',  // Accessibility role locator
);
// Throws permanent ElementResolutionError if multiple matches exist;
// use nth indexing (e.g., nth=0) to disambiguate

```

## Summary

- The **element resolution pipeline** in [`package/ego-browser/src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts) translates high-level selectors into browser-actionable targets.
- The pipeline supports **multiple input types**: numeric refs (`@23`), CSS selectors, XPath, text queries, and accessibility role locators.
- **Two return modes** are available: coordinate centers (`{x, y}`) for pointer interactions and object IDs for direct CDP manipulation.
- **Error classification** (`transient` vs `permanent`) enables intelligent retry logic in helper functions.
- **Snapshot-based ref mapping** provides stable identifiers across DOM mutations, with automatic fallback to locator resolution when references become stale.

## Frequently Asked Questions

### What is the element resolution pipeline in ego-lite?

The element resolution pipeline is the architectural component that converts human-readable selectors and numeric references into concrete browser coordinates or object identifiers. According to the citrolabs/ego-lite source code, this pipeline handles parsing, snapshot-based reference resolution, and CDP command generation to locate elements reliably across iframes and dynamic DOM states.

### How does ego-lite handle stale element references?

When a numeric reference (e.g., `@42`) resolves to a node lacking a valid box model or backend node ID, the pipeline falls back to role or name-based lookup via the Accessibility tree. This fallback mechanism, implemented in `resolveElementCenter` and `resolveElementObjectId`, ensures that temporary DOM changes do not break automation sequences while maintaining reference stability where possible.

### What types of locators does the element resolver support?

The resolver supports **role** locators (accessibility tree queries), **CSS** selectors, **XPath** expressions, **text** content matching, **label** associations, and **query** strings. Each type triggers a specific resolution strategy: role locators use `Accessibility.getFullAXTree`, while CSS and text locators generate JavaScript evaluation snippets via `Runtime.evaluate`.

### How does ego-lite classify element resolution errors?

The pipeline throws `ElementResolutionError` instances with a `kind` property set to either **`transient`** or **`permanent`**. Transient errors indicate temporary conditions like elements not yet rendered, triggering retry logic in helpers like `waitForSelector`. Permanent errors indicate fundamental issues like ambiguous selectors or invalid syntax, causing immediate failure without retry attempts.