cryptique-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3580 -0
- package/lib/cjs/index.js +6477 -0
- package/lib/esm/index.js +6475 -0
- package/lib/types/index.d.ts +163 -0
- package/lib/umd/index.js +6483 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,3580 @@
|
|
|
1
|
+
# Cryptique Analytics SDK - Function Reference Guide
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Architecture Overview](#architecture-overview)
|
|
10
|
+
2. [Section 1: Configuration](#section-1-configuration)
|
|
11
|
+
3. [Section 2: Storage Management](#section-2-storage-management)
|
|
12
|
+
4. [Section 3: Session ID Management](#section-3-session-id-management)
|
|
13
|
+
5. [Utility Functions](#utility-functions)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Architecture Overview
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
21
|
+
### Key Principles:
|
|
22
|
+
- **Single Source of Truth**: Only one function handles each responsibility
|
|
23
|
+
- **Modular Design**: Each section is independent and can be understood separately
|
|
24
|
+
- **Clear Data Flow**: Functions have defined inputs/outputs
|
|
25
|
+
- **Error Handling**: Centralized error handling in each section
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Section 1: Configuration
|
|
30
|
+
|
|
31
|
+
### Purpose
|
|
32
|
+
Centralizes all constants, API URLs, storage keys, and configuration values in one place.
|
|
33
|
+
|
|
34
|
+
### Why It Matters
|
|
35
|
+
- Easy to update URLs/keys without searching through code
|
|
36
|
+
- Prevents typos and inconsistencies
|
|
37
|
+
- Makes it clear what can be configured
|
|
38
|
+
- Single place to change environment (dev/prod)
|
|
39
|
+
|
|
40
|
+
### Configuration Object: `CONFIG`
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
CONFIG.API.TRACK // Main session tracking endpoint
|
|
44
|
+
CONFIG.API.OVERVIEW // Overview/analytics data endpoint
|
|
45
|
+
CONFIG.API.CUSTOM_EVENTS // Custom event tracking endpoint
|
|
46
|
+
CONFIG.API.UTM_EVENTS // UTM campaign tracking endpoint
|
|
47
|
+
|
|
48
|
+
CONFIG.VERSION // SDK version string
|
|
49
|
+
|
|
50
|
+
CONFIG.STORAGE_KEYS.SESSION // Primary session storage key
|
|
51
|
+
CONFIG.STORAGE_KEYS.USER_ID // User ID storage key
|
|
52
|
+
CONFIG.STORAGE_KEYS.CONSENT // Consent flag storage key
|
|
53
|
+
|
|
54
|
+
CONFIG.SESSION.TIMEOUT_MS // Session timeout (30 minutes)
|
|
55
|
+
CONFIG.SESSION.BACKUP_VALIDITY_MS // Backup validity (5 minutes)
|
|
56
|
+
CONFIG.SESSION.BOUNCE_THRESHOLD_SECONDS // Bounce threshold (30 seconds)
|
|
57
|
+
|
|
58
|
+
CONFIG.INTERVALS.SESSION_TRACKING_MS // Session update interval (5 seconds)
|
|
59
|
+
CONFIG.INTERVALS.OVERVIEW_DEBOUNCE_MS // Overview debounce (10 seconds)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### When to Use
|
|
63
|
+
- **Accessing API URLs**: Use `CONFIG.API.*` instead of hardcoding URLs
|
|
64
|
+
- **Accessing Storage Keys**: Use `CONFIG.STORAGE_KEYS.*` instead of string literals
|
|
65
|
+
- **Session Configuration**: Use `CONFIG.SESSION.*` for timeout and validity checks
|
|
66
|
+
- **Changing Environment**: Update `CONFIG` object to switch between dev/prod
|
|
67
|
+
|
|
68
|
+
### Reusability
|
|
69
|
+
✅ **Highly Reusable**: Import or reference `CONFIG` anywhere you need configuration values.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Section 2: Storage Management
|
|
74
|
+
|
|
75
|
+
### Purpose
|
|
76
|
+
Single source of truth for ALL storage operations (localStorage, sessionStorage). Prevents conflicts and ensures consistency.
|
|
77
|
+
|
|
78
|
+
### Why It Matters
|
|
79
|
+
- Prevents storage conflicts (multiple functions writing different formats)
|
|
80
|
+
- Centralized error handling
|
|
81
|
+
- Easy to debug storage issues
|
|
82
|
+
- Can add features (encryption, compression) in one place
|
|
83
|
+
|
|
84
|
+
### StorageManager Object
|
|
85
|
+
|
|
86
|
+
#### `StorageManager.loadSession()`
|
|
87
|
+
**Purpose**: Load session from sessionStorage with localStorage backup fallback.
|
|
88
|
+
|
|
89
|
+
**Logic Flow**:
|
|
90
|
+
1. Try sessionStorage first (primary source)
|
|
91
|
+
2. If found, update localStorage backup
|
|
92
|
+
3. If not found, try localStorage backup
|
|
93
|
+
4. If backup is recent (< 5 min), restore it
|
|
94
|
+
5. Return session or null
|
|
95
|
+
|
|
96
|
+
**When to Use**:
|
|
97
|
+
- Loading existing session on page load
|
|
98
|
+
- Checking if session exists
|
|
99
|
+
- Recovering session after tab close
|
|
100
|
+
|
|
101
|
+
**Returns**: Session object with `{id, userId, lastActivity}` or `null`
|
|
102
|
+
|
|
103
|
+
**Example**:
|
|
104
|
+
```javascript
|
|
105
|
+
const session = StorageManager.loadSession();
|
|
106
|
+
if (session) {
|
|
107
|
+
console.log('Session found:', session.id);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
#### `StorageManager.saveSession(session)`
|
|
114
|
+
**Purpose**: Save session to both sessionStorage and localStorage backup.
|
|
115
|
+
|
|
116
|
+
**Logic**:
|
|
117
|
+
- Saves to sessionStorage (primary, fast, tab-specific)
|
|
118
|
+
- Also saves to localStorage (backup, survives tab close)
|
|
119
|
+
- Ensures we never lose session data
|
|
120
|
+
|
|
121
|
+
**When to Use**:
|
|
122
|
+
- After creating new session
|
|
123
|
+
- After updating session data
|
|
124
|
+
- After session activity updates
|
|
125
|
+
|
|
126
|
+
**Parameters**: `session` - Session object with `{id, userId, lastActivity, ...}`
|
|
127
|
+
|
|
128
|
+
**Example**:
|
|
129
|
+
```javascript
|
|
130
|
+
const session = {
|
|
131
|
+
id: 'session-id',
|
|
132
|
+
userId: 'user-id',
|
|
133
|
+
lastActivity: Date.now()
|
|
134
|
+
};
|
|
135
|
+
StorageManager.saveSession(session);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
#### `StorageManager.updateBackup(session)`
|
|
141
|
+
**Purpose**: Update localStorage backup (called automatically by `saveSession`).
|
|
142
|
+
|
|
143
|
+
**Logic**:
|
|
144
|
+
- Creates lightweight backup with essential data only
|
|
145
|
+
- Stores: sessionId, userId, timestamp
|
|
146
|
+
- Used for session recovery across tabs
|
|
147
|
+
|
|
148
|
+
**When to Use**:
|
|
149
|
+
- Usually called automatically by `saveSession`
|
|
150
|
+
- Can be called manually if you only want to update backup
|
|
151
|
+
|
|
152
|
+
**Parameters**: `session` - Session object
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
#### `StorageManager.getUserId()`
|
|
157
|
+
**Purpose**: Get or create persistent user ID.
|
|
158
|
+
|
|
159
|
+
**Logic**:
|
|
160
|
+
- Checks localStorage for existing user ID
|
|
161
|
+
- If not found, generates new ID: `"usr_" + random string`
|
|
162
|
+
- Stores in localStorage (survives browser restarts)
|
|
163
|
+
|
|
164
|
+
**When to Use**:
|
|
165
|
+
- Initializing user session
|
|
166
|
+
- Tracking user across sessions
|
|
167
|
+
- User-level analytics
|
|
168
|
+
|
|
169
|
+
**Returns**: User ID string (e.g., `"usr_abc123xyz"`)
|
|
170
|
+
|
|
171
|
+
**Example**:
|
|
172
|
+
```javascript
|
|
173
|
+
const userId = StorageManager.getUserId();
|
|
174
|
+
// Always returns same ID for same user
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
#### `StorageManager.getConsent()` / `StorageManager.setConsent(consent)`
|
|
180
|
+
**Purpose**: Get/set user consent status for GDPR/privacy compliance.
|
|
181
|
+
|
|
182
|
+
**Logic**:
|
|
183
|
+
- Stores consent flag in localStorage
|
|
184
|
+
- Returns `true` if consent given, `false` otherwise
|
|
185
|
+
- Defaults to `false` if storage fails
|
|
186
|
+
|
|
187
|
+
**When to Use**:
|
|
188
|
+
- Checking if tracking is allowed
|
|
189
|
+
- Storing user consent preference
|
|
190
|
+
- Privacy compliance
|
|
191
|
+
|
|
192
|
+
**Example**:
|
|
193
|
+
```javascript
|
|
194
|
+
if (StorageManager.getConsent()) {
|
|
195
|
+
// User has consented, proceed with tracking
|
|
196
|
+
} else {
|
|
197
|
+
// Show consent banner
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// User gives consent
|
|
201
|
+
StorageManager.setConsent(true);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
#### `StorageManager.getReferrer()` / `StorageManager.saveReferrer(referrer)`
|
|
207
|
+
**Purpose**: Get/set stored referrer (falls back to `document.referrer`).
|
|
208
|
+
|
|
209
|
+
**Logic**:
|
|
210
|
+
- Stores referrer in localStorage
|
|
211
|
+
- Falls back to `document.referrer` if not stored
|
|
212
|
+
- Falls back to `"direct"` if no referrer
|
|
213
|
+
|
|
214
|
+
**Why Store**:
|
|
215
|
+
- `document.referrer` can be lost on navigation
|
|
216
|
+
- We want to track original referrer for entire session
|
|
217
|
+
|
|
218
|
+
**When to Use**:
|
|
219
|
+
- Getting referrer for session tracking
|
|
220
|
+
- Storing referrer on first page visit
|
|
221
|
+
- Traffic source analysis
|
|
222
|
+
|
|
223
|
+
**Example**:
|
|
224
|
+
```javascript
|
|
225
|
+
const referrer = StorageManager.getReferrer();
|
|
226
|
+
// Returns stored referrer or document.referrer or "direct"
|
|
227
|
+
|
|
228
|
+
StorageManager.saveReferrer("google.com");
|
|
229
|
+
// Stores referrer for future use
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
#### `StorageManager.acquireLock(name, ttl)`
|
|
235
|
+
**Purpose**: Acquire a lock to prevent race conditions.
|
|
236
|
+
|
|
237
|
+
**Logic**:
|
|
238
|
+
- Stores timestamp in localStorage with key `"lock_{name}"`
|
|
239
|
+
- If lock exists and is recent (< TTL), return `false` (locked)
|
|
240
|
+
- Otherwise, set lock and return `true` (acquired)
|
|
241
|
+
- Locks expire automatically after TTL
|
|
242
|
+
|
|
243
|
+
**When to Use**:
|
|
244
|
+
- Preventing multiple functions from creating sessions simultaneously
|
|
245
|
+
- Preventing duplicate API calls
|
|
246
|
+
- Preventing concurrent initialization
|
|
247
|
+
|
|
248
|
+
**Parameters**:
|
|
249
|
+
- `name`: Lock name (e.g., `'session_creation'`)
|
|
250
|
+
- `ttl`: Time to live in milliseconds (default: 500ms)
|
|
251
|
+
|
|
252
|
+
**Returns**: `true` if lock acquired, `false` if already locked
|
|
253
|
+
|
|
254
|
+
**Example**:
|
|
255
|
+
```javascript
|
|
256
|
+
if (StorageManager.acquireLock('session_creation')) {
|
|
257
|
+
// Only one function can execute this block at a time
|
|
258
|
+
createSession();
|
|
259
|
+
StorageManager.releaseLock('session_creation'); // Optional
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
#### `StorageManager.releaseLock(name)`
|
|
266
|
+
**Purpose**: Release a lock (optional - locks expire automatically).
|
|
267
|
+
|
|
268
|
+
**When to Use**:
|
|
269
|
+
- Explicitly releasing lock before TTL expires
|
|
270
|
+
- Usually not needed (locks expire automatically)
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
#### `StorageManager.saveLastSession(sessionData)` / `StorageManager.getLastSession()`
|
|
275
|
+
**Purpose**: Save/get last session as backup for recovery on next page load.
|
|
276
|
+
|
|
277
|
+
**Logic**:
|
|
278
|
+
- Saves full session data to localStorage
|
|
279
|
+
- Used as last-resort backup if all other methods fail
|
|
280
|
+
- Can be retrieved on next page load
|
|
281
|
+
|
|
282
|
+
**When to Use**:
|
|
283
|
+
- Before page unload (as backup)
|
|
284
|
+
- On page load (to recover lost session)
|
|
285
|
+
- Error recovery scenarios
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
#### `StorageManager.clearAll()`
|
|
290
|
+
**Purpose**: Clear all SDK storage (useful for testing/reset).
|
|
291
|
+
|
|
292
|
+
**Logic**:
|
|
293
|
+
- Removes session, backup, and last session
|
|
294
|
+
- Does NOT clear USER_ID or CONSENT (persistent preferences)
|
|
295
|
+
|
|
296
|
+
**When to Use**:
|
|
297
|
+
- Testing/debugging
|
|
298
|
+
- User logout
|
|
299
|
+
- Privacy reset
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
#### `StorageManager.updateSessionActivity(session)`
|
|
304
|
+
**Purpose**: Update session activity timestamp.
|
|
305
|
+
|
|
306
|
+
**Logic**:
|
|
307
|
+
- Updates `sessionData.lastActivity = Date.now()`
|
|
308
|
+
- If session provided, updates that session object and saves it
|
|
309
|
+
- If no session provided, loads current session and updates it
|
|
310
|
+
- Saves updated session to storage
|
|
311
|
+
|
|
312
|
+
**Parameters**: `session` (optional) - Session object to update. If not provided, loads current session.
|
|
313
|
+
|
|
314
|
+
**Returns**: Current timestamp (Date.now())
|
|
315
|
+
|
|
316
|
+
**When to Use**:
|
|
317
|
+
- When tracking user activity (page visits, interactions, etc.)
|
|
318
|
+
- To keep session alive
|
|
319
|
+
- Called automatically by various managers
|
|
320
|
+
|
|
321
|
+
**Example**:
|
|
322
|
+
```javascript
|
|
323
|
+
StorageManager.updateSessionActivity();
|
|
324
|
+
// Updates lastActivity and saves to storage
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### Reusability
|
|
330
|
+
✅ **Highly Reusable**: All storage operations should go through `StorageManager`. Never access `localStorage` or `sessionStorage` directly.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Section 3: Session ID Management
|
|
335
|
+
|
|
336
|
+
### Purpose
|
|
337
|
+
Single source of truth for session ID creation, retrieval, and management. Prevents duplicate sessions and ensures session continuity.
|
|
338
|
+
|
|
339
|
+
### Why It Matters
|
|
340
|
+
- Prevents duplicate session IDs
|
|
341
|
+
- Ensures session continuity across page navigation
|
|
342
|
+
- Handles edge cases (fast navigation, multiple tabs, storage failures)
|
|
343
|
+
- Single place to fix session ID bugs
|
|
344
|
+
|
|
345
|
+
### SessionIdManager Object
|
|
346
|
+
|
|
347
|
+
#### `SessionIdManager.generate()`
|
|
348
|
+
**Purpose**: Generate a new UUID v4 session ID.
|
|
349
|
+
|
|
350
|
+
**Logic**:
|
|
351
|
+
- Creates UUID v4 format: `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`
|
|
352
|
+
- '4' indicates version 4 (random)
|
|
353
|
+
- 'y' is one of 8, 9, A, or B
|
|
354
|
+
- Guaranteed unique (collision-resistant)
|
|
355
|
+
|
|
356
|
+
**When to Use**:
|
|
357
|
+
- Creating new session (called internally by `getOrCreate()`)
|
|
358
|
+
- Should NOT be called directly - use `getOrCreate()` instead
|
|
359
|
+
|
|
360
|
+
**Returns**: UUID v4 string
|
|
361
|
+
|
|
362
|
+
**Example**:
|
|
363
|
+
```javascript
|
|
364
|
+
// Don't call directly - use getOrCreate() instead
|
|
365
|
+
const sessionId = SessionIdManager.getOrCreate();
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
#### `SessionIdManager.isSessionValid(session)`
|
|
371
|
+
**Purpose**: Check if a session is still valid (not expired).
|
|
372
|
+
|
|
373
|
+
**Logic**:
|
|
374
|
+
- Session is valid if:
|
|
375
|
+
- `lastActivity` exists AND is less than 5 minutes ago
|
|
376
|
+
- OR `lastActivity` doesn't exist (new session format)
|
|
377
|
+
- Uses `CONFIG.SESSION.TIMEOUT_MS` (30 minutes) for timeout check
|
|
378
|
+
|
|
379
|
+
**When to Use**:
|
|
380
|
+
- Before reusing existing session
|
|
381
|
+
- Checking if session should be expired
|
|
382
|
+
- Validating session before use
|
|
383
|
+
|
|
384
|
+
**Parameters**: `session` - Session object with `lastActivity` property
|
|
385
|
+
|
|
386
|
+
**Returns**: `true` if valid, `false` if expired
|
|
387
|
+
|
|
388
|
+
**Example**:
|
|
389
|
+
```javascript
|
|
390
|
+
const session = StorageManager.loadSession();
|
|
391
|
+
if (SessionIdManager.isSessionValid(session)) {
|
|
392
|
+
// Session is still active
|
|
393
|
+
} else {
|
|
394
|
+
// Session expired, create new one
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
#### `SessionIdManager.updateActivity(session)`
|
|
401
|
+
**Purpose**: Update session activity timestamp to keep session alive.
|
|
402
|
+
|
|
403
|
+
**Logic**:
|
|
404
|
+
- Sets `session.lastActivity` to current timestamp
|
|
405
|
+
- Saves session to storage
|
|
406
|
+
- Called when session is reused
|
|
407
|
+
|
|
408
|
+
**When to Use**:
|
|
409
|
+
- After reusing existing session
|
|
410
|
+
- After user activity
|
|
411
|
+
- To prevent session expiration
|
|
412
|
+
|
|
413
|
+
**Parameters**: `session` - Session object
|
|
414
|
+
|
|
415
|
+
**Returns**: Current timestamp (number)
|
|
416
|
+
|
|
417
|
+
**Example**:
|
|
418
|
+
```javascript
|
|
419
|
+
const session = StorageManager.loadSession();
|
|
420
|
+
SessionIdManager.updateActivity(session);
|
|
421
|
+
// Session activity updated, session stays alive
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
#### `SessionIdManager.getOrCreate()`
|
|
427
|
+
**Purpose**: Get existing session ID or create new one - THE SINGLE SOURCE OF TRUTH.
|
|
428
|
+
|
|
429
|
+
**Logic Flow**:
|
|
430
|
+
1. Ensure we have user ID first
|
|
431
|
+
2. Try to load existing session (with retries for fast navigation)
|
|
432
|
+
3. If found, check if it's still valid (< 5 min inactivity)
|
|
433
|
+
4. If valid, reuse it and update activity
|
|
434
|
+
5. If expired or not found, do final checks (with delays)
|
|
435
|
+
6. If still not found, create new session
|
|
436
|
+
7. Verify it was saved correctly
|
|
437
|
+
|
|
438
|
+
**Key Features**:
|
|
439
|
+
- Retry logic handles fast page navigation (up to 3 attempts)
|
|
440
|
+
- Final checks with delays (up to 5 attempts, 10ms between)
|
|
441
|
+
- Multiple fallbacks: sessionStorage → localStorage → generate new
|
|
442
|
+
- Verification ensures session was saved correctly
|
|
443
|
+
|
|
444
|
+
**When to Use**:
|
|
445
|
+
- **ONLY function that should create session IDs**
|
|
446
|
+
- On page load to get/create session
|
|
447
|
+
- When you need to ensure session exists
|
|
448
|
+
- Never call `generate()` directly - always use this
|
|
449
|
+
|
|
450
|
+
**Returns**: Session ID string (UUID v4)
|
|
451
|
+
|
|
452
|
+
**Example**:
|
|
453
|
+
```javascript
|
|
454
|
+
// This is the ONLY way to get/create session ID
|
|
455
|
+
const sessionId = SessionIdManager.getOrCreate();
|
|
456
|
+
sessionData.sessionId = sessionId;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
#### `SessionIdManager.syncFromStorage()`
|
|
462
|
+
**Purpose**: Sync session ID from storage if there's a mismatch.
|
|
463
|
+
|
|
464
|
+
**Logic**:
|
|
465
|
+
- Loads session from storage
|
|
466
|
+
- If storage has different ID than `sessionData.sessionId`, sync it
|
|
467
|
+
- Fixes inconsistencies between memory and storage
|
|
468
|
+
|
|
469
|
+
**When to Use**:
|
|
470
|
+
- When you suspect session ID mismatch
|
|
471
|
+
- After loading session from storage
|
|
472
|
+
- To fix inconsistencies
|
|
473
|
+
|
|
474
|
+
**Returns**: `true` if synced, `false` if no sync needed
|
|
475
|
+
|
|
476
|
+
**Example**:
|
|
477
|
+
```javascript
|
|
478
|
+
if (SessionIdManager.syncFromStorage()) {
|
|
479
|
+
console.log('Session ID synced from storage');
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
#### `SessionIdManager.initialize()`
|
|
486
|
+
**Purpose**: Initialize session data with session ID.
|
|
487
|
+
|
|
488
|
+
**Logic**:
|
|
489
|
+
- Calls `getOrCreate()` to get/create session ID
|
|
490
|
+
- Sets `sessionData.sessionId` and `userSession.sessionId`
|
|
491
|
+
- Ensures session ID is never null/undefined
|
|
492
|
+
- Has fallbacks if initialization fails
|
|
493
|
+
|
|
494
|
+
**When to Use**:
|
|
495
|
+
- On SDK initialization
|
|
496
|
+
- When you need to ensure session is initialized
|
|
497
|
+
- Usually called by `initSessionOnce()`
|
|
498
|
+
|
|
499
|
+
**Example**:
|
|
500
|
+
```javascript
|
|
501
|
+
SessionIdManager.initialize();
|
|
502
|
+
// sessionData.sessionId is now set
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
### Initialization Guard: `initSessionOnce()`
|
|
508
|
+
|
|
509
|
+
#### `initSessionOnce()`
|
|
510
|
+
**Purpose**: Initialize session once - prevents race conditions and duplicate initialization.
|
|
511
|
+
|
|
512
|
+
**Logic**:
|
|
513
|
+
- Uses lock mechanism to ensure only one initialization happens
|
|
514
|
+
- Checks if already initialized
|
|
515
|
+
- If locked, waits and retries
|
|
516
|
+
- Ensures session ID is set before returning
|
|
517
|
+
|
|
518
|
+
**When to Use**:
|
|
519
|
+
- On SDK startup
|
|
520
|
+
- Should be called before any tracking begins
|
|
521
|
+
- Prevents multiple initializations in multiple tabs
|
|
522
|
+
|
|
523
|
+
**Example**:
|
|
524
|
+
```javascript
|
|
525
|
+
initSessionOnce();
|
|
526
|
+
// Session is now initialized, safe to start tracking
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
### Utility Functions (Used by Session ID Management)
|
|
532
|
+
|
|
533
|
+
#### `retryWithDelay(fn, maxAttempts, delayMs)`
|
|
534
|
+
**Purpose**: Retry a function with delays between attempts.
|
|
535
|
+
|
|
536
|
+
**Logic**:
|
|
537
|
+
- Calls function up to `maxAttempts` times
|
|
538
|
+
- Waits `delayMs` milliseconds between attempts
|
|
539
|
+
- Returns first successful result
|
|
540
|
+
- Returns `null` if all attempts fail
|
|
541
|
+
|
|
542
|
+
**When to Use**:
|
|
543
|
+
- Handling timing issues during fast page navigation
|
|
544
|
+
- Retrying storage operations
|
|
545
|
+
- Any operation that might fail due to timing
|
|
546
|
+
|
|
547
|
+
**Parameters**:
|
|
548
|
+
- `fn`: Function to retry (should return truthy value on success)
|
|
549
|
+
- `maxAttempts`: Maximum number of attempts (default: 3)
|
|
550
|
+
- `delayMs`: Delay between attempts in milliseconds (default: 5)
|
|
551
|
+
|
|
552
|
+
**Returns**: Result from function or `null`
|
|
553
|
+
|
|
554
|
+
**Example**:
|
|
555
|
+
```javascript
|
|
556
|
+
const session = retryWithDelay(() => {
|
|
557
|
+
return StorageManager.loadSession();
|
|
558
|
+
}, 3, 5);
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
#### `ensureArray(value, defaultValue)`
|
|
564
|
+
**Purpose**: Ensure value is an array, return default if not.
|
|
565
|
+
|
|
566
|
+
**When to Use**:
|
|
567
|
+
- Normalizing data that might be array or not
|
|
568
|
+
- Ensuring arrays exist before operations
|
|
569
|
+
|
|
570
|
+
**Returns**: Array (original or default)
|
|
571
|
+
|
|
572
|
+
**Example**:
|
|
573
|
+
```javascript
|
|
574
|
+
const visits = ensureArray(sessionData.pageVisits, []);
|
|
575
|
+
// visits is guaranteed to be an array
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
#### `ensureObject(value, defaultValue)`
|
|
581
|
+
**Purpose**: Ensure value is an object, return default if not.
|
|
582
|
+
|
|
583
|
+
**When to Use**:
|
|
584
|
+
- Normalizing data that might be object or not
|
|
585
|
+
- Ensuring objects exist before operations
|
|
586
|
+
|
|
587
|
+
**Returns**: Object (original or default)
|
|
588
|
+
|
|
589
|
+
**Example**:
|
|
590
|
+
```javascript
|
|
591
|
+
const data = ensureObject(rawData, {});
|
|
592
|
+
// data is guaranteed to be an object
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
#### `nowIso()`
|
|
598
|
+
**Purpose**: Get ISO timestamp string.
|
|
599
|
+
|
|
600
|
+
**Returns**: ISO 8601 timestamp string (e.g., `"2024-01-15T10:30:00.000Z"`)
|
|
601
|
+
|
|
602
|
+
**When to Use**:
|
|
603
|
+
- Storing timestamps in ISO format
|
|
604
|
+
- Normalizing timestamps
|
|
605
|
+
- API payloads requiring ISO format
|
|
606
|
+
|
|
607
|
+
**Example**:
|
|
608
|
+
```javascript
|
|
609
|
+
const timestamp = nowIso();
|
|
610
|
+
// "2024-01-15T10:30:00.000Z"
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
#### `getUTMParameters()`
|
|
616
|
+
**Purpose**: Extract UTM parameters from URL.
|
|
617
|
+
|
|
618
|
+
**Returns**: Object with UTM parameters:
|
|
619
|
+
```javascript
|
|
620
|
+
{
|
|
621
|
+
source: 'google',
|
|
622
|
+
medium: 'cpc',
|
|
623
|
+
campaign: 'summer_sale',
|
|
624
|
+
term: 'keyword',
|
|
625
|
+
content: 'ad_variant',
|
|
626
|
+
utm_id: '12345'
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
**When to Use**:
|
|
631
|
+
- Tracking campaign sources
|
|
632
|
+
- Session initialization
|
|
633
|
+
- UTM event logging
|
|
634
|
+
|
|
635
|
+
**Example**:
|
|
636
|
+
```javascript
|
|
637
|
+
const utmData = getUTMParameters();
|
|
638
|
+
if (utmData.source) {
|
|
639
|
+
console.log('User came from:', utmData.source);
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
#### `getProperReferrer()`
|
|
646
|
+
**Purpose**: Get referrer, prioritizing UTM source over document.referrer.
|
|
647
|
+
|
|
648
|
+
**Logic**:
|
|
649
|
+
- If UTM source exists, return it
|
|
650
|
+
- Otherwise, return stored referrer or document.referrer or "direct"
|
|
651
|
+
|
|
652
|
+
**When to Use**:
|
|
653
|
+
- Session initialization
|
|
654
|
+
- Traffic source tracking
|
|
655
|
+
- When you need the best referrer value
|
|
656
|
+
|
|
657
|
+
**Returns**: Referrer string
|
|
658
|
+
|
|
659
|
+
**Example**:
|
|
660
|
+
```javascript
|
|
661
|
+
const referrer = getProperReferrer();
|
|
662
|
+
// Returns UTM source if available, otherwise stored/browser referrer
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
### Reusability
|
|
668
|
+
|
|
669
|
+
✅ **SessionIdManager.getOrCreate()**: **MUST USE** - This is the ONLY function that should create session IDs. Never call `generate()` directly.
|
|
670
|
+
|
|
671
|
+
✅ **SessionIdManager.isSessionValid()**: Reusable for checking session validity anywhere.
|
|
672
|
+
|
|
673
|
+
✅ **Utility Functions**: All utility functions are reusable across the SDK.
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Data Structures
|
|
678
|
+
|
|
679
|
+
### `sessionData`
|
|
680
|
+
Minimal structure for Session ID Management:
|
|
681
|
+
```javascript
|
|
682
|
+
{
|
|
683
|
+
sessionId: null, // Set by SessionIdManager
|
|
684
|
+
siteId: string,
|
|
685
|
+
userId: null, // Set from StorageManager
|
|
686
|
+
referrer: "direct",
|
|
687
|
+
utmData: {},
|
|
688
|
+
startTime: null,
|
|
689
|
+
lastActivity: null,
|
|
690
|
+
isFirstPage: true
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### `userSession`
|
|
695
|
+
Synced with sessionData:
|
|
696
|
+
```javascript
|
|
697
|
+
{
|
|
698
|
+
sessionId: null, // Synced with sessionData.sessionId
|
|
699
|
+
userId: null // Synced with sessionData.userId
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
## Best Practices
|
|
706
|
+
|
|
707
|
+
### ✅ DO:
|
|
708
|
+
- Use `StorageManager` for all storage operations
|
|
709
|
+
- Use `SessionIdManager.getOrCreate()` to get/create session IDs
|
|
710
|
+
- Use `CONFIG` for all configuration values
|
|
711
|
+
- Use `initSessionOnce()` before starting tracking
|
|
712
|
+
- Check `SessionIdManager.isSessionValid()` before reusing sessions
|
|
713
|
+
|
|
714
|
+
### ❌ DON'T:
|
|
715
|
+
- Don't access `localStorage` or `sessionStorage` directly
|
|
716
|
+
- Don't call `SessionIdManager.generate()` directly
|
|
717
|
+
- Don't create session IDs in multiple places
|
|
718
|
+
- Don't hardcode storage keys or API URLs
|
|
719
|
+
- Don't skip initialization guard
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## Quick Reference
|
|
724
|
+
|
|
725
|
+
### Getting Session ID
|
|
726
|
+
```javascript
|
|
727
|
+
const sessionId = SessionIdManager.getOrCreate();
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### Loading Session
|
|
731
|
+
```javascript
|
|
732
|
+
const session = StorageManager.loadSession();
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Saving Session
|
|
736
|
+
```javascript
|
|
737
|
+
StorageManager.saveSession(session);
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Getting User ID
|
|
741
|
+
```javascript
|
|
742
|
+
const userId = StorageManager.getUserId();
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Checking Consent
|
|
746
|
+
```javascript
|
|
747
|
+
if (StorageManager.getConsent()) {
|
|
748
|
+
// User consented
|
|
749
|
+
}
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
### Acquiring Lock
|
|
753
|
+
```javascript
|
|
754
|
+
if (StorageManager.acquireLock('operation_name')) {
|
|
755
|
+
// Do operation
|
|
756
|
+
StorageManager.releaseLock('operation_name');
|
|
757
|
+
}
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
---
|
|
761
|
+
|
|
762
|
+
## Section 4: Session Duration Calculation
|
|
763
|
+
|
|
764
|
+
### Purpose
|
|
765
|
+
Single source of truth for session duration calculations. Ensures accurate session duration tracking and consistency between page durations and session duration.
|
|
766
|
+
|
|
767
|
+
### Why It Matters
|
|
768
|
+
- Prevents inaccurate session times
|
|
769
|
+
- Ensures consistency between page and session data
|
|
770
|
+
- Proper bounce detection
|
|
771
|
+
- Single place to fix duration bugs
|
|
772
|
+
|
|
773
|
+
### DurationManager Object
|
|
774
|
+
|
|
775
|
+
#### `DurationManager.calculateSeconds(startTime, endTime)`
|
|
776
|
+
**Purpose**: Calculate duration in seconds between two timestamps (rounded).
|
|
777
|
+
|
|
778
|
+
**Logic**:
|
|
779
|
+
- Converts timestamps to Date objects
|
|
780
|
+
- Calculates difference in milliseconds
|
|
781
|
+
- Converts to seconds
|
|
782
|
+
- Rounds to nearest second
|
|
783
|
+
|
|
784
|
+
**When to Use**:
|
|
785
|
+
- General duration calculations where rounding is acceptable
|
|
786
|
+
- Session duration from start time
|
|
787
|
+
|
|
788
|
+
**Parameters**:
|
|
789
|
+
- `startTime`: Start timestamp (Date object or string/number)
|
|
790
|
+
- `endTime`: End timestamp (optional, defaults to current time)
|
|
791
|
+
|
|
792
|
+
**Returns**: Duration in seconds (rounded)
|
|
793
|
+
|
|
794
|
+
**Example**:
|
|
795
|
+
```javascript
|
|
796
|
+
const duration = DurationManager.calculateSeconds(sessionData.startTime);
|
|
797
|
+
// Returns rounded duration in seconds
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
#### `DurationManager.calculateSecondsFloor(startTime, endTime)`
|
|
803
|
+
**Purpose**: Calculate duration in seconds between two timestamps (floored).
|
|
804
|
+
|
|
805
|
+
**Logic**: Same as `calculateSeconds()` but uses `Math.floor()` instead of `Math.round()`
|
|
806
|
+
|
|
807
|
+
**When to Use**:
|
|
808
|
+
- Page durations (more conservative, prevents over-counting)
|
|
809
|
+
- When you need exact seconds without rounding up
|
|
810
|
+
|
|
811
|
+
**Returns**: Duration in seconds (floored)
|
|
812
|
+
|
|
813
|
+
**Example**:
|
|
814
|
+
```javascript
|
|
815
|
+
const pageDuration = DurationManager.calculateSecondsFloor(page.mountTime, page.unmountTime);
|
|
816
|
+
// Returns floored duration in seconds
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
#### `DurationManager.calculateFromPages(pageVisits)`
|
|
822
|
+
**Purpose**: Calculate session duration from page mount/unmount times (PREFERRED METHOD).
|
|
823
|
+
|
|
824
|
+
**Logic**:
|
|
825
|
+
1. Get first page `mountTime` (session start)
|
|
826
|
+
2. Get last page `unmountTime` or current time (session end)
|
|
827
|
+
3. Calculate: `endTime - startTime`
|
|
828
|
+
|
|
829
|
+
**When to Use**:
|
|
830
|
+
- When you have page visit data with mount/unmount times
|
|
831
|
+
- Most accurate calculation method
|
|
832
|
+
|
|
833
|
+
**Parameters**: `pageVisits` - Array of page visit objects with `mountTime` and `unmountTime`
|
|
834
|
+
|
|
835
|
+
**Returns**: Duration in seconds or `null` if can't calculate
|
|
836
|
+
|
|
837
|
+
**Example**:
|
|
838
|
+
```javascript
|
|
839
|
+
const duration = DurationManager.calculateFromPages(sessionData.pageVisits);
|
|
840
|
+
if (duration !== null) {
|
|
841
|
+
// Successfully calculated from page times
|
|
842
|
+
}
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
#### `DurationManager.updateSessionDuration(pageVisits, startTime)`
|
|
848
|
+
**Purpose**: Update `sessionData.duration` - THE SINGLE SOURCE OF TRUTH.
|
|
849
|
+
|
|
850
|
+
**Logic**:
|
|
851
|
+
1. Try to calculate from page mount/unmount times (preferred)
|
|
852
|
+
2. If that fails, calculate from session start time (fallback)
|
|
853
|
+
3. Update `sessionData.duration`
|
|
854
|
+
4. Return duration
|
|
855
|
+
|
|
856
|
+
**When to Use**:
|
|
857
|
+
- **ONLY function that should modify sessionData.duration**
|
|
858
|
+
- Whenever you need to update session duration
|
|
859
|
+
- Before sending data to backend
|
|
860
|
+
|
|
861
|
+
**Parameters**:
|
|
862
|
+
- `pageVisits`: Optional, defaults to `sessionData.pageVisits`
|
|
863
|
+
- `startTime`: Optional, defaults to `sessionData.startTime`
|
|
864
|
+
|
|
865
|
+
**Returns**: Duration in seconds
|
|
866
|
+
|
|
867
|
+
**Example**:
|
|
868
|
+
```javascript
|
|
869
|
+
// This is the ONLY way to update session duration
|
|
870
|
+
const duration = DurationManager.updateSessionDuration();
|
|
871
|
+
sessionData.duration = duration; // Already set by function
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
---
|
|
875
|
+
|
|
876
|
+
#### `DurationManager.calculateAllPageDurations(pageVisits, currentTime)`
|
|
877
|
+
**Purpose**: Calculate duration for all pages in the session.
|
|
878
|
+
|
|
879
|
+
**Logic for each page**:
|
|
880
|
+
1. If has `mountTime` and `unmountTime`: use mount→unmount
|
|
881
|
+
2. If has `mountTime` but no `unmountTime`: use mount→current
|
|
882
|
+
3. If no `mountTime`: use timestamp fallback
|
|
883
|
+
4. If no data: use minimum 1 second
|
|
884
|
+
|
|
885
|
+
**When to Use**:
|
|
886
|
+
- When you need to calculate/update all page durations
|
|
887
|
+
- Before aligning page durations with session duration
|
|
888
|
+
- On page navigation
|
|
889
|
+
|
|
890
|
+
**Parameters**:
|
|
891
|
+
- `pageVisits`: Array of page visit objects
|
|
892
|
+
- `currentTime`: Optional, defaults to `Date.now()`
|
|
893
|
+
|
|
894
|
+
**Returns**: Array of page visits with calculated durations
|
|
895
|
+
|
|
896
|
+
**Example**:
|
|
897
|
+
```javascript
|
|
898
|
+
const updatedPageVisits = DurationManager.calculateAllPageDurations(sessionData.pageVisits);
|
|
899
|
+
sessionData.pageVisits = updatedPageVisits;
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
---
|
|
903
|
+
|
|
904
|
+
#### `DurationManager.alignPageDurations(pageVisits, sessionDuration)`
|
|
905
|
+
**Purpose**: Ensure sum of page durations matches session duration exactly.
|
|
906
|
+
|
|
907
|
+
**Logic**:
|
|
908
|
+
1. Sum all page durations
|
|
909
|
+
2. Compare sum to session duration
|
|
910
|
+
3. If different, adjust last page duration by the difference
|
|
911
|
+
4. Ensure minimum 1 second
|
|
912
|
+
|
|
913
|
+
**When to Use**:
|
|
914
|
+
- Before sending data to backend (ensures consistency)
|
|
915
|
+
- After calculating session duration
|
|
916
|
+
- To fix discrepancies between page and session durations
|
|
917
|
+
|
|
918
|
+
**Parameters**:
|
|
919
|
+
- `pageVisits`: Array of page visit objects
|
|
920
|
+
- `sessionDuration`: Target session duration in seconds
|
|
921
|
+
|
|
922
|
+
**Returns**: Updated page visits array
|
|
923
|
+
|
|
924
|
+
**Example**:
|
|
925
|
+
```javascript
|
|
926
|
+
const alignedPageVisits = DurationManager.alignPageDurations(
|
|
927
|
+
sessionData.pageVisits,
|
|
928
|
+
sessionData.duration
|
|
929
|
+
);
|
|
930
|
+
sessionData.pageVisits = alignedPageVisits;
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
---
|
|
934
|
+
|
|
935
|
+
#### `DurationManager.calculateBounceStatus(duration, pagesViewed)`
|
|
936
|
+
**Purpose**: Determine if session is a bounce.
|
|
937
|
+
|
|
938
|
+
**Logic**:
|
|
939
|
+
- Bounce = duration < 30 seconds AND pagesViewed <= 1
|
|
940
|
+
- Uses `CONFIG.SESSION.BOUNCE_THRESHOLD_SECONDS` (30 seconds)
|
|
941
|
+
|
|
942
|
+
**When to Use**:
|
|
943
|
+
- When you need to check if session is a bounce
|
|
944
|
+
- Before sending data to backend
|
|
945
|
+
- For bounce rate calculations
|
|
946
|
+
|
|
947
|
+
**Parameters**:
|
|
948
|
+
- `duration`: Session duration in seconds
|
|
949
|
+
- `pagesViewed`: Number of pages viewed
|
|
950
|
+
|
|
951
|
+
**Returns**: `true` if bounce, `false` if not
|
|
952
|
+
|
|
953
|
+
**Example**:
|
|
954
|
+
```javascript
|
|
955
|
+
const isBounce = DurationManager.calculateBounceStatus(
|
|
956
|
+
sessionData.duration,
|
|
957
|
+
sessionData.pagesViewed
|
|
958
|
+
);
|
|
959
|
+
sessionData.isBounce = isBounce;
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
---
|
|
963
|
+
|
|
964
|
+
#### `DurationManager.ensureValid(duration)`
|
|
965
|
+
**Purpose**: Ensure duration is always a valid number.
|
|
966
|
+
|
|
967
|
+
**Logic**:
|
|
968
|
+
- Converts to number
|
|
969
|
+
- If null, undefined, NaN, or <= 0: return 1 (minimum)
|
|
970
|
+
- Otherwise: return floored value
|
|
971
|
+
|
|
972
|
+
**When to Use**:
|
|
973
|
+
- Before using duration values
|
|
974
|
+
- When validating duration data
|
|
975
|
+
- To prevent invalid durations
|
|
976
|
+
|
|
977
|
+
**Parameters**: `duration` - Duration value (any type)
|
|
978
|
+
|
|
979
|
+
**Returns**: Valid duration number (minimum 1 second)
|
|
980
|
+
|
|
981
|
+
**Example**:
|
|
982
|
+
```javascript
|
|
983
|
+
const validDuration = DurationManager.ensureValid(page.duration);
|
|
984
|
+
// Always returns valid number (minimum 1)
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
---
|
|
988
|
+
|
|
989
|
+
### Calculation Methods
|
|
990
|
+
|
|
991
|
+
#### Method A: From Page Mount/Unmount Times (Preferred)
|
|
992
|
+
- **Uses**: `mountTime` and `unmountTime` of pages
|
|
993
|
+
- **Accuracy**: Most accurate
|
|
994
|
+
- **Logic**: `lastPage.unmountTime - firstPage.mountTime`
|
|
995
|
+
- **When**: If page visits have `mountTime` data
|
|
996
|
+
|
|
997
|
+
#### Method B: From Session Start Time (Fallback)
|
|
998
|
+
- **Uses**: `sessionData.startTime` and current time
|
|
999
|
+
- **Accuracy**: Less accurate (includes navigation time)
|
|
1000
|
+
- **Logic**: `currentTime - startTime`
|
|
1001
|
+
- **When**: If no page mount/unmount times available
|
|
1002
|
+
|
|
1003
|
+
### Key Principles
|
|
1004
|
+
|
|
1005
|
+
1. **Single Source of Truth**: Only `updateSessionDuration()` modifies `sessionData.duration`
|
|
1006
|
+
2. **Preferred Method**: Use page mount/unmount times when available
|
|
1007
|
+
3. **Fallback Logic**: Always have a fallback calculation method
|
|
1008
|
+
4. **Consistency**: Page durations must sum to session duration
|
|
1009
|
+
5. **Validation**: Always validate durations before use
|
|
1010
|
+
6. **Minimum Values**: Never allow 0 or negative durations
|
|
1011
|
+
|
|
1012
|
+
### Reusability
|
|
1013
|
+
|
|
1014
|
+
✅ **DurationManager.updateSessionDuration()**: **MUST USE** - This is the ONLY function that should modify `sessionData.duration`. Never modify duration directly.
|
|
1015
|
+
|
|
1016
|
+
✅ **DurationManager.calculateBounceStatus()**: Reusable for bounce detection anywhere.
|
|
1017
|
+
|
|
1018
|
+
✅ **DurationManager.ensureValid()**: Reusable for validating any duration value.
|
|
1019
|
+
|
|
1020
|
+
---
|
|
1021
|
+
|
|
1022
|
+
## Section 5: Location/Geolocation Management
|
|
1023
|
+
|
|
1024
|
+
### Purpose
|
|
1025
|
+
Single source of truth for location/geolocation data fetching and storage. Handles IP-based geolocation via ipinfo.io API.
|
|
1026
|
+
|
|
1027
|
+
### Why It Matters
|
|
1028
|
+
- Centralized location data management
|
|
1029
|
+
- Caching prevents unnecessary API calls
|
|
1030
|
+
- Fallback ensures we always have location data
|
|
1031
|
+
- Single place to fix location bugs
|
|
1032
|
+
|
|
1033
|
+
### LocationManager Object
|
|
1034
|
+
|
|
1035
|
+
#### `LocationManager.fetchLocationData()`
|
|
1036
|
+
**Purpose**: Fetch location data from ipinfo.io API.
|
|
1037
|
+
|
|
1038
|
+
**Logic Flow**:
|
|
1039
|
+
1. Try primary URL with timeout
|
|
1040
|
+
2. If fails, try backup URL
|
|
1041
|
+
3. If both fail, return default values
|
|
1042
|
+
4. Parse and store location data
|
|
1043
|
+
|
|
1044
|
+
**When to Use**:
|
|
1045
|
+
- Initializing location data
|
|
1046
|
+
- Refreshing location data
|
|
1047
|
+
- When location data is missing
|
|
1048
|
+
|
|
1049
|
+
**Returns**: Promise that resolves with location data object
|
|
1050
|
+
|
|
1051
|
+
**Location Data Structure**:
|
|
1052
|
+
```javascript
|
|
1053
|
+
{
|
|
1054
|
+
country: "US",
|
|
1055
|
+
ip_address: "192.168.1.1",
|
|
1056
|
+
city: "New York",
|
|
1057
|
+
region: "NY",
|
|
1058
|
+
latitude: "40.7128",
|
|
1059
|
+
longitude: "-74.0060",
|
|
1060
|
+
timezone: "America/New_York",
|
|
1061
|
+
org: "AS12345 Example Org",
|
|
1062
|
+
postal: "10001"
|
|
1063
|
+
}
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
---
|
|
1067
|
+
|
|
1068
|
+
#### `LocationManager.storeLocationData(locationData)`
|
|
1069
|
+
**Purpose**: Store location data in sessionData (flattened fields).
|
|
1070
|
+
|
|
1071
|
+
**Logic**:
|
|
1072
|
+
- Stores all location fields directly on sessionData:
|
|
1073
|
+
- `ip_address`, `city`, `region`, `country`, `latitude`, `longitude`, `timezone`, `org`, `postal`
|
|
1074
|
+
- Also stores in `sessionData.locationData` object (for structured access)
|
|
1075
|
+
- Updates cached country name
|
|
1076
|
+
|
|
1077
|
+
**When to Use**:
|
|
1078
|
+
- After fetching location data
|
|
1079
|
+
- When location data is updated
|
|
1080
|
+
|
|
1081
|
+
**Parameters**: `locationData` - Location data object
|
|
1082
|
+
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
#### `LocationManager.getCountryName()`
|
|
1086
|
+
**Purpose**: Get country name (cached or fetch).
|
|
1087
|
+
|
|
1088
|
+
**Logic**:
|
|
1089
|
+
1. Check runtimeState.countryName cache
|
|
1090
|
+
2. Check sessionData.country
|
|
1091
|
+
3. If not available, fetch and cache
|
|
1092
|
+
|
|
1093
|
+
**Returns**: Promise that resolves with country name string
|
|
1094
|
+
|
|
1095
|
+
---
|
|
1096
|
+
|
|
1097
|
+
#### `LocationManager.getLocationData()`
|
|
1098
|
+
**Purpose**: Get full location data (cached or fetch).
|
|
1099
|
+
|
|
1100
|
+
**Logic**:
|
|
1101
|
+
1. Check sessionData.locationData
|
|
1102
|
+
2. If not available or invalid, fetch
|
|
1103
|
+
3. Return location data object
|
|
1104
|
+
|
|
1105
|
+
**Returns**: Promise that resolves with location data
|
|
1106
|
+
|
|
1107
|
+
---
|
|
1108
|
+
|
|
1109
|
+
#### `LocationManager.initialize()`
|
|
1110
|
+
**Purpose**: Initialize location data (non-blocking).
|
|
1111
|
+
|
|
1112
|
+
**When to Use**:
|
|
1113
|
+
- On SDK initialization
|
|
1114
|
+
- Should be called early but doesn't block other operations
|
|
1115
|
+
|
|
1116
|
+
---
|
|
1117
|
+
|
|
1118
|
+
### Reusability
|
|
1119
|
+
|
|
1120
|
+
✅ **LocationManager.fetchLocationData()**: Reusable for fetching location anywhere.
|
|
1121
|
+
|
|
1122
|
+
✅ **LocationManager.storeLocationData()**: Reusable for storing location data.
|
|
1123
|
+
|
|
1124
|
+
---
|
|
1125
|
+
|
|
1126
|
+
## Section 6: Session Data Structure & Normalization
|
|
1127
|
+
|
|
1128
|
+
### Purpose
|
|
1129
|
+
Complete sessionData structure initialization and data normalization for backend compatibility. Handles field mapping (camelCase → snake_case) and ensures all required fields are present.
|
|
1130
|
+
|
|
1131
|
+
### Why It Matters
|
|
1132
|
+
- Ensures all required fields are present (even if null)
|
|
1133
|
+
- Handles field mapping consistently (camelCase → snake_case)
|
|
1134
|
+
- Single place to manage session data structure
|
|
1135
|
+
- Prevents missing fields errors
|
|
1136
|
+
|
|
1137
|
+
### Complete SessionData Structure
|
|
1138
|
+
|
|
1139
|
+
```javascript
|
|
1140
|
+
let sessionData = {
|
|
1141
|
+
// IDs
|
|
1142
|
+
sessionId: null, // UUID v4 (internal camelCase)
|
|
1143
|
+
siteId: SITE_ID,
|
|
1144
|
+
teamId: null, // May be set externally or from backend
|
|
1145
|
+
userId: null,
|
|
1146
|
+
|
|
1147
|
+
// Time fields
|
|
1148
|
+
startTime: null, // ISO string (internal)
|
|
1149
|
+
endTime: null, // ISO string (internal)
|
|
1150
|
+
lastActivity: null, // Timestamp (internal)
|
|
1151
|
+
duration: 0, // Seconds
|
|
1152
|
+
|
|
1153
|
+
// Basic session data
|
|
1154
|
+
pagesViewed: 0, // Internal camelCase
|
|
1155
|
+
isBounce: true, // Internal camelCase
|
|
1156
|
+
previousSessionId: null,
|
|
1157
|
+
timeSinceLastSession: null,
|
|
1158
|
+
|
|
1159
|
+
// Entry/Exit pages
|
|
1160
|
+
entryPage: null,
|
|
1161
|
+
exitPage: null,
|
|
1162
|
+
referrer: "direct",
|
|
1163
|
+
|
|
1164
|
+
// Location fields (FLATTENED - stored directly on sessionData)
|
|
1165
|
+
ip_address: null, // snake_case for backend
|
|
1166
|
+
city: null,
|
|
1167
|
+
region: null,
|
|
1168
|
+
country: "",
|
|
1169
|
+
latitude: null,
|
|
1170
|
+
longitude: null,
|
|
1171
|
+
timezone: null,
|
|
1172
|
+
org: null,
|
|
1173
|
+
postal: null,
|
|
1174
|
+
|
|
1175
|
+
// Browser fields (FLATTENED)
|
|
1176
|
+
browser_name: null,
|
|
1177
|
+
browser_version: null,
|
|
1178
|
+
|
|
1179
|
+
// Device fields (FLATTENED)
|
|
1180
|
+
device_type: null,
|
|
1181
|
+
os: null,
|
|
1182
|
+
resolution: null,
|
|
1183
|
+
user_agent: null,
|
|
1184
|
+
language: null,
|
|
1185
|
+
|
|
1186
|
+
// Wallet fields (FLATTENED)
|
|
1187
|
+
wallet_address: null,
|
|
1188
|
+
wallet_type: null,
|
|
1189
|
+
chain_name: null,
|
|
1190
|
+
is_web3_user: false,
|
|
1191
|
+
wallet_connected: false,
|
|
1192
|
+
|
|
1193
|
+
// UTM fields (FLATTENED)
|
|
1194
|
+
utm_source: null,
|
|
1195
|
+
utm_medium: null,
|
|
1196
|
+
utm_campaign: null,
|
|
1197
|
+
utm_term: null,
|
|
1198
|
+
utm_content: null,
|
|
1199
|
+
utm_id: null,
|
|
1200
|
+
|
|
1201
|
+
// JSONB fields
|
|
1202
|
+
interactions: {},
|
|
1203
|
+
visited_pages: []
|
|
1204
|
+
};
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
### SessionDataManager Object
|
|
1208
|
+
|
|
1209
|
+
#### `SessionDataManager.initialize()`
|
|
1210
|
+
**Purpose**: Initialize complete sessionData structure.
|
|
1211
|
+
|
|
1212
|
+
**Logic**:
|
|
1213
|
+
- Ensures all required fields exist (some may already be set)
|
|
1214
|
+
- Sets default values for missing fields
|
|
1215
|
+
- Initializes time fields (startTime, endTime, lastActivity, duration)
|
|
1216
|
+
- Initializes basic session data (pagesViewed, isBounce, etc.)
|
|
1217
|
+
- Initializes all location, browser, device, wallet, and UTM fields
|
|
1218
|
+
- Initializes JSONB fields (interactions, visited_pages)
|
|
1219
|
+
|
|
1220
|
+
**When to Use**:
|
|
1221
|
+
- On SDK initialization
|
|
1222
|
+
- When sessionData structure needs to be validated
|
|
1223
|
+
- Before sending data to backend
|
|
1224
|
+
|
|
1225
|
+
**Example**:
|
|
1226
|
+
```javascript
|
|
1227
|
+
SessionDataManager.initialize();
|
|
1228
|
+
// Ensures all fields are present and properly initialized
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
---
|
|
1232
|
+
|
|
1233
|
+
#### `SessionDataManager.toSnakeCase(camelCase)`
|
|
1234
|
+
**Purpose**: Convert camelCase field name to snake_case.
|
|
1235
|
+
|
|
1236
|
+
**Examples**:
|
|
1237
|
+
- `sessionId` → `session_id`
|
|
1238
|
+
- `pagesViewed` → `pages_viewed`
|
|
1239
|
+
- `isBounce` → `is_bounce`
|
|
1240
|
+
|
|
1241
|
+
**Parameters**: `camelCase` - Field name in camelCase
|
|
1242
|
+
|
|
1243
|
+
**Returns**: Field name in snake_case
|
|
1244
|
+
|
|
1245
|
+
---
|
|
1246
|
+
|
|
1247
|
+
#### `SessionDataManager.toCamelCase(snakeCase)`
|
|
1248
|
+
**Purpose**: Convert snake_case field name to camelCase.
|
|
1249
|
+
|
|
1250
|
+
**Examples**:
|
|
1251
|
+
- `session_id` → `sessionId`
|
|
1252
|
+
- `pages_viewed` → `pagesViewed`
|
|
1253
|
+
- `is_bounce` → `isBounce`
|
|
1254
|
+
|
|
1255
|
+
**Parameters**: `snakeCase` - Field name in snake_case
|
|
1256
|
+
|
|
1257
|
+
**Returns**: Field name in camelCase
|
|
1258
|
+
|
|
1259
|
+
---
|
|
1260
|
+
|
|
1261
|
+
#### `SessionDataManager.mapFieldName(internalName)`
|
|
1262
|
+
**Purpose**: Map internal field names to backend field names.
|
|
1263
|
+
|
|
1264
|
+
**Logic**:
|
|
1265
|
+
- Uses predefined field map for special cases
|
|
1266
|
+
- Falls back to `toSnakeCase()` for unmapped fields
|
|
1267
|
+
- Handles special cases where mapping isn't straightforward
|
|
1268
|
+
|
|
1269
|
+
**Field Map**:
|
|
1270
|
+
- `sessionId` → `session_id`
|
|
1271
|
+
- `siteId` → `site_id`
|
|
1272
|
+
- `teamId` → `team_id`
|
|
1273
|
+
- `userId` → `user_id`
|
|
1274
|
+
- `startTime` → `start_time`
|
|
1275
|
+
- `endTime` → `end_time`
|
|
1276
|
+
- `lastActivity` → `last_activity`
|
|
1277
|
+
- `pagesViewed` → `pages_viewed`
|
|
1278
|
+
- `isBounce` → `is_bounce`
|
|
1279
|
+
- `previousSessionId` → `previous_session_id`
|
|
1280
|
+
- `timeSinceLastSession` → `time_since_last_session`
|
|
1281
|
+
- `entryPage` → `entry_page`
|
|
1282
|
+
- `exitPage` → `exit_page`
|
|
1283
|
+
|
|
1284
|
+
**Parameters**: `internalName` - Internal field name (camelCase)
|
|
1285
|
+
|
|
1286
|
+
**Returns**: Backend field name (snake_case)
|
|
1287
|
+
|
|
1288
|
+
---
|
|
1289
|
+
|
|
1290
|
+
### Field Mapping Strategy
|
|
1291
|
+
|
|
1292
|
+
**Internal (JavaScript - camelCase)**:
|
|
1293
|
+
- `sessionId` → Backend: `id` (auto-generated by PostgreSQL) and `session_id`
|
|
1294
|
+
- `siteId` → Backend: `site_id`
|
|
1295
|
+
- `userId` → Backend: `user_id`
|
|
1296
|
+
- `startTime` → Backend: `start_time`
|
|
1297
|
+
- `pagesViewed` → Backend: `pages_viewed`
|
|
1298
|
+
- `isBounce` → Backend: `is_bounce`
|
|
1299
|
+
- `lastActivity` → Backend: `last_activity`
|
|
1300
|
+
- `previousSessionId` → Backend: `previous_session_id`
|
|
1301
|
+
- `timeSinceLastSession` → Backend: `time_since_last_session`
|
|
1302
|
+
- `entryPage` → Backend: `entry_page`
|
|
1303
|
+
- `exitPage` → Backend: `exit_page`
|
|
1304
|
+
|
|
1305
|
+
**Location fields**: Already in snake_case (stored directly)
|
|
1306
|
+
|
|
1307
|
+
**Browser/Device/Wallet/UTM fields**: Already in snake_case (stored directly)
|
|
1308
|
+
|
|
1309
|
+
---
|
|
1310
|
+
|
|
1311
|
+
## Section 7: Browser/Device Detection
|
|
1312
|
+
|
|
1313
|
+
### Purpose
|
|
1314
|
+
Extract and store browser/device information as flattened fields. Centralized browser and device detection for consistent data collection.
|
|
1315
|
+
|
|
1316
|
+
### Why It Matters
|
|
1317
|
+
- Centralized browser/device detection
|
|
1318
|
+
- Consistent field structure
|
|
1319
|
+
- Single place to fix detection bugs
|
|
1320
|
+
- Handles modern User-Agent Client Hints API with fallbacks
|
|
1321
|
+
|
|
1322
|
+
### BrowserDeviceManager Object
|
|
1323
|
+
|
|
1324
|
+
#### `BrowserDeviceManager.detect()`
|
|
1325
|
+
**Purpose**: Detect browser and device information.
|
|
1326
|
+
|
|
1327
|
+
**Logic**:
|
|
1328
|
+
1. Detects device type (desktop/mobile/tablet) from user agent
|
|
1329
|
+
2. Extracts browser name using modern User-Agent Client Hints API (if available)
|
|
1330
|
+
3. Falls back to parsing user agent string for browser name
|
|
1331
|
+
4. Extracts OS, resolution, user agent, and language
|
|
1332
|
+
|
|
1333
|
+
**Device Type Detection**:
|
|
1334
|
+
- Mobile: `/Mobi|Android/i` in user agent
|
|
1335
|
+
- Tablet: `/Tablet|iPad/i` in user agent
|
|
1336
|
+
- Desktop: Default (if not mobile or tablet)
|
|
1337
|
+
|
|
1338
|
+
**Browser Detection**:
|
|
1339
|
+
- Primary: `navigator.userAgentData?.brands?.[0]?.brand` (modern API)
|
|
1340
|
+
- Fallback: Parse user agent string (Chrome, Firefox, Safari, Edge, Opera)
|
|
1341
|
+
- Final fallback: `navigator.appName`
|
|
1342
|
+
|
|
1343
|
+
**Returns**: Object with browser/device fields (all snake_case):
|
|
1344
|
+
```javascript
|
|
1345
|
+
{
|
|
1346
|
+
browser_name: "Chrome", // Browser name
|
|
1347
|
+
browser_version: "120.0.0.0", // Browser version
|
|
1348
|
+
device_type: "desktop", // "desktop" | "mobile" | "tablet"
|
|
1349
|
+
os: "Win32", // Operating system
|
|
1350
|
+
resolution: "1920x1080", // Screen resolution
|
|
1351
|
+
user_agent: navigator.userAgent, // Full user agent string
|
|
1352
|
+
language: "en-US" // Browser language
|
|
1353
|
+
}
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
**When to Use**:
|
|
1357
|
+
- On SDK initialization
|
|
1358
|
+
- When browser/device info needs to be refreshed
|
|
1359
|
+
- Before sending data to backend
|
|
1360
|
+
|
|
1361
|
+
**Example**:
|
|
1362
|
+
```javascript
|
|
1363
|
+
const browserDeviceData = BrowserDeviceManager.detect();
|
|
1364
|
+
// Returns: { browser_name: "Chrome", device_type: "desktop", ... }
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
---
|
|
1368
|
+
|
|
1369
|
+
#### `BrowserDeviceManager.store(data)`
|
|
1370
|
+
**Purpose**: Store browser/device data in sessionData (flattened).
|
|
1371
|
+
|
|
1372
|
+
**Logic**:
|
|
1373
|
+
- Stores all fields directly on sessionData (flattened for backend compatibility)
|
|
1374
|
+
- If no data provided, calls `detect()` first
|
|
1375
|
+
- All fields stored in snake_case format
|
|
1376
|
+
|
|
1377
|
+
**Stores on sessionData**:
|
|
1378
|
+
- `browser_name` - Browser name
|
|
1379
|
+
- `browser_version` - Browser version
|
|
1380
|
+
- `device_type` - Device type (desktop/mobile/tablet)
|
|
1381
|
+
- `os` - Operating system
|
|
1382
|
+
- `resolution` - Screen resolution (e.g., "1920x1080")
|
|
1383
|
+
- `user_agent` - Full user agent string
|
|
1384
|
+
- `language` - Browser language
|
|
1385
|
+
|
|
1386
|
+
**Parameters**: `data` (optional) - Browser/device data object. If not provided, calls `detect()`.
|
|
1387
|
+
|
|
1388
|
+
**When to Use**:
|
|
1389
|
+
- After detecting browser/device info
|
|
1390
|
+
- On SDK initialization
|
|
1391
|
+
- When browser/device data needs to be updated
|
|
1392
|
+
|
|
1393
|
+
**Example**:
|
|
1394
|
+
```javascript
|
|
1395
|
+
BrowserDeviceManager.store(); // Auto-detects and stores
|
|
1396
|
+
// OR
|
|
1397
|
+
const data = BrowserDeviceManager.detect();
|
|
1398
|
+
BrowserDeviceManager.store(data);
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
---
|
|
1402
|
+
|
|
1403
|
+
#### `BrowserDeviceManager.initialize()`
|
|
1404
|
+
**Purpose**: Initialize browser/device detection.
|
|
1405
|
+
|
|
1406
|
+
**Logic**:
|
|
1407
|
+
- Calls `detect()` to get browser/device information
|
|
1408
|
+
- Calls `store()` to save data to sessionData
|
|
1409
|
+
|
|
1410
|
+
**When to Use**:
|
|
1411
|
+
- On SDK initialization
|
|
1412
|
+
- Should be called early in initialization flow
|
|
1413
|
+
|
|
1414
|
+
**Example**:
|
|
1415
|
+
```javascript
|
|
1416
|
+
BrowserDeviceManager.initialize();
|
|
1417
|
+
// Detects and stores browser/device info in sessionData
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
---
|
|
1421
|
+
|
|
1422
|
+
### Reusability
|
|
1423
|
+
|
|
1424
|
+
✅ **BrowserDeviceManager.detect()**: Reusable for detecting browser/device anywhere.
|
|
1425
|
+
|
|
1426
|
+
✅ **BrowserDeviceManager.store()**: Reusable for storing browser/device data.
|
|
1427
|
+
|
|
1428
|
+
✅ **BrowserDeviceManager.initialize()**: Reusable for initialization.
|
|
1429
|
+
|
|
1430
|
+
---
|
|
1431
|
+
|
|
1432
|
+
## Section 8: UTM Parameter Management
|
|
1433
|
+
|
|
1434
|
+
### Purpose
|
|
1435
|
+
Extract and store UTM parameters as flattened fields. Centralized UTM parameter extraction from URL query string.
|
|
1436
|
+
|
|
1437
|
+
### Why It Matters
|
|
1438
|
+
- Centralized UTM parameter extraction
|
|
1439
|
+
- Consistent field structure
|
|
1440
|
+
- Single place to fix UTM bugs
|
|
1441
|
+
- Handles missing parameters gracefully
|
|
1442
|
+
|
|
1443
|
+
### UTMManager Object
|
|
1444
|
+
|
|
1445
|
+
#### `UTMManager.extract()`
|
|
1446
|
+
**Purpose**: Extract UTM parameters from URL query string.
|
|
1447
|
+
|
|
1448
|
+
**Logic**:
|
|
1449
|
+
- Uses `URLSearchParams` to parse query string
|
|
1450
|
+
- Extracts all 6 UTM parameters
|
|
1451
|
+
- Returns null for missing parameters (not empty strings)
|
|
1452
|
+
|
|
1453
|
+
**Returns**: Object with UTM fields (all snake_case):
|
|
1454
|
+
```javascript
|
|
1455
|
+
{
|
|
1456
|
+
utm_source: "google", // or null
|
|
1457
|
+
utm_medium: "cpc", // or null
|
|
1458
|
+
utm_campaign: "summer_sale", // or null
|
|
1459
|
+
utm_term: "keyword", // or null
|
|
1460
|
+
utm_content: "ad_variant", // or null
|
|
1461
|
+
utm_id: "12345" // or null
|
|
1462
|
+
}
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
**When to Use**:
|
|
1466
|
+
- On SDK initialization
|
|
1467
|
+
- When UTM parameters need to be refreshed
|
|
1468
|
+
- Before sending data to backend
|
|
1469
|
+
|
|
1470
|
+
**Example**:
|
|
1471
|
+
```javascript
|
|
1472
|
+
const utmData = UTMManager.extract();
|
|
1473
|
+
// Returns: { utm_source: "google", utm_medium: "cpc", ... }
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
---
|
|
1477
|
+
|
|
1478
|
+
#### `UTMManager.store(data)`
|
|
1479
|
+
**Purpose**: Store UTM data in sessionData (flattened).
|
|
1480
|
+
|
|
1481
|
+
**Logic**:
|
|
1482
|
+
- Stores all 6 UTM fields directly on sessionData (flattened)
|
|
1483
|
+
- Also stores in `sessionData.utmData` object (for backward compatibility)
|
|
1484
|
+
- If no data provided, calls `extract()` first
|
|
1485
|
+
|
|
1486
|
+
**Stores on sessionData**:
|
|
1487
|
+
- `utm_source` - UTM source parameter
|
|
1488
|
+
- `utm_medium` - UTM medium parameter
|
|
1489
|
+
- `utm_campaign` - UTM campaign parameter
|
|
1490
|
+
- `utm_term` - UTM term parameter
|
|
1491
|
+
- `utm_content` - UTM content parameter
|
|
1492
|
+
- `utm_id` - UTM ID parameter
|
|
1493
|
+
|
|
1494
|
+
**Parameters**: `data` (optional) - UTM data object. If not provided, calls `extract()`.
|
|
1495
|
+
|
|
1496
|
+
**When to Use**:
|
|
1497
|
+
- After extracting UTM parameters
|
|
1498
|
+
- On SDK initialization
|
|
1499
|
+
- When UTM data needs to be updated
|
|
1500
|
+
|
|
1501
|
+
**Example**:
|
|
1502
|
+
```javascript
|
|
1503
|
+
UTMManager.store(); // Auto-extracts and stores
|
|
1504
|
+
// OR
|
|
1505
|
+
const data = UTMManager.extract();
|
|
1506
|
+
UTMManager.store(data);
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
---
|
|
1510
|
+
|
|
1511
|
+
#### `UTMManager.initialize()`
|
|
1512
|
+
**Purpose**: Initialize UTM parameter extraction.
|
|
1513
|
+
|
|
1514
|
+
**Logic**:
|
|
1515
|
+
- Calls `extract()` to get UTM parameters
|
|
1516
|
+
- Calls `store()` to save data to sessionData
|
|
1517
|
+
|
|
1518
|
+
**When to Use**:
|
|
1519
|
+
- On SDK initialization
|
|
1520
|
+
- Should be called early in initialization flow
|
|
1521
|
+
|
|
1522
|
+
**Example**:
|
|
1523
|
+
```javascript
|
|
1524
|
+
UTMManager.initialize();
|
|
1525
|
+
// Extracts and stores UTM parameters in sessionData
|
|
1526
|
+
```
|
|
1527
|
+
|
|
1528
|
+
---
|
|
1529
|
+
|
|
1530
|
+
### Reusability
|
|
1531
|
+
|
|
1532
|
+
✅ **UTMManager.extract()**: Reusable for extracting UTM parameters anywhere.
|
|
1533
|
+
|
|
1534
|
+
✅ **UTMManager.store()**: Reusable for storing UTM data.
|
|
1535
|
+
|
|
1536
|
+
✅ **UTMManager.initialize()**: Reusable for initialization.
|
|
1537
|
+
|
|
1538
|
+
---
|
|
1539
|
+
|
|
1540
|
+
## Section 9: Wallet/Web3 Management
|
|
1541
|
+
|
|
1542
|
+
### Purpose
|
|
1543
|
+
Detect and store wallet/Web3 information as flattened fields. Comprehensive support for EVM wallets and chains with real-time tracking.
|
|
1544
|
+
|
|
1545
|
+
### Why It Matters
|
|
1546
|
+
- **Comprehensive Wallet Detection**: Supports 30+ EVM wallets
|
|
1547
|
+
- **Multi-Chain Support**: Recognizes 40+ EVM-compatible chains
|
|
1548
|
+
- **EIP-6963 Support**: Modern multi-wallet detection standard
|
|
1549
|
+
- **Real-Time Tracking**: Listens for wallet events (account changes, chain changes)
|
|
1550
|
+
- **Non-Intrusive**: Detects wallets without prompting user
|
|
1551
|
+
- **Single Source of Truth**: Centralized wallet state management
|
|
1552
|
+
|
|
1553
|
+
### WalletManager Object
|
|
1554
|
+
|
|
1555
|
+
#### Supported Wallets (30+ EVM Wallets)
|
|
1556
|
+
|
|
1557
|
+
**Major Wallets:**
|
|
1558
|
+
- ✅ **MetaMask** - Most popular Ethereum wallet
|
|
1559
|
+
- ✅ **Trust Wallet** - Popular mobile wallet
|
|
1560
|
+
- ✅ **Coinbase Wallet** - Coinbase's official wallet
|
|
1561
|
+
- ✅ **Brave Wallet** - Built into Brave browser
|
|
1562
|
+
- ✅ **Frame** - Privacy-focused wallet
|
|
1563
|
+
- ✅ **Phantom** - Solana wallet (EVM support)
|
|
1564
|
+
- ✅ **TronLink** - Tron wallet
|
|
1565
|
+
|
|
1566
|
+
**Additional Wallets:**
|
|
1567
|
+
- ✅ **OKX Wallet** - OKX exchange wallet
|
|
1568
|
+
- ✅ **TokenPocket** - Multi-chain wallet
|
|
1569
|
+
- ✅ **SafePal** - Hardware wallet software
|
|
1570
|
+
- ✅ **MathWallet** - Multi-chain wallet
|
|
1571
|
+
- ✅ **1inch Wallet** - DEX aggregator wallet
|
|
1572
|
+
- ✅ **Alpha Wallet** - Mobile wallet
|
|
1573
|
+
- ✅ **AToken** - Multi-chain wallet
|
|
1574
|
+
- ✅ **BitKeep** - Popular DeFi wallet
|
|
1575
|
+
- ✅ **BlockWallet** - Privacy wallet
|
|
1576
|
+
- ✅ **Coin98** - Vietnamese wallet
|
|
1577
|
+
- ✅ **Dawn** - Ethereum wallet
|
|
1578
|
+
- ✅ **Enkrypt** - Multi-chain wallet
|
|
1579
|
+
- ✅ **Exodus** - Desktop wallet
|
|
1580
|
+
- ✅ **Frontier** - DeFi wallet
|
|
1581
|
+
- ✅ **Gamestop** - GameStop wallet
|
|
1582
|
+
- ✅ **HyperPay** - Payment wallet
|
|
1583
|
+
- ✅ **ImToken** - Chinese wallet
|
|
1584
|
+
- ✅ **Infinity Wallet** - Multi-chain wallet
|
|
1585
|
+
- ✅ **Liquality** - Atomic swap wallet
|
|
1586
|
+
- ✅ **MEW CX** - MyEtherWallet
|
|
1587
|
+
- ✅ **Opera** - Opera browser wallet
|
|
1588
|
+
- ✅ **Portal** - WalletConnect wallet
|
|
1589
|
+
- ✅ **Rabby** - DeFi wallet
|
|
1590
|
+
- ✅ **Rainbow** - Beautiful mobile wallet
|
|
1591
|
+
- ✅ **Status** - Mobile wallet
|
|
1592
|
+
- ✅ **Tally** - Community wallet
|
|
1593
|
+
- ✅ **Tokenary** - iOS wallet
|
|
1594
|
+
- ✅ **Torus** - Social login wallet
|
|
1595
|
+
- ✅ **XDEFI** - Multi-chain wallet
|
|
1596
|
+
- ✅ **Zerion** - DeFi portfolio wallet
|
|
1597
|
+
|
|
1598
|
+
**Detection Methods:**
|
|
1599
|
+
1. **EIP-6963** (Modern Standard): Detects wallets via `eip6963:announceProvider` event
|
|
1600
|
+
2. **window.ethereum Properties**: Checks wallet-specific properties (e.g., `isMetaMask`)
|
|
1601
|
+
3. **Global Wallet Objects**: Checks for wallet-specific global objects (e.g., `window.okxwallet`)
|
|
1602
|
+
|
|
1603
|
+
---
|
|
1604
|
+
|
|
1605
|
+
#### Supported Chains (40+ EVM-Compatible Chains)
|
|
1606
|
+
|
|
1607
|
+
**Mainnets:**
|
|
1608
|
+
- ✅ **Ethereum Mainnet** (Chain ID: 1)
|
|
1609
|
+
- ✅ **Binance Smart Chain** (Chain ID: 56)
|
|
1610
|
+
- ✅ **Polygon** (Chain ID: 137)
|
|
1611
|
+
- ✅ **Optimism** (Chain ID: 10)
|
|
1612
|
+
- ✅ **Arbitrum One** (Chain ID: 42161)
|
|
1613
|
+
- ✅ **Avalanche C-Chain** (Chain ID: 43114)
|
|
1614
|
+
- ✅ **Fantom Opera** (Chain ID: 250)
|
|
1615
|
+
- ✅ **Gnosis Chain** (Chain ID: 100)
|
|
1616
|
+
- ✅ **Aurora** (Chain ID: 1313161554)
|
|
1617
|
+
- ✅ **Base** (Chain ID: 8453)
|
|
1618
|
+
- ✅ **zkSync Era** (Chain ID: 324)
|
|
1619
|
+
- ✅ **Linea** (Chain ID: 59144)
|
|
1620
|
+
- ✅ **Scroll** (Chain ID: 534352)
|
|
1621
|
+
- ✅ **Mantle** (Chain ID: 5000)
|
|
1622
|
+
- ✅ **Celo** (Chain ID: 42220)
|
|
1623
|
+
- ✅ **Moonbeam** (Chain ID: 1284)
|
|
1624
|
+
- ✅ **Moonriver** (Chain ID: 1285)
|
|
1625
|
+
- ✅ **Harmony** (Chain ID: 1666600000)
|
|
1626
|
+
- ✅ **Cronos** (Chain ID: 25)
|
|
1627
|
+
- ✅ **Metis** (Chain ID: 1088)
|
|
1628
|
+
- ✅ **Polygon zkEVM** (Chain ID: 1101)
|
|
1629
|
+
- ✅ **opBNB** (Chain ID: 204)
|
|
1630
|
+
- ✅ **OKXChain** (Chain ID: 196)
|
|
1631
|
+
- ✅ **Blast** (Chain ID: 81457)
|
|
1632
|
+
- ✅ **Chiliz Chain** (Chain ID: 88888)
|
|
1633
|
+
- ✅ **ZetaChain** (Chain ID: 7001)
|
|
1634
|
+
- ✅ **Core** (Chain ID: 1116)
|
|
1635
|
+
- ✅ **Astar zkEVM** (Chain ID: 3776)
|
|
1636
|
+
|
|
1637
|
+
**Testnets:**
|
|
1638
|
+
- ✅ **Holesky** (Chain ID: 17000)
|
|
1639
|
+
- ✅ **Sepolia** (Chain ID: 11155111)
|
|
1640
|
+
- ✅ **Goerli** (Chain ID: 5)
|
|
1641
|
+
- ✅ **BSC Testnet** (Chain ID: 97)
|
|
1642
|
+
- ✅ **Mumbai** (Chain ID: 80001)
|
|
1643
|
+
- ✅ **Arbitrum Sepolia** (Chain ID: 421614)
|
|
1644
|
+
- ✅ **Optimism Sepolia** (Chain ID: 11155420)
|
|
1645
|
+
- ✅ **Base Sepolia** (Chain ID: 84532)
|
|
1646
|
+
- ✅ **Avalanche Fuji** (Chain ID: 43113)
|
|
1647
|
+
- ✅ **Fantom Testnet** (Chain ID: 4002)
|
|
1648
|
+
|
|
1649
|
+
**Unknown Chains:**
|
|
1650
|
+
- ✅ Automatically displays as `Unknown Chain (ID: <chainId>)` for unrecognized chains
|
|
1651
|
+
|
|
1652
|
+
---
|
|
1653
|
+
|
|
1654
|
+
#### `WalletManager.detectWalletType()`
|
|
1655
|
+
**Purpose**: Detect wallet type from available providers.
|
|
1656
|
+
|
|
1657
|
+
**Logic**:
|
|
1658
|
+
1. Checks EIP-6963 providers first (modern standard)
|
|
1659
|
+
2. Checks `window.ethereum` wallet patterns (30+ wallets)
|
|
1660
|
+
3. Checks global wallet objects (OKX, TokenPocket, etc.)
|
|
1661
|
+
4. Falls back to "Web3 Wallet" if `window.ethereum` exists but no specific wallet detected
|
|
1662
|
+
5. Returns "No Wallet Detected" if no wallet found
|
|
1663
|
+
|
|
1664
|
+
**Returns**: Wallet name string (e.g., "MetaMask", "Trust Wallet", "No Wallet Detected")
|
|
1665
|
+
|
|
1666
|
+
**When to Use**:
|
|
1667
|
+
- On SDK initialization
|
|
1668
|
+
- When wallet type needs to be refreshed
|
|
1669
|
+
- Before sending data to backend
|
|
1670
|
+
|
|
1671
|
+
**Example**:
|
|
1672
|
+
```javascript
|
|
1673
|
+
const walletType = WalletManager.detectWalletType();
|
|
1674
|
+
// Returns: "MetaMask" or "Trust Wallet" or "No Wallet Detected"
|
|
1675
|
+
```
|
|
1676
|
+
|
|
1677
|
+
---
|
|
1678
|
+
|
|
1679
|
+
#### `WalletManager.getChainNameFromId(chainId)`
|
|
1680
|
+
**Purpose**: Convert chain ID to human-readable chain name.
|
|
1681
|
+
|
|
1682
|
+
**Logic**:
|
|
1683
|
+
- Handles hex string chain IDs (e.g., "0x1" → 1)
|
|
1684
|
+
- Handles decimal string chain IDs (e.g., "1" → 1)
|
|
1685
|
+
- Looks up chain name in `CHAIN_NAMES` mapping
|
|
1686
|
+
- Returns "Unknown Chain (ID: <chainId>)" for unrecognized chains
|
|
1687
|
+
|
|
1688
|
+
**Parameters**: `chainId` - Chain ID (number, hex string, or decimal string)
|
|
1689
|
+
|
|
1690
|
+
**Returns**: Chain name string (e.g., "Ethereum Mainnet", "Polygon")
|
|
1691
|
+
|
|
1692
|
+
**Example**:
|
|
1693
|
+
```javascript
|
|
1694
|
+
const chainName = WalletManager.getChainNameFromId(1);
|
|
1695
|
+
// Returns: "Ethereum Mainnet"
|
|
1696
|
+
|
|
1697
|
+
const chainName = WalletManager.getChainNameFromId("0x38");
|
|
1698
|
+
// Returns: "Binance Smart Chain"
|
|
1699
|
+
```
|
|
1700
|
+
|
|
1701
|
+
---
|
|
1702
|
+
|
|
1703
|
+
#### `WalletManager.detectChainId()`
|
|
1704
|
+
**Purpose**: Detect chain ID from provider.
|
|
1705
|
+
|
|
1706
|
+
**Logic**:
|
|
1707
|
+
1. Checks `window.ethereum.chainId` property (modern providers)
|
|
1708
|
+
2. Requests chain ID via `eth_chainId` method
|
|
1709
|
+
3. Falls back to Web3.js if available
|
|
1710
|
+
4. Returns null if no provider or error
|
|
1711
|
+
|
|
1712
|
+
**Returns**: Promise that resolves to chain ID (hex string) or null
|
|
1713
|
+
|
|
1714
|
+
**When to Use**:
|
|
1715
|
+
- When chain ID needs to be detected
|
|
1716
|
+
- Before converting to chain name
|
|
1717
|
+
|
|
1718
|
+
**Example**:
|
|
1719
|
+
```javascript
|
|
1720
|
+
const chainId = await WalletManager.detectChainId();
|
|
1721
|
+
// Returns: "0x1" (Ethereum) or null
|
|
1722
|
+
```
|
|
1723
|
+
|
|
1724
|
+
---
|
|
1725
|
+
|
|
1726
|
+
#### `WalletManager.detectChainName()`
|
|
1727
|
+
**Purpose**: Detect chain name from provider.
|
|
1728
|
+
|
|
1729
|
+
**Logic**:
|
|
1730
|
+
1. Gets chain ID via `detectChainId()`
|
|
1731
|
+
2. Converts chain ID to chain name via `getChainNameFromId()`
|
|
1732
|
+
3. Returns "Not Connected" if no chain ID
|
|
1733
|
+
|
|
1734
|
+
**Returns**: Promise that resolves to chain name string
|
|
1735
|
+
|
|
1736
|
+
**When to Use**:
|
|
1737
|
+
- On SDK initialization
|
|
1738
|
+
- When chain name needs to be refreshed
|
|
1739
|
+
- After chain change events
|
|
1740
|
+
|
|
1741
|
+
**Example**:
|
|
1742
|
+
```javascript
|
|
1743
|
+
const chainName = await WalletManager.detectChainName();
|
|
1744
|
+
// Returns: "Ethereum Mainnet" or "Not Connected"
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
---
|
|
1748
|
+
|
|
1749
|
+
#### `WalletManager.checkWalletConnected()`
|
|
1750
|
+
**Purpose**: Check if wallet is connected (non-intrusive).
|
|
1751
|
+
|
|
1752
|
+
**Logic**:
|
|
1753
|
+
- Uses `eth_accounts` method (doesn't prompt user)
|
|
1754
|
+
- Returns true if accounts exist, false otherwise
|
|
1755
|
+
- Returns false if no provider
|
|
1756
|
+
|
|
1757
|
+
**Returns**: Promise that resolves to boolean
|
|
1758
|
+
|
|
1759
|
+
**When to Use**:
|
|
1760
|
+
- On SDK initialization (silent check)
|
|
1761
|
+
- Before updating wallet info
|
|
1762
|
+
- To check connection status without prompting
|
|
1763
|
+
|
|
1764
|
+
**Example**:
|
|
1765
|
+
```javascript
|
|
1766
|
+
const isConnected = await WalletManager.checkWalletConnected();
|
|
1767
|
+
// Returns: true or false
|
|
1768
|
+
```
|
|
1769
|
+
|
|
1770
|
+
---
|
|
1771
|
+
|
|
1772
|
+
#### `WalletManager.getWalletAddress()`
|
|
1773
|
+
**Purpose**: Get connected wallet address (non-intrusive).
|
|
1774
|
+
|
|
1775
|
+
**Logic**:
|
|
1776
|
+
- Uses `eth_accounts` method (doesn't prompt user)
|
|
1777
|
+
- Returns first account address or null
|
|
1778
|
+
- Returns null if no provider or error
|
|
1779
|
+
|
|
1780
|
+
**Returns**: Promise that resolves to wallet address (hex string) or null
|
|
1781
|
+
|
|
1782
|
+
**When to Use**:
|
|
1783
|
+
- When wallet address needs to be retrieved
|
|
1784
|
+
- After wallet connection
|
|
1785
|
+
- On account change events
|
|
1786
|
+
|
|
1787
|
+
**Example**:
|
|
1788
|
+
```javascript
|
|
1789
|
+
const address = await WalletManager.getWalletAddress();
|
|
1790
|
+
// Returns: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" or null
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
---
|
|
1794
|
+
|
|
1795
|
+
#### `WalletManager.updateWalletInfo()`
|
|
1796
|
+
**Purpose**: Update all wallet information (comprehensive).
|
|
1797
|
+
|
|
1798
|
+
**Logic**:
|
|
1799
|
+
1. Detects wallet type
|
|
1800
|
+
2. Checks if wallet is connected
|
|
1801
|
+
3. Gets wallet address if connected
|
|
1802
|
+
4. Gets chain name if connected
|
|
1803
|
+
5. Determines if user is Web3 user (has wallet installed)
|
|
1804
|
+
6. Stores all fields in sessionData (flattened)
|
|
1805
|
+
|
|
1806
|
+
**Stores on sessionData**:
|
|
1807
|
+
- `wallet_address` - Connected wallet address (or null)
|
|
1808
|
+
- `wallet_type` - Wallet name (e.g., "MetaMask")
|
|
1809
|
+
- `chain_name` - Chain name (e.g., "Ethereum Mainnet")
|
|
1810
|
+
- `is_web3_user` - Boolean (true if wallet installed, even if not connected)
|
|
1811
|
+
- `wallet_connected` - Boolean (true only if wallet is connected)
|
|
1812
|
+
|
|
1813
|
+
**Returns**: Promise that resolves to wallet info object or null on error
|
|
1814
|
+
|
|
1815
|
+
**When to Use**:
|
|
1816
|
+
- On SDK initialization
|
|
1817
|
+
- After wallet connection
|
|
1818
|
+
- On account change events
|
|
1819
|
+
- On chain change events
|
|
1820
|
+
- Before sending data to backend
|
|
1821
|
+
|
|
1822
|
+
**Example**:
|
|
1823
|
+
```javascript
|
|
1824
|
+
const walletInfo = await WalletManager.updateWalletInfo();
|
|
1825
|
+
// Returns: {
|
|
1826
|
+
// wallet_address: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
|
|
1827
|
+
// wallet_type: "MetaMask",
|
|
1828
|
+
// chain_name: "Ethereum Mainnet",
|
|
1829
|
+
// is_web3_user: true,
|
|
1830
|
+
// wallet_connected: true
|
|
1831
|
+
// }
|
|
1832
|
+
```
|
|
1833
|
+
|
|
1834
|
+
---
|
|
1835
|
+
|
|
1836
|
+
#### `WalletManager.setupEventListeners()`
|
|
1837
|
+
**Purpose**: Setup wallet event listeners for real-time tracking.
|
|
1838
|
+
|
|
1839
|
+
**Logic**:
|
|
1840
|
+
- Listens for `accountsChanged` event (wallet connect/disconnect, account switch)
|
|
1841
|
+
- Listens for `chainChanged` event (network switch)
|
|
1842
|
+
- Updates wallet info automatically on events
|
|
1843
|
+
- Handles wallet disconnection gracefully
|
|
1844
|
+
|
|
1845
|
+
**When to Use**:
|
|
1846
|
+
- On SDK initialization
|
|
1847
|
+
- Should be called once to setup listeners
|
|
1848
|
+
|
|
1849
|
+
**Example**:
|
|
1850
|
+
```javascript
|
|
1851
|
+
WalletManager.setupEventListeners();
|
|
1852
|
+
// Now automatically tracks wallet changes
|
|
1853
|
+
```
|
|
1854
|
+
|
|
1855
|
+
---
|
|
1856
|
+
|
|
1857
|
+
#### `WalletManager.initialize()`
|
|
1858
|
+
**Purpose**: Initialize wallet detection and tracking.
|
|
1859
|
+
|
|
1860
|
+
**Logic**:
|
|
1861
|
+
1. Calls `updateWalletInfo()` (non-blocking, initial detection)
|
|
1862
|
+
2. Calls `setupEventListeners()` (setup real-time tracking)
|
|
1863
|
+
|
|
1864
|
+
**When to Use**:
|
|
1865
|
+
- On SDK initialization
|
|
1866
|
+
- Should be called early but doesn't block other operations
|
|
1867
|
+
|
|
1868
|
+
**Example**:
|
|
1869
|
+
```javascript
|
|
1870
|
+
await WalletManager.initialize();
|
|
1871
|
+
// Detects wallet, sets up listeners, tracks changes
|
|
1872
|
+
```
|
|
1873
|
+
|
|
1874
|
+
---
|
|
1875
|
+
|
|
1876
|
+
### Wallet Detection Flow
|
|
1877
|
+
|
|
1878
|
+
```
|
|
1879
|
+
1. SDK Initialization
|
|
1880
|
+
↓
|
|
1881
|
+
2. WalletManager.initialize()
|
|
1882
|
+
↓
|
|
1883
|
+
3. Check EIP-6963 providers (modern standard)
|
|
1884
|
+
↓
|
|
1885
|
+
4. Check window.ethereum wallet patterns (30+ wallets)
|
|
1886
|
+
↓
|
|
1887
|
+
5. Check global wallet objects
|
|
1888
|
+
↓
|
|
1889
|
+
6. Detect wallet type
|
|
1890
|
+
↓
|
|
1891
|
+
7. Check connection status (non-intrusive)
|
|
1892
|
+
↓
|
|
1893
|
+
8. Get wallet address (if connected)
|
|
1894
|
+
↓
|
|
1895
|
+
9. Get chain ID and convert to chain name
|
|
1896
|
+
↓
|
|
1897
|
+
10. Store all fields in sessionData
|
|
1898
|
+
↓
|
|
1899
|
+
11. Setup event listeners (accountsChanged, chainChanged)
|
|
1900
|
+
↓
|
|
1901
|
+
12. Real-time tracking active
|
|
1902
|
+
```
|
|
1903
|
+
|
|
1904
|
+
---
|
|
1905
|
+
|
|
1906
|
+
### Key Features
|
|
1907
|
+
|
|
1908
|
+
✅ **Non-Intrusive Detection**: Never prompts user for wallet connection
|
|
1909
|
+
✅ **EIP-6963 Support**: Modern multi-wallet detection standard
|
|
1910
|
+
✅ **30+ Wallet Support**: Comprehensive EVM wallet detection
|
|
1911
|
+
✅ **40+ Chain Support**: Recognizes all major EVM chains
|
|
1912
|
+
✅ **Real-Time Tracking**: Automatically updates on wallet events
|
|
1913
|
+
✅ **Error Handling**: Graceful fallbacks on errors
|
|
1914
|
+
✅ **Flattened Storage**: All fields stored directly on sessionData
|
|
1915
|
+
|
|
1916
|
+
---
|
|
1917
|
+
|
|
1918
|
+
### Reusability
|
|
1919
|
+
|
|
1920
|
+
✅ **WalletManager.detectWalletType()**: Reusable for detecting wallet anywhere.
|
|
1921
|
+
|
|
1922
|
+
✅ **WalletManager.getChainNameFromId()**: Reusable for chain ID to name conversion.
|
|
1923
|
+
|
|
1924
|
+
✅ **WalletManager.updateWalletInfo()**: Reusable for updating wallet state.
|
|
1925
|
+
|
|
1926
|
+
✅ **WalletManager.initialize()**: Reusable for initialization.
|
|
1927
|
+
|
|
1928
|
+
---
|
|
1929
|
+
|
|
1930
|
+
## Section 10: Interaction Management
|
|
1931
|
+
|
|
1932
|
+
### Purpose
|
|
1933
|
+
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.
|
|
1934
|
+
|
|
1935
|
+
### Why It Matters
|
|
1936
|
+
- Centralized interaction tracking
|
|
1937
|
+
- Consistent data structure
|
|
1938
|
+
- Single place to fix interaction bugs
|
|
1939
|
+
- Prevents repeating interactions
|
|
1940
|
+
- Automatic timestamping and categorization
|
|
1941
|
+
- Immediate session storage updates
|
|
1942
|
+
|
|
1943
|
+
### InteractionManager Object
|
|
1944
|
+
|
|
1945
|
+
#### Interaction Categories (19 Categories)
|
|
1946
|
+
|
|
1947
|
+
**User Actions:**
|
|
1948
|
+
- `clicks` - Click events
|
|
1949
|
+
- `hoverEvents` - Hover/mouseover events
|
|
1950
|
+
- `touchEvents` - Touch events (mobile)
|
|
1951
|
+
- `keyboardEvents` - Keyboard input events
|
|
1952
|
+
- `copyPasteEvents` - Copy/paste events
|
|
1953
|
+
- `contextMenuEvents` - Right-click context menu events
|
|
1954
|
+
- `dragDropEvents` - Drag and drop events
|
|
1955
|
+
|
|
1956
|
+
**Form Interactions:**
|
|
1957
|
+
- `formInteractions` - General form interactions
|
|
1958
|
+
- `formSubmissions` - Form submission events
|
|
1959
|
+
- `fieldChanges` - Form field value changes
|
|
1960
|
+
- `validationErrors` - Form validation errors
|
|
1961
|
+
- `formAnalytics` - Form analytics data
|
|
1962
|
+
|
|
1963
|
+
**Media & Content:**
|
|
1964
|
+
- `mediaInteractions` - Video/audio player interactions
|
|
1965
|
+
|
|
1966
|
+
**Navigation & Scrolling:**
|
|
1967
|
+
- `scrollEvents` - Scroll events
|
|
1968
|
+
- `focusEvents` - Focus/blur events
|
|
1969
|
+
- `windowEvents` - Window resize, visibility, etc.
|
|
1970
|
+
|
|
1971
|
+
**Performance & Errors:**
|
|
1972
|
+
- `performanceEvents` - Performance metrics
|
|
1973
|
+
- `errorEvents` - JavaScript errors
|
|
1974
|
+
- `networkEvents` - Network request events
|
|
1975
|
+
|
|
1976
|
+
---
|
|
1977
|
+
|
|
1978
|
+
#### `InteractionManager.initialize()`
|
|
1979
|
+
**Purpose**: Initialize interactions structure.
|
|
1980
|
+
|
|
1981
|
+
**Logic**:
|
|
1982
|
+
- Creates empty interactions object with all 19 categories
|
|
1983
|
+
- Sets `totalInteractions` to 0
|
|
1984
|
+
- Initializes all category arrays as empty arrays
|
|
1985
|
+
|
|
1986
|
+
**When to Use**:
|
|
1987
|
+
- On SDK initialization
|
|
1988
|
+
- When interactions need to be reset
|
|
1989
|
+
- Before adding interactions
|
|
1990
|
+
|
|
1991
|
+
**Example**:
|
|
1992
|
+
```javascript
|
|
1993
|
+
InteractionManager.initialize();
|
|
1994
|
+
// Creates empty interactions structure
|
|
1995
|
+
```
|
|
1996
|
+
|
|
1997
|
+
---
|
|
1998
|
+
|
|
1999
|
+
#### `InteractionManager.add(category, interactionData)`
|
|
2000
|
+
**Purpose**: Add interaction to appropriate category.
|
|
2001
|
+
|
|
2002
|
+
**Logic**:
|
|
2003
|
+
1. Ensures interactions are initialized
|
|
2004
|
+
2. Validates category
|
|
2005
|
+
3. Adds timestamp if not provided
|
|
2006
|
+
4. Adds to specific category array
|
|
2007
|
+
5. Updates total interactions count
|
|
2008
|
+
6. Updates session activity
|
|
2009
|
+
7. Immediately updates session storage
|
|
2010
|
+
|
|
2011
|
+
**Parameters**:
|
|
2012
|
+
- `category` - Interaction category (e.g., 'clicks', 'formInteractions')
|
|
2013
|
+
- `interactionData` - Interaction data object (can include any fields)
|
|
2014
|
+
|
|
2015
|
+
**Interaction Data Structure**:
|
|
2016
|
+
```javascript
|
|
2017
|
+
{
|
|
2018
|
+
timestamp: "2024-12-15T10:30:00.000Z", // Auto-added if not provided
|
|
2019
|
+
elementId: "button-123", // Optional: Element identifier
|
|
2020
|
+
target: "button", // Optional: Target element
|
|
2021
|
+
selector: "#submit-btn", // Optional: CSS selector
|
|
2022
|
+
// ... any other fields specific to interaction type
|
|
2023
|
+
}
|
|
2024
|
+
```
|
|
2025
|
+
|
|
2026
|
+
**When to Use**:
|
|
2027
|
+
- When tracking any user interaction
|
|
2028
|
+
- Called by event trackers (Section 14)
|
|
2029
|
+
- For custom interaction tracking
|
|
2030
|
+
|
|
2031
|
+
**Example**:
|
|
2032
|
+
```javascript
|
|
2033
|
+
// Track a click
|
|
2034
|
+
InteractionManager.add('clicks', {
|
|
2035
|
+
elementId: 'submit-button',
|
|
2036
|
+
target: 'button',
|
|
2037
|
+
selector: '#submit-btn',
|
|
2038
|
+
x: 100,
|
|
2039
|
+
y: 200
|
|
2040
|
+
});
|
|
2041
|
+
|
|
2042
|
+
// Track a form submission
|
|
2043
|
+
InteractionManager.add('formSubmissions', {
|
|
2044
|
+
formId: 'contact-form',
|
|
2045
|
+
fields: ['name', 'email', 'message'],
|
|
2046
|
+
success: true
|
|
2047
|
+
});
|
|
2048
|
+
```
|
|
2049
|
+
|
|
2050
|
+
---
|
|
2051
|
+
|
|
2052
|
+
#### `InteractionManager.updateTotalCount()`
|
|
2053
|
+
**Purpose**: Update total interactions count.
|
|
2054
|
+
|
|
2055
|
+
**Logic**:
|
|
2056
|
+
- Calculates total from all category arrays
|
|
2057
|
+
- Updates `sessionData.interactions.totalInteractions`
|
|
2058
|
+
|
|
2059
|
+
**When to Use**:
|
|
2060
|
+
- Automatically called by `add()`
|
|
2061
|
+
- After manual interaction modifications
|
|
2062
|
+
- After deduplication
|
|
2063
|
+
|
|
2064
|
+
---
|
|
2065
|
+
|
|
2066
|
+
#### `InteractionManager.updateSessionStorage()`
|
|
2067
|
+
**Purpose**: Update session storage with interactions.
|
|
2068
|
+
|
|
2069
|
+
**Logic**:
|
|
2070
|
+
- Loads current session from storage
|
|
2071
|
+
- Updates interactions in session data
|
|
2072
|
+
- Saves session back to storage
|
|
2073
|
+
|
|
2074
|
+
**When to Use**:
|
|
2075
|
+
- Automatically called by `add()`
|
|
2076
|
+
- After bulk interaction updates
|
|
2077
|
+
|
|
2078
|
+
---
|
|
2079
|
+
|
|
2080
|
+
#### `InteractionManager.getChronological()`
|
|
2081
|
+
**Purpose**: Get all interactions sorted chronologically.
|
|
2082
|
+
|
|
2083
|
+
**Logic**:
|
|
2084
|
+
1. Collects all interactions from all categories
|
|
2085
|
+
2. Adds category identifiers (`category`, `originalCategory`)
|
|
2086
|
+
3. Sorts by timestamp (oldest first)
|
|
2087
|
+
|
|
2088
|
+
**Returns**: Array of interactions sorted by timestamp
|
|
2089
|
+
|
|
2090
|
+
**Interaction Structure in Chronological Array**:
|
|
2091
|
+
```javascript
|
|
2092
|
+
[
|
|
2093
|
+
{
|
|
2094
|
+
timestamp: "2024-12-15T10:30:00.000Z",
|
|
2095
|
+
category: "click", // Normalized category name
|
|
2096
|
+
originalCategory: "clicks", // Original category array name
|
|
2097
|
+
elementId: "button-123",
|
|
2098
|
+
// ... other interaction data
|
|
2099
|
+
},
|
|
2100
|
+
{
|
|
2101
|
+
timestamp: "2024-12-15T10:30:05.000Z",
|
|
2102
|
+
category: "scrollEvent",
|
|
2103
|
+
originalCategory: "scrollEvents",
|
|
2104
|
+
scrollY: 100,
|
|
2105
|
+
// ... other interaction data
|
|
2106
|
+
}
|
|
2107
|
+
]
|
|
2108
|
+
```
|
|
2109
|
+
|
|
2110
|
+
**When to Use**:
|
|
2111
|
+
- Before sending data to backend
|
|
2112
|
+
- For analytics/reporting
|
|
2113
|
+
- For chronological interaction analysis
|
|
2114
|
+
|
|
2115
|
+
**Example**:
|
|
2116
|
+
```javascript
|
|
2117
|
+
const chronological = InteractionManager.getChronological();
|
|
2118
|
+
// Returns: Array of all interactions sorted by time
|
|
2119
|
+
```
|
|
2120
|
+
|
|
2121
|
+
---
|
|
2122
|
+
|
|
2123
|
+
#### `InteractionManager.getChronologicalForBackend()`
|
|
2124
|
+
**Purpose**: Get chronological interactions for backend.
|
|
2125
|
+
|
|
2126
|
+
**Logic**:
|
|
2127
|
+
- Gets chronological array via `getChronological()`
|
|
2128
|
+
- Returns both chronological and categorized structures
|
|
2129
|
+
|
|
2130
|
+
**Returns**: Object with:
|
|
2131
|
+
```javascript
|
|
2132
|
+
{
|
|
2133
|
+
chronological: [...], // Sorted array
|
|
2134
|
+
categorized: {...} // Original categorized structure
|
|
2135
|
+
}
|
|
2136
|
+
```
|
|
2137
|
+
|
|
2138
|
+
**When to Use**:
|
|
2139
|
+
- When sending data to backend
|
|
2140
|
+
- When both formats are needed
|
|
2141
|
+
|
|
2142
|
+
**Example**:
|
|
2143
|
+
```javascript
|
|
2144
|
+
const interactions = InteractionManager.getChronologicalForBackend();
|
|
2145
|
+
// Returns: { chronological: [...], categorized: {...} }
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
---
|
|
2149
|
+
|
|
2150
|
+
#### `InteractionManager.deduplicate()`
|
|
2151
|
+
**Purpose**: Remove duplicate interactions.
|
|
2152
|
+
|
|
2153
|
+
**Logic**:
|
|
2154
|
+
- Compares interactions by timestamp, category, and element identifier
|
|
2155
|
+
- Keeps first occurrence of duplicates
|
|
2156
|
+
- Updates total count after deduplication
|
|
2157
|
+
- Updates session storage
|
|
2158
|
+
|
|
2159
|
+
**Deduplication Key**: `${timestamp}_${category}_${elementId}`
|
|
2160
|
+
|
|
2161
|
+
**When to Use**:
|
|
2162
|
+
- Before sending data to backend
|
|
2163
|
+
- After bulk interaction imports
|
|
2164
|
+
- To clean up duplicate events
|
|
2165
|
+
|
|
2166
|
+
**Example**:
|
|
2167
|
+
```javascript
|
|
2168
|
+
InteractionManager.deduplicate();
|
|
2169
|
+
// Removes duplicate interactions
|
|
2170
|
+
```
|
|
2171
|
+
|
|
2172
|
+
---
|
|
2173
|
+
|
|
2174
|
+
#### `InteractionManager.getByCategory(category)`
|
|
2175
|
+
**Purpose**: Get interactions by category.
|
|
2176
|
+
|
|
2177
|
+
**Parameters**: `category` - Interaction category name
|
|
2178
|
+
|
|
2179
|
+
**Returns**: Array of interactions for that category
|
|
2180
|
+
|
|
2181
|
+
**When to Use**:
|
|
2182
|
+
- When analyzing specific interaction types
|
|
2183
|
+
- For category-specific analytics
|
|
2184
|
+
|
|
2185
|
+
**Example**:
|
|
2186
|
+
```javascript
|
|
2187
|
+
const clicks = InteractionManager.getByCategory('clicks');
|
|
2188
|
+
// Returns: Array of all click interactions
|
|
2189
|
+
```
|
|
2190
|
+
|
|
2191
|
+
---
|
|
2192
|
+
|
|
2193
|
+
#### `InteractionManager.getTotalCount()`
|
|
2194
|
+
**Purpose**: Get total interactions count.
|
|
2195
|
+
|
|
2196
|
+
**Returns**: Total number of interactions across all categories
|
|
2197
|
+
|
|
2198
|
+
**When to Use**:
|
|
2199
|
+
- For analytics/reporting
|
|
2200
|
+
- To check interaction volume
|
|
2201
|
+
|
|
2202
|
+
**Example**:
|
|
2203
|
+
```javascript
|
|
2204
|
+
const total = InteractionManager.getTotalCount();
|
|
2205
|
+
// Returns: 42 (total interactions)
|
|
2206
|
+
```
|
|
2207
|
+
|
|
2208
|
+
---
|
|
2209
|
+
|
|
2210
|
+
#### `InteractionManager.clear()`
|
|
2211
|
+
**Purpose**: Clear all interactions.
|
|
2212
|
+
|
|
2213
|
+
**Logic**:
|
|
2214
|
+
- Resets all category arrays to empty
|
|
2215
|
+
- Sets total count to 0
|
|
2216
|
+
- Updates session storage
|
|
2217
|
+
|
|
2218
|
+
**When to Use**:
|
|
2219
|
+
- For testing/reset
|
|
2220
|
+
- When starting fresh session
|
|
2221
|
+
|
|
2222
|
+
**Example**:
|
|
2223
|
+
```javascript
|
|
2224
|
+
InteractionManager.clear();
|
|
2225
|
+
// Clears all interactions
|
|
2226
|
+
```
|
|
2227
|
+
|
|
2228
|
+
---
|
|
2229
|
+
|
|
2230
|
+
### Reusability
|
|
2231
|
+
|
|
2232
|
+
✅ **InteractionManager.add()**: Reusable for tracking any interaction type.
|
|
2233
|
+
|
|
2234
|
+
✅ **InteractionManager.getChronological()**: Reusable for chronological analysis.
|
|
2235
|
+
|
|
2236
|
+
✅ **InteractionManager.deduplicate()**: Reusable for cleaning duplicate interactions.
|
|
2237
|
+
|
|
2238
|
+
✅ **InteractionManager.getByCategory()**: Reusable for category-specific queries.
|
|
2239
|
+
|
|
2240
|
+
---
|
|
2241
|
+
|
|
2242
|
+
## Section 11: Page Visit Management
|
|
2243
|
+
|
|
2244
|
+
### Purpose
|
|
2245
|
+
Track and manage page visits with durations. Handles entry/exit pages, mount/unmount times, and page duration calculations.
|
|
2246
|
+
|
|
2247
|
+
### Why It Matters
|
|
2248
|
+
- Centralized page visit tracking
|
|
2249
|
+
- Accurate page duration calculations
|
|
2250
|
+
- Proper entry/exit page tracking
|
|
2251
|
+
- Single place to fix page visit bugs
|
|
2252
|
+
- Prevents duplicate page visits
|
|
2253
|
+
|
|
2254
|
+
### PageVisitManager Object
|
|
2255
|
+
|
|
2256
|
+
#### `PageVisitManager.track(path, timestamp)`
|
|
2257
|
+
**Purpose**: Track a page visit.
|
|
2258
|
+
|
|
2259
|
+
**Logic**:
|
|
2260
|
+
1. Ensures session ID exists
|
|
2261
|
+
2. Gets current path and URL
|
|
2262
|
+
3. Checks if page already visited (prevents duplicates)
|
|
2263
|
+
4. Creates page visit entry with mount time
|
|
2264
|
+
5. Updates previous page's unmount time and duration
|
|
2265
|
+
6. Sets entry page if first page
|
|
2266
|
+
7. Updates pagesViewed count
|
|
2267
|
+
8. Calculates session duration
|
|
2268
|
+
9. Updates session activity
|
|
2269
|
+
10. Saves to storage
|
|
2270
|
+
|
|
2271
|
+
**Parameters**:
|
|
2272
|
+
- `path` (optional) - Page path. Defaults to `window.location.pathname + search`
|
|
2273
|
+
- `timestamp` (optional) - Page visit timestamp. Defaults to current time
|
|
2274
|
+
|
|
2275
|
+
**Stores in `visited_pages` array**:
|
|
2276
|
+
```javascript
|
|
2277
|
+
{
|
|
2278
|
+
path: "/home",
|
|
2279
|
+
timestamp: "2024-12-15T10:30:00.000Z",
|
|
2280
|
+
duration: 180,
|
|
2281
|
+
isEntry: true,
|
|
2282
|
+
isExit: false,
|
|
2283
|
+
mountTime: 1702641000000, // Milliseconds
|
|
2284
|
+
unmountTime: 1702641180000 // Milliseconds
|
|
2285
|
+
}
|
|
2286
|
+
```
|
|
2287
|
+
|
|
2288
|
+
**When to Use**:
|
|
2289
|
+
- On page load
|
|
2290
|
+
- On SPA navigation
|
|
2291
|
+
- When tracking page visits
|
|
2292
|
+
|
|
2293
|
+
**Example**:
|
|
2294
|
+
```javascript
|
|
2295
|
+
PageVisitManager.track();
|
|
2296
|
+
// Tracks current page visit
|
|
2297
|
+
```
|
|
2298
|
+
|
|
2299
|
+
---
|
|
2300
|
+
|
|
2301
|
+
#### `PageVisitManager.trackUnmount()`
|
|
2302
|
+
**Purpose**: Track page unmount (when user navigates away).
|
|
2303
|
+
|
|
2304
|
+
**Logic**:
|
|
2305
|
+
- Finds current page in page visits
|
|
2306
|
+
- Sets unmount time
|
|
2307
|
+
- Calculates page duration using DurationManager
|
|
2308
|
+
- Recalculates session duration
|
|
2309
|
+
- Saves to storage
|
|
2310
|
+
|
|
2311
|
+
**When to Use**:
|
|
2312
|
+
- Automatically called on `beforeunload` event
|
|
2313
|
+
- Automatically called on `visibilitychange` event (SPA navigation)
|
|
2314
|
+
- Can be called manually if needed
|
|
2315
|
+
|
|
2316
|
+
**Example**:
|
|
2317
|
+
```javascript
|
|
2318
|
+
PageVisitManager.trackUnmount();
|
|
2319
|
+
// Tracks current page unmount
|
|
2320
|
+
```
|
|
2321
|
+
|
|
2322
|
+
---
|
|
2323
|
+
|
|
2324
|
+
#### `PageVisitManager.getAll()`
|
|
2325
|
+
**Purpose**: Get all page visits.
|
|
2326
|
+
|
|
2327
|
+
**Returns**: Array of all page visits
|
|
2328
|
+
|
|
2329
|
+
**When to Use**:
|
|
2330
|
+
- For analytics/reporting
|
|
2331
|
+
- To check page visit history
|
|
2332
|
+
|
|
2333
|
+
**Example**:
|
|
2334
|
+
```javascript
|
|
2335
|
+
const visits = PageVisitManager.getAll();
|
|
2336
|
+
// Returns: Array of all page visits
|
|
2337
|
+
```
|
|
2338
|
+
|
|
2339
|
+
---
|
|
2340
|
+
|
|
2341
|
+
#### `PageVisitManager.getEntryPage()`
|
|
2342
|
+
**Purpose**: Get entry page (first page visited).
|
|
2343
|
+
|
|
2344
|
+
**Returns**: First page visit object or null
|
|
2345
|
+
|
|
2346
|
+
**When to Use**:
|
|
2347
|
+
- To get entry page information
|
|
2348
|
+
- For analytics
|
|
2349
|
+
|
|
2350
|
+
**Example**:
|
|
2351
|
+
```javascript
|
|
2352
|
+
const entryPage = PageVisitManager.getEntryPage();
|
|
2353
|
+
// Returns: First page visit or null
|
|
2354
|
+
```
|
|
2355
|
+
|
|
2356
|
+
---
|
|
2357
|
+
|
|
2358
|
+
#### `PageVisitManager.getExitPage()`
|
|
2359
|
+
**Purpose**: Get exit page (last page visited).
|
|
2360
|
+
|
|
2361
|
+
**Returns**: Last page visit object or null
|
|
2362
|
+
|
|
2363
|
+
**When to Use**:
|
|
2364
|
+
- To get exit page information
|
|
2365
|
+
- For analytics
|
|
2366
|
+
|
|
2367
|
+
**Example**:
|
|
2368
|
+
```javascript
|
|
2369
|
+
const exitPage = PageVisitManager.getExitPage();
|
|
2370
|
+
// Returns: Last page visit or null
|
|
2371
|
+
```
|
|
2372
|
+
|
|
2373
|
+
---
|
|
2374
|
+
|
|
2375
|
+
#### `PageVisitManager.initialize()`
|
|
2376
|
+
**Purpose**: Initialize page visit tracking.
|
|
2377
|
+
|
|
2378
|
+
**Logic**:
|
|
2379
|
+
- Tracks initial page visit
|
|
2380
|
+
- Sets up `beforeunload` listener for page unmount
|
|
2381
|
+
- Sets up `visibilitychange` listener for SPA navigation
|
|
2382
|
+
|
|
2383
|
+
**When to Use**:
|
|
2384
|
+
- On SDK initialization
|
|
2385
|
+
- Should be called early in initialization flow
|
|
2386
|
+
|
|
2387
|
+
**Example**:
|
|
2388
|
+
```javascript
|
|
2389
|
+
PageVisitManager.initialize();
|
|
2390
|
+
// Tracks initial page and sets up listeners
|
|
2391
|
+
```
|
|
2392
|
+
|
|
2393
|
+
---
|
|
2394
|
+
|
|
2395
|
+
### Reusability
|
|
2396
|
+
|
|
2397
|
+
✅ **PageVisitManager.track()**: Reusable for tracking any page visit.
|
|
2398
|
+
|
|
2399
|
+
✅ **PageVisitManager.trackUnmount()**: Reusable for tracking page unmount.
|
|
2400
|
+
|
|
2401
|
+
✅ **PageVisitManager.getAll()**: Reusable for getting page visit data.
|
|
2402
|
+
|
|
2403
|
+
---
|
|
2404
|
+
|
|
2405
|
+
## Section 12: Data Transformation/Normalization
|
|
2406
|
+
|
|
2407
|
+
### Purpose
|
|
2408
|
+
Transform sessionData from internal format (camelCase) to backend format (snake_case). Handles data normalization, field mapping, and ensures all required fields are present.
|
|
2409
|
+
|
|
2410
|
+
### Why It Matters
|
|
2411
|
+
- Consistent data format for backend
|
|
2412
|
+
- Single place to handle data transformation
|
|
2413
|
+
- Prevents missing fields errors
|
|
2414
|
+
- Handles data shape normalization
|
|
2415
|
+
- Ensures proper timestamp formatting
|
|
2416
|
+
|
|
2417
|
+
### DataTransformer Object
|
|
2418
|
+
|
|
2419
|
+
#### `DataTransformer.normalizeShape(raw)`
|
|
2420
|
+
**Purpose**: Normalize client session shape.
|
|
2421
|
+
|
|
2422
|
+
**Logic**:
|
|
2423
|
+
1. Handles field aliases:
|
|
2424
|
+
- `pageViews` → `pagesViewed`
|
|
2425
|
+
- `visitedPages` → `pageVisits`
|
|
2426
|
+
- `id` → `sessionId`
|
|
2427
|
+
2. Ensures arrays exist (`pageVisits`, `visited_pages`, `interactions`)
|
|
2428
|
+
3. Normalizes timestamps to ISO strings
|
|
2429
|
+
4. Normalizes page visit timestamps
|
|
2430
|
+
|
|
2431
|
+
**Parameters**: `raw` - Raw session data object
|
|
2432
|
+
|
|
2433
|
+
**Returns**: Normalized session data object
|
|
2434
|
+
|
|
2435
|
+
**When to Use**:
|
|
2436
|
+
- Before transforming to backend format
|
|
2437
|
+
- When loading data from storage
|
|
2438
|
+
- To ensure consistent data shape
|
|
2439
|
+
|
|
2440
|
+
**Example**:
|
|
2441
|
+
```javascript
|
|
2442
|
+
const normalized = DataTransformer.normalizeShape(rawData);
|
|
2443
|
+
// Returns: Normalized data with aliases fixed and timestamps formatted
|
|
2444
|
+
```
|
|
2445
|
+
|
|
2446
|
+
---
|
|
2447
|
+
|
|
2448
|
+
#### `DataTransformer.toBackendFormat(data)`
|
|
2449
|
+
**Purpose**: Transform sessionData to backend-compatible format.
|
|
2450
|
+
|
|
2451
|
+
**Logic**:
|
|
2452
|
+
1. Normalizes data shape using `normalizeShape()`
|
|
2453
|
+
2. Maps camelCase fields to snake_case
|
|
2454
|
+
3. Ensures `id` and `session_id` both set to sessionId value
|
|
2455
|
+
4. Flattens nested structures
|
|
2456
|
+
5. Removes internal-only fields (`utmData`, `isFirstPage`, `locationData`)
|
|
2457
|
+
6. Adds chronological interactions
|
|
2458
|
+
7. Ensures all required fields are present (even if null)
|
|
2459
|
+
|
|
2460
|
+
**Parameters**: `data` (optional) - Session data object. Defaults to `sessionData`.
|
|
2461
|
+
|
|
2462
|
+
**Returns**: Transformed object ready for backend
|
|
2463
|
+
|
|
2464
|
+
**Field Mapping**:
|
|
2465
|
+
- `sessionId` → `id` and `session_id` (both set to same value)
|
|
2466
|
+
- `siteId` → `site_id`
|
|
2467
|
+
- `userId` → `user_id`
|
|
2468
|
+
- `startTime` → `start_time`
|
|
2469
|
+
- `pagesViewed` → `pages_viewed`
|
|
2470
|
+
- `isBounce` → `is_bounce`
|
|
2471
|
+
- `entryPage` → `entry_page`
|
|
2472
|
+
- `exitPage` → `exit_page`
|
|
2473
|
+
- All location, browser, device, wallet, UTM fields already in snake_case
|
|
2474
|
+
|
|
2475
|
+
**Removed Fields**:
|
|
2476
|
+
- `utmData` (internal object, already flattened to utm_* fields)
|
|
2477
|
+
- `isFirstPage` (internal tracking flag)
|
|
2478
|
+
- `locationData` (internal object, already flattened)
|
|
2479
|
+
|
|
2480
|
+
**Example**:
|
|
2481
|
+
```javascript
|
|
2482
|
+
// Internal format
|
|
2483
|
+
{
|
|
2484
|
+
sessionId: "abc-123",
|
|
2485
|
+
pagesViewed: 3,
|
|
2486
|
+
isBounce: false,
|
|
2487
|
+
startTime: "2024-12-15T10:30:00.000Z"
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// Backend format
|
|
2491
|
+
{
|
|
2492
|
+
id: "abc-123", // Same as session_id (PostgreSQL auto-generates)
|
|
2493
|
+
session_id: "abc-123",
|
|
2494
|
+
pages_viewed: 3,
|
|
2495
|
+
is_bounce: false,
|
|
2496
|
+
start_time: "2024-12-15T10:30:00.000Z",
|
|
2497
|
+
// ... all other fields in snake_case
|
|
2498
|
+
interactions: {
|
|
2499
|
+
...interactions,
|
|
2500
|
+
chronological: [...] // Added chronological interactions
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
```
|
|
2504
|
+
|
|
2505
|
+
**When to Use**:
|
|
2506
|
+
- Before sending data to backend API
|
|
2507
|
+
- When preparing session data for storage
|
|
2508
|
+
- For data export/analytics
|
|
2509
|
+
|
|
2510
|
+
**Example**:
|
|
2511
|
+
```javascript
|
|
2512
|
+
const backendData = DataTransformer.toBackendFormat();
|
|
2513
|
+
// Returns: Transformed data ready for backend
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
---
|
|
2517
|
+
|
|
2518
|
+
#### `DataTransformer.toOverviewFormat(data)`
|
|
2519
|
+
**Purpose**: Transform for overview API (simplified format).
|
|
2520
|
+
|
|
2521
|
+
**Logic**:
|
|
2522
|
+
- Creates simplified format for overview/analytics endpoint
|
|
2523
|
+
- Includes essential fields only
|
|
2524
|
+
- Formats data for overview payload
|
|
2525
|
+
|
|
2526
|
+
**Parameters**: `data` (optional) - Session data object. Defaults to `sessionData`.
|
|
2527
|
+
|
|
2528
|
+
**Returns**: Transformed object for overview API
|
|
2529
|
+
|
|
2530
|
+
**When to Use**:
|
|
2531
|
+
- When sending data to overview/analytics endpoint
|
|
2532
|
+
- For summary/aggregated data
|
|
2533
|
+
|
|
2534
|
+
**Example**:
|
|
2535
|
+
```javascript
|
|
2536
|
+
const overviewData = DataTransformer.toOverviewFormat();
|
|
2537
|
+
// Returns: Simplified format for overview API
|
|
2538
|
+
```
|
|
2539
|
+
|
|
2540
|
+
---
|
|
2541
|
+
|
|
2542
|
+
### Reusability
|
|
2543
|
+
|
|
2544
|
+
✅ **DataTransformer.normalizeShape()**: Reusable for normalizing any session data.
|
|
2545
|
+
|
|
2546
|
+
✅ **DataTransformer.toBackendFormat()**: Reusable for transforming data to backend format.
|
|
2547
|
+
|
|
2548
|
+
✅ **DataTransformer.toOverviewFormat()**: Reusable for overview/analytics data.
|
|
2549
|
+
|
|
2550
|
+
---
|
|
2551
|
+
|
|
2552
|
+
## Section 13: API Communication
|
|
2553
|
+
|
|
2554
|
+
### Purpose
|
|
2555
|
+
Unified API client for all backend communication. Handles sending session data, overview data, custom events, and UTM events with proper error handling and retry logic.
|
|
2556
|
+
|
|
2557
|
+
### Why It Matters
|
|
2558
|
+
- Single place to handle all API communication
|
|
2559
|
+
- Consistent error handling
|
|
2560
|
+
- Retry logic for reliability
|
|
2561
|
+
- Proper use of sendBeacon vs fetch
|
|
2562
|
+
- Debouncing to reduce network noise
|
|
2563
|
+
|
|
2564
|
+
### APIClient Object
|
|
2565
|
+
|
|
2566
|
+
#### `APIClient.determineTrafficSource()`
|
|
2567
|
+
**Purpose**: Determine traffic source for overview data.
|
|
2568
|
+
|
|
2569
|
+
**Logic**:
|
|
2570
|
+
1. Checks UTM source (highest priority)
|
|
2571
|
+
2. Checks external referrer
|
|
2572
|
+
3. Defaults to "direct"
|
|
2573
|
+
|
|
2574
|
+
**Returns**: Traffic source string
|
|
2575
|
+
|
|
2576
|
+
**When to Use**:
|
|
2577
|
+
- When building overview payload
|
|
2578
|
+
- For traffic source analysis
|
|
2579
|
+
|
|
2580
|
+
**Example**:
|
|
2581
|
+
```javascript
|
|
2582
|
+
const source = APIClient.determineTrafficSource();
|
|
2583
|
+
// Returns: "google", "example.com", or "direct"
|
|
2584
|
+
```
|
|
2585
|
+
|
|
2586
|
+
---
|
|
2587
|
+
|
|
2588
|
+
#### `APIClient.send(endpoint, data, options)`
|
|
2589
|
+
**Purpose**: Send data to API endpoint (unified method).
|
|
2590
|
+
|
|
2591
|
+
**Logic**:
|
|
2592
|
+
1. Transforms data using DataTransformer if needed
|
|
2593
|
+
2. Uses sendBeacon if `useBeacon` is true (for beforeunload)
|
|
2594
|
+
3. Uses fetch with keepalive for regular sends
|
|
2595
|
+
4. Handles errors gracefully
|
|
2596
|
+
5. Retries on failure (if retries > 0)
|
|
2597
|
+
|
|
2598
|
+
**Parameters**:
|
|
2599
|
+
- `endpoint` - API endpoint URL
|
|
2600
|
+
- `data` - Data to send
|
|
2601
|
+
- `options` (optional) - Configuration object:
|
|
2602
|
+
- `useBeacon` (default: false) - Use sendBeacon instead of fetch
|
|
2603
|
+
- `retries` (default: 0) - Number of retry attempts
|
|
2604
|
+
- `timeout` (default: 5000) - Request timeout in milliseconds
|
|
2605
|
+
- `transform` (default: true) - Transform data before sending
|
|
2606
|
+
|
|
2607
|
+
**Returns**: Promise that resolves on success
|
|
2608
|
+
|
|
2609
|
+
**When to Use**:
|
|
2610
|
+
- For all API communication
|
|
2611
|
+
- When sending any data to backend
|
|
2612
|
+
|
|
2613
|
+
**Example**:
|
|
2614
|
+
```javascript
|
|
2615
|
+
// Regular send with retries
|
|
2616
|
+
await APIClient.send(CONFIG.API.TRACK, payload, {
|
|
2617
|
+
retries: 2,
|
|
2618
|
+
timeout: 5000
|
|
2619
|
+
});
|
|
2620
|
+
|
|
2621
|
+
// SendBeacon for beforeunload
|
|
2622
|
+
await APIClient.send(CONFIG.API.OVERVIEW, payload, {
|
|
2623
|
+
useBeacon: true,
|
|
2624
|
+
retries: 0
|
|
2625
|
+
});
|
|
2626
|
+
```
|
|
2627
|
+
|
|
2628
|
+
---
|
|
2629
|
+
|
|
2630
|
+
#### `APIClient.sendSessionData(data)`
|
|
2631
|
+
**Purpose**: Send session data to TRACK endpoint.
|
|
2632
|
+
|
|
2633
|
+
**Logic**:
|
|
2634
|
+
1. Ensures session ID exists
|
|
2635
|
+
2. Transforms data using DataTransformer.toBackendFormat()
|
|
2636
|
+
3. Sends to TRACK endpoint
|
|
2637
|
+
4. Retries on failure (2 attempts)
|
|
2638
|
+
|
|
2639
|
+
**Parameters**: `data` (optional) - Session data object. Defaults to `sessionData`.
|
|
2640
|
+
|
|
2641
|
+
**When to Use**:
|
|
2642
|
+
- On session initialization
|
|
2643
|
+
- Periodically during session (via interval)
|
|
2644
|
+
- On session updates
|
|
2645
|
+
|
|
2646
|
+
**Example**:
|
|
2647
|
+
```javascript
|
|
2648
|
+
await APIClient.sendSessionData();
|
|
2649
|
+
// Sends current session data to TRACK endpoint
|
|
2650
|
+
```
|
|
2651
|
+
|
|
2652
|
+
---
|
|
2653
|
+
|
|
2654
|
+
#### `APIClient.sendOverviewData(useBeacon)`
|
|
2655
|
+
**Purpose**: Send overview data to OVERVIEW endpoint.
|
|
2656
|
+
|
|
2657
|
+
**Logic**:
|
|
2658
|
+
1. Recalculates session duration
|
|
2659
|
+
2. Updates bounce status
|
|
2660
|
+
3. Builds overview payload with user journey
|
|
2661
|
+
4. Determines traffic source
|
|
2662
|
+
5. Sends to OVERVIEW endpoint
|
|
2663
|
+
|
|
2664
|
+
**Parameters**: `useBeacon` (optional, default: false) - Use sendBeacon instead of fetch
|
|
2665
|
+
|
|
2666
|
+
**Overview Payload Structure**:
|
|
2667
|
+
```javascript
|
|
2668
|
+
{
|
|
2669
|
+
siteId: SITE_ID,
|
|
2670
|
+
userId: userId,
|
|
2671
|
+
sessionId: sessionId,
|
|
2672
|
+
sessionStartDate: "2024-12-15",
|
|
2673
|
+
totalSessions: 1,
|
|
2674
|
+
bounceSessions: 0,
|
|
2675
|
+
totalPagesVisited: 3,
|
|
2676
|
+
isBounce: false,
|
|
2677
|
+
userJourney: {
|
|
2678
|
+
[userId]: {
|
|
2679
|
+
currentSessionId: sessionId,
|
|
2680
|
+
country: "US",
|
|
2681
|
+
isWeb3User: true,
|
|
2682
|
+
walletAddress: "0x...",
|
|
2683
|
+
walletConnected: true,
|
|
2684
|
+
visitTimes: [Date],
|
|
2685
|
+
walletConnectedInVisit: [boolean],
|
|
2686
|
+
visitDuration: [number], // Seconds
|
|
2687
|
+
sessionPaths: [[paths]], // Array of arrays
|
|
2688
|
+
trafficSource: "google"
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
```
|
|
2693
|
+
|
|
2694
|
+
**When to Use**:
|
|
2695
|
+
- At session end
|
|
2696
|
+
- Periodically (debounced)
|
|
2697
|
+
- On beforeunload (with useBeacon: true)
|
|
2698
|
+
|
|
2699
|
+
**Example**:
|
|
2700
|
+
```javascript
|
|
2701
|
+
// Regular send
|
|
2702
|
+
await APIClient.sendOverviewData();
|
|
2703
|
+
|
|
2704
|
+
// Beforeunload send
|
|
2705
|
+
await APIClient.sendOverviewData(true);
|
|
2706
|
+
```
|
|
2707
|
+
|
|
2708
|
+
---
|
|
2709
|
+
|
|
2710
|
+
#### `APIClient.scheduleSendOverview(debounceMs, useBeacon)`
|
|
2711
|
+
**Purpose**: Schedule overview data send (debounced).
|
|
2712
|
+
|
|
2713
|
+
**Logic**:
|
|
2714
|
+
- Clears existing timeout if any
|
|
2715
|
+
- Sets new timeout to send overview data
|
|
2716
|
+
- Debounces to reduce network noise
|
|
2717
|
+
|
|
2718
|
+
**Parameters**:
|
|
2719
|
+
- `debounceMs` (optional, default: 10000) - Debounce time in milliseconds
|
|
2720
|
+
- `useBeacon` (optional, default: false) - Use sendBeacon
|
|
2721
|
+
|
|
2722
|
+
**When to Use**:
|
|
2723
|
+
- During session tracking interval
|
|
2724
|
+
- To debounce overview sends
|
|
2725
|
+
|
|
2726
|
+
**Example**:
|
|
2727
|
+
```javascript
|
|
2728
|
+
APIClient.scheduleSendOverview(10000, false);
|
|
2729
|
+
// Schedules overview send in 10 seconds
|
|
2730
|
+
```
|
|
2731
|
+
|
|
2732
|
+
---
|
|
2733
|
+
|
|
2734
|
+
#### `APIClient.sendCustomEvent(eventKey, eventName, eventType, parameters)`
|
|
2735
|
+
**Purpose**: Send custom event to CUSTOM_EVENTS endpoint.
|
|
2736
|
+
|
|
2737
|
+
**Logic**:
|
|
2738
|
+
1. Validates eventKey and eventName
|
|
2739
|
+
2. Gets location data (non-blocking)
|
|
2740
|
+
3. Updates session activity
|
|
2741
|
+
4. Updates wallet info (non-blocking)
|
|
2742
|
+
5. Converts parameters to array format
|
|
2743
|
+
6. Builds custom event payload
|
|
2744
|
+
7. Sends to CUSTOM_EVENTS endpoint
|
|
2745
|
+
|
|
2746
|
+
**Parameters**:
|
|
2747
|
+
- `eventKey` - Event key/identifier
|
|
2748
|
+
- `eventName` - Event name
|
|
2749
|
+
- `eventType` - Event type
|
|
2750
|
+
- `parameters` (optional) - Event parameters (object or array)
|
|
2751
|
+
|
|
2752
|
+
**Custom Event Payload Structure**:
|
|
2753
|
+
```javascript
|
|
2754
|
+
{
|
|
2755
|
+
siteId: SITE_ID,
|
|
2756
|
+
eventKey: "evt_123",
|
|
2757
|
+
eventName: "Button Click",
|
|
2758
|
+
eventType: "click",
|
|
2759
|
+
eventDescription: "",
|
|
2760
|
+
parameters: [{ name: "buttonId", value: "submit", paramType: "string" }],
|
|
2761
|
+
pagePath: "/home",
|
|
2762
|
+
sessionIds: [sessionId],
|
|
2763
|
+
userIds: [userId],
|
|
2764
|
+
datetimes: ["2024-12-15T10:30:00.000Z"],
|
|
2765
|
+
geography: [{ city, country, region, timezone }],
|
|
2766
|
+
source: ["google"],
|
|
2767
|
+
web3user: [true],
|
|
2768
|
+
walletAddress: ["0x..."]
|
|
2769
|
+
}
|
|
2770
|
+
```
|
|
2771
|
+
|
|
2772
|
+
**When to Use**:
|
|
2773
|
+
- When tracking custom events
|
|
2774
|
+
- For user-defined events
|
|
2775
|
+
|
|
2776
|
+
**Example**:
|
|
2777
|
+
```javascript
|
|
2778
|
+
await APIClient.sendCustomEvent(
|
|
2779
|
+
"evt_123",
|
|
2780
|
+
"Button Click",
|
|
2781
|
+
"click",
|
|
2782
|
+
{ buttonId: "submit", page: "/home" }
|
|
2783
|
+
);
|
|
2784
|
+
```
|
|
2785
|
+
|
|
2786
|
+
---
|
|
2787
|
+
|
|
2788
|
+
#### `APIClient.sendUTMEvent()`
|
|
2789
|
+
**Purpose**: Send UTM event to UTM_EVENTS endpoint.
|
|
2790
|
+
|
|
2791
|
+
**Logic**:
|
|
2792
|
+
1. Extracts UTM parameters
|
|
2793
|
+
2. Only sends if UTM parameters exist
|
|
2794
|
+
3. Builds UTM event payload
|
|
2795
|
+
4. Sends to UTM_EVENTS endpoint
|
|
2796
|
+
|
|
2797
|
+
**When to Use**:
|
|
2798
|
+
- On page load with UTM parameters
|
|
2799
|
+
- For UTM campaign tracking
|
|
2800
|
+
|
|
2801
|
+
**Example**:
|
|
2802
|
+
```javascript
|
|
2803
|
+
await APIClient.sendUTMEvent();
|
|
2804
|
+
// Sends UTM data if UTM parameters exist
|
|
2805
|
+
```
|
|
2806
|
+
|
|
2807
|
+
---
|
|
2808
|
+
|
|
2809
|
+
### Error Handling
|
|
2810
|
+
|
|
2811
|
+
**Retry Logic**:
|
|
2812
|
+
- Automatic retries on failure (configurable)
|
|
2813
|
+
- Exponential backoff (1 second delay)
|
|
2814
|
+
- Maximum retry attempts configurable
|
|
2815
|
+
|
|
2816
|
+
**sendBeacon vs fetch**:
|
|
2817
|
+
- **sendBeacon**: Used for beforeunload (fire-and-forget, no response)
|
|
2818
|
+
- **fetch**: Used for regular sends (with keepalive, can retry)
|
|
2819
|
+
|
|
2820
|
+
**Error Logging**:
|
|
2821
|
+
- Comprehensive error logging
|
|
2822
|
+
- Network error detection
|
|
2823
|
+
- CORS error detection
|
|
2824
|
+
- Ad blocker detection
|
|
2825
|
+
|
|
2826
|
+
---
|
|
2827
|
+
|
|
2828
|
+
### Reusability
|
|
2829
|
+
|
|
2830
|
+
✅ **APIClient.send()**: Reusable for sending data to any endpoint.
|
|
2831
|
+
|
|
2832
|
+
✅ **APIClient.sendSessionData()**: Reusable for session tracking.
|
|
2833
|
+
|
|
2834
|
+
✅ **APIClient.sendOverviewData()**: Reusable for overview/analytics.
|
|
2835
|
+
|
|
2836
|
+
✅ **APIClient.sendCustomEvent()**: Reusable for custom event tracking.
|
|
2837
|
+
|
|
2838
|
+
---
|
|
2839
|
+
|
|
2840
|
+
## Section 14: Event Trackers
|
|
2841
|
+
|
|
2842
|
+
### Purpose
|
|
2843
|
+
Individual tracking modules for different event types. All trackers listen to DOM events and use `InteractionManager.add()` to store interactions.
|
|
2844
|
+
|
|
2845
|
+
### Why It Matters
|
|
2846
|
+
- Modular tracking (can enable/disable individual trackers)
|
|
2847
|
+
- Single source of truth for interaction storage
|
|
2848
|
+
- Easy to add new trackers
|
|
2849
|
+
- Consistent interaction data structure
|
|
2850
|
+
- Error handling prevents tracker failures from breaking SDK
|
|
2851
|
+
|
|
2852
|
+
### EventTrackers Object
|
|
2853
|
+
|
|
2854
|
+
All trackers are methods of the `EventTrackers` object. Each tracker:
|
|
2855
|
+
- Listens to specific DOM events
|
|
2856
|
+
- Extracts relevant data
|
|
2857
|
+
- Uses `InteractionManager.add()` to store interactions
|
|
2858
|
+
- Handles errors gracefully
|
|
2859
|
+
|
|
2860
|
+
---
|
|
2861
|
+
|
|
2862
|
+
#### `EventTrackers.startClickTracking()`
|
|
2863
|
+
**Purpose**: Track click events on interactive elements.
|
|
2864
|
+
|
|
2865
|
+
**Logic**:
|
|
2866
|
+
1. Listens to click events on document body
|
|
2867
|
+
2. Finds closest interactive element (a, button, [role="button"], [data-cryptique-id])
|
|
2868
|
+
3. Extracts element data (tagName, id, className, href, etc.)
|
|
2869
|
+
4. Captures click coordinates
|
|
2870
|
+
5. Stores interaction using `InteractionManager.add('clicks', ...)`
|
|
2871
|
+
|
|
2872
|
+
**Tracks**:
|
|
2873
|
+
- Click coordinates (x, y)
|
|
2874
|
+
- Element details (tagName, id, className, href, aria-label, role)
|
|
2875
|
+
- Path where click occurred
|
|
2876
|
+
|
|
2877
|
+
**When to Use**: Automatically called during initialization.
|
|
2878
|
+
|
|
2879
|
+
**Example**:
|
|
2880
|
+
```javascript
|
|
2881
|
+
EventTrackers.startClickTracking();
|
|
2882
|
+
// Now tracking all clicks on interactive elements
|
|
2883
|
+
```
|
|
2884
|
+
|
|
2885
|
+
---
|
|
2886
|
+
|
|
2887
|
+
#### `EventTrackers.startFormTracking()`
|
|
2888
|
+
**Purpose**: Track form interactions (submissions, field changes, focus/blur, validation errors).
|
|
2889
|
+
|
|
2890
|
+
**Logic**:
|
|
2891
|
+
1. Listens to form submit events
|
|
2892
|
+
2. Listens to input/change events on form fields
|
|
2893
|
+
3. Listens to focus/blur events on form fields
|
|
2894
|
+
4. Listens to invalid events for validation errors
|
|
2895
|
+
5. Stores interactions using `InteractionManager.add()`
|
|
2896
|
+
|
|
2897
|
+
**Tracks**:
|
|
2898
|
+
- Form submissions (formId, formAction, formMethod, fieldCount)
|
|
2899
|
+
- Field changes (fieldType, fieldName, fieldId, valueLength)
|
|
2900
|
+
- Focus/blur events (fieldType, fieldName, fieldId, formId)
|
|
2901
|
+
- Validation errors (fieldType, fieldName, validationMessage)
|
|
2902
|
+
|
|
2903
|
+
**When to Use**: Automatically called during initialization.
|
|
2904
|
+
|
|
2905
|
+
**Example**:
|
|
2906
|
+
```javascript
|
|
2907
|
+
EventTrackers.startFormTracking();
|
|
2908
|
+
// Now tracking all form interactions
|
|
2909
|
+
```
|
|
2910
|
+
|
|
2911
|
+
---
|
|
2912
|
+
|
|
2913
|
+
#### `EventTrackers.startMediaTracking()`
|
|
2914
|
+
**Purpose**: Track media interactions (video play/pause, image clicks/loads).
|
|
2915
|
+
|
|
2916
|
+
**Logic**:
|
|
2917
|
+
1. Listens to play/pause events on video elements
|
|
2918
|
+
2. Listens to click events on image elements
|
|
2919
|
+
3. Tracks existing images on page load
|
|
2920
|
+
4. Stores interactions using `InteractionManager.add('mediaInteractions', ...)`
|
|
2921
|
+
|
|
2922
|
+
**Tracks**:
|
|
2923
|
+
- Video play/pause (mediaType, mediaId, mediaSrc, currentTime)
|
|
2924
|
+
- Image clicks (imageId, imageSrc, imageAlt, imageWidth, imageHeight)
|
|
2925
|
+
- Image loads (imageWidth, imageHeight)
|
|
2926
|
+
|
|
2927
|
+
**When to Use**: Automatically called during initialization.
|
|
2928
|
+
|
|
2929
|
+
**Example**:
|
|
2930
|
+
```javascript
|
|
2931
|
+
EventTrackers.startMediaTracking();
|
|
2932
|
+
// Now tracking all media interactions
|
|
2933
|
+
```
|
|
2934
|
+
|
|
2935
|
+
---
|
|
2936
|
+
|
|
2937
|
+
#### `EventTrackers.startScrollTracking()`
|
|
2938
|
+
**Purpose**: Track scroll events with debouncing.
|
|
2939
|
+
|
|
2940
|
+
**Logic**:
|
|
2941
|
+
1. Listens to scroll events
|
|
2942
|
+
2. Debounces scroll events (150ms)
|
|
2943
|
+
3. Calculates scroll position and percentage
|
|
2944
|
+
4. Only tracks if scroll position changed significantly (>50px)
|
|
2945
|
+
5. Stores interactions using `InteractionManager.add('scrollEvents', ...)`
|
|
2946
|
+
|
|
2947
|
+
**Tracks**:
|
|
2948
|
+
- Scroll position (scrollY, scrollPercent)
|
|
2949
|
+
- Window and document dimensions
|
|
2950
|
+
- Path where scroll occurred
|
|
2951
|
+
|
|
2952
|
+
**When to Use**: Automatically called during initialization.
|
|
2953
|
+
|
|
2954
|
+
**Example**:
|
|
2955
|
+
```javascript
|
|
2956
|
+
EventTrackers.startScrollTracking();
|
|
2957
|
+
// Now tracking scroll events (debounced)
|
|
2958
|
+
```
|
|
2959
|
+
|
|
2960
|
+
---
|
|
2961
|
+
|
|
2962
|
+
#### `EventTrackers.startHoverTracking()`
|
|
2963
|
+
**Purpose**: Track hover events on interactive elements.
|
|
2964
|
+
|
|
2965
|
+
**Logic**:
|
|
2966
|
+
1. Listens to mouseenter events
|
|
2967
|
+
2. Only tracks hovers longer than 200ms
|
|
2968
|
+
3. Tracks hover duration
|
|
2969
|
+
4. Updates hover duration on mouseleave
|
|
2970
|
+
5. Stores interactions using `InteractionManager.add('hoverEvents', ...)`
|
|
2971
|
+
|
|
2972
|
+
**Tracks**:
|
|
2973
|
+
- Element details (elementType, elementId, elementClass)
|
|
2974
|
+
- Hover duration (updated on mouseleave)
|
|
2975
|
+
- Path where hover occurred
|
|
2976
|
+
|
|
2977
|
+
**When to Use**: Automatically called during initialization.
|
|
2978
|
+
|
|
2979
|
+
**Example**:
|
|
2980
|
+
```javascript
|
|
2981
|
+
EventTrackers.startHoverTracking();
|
|
2982
|
+
// Now tracking hover events (200ms threshold)
|
|
2983
|
+
```
|
|
2984
|
+
|
|
2985
|
+
---
|
|
2986
|
+
|
|
2987
|
+
#### `EventTrackers.startKeyboardTracking()`
|
|
2988
|
+
**Purpose**: Track keyboard shortcuts and special keys.
|
|
2989
|
+
|
|
2990
|
+
**Logic**:
|
|
2991
|
+
1. Listens to keydown events
|
|
2992
|
+
2. Tracks significant keys (Enter, Escape, Tab, Arrows, F-keys, etc.)
|
|
2993
|
+
3. Tracks modifier key combinations (Ctrl, Alt, Shift, Meta)
|
|
2994
|
+
4. Stores interactions using `InteractionManager.add('keyboardEvents', ...)`
|
|
2995
|
+
|
|
2996
|
+
**Tracks**:
|
|
2997
|
+
- Key pressed (key, keyCode)
|
|
2998
|
+
- Modifier keys (ctrlKey, altKey, shiftKey, metaKey)
|
|
2999
|
+
- Target element (tagName, id, className)
|
|
3000
|
+
|
|
3001
|
+
**When to Use**: Automatically called during initialization.
|
|
3002
|
+
|
|
3003
|
+
**Example**:
|
|
3004
|
+
```javascript
|
|
3005
|
+
EventTrackers.startKeyboardTracking();
|
|
3006
|
+
// Now tracking keyboard shortcuts
|
|
3007
|
+
```
|
|
3008
|
+
|
|
3009
|
+
---
|
|
3010
|
+
|
|
3011
|
+
#### `EventTrackers.startCopyPasteTracking()`
|
|
3012
|
+
**Purpose**: Track copy and paste actions.
|
|
3013
|
+
|
|
3014
|
+
**Logic**:
|
|
3015
|
+
1. Listens to copy events
|
|
3016
|
+
2. Listens to paste events
|
|
3017
|
+
3. Extracts selected text (copy) or target element (paste)
|
|
3018
|
+
4. Stores interactions using `InteractionManager.add('copyPasteEvents', ...)`
|
|
3019
|
+
|
|
3020
|
+
**Tracks**:
|
|
3021
|
+
- Copy actions (selectedText, target element)
|
|
3022
|
+
- Paste actions (target element)
|
|
3023
|
+
|
|
3024
|
+
**When to Use**: Automatically called during initialization.
|
|
3025
|
+
|
|
3026
|
+
**Example**:
|
|
3027
|
+
```javascript
|
|
3028
|
+
EventTrackers.startCopyPasteTracking();
|
|
3029
|
+
// Now tracking copy/paste actions
|
|
3030
|
+
```
|
|
3031
|
+
|
|
3032
|
+
---
|
|
3033
|
+
|
|
3034
|
+
#### `EventTrackers.startContextMenuTracking()`
|
|
3035
|
+
**Purpose**: Track right-click context menu usage.
|
|
3036
|
+
|
|
3037
|
+
**Logic**:
|
|
3038
|
+
1. Listens to contextmenu events
|
|
3039
|
+
2. Captures click coordinates
|
|
3040
|
+
3. Extracts target element details
|
|
3041
|
+
4. Stores interactions using `InteractionManager.add('contextMenuEvents', ...)`
|
|
3042
|
+
|
|
3043
|
+
**Tracks**:
|
|
3044
|
+
- Target element (tagName, id, className)
|
|
3045
|
+
- Click coordinates (x, y)
|
|
3046
|
+
|
|
3047
|
+
**When to Use**: Automatically called during initialization.
|
|
3048
|
+
|
|
3049
|
+
**Example**:
|
|
3050
|
+
```javascript
|
|
3051
|
+
EventTrackers.startContextMenuTracking();
|
|
3052
|
+
// Now tracking context menu usage
|
|
3053
|
+
```
|
|
3054
|
+
|
|
3055
|
+
---
|
|
3056
|
+
|
|
3057
|
+
#### `EventTrackers.startDragDropTracking()`
|
|
3058
|
+
**Purpose**: Track drag and drop operations.
|
|
3059
|
+
|
|
3060
|
+
**Logic**:
|
|
3061
|
+
1. Listens to dragstart events
|
|
3062
|
+
2. Listens to drop events
|
|
3063
|
+
3. Extracts dataTransfer information
|
|
3064
|
+
4. Stores interactions using `InteractionManager.add('dragDropEvents', ...)`
|
|
3065
|
+
|
|
3066
|
+
**Tracks**:
|
|
3067
|
+
- Drag start (target element, dataTransfer types, files count)
|
|
3068
|
+
- Drop action (target element, dataTransfer types, files count)
|
|
3069
|
+
|
|
3070
|
+
**When to Use**: Automatically called during initialization.
|
|
3071
|
+
|
|
3072
|
+
**Example**:
|
|
3073
|
+
```javascript
|
|
3074
|
+
EventTrackers.startDragDropTracking();
|
|
3075
|
+
// Now tracking drag/drop operations
|
|
3076
|
+
```
|
|
3077
|
+
|
|
3078
|
+
---
|
|
3079
|
+
|
|
3080
|
+
#### `EventTrackers.startTouchTracking()`
|
|
3081
|
+
**Purpose**: Track touch events for mobile devices.
|
|
3082
|
+
|
|
3083
|
+
**Logic**:
|
|
3084
|
+
1. Listens to touchstart events
|
|
3085
|
+
2. Listens to touchmove events
|
|
3086
|
+
3. Listens to touchend events
|
|
3087
|
+
4. Extracts touch coordinates and count
|
|
3088
|
+
5. Stores interactions using `InteractionManager.add('touchEvents', ...)`
|
|
3089
|
+
|
|
3090
|
+
**Tracks**:
|
|
3091
|
+
- Touch start (touches count, coordinates, target element)
|
|
3092
|
+
- Touch move (touches count)
|
|
3093
|
+
- Touch end (touches count)
|
|
3094
|
+
|
|
3095
|
+
**When to Use**: Automatically called during initialization.
|
|
3096
|
+
|
|
3097
|
+
**Example**:
|
|
3098
|
+
```javascript
|
|
3099
|
+
EventTrackers.startTouchTracking();
|
|
3100
|
+
// Now tracking touch events
|
|
3101
|
+
```
|
|
3102
|
+
|
|
3103
|
+
---
|
|
3104
|
+
|
|
3105
|
+
#### `EventTrackers.startWindowTracking()`
|
|
3106
|
+
**Purpose**: Track window resize, orientation changes, focus/blur.
|
|
3107
|
+
|
|
3108
|
+
**Logic**:
|
|
3109
|
+
1. Listens to resize events (debounced 250ms)
|
|
3110
|
+
2. Listens to orientationchange events
|
|
3111
|
+
3. Listens to window focus/blur events
|
|
3112
|
+
4. Extracts window dimensions and focused element
|
|
3113
|
+
5. Stores interactions using `InteractionManager.add('windowEvents', ...)`
|
|
3114
|
+
|
|
3115
|
+
**Tracks**:
|
|
3116
|
+
- Window resize (dimensions, focused element)
|
|
3117
|
+
- Orientation change (orientation, focused element)
|
|
3118
|
+
- Window focus/blur (focused element)
|
|
3119
|
+
|
|
3120
|
+
**When to Use**: Automatically called during initialization.
|
|
3121
|
+
|
|
3122
|
+
**Example**:
|
|
3123
|
+
```javascript
|
|
3124
|
+
EventTrackers.startWindowTracking();
|
|
3125
|
+
// Now tracking window events
|
|
3126
|
+
```
|
|
3127
|
+
|
|
3128
|
+
---
|
|
3129
|
+
|
|
3130
|
+
#### `EventTrackers.startPerformanceTracking()`
|
|
3131
|
+
**Purpose**: Track page load performance metrics.
|
|
3132
|
+
|
|
3133
|
+
**Logic**:
|
|
3134
|
+
1. Listens to window load event
|
|
3135
|
+
2. Waits 1 second for all resources to load
|
|
3136
|
+
3. Extracts performance metrics (loadTime, domContentLoaded, firstPaint)
|
|
3137
|
+
4. Stores interactions using `InteractionManager.add('performanceEvents', ...)`
|
|
3138
|
+
|
|
3139
|
+
**Tracks**:
|
|
3140
|
+
- Load time (navigationStart to loadEventEnd)
|
|
3141
|
+
- DOM content loaded time
|
|
3142
|
+
- First paint time
|
|
3143
|
+
- First contentful paint time
|
|
3144
|
+
|
|
3145
|
+
**When to Use**: Automatically called during initialization.
|
|
3146
|
+
|
|
3147
|
+
**Example**:
|
|
3148
|
+
```javascript
|
|
3149
|
+
EventTrackers.startPerformanceTracking();
|
|
3150
|
+
// Now tracking page load performance
|
|
3151
|
+
```
|
|
3152
|
+
|
|
3153
|
+
---
|
|
3154
|
+
|
|
3155
|
+
#### `EventTrackers.startErrorTracking()`
|
|
3156
|
+
**Purpose**: Track JavaScript errors and unhandled promise rejections.
|
|
3157
|
+
|
|
3158
|
+
**Logic**:
|
|
3159
|
+
1. Listens to window error events
|
|
3160
|
+
2. Listens to unhandledrejection events
|
|
3161
|
+
3. Extracts error details (message, filename, line, column, stack)
|
|
3162
|
+
4. Stores interactions using `InteractionManager.add('errorEvents', ...)`
|
|
3163
|
+
|
|
3164
|
+
**Tracks**:
|
|
3165
|
+
- JavaScript errors (message, filename, lineno, colno, stack, errorElement)
|
|
3166
|
+
- Unhandled promise rejections (message, error)
|
|
3167
|
+
|
|
3168
|
+
**When to Use**: Automatically called during initialization.
|
|
3169
|
+
|
|
3170
|
+
**Example**:
|
|
3171
|
+
```javascript
|
|
3172
|
+
EventTrackers.startErrorTracking();
|
|
3173
|
+
// Now tracking JavaScript errors
|
|
3174
|
+
```
|
|
3175
|
+
|
|
3176
|
+
---
|
|
3177
|
+
|
|
3178
|
+
#### `EventTrackers.startNetworkTracking()`
|
|
3179
|
+
**Purpose**: Track network errors (fetch and XMLHttpRequest).
|
|
3180
|
+
|
|
3181
|
+
**Logic**:
|
|
3182
|
+
1. Wraps window.fetch to intercept requests
|
|
3183
|
+
2. Wraps XMLHttpRequest.open and send
|
|
3184
|
+
3. Only tracks error status codes (4xx, 5xx) and network errors
|
|
3185
|
+
4. Extracts error details (url, method, status, error message, duration)
|
|
3186
|
+
5. Stores interactions using `InteractionManager.add('networkEvents', ...)`
|
|
3187
|
+
|
|
3188
|
+
**Tracks**:
|
|
3189
|
+
- API errors (url, method, status, error, duration)
|
|
3190
|
+
- Network errors (url, method, status: 0, error, duration)
|
|
3191
|
+
|
|
3192
|
+
**When to Use**: Automatically called during initialization.
|
|
3193
|
+
|
|
3194
|
+
**Example**:
|
|
3195
|
+
```javascript
|
|
3196
|
+
EventTrackers.startNetworkTracking();
|
|
3197
|
+
// Now tracking network errors (4xx, 5xx, network failures)
|
|
3198
|
+
```
|
|
3199
|
+
|
|
3200
|
+
---
|
|
3201
|
+
|
|
3202
|
+
#### `EventTrackers.startAdvancedFormTracking()`
|
|
3203
|
+
**Purpose**: Track form completion rates and abandonment.
|
|
3204
|
+
|
|
3205
|
+
**Logic**:
|
|
3206
|
+
1. Tracks form start time on first field focus
|
|
3207
|
+
2. Tracks form completion time on submit
|
|
3208
|
+
3. Calculates completion time
|
|
3209
|
+
4. Stores interactions using `InteractionManager.add('formAnalytics', ...)`
|
|
3210
|
+
|
|
3211
|
+
**Tracks**:
|
|
3212
|
+
- Form completion (formId, completionTime, fieldCount)
|
|
3213
|
+
|
|
3214
|
+
**When to Use**: Automatically called during initialization.
|
|
3215
|
+
|
|
3216
|
+
**Example**:
|
|
3217
|
+
```javascript
|
|
3218
|
+
EventTrackers.startAdvancedFormTracking();
|
|
3219
|
+
// Now tracking form analytics
|
|
3220
|
+
```
|
|
3221
|
+
|
|
3222
|
+
---
|
|
3223
|
+
|
|
3224
|
+
#### `EventTrackers.initialize()`
|
|
3225
|
+
**Purpose**: Initialize all event trackers.
|
|
3226
|
+
|
|
3227
|
+
**Logic**:
|
|
3228
|
+
- Calls all tracker start methods in sequence
|
|
3229
|
+
- All trackers are started automatically
|
|
3230
|
+
|
|
3231
|
+
**When to Use**: Called during SDK initialization.
|
|
3232
|
+
|
|
3233
|
+
**Example**:
|
|
3234
|
+
```javascript
|
|
3235
|
+
EventTrackers.initialize();
|
|
3236
|
+
// Starts all event trackers
|
|
3237
|
+
```
|
|
3238
|
+
|
|
3239
|
+
---
|
|
3240
|
+
|
|
3241
|
+
### Tracker Categories
|
|
3242
|
+
|
|
3243
|
+
Each tracker stores interactions in specific categories:
|
|
3244
|
+
- `clicks` - Click events
|
|
3245
|
+
- `formSubmissions` - Form submissions
|
|
3246
|
+
- `fieldChanges` - Form field changes
|
|
3247
|
+
- `focusEvents` - Focus/blur events
|
|
3248
|
+
- `validationErrors` - Form validation errors
|
|
3249
|
+
- `mediaInteractions` - Video/audio/image interactions
|
|
3250
|
+
- `scrollEvents` - Scroll events
|
|
3251
|
+
- `hoverEvents` - Hover events
|
|
3252
|
+
- `keyboardEvents` - Keyboard shortcuts
|
|
3253
|
+
- `copyPasteEvents` - Copy/paste actions
|
|
3254
|
+
- `contextMenuEvents` - Context menu usage
|
|
3255
|
+
- `dragDropEvents` - Drag/drop operations
|
|
3256
|
+
- `touchEvents` - Touch events
|
|
3257
|
+
- `windowEvents` - Window events (resize, orientation, focus/blur)
|
|
3258
|
+
- `performanceEvents` - Performance metrics
|
|
3259
|
+
- `errorEvents` - JavaScript errors
|
|
3260
|
+
- `networkEvents` - Network errors
|
|
3261
|
+
- `formAnalytics` - Form analytics
|
|
3262
|
+
|
|
3263
|
+
---
|
|
3264
|
+
|
|
3265
|
+
### Error Handling
|
|
3266
|
+
|
|
3267
|
+
All trackers have try-catch blocks to prevent failures from breaking the SDK:
|
|
3268
|
+
- Errors are logged to console
|
|
3269
|
+
- Tracker failures don't affect other trackers
|
|
3270
|
+
- SDK continues to function even if a tracker fails
|
|
3271
|
+
|
|
3272
|
+
---
|
|
3273
|
+
|
|
3274
|
+
### Reusability
|
|
3275
|
+
|
|
3276
|
+
✅ **Modular Design**: Each tracker is independent and can be enabled/disabled.
|
|
3277
|
+
|
|
3278
|
+
✅ **Consistent API**: All trackers use `InteractionManager.add()` to store interactions.
|
|
3279
|
+
|
|
3280
|
+
✅ **Easy to Extend**: Add new trackers by following the same pattern.
|
|
3281
|
+
|
|
3282
|
+
✅ **Error Resilient**: Trackers handle errors gracefully without breaking the SDK.
|
|
3283
|
+
|
|
3284
|
+
---
|
|
3285
|
+
|
|
3286
|
+
## Section 15: Initialization
|
|
3287
|
+
|
|
3288
|
+
### Purpose
|
|
3289
|
+
Orchestrates all sections in the correct order. Handles initialization, session tracking intervals, beforeunload events, and error recovery.
|
|
3290
|
+
|
|
3291
|
+
### Why It Matters
|
|
3292
|
+
- Single entry point for SDK initialization
|
|
3293
|
+
- Clear initialization order
|
|
3294
|
+
- Error recovery and fallbacks
|
|
3295
|
+
- Prevents duplicate initialization
|
|
3296
|
+
- Handles unsent session data from previous page loads
|
|
3297
|
+
|
|
3298
|
+
### Initializer Object
|
|
3299
|
+
|
|
3300
|
+
The `Initializer` object contains all initialization functions and orchestrates the SDK startup.
|
|
3301
|
+
|
|
3302
|
+
---
|
|
3303
|
+
|
|
3304
|
+
#### `Initializer.checkForUnsentData()`
|
|
3305
|
+
**Purpose**: Check for unsent session data from previous page loads.
|
|
3306
|
+
|
|
3307
|
+
**Logic**:
|
|
3308
|
+
1. Checks localStorage for `cryptique_last_session`
|
|
3309
|
+
2. Sends stored session data if found
|
|
3310
|
+
3. Clears stored data after successful send
|
|
3311
|
+
|
|
3312
|
+
**When to Use**: Called during SDK initialization.
|
|
3313
|
+
|
|
3314
|
+
**Example**:
|
|
3315
|
+
```javascript
|
|
3316
|
+
await Initializer.checkForUnsentData();
|
|
3317
|
+
// Sends any unsent session data from previous page load
|
|
3318
|
+
```
|
|
3319
|
+
|
|
3320
|
+
---
|
|
3321
|
+
|
|
3322
|
+
#### `Initializer.initializeSessionData()`
|
|
3323
|
+
**Purpose**: Initialize session data structure.
|
|
3324
|
+
|
|
3325
|
+
**Logic**:
|
|
3326
|
+
1. Gets or creates session ID
|
|
3327
|
+
2. Gets or creates user ID
|
|
3328
|
+
3. Initializes session data structure
|
|
3329
|
+
4. Initializes interactions
|
|
3330
|
+
5. Initializes page visits
|
|
3331
|
+
|
|
3332
|
+
**When to Use**: Called during SDK initialization.
|
|
3333
|
+
|
|
3334
|
+
**Example**:
|
|
3335
|
+
```javascript
|
|
3336
|
+
Initializer.initializeSessionData();
|
|
3337
|
+
// Initializes all session data structures
|
|
3338
|
+
```
|
|
3339
|
+
|
|
3340
|
+
---
|
|
3341
|
+
|
|
3342
|
+
#### `Initializer.initializeLocation()`
|
|
3343
|
+
**Purpose**: Initialize location data (non-blocking).
|
|
3344
|
+
|
|
3345
|
+
**Logic**:
|
|
3346
|
+
- Starts location fetch asynchronously
|
|
3347
|
+
- Doesn't block initialization
|
|
3348
|
+
- Errors are non-critical
|
|
3349
|
+
|
|
3350
|
+
**When to Use**: Called during SDK initialization.
|
|
3351
|
+
|
|
3352
|
+
**Example**:
|
|
3353
|
+
```javascript
|
|
3354
|
+
Initializer.initializeLocation();
|
|
3355
|
+
// Starts location fetch (non-blocking)
|
|
3356
|
+
```
|
|
3357
|
+
|
|
3358
|
+
---
|
|
3359
|
+
|
|
3360
|
+
#### `Initializer.initializeBrowserDevice()`
|
|
3361
|
+
**Purpose**: Initialize browser/device detection.
|
|
3362
|
+
|
|
3363
|
+
**Logic**:
|
|
3364
|
+
- Calls BrowserDeviceManager.detect()
|
|
3365
|
+
- Extracts browser/device information
|
|
3366
|
+
- Stores in sessionData
|
|
3367
|
+
|
|
3368
|
+
**When to Use**: Called during SDK initialization.
|
|
3369
|
+
|
|
3370
|
+
**Example**:
|
|
3371
|
+
```javascript
|
|
3372
|
+
Initializer.initializeBrowserDevice();
|
|
3373
|
+
// Detects and stores browser/device info
|
|
3374
|
+
```
|
|
3375
|
+
|
|
3376
|
+
---
|
|
3377
|
+
|
|
3378
|
+
#### `Initializer.initializeUTM()`
|
|
3379
|
+
**Purpose**: Initialize UTM parameters.
|
|
3380
|
+
|
|
3381
|
+
**Logic**:
|
|
3382
|
+
- Calls UTMManager.extract()
|
|
3383
|
+
- Extracts UTM parameters from URL
|
|
3384
|
+
- Stores in sessionData
|
|
3385
|
+
|
|
3386
|
+
**When to Use**: Called during SDK initialization.
|
|
3387
|
+
|
|
3388
|
+
**Example**:
|
|
3389
|
+
```javascript
|
|
3390
|
+
Initializer.initializeUTM();
|
|
3391
|
+
// Extracts and stores UTM parameters
|
|
3392
|
+
```
|
|
3393
|
+
|
|
3394
|
+
---
|
|
3395
|
+
|
|
3396
|
+
#### `Initializer.initializeWallet()`
|
|
3397
|
+
**Purpose**: Initialize wallet/Web3 detection.
|
|
3398
|
+
|
|
3399
|
+
**Logic**:
|
|
3400
|
+
- Calls WalletManager.initialize()
|
|
3401
|
+
- Detects wallets and chains
|
|
3402
|
+
- Sets up wallet event listeners
|
|
3403
|
+
|
|
3404
|
+
**When to Use**: Called during SDK initialization.
|
|
3405
|
+
|
|
3406
|
+
**Example**:
|
|
3407
|
+
```javascript
|
|
3408
|
+
Initializer.initializeWallet();
|
|
3409
|
+
// Initializes wallet detection
|
|
3410
|
+
```
|
|
3411
|
+
|
|
3412
|
+
---
|
|
3413
|
+
|
|
3414
|
+
#### `Initializer.trackInitialPage()`
|
|
3415
|
+
**Purpose**: Track initial page visit.
|
|
3416
|
+
|
|
3417
|
+
**Logic**:
|
|
3418
|
+
- Calls PageVisitManager.track()
|
|
3419
|
+
- Records page mount time
|
|
3420
|
+
- Sets entry page flag
|
|
3421
|
+
|
|
3422
|
+
**When to Use**: Called during SDK initialization.
|
|
3423
|
+
|
|
3424
|
+
**Example**:
|
|
3425
|
+
```javascript
|
|
3426
|
+
Initializer.trackInitialPage();
|
|
3427
|
+
// Tracks the initial page visit
|
|
3428
|
+
```
|
|
3429
|
+
|
|
3430
|
+
---
|
|
3431
|
+
|
|
3432
|
+
#### `Initializer.startSessionTracking()`
|
|
3433
|
+
**Purpose**: Start session tracking interval.
|
|
3434
|
+
|
|
3435
|
+
**Logic**:
|
|
3436
|
+
1. Clears any existing interval
|
|
3437
|
+
2. Sets up interval (every 15 seconds)
|
|
3438
|
+
3. In each interval:
|
|
3439
|
+
- Updates session duration
|
|
3440
|
+
- Updates wallet info (non-blocking)
|
|
3441
|
+
- Updates session activity
|
|
3442
|
+
- Syncs data from storage
|
|
3443
|
+
- Updates bounce status
|
|
3444
|
+
- Saves session to storage
|
|
3445
|
+
- Sends session data
|
|
3446
|
+
- Schedules overview send (debounced)
|
|
3447
|
+
|
|
3448
|
+
**Interval**: 15 seconds (CONFIG.INTERVALS.SESSION_TRACKING_MS)
|
|
3449
|
+
|
|
3450
|
+
**When to Use**: Called during SDK initialization.
|
|
3451
|
+
|
|
3452
|
+
**Example**:
|
|
3453
|
+
```javascript
|
|
3454
|
+
Initializer.startSessionTracking();
|
|
3455
|
+
// Starts periodic session tracking
|
|
3456
|
+
```
|
|
3457
|
+
|
|
3458
|
+
---
|
|
3459
|
+
|
|
3460
|
+
#### `Initializer.setupBeforeUnload()`
|
|
3461
|
+
**Purpose**: Set up beforeunload handler for final data send.
|
|
3462
|
+
|
|
3463
|
+
**Logic**:
|
|
3464
|
+
1. Listens to beforeunload event
|
|
3465
|
+
2. Syncs all data from storage
|
|
3466
|
+
3. Tracks page unmount
|
|
3467
|
+
4. Updates final session duration
|
|
3468
|
+
5. Updates bounce status
|
|
3469
|
+
6. Clears pending debounced sends
|
|
3470
|
+
7. Sends final overview data (sendBeacon)
|
|
3471
|
+
8. Sends final session data (sendBeacon)
|
|
3472
|
+
9. Stores backup in localStorage if send fails
|
|
3473
|
+
10. Clears tracking interval
|
|
3474
|
+
|
|
3475
|
+
**When to Use**: Called during SDK initialization.
|
|
3476
|
+
|
|
3477
|
+
**Example**:
|
|
3478
|
+
```javascript
|
|
3479
|
+
Initializer.setupBeforeUnload();
|
|
3480
|
+
// Sets up beforeunload handler
|
|
3481
|
+
```
|
|
3482
|
+
|
|
3483
|
+
---
|
|
3484
|
+
|
|
3485
|
+
#### `Initializer.sendInitialSessionData()`
|
|
3486
|
+
**Purpose**: Send initial session data after initialization.
|
|
3487
|
+
|
|
3488
|
+
**Logic**:
|
|
3489
|
+
1. Waits 500ms for location data to load
|
|
3490
|
+
2. Sends initial session data
|
|
3491
|
+
3. Errors are non-critical (will retry in interval)
|
|
3492
|
+
|
|
3493
|
+
**When to Use**: Called during SDK initialization.
|
|
3494
|
+
|
|
3495
|
+
**Example**:
|
|
3496
|
+
```javascript
|
|
3497
|
+
await Initializer.sendInitialSessionData();
|
|
3498
|
+
// Sends initial session data
|
|
3499
|
+
```
|
|
3500
|
+
|
|
3501
|
+
---
|
|
3502
|
+
|
|
3503
|
+
#### `Initializer.initialize()`
|
|
3504
|
+
**Purpose**: Main initialization function that orchestrates all sections.
|
|
3505
|
+
|
|
3506
|
+
**Initialization Order**:
|
|
3507
|
+
1. Check sessionStorage availability
|
|
3508
|
+
2. Check for unsent session data
|
|
3509
|
+
3. Initialize session data structure
|
|
3510
|
+
4. Initialize location (non-blocking)
|
|
3511
|
+
5. Initialize browser/device detection
|
|
3512
|
+
6. Initialize UTM parameters
|
|
3513
|
+
7. Initialize wallet/Web3 detection
|
|
3514
|
+
8. Track initial page visit
|
|
3515
|
+
9. Start event trackers
|
|
3516
|
+
10. Start session tracking interval
|
|
3517
|
+
11. Set up beforeunload handler
|
|
3518
|
+
12. Mark SDK as initialized
|
|
3519
|
+
13. Send initial session data (non-blocking)
|
|
3520
|
+
|
|
3521
|
+
**When to Use**: Called automatically on DOMContentLoaded or immediately if DOM already loaded.
|
|
3522
|
+
|
|
3523
|
+
**Example**:
|
|
3524
|
+
```javascript
|
|
3525
|
+
await Initializer.initialize();
|
|
3526
|
+
// Initializes entire SDK
|
|
3527
|
+
```
|
|
3528
|
+
|
|
3529
|
+
---
|
|
3530
|
+
|
|
3531
|
+
### Auto-Initialization
|
|
3532
|
+
|
|
3533
|
+
The SDK automatically initializes when:
|
|
3534
|
+
- DOM is ready (DOMContentLoaded event)
|
|
3535
|
+
- DOM is already loaded (immediate initialization)
|
|
3536
|
+
|
|
3537
|
+
**No manual initialization required** - the SDK initializes automatically when the script loads.
|
|
3538
|
+
|
|
3539
|
+
---
|
|
3540
|
+
|
|
3541
|
+
### Error Handling
|
|
3542
|
+
|
|
3543
|
+
All initialization functions have try-catch blocks:
|
|
3544
|
+
- Errors in one section don't break other sections
|
|
3545
|
+
- Non-critical errors are logged as warnings
|
|
3546
|
+
- Critical errors are logged as errors
|
|
3547
|
+
- SDK continues to function even if some sections fail
|
|
3548
|
+
|
|
3549
|
+
---
|
|
3550
|
+
|
|
3551
|
+
### Reusability
|
|
3552
|
+
|
|
3553
|
+
✅ **Modular Design**: Each initialization step is independent.
|
|
3554
|
+
|
|
3555
|
+
✅ **Error Resilient**: Failures in one section don't break others.
|
|
3556
|
+
|
|
3557
|
+
✅ **Auto-Initialization**: SDK initializes automatically.
|
|
3558
|
+
|
|
3559
|
+
✅ **Recovery**: Handles unsent session data from previous page loads.
|
|
3560
|
+
|
|
3561
|
+
---
|
|
3562
|
+
|
|
3563
|
+
## Notes
|
|
3564
|
+
|
|
3565
|
+
- All functions are designed to be self-contained and reusable
|
|
3566
|
+
- Error handling is built into each function
|
|
3567
|
+
- Functions follow consistent naming conventions
|
|
3568
|
+
- Documentation is kept in sync with code
|
|
3569
|
+
- This guide is a living document - update as you add new functions
|
|
3570
|
+
- **Field Naming**: Internal uses camelCase, backend uses snake_case
|
|
3571
|
+
- **Transformation**: Data is transformed on send, not stored
|
|
3572
|
+
- **Flattening**: All nested objects are flattened before sending to backend
|
|
3573
|
+
|
|
3574
|
+
## Notes
|
|
3575
|
+
|
|
3576
|
+
- All functions are designed to be self-contained and reusable
|
|
3577
|
+
- Error handling is built into each function
|
|
3578
|
+
- Functions follow consistent naming conventions
|
|
3579
|
+
- Documentation is kept in sync with code
|
|
3580
|
+
- This guide is a living document - update as you add new functions
|