humanbehavior-js 0.0.8 → 0.0.9
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/cjs/index.js +254 -41
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/index.js +188 -51
- package/dist/cjs/react/index.js.map +1 -1
- package/dist/esm/index.js +250 -42
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/index.js +189 -52
- package/dist/esm/react/index.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/dist/types/index.d.ts +59 -2
- package/package.json +2 -2
- package/src/api.ts +8 -6
- package/src/index.ts +3 -0
- package/src/react/index.tsx +85 -62
- package/src/redact.ts +19 -17
- package/src/tracker.ts +139 -20
- package/src/utils/logger.ts +144 -0
package/src/tracker.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as rrweb from 'rrweb';
|
|
|
2
2
|
import { v1 as uuidv1 } from 'uuid';
|
|
3
3
|
import { HumanBehaviorAPI } from './api';
|
|
4
4
|
import { RedactionManager, RedactionOptions } from './redact';
|
|
5
|
+
import { logger, logError, logWarn, logInfo, logDebug } from './utils/logger';
|
|
5
6
|
|
|
6
7
|
// Check if we're in a browser environment
|
|
7
8
|
const isBrowser = typeof window !== 'undefined';
|
|
@@ -30,6 +31,20 @@ export class HumanBehaviorTracker {
|
|
|
30
31
|
private initialized: boolean = false;
|
|
31
32
|
public initializationPromise: Promise<void> | null = null;
|
|
32
33
|
private redactionManager: RedactionManager;
|
|
34
|
+
|
|
35
|
+
// Console tracking properties
|
|
36
|
+
private originalConsole: {
|
|
37
|
+
log: typeof console.log;
|
|
38
|
+
warn: typeof console.warn;
|
|
39
|
+
error: typeof console.error;
|
|
40
|
+
} | null = null;
|
|
41
|
+
private originalLogger: {
|
|
42
|
+
error: typeof logError;
|
|
43
|
+
warn: typeof logWarn;
|
|
44
|
+
info: typeof logInfo;
|
|
45
|
+
debug: typeof logDebug;
|
|
46
|
+
} | null = null;
|
|
47
|
+
private consoleTrackingEnabled: boolean = false;
|
|
33
48
|
|
|
34
49
|
constructor(apiKey: string | undefined, ingestionUrl?: string) {
|
|
35
50
|
if (!apiKey) {
|
|
@@ -89,13 +104,13 @@ export class HumanBehaviorTracker {
|
|
|
89
104
|
this.start();
|
|
90
105
|
this.processRejectedEvents();
|
|
91
106
|
} else {
|
|
92
|
-
|
|
107
|
+
logWarn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
|
|
93
108
|
}
|
|
94
109
|
|
|
95
110
|
this.initialized = true;
|
|
96
|
-
|
|
111
|
+
logInfo('HumanBehaviorTracker initialized');
|
|
97
112
|
} catch (error) {
|
|
98
|
-
|
|
113
|
+
logError('Failed to initialize HumanBehaviorTracker:', error);
|
|
99
114
|
throw error;
|
|
100
115
|
}
|
|
101
116
|
}
|
|
@@ -108,25 +123,123 @@ export class HumanBehaviorTracker {
|
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
public static logToStorage(message: string) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
logInfo(message);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Configure logging behavior for the SDK
|
|
131
|
+
* @param config Logger configuration options
|
|
132
|
+
*/
|
|
133
|
+
public static configureLogging(config: { level?: 'none' | 'error' | 'warn' | 'info' | 'debug', enableConsole?: boolean, enableStorage?: boolean }) {
|
|
134
|
+
const levelMap = {
|
|
135
|
+
'none': 0,
|
|
136
|
+
'error': 1,
|
|
137
|
+
'warn': 2,
|
|
138
|
+
'info': 3,
|
|
139
|
+
'debug': 4
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
logger.setConfig({
|
|
143
|
+
level: levelMap[config.level || 'error'],
|
|
144
|
+
enableConsole: config.enableConsole !== false,
|
|
145
|
+
enableStorage: config.enableStorage || false
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Enable console event tracking
|
|
151
|
+
*/
|
|
152
|
+
public enableConsoleTracking(): void {
|
|
153
|
+
if (!isBrowser || this.consoleTrackingEnabled) return;
|
|
154
|
+
|
|
155
|
+
// Store original console methods
|
|
156
|
+
this.originalConsole = {
|
|
157
|
+
log: console.log,
|
|
158
|
+
warn: console.warn,
|
|
159
|
+
error: console.error
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Store original logger methods
|
|
163
|
+
this.originalLogger = {
|
|
164
|
+
error: logError,
|
|
165
|
+
warn: logWarn,
|
|
166
|
+
info: logInfo,
|
|
167
|
+
debug: logDebug
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Override console methods to capture ALL console output (including logger output)
|
|
171
|
+
console.log = (...args) => {
|
|
172
|
+
this.trackConsoleEvent('log', args);
|
|
173
|
+
this.originalConsole!.log(...args);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
console.warn = (...args) => {
|
|
177
|
+
this.trackConsoleEvent('warn', args);
|
|
178
|
+
this.originalConsole!.warn(...args);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
console.error = (...args) => {
|
|
182
|
+
this.trackConsoleEvent('error', args);
|
|
183
|
+
this.originalConsole!.error(...args);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
this.consoleTrackingEnabled = true;
|
|
187
|
+
this.originalLogger!.debug('Console tracking enabled');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Disable console event tracking
|
|
192
|
+
*/
|
|
193
|
+
public disableConsoleTracking(): void {
|
|
194
|
+
if (!isBrowser || !this.consoleTrackingEnabled || !this.originalConsole) return;
|
|
195
|
+
|
|
196
|
+
// Restore original console methods
|
|
197
|
+
console.log = this.originalConsole.log;
|
|
198
|
+
console.warn = this.originalConsole.warn;
|
|
199
|
+
console.error = this.originalConsole.error;
|
|
200
|
+
|
|
201
|
+
this.consoleTrackingEnabled = false;
|
|
202
|
+
this.originalConsole = null;
|
|
203
|
+
this.originalLogger = null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Track console events
|
|
208
|
+
*/
|
|
209
|
+
private trackConsoleEvent(level: 'log' | 'warn' | 'error', args: any[]): void {
|
|
210
|
+
if (!this.initialized) return;
|
|
211
|
+
|
|
212
|
+
const consoleEvent = {
|
|
213
|
+
type: 5, // Custom event type
|
|
214
|
+
data: {
|
|
215
|
+
payload: {
|
|
216
|
+
type: 'console',
|
|
217
|
+
level: level,
|
|
218
|
+
message: args.map(arg =>
|
|
219
|
+
typeof arg === 'string' ? arg :
|
|
220
|
+
typeof arg === 'object' ? JSON.stringify(arg) :
|
|
221
|
+
String(arg)
|
|
222
|
+
).join(' '),
|
|
223
|
+
timestamp: Date.now(),
|
|
224
|
+
url: window.location.href
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
timestamp: Date.now()
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
this.addEvent(consoleEvent);
|
|
118
231
|
}
|
|
119
232
|
|
|
120
233
|
private setupPageUnloadHandler() {
|
|
121
234
|
if (!isBrowser) return;
|
|
122
235
|
|
|
123
|
-
|
|
236
|
+
logDebug('Setting up page unload handler');
|
|
124
237
|
|
|
125
238
|
// Handle visibility changes for sending events
|
|
126
239
|
window.addEventListener('visibilitychange', () => {
|
|
127
240
|
// Only send events when page becomes hidden
|
|
128
241
|
if (document.visibilityState === 'hidden') {
|
|
129
|
-
|
|
242
|
+
logDebug('Page hidden - sending pending events');
|
|
130
243
|
this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
|
|
131
244
|
}
|
|
132
245
|
});
|
|
@@ -148,11 +261,11 @@ export class HumanBehaviorTracker {
|
|
|
148
261
|
|
|
149
262
|
public viewLogs() {
|
|
150
263
|
try {
|
|
151
|
-
const logs =
|
|
264
|
+
const logs = logger.getLogs();
|
|
152
265
|
console.log('HumanBehavior Logs:', logs);
|
|
153
|
-
|
|
266
|
+
logger.clearLogs(); // Clear logs after viewing
|
|
154
267
|
} catch (e) {
|
|
155
|
-
|
|
268
|
+
logError('Failed to read logs:', e);
|
|
156
269
|
}
|
|
157
270
|
}
|
|
158
271
|
|
|
@@ -194,6 +307,9 @@ export class HumanBehaviorTracker {
|
|
|
194
307
|
this.flush();
|
|
195
308
|
}, this.FLUSH_INTERVAL_MS);
|
|
196
309
|
|
|
310
|
+
// Enable console tracking
|
|
311
|
+
this.enableConsoleTracking();
|
|
312
|
+
|
|
197
313
|
// Start recording with redaction enabled
|
|
198
314
|
rrweb.record({
|
|
199
315
|
emit: (event) => {
|
|
@@ -216,6 +332,9 @@ export class HumanBehaviorTracker {
|
|
|
216
332
|
clearInterval(this.flushInterval);
|
|
217
333
|
this.flushInterval = null;
|
|
218
334
|
}
|
|
335
|
+
|
|
336
|
+
// Disable console tracking
|
|
337
|
+
this.disableConsoleTracking();
|
|
219
338
|
}
|
|
220
339
|
|
|
221
340
|
public async addEvent(event: any) {
|
|
@@ -250,7 +369,7 @@ export class HumanBehaviorTracker {
|
|
|
250
369
|
this.rejectedEvents = [];
|
|
251
370
|
this.sessionId = newSessionId;
|
|
252
371
|
} catch (error) {
|
|
253
|
-
|
|
372
|
+
logError('Failed to process rejected events:', error);
|
|
254
373
|
} finally {
|
|
255
374
|
this.isProcessingRejectedEvents = false;
|
|
256
375
|
}
|
|
@@ -270,13 +389,13 @@ export class HumanBehaviorTracker {
|
|
|
270
389
|
this.queueSizeBytes = 0;
|
|
271
390
|
|
|
272
391
|
if (eventsToProcess.length > 0) {
|
|
273
|
-
|
|
392
|
+
logDebug('Flushing events:', eventsToProcess);
|
|
274
393
|
try {
|
|
275
394
|
await this.api.sendEvents(eventsToProcess, this.sessionId, this.endUserId!);
|
|
276
395
|
} catch (error: any) {
|
|
277
396
|
// If we get a 400 error, store events for retry
|
|
278
397
|
if (error.message?.includes('ERROR: Session already completed')) {
|
|
279
|
-
|
|
398
|
+
logInfo('Session expired, storing events for retry');
|
|
280
399
|
this.rejectedEvents.push(...eventsToProcess);
|
|
281
400
|
this.processRejectedEvents();
|
|
282
401
|
} else {
|
|
@@ -317,7 +436,7 @@ export class HumanBehaviorTracker {
|
|
|
317
436
|
public async redact(options?: RedactionOptions): Promise<void> {
|
|
318
437
|
await this.ensureInitialized();
|
|
319
438
|
if (!isBrowser) {
|
|
320
|
-
|
|
439
|
+
logWarn('Redaction is only available in browser environments');
|
|
321
440
|
return;
|
|
322
441
|
}
|
|
323
442
|
|
|
@@ -331,7 +450,7 @@ export class HumanBehaviorTracker {
|
|
|
331
450
|
*/
|
|
332
451
|
public setRedactedFields(fields: string[]): void {
|
|
333
452
|
if (!isBrowser) {
|
|
334
|
-
|
|
453
|
+
logWarn('Redaction is only available in browser environments');
|
|
335
454
|
return;
|
|
336
455
|
}
|
|
337
456
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export enum LogLevel {
|
|
2
|
+
NONE = 0,
|
|
3
|
+
ERROR = 1,
|
|
4
|
+
WARN = 2,
|
|
5
|
+
INFO = 3,
|
|
6
|
+
DEBUG = 4
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LoggerConfig {
|
|
10
|
+
level: LogLevel;
|
|
11
|
+
enableConsole: boolean;
|
|
12
|
+
enableStorage: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class Logger {
|
|
16
|
+
private config: LoggerConfig = {
|
|
17
|
+
level: LogLevel.ERROR, // Default to only errors in production
|
|
18
|
+
enableConsole: true,
|
|
19
|
+
enableStorage: false
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
private isBrowser = typeof window !== 'undefined';
|
|
23
|
+
|
|
24
|
+
constructor(config?: Partial<LoggerConfig>) {
|
|
25
|
+
if (config) {
|
|
26
|
+
this.config = { ...this.config, ...config };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setConfig(config: Partial<LoggerConfig>): void {
|
|
31
|
+
this.config = { ...this.config, ...config };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private shouldLog(level: LogLevel): boolean {
|
|
35
|
+
return level <= this.config.level;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private formatMessage(level: string, message: string, ...args: any[]): string {
|
|
39
|
+
const timestamp = new Date().toISOString();
|
|
40
|
+
return `[HumanBehavior ${level}] ${timestamp}: ${message}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
error(message: string, ...args: any[]): void {
|
|
44
|
+
if (!this.shouldLog(LogLevel.ERROR)) return;
|
|
45
|
+
|
|
46
|
+
const formattedMessage = this.formatMessage('ERROR', message);
|
|
47
|
+
|
|
48
|
+
if (this.config.enableConsole) {
|
|
49
|
+
console.error(formattedMessage, ...args);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
53
|
+
this.logToStorage(formattedMessage, args);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
warn(message: string, ...args: any[]): void {
|
|
58
|
+
if (!this.shouldLog(LogLevel.WARN)) return;
|
|
59
|
+
|
|
60
|
+
const formattedMessage = this.formatMessage('WARN', message);
|
|
61
|
+
|
|
62
|
+
if (this.config.enableConsole) {
|
|
63
|
+
console.warn(formattedMessage, ...args);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
67
|
+
this.logToStorage(formattedMessage, args);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
info(message: string, ...args: any[]): void {
|
|
72
|
+
if (!this.shouldLog(LogLevel.INFO)) return;
|
|
73
|
+
|
|
74
|
+
const formattedMessage = this.formatMessage('INFO', message);
|
|
75
|
+
|
|
76
|
+
if (this.config.enableConsole) {
|
|
77
|
+
console.log(formattedMessage, ...args);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
81
|
+
this.logToStorage(formattedMessage, args);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
debug(message: string, ...args: any[]): void {
|
|
86
|
+
if (!this.shouldLog(LogLevel.DEBUG)) return;
|
|
87
|
+
|
|
88
|
+
const formattedMessage = this.formatMessage('DEBUG', message);
|
|
89
|
+
|
|
90
|
+
if (this.config.enableConsole) {
|
|
91
|
+
console.log(formattedMessage, ...args);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (this.config.enableStorage && this.isBrowser) {
|
|
95
|
+
this.logToStorage(formattedMessage, args);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private logToStorage(message: string, args: any[]): void {
|
|
100
|
+
try {
|
|
101
|
+
const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
|
|
102
|
+
const logEntry = {
|
|
103
|
+
message,
|
|
104
|
+
args: args.length > 0 ? args : undefined,
|
|
105
|
+
timestamp: Date.now()
|
|
106
|
+
};
|
|
107
|
+
logs.push(logEntry);
|
|
108
|
+
|
|
109
|
+
// Keep only last 1000 logs to prevent storage bloat
|
|
110
|
+
if (logs.length > 1000) {
|
|
111
|
+
logs.splice(0, logs.length - 1000);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
localStorage.setItem('human_behavior_logs', JSON.stringify(logs));
|
|
115
|
+
} catch (e) {
|
|
116
|
+
// Silently fail if storage is not available
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getLogs(): any[] {
|
|
121
|
+
if (!this.isBrowser) return [];
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
|
|
125
|
+
} catch (e) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
clearLogs(): void {
|
|
131
|
+
if (this.isBrowser) {
|
|
132
|
+
localStorage.removeItem('human_behavior_logs');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Create singleton instance
|
|
138
|
+
export const logger = new Logger();
|
|
139
|
+
|
|
140
|
+
// Export convenience methods
|
|
141
|
+
export const logError = (message: string, ...args: any[]) => logger.error(message, ...args);
|
|
142
|
+
export const logWarn = (message: string, ...args: any[]) => logger.warn(message, ...args);
|
|
143
|
+
export const logInfo = (message: string, ...args: any[]) => logger.info(message, ...args);
|
|
144
|
+
export const logDebug = (message: string, ...args: any[]) => logger.debug(message, ...args);
|