humanbehavior-js 0.2.1 → 0.2.2
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/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/api.js +312 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/api.js.map +1 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/index.js +19 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/index.js.map +1 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/react/index.js +222 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/react/index.js.map +1 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/redact.js +416 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/redact.js.map +1 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/tracker.js +950 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/tracker.js.map +1 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/utils/logger.js +117 -0
- package/.rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist/utils/logger.js.map +1 -0
- package/dist/cjs/index.js +2 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js +26 -451
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +2 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +5 -430
- package/dist/esm/react/index.js.map +1 -1
- package/dist/index.min.js +15 -1
- package/dist/index.min.js.map +1 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/react/index.d.ts +2 -3
- package/package.json +10 -32
- package/rollup.config.js +106 -0
- package/simple-demo.html +26 -0
- package/simple-spa.html +658 -0
- package/src/index.ts +2 -2
- package/tsconfig.json +24 -0
- /package/{dist → .rollup.cache/Users/hudsonch/Desktop/HumanBehaviorInternship/humanbehavior-js/dist}/.tsbuildinfo +0 -0
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import * as rrweb from 'rrweb';
|
|
3
|
+
import { v1 as uuidv1 } from 'uuid';
|
|
4
|
+
import { HumanBehaviorAPI } from './api';
|
|
5
|
+
import { RedactionManager } from './redact';
|
|
6
|
+
import { logger, logError, logWarn, logInfo, logDebug } from './utils/logger';
|
|
7
|
+
// Check if we're in a browser environment
|
|
8
|
+
const isBrowser = typeof window !== 'undefined';
|
|
9
|
+
export class HumanBehaviorTracker {
|
|
10
|
+
/**
|
|
11
|
+
* Initialize the HumanBehavior tracker
|
|
12
|
+
* This is the main entry point - call this once per page
|
|
13
|
+
*/
|
|
14
|
+
static init(apiKey, options) {
|
|
15
|
+
// Return existing instance if already initialized
|
|
16
|
+
if (isBrowser && window.__humanBehaviorGlobalTracker) {
|
|
17
|
+
logDebug('Tracker already initialized, returning existing instance');
|
|
18
|
+
return window.__humanBehaviorGlobalTracker;
|
|
19
|
+
}
|
|
20
|
+
// Configure logging if specified
|
|
21
|
+
if (options === null || options === void 0 ? void 0 : options.logLevel) {
|
|
22
|
+
this.configureLogging({ level: options.logLevel });
|
|
23
|
+
}
|
|
24
|
+
// Create new tracker instance
|
|
25
|
+
const tracker = new HumanBehaviorTracker(apiKey, options === null || options === void 0 ? void 0 : options.ingestionUrl);
|
|
26
|
+
// Set redacted fields if specified
|
|
27
|
+
if (options === null || options === void 0 ? void 0 : options.redactFields) {
|
|
28
|
+
tracker.setRedactedFields(options.redactFields);
|
|
29
|
+
}
|
|
30
|
+
// Setup automatic tracking if enabled
|
|
31
|
+
if ((options === null || options === void 0 ? void 0 : options.enableAutomaticTracking) !== false) {
|
|
32
|
+
tracker.setupAutomaticTracking(options === null || options === void 0 ? void 0 : options.automaticTrackingOptions);
|
|
33
|
+
}
|
|
34
|
+
// Test connection (non-blocking)
|
|
35
|
+
if (isBrowser) {
|
|
36
|
+
const testUrl = tracker.api['baseUrl'] + '/api/health';
|
|
37
|
+
fetch(testUrl, { method: 'HEAD' })
|
|
38
|
+
.then(() => logDebug('Connection test successful'))
|
|
39
|
+
.catch((error) => {
|
|
40
|
+
logWarn('Connection test failed - ad blocker may be active:', error.message);
|
|
41
|
+
tracker._connectionBlocked = true;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Start tracking
|
|
45
|
+
tracker.start();
|
|
46
|
+
return tracker;
|
|
47
|
+
}
|
|
48
|
+
constructor(apiKey, ingestionUrl) {
|
|
49
|
+
this.eventIngestionQueue = [];
|
|
50
|
+
this.queueSizeBytes = 0;
|
|
51
|
+
this.userProperties = {};
|
|
52
|
+
this.isProcessing = false;
|
|
53
|
+
this.flushInterval = null;
|
|
54
|
+
this.FLUSH_INTERVAL_MS = 5000; // Flush every 5 seconds
|
|
55
|
+
this.endUserId = null;
|
|
56
|
+
this.initialized = false;
|
|
57
|
+
this.initializationPromise = null;
|
|
58
|
+
// Console tracking properties
|
|
59
|
+
this.originalConsole = null;
|
|
60
|
+
this.consoleTrackingEnabled = false;
|
|
61
|
+
// Navigation tracking properties
|
|
62
|
+
this.navigationTrackingEnabled = false;
|
|
63
|
+
this.currentUrl = '';
|
|
64
|
+
this.previousUrl = '';
|
|
65
|
+
this.originalPushState = null;
|
|
66
|
+
this.originalReplaceState = null;
|
|
67
|
+
this.navigationListeners = [];
|
|
68
|
+
this._connectionBlocked = false;
|
|
69
|
+
if (!apiKey) {
|
|
70
|
+
throw new Error('Human Behavior API Key is required');
|
|
71
|
+
}
|
|
72
|
+
// Initialize API
|
|
73
|
+
//const defaultIngestionUrl = 'http://3.137.217.33:3000'; // AWS Development Server
|
|
74
|
+
//const defaultIngestionUrl = 'http://ingestion-server-alb-1823866402.us-east-2.elb.amazonaws.com'; // ALB
|
|
75
|
+
const defaultIngestionUrl = 'https://ingest.humanbehavior.co'; // HTTPS ALB
|
|
76
|
+
this.api = new HumanBehaviorAPI({
|
|
77
|
+
apiKey: apiKey,
|
|
78
|
+
ingestionUrl: ingestionUrl || defaultIngestionUrl
|
|
79
|
+
});
|
|
80
|
+
this.apiKey = apiKey;
|
|
81
|
+
this.redactionManager = new RedactionManager();
|
|
82
|
+
// Handle session restoration with improved continuity
|
|
83
|
+
if (isBrowser) {
|
|
84
|
+
const existingSessionId = localStorage.getItem('human_behavior_session_id');
|
|
85
|
+
const lastActivity = localStorage.getItem('human_behavior_last_activity');
|
|
86
|
+
const thirtyMinutesAgo = Date.now() - (30 * 60 * 1000);
|
|
87
|
+
// Check if we have an existing session that's still within the activity window
|
|
88
|
+
if (existingSessionId && lastActivity && parseInt(lastActivity) > thirtyMinutesAgo) {
|
|
89
|
+
this.sessionId = existingSessionId;
|
|
90
|
+
logDebug(`Reusing existing session: ${this.sessionId}`);
|
|
91
|
+
// Update activity timestamp to extend the session window
|
|
92
|
+
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Clear old session data if it's expired
|
|
96
|
+
if (existingSessionId) {
|
|
97
|
+
logDebug(`Session expired, clearing old session: ${existingSessionId}`);
|
|
98
|
+
localStorage.removeItem('human_behavior_session_id');
|
|
99
|
+
localStorage.removeItem('human_behavior_last_activity');
|
|
100
|
+
}
|
|
101
|
+
this.sessionId = uuidv1();
|
|
102
|
+
logDebug(`Creating new session: ${this.sessionId}`);
|
|
103
|
+
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
104
|
+
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
105
|
+
}
|
|
106
|
+
this.currentUrl = window.location.href;
|
|
107
|
+
window.__humanBehaviorGlobalTracker = this;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.sessionId = uuidv1();
|
|
111
|
+
}
|
|
112
|
+
// Start initialization
|
|
113
|
+
this.initializationPromise = this.init();
|
|
114
|
+
}
|
|
115
|
+
init() {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
try {
|
|
118
|
+
const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
119
|
+
logDebug(`Initializing with sessionId: ${this.sessionId}, userId: ${userId}`);
|
|
120
|
+
const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
|
|
121
|
+
// Check if server returned a different session ID (for session continuity)
|
|
122
|
+
if (sessionId !== this.sessionId) {
|
|
123
|
+
logDebug(`Server returned different sessionId: ${sessionId} (client had: ${this.sessionId})`);
|
|
124
|
+
this.sessionId = sessionId;
|
|
125
|
+
// Update localStorage with server's session ID for continuity
|
|
126
|
+
if (isBrowser) {
|
|
127
|
+
localStorage.setItem('human_behavior_session_id', this.sessionId);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
this.endUserId = endUserId;
|
|
131
|
+
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
|
|
132
|
+
// Only setup browser-specific handlers when in browser environment
|
|
133
|
+
if (isBrowser) {
|
|
134
|
+
this.setupPageUnloadHandler();
|
|
135
|
+
this.setupNavigationTracking();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
logWarn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
|
|
139
|
+
}
|
|
140
|
+
this.initialized = true;
|
|
141
|
+
logInfo(`HumanBehaviorTracker initialized with sessionId: ${this.sessionId}, endUserId: ${endUserId}`);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
logError('Failed to initialize HumanBehaviorTracker:', error);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
ensureInitialized() {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
if (!this.initializationPromise) {
|
|
152
|
+
throw new Error('HumanBehaviorTracker initialization failed');
|
|
153
|
+
}
|
|
154
|
+
yield this.initializationPromise;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Setup navigation event tracking for SPA navigation
|
|
159
|
+
*/
|
|
160
|
+
setupNavigationTracking() {
|
|
161
|
+
if (!isBrowser || this.navigationTrackingEnabled)
|
|
162
|
+
return;
|
|
163
|
+
this.navigationTrackingEnabled = true;
|
|
164
|
+
logDebug('Setting up navigation tracking');
|
|
165
|
+
// Store original history methods
|
|
166
|
+
this.originalPushState = history.pushState;
|
|
167
|
+
this.originalReplaceState = history.replaceState;
|
|
168
|
+
// Override pushState to capture programmatic navigation
|
|
169
|
+
history.pushState = (...args) => {
|
|
170
|
+
this.previousUrl = this.currentUrl;
|
|
171
|
+
this.currentUrl = window.location.href;
|
|
172
|
+
// Call original method
|
|
173
|
+
this.originalPushState.apply(history, args);
|
|
174
|
+
// Track navigation event
|
|
175
|
+
this.trackNavigationEvent('pushState', this.previousUrl, this.currentUrl);
|
|
176
|
+
};
|
|
177
|
+
// Override replaceState to capture programmatic navigation
|
|
178
|
+
history.replaceState = (...args) => {
|
|
179
|
+
this.previousUrl = this.currentUrl;
|
|
180
|
+
this.currentUrl = window.location.href;
|
|
181
|
+
// Call original method
|
|
182
|
+
this.originalReplaceState.apply(history, args);
|
|
183
|
+
// Track navigation event
|
|
184
|
+
this.trackNavigationEvent('replaceState', this.previousUrl, this.currentUrl);
|
|
185
|
+
};
|
|
186
|
+
// Listen for popstate events (back/forward navigation)
|
|
187
|
+
const popstateListener = () => {
|
|
188
|
+
this.previousUrl = this.currentUrl;
|
|
189
|
+
this.currentUrl = window.location.href;
|
|
190
|
+
this.trackNavigationEvent('popstate', this.previousUrl, this.currentUrl);
|
|
191
|
+
};
|
|
192
|
+
window.addEventListener('popstate', popstateListener);
|
|
193
|
+
this.navigationListeners.push(() => {
|
|
194
|
+
window.removeEventListener('popstate', popstateListener);
|
|
195
|
+
});
|
|
196
|
+
// Listen for hashchange events
|
|
197
|
+
const hashchangeListener = () => {
|
|
198
|
+
this.previousUrl = this.currentUrl;
|
|
199
|
+
this.currentUrl = window.location.href;
|
|
200
|
+
this.trackNavigationEvent('hashchange', this.previousUrl, this.currentUrl);
|
|
201
|
+
};
|
|
202
|
+
window.addEventListener('hashchange', hashchangeListener);
|
|
203
|
+
this.navigationListeners.push(() => {
|
|
204
|
+
window.removeEventListener('hashchange', hashchangeListener);
|
|
205
|
+
});
|
|
206
|
+
// Track initial page load
|
|
207
|
+
this.trackNavigationEvent('pageLoad', '', this.currentUrl);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Track navigation events and send custom events
|
|
211
|
+
*/
|
|
212
|
+
trackNavigationEvent(type, fromUrl, toUrl) {
|
|
213
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
214
|
+
if (!this.initialized)
|
|
215
|
+
return;
|
|
216
|
+
try {
|
|
217
|
+
const navigationData = {
|
|
218
|
+
type: type,
|
|
219
|
+
from: fromUrl,
|
|
220
|
+
to: toUrl,
|
|
221
|
+
timestamp: new Date().toISOString(),
|
|
222
|
+
pathname: window.location.pathname,
|
|
223
|
+
search: window.location.search,
|
|
224
|
+
hash: window.location.hash,
|
|
225
|
+
referrer: document.referrer
|
|
226
|
+
};
|
|
227
|
+
// Add navigation event to the main event stream
|
|
228
|
+
yield this.addEvent({
|
|
229
|
+
type: 5, // Custom event type
|
|
230
|
+
data: {
|
|
231
|
+
payload: Object.assign({ eventType: 'navigation' }, navigationData)
|
|
232
|
+
},
|
|
233
|
+
timestamp: Date.now()
|
|
234
|
+
});
|
|
235
|
+
logDebug(`Navigation tracked: ${type} from ${fromUrl} to ${toUrl}`);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
logError('Failed to track navigation event:', error);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Track a page view event (PostHog-style)
|
|
244
|
+
*/
|
|
245
|
+
trackPageView(url) {
|
|
246
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
247
|
+
if (!this.initialized)
|
|
248
|
+
return;
|
|
249
|
+
try {
|
|
250
|
+
const pageViewData = {
|
|
251
|
+
url: url || window.location.href,
|
|
252
|
+
pathname: window.location.pathname,
|
|
253
|
+
search: window.location.search,
|
|
254
|
+
hash: window.location.hash,
|
|
255
|
+
referrer: document.referrer,
|
|
256
|
+
timestamp: new Date().toISOString()
|
|
257
|
+
};
|
|
258
|
+
// Add pageview event to the main event stream
|
|
259
|
+
yield this.addEvent({
|
|
260
|
+
type: 5, // Custom event type
|
|
261
|
+
data: {
|
|
262
|
+
payload: Object.assign({ eventType: 'pageview' }, pageViewData)
|
|
263
|
+
},
|
|
264
|
+
timestamp: Date.now()
|
|
265
|
+
});
|
|
266
|
+
logDebug(`Pageview tracked: ${pageViewData.url}`);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
logError('Failed to track pageview event:', error);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Track a custom event (PostHog-style)
|
|
275
|
+
*/
|
|
276
|
+
customEvent(eventName, properties) {
|
|
277
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
var _a, _b, _c, _d, _e;
|
|
279
|
+
if (!this.initialized)
|
|
280
|
+
return;
|
|
281
|
+
try {
|
|
282
|
+
// Send custom event directly to the API
|
|
283
|
+
yield this.api.sendCustomEvent(this.sessionId, eventName, properties);
|
|
284
|
+
logDebug(`Custom event tracked: ${eventName}`, properties);
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
logError('Failed to track custom event:', error);
|
|
288
|
+
// Handle specific error types - check for any custom event failure
|
|
289
|
+
if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('500')) ||
|
|
290
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('Internal Server Error')) ||
|
|
291
|
+
((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('Failed to send custom event'))) {
|
|
292
|
+
logWarn('Custom event endpoint failed, using fallback');
|
|
293
|
+
}
|
|
294
|
+
else if ((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('ERR_BLOCKED_BY_CLIENT')) {
|
|
295
|
+
logWarn('Custom event request blocked by ad blocker, using fallback');
|
|
296
|
+
}
|
|
297
|
+
else if ((_e = error.message) === null || _e === void 0 ? void 0 : _e.includes('Failed to fetch')) {
|
|
298
|
+
logWarn('Custom event network error, using fallback');
|
|
299
|
+
}
|
|
300
|
+
// Always try fallback for any custom event error
|
|
301
|
+
try {
|
|
302
|
+
const customEventData = {
|
|
303
|
+
eventName: eventName,
|
|
304
|
+
properties: properties || {},
|
|
305
|
+
timestamp: new Date().toISOString(),
|
|
306
|
+
url: window.location.href,
|
|
307
|
+
pathname: window.location.pathname
|
|
308
|
+
};
|
|
309
|
+
yield this.addEvent({
|
|
310
|
+
type: 5, // Custom event type
|
|
311
|
+
data: {
|
|
312
|
+
payload: Object.assign({ eventType: 'custom' }, customEventData)
|
|
313
|
+
},
|
|
314
|
+
timestamp: Date.now()
|
|
315
|
+
});
|
|
316
|
+
logDebug(`Custom event added to event stream as fallback: ${eventName}`);
|
|
317
|
+
}
|
|
318
|
+
catch (fallbackError) {
|
|
319
|
+
logError('Failed to add custom event to event stream as fallback:', fallbackError);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Setup automatic tracking for buttons, links, and forms
|
|
326
|
+
*/
|
|
327
|
+
setupAutomaticTracking(options) {
|
|
328
|
+
if (!isBrowser)
|
|
329
|
+
return;
|
|
330
|
+
const config = {
|
|
331
|
+
trackButtons: (options === null || options === void 0 ? void 0 : options.trackButtons) !== false,
|
|
332
|
+
trackLinks: (options === null || options === void 0 ? void 0 : options.trackLinks) !== false,
|
|
333
|
+
trackForms: (options === null || options === void 0 ? void 0 : options.trackForms) !== false,
|
|
334
|
+
includeText: (options === null || options === void 0 ? void 0 : options.includeText) !== false,
|
|
335
|
+
includeClasses: (options === null || options === void 0 ? void 0 : options.includeClasses) || false
|
|
336
|
+
};
|
|
337
|
+
logDebug('Setting up automatic tracking with config:', config);
|
|
338
|
+
// Setup button tracking
|
|
339
|
+
if (config.trackButtons) {
|
|
340
|
+
this.setupAutomaticButtonTracking(config);
|
|
341
|
+
}
|
|
342
|
+
// Setup link tracking
|
|
343
|
+
if (config.trackLinks) {
|
|
344
|
+
this.setupAutomaticLinkTracking(config);
|
|
345
|
+
}
|
|
346
|
+
// Setup form tracking
|
|
347
|
+
if (config.trackForms) {
|
|
348
|
+
this.setupAutomaticFormTracking(config);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Setup automatic button tracking
|
|
353
|
+
*/
|
|
354
|
+
setupAutomaticButtonTracking(config) {
|
|
355
|
+
document.addEventListener('click', (event) => __awaiter(this, void 0, void 0, function* () {
|
|
356
|
+
var _a;
|
|
357
|
+
const target = event.target;
|
|
358
|
+
// Track button clicks
|
|
359
|
+
if (target.tagName === 'BUTTON' || target.closest('button')) {
|
|
360
|
+
const button = target.tagName === 'BUTTON'
|
|
361
|
+
? target
|
|
362
|
+
: target.closest('button');
|
|
363
|
+
const properties = {
|
|
364
|
+
buttonId: button.id || null,
|
|
365
|
+
buttonType: button.type || 'button',
|
|
366
|
+
page: window.location.pathname,
|
|
367
|
+
timestamp: Date.now()
|
|
368
|
+
};
|
|
369
|
+
if (config.includeText) {
|
|
370
|
+
properties.buttonText = ((_a = button.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || null;
|
|
371
|
+
}
|
|
372
|
+
if (config.includeClasses) {
|
|
373
|
+
properties.buttonClass = button.className || null;
|
|
374
|
+
}
|
|
375
|
+
// Remove null values
|
|
376
|
+
Object.keys(properties).forEach(key => {
|
|
377
|
+
if (properties[key] === null) {
|
|
378
|
+
delete properties[key];
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
yield this.customEvent('button_clicked', properties);
|
|
382
|
+
}
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Setup automatic link tracking
|
|
387
|
+
*/
|
|
388
|
+
setupAutomaticLinkTracking(config) {
|
|
389
|
+
document.addEventListener('click', (event) => __awaiter(this, void 0, void 0, function* () {
|
|
390
|
+
var _a;
|
|
391
|
+
const target = event.target;
|
|
392
|
+
// Track link clicks
|
|
393
|
+
if (target.tagName === 'A' || target.closest('a')) {
|
|
394
|
+
const link = target.tagName === 'A'
|
|
395
|
+
? target
|
|
396
|
+
: target.closest('a');
|
|
397
|
+
const properties = {
|
|
398
|
+
linkUrl: link.href || null,
|
|
399
|
+
linkId: link.id || null,
|
|
400
|
+
linkTarget: link.target || null,
|
|
401
|
+
page: window.location.pathname,
|
|
402
|
+
timestamp: Date.now()
|
|
403
|
+
};
|
|
404
|
+
if (config.includeText) {
|
|
405
|
+
properties.linkText = ((_a = link.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || null;
|
|
406
|
+
}
|
|
407
|
+
if (config.includeClasses) {
|
|
408
|
+
properties.linkClass = link.className || null;
|
|
409
|
+
}
|
|
410
|
+
// Remove null values
|
|
411
|
+
Object.keys(properties).forEach(key => {
|
|
412
|
+
if (properties[key] === null) {
|
|
413
|
+
delete properties[key];
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
yield this.customEvent('link_clicked', properties);
|
|
417
|
+
}
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Setup automatic form tracking
|
|
422
|
+
*/
|
|
423
|
+
setupAutomaticFormTracking(config) {
|
|
424
|
+
document.addEventListener('submit', (event) => __awaiter(this, void 0, void 0, function* () {
|
|
425
|
+
const form = event.target;
|
|
426
|
+
const formData = new FormData(form);
|
|
427
|
+
const properties = {
|
|
428
|
+
formId: form.id || null,
|
|
429
|
+
formAction: form.action || null,
|
|
430
|
+
formMethod: form.method || 'get',
|
|
431
|
+
fields: Array.from(formData.keys()),
|
|
432
|
+
page: window.location.pathname,
|
|
433
|
+
timestamp: Date.now()
|
|
434
|
+
};
|
|
435
|
+
if (config.includeClasses) {
|
|
436
|
+
properties.formClass = form.className || null;
|
|
437
|
+
}
|
|
438
|
+
// Remove null values
|
|
439
|
+
Object.keys(properties).forEach(key => {
|
|
440
|
+
if (properties[key] === null) {
|
|
441
|
+
delete properties[key];
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
yield this.customEvent('form_submitted', properties);
|
|
445
|
+
}));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Cleanup navigation tracking
|
|
449
|
+
*/
|
|
450
|
+
cleanupNavigationTracking() {
|
|
451
|
+
if (!this.navigationTrackingEnabled)
|
|
452
|
+
return;
|
|
453
|
+
// Restore original history methods
|
|
454
|
+
if (this.originalPushState) {
|
|
455
|
+
history.pushState = this.originalPushState;
|
|
456
|
+
}
|
|
457
|
+
if (this.originalReplaceState) {
|
|
458
|
+
history.replaceState = this.originalReplaceState;
|
|
459
|
+
}
|
|
460
|
+
// Remove event listeners
|
|
461
|
+
this.navigationListeners.forEach(cleanup => cleanup());
|
|
462
|
+
this.navigationListeners = [];
|
|
463
|
+
this.navigationTrackingEnabled = false;
|
|
464
|
+
logDebug('Navigation tracking cleaned up');
|
|
465
|
+
}
|
|
466
|
+
static logToStorage(message) {
|
|
467
|
+
logInfo(message);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Configure logging behavior for the SDK
|
|
471
|
+
* @param config Logger configuration options
|
|
472
|
+
*/
|
|
473
|
+
static configureLogging(config) {
|
|
474
|
+
const levelMap = {
|
|
475
|
+
'none': 0,
|
|
476
|
+
'error': 1,
|
|
477
|
+
'warn': 2,
|
|
478
|
+
'info': 3,
|
|
479
|
+
'debug': 4
|
|
480
|
+
};
|
|
481
|
+
logger.setConfig({
|
|
482
|
+
level: levelMap[config.level || 'error'],
|
|
483
|
+
enableConsole: config.enableConsole !== false,
|
|
484
|
+
enableStorage: config.enableStorage || false
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Enable console event tracking
|
|
489
|
+
*/
|
|
490
|
+
enableConsoleTracking() {
|
|
491
|
+
if (!isBrowser || this.consoleTrackingEnabled)
|
|
492
|
+
return;
|
|
493
|
+
// Store original console methods
|
|
494
|
+
this.originalConsole = {
|
|
495
|
+
log: console.log,
|
|
496
|
+
warn: console.warn,
|
|
497
|
+
error: console.error
|
|
498
|
+
};
|
|
499
|
+
// Override console methods to capture ALL console output (including logger output)
|
|
500
|
+
console.log = (...args) => {
|
|
501
|
+
this.trackConsoleEvent('log', args);
|
|
502
|
+
this.originalConsole.log(...args);
|
|
503
|
+
};
|
|
504
|
+
console.warn = (...args) => {
|
|
505
|
+
this.trackConsoleEvent('warn', args);
|
|
506
|
+
this.originalConsole.warn(...args);
|
|
507
|
+
};
|
|
508
|
+
console.error = (...args) => {
|
|
509
|
+
this.trackConsoleEvent('error', args);
|
|
510
|
+
this.originalConsole.error(...args);
|
|
511
|
+
};
|
|
512
|
+
this.consoleTrackingEnabled = true;
|
|
513
|
+
logDebug('Console tracking enabled');
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Disable console event tracking
|
|
517
|
+
*/
|
|
518
|
+
disableConsoleTracking() {
|
|
519
|
+
if (!isBrowser || !this.consoleTrackingEnabled)
|
|
520
|
+
return;
|
|
521
|
+
// Restore original console methods
|
|
522
|
+
if (this.originalConsole) {
|
|
523
|
+
console.log = this.originalConsole.log;
|
|
524
|
+
console.warn = this.originalConsole.warn;
|
|
525
|
+
console.error = this.originalConsole.error;
|
|
526
|
+
}
|
|
527
|
+
this.consoleTrackingEnabled = false;
|
|
528
|
+
logDebug('Console tracking disabled');
|
|
529
|
+
}
|
|
530
|
+
trackConsoleEvent(level, args) {
|
|
531
|
+
if (!this.initialized)
|
|
532
|
+
return;
|
|
533
|
+
try {
|
|
534
|
+
const consoleData = {
|
|
535
|
+
level: level,
|
|
536
|
+
message: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' '),
|
|
537
|
+
timestamp: new Date().toISOString(),
|
|
538
|
+
url: window.location.href
|
|
539
|
+
};
|
|
540
|
+
// Add console event to the main event stream
|
|
541
|
+
this.addEvent({
|
|
542
|
+
type: 5, // Custom event type
|
|
543
|
+
data: {
|
|
544
|
+
payload: Object.assign({ eventType: 'console' }, consoleData)
|
|
545
|
+
},
|
|
546
|
+
timestamp: Date.now()
|
|
547
|
+
}).catch(error => {
|
|
548
|
+
logError('Failed to track console event:', error);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
logError('Error in trackConsoleEvent:', error);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
setupPageUnloadHandler() {
|
|
556
|
+
if (!isBrowser)
|
|
557
|
+
return;
|
|
558
|
+
logDebug('Setting up page unload handler');
|
|
559
|
+
// Handle visibility changes for sending events
|
|
560
|
+
window.addEventListener('visibilitychange', () => {
|
|
561
|
+
// Only send events when page becomes hidden
|
|
562
|
+
if (document.visibilityState === 'hidden') {
|
|
563
|
+
logDebug('Page hidden - sending pending events');
|
|
564
|
+
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
// Handle actual page unload/close
|
|
568
|
+
window.addEventListener('beforeunload', () => {
|
|
569
|
+
// Send final events
|
|
570
|
+
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
571
|
+
});
|
|
572
|
+
// Update activity timestamp on user interaction (not on page load)
|
|
573
|
+
const updateActivity = () => {
|
|
574
|
+
localStorage.setItem('human_behavior_last_activity', Date.now().toString());
|
|
575
|
+
};
|
|
576
|
+
// Listen for user interactions to update activity timestamp
|
|
577
|
+
window.addEventListener('click', updateActivity);
|
|
578
|
+
window.addEventListener('keydown', updateActivity);
|
|
579
|
+
window.addEventListener('scroll', updateActivity);
|
|
580
|
+
window.addEventListener('mousemove', updateActivity);
|
|
581
|
+
}
|
|
582
|
+
viewLogs() {
|
|
583
|
+
try {
|
|
584
|
+
const logs = logger.getLogs();
|
|
585
|
+
console.log('HumanBehavior Logs:', logs);
|
|
586
|
+
logger.clearLogs(); // Clear logs after viewing
|
|
587
|
+
}
|
|
588
|
+
catch (e) {
|
|
589
|
+
logError('Failed to read logs:', e);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
addUserInfo(_a) {
|
|
593
|
+
return __awaiter(this, arguments, void 0, function* ({ userId, userProperties }) {
|
|
594
|
+
yield this.ensureInitialized();
|
|
595
|
+
if (!this.endUserId) {
|
|
596
|
+
throw new Error('Cannot add user info before tracker initialization');
|
|
597
|
+
}
|
|
598
|
+
this.userProperties = userProperties;
|
|
599
|
+
yield this.api.sendUserData(this.endUserId, userProperties, this.sessionId);
|
|
600
|
+
if (userId) {
|
|
601
|
+
this.endUserId = userId;
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Add user identification information to the tracker
|
|
607
|
+
* @param authFields Array of field names to check for existing users (e.g., ['email', 'phoneNumber'])
|
|
608
|
+
*/
|
|
609
|
+
auth(id) {
|
|
610
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
611
|
+
yield this.ensureInitialized();
|
|
612
|
+
if (!this.endUserId) {
|
|
613
|
+
throw new Error('Cannot authenticate before tracker initialization');
|
|
614
|
+
}
|
|
615
|
+
const response = yield this.api.sendUserAuth(this.endUserId, { id }, this.sessionId, ["id"]);
|
|
616
|
+
if (response && response.userId && response.userId !== this.endUserId) {
|
|
617
|
+
this.endUserId = response.userId;
|
|
618
|
+
this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, response.userId, 365);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
start() {
|
|
623
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
624
|
+
yield this.ensureInitialized();
|
|
625
|
+
if (!isBrowser)
|
|
626
|
+
return;
|
|
627
|
+
// Start periodic flushing
|
|
628
|
+
this.flushInterval = window.setInterval(() => {
|
|
629
|
+
this.flush();
|
|
630
|
+
}, this.FLUSH_INTERVAL_MS);
|
|
631
|
+
// Enable console tracking
|
|
632
|
+
this.enableConsoleTracking();
|
|
633
|
+
// Start recording with redaction enabled
|
|
634
|
+
rrweb.record({
|
|
635
|
+
emit: (event) => {
|
|
636
|
+
this.addEvent(event);
|
|
637
|
+
},
|
|
638
|
+
inlineStylesheet: true,
|
|
639
|
+
recordCanvas: true,
|
|
640
|
+
collectFonts: true,
|
|
641
|
+
inlineImages: true,
|
|
642
|
+
blockClass: 'rr-block',
|
|
643
|
+
ignoreClass: 'rr-ignore',
|
|
644
|
+
maskTextClass: 'rr-ignore'
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
stop() {
|
|
649
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
650
|
+
yield this.ensureInitialized();
|
|
651
|
+
if (!isBrowser)
|
|
652
|
+
return;
|
|
653
|
+
if (this.flushInterval) {
|
|
654
|
+
clearInterval(this.flushInterval);
|
|
655
|
+
this.flushInterval = null;
|
|
656
|
+
}
|
|
657
|
+
// Disable console tracking
|
|
658
|
+
this.disableConsoleTracking();
|
|
659
|
+
// Cleanup navigation tracking
|
|
660
|
+
this.cleanupNavigationTracking();
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
addEvent(event) {
|
|
664
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
665
|
+
yield this.ensureInitialized();
|
|
666
|
+
// Process event through redaction manager if active
|
|
667
|
+
const processedEvent = this.redactionManager.processEvent(event);
|
|
668
|
+
const eventSize = new TextEncoder().encode(JSON.stringify(processedEvent)).length;
|
|
669
|
+
this.eventIngestionQueue.push(processedEvent);
|
|
670
|
+
this.queueSizeBytes += eventSize;
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
flush() {
|
|
674
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
675
|
+
var _a, _b, _c, _d, _e, _f;
|
|
676
|
+
// Prevent concurrent flushes
|
|
677
|
+
if (this.isProcessing || !this.initialized) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
this.isProcessing = true;
|
|
681
|
+
try {
|
|
682
|
+
// Swap the current queue with an empty one atomically
|
|
683
|
+
const eventsToProcess = this.eventIngestionQueue;
|
|
684
|
+
this.eventIngestionQueue = [];
|
|
685
|
+
this.queueSizeBytes = 0;
|
|
686
|
+
if (eventsToProcess.length > 0) {
|
|
687
|
+
logDebug('Flushing events:', eventsToProcess);
|
|
688
|
+
try {
|
|
689
|
+
// Use chunked sending to handle large payloads
|
|
690
|
+
yield this.api.sendEventsChunked(eventsToProcess, this.sessionId, this.endUserId);
|
|
691
|
+
}
|
|
692
|
+
catch (error) {
|
|
693
|
+
// Handle specific error types with graceful degradation
|
|
694
|
+
if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ERROR: Session already completed')) {
|
|
695
|
+
logWarn('Session expired, events will be lost');
|
|
696
|
+
}
|
|
697
|
+
else if (((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('413')) || ((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('Content Too Large'))) {
|
|
698
|
+
logWarn('Payload too large, events will be lost');
|
|
699
|
+
}
|
|
700
|
+
else if (((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('ERR_BLOCKED_BY_CLIENT')) ||
|
|
701
|
+
((_e = error.message) === null || _e === void 0 ? void 0 : _e.includes('Failed to fetch')) ||
|
|
702
|
+
((_f = error.message) === null || _f === void 0 ? void 0 : _f.includes('NetworkError'))) {
|
|
703
|
+
logWarn('Request blocked by ad blocker or network issue, events will be lost');
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
throw error;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
finally {
|
|
712
|
+
this.isProcessing = false;
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
// Add helper methods for cookie management with localStorage fallback
|
|
717
|
+
setCookie(name, value, daysToExpire) {
|
|
718
|
+
if (!isBrowser)
|
|
719
|
+
return;
|
|
720
|
+
try {
|
|
721
|
+
// Try to set cookie first
|
|
722
|
+
const date = new Date();
|
|
723
|
+
date.setTime(date.getTime() + (daysToExpire * 24 * 60 * 60 * 1000));
|
|
724
|
+
const expires = `expires=${date.toUTCString()}`;
|
|
725
|
+
document.cookie = `${name}=${value};${expires};path=/;SameSite=Lax`;
|
|
726
|
+
// Also store in localStorage as backup
|
|
727
|
+
localStorage.setItem(name, value);
|
|
728
|
+
logDebug(`Set cookie and localStorage: ${name}`);
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
// If cookie fails, use localStorage only
|
|
732
|
+
try {
|
|
733
|
+
localStorage.setItem(name, value);
|
|
734
|
+
logDebug(`Cookie blocked, using localStorage: ${name}`);
|
|
735
|
+
}
|
|
736
|
+
catch (localStorageError) {
|
|
737
|
+
logError('Failed to store user ID in both cookie and localStorage:', localStorageError);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
getCookie(name) {
|
|
742
|
+
if (!isBrowser)
|
|
743
|
+
return null;
|
|
744
|
+
try {
|
|
745
|
+
// Try to get from cookie first
|
|
746
|
+
const nameEQ = name + "=";
|
|
747
|
+
const ca = document.cookie.split(';');
|
|
748
|
+
for (let i = 0; i < ca.length; i++) {
|
|
749
|
+
let c = ca[i];
|
|
750
|
+
while (c.charAt(0) === ' ')
|
|
751
|
+
c = c.substring(1, c.length);
|
|
752
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
753
|
+
const cookieValue = c.substring(nameEQ.length, c.length);
|
|
754
|
+
logDebug(`Found cookie: ${name}`);
|
|
755
|
+
return cookieValue;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
// If cookie not found, try localStorage
|
|
759
|
+
const localStorageValue = localStorage.getItem(name);
|
|
760
|
+
if (localStorageValue) {
|
|
761
|
+
logDebug(`Cookie not found, using localStorage: ${name}`);
|
|
762
|
+
return localStorageValue;
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
// If cookie access fails, try localStorage
|
|
768
|
+
try {
|
|
769
|
+
const localStorageValue = localStorage.getItem(name);
|
|
770
|
+
if (localStorageValue) {
|
|
771
|
+
logDebug(`Cookie access failed, using localStorage: ${name}`);
|
|
772
|
+
return localStorageValue;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
catch (localStorageError) {
|
|
776
|
+
logError('Failed to access both cookie and localStorage:', localStorageError);
|
|
777
|
+
}
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Delete a cookie by setting its expiration date to the past
|
|
783
|
+
* @param name The name of the cookie to delete
|
|
784
|
+
*/
|
|
785
|
+
deleteCookie(name) {
|
|
786
|
+
if (!isBrowser)
|
|
787
|
+
return;
|
|
788
|
+
try {
|
|
789
|
+
// Delete cookie by setting expiration to past
|
|
790
|
+
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Lax`;
|
|
791
|
+
logDebug(`Deleted cookie: ${name}`);
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
logError(`Failed to delete cookie: ${name}`, error);
|
|
795
|
+
}
|
|
796
|
+
// Also remove from localStorage
|
|
797
|
+
try {
|
|
798
|
+
localStorage.removeItem(name);
|
|
799
|
+
logDebug(`Removed from localStorage: ${name}`);
|
|
800
|
+
}
|
|
801
|
+
catch (error) {
|
|
802
|
+
logError(`Failed to remove from localStorage: ${name}`, error);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Log out the current user by clearing all user-related data
|
|
807
|
+
* This will delete the user ID cookie, clear localStorage, and reset user properties
|
|
808
|
+
*/
|
|
809
|
+
logout() {
|
|
810
|
+
if (!isBrowser)
|
|
811
|
+
return;
|
|
812
|
+
try {
|
|
813
|
+
// Clear user ID cookie and localStorage
|
|
814
|
+
const userIdCookieName = `human_behavior_end_user_id_${this.apiKey}`;
|
|
815
|
+
this.deleteCookie(userIdCookieName);
|
|
816
|
+
// Clear session data from localStorage
|
|
817
|
+
localStorage.removeItem('human_behavior_session_id');
|
|
818
|
+
localStorage.removeItem('human_behavior_last_activity');
|
|
819
|
+
// Reset user-related properties
|
|
820
|
+
this.endUserId = null;
|
|
821
|
+
this.userProperties = {};
|
|
822
|
+
logInfo('User logged out - cleared all user data and cookies');
|
|
823
|
+
}
|
|
824
|
+
catch (error) {
|
|
825
|
+
logError('Error during logout:', error);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Start redaction functionality for sensitive input fields
|
|
830
|
+
* @param options Optional configuration for redaction behavior
|
|
831
|
+
*/
|
|
832
|
+
redact(options) {
|
|
833
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
834
|
+
yield this.ensureInitialized();
|
|
835
|
+
if (!isBrowser) {
|
|
836
|
+
logWarn('Redaction is only available in browser environments');
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
// Create a new redaction manager with the provided options
|
|
840
|
+
this.redactionManager = new RedactionManager(options);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Set specific fields to be redacted during session recording
|
|
845
|
+
* @param fields Array of CSS selectors for fields to redact (e.g., ['input[type="password"]', '#email-field'])
|
|
846
|
+
*/
|
|
847
|
+
setRedactedFields(fields) {
|
|
848
|
+
if (!isBrowser) {
|
|
849
|
+
logWarn('Redaction is only available in browser environments');
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
this.redactionManager.setFieldsToRedact(fields);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Check if redaction is currently active
|
|
856
|
+
*/
|
|
857
|
+
isRedactionActive() {
|
|
858
|
+
return this.redactionManager.isActive();
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Get the currently selected fields for redaction
|
|
862
|
+
*/
|
|
863
|
+
getRedactedFields() {
|
|
864
|
+
return this.redactionManager.getSelectedFields();
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Get the current session ID
|
|
868
|
+
*/
|
|
869
|
+
getSessionId() {
|
|
870
|
+
return this.sessionId;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Get the current URL being tracked
|
|
874
|
+
*/
|
|
875
|
+
getCurrentUrl() {
|
|
876
|
+
return this.currentUrl;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Test if the tracker can reach the ingestion server
|
|
880
|
+
*/
|
|
881
|
+
testConnection() {
|
|
882
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
883
|
+
try {
|
|
884
|
+
yield this.api.init(this.sessionId, this.endUserId);
|
|
885
|
+
return { success: true };
|
|
886
|
+
}
|
|
887
|
+
catch (error) {
|
|
888
|
+
return {
|
|
889
|
+
success: false,
|
|
890
|
+
error: error.message || 'Unknown error'
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Get connection status and recommendations
|
|
897
|
+
*/
|
|
898
|
+
getConnectionStatus() {
|
|
899
|
+
const recommendations = [];
|
|
900
|
+
let blocked = false;
|
|
901
|
+
// Check if we have queued events (might indicate blocking)
|
|
902
|
+
if (this.eventIngestionQueue.length > 0) {
|
|
903
|
+
blocked = true;
|
|
904
|
+
recommendations.push('Some requests may be blocked by ad blockers');
|
|
905
|
+
}
|
|
906
|
+
// Check if connection was blocked during initialization
|
|
907
|
+
if (this._connectionBlocked) {
|
|
908
|
+
blocked = true;
|
|
909
|
+
recommendations.push('Initial connection test failed - ad blocker may be active');
|
|
910
|
+
}
|
|
911
|
+
// Check if we're in a browser environment
|
|
912
|
+
if (typeof window === 'undefined') {
|
|
913
|
+
recommendations.push('Not running in browser environment');
|
|
914
|
+
}
|
|
915
|
+
// Check if navigator.sendBeacon is available
|
|
916
|
+
if (typeof navigator.sendBeacon === 'undefined') {
|
|
917
|
+
recommendations.push('sendBeacon not available, using fetch fallback');
|
|
918
|
+
}
|
|
919
|
+
return { blocked, recommendations };
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Check if the current user is a preexisting user
|
|
923
|
+
* Returns true if the user has an existing endUserId cookie from a previous session
|
|
924
|
+
*/
|
|
925
|
+
isPreexistingUser() {
|
|
926
|
+
if (!isBrowser) {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
// Check if there's an existing endUserId cookie for this API key
|
|
930
|
+
const existingEndUserId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
|
|
931
|
+
return existingEndUserId !== null && existingEndUserId !== this.endUserId;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Get user information including whether they are preexisting
|
|
935
|
+
*/
|
|
936
|
+
getUserInfo() {
|
|
937
|
+
return {
|
|
938
|
+
endUserId: this.endUserId,
|
|
939
|
+
sessionId: this.sessionId,
|
|
940
|
+
isPreexistingUser: this.isPreexistingUser(),
|
|
941
|
+
initialized: this.initialized
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// Only expose to window object in browser environments
|
|
946
|
+
if (isBrowser) {
|
|
947
|
+
window.HumanBehaviorTracker = HumanBehaviorTracker;
|
|
948
|
+
}
|
|
949
|
+
export default HumanBehaviorTracker;
|
|
950
|
+
//# sourceMappingURL=tracker.js.map
|