npm package: @loyalguru/embed-sdk
Description: SDK to embed Loyal Guru White Label modules (loyalty, CDP, coupons, login) into external websites and apps using an iframe-based approach.
Author: Loyal Guru
Latest version: 1.1.2

# WhiteLabel SDK

SDK to embed Loyal Guru White Label modules (loyalty, CDP, coupons, login) into external websites and apps using an iframe-based approach.

This SDK creates and manages an `<iframe>` that points to your White Label webview (hosted in Loyal Guru infra or in your environment), sends the auth token to it, listens for resize events, and exposes hooks for token refresh and analytics events.



## Features

- Simple initialization using a container `div`
- Supports multiple modules: `loyalty`, `cdp`, `coupons`, `login`
- Secure token 
- Optional auto-resize (iframe grows/shrinks based on content height)
- Hooks for:
  - `onLoad`
  - `onError`
  - `onSize` (height/width from iframe)
  - token refresh request (`REQUEST_NEW_TOKEN`)
- GTM support via `LG_DATALAYER_EVENT` (pushes into `window.dataLayer`)



## Installation

### 1. From source (local development)

```bash
git clone https://github.com/loyalguru/WhiteLabel-SDK.git
cd WhiteLabel-SDK
npm install
```

> The project uses Rollup to build a UMD bundle (`embed-sdk.umd.js`) and TypeScript for typings.



## Build

To generate the distributable bundle:

```bash
npm run build
```

This will create the compiled files inside the `dist/` folder (according to your current `package.json`):

- `dist/embed-sdk.umd.js` → UMD bundle you can include in a `<script>`
- `dist/index.d.ts` → TypeScript declarations

Publish this to npm/GitHub Packages or serve it from a static/CDN bucket.


## Usage

Load the SDK in a plain HTML page and initialize it like this:

```html
<!DOCTYPE html>
<html>
<head>
  <title>Test SDK</title>
  <!-- Load the built SDK (UMD) -->
  <script src="embed-sdk.umd.js"></script>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <!-- Container where the iframe will be injected -->
  <div id="loyalty-container"></div>

  <script>
    // Initialize the SDK
    EmbedLoyaltyApp.init({
      containerId: 'loyalty-container',
      module: 'loyalty',              // 'loyalty' | 'cdp' | 'coupons' | 'login'
      iframeOrigin: 'http://localhost:4200', // URL where the white-label modules are hosted
      token: 'YOUR_JWT_TOKEN_HERE',   // JWT generated by your backend
      locale: 'fr',                   // optional, defaults to 'en'
      autoResize: false,              // true: iframe resizes automatically
      onLoad: () => console.log('[host] Iframe loaded'),
      onSize: ({ height }) => console.log('[host] iframe height:', height),
      onError: (err) => console.error('Loyalty iframe error', err)
    });

    // Listen for token refresh requests coming from the iframe
    EmbedLoyaltyApp.onTokenRefreshRequested(async () => {
      // fetch/refresh token from your backend
      const newToken = 'NEW_TOKEN_FROM_BACKEND';
      EmbedLoyaltyApp.refreshToken(newToken);
      console.log('Token refreshed and sent to iframe');
    });

    // Listen to size messages (alternative way)
    EmbedLoyaltyApp.onSizeRequested(({ height, width }) => {
      console.log('[host] SIZE via onSizeRequested =>', height, width);
    });
  </script>
</body>
</html>
```


## API

### `EmbedLoyaltyApp.init(config: InitConfig)`

Creates the iframe and injects it into the container.

**Config shape:**

```ts
type EmbedModule = 'loyalty' | 'cdp' | 'coupons' | 'login';

type InitConfig = {
  containerId: string; // id of the DOM element where the iframe will be appended
  module: EmbedModule; // module to load from the white-label host
  iframeOrigin: string; // e.g. https://wl.loyalguru.com
  token: string;        // JWT to authenticate user/session
  locale?: string;      // optional, defaults to 'en'
  onLoad?: () => void;
  onError?: (e: Error) => void;
  onSize?: (payload: { height: number; width?: number; origin: string }) => void;
  autoResize?: boolean; // if true, SDK updates iframe height on SIZE messages
};
```

**What it does internally:**

1. Finds the container (`document.getElementById(...)`)
2. Creates an `<iframe>` pointing to:  
   ```text
   ${iframeOrigin}/${module}
   ```
3. Adds secure-ish sandbox attributes:
   ```text
   allow-scripts, allow-same-origin, allow-popups, allow-popups-to-escape-sandbox
   ```
4. Sends the auth token to the iframe using `postMessage`:
   ```ts
   {
     type: 'AUTH_TOKEN',
     token: '...',
     locale: '...'
   }
   ```
5. Listens for messages coming from iframe:
   - `REQUEST_NEW_TOKEN`
   - `LG_DATALAYER_EVENT`
   - `SIZE`



### `EmbedLoyaltyApp.refreshToken(newToken: string)`

Updates the token stored in the SDK and sends it again to the iframe.

Use this when backend issues a new JWT (expiration, refresh flow, etc.):

```ts
EmbedLoyaltyApp.refreshToken(freshTokenFromBackend);
```



### `EmbedLoyaltyApp.onTokenRefreshRequested(callback: () => void)`

The iframe can send a message:  
```json
{ "type": "REQUEST_NEW_TOKEN" }
```
When that happens, the SDK will call to the callback. Call your backend, get a fresh token and call `refreshToken(...)`.

Example:

```ts
EmbedLoyaltyApp.onTokenRefreshRequested(async () => {
  const newToken = await fetchNewTokenSomehow();
  EmbedLoyaltyApp.refreshToken(newToken);
});
```



### `EmbedLoyaltyApp.onSizeRequested(callback)`

Even if `autoResize` is off, you can still listen to size reports coming from the iframe:

```ts
EmbedLoyaltyApp.onSizeRequested(({ height, width, origin }) => {
  console.log('Iframe wants to be', height, 'px high');
});
```



### `EmbedLoyaltyApp.destroy()`

Removes the iframe from the DOM and clears internal references.

```ts
EmbedLoyaltyApp.destroy();
```



## Messages from the iframe

The SDK listens to `window.postMessage` from the iframe. Current supported messages:

- **`REQUEST_NEW_TOKEN`** -> triggers the registered `onTokenRefreshRequested` callback.
- **`LG_DATALAYER_EVENT`** -> pushes `data.payload` into `window.dataLayer` (for GTM).
- **`SIZE`** → `{ type: 'SIZE', height: number, width?: number }`  
  - If `autoResize` is `true`, the SDK updates `iframe.style.height`.
  - Also forwards this to `onSize` / `onSizeRequested`.



## Styling & Iframe behavior

The iframe is created with these default styles:

```ts
Object.assign(this.iframe.style, {
  border: 'none',
  width: '100%',
  height: autoResize ? '0px' : '100vh',
  margin: '0',
  padding: '0',
  display: 'block',
  backgroundColor: 'transparent',
  overflow: 'hidden',
  transition: 'height 0.25s ease',
  'max-height': '100vh'
});
```

If the parent host page has scroll constraints, you can override these with your own CSS.



## Development Notes

- The SDK currently **does not** strictly enforce `event.origin` in the `message` listener (the line is commented):
  ```ts
  // if (event.origin !== this.origin) return;
  ```
  In production  should keep origin checks enabled to avoid processing messages from unknown sources.

- The iframe URL is built as `${iframeOrigin}/${module}`. Make sure your white-label host actually serves those routes.

- If you plan to publish to npm as `@loyalguru/embed-sdk`, update the `repository`, `name`, and CI workflow accordingly.