humanbehavior-js 0.4.15 → 0.4.17

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.
Files changed (89) hide show
  1. package/dist/cjs/wizard/index.cjs +6 -8
  2. package/dist/cjs/wizard/index.cjs.map +1 -1
  3. package/dist/cli/ai-auto-install.js +6 -8
  4. package/dist/cli/ai-auto-install.js.map +1 -1
  5. package/dist/esm/wizard/index.js +6 -8
  6. package/dist/esm/wizard/index.js.map +1 -1
  7. package/package/WIZARD_USAGE_GUIDE.md +381 -0
  8. package/package/canvas-recording-demo.html +143 -0
  9. package/package/clean-console-demo.html +39 -0
  10. package/package/dist/cjs/angular/index.cjs +14354 -0
  11. package/package/dist/cjs/angular/index.cjs.map +1 -0
  12. package/package/dist/cjs/index.cjs +14323 -0
  13. package/package/dist/cjs/index.cjs.map +1 -0
  14. package/package/dist/cjs/install-wizard.cjs +1530 -0
  15. package/package/dist/cjs/install-wizard.cjs.map +1 -0
  16. package/package/dist/cjs/react/index.cjs +14478 -0
  17. package/package/dist/cjs/react/index.cjs.map +1 -0
  18. package/package/dist/cjs/remix/index.cjs +14452 -0
  19. package/package/dist/cjs/remix/index.cjs.map +1 -0
  20. package/package/dist/cjs/svelte/index.cjs +14308 -0
  21. package/package/dist/cjs/svelte/index.cjs.map +1 -0
  22. package/package/dist/cjs/vue/index.cjs +14317 -0
  23. package/package/dist/cjs/vue/index.cjs.map +1 -0
  24. package/package/dist/cjs/wizard/index.cjs +3446 -0
  25. package/package/dist/cjs/wizard/index.cjs.map +1 -0
  26. package/package/dist/cli/ai-auto-install.cjs +57161 -0
  27. package/package/dist/cli/ai-auto-install.cjs.map +1 -0
  28. package/package/dist/cli/ai-auto-install.js +1969 -0
  29. package/package/dist/cli/ai-auto-install.js.map +1 -0
  30. package/package/dist/cli/auto-install.cjs +56352 -0
  31. package/package/dist/cli/auto-install.cjs.map +1 -0
  32. package/package/dist/cli/auto-install.js +1957 -0
  33. package/package/dist/cli/auto-install.js.map +1 -0
  34. package/package/dist/esm/angular/index.js +14350 -0
  35. package/package/dist/esm/angular/index.js.map +1 -0
  36. package/package/dist/esm/index.js +14309 -0
  37. package/package/dist/esm/index.js.map +1 -0
  38. package/package/dist/esm/install-wizard.js +1507 -0
  39. package/package/dist/esm/install-wizard.js.map +1 -0
  40. package/package/dist/esm/react/index.js +14472 -0
  41. package/package/dist/esm/react/index.js.map +1 -0
  42. package/package/dist/esm/remix/index.js +14448 -0
  43. package/package/dist/esm/remix/index.js.map +1 -0
  44. package/package/dist/esm/svelte/index.js +14306 -0
  45. package/package/dist/esm/svelte/index.js.map +1 -0
  46. package/package/dist/esm/vue/index.js +14315 -0
  47. package/package/dist/esm/vue/index.js.map +1 -0
  48. package/package/dist/esm/wizard/index.js +3415 -0
  49. package/package/dist/esm/wizard/index.js.map +1 -0
  50. package/package/dist/index.min.js +2 -0
  51. package/package/dist/index.min.js.map +1 -0
  52. package/package/dist/types/angular/index.d.ts +267 -0
  53. package/package/dist/types/index.d.ts +373 -0
  54. package/package/dist/types/install-wizard.d.ts +156 -0
  55. package/package/dist/types/react/index.d.ts +255 -0
  56. package/package/dist/types/remix/index.d.ts +246 -0
  57. package/package/dist/types/svelte/index.d.ts +232 -0
  58. package/package/dist/types/vue/index.d.ts +15 -0
  59. package/package/dist/types/wizard/index.d.ts +523 -0
  60. package/package/package.json +105 -0
  61. package/package/readme.md +281 -0
  62. package/package/rollup.config.js +422 -0
  63. package/package/simple-demo.html +26 -0
  64. package/package/simple-spa.html +838 -0
  65. package/package/src/angular/index.ts +79 -0
  66. package/package/src/api.ts +376 -0
  67. package/package/src/index.ts +28 -0
  68. package/package/src/react/AutoInstallWizard.tsx +557 -0
  69. package/package/src/react/browser.ts +8 -0
  70. package/package/src/react/index.tsx +308 -0
  71. package/package/src/redact.ts +521 -0
  72. package/package/src/remix/index.ts +16 -0
  73. package/package/src/svelte/index.ts +14 -0
  74. package/package/src/tracker.ts +1319 -0
  75. package/package/src/types/clack.d.ts +31 -0
  76. package/package/src/utils/logger.ts +144 -0
  77. package/package/src/vue/index.ts +29 -0
  78. package/package/src/wizard/README.md +114 -0
  79. package/package/src/wizard/ai/ai-install-wizard.ts +897 -0
  80. package/package/src/wizard/ai/manual-framework-wizard.ts +238 -0
  81. package/package/src/wizard/cli/ai-auto-install.ts +243 -0
  82. package/package/src/wizard/cli/auto-install.ts +224 -0
  83. package/package/src/wizard/core/install-wizard.ts +1744 -0
  84. package/package/src/wizard/index.ts +23 -0
  85. package/package/src/wizard/services/centralized-ai-service.ts +668 -0
  86. package/package/src/wizard/services/remote-ai-service.ts +240 -0
  87. package/package/tsconfig.json +24 -0
  88. package/package.json +1 -1
  89. package/src/wizard/cli/ai-auto-install.ts +4 -6
@@ -0,0 +1,308 @@
1
+ import React, { useEffect, useState, createContext, useContext, ReactNode, useCallback, useMemo, useRef } from "react";
2
+ import { HumanBehaviorTracker, logError, logWarn, logDebug } from "./browser";
3
+
4
+ // Check if we're in a browser environment
5
+ const isBrowser = () => typeof window !== 'undefined';
6
+
7
+ // Define the public interface that components will interact with
8
+ interface HumanBehaviorInterface {
9
+ addEvent: (event: any) => void;
10
+ identifyUser: ({ userProperties }: { userProperties: Record<string, any> }) => Promise<string>;
11
+ start: () => void;
12
+ stop: () => void;
13
+ logout: () => void;
14
+ viewLogs: () => void;
15
+ }
16
+
17
+ interface HumanBehaviorContextType {
18
+ humanBehavior: HumanBehaviorTracker | null;
19
+ queueEvent: (event: any) => void;
20
+ }
21
+
22
+ interface HumanBehaviorProviderProps {
23
+ // Either provide an apiKey to create a new client, or provide an existing client
24
+ apiKey?: string;
25
+ client?: HumanBehaviorTracker;
26
+ children: ReactNode;
27
+ options?: {
28
+ ingestionUrl?: string;
29
+ logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
30
+ redactFields?: string[];
31
+ suppressConsoleErrors?: boolean;
32
+ recordCanvas?: boolean; // Enable canvas recording with PostHog-style protection
33
+ };
34
+ }
35
+
36
+ // Default context to prevent unnecessary re-renders
37
+ const defaultContext: HumanBehaviorContextType = {
38
+ humanBehavior: null,
39
+ queueEvent: (event: any) => {
40
+ // In server-side, just no-op
41
+ if (typeof window === 'undefined') {
42
+ return;
43
+ }
44
+ logWarn('HumanBehavior not initialized yet, event queued:', event);
45
+ }
46
+ };
47
+
48
+ const HumanBehaviorContext = createContext<HumanBehaviorTracker | null>(null);
49
+
50
+ export const HumanBehaviorProvider = ({ apiKey, client, children, options }: HumanBehaviorProviderProps) => {
51
+ const [humanBehavior, setHumanBehavior] = useState<HumanBehaviorTracker | null>(client || null);
52
+ const [eventQueue, setEventQueue] = useState<any[]>([]);
53
+ const [isMounted, setIsMounted] = useState(false);
54
+ const [isInitialized, setIsInitialized] = useState(false);
55
+
56
+ // Use refs to avoid dependency issues in useEffect
57
+ const apiKeyRef = useRef(apiKey);
58
+ const clientRef = useRef(client);
59
+ const eventQueueRef = useRef(eventQueue);
60
+
61
+ // Update refs when props change
62
+ useEffect(() => {
63
+ apiKeyRef.current = apiKey;
64
+ clientRef.current = client;
65
+ }, [apiKey, client]);
66
+
67
+ // Update eventQueue ref when queue changes
68
+ useEffect(() => {
69
+ eventQueueRef.current = eventQueue;
70
+ }, [eventQueue]);
71
+
72
+ // Memoized queueEvent function to prevent unnecessary re-renders
73
+ const queueEvent = useCallback((event: any) => {
74
+ setEventQueue(prev => [...prev, event]);
75
+ }, []);
76
+
77
+ // Handle mounting state
78
+ useEffect(() => {
79
+ setIsMounted(true);
80
+ }, []);
81
+
82
+ useEffect(() => {
83
+ // Only run in browser
84
+ if (!(isBrowser())) {
85
+ return;
86
+ }
87
+
88
+ // Skip if not mounted yet (handles Next.js hydration)
89
+ if (!isMounted) {
90
+ return;
91
+ }
92
+
93
+ // If client is provided, use that
94
+ if (clientRef.current) {
95
+ setHumanBehavior(clientRef.current);
96
+ setIsInitialized(true);
97
+ return;
98
+ }
99
+
100
+ // If no client is provided, apiKey is required
101
+ if (!apiKeyRef.current || apiKeyRef.current.trim() === '') {
102
+ logError("An apiKey is required when no client is provided");
103
+ return;
104
+ }
105
+
106
+ if (humanBehavior !== null) {
107
+ return;
108
+ }
109
+
110
+ // Create new tracker instance with the validated apiKey and options
111
+ const tracker = HumanBehaviorTracker.init(
112
+ apiKeyRef.current.trim(),
113
+ {
114
+ ingestionUrl: options?.ingestionUrl,
115
+ logLevel: options?.logLevel,
116
+ redactFields: options?.redactFields,
117
+ suppressConsoleErrors: options?.suppressConsoleErrors,
118
+ recordCanvas: options?.recordCanvas, // Pass canvas recording option
119
+ }
120
+ );
121
+
122
+ setHumanBehavior(tracker);
123
+
124
+ // Wait for initialization to complete
125
+ tracker.initializationPromise?.then(async () => {
126
+ await tracker.start();
127
+ setIsInitialized(true);
128
+
129
+ // Process any queued events
130
+ const currentQueue = eventQueueRef.current;
131
+ if (currentQueue.length > 0) {
132
+ for (const event of currentQueue) {
133
+ if (event.type === 'identify') {
134
+ logDebug('Processing queued identify event', event);
135
+ try {
136
+ await tracker.identifyUser({ userProperties: event.userProperties });
137
+ } catch (error) {
138
+ logError('Failed to process queued user info:', error);
139
+ }
140
+ } else {
141
+ tracker.addEvent(event);
142
+ }
143
+ }
144
+ setEventQueue([]); // Clear the queue
145
+ }
146
+ }).catch(error => {
147
+ logError('Failed to initialize HumanBehaviorTracker:', error);
148
+ });
149
+ }, [isMounted, humanBehavior]); // Removed apiKey, client, eventQueue from dependencies
150
+
151
+ // Memoized context value to prevent unnecessary re-renders
152
+ const contextValue = useMemo(() => {
153
+ if (!isMounted) {
154
+ return null;
155
+ }
156
+
157
+ if (!isInitialized) {
158
+ return null;
159
+ }
160
+
161
+ return humanBehavior;
162
+ }, [isMounted, isInitialized, humanBehavior]);
163
+
164
+ // If not in browser, render children without context
165
+ if (!(isBrowser())) {
166
+ return <>{children}</>;
167
+ }
168
+
169
+ return (
170
+ <HumanBehaviorContext.Provider value={contextValue}>
171
+ <HumanBehaviorPageView />
172
+ {children}
173
+ </HumanBehaviorContext.Provider>
174
+ );
175
+ };
176
+
177
+ // Default implementation for when tracker is not available
178
+ const defaultImplementation: HumanBehaviorInterface = {
179
+ addEvent: () => {},
180
+ identifyUser: async ({ userProperties }: { userProperties: Record<string, any> }) => {
181
+ return '';
182
+ },
183
+ start: () => {},
184
+ stop: () => {},
185
+ logout: () => {},
186
+ viewLogs: () => {},
187
+ };
188
+
189
+ // Memoized queuing implementation for initialization period
190
+ const createQueuingImplementation = (queueEvent: (event: any) => void): HumanBehaviorInterface => ({
191
+ addEvent: (event: any) => {
192
+ queueEvent(event);
193
+ },
194
+ identifyUser: async ({ userProperties }: { userProperties: Record<string, any> }) => {
195
+ queueEvent({
196
+ type: 'identify',
197
+ userProperties,
198
+ });
199
+ return ''; // Return empty string to match interface
200
+ },
201
+ start: () => {
202
+ // Start will be called automatically when initialized
203
+ },
204
+ stop: () => {
205
+ // Stop is a no-op when not initialized
206
+ },
207
+ logout: () => {
208
+ // Logout is a no-op when not initialized
209
+ },
210
+ viewLogs: () => {
211
+ logWarn('Logs are not available until HumanBehaviorTracker is initialized');
212
+ }
213
+ });
214
+
215
+ export const useHumanBehavior = () => {
216
+ const tracker = useContext(HumanBehaviorContext);
217
+ if (!tracker) {
218
+ throw new Error('useHumanBehavior must be used within a HumanBehaviorProvider');
219
+ }
220
+ return tracker;
221
+ };
222
+
223
+ // Custom hook for managing redaction fields dynamically
224
+ export const useRedaction = () => {
225
+ const tracker = useHumanBehavior();
226
+
227
+ const setRedactedFields = useCallback((fields: string[]) => {
228
+ tracker.setRedactedFields(fields);
229
+ }, [tracker]);
230
+
231
+ const isRedactionActive = useCallback(() => {
232
+ return tracker.isRedactionActive();
233
+ }, [tracker]);
234
+
235
+ const getRedactedFields = useCallback(() => {
236
+ return tracker.getRedactedFields();
237
+ }, [tracker]);
238
+
239
+ return {
240
+ setRedactedFields,
241
+ isRedactionActive,
242
+ getRedactedFields
243
+ };
244
+ };
245
+
246
+ // Custom hook for managing user info
247
+ export const useUserTracking = () => {
248
+ const tracker = useHumanBehavior();
249
+
250
+ const identifyUser = useCallback(async ({ userProperties }: { userProperties: Record<string, any> }) => {
251
+ try {
252
+ await tracker.identifyUser({ userProperties });
253
+ return { success: true };
254
+ } catch (error) {
255
+ logError('Failed to identify user:', error);
256
+ return { success: false, error };
257
+ }
258
+ }, [tracker]);
259
+
260
+ return {
261
+ identifyUser
262
+ };
263
+ };
264
+
265
+ // Automatic page tracking component (similar to PostHog's PostHogPageView)
266
+ function HumanBehaviorPageView() {
267
+ const tracker = useContext(HumanBehaviorContext);
268
+
269
+ useEffect(() => {
270
+ if (tracker && typeof window !== 'undefined') {
271
+ // Track initial page load
272
+ tracker.trackPageView();
273
+
274
+ // Listen for route changes (for SPAs)
275
+ const handleRouteChange = () => {
276
+ tracker.trackPageView();
277
+ };
278
+
279
+ // Listen for popstate (back/forward navigation)
280
+ window.addEventListener('popstate', handleRouteChange);
281
+
282
+ // Listen for pushstate/replacestate (programmatic navigation)
283
+ const originalPushState = history.pushState;
284
+ const originalReplaceState = history.replaceState;
285
+
286
+ history.pushState = function(...args) {
287
+ originalPushState.apply(this, args);
288
+ handleRouteChange();
289
+ };
290
+
291
+ history.replaceState = function(...args) {
292
+ originalReplaceState.apply(this, args);
293
+ handleRouteChange();
294
+ };
295
+
296
+ return () => {
297
+ window.removeEventListener('popstate', handleRouteChange);
298
+ history.pushState = originalPushState;
299
+ history.replaceState = originalReplaceState;
300
+ };
301
+ }
302
+ }, [tracker]);
303
+
304
+ return null;
305
+ }
306
+
307
+ // Export the tracker class for direct use
308
+ export { HumanBehaviorTracker };