npm package: @shoppi/analytics
Description: Analytics SDK for Shoppi clients
Author: Shoppi
Latest version: 1.0.20

# Shoppi Analytics SDK

A lightweight, type-safe analytics SDK for tracking user interactions with e-commerce applications.

## Features

- Type-safe event tracking with TypeScript support
- Specialized methods for common e-commerce events
- Configurable batch processing and automatic retries
- Flow context tracking for analyzing user journeys
- Robust user identity management with persistent tracking
- Automatic timestamp and client metadata handling
- Minimal configuration required
- Support for selector-based tracking with HTML data attributes

## Table of Contents

- [Installation](#installation)
- [Usage Methods](#usage-methods)
- [Quick Start](#quick-start)
- [Initialization](#initialization)
- [User Identification](#user-identification)
- [Basic Usage](#basic-usage)
  - [Selector-based Tracking](#selector-based-tracking)
  - [Properties-based Tracking](#properties-based-tracking)
- [Data Attributes](#data-attributes)
- [Event Types](#event-types)
  - [View Events](#view-events)
  - [Click Events](#click-events)
  - [Search Events](#search-events)
  - [Conversion Events](#conversion-events)
- [Flow Types](#flow-types)
- [Advanced Usage](#advanced-usage)
  - [Manual Event Tracking](#manual-event-tracking)
  - [Event Batching and Flushing](#event-batching-and-flushing)
  - [Tracking Dynamically Loaded Content](#tracking-dynamically-loaded-content)
  - [Custom Tracking Wrappers](#custom-tracking-wrappers)
- [Configuration Options](#configuration-options)
- [Best Practices](#best-practices)
- [API Reference](#api-reference)
- [Cross-Device Tracking](#cross-device-tracking)
- [Changelog](#changelog)

## Installation

```bash
npm install shoppi-analytics
```

## Usage Methods

The SDK supports three modes of usage:

### 1. TypeScript

```typescript
import { Analytics, FlowType } from 'shoppi-analytics';

const analytics = Analytics.init({
  apiKey: 'your-api-key',
  clientId: 'your-client-id',
  debug: true
});

analytics.trackProductDetailView({
  objectId: 'product-123',
  price: 99.99,
  category: 'clothing'
});
```

### 2. JavaScript (Node.js/Bundler)

```javascript
// ESM import
import { Analytics } from 'shoppi-analytics';

// OR CommonJS require
const { Analytics } = require('shoppi-analytics');

const analytics = Analytics.init({
  apiKey: 'your-api-key',
  clientId: 'your-client-id'
});

analytics.trackProductClick({
  objectId: 'product-123',
  price: 99.99
});
```

### 3. Browser (Script Tag)

```html
<script src="https://cdn.example.com/shoppi-analytics/dist/shoppi-analytics.js"></script>

<script>
  // Clean interface - no nested Analytics property needed
  const analytics = ShoppiAnalytics.init({
    apiKey: 'your-api-key',
    clientId: 'your-client-id'
  });
  
  // Track events
  analytics.trackProductClick({
    objectId: 'product-123',
    price: 99.99
  }, ShoppiAnalytics.FlowType.DIRECT);
</script>
```

## Quick Start

### Initialization

```typescript
import { Analytics, AnalyticsConfig, FlowType } from 'shoppi-analytics';

const config: AnalyticsConfig = {
  apiKey: 'your-api-key',
  clientId: 'your-client-id',
  debug: true, // Optional, enables debug logging
  // Optional user tokens - recommended for user journey tracking
  userToken: {
    anonymousId: 'client-anon-id-123', // Client-provided anonymous user ID
    authenticatedId: 'user-456'         // Client-provided authenticated user ID
  }
};

// Initialize the SDK
const analytics = Analytics.init(config);

// Track events using selectors
analytics.trackProductClick(`[id="${product.id}"]`);

// Using flow-specific methods with selectors
analytics.trackSearchAddToCart(`[data-shoppi-object-id="${product.id}"][data-shoppi-price="${product.price}"]`);
analytics.trackOutfitAddToCart(`[data-shoppi-object-id="${outfitProduct.id}"][data-shoppi-price="${outfitProduct.price}"]`);
analytics.trackSimilarAddToCart(`[data-shoppi-object-id="${similarProduct.id}"][data-shoppi-price="${similarProduct.price}"]`);

// Or using properties
analytics.trackAddToCart({
  objectId: 'product-123',
  price: 99.99
}, FlowType.DIRECT);
```

## User Identification

The SDK manages three types of user identifiers:

1. **anonymousId** (optional): Client-provided identifier for anonymous users
2. **authenticatedId** (optional): Client-provided identifier for authenticated users
3. **shoppiId** (required, managed by SDK): Persistent identifier generated and stored by the SDK

You can set these during initialization or update them later:

```typescript
// Set during initialization
const analytics = Analytics.init({
  // required config...
  userToken: {
    anonymousId: 'client-anon-id-123',
    authenticatedId: 'user-456'
  }
});

// Update later
analytics.setAnonymousId('new-anon-id');
analytics.setAuthenticatedId('new-auth-id');
analytics.setUserToken({
  anonymousId: 'another-anon-id',
  authenticatedId: 'another-auth-id'
});
```

### Best Practices for User Identification

1. **Initialize with existing IDs when available**
   ```typescript
   // Retrieve IDs from storage or your authentication system
   const anonymousId = localStorage.getItem('your_anon_id_key');
   const authenticatedId = getAuthenticatedUser()?.id;

   // Initialize with existing IDs
   const analytics = Analytics.init({
     // Other config...
     userToken: {
       anonymousId,
       authenticatedId
     }
   });
   ```

2. **Generate and store client anonymous ID**
   ```typescript
   // Generate client-side anonymous ID if not already available
   let anonymousId = localStorage.getItem('your_anon_id_key');
   if (!anonymousId) {
     anonymousId = generateRandomId(); // Your ID generation method
     localStorage.setItem('your_anon_id_key', anonymousId);
   }
   
   const analytics = Analytics.init({
     // Other config...
     userToken: {
       anonymousId
     }
   });
   ```

3. **Update authenticated ID after login**
   ```typescript
   // After user login
   function onUserLogin(user) {
     // Update the authenticated ID while preserving anonymous ID
     analytics.setAuthenticatedId(user.id);
   }
   ```

4. **Keep both IDs after authentication**
   - Always maintain both the anonymous ID and authenticated ID when a user logs in
   - This ensures consistent tracking across anonymous and authenticated sessions

5. **For SPAs and page reloads**
   - Initialize analytics with the same user tokens on page changes
   - For single-page applications, initialize once with persistent tokens

## Basic Usage

The SDK provides two approaches to tracking events:

### Selector-based Tracking

Use CSS selectors to reference HTML elements with data attributes:

```html
<!-- HTML element with data attributes -->
<div class="product-card" 
     id="product-123"
     data-shoppi-object-id="product-123"
     data-shoppi-price="99.99"
     data-shoppi-category="clothing"
     data-shoppi-title="Summer T-Shirt">
  <img src="product.jpg" alt="Product Image">
  <h3>Summer T-Shirt</h3>
  <p>$99.99</p>
</div>
```

```typescript
// Track a product click when the element is clicked
document.getElementById('product-123').addEventListener('click', function() {
  analytics.trackProductClick(`[id="product-123"]`);
});
```

### Properties-based Tracking

Directly pass properties as objects:

```typescript
// Track a product view
analytics.trackProductDetailView({
  objectId: 'product-123',
  price: 99.99,
  category: 'clothing',
  title: 'Summer T-Shirt'
});

// Track adding to cart with specified flow
analytics.trackAddToCart({
  objectId: 'product-123',
  price: 99.99
}, FlowType.DIRECT);

// Using flow-specific methods
analytics.trackSearchAddToCart({
  objectId: 'product-123',
  price: 99.99
});

analytics.trackOutfitAddToCart({
  objectId: 'product-456',
  price: 149.99
});

analytics.trackSimilarAddToCart({
  objectId: 'product-789',
  price: 199.99
});
```

## Data Attributes

The SDK supports automatic event tracking through HTML data attributes. All attributes should be prefixed with `data-shoppi-`.

### Attribute Format

- All attributes are prefixed with `data-shoppi-`
- Property names are converted from kebab-case to camelCase automatically
- Example: `data-shoppi-object-id` becomes `objectId` in the event properties
- Values are automatically converted to appropriate types (numbers, booleans)

### Required Attributes

Different events require different attributes:

- Most events require `data-shoppi-object-id`
- Search events require `data-shoppi-query-id` and `data-shoppi-query`
- Conversion events require `data-shoppi-flow`
- Purchase events also require `data-shoppi-value` and `data-shoppi-currency`

### Example Elements

**Product Detail:**
```html
<div class="product-detail" 
     id="product-123" 
     data-shoppi-object-id="product-123"
     data-shoppi-price="99.99"
     data-shoppi-category="clothing"
     data-shoppi-brand="BrandName"
     data-shoppi-title="Summer T-Shirt">
  <!-- Product content -->
</div>
```

**Search Results:**
```html
<div class="search-results" 
     id="search-results" 
     data-shoppi-query-id="search-123"
     data-shoppi-query="summer t-shirt"
     data-shoppi-results-count="24">
  <!-- Search results content -->
</div>
```

**Add to Cart Button:**
```html
<button id="add-to-cart" 
        data-shoppi-object-id="product-123"
        data-shoppi-price="99.99"
        data-shoppi-quantity="1"
        data-shoppi-flow="direct">
  Add to Cart
</button>
```

## Event Types

The SDK supports different event types for tracking various user interactions:

### View Events

Track when users view content like products, search results, or outfits.

```typescript
// Track product detail view
analytics.trackProductDetailView({
  objectId: 'product-123',
  price: 99.99,
  category: 'clothing'
});

// Track search results view
analytics.trackSearchResultsView({
  queryId: 'search-456',
  query: 't-shirt',
  resultsCount: 24
});

// Track outfit view
analytics.trackOutfitView({
  objectId: 'outfit-789'
}, FlowType.OUTFIT);

// Track similar products view
analytics.trackSimilarProductsView({
  sourceObjectId: 'product-123'
});

// Track category page view
analytics.trackCategoryPageView({
  categoryId: 'men-clothing'
});
```

### Click Events

Track when users click on interactive elements.

```typescript
// Track product click
analytics.trackProductClick({
  objectId: 'product-123',
  price: 99.99
});

// Track search result click
analytics.trackSearchResultClick({
  objectId: 'product-123',
  queryId: 'search-456',
  position: 3
});

// Track outfit item click
analytics.trackOutfitItemClick({
  objectId: 'product-123',
  position: 2
}, FlowType.OUTFIT);

// Track similar product click
analytics.trackSimilarProductClick({
  objectId: 'product-456',
  sourceObjectId: 'product-123'
});
```

### Search Events

Track search-related actions like queries, filtering, and sorting.

```typescript
// Track search query
analytics.trackSearchQuery({
  queryId: 'search-456',
  query: 't-shirt'
});

// Track filter applied
analytics.trackFilterApplied({
  queryId: 'search-456',
  filters: {
    category: 'clothing',
    size: 'medium',
    color: 'blue'
  }
});

// Track sort changed
analytics.trackSortChanged({
  queryId: 'search-456',
  sort: 'price_asc'
});
```

### Conversion Events

Track commercial actions like adding to cart or purchasing.

```typescript
// Track add to cart (flow is required for conversion events)
analytics.trackAddToCart({
  objectId: 'product-123',
  price: 99.99,
  quantity: 1
}, FlowType.DIRECT);

// Track purchase
analytics.trackPurchase({
  objectId: 'product-123',
  value: 99.99,
  currency: 'USD',
  quantity: 1
}, FlowType.DIRECT);

// Track save for later
analytics.trackSaveForLater({
  objectId: 'product-123'
}, FlowType.SEARCH);
```

## Flow Types

Flow types indicate the user journey or context:

```typescript
// Available flow types:
FlowType.DIRECT    // Direct navigation or interaction
FlowType.SEARCH    // From search results
FlowType.SIMILAR   // From similar product recommendations
FlowType.OUTFIT    // From outfit/collection recommendations
FlowType.CATEGORY  // From category browsing
```

Most event methods use an appropriate default flow type, but you can override it. For conversion events, you must specify the flow explicitly to track attribution.

### Default Flow Types

The SDK automatically sets appropriate default flow types for each event:

| Event Method | Default Flow Type |
|--------------|------------------|
| trackSearchResultsView | FlowType.SEARCH |
| trackSearchResultClick | FlowType.SEARCH |
| trackSearchQuery | FlowType.SEARCH |
| trackFilterApplied | FlowType.SEARCH |
| trackSortChanged | FlowType.SEARCH |
| trackOutfitView | FlowType.OUTFIT |
| trackOutfitItemClick | FlowType.OUTFIT |
| trackSimilarProductClick | FlowType.SIMILAR |
| trackSimilarProductsView | FlowType.SIMILAR |
| trackCategoryPageView | FlowType.CATEGORY |
| trackProductDetailView | FlowType.DIRECT |
| trackProductClick | FlowType.DIRECT |

## Advanced Usage

### Manual Event Tracking

For more control or custom events, use the generic `trackEvent` method:

```typescript
import { EventType, EventSubtype, FlowType } from 'shoppi-analytics';

analytics.trackEvent(
  EventType.CONVERSION,
  EventSubtype.ADD_TO_CART,
  {
    objectId: 'product-123',
    objectType: 'product',
    quantity: 2,
    price: 99.99
  },
  FlowType.SEARCH
);
```

### Event Batching and Flushing

Events are batched and sent automatically based on your configuration:

- Events are queued until the batch size is reached (default: 10)
- Events are automatically sent every X milliseconds (default: 5000ms)
- You can manually flush events at any time:

```typescript
// Send all queued events immediately
analytics.flush();

// For single page applications, flush on route change
router.beforeEach((to, from, next) => {
  analytics.flush().then(() => next());
});

// Always flush before page unload
window.addEventListener('beforeunload', function() {
  analytics.flush();
});

// Call when your application is closing to ensure all events are sent
analytics.shutdown();
```

### Tracking Dynamically Loaded Content

For content loaded dynamically via AJAX or other methods:

```typescript
function loadProductsAndTrack(products) {
  const container = document.getElementById('product-container');
  
  // Clear container
  container.innerHTML = '';
  
  // Add products and track them
  products.forEach((product, index) => {
    // Create element
    const productEl = document.createElement('div');
    productEl.className = 'product-item';
    productEl.id = `product-${product.id}`;
    
    // Add data attributes
    productEl.dataset.shoppiObjectId = product.id;
    productEl.dataset.shoppiPosition = index + 1;
    productEl.dataset.shoppiPrice = product.price;
    productEl.dataset.shoppiCategory = product.category;
    
    // Add content
    productEl.innerHTML = `
      <h3>${product.name}</h3>
      <p>$${product.price}</p>
    `;
    
    // Add click handler
    productEl.addEventListener('click', () => {
      analytics.trackProductClick(`[id="product-${product.id}"]`);
    });
    
    // Add to container
    container.appendChild(productEl);
  });
}
```

## Event Types and Required Fields

| Event Type | Event Subtype | Description | Base Required Fields | Additional Required Fields | Flow Type Support |
|------------|---------------|-------------|---------------------|--------------------------|------------------|
| SEARCH | QUERY | Track search query execution | eventType, eventSubtype, eventName, flow, queryID, timestamp | query | All |
| | FILTER_APPLIED | Track when filters are applied | eventType, eventSubtype, eventName, flow, queryID, timestamp | filterName, filterValue | All |
| | FILTER_REMOVED | Track when filters are removed | eventType, eventSubtype, eventName, flow, queryID, timestamp | filterName, filterValue | All |
| | SORT_CHANGED | Track sort order changes | eventType, eventSubtype, eventName, flow, queryID, timestamp | sortOption | All |
| | ZERO_RESULTS | Track searches with no results | eventType, eventSubtype, eventName, flow, queryID, timestamp | query | All |
| CLICK | PRODUCT | Track product clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | - | All |
| | SEARCH_RESULT | Track search result clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | queryID, position | SEARCH |
| | SIMILAR_PRODUCT | Track similar product clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | sourceObjectID, position | SIMILAR |
| | OUTFIT_ITEM | Track outfit item clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | outfitID, position | OUTFIT |
| | OUTFIT | Track outfit clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | outfitID | OUTFIT |
| | PAGINATION | Track pagination clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | - | All |
| | FILTER | Track filter clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | - | All |
| | SORT | Track sort option clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | - | All |
| | SUGGESTION | Track search suggestion clicks | eventType, eventSubtype, eventName, flow, objectID, timestamp | suggestion, queryID | SEARCH |
| VIEW | PRODUCT_DETAIL | Track product detail views | eventType, eventSubtype, eventName, flow, timestamp | objectID | All |
| | SEARCH_RESULTS | Track search results views | eventType, eventSubtype, eventName, flow, timestamp | queryID, resultsCount | SEARCH |
| | SIMILAR_PRODUCTS | Track similar products views | eventType, eventSubtype, eventName, flow, timestamp | sourceObjectID | SIMILAR |
| | OUTFIT | Track outfit views | eventType, eventSubtype, eventName, flow, timestamp | outfitID | OUTFIT |
| | OUTFIT_COLLECTION | Track outfit collection views | eventType, eventSubtype, eventName, flow, timestamp | outfitID | OUTFIT |
| | CATEGORY_PAGE | Track category page views | eventType, eventSubtype, eventName, flow, timestamp | categoryID | CATEGORY |
| CONVERSION | PURCHASE | Track product purchases | eventType, eventSubtype, eventName, flow, objectID, timestamp | value, currency | All |
| | ADD_TO_CART | Track add to cart actions | eventType, eventSubtype, eventName, flow, objectID, timestamp | quantity | All |
| | SAVE_FOR_LATER | Track save for later actions | eventType, eventSubtype, eventName, flow, objectID, timestamp | - | All |
| IMPRESSION | PRODUCT_LIST | Track product list impressions | eventType, eventSubtype, eventName, flow, items, timestamp | - | All |
| | SIMILAR_PRODUCTS | Track similar products impressions | eventType, eventSubtype, eventName, flow, items, timestamp | sourceObjectID | SIMILAR |
| | OUTFIT_LIST | Track outfit list impressions | eventType, eventSubtype, eventName, flow, items, timestamp | outfitID | OUTFIT |
| | SEARCH_RESULTS | Track search results impressions | eventType, eventSubtype, eventName, flow, items, timestamp | queryID, resultsCount | SEARCH |

### Flow Types and Required Fields

| Flow Type | Description | Required Fields | Use Case |
|-----------|-------------|-----------------|-----------|
| DIRECT | Direct navigation or interaction | - | User directly navigates to a product or takes action without context |
| SEARCH | From search results | queryID | User interacts with content from search results |
| SIMILAR | From similar product recommendations | sourceObjectID | User interacts with similar product recommendations |
| OUTFIT | From outfit/collection recommendations | outfitID | User interacts with outfit or collection content |
| CATEGORY | From category browsing | categoryID | User browses and interacts with category pages |

### Notes:
- All events include common fields like `timestamp` and `flow`
- Flow-specific fields are required when using that flow type
- Additional custom properties can be included beyond the required fields
- Some events may require specific combinations of fields based on the context

## Configuration Options

- **apiKey**: Your API key for accessing the analytics server
- **clientId**: Your client ID for identifying the application
- **debug**: Enable or disable debug logging
- **userToken**: Optional user tokens for tracking user journeys
- **batchSize**: Maximum number of events to send in a single batch (default: 10)
- **flushInterval**: Interval in milliseconds to send events automatically (default: 5000ms)

## Best Practices

- **Initialize with existing IDs when available**
- **Generate and store client anonymous ID**
- **Update authenticated ID after login**
- **Keep both IDs after authentication**
- **For SPAs and page reloads**
- **Use appropriate flow types for conversion events**
- **Flush events before page unload**
- **Use selector-based tracking for dynamically loaded content**

## API Reference

### Core Methods

- **init(config)**: Initialize the analytics SDK
- **setUserToken(token)**: Set user identification tokens
- **setAnonymousId(id)**: Set anonymous user ID
- **setAuthenticatedId(id)**: Set authenticated user ID
- **flush()**: Flush queued events
- **shutdown()**: Shutdown the SDK

### View Events

- **trackProductDetailView**: Track product detail view
- **trackSearchResultsView**: Track search results view
- **trackOutfitView**: Track outfit view
- **trackSimilarProductsView**: Track similar products view
- **trackCategoryPageView**: Track category page view

### Click Events

- **trackProductClick**: Track product click
- **trackSearchResultClick**: Track search result click
- **trackOutfitItemClick**: Track outfit item click
- **trackSimilarProductClick**: Track similar product click

### Search Events

- **trackSearchQuery**: Track search query
- **trackFilterApplied**: Track filter applied
- **trackSortChanged**: Track sort changed

### Conversion Events

- **trackAddToCart**: Track add to cart
- **trackSearchAddToCart**: Track add to cart from search flow
- **trackOutfitAddToCart**: Track add to cart from outfit flow
- **trackSimilarAddToCart**: Track add to cart from similar products flow
- **trackPurchase**: Track purchase
- **trackSaveForLater**: Track save for later

### Generic Event Tracking

- **trackEvent**: Track custom event

## Cross-Device Tracking

The SDK supports cross-device tracking by using persistent identifiers.

## Changelog

- **v1.0.0**: Initial release
- **v1.1.0**: Added support for cross-device tracking
- **v1.2.0**: Added support for manual event tracking
- **v1.3.0**: Added support for event batching and flushing
- **v1.4.0**: Added support for tracking dynamically loaded content
- **v1.5.0**: Added support for custom tracking wrappers
- **v1.6.0**: Added support for event types and required fields
- **v1.7.0**: Added default flow types for all event methods