cryptique-sdk 1.2.20 → 1.2.21
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/lib/cjs/index.js +22 -527
- package/lib/esm/index.js +22 -527
- package/lib/umd/index.js +22 -527
- package/package.json +1 -1
package/lib/cjs/index.js
CHANGED
|
@@ -92,8 +92,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
92
92
|
|
|
93
93
|
// Geolocation API
|
|
94
94
|
GEOLOCATION: {
|
|
95
|
-
PRIMARY_URL: "https://ipinfo.io/json?token=
|
|
96
|
-
BACKUP_URL: "https://ipinfo.io/json?token=
|
|
95
|
+
PRIMARY_URL: "https://ipinfo.io/json?token=3a0c034aefbef8",
|
|
96
|
+
BACKUP_URL: "https://ipinfo.io/json?token=3a0c034aefbef8&http=1.1",
|
|
97
97
|
TIMEOUT_MS: 5000 // 5 second timeout
|
|
98
98
|
},
|
|
99
99
|
|
|
@@ -1139,7 +1139,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
1139
1139
|
isFirstPage: true,
|
|
1140
1140
|
|
|
1141
1141
|
// JSONB fields
|
|
1142
|
-
interactions: {},
|
|
1143
1142
|
visited_pages: [],
|
|
1144
1143
|
|
|
1145
1144
|
// Also stored in locationData object for structured access (backward compatibility)
|
|
@@ -2084,7 +2083,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
2084
2083
|
if (sessionData.utm_id === undefined) sessionData.utm_id = null;
|
|
2085
2084
|
|
|
2086
2085
|
// JSONB fields
|
|
2087
|
-
if (!sessionData.interactions) sessionData.interactions = {};
|
|
2088
2086
|
if (!sessionData.visited_pages) sessionData.visited_pages = [];
|
|
2089
2087
|
|
|
2090
2088
|
// Internal tracking
|
|
@@ -2149,8 +2147,7 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
2149
2147
|
// UTM fields (already snake_case, no mapping needed)
|
|
2150
2148
|
|
|
2151
2149
|
// JSONB fields
|
|
2152
|
-
'visited_pages': 'visited_pages'
|
|
2153
|
-
'interactions': 'interactions' // Already snake_case
|
|
2150
|
+
'visited_pages': 'visited_pages' // Already snake_case
|
|
2154
2151
|
};
|
|
2155
2152
|
|
|
2156
2153
|
return fieldMap[internalName] || this.toSnakeCase(internalName);
|
|
@@ -3194,333 +3191,24 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
3194
3191
|
}
|
|
3195
3192
|
};
|
|
3196
3193
|
|
|
3197
|
-
//
|
|
3198
|
-
//
|
|
3199
|
-
//
|
|
3200
|
-
//
|
|
3201
|
-
// Handles all user interactions (clicks, forms, scroll, hover, etc.)
|
|
3202
|
-
//
|
|
3203
|
-
// KEY PRINCIPLES:
|
|
3204
|
-
// 1. All interactions stored in categorized structure
|
|
3205
|
-
// 2. Automatic timestamp addition
|
|
3206
|
-
// 3. Total interactions count maintained
|
|
3207
|
-
// 4. Chronological sorting for backend
|
|
3208
|
-
// 5. Immediate session storage updates
|
|
3209
|
-
// 6. Prevents duplicate interactions
|
|
3210
|
-
//
|
|
3211
|
-
// BENEFITS:
|
|
3212
|
-
// - Centralized interaction tracking
|
|
3213
|
-
// - Consistent data structure
|
|
3214
|
-
// - Single place to fix interaction bugs
|
|
3215
|
-
// - Prevents repeating interactions
|
|
3216
|
-
// ============================================================================
|
|
3194
|
+
// SECTION 10: INTERACTION MANAGEMENT — REMOVED
|
|
3195
|
+
// Interactions JSONB tracking has been removed from sessions.
|
|
3196
|
+
// Important signals (clicks, errors, etc.) are captured as individual
|
|
3197
|
+
// queryable auto events in the events table instead.
|
|
3217
3198
|
|
|
3218
|
-
|
|
3219
|
-
* InteractionManager - Interaction tracking and management
|
|
3220
|
-
*
|
|
3221
|
-
* This object handles all user interaction tracking, storage, and management
|
|
3222
|
-
*/
|
|
3199
|
+
// No-op stub — all methods removed with interactions feature.
|
|
3223
3200
|
const InteractionManager = {
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
'hoverEvents',
|
|
3236
|
-
'formSubmissions',
|
|
3237
|
-
'fieldChanges',
|
|
3238
|
-
'validationErrors',
|
|
3239
|
-
'keyboardEvents',
|
|
3240
|
-
'copyPasteEvents',
|
|
3241
|
-
'contextMenuEvents',
|
|
3242
|
-
'dragDropEvents',
|
|
3243
|
-
'touchEvents',
|
|
3244
|
-
'windowEvents',
|
|
3245
|
-
'performanceEvents',
|
|
3246
|
-
'errorEvents',
|
|
3247
|
-
'networkEvents',
|
|
3248
|
-
'formAnalytics'
|
|
3249
|
-
],
|
|
3250
|
-
|
|
3251
|
-
/**
|
|
3252
|
-
* Initialize interactions structure
|
|
3253
|
-
*
|
|
3254
|
-
* Creates empty interactions object with all categories
|
|
3255
|
-
*/
|
|
3256
|
-
initialize() {
|
|
3257
|
-
if (!sessionData.interactions || Object.keys(sessionData.interactions).length === 0) {
|
|
3258
|
-
sessionData.interactions = {
|
|
3259
|
-
totalInteractions: 0,
|
|
3260
|
-
clicks: [],
|
|
3261
|
-
formInteractions: [],
|
|
3262
|
-
mediaInteractions: [],
|
|
3263
|
-
scrollEvents: [],
|
|
3264
|
-
focusEvents: [],
|
|
3265
|
-
hoverEvents: [],
|
|
3266
|
-
formSubmissions: [],
|
|
3267
|
-
fieldChanges: [],
|
|
3268
|
-
validationErrors: [],
|
|
3269
|
-
keyboardEvents: [],
|
|
3270
|
-
copyPasteEvents: [],
|
|
3271
|
-
contextMenuEvents: [],
|
|
3272
|
-
dragDropEvents: [],
|
|
3273
|
-
touchEvents: [],
|
|
3274
|
-
windowEvents: [],
|
|
3275
|
-
performanceEvents: [],
|
|
3276
|
-
errorEvents: [],
|
|
3277
|
-
networkEvents: [],
|
|
3278
|
-
formAnalytics: []
|
|
3279
|
-
};
|
|
3280
|
-
}
|
|
3281
|
-
},
|
|
3282
|
-
|
|
3283
|
-
/**
|
|
3284
|
-
* Add interaction to appropriate category
|
|
3285
|
-
*
|
|
3286
|
-
* Logic:
|
|
3287
|
-
* 1. Ensures interactions are initialized
|
|
3288
|
-
* 2. Adds timestamp if not provided
|
|
3289
|
-
* 3. Adds to specific category array
|
|
3290
|
-
* 4. Updates total interactions count
|
|
3291
|
-
* 5. Updates session activity
|
|
3292
|
-
* 6. Immediately updates session storage
|
|
3293
|
-
*
|
|
3294
|
-
* Parameters:
|
|
3295
|
-
* - category: Interaction category (e.g., 'clicks', 'formInteractions')
|
|
3296
|
-
* - interactionData: Interaction data object
|
|
3297
|
-
*/
|
|
3298
|
-
add(category, interactionData) {
|
|
3299
|
-
// Ensure interactions are initialized
|
|
3300
|
-
this.initialize();
|
|
3301
|
-
|
|
3302
|
-
// Validate category
|
|
3303
|
-
if (!this.CATEGORIES.includes(category)) {
|
|
3304
|
-
return;
|
|
3305
|
-
}
|
|
3306
|
-
|
|
3307
|
-
// Add timestamp if not provided
|
|
3308
|
-
if (!interactionData.timestamp) {
|
|
3309
|
-
interactionData.timestamp = nowIso();
|
|
3310
|
-
}
|
|
3311
|
-
|
|
3312
|
-
// Add to specific category
|
|
3313
|
-
if (sessionData.interactions[category]) {
|
|
3314
|
-
sessionData.interactions[category].push(interactionData);
|
|
3315
|
-
}
|
|
3316
|
-
|
|
3317
|
-
// Update total interactions count
|
|
3318
|
-
this.updateTotalCount();
|
|
3319
|
-
|
|
3320
|
-
// Update last activity
|
|
3321
|
-
sessionData.lastActivity = Date.now();
|
|
3322
|
-
|
|
3323
|
-
// Immediately update session storage
|
|
3324
|
-
this.updateSessionStorage();
|
|
3325
|
-
},
|
|
3326
|
-
|
|
3327
|
-
/**
|
|
3328
|
-
* Update total interactions count
|
|
3329
|
-
*
|
|
3330
|
-
* Calculates total from all category arrays
|
|
3331
|
-
*/
|
|
3332
|
-
updateTotalCount() {
|
|
3333
|
-
if (!sessionData.interactions) return;
|
|
3334
|
-
|
|
3335
|
-
let total = 0;
|
|
3336
|
-
for (const category of this.CATEGORIES) {
|
|
3337
|
-
if (sessionData.interactions[category] && Array.isArray(sessionData.interactions[category])) {
|
|
3338
|
-
total += sessionData.interactions[category].length;
|
|
3339
|
-
}
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
sessionData.interactions.totalInteractions = total;
|
|
3343
|
-
},
|
|
3344
|
-
|
|
3345
|
-
/**
|
|
3346
|
-
* Update session storage with interactions
|
|
3347
|
-
*
|
|
3348
|
-
* Immediately syncs interactions to session storage
|
|
3349
|
-
*/
|
|
3350
|
-
updateSessionStorage() {
|
|
3351
|
-
try {
|
|
3352
|
-
const storedSession = StorageManager.loadSession();
|
|
3353
|
-
if (storedSession && storedSession.sessionData) {
|
|
3354
|
-
storedSession.sessionData.interactions = sessionData.interactions;
|
|
3355
|
-
StorageManager.saveSession(storedSession);
|
|
3356
|
-
}
|
|
3357
|
-
} catch (error) {
|
|
3358
|
-
}
|
|
3359
|
-
},
|
|
3360
|
-
|
|
3361
|
-
/**
|
|
3362
|
-
* Get all interactions sorted chronologically
|
|
3363
|
-
*
|
|
3364
|
-
* Collects all interactions from all categories, adds category identifiers,
|
|
3365
|
-
* and sorts by timestamp
|
|
3366
|
-
*
|
|
3367
|
-
* Returns: Array of interactions sorted by timestamp
|
|
3368
|
-
*/
|
|
3369
|
-
getChronological() {
|
|
3370
|
-
if (!sessionData.interactions) {
|
|
3371
|
-
return [];
|
|
3372
|
-
}
|
|
3373
|
-
|
|
3374
|
-
const allInteractions = [];
|
|
3375
|
-
|
|
3376
|
-
// Map of category to category identifier
|
|
3377
|
-
const categoryMap = {
|
|
3378
|
-
'clicks': { category: 'click', originalCategory: 'clicks' },
|
|
3379
|
-
'formInteractions': { category: 'formInteraction', originalCategory: 'formInteractions' },
|
|
3380
|
-
'mediaInteractions': { category: 'mediaInteraction', originalCategory: 'mediaInteractions' },
|
|
3381
|
-
'scrollEvents': { category: 'scrollEvent', originalCategory: 'scrollEvents' },
|
|
3382
|
-
'focusEvents': { category: 'focusEvent', originalCategory: 'focusEvents' },
|
|
3383
|
-
'hoverEvents': { category: 'hoverEvent', originalCategory: 'hoverEvents' },
|
|
3384
|
-
'formSubmissions': { category: 'formSubmission', originalCategory: 'formSubmissions' },
|
|
3385
|
-
'fieldChanges': { category: 'fieldChange', originalCategory: 'fieldChanges' },
|
|
3386
|
-
'validationErrors': { category: 'validationError', originalCategory: 'validationErrors' },
|
|
3387
|
-
'keyboardEvents': { category: 'keyboardEvent', originalCategory: 'keyboardEvents' },
|
|
3388
|
-
'copyPasteEvents': { category: 'copyPasteEvent', originalCategory: 'copyPasteEvents' },
|
|
3389
|
-
'contextMenuEvents': { category: 'contextMenuEvent', originalCategory: 'contextMenuEvents' },
|
|
3390
|
-
'dragDropEvents': { category: 'dragDropEvent', originalCategory: 'dragDropEvents' },
|
|
3391
|
-
'touchEvents': { category: 'touchEvent', originalCategory: 'touchEvents' },
|
|
3392
|
-
'windowEvents': { category: 'windowEvent', originalCategory: 'windowEvents' },
|
|
3393
|
-
'performanceEvents': { category: 'performanceEvent', originalCategory: 'performanceEvents' },
|
|
3394
|
-
'errorEvents': { category: 'errorEvent', originalCategory: 'errorEvents' },
|
|
3395
|
-
'networkEvents': { category: 'networkEvent', originalCategory: 'networkEvents' },
|
|
3396
|
-
'formAnalytics': { category: 'formAnalytic', originalCategory: 'formAnalytics' }
|
|
3397
|
-
};
|
|
3398
|
-
|
|
3399
|
-
// Collect all interactions from all categories
|
|
3400
|
-
for (const category of this.CATEGORIES) {
|
|
3401
|
-
if (sessionData.interactions[category] && Array.isArray(sessionData.interactions[category])) {
|
|
3402
|
-
const categoryInfo = categoryMap[category];
|
|
3403
|
-
sessionData.interactions[category].forEach(interaction => {
|
|
3404
|
-
allInteractions.push({
|
|
3405
|
-
...interaction,
|
|
3406
|
-
category: categoryInfo.category,
|
|
3407
|
-
originalCategory: categoryInfo.originalCategory
|
|
3408
|
-
});
|
|
3409
|
-
});
|
|
3410
|
-
}
|
|
3411
|
-
}
|
|
3412
|
-
|
|
3413
|
-
// Sort by timestamp (chronological order)
|
|
3414
|
-
allInteractions.sort((a, b) => {
|
|
3415
|
-
const timestampA = new Date(a.timestamp || 0);
|
|
3416
|
-
const timestampB = new Date(b.timestamp || 0);
|
|
3417
|
-
return timestampA - timestampB;
|
|
3418
|
-
});
|
|
3419
|
-
|
|
3420
|
-
return allInteractions;
|
|
3421
|
-
},
|
|
3422
|
-
|
|
3423
|
-
/**
|
|
3424
|
-
* Get chronological interactions for backend
|
|
3425
|
-
*
|
|
3426
|
-
* Returns both chronological array and categorized structure
|
|
3427
|
-
*
|
|
3428
|
-
* Returns: Object with chronological and categorized interactions
|
|
3429
|
-
*/
|
|
3430
|
-
getChronologicalForBackend() {
|
|
3431
|
-
const chronological = this.getChronological();
|
|
3432
|
-
|
|
3433
|
-
return {
|
|
3434
|
-
chronological: chronological,
|
|
3435
|
-
categorized: sessionData.interactions
|
|
3436
|
-
};
|
|
3437
|
-
},
|
|
3438
|
-
|
|
3439
|
-
/**
|
|
3440
|
-
* Deduplicate interactions
|
|
3441
|
-
*
|
|
3442
|
-
* Removes duplicate interactions based on timestamp, category, and key fields
|
|
3443
|
-
*
|
|
3444
|
-
* Logic:
|
|
3445
|
-
* - Compares interactions by timestamp, category, and element identifier
|
|
3446
|
-
* - Keeps first occurrence of duplicates
|
|
3447
|
-
* - Updates total count after deduplication
|
|
3448
|
-
*/
|
|
3449
|
-
deduplicate() {
|
|
3450
|
-
if (!sessionData.interactions) return;
|
|
3451
|
-
|
|
3452
|
-
const seen = new Set();
|
|
3453
|
-
|
|
3454
|
-
for (const category of this.CATEGORIES) {
|
|
3455
|
-
if (sessionData.interactions[category] && Array.isArray(sessionData.interactions[category])) {
|
|
3456
|
-
sessionData.interactions[category] = sessionData.interactions[category].filter(interaction => {
|
|
3457
|
-
// Create unique key from timestamp, category, and element identifier
|
|
3458
|
-
const elementId = interaction.elementId || interaction.target || interaction.selector || '';
|
|
3459
|
-
const key = `${interaction.timestamp}_${category}_${elementId}`;
|
|
3460
|
-
|
|
3461
|
-
if (seen.has(key)) {
|
|
3462
|
-
return false; // Duplicate, remove
|
|
3463
|
-
}
|
|
3464
|
-
|
|
3465
|
-
seen.add(key);
|
|
3466
|
-
return true; // Keep
|
|
3467
|
-
});
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
|
-
|
|
3471
|
-
// Update total count after deduplication
|
|
3472
|
-
this.updateTotalCount();
|
|
3473
|
-
|
|
3474
|
-
// Update session storage
|
|
3475
|
-
this.updateSessionStorage();
|
|
3476
|
-
},
|
|
3477
|
-
|
|
3478
|
-
/**
|
|
3479
|
-
* Get interactions by category
|
|
3480
|
-
*
|
|
3481
|
-
* Returns all interactions for a specific category
|
|
3482
|
-
*
|
|
3483
|
-
* Parameters: category - Interaction category name
|
|
3484
|
-
* Returns: Array of interactions for that category
|
|
3485
|
-
*/
|
|
3486
|
-
getByCategory(category) {
|
|
3487
|
-
if (!sessionData.interactions) {
|
|
3488
|
-
return [];
|
|
3489
|
-
}
|
|
3490
|
-
|
|
3491
|
-
if (!this.CATEGORIES.includes(category)) {
|
|
3492
|
-
return [];
|
|
3493
|
-
}
|
|
3494
|
-
|
|
3495
|
-
return sessionData.interactions[category] || [];
|
|
3496
|
-
},
|
|
3497
|
-
|
|
3498
|
-
/**
|
|
3499
|
-
* Get total interactions count
|
|
3500
|
-
*
|
|
3501
|
-
* Returns total number of interactions across all categories
|
|
3502
|
-
*/
|
|
3503
|
-
getTotalCount() {
|
|
3504
|
-
if (!sessionData.interactions) {
|
|
3505
|
-
return 0;
|
|
3506
|
-
}
|
|
3507
|
-
|
|
3508
|
-
return sessionData.interactions.totalInteractions || 0;
|
|
3509
|
-
},
|
|
3510
|
-
|
|
3511
|
-
/**
|
|
3512
|
-
* Clear all interactions
|
|
3513
|
-
*
|
|
3514
|
-
* Resets interactions object to empty state
|
|
3515
|
-
*/
|
|
3516
|
-
clear() {
|
|
3517
|
-
this.initialize();
|
|
3518
|
-
for (const category of this.CATEGORIES) {
|
|
3519
|
-
sessionData.interactions[category] = [];
|
|
3520
|
-
}
|
|
3521
|
-
sessionData.interactions.totalInteractions = 0;
|
|
3522
|
-
this.updateSessionStorage();
|
|
3523
|
-
}
|
|
3201
|
+
CATEGORIES: [],
|
|
3202
|
+
initialize() {},
|
|
3203
|
+
add() {},
|
|
3204
|
+
updateTotalCount() {},
|
|
3205
|
+
updateSessionStorage() {},
|
|
3206
|
+
getChronological() { return []; },
|
|
3207
|
+
getChronologicalForBackend() { return { chronological: [], categorized: {} }; },
|
|
3208
|
+
deduplicate() {},
|
|
3209
|
+
getByCategory() { return []; },
|
|
3210
|
+
getTotalCount() { return 0; },
|
|
3211
|
+
clear() {}
|
|
3524
3212
|
};
|
|
3525
3213
|
|
|
3526
3214
|
// ============================================================================
|
|
@@ -3988,7 +3676,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
3988
3676
|
// Ensure arrays exist
|
|
3989
3677
|
s.pageVisits = s.pageVisits || [];
|
|
3990
3678
|
s.visited_pages = s.visited_pages || [];
|
|
3991
|
-
s.interactions = s.interactions || { totalInteractions: 0 };
|
|
3992
3679
|
|
|
3993
3680
|
// Normalize timestamps to ISO strings
|
|
3994
3681
|
if (s.startTime && !(typeof s.startTime === 'string')) {
|
|
@@ -4043,171 +3730,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4043
3730
|
// First normalize the shape
|
|
4044
3731
|
sourceData = this.normalizeShape(sourceData);
|
|
4045
3732
|
|
|
4046
|
-
// Get
|
|
4047
|
-
const chronologicalInteractions = InteractionManager.getChronologicalForBackend();
|
|
4048
|
-
|
|
4049
|
-
// Get page visits and session-level interactions
|
|
3733
|
+
// Get page visits
|
|
4050
3734
|
const pageVisits = sourceData.visited_pages || sourceData.pageVisits || sourceData.visitedPages || [];
|
|
4051
|
-
const sessionInteractions = sourceData.interactions || {};
|
|
4052
|
-
|
|
4053
|
-
// CRITICAL FIX: Distribute session-level interactions to appropriate pages based on path
|
|
4054
|
-
const interactionTypes = [
|
|
4055
|
-
'clicks', 'mediaInteractions', 'contextMenuEvents', 'windowEvents',
|
|
4056
|
-
'formInteractions', 'scrollEvents', 'focusEvents', 'hoverEvents',
|
|
4057
|
-
'formSubmissions', 'fieldChanges',
|
|
4058
|
-
'keyboardEvents', 'copyPasteEvents', 'dragDropEvents',
|
|
4059
|
-
'touchEvents', 'formAnalytics'
|
|
4060
|
-
// Note: validationErrors, performanceEvents, errorEvents, networkEvents are no longer
|
|
4061
|
-
// stored in session interactions — they are emitted as individual queryable auto events
|
|
4062
|
-
// (form_validation_error, page_performance, js_error, network_error)
|
|
4063
|
-
];
|
|
4064
|
-
|
|
4065
|
-
// Helper to normalize path for matching
|
|
4066
|
-
const normalizePathForMatching = (path) => {
|
|
4067
|
-
if (!path) return '/';
|
|
4068
|
-
// Remove query params and hash
|
|
4069
|
-
const cleanPath = path.split('?')[0].split('#')[0];
|
|
4070
|
-
return cleanPath || '/';
|
|
4071
|
-
};
|
|
4072
|
-
|
|
4073
|
-
// Get session start time in milliseconds for alignment
|
|
4074
|
-
const sessionStartTime = sourceData.start_time || sourceData.startTime;
|
|
4075
|
-
const sessionStartMs = sessionStartTime
|
|
4076
|
-
? (typeof sessionStartTime === 'string' ? new Date(sessionStartTime).getTime() : sessionStartTime)
|
|
4077
|
-
: Date.now();
|
|
4078
|
-
|
|
4079
|
-
// Distribute interactions to pages and fix mount/unmount times
|
|
4080
|
-
// CRITICAL: Create copies to avoid mutating original objects
|
|
4081
|
-
const pagesWithInteractions = pageVisits.map((pageVisit, index) => {
|
|
4082
|
-
// Create a copy of the page visit to avoid mutating the original
|
|
4083
|
-
const pageVisitCopy = { ...pageVisit };
|
|
4084
|
-
const pagePath = normalizePathForMatching(pageVisitCopy.path || pageVisitCopy.url);
|
|
4085
|
-
const isFirstPage = index === 0 || pageVisitCopy.isEntry === true;
|
|
4086
|
-
|
|
4087
|
-
// CRITICAL FIX: If unmountTime is null, set it to next page's mountTime
|
|
4088
|
-
if (!pageVisitCopy.unmountTime && index < pageVisits.length - 1) {
|
|
4089
|
-
const nextPage = pageVisits[index + 1];
|
|
4090
|
-
if (nextPage && nextPage.mountTime) {
|
|
4091
|
-
pageVisitCopy.unmountTime = typeof nextPage.mountTime === 'number'
|
|
4092
|
-
? nextPage.mountTime
|
|
4093
|
-
: new Date(nextPage.mountTime).getTime();
|
|
4094
|
-
// Recalculate duration
|
|
4095
|
-
if (pageVisitCopy.mountTime) {
|
|
4096
|
-
const mountMs = typeof pageVisitCopy.mountTime === 'number'
|
|
4097
|
-
? pageVisitCopy.mountTime
|
|
4098
|
-
: new Date(pageVisitCopy.mountTime).getTime();
|
|
4099
|
-
pageVisitCopy.duration = Math.floor((pageVisitCopy.unmountTime - mountMs) / 1000);
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
}
|
|
4103
|
-
|
|
4104
|
-
// If still no unmountTime and this is the last page, set to session endTime
|
|
4105
|
-
if (!pageVisitCopy.unmountTime && index === pageVisits.length - 1) {
|
|
4106
|
-
const endTime = sourceData.end_time || sourceData.endTime;
|
|
4107
|
-
if (endTime) {
|
|
4108
|
-
pageVisitCopy.unmountTime = typeof endTime === 'number'
|
|
4109
|
-
? endTime
|
|
4110
|
-
: new Date(endTime).getTime();
|
|
4111
|
-
// Recalculate duration
|
|
4112
|
-
if (pageVisitCopy.mountTime) {
|
|
4113
|
-
const mountMs = typeof pageVisitCopy.mountTime === 'number'
|
|
4114
|
-
? pageVisitCopy.mountTime
|
|
4115
|
-
: new Date(pageVisitCopy.mountTime).getTime();
|
|
4116
|
-
pageVisitCopy.duration = Math.floor((pageVisitCopy.unmountTime - mountMs) / 1000);
|
|
4117
|
-
}
|
|
4118
|
-
}
|
|
4119
|
-
}
|
|
4120
|
-
|
|
4121
|
-
// FIX: Align first page mountTime with session start_time (only in copy, not original)
|
|
4122
|
-
if (isFirstPage && pageVisitCopy.mountTime) {
|
|
4123
|
-
const mountTimeMs = typeof pageVisitCopy.mountTime === 'number'
|
|
4124
|
-
? pageVisitCopy.mountTime
|
|
4125
|
-
: new Date(pageVisitCopy.mountTime).getTime();
|
|
4126
|
-
|
|
4127
|
-
// If mountTime is more than 5 minutes off from session start, align it
|
|
4128
|
-
const timeDiff = Math.abs(mountTimeMs - sessionStartMs);
|
|
4129
|
-
if (timeDiff > 5 * 60 * 1000) { // More than 5 minutes difference
|
|
4130
|
-
pageVisitCopy.mountTime = sessionStartMs;
|
|
4131
|
-
// Recalculate duration if unmountTime exists
|
|
4132
|
-
if (pageVisitCopy.unmountTime) {
|
|
4133
|
-
const unmountTimeMs = typeof pageVisitCopy.unmountTime === 'number'
|
|
4134
|
-
? pageVisitCopy.unmountTime
|
|
4135
|
-
: new Date(pageVisitCopy.unmountTime).getTime();
|
|
4136
|
-
pageVisitCopy.duration = Math.floor((unmountTimeMs - sessionStartMs) / 1000);
|
|
4137
|
-
}
|
|
4138
|
-
}
|
|
4139
|
-
}
|
|
4140
|
-
|
|
4141
|
-
// Ensure mountTime and unmountTime are numbers (milliseconds) for consistency (in copy only)
|
|
4142
|
-
if (pageVisitCopy.mountTime && typeof pageVisitCopy.mountTime !== 'number') {
|
|
4143
|
-
pageVisitCopy.mountTime = new Date(pageVisitCopy.mountTime).getTime();
|
|
4144
|
-
}
|
|
4145
|
-
if (pageVisitCopy.unmountTime && typeof pageVisitCopy.unmountTime !== 'number') {
|
|
4146
|
-
pageVisitCopy.unmountTime = new Date(pageVisitCopy.unmountTime).getTime();
|
|
4147
|
-
}
|
|
4148
|
-
|
|
4149
|
-
// Initialize interactions object for this page if it doesn't exist (in copy)
|
|
4150
|
-
if (!pageVisitCopy.interactions) {
|
|
4151
|
-
pageVisitCopy.interactions = {
|
|
4152
|
-
totalInteractions: 0,
|
|
4153
|
-
clicks: [],
|
|
4154
|
-
mediaInteractions: [],
|
|
4155
|
-
contextMenuEvents: [],
|
|
4156
|
-
windowEvents: [],
|
|
4157
|
-
formInteractions: [],
|
|
4158
|
-
scrollEvents: [],
|
|
4159
|
-
focusEvents: [],
|
|
4160
|
-
hoverEvents: [],
|
|
4161
|
-
formSubmissions: [],
|
|
4162
|
-
fieldChanges: [],
|
|
4163
|
-
validationErrors: [],
|
|
4164
|
-
keyboardEvents: [],
|
|
4165
|
-
copyPasteEvents: [],
|
|
4166
|
-
dragDropEvents: [],
|
|
4167
|
-
touchEvents: [],
|
|
4168
|
-
performanceEvents: [],
|
|
4169
|
-
errorEvents: [],
|
|
4170
|
-
networkEvents: [],
|
|
4171
|
-
formAnalytics: []
|
|
4172
|
-
};
|
|
4173
|
-
}
|
|
4174
|
-
|
|
4175
|
-
// Distribute interactions from session-level to this page based on path matching (in copy)
|
|
4176
|
-
interactionTypes.forEach(type => {
|
|
4177
|
-
if (Array.isArray(sessionInteractions[type])) {
|
|
4178
|
-
sessionInteractions[type].forEach(interaction => {
|
|
4179
|
-
const interactionPath = normalizePathForMatching(interaction.path);
|
|
4180
|
-
if (interactionPath === pagePath) {
|
|
4181
|
-
// Add interaction to this page (avoid duplicates)
|
|
4182
|
-
const exists = pageVisitCopy.interactions[type].some(existing => {
|
|
4183
|
-
// Check for duplicates based on timestamp and key properties
|
|
4184
|
-
if (existing.timestamp && interaction.timestamp) {
|
|
4185
|
-
return existing.timestamp === interaction.timestamp;
|
|
4186
|
-
}
|
|
4187
|
-
// For clicks, check element properties
|
|
4188
|
-
if (type === 'clicks' && existing.element && interaction.element) {
|
|
4189
|
-
return existing.element.id === interaction.element.id &&
|
|
4190
|
-
existing.element.tagName === interaction.element.tagName;
|
|
4191
|
-
}
|
|
4192
|
-
return false;
|
|
4193
|
-
});
|
|
4194
|
-
|
|
4195
|
-
if (!exists) {
|
|
4196
|
-
pageVisitCopy.interactions[type].push(interaction);
|
|
4197
|
-
}
|
|
4198
|
-
}
|
|
4199
|
-
});
|
|
4200
|
-
}
|
|
4201
|
-
});
|
|
4202
|
-
|
|
4203
|
-
// Calculate totalInteractions for this page (in copy)
|
|
4204
|
-
const total = interactionTypes.reduce((sum, type) => {
|
|
4205
|
-
return sum + (pageVisitCopy.interactions[type]?.length || 0);
|
|
4206
|
-
}, 0);
|
|
4207
|
-
pageVisitCopy.interactions.totalInteractions = total;
|
|
4208
|
-
|
|
4209
|
-
return pageVisitCopy;
|
|
4210
|
-
});
|
|
4211
3735
|
|
|
4212
3736
|
// Create transformed object - PostgreSQL snake_case format only
|
|
4213
3737
|
const transformed = {
|
|
@@ -4272,12 +3796,8 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4272
3796
|
utm_content: sourceData.utm_content || sourceData.utmData?.content || null,
|
|
4273
3797
|
utm_id: sourceData.utm_id || sourceData.utmData?.utm_id || null,
|
|
4274
3798
|
|
|
4275
|
-
// JSONB fields
|
|
4276
|
-
visited_pages:
|
|
4277
|
-
interactions: {
|
|
4278
|
-
...sessionInteractions,
|
|
4279
|
-
chronological: chronologicalInteractions.chronological || []
|
|
4280
|
-
},
|
|
3799
|
+
// JSONB fields
|
|
3800
|
+
visited_pages: pageVisits,
|
|
4281
3801
|
|
|
4282
3802
|
// Active tab time in ms (tab foreground time only, for active_time_seconds in DB)
|
|
4283
3803
|
active_time_ms: sourceData.active_time_ms || null
|
|
@@ -4945,12 +4465,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
4945
4465
|
if (hoveredElements.has(elementId)) {
|
|
4946
4466
|
hoveredElements.delete(elementId);
|
|
4947
4467
|
|
|
4948
|
-
// Update hover duration in the interaction
|
|
4949
|
-
const interactions = InteractionManager.getByCategory('hoverEvents');
|
|
4950
|
-
const hoverEvent = interactions.find(h => h.elementId === elementId);
|
|
4951
|
-
if (hoverEvent) {
|
|
4952
|
-
hoverEvent.hoverDuration = Date.now() - new Date(hoverEvent.timestamp).getTime();
|
|
4953
|
-
}
|
|
4954
4468
|
}
|
|
4955
4469
|
}
|
|
4956
4470
|
});
|
|
@@ -6269,9 +5783,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6269
5783
|
// Initialize session data structure
|
|
6270
5784
|
SessionDataManager.initialize(sessionId, userId);
|
|
6271
5785
|
|
|
6272
|
-
// Initialize interactions
|
|
6273
|
-
InteractionManager.initialize();
|
|
6274
|
-
|
|
6275
5786
|
// Initialize page visits
|
|
6276
5787
|
PageVisitManager.initialize();
|
|
6277
5788
|
} catch (error) {
|
|
@@ -6414,15 +5925,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6414
5925
|
sessionData.pagesViewed = sessionData.visited_pages.length;
|
|
6415
5926
|
}
|
|
6416
5927
|
|
|
6417
|
-
// Sync interactions
|
|
6418
|
-
if (storedSession.sessionData.interactions) {
|
|
6419
|
-
const storedInteractions = storedSession.sessionData.interactions;
|
|
6420
|
-
if (storedInteractions.totalInteractions >
|
|
6421
|
-
(sessionData.interactions?.totalInteractions || 0)) {
|
|
6422
|
-
// Update interactions from storage
|
|
6423
|
-
sessionData.interactions = storedInteractions;
|
|
6424
|
-
}
|
|
6425
|
-
}
|
|
6426
5928
|
}
|
|
6427
5929
|
|
|
6428
5930
|
// Update endTime
|
|
@@ -6485,9 +5987,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
6485
5987
|
}));
|
|
6486
5988
|
sessionData.pagesViewed = sessionData.visited_pages.length;
|
|
6487
5989
|
}
|
|
6488
|
-
if (storedSession.sessionData.interactions) {
|
|
6489
|
-
sessionData.interactions = storedSession.sessionData.interactions;
|
|
6490
|
-
}
|
|
6491
5990
|
}
|
|
6492
5991
|
|
|
6493
5992
|
// Track browser close (last page unmount)
|
|
@@ -8670,10 +8169,6 @@ if (window.Cryptique && window.Cryptique.initialized) ; else {
|
|
|
8670
8169
|
return sessionData;
|
|
8671
8170
|
},
|
|
8672
8171
|
|
|
8673
|
-
// Interaction Functions
|
|
8674
|
-
getChronologicalInteractions: InteractionManager.getChronological.bind(InteractionManager),
|
|
8675
|
-
sortInteractionsChronologically: InteractionManager.getChronological.bind(InteractionManager),
|
|
8676
|
-
|
|
8677
8172
|
// Auto Events Configuration
|
|
8678
8173
|
enableAutoEvents: function() {
|
|
8679
8174
|
CONFIG.AUTO_EVENTS.enabled = true;
|