Understanding the Element Resolution Pipeline in Ego-Lite
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 that bridges high-level user interactions with low-level browser internals. Located primarily in 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
nthorlastmodifiers for disambiguation
This parsing logic is implemented at the start of parseLocator in 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:
- Look up the ref ID using
refMap.get(refId) - If a
backendNodeIdexists, executeDOM.getBoxModel(for coordinates) orDOM.resolveNode(for object IDs) - 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: Core implementation containing parsing logic, resolution strategies, and error handling.ref-map.ts: Constructs the reference-to-backendNode mapping from DOM snapshots.ref-state.ts: Maintains mutable snapshot state powering the reference map.cdp-eval.ts: Abstraction layer for CDP commands (Runtime.evaluate,DOM.getBoxModel,DOM.resolveNode).locator-query.ts: Generates DOM query expressions for CSS and text-based locators.helpers.ts: Public API wrappers (click,hover,waitForSelector) that invoke the resolver.
Practical Code Examples
Resolving Center Coordinates from CSS Selectors
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
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
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.tstranslates 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 (
transientvspermanent) 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →