@checkflow/sdk 1.0.4 → 1.0.5
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/dist/analytics-tracker.d.ts +4 -0
- package/dist/index.esm.js +147 -11
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +147 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -69,10 +69,13 @@ export declare class AnalyticsTracker {
|
|
|
69
69
|
private lastScrollTime;
|
|
70
70
|
private isFirstPageView;
|
|
71
71
|
private performanceObserver?;
|
|
72
|
+
private recordingUploadTimer;
|
|
73
|
+
private readonly RECORDING_UPLOAD_INTERVAL;
|
|
72
74
|
constructor(apiClient: APIClient, options?: Partial<AnalyticsOptions>);
|
|
73
75
|
startTracking(): Promise<void>;
|
|
74
76
|
stopTracking(): Promise<void>;
|
|
75
77
|
private saveSessionRecording;
|
|
78
|
+
private setupPeriodicRecordingUpload;
|
|
76
79
|
private createSession;
|
|
77
80
|
private updateSessionEnd;
|
|
78
81
|
private setupEventListeners;
|
|
@@ -88,6 +91,7 @@ export declare class AnalyticsTracker {
|
|
|
88
91
|
private setupSPATracking;
|
|
89
92
|
private setupPerformanceTracking;
|
|
90
93
|
private trackPageView;
|
|
94
|
+
private capturePageScreenshotForHeatmap;
|
|
91
95
|
private trackPageLoadPerformance;
|
|
92
96
|
private queueInteraction;
|
|
93
97
|
private flushInteractions;
|
package/dist/index.esm.js
CHANGED
|
@@ -608,14 +608,40 @@ async function captureScreenshot(options = {}) {
|
|
|
608
608
|
}
|
|
609
609
|
// Mask elements if specified
|
|
610
610
|
const maskedElements = [];
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
611
|
+
// Default sensitive selectors to mask
|
|
612
|
+
const defaultMaskSelectors = [
|
|
613
|
+
'input[type="password"]',
|
|
614
|
+
'input[type="email"]',
|
|
615
|
+
'input[type="tel"]',
|
|
616
|
+
'input[name*="password"]',
|
|
617
|
+
'input[name*="email"]',
|
|
618
|
+
'input[name*="phone"]',
|
|
619
|
+
'input[placeholder*="password"]',
|
|
620
|
+
'input[placeholder*="email"]',
|
|
621
|
+
'textarea[name*="message"]',
|
|
622
|
+
'[data-sensitive]'
|
|
623
|
+
];
|
|
624
|
+
const maskSelectors = [...defaultMaskSelectors, ...(options.maskElements || [])];
|
|
625
|
+
maskSelectors.forEach((selector) => {
|
|
626
|
+
document.querySelectorAll(selector).forEach((el) => {
|
|
627
|
+
maskedElements.push({ element: el, innerHTML: el.innerHTML });
|
|
628
|
+
// Mask based on element type
|
|
629
|
+
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
|
|
630
|
+
const input = el;
|
|
631
|
+
if (input.value) {
|
|
632
|
+
input.setAttribute('data-original-value', input.value);
|
|
633
|
+
input.value = '*'.repeat(Math.min(input.value.length, 8));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
// Mask text content
|
|
638
|
+
const text = el.textContent || '';
|
|
639
|
+
if (text.trim()) {
|
|
640
|
+
el.innerHTML = '*'.repeat(Math.min(text.length, 10));
|
|
641
|
+
}
|
|
642
|
+
}
|
|
617
643
|
});
|
|
618
|
-
}
|
|
644
|
+
});
|
|
619
645
|
// Wait for delay if specified
|
|
620
646
|
if (options.delay) {
|
|
621
647
|
await new Promise((resolve) => setTimeout(resolve, options.delay));
|
|
@@ -635,7 +661,17 @@ async function captureScreenshot(options = {}) {
|
|
|
635
661
|
});
|
|
636
662
|
// Restore masked elements
|
|
637
663
|
maskedElements.forEach(({ element, innerHTML }) => {
|
|
638
|
-
element.
|
|
664
|
+
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
|
|
665
|
+
const input = element;
|
|
666
|
+
const originalValue = input.getAttribute('data-original-value');
|
|
667
|
+
if (originalValue) {
|
|
668
|
+
input.value = originalValue;
|
|
669
|
+
input.removeAttribute('data-original-value');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
element.innerHTML = innerHTML;
|
|
674
|
+
}
|
|
639
675
|
});
|
|
640
676
|
// Convert to base64
|
|
641
677
|
const quality = (options.quality || 80) / 100;
|
|
@@ -723,6 +759,14 @@ class ContextCapture {
|
|
|
723
759
|
}
|
|
724
760
|
}
|
|
725
761
|
|
|
762
|
+
var contextCapture = /*#__PURE__*/Object.freeze({
|
|
763
|
+
__proto__: null,
|
|
764
|
+
ContextCapture: ContextCapture,
|
|
765
|
+
capturePageContext: capturePageContext,
|
|
766
|
+
capturePerformance: capturePerformance,
|
|
767
|
+
captureScreenshot: captureScreenshot
|
|
768
|
+
});
|
|
769
|
+
|
|
726
770
|
/**
|
|
727
771
|
* Error Capture Module
|
|
728
772
|
* Automatic error capture and reporting
|
|
@@ -2898,7 +2942,7 @@ class SessionRecording {
|
|
|
2898
2942
|
const isSensitive = this.isSensitiveElement(target);
|
|
2899
2943
|
this.addEvent('input', {
|
|
2900
2944
|
selector: this.getElementSelector(target),
|
|
2901
|
-
value: isSensitive ? '
|
|
2945
|
+
value: isSensitive ? '********' : target.value?.substring(0, 100),
|
|
2902
2946
|
type: target.type || 'text',
|
|
2903
2947
|
});
|
|
2904
2948
|
};
|
|
@@ -3481,6 +3525,9 @@ class AnalyticsTracker {
|
|
|
3481
3525
|
this.scrollEvents = 0;
|
|
3482
3526
|
this.lastScrollTime = 0;
|
|
3483
3527
|
this.isFirstPageView = true;
|
|
3528
|
+
// Session recording upload timer (every 10 minutes)
|
|
3529
|
+
this.recordingUploadTimer = null;
|
|
3530
|
+
this.RECORDING_UPLOAD_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
|
3484
3531
|
// ==================== Private Methods - Event Handlers ====================
|
|
3485
3532
|
this.handleClick = (event) => {
|
|
3486
3533
|
const target = event.target;
|
|
@@ -3562,6 +3609,7 @@ class AnalyticsTracker {
|
|
|
3562
3609
|
this.queueInteraction(interaction);
|
|
3563
3610
|
};
|
|
3564
3611
|
this.handlePageUnload = () => {
|
|
3612
|
+
console.log('🎬 [AnalyticsTracker] Page unload detected - saving session recording and interactions');
|
|
3565
3613
|
// Send any pending interactions synchronously
|
|
3566
3614
|
if (this.interactionBuffer.length > 0) {
|
|
3567
3615
|
// Use sendBeacon for reliable delivery
|
|
@@ -3571,11 +3619,51 @@ class AnalyticsTracker {
|
|
|
3571
3619
|
navigator.sendBeacon(url, data);
|
|
3572
3620
|
}
|
|
3573
3621
|
}
|
|
3622
|
+
// Save session recording synchronously before page unloads
|
|
3623
|
+
try {
|
|
3624
|
+
const checkflowInstance = window.checkflow;
|
|
3625
|
+
if (checkflowInstance?.sessionRecording) {
|
|
3626
|
+
const recordingData = checkflowInstance.sessionRecording.getRecordingData();
|
|
3627
|
+
if (recordingData && recordingData.events.length > 0) {
|
|
3628
|
+
console.log('🎬 [AnalyticsTracker] Sending session recording via sendBeacon', {
|
|
3629
|
+
eventCount: recordingData.events.length,
|
|
3630
|
+
duration: recordingData.duration
|
|
3631
|
+
});
|
|
3632
|
+
// Send recording data via sendBeacon
|
|
3633
|
+
const recordingPayload = JSON.stringify({
|
|
3634
|
+
events: recordingData.events,
|
|
3635
|
+
durationMs: recordingData.duration * 1000
|
|
3636
|
+
});
|
|
3637
|
+
const recordingUrl = `${this.apiClient.getBaseUrl()}/api/v1/analytics/sessions/${this.sessionId}/recording`;
|
|
3638
|
+
if (navigator.sendBeacon) {
|
|
3639
|
+
const success = navigator.sendBeacon(recordingUrl, recordingPayload);
|
|
3640
|
+
console.log('🎬 [AnalyticsTracker] sendBeacon recording result:', success);
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
else {
|
|
3644
|
+
console.warn('🎬 [AnalyticsTracker] No recording events to save on unload');
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
else {
|
|
3648
|
+
console.warn('🎬 [AnalyticsTracker] No session recording instance found on unload');
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
catch (error) {
|
|
3652
|
+
console.error('🎬 [AnalyticsTracker] Failed to save recording on unload:', error);
|
|
3653
|
+
}
|
|
3574
3654
|
};
|
|
3575
3655
|
this.handleVisibilityChange = () => {
|
|
3576
3656
|
if (document.hidden) {
|
|
3577
|
-
|
|
3657
|
+
console.log('🎬 [AnalyticsTracker] Tab became hidden - saving data immediately');
|
|
3658
|
+
// Page became hidden - flush interactions AND save session recording
|
|
3578
3659
|
this.flushInteractions();
|
|
3660
|
+
// Save session recording immediately when tab becomes hidden
|
|
3661
|
+
this.saveSessionRecording().catch(error => {
|
|
3662
|
+
console.warn('🎬 [AnalyticsTracker] Failed to save recording on visibility change:', error);
|
|
3663
|
+
});
|
|
3664
|
+
}
|
|
3665
|
+
else {
|
|
3666
|
+
console.log('🎬 [AnalyticsTracker] Tab became visible again');
|
|
3579
3667
|
}
|
|
3580
3668
|
};
|
|
3581
3669
|
this.apiClient = apiClient;
|
|
@@ -3610,6 +3698,8 @@ class AnalyticsTracker {
|
|
|
3610
3698
|
this.setupEventListeners();
|
|
3611
3699
|
// Track initial page view
|
|
3612
3700
|
this.trackPageView();
|
|
3701
|
+
// Start periodic session recording upload timer (every 10 minutes)
|
|
3702
|
+
this.setupPeriodicRecordingUpload();
|
|
3613
3703
|
this.isActive = true;
|
|
3614
3704
|
this.log('Analytics tracking started', { sessionId: this.sessionId });
|
|
3615
3705
|
}
|
|
@@ -3635,6 +3725,10 @@ class AnalyticsTracker {
|
|
|
3635
3725
|
clearTimeout(this.batchTimer);
|
|
3636
3726
|
this.batchTimer = null;
|
|
3637
3727
|
}
|
|
3728
|
+
if (this.recordingUploadTimer) {
|
|
3729
|
+
clearInterval(this.recordingUploadTimer);
|
|
3730
|
+
this.recordingUploadTimer = null;
|
|
3731
|
+
}
|
|
3638
3732
|
// Stop performance observer
|
|
3639
3733
|
if (this.performanceObserver) {
|
|
3640
3734
|
this.performanceObserver.disconnect();
|
|
@@ -3697,6 +3791,24 @@ class AnalyticsTracker {
|
|
|
3697
3791
|
}
|
|
3698
3792
|
}
|
|
3699
3793
|
// ==================== Private Methods - Session Management ====================
|
|
3794
|
+
setupPeriodicRecordingUpload() {
|
|
3795
|
+
console.log('🎬 [AnalyticsTracker] Setting up periodic recording upload (every 10 minutes)');
|
|
3796
|
+
this.recordingUploadTimer = window.setInterval(async () => {
|
|
3797
|
+
try {
|
|
3798
|
+
console.log('🎬 [AnalyticsTracker] Periodic upload triggered');
|
|
3799
|
+
await this.saveSessionRecording();
|
|
3800
|
+
// Clear events from recording to avoid duplicate uploads
|
|
3801
|
+
const checkflowInstance = window.checkflow;
|
|
3802
|
+
if (checkflowInstance?.sessionRecording) {
|
|
3803
|
+
checkflowInstance.sessionRecording.clearEvents();
|
|
3804
|
+
console.log('🎬 [AnalyticsTracker] Cleared recording events after upload');
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
catch (error) {
|
|
3808
|
+
console.error('🎬 [AnalyticsTracker] Periodic recording upload failed:', error);
|
|
3809
|
+
}
|
|
3810
|
+
}, this.RECORDING_UPLOAD_INTERVAL);
|
|
3811
|
+
}
|
|
3700
3812
|
async createSession() {
|
|
3701
3813
|
const sessionData = {
|
|
3702
3814
|
sessionId: this.sessionId,
|
|
@@ -3827,7 +3939,7 @@ class AnalyticsTracker {
|
|
|
3827
3939
|
this.log('Performance tracking setup failed:', error);
|
|
3828
3940
|
}
|
|
3829
3941
|
}
|
|
3830
|
-
trackPageView() {
|
|
3942
|
+
async trackPageView() {
|
|
3831
3943
|
const loadTime = this.isFirstPageView ? this.getPageLoadTime() : undefined;
|
|
3832
3944
|
const domReadyTime = this.isFirstPageView ? this.getDOMReadyTime() : undefined;
|
|
3833
3945
|
const interaction = {
|
|
@@ -3841,6 +3953,30 @@ class AnalyticsTracker {
|
|
|
3841
3953
|
this.isFirstPageView = false;
|
|
3842
3954
|
// Reset scroll tracking for new page
|
|
3843
3955
|
this.scrollEvents = 0;
|
|
3956
|
+
// Capture page screenshot for heatmap background (async, don't wait)
|
|
3957
|
+
this.capturePageScreenshotForHeatmap().catch(error => {
|
|
3958
|
+
console.warn('🖼️ [AnalyticsTracker] Page screenshot capture failed:', error);
|
|
3959
|
+
});
|
|
3960
|
+
}
|
|
3961
|
+
async capturePageScreenshotForHeatmap() {
|
|
3962
|
+
try {
|
|
3963
|
+
console.log('🖼️ [AnalyticsTracker] Capturing page screenshot for heatmap...');
|
|
3964
|
+
// Dynamic import of html2canvas to avoid bundle bloat
|
|
3965
|
+
const { captureScreenshot } = await Promise.resolve().then(function () { return contextCapture; });
|
|
3966
|
+
const screenshot = await captureScreenshot({
|
|
3967
|
+
quality: 60, // Lower quality for heatmap backgrounds
|
|
3968
|
+
fullPage: false, // Capture viewport only for consistency
|
|
3969
|
+
});
|
|
3970
|
+
if (screenshot) {
|
|
3971
|
+
// Upload screenshot to backend for heatmap association
|
|
3972
|
+
await this.apiClient.uploadScreenshotOnly(screenshot, window.location.href);
|
|
3973
|
+
console.log('🖼️ [AnalyticsTracker] Page screenshot uploaded for heatmap');
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
catch (error) {
|
|
3977
|
+
// Don't throw - screenshot capture is optional for heatmaps
|
|
3978
|
+
console.warn('🖼️ [AnalyticsTracker] Could not capture page screenshot:', error);
|
|
3979
|
+
}
|
|
3844
3980
|
}
|
|
3845
3981
|
trackPageLoadPerformance(timing) {
|
|
3846
3982
|
const loadTime = Math.round(timing.loadEventEnd - timing.loadEventStart);
|