humanbehavior-js 0.0.4 → 0.0.7

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.
@@ -1,3 +1,85 @@
1
+ interface RedactionOptions {
2
+ redactedText?: string;
3
+ excludeSelectors?: string[];
4
+ userFields?: string[];
5
+ }
6
+ declare class RedactionManager {
7
+ private redactedText;
8
+ private userSelectedFields;
9
+ private excludeSelectors;
10
+ constructor(options?: RedactionOptions);
11
+ /**
12
+ * Set specific fields to be redacted
13
+ * @param fields Array of CSS selectors for fields to redact
14
+ */
15
+ setFieldsToRedact(fields: string[]): void;
16
+ /**
17
+ * Check if redaction is currently active (has fields selected)
18
+ */
19
+ isActive(): boolean;
20
+ /**
21
+ * Get the currently selected fields for redaction
22
+ */
23
+ getSelectedFields(): string[];
24
+ /**
25
+ * Process an event and redact sensitive data if needed
26
+ */
27
+ processEvent(event: any): any;
28
+ /**
29
+ * Redact sensitive data in input events
30
+ */
31
+ private redactInputEvent;
32
+ /**
33
+ * Redact sensitive data in DOM mutation events
34
+ */
35
+ private redactDOMEvent;
36
+ /**
37
+ * Check if a DOM change should be redacted based on its ID
38
+ */
39
+ private shouldRedactDOMChange;
40
+ /**
41
+ * Redact sensitive data in mouse/touch interaction events
42
+ */
43
+ private redactMouseEvent;
44
+ /**
45
+ * Redact sensitive data in full snapshot events
46
+ */
47
+ private redactFullSnapshot;
48
+ /**
49
+ * Recursively redact sensitive data in DOM nodes
50
+ */
51
+ private redactNode;
52
+ /**
53
+ * Check if a node should be redacted based on its attributes
54
+ */
55
+ private shouldRedactNode;
56
+ /**
57
+ * Check if a CSS selector would match a node based on its attributes
58
+ */
59
+ private selectorMatchesNode;
60
+ /**
61
+ * Basic selector matching for environments where matches() is not available
62
+ */
63
+ private basicSelectorMatch;
64
+ /**
65
+ * Check if an event is from a field that should be redacted
66
+ */
67
+ private isFieldSelected;
68
+ /**
69
+ * Check if an element should be redacted based on user-selected fields
70
+ */
71
+ private shouldRedactElement;
72
+ /**
73
+ * Get the original value of a redacted element (for debugging)
74
+ */
75
+ getOriginalValue(element: HTMLElement): string | undefined;
76
+ /**
77
+ * Check if an element is currently being redacted
78
+ */
79
+ isElementRedacted(element: HTMLElement): boolean;
80
+ }
81
+ declare const redactionManager: RedactionManager;
82
+
1
83
  declare global {
2
84
  interface Window {
3
85
  HumanBehaviorTracker: typeof HumanBehaviorTracker;
@@ -18,13 +100,19 @@ declare class HumanBehaviorTracker {
18
100
  private apiKey;
19
101
  private initialized;
20
102
  initializationPromise: Promise<void> | null;
21
- constructor(apiKey: string | undefined);
103
+ private redactionManager;
104
+ constructor(apiKey: string | undefined, ingestionUrl: string | undefined);
22
105
  private init;
23
106
  private ensureInitialized;
24
107
  static logToStorage(message: string): void;
25
108
  private setupPageUnloadHandler;
26
109
  viewLogs(): void;
27
- identifyUser(userProperties: Record<string, any>): Promise<void>;
110
+ addUserInfo(userProperties: Record<string, any>): Promise<void>;
111
+ /**
112
+ * Authenticate user using existing userInfo data
113
+ * @param authFields Array of field names to check for existing users (e.g., ['email', 'phoneNumber'])
114
+ */
115
+ auth(authFields: string[]): Promise<void>;
28
116
  customEvent(eventName: string, eventProperties?: Record<string, any>): Promise<void>;
29
117
  start(): Promise<void>;
30
118
  stop(): Promise<void>;
@@ -33,6 +121,24 @@ declare class HumanBehaviorTracker {
33
121
  private flush;
34
122
  private setCookie;
35
123
  private getCookie;
124
+ /**
125
+ * Start redaction functionality for sensitive input fields
126
+ * @param options Optional configuration for redaction behavior
127
+ */
128
+ redact(options?: RedactionOptions): Promise<void>;
129
+ /**
130
+ * Set specific fields to be redacted during session recording
131
+ * @param fields Array of CSS selectors for fields to redact (e.g., ['input[type="password"]', '#email-field'])
132
+ */
133
+ setRedactedFields(fields: string[]): void;
134
+ /**
135
+ * Check if redaction is currently active
136
+ */
137
+ isRedactionActive(): boolean;
138
+ /**
139
+ * Get the currently selected fields for redaction
140
+ */
141
+ getRedactedFields(): string[];
36
142
  }
37
143
 
38
144
  declare const MAX_CHUNK_SIZE_BYTES: number;
@@ -41,8 +147,9 @@ declare function validateSingleEventSize(event: any, sessionId: string): void;
41
147
  declare class HumanBehaviorAPI {
42
148
  private apiKey;
43
149
  private baseUrl;
44
- constructor({ apiKey }: {
150
+ constructor({ apiKey, ingestionUrl }: {
45
151
  apiKey: string;
152
+ ingestionUrl: string;
46
153
  });
47
154
  init(sessionId: string, userId: string | null): Promise<{
48
155
  sessionId: any;
@@ -51,6 +158,7 @@ declare class HumanBehaviorAPI {
51
158
  sendEvents(events: any[], sessionId: string, userId: string): Promise<void>;
52
159
  sendEventsChunked(events: any[], sessionId: string): Promise<any[]>;
53
160
  sendUserData(userId: string, userData: Record<string, any>, sessionId: string): Promise<any>;
161
+ sendUserAuth(userId: string, userData: Record<string, any>, sessionId: string, authFields: string[]): Promise<any>;
54
162
  sendSessionComplete(sessionId: string): Promise<void>;
55
163
  sendCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string): Promise<any>;
56
164
  sendCustomEvents(events: any[], sessionId: string): Promise<any>;
@@ -60,4 +168,5 @@ declare class HumanBehaviorAPI {
60
168
  sendBeaconCustomEvents(events: any[], sessionId: string): boolean;
61
169
  }
62
170
 
63
- export { HumanBehaviorAPI, HumanBehaviorTracker, MAX_CHUNK_SIZE_BYTES, HumanBehaviorTracker as default, isChunkSizeExceeded, validateSingleEventSize };
171
+ export { HumanBehaviorAPI, HumanBehaviorTracker, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, isChunkSizeExceeded, redactionManager, validateSingleEventSize };
172
+ export type { RedactionOptions };
@@ -3,7 +3,7 @@ import { HumanBehaviorTracker } from '..';
3
3
 
4
4
  interface HumanBehaviorInterface {
5
5
  addEvent: (event: any) => void;
6
- identifyUser: (userProperties: Record<string, any>) => Promise<void>;
6
+ addUserInfo: (userProperties: Record<string, any>) => Promise<void>;
7
7
  start: () => void;
8
8
  stop: () => void;
9
9
  viewLogs: () => void;
package/package.json CHANGED
@@ -1,19 +1,12 @@
1
1
  {
2
2
  "name": "humanbehavior-js",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "description": "SDK for HumanBehavior session and event recording",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
7
7
  "module": "./dist/esm/index.js",
8
8
  "types": "./dist/types/index.d.ts",
9
9
  "unpkg": "dist/index.min.js",
10
- "files": [
11
- "dist",
12
- "react.js",
13
- "react.d.ts",
14
- "index.js",
15
- "index.d.ts"
16
- ],
17
10
  "exports": {
18
11
  ".": {
19
12
  "import": "./dist/esm/index.js",
@@ -45,15 +38,11 @@
45
38
  "@rollup/plugin-node-resolve": "^16.0.1",
46
39
  "@rollup/plugin-terser": "^0.4.4",
47
40
  "@rollup/plugin-typescript": "^12.1.2",
48
- "@types/cors": "^2.8.17",
49
- "@types/express": "^5.0.1",
41
+ "@types/node": "^24.0.3",
50
42
  "rollup": "^4.36.0",
51
43
  "rollup-plugin-dts": "^6.2.1",
52
- "ts-loader": "^9.5.2",
53
44
  "tslib": "^2.8.1",
54
- "typescript": "^5.8.2",
55
- "webpack": "^5.98.0",
56
- "webpack-cli": "^6.0.1"
45
+ "typescript": "^5.8.2"
57
46
  },
58
47
  "publishConfig": {
59
48
  "access": "public"
@@ -0,0 +1,106 @@
1
+ import typescript from '@rollup/plugin-typescript';
2
+ import resolve from '@rollup/plugin-node-resolve';
3
+ import commonjs from '@rollup/plugin-commonjs';
4
+ import terser from '@rollup/plugin-terser';
5
+ import dts from 'rollup-plugin-dts';
6
+
7
+ // Only React should be external since it's typically provided by the host application
8
+ const external = ['react', 'react-dom'];
9
+
10
+ // Global variables for UMD build
11
+ const globals = {
12
+ react: 'React',
13
+ 'react-dom': 'ReactDOM'
14
+ };
15
+
16
+ export default [
17
+ // Main SDK bundle
18
+ {
19
+ input: 'src/index.ts',
20
+ output: [
21
+ {
22
+ file: 'dist/cjs/index.js',
23
+ format: 'cjs',
24
+ name: 'HumanBehaviorTracker',
25
+ globals,
26
+ exports: 'named',
27
+ sourcemap: true
28
+ },
29
+ {
30
+ file: 'dist/esm/index.js',
31
+ format: 'es',
32
+ sourcemap: true
33
+ },
34
+ {
35
+ file: 'dist/index.min.js',
36
+ format: 'umd',
37
+ name: 'HumanBehaviorTracker',
38
+ globals,
39
+ exports: 'auto',
40
+ sourcemap: true,
41
+ plugins: [terser()]
42
+ }
43
+ ],
44
+ plugins: [
45
+ resolve(),
46
+ commonjs(),
47
+ typescript({
48
+ tsconfig: './tsconfig.json',
49
+ declaration: false
50
+ })
51
+ ],
52
+ external
53
+ },
54
+
55
+ // React component bundle
56
+ {
57
+ input: './src/react/index.tsx',
58
+ output: [
59
+ {
60
+ file: 'dist/cjs/react/index.js',
61
+ format: 'cjs',
62
+ name: 'HumanBehaviorReact',
63
+ globals: {
64
+ ...globals,
65
+ '..': 'HumanBehaviorTracker'
66
+ },
67
+ exports: 'named',
68
+ sourcemap: true
69
+ },
70
+ {
71
+ file: 'dist/esm/react/index.js',
72
+ format: 'es',
73
+ sourcemap: true
74
+ }
75
+ ],
76
+ plugins: [
77
+ resolve(),
78
+ commonjs(),
79
+ typescript({
80
+ tsconfig: './tsconfig.json',
81
+ declaration: false
82
+ })
83
+ ],
84
+ external: [...external, '..']
85
+ },
86
+
87
+ // Type definition bundles - generate these separately
88
+ {
89
+ input: 'src/index.ts',
90
+ output: {
91
+ file: 'dist/types/index.d.ts',
92
+ format: 'es'
93
+ },
94
+ plugins: [dts()],
95
+ external
96
+ },
97
+ {
98
+ input: 'src/react/index.tsx',
99
+ output: {
100
+ file: 'dist/types/react/index.d.ts',
101
+ format: 'es'
102
+ },
103
+ plugins: [dts()],
104
+ external: [...external, '..']
105
+ }
106
+ ];
package/src/api.ts ADDED
@@ -0,0 +1,348 @@
1
+ export const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
2
+
3
+ export function isChunkSizeExceeded(currentChunk: any[], newEvent: any, sessionId: string): boolean {
4
+ const nextChunkSize = new TextEncoder().encode(JSON.stringify({
5
+ sessionId,
6
+ events: [...currentChunk, newEvent]
7
+ })).length;
8
+
9
+ return nextChunkSize > MAX_CHUNK_SIZE_BYTES;
10
+ }
11
+
12
+ export function validateSingleEventSize(event: any, sessionId: string): void {
13
+ const singleEventSize = new TextEncoder().encode(JSON.stringify({
14
+ sessionId,
15
+ events: [event]
16
+ })).length;
17
+
18
+ if (singleEventSize > MAX_CHUNK_SIZE_BYTES) {
19
+ throw new Error(`Single event size (${singleEventSize} bytes) exceeds maximum chunk size (${MAX_CHUNK_SIZE_BYTES} bytes)`);
20
+ }
21
+ }
22
+
23
+ export class HumanBehaviorAPI {
24
+ private apiKey: string;
25
+ private baseUrl: string;
26
+
27
+ constructor({ apiKey, ingestionUrl }: { apiKey: string, ingestionUrl: string }) {
28
+ this.apiKey = apiKey;
29
+ this.baseUrl = ingestionUrl;
30
+ }
31
+
32
+ public async init(sessionId: string, userId: string | null) {
33
+ const response = await fetch(`${this.baseUrl}/api/ingestion/init`, {
34
+ method: 'POST',
35
+ headers: {
36
+ 'Content-Type': 'application/json',
37
+ 'Authorization': `Bearer ${this.apiKey}`
38
+ },
39
+ body: JSON.stringify({
40
+ sessionId: sessionId,
41
+ endUserId: userId
42
+ })
43
+ });
44
+
45
+ if (!response.ok) {
46
+ throw new Error(`Failed to initialize ingestion: ${response.statusText}`);
47
+ }
48
+
49
+ const responseJson = await response.json();
50
+ return {
51
+ sessionId: responseJson.sessionId,
52
+ endUserId: responseJson.endUserId
53
+ }
54
+ }
55
+
56
+ async sendEvents(events: any[], sessionId: string, userId: string) {
57
+ const response = await fetch(`${this.baseUrl}/api/ingestion/events`, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/json',
61
+ 'Authorization': `Bearer ${this.apiKey}`
62
+ },
63
+ body: JSON.stringify({
64
+ sessionId,
65
+ events: events,
66
+ endUserId: userId
67
+ })
68
+ });
69
+
70
+ if (!response.ok) {
71
+ throw new Error(`Failed to send events: ${response.statusText}`);
72
+ }
73
+ }
74
+
75
+ async sendEventsChunked(events: any[], sessionId: string) {
76
+ try {
77
+ const results = [];
78
+ let currentChunk: any[] = [];
79
+
80
+ for (const event of events) {
81
+ if (isChunkSizeExceeded(currentChunk, event, sessionId)) {
82
+ // If current chunk is not empty, send it first
83
+ if (currentChunk.length > 0) {
84
+ const response = await fetch(`${this.baseUrl}/api/ingestion/events`, {
85
+ method: 'POST',
86
+ headers: {
87
+ 'Content-Type': 'application/json',
88
+ 'Authorization': `Bearer ${this.apiKey}`
89
+ },
90
+ body: JSON.stringify({
91
+ sessionId,
92
+ events: currentChunk
93
+ })
94
+ });
95
+
96
+ if (!response.ok) {
97
+ throw new Error(`Failed to send events: ${response.statusText}`);
98
+ }
99
+
100
+ results.push(await response.json());
101
+ currentChunk = [];
102
+ }
103
+
104
+ // Validate single event size
105
+ validateSingleEventSize(event, sessionId);
106
+
107
+ // Start new chunk with this event
108
+ currentChunk = [event];
109
+ } else {
110
+ // Add event to current chunk
111
+ currentChunk.push(event);
112
+ }
113
+ }
114
+
115
+ // Send any remaining events
116
+ if (currentChunk.length > 0) {
117
+ const response = await fetch(`${this.baseUrl}/api/ingestion/events`, {
118
+ method: 'POST',
119
+ headers: {
120
+ 'Content-Type': 'application/json',
121
+ 'Authorization': `Bearer ${this.apiKey}`
122
+ },
123
+ body: JSON.stringify({
124
+ sessionId,
125
+ events: currentChunk
126
+ })
127
+ });
128
+
129
+ if (!response.ok) {
130
+ throw new Error(`Failed to send events: ${response.statusText}`);
131
+ }
132
+
133
+ results.push(await response.json());
134
+ }
135
+
136
+ return results.flat();
137
+ } catch (error) {
138
+ console.error('Error sending events:', error);
139
+ throw error;
140
+ }
141
+ }
142
+
143
+ async sendUserData(userId: string, userData: Record<string, any>, sessionId: string) {
144
+ try {
145
+ const response = await fetch(`${this.baseUrl}/api/ingestion/user`, {
146
+ method: 'POST',
147
+ headers: {
148
+ 'Content-Type': 'application/json',
149
+ 'Authorization': `Bearer ${this.apiKey}`
150
+ },
151
+ body: JSON.stringify({
152
+ userId: userId,
153
+ userAttributes: userData,
154
+ sessionId: sessionId
155
+ })
156
+ });
157
+
158
+ if (!response.ok) {
159
+ throw new Error(`Failed to send user data: ${response.statusText} with API key: ${this.apiKey}`);
160
+ }
161
+
162
+ return await response.json();
163
+ } catch (error) {
164
+ console.error('Error sending user data:', error);
165
+ throw error;
166
+ }
167
+ }
168
+
169
+ async sendUserAuth(userId: string, userData: Record<string, any>, sessionId: string, authFields: string[]) {
170
+ try {
171
+ const response = await fetch(`${this.baseUrl}/api/ingestion/user/auth`, {
172
+ method: 'POST',
173
+ headers: {
174
+ 'Content-Type': 'application/json',
175
+ 'Authorization': `Bearer ${this.apiKey}`
176
+ },
177
+ body: JSON.stringify({
178
+ userId: userId,
179
+ userAttributes: userData,
180
+ sessionId: sessionId,
181
+ authFields: authFields
182
+ })
183
+ });
184
+
185
+ if (!response.ok) {
186
+ throw new Error(`Failed to authenticate user: ${response.statusText} with API key: ${this.apiKey}`);
187
+ }
188
+
189
+ return await response.json();
190
+ } catch (error) {
191
+ console.error('Error authenticating user:', error);
192
+ throw error;
193
+ }
194
+ }
195
+
196
+ async sendSessionComplete(sessionId: string) {
197
+ const response = await fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
198
+ method: 'POST',
199
+ headers: {
200
+ 'Content-Type': 'application/json',
201
+ 'Authorization': `Bearer ${this.apiKey}`
202
+ },
203
+ body: JSON.stringify({ sessionId })
204
+ });
205
+
206
+ if (!response.ok) {
207
+ throw new Error(`Failed to send session complete: ${response.statusText}`);
208
+ }
209
+ }
210
+
211
+ async sendCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string) {
212
+ const maxRetries = 3;
213
+ let retryCount = 0;
214
+
215
+ while (retryCount < maxRetries) {
216
+ try {
217
+ const response = await fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
218
+ method: 'POST',
219
+ headers: {
220
+ 'Content-Type': 'application/json',
221
+ 'Authorization': `Bearer ${this.apiKey}`
222
+ },
223
+ body: JSON.stringify({
224
+ name: eventName,
225
+ properties: eventProperties,
226
+ sessionId: sessionId,
227
+ timestamp: new Date().toISOString()
228
+ })
229
+ });
230
+
231
+ if (!response.ok) {
232
+ throw new Error(`Failed to send custom event: ${response.statusText}`);
233
+ }
234
+
235
+ return await response.json();
236
+ } catch (error) {
237
+ retryCount++;
238
+ if (retryCount === maxRetries) {
239
+ console.error('Error sending custom event after max retries:', error);
240
+ throw error;
241
+ }
242
+ // Exponential backoff
243
+ await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
244
+ }
245
+ }
246
+ }
247
+
248
+ async sendCustomEvents(events: any[], sessionId: string) {
249
+ const maxRetries = 3;
250
+ let retryCount = 0;
251
+
252
+ while (retryCount < maxRetries) {
253
+ try {
254
+ const response = await fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
255
+ method: 'POST',
256
+ headers: {
257
+ 'Content-Type': 'application/json',
258
+ 'Authorization': `Bearer ${this.apiKey}`
259
+ },
260
+ body: JSON.stringify({
261
+ events: events.map(event => ({
262
+ ...event,
263
+ sessionId: sessionId
264
+ }))
265
+ })
266
+ });
267
+
268
+ if (!response.ok) {
269
+ throw new Error(`Failed to send custom events: ${response.statusText}`);
270
+ }
271
+
272
+ return await response.json();
273
+ } catch (error) {
274
+ retryCount++;
275
+ if (retryCount === maxRetries) {
276
+ console.error('Error sending custom events after max retries:', error);
277
+ throw error;
278
+ }
279
+ // Exponential backoff
280
+ await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
281
+ }
282
+ }
283
+ }
284
+
285
+ public sendBeaconEvents(events: any[], sessionId: string, isSessionComplete: boolean = false) {
286
+ const data = new URLSearchParams()
287
+ data.append('events', encodeURIComponent(JSON.stringify(events)))
288
+ data.append('sessionId', encodeURIComponent(sessionId))
289
+ data.append('timestamp', encodeURIComponent(Date.now().toString()))
290
+ data.append('apiKey', encodeURIComponent(this.apiKey))
291
+ if (isSessionComplete) {
292
+ console.log('Session complete beacon sending');
293
+ localStorage.setItem('koalaware_session_complete', Date.now().toString());
294
+ data.append('sessionComplete', encodeURIComponent('true'))
295
+ }
296
+
297
+ const success = navigator.sendBeacon(
298
+ `${this.baseUrl}/api/ingestion/events`,
299
+ data
300
+ );
301
+
302
+ // KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
303
+ // KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
304
+ }
305
+
306
+ public sendBeaconSessionComplete(sessionId: string) {
307
+ const data = new URLSearchParams()
308
+ data.append('sessionId', sessionId)
309
+ data.append('apiKey', this.apiKey)
310
+ data.append('sessionComplete', 'true')
311
+
312
+ const success = navigator.sendBeacon(
313
+ `${this.baseUrl}/api/ingestion/sessionComplete`,
314
+ data
315
+ );
316
+
317
+ // KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
318
+ // KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
319
+ }
320
+
321
+ public sendBeaconCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string) {
322
+ const data = new URLSearchParams()
323
+ data.append('name', encodeURIComponent(eventName))
324
+ data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)))
325
+ data.append('sessionId', encodeURIComponent(sessionId))
326
+ data.append('timestamp', encodeURIComponent(new Date().toISOString()))
327
+ data.append('apiKey', encodeURIComponent(this.apiKey))
328
+
329
+ return navigator.sendBeacon(
330
+ `${this.baseUrl}/api/ingestion/customEvent`,
331
+ data
332
+ );
333
+ }
334
+
335
+ public sendBeaconCustomEvents(events: any[], sessionId: string) {
336
+ const data = new URLSearchParams()
337
+ data.append('events', encodeURIComponent(JSON.stringify(events.map(event => ({
338
+ ...event,
339
+ sessionId: sessionId
340
+ })))))
341
+ data.append('apiKey', encodeURIComponent(this.apiKey))
342
+
343
+ return navigator.sendBeacon(
344
+ `${this.baseUrl}/api/ingestion/customEvent/batch`,
345
+ data
346
+ );
347
+ }
348
+ }
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Main entry point for the HumanBehavior SDK
3
+ */
4
+
5
+ import { HumanBehaviorTracker } from './tracker';
6
+
7
+ // Export everything from the tracker module
8
+ export * from './tracker';
9
+
10
+ // Export everything from the API module
11
+ export * from './api';
12
+
13
+ // Export redaction functionality
14
+ export * from './redact';
15
+
16
+ // Also export the tracker as the default export
17
+ export { HumanBehaviorTracker as default } from './tracker';
18
+
19
+ // For UMD builds, expose the main class globally
20
+ if (typeof window !== 'undefined') {
21
+ (window as any).HumanBehaviorTracker = HumanBehaviorTracker;
22
+ }