Cryptique Analytics SDK - Comprehensive web analytics and user tracking for modern web applications
npm install cryptique-sdkjavascript
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)
`
$3
- 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
$3
✅ Highly Reusable: Import or reference CONFIG anywhere you need configuration values.
---
Section 2: Storage Management
$3
Single source of truth for ALL storage operations (localStorage, sessionStorage). Prevents conflicts and ensures consistency.
$3
- Prevents storage conflicts (multiple functions writing different formats)
- Centralized error handling
- Easy to debug storage issues
- Can add features (encryption, compression) in one place
$3
#### 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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
StorageManager.updateSessionActivity();
// Updates lastActivity and saves to storage
`
---
$3
✅ Highly Reusable: All storage operations should go through StorageManager. Never access localStorage or sessionStorage directly.
---
Section 3: Session ID Management
$3
Single source of truth for session ID creation, retrieval, and management. Prevents duplicate sessions and ensures session continuity.
$3
- 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
$3
#### 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:
`javascript
// 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:
`javascript
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:
`javascript
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:
`javascript
// 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:
`javascript
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:
`javascript
SessionIdManager.initialize();
// sessionData.sessionId is now set
`
---
$3
#### 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:
`javascript
initSessionOnce();
// Session is now initialized, safe to start tracking
`
---
$3
#### 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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
const timestamp = nowIso();
// "2024-01-15T10:30:00.000Z"
`
---
#### getUTMParameters()
Purpose: Extract UTM parameters from URL.
Returns: Object with UTM parameters:
`javascript
{
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:
`javascript
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:
`javascript
const referrer = getProperReferrer();
// Returns UTM source if available, otherwise stored/browser referrer
`
---
$3
✅ 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
$3
Minimal structure for Session ID Management:
`javascript
{
sessionId: null, // Set by SessionIdManager
siteId: string,
userId: null, // Set from StorageManager
referrer: "direct",
utmData: {},
startTime: null,
lastActivity: null,
isFirstPage: true
}
`
$3
Synced with sessionData:
`javascript
{
sessionId: null, // Synced with sessionData.sessionId
userId: null // Synced with sessionData.userId
}
`
---
Best Practices
$3
- 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
$3
- 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
$3
`javascript
const sessionId = SessionIdManager.getOrCreate();
`
$3
`javascript
const session = StorageManager.loadSession();
`
$3
`javascript
StorageManager.saveSession(session);
`
$3
`javascript
const userId = StorageManager.getUserId();
`
$3
`javascript
if (StorageManager.getConsent()) {
// User consented
}
`
$3
`javascript
if (StorageManager.acquireLock('operation_name')) {
// Do operation
StorageManager.releaseLock('operation_name');
}
`
---
Section 4: Session Duration Calculation
$3
Single source of truth for session duration calculations. Ensures accurate session duration tracking and consistency between page durations and session duration.
$3
- Prevents inaccurate session times
- Ensures consistency between page and session data
- Proper bounce detection
- Single place to fix duration bugs
$3
#### 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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
// 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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
const validDuration = DurationManager.ensureValid(page.duration);
// Always returns valid number (minimum 1)
`
---
$3
#### 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
$3
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
$3
✅ 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
$3
Single source of truth for location/geolocation data fetching and storage. Handles IP-based geolocation via ipinfo.io API.
$3
- Centralized location data management
- Caching prevents unnecessary API calls
- Fallback ensures we always have location data
- Single place to fix location bugs
$3
#### 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:
`javascript
{
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
---
$3
✅ LocationManager.fetchLocationData(): Reusable for fetching location anywhere.
✅ LocationManager.storeLocationData(): Reusable for storing location data.
---
Section 6: Session Data Structure & Normalization
$3
Complete sessionData structure initialization and data normalization for backend compatibility. Handles field mapping (camelCase → snake_case) and ensures all required fields are present.
$3
- 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
$3
`javascript
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: []
};
`
$3
#### 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:
`javascript
SessionDataManager.initialize();
// Ensures all fields are present and properly initialized
`
---
#### SessionDataManager.toSnakeCase(camelCase)
Purpose: Convert camelCase field name to snake_case.
Examples:
- sessionId → session_id
- pagesViewed → pages_viewed
- isBounce → 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 → sessionId
- pages_viewed → pagesViewed
- is_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_id
- siteId → site_id
- teamId → team_id
- userId → user_id
- startTime → start_time
- endTime → end_time
- lastActivity → last_activity
- pagesViewed → pages_viewed
- isBounce → is_bounce
- previousSessionId → previous_session_id
- timeSinceLastSession → time_since_last_session
- entryPage → entry_page
- exitPage → exit_page
Parameters: internalName - Internal field name (camelCase)
Returns: Backend field name (snake_case)
---
$3
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
$3
Extract and store browser/device information as flattened fields. Centralized browser and device detection for consistent data collection.
$3
- Centralized browser/device detection
- Consistent field structure
- Single place to fix detection bugs
- Handles modern User-Agent Client Hints API with fallbacks
$3
#### 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):
`javascript
{
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:
`javascript
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:
`javascript
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:
`javascript
BrowserDeviceManager.initialize();
// Detects and stores browser/device info in sessionData
`
---
$3
✅ 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
$3
Extract and store UTM parameters as flattened fields. Centralized UTM parameter extraction from URL query string.
$3
- Centralized UTM parameter extraction
- Consistent field structure
- Single place to fix UTM bugs
- Handles missing parameters gracefully
$3
#### 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):
`javascript
{
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:
`javascript
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:
`javascript
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:
`javascript
UTMManager.initialize();
// Extracts and stores UTM parameters in sessionData
`
---
$3
✅ 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
$3
Detect and store wallet/Web3 information as flattened fields. Comprehensive support for EVM wallets and chains with real-time tracking.
$3
- 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
$3
#### 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: 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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
await WalletManager.initialize();
// Detects wallet, sets up listeners, tracks changes
`
---
$3
`
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
`
---
$3
✅ 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
---
$3
✅ 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
$3
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.
$3
- Centralized interaction tracking
- Consistent data structure
- Single place to fix interaction bugs
- Prevents repeating interactions
- Automatic timestamping and categorization
- Immediate session storage updates
$3
#### 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:
`javascript
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:
`javascript
{
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:
`javascript
// 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:
`javascript
[
{
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:
`javascript
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:
`javascript
{
chronological: [...], // Sorted array
categorized: {...} // Original categorized structure
}
`
When to Use:
- When sending data to backend
- When both formats are needed
Example:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
InteractionManager.clear();
// Clears all interactions
`
---
$3
✅ 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
$3
Track and manage page visits with durations. Handles entry/exit pages, mount/unmount times, and page duration calculations.
$3
- Centralized page visit tracking
- Accurate page duration calculations
- Proper entry/exit page tracking
- Single place to fix page visit bugs
- Prevents duplicate page visits
$3
#### 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:
`javascript
{
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
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:
`javascript
const exitPage = PageVisitManager.getExitPage();
// Returns: Last page visit or null
`
---
#### PageVisitManager.init