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
- Architecture Overview
- Section 1: Configuration
- Section 2: Storage Management
- Section 3: Session ID Management
- 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
CONFIGobject 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:
- Try sessionStorage first (primary source)
- If found, update localStorage backup
- If not found, try localStorage backup
- If backup is recent (< 5 min), restore it
- 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 userStorageManager.getConsent() / StorageManager.setConsent(consent)
Purpose: Get/set user consent status for GDPR/privacy compliance.
Logic:
- Stores consent flag in localStorage
- Returns
trueif consent given,falseotherwise - Defaults to
falseif 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.referrerif not stored - Falls back to
"direct"if no referrer
Why Store:
document.referrercan 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 useStorageManager.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 storageReusability
✅ 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:
lastActivityexists AND is less than 5 minutes ago- OR
lastActivitydoesn'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.lastActivityto 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 aliveSessionIdManager.getOrCreate()
Purpose: Get existing session ID or create new one - THE SINGLE SOURCE OF TRUTH.
Logic Flow:
- Ensure we have user ID first
- Try to load existing session (with retries for fast navigation)
- If found, check if it's still valid (< 5 min inactivity)
- If valid, reuse it and update activity
- If expired or not found, do final checks (with delays)
- If still not found, create new session
- 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.sessionIdanduserSession.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 setInitialization 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 trackingUtility Functions (Used by Session ID Management)
retryWithDelay(fn, maxAttempts, delayMs)
Purpose: Retry a function with delays between attempts.
Logic:
- Calls function up to
maxAttemptstimes - Waits
delayMsmilliseconds between attempts - Returns first successful result
- Returns
nullif 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 arrayensureObject(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 objectnowIso()
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 referrerReusability
✅ 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
StorageManagerfor all storage operations - Use
SessionIdManager.getOrCreate()to get/create session IDs - Use
CONFIGfor all configuration values - Use
initSessionOnce()before starting tracking - Check
SessionIdManager.isSessionValid()before reusing sessions
❌ DON'T:
- Don't access
localStorageorsessionStoragedirectly - 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 secondsDurationManager.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 secondsDurationManager.calculateFromPages(pageVisits)
Purpose: Calculate session duration from page mount/unmount times (PREFERRED METHOD).
Logic:
- Get first page
mountTime(session start) - Get last page
unmountTimeor current time (session end) - 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:
- Try to calculate from page mount/unmount times (preferred)
- If that fails, calculate from session start time (fallback)
- Update
sessionData.duration - 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 tosessionData.pageVisitsstartTime: Optional, defaults tosessionData.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 functionDurationManager.calculateAllPageDurations(pageVisits, currentTime)
Purpose: Calculate duration for all pages in the session.
Logic for each page:
- If has
mountTimeandunmountTime: use mount→unmount - If has
mountTimebut nounmountTime: use mount→current - If no
mountTime: use timestamp fallback - 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 objectscurrentTime: Optional, defaults toDate.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:
- Sum all page durations
- Compare sum to session duration
- If different, adjust last page duration by the difference
- 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 objectssessionDuration: 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 secondspagesViewed: 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:
mountTimeandunmountTimeof pages - Accuracy: Most accurate
- Logic:
lastPage.unmountTime - firstPage.mountTime - When: If page visits have
mountTimedata
Method B: From Session Start Time (Fallback)
- Uses:
sessionData.startTimeand current time - Accuracy: Less accurate (includes navigation time)
- Logic:
currentTime - startTime - When: If no page mount/unmount times available
Key Principles
- Single Source of Truth: Only
updateSessionDuration()modifiessessionData.duration - Preferred Method: Use page mount/unmount times when available
- Fallback Logic: Always have a fallback calculation method
- Consistency: Page durations must sum to session duration
- Validation: Always validate durations before use
- 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:
- Try primary URL with timeout
- If fails, try backup URL
- If both fail, return default values
- 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.locationDataobject (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:
- Check runtimeState.countryName cache
- Check sessionData.country
- 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:
- Check sessionData.locationData
- If not available or invalid, fetch
- 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 initializedSessionDataManager.toSnakeCase(camelCase)
Purpose: Convert camelCase field name to snake_case.
Examples:
sessionId→session_idpagesViewed→pages_viewedisBounce→is_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_id→sessionIdpages_viewed→pagesViewedis_bounce→isBounce
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:
sessionId→session_idsiteId→site_idteamId→team_iduserId→user_idstartTime→start_timeendTime→end_timelastActivity→last_activitypagesViewed→pages_viewedisBounce→is_bouncepreviousSessionId→previous_session_idtimeSinceLastSession→time_since_last_sessionentryPage→entry_pageexitPage→exit_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) andsession_idsiteId→ Backend:site_iduserId→ Backend:user_idstartTime→ Backend:start_timepagesViewed→ Backend:pages_viewedisBounce→ Backend:is_bouncelastActivity→ Backend:last_activitypreviousSessionId→ Backend:previous_session_idtimeSinceLastSession→ Backend:time_since_last_sessionentryPage→ Backend:entry_pageexitPage→ 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:
- Detects device type (desktop/mobile/tablet) from user agent
- Extracts browser name using modern User-Agent Client Hints API (if available)
- Falls back to parsing user agent string for browser name
- Extracts OS, resolution, user agent, and language
Device Type Detection:
- Mobile:
/Mobi|Android/iin user agent - Tablet:
/Tablet|iPad/iin 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 namebrowser_version- Browser versiondevice_type- Device type (desktop/mobile/tablet)os- Operating systemresolution- Screen resolution (e.g., "1920x1080")user_agent- Full user agent stringlanguage- 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 sessionDataReusability
✅ 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
URLSearchParamsto 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.utmDataobject (for backward compatibility) - If no data provided, calls
extract()first
Stores on sessionData:
utm_source- UTM source parameterutm_medium- UTM medium parameterutm_campaign- UTM campaign parameterutm_term- UTM term parameterutm_content- UTM content parameterutm_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 sessionDataReusability
✅ 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:
- EIP-6963 (Modern Standard): Detects wallets via
eip6963:announceProviderevent - window.ethereum Properties: Checks wallet-specific properties (e.g.,
isMetaMask) - 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:
- Checks EIP-6963 providers first (modern standard)
- Checks
window.ethereumwallet patterns (30+ wallets) - Checks global wallet objects (OKX, TokenPocket, etc.)
- Falls back to "Web3 Wallet" if
window.ethereumexists but no specific wallet detected - 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_NAMESmapping - 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:
- Checks
window.ethereum.chainIdproperty (modern providers) - Requests chain ID via
eth_chainIdmethod - Falls back to Web3.js if available
- 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 nullWalletManager.detectChainName()
Purpose: Detect chain name from provider.
Logic:
- Gets chain ID via
detectChainId() - Converts chain ID to chain name via
getChainNameFromId() - 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_accountsmethod (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 falseWalletManager.getWalletAddress()
Purpose: Get connected wallet address (non-intrusive).
Logic:
- Uses
eth_accountsmethod (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 nullWalletManager.updateWalletInfo()
Purpose: Update all wallet information (comprehensive).
Logic:
- Detects wallet type
- Checks if wallet is connected
- Gets wallet address if connected
- Gets chain name if connected
- Determines if user is Web3 user (has wallet installed)
- 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
accountsChangedevent (wallet connect/disconnect, account switch) - Listens for
chainChangedevent (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 changesWalletManager.initialize()
Purpose: Initialize wallet detection and tracking.
Logic:
- Calls
updateWalletInfo()(non-blocking, initial detection) - 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 changesWallet 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 activeKey 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 eventshoverEvents- Hover/mouseover eventstouchEvents- Touch events (mobile)keyboardEvents- Keyboard input eventscopyPasteEvents- Copy/paste eventscontextMenuEvents- Right-click context menu eventsdragDropEvents- Drag and drop events
Form Interactions:
formInteractions- General form interactionsformSubmissions- Form submission eventsfieldChanges- Form field value changesvalidationErrors- Form validation errorsformAnalytics- Form analytics data
Media & Content:
mediaInteractions- Video/audio player interactions
Navigation & Scrolling:
scrollEvents- Scroll eventsfocusEvents- Focus/blur eventswindowEvents- Window resize, visibility, etc.
Performance & Errors:
performanceEvents- Performance metricserrorEvents- JavaScript errorsnetworkEvents- Network request events
InteractionManager.initialize()
Purpose: Initialize interactions structure.
Logic:
- Creates empty interactions object with all 19 categories
- Sets
totalInteractionsto 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 structureInteractionManager.add(category, interactionData)
Purpose: Add interaction to appropriate category.
Logic:
- Ensures interactions are initialized
- Validates category
- Adds timestamp if not provided
- Adds to specific category array
- Updates total interactions count
- Updates session activity
- 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:
- Collects all interactions from all categories
- Adds category identifiers (
category,originalCategory) - 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 timeInteractionManager.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 interactionsInteractionManager.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 interactionsInteractionManager.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 interactionsReusability
✅ 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:
- Ensures session ID exists
- Gets current path and URL
- Checks if page already visited (prevents duplicates)
- Creates page visit entry with mount time
- Updates previous page's unmount time and duration
- Sets entry page if first page
- Updates pagesViewed count
- Calculates session duration
- Updates session activity
- Saves to storage
Parameters:
path(optional) - Page path. Defaults towindow.location.pathname + searchtimestamp(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 visitPageVisitManager.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
beforeunloadevent - Automatically called on
visibilitychangeevent (SPA navigation) - Can be called manually if needed
Example:
PageVisitManager.trackUnmount();
// Tracks current page unmountPageVisitManager.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 visitsPageVisitManager.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 nullPageVisitManager.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