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