npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cryptique-sdk

v1.0.9

Published

Cryptique Analytics SDK - Comprehensive web analytics and user tracking for modern web applications

Downloads

977

Readme

Cryptique Analytics SDK - Function Reference Guide

This document provides a comprehensive reference for all functions in the modular Cryptique SDK. Use this guide to understand the logic, purpose, and when to reuse functions.


Table of Contents

  1. Architecture Overview
  2. Section 1: Configuration
  3. Section 2: Storage Management
  4. Section 3: Session ID Management
  5. Utility Functions

Architecture Overview

The SDK is organized into modular sections, each with a single source of truth. This prevents conflicts, duplicate code, and makes it easy to find and reuse functions.

Key Principles:

  • Single Source of Truth: Only one function handles each responsibility
  • Modular Design: Each section is independent and can be understood separately
  • Clear Data Flow: Functions have defined inputs/outputs
  • Error Handling: Centralized error handling in each section

Section 1: Configuration

Purpose

Centralizes all constants, API URLs, storage keys, and configuration values in one place.

Why It Matters

  • Easy to update URLs/keys without searching through code
  • Prevents typos and inconsistencies
  • Makes it clear what can be configured
  • Single place to change environment (dev/prod)

Configuration Object: CONFIG

CONFIG.API.TRACK              // Main session tracking endpoint
CONFIG.API.OVERVIEW           // Overview/analytics data endpoint
CONFIG.API.CUSTOM_EVENTS      // Custom event tracking endpoint
CONFIG.API.UTM_EVENTS         // UTM campaign tracking endpoint

CONFIG.VERSION                // SDK version string

CONFIG.STORAGE_KEYS.SESSION   // Primary session storage key
CONFIG.STORAGE_KEYS.USER_ID   // User ID storage key
CONFIG.STORAGE_KEYS.CONSENT   // Consent flag storage key

CONFIG.SESSION.TIMEOUT_MS     // Session timeout (30 minutes)
CONFIG.SESSION.BACKUP_VALIDITY_MS  // Backup validity (5 minutes)
CONFIG.SESSION.BOUNCE_THRESHOLD_SECONDS  // Bounce threshold (30 seconds)

CONFIG.INTERVALS.SESSION_TRACKING_MS  // Session update interval (5 seconds)
CONFIG.INTERVALS.OVERVIEW_DEBOUNCE_MS  // Overview debounce (10 seconds)

When to Use

  • Accessing API URLs: Use CONFIG.API.* instead of hardcoding URLs
  • Accessing Storage Keys: Use CONFIG.STORAGE_KEYS.* instead of string literals
  • Session Configuration: Use CONFIG.SESSION.* for timeout and validity checks
  • Changing Environment: Update CONFIG object to switch between dev/prod

Reusability

Highly Reusable: Import or reference CONFIG anywhere you need configuration values.


Section 2: Storage Management

Purpose

Single source of truth for ALL storage operations (localStorage, sessionStorage). Prevents conflicts and ensures consistency.

Why It Matters

  • Prevents storage conflicts (multiple functions writing different formats)
  • Centralized error handling
  • Easy to debug storage issues
  • Can add features (encryption, compression) in one place

StorageManager Object

StorageManager.loadSession()

Purpose: Load session from sessionStorage with localStorage backup fallback.

Logic Flow:

  1. Try sessionStorage first (primary source)
  2. If found, update localStorage backup
  3. If not found, try localStorage backup
  4. If backup is recent (< 5 min), restore it
  5. Return session or null

When to Use:

  • Loading existing session on page load
  • Checking if session exists
  • Recovering session after tab close

Returns: Session object with {id, userId, lastActivity} or null

Example:

const session = StorageManager.loadSession();
if (session) {
  console.log('Session found:', session.id);
}

StorageManager.saveSession(session)

Purpose: Save session to both sessionStorage and localStorage backup.

Logic:

  • Saves to sessionStorage (primary, fast, tab-specific)
  • Also saves to localStorage (backup, survives tab close)
  • Ensures we never lose session data

When to Use:

  • After creating new session
  • After updating session data
  • After session activity updates

Parameters: session - Session object with {id, userId, lastActivity, ...}

Example:

const session = {
  id: 'session-id',
  userId: 'user-id',
  lastActivity: Date.now()
};
StorageManager.saveSession(session);

StorageManager.updateBackup(session)

Purpose: Update localStorage backup (called automatically by saveSession).

Logic:

  • Creates lightweight backup with essential data only
  • Stores: sessionId, userId, timestamp
  • Used for session recovery across tabs

When to Use:

  • Usually called automatically by saveSession
  • Can be called manually if you only want to update backup

Parameters: session - Session object


StorageManager.getUserId()

Purpose: Get or create persistent user ID.

Logic:

  • Checks localStorage for existing user ID
  • If not found, generates new ID: "usr_" + random string
  • Stores in localStorage (survives browser restarts)

When to Use:

  • Initializing user session
  • Tracking user across sessions
  • User-level analytics

Returns: User ID string (e.g., "usr_abc123xyz")

Example:

const userId = StorageManager.getUserId();
// Always returns same ID for same user

StorageManager.getConsent() / StorageManager.setConsent(consent)

Purpose: Get/set user consent status for GDPR/privacy compliance.

Logic:

  • Stores consent flag in localStorage
  • Returns true if consent given, false otherwise
  • Defaults to false if storage fails

When to Use:

  • Checking if tracking is allowed
  • Storing user consent preference
  • Privacy compliance

Example:

if (StorageManager.getConsent()) {
  // User has consented, proceed with tracking
} else {
  // Show consent banner
}

// User gives consent
StorageManager.setConsent(true);

StorageManager.getReferrer() / StorageManager.saveReferrer(referrer)

Purpose: Get/set stored referrer (falls back to document.referrer).

Logic:

  • Stores referrer in localStorage
  • Falls back to document.referrer if not stored
  • Falls back to "direct" if no referrer

Why Store:

  • document.referrer can be lost on navigation
  • We want to track original referrer for entire session

When to Use:

  • Getting referrer for session tracking
  • Storing referrer on first page visit
  • Traffic source analysis

Example:

const referrer = StorageManager.getReferrer();
// Returns stored referrer or document.referrer or "direct"

StorageManager.saveReferrer("google.com");
// Stores referrer for future use

StorageManager.acquireLock(name, ttl)

Purpose: Acquire a lock to prevent race conditions.

Logic:

  • Stores timestamp in localStorage with key "lock_{name}"
  • If lock exists and is recent (< TTL), return false (locked)
  • Otherwise, set lock and return true (acquired)
  • Locks expire automatically after TTL

When to Use:

  • Preventing multiple functions from creating sessions simultaneously
  • Preventing duplicate API calls
  • Preventing concurrent initialization

Parameters:

  • name: Lock name (e.g., 'session_creation')
  • ttl: Time to live in milliseconds (default: 500ms)

Returns: true if lock acquired, false if already locked

Example:

if (StorageManager.acquireLock('session_creation')) {
  // Only one function can execute this block at a time
  createSession();
  StorageManager.releaseLock('session_creation'); // Optional
}

StorageManager.releaseLock(name)

Purpose: Release a lock (optional - locks expire automatically).

When to Use:

  • Explicitly releasing lock before TTL expires
  • Usually not needed (locks expire automatically)

StorageManager.saveLastSession(sessionData) / StorageManager.getLastSession()

Purpose: Save/get last session as backup for recovery on next page load.

Logic:

  • Saves full session data to localStorage
  • Used as last-resort backup if all other methods fail
  • Can be retrieved on next page load

When to Use:

  • Before page unload (as backup)
  • On page load (to recover lost session)
  • Error recovery scenarios

StorageManager.clearAll()

Purpose: Clear all SDK storage (useful for testing/reset).

Logic:

  • Removes session, backup, and last session
  • Does NOT clear USER_ID or CONSENT (persistent preferences)

When to Use:

  • Testing/debugging
  • User logout
  • Privacy reset

StorageManager.updateSessionActivity(session)

Purpose: Update session activity timestamp.

Logic:

  • Updates sessionData.lastActivity = Date.now()
  • If session provided, updates that session object and saves it
  • If no session provided, loads current session and updates it
  • Saves updated session to storage

Parameters: session (optional) - Session object to update. If not provided, loads current session.

Returns: Current timestamp (Date.now())

When to Use:

  • When tracking user activity (page visits, interactions, etc.)
  • To keep session alive
  • Called automatically by various managers

Example:

StorageManager.updateSessionActivity();
// Updates lastActivity and saves to storage

Reusability

Highly Reusable: All storage operations should go through StorageManager. Never access localStorage or sessionStorage directly.


Section 3: Session ID Management

Purpose

Single source of truth for session ID creation, retrieval, and management. Prevents duplicate sessions and ensures session continuity.

Why It Matters

  • Prevents duplicate session IDs
  • Ensures session continuity across page navigation
  • Handles edge cases (fast navigation, multiple tabs, storage failures)
  • Single place to fix session ID bugs

SessionIdManager Object

SessionIdManager.generate()

Purpose: Generate a new UUID v4 session ID.

Logic:

  • Creates UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
  • '4' indicates version 4 (random)
  • 'y' is one of 8, 9, A, or B
  • Guaranteed unique (collision-resistant)

When to Use:

  • Creating new session (called internally by getOrCreate())
  • Should NOT be called directly - use getOrCreate() instead

Returns: UUID v4 string

Example:

// Don't call directly - use getOrCreate() instead
const sessionId = SessionIdManager.getOrCreate();

SessionIdManager.isSessionValid(session)

Purpose: Check if a session is still valid (not expired).

Logic:

  • Session is valid if:
    • lastActivity exists AND is less than 5 minutes ago
    • OR lastActivity doesn't exist (new session format)
  • Uses CONFIG.SESSION.TIMEOUT_MS (30 minutes) for timeout check

When to Use:

  • Before reusing existing session
  • Checking if session should be expired
  • Validating session before use

Parameters: session - Session object with lastActivity property

Returns: true if valid, false if expired

Example:

const session = StorageManager.loadSession();
if (SessionIdManager.isSessionValid(session)) {
  // Session is still active
} else {
  // Session expired, create new one
}

SessionIdManager.updateActivity(session)

Purpose: Update session activity timestamp to keep session alive.

Logic:

  • Sets session.lastActivity to current timestamp
  • Saves session to storage
  • Called when session is reused

When to Use:

  • After reusing existing session
  • After user activity
  • To prevent session expiration

Parameters: session - Session object

Returns: Current timestamp (number)

Example:

const session = StorageManager.loadSession();
SessionIdManager.updateActivity(session);
// Session activity updated, session stays alive

SessionIdManager.getOrCreate()

Purpose: Get existing session ID or create new one - THE SINGLE SOURCE OF TRUTH.

Logic Flow:

  1. Ensure we have user ID first
  2. Try to load existing session (with retries for fast navigation)
  3. If found, check if it's still valid (< 5 min inactivity)
  4. If valid, reuse it and update activity
  5. If expired or not found, do final checks (with delays)
  6. If still not found, create new session
  7. Verify it was saved correctly

Key Features:

  • Retry logic handles fast page navigation (up to 3 attempts)
  • Final checks with delays (up to 5 attempts, 10ms between)
  • Multiple fallbacks: sessionStorage → localStorage → generate new
  • Verification ensures session was saved correctly

When to Use:

  • ONLY function that should create session IDs
  • On page load to get/create session
  • When you need to ensure session exists
  • Never call generate() directly - always use this

Returns: Session ID string (UUID v4)

Example:

// This is the ONLY way to get/create session ID
const sessionId = SessionIdManager.getOrCreate();
sessionData.sessionId = sessionId;

SessionIdManager.syncFromStorage()

Purpose: Sync session ID from storage if there's a mismatch.

Logic:

  • Loads session from storage
  • If storage has different ID than sessionData.sessionId, sync it
  • Fixes inconsistencies between memory and storage

When to Use:

  • When you suspect session ID mismatch
  • After loading session from storage
  • To fix inconsistencies

Returns: true if synced, false if no sync needed

Example:

if (SessionIdManager.syncFromStorage()) {
  console.log('Session ID synced from storage');
}

SessionIdManager.initialize()

Purpose: Initialize session data with session ID.

Logic:

  • Calls getOrCreate() to get/create session ID
  • Sets sessionData.sessionId and userSession.sessionId
  • Ensures session ID is never null/undefined
  • Has fallbacks if initialization fails

When to Use:

  • On SDK initialization
  • When you need to ensure session is initialized
  • Usually called by initSessionOnce()

Example:

SessionIdManager.initialize();
// sessionData.sessionId is now set

Initialization Guard: initSessionOnce()

initSessionOnce()

Purpose: Initialize session once - prevents race conditions and duplicate initialization.

Logic:

  • Uses lock mechanism to ensure only one initialization happens
  • Checks if already initialized
  • If locked, waits and retries
  • Ensures session ID is set before returning

When to Use:

  • On SDK startup
  • Should be called before any tracking begins
  • Prevents multiple initializations in multiple tabs

Example:

initSessionOnce();
// Session is now initialized, safe to start tracking

Utility Functions (Used by Session ID Management)

retryWithDelay(fn, maxAttempts, delayMs)

Purpose: Retry a function with delays between attempts.

Logic:

  • Calls function up to maxAttempts times
  • Waits delayMs milliseconds between attempts
  • Returns first successful result
  • Returns null if all attempts fail

When to Use:

  • Handling timing issues during fast page navigation
  • Retrying storage operations
  • Any operation that might fail due to timing

Parameters:

  • fn: Function to retry (should return truthy value on success)
  • maxAttempts: Maximum number of attempts (default: 3)
  • delayMs: Delay between attempts in milliseconds (default: 5)

Returns: Result from function or null

Example:

const session = retryWithDelay(() => {
  return StorageManager.loadSession();
}, 3, 5);

ensureArray(value, defaultValue)

Purpose: Ensure value is an array, return default if not.

When to Use:

  • Normalizing data that might be array or not
  • Ensuring arrays exist before operations

Returns: Array (original or default)

Example:

const visits = ensureArray(sessionData.pageVisits, []);
// visits is guaranteed to be an array

ensureObject(value, defaultValue)

Purpose: Ensure value is an object, return default if not.

When to Use:

  • Normalizing data that might be object or not
  • Ensuring objects exist before operations

Returns: Object (original or default)

Example:

const data = ensureObject(rawData, {});
// data is guaranteed to be an object

nowIso()

Purpose: Get ISO timestamp string.

Returns: ISO 8601 timestamp string (e.g., "2024-01-15T10:30:00.000Z")

When to Use:

  • Storing timestamps in ISO format
  • Normalizing timestamps
  • API payloads requiring ISO format

Example:

const timestamp = nowIso();
// "2024-01-15T10:30:00.000Z"

getUTMParameters()

Purpose: Extract UTM parameters from URL.

Returns: Object with UTM parameters:

{
  source: 'google',
  medium: 'cpc',
  campaign: 'summer_sale',
  term: 'keyword',
  content: 'ad_variant',
  utm_id: '12345'
}

When to Use:

  • Tracking campaign sources
  • Session initialization
  • UTM event logging

Example:

const utmData = getUTMParameters();
if (utmData.source) {
  console.log('User came from:', utmData.source);
}

getProperReferrer()

Purpose: Get referrer, prioritizing UTM source over document.referrer.

Logic:

  • If UTM source exists, return it
  • Otherwise, return stored referrer or document.referrer or "direct"

When to Use:

  • Session initialization
  • Traffic source tracking
  • When you need the best referrer value

Returns: Referrer string

Example:

const referrer = getProperReferrer();
// Returns UTM source if available, otherwise stored/browser referrer

Reusability

SessionIdManager.getOrCreate(): MUST USE - This is the ONLY function that should create session IDs. Never call generate() directly.

SessionIdManager.isSessionValid(): Reusable for checking session validity anywhere.

Utility Functions: All utility functions are reusable across the SDK.


Data Structures

sessionData

Minimal structure for Session ID Management:

{
  sessionId: null,        // Set by SessionIdManager
  siteId: string,
  userId: null,          // Set from StorageManager
  referrer: "direct",
  utmData: {},
  startTime: null,
  lastActivity: null,
  isFirstPage: true
}

userSession

Synced with sessionData:

{
  sessionId: null,        // Synced with sessionData.sessionId
  userId: null            // Synced with sessionData.userId
}

Best Practices

✅ DO:

  • Use StorageManager for all storage operations
  • Use SessionIdManager.getOrCreate() to get/create session IDs
  • Use CONFIG for all configuration values
  • Use initSessionOnce() before starting tracking
  • Check SessionIdManager.isSessionValid() before reusing sessions

❌ DON'T:

  • Don't access localStorage or sessionStorage directly
  • Don't call SessionIdManager.generate() directly
  • Don't create session IDs in multiple places
  • Don't hardcode storage keys or API URLs
  • Don't skip initialization guard

Quick Reference

Getting Session ID

const sessionId = SessionIdManager.getOrCreate();

Loading Session

const session = StorageManager.loadSession();

Saving Session

StorageManager.saveSession(session);

Getting User ID

const userId = StorageManager.getUserId();

Checking Consent

if (StorageManager.getConsent()) {
  // User consented
}

Acquiring Lock

if (StorageManager.acquireLock('operation_name')) {
  // Do operation
  StorageManager.releaseLock('operation_name');
}

Section 4: Session Duration Calculation

Purpose

Single source of truth for session duration calculations. Ensures accurate session duration tracking and consistency between page durations and session duration.

Why It Matters

  • Prevents inaccurate session times
  • Ensures consistency between page and session data
  • Proper bounce detection
  • Single place to fix duration bugs

DurationManager Object

DurationManager.calculateSeconds(startTime, endTime)

Purpose: Calculate duration in seconds between two timestamps (rounded).

Logic:

  • Converts timestamps to Date objects
  • Calculates difference in milliseconds
  • Converts to seconds
  • Rounds to nearest second

When to Use:

  • General duration calculations where rounding is acceptable
  • Session duration from start time

Parameters:

  • startTime: Start timestamp (Date object or string/number)
  • endTime: End timestamp (optional, defaults to current time)

Returns: Duration in seconds (rounded)

Example:

const duration = DurationManager.calculateSeconds(sessionData.startTime);
// Returns rounded duration in seconds

DurationManager.calculateSecondsFloor(startTime, endTime)

Purpose: Calculate duration in seconds between two timestamps (floored).

Logic: Same as calculateSeconds() but uses Math.floor() instead of Math.round()

When to Use:

  • Page durations (more conservative, prevents over-counting)
  • When you need exact seconds without rounding up

Returns: Duration in seconds (floored)

Example:

const pageDuration = DurationManager.calculateSecondsFloor(page.mountTime, page.unmountTime);
// Returns floored duration in seconds

DurationManager.calculateFromPages(pageVisits)

Purpose: Calculate session duration from page mount/unmount times (PREFERRED METHOD).

Logic:

  1. Get first page mountTime (session start)
  2. Get last page unmountTime or current time (session end)
  3. Calculate: endTime - startTime

When to Use:

  • When you have page visit data with mount/unmount times
  • Most accurate calculation method

Parameters: pageVisits - Array of page visit objects with mountTime and unmountTime

Returns: Duration in seconds or null if can't calculate

Example:

const duration = DurationManager.calculateFromPages(sessionData.pageVisits);
if (duration !== null) {
  // Successfully calculated from page times
}

DurationManager.updateSessionDuration(pageVisits, startTime)

Purpose: Update sessionData.duration - THE SINGLE SOURCE OF TRUTH.

Logic:

  1. Try to calculate from page mount/unmount times (preferred)
  2. If that fails, calculate from session start time (fallback)
  3. Update sessionData.duration
  4. Return duration

When to Use:

  • ONLY function that should modify sessionData.duration
  • Whenever you need to update session duration
  • Before sending data to backend

Parameters:

  • pageVisits: Optional, defaults to sessionData.pageVisits
  • startTime: Optional, defaults to sessionData.startTime

Returns: Duration in seconds

Example:

// This is the ONLY way to update session duration
const duration = DurationManager.updateSessionDuration();
sessionData.duration = duration; // Already set by function

DurationManager.calculateAllPageDurations(pageVisits, currentTime)

Purpose: Calculate duration for all pages in the session.

Logic for each page:

  1. If has mountTime and unmountTime: use mount→unmount
  2. If has mountTime but no unmountTime: use mount→current
  3. If no mountTime: use timestamp fallback
  4. If no data: use minimum 1 second

When to Use:

  • When you need to calculate/update all page durations
  • Before aligning page durations with session duration
  • On page navigation

Parameters:

  • pageVisits: Array of page visit objects
  • currentTime: Optional, defaults to Date.now()

Returns: Array of page visits with calculated durations

Example:

const updatedPageVisits = DurationManager.calculateAllPageDurations(sessionData.pageVisits);
sessionData.pageVisits = updatedPageVisits;

DurationManager.alignPageDurations(pageVisits, sessionDuration)

Purpose: Ensure sum of page durations matches session duration exactly.

Logic:

  1. Sum all page durations
  2. Compare sum to session duration
  3. If different, adjust last page duration by the difference
  4. Ensure minimum 1 second

When to Use:

  • Before sending data to backend (ensures consistency)
  • After calculating session duration
  • To fix discrepancies between page and session durations

Parameters:

  • pageVisits: Array of page visit objects
  • sessionDuration: Target session duration in seconds

Returns: Updated page visits array

Example:

const alignedPageVisits = DurationManager.alignPageDurations(
  sessionData.pageVisits,
  sessionData.duration
);
sessionData.pageVisits = alignedPageVisits;

DurationManager.calculateBounceStatus(duration, pagesViewed)

Purpose: Determine if session is a bounce.

Logic:

  • Bounce = duration < 30 seconds AND pagesViewed <= 1
  • Uses CONFIG.SESSION.BOUNCE_THRESHOLD_SECONDS (30 seconds)

When to Use:

  • When you need to check if session is a bounce
  • Before sending data to backend
  • For bounce rate calculations

Parameters:

  • duration: Session duration in seconds
  • pagesViewed: Number of pages viewed

Returns: true if bounce, false if not

Example:

const isBounce = DurationManager.calculateBounceStatus(
  sessionData.duration,
  sessionData.pagesViewed
);
sessionData.isBounce = isBounce;

DurationManager.ensureValid(duration)

Purpose: Ensure duration is always a valid number.

Logic:

  • Converts to number
  • If null, undefined, NaN, or <= 0: return 1 (minimum)
  • Otherwise: return floored value

When to Use:

  • Before using duration values
  • When validating duration data
  • To prevent invalid durations

Parameters: duration - Duration value (any type)

Returns: Valid duration number (minimum 1 second)

Example:

const validDuration = DurationManager.ensureValid(page.duration);
// Always returns valid number (minimum 1)

Calculation Methods

Method A: From Page Mount/Unmount Times (Preferred)

  • Uses: mountTime and unmountTime of pages
  • Accuracy: Most accurate
  • Logic: lastPage.unmountTime - firstPage.mountTime
  • When: If page visits have mountTime data

Method B: From Session Start Time (Fallback)

  • Uses: sessionData.startTime and current time
  • Accuracy: Less accurate (includes navigation time)
  • Logic: currentTime - startTime
  • When: If no page mount/unmount times available

Key Principles

  1. Single Source of Truth: Only updateSessionDuration() modifies sessionData.duration
  2. Preferred Method: Use page mount/unmount times when available
  3. Fallback Logic: Always have a fallback calculation method
  4. Consistency: Page durations must sum to session duration
  5. Validation: Always validate durations before use
  6. Minimum Values: Never allow 0 or negative durations

Reusability

DurationManager.updateSessionDuration(): MUST USE - This is the ONLY function that should modify sessionData.duration. Never modify duration directly.

DurationManager.calculateBounceStatus(): Reusable for bounce detection anywhere.

DurationManager.ensureValid(): Reusable for validating any duration value.


Section 5: Location/Geolocation Management

Purpose

Single source of truth for location/geolocation data fetching and storage. Handles IP-based geolocation via ipinfo.io API.

Why It Matters

  • Centralized location data management
  • Caching prevents unnecessary API calls
  • Fallback ensures we always have location data
  • Single place to fix location bugs

LocationManager Object

LocationManager.fetchLocationData()

Purpose: Fetch location data from ipinfo.io API.

Logic Flow:

  1. Try primary URL with timeout
  2. If fails, try backup URL
  3. If both fail, return default values
  4. Parse and store location data

When to Use:

  • Initializing location data
  • Refreshing location data
  • When location data is missing

Returns: Promise that resolves with location data object

Location Data Structure:

{
  country: "US",
  ip_address: "192.168.1.1",
  city: "New York",
  region: "NY",
  latitude: "40.7128",
  longitude: "-74.0060",
  timezone: "America/New_York",
  org: "AS12345 Example Org",
  postal: "10001"
}

LocationManager.storeLocationData(locationData)

Purpose: Store location data in sessionData (flattened fields).

Logic:

  • Stores all location fields directly on sessionData:
    • ip_address, city, region, country, latitude, longitude, timezone, org, postal
  • Also stores in sessionData.locationData object (for structured access)
  • Updates cached country name

When to Use:

  • After fetching location data
  • When location data is updated

Parameters: locationData - Location data object


LocationManager.getCountryName()

Purpose: Get country name (cached or fetch).

Logic:

  1. Check runtimeState.countryName cache
  2. Check sessionData.country
  3. If not available, fetch and cache

Returns: Promise that resolves with country name string


LocationManager.getLocationData()

Purpose: Get full location data (cached or fetch).

Logic:

  1. Check sessionData.locationData
  2. If not available or invalid, fetch
  3. Return location data object

Returns: Promise that resolves with location data


LocationManager.initialize()

Purpose: Initialize location data (non-blocking).

When to Use:

  • On SDK initialization
  • Should be called early but doesn't block other operations

Reusability

LocationManager.fetchLocationData(): Reusable for fetching location anywhere.

LocationManager.storeLocationData(): Reusable for storing location data.


Section 6: Session Data Structure & Normalization

Purpose

Complete sessionData structure initialization and data normalization for backend compatibility. Handles field mapping (camelCase → snake_case) and ensures all required fields are present.

Why It Matters

  • Ensures all required fields are present (even if null)
  • Handles field mapping consistently (camelCase → snake_case)
  • Single place to manage session data structure
  • Prevents missing fields errors

Complete SessionData Structure

let sessionData = {
  // IDs
  sessionId: null,              // UUID v4 (internal camelCase)
  siteId: SITE_ID,
  teamId: null,                 // May be set externally or from backend
  userId: null,
  
  // Time fields
  startTime: null,              // ISO string (internal)
  endTime: null,                // ISO string (internal)
  lastActivity: null,           // Timestamp (internal)
  duration: 0,                  // Seconds
  
  // Basic session data
  pagesViewed: 0,               // Internal camelCase
  isBounce: true,               // Internal camelCase
  previousSessionId: null,
  timeSinceLastSession: null,
  
  // Entry/Exit pages
  entryPage: null,
  exitPage: null,
  referrer: "direct",
  
  // Location fields (FLATTENED - stored directly on sessionData)
  ip_address: null,             // snake_case for backend
  city: null,
  region: null,
  country: "",
  latitude: null,
  longitude: null,
  timezone: null,
  org: null,
  postal: null,
  
  // Browser fields (FLATTENED)
  browser_name: null,
  browser_version: null,
  
  // Device fields (FLATTENED)
  device_type: null,
  os: null,
  resolution: null,
  user_agent: null,
  language: null,
  
  // Wallet fields (FLATTENED)
  wallet_address: null,
  wallet_type: null,
  chain_name: null,
  is_web3_user: false,
  wallet_connected: false,
  
  // UTM fields (FLATTENED)
  utm_source: null,
  utm_medium: null,
  utm_campaign: null,
  utm_term: null,
  utm_content: null,
  utm_id: null,
  
  // JSONB fields
  interactions: {},
  visited_pages: []
};

SessionDataManager Object

SessionDataManager.initialize()

Purpose: Initialize complete sessionData structure.

Logic:

  • Ensures all required fields exist (some may already be set)
  • Sets default values for missing fields
  • Initializes time fields (startTime, endTime, lastActivity, duration)
  • Initializes basic session data (pagesViewed, isBounce, etc.)
  • Initializes all location, browser, device, wallet, and UTM fields
  • Initializes JSONB fields (interactions, visited_pages)

When to Use:

  • On SDK initialization
  • When sessionData structure needs to be validated
  • Before sending data to backend

Example:

SessionDataManager.initialize();
// Ensures all fields are present and properly initialized

SessionDataManager.toSnakeCase(camelCase)

Purpose: Convert camelCase field name to snake_case.

Examples:

  • sessionIdsession_id
  • pagesViewedpages_viewed
  • isBounceis_bounce

Parameters: camelCase - Field name in camelCase

Returns: Field name in snake_case


SessionDataManager.toCamelCase(snakeCase)

Purpose: Convert snake_case field name to camelCase.

Examples:

  • session_idsessionId
  • pages_viewedpagesViewed
  • is_bounceisBounce

Parameters: snakeCase - Field name in snake_case

Returns: Field name in camelCase


SessionDataManager.mapFieldName(internalName)

Purpose: Map internal field names to backend field names.

Logic:

  • Uses predefined field map for special cases
  • Falls back to toSnakeCase() for unmapped fields
  • Handles special cases where mapping isn't straightforward

Field Map:

  • sessionIdsession_id
  • siteIdsite_id
  • teamIdteam_id
  • userIduser_id
  • startTimestart_time
  • endTimeend_time
  • lastActivitylast_activity
  • pagesViewedpages_viewed
  • isBounceis_bounce
  • previousSessionIdprevious_session_id
  • timeSinceLastSessiontime_since_last_session
  • entryPageentry_page
  • exitPageexit_page

Parameters: internalName - Internal field name (camelCase)

Returns: Backend field name (snake_case)


Field Mapping Strategy

Internal (JavaScript - camelCase):

  • sessionId → Backend: id (auto-generated by PostgreSQL) and session_id
  • siteId → Backend: site_id
  • userId → Backend: user_id
  • startTime → Backend: start_time
  • pagesViewed → Backend: pages_viewed
  • isBounce → Backend: is_bounce
  • lastActivity → Backend: last_activity
  • previousSessionId → Backend: previous_session_id
  • timeSinceLastSession → Backend: time_since_last_session
  • entryPage → Backend: entry_page
  • exitPage → Backend: exit_page

Location fields: Already in snake_case (stored directly)

Browser/Device/Wallet/UTM fields: Already in snake_case (stored directly)


Section 7: Browser/Device Detection

Purpose

Extract and store browser/device information as flattened fields. Centralized browser and device detection for consistent data collection.

Why It Matters

  • Centralized browser/device detection
  • Consistent field structure
  • Single place to fix detection bugs
  • Handles modern User-Agent Client Hints API with fallbacks

BrowserDeviceManager Object

BrowserDeviceManager.detect()

Purpose: Detect browser and device information.

Logic:

  1. Detects device type (desktop/mobile/tablet) from user agent
  2. Extracts browser name using modern User-Agent Client Hints API (if available)
  3. Falls back to parsing user agent string for browser name
  4. Extracts OS, resolution, user agent, and language

Device Type Detection:

  • Mobile: /Mobi|Android/i in user agent
  • Tablet: /Tablet|iPad/i in user agent
  • Desktop: Default (if not mobile or tablet)

Browser Detection:

  • Primary: navigator.userAgentData?.brands?.[0]?.brand (modern API)
  • Fallback: Parse user agent string (Chrome, Firefox, Safari, Edge, Opera)
  • Final fallback: navigator.appName

Returns: Object with browser/device fields (all snake_case):

{
  browser_name: "Chrome",           // Browser name
  browser_version: "120.0.0.0",     // Browser version
  device_type: "desktop",           // "desktop" | "mobile" | "tablet"
  os: "Win32",                      // Operating system
  resolution: "1920x1080",          // Screen resolution
  user_agent: navigator.userAgent,  // Full user agent string
  language: "en-US"                 // Browser language
}

When to Use:

  • On SDK initialization
  • When browser/device info needs to be refreshed
  • Before sending data to backend

Example:

const browserDeviceData = BrowserDeviceManager.detect();
// Returns: { browser_name: "Chrome", device_type: "desktop", ... }

BrowserDeviceManager.store(data)

Purpose: Store browser/device data in sessionData (flattened).

Logic:

  • Stores all fields directly on sessionData (flattened for backend compatibility)
  • If no data provided, calls detect() first
  • All fields stored in snake_case format

Stores on sessionData:

  • browser_name - Browser name
  • browser_version - Browser version
  • device_type - Device type (desktop/mobile/tablet)
  • os - Operating system
  • resolution - Screen resolution (e.g., "1920x1080")
  • user_agent - Full user agent string
  • language - Browser language

Parameters: data (optional) - Browser/device data object. If not provided, calls detect().

When to Use:

  • After detecting browser/device info
  • On SDK initialization
  • When browser/device data needs to be updated

Example:

BrowserDeviceManager.store(); // Auto-detects and stores
// OR
const data = BrowserDeviceManager.detect();
BrowserDeviceManager.store(data);

BrowserDeviceManager.initialize()

Purpose: Initialize browser/device detection.

Logic:

  • Calls detect() to get browser/device information
  • Calls store() to save data to sessionData

When to Use:

  • On SDK initialization
  • Should be called early in initialization flow

Example:

BrowserDeviceManager.initialize();
// Detects and stores browser/device info in sessionData

Reusability

BrowserDeviceManager.detect(): Reusable for detecting browser/device anywhere.

BrowserDeviceManager.store(): Reusable for storing browser/device data.

BrowserDeviceManager.initialize(): Reusable for initialization.


Section 8: UTM Parameter Management

Purpose

Extract and store UTM parameters as flattened fields. Centralized UTM parameter extraction from URL query string.

Why It Matters

  • Centralized UTM parameter extraction
  • Consistent field structure
  • Single place to fix UTM bugs
  • Handles missing parameters gracefully

UTMManager Object

UTMManager.extract()

Purpose: Extract UTM parameters from URL query string.

Logic:

  • Uses URLSearchParams to parse query string
  • Extracts all 6 UTM parameters
  • Returns null for missing parameters (not empty strings)

Returns: Object with UTM fields (all snake_case):

{
  utm_source: "google",        // or null
  utm_medium: "cpc",           // or null
  utm_campaign: "summer_sale",  // or null
  utm_term: "keyword",         // or null
  utm_content: "ad_variant",   // or null
  utm_id: "12345"              // or null
}

When to Use:

  • On SDK initialization
  • When UTM parameters need to be refreshed
  • Before sending data to backend

Example:

const utmData = UTMManager.extract();
// Returns: { utm_source: "google", utm_medium: "cpc", ... }

UTMManager.store(data)

Purpose: Store UTM data in sessionData (flattened).

Logic:

  • Stores all 6 UTM fields directly on sessionData (flattened)
  • Also stores in sessionData.utmData object (for backward compatibility)
  • If no data provided, calls extract() first

Stores on sessionData:

  • utm_source - UTM source parameter
  • utm_medium - UTM medium parameter
  • utm_campaign - UTM campaign parameter
  • utm_term - UTM term parameter
  • utm_content - UTM content parameter
  • utm_id - UTM ID parameter

Parameters: data (optional) - UTM data object. If not provided, calls extract().

When to Use:

  • After extracting UTM parameters
  • On SDK initialization
  • When UTM data needs to be updated

Example:

UTMManager.store(); // Auto-extracts and stores
// OR
const data = UTMManager.extract();
UTMManager.store(data);

UTMManager.initialize()

Purpose: Initialize UTM parameter extraction.

Logic:

  • Calls extract() to get UTM parameters
  • Calls store() to save data to sessionData

When to Use:

  • On SDK initialization
  • Should be called early in initialization flow

Example:

UTMManager.initialize();
// Extracts and stores UTM parameters in sessionData

Reusability

UTMManager.extract(): Reusable for extracting UTM parameters anywhere.

UTMManager.store(): Reusable for storing UTM data.

UTMManager.initialize(): Reusable for initialization.


Section 9: Wallet/Web3 Management

Purpose

Detect and store wallet/Web3 information as flattened fields. Comprehensive support for EVM wallets and chains with real-time tracking.

Why It Matters

  • Comprehensive Wallet Detection: Supports 30+ EVM wallets
  • Multi-Chain Support: Recognizes 40+ EVM-compatible chains
  • EIP-6963 Support: Modern multi-wallet detection standard
  • Real-Time Tracking: Listens for wallet events (account changes, chain changes)
  • Non-Intrusive: Detects wallets without prompting user
  • Single Source of Truth: Centralized wallet state management

WalletManager Object

Supported Wallets (30+ EVM Wallets)

Major Wallets:

  • MetaMask - Most popular Ethereum wallet
  • Trust Wallet - Popular mobile wallet
  • Coinbase Wallet - Coinbase's official wallet
  • Brave Wallet - Built into Brave browser
  • Frame - Privacy-focused wallet
  • Phantom - Solana wallet (EVM support)
  • TronLink - Tron wallet

Additional Wallets:

  • OKX Wallet - OKX exchange wallet
  • TokenPocket - Multi-chain wallet
  • SafePal - Hardware wallet software
  • MathWallet - Multi-chain wallet
  • 1inch Wallet - DEX aggregator wallet
  • Alpha Wallet - Mobile wallet
  • AToken - Multi-chain wallet
  • BitKeep - Popular DeFi wallet
  • BlockWallet - Privacy wallet
  • Coin98 - Vietnamese wallet
  • Dawn - Ethereum wallet
  • Enkrypt - Multi-chain wallet
  • Exodus - Desktop wallet
  • Frontier - DeFi wallet
  • Gamestop - GameStop wallet
  • HyperPay - Payment wallet
  • ImToken - Chinese wallet
  • Infinity Wallet - Multi-chain wallet
  • Liquality - Atomic swap wallet
  • MEW CX - MyEtherWallet
  • Opera - Opera browser wallet
  • Portal - WalletConnect wallet
  • Rabby - DeFi wallet
  • Rainbow - Beautiful mobile wallet
  • Status - Mobile wallet
  • Tally - Community wallet
  • Tokenary - iOS wallet
  • Torus - Social login wallet
  • XDEFI - Multi-chain wallet
  • Zerion - DeFi portfolio wallet

Detection Methods:

  1. EIP-6963 (Modern Standard): Detects wallets via eip6963:announceProvider event
  2. window.ethereum Properties: Checks wallet-specific properties (e.g., isMetaMask)
  3. Global Wallet Objects: Checks for wallet-specific global objects (e.g., window.okxwallet)

Supported Chains (40+ EVM-Compatible Chains)

Mainnets:

  • Ethereum Mainnet (Chain ID: 1)
  • Binance Smart Chain (Chain ID: 56)
  • Polygon (Chain ID: 137)
  • Optimism (Chain ID: 10)
  • Arbitrum One (Chain ID: 42161)
  • Avalanche C-Chain (Chain ID: 43114)
  • Fantom Opera (Chain ID: 250)
  • Gnosis Chain (Chain ID: 100)
  • Aurora (Chain ID: 1313161554)
  • Base (Chain ID: 8453)
  • zkSync Era (Chain ID: 324)
  • Linea (Chain ID: 59144)
  • Scroll (Chain ID: 534352)
  • Mantle (Chain ID: 5000)
  • Celo (Chain ID: 42220)
  • Moonbeam (Chain ID: 1284)
  • Moonriver (Chain ID: 1285)
  • Harmony (Chain ID: 1666600000)
  • Cronos (Chain ID: 25)
  • Metis (Chain ID: 1088)
  • Polygon zkEVM (Chain ID: 1101)
  • opBNB (Chain ID: 204)
  • OKXChain (Chain ID: 196)
  • Blast (Chain ID: 81457)
  • Chiliz Chain (Chain ID: 88888)
  • ZetaChain (Chain ID: 7001)
  • Core (Chain ID: 1116)
  • Astar zkEVM (Chain ID: 3776)

Testnets:

  • Holesky (Chain ID: 17000)
  • Sepolia (Chain ID: 11155111)
  • Goerli (Chain ID: 5)
  • BSC Testnet (Chain ID: 97)
  • Mumbai (Chain ID: 80001)
  • Arbitrum Sepolia (Chain ID: 421614)
  • Optimism Sepolia (Chain ID: 11155420)
  • Base Sepolia (Chain ID: 84532)
  • Avalanche Fuji (Chain ID: 43113)
  • Fantom Testnet (Chain ID: 4002)

Unknown Chains:

  • ✅ Automatically displays as Unknown Chain (ID: <chainId>) for unrecognized chains

WalletManager.detectWalletType()

Purpose: Detect wallet type from available providers.

Logic:

  1. Checks EIP-6963 providers first (modern standard)
  2. Checks window.ethereum wallet patterns (30+ wallets)
  3. Checks global wallet objects (OKX, TokenPocket, etc.)
  4. Falls back to "Web3 Wallet" if window.ethereum exists but no specific wallet detected
  5. Returns "No Wallet Detected" if no wallet found

Returns: Wallet name string (e.g., "MetaMask", "Trust Wallet", "No Wallet Detected")

When to Use:

  • On SDK initialization
  • When wallet type needs to be refreshed
  • Before sending data to backend

Example:

const walletType = WalletManager.detectWalletType();
// Returns: "MetaMask" or "Trust Wallet" or "No Wallet Detected"

WalletManager.getChainNameFromId(chainId)

Purpose: Convert chain ID to human-readable chain name.

Logic:

  • Handles hex string chain IDs (e.g., "0x1" → 1)
  • Handles decimal string chain IDs (e.g., "1" → 1)
  • Looks up chain name in CHAIN_NAMES mapping
  • Returns "Unknown Chain (ID: )" for unrecognized chains

Parameters: chainId - Chain ID (number, hex string, or decimal string)

Returns: Chain name string (e.g., "Ethereum Mainnet", "Polygon")

Example:

const chainName = WalletManager.getChainNameFromId(1);
// Returns: "Ethereum Mainnet"

const chainName = WalletManager.getChainNameFromId("0x38");
// Returns: "Binance Smart Chain"

WalletManager.detectChainId()

Purpose: Detect chain ID from provider.

Logic:

  1. Checks window.ethereum.chainId property (modern providers)
  2. Requests chain ID via eth_chainId method
  3. Falls back to Web3.js if available
  4. Returns null if no provider or error

Returns: Promise that resolves to chain ID (hex string) or null

When to Use:

  • When chain ID needs to be detected
  • Before converting to chain name

Example:

const chainId = await WalletManager.detectChainId();
// Returns: "0x1" (Ethereum) or null

WalletManager.detectChainName()

Purpose: Detect chain name from provider.

Logic:

  1. Gets chain ID via detectChainId()
  2. Converts chain ID to chain name via getChainNameFromId()
  3. Returns "Not Connected" if no chain ID

Returns: Promise that resolves to chain name string

When to Use:

  • On SDK initialization
  • When chain name needs to be refreshed
  • After chain change events

Example:

const chainName = await WalletManager.detectChainName();
// Returns: "Ethereum Mainnet" or "Not Connected"

WalletManager.checkWalletConnected()

Purpose: Check if wallet is connected (non-intrusive).

Logic:

  • Uses eth_accounts method (doesn't prompt user)
  • Returns true if accounts exist, false otherwise
  • Returns false if no provider

Returns: Promise that resolves to boolean

When to Use:

  • On SDK initialization (silent check)
  • Before updating wallet info
  • To check connection status without prompting

Example:

const isConnected = await WalletManager.checkWalletConnected();
// Returns: true or false

WalletManager.getWalletAddress()

Purpose: Get connected wallet address (non-intrusive).

Logic:

  • Uses eth_accounts method (doesn't prompt user)
  • Returns first account address or null
  • Returns null if no provider or error

Returns: Promise that resolves to wallet address (hex string) or null

When to Use:

  • When wallet address needs to be retrieved
  • After wallet connection
  • On account change events

Example:

const address = await WalletManager.getWalletAddress();
// Returns: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" or null

WalletManager.updateWalletInfo()

Purpose: Update all wallet information (comprehensive).

Logic:

  1. Detects wallet type
  2. Checks if wallet is connected
  3. Gets wallet address if connected
  4. Gets chain name if connected
  5. Determines if user is Web3 user (has wallet installed)
  6. Stores all fields in sessionData (flattened)

Stores on sessionData:

  • wallet_address - Connected wallet address (or null)
  • wallet_type - Wallet name (e.g., "MetaMask")
  • chain_name - Chain name (e.g., "Ethereum Mainnet")
  • is_web3_user - Boolean (true if wallet installed, even if not connected)
  • wallet_connected - Boolean (true only if wallet is connected)

Returns: Promise that resolves to wallet info object or null on error

When to Use:

  • On SDK initialization
  • After wallet connection
  • On account change events
  • On chain change events
  • Before sending data to backend

Example:

const walletInfo = await WalletManager.updateWalletInfo();
// Returns: {
//   wallet_address: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
//   wallet_type: "MetaMask",
//   chain_name: "Ethereum Mainnet",
//   is_web3_user: true,
//   wallet_connected: true
// }

WalletManager.setupEventListeners()

Purpose: Setup wallet event listeners for real-time tracking.

Logic:

  • Listens for accountsChanged event (wallet connect/disconnect, account switch)
  • Listens for chainChanged event (network switch)
  • Updates wallet info automatically on events
  • Handles wallet disconnection gracefully

When to Use:

  • On SDK initialization
  • Should be called once to setup listeners

Example:

WalletManager.setupEventListeners();
// Now automatically tracks wallet changes

WalletManager.initialize()

Purpose: Initialize wallet detection and tracking.

Logic:

  1. Calls updateWalletInfo() (non-blocking, initial detection)
  2. Calls setupEventListeners() (setup real-time tracking)

When to Use:

  • On SDK initialization
  • Should be called early but doesn't block other operations

Example:

await WalletManager.initialize();
// Detects wallet, sets up listeners, tracks changes

Wallet Detection Flow

1. SDK Initialization
   ↓
2. WalletManager.initialize()
   ↓
3. Check EIP-6963 providers (modern standard)
   ↓
4. Check window.ethereum wallet patterns (30+ wallets)
   ↓
5. Check global wallet objects
   ↓
6. Detect wallet type
   ↓
7. Check connection status (non-intrusive)
   ↓
8. Get wallet address (if connected)
   ↓
9. Get chain ID and convert to chain name
   ↓
10. Store all fields in sessionData
   ↓
11. Setup event listeners (accountsChanged, chainChanged)
   ↓
12. Real-time tracking active

Key Features

Non-Intrusive Detection: Never prompts user for wallet connection ✅ EIP-6963 Support: Modern multi-wallet detection standard ✅ 30+ Wallet Support: Comprehensive EVM wallet detection ✅ 40+ Chain Support: Recognizes all major EVM chains ✅ Real-Time Tracking: Automatically updates on wallet events ✅ Error Handling: Graceful fallbacks on errors ✅ Flattened Storage: All fields stored directly on sessionData


Reusability

WalletManager.detectWalletType(): Reusable for detecting wallet anywhere.

WalletManager.getChainNameFromId(): Reusable for chain ID to name conversion.

WalletManager.updateWalletInfo(): Reusable for updating wallet state.

WalletManager.initialize(): Reusable for initialization.


Section 10: Interaction Management

Purpose

Single source of truth for interaction tracking and management. Handles all user interactions (clicks, forms, scroll, hover, etc.) with automatic timestamping, categorization, and chronological sorting.

Why It Matters

  • Centralized interaction tracking
  • Consistent data structure
  • Single place to fix interaction bugs
  • Prevents repeating interactions
  • Automatic timestamping and categorization
  • Immediate session storage updates

InteractionManager Object

Interaction Categories (19 Categories)

User Actions:

  • clicks - Click events
  • hoverEvents - Hover/mouseover events
  • touchEvents - Touch events (mobile)
  • keyboardEvents - Keyboard input events
  • copyPasteEvents - Copy/paste events
  • contextMenuEvents - Right-click context menu events
  • dragDropEvents - Drag and drop events

Form Interactions:

  • formInteractions - General form interactions
  • formSubmissions - Form submission events
  • fieldChanges - Form field value changes
  • validationErrors - Form validation errors
  • formAnalytics - Form analytics data

Media & Content:

  • mediaInteractions - Video/audio player interactions

Navigation & Scrolling:

  • scrollEvents - Scroll events
  • focusEvents - Focus/blur events
  • windowEvents - Window resize, visibility, etc.

Performance & Errors:

  • performanceEvents - Performance metrics
  • errorEvents - JavaScript errors
  • networkEvents - Network request events

InteractionManager.initialize()

Purpose: Initialize interactions structure.

Logic:

  • Creates empty interactions object with all 19 categories
  • Sets totalInteractions to 0
  • Initializes all category arrays as empty arrays

When to Use:

  • On SDK initialization
  • When interactions need to be reset
  • Before adding interactions

Example:

InteractionManager.initialize();
// Creates empty interactions structure

InteractionManager.add(category, interactionData)

Purpose: Add interaction to appropriate category.

Logic:

  1. Ensures interactions are initialized
  2. Validates category
  3. Adds timestamp if not provided
  4. Adds to specific category array
  5. Updates total interactions count
  6. Updates session activity
  7. Immediately updates session storage

Parameters:

  • category - Interaction category (e.g., 'clicks', 'formInteractions')
  • interactionData - Interaction data object (can include any fields)

Interaction Data Structure:

{
  timestamp: "2024-12-15T10:30:00.000Z",  // Auto-added if not provided
  elementId: "button-123",                 // Optional: Element identifier
  target: "button",                         // Optional: Target element
  selector: "#submit-btn",                 // Optional: CSS selector
  // ... any other fields specific to interaction type
}

When to Use:

  • When tracking any user interaction
  • Called by event trackers (Section 14)
  • For custom interaction tracking

Example:

// Track a click
InteractionManager.add('clicks', {
  elementId: 'submit-button',
  target: 'button',
  selector: '#submit-btn',
  x: 100,
  y: 200
});

// Track a form submission
InteractionManager.add('formSubmissions', {
  formId: 'contact-form',
  fields: ['name', 'email', 'message'],
  success: true
});

InteractionManager.updateTotalCount()

Purpose: Update total interactions count.

Logic:

  • Calculates total from all category arrays
  • Updates sessionData.interactions.totalInteractions

When to Use:

  • Automatically called by add()
  • After manual interaction modifications
  • After deduplication

InteractionManager.updateSessionStorage()

Purpose: Update session storage with interactions.

Logic:

  • Loads current session from storage
  • Updates interactions in session data
  • Saves session back to storage

When to Use:

  • Automatically called by add()
  • After bulk interaction updates

InteractionManager.getChronological()

Purpose: Get all interactions sorted chronologically.

Logic:

  1. Collects all interactions from all categories
  2. Adds category identifiers (category, originalCategory)
  3. Sorts by timestamp (oldest first)

Returns: Array of interactions sorted by timestamp

Interaction Structure in Chronological Array:

[
  {
    timestamp: "2024-12-15T10:30:00.000Z",
    category: "click",                    // Normalized category name
    originalCategory: "clicks",           // Original category array name
    elementId: "button-123",
    // ... other interaction data
  },
  {
    timestamp: "2024-12-15T10:30:05.000Z",
    category: "scrollEvent",
    originalCategory: "scrollEvents",
    scrollY: 100,
    // ... other interaction data
  }
]

When to Use:

  • Before sending data to backend
  • For analytics/reporting
  • For chronological interaction analysis

Example:

const chronological = InteractionManager.getChronological();
// Returns: Array of all interactions sorted by time

InteractionManager.getChronologicalForBackend()

Purpose: Get chronological interactions for backend.

Logic:

  • Gets chronological array via getChronological()
  • Returns both chronological and categorized structures

Returns: Object with:

{
  chronological: [...],      // Sorted array
  categorized: {...}         // Original categorized structure
}

When to Use:

  • When sending data to backend
  • When both formats are needed

Example:

const interactions = InteractionManager.getChronologicalForBackend();
// Returns: { chronological: [...], categorized: {...} }

InteractionManager.deduplicate()

Purpose: Remove duplicate interactions.

Logic:

  • Compares interactions by timestamp, category, and element identifier
  • Keeps first occurrence of duplicates
  • Updates total count after deduplication
  • Updates session storage

Deduplication Key: ${timestamp}_${category}_${elementId}

When to Use:

  • Before sending data to backend
  • After bulk interaction imports
  • To clean up duplicate events

Example:

InteractionManager.deduplicate();
// Removes duplicate interactions

InteractionManager.getByCategory(category)

Purpose: Get interactions by category.

Parameters: category - Interaction category name

Returns: Array of interactions for that category

When to Use:

  • When analyzing specific interaction types
  • For category-specific analytics

Example:

const clicks = InteractionManager.getByCategory('clicks');
// Returns: Array of all click interactions

InteractionManager.getTotalCount()

Purpose: Get total interactions count.

Returns: Total number of interactions across all categories

When to Use:

  • For analytics/reporting
  • To check interaction volume

Example:

const total = InteractionManager.getTotalCount();
// Returns: 42 (total interactions)

InteractionManager.clear()

Purpose: Clear all interactions.

Logic:

  • Resets all category arrays to empty
  • Sets total count to 0
  • Updates session storage

When to Use:

  • For testing/reset
  • When starting fresh session

Example:

InteractionManager.clear();
// Clears all interactions

Reusability

InteractionManager.add(): Reusable for tracking any interaction type.

InteractionManager.getChronological(): Reusable for chronological analysis.

InteractionManager.deduplicate(): Reusable for cleaning duplicate interactions.

InteractionManager.getByCategory(): Reusable for category-specific queries.


Section 11: Page Visit Management

Purpose

Track and manage page visits with durations. Handles entry/exit pages, mount/unmount times, and page duration calculations.

Why It Matters

  • Centralized page visit tracking
  • Accurate page duration calculations
  • Proper entry/exit page tracking
  • Single place to fix page visit bugs
  • Prevents duplicate page visits

PageVisitManager Object

PageVisitManager.track(path, timestamp)

Purpose: Track a page visit.

Logic:

  1. Ensures session ID exists
  2. Gets current path and URL
  3. Checks if page already visited (prevents duplicates)
  4. Creates page visit entry with mount time
  5. Updates previous page's unmount time and duration
  6. Sets entry page if first page
  7. Updates pagesViewed count
  8. Calculates session duration
  9. Updates session activity
  10. Saves to storage

Parameters:

  • path (optional) - Page path. Defaults to window.location.pathname + search
  • timestamp (optional) - Page visit timestamp. Defaults to current time

Stores in visited_pages array:

{
  path: "/home",
  timestamp: "2024-12-15T10:30:00.000Z",
  duration: 180,
  isEntry: true,
  isExit: false,
  mountTime: 1702641000000,  // Milliseconds
  unmountTime: 1702641180000  // Milliseconds
}

When to Use:

  • On page load
  • On SPA navigation
  • When tracking page visits

Example:

PageVisitManager.track();
// Tracks current page visit

PageVisitManager.trackUnmount()

Purpose: Track page unmount (when user navigates away).

Logic:

  • Finds current page in page visits
  • Sets unmount time
  • Calculates page duration using DurationManager
  • Recalculates session duration
  • Saves to storage

When to Use:

  • Automatically called on beforeunload event
  • Automatically called on visibilitychange event (SPA navigation)
  • Can be called manually if needed

Example:

PageVisitManager.trackUnmount();
// Tracks current page unmount

PageVisitManager.getAll()

Purpose: Get all page visits.

Returns: Array of all page visits

When to Use:

  • For analytics/reporting
  • To check page visit history

Example:

const visits = PageVisitManager.getAll();
// Returns: Array of all page visits

PageVisitManager.getEntryPage()

Purpose: Get entry page (first page visited).

Returns: First page visit object or null

When to Use:

  • To get entry page information
  • For analytics

Example:

const entryPage = PageVisitManager.getEntryPage();
// Returns: First page visit or null

PageVisitManager.getExitPage()

Purpose: Get exit page (last page visited).

Returns: Last page visit object or null

When to Use:

  • To get exit page information
  • For analytics

Example:

const exitPage = PageVisitManager.getExitPage();
// Returns: Last page visit or null

`PageVisitManager.init