> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Analytics

> Capture product events and user behavior with a type-safe, container-managed analytics API.

The `@ooneex/analytics` component tracks product events, user behavior, and metrics. It provides an `IAnalytics` interface and a `@decorator.analytics()` decorator so you can build typed, purpose-specific tracking classes, register them with the container, and send events to whatever backend you choose.

## Why this component

* **One method to learn.** `capture()` records an event from a typed options object.
* **Type-safe events.** Each analytics class defines its own capture options type instead of an untyped bag of fields.
* **Container-managed.** Register with a decorator and resolve from the container — no manual wiring.
* **Backend agnostic.** Implement `IAnalytics` to target any sink (a data warehouse, an HTTP endpoint, a queue) while keeping the same call sites.

## How it works

You define an analytics class, implement `capture()` with your tracking logic, and register it with `@decorator.analytics()`. The container manages its lifecycle; callers resolve the class and call `capture()` with a typed options object.

```
container.get(MyAnalytics)        // resolve the registered class
  → analytics.capture(options)    // your implementation forwards the event
  → your backend                  // warehouse, HTTP endpoint, queue, ...
```

Because the interface is the only contract, you can swap or stub the backend without touching any call site.

## Decorator and usage

### `@decorator.analytics()`

Registers an analytics class with the container. It accepts an optional scope (defaults to singleton).

```typescript theme={null}
import { decorator } from "@ooneex/analytics";
import type { IAnalytics } from "@ooneex/analytics";

type PageViewOptionsType = {
  userId: string;
  page: string;
};

@decorator.analytics()
export class PageViewAnalytics implements IAnalytics<PageViewOptionsType> {
  public capture(options: PageViewOptionsType): void {
    // Tracking logic for page views.
  }
}
```

Resolve it from the container and call `capture()`:

```typescript theme={null}
import { container } from "@ooneex/container";

const analytics = container.get(PageViewAnalytics);
analytics.capture({ userId: "user-123", page: "/pricing" });
```

Choose a non-default scope when you need a fresh instance per request:

```typescript theme={null}
import { EContainerScope } from "@ooneex/container";

@decorator.analytics(EContainerScope.Request)
export class RequestAnalytics implements IAnalytics {
  public capture(options: Record<string, unknown>): void {
    // New instance per HTTP request.
  }
}
```

## Capturing events

`capture()` takes the typed options object your class defines — model it on the event you track. Keep one class per concern so each has a precise shape.

```typescript theme={null}
type PurchaseOptionsType = {
  userId: string;
  productId: string;
  price: number;
  currency: string;
};

@decorator.analytics()
export class PurchaseAnalytics implements IAnalytics<PurchaseOptionsType> {
  public capture(options: PurchaseOptionsType): void {
    // Forward the event to your backend.
  }
}
```

```typescript theme={null}
const analytics = container.get(PurchaseAnalytics);
analytics.capture({
  userId: "user-123",
  productId: "prod-456",
  price: 99.99,
  currency: "USD",
});
```

## Exceptions

The component ships `AnalyticsException` for signaling analytics failures from your implementation. It carries a machine-readable `key`, a human-readable `message`, and a `data` object, so callers can branch on the key.

```typescript theme={null}
import { AnalyticsException } from "@ooneex/analytics";

@decorator.analytics()
export class PurchaseAnalytics implements IAnalytics<PurchaseOptionsType> {
  public capture(options: PurchaseOptionsType): void {
    if (!options.userId) {
      throw new AnalyticsException("Missing user id", "USER_ID_REQUIRED", {
        event: "purchase_completed",
      });
    }
    // Forward the event to your backend.
  }
}
```

Catch it at the call site to handle analytics errors without breaking the request:

```typescript theme={null}
import { AnalyticsException } from "@ooneex/analytics";

try {
  analytics.capture(options);
} catch (error) {
  if (error instanceof AnalyticsException) {
    logger.error(`Analytics error [${error.key}]: ${error.message}`, error.data);
  } else {
    throw error;
  }
}
```

## Best practices

* **Type your capture options.** Replace `Record<string, unknown>` with a precise options type per class so events stay consistent.
* **One class per tracked concern.** A `PageViewAnalytics`, a `CheckoutAnalytics`, etc. — small, focused classes are easier to test and reason about.
* **Name events consistently.** Use a stable convention like `snake_case` verbs (`purchase_completed`, `button_clicked`) so dashboards stay clean.
* **Keep backend credentials in the environment.** Load any keys your implementation needs from `.env`; never hard-code them.
* **`capture()` is fire-and-forget.** It returns `void` — keep it lightweight and off the request's critical path.
* **Throw `AnalyticsException` with a stable `key`.** Callers branch on the key; keep keys constant and put variable detail in `data`.

## CLI command

Scaffold an analytics class and its test file with the generator. It writes the class under `modules/<module>/src/analytics/<Name>Analytics.ts` and installs `@ooneex/analytics` if it is missing.

```bash theme={null}
# Interactive: prompts for the name
ooneex analytics:create

# Provide the name
ooneex analytics:create --name=PageView

# Target a module and overwrite
ooneex analytics:create --name=PageView --module=tracking --override
```

| Option       | Description                                                             | Default             |
| ------------ | ----------------------------------------------------------------------- | ------------------- |
| `--name`     | Analytics class name. The `Analytics` suffix is appended automatically. | Prompted if omitted |
| `--module`   | Target module the class is generated into.                              | `shared`            |
| `--override` | Overwrite an existing class without prompting.                          | `false`             |

The generated class starts from this shape, ready for you to type the options and implement `capture()`:

```typescript theme={null}
import { decorator } from "@ooneex/analytics";
import type { IAnalytics } from "@ooneex/analytics";

type CaptureOptionsType = Record<string, unknown>;

@decorator.analytics()
export class PageViewAnalytics<T extends CaptureOptionsType = CaptureOptionsType>
  implements IAnalytics<T>
{
  public capture(options: T): void {
    // console.log("Analytics captured:", options);
  }
}
```

See [analytics:create](/cli/commands/analytics-create) for the full command reference.

## Use with Claude and Codex

The generator ships a matching `analytics:create` skill. It runs the scaffold and then guides your AI agent through completing the class — defining a proper capture options type and implementing the tracking logic in `capture()`. Initialize the skills once for your agent:

<Tabs>
  <Tab title="Claude">
    ```bash theme={null}
    ooneex claude:init
    ```

    Then ask Claude in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Add analytics for page views that captures the user id and page path.
    ```
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    ooneex codex:init
    ```

    Then ask Codex in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Add analytics for page views that captures the user id and page path.
    ```
  </Tab>
</Tabs>

For example, the prompt above maps to `analytics:create --name=PageView`, then types the capture options and implements `capture()`.
