npm package: @sesamy/sesamy-js
Description: The Sesamy browser javascript API
Latest version: 1.112.0

# sesamy-js

The Sesamy browser javascript API

The project handles analytics, authentication and communication with the Sesamy API

## Analytics

The analytics module is used to track events and page views. It is using the [GetAnalytics](https://getanalytics.io/) library to send events to the Sesamy interaction endpoint.

The following events are tracked:

- Page views, with events triggered on router updates
- Scroll events, with events triggered on scroll at 25%, 50%, 75%, and 100%
- Active and idle duration

## API

The following methods are available on the `sesamy` object:

- amendments
  - create: create an amendment to a contract
  - confirm: confirms a created amendment for a contract
- analytics
  - track: tracks an event with optional properties
- attribution
  - get: get the attributions for a user
  - set: set the attributions for a user
- auth
  - getUser: fetches the user's profile
  - isAuthenticated: checks if the user is authenticated
  - login: smart login that automatically chooses popup or redirect based on browser context
  - loginWithRedirect: redirects the user to the login page
  - logout: logs the user out
  - setToken: stores a token in session storage
  - getTokenSilently: retrieves a token without user interaction
- bills
  - get: get a bill by id
  - list: lists all the user's bills
- browser
  - detectAdblock: detects if an ad blocker is enabled
  - isInAppBrowser: detects if the browser is an in-app browser
  - isIncognito: detects if the browser is in incognito mode
- checkouts
  - create: creates a checkout session
  - get: gets a checkout session by id
  - update: updates a checkout session
- clearCache: clears the cache for the sesamy-js library
- content
  - list: get all articles based on the configured selectors
  - get: get an article based on an element or a selector
  - getLanguage: get the current language
  - unlock: retrieves the locked content using an element or a css selector
  - getPropertyFromHTML: extracts a property from HTML content
- contracts
  - cancel: cancel a contract by id
  - get: get a contract by id
  - list: lists all the user's contracts
- entitlements
  - access: fetches the user's access URL for an entitlement
  - get: gets a single entitlement by id
  - hasAccess: checks if the user has access to a specific item
  - list: lists the user's entitlements
  - signedLinks: lists signed links registered in the current session
- flags
  - get: gets a feature flag value
  - set: sets a feature flag value
  - list: lists all feature flags
  - delete: deletes a feature flag
- fulfillments
  - list: list the user's fulfillments for a SKU
  - requestDelivery: requests delivery for a fulfillment by SKU and delivery method
- generateLink: creates a link to a Sesamy hosted service such as account, change-payment, consume, or checkout. If the user is authenticated, the link will be signed so that the user can access the service without logging in again.
- getPaymentIssues: returns a list of failed payments and cards that will expire
- getVersion: returns the version of the sesamy-js library
- init: Initializes the sesamy-js library
- isReady: returns a boolean indicating whether the initialization process has completed
- log: logs a message to the console (only when debug mode is enabled)
- paywalls
  - get: get a paywall by id
- products
  - autoOnboard: trigger the auto-onboarding process for a product by SKU
  - get: get a product by SKU
  - linkSpotify: links a Spotify account to a product
- profile
  - get: fetches the user's profile
  - isSpotifyLinked: returns true if the user has linked their Spotify account
  - openHostedAccountPage: opens the hosted account page
  - unlinkSpotify: unlinks the user's Spotify account
  - update: updates the user's profile
- proxy
  - getContent: retrieves locked content using an access token and URL.
- subscriptions (deprecated, use contracts instead)
  - list: lists all the user's subscriptions
- tags
  - delete: deletes a tag for the user
  - get: fetches the user's tags
  - set: sets a tag for the user
- tallies
  - delete: delete a tally by id
  - get: get a tally for a user by id
  - list: list all tallies for a user
  - push: push an item to a tally
- transactions
  - get: get a transaction by id
  - list: lists all the user's transactions
- userMetadata
  - delete: deletes the user's metadata by id
  - get: get the user's metadata by id
  - list: list all the user's metadata
  - set: sets the user's metadata
- vendors
  - get: get the settings for the current vendor
  - list: list all publishers connected to the user. This is not available for publisher tokens

## Events

The following events are emitted by the sesamy-js library:

- `sesamyJsReady` - Emitted when the `sesamy-js` library is ready to be used
- `sesamyJsAuthenticated` - Emitted when the user is authenticated
- `sesamyJsClearCache` - Emitted when the cache is cleared
- `sesamyJsLogout` - Emitted when the user logs out
- `sesamyJsPurchase` - Emitted after a purchase is completed and passes an itemSrc property with the source of the purchase

# Url triggers

## Query parameters

The library can trigger actions based on query parameters. The following query parameters are supported:

- `sesamy-refresh`. If present, the library will clear the cache and emit a `sesamyJsClearCache` event.
- `sesamy-user`. If present, the library will validate that the user is authenticated with the correct email. If not, the user will be logged out and a new login with be initiated.
- `sesamy-login`. If present, the library will validate that the user is authenticated. If not, the user will be logged out and a new login with be initiated.
- `sesamy-purchase`. Triggers a sesamyJsPurchase event.
- `sesamy-token`. If present, the library parse the token and add any content permissions that the token grants.

# Hash triggers

The library can read the access token from the hash. It is not the preferred way of logging in a user but can be used when redirecting the user across domains where a cookie-based solution is not possible. The hash is not sent to the server so the is no risk of leaking the token. The token hash is passed like this: `#access_token=<token>`

## Usage

The script can either be initiated with a JSON object in a script tag or by calling the init method.

The script will look for a script tag with the id `sesamy-js`, and if it isn't found it will wait for a call to the init function before initializing. The only required part is the `clientId` attribute.

```html
<script type="application/json" id="sesamy-js">
  {
    "clientId": "demo"
  }
</script>
```

These are the available configuration options, with their default values:

```javascript
{
  clientId: null,
  organization: null,
  api: {
    namespace: 'sesamy',
    endpoint: 'https://api2.sesamy.com'
  },
  analytics: {
    enabled: true,
    endpoint: 'https://logs.sesamy.com/events'
  },
  auth: {
    clientId: null,
    organization: null,
    enabled: true,
    domain: null,        // Optional: custom Auth0 domain (e.g., 'auth.example.com')
    domains: []          // Optional: array of auth domain strings for multi-domain setups
  },
  content: [
    {
      type: 'article',
      path: '/articles',  // Optional: matches URLs containing this path
      pass: 'premium',    // Optional: pass requirement
      price: {           // Optional: price information
        amount: 9.99,
        currency: 'USD'
      },
      paywallUrl: 'https://example.com/paywall',  // Optional: custom paywall URL
      enablePaywallSettingsUrlFallback: false,  // Optional: enable fallback to sesamy-paywall settings-url
      selectors: {
        article: { selector: 'article' },
        image: { selector: 'img', attribute: 'src' },
        title: { selector: 'h1', attribute: 'textContent' },
        excerpt: { selector: 'p', attribute: 'textContent' },
        price: { selector: 'article', attribute: 'data-price' },
        currency: { selector: 'article', attribute: 'data-currency' },
        url: { selector: 'link', attribute: 'href' },
        id: { selector: 'article', attribute: 'data-id' },
        pass: { selector: 'article', attribute: 'data-pass' },
        }
      }
    ],
  tranforms: {
    enabled: false,
    rules: []
  }
}
```

# Authentication Configuration

The `auth` configuration object controls how the Sesamy library handles user authentication. Below are the available options:

## Basic Configuration

```javascript
{
  auth: {
    clientId: 'your-client-id',  // Required: Your Auth0 client ID
    organization: 'org_123',      // Optional: Auth0 organization ID
    enabled: true                  // Optional: Enable/disable authentication (default: true)
  }
}
```

## Custom Domain Configuration

### Single Domain

For custom Auth0 domains, use the `domain` property:

```javascript
{
  auth: {
    clientId: 'your-client-id',
    domain: 'auth.example.com'    // Optional: Custom Auth0 domain
  }
}
```

### Multiple Domains (Multi-tenant/White-label)

For applications that operate across multiple domains (e.g., white-label solutions, multi-region setups), use the `domains` array. The library will automatically select the appropriate auth domain based on the current page's top-level domain:

```javascript
{
  auth: {
    clientId: 'your-client-id',
    domains: [
      'auth.brand1.com',
      'auth.brand2.com',
      'auth.example.co.uk'
    ],
    domain: 'default-auth.example.com'  // Optional: fallback if no match found
  }
}
```

**How it works:**

1. The library extracts the top-level domain from the current page URL
2. Each domain in the `domains` array is checked - the library derives its top-level domain (e.g., `auth.brand1.com` → `brand1.com`)
3. If the current page's top-level domain matches a domain in the array, that auth domain is used
4. If no match is found, it falls back to the `domain` property
5. If neither is available, it uses the default Sesamy auth domain

**Example scenarios:**

```javascript
// Multi-region setup
{
  auth: {
    clientId: 'your-client-id',
    domains: [
      'auth.us.example.com',    // Used when on any *.example.com page
      'auth.uk.example.co.uk',  // Used when on any *.example.co.uk page
      'auth.au.example.com.au'  // Used when on any *.example.com.au page
    ]
  }
}

// White-label setup
{
  auth: {
    clientId: 'your-client-id',
    domains: [
      'auth.client1.com',
      'auth.client2.com',
      'auth.client3.com'
    ],
    domain: 'auth.myplatform.com'  // Default for unlisted domains
  }
}
```

## Complete Example

```javascript
{
  clientId: "demo",
  organization: "org_abc123",
  auth: {
    clientId: "demo",
    organization: "org_abc123",
    enabled: true,
    domains: [
      'auth.example.com',
      'auth.example.co.uk'
    ],
    domain: 'auth.fallback.com'
  }
}
```

## Cross-Domain Authentication

When your application domain differs from your Auth0 domain's top-level domain, the library automatically uses popup-based login on mobile browsers (Safari/iOS and Android) to handle cross-domain authentication properly. Desktop browsers will use redirect-based login even when cross-domain.

**Examples:**

- Your app: `app.example.com`, Auth domain: `auth.different.com`
  - Mobile (Safari/iOS/Android): Popup login
  - Desktop: Redirect login
- Your app: `app.example.com`, Auth domain: `auth.example.com` (same TLD)
  - All browsers: Redirect login

# Custom HTML Attributes

## Visibility

### Overview

The `sesamy-visibility` attribute allows dynamic visibility control of HTML elements based on the authentication state of a user.

### Usage

This attribute can be added to HTML elements to automatically show or hide them based on authentication status.

#### Example

```html
<div sesamy-visibility="logged-in">Welcome, User!</div>
<div sesamy-visibility="not-logged-in">Please log in.</div>
```

## Links

### Overview

The `sesamy-link` attribute attaches a click handler to an element that redirects the user.

### Usage

This attribute can be added to HTML elements to redirect the user to the following targets: `login`, `logout` and `account`.

#### Example

```html
<div sesamy-link="login">Login</div>
<div sesamy-link="logout">Logout</div>
<div sesamy-link="account">Account</div>
```

# Core API

## `isReady()`

Checks if the initialization process of the Sesamy library has completed. This function allows you to determine when it's safe to use the library's features that depend on initialization being complete.

### Returns

- boolean: Returns `true` if initialization has completed, otherwise it returns `false`.

### Example

```javascript
// Check if the Sesamy library is ready
if (window.sesamy.isReady()) {
  // Safe to use Sesamy APIs that require initialization
  console.log('Sesamy is ready to use');
} else {
  console.log('Sesamy is still initializing');
}
```

You can also use it with an event listener to perform actions when the library is ready:

```javascript
// Alternative approach using the ready event
window.addEventListener('sesamyJsReady', () => {
  // This will be true at this point
  console.log('Sesamy initialized status:', window.sesamy.isReady());
  // Perform actions that require Sesamy to be initialized
});
```

## `log(message: string)`

Logs a message to the console, but only when debug mode is enabled. This is useful for debugging purposes without cluttering the console for end users.

### Parameters

- `message` (string): The message to log to the console.

### Example

```javascript
// Log a debug message
window.sesamy.log('Testing the checkout process');
```

### Note

The log function only outputs messages when debug mode is enabled. Debug mode can be enabled by:

1. Adding `?sesamy-debug=true` to the URL
2. Setting `localStorage.debug = true`

# Auth API

## `isAuthenticated()`

Checks if the user is currently authenticated. This function verifies the existence of a valid session by checking both the local storage for an access token and the state of the Auth0 client.

### Returns

- boolean: Returns `true` if the user is authenticated either via local storage tokens or the Auth0 client; otherwise, it returns `false`.

### Example

The following example demonstrates how to check if a user is currently authenticated:

```javascript
import { isAuthenticated } from '@sesamy/sesamy-js';

isAuthenticated()
  .then((isAuth) => {
    if (isAuth) {
      console.log('User is authenticated.');
    } else {
      console.log('User is not authenticated.');
    }
  })
  .catch((error) => {
    console.error('Error checking authentication status:', error);
  });
```

## `login(options?: RedirectOptions)`

Initiates a smart login flow that automatically chooses the best authentication method based on the user's browser context. This function detects if the user is in incognito mode and adapts accordingly:

- **Incognito mode**: Uses popup-based authentication (`loginWithPopup`) which works better in private browsing sessions, especially on Safari mobile browsers
- **Normal browsing**: Uses redirect-based authentication (`loginWithRedirect`) which is the standard approach for regular browsing

This is the recommended login method as it provides the best user experience across different browser scenarios without requiring manual detection of the browsing context.

### Parameters

- `options` (optional): An object containing customization options for the login flow.
  - `appState`: (optional, object) Used to store state before doing the redirect.
  - `authorizationParams`: (optional, object) Additional parameters to include in the authorization request.
    - `audience`: (optional, string) The audience to request access to.
    - `scope`: (optional, string) The scope of the access request.
    - `login_hint`: (optional, string) A hint to pre-fill the username on the login page.
    - `organization`: (optional, string) The organization to authenticate with.
    - `redirect_uri`: (optional, string) The URL to redirect the user to after login.

### Returns

This function returns a `Promise` that resolves when the login flow is successfully initiated.

### Example

The following example demonstrates how to use the smart `login` function:

```javascript
import { login } from '@sesamy/sesamy-js';

// Smart login that adapts to browser context
const loginOptions = {
  authorizationParams: {
    login_hint: 'user@example.com',
  },
};

login(loginOptions)
  .then(() => {
    console.log('Login initiated successfully');
  })
  .catch((error) => {
    console.error('Login failed:', error);
  });
```

### Browser Compatibility Notes

- **Incognito Mode**: Automatically uses popup authentication which provides better compatibility in private browsing mode
- **Normal Browsing**: Uses redirect authentication which is more reliable for standard browsing sessions
- **Cross-Browser**: Works consistently across different browsers and mobile devices

## `loginWithRedirect(options?: LoginWithRedirectOptions)`

Initiates a login flow that redirects the user to an Auth0 hosted login page. Once the user authenticates, they will be redirected back to the specified `redirect_uri` within your application.

This is the most common login method and works well for most browsers and scenarios. However, it may have limitations in incognito mode on certain browsers (particularly Safari on mobile devices).

### Parameters

- `options` (optional): An object containing customization options for the login flow.
  - `appState`: (optional, object) Used to store state before doing the redirect.
  - `authorizationParams`: (optional, object) Additional parameters to include in the authorization request.
    - `audience`: (optional, string) The audience to request access to.
    - `scope`: (optional, string) The scope of the access request.
    - `login_hint`: (optional, string) A hint to pre-fill the username on the login page.
    - `organization`: (optional, string) The organization to authenticate with.
    - `redirect_uri`: (optional, string) The URL to redirect the user to after login.

### Returns

This function returns a `Promise` that resolves when the redirect is successfully initiated.

### Example

The following example demonstrates how to use `loginWithRedirect` to initiate a login flow with an email address pre-filled:

```javascript
import { loginWithRedirect } from '@sesamy/sesamy-js';

// Optionally pass the user's email to pre-fill on the login form
const loginOptions = {
  authorizationParams: {
    login_hint: 'user@example.com',
  },
};

// Redirect the user to the login page
loginWithRedirect(loginOptions);
```

## `loginWithPopup(options?: LoginWithPopupOptions)`

Initiates a login flow using a popup window instead of redirecting the current page. This method can provide better user experience in certain scenarios and may work better in incognito mode on some browsers where redirect-based authentication has limitations.

**Important:** After successful authentication, the popup method will trigger a page refresh by default. To prevent this refresh, you must listen for the `sesamyJsAuthenticated` event and call `preventDefault()` on it.

### When to Use Popup Login

- When you want to maintain the user's current page state
- In incognito mode scenarios where redirect-based authentication may not work properly
- When integrating with single-page applications where full page redirects are undesirable
- When you need more control over the authentication flow

### Parameters

- `options` (optional): An object containing customization options for the login flow.
  - `appState`: (optional, object) Used to store state before doing the redirect.
  - `authorizationParams`: (optional, object) Additional parameters to include in the authorization request.
    - `audience`: (optional, string) The audience to request access to.
    - `scope`: (optional, string) The scope of the access request.
    - `login_hint`: (optional, string) A hint to pre-fill the username on the login page.
    - `organization`: (optional, string) The organization to authenticate with.
    - `redirect_uri`: (optional, string) The URL to redirect the user to after login.

### Returns

This function returns a `Promise` that resolves when the popup authentication is completed.

### Example

The following example demonstrates how to use `loginWithPopup` and prevent the automatic page refresh:

```javascript
import { loginWithPopup } from '@sesamy/sesamy-js';

// Listen for authentication event to prevent page refresh
window.addEventListener('sesamyJsAuthenticated', (event) => {
  // Prevent the automatic page refresh
  event.preventDefault();

  // Handle the successful authentication
  console.log('User authenticated successfully!');

  // Perform any custom actions after authentication
  // e.g., update UI, redirect to a specific section, etc.
});

// Initiate popup login
const loginOptions = {
  authorizationParams: {
    login_hint: 'user@example.com',
  },
};

loginWithPopup(loginOptions)
  .then(() => {
    console.log('Popup login completed');
  })
  .catch((error) => {
    console.error('Popup login failed:', error);
  });
```

### Browser Compatibility Notes

- **Incognito Mode**: `loginWithPopup` may provide better compatibility in incognito/private browsing mode, especially on Safari mobile browsers where redirect-based authentication can fail.
- **Popup Blockers**: Some browsers or extensions may block popups. Always provide fallback options or user instructions.
- **Mobile Browsers**: Popup behavior may vary on mobile devices. Test thoroughly across different mobile browsers.

## `logout(options?: LogoutOptions)`

Terminates the user's session and optionally redirects the user to a specified URL after logout. This function clears any local session tokens and interacts with the Auth0 API to end the session.

### Parameters

- `options` (optional): An object containing customization options for the logout process.
  - `returnTo`: (optional, string) The URL to redirect users to after logging out. Defaults to the current page URL if not provided.

### Returns

This function does not return a value. It causes a redirect to the specified `returnTo` URL or performs a page refresh if no URL is provided.

### Example

The following example demonstrates how to log out a user and redirect them to the homepage:

```javascript
import { logout } from '@sesamy/sesamy-js';

// Specify the URL to redirect to after logout
const logoutOptions = {
  returnTo: 'https://www.yourdomain.com',
};
// Redirects the user to the auth server to log out
logout(logoutOptions);
```

## `setToken(accessToken: string)`

Stores the provided access token in local storage. This function parses the access token to extract user information and triggers an authentication event.

### Parameters

- `accessToken` (string): The access token to store and use for session management.

### Returns

This function does not return a value but triggers an event indicating that a user has been authenticated with the new token.

### Example

The following example demonstrates how to store an access token:

```javascript
import { setToken } from '@sesamy/sesamy-js';

// Example access token
const accessToken = 'your.access.token';

setToken(accessToken)
  .then(() => {
    console.log('Token set successfully and user authenticated.');
  })
  .catch((error) => {
    console.error('Failed to set token:', error);
  });
```

## `getTokenSilently(throwOnUnauthorized?: boolean, forceRefresh?: boolean)`

Retrieves a valid access token for the current user session without requiring user interaction. This function attempts to get the token from local storage or, if not available or expired, from the Auth0 client. It can optionally force a refresh or throw on unauthorized errors.

### Parameters

- `throwOnUnauthorized` (boolean, optional): If `true`, throws an error if the user is not authorized. Defaults to `true`.
- `forceRefresh` (boolean, optional): If `true`, forces a refresh of the token if it is close to expiring. Defaults to `false`.

### Returns

- `Promise<string | null>`: Resolves to the access token string if available, or `null` if not authenticated and `throwOnUnauthorized` is `false`.

### Example

```javascript
import { getTokenSilently } from '@sesamy/sesamy-js';

// Get the current access token
getTokenSilently()
  .then((token) => {
    if (token) {
      console.log('Access token:', token);
    } else {
      console.log('No access token available');
    }
  })
  .catch((error) => {
    console.error('Error retrieving token:', error);
  });
```

### Security & Storage Notes

- The access token is stored in local storage by default. This is necessary because many publishers use multi-page applications, and JavaScript running on the publisher's site can access the token anyway.
- If the publisher uses a custom domain for authentication (so the auth domain matches the publisher's top-level domain), the session token will be stored in an `httpOnly` cookie. In this case, malicious scripts can only access short-lived access tokens, not the session token itself.
- Always ensure you trust third-party scripts running on your site, as they may be able to access tokens stored in local storage.

# Entitlements API

## `entitlements.get(entitlementId: string)`

Fetches a specific entitlement by its ID.

### Parameters

- `entitlementId` (string): The ID of the entitlement to retrieve.

### Returns

- `Promise<Entitlement | undefined>`: The entitlement object if found, otherwise `undefined`.

### Example

```javascript
// Fetch a specific entitlement by its ID
window.sesamy.entitlements
  .get('entitlement-id')
  .then((entitlement) => {
    if (entitlement) {
      console.log('Entitlement:', entitlement);
    } else {
      console.log('Entitlement not found');
    }
  })
  .catch((error) => {
    console.error('Error fetching entitlement:', error);
  });
```

---

## `entitlements.list(params: GetEntitlementsOptions)`

Fetches the list of entitlements, with optional filters and inclusion of signed links.

### Parameters

- `params` (GetEntitlementsOptions): Optional parameters for fetching entitlements.
  - `includeSignedLinks` (boolean, optional): Whether to include signed links. Default is `true`.
  - `waitForEntitlementAfter` (Date, optional): If provided, waits for an entitlement that is available after the given date.

### Returns

- `Promise<Entitlement[]>`: An array of entitlements.

### Example

```javascript
// Fetch all entitlements, including signed links
window.sesamy.entitlements
  .list({ includeSignedLinks: true })
  .then((entitlements) => {
    console.log('Entitlements:', entitlements);
  })
  .catch((error) => {
    console.error('Error fetching entitlements:', error);
  });
```

---

## `entitlements.access(entitlementId: string)`

Fetches the access details for a specific entitlement by its ID.

### Parameters

- `entitlementId` (string): The ID of the entitlement to access.

### Returns

- `Promise<Access>`: The access details for the given entitlement.

### Example

```javascript
// Fetch access details for a specific entitlement
window.sesamy.entitlements
  .access('entitlement-id')
  .then((access) => {
    console.log('Access details:', access);
  })
  .catch((error) => {
    console.error('Error fetching entitlement access:', error);
  });
```

---

## `entitlements.signedLinks()`

Fetches signed links for the current session's entitlements.

### Parameters

None.

### Returns

- `SignedLink[]`: An array of signed links for the current entitlements.

### Example

```javascript
// Fetch signed links for the current entitlements
const signedLinks = window.sesamy.entitlements.signedLinks();
console.log('Signed links:', signedLinks);
```

---

### Types

- GetEntitlementsOptions: An object containing optional parameters for fetching entitlements.
  - `includeSignedLinks` (boolean, optional): Whether to include signed links. Defaults to `true`.
  - `waitForEntitlementAfter` (Date, optional): A date to filter entitlements that are available after the given date.

- Entitlement: Represents an entitlement object.

- Access: Represents the access details for an entitlement.

- SignedLink: Represents a signed link for accessing an entitlement.

# Services

## `getProfile()`

Fetches the user's profile information from the Sesamy-hosted service. If the user is authenticated, the profile data will be retrieved.

### Returns

`Promise<Profile | null>`: A promise that resolves to the user's profile information if the user is authenticated, otherwise it resolves to `null`.

### Example

The following example demonstrates how to fetch the user's profile information:

```javascript
import { getProfile } from '@sesamy/sesamy-js';

// Fetch the user's profile information
getProfile()
  .then((profile) => {
    if (profile) {
      console.log('User profile:', profile);
    } else {
      console.log('User is not authenticated');
    }
  })
  .catch((error) => {
    console.error('Error fetching profile:', error);
  });
```

### Profile Type

```typescript
export type Profile = {
  userId: string;
  firstName?: string;
  lastName?: string;
  emailVerified: boolean;
  email: string;
  name?: string;
  locale?: string;
  picture?: string;
  createdAt: string;
  updatedAt: string;
  mobilePhone?: string;
  tags: string[];
  user_metadata?: { [id: string]: string | number };
  billingAddress?: Address;
  deliveryAddress?: Address;
};
```

### Address Type

```typescript
export type Address = {
  street: string;
  co?: string;
  city: string;
  state: string;
  postalCode: string;
  country: string;
};
```

## `updateProfile(profile: Partial<Profile>)`

Updates the user's profile information on the Sesamy-hosted service. After updating, the cache is cleared to ensure that the latest profile information is available.

### Parameters

- `profile` (Partial<Profile>): An object containing the profile fields to be updated. Only the provided fields will be updated.

### Returns

`Promise<boolean>`: A promise that resolves to `true` if the profile update is successful, otherwise `false`.

### Example

The following example demonstrates how to update the user's profile information:

```javascript
import { updateProfile } from '@sesamy/sesamy-js';

// Define the profile fields to be updated
const updatedProfile = {
  firstName: 'John',
  lastName: 'Doe',
  locale: 'en-US',
  mobilePhone: '+1234567890',
};

// Update the user's profile information
updateProfile(updatedProfile)
  .then((success) => {
    if (success) {
      console.log('Profile updated successfully');
    } else {
      console.log('Profile update failed');
    }
  })
  .catch((error) => {
    console.error('Error updating profile:', error);
  });
```

### Profile Type

```typescript
export type Profile = {
  userId: string;
  firstName?: string;
  lastName?: string;
  emailVerified: boolean;
  email: string;
  name?: string;
  locale?: string;
  picture?: string;
  createdAt: string;
  updatedAt: string;
  mobilePhone?: string;
  tags: string[];
  user_metadata?: { [id: string]: string | number };
  billingAddress?: Address;
  deliveryAddress?: Address;
};
```

### Address Type

```typescript
export type Address = {
  street: string;
  co?: string;
  city: string;
  state: string;
  postalCode: string;
  country: string;
};
```

## `generateLink(params: GenerateAccountLink | GenerateChangePaymentLink | GenerateConsumeLink | GenerateCheckoutLink | GenerateChangePlanLink)`

Generates a link to a Sesamy-hosted service such as account or consume. If the user is authenticated, the link will be signed so that the user can access the service without logging in again.

### Parameters

- params (GenerateAccountLink | GenerateConsumeLink):
  - The parameters for generating the link.
    attribute.
    Common parameters:
    - target (string, required): The target service. Valid values:
      - 'account': Generate link to account page
      - 'change-payment': Generate link to payment change page
      - 'change-plan': Generate link to payment plan page
      - 'consume': Generate link to consume content
      - 'checkout': Generate link to checkout page
    - shorten (boolean, optional): If true, the link will be shortened
    - ttl (number, optional): The time-to-live for the shortened link in seconds
    - redirectUrl (string, optional): The URL to redirect to after the link is accessed
    - language (string, optional): The language for the link. Defaults to HTML element's `lang` attribute

  - Target-specific parameters:
    For target='consume':
    - sku (string, required): The SKU of the product to consume
    - episodeId (string, optional): The ID of the episode to consume

  - For target='change-payment|change-plan':
    - contractId (string, required): The ID of the contract to change payment or plan for

### Returns

string: The generated link URL. If shorten is true, the shortened URL is returned.

### Example

The following example demonstrates how to generate a link to the Sesamy account page:

```javascript
import { generateLink } from '@sesamy/sesamy-js';

// Generate a link to the account page
const linkParams = {
  target: 'account',
  shorten: true,
  ttl: 3600, // 1 hour in seconds
  redirectUrl: 'https://www.yourdomain.com/account',
};

generateLink(linkParams)
  .then((link) => {
    console.log('Generated link:', link);
  })
  .catch((error) => {
    console.error('Error generating link:', error);
  });
```

And here is an example for generating a link to consume a specific product:

```javascript
import { generateLink } from '@sesamy/sesamy-js';

// Generate a link to consume a product
const linkParams = {
  target: 'consume',
  sku: 'sid:eUv45QXTrcGNhMJpaCQNe',
  episodeId: 'episode-id',
  shorten: true,
  ttl: 3600, // 1 hour in seconds
  redirectUrl: 'https://www.yourdomain.com/consume',
};

generateLink(linkParams)
  .then((link) => {
    console.log('Generated link:', link);
  })
  .catch((error) => {
    console.error('Error generating link:', error);
  });
```

## `checkouts.create(params: CreateCheckoutParams)`

Creates a checkout session with Sesamy. This function initializes a checkout process by sending the necessary parameters to the Sesamy API.

### Parameters

- params (CreateCheckoutParams): The parameters for creating the checkout session.
  - **items** (Array of objects, **required**): An array of items to be included in the checkout.
    - sku (string, optional): The SKU of the product to be purchased.
    - url (string, optional): The URL of the item.
    - purchaseOptionId (string, optional): The ID of the purchase option for the product.
    - price (number, optional): The price of the item.
    - currency (string, optional): The currency code (e.g., 'USD').
    - geoRestrictions (object, optional): Geographic restrictions for the item.
      - type ('ALLOW' | 'BLOCK'): Type of restriction.
      - countries (Array of strings): List of country codes.
  - **redirectUrl** (string, **required**): The URL to redirect to after the checkout process is completed.
  - language (string, optional): The language for the checkout session. If not set, it will be automatically fetched from the HTML element's `lang` attribute.
  - givenName (string, optional): The given name of the customer.
  - familyName (string, optional): The family name of the customer.
  - email (string, optional): The email address of the customer.
  - phoneNumber (string, optional): The phone number of the customer.
  - birthDate (string, optional): The birth date of the customer in ISO format.
  - country (string, optional): The country of the customer (country code).
  - address (Address, optional): The billing address for the checkout session.
    - street (string, optional): The street address.
    - city (string, optional): The city.
    - zip (string, optional): The postal code.
    - country (string, optional): The country code.
  - businessAddress (Address, optional): The business address for B2B transactions.
  - isBusiness (boolean, optional): Indicates if the checkout is for a business.
  - price (number, optional): Override price for the entire order (takes precedence over item prices).
  - currency (string, optional): Override currency for the entire order.
  - paymentMethodsFilter (Array of objects, optional): Filter available payment methods in the checkout.
    - provider (string): The payment provider (e.g., 'stripe', 'klarna').
    - methods (Array of strings, optional): Specific payment methods for the provider.
  - requestedDiscountCodes (Array of strings, optional): Discount codes to apply to the checkout.
  - attribution (object, optional): Attribution and tracking data.
    - utmSource (string, optional): UTM source parameter.
    - utmMedium (string, optional): UTM medium parameter.
    - utmCampaign (string, optional): UTM campaign parameter.
    - utmTerm (string, optional): UTM term parameter.
    - utmContent (string, optional): UTM content parameter.
    - ref (string, optional): Referral identifier.
    - referrer (string, optional): Referrer URL.
    - itemSrc (string, optional): Source of the item.
    - publisherContentId (string, optional): Publisher content ID.
    - source ('CHECKOUT' | 'PAYWALL' | 'OTHERS', optional): Source of the checkout.
    - sourceId (string, optional): Source identifier.
    - \_ga, \_gid, \_fbp, \_fbc (strings, optional): Analytics tracking cookies.
  - referralEmail (string, optional): Email of the referrer.
  - payerEmail (string, optional): Email of the payer (person paying for the purchase).
  - requireAddress (boolean, optional): If true, requires the customer to provide an address during checkout.
  - giftMode (boolean, optional): If true, enables gift mode for the checkout.
  - metadata (Record<string, string>, optional): Custom metadata to store with the checkout (e.g., { "campaign": "summer2024", "source": "mobile" }).

### Returns

Promise\<Checkout\>: A promise that resolves to the response JSON object from the Sesamy API, which contains details about the created checkout session.

### Checkout Response Schema

- id (string): The unique identifier of the checkout session.
- checkoutUrl (string): The URL where the user should be redirected to complete the checkout.
- status ('PENDING' | 'PAID'): The status of the checkout session.
- type ('RECURRING' | 'SINGLE'): The type of checkout session.
- createdAt (string): ISO timestamp of when the checkout was created.
- updatedAt (string): ISO timestamp of when the checkout was last updated.
- items (Array of objects): The items included in the checkout session.
  - sku (string): The SKU of the product.
  - url (string): The URL of the item.
  - purchaseOptionId (string, optional): The ID of the purchase option.
  - price (number, optional): The price of the item.
  - currency (string, optional): The currency of the item.
- itemsOwned (Array of objects): Items already owned by the user.
  - sku (string): The SKU of the product.
  - purchaseOptionId (string): The purchase option ID.
  - title (string): The title of the product.
- appliedDiscountCodes (Array of objects): Discount codes applied to the checkout.
  - name (string): Name of the discount.
  - description (string): Description of the discount.
  - id (string): ID of the discount code.
  - code (string): The discount code.
  - status ('APPLIED' | 'UNAPPLICABLE'): Status of the discount code.
- availablePaymentMethods (Array of objects): Available payment methods for checkout.
  - provider (string): The payment provider.
  - methods (Array of strings, optional): Available payment methods for the provider.
- email (string, optional): The email address for the checkout session.
- language (string): The language used for the checkout session.
- currency (string): The currency used for the checkout session.
- country (string): The country associated with the checkout session.
- redirectUrl (string): The redirect URL after completion.
- fieldSettings (object, optional): Field visibility and requirement settings.

### Example

The following example demonstrates how to create a checkout session with Sesamy:

```javascript
import { init } from '@sesamy/sesamy-js';

const sesamy = await init({ clientId: 'your-client-id' });

// Create a simple checkout with one item
const checkout = await sesamy.checkouts.create({
  items: [
    {
      sku: 'premium_monthly',
      purchaseOptionId: 'po_123',
      price: 9.99,
      currency: 'USD',
    },
  ],
  email: 'customer@example.com',
  redirectUrl: 'https://yoursite.com/checkout-complete',
  requireAddress: true,
  metadata: {
    campaignId: 'summer_2024',
    source: 'mobile_app',
  },
});

// Redirect to checkout
window.location.href = checkout.checkoutUrl;
```

Advanced example with gift mode and payer email:

```javascript
const checkout = await sesamy.checkouts.create({
  items: [
    {
      sku: 'premium_gift',
      purchaseOptionId: 'po_gift',
      price: 49.99,
      currency: 'USD',
    },
  ],
  email: 'recipient@example.com',
  payerEmail: 'sender@example.com',
  redirectUrl: 'https://yoursite.com/checkout-complete',
  giftMode: true,
  language: 'en',
  metadata: {
    giftMessage: 'Happy Birthday!',
    giftFrom: 'John',
  },
});
```

### Additional Notes

- The `items` array must contain at least one item.
- The `redirectUrl` is required and will be used after the checkout is completed.
- The `language` parameter is optional. If not provided, it will be determined from the HTML document's `lang` attribute.
- Attribution data is automatically enhanced with tracking cookies (\_ga, \_gid, \_fbp, \_fbc) if available.
- Discount codes validation happens on the Sesamy backend; invalid codes will be marked as 'UNAPPLICABLE' in the response.

## `checkouts.update(id: string, params: Partial<CreateCheckoutParams>)`

Updates an existing checkout session with Sesamy. You can update customer information, items, or other checkout details.

### Parameters

- id (string): The unique identifier of the checkout session to update.
- params (Partial<CreateCheckoutParams>): The parameters to update. All fields are optional.
  - items (Array of objects, optional): Updated array of items.
    - sku (string, optional): The SKU of the product.
    - url (string, optional): The URL of the item.
    - purchaseOptionId (string, optional): The ID of the purchase option.
    - price (number, optional): The price of the item.
    - currency (string, optional): The currency code.
  - redirectUrl (string, optional): Updated redirect URL.
  - language (string, optional): Updated language for the checkout session.
  - email (string, optional): Updated email address.
  - givenName (string, optional): Updated given name.
  - familyName (string, optional): Updated family name.
  - phoneNumber (string, optional): Updated phone number.
  - birthDate (string, optional): Updated birth date.
  - country (string, optional): Updated country.
  - address (Address, optional): Updated billing address.
  - businessAddress (Address, optional): Updated business address.
  - isBusiness (boolean, optional): Updated business status.
  - price (number, optional): Updated order price.
  - currency (string, optional): Updated order currency.
  - paymentMethodsFilter (Array of objects, optional): Updated payment method filters.
  - requestedDiscountCodes (Array of strings, optional): Updated discount codes to apply.
  - attribution (object, optional): Updated attribution and tracking data.
  - payerEmail (string, optional): Updated payer email.
  - requireAddress (boolean, optional): Updated address requirement.
  - giftMode (boolean, optional): Updated gift mode status.
  - metadata (Record<string, string>, optional): Updated metadata.

### Returns

Promise\<Checkout\>: A promise that resolves to the updated checkout session object.

### Example

Basic update example:

```javascript
import { init } from '@sesamy/sesamy-js';

const sesamy = await init({ clientId: 'your-client-id' });

const updated = await sesamy.checkouts.update('checkout_123', {
  email: 'newemail@example.com',
  givenName: 'Jane',
});

console.log('Checkout updated:', updated);
```

Update with new discount codes:

```javascript
const updated = await sesamy.checkouts.update('checkout_123', {
  requestedDiscountCodes: ['NEWCODE', 'SUMMER20'],
});

console.log('Applied discount codes:', updated.appliedDiscountCodes);
```

### Additional Notes

- Only provide the fields you want to update; omitted fields will remain unchanged.
- Discount codes are validated on the Sesamy backend.
- Changes to payment method filters will be reflected in the checkout session immediately.
- country (string): The country associated with the checkout session.
- language (string): The language used for the checkout session.
- items (Array of objects): The items included in the checkout session.
  - sku (string): The SKU of the product.
  - purchaseOptionId (string, optional): The ID of the purchase option for the product.
  - title (string): The title of the product.
  - cover (string): The cover image URL of the product.
- itemsOwned (Array of strings): The SKUs of the items already owned by the user.
- discountCodes (Array of strings): The discount codes applied to the checkout session.
- paymentOptions (Array of objects): The available payment options.
  - provider (string): The payment provider.
  - methods (Array of strings): The payment methods supported by the provider.

## `checkouts.get(id: string)`

Retrieves the details of a specific checkout session from Sesamy.

### Parameters

- id (string): The unique identifier of the checkout session to retrieve.

### Returns

Promise\<Checkout\>: A promise that resolves to the checkout session object with all details.

### Example

```javascript
import { init } from '@sesamy/sesamy-js';

const sesamy = await init({ clientId: 'your-client-id' });

const checkout = await sesamy.checkouts.get('checkout_123');
console.log('Checkout status:', checkout.status);
console.log('Checkout URL:', checkout.checkoutUrl);
console.log('Applied discounts:', checkout.appliedDiscountCodes);
console.log('Items owned:', checkout.itemsOwned);
```

### Additional Notes

- The `id` parameter is required and must be a valid checkout session identifier.
- Returns the complete checkout session data including status, items, and applied discounts.

# Browser API

## `detectAdblock()`

Detects the presence of an ad blocker in the browser by dynamically creating and checking the visibility of several ad-related HTML elements.

### Parameters

None

### Returns

Promise: A promise that resolves to a boolean value indicating whether an ad blocker is enabled (`true`) or not (`false`).

### Example

The following example demonstrates how to use the `detectAdblock` function to detect if an ad blocker is enabled:

```javascript
import { detectAdblock } from '@sesamy/sesamy-js';

// Detect if an ad blocker is enabled
detectAdblock()
  .then((isAdblockEnabled) => {
    if (isAdblockEnabled) {
      console.log('Adblocker is enabled');
    } else {
      console.log('Adblocker is not enabled');
    }
  })
  .catch((error) => {
    console.error('Error detecting adblock:', error);
  });
```

### Details

- The function creates a set of HTML `<div>` elements with IDs typically used by ad containers (e.g., 'AdHeader', 'AdContainer', etc.).
- These elements are appended to the body of the document and their visibility is checked after a short delay (100 milliseconds).

## `isIncognito()`

Synchronously checks if the browser is running in incognito mode by reading from session storage. This function returns the cached incognito status without performing any detection.

### Parameters

None

### Returns

`boolean | undefined`: Returns `true` if incognito mode is confirmed, `false` if not incognito, or `undefined` if the incognito status hasn't been determined yet.

### Example

The following example demonstrates how to use the `isIncognito` function:

```javascript
import { isIncognito } from '@sesamy/sesamy-js';

// Check cached incognito status
const incognitoStatus = isIncognito();

if (incognitoStatus === true) {
  console.log('Incognito mode is enabled');
} else if (incognitoStatus === false) {
  console.log('Incognito mode is not enabled');
} else {
  console.log('Incognito status not yet determined');
  // You may want to call detectAndCacheIncognito() first
}
```

## `detectAndCacheIncognito()`

Detects incognito mode using the detectincognitojs library and persists the result to session storage. This function only performs detection if the result is not already cached in session storage.

### Parameters

None

### Returns

`Promise<boolean>`: A promise that resolves to `true` if the browser is running in incognito mode, or `false` if not.

### Example

The following example demonstrates how to use the `detectAndCacheIncognito` function:

```javascript
import { detectAndCacheIncognito, isIncognito } from '@sesamy/sesamy-js';

// Detect and cache incognito status (typically called once during app initialization)
detectAndCacheIncognito().then((isIncognitoEnabled) => {
  if (isIncognitoEnabled) {
    console.log('Incognito mode detected and cached');
  } else {
    console.log('Normal browsing mode detected and cached');
  }

  // Now you can use the synchronous isIncognito() function
  console.log('Cached status:', isIncognito());
});
```

### Usage Pattern

The recommended usage pattern is to call `detectAndCacheIncognito()` once during application initialization, then use the synchronous `isIncognito()` function throughout your application:

```javascript
// During app initialization
await detectAndCacheIncognito();

// Throughout your app - fast synchronous checks
if (isIncognito() === true) {
  // Handle incognito mode
} else if (isIncognito() === false) {
  // Handle normal mode
}
```

## `isInAppBrowser()`

Detects if the current browsing context is an in-app browser and identifies the browser name if applicable.

### Parameters

None

### Returns

Object: An object containing two properties:

- `isInAppBrowser` (boolean): Indicates whether the current browsing context is an in-app browser (`true`) or not (`false`).
- `browserName` (string | null): The name of the in-app browser if detected, or `null` if not detected.

### Example

The following example demonstrates how to use the `isInAppBrowser` function to check if the current context is an in-app browser and log the browser name if applicable:

```javascript
import { isInAppBrowser } from '@sesamy/sesamy-js';

// Detect if the current context is an in-app browser
const result = isInAppBrowser();

if (result.isInAppBrowser) {
  console.log(`Running inside an in-app browser: ${result.browserName}`);
} else {
  console.log('Not running inside an in-app browser');
}
```

### Details

- A predefined list of common in-app browsers (e.g., Facebook, Instagram, LinkedIn, etc.) is checked against the user agent.
- If a match is found, the function returns `isInAppBrowser: true` and the corresponding `browserName`.
- If no known in-app browser signatures are detected, the function returns `isInAppBrowser: false` and `browserName: null`.

# Transforms

The transform module allows for dynamic content manipulation based on specified rules. This can include inserting, replacing, or removing elements on a webpage.

## Configuration

The transform configuration is defined within the `Config` interface. The following properties are available:

- `enabled` (boolean, optional): Enables or disables the transform module.
- `rules` (array): An array of rule objects that define the transformations to apply.

Each rule object contains the following properties:

- `selector` (string): A CSS selector to target elements on the page.
- `transform` (string): The type of transformation to apply. Valid values are 'insert', 'replace', 'remove', 'gradient', and 'wrap'.
- `content` (string, optional): The content to insert or replace. Required for 'insert' and 'replace' transforms.
- 'insertPosition' (string, optional): The position to insert the content. Valid values are 'before', 'after' and 'insider'.
- `contentType` (string, optional): Specifies the type of content. Valid values are 'html', 'selector', and 'url'.
- `path` (string, optional): A regular expression to match the URL path. If provided, the rule will only apply when the path matches the current URL.
- `entitlement` (string, optional): Specifies an entitlement required for the rule to apply.
- `authenticated` (boolean, optional): Specifies if the rule should only apply to authenticated users.

### Example Configuration

Below is an example configuration for the transform module:

```json
{
  "transform": {
    "enabled": true,
    "rules": [
      {
        "selector": "#example",
        "transform": "replace",
        "content": "<p>New content</p>",
        "contentType": "html"
      },
      {
        "selector": ".ad-banner",
        "transform": "remove"
      },
      {
        "selector": ".insert-content",
        "transform": "insert",
        "content": "#source-element",
        "contentType": "selector"
      },
      {
        "selector": ".load-from-url",
        "transform": "insert",
        "content": "https://example.com/content",
        "contentType": "url"
      }
    ]
  }
}
```

### Usage

To use the transform module, include the configuration in your initialization script:

```html
<script type="application/json" id="sesamy-js">
  {
    "clientId": "demo",
    "transform": {
      "enabled": true,
      "rules": [
        {
          "selector": "#example",
          "transform": "replace",
          "content": "<p>New content</p>",
          "contentType": "html"
        },
        {
          "selector": ".ad-banner",
          "transform": "remove"
        },
        {
          "selector": ".insert-content",
          "transform": "insert",
          "content": "#source-element",
          "contentType": "selector"
        },
        {
          "selector": ".load-from-url",
          "transform": "insert",
          "content": "https://example.com/content",
          "contentType": "url"
        }
      ]
    }
  }
</script>
```

This configuration will replace the content of the element with the ID `example`, remove elements with the class `ad-banner`, insert the content of the element with the ID `source-element` into elements with the class `insert-content`, and load content from a URL into elements with the class `load-from-url`.

# Content

The content module allows you to extract dynamic article data from a webpage based on specific selectors. This can include elements like the title, image, excerpt, price, and more.

## `content.getPropertyFromHTML(options: { publisherContentId?: string, key: string, defaultValue?: string })`

Searches for a specified property in the HTML content, following a specific lookup hierarchy:

1. First, it looks for a `<sesamy-content-data>` element with the given `publisherContentId` and checks for the property as an attribute.
2. If not found, it checks for meta tags in the document head with the formats `sesamy:{key}`, `og:{key}`, or just `{key}`.
3. If still not found, it returns the provided default value (empty string if no default specified).

### Parameters

- `options` (object): Options for retrieving the property.
  - `publisherContentId` (string, optional): The ID of the content to search for.
  - `key` (string): The name of the property to retrieve.
  - `defaultValue` (string, optional): The default value to return if the property is not found. Defaults to an empty string.

### Returns

- `string`: The value of the property if found, or the default value.

### Example

```javascript
// Get the title from HTML content
const title = window.sesamy.content.getPropertyFromHTML({
  publisherContentId: 'article-123',
  key: 'title',
  defaultValue: 'Untitled Article',
});

console.log('Article title:', title);
```

## Configuration

The content configuration is defined within the `Config` json. The following properties are available:

### Content Node Properties

- `article` (object): Defines the selector for the article element itself.
- `image` (object): Specifies the selector and attribute for extracting article images.
- `title` (object): Specifies the selector and attribute for extracting the article title.
- `excerpt` (object): Specifies the selector and attribute for extracting the article excerpt.
- `price` (object): Specifies the selector and attribute for extracting the article price.
- `currency` (object): Specifies the selector and attribute for extracting the article currency.
- `url` (object): Specifies the selector and attribute for extracting the article URL.
- `id` (object): Specifies the selector and attribute for extracting the article ID.

Each field object contains the following properties:

- `selector` (string): A CSS selector to target elements on the page.
- `attribute` (string, optional): The attribute to retrieve from the selected element. If not specified, the text content of the element is used.

### Feature Flags

#### `enablePaywallSettingsUrlFallback`

**Type:** `boolean`
**Default:** `false`
**Location:** Per content node in `content[].enablePaywallSettingsUrlFallback`

When enabled on a content node, this feature flag allows the content module to fall back to checking for a `settings-url` attribute on `<sesamy-paywall>` elements when no `paywallUrl` is found through the standard detection methods (meta tags or article attributes).

**Use Case:** This is useful for pages that use the `<sesamy-paywall>` custom element but don't explicitly set the paywall URL in meta tags or on the article element. The paywall component may have a `settings-url` attribute that points to the paywall configuration.

**Priority Order:**

1. Article element `paywall-url` attribute
2. Content configuration `paywallUrl` property
3. Meta tags (`sesamy:paywall-url`, `og:paywall-url`, etc.)
4. **If `enablePaywallSettingsUrlFallback` is true:** `<sesamy-paywall settings-url="...">` attribute

**Example:**

```javascript
// Enable the fallback feature
{
  clientId: "demo",
  content: [
    {
      type: 'article',
      enablePaywallSettingsUrlFallback: true,
      selectors: {
        article: { selector: 'sesamy-article' }
      }
    }
  ]
}
```

```html
<!-- Page with sesamy-paywall element -->
<sesamy-paywall settings-url="https://example.com/api/paywalls/123"></sesamy-paywall>
<sesamy-article item-src="https://example.com/article/456">
  <!-- Article content -->
</sesamy-article>

<script>
  // The content.get() method will now find the paywall URL from the sesamy-paywall element
  const article = window.sesamy.content.get('sesamy-article');
  console.log(article.paywallUrl); // "https://example.com/api/paywalls/123"
</script>
```

### Example Configuration

Below is an example configuration for the content module:

```json
{
  "content": {
    "article": {
      "selector": "article"
    },
    "image": {
      "selector": "img",
      "attribute": "src"
    },
    "title": {
      "selector": "h1",
      "attribute": "textContent"
    },
    "excerpt": {
      "selector": "p",
      "attribute": "textContent"
    },
    "price": {
      "selector": "article",
      "attribute": "data-price"
    },
    "currency": {
      "selector": "article",
      "attribute": "data-currency"
    },
    "url": {
      "selector": "link",
      "attribute": "href"
    },
    "id": {
      "selector": "article",
      "attribute": "data-id"
    }
  }
}
```

### Usage

To use the content module, initialize it with the appropriate configuration:

```js
import { init } from '@sesamy/sesamy-js';

init({
  content: {
    article: { selector: 'article' },
    image: { selector: 'img', attribute: 'src' },
    title: { selector: 'h1', attribute: 'textContent' },
    excerpt: { selector: 'p', attribute: 'textContent' },
    price: { selector: 'article', attribute: 'data-price' },
    currency: { selector: 'article', attribute: 'data-currency' },
    url: { selector: 'link', attribute: 'href' },
    id: { selector: 'article', attribute: 'data-id' },
  },
});
```

### Methods

#### `list()`

Returns an array of article objects containing the extracted data for each article on the page.

Each article object contains the following properties:

- `title` (string, optional): The title of the article.
- `excerpt` (string, optional): The article's excerpt.
- `image` (string, optional): The source URL of the article's image.s
- `price` (number, optional): The price of the article.
- `currency` (string, optional): The currency of the article price.
- `url` (string, optional): The URL of the article.
- `id` (string, optional): The article's unique ID.
- `element` (Element): The original DOM element for the article.

### Example Usage

```js
import { init } from '@sesamy/sesamy-js';

const sesamyjs = await init({ ...config });

// Extract and log all content
const articles = sesamy.content.list();
console.log(articles);
```

In this example, `content.list()` will return an array of articles extracted from the page based on the configured selectors. Each article object contains fields such as title, excerpt, image, price, currency, URL, and ID.

#### `get(articleElementOrSelector: Element | string)`

Extracts article data from a specific element on the page.

### Parameters

- `articleElementOrSelector` (Element | string): Either a DOM element or a CSS selector string identifying the article element.

### Returns

- `Article | null`: An article object containing the extracted data, or `null` if the element is not found.

The returned article object contains the following properties:

- `title` (string, optional): The title of the article.
- `excerpt` (string, optional): The article's excerpt.
- `image` (string, optional): The source URL of the article's image.
- `price` (number, optional): The price of the article.
- `currency` (string, optional): The currency of the article price.
- `url` (string, optional): The URL of the article.
- `id` (string, optional): The article's unique ID.
- `pass` (string, optional): Comma-separated list of passes required for access.
- `paywallUrl` (string, optional): URL to the paywall configuration.
- `accessLevel` ('public' | 'logged-in' | 'entitlement', optional): The access level required for the content.
- `selector` (string): The CSS selector for the article element.
- `element` (Element): The original DOM element for the article.
- `hasAccess` (boolean, optional): Whether the user has access to the content. Set by `hasAccess()` method.
- `entitlement` (Entitlement, optional): The entitlement object if access is granted via entitlement. Set by `hasAccess()` method.

### Example Usage

```js
// Get article data by CSS selector
const article = window.sesamy.content.get('article.premium-content');
console.log('Article:', article);

// Get article data by DOM element
const element = document.querySelector('.my-article');
const article = window.sesamy.content.get(element);
console.log('Article:', article);
```

#### `hasAccess(articleElementOrSelector: Element | string)`

Checks if the current user has access to the specified article. This method intelligently determines the access requirements based on the article's paywall configuration and decorates the article with access status and entitlement information.

### Parameters

- `articleElementOrSelector` (Element | string): Either a DOM element or a CSS selector string identifying the article element.

### Returns

- `Promise<Article>`: A promise that resolves to the article object decorated with access information:
  - `hasAccess` (boolean): `true` if the user has access, `false` otherwise
  - `entitlement` (Entitlement, optional): The entitlement object if access is granted via entitlement
  - All other article properties (title, url, etc.)

### Behavior

1. **Paywall-based Access**: If the article has a `paywallUrl`, the method fetches the paywall configuration to determine the access type based on the paywall template:
   - **Login Paywall** (`template: 'LOGIN'`): Sets `accessLevel` to `'logged-in'`, checks if the user is authenticated, and sets `hasAccess` accordingly.
   - **Entitlement Paywall** (`template: 'BOXES'`): Sets `accessLevel` to `'entitlement'`, checks for entitlement, and sets both `hasAccess` and `entitlement`.

2. **Standard Entitlement Check**: If no `paywallUrl` is specified, the method uses the article's `url` and `pass` properties to check for entitlement access, setting both `hasAccess` and `entitlement`.

### Example Usage

```js
// Check access for an article
const entitlement = await window.sesamy.content.hasAccess('.premium-article');

if (entitlement) {
  console.log('User has access via entitlement:', entitlement.id);
  // Show premium content
} else {
  console.log('User does not have access');
  // Show paywall or login prompt
}

// Use with async/await
async function checkArticleAccess() {
  try {
    const entitlement = await window.sesamy.content.hasAccess('article#main');

    if (entitlement) {
      // Unlock or display content
      console.log('Access granted');

      // Access entitlement details if available
      console.log('Entitlement type:', entitlement.type);
      console.log('Entitlement SKU:', entitlement.sku);
    } else {
      // Redirect to paywall
      const article = window.sesamy.content.get('article#main');
      console.lo