humanbehavior-js 0.0.9 → 0.1.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/src/api.ts CHANGED
@@ -207,106 +207,17 @@ export class HumanBehaviorAPI {
207
207
  }
208
208
  }
209
209
 
210
- async sendSessionComplete(sessionId: string) {
211
- const response = await fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
212
- method: 'POST',
213
- headers: {
214
- 'Content-Type': 'application/json',
215
- 'Authorization': `Bearer ${this.apiKey}`
216
- },
217
- body: JSON.stringify({ sessionId })
218
- });
219
-
220
- if (!response.ok) {
221
- throw new Error(`Failed to send session complete: ${response.statusText}`);
222
- }
223
- }
224
210
 
225
- async sendCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string) {
226
- const maxRetries = 3;
227
- let retryCount = 0;
228
-
229
- while (retryCount < maxRetries) {
230
- try {
231
- const response = await fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
232
- method: 'POST',
233
- headers: {
234
- 'Content-Type': 'application/json',
235
- 'Authorization': `Bearer ${this.apiKey}`
236
- },
237
- body: JSON.stringify({
238
- name: eventName,
239
- properties: eventProperties,
240
- sessionId: sessionId,
241
- timestamp: new Date().toISOString()
242
- })
243
- });
244
-
245
- if (!response.ok) {
246
- throw new Error(`Failed to send custom event: ${response.statusText}`);
247
- }
248
-
249
- return await response.json();
250
- } catch (error) {
251
- retryCount++;
252
- if (retryCount === maxRetries) {
253
- logError('Error sending custom event after max retries:', error);
254
- throw error;
255
- }
256
- // Exponential backoff
257
- await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
258
- }
259
- }
260
- }
261
211
 
262
- async sendCustomEvents(events: any[], sessionId: string) {
263
- const maxRetries = 3;
264
- let retryCount = 0;
265
-
266
- while (retryCount < maxRetries) {
267
- try {
268
- const response = await fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
269
- method: 'POST',
270
- headers: {
271
- 'Content-Type': 'application/json',
272
- 'Authorization': `Bearer ${this.apiKey}`
273
- },
274
- body: JSON.stringify({
275
- events: events.map(event => ({
276
- ...event,
277
- sessionId: sessionId
278
- }))
279
- })
280
- });
281
-
282
- if (!response.ok) {
283
- throw new Error(`Failed to send custom events: ${response.statusText}`);
284
- }
285
-
286
- return await response.json();
287
- } catch (error) {
288
- retryCount++;
289
- if (retryCount === maxRetries) {
290
- logError('Error sending custom events after max retries:', error);
291
- throw error;
292
- }
293
- // Exponential backoff
294
- await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
295
- }
296
- }
297
- }
298
212
 
299
- public sendBeaconEvents(events: any[], sessionId: string, isSessionComplete: boolean = false) {
213
+
214
+ public sendBeaconEvents(events: any[], sessionId: string) {
300
215
  const data = new URLSearchParams()
301
216
  data.append('events', encodeURIComponent(JSON.stringify(events)))
302
217
  data.append('sessionId', encodeURIComponent(sessionId))
303
218
  data.append('timestamp', encodeURIComponent(Date.now().toString()))
304
219
  data.append('apiKey', encodeURIComponent(this.apiKey))
305
- if (isSessionComplete) {
306
- logInfo('Session complete beacon sending');
307
- localStorage.setItem('koalaware_session_complete', Date.now().toString());
308
- data.append('sessionComplete', encodeURIComponent('true'))
309
- }
220
+
310
221
 
311
222
  const success = navigator.sendBeacon(
312
223
  `${this.baseUrl}/api/ingestion/events`,
@@ -316,47 +227,6 @@ export class HumanBehaviorAPI {
316
227
  // KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
317
228
  // KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
318
229
  }
230
+
319
231
 
320
- public sendBeaconSessionComplete(sessionId: string) {
321
- const data = new URLSearchParams()
322
- data.append('sessionId', sessionId)
323
- data.append('apiKey', this.apiKey)
324
- data.append('sessionComplete', 'true')
325
-
326
- const success = navigator.sendBeacon(
327
- `${this.baseUrl}/api/ingestion/sessionComplete`,
328
- data
329
- );
330
-
331
- // KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
332
- // KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
333
- }
334
-
335
- public sendBeaconCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string) {
336
- const data = new URLSearchParams()
337
- data.append('name', encodeURIComponent(eventName))
338
- data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)))
339
- data.append('sessionId', encodeURIComponent(sessionId))
340
- data.append('timestamp', encodeURIComponent(new Date().toISOString()))
341
- data.append('apiKey', encodeURIComponent(this.apiKey))
342
-
343
- return navigator.sendBeacon(
344
- `${this.baseUrl}/api/ingestion/customEvent`,
345
- data
346
- );
347
- }
348
-
349
- public sendBeaconCustomEvents(events: any[], sessionId: string) {
350
- const data = new URLSearchParams()
351
- data.append('events', encodeURIComponent(JSON.stringify(events.map(event => ({
352
- ...event,
353
- sessionId: sessionId
354
- })))))
355
- data.append('apiKey', encodeURIComponent(this.apiKey))
356
-
357
- return navigator.sendBeacon(
358
- `${this.baseUrl}/api/ingestion/customEvent/batch`,
359
- data
360
- );
361
- }
362
232
  }
@@ -24,6 +24,11 @@ interface HumanBehaviorProviderProps {
24
24
  apiKey?: string;
25
25
  client?: HumanBehaviorTracker;
26
26
  children: ReactNode;
27
+ options?: {
28
+ ingestionUrl?: string;
29
+ logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
30
+ redactFields?: string[];
31
+ };
27
32
  }
28
33
 
29
34
  // Default context to prevent unnecessary re-renders
@@ -38,9 +43,9 @@ const defaultContext: HumanBehaviorContextType = {
38
43
  }
39
44
  };
40
45
 
41
- const HumanBehaviorContext = createContext<HumanBehaviorContextType>(defaultContext);
46
+ const HumanBehaviorContext = createContext<HumanBehaviorTracker | null>(null);
42
47
 
43
- export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehaviorProviderProps) => {
48
+ export const HumanBehaviorProvider = ({ apiKey, client, children, options }: HumanBehaviorProviderProps) => {
44
49
  const [humanBehavior, setHumanBehavior] = useState<HumanBehaviorTracker | null>(client || null);
45
50
  const [eventQueue, setEventQueue] = useState<any[]>([]);
46
51
  const [isMounted, setIsMounted] = useState(false);
@@ -132,15 +137,15 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
132
137
  // Memoized context value to prevent unnecessary re-renders
133
138
  const contextValue = useMemo(() => {
134
139
  if (!isMounted) {
135
- return { humanBehavior: null, queueEvent };
140
+ return null;
136
141
  }
137
142
 
138
143
  if (!isInitialized) {
139
- return { humanBehavior: null, queueEvent };
144
+ return null;
140
145
  }
141
146
 
142
- return { humanBehavior, queueEvent };
143
- }, [isMounted, isInitialized, humanBehavior, queueEvent]);
147
+ return humanBehavior;
148
+ }, [isMounted, isInitialized, humanBehavior]);
144
149
 
145
150
  // If not in browser, render children without context
146
151
  if (!(isBrowser())) {
@@ -149,6 +154,7 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
149
154
 
150
155
  return (
151
156
  <HumanBehaviorContext.Provider value={contextValue}>
157
+ <HumanBehaviorPageView />
152
158
  {children}
153
159
  </HumanBehaviorContext.Provider>
154
160
  );
@@ -185,28 +191,55 @@ const createQueuingImplementation = (queueEvent: (event: any) => void): HumanBeh
185
191
  }
186
192
  });
187
193
 
188
- export const useHumanBehavior = (): HumanBehaviorInterface => {
189
- const context = useContext(HumanBehaviorContext);
190
-
191
- // Only throw if the hook is used outside of a provider entirely
192
- if (context === null) {
193
- throw new Error("useHumanBehavior must be used within a HumanBehaviorProvider");
194
+ export const useHumanBehavior = () => {
195
+ const tracker = useContext(HumanBehaviorContext);
196
+ if (!tracker) {
197
+ throw new Error('useHumanBehavior must be used within a HumanBehaviorProvider');
194
198
  }
199
+ return tracker;
200
+ };
195
201
 
196
- // If we're in the server-side, return a no-op implementation
197
- if (typeof window === 'undefined') {
198
- logWarn('HumanBehavior is being used before being initialized');
199
- return serverSideImplementation;
200
- }
201
-
202
- // If we have an initialized tracker, return it as the interface
203
- if (context.humanBehavior) {
204
- return context.humanBehavior;
205
- }
202
+ // Automatic page tracking component (similar to PostHog's PostHogPageView)
203
+ function HumanBehaviorPageView() {
204
+ const tracker = useContext(HumanBehaviorContext);
206
205
 
207
- // If we reach here, we're in the initialization period where:
208
- // - context.humanBehavior is null
209
- // - context.queueEvent is available
210
- // - we need to queue all operations until initialization completes
211
- return useMemo(() => createQueuingImplementation(context.queueEvent), [context.queueEvent]);
212
- };
206
+ useEffect(() => {
207
+ if (tracker && typeof window !== 'undefined') {
208
+ // Track initial page load
209
+ tracker.trackPageView();
210
+
211
+ // Listen for route changes (for SPAs)
212
+ const handleRouteChange = () => {
213
+ tracker.trackPageView();
214
+ };
215
+
216
+ // Listen for popstate (back/forward navigation)
217
+ window.addEventListener('popstate', handleRouteChange);
218
+
219
+ // Listen for pushstate/replacestate (programmatic navigation)
220
+ const originalPushState = history.pushState;
221
+ const originalReplaceState = history.replaceState;
222
+
223
+ history.pushState = function(...args) {
224
+ originalPushState.apply(this, args);
225
+ handleRouteChange();
226
+ };
227
+
228
+ history.replaceState = function(...args) {
229
+ originalReplaceState.apply(this, args);
230
+ handleRouteChange();
231
+ };
232
+
233
+ return () => {
234
+ window.removeEventListener('popstate', handleRouteChange);
235
+ history.pushState = originalPushState;
236
+ history.replaceState = originalReplaceState;
237
+ };
238
+ }
239
+ }, [tracker]);
240
+
241
+ return null;
242
+ }
243
+
244
+ // Export the tracker class for direct use
245
+ export { HumanBehaviorTracker };