# `@repobit/dex-data-layer`

# BD Adobe Data Layer Utilities

BD Adobe Data Layer Utilities is an npm package that provides a robust and flexible toolkit for managing and pushing events to Adobe’s Data Layer. It offers a collection of event classes (such as button clicks, form submissions, page load events, product events, etc.) along with a centralized service to handle event merging and dispatching to the global Adobe data layer. This ensures that events are recorded consistently for analytics and tracking.

---

## Overview

This package standardizes the way web applications interact with Adobe’s Data Layer by:

- Defining a set of event classes that encapsulate common interactions (e.g., page events, product events, user actions).
- Providing a central service (`AdobeDataLayerService`) that manages the merging and pushing of these events into the global data layer (`window.adobeDataLayer`).
- Implementing a custom merge strategy for product-related events to prevent duplication and ensure data consistency.

---

## Key Features

- **Event Abstraction:**  
  A comprehensive set of event classes, each with well-defined constructors and return types, to capture various user interactions and system events.

- **Centralized Data Layer Management:**  
  The `AdobeDataLayerService` class centralizes event management by merging events (especially product events) before dispatching them to the global data layer.

- **Custom Merge Strategy:**  
  Uses a tailored merge function to consolidate multiple `ProductLoadedEvent` entries, ensuring that only unique product details are retained.

- **Seamless Global Integration:**  
  Automatically integrates with the global `window.adobeDataLayer` array, making the pushed events readily available for Adobe Tag Management and analytics tools.

- **Extensive Use Cases:**  
  Comprehensive unit tests demonstrate real-world scenarios, including page load tracking, user detection, button clicks, and purchase events.

---

## Installation

Install the package via npm or yarn:

```bash
npm install @repobit/dex-data-layer
```
# Classes and Their Constructors / Return Types

## AdobeDataLayerService
### Static class (not instantiated)

### Key Methods:

- ## push(event: DataLayerEvent): void
  - ### Input: An instance of a DataLayerEvent (e.g., ButtonClickEvent, CdpEvent, etc.).
  - ### Behavior:
    - For `ProductLoadedEvent`, the event is merged into a local data structure (using the `deepmerge` library with a custom array merge function) keyed by the event name (e.g., `product loaded`). This allows multiple product events to be consolidated, reducing redundancy.
    - For all other situations, the event is directly pushed to the global `window.adobeDataLayer`, making it immediately available for consumption by Adobe’s tag management tools.
- ## pushEventsToDataLayer(): void
  - ### Behavior:
    - Iterates through the locally stored events (like merged product events) and pushes them into the global data layer.

### Use case:
When a user action or system event occurs (for example, a button click or product load), create the corresponding event instance and call AdobeDataLayerService.push(event). For product events, later call AdobeDataLayerService.pushEventsToDataLayer() to flush the merged data.

## ButtonClickEvent
### Constructor: constructor(clickEvent: 'trial downloaded' | 'product downloaded' | string, productId: string)
### Return Type:
Returns an instance of `ButtonClickEvent` with:
- `event`: A string indicating the type of button click event.
- `product`: An object containing product details. For example, if the event is 'trial downloaded', the product object will have a trial key with an array of product IDs.

## CdpEvent
### Constructor: constructor(cdpData: CdpEventParameters)
### Return Type:
Returns an instance of `CdpEvent` with:
- `event`: Constant string `cdp data`.
- `parameters`: An object containing CDP parameters

## FormEvent
### Constructor: constructor(event: string, userObject: FormEventUserObject)
### Return Type:
Returns an instance of `FormEvent` with:
- `event`: A string representing the form event (e.g., `form completed`).
- `user`: An object containing form-specific data (such as form ID and form name).

## OneClickPurchaseEvent
### Constructor: constructor(event: string, cart: OneClickPurchaseEventCartObject, transaction: OneClickPurchaseEventTransactionObject)
### Return Type:
Returns an instance of `OneClickPurchaseEvent` with:
- `event`:  A string representing the purchase event (e.g., `purchase`).
- `cart`: An object detailing the cart (if provided).
- `transaction`: An object detailing the transaction (if provided).

## PageErrorEvent
### Constructor: constructor()
### Return Type:
Returns an instance of `PageErrorEvent` with:
- `event`:  Constant string `page error`.

## PageLoadStartedEvent
### Constructor: constructor(page: Page, pageData: PageLoadStartedDataObject)
### Return Type:
Returns an instance of `PageLoadStartedEvent` with:
- `event`: Constant string `page load started`.
- `pageInstanceID`: A value derived from the page’s environment (e.g., 'dev', 'stage', or 'prod').
- `page`: A complex object that contains detailed page information (sections, query parameters, server details, etc.) constructed from the provided Page instance and pageData. This object is easily constructed using the `Page class` from the `@repobit/dex-utils` package.
### Description:
Important to note: this event should only appear once in the project. For other cases like modals please use `WindowLoadStartedEvent`.

## PageLoadedEvent
### Constructor: constructor()
### Return Type:
Returns an instance of `PageLoadedEvent` with:
- `event`:  Constant string `page loaded`.
### Description:
Important to note: this event should only appear once in the project. For other cases like modals please use `WindowLoadedEvent`.

## ProductLoadedEvent
### Constructor: constructor(option: ProductOption | { ID: string }, type: 'all' | 'info' | 'comparison' | string)
### Return Type:
Returns an instance of `ProductLoadedEvent` with:
- `event`: Constant string `product loaded`.
- `product`: An object where keys are the product type (e.g., all, info) and the values are arrays of product details (either raw product IDs or detailed option information).
### Description:
Important to note: all the product loaded events are stored in a class variable untill a `PageLoadedEvent` is pushed. When that happens, a `ProductLoadedEvent` gets pushed before the `PageLoadedEvent`.

## UserDetectedEvent
### Constructors:
- ### constructor(page: Page, userData: UserDataObject) -> for when the page gets initialised
- ### constructor() -> for when a modal opens. This will copy the already existing UserDetectedEvent from the data layer
### Return Type:
Returns an instance of `UserDetectedEvent` with:
- `event`: Constant string `user detected`.
- `user`: An object that includes user detection details (e.g., login status, unique identifiers such as the visitorID which can be received from the getUserVisitorId function in the utils package).

## VisitorIdEvent
### Constructor: constructor(visitorId: string)
### Return Type:
Returns an instance of `VisitorIdEvent` with:
- `event`: Constant string `visitorID ready`.
- `user`: An object containing the provided `visitorId`.

## WindowLoadStartedEvent
### Constructor: constructor(pageInfo: (info: PageLoadStartedCompletePageObject['info']) => PageLoadStartedCompletePageObject['info'] | PageLoadStartedInfo, pageAttributes?: PageLoadStartedAttributes)
### Return Type:
Returns an instance of `WindowLoadStartedEvent` with:
- `event`: Constant string `page load started`.
- `pageInstanceID`: A value derived from the page’s environment (e.g., 'dev', 'stage', or 'prod').
- `page`: A complex object like the one in `PageLoadStartedEvent`. This one, however contains new data, or can construct on top of the `PageLoadStartedEvent`.
### Description:
The `WindowLoadStartedEvent` class is designed to update or extend the data from an already existing page load started event. It accesses an existing PageLoadStartedEvent stored in the global `window.adobeDataLayer` by filtering on the event property. Once found, it merges the existing page information with the new data provided via the constructor parameters:
- If pageInfo is a function, it allows a dynamic transformation on the current page info.
- If pageInfo is an object, it simply merges with the existing info.
Likewise, any provided pageAttributes override or extend the current attributes from the existing event.

## WindowLoadedEvent
### Constructor: constructor()
### Return Type:
Returns an instance of `WindowLoadedEvent` with:
- `event`:  Constant string `page loaded`.
### Description:
This class was designed to be a mirror of the `PageLoadedEvent` for modals.

---

# Usage Examples

## Page Load Started Event:

```typescript
const testPage = new Page('en-us', 'consumer', 'dev');
AdobeDataLayerService.push(new PageLoadStartedEvent(testPage, {
  name: 'en-us:consumer:solutions',
  geoRegion: 'en-us'
}));
const insertedEvent = window.adobeDataLayer[0] as PageLoadStartedEvent;
console.log(insertedEvent.event); // "page load started"
```

## User Detected Event:

```typescript
AdobeDataLayerService.push(new UserDetectedEvent(testPage, {
  ID: '9999',
  productFinding: 'consumer-page'
}));
const insertedEvent = window.adobeDataLayer[0] as UserDetectedEvent;
console.log(insertedEvent.event); // "user detected"
```

## Button Click Event:

```typescript
AdobeDataLayerService.push(new ButtonClickEvent('trial downloaded', '9999'));
const insertedEvent = window.adobeDataLayer[0] as ButtonClickEvent;
console.log(insertedEvent.event); // "trial downloaded"
```

## Product Loaded Event:
The code below is just for example purposes. Product Option is generated by the `Store` and should only be used in the context of a `Store Resolver`.

```typescript
const option = new ProductOption({ /* product details */ });
AdobeDataLayerService.push(new ProductLoadedEvent(option, 'all'));
// For products to get loaded there needs to be a page loaded event
AdobeDataLayerService.push(new PageLoadedEvent());
const insertedEvent = window.adobeDataLayer[0] as ProductLoadedEvent;
console.log(insertedEvent.event); // "product loaded"
```