@grainql/analytics-web 2.5.4 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/activity.js +1 -1
- package/dist/cjs/activity.js +1 -1
- package/dist/cjs/activity.js.map +1 -1
- package/dist/cjs/consent.js +4 -4
- package/dist/cjs/consent.js.map +1 -1
- package/dist/cjs/heartbeat.d.ts.map +1 -1
- package/dist/cjs/heartbeat.js +0 -6
- package/dist/cjs/heartbeat.js.map +1 -1
- package/dist/cjs/heatmap-tracking.d.ts +90 -0
- package/dist/cjs/heatmap-tracking.d.ts.map +1 -0
- package/dist/cjs/heatmap-tracking.js +465 -0
- package/dist/cjs/heatmap-tracking.js.map +1 -0
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interaction-tracking.d.ts.map +1 -1
- package/dist/cjs/interaction-tracking.js +9 -18
- package/dist/cjs/interaction-tracking.js.map +1 -1
- package/dist/cjs/page-tracking.d.ts.map +1 -1
- package/dist/cjs/page-tracking.js +0 -9
- package/dist/cjs/page-tracking.js.map +1 -1
- package/dist/cjs/section-tracking.d.ts.map +1 -1
- package/dist/cjs/section-tracking.js +1 -7
- package/dist/cjs/section-tracking.js.map +1 -1
- package/dist/cjs/types/heatmap-tracking.d.ts +41 -0
- package/dist/cjs/types/heatmap-tracking.d.ts.map +1 -0
- package/dist/cjs/types/heatmap-tracking.js +6 -0
- package/dist/cjs/types/heatmap-tracking.js.map +1 -0
- package/dist/consent.js +4 -4
- package/dist/esm/activity.js +1 -1
- package/dist/esm/activity.js.map +1 -1
- package/dist/esm/consent.js +4 -4
- package/dist/esm/consent.js.map +1 -1
- package/dist/esm/heartbeat.d.ts.map +1 -1
- package/dist/esm/heartbeat.js +0 -6
- package/dist/esm/heartbeat.js.map +1 -1
- package/dist/esm/heatmap-tracking.d.ts +90 -0
- package/dist/esm/heatmap-tracking.d.ts.map +1 -0
- package/dist/esm/heatmap-tracking.js +461 -0
- package/dist/esm/heatmap-tracking.js.map +1 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interaction-tracking.d.ts.map +1 -1
- package/dist/esm/interaction-tracking.js +9 -18
- package/dist/esm/interaction-tracking.js.map +1 -1
- package/dist/esm/page-tracking.d.ts.map +1 -1
- package/dist/esm/page-tracking.js +0 -9
- package/dist/esm/page-tracking.js.map +1 -1
- package/dist/esm/section-tracking.d.ts.map +1 -1
- package/dist/esm/section-tracking.js +1 -7
- package/dist/esm/section-tracking.js.map +1 -1
- package/dist/esm/types/heatmap-tracking.d.ts +41 -0
- package/dist/esm/types/heatmap-tracking.d.ts.map +1 -0
- package/dist/esm/types/heatmap-tracking.js +5 -0
- package/dist/esm/types/heatmap-tracking.js.map +1 -0
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +0 -6
- package/dist/heatmap-tracking.d.ts +90 -0
- package/dist/heatmap-tracking.d.ts.map +1 -0
- package/dist/heatmap-tracking.js +465 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +503 -79
- package/dist/index.global.dev.js.map +4 -4
- package/dist/index.global.js +2 -2
- package/dist/index.global.js.map +4 -4
- package/dist/index.js +61 -38
- package/dist/index.mjs +61 -38
- package/dist/interaction-tracking.d.ts.map +1 -1
- package/dist/interaction-tracking.js +9 -18
- package/dist/page-tracking.d.ts.map +1 -1
- package/dist/page-tracking.js +0 -9
- package/dist/section-tracking.d.ts.map +1 -1
- package/dist/section-tracking.js +1 -7
- package/dist/types/heatmap-tracking.d.ts +41 -0
- package/dist/types/heatmap-tracking.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.global.dev.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Grain Analytics Web SDK v2.
|
|
1
|
+
/* Grain Analytics Web SDK v2.6.0 | MIT License | Development Build */
|
|
2
2
|
"use strict";
|
|
3
3
|
var Grain = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -22,6 +22,438 @@ var Grain = (() => {
|
|
|
22
22
|
};
|
|
23
23
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
24
|
|
|
25
|
+
// src/heatmap-tracking.ts
|
|
26
|
+
var heatmap_tracking_exports = {};
|
|
27
|
+
__export(heatmap_tracking_exports, {
|
|
28
|
+
HeatmapTrackingManager: () => HeatmapTrackingManager
|
|
29
|
+
});
|
|
30
|
+
var DEFAULT_OPTIONS, HeatmapTrackingManager;
|
|
31
|
+
var init_heatmap_tracking = __esm({
|
|
32
|
+
"src/heatmap-tracking.ts"() {
|
|
33
|
+
"use strict";
|
|
34
|
+
DEFAULT_OPTIONS = {
|
|
35
|
+
scrollDebounceDelay: 100,
|
|
36
|
+
batchDelay: 2e3,
|
|
37
|
+
maxBatchSize: 20,
|
|
38
|
+
debug: false
|
|
39
|
+
};
|
|
40
|
+
HeatmapTrackingManager = class {
|
|
41
|
+
// 3 seconds - same as section tracking
|
|
42
|
+
constructor(tracker, options = {}) {
|
|
43
|
+
this.isDestroyed = false;
|
|
44
|
+
// Tracking state
|
|
45
|
+
this.currentScrollState = null;
|
|
46
|
+
this.pendingClicks = [];
|
|
47
|
+
this.pendingScrolls = [];
|
|
48
|
+
// Timers
|
|
49
|
+
this.scrollDebounceTimer = null;
|
|
50
|
+
this.batchTimer = null;
|
|
51
|
+
this.scrollTrackingTimer = null;
|
|
52
|
+
this.periodicScrollTimer = null;
|
|
53
|
+
// Scroll tracking
|
|
54
|
+
this.lastScrollPosition = 0;
|
|
55
|
+
this.lastScrollTime = Date.now();
|
|
56
|
+
this.SPLIT_DURATION = 3e3;
|
|
57
|
+
this.tracker = tracker;
|
|
58
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
59
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
60
|
+
if (document.readyState === "loading") {
|
|
61
|
+
document.addEventListener("DOMContentLoaded", () => this.initialize());
|
|
62
|
+
} else {
|
|
63
|
+
setTimeout(() => this.initialize(), 0);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Initialize heatmap tracking
|
|
69
|
+
*/
|
|
70
|
+
initialize() {
|
|
71
|
+
if (this.isDestroyed)
|
|
72
|
+
return;
|
|
73
|
+
this.log("Initializing heatmap tracking");
|
|
74
|
+
this.setupClickTracking();
|
|
75
|
+
this.setupScrollTracking();
|
|
76
|
+
this.startScrollTracking();
|
|
77
|
+
this.setupUnloadHandler();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Setup click event tracking
|
|
81
|
+
*/
|
|
82
|
+
setupClickTracking() {
|
|
83
|
+
if (typeof document === "undefined")
|
|
84
|
+
return;
|
|
85
|
+
const clickHandler = (event) => {
|
|
86
|
+
if (this.isDestroyed)
|
|
87
|
+
return;
|
|
88
|
+
if (!this.tracker.hasConsent("analytics"))
|
|
89
|
+
return;
|
|
90
|
+
this.handleClick(event);
|
|
91
|
+
};
|
|
92
|
+
document.addEventListener("click", clickHandler, { passive: true, capture: true });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Setup scroll event tracking
|
|
96
|
+
*/
|
|
97
|
+
setupScrollTracking() {
|
|
98
|
+
if (typeof window === "undefined")
|
|
99
|
+
return;
|
|
100
|
+
const scrollHandler = () => {
|
|
101
|
+
if (this.scrollDebounceTimer !== null) {
|
|
102
|
+
clearTimeout(this.scrollDebounceTimer);
|
|
103
|
+
}
|
|
104
|
+
this.scrollDebounceTimer = window.setTimeout(() => {
|
|
105
|
+
this.handleScroll();
|
|
106
|
+
this.scrollDebounceTimer = null;
|
|
107
|
+
}, this.options.scrollDebounceDelay);
|
|
108
|
+
};
|
|
109
|
+
window.addEventListener("scroll", scrollHandler, { passive: true });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Start periodic scroll state tracking
|
|
113
|
+
*/
|
|
114
|
+
startScrollTracking() {
|
|
115
|
+
if (typeof window === "undefined")
|
|
116
|
+
return;
|
|
117
|
+
this.updateScrollState();
|
|
118
|
+
this.scrollTrackingTimer = window.setInterval(() => {
|
|
119
|
+
if (this.isDestroyed)
|
|
120
|
+
return;
|
|
121
|
+
this.updateScrollState();
|
|
122
|
+
}, 500);
|
|
123
|
+
this.startPeriodicScrollTracking();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Start periodic scroll tracking (sends events every 3 seconds)
|
|
127
|
+
*/
|
|
128
|
+
startPeriodicScrollTracking() {
|
|
129
|
+
if (typeof window === "undefined")
|
|
130
|
+
return;
|
|
131
|
+
this.periodicScrollTimer = window.setInterval(() => {
|
|
132
|
+
if (this.isDestroyed || !this.currentScrollState)
|
|
133
|
+
return;
|
|
134
|
+
if (!this.tracker.hasConsent("analytics"))
|
|
135
|
+
return;
|
|
136
|
+
const currentTime = Date.now();
|
|
137
|
+
const duration = currentTime - this.currentScrollState.entryTime;
|
|
138
|
+
if (duration > 1e3) {
|
|
139
|
+
const scrollY = window.scrollY || window.pageYOffset;
|
|
140
|
+
const viewportHeight = window.innerHeight;
|
|
141
|
+
const pageHeight = document.documentElement.scrollHeight;
|
|
142
|
+
const scrollData = {
|
|
143
|
+
pageUrl: window.location.href,
|
|
144
|
+
viewportSection: this.currentScrollState.viewportSection,
|
|
145
|
+
scrollDepthPx: scrollY,
|
|
146
|
+
durationMs: duration,
|
|
147
|
+
entryTimestamp: this.currentScrollState.entryTime,
|
|
148
|
+
exitTimestamp: currentTime,
|
|
149
|
+
pageHeight,
|
|
150
|
+
viewportHeight
|
|
151
|
+
};
|
|
152
|
+
this.tracker.trackSystemEvent("_grain_heatmap_scroll", {
|
|
153
|
+
page_url: scrollData.pageUrl,
|
|
154
|
+
viewport_section: scrollData.viewportSection,
|
|
155
|
+
scroll_depth_px: scrollData.scrollDepthPx,
|
|
156
|
+
duration_ms: scrollData.durationMs,
|
|
157
|
+
entry_timestamp: scrollData.entryTimestamp,
|
|
158
|
+
exit_timestamp: scrollData.exitTimestamp,
|
|
159
|
+
page_height: scrollData.pageHeight,
|
|
160
|
+
viewport_height: scrollData.viewportHeight,
|
|
161
|
+
is_split: true
|
|
162
|
+
// Flag to indicate periodic tracking, not final exit
|
|
163
|
+
}, { flush: true });
|
|
164
|
+
this.currentScrollState.entryTime = currentTime;
|
|
165
|
+
}
|
|
166
|
+
}, this.SPLIT_DURATION);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Setup page unload handler to beacon remaining data
|
|
170
|
+
*/
|
|
171
|
+
setupUnloadHandler() {
|
|
172
|
+
if (typeof window === "undefined")
|
|
173
|
+
return;
|
|
174
|
+
const unloadHandler = () => {
|
|
175
|
+
if (this.currentScrollState) {
|
|
176
|
+
const currentTime = Date.now();
|
|
177
|
+
const duration = currentTime - this.currentScrollState.entryTime;
|
|
178
|
+
if (duration > 100) {
|
|
179
|
+
const scrollData = {
|
|
180
|
+
pageUrl: window.location.href,
|
|
181
|
+
viewportSection: this.currentScrollState.viewportSection,
|
|
182
|
+
scrollDepthPx: this.currentScrollState.scrollDepthPx,
|
|
183
|
+
durationMs: duration,
|
|
184
|
+
entryTimestamp: this.currentScrollState.entryTime,
|
|
185
|
+
exitTimestamp: currentTime,
|
|
186
|
+
pageHeight: document.documentElement.scrollHeight,
|
|
187
|
+
viewportHeight: window.innerHeight
|
|
188
|
+
};
|
|
189
|
+
this.pendingScrolls.push(scrollData);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
this.flushPendingEventsWithBeacon();
|
|
193
|
+
};
|
|
194
|
+
window.addEventListener("beforeunload", unloadHandler);
|
|
195
|
+
window.addEventListener("pagehide", unloadHandler);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Handle click event
|
|
199
|
+
*/
|
|
200
|
+
handleClick(event) {
|
|
201
|
+
if (!this.tracker.hasConsent("analytics"))
|
|
202
|
+
return;
|
|
203
|
+
const element = event.target;
|
|
204
|
+
if (!element)
|
|
205
|
+
return;
|
|
206
|
+
const pageUrl = window.location.href;
|
|
207
|
+
const xpath = this.generateXPath(element);
|
|
208
|
+
const viewportX = Math.round(event.clientX);
|
|
209
|
+
const viewportY = Math.round(event.clientY);
|
|
210
|
+
const pageX = Math.round(event.pageX);
|
|
211
|
+
const pageY = Math.round(event.pageY);
|
|
212
|
+
const elementTag = element.tagName?.toLowerCase() || "unknown";
|
|
213
|
+
const elementText = element.textContent?.trim().substring(0, 100);
|
|
214
|
+
const clickData = {
|
|
215
|
+
pageUrl,
|
|
216
|
+
xpath,
|
|
217
|
+
viewportX,
|
|
218
|
+
viewportY,
|
|
219
|
+
pageX,
|
|
220
|
+
pageY,
|
|
221
|
+
elementTag,
|
|
222
|
+
elementText: elementText || void 0,
|
|
223
|
+
timestamp: Date.now()
|
|
224
|
+
};
|
|
225
|
+
const isNavigationLink = element instanceof HTMLAnchorElement && element.href;
|
|
226
|
+
if (isNavigationLink) {
|
|
227
|
+
this.tracker.trackSystemEvent("_grain_heatmap_click", {
|
|
228
|
+
page_url: clickData.pageUrl,
|
|
229
|
+
xpath: clickData.xpath,
|
|
230
|
+
viewport_x: clickData.viewportX,
|
|
231
|
+
viewport_y: clickData.viewportY,
|
|
232
|
+
page_x: clickData.pageX,
|
|
233
|
+
page_y: clickData.pageY,
|
|
234
|
+
element_tag: clickData.elementTag,
|
|
235
|
+
element_text: clickData.elementText,
|
|
236
|
+
timestamp: clickData.timestamp
|
|
237
|
+
}, { flush: true });
|
|
238
|
+
} else {
|
|
239
|
+
this.pendingClicks.push(clickData);
|
|
240
|
+
this.considerBatchFlush();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Handle scroll event
|
|
245
|
+
*/
|
|
246
|
+
handleScroll() {
|
|
247
|
+
if (!this.tracker.hasConsent("analytics"))
|
|
248
|
+
return;
|
|
249
|
+
this.updateScrollState();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Update current scroll state
|
|
253
|
+
*/
|
|
254
|
+
updateScrollState() {
|
|
255
|
+
if (typeof window === "undefined")
|
|
256
|
+
return;
|
|
257
|
+
if (!this.tracker.hasConsent("analytics"))
|
|
258
|
+
return;
|
|
259
|
+
const currentTime = Date.now();
|
|
260
|
+
const scrollY = window.scrollY || window.pageYOffset;
|
|
261
|
+
const viewportHeight = window.innerHeight;
|
|
262
|
+
const pageHeight = document.documentElement.scrollHeight;
|
|
263
|
+
const viewportSection = Math.floor(scrollY / viewportHeight);
|
|
264
|
+
if (this.currentScrollState && this.currentScrollState.viewportSection !== viewportSection) {
|
|
265
|
+
const duration = currentTime - this.currentScrollState.entryTime;
|
|
266
|
+
if (duration > 100) {
|
|
267
|
+
const scrollData = {
|
|
268
|
+
pageUrl: window.location.href,
|
|
269
|
+
viewportSection: this.currentScrollState.viewportSection,
|
|
270
|
+
scrollDepthPx: this.currentScrollState.scrollDepthPx,
|
|
271
|
+
durationMs: duration,
|
|
272
|
+
entryTimestamp: this.currentScrollState.entryTime,
|
|
273
|
+
exitTimestamp: currentTime,
|
|
274
|
+
pageHeight,
|
|
275
|
+
viewportHeight
|
|
276
|
+
};
|
|
277
|
+
this.pendingScrolls.push(scrollData);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (!this.currentScrollState || this.currentScrollState.viewportSection !== viewportSection) {
|
|
281
|
+
this.currentScrollState = {
|
|
282
|
+
viewportSection,
|
|
283
|
+
entryTime: currentTime,
|
|
284
|
+
scrollDepthPx: scrollY
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
this.lastScrollPosition = scrollY;
|
|
288
|
+
this.lastScrollTime = currentTime;
|
|
289
|
+
this.considerBatchFlush();
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Generate XPath for an element
|
|
293
|
+
*/
|
|
294
|
+
generateXPath(element) {
|
|
295
|
+
if (!element)
|
|
296
|
+
return "";
|
|
297
|
+
if (element.id) {
|
|
298
|
+
return `//*[@id="${element.id}"]`;
|
|
299
|
+
}
|
|
300
|
+
const paths = [];
|
|
301
|
+
let currentElement = element;
|
|
302
|
+
while (currentElement && currentElement.nodeType === Node.ELEMENT_NODE) {
|
|
303
|
+
let index = 0;
|
|
304
|
+
let sibling = currentElement;
|
|
305
|
+
while (sibling) {
|
|
306
|
+
sibling = sibling.previousElementSibling;
|
|
307
|
+
if (sibling && sibling.nodeName === currentElement.nodeName) {
|
|
308
|
+
index++;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const tagName = currentElement.nodeName.toLowerCase();
|
|
312
|
+
const pathIndex = index > 0 ? `[${index + 1}]` : "";
|
|
313
|
+
paths.unshift(`${tagName}${pathIndex}`);
|
|
314
|
+
currentElement = currentElement.parentElement;
|
|
315
|
+
}
|
|
316
|
+
return paths.length ? `/${paths.join("/")}` : "";
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Consider flushing batched events
|
|
320
|
+
*/
|
|
321
|
+
considerBatchFlush() {
|
|
322
|
+
const totalEvents = this.pendingClicks.length + this.pendingScrolls.length;
|
|
323
|
+
if (totalEvents >= this.options.maxBatchSize) {
|
|
324
|
+
this.flushPendingEvents();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (this.batchTimer === null && totalEvents > 0) {
|
|
328
|
+
this.batchTimer = window.setTimeout(() => {
|
|
329
|
+
this.flushPendingEvents();
|
|
330
|
+
this.batchTimer = null;
|
|
331
|
+
}, this.options.batchDelay);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Flush pending events
|
|
336
|
+
*/
|
|
337
|
+
flushPendingEvents() {
|
|
338
|
+
if (this.isDestroyed)
|
|
339
|
+
return;
|
|
340
|
+
if (!this.tracker.hasConsent("analytics")) {
|
|
341
|
+
this.pendingClicks = [];
|
|
342
|
+
this.pendingScrolls = [];
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (this.pendingClicks.length > 0) {
|
|
346
|
+
for (const clickData of this.pendingClicks) {
|
|
347
|
+
this.tracker.trackSystemEvent("_grain_heatmap_click", {
|
|
348
|
+
page_url: clickData.pageUrl,
|
|
349
|
+
xpath: clickData.xpath,
|
|
350
|
+
viewport_x: clickData.viewportX,
|
|
351
|
+
viewport_y: clickData.viewportY,
|
|
352
|
+
page_x: clickData.pageX,
|
|
353
|
+
page_y: clickData.pageY,
|
|
354
|
+
element_tag: clickData.elementTag,
|
|
355
|
+
element_text: clickData.elementText,
|
|
356
|
+
timestamp: clickData.timestamp
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
this.pendingClicks = [];
|
|
360
|
+
}
|
|
361
|
+
if (this.pendingScrolls.length > 0) {
|
|
362
|
+
for (const scrollData of this.pendingScrolls) {
|
|
363
|
+
this.tracker.trackSystemEvent("_grain_heatmap_scroll", {
|
|
364
|
+
page_url: scrollData.pageUrl,
|
|
365
|
+
viewport_section: scrollData.viewportSection,
|
|
366
|
+
scroll_depth_px: scrollData.scrollDepthPx,
|
|
367
|
+
duration_ms: scrollData.durationMs,
|
|
368
|
+
entry_timestamp: scrollData.entryTimestamp,
|
|
369
|
+
exit_timestamp: scrollData.exitTimestamp,
|
|
370
|
+
page_height: scrollData.pageHeight,
|
|
371
|
+
viewport_height: scrollData.viewportHeight
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
this.pendingScrolls = [];
|
|
375
|
+
}
|
|
376
|
+
if (this.batchTimer !== null) {
|
|
377
|
+
clearTimeout(this.batchTimer);
|
|
378
|
+
this.batchTimer = null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Flush pending events with beacon (for page unload)
|
|
383
|
+
*/
|
|
384
|
+
flushPendingEventsWithBeacon() {
|
|
385
|
+
if (!this.tracker.hasConsent("analytics")) {
|
|
386
|
+
this.pendingClicks = [];
|
|
387
|
+
this.pendingScrolls = [];
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (this.pendingClicks.length > 0) {
|
|
391
|
+
for (const clickData of this.pendingClicks) {
|
|
392
|
+
this.tracker.trackSystemEvent("_grain_heatmap_click", {
|
|
393
|
+
page_url: clickData.pageUrl,
|
|
394
|
+
xpath: clickData.xpath,
|
|
395
|
+
viewport_x: clickData.viewportX,
|
|
396
|
+
viewport_y: clickData.viewportY,
|
|
397
|
+
page_x: clickData.pageX,
|
|
398
|
+
page_y: clickData.pageY,
|
|
399
|
+
element_tag: clickData.elementTag,
|
|
400
|
+
element_text: clickData.elementText,
|
|
401
|
+
timestamp: clickData.timestamp
|
|
402
|
+
}, { flush: true });
|
|
403
|
+
}
|
|
404
|
+
this.pendingClicks = [];
|
|
405
|
+
}
|
|
406
|
+
if (this.pendingScrolls.length > 0) {
|
|
407
|
+
for (const scrollData of this.pendingScrolls) {
|
|
408
|
+
this.tracker.trackSystemEvent("_grain_heatmap_scroll", {
|
|
409
|
+
page_url: scrollData.pageUrl,
|
|
410
|
+
viewport_section: scrollData.viewportSection,
|
|
411
|
+
scroll_depth_px: scrollData.scrollDepthPx,
|
|
412
|
+
duration_ms: scrollData.durationMs,
|
|
413
|
+
entry_timestamp: scrollData.entryTimestamp,
|
|
414
|
+
exit_timestamp: scrollData.exitTimestamp,
|
|
415
|
+
page_height: scrollData.pageHeight,
|
|
416
|
+
viewport_height: scrollData.viewportHeight
|
|
417
|
+
}, { flush: true });
|
|
418
|
+
}
|
|
419
|
+
this.pendingScrolls = [];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Log debug message
|
|
424
|
+
*/
|
|
425
|
+
log(...args) {
|
|
426
|
+
if (this.options.debug) {
|
|
427
|
+
this.tracker.log("[Heatmap Tracking]", ...args);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Destroy the tracking manager
|
|
432
|
+
*/
|
|
433
|
+
destroy() {
|
|
434
|
+
this.isDestroyed = true;
|
|
435
|
+
if (this.scrollDebounceTimer !== null) {
|
|
436
|
+
clearTimeout(this.scrollDebounceTimer);
|
|
437
|
+
this.scrollDebounceTimer = null;
|
|
438
|
+
}
|
|
439
|
+
if (this.batchTimer !== null) {
|
|
440
|
+
clearTimeout(this.batchTimer);
|
|
441
|
+
this.batchTimer = null;
|
|
442
|
+
}
|
|
443
|
+
if (this.scrollTrackingTimer !== null) {
|
|
444
|
+
clearInterval(this.scrollTrackingTimer);
|
|
445
|
+
this.scrollTrackingTimer = null;
|
|
446
|
+
}
|
|
447
|
+
if (this.periodicScrollTimer !== null) {
|
|
448
|
+
clearInterval(this.periodicScrollTimer);
|
|
449
|
+
this.periodicScrollTimer = null;
|
|
450
|
+
}
|
|
451
|
+
this.flushPendingEvents();
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
25
457
|
// src/interaction-tracking.ts
|
|
26
458
|
var interaction_tracking_exports = {};
|
|
27
459
|
__export(interaction_tracking_exports, {
|
|
@@ -62,7 +494,7 @@ var Grain = (() => {
|
|
|
62
494
|
attachAllListeners() {
|
|
63
495
|
if (this.isDestroyed)
|
|
64
496
|
return;
|
|
65
|
-
this.log("Attaching interaction listeners
|
|
497
|
+
this.log("Attaching interaction listeners");
|
|
66
498
|
for (const interaction of this.interactions) {
|
|
67
499
|
this.attachInteractionListener(interaction);
|
|
68
500
|
}
|
|
@@ -92,7 +524,6 @@ var Grain = (() => {
|
|
|
92
524
|
handlers.push({ event: "focus", handler: focusHandler });
|
|
93
525
|
}
|
|
94
526
|
this.attachedListeners.set(element, handlers);
|
|
95
|
-
this.log("Attached listeners to element for:", interaction.eventName);
|
|
96
527
|
}
|
|
97
528
|
/**
|
|
98
529
|
* Handle click event on interaction
|
|
@@ -116,17 +547,12 @@ var Grain = (() => {
|
|
|
116
547
|
...isNavigationLink && { href: element.href },
|
|
117
548
|
timestamp: Date.now()
|
|
118
549
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
this.tracker.track(interaction.eventName, eventProperties);
|
|
550
|
+
const result = this.tracker.track(interaction.eventName, eventProperties, { flush: true });
|
|
551
|
+
if (result instanceof Promise) {
|
|
552
|
+
result.catch((error) => {
|
|
553
|
+
this.log("Failed to track click:", error);
|
|
554
|
+
});
|
|
128
555
|
}
|
|
129
|
-
this.log("Tracked click interaction:", interaction.eventName);
|
|
130
556
|
}
|
|
131
557
|
/**
|
|
132
558
|
* Handle focus event on interaction (for form fields)
|
|
@@ -147,7 +573,6 @@ var Grain = (() => {
|
|
|
147
573
|
element_class: element.className || void 0,
|
|
148
574
|
timestamp: Date.now()
|
|
149
575
|
});
|
|
150
|
-
this.log("Tracked focus interaction:", interaction.eventName);
|
|
151
576
|
}
|
|
152
577
|
/**
|
|
153
578
|
* Find element by XPath selector
|
|
@@ -241,7 +666,6 @@ var Grain = (() => {
|
|
|
241
666
|
element.removeEventListener(event, handler);
|
|
242
667
|
});
|
|
243
668
|
this.attachedListeners.delete(element);
|
|
244
|
-
this.log("Detached listeners from element");
|
|
245
669
|
}
|
|
246
670
|
/**
|
|
247
671
|
* Log debug messages
|
|
@@ -296,11 +720,11 @@ var Grain = (() => {
|
|
|
296
720
|
__export(section_tracking_exports, {
|
|
297
721
|
SectionTrackingManager: () => SectionTrackingManager
|
|
298
722
|
});
|
|
299
|
-
var
|
|
723
|
+
var DEFAULT_OPTIONS2, SectionTrackingManager;
|
|
300
724
|
var init_section_tracking = __esm({
|
|
301
725
|
"src/section-tracking.ts"() {
|
|
302
726
|
"use strict";
|
|
303
|
-
|
|
727
|
+
DEFAULT_OPTIONS2 = {
|
|
304
728
|
minDwellTime: 1e3,
|
|
305
729
|
// 1 second minimum
|
|
306
730
|
scrollVelocityThreshold: 500,
|
|
@@ -334,7 +758,7 @@ var Grain = (() => {
|
|
|
334
758
|
this.SPLIT_DURATION = 3e3;
|
|
335
759
|
this.tracker = tracker;
|
|
336
760
|
this.sections = sections;
|
|
337
|
-
this.options = { ...
|
|
761
|
+
this.options = { ...DEFAULT_OPTIONS2, ...options };
|
|
338
762
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
339
763
|
if (document.readyState === "loading") {
|
|
340
764
|
document.addEventListener("DOMContentLoaded", () => this.initialize());
|
|
@@ -349,7 +773,7 @@ var Grain = (() => {
|
|
|
349
773
|
initialize() {
|
|
350
774
|
if (this.isDestroyed)
|
|
351
775
|
return;
|
|
352
|
-
this.log("Initializing section tracking
|
|
776
|
+
this.log("Initializing section tracking");
|
|
353
777
|
this.setupIntersectionObserver();
|
|
354
778
|
this.setupScrollListener();
|
|
355
779
|
this.initializeSections();
|
|
@@ -419,7 +843,6 @@ var Grain = (() => {
|
|
|
419
843
|
if (this.intersectionObserver) {
|
|
420
844
|
this.intersectionObserver.observe(element);
|
|
421
845
|
}
|
|
422
|
-
this.log("Section initialized and observed:", section.sectionName);
|
|
423
846
|
}
|
|
424
847
|
}
|
|
425
848
|
/**
|
|
@@ -449,7 +872,6 @@ var Grain = (() => {
|
|
|
449
872
|
* Handle section entry (became visible)
|
|
450
873
|
*/
|
|
451
874
|
handleSectionEntry(state) {
|
|
452
|
-
this.log("Section entered view:", state.config.sectionName);
|
|
453
875
|
state.entryTime = Date.now();
|
|
454
876
|
state.entryScrollSpeed = this.scrollVelocity;
|
|
455
877
|
state.lastScrollPosition = window.scrollY;
|
|
@@ -500,7 +922,6 @@ var Grain = (() => {
|
|
|
500
922
|
is_split: true
|
|
501
923
|
// Flag to indicate this is a periodic split, not final exit
|
|
502
924
|
});
|
|
503
|
-
this.log("Tracked periodic section view split:", state.config.sectionName, "duration:", duration);
|
|
504
925
|
state.entryTime = now;
|
|
505
926
|
state.entryScrollSpeed = this.scrollVelocity;
|
|
506
927
|
}
|
|
@@ -522,7 +943,6 @@ var Grain = (() => {
|
|
|
522
943
|
* Handle section exit (became invisible)
|
|
523
944
|
*/
|
|
524
945
|
handleSectionExit(state) {
|
|
525
|
-
this.log("Section exited view:", state.config.sectionName);
|
|
526
946
|
this.stopPeriodicTracking(state.config.sectionName);
|
|
527
947
|
if (state.entryTime === null)
|
|
528
948
|
return;
|
|
@@ -604,7 +1024,6 @@ var Grain = (() => {
|
|
|
604
1024
|
*/
|
|
605
1025
|
queueSectionView(viewData) {
|
|
606
1026
|
this.pendingEvents.push(viewData);
|
|
607
|
-
this.log("Queued section view:", viewData.sectionName, "duration:", viewData.duration);
|
|
608
1027
|
if (this.batchTimer === null) {
|
|
609
1028
|
this.batchTimer = window.setTimeout(() => {
|
|
610
1029
|
this.flushPendingEvents();
|
|
@@ -621,7 +1040,6 @@ var Grain = (() => {
|
|
|
621
1040
|
this.pendingEvents = [];
|
|
622
1041
|
return;
|
|
623
1042
|
}
|
|
624
|
-
this.log("Flushing", this.pendingEvents.length, "section view events");
|
|
625
1043
|
for (const viewData of this.pendingEvents) {
|
|
626
1044
|
this.tracker.trackSystemEvent("_grain_section_view", {
|
|
627
1045
|
section_name: viewData.sectionName,
|
|
@@ -782,7 +1200,6 @@ var Grain = (() => {
|
|
|
782
1200
|
this.saveConsentState();
|
|
783
1201
|
}
|
|
784
1202
|
} catch (error) {
|
|
785
|
-
console.error("[Grain Consent] Failed to load consent state:", error);
|
|
786
1203
|
}
|
|
787
1204
|
}
|
|
788
1205
|
/**
|
|
@@ -794,7 +1211,6 @@ var Grain = (() => {
|
|
|
794
1211
|
try {
|
|
795
1212
|
localStorage.setItem(this.storageKey, JSON.stringify(this.consentState));
|
|
796
1213
|
} catch (error) {
|
|
797
|
-
console.error("[Grain Consent] Failed to save consent state:", error);
|
|
798
1214
|
}
|
|
799
1215
|
}
|
|
800
1216
|
/**
|
|
@@ -895,7 +1311,6 @@ var Grain = (() => {
|
|
|
895
1311
|
try {
|
|
896
1312
|
listener(this.consentState);
|
|
897
1313
|
} catch (error) {
|
|
898
|
-
console.error("[Grain Consent] Listener error:", error);
|
|
899
1314
|
}
|
|
900
1315
|
});
|
|
901
1316
|
}
|
|
@@ -909,7 +1324,6 @@ var Grain = (() => {
|
|
|
909
1324
|
localStorage.removeItem(this.storageKey);
|
|
910
1325
|
this.consentState = null;
|
|
911
1326
|
} catch (error) {
|
|
912
|
-
console.error("[Grain Consent] Failed to clear consent:", error);
|
|
913
1327
|
}
|
|
914
1328
|
}
|
|
915
1329
|
};
|
|
@@ -1089,7 +1503,6 @@ var Grain = (() => {
|
|
|
1089
1503
|
try {
|
|
1090
1504
|
listener();
|
|
1091
1505
|
} catch (error) {
|
|
1092
|
-
console.error("[Activity Detector] Listener error:", error);
|
|
1093
1506
|
}
|
|
1094
1507
|
}
|
|
1095
1508
|
}
|
|
@@ -1190,9 +1603,6 @@ var Grain = (() => {
|
|
|
1190
1603
|
}
|
|
1191
1604
|
this.tracker.trackSystemEvent("_grain_heartbeat", properties);
|
|
1192
1605
|
this.lastHeartbeatTime = now;
|
|
1193
|
-
if (this.config.debug) {
|
|
1194
|
-
console.log("[Heartbeat] Sent heartbeat:", properties);
|
|
1195
|
-
}
|
|
1196
1606
|
}
|
|
1197
1607
|
/**
|
|
1198
1608
|
* Destroy the heartbeat manager
|
|
@@ -1205,9 +1615,6 @@ var Grain = (() => {
|
|
|
1205
1615
|
this.heartbeatTimer = null;
|
|
1206
1616
|
}
|
|
1207
1617
|
this.isDestroyed = true;
|
|
1208
|
-
if (this.config.debug) {
|
|
1209
|
-
console.log("[Heartbeat] Destroyed");
|
|
1210
|
-
}
|
|
1211
1618
|
}
|
|
1212
1619
|
};
|
|
1213
1620
|
|
|
@@ -4408,9 +4815,6 @@ var Grain = (() => {
|
|
|
4408
4815
|
properties.viewport = `${window.innerWidth}x${window.innerHeight}`;
|
|
4409
4816
|
}
|
|
4410
4817
|
this.tracker.trackSystemEvent("page_view", properties);
|
|
4411
|
-
if (this.config.debug) {
|
|
4412
|
-
console.log("[Page Tracking] Tracked page view:", properties);
|
|
4413
|
-
}
|
|
4414
4818
|
}
|
|
4415
4819
|
/**
|
|
4416
4820
|
* Extract domain from URL
|
|
@@ -4533,9 +4937,6 @@ var Grain = (() => {
|
|
|
4533
4937
|
}
|
|
4534
4938
|
}
|
|
4535
4939
|
this.tracker.trackSystemEvent("page_view", baseProperties);
|
|
4536
|
-
if (this.config.debug) {
|
|
4537
|
-
console.log("[Page Tracking] Manually tracked page:", baseProperties);
|
|
4538
|
-
}
|
|
4539
4940
|
}
|
|
4540
4941
|
/**
|
|
4541
4942
|
* Get page view count for current session
|
|
@@ -4562,9 +4963,6 @@ var Grain = (() => {
|
|
|
4562
4963
|
window.removeEventListener("hashchange", this.handleHashChange);
|
|
4563
4964
|
}
|
|
4564
4965
|
this.isDestroyed = true;
|
|
4565
|
-
if (this.config.debug) {
|
|
4566
|
-
console.log("[Page Tracking] Destroyed");
|
|
4567
|
-
}
|
|
4568
4966
|
}
|
|
4569
4967
|
};
|
|
4570
4968
|
|
|
@@ -4592,6 +4990,7 @@ var Grain = (() => {
|
|
|
4592
4990
|
// Auto-tracking properties
|
|
4593
4991
|
this.interactionTrackingManager = null;
|
|
4594
4992
|
this.sectionTrackingManager = null;
|
|
4993
|
+
this.heatmapTrackingManager = null;
|
|
4595
4994
|
// Session tracking
|
|
4596
4995
|
this.sessionStartTime = Date.now();
|
|
4597
4996
|
this.sessionEventCount = 0;
|
|
@@ -4627,6 +5026,8 @@ var Grain = (() => {
|
|
|
4627
5026
|
// 5 minutes
|
|
4628
5027
|
enableAutoPageView: true,
|
|
4629
5028
|
stripQueryParams: true,
|
|
5029
|
+
// Heatmap Tracking defaults
|
|
5030
|
+
enableHeatmapTracking: true,
|
|
4630
5031
|
...config,
|
|
4631
5032
|
tenantId: config.tenantId
|
|
4632
5033
|
};
|
|
@@ -4649,6 +5050,9 @@ var Grain = (() => {
|
|
|
4649
5050
|
if (typeof window !== "undefined") {
|
|
4650
5051
|
this.initializeAutomaticTracking();
|
|
4651
5052
|
this.trackSessionStart();
|
|
5053
|
+
if (this.config.enableHeatmapTracking) {
|
|
5054
|
+
this.initializeHeatmapTracking();
|
|
5055
|
+
}
|
|
4652
5056
|
}
|
|
4653
5057
|
this.consentManager.addListener((state) => {
|
|
4654
5058
|
if (state.granted) {
|
|
@@ -4872,6 +5276,8 @@ var Grain = (() => {
|
|
|
4872
5276
|
* Log formatted error gracefully
|
|
4873
5277
|
*/
|
|
4874
5278
|
logError(formattedError) {
|
|
5279
|
+
if (!this.config.debug)
|
|
5280
|
+
return;
|
|
4875
5281
|
const { code, message, digest, timestamp, context } = formattedError;
|
|
4876
5282
|
const errorOutput = {
|
|
4877
5283
|
"\u{1F6A8} Grain Analytics Error": {
|
|
@@ -4889,9 +5295,7 @@ var Grain = (() => {
|
|
|
4889
5295
|
}
|
|
4890
5296
|
};
|
|
4891
5297
|
console.error("\u{1F6A8} Grain Analytics Error:", errorOutput);
|
|
4892
|
-
|
|
4893
|
-
console.error(`[Grain Analytics] ${code}: ${message} (${context}) - Events: ${digest.eventCount}, Props: ${digest.totalProperties}, Size: ${digest.totalSize}B`);
|
|
4894
|
-
}
|
|
5298
|
+
console.error(`[Grain Analytics] ${code}: ${message} (${context}) - Events: ${digest.eventCount}, Props: ${digest.totalProperties}, Size: ${digest.totalSize}B`);
|
|
4895
5299
|
}
|
|
4896
5300
|
/**
|
|
4897
5301
|
* Safely execute a function with error handling
|
|
@@ -4990,7 +5394,6 @@ var Grain = (() => {
|
|
|
4990
5394
|
try {
|
|
4991
5395
|
const headers = await this.getAuthHeaders();
|
|
4992
5396
|
const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/multi`;
|
|
4993
|
-
this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);
|
|
4994
5397
|
const response = await fetch(url, {
|
|
4995
5398
|
method: "POST",
|
|
4996
5399
|
headers,
|
|
@@ -5055,7 +5458,6 @@ var Grain = (() => {
|
|
|
5055
5458
|
body,
|
|
5056
5459
|
keepalive: true
|
|
5057
5460
|
});
|
|
5058
|
-
this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);
|
|
5059
5461
|
} catch (error) {
|
|
5060
5462
|
const formattedError = this.formatError(error, "sendEventsWithBeacon", events);
|
|
5061
5463
|
this.logError(formattedError);
|
|
@@ -5121,7 +5523,6 @@ var Grain = (() => {
|
|
|
5121
5523
|
debug: this.config.debug
|
|
5122
5524
|
}
|
|
5123
5525
|
);
|
|
5124
|
-
this.log("Heartbeat tracking initialized");
|
|
5125
5526
|
} catch (error) {
|
|
5126
5527
|
this.log("Failed to initialize heartbeat tracking:", error);
|
|
5127
5528
|
}
|
|
@@ -5136,19 +5537,48 @@ var Grain = (() => {
|
|
|
5136
5537
|
tenantId: this.config.tenantId
|
|
5137
5538
|
}
|
|
5138
5539
|
);
|
|
5139
|
-
this.log("Auto page view tracking initialized");
|
|
5140
5540
|
} catch (error) {
|
|
5141
5541
|
this.log("Failed to initialize page view tracking:", error);
|
|
5142
5542
|
}
|
|
5143
5543
|
}
|
|
5144
5544
|
this.initializeAutoTracking();
|
|
5145
5545
|
}
|
|
5546
|
+
/**
|
|
5547
|
+
* Initialize heatmap tracking
|
|
5548
|
+
*/
|
|
5549
|
+
initializeHeatmapTracking() {
|
|
5550
|
+
if (typeof window === "undefined")
|
|
5551
|
+
return;
|
|
5552
|
+
try {
|
|
5553
|
+
this.log("Initializing heatmap tracking");
|
|
5554
|
+
Promise.resolve().then(() => (init_heatmap_tracking(), heatmap_tracking_exports)).then(({ HeatmapTrackingManager: HeatmapTrackingManager2 }) => {
|
|
5555
|
+
try {
|
|
5556
|
+
this.heatmapTrackingManager = new HeatmapTrackingManager2(
|
|
5557
|
+
this,
|
|
5558
|
+
{
|
|
5559
|
+
scrollDebounceDelay: 100,
|
|
5560
|
+
batchDelay: 2e3,
|
|
5561
|
+
maxBatchSize: 20,
|
|
5562
|
+
debug: this.config.debug
|
|
5563
|
+
}
|
|
5564
|
+
);
|
|
5565
|
+
this.log("Heatmap tracking initialized");
|
|
5566
|
+
} catch (error) {
|
|
5567
|
+
this.log("Failed to initialize heatmap tracking:", error);
|
|
5568
|
+
}
|
|
5569
|
+
}).catch((error) => {
|
|
5570
|
+
this.log("Failed to load heatmap tracking module:", error);
|
|
5571
|
+
});
|
|
5572
|
+
} catch (error) {
|
|
5573
|
+
this.log("Failed to initialize heatmap tracking:", error);
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5146
5576
|
/**
|
|
5147
5577
|
* Initialize auto-tracking (interactions and sections)
|
|
5148
5578
|
*/
|
|
5149
5579
|
async initializeAutoTracking() {
|
|
5150
5580
|
try {
|
|
5151
|
-
this.log("Initializing auto-tracking
|
|
5581
|
+
this.log("Initializing auto-tracking");
|
|
5152
5582
|
const userId = this.globalUserId || this.persistentAnonymousUserId || this.generateUUID();
|
|
5153
5583
|
const currentUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
5154
5584
|
const request = {
|
|
@@ -5160,23 +5590,19 @@ var Grain = (() => {
|
|
|
5160
5590
|
};
|
|
5161
5591
|
const headers = await this.getAuthHeaders();
|
|
5162
5592
|
const url = `${this.config.apiUrl}/v1/client/${encodeURIComponent(this.config.tenantId)}/config/configurations`;
|
|
5163
|
-
this.log("Fetching auto-tracking config from:", url);
|
|
5164
5593
|
const response = await fetch(url, {
|
|
5165
5594
|
method: "POST",
|
|
5166
5595
|
headers,
|
|
5167
5596
|
body: JSON.stringify(request)
|
|
5168
5597
|
});
|
|
5169
5598
|
if (!response.ok) {
|
|
5170
|
-
this.log("Failed to fetch auto-tracking config:", response.status
|
|
5599
|
+
this.log("Failed to fetch auto-tracking config:", response.status);
|
|
5171
5600
|
return;
|
|
5172
5601
|
}
|
|
5173
5602
|
const configResponse = await response.json();
|
|
5174
|
-
this.log("Received config response:", configResponse);
|
|
5175
5603
|
if (configResponse.autoTrackingConfig) {
|
|
5176
|
-
this.log("Auto-tracking config
|
|
5604
|
+
this.log("Auto-tracking config loaded");
|
|
5177
5605
|
this.setupAutoTrackingManagers(configResponse.autoTrackingConfig);
|
|
5178
|
-
} else {
|
|
5179
|
-
this.log("No auto-tracking config in response");
|
|
5180
5606
|
}
|
|
5181
5607
|
} catch (error) {
|
|
5182
5608
|
this.log("Failed to initialize auto-tracking:", error);
|
|
@@ -5186,9 +5612,9 @@ var Grain = (() => {
|
|
|
5186
5612
|
* Setup auto-tracking managers
|
|
5187
5613
|
*/
|
|
5188
5614
|
setupAutoTrackingManagers(config) {
|
|
5189
|
-
this.log("Setting up auto-tracking managers
|
|
5615
|
+
this.log("Setting up auto-tracking managers");
|
|
5190
5616
|
if (config.interactions && config.interactions.length > 0) {
|
|
5191
|
-
this.log("Loading interaction tracking
|
|
5617
|
+
this.log("Loading interaction tracking:", config.interactions.length, "interactions");
|
|
5192
5618
|
Promise.resolve().then(() => (init_interaction_tracking(), interaction_tracking_exports)).then(({ InteractionTrackingManager: InteractionTrackingManager2 }) => {
|
|
5193
5619
|
try {
|
|
5194
5620
|
this.interactionTrackingManager = new InteractionTrackingManager2(
|
|
@@ -5200,18 +5626,16 @@ var Grain = (() => {
|
|
|
5200
5626
|
mutationDebounceDelay: 500
|
|
5201
5627
|
}
|
|
5202
5628
|
);
|
|
5203
|
-
this.log("
|
|
5629
|
+
this.log("Interaction tracking initialized");
|
|
5204
5630
|
} catch (error) {
|
|
5205
|
-
this.log("
|
|
5631
|
+
this.log("Failed to initialize interaction tracking:", error);
|
|
5206
5632
|
}
|
|
5207
5633
|
}).catch((error) => {
|
|
5208
|
-
this.log("
|
|
5634
|
+
this.log("Failed to load interaction tracking module:", error);
|
|
5209
5635
|
});
|
|
5210
|
-
} else {
|
|
5211
|
-
this.log("No interactions configured for auto-tracking");
|
|
5212
5636
|
}
|
|
5213
5637
|
if (config.sections && config.sections.length > 0) {
|
|
5214
|
-
this.log("Loading section tracking
|
|
5638
|
+
this.log("Loading section tracking:", config.sections.length, "sections");
|
|
5215
5639
|
Promise.resolve().then(() => (init_section_tracking(), section_tracking_exports)).then(({ SectionTrackingManager: SectionTrackingManager2 }) => {
|
|
5216
5640
|
try {
|
|
5217
5641
|
this.sectionTrackingManager = new SectionTrackingManager2(
|
|
@@ -5226,15 +5650,13 @@ var Grain = (() => {
|
|
|
5226
5650
|
debug: this.config.debug
|
|
5227
5651
|
}
|
|
5228
5652
|
);
|
|
5229
|
-
this.log("
|
|
5653
|
+
this.log("Section tracking initialized");
|
|
5230
5654
|
} catch (error) {
|
|
5231
|
-
this.log("
|
|
5655
|
+
this.log("Failed to initialize section tracking:", error);
|
|
5232
5656
|
}
|
|
5233
5657
|
}).catch((error) => {
|
|
5234
|
-
this.log("
|
|
5658
|
+
this.log("Failed to load section tracking module:", error);
|
|
5235
5659
|
});
|
|
5236
|
-
} else {
|
|
5237
|
-
this.log("No sections configured for auto-tracking");
|
|
5238
5660
|
}
|
|
5239
5661
|
}
|
|
5240
5662
|
/**
|
|
@@ -5290,7 +5712,7 @@ var Grain = (() => {
|
|
|
5290
5712
|
properties.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
5291
5713
|
}
|
|
5292
5714
|
this.trackSystemEvent("_grain_session_start", properties);
|
|
5293
|
-
this.log("Session started
|
|
5715
|
+
this.log("Session started");
|
|
5294
5716
|
}
|
|
5295
5717
|
/**
|
|
5296
5718
|
* Track session end event
|
|
@@ -5315,7 +5737,7 @@ var Grain = (() => {
|
|
|
5315
5737
|
properties.page_count = pageCount;
|
|
5316
5738
|
}
|
|
5317
5739
|
this.trackSystemEvent("_grain_session_end", properties);
|
|
5318
|
-
this.log("Session ended
|
|
5740
|
+
this.log("Session ended");
|
|
5319
5741
|
}
|
|
5320
5742
|
/**
|
|
5321
5743
|
* Detect browser name
|
|
@@ -5412,7 +5834,7 @@ var Grain = (() => {
|
|
|
5412
5834
|
};
|
|
5413
5835
|
this.eventQueue.push(event);
|
|
5414
5836
|
this.eventCountSinceLastHeartbeat++;
|
|
5415
|
-
this.log(`Queued system event: ${eventName}
|
|
5837
|
+
this.log(`Queued system event: ${eventName}`);
|
|
5416
5838
|
if (this.eventQueue.length >= this.config.batchSize) {
|
|
5417
5839
|
this.flush().catch((error) => {
|
|
5418
5840
|
const formattedError = this.formatError(error, "flush system event");
|
|
@@ -5502,7 +5924,7 @@ var Grain = (() => {
|
|
|
5502
5924
|
this.eventQueue.push(formattedEvent);
|
|
5503
5925
|
this.eventCountSinceLastHeartbeat++;
|
|
5504
5926
|
this.sessionEventCount++;
|
|
5505
|
-
this.log(`Queued event: ${event.eventName}
|
|
5927
|
+
this.log(`Queued event: ${event.eventName}`);
|
|
5506
5928
|
if (opts.flush || this.eventQueue.length >= this.config.batchSize) {
|
|
5507
5929
|
await this.flush();
|
|
5508
5930
|
}
|
|
@@ -5701,7 +6123,6 @@ var Grain = (() => {
|
|
|
5701
6123
|
try {
|
|
5702
6124
|
const headers = await this.getAuthHeaders();
|
|
5703
6125
|
const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}/properties`;
|
|
5704
|
-
this.log(`Setting properties for user ${payload.userId} (attempt ${attempt + 1})`);
|
|
5705
6126
|
const response = await fetch(url, {
|
|
5706
6127
|
method: "POST",
|
|
5707
6128
|
headers,
|
|
@@ -5927,7 +6348,6 @@ var Grain = (() => {
|
|
|
5927
6348
|
try {
|
|
5928
6349
|
const headers = await this.getAuthHeaders();
|
|
5929
6350
|
const url = `${this.config.apiUrl}/v1/client/${encodeURIComponent(this.config.tenantId)}/config/configurations`;
|
|
5930
|
-
this.log(`Fetching configurations for user ${userId} (attempt ${attempt + 1})`);
|
|
5931
6351
|
const response = await fetch(url, {
|
|
5932
6352
|
method: "POST",
|
|
5933
6353
|
headers,
|
|
@@ -5954,7 +6374,7 @@ var Grain = (() => {
|
|
|
5954
6374
|
if (configResponse.configurations) {
|
|
5955
6375
|
this.updateConfigCache(configResponse, userId);
|
|
5956
6376
|
}
|
|
5957
|
-
this.log(
|
|
6377
|
+
this.log("Successfully fetched configurations");
|
|
5958
6378
|
return configResponse;
|
|
5959
6379
|
} catch (error) {
|
|
5960
6380
|
lastError = error;
|
|
@@ -6203,6 +6623,10 @@ var Grain = (() => {
|
|
|
6203
6623
|
this.sectionTrackingManager.destroy();
|
|
6204
6624
|
this.sectionTrackingManager = null;
|
|
6205
6625
|
}
|
|
6626
|
+
if (this.heatmapTrackingManager) {
|
|
6627
|
+
this.heatmapTrackingManager.destroy();
|
|
6628
|
+
this.heatmapTrackingManager = null;
|
|
6629
|
+
}
|
|
6206
6630
|
if (this.eventQueue.length > 0) {
|
|
6207
6631
|
const eventsToSend = [...this.eventQueue];
|
|
6208
6632
|
this.eventQueue = [];
|