humanbehavior-js 0.0.7 → 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 +277 -47
- 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 +273 -48
- 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 +60 -3
- package/package.json +2 -2
- package/readme.md +116 -28
- package/src/api.ts +22 -8
- package/src/index.ts +3 -0
- package/src/react/index.tsx +85 -62
- package/src/redact.ts +19 -17
- package/src/tracker.ts +157 -26
- package/src/utils/logger.ts +144 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -101,10 +101,34 @@ declare class HumanBehaviorTracker {
|
|
|
101
101
|
private initialized;
|
|
102
102
|
initializationPromise: Promise<void> | null;
|
|
103
103
|
private redactionManager;
|
|
104
|
-
|
|
104
|
+
private originalConsole;
|
|
105
|
+
private originalLogger;
|
|
106
|
+
private consoleTrackingEnabled;
|
|
107
|
+
constructor(apiKey: string | undefined, ingestionUrl?: string);
|
|
105
108
|
private init;
|
|
106
109
|
private ensureInitialized;
|
|
107
110
|
static logToStorage(message: string): void;
|
|
111
|
+
/**
|
|
112
|
+
* Configure logging behavior for the SDK
|
|
113
|
+
* @param config Logger configuration options
|
|
114
|
+
*/
|
|
115
|
+
static configureLogging(config: {
|
|
116
|
+
level?: 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
117
|
+
enableConsole?: boolean;
|
|
118
|
+
enableStorage?: boolean;
|
|
119
|
+
}): void;
|
|
120
|
+
/**
|
|
121
|
+
* Enable console event tracking
|
|
122
|
+
*/
|
|
123
|
+
enableConsoleTracking(): void;
|
|
124
|
+
/**
|
|
125
|
+
* Disable console event tracking
|
|
126
|
+
*/
|
|
127
|
+
disableConsoleTracking(): void;
|
|
128
|
+
/**
|
|
129
|
+
* Track console events
|
|
130
|
+
*/
|
|
131
|
+
private trackConsoleEvent;
|
|
108
132
|
private setupPageUnloadHandler;
|
|
109
133
|
viewLogs(): void;
|
|
110
134
|
addUserInfo(userProperties: Record<string, any>): Promise<void>;
|
|
@@ -168,5 +192,38 @@ declare class HumanBehaviorAPI {
|
|
|
168
192
|
sendBeaconCustomEvents(events: any[], sessionId: string): boolean;
|
|
169
193
|
}
|
|
170
194
|
|
|
171
|
-
|
|
172
|
-
|
|
195
|
+
declare enum LogLevel {
|
|
196
|
+
NONE = 0,
|
|
197
|
+
ERROR = 1,
|
|
198
|
+
WARN = 2,
|
|
199
|
+
INFO = 3,
|
|
200
|
+
DEBUG = 4
|
|
201
|
+
}
|
|
202
|
+
interface LoggerConfig {
|
|
203
|
+
level: LogLevel;
|
|
204
|
+
enableConsole: boolean;
|
|
205
|
+
enableStorage: boolean;
|
|
206
|
+
}
|
|
207
|
+
declare class Logger {
|
|
208
|
+
private config;
|
|
209
|
+
private isBrowser;
|
|
210
|
+
constructor(config?: Partial<LoggerConfig>);
|
|
211
|
+
setConfig(config: Partial<LoggerConfig>): void;
|
|
212
|
+
private shouldLog;
|
|
213
|
+
private formatMessage;
|
|
214
|
+
error(message: string, ...args: any[]): void;
|
|
215
|
+
warn(message: string, ...args: any[]): void;
|
|
216
|
+
info(message: string, ...args: any[]): void;
|
|
217
|
+
debug(message: string, ...args: any[]): void;
|
|
218
|
+
private logToStorage;
|
|
219
|
+
getLogs(): any[];
|
|
220
|
+
clearLogs(): void;
|
|
221
|
+
}
|
|
222
|
+
declare const logger: Logger;
|
|
223
|
+
declare const logError: (message: string, ...args: any[]) => void;
|
|
224
|
+
declare const logWarn: (message: string, ...args: any[]) => void;
|
|
225
|
+
declare const logInfo: (message: string, ...args: any[]) => void;
|
|
226
|
+
declare const logDebug: (message: string, ...args: any[]) => void;
|
|
227
|
+
|
|
228
|
+
export { HumanBehaviorAPI, HumanBehaviorTracker, LogLevel, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, isChunkSizeExceeded, logDebug, logError, logInfo, logWarn, logger, redactionManager, validateSingleEventSize };
|
|
229
|
+
export type { LoggerConfig, RedactionOptions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "humanbehavior-js",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "SDK for HumanBehavior session and event recording",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"repository": {
|
|
51
51
|
"type": "git",
|
|
52
|
-
"url": "https://github.com/humanbehavior-gh/HumanBehavior.git"
|
|
52
|
+
"url": "git+https://github.com/humanbehavior-gh/HumanBehavior.git"
|
|
53
53
|
},
|
|
54
54
|
"keywords": [
|
|
55
55
|
"session-recording",
|
package/readme.md
CHANGED
|
@@ -1,57 +1,145 @@
|
|
|
1
|
-
|
|
1
|
+
# HumanBehavior SDK
|
|
2
2
|
|
|
3
|
+
A JavaScript SDK for session recording and user behavior analytics.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
## Installation
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install humanbehavior-js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Vanilla JavaScript
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<script src="https://unpkg.com/humanbehavior-js@0.0.7/dist/index.min.js"></script>
|
|
17
|
+
<script>
|
|
18
|
+
const tracker = new HumanBehaviorTracker("your-api-key-here");
|
|
19
|
+
tracker.start();
|
|
20
|
+
|
|
21
|
+
// Set up redaction for sensitive fields
|
|
22
|
+
tracker.setRedactedFields(['#password', 'input[type="password"]']);
|
|
23
|
+
</script>
|
|
24
|
+
```
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
|
|
26
|
+
### Next.js
|
|
27
|
+
|
|
28
|
+
Add the following to your `providers.tsx` file:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
10
31
|
"use client"
|
|
11
32
|
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
33
|
+
import { HumanBehaviorTracker } from "humanbehavior-js";
|
|
34
|
+
import { HumanBehaviorProvider } from "humanbehavior-js/react";
|
|
14
35
|
import { useEffect, useState } from "react";
|
|
15
36
|
|
|
16
|
-
export function
|
|
17
|
-
const [
|
|
37
|
+
export function HumanBehaviorProviderWrapper({ children }: { children: React.ReactNode }) {
|
|
38
|
+
const [tracker, setTracker] = useState<HumanBehaviorTracker | null>(null);
|
|
18
39
|
|
|
19
40
|
useEffect(() => {
|
|
20
|
-
const apiKey =
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
const apiKey = process.env.NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY;
|
|
42
|
+
if (apiKey) {
|
|
43
|
+
const tracker = new HumanBehaviorTracker(apiKey);
|
|
44
|
+
setTracker(tracker);
|
|
45
|
+
}
|
|
23
46
|
}, []);
|
|
24
47
|
|
|
25
48
|
return (
|
|
26
|
-
<
|
|
49
|
+
<HumanBehaviorProvider client={tracker}>
|
|
50
|
+
{children}
|
|
51
|
+
</HumanBehaviorProvider>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or use the simpler approach with just an API key:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
"use client"
|
|
60
|
+
|
|
61
|
+
import { HumanBehaviorProvider } from "humanbehavior-js/react";
|
|
62
|
+
|
|
63
|
+
export function HumanBehaviorProviderWrapper({ children }: { children: React.ReactNode }) {
|
|
64
|
+
return (
|
|
65
|
+
<HumanBehaviorProvider apiKey={process.env.NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY}>
|
|
27
66
|
{children}
|
|
28
|
-
</
|
|
67
|
+
</HumanBehaviorProvider>
|
|
29
68
|
)
|
|
30
69
|
}
|
|
31
70
|
```
|
|
32
71
|
|
|
33
|
-
Next, add the provider to your root app layout
|
|
34
|
-
|
|
72
|
+
Next, add the provider to your root app layout:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
35
75
|
export default async function RootLayout({
|
|
36
76
|
children
|
|
37
77
|
}: {
|
|
38
78
|
children: React.ReactNode;
|
|
39
79
|
}) {
|
|
40
|
-
const session = await auth();
|
|
41
80
|
return (
|
|
42
|
-
<html lang='en'
|
|
43
|
-
<body
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<Providers session={session}>
|
|
48
|
-
<Toaster />
|
|
49
|
-
{children}
|
|
50
|
-
</Providers>
|
|
51
|
-
</NuqsAdapter>
|
|
52
|
-
</KoalawareProvider>
|
|
81
|
+
<html lang='en' suppressHydrationWarning>
|
|
82
|
+
<body>
|
|
83
|
+
<HumanBehaviorProviderWrapper>
|
|
84
|
+
{children}
|
|
85
|
+
</HumanBehaviorProviderWrapper>
|
|
53
86
|
</body>
|
|
54
87
|
</html>
|
|
55
88
|
);
|
|
56
89
|
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Using the Hook
|
|
93
|
+
|
|
94
|
+
In any component within the provider:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { useHumanBehavior } from "humanbehavior-js/react";
|
|
98
|
+
|
|
99
|
+
export function MyComponent() {
|
|
100
|
+
const humanBehavior = useHumanBehavior();
|
|
101
|
+
|
|
102
|
+
const handleUserAction = async () => {
|
|
103
|
+
// Add user information
|
|
104
|
+
await humanBehavior.addUserInfo({
|
|
105
|
+
email: "user@example.com",
|
|
106
|
+
name: "John Doe"
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Track custom events
|
|
110
|
+
humanBehavior.addEvent({
|
|
111
|
+
type: "custom",
|
|
112
|
+
name: "button_clicked",
|
|
113
|
+
data: { buttonId: "submit" }
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return <button onClick={handleUserAction}>Click me</button>;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Environment Variables
|
|
122
|
+
|
|
123
|
+
For security, store your API key in environment variables:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# .env.local
|
|
127
|
+
NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY=your-api-key-here
|
|
128
|
+
NEXT_PUBLIC_INGESTION_URL=https://ingestion.humanbehavior.ai
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Features
|
|
132
|
+
|
|
133
|
+
- **Session Recording**: Automatic recording of user interactions
|
|
134
|
+
- **Data Redaction**: Protect sensitive information in recordings
|
|
135
|
+
- **User Identification**: Track users across sessions
|
|
136
|
+
- **Custom Events**: Send custom analytics events
|
|
137
|
+
- **React Integration**: Hooks and providers for React applications
|
|
138
|
+
|
|
139
|
+
## Build
|
|
140
|
+
|
|
141
|
+
To build the SDK locally:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm run build
|
|
57
145
|
```
|
package/src/api.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logError, logInfo } from './utils/logger';
|
|
2
|
+
|
|
1
3
|
export const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
|
|
2
4
|
|
|
3
5
|
export function isChunkSizeExceeded(currentChunk: any[], newEvent: any, sessionId: string): boolean {
|
|
@@ -30,15 +32,27 @@ export class HumanBehaviorAPI {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
public async init(sessionId: string, userId: string | null) {
|
|
35
|
+
// Get current page URL and referrer if in browser environment
|
|
36
|
+
let entryURL = null;
|
|
37
|
+
let referrer = null;
|
|
38
|
+
|
|
39
|
+
if (typeof window !== 'undefined') {
|
|
40
|
+
entryURL = window.location.href;
|
|
41
|
+
referrer = document.referrer;
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
const response = await fetch(`${this.baseUrl}/api/ingestion/init`, {
|
|
34
45
|
method: 'POST',
|
|
35
46
|
headers: {
|
|
36
47
|
'Content-Type': 'application/json',
|
|
37
|
-
'Authorization': `Bearer ${this.apiKey}
|
|
48
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
49
|
+
'Referer': referrer || ''
|
|
38
50
|
},
|
|
39
51
|
body: JSON.stringify({
|
|
40
52
|
sessionId: sessionId,
|
|
41
|
-
endUserId: userId
|
|
53
|
+
endUserId: userId,
|
|
54
|
+
entryURL: entryURL,
|
|
55
|
+
referrer: referrer
|
|
42
56
|
})
|
|
43
57
|
});
|
|
44
58
|
|
|
@@ -135,7 +149,7 @@ export class HumanBehaviorAPI {
|
|
|
135
149
|
|
|
136
150
|
return results.flat();
|
|
137
151
|
} catch (error) {
|
|
138
|
-
|
|
152
|
+
logError('Error sending events:', error);
|
|
139
153
|
throw error;
|
|
140
154
|
}
|
|
141
155
|
}
|
|
@@ -161,7 +175,7 @@ export class HumanBehaviorAPI {
|
|
|
161
175
|
|
|
162
176
|
return await response.json();
|
|
163
177
|
} catch (error) {
|
|
164
|
-
|
|
178
|
+
logError('Error sending user data:', error);
|
|
165
179
|
throw error;
|
|
166
180
|
}
|
|
167
181
|
}
|
|
@@ -188,7 +202,7 @@ export class HumanBehaviorAPI {
|
|
|
188
202
|
|
|
189
203
|
return await response.json();
|
|
190
204
|
} catch (error) {
|
|
191
|
-
|
|
205
|
+
logError('Error authenticating user:', error);
|
|
192
206
|
throw error;
|
|
193
207
|
}
|
|
194
208
|
}
|
|
@@ -236,7 +250,7 @@ export class HumanBehaviorAPI {
|
|
|
236
250
|
} catch (error) {
|
|
237
251
|
retryCount++;
|
|
238
252
|
if (retryCount === maxRetries) {
|
|
239
|
-
|
|
253
|
+
logError('Error sending custom event after max retries:', error);
|
|
240
254
|
throw error;
|
|
241
255
|
}
|
|
242
256
|
// Exponential backoff
|
|
@@ -273,7 +287,7 @@ export class HumanBehaviorAPI {
|
|
|
273
287
|
} catch (error) {
|
|
274
288
|
retryCount++;
|
|
275
289
|
if (retryCount === maxRetries) {
|
|
276
|
-
|
|
290
|
+
logError('Error sending custom events after max retries:', error);
|
|
277
291
|
throw error;
|
|
278
292
|
}
|
|
279
293
|
// Exponential backoff
|
|
@@ -289,7 +303,7 @@ export class HumanBehaviorAPI {
|
|
|
289
303
|
data.append('timestamp', encodeURIComponent(Date.now().toString()))
|
|
290
304
|
data.append('apiKey', encodeURIComponent(this.apiKey))
|
|
291
305
|
if (isSessionComplete) {
|
|
292
|
-
|
|
306
|
+
logInfo('Session complete beacon sending');
|
|
293
307
|
localStorage.setItem('koalaware_session_complete', Date.now().toString());
|
|
294
308
|
data.append('sessionComplete', encodeURIComponent('true'))
|
|
295
309
|
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,9 @@ export * from './api';
|
|
|
13
13
|
// Export redaction functionality
|
|
14
14
|
export * from './redact';
|
|
15
15
|
|
|
16
|
+
// Export logger functionality
|
|
17
|
+
export * from './utils/logger';
|
|
18
|
+
|
|
16
19
|
// Also export the tracker as the default export
|
|
17
20
|
export { HumanBehaviorTracker as default } from './tracker';
|
|
18
21
|
|
package/src/react/index.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useEffect, useState, createContext, useContext, ReactNode } from "react";
|
|
1
|
+
import React, { useEffect, useState, createContext, useContext, ReactNode, useCallback, useMemo, useRef } from "react";
|
|
2
2
|
import { HumanBehaviorTracker } from "..";
|
|
3
|
+
import { logError, logWarn, logDebug } from "../utils/logger";
|
|
3
4
|
|
|
4
5
|
// Check if we're in a browser environment
|
|
5
6
|
const isBrowser = () => typeof window !== 'undefined';
|
|
@@ -25,6 +26,7 @@ interface HumanBehaviorProviderProps {
|
|
|
25
26
|
children: ReactNode;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
// Default context to prevent unnecessary re-renders
|
|
28
30
|
const defaultContext: HumanBehaviorContextType = {
|
|
29
31
|
humanBehavior: null,
|
|
30
32
|
queueEvent: (event: any) => {
|
|
@@ -32,7 +34,7 @@ const defaultContext: HumanBehaviorContextType = {
|
|
|
32
34
|
if (typeof window === 'undefined') {
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
|
-
|
|
37
|
+
logWarn('HumanBehavior not initialized yet, event queued:', event);
|
|
36
38
|
}
|
|
37
39
|
};
|
|
38
40
|
|
|
@@ -43,11 +45,27 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
|
|
|
43
45
|
const [eventQueue, setEventQueue] = useState<any[]>([]);
|
|
44
46
|
const [isMounted, setIsMounted] = useState(false);
|
|
45
47
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
48
|
+
|
|
49
|
+
// Use refs to avoid dependency issues in useEffect
|
|
50
|
+
const apiKeyRef = useRef(apiKey);
|
|
51
|
+
const clientRef = useRef(client);
|
|
52
|
+
const eventQueueRef = useRef(eventQueue);
|
|
53
|
+
|
|
54
|
+
// Update refs when props change
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
apiKeyRef.current = apiKey;
|
|
57
|
+
clientRef.current = client;
|
|
58
|
+
}, [apiKey, client]);
|
|
59
|
+
|
|
60
|
+
// Update eventQueue ref when queue changes
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
eventQueueRef.current = eventQueue;
|
|
63
|
+
}, [eventQueue]);
|
|
46
64
|
|
|
47
|
-
//
|
|
48
|
-
const queueEvent = (event: any) => {
|
|
65
|
+
// Memoized queueEvent function to prevent unnecessary re-renders
|
|
66
|
+
const queueEvent = useCallback((event: any) => {
|
|
49
67
|
setEventQueue(prev => [...prev, event]);
|
|
50
|
-
};
|
|
68
|
+
}, []);
|
|
51
69
|
|
|
52
70
|
// Handle mounting state
|
|
53
71
|
useEffect(() => {
|
|
@@ -66,15 +84,15 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
|
|
|
66
84
|
}
|
|
67
85
|
|
|
68
86
|
// If client is provided, use that
|
|
69
|
-
if (
|
|
70
|
-
setHumanBehavior(
|
|
87
|
+
if (clientRef.current) {
|
|
88
|
+
setHumanBehavior(clientRef.current);
|
|
71
89
|
setIsInitialized(true);
|
|
72
90
|
return;
|
|
73
91
|
}
|
|
74
92
|
|
|
75
93
|
// If no client is provided, apiKey is required
|
|
76
|
-
if (!
|
|
77
|
-
|
|
94
|
+
if (!apiKeyRef.current || apiKeyRef.current.trim() === '') {
|
|
95
|
+
logError("An apiKey is required when no client is provided");
|
|
78
96
|
return;
|
|
79
97
|
}
|
|
80
98
|
|
|
@@ -83,7 +101,10 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
|
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
// Create new tracker instance with the validated apiKey
|
|
86
|
-
const tracker = new HumanBehaviorTracker(
|
|
104
|
+
const tracker = new HumanBehaviorTracker(
|
|
105
|
+
apiKeyRef.current.trim(),
|
|
106
|
+
process.env.NEXT_PUBLIC_INGESTION_URL || 'https://ingestion.humanbehavior.ai'
|
|
107
|
+
);
|
|
87
108
|
setHumanBehavior(tracker);
|
|
88
109
|
|
|
89
110
|
// Wait for initialization to complete
|
|
@@ -91,10 +112,11 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
|
|
|
91
112
|
setIsInitialized(true);
|
|
92
113
|
|
|
93
114
|
// Process any queued events
|
|
94
|
-
|
|
95
|
-
|
|
115
|
+
const currentQueue = eventQueueRef.current;
|
|
116
|
+
if (currentQueue.length > 0) {
|
|
117
|
+
currentQueue.forEach(event => {
|
|
96
118
|
if (event.type === 'identify') {
|
|
97
|
-
|
|
119
|
+
logDebug('Processing queued identify event', event.userProperties);
|
|
98
120
|
tracker.addUserInfo(event.userProperties);
|
|
99
121
|
} else {
|
|
100
122
|
tracker.addEvent(event);
|
|
@@ -103,40 +125,66 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
|
|
|
103
125
|
setEventQueue([]); // Clear the queue
|
|
104
126
|
}
|
|
105
127
|
}).catch(error => {
|
|
106
|
-
|
|
128
|
+
logError('Failed to initialize HumanBehaviorTracker:', error);
|
|
107
129
|
});
|
|
108
|
-
}, [apiKey, client, eventQueue
|
|
130
|
+
}, [isMounted, humanBehavior]); // Removed apiKey, client, eventQueue from dependencies
|
|
131
|
+
|
|
132
|
+
// Memoized context value to prevent unnecessary re-renders
|
|
133
|
+
const contextValue = useMemo(() => {
|
|
134
|
+
if (!isMounted) {
|
|
135
|
+
return { humanBehavior: null, queueEvent };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!isInitialized) {
|
|
139
|
+
return { humanBehavior: null, queueEvent };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { humanBehavior, queueEvent };
|
|
143
|
+
}, [isMounted, isInitialized, humanBehavior, queueEvent]);
|
|
109
144
|
|
|
110
145
|
// If not in browser, render children without context
|
|
111
146
|
if (!(isBrowser())) {
|
|
112
147
|
return <>{children}</>;
|
|
113
148
|
}
|
|
114
149
|
|
|
115
|
-
// If not mounted yet, render children with queuing context
|
|
116
|
-
if (!isMounted) {
|
|
117
|
-
return (
|
|
118
|
-
<HumanBehaviorContext.Provider value={{ humanBehavior: null, queueEvent }}>
|
|
119
|
-
{children}
|
|
120
|
-
</HumanBehaviorContext.Provider>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// If not initialized yet, render children with queuing context
|
|
125
|
-
if (!isInitialized) {
|
|
126
|
-
return (
|
|
127
|
-
<HumanBehaviorContext.Provider value={{ humanBehavior: null, queueEvent }}>
|
|
128
|
-
{children}
|
|
129
|
-
</HumanBehaviorContext.Provider>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
150
|
return (
|
|
134
|
-
<HumanBehaviorContext.Provider value={
|
|
151
|
+
<HumanBehaviorContext.Provider value={contextValue}>
|
|
135
152
|
{children}
|
|
136
153
|
</HumanBehaviorContext.Provider>
|
|
137
154
|
);
|
|
138
155
|
};
|
|
139
156
|
|
|
157
|
+
// No-op implementation for server-side
|
|
158
|
+
const serverSideImplementation: HumanBehaviorInterface = {
|
|
159
|
+
addEvent: () => {},
|
|
160
|
+
addUserInfo: async () => {},
|
|
161
|
+
start: () => {},
|
|
162
|
+
stop: () => {},
|
|
163
|
+
viewLogs: () => {},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Memoized queuing implementation for initialization period
|
|
167
|
+
const createQueuingImplementation = (queueEvent: (event: any) => void): HumanBehaviorInterface => ({
|
|
168
|
+
addEvent: (event: any) => {
|
|
169
|
+
queueEvent(event);
|
|
170
|
+
},
|
|
171
|
+
addUserInfo: async (userProperties: Record<string, any>) => {
|
|
172
|
+
queueEvent({
|
|
173
|
+
type: 'identify',
|
|
174
|
+
userProperties,
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
start: () => {
|
|
178
|
+
// Start will be called automatically when initialized
|
|
179
|
+
},
|
|
180
|
+
stop: () => {
|
|
181
|
+
// Stop is a no-op when not initialized
|
|
182
|
+
},
|
|
183
|
+
viewLogs: () => {
|
|
184
|
+
logWarn('Logs are not available until HumanBehaviorTracker is initialized');
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
140
188
|
export const useHumanBehavior = (): HumanBehaviorInterface => {
|
|
141
189
|
const context = useContext(HumanBehaviorContext);
|
|
142
190
|
|
|
@@ -147,14 +195,8 @@ export const useHumanBehavior = (): HumanBehaviorInterface => {
|
|
|
147
195
|
|
|
148
196
|
// If we're in the server-side, return a no-op implementation
|
|
149
197
|
if (typeof window === 'undefined') {
|
|
150
|
-
|
|
151
|
-
return
|
|
152
|
-
addEvent: () => {},
|
|
153
|
-
addUserInfo: async () => {},
|
|
154
|
-
start: () => {},
|
|
155
|
-
stop: () => {},
|
|
156
|
-
viewLogs: () => {},
|
|
157
|
-
};
|
|
198
|
+
logWarn('HumanBehavior is being used before being initialized');
|
|
199
|
+
return serverSideImplementation;
|
|
158
200
|
}
|
|
159
201
|
|
|
160
202
|
// If we have an initialized tracker, return it as the interface
|
|
@@ -166,24 +208,5 @@ export const useHumanBehavior = (): HumanBehaviorInterface => {
|
|
|
166
208
|
// - context.humanBehavior is null
|
|
167
209
|
// - context.queueEvent is available
|
|
168
210
|
// - we need to queue all operations until initialization completes
|
|
169
|
-
return
|
|
170
|
-
addEvent: (event: any) => {
|
|
171
|
-
context.queueEvent(event);
|
|
172
|
-
},
|
|
173
|
-
addUserInfo: async (userProperties: Record<string, any>) => {
|
|
174
|
-
context.queueEvent({
|
|
175
|
-
type: 'identify',
|
|
176
|
-
userProperties,
|
|
177
|
-
});
|
|
178
|
-
},
|
|
179
|
-
start: () => {
|
|
180
|
-
// Start will be called automatically when initialized
|
|
181
|
-
},
|
|
182
|
-
stop: () => {
|
|
183
|
-
// Stop is a no-op when not initialized
|
|
184
|
-
},
|
|
185
|
-
viewLogs: () => {
|
|
186
|
-
console.warn('Logs are not available until HumanBehaviorTracker is initialized');
|
|
187
|
-
}
|
|
188
|
-
};
|
|
211
|
+
return useMemo(() => createQueuingImplementation(context.queueEvent), [context.queueEvent]);
|
|
189
212
|
};
|