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