humanbehavior-js 0.4.28 → 0.5.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.
Files changed (140) hide show
  1. package/README.md +151 -0
  2. package/package.json +114 -79
  3. package/packages/angular/dist/index.d.ts +46 -0
  4. package/packages/angular/dist/index.d.ts.map +1 -0
  5. package/packages/angular/dist/index.js +2 -0
  6. package/packages/angular/dist/index.js.map +1 -0
  7. package/packages/angular/dist/index.mjs +2 -0
  8. package/packages/angular/dist/index.mjs.map +1 -0
  9. package/packages/browser/dist/index.d.ts +5 -0
  10. package/packages/browser/dist/index.d.ts.map +1 -0
  11. package/packages/browser/dist/index.iife.js +12095 -0
  12. package/packages/browser/dist/index.iife.js.map +1 -0
  13. package/packages/browser/dist/index.js +2 -0
  14. package/packages/browser/dist/index.js.map +1 -0
  15. package/packages/browser/dist/index.min.js +2 -0
  16. package/packages/browser/dist/index.min.js.map +1 -0
  17. package/packages/browser/dist/index.mjs +2 -0
  18. package/packages/browser/dist/index.mjs.map +1 -0
  19. package/packages/react/dist/browser.d.ts +2 -0
  20. package/packages/react/dist/browser.d.ts.map +1 -0
  21. package/packages/react/dist/index.d.ts +48 -0
  22. package/packages/react/dist/index.d.ts.map +1 -0
  23. package/packages/react/dist/index.js +2 -0
  24. package/packages/react/dist/index.js.map +1 -0
  25. package/packages/react/dist/index.mjs +2 -0
  26. package/packages/react/dist/index.mjs.map +1 -0
  27. package/packages/remix/dist/index.d.ts +8 -0
  28. package/packages/remix/dist/index.d.ts.map +1 -0
  29. package/packages/remix/dist/index.js +2 -0
  30. package/packages/remix/dist/index.js.map +1 -0
  31. package/packages/remix/dist/index.mjs +2 -0
  32. package/packages/remix/dist/index.mjs.map +1 -0
  33. package/packages/svelte/dist/index.d.ts +11 -0
  34. package/packages/svelte/dist/index.d.ts.map +1 -0
  35. package/packages/svelte/dist/index.js +2 -0
  36. package/packages/svelte/dist/index.js.map +1 -0
  37. package/packages/svelte/dist/index.mjs +2 -0
  38. package/packages/svelte/dist/index.mjs.map +1 -0
  39. package/{dist/types/vue → packages/vue/dist}/index.d.ts +4 -5
  40. package/packages/vue/dist/index.d.ts.map +1 -0
  41. package/packages/vue/dist/index.js +2 -0
  42. package/packages/vue/dist/index.js.map +1 -0
  43. package/packages/vue/dist/index.mjs +2 -0
  44. package/packages/vue/dist/index.mjs.map +1 -0
  45. package/packages/wizard/dist/ai/ai-install-wizard.d.ts +145 -0
  46. package/packages/wizard/dist/ai/ai-install-wizard.d.ts.map +1 -0
  47. package/packages/wizard/dist/ai/manual-framework-wizard.d.ts +52 -0
  48. package/packages/wizard/dist/ai/manual-framework-wizard.d.ts.map +1 -0
  49. package/packages/wizard/dist/cli/ai-auto-install.d.ts +27 -0
  50. package/packages/wizard/dist/cli/ai-auto-install.d.ts.map +1 -0
  51. package/{dist → packages/wizard/dist}/cli/ai-auto-install.js +821 -905
  52. package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -0
  53. package/packages/wizard/dist/cli/auto-install.d.ts +26 -0
  54. package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -0
  55. package/{dist → packages/wizard/dist}/cli/auto-install.js +821 -905
  56. package/packages/wizard/dist/cli/auto-install.js.map +1 -0
  57. package/{dist/types → packages/wizard/dist/core}/install-wizard.d.ts +6 -8
  58. package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -0
  59. package/packages/wizard/dist/index.d.ts +18 -0
  60. package/packages/wizard/dist/index.d.ts.map +1 -0
  61. package/packages/wizard/dist/index.js +2 -0
  62. package/packages/wizard/dist/index.js.map +1 -0
  63. package/packages/wizard/dist/index.mjs +2 -0
  64. package/packages/wizard/dist/index.mjs.map +1 -0
  65. package/packages/wizard/dist/services/centralized-ai-service.d.ts +159 -0
  66. package/packages/wizard/dist/services/centralized-ai-service.d.ts.map +1 -0
  67. package/packages/wizard/dist/services/remote-ai-service.d.ts +58 -0
  68. package/packages/wizard/dist/services/remote-ai-service.d.ts.map +1 -0
  69. package/WIZARD_USAGE_GUIDE.md +0 -381
  70. package/dist/cjs/angular/index.cjs +0 -14979
  71. package/dist/cjs/angular/index.cjs.map +0 -1
  72. package/dist/cjs/index.cjs +0 -14964
  73. package/dist/cjs/index.cjs.map +0 -1
  74. package/dist/cjs/install-wizard.cjs +0 -1576
  75. package/dist/cjs/install-wizard.cjs.map +0 -1
  76. package/dist/cjs/react/index.cjs +0 -15103
  77. package/dist/cjs/react/index.cjs.map +0 -1
  78. package/dist/cjs/remix/index.cjs +0 -15077
  79. package/dist/cjs/remix/index.cjs.map +0 -1
  80. package/dist/cjs/svelte/index.cjs +0 -14933
  81. package/dist/cjs/svelte/index.cjs.map +0 -1
  82. package/dist/cjs/vue/index.cjs +0 -14942
  83. package/dist/cjs/vue/index.cjs.map +0 -1
  84. package/dist/cjs/wizard/index.cjs +0 -3490
  85. package/dist/cjs/wizard/index.cjs.map +0 -1
  86. package/dist/cli/ai-auto-install.js.map +0 -1
  87. package/dist/cli/auto-install.js.map +0 -1
  88. package/dist/esm/angular/index.js +0 -14975
  89. package/dist/esm/angular/index.js.map +0 -1
  90. package/dist/esm/index.js +0 -14941
  91. package/dist/esm/index.js.map +0 -1
  92. package/dist/esm/install-wizard.js +0 -1553
  93. package/dist/esm/install-wizard.js.map +0 -1
  94. package/dist/esm/react/index.js +0 -15097
  95. package/dist/esm/react/index.js.map +0 -1
  96. package/dist/esm/remix/index.js +0 -15073
  97. package/dist/esm/remix/index.js.map +0 -1
  98. package/dist/esm/svelte/index.js +0 -14931
  99. package/dist/esm/svelte/index.js.map +0 -1
  100. package/dist/esm/vue/index.js +0 -14940
  101. package/dist/esm/vue/index.js.map +0 -1
  102. package/dist/esm/wizard/index.js +0 -3459
  103. package/dist/esm/wizard/index.js.map +0 -1
  104. package/dist/index.min.js +0 -2
  105. package/dist/index.min.js.map +0 -1
  106. package/dist/types/angular/index.d.ts +0 -357
  107. package/dist/types/index.d.ts +0 -644
  108. package/dist/types/react/index.d.ts +0 -345
  109. package/dist/types/remix/index.d.ts +0 -336
  110. package/dist/types/svelte/index.d.ts +0 -322
  111. package/dist/types/wizard/index.d.ts +0 -523
  112. package/readme.md +0 -335
  113. package/rollup.config.js +0 -422
  114. package/simple-spa.html +0 -1000
  115. package/src/angular/index.ts +0 -79
  116. package/src/api.ts +0 -416
  117. package/src/index.ts +0 -35
  118. package/src/react/AutoInstallWizard.tsx +0 -557
  119. package/src/react/browser.ts +0 -8
  120. package/src/react/index.tsx +0 -308
  121. package/src/redact.ts +0 -327
  122. package/src/remix/index.ts +0 -16
  123. package/src/svelte/index.ts +0 -14
  124. package/src/tracker.ts +0 -1587
  125. package/src/types/clack.d.ts +0 -31
  126. package/src/utils/ip-detector.ts +0 -158
  127. package/src/utils/logger.ts +0 -144
  128. package/src/utils/property-detector.ts +0 -345
  129. package/src/utils/property-manager.ts +0 -274
  130. package/src/vue/index.ts +0 -29
  131. package/src/wizard/README.md +0 -114
  132. package/src/wizard/ai/ai-install-wizard.ts +0 -897
  133. package/src/wizard/ai/manual-framework-wizard.ts +0 -238
  134. package/src/wizard/cli/ai-auto-install.ts +0 -241
  135. package/src/wizard/cli/auto-install.ts +0 -224
  136. package/src/wizard/core/install-wizard.ts +0 -1794
  137. package/src/wizard/index.ts +0 -23
  138. package/src/wizard/services/centralized-ai-service.ts +0 -668
  139. package/src/wizard/services/remote-ai-service.ts +0 -240
  140. package/tsconfig.json +0 -24
@@ -1,308 +0,0 @@
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 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 unredaction fields dynamically
224
- export const useRedaction = () => {
225
- const tracker = useHumanBehavior();
226
-
227
- const setUnredactedFields = useCallback((fields: string[]) => {
228
- tracker.setUnredactedFields(fields);
229
- }, [tracker]);
230
-
231
- const hasUnredactedFields = useCallback(() => {
232
- return tracker.hasUnredactedFields();
233
- }, [tracker]);
234
-
235
- const getUnredactedFields = useCallback(() => {
236
- return tracker.getUnredactedFields();
237
- }, [tracker]);
238
-
239
- return {
240
- setUnredactedFields,
241
- hasUnredactedFields,
242
- getUnredactedFields
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
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 };
package/src/redact.ts DELETED
@@ -1,327 +0,0 @@
1
- // Simplified redaction functionality for HumanBehavior SDK
2
- // Since rrweb auto-redacts all input fields by default, this module only handles
3
- // selectively unredacting specific fields (except passwords which remain protected)
4
-
5
- import { logDebug, logWarn } from './utils/logger';
6
-
7
- // Check if we're in a browser environment
8
- const isBrowser = typeof window !== 'undefined';
9
-
10
- export interface RedactionOptions {
11
- redactedText?: string;
12
- excludeSelectors?: string[];
13
- userFields?: string[]; // Fields that the user wants to unredact (legacy)
14
- redactionStrategy?: {
15
- mode: 'privacy-first' | 'visibility-first';
16
- unredactFields?: string[]; // Fields to make visible (when mode: 'privacy-first')
17
- redactFields?: string[]; // Fields to hide (when mode: 'visibility-first')
18
- };
19
- legacyRedactFields?: string[]; // For backward compatibility
20
- }
21
-
22
- export class RedactionManager {
23
- private redactedText: string = '[REDACTED]';
24
- private unredactedFields: Set<string> = new Set(); // Fields that user wants to unredact
25
- private redactedFields: Set<string> = new Set(); // Fields that user wants to redact
26
- private redactionMode: 'privacy-first' | 'visibility-first' = 'privacy-first';
27
- private excludeSelectors: string[] = [
28
- '[data-no-redact="true"]',
29
- '.human-behavior-no-redact'
30
- ];
31
-
32
- constructor(options?: RedactionOptions) {
33
- if (options?.redactedText) {
34
- this.redactedText = options.redactedText;
35
- }
36
- if (options?.excludeSelectors) {
37
- this.excludeSelectors = [...this.excludeSelectors, ...options.excludeSelectors];
38
- }
39
-
40
- // Handle new redaction strategy
41
- if (options?.redactionStrategy) {
42
- this.redactionMode = options.redactionStrategy.mode;
43
-
44
- if (this.redactionMode === 'privacy-first') {
45
- // Privacy-first: everything redacted by default, unredact specific fields
46
- if (options.redactionStrategy.unredactFields) {
47
- this.setFieldsToUnredact(options.redactionStrategy.unredactFields);
48
- }
49
- } else {
50
- // Visibility-first: everything visible by default, redact specific fields
51
- if (options.redactionStrategy.redactFields) {
52
- this.setFieldsToRedact(options.redactionStrategy.redactFields);
53
- }
54
- }
55
- }
56
-
57
- // Handle legacy redactFields (backward compatibility)
58
- if (options?.legacyRedactFields) {
59
- this.setFieldsToUnredact(options.legacyRedactFields);
60
- }
61
-
62
- // Handle legacy userFields
63
- if (options?.userFields) {
64
- this.setFieldsToUnredact(options.userFields);
65
- }
66
- }
67
-
68
- /**
69
- * Set specific fields to be redacted (for visibility-first mode)
70
- * @param fields Array of CSS selectors for fields to redact
71
- */
72
- public setFieldsToRedact(fields: string[]): void {
73
- this.redactedFields.clear();
74
-
75
- // Always include password fields in redacted list
76
- const passwordFields = [
77
- 'input[type="password"]',
78
- 'input[type="password" i]',
79
- '[type="password"]',
80
- '[type="password" i]'
81
- ];
82
-
83
- // Add password fields and user-specified fields
84
- [...passwordFields, ...fields].forEach(field => {
85
- this.redactedFields.add(field);
86
- });
87
-
88
- if (this.redactedFields.size > 0) {
89
- logDebug(`Redaction: Active for ${this.redactedFields.size} field(s):`, Array.from(this.redactedFields));
90
- } else {
91
- logDebug('Redaction: No fields to redact');
92
- }
93
-
94
- this.applyRedactionClasses();
95
- }
96
-
97
- /**
98
- * Set specific fields to be unredacted (everything else stays redacted by rrweb)
99
- * @param fields Array of CSS selectors for fields to unredact
100
- */
101
- public setFieldsToUnredact(fields: string[]): void {
102
- this.unredactedFields.clear();
103
-
104
- // Filter out password fields (they cannot be unredacted)
105
- const validFields = fields.filter(field => {
106
- const isPasswordField = this.isPasswordSelector(field);
107
- if (isPasswordField) {
108
- logWarn(`Cannot unredact password field: ${field} - Password fields are always protected`);
109
- return false;
110
- }
111
- return true;
112
- });
113
-
114
- validFields.forEach(field => this.unredactedFields.add(field));
115
-
116
- if (validFields.length > 0) {
117
- logDebug(`Unredaction: Active for ${validFields.length} field(s):`, validFields);
118
- } else {
119
- logDebug('Unredaction: No valid fields to unredact');
120
- }
121
-
122
- this.applyUnredactionClasses();
123
- }
124
-
125
- /**
126
- * Remove specific fields from unredaction (they become redacted again)
127
- * @param fields Array of CSS selectors for fields to redact
128
- */
129
- public redactFields(fields: string[]): void {
130
- fields.forEach(field => {
131
- this.unredactedFields.delete(field);
132
- });
133
-
134
- if (this.unredactedFields.size > 0) {
135
- logDebug(`Unredaction: Removed ${fields.length} field(s), ${this.unredactedFields.size} remaining:`, Array.from(this.unredactedFields));
136
- } else {
137
- logDebug('Unredaction: All fields redacted');
138
- }
139
-
140
- this.applyUnredactionClasses();
141
- }
142
-
143
- /**
144
- * Clear all unredacted fields (everything becomes redacted again)
145
- */
146
- public clearUnredactedFields(): void {
147
- this.unredactedFields.clear();
148
- logDebug('Unredaction: All fields cleared, everything redacted');
149
-
150
- this.removeUnredactionClasses();
151
- }
152
-
153
- /**
154
- * Check if any fields are currently unredacted
155
- */
156
- public hasUnredactedFields(): boolean {
157
- return this.unredactedFields.size > 0;
158
- }
159
-
160
- /**
161
- * Get the current redaction mode
162
- */
163
- public getRedactionMode(): 'privacy-first' | 'visibility-first' {
164
- return this.redactionMode;
165
- }
166
-
167
- /**
168
- * Get the currently unredacted fields
169
- */
170
- public getUnredactedFields(): string[] {
171
- return Array.from(this.unredactedFields);
172
- }
173
-
174
- /**
175
- * Get CSS selectors for rrweb masking configuration
176
- * Returns null if no fields are unredacted (everything stays redacted)
177
- */
178
- public getMaskTextSelector(): string | null {
179
- if (this.redactionMode === 'privacy-first') {
180
- // Privacy-first: mask everything except unredacted fields
181
- if (this.unredactedFields.size === 0) {
182
- return null; // Everything stays redacted
183
- }
184
- return Array.from(this.unredactedFields).join(',');
185
- } else {
186
- // Visibility-first: mask only redacted fields
187
- if (this.redactedFields.size === 0) {
188
- return null; // Nothing to redact
189
- }
190
- return Array.from(this.redactedFields).join(',');
191
- }
192
- }
193
-
194
- /**
195
- * Apply redaction classes to DOM elements (for visibility-first mode)
196
- * Adds 'rr-mask' class to elements that should be redacted
197
- */
198
- public applyRedactionClasses(): void {
199
- if (this.redactedFields.size === 0) {
200
- return;
201
- }
202
-
203
- // Add 'rr-mask' class to redacted elements
204
- this.redactedFields.forEach(selector => {
205
- try {
206
- const elements = document.querySelectorAll(selector);
207
- elements.forEach(element => {
208
- element.classList.add('rr-mask');
209
- });
210
- logDebug(`Added rr-mask class to ${elements.length} element(s) for selector: ${selector}`);
211
- } catch (e) {
212
- logWarn(`Invalid selector: ${selector}`);
213
- }
214
- });
215
- }
216
-
217
- /**
218
- * Apply unredaction classes to DOM elements
219
- * Removes 'rr-mask' class from elements that should be unredacted
220
- */
221
- public applyUnredactionClasses(): void {
222
- if (this.unredactedFields.size === 0) {
223
- return;
224
- }
225
-
226
- // Remove 'rr-mask' class from unredacted elements
227
- this.unredactedFields.forEach(selector => {
228
- try {
229
- const elements = document.querySelectorAll(selector);
230
- elements.forEach(element => {
231
- element.classList.remove('rr-mask');
232
- });
233
- logDebug(`Removed rr-mask class from ${elements.length} element(s) for selector: ${selector}`);
234
- } catch (e) {
235
- logWarn(`Invalid selector: ${selector}`);
236
- }
237
- });
238
- }
239
-
240
- /**
241
- * Remove all unredaction classes from DOM elements
242
- */
243
- public removeUnredactionClasses(): void {
244
- // Note: This doesn't add 'rr-mask' classes back - rrweb handles that automatically
245
- logDebug('Unredaction classes removed');
246
- }
247
-
248
- /**
249
- * Check if a selector represents a password field
250
- */
251
- private isPasswordSelector(selector: string): boolean {
252
- const passwordPatterns = [
253
- 'input[type="password"]',
254
- 'input[type="password" i]',
255
- '[type="password"]',
256
- '[type="password" i]'
257
- ];
258
-
259
- return passwordPatterns.some(pattern =>
260
- selector.toLowerCase().includes(pattern.toLowerCase().replace(/[\[\]]/g, ''))
261
- );
262
- }
263
-
264
- /**
265
- * Get the original value of an element (for debugging)
266
- */
267
- public getOriginalValue(element: HTMLElement): string | undefined {
268
- if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
269
- return element.value;
270
- }
271
- return undefined;
272
- }
273
-
274
- /**
275
- * Check if an element is currently unredacted
276
- */
277
- public isElementUnredacted(element: HTMLElement): boolean {
278
- return this.shouldUnredactElement(element);
279
- }
280
-
281
- /**
282
- * Check if an element should be unredacted
283
- */
284
- public shouldUnredactElement(element: HTMLElement): boolean {
285
- if (this.redactionMode === 'privacy-first') {
286
- // Privacy-first: check if element is in unredacted fields
287
- if (this.unredactedFields.size === 0) {
288
- return false; // Nothing unredacted
289
- }
290
-
291
- // Check if any selector matches this element
292
- for (const selector of this.unredactedFields) {
293
- try {
294
- if (element.matches(selector)) {
295
- return true;
296
- }
297
- } catch (e) {
298
- logWarn(`Invalid selector: ${selector}`);
299
- }
300
- }
301
- return false;
302
- } else {
303
- // Visibility-first: check if element is NOT in redacted fields
304
- if (this.redactedFields.size === 0) {
305
- return true; // Nothing redacted, everything visible
306
- }
307
-
308
- // Check if any selector matches this element
309
- for (const selector of this.redactedFields) {
310
- try {
311
- if (element.matches(selector)) {
312
- return false; // Element is redacted
313
- }
314
- } catch (e) {
315
- logWarn(`Invalid selector: ${selector}`);
316
- }
317
- }
318
- return true; // Element is not redacted
319
- }
320
- }
321
- }
322
-
323
- // Export a default instance
324
- export const redactionManager = new RedactionManager();
325
-
326
- // Export the class for custom instances
327
- export default RedactionManager;
@@ -1,16 +0,0 @@
1
- import { HumanBehaviorTracker } from '../index.js';
2
- import type { LoaderFunctionArgs } from '@remix-run/node';
3
-
4
- // Remix-specific loader helper
5
- export function createHumanBehaviorLoader() {
6
- return async ({ request }: LoaderFunctionArgs) => {
7
- return {
8
- ENV: {
9
- HUMANBEHAVIOR_API_KEY: process.env.HUMANBEHAVIOR_API_KEY,
10
- },
11
- };
12
- };
13
- }
14
-
15
- // Re-export React components for convenience
16
- export { HumanBehaviorProvider, useHumanBehavior } from '../react/index.js';
@@ -1,14 +0,0 @@
1
- import { HumanBehaviorTracker } from '../index.js';
2
-
3
- // Create a Svelte store-like interface for HumanBehavior
4
- export const humanBehaviorStore = {
5
- init: (apiKey: string, options?: {
6
- ingestionUrl?: string;
7
- logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
8
- redactFields?: string[];
9
- suppressConsoleErrors?: boolean;
10
- recordCanvas?: boolean; // Enable canvas recording with protection
11
- }) => {
12
- return HumanBehaviorTracker.init(apiKey, options);
13
- }
14
- };