# How the installEgoSdk Function Wraps Helpers with Ready Signal Handling

> Discover how citrolabs/ego-lite's installEgoSdk function ensures helpers wait for runtime initialization by wrapping them with ready signal handling.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-08-16

---

**The `installEgoSdk` function in `citrolabs/ego-lite` normalizes an optional `ready` promise and wraps every asynchronous helper via `wrapReady` so that all helper calls automatically wait for the browser runtime to initialize before executing.**

The `installEgoSdk` function serves as the main entry point for injecting Ego-Browser helpers into a target object such as `globalThis`. When you call this function with an optional **ready signal**, it guarantees that every subsequent helper invocation respects the underlying browser runtime's initialization state. This design is implemented in the `ego-browser` package within the `citrolabs/ego-lite` repository, as shown in [`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts).

## Ready Signal Normalization and Error Capture

Before any helper is exposed, `installEgoSdk` prepares the ready signal in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts). It converts the user-provided `options.ready` value into a standard promise:

```ts
const readySignal = Promise.resolve(options.ready);

```

(See line 57 of [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts).)

Immediately after creation, a `.catch` handler attaches to capture any early rejection. If the ready promise fails during initialization, the first error is stored in `readyError` (lines 58-61). This allows the SDK to propagate the exact same failure to every future helper call instead of swallowing it.

## Classifying Sync and Async Helpers

Once the ready signal is secured, `installEgoSdk` iterates over every helper provided by `helperContext()` in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts). For each name/value pair, it checks whether the helper belongs to the `SYNC_HELPERS` set:

- **Sync helpers** are exposed directly without modification.
- **Async helpers** are passed through `wrapReady`, which returns a wrapped function or object that pauses execution until `readySignal` resolves (lines 63-67).

This classification ensures that lightweight synchronous utilities remain unaffected while all asynchronous browser operations respect the ready state.

## Inside the wrapReady Implementation

The core wrapping logic lives in `wrapReady` (lines 19-47 of [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts)). It inspects the helper type and applies the appropriate wrapper.

### Wrapping Functions

If the helper is a function, `wrapReady` returns a new async function that performs three steps:

1. Awaits `readySignal`.
2. Checks `readyError` and re-throws if an initialization failure occurred.
3. Calls the original helper with the provided arguments.

This means a call like `page.goto(...)` automatically stalls until the browser session and CDP transport are fully available.

### Wrapping Objects Recursively

When the helper is an object, `wrapReady` recursively wraps every property. This ensures that nested helper namespaces also wait for the ready signal before executing any method.

### Factory Helper Special Case

A special check for `isSyncFactoryHelper` allows factory helpers that return synchronous objects to be wrapped correctly. Without this branch, factory functions could return unwrapped objects that bypass the ready check.

## Exposing Helpers on the Target

After wrapping, `installEgoSdk` defines each helper on the target object using `Object.defineProperty` (lines 67-72). This creates a clean property for every helper so that user scripts can call them as if they were native globals, while the underlying promise logic remains transparent. You can also see how the module is loaded as a library in [`package/ego-browser/src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/run.ts).

## Practical Usage Example

Here is how you install the SDK with a custom ready promise and invoke a wrapped helper:

```ts
// Example: install the SDK with a ready promise that resolves when the browser is ready
import { installEgoSdk } from "ego-browser";

const ready = new Promise<void>((resolve) => {
  // Imagine the host sets up the browser and calls resolve() when done
  setTimeout(resolve, 2_000); // simulate async init
});

installEgoSdk(globalThis, { ready });

// Later in the script – this call will wait for the ready promise first
await page.goto("https://example.com");   // `page.goto` is wrapped by installEgoSdk

```

Under the hood, the wrapper generated by `wrapReady` looks conceptually like this:

```ts
function wrapReady(value, readySignal, readyError) {
  if (typeof value === "function") {
    return async (...args) => {
      await readySignal;                     // wait for the SDK to be ready
      const err = readyError();
      if (err) throw err;                     // propagate early init errors
      return value(...args);                  // finally call the original helper
    };
  }
  // Objects are recursively wrapped so nested helpers also wait
  // …
}

```

## Summary

- `installEgoSdk` accepts an optional `ready` promise in `options.ready` and normalizes it via `Promise.resolve` in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts).
- A `.catch` handler captures the first initialization error in `readyError` so all later calls fail consistently.
- Helpers from `helperContext()` are split into synchronous and asynchronous groups; only async helpers are wrapped.
- `wrapReady` (lines 19-47) generates async wrappers for functions and recursively wraps objects, with special handling for factory helpers.
- Wrapped helpers are attached to the target via `Object.defineProperty` (lines 67-72), making the ready check transparent to consumers.

## Frequently Asked Questions

### What is the purpose of the ready signal in installEgoSdk?

The ready signal lets the host application tell the SDK when the underlying browser runtime and CDP transport are fully initialized. It prevents helpers from executing before the environment is ready, which avoids race conditions and undefined behavior.

### How does installEgoSdk handle helpers that are synchronous?

Helpers listed in the `SYNC_HELPERS` set are exposed directly on the target without being passed through `wrapReady`. They do not wait for the ready promise because they do not interact with the asynchronous browser runtime.

### What happens if the ready promise rejects?

If `options.ready` rejects, the error is captured in `readyError` inside [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts). Every subsequent call to a wrapped async helper will await the rejected promise, check `readyError`, and re-throw the same error, ensuring consistent failure handling across the SDK.

### Where are the helper definitions sourced from in ego-lite?

The helpers are supplied by `helperContext()`, which is defined in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts). `installEgoSdk` imports this context and iterates over its entries to decide which ones to wrap and expose.