@lavarage/telemetry 1.0.1 → 1.2.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/README.md CHANGED
@@ -22,7 +22,9 @@ const telemetry = new LavarageTelemetry({
22
22
  telemetry.interceptFetch();
23
23
 
24
24
  // Connect wallet (triggers login event)
25
- telemetry.setWallet('YourWalletAddress123...');
25
+ // Wallet platform is automatically detected (MetaMask, Phantom, etc.)
26
+ // or you can specify it manually:
27
+ telemetry.setWallet('YourWalletAddress123...', 'MetaMask');
26
28
 
27
29
  // Track trading pair views
28
30
  telemetry.trackPairView('SOL/USDC', { source: 'homepage' });
@@ -206,12 +208,25 @@ All telemetry operations are wrapped in try-catch blocks. Telemetry errors will
206
208
 
207
209
  ### Methods
208
210
 
209
- #### `setWallet(walletAddress: string)`
211
+ #### `setWallet(walletAddress: string, walletPlatform?: string)`
212
+
213
+ Set the current wallet address and trigger a login event. The wallet platform (e.g., 'MetaMask', 'Phantom', 'WalletConnect') is automatically detected from the browser environment, but you can also specify it manually.
210
214
 
211
- Set the current wallet address and trigger a login event.
215
+ **Supported auto-detected platforms:**
216
+ - MetaMask
217
+ - Coinbase Wallet
218
+ - Trust Wallet
219
+ - WalletConnect
220
+ - Phantom (Solana)
221
+ - Solflare (Solana)
222
+ - Generic Ethereum/Solana providers
212
223
 
213
224
  ```typescript
225
+ // Auto-detect wallet platform
214
226
  telemetry.setWallet('YourWalletAddress123...');
227
+
228
+ // Or specify manually
229
+ telemetry.setWallet('YourWalletAddress123...', 'MetaMask');
215
230
  ```
216
231
 
217
232
  #### `interceptFetch()`
@@ -248,6 +263,22 @@ Track a system event (not tied to any wallet address). System events are display
248
263
  ```typescript
249
264
  // Track app startup
250
265
  telemetry.trackSystemEvent('app_start', 'Application initialized', 'info');
266
+ ```
267
+
268
+ #### `trackStateChange(stateName: string, previousValue?: any, newValue?: any, action?: string, metadata?: object)`
269
+
270
+ Track a React state change. Useful for debugging and understanding user interactions.
271
+
272
+ ```typescript
273
+ // Track a state change manually
274
+ telemetry.trackStateChange(
275
+ 'userPreferences',
276
+ { theme: 'light' },
277
+ { theme: 'dark' },
278
+ 'TOGGLE_THEME',
279
+ { source: 'settings-panel' }
280
+ );
281
+ ```
251
282
 
252
283
  // Track feature usage
253
284
  telemetry.trackSystemEvent('feature_used', 'User enabled dark mode', 'info', {
@@ -318,6 +349,177 @@ Clean up the telemetry instance, restore original functions, and flush remaining
318
349
  telemetry.destroy();
319
350
  ```
320
351
 
352
+ ## React State Tracking
353
+
354
+ The SDK provides React hooks for automatically tracking state changes in your React components.
355
+
356
+ ### Installation
357
+
358
+ ```bash
359
+ # Install the SDK
360
+ npm install @lavarage/telemetry
361
+
362
+ # If you want to use React hooks, also install React (peer dependency)
363
+ npm install react
364
+ ```
365
+
366
+ ### Quick Start
367
+
368
+ ```typescript
369
+ import { LavarageTelemetry } from '@lavarage/telemetry';
370
+ // Import React hooks from the separate entry point
371
+ import { useTrackedState } from '@lavarage/telemetry/react';
372
+
373
+ const telemetry = new LavarageTelemetry({
374
+ apiEndpoint: 'https://telemetry.lavarage.com',
375
+ platform: 'lavarage-web',
376
+ });
377
+
378
+ function MyComponent() {
379
+ // Automatically tracks all state changes
380
+ const [count, setCount] = useTrackedState(0, {
381
+ stateName: 'counter',
382
+ telemetry: telemetry,
383
+ action: 'SET_COUNT'
384
+ });
385
+
386
+ return (
387
+ <button onClick={() => setCount(count + 1)}>
388
+ Count: {count}
389
+ </button>
390
+ );
391
+ }
392
+ ```
393
+
394
+ ### Available Hooks
395
+
396
+ #### `useTrackedState(initialState, config)`
397
+
398
+ Wraps React's `useState` and automatically tracks state changes.
399
+
400
+ ```typescript
401
+ const [state, setState] = useTrackedState(initialValue, {
402
+ stateName: 'userPreferences', // Required: name for this state
403
+ telemetry: telemetryInstance, // Required: telemetry instance
404
+ trackInitial: false, // Optional: track initial state (default: false)
405
+ action: 'UPDATE_PREFERENCES', // Optional: action name
406
+ metadata: { source: 'settings' }, // Optional: additional metadata
407
+ shouldTrack: (prev, next) => { // Optional: filter which changes to track
408
+ return prev !== next; // Only track if values actually changed
409
+ }
410
+ });
411
+ ```
412
+
413
+ #### `useTrackedReducer(reducer, initialState, config)`
414
+
415
+ Wraps React's `useReducer` and automatically tracks state changes with action information.
416
+
417
+ ```typescript
418
+ const [state, dispatch] = useTrackedReducer(reducer, initialState, {
419
+ stateName: 'cart',
420
+ telemetry: telemetryInstance,
421
+ trackInitial: false,
422
+ metadata: { userId: '123' }
423
+ });
424
+
425
+ // Dispatch actions normally - they'll be tracked automatically
426
+ dispatch({ type: 'ADD_ITEM', payload: { id: 1, name: 'Product' } });
427
+ ```
428
+
429
+ #### `useStateTracker(telemetry)`
430
+
431
+ Returns a function to manually track state changes with more control.
432
+
433
+ ```typescript
434
+ const trackState = useStateTracker(telemetry);
435
+
436
+ const handleChange = (newValue) => {
437
+ trackState('theme', currentValue, newValue, 'SET_THEME', {
438
+ source: 'user-action'
439
+ });
440
+ setCurrentValue(newValue);
441
+ };
442
+ ```
443
+
444
+ ### Configuration Options
445
+
446
+ - **stateName** (required): A unique identifier for this state (e.g., 'cart', 'userPreferences', 'theme')
447
+ - **telemetry** (required): Your telemetry instance
448
+ - **trackInitial** (optional): Whether to track the initial state value (default: false)
449
+ - **action** (optional): Action name to use when tracking (default: 'STATE_CHANGE')
450
+ - **metadata** (optional): Additional context to include with each state change
451
+ - **shouldTrack** (optional): Function to filter which changes to track - useful for avoiding noise
452
+
453
+ ### Example: Shopping Cart
454
+
455
+ ```typescript
456
+ import { useTrackedState } from '@lavarage/telemetry/react';
457
+
458
+ function ShoppingCart() {
459
+ const [items, setItems] = useTrackedState([], {
460
+ stateName: 'cartItems',
461
+ telemetry: telemetry,
462
+ action: 'CART_UPDATE',
463
+ shouldTrack: (prev, next) => {
464
+ // Only track if cart actually changed (not just reference)
465
+ return JSON.stringify(prev) !== JSON.stringify(next);
466
+ }
467
+ });
468
+
469
+ const addItem = (item) => {
470
+ setItems([...items, item]);
471
+ // State change is automatically tracked!
472
+ };
473
+
474
+ return (
475
+ <div>
476
+ {items.map(item => <div key={item.id}>{item.name}</div>)}
477
+ <button onClick={() => addItem({ id: 1, name: 'Product' })}>
478
+ Add Item
479
+ </button>
480
+ </div>
481
+ );
482
+ }
483
+ ```
484
+
485
+ ### Example: User Preferences with useReducer
486
+
487
+ ```typescript
488
+ import { useTrackedReducer } from '@lavarage/telemetry/react';
489
+
490
+ function preferencesReducer(state, action) {
491
+ switch (action.type) {
492
+ case 'SET_THEME':
493
+ return { ...state, theme: action.payload };
494
+ case 'SET_LANGUAGE':
495
+ return { ...state, language: action.payload };
496
+ default:
497
+ return state;
498
+ }
499
+ }
500
+
501
+ function UserPreferences() {
502
+ const [prefs, dispatch] = useTrackedReducer(
503
+ preferencesReducer,
504
+ { theme: 'light', language: 'en' },
505
+ {
506
+ stateName: 'userPreferences',
507
+ telemetry: telemetry,
508
+ metadata: { userId: currentUser.id }
509
+ }
510
+ );
511
+
512
+ return (
513
+ <div>
514
+ <button onClick={() => dispatch({ type: 'SET_THEME', payload: 'dark' })}>
515
+ Dark Mode
516
+ </button>
517
+ {/* State changes are automatically tracked with action type! */}
518
+ </div>
519
+ );
520
+ }
521
+ ```
522
+
321
523
  ## Event Types
322
524
 
323
525
  The SDK tracks the following event types:
@@ -328,6 +530,7 @@ The SDK tracks the following event types:
328
530
  - **request**: Network requests (fetch/Axios)
329
531
  - **system_event**: System-level events not tied to any wallet address (displayed in separate dashboard panel)
330
532
  - **log**: Custom log events (via `logWalletEvent()` or `sendLog()`)
533
+ - **state_change**: React state changes (via `trackStateChange()` or React hooks)
331
534
 
332
535
  ## Batching
333
536
 
@@ -425,12 +628,12 @@ await telemetry.sendLog(
425
628
  );
426
629
  ```
427
630
 
428
- #### `setWallet(walletAddress: string)`
631
+ #### `setWallet(walletAddress: string, walletPlatform?: string)`
429
632
 
430
- Set the default wallet address for subsequent events. This is optional for backend use since you can specify the wallet in each log call.
633
+ Set the default wallet address for subsequent events. This is optional for backend use since you can specify the wallet in each log call. You can also optionally specify the wallet platform.
431
634
 
432
635
  ```typescript
433
- telemetry.setWallet('WalletAddress123...');
636
+ telemetry.setWallet('WalletAddress123...', 'MetaMask');
434
637
  // Now all events will use this wallet by default
435
638
  telemetry.logWalletEvent(
436
639
  null, // Will use the wallet set above
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { TelemetryConfig, TelemetryEvent, ErrorEvent, RequestEvent, HostFilterInput, HostFilterConfig, ErrorFilterConfig, SystemEvent } from './types';
2
2
  export type { TelemetryConfig, TelemetryEvent, ErrorEvent, RequestEvent, HostFilterInput, HostFilterConfig, ErrorFilterConfig, SystemEvent, };
3
+ export type { StateChangeEvent } from './types';
3
4
  export declare class LavarageTelemetry {
4
5
  private apiEndpoint;
5
6
  private platform;
@@ -17,6 +18,11 @@ export declare class LavarageTelemetry {
17
18
  constructor(config: TelemetryConfig);
18
19
  private generateSessionId;
19
20
  private generateRequestId;
21
+ /**
22
+ * Detect wallet platform/provider from browser environment
23
+ * Checks for common wallet providers like MetaMask, Phantom, WalletConnect, etc.
24
+ */
25
+ private detectWalletPlatform;
20
26
  private normalizeHostFilter;
21
27
  private shouldCaptureHost;
22
28
  private matchesHost;
@@ -29,7 +35,7 @@ export declare class LavarageTelemetry {
29
35
  private flush;
30
36
  private initializeCapture;
31
37
  private trackError;
32
- setWallet(walletAddress: string): void;
38
+ setWallet(walletAddress: string, walletPlatform?: string): void;
33
39
  interceptFetch(): void;
34
40
  interceptAxios(axiosInstance: any): void;
35
41
  trackPairView(pair: string, metadata?: object): void;
@@ -59,5 +65,15 @@ export declare class LavarageTelemetry {
59
65
  */
60
66
  sendLog(walletAddress: string, eventType: string, message: string, metadata?: object): Promise<void>;
61
67
  updateHostFilter(captureHosts: HostFilterInput): void;
68
+ /**
69
+ * Track a React state change
70
+ * Use this method to manually log state changes, or use the useTrackedState/useTrackedReducer hooks for automatic tracking
71
+ * @param stateName - Name/identifier for the state (e.g., 'userPreferences', 'cartItems', 'theme')
72
+ * @param previousValue - Previous state value (optional, will be sanitized)
73
+ * @param newValue - New state value (optional, will be sanitized)
74
+ * @param action - Action that caused the change (e.g., 'SET_USER', 'ADD_TO_CART', 'TOGGLE_THEME')
75
+ * @param metadata - Additional context about the state change
76
+ */
77
+ trackStateChange(stateName: string, previousValue?: any, newValue?: any, action?: string, metadata?: object): void;
62
78
  destroy(): void;
63
79
  }
package/dist/index.js CHANGED
@@ -33,6 +33,77 @@ class LavarageTelemetry {
33
33
  generateRequestId() {
34
34
  return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
35
35
  }
36
+ /**
37
+ * Detect wallet platform/provider from browser environment
38
+ * Checks for common wallet providers like MetaMask, Phantom, WalletConnect, etc.
39
+ */
40
+ detectWalletPlatform() {
41
+ if (typeof window === 'undefined') {
42
+ return null;
43
+ }
44
+ try {
45
+ const win = window;
46
+ // Check for Ethereum wallets
47
+ if (win.ethereum) {
48
+ // MetaMask
49
+ if (win.ethereum.isMetaMask) {
50
+ return 'MetaMask';
51
+ }
52
+ // Coinbase Wallet
53
+ if (win.ethereum.isCoinbaseWallet) {
54
+ return 'Coinbase Wallet';
55
+ }
56
+ // Trust Wallet
57
+ if (win.ethereum.isTrust) {
58
+ return 'Trust Wallet';
59
+ }
60
+ // WalletConnect
61
+ if (win.ethereum.isWalletConnect) {
62
+ return 'WalletConnect';
63
+ }
64
+ // Generic Ethereum provider
65
+ if (win.ethereum.providers && Array.isArray(win.ethereum.providers)) {
66
+ // Multiple providers detected, try to identify
67
+ for (const provider of win.ethereum.providers) {
68
+ if (provider.isMetaMask)
69
+ return 'MetaMask';
70
+ if (provider.isCoinbaseWallet)
71
+ return 'Coinbase Wallet';
72
+ if (provider.isTrust)
73
+ return 'Trust Wallet';
74
+ }
75
+ }
76
+ // Check provider name/version
77
+ if (win.ethereum.providerName) {
78
+ return win.ethereum.providerName;
79
+ }
80
+ // Default to generic Ethereum
81
+ return 'Ethereum';
82
+ }
83
+ // Check for Solana wallets
84
+ if (win.solana) {
85
+ // Phantom
86
+ if (win.solana.isPhantom) {
87
+ return 'Phantom';
88
+ }
89
+ // Solflare
90
+ if (win.solana.isSolflare) {
91
+ return 'Solflare';
92
+ }
93
+ // Generic Solana
94
+ return 'Solana';
95
+ }
96
+ // Check for other wallet providers
97
+ if (win.web3) {
98
+ return 'Web3';
99
+ }
100
+ return null;
101
+ }
102
+ catch (error) {
103
+ // Silently fail detection
104
+ return null;
105
+ }
106
+ }
36
107
  normalizeHostFilter(input) {
37
108
  if (!input) {
38
109
  return { mode: 'all' };
@@ -325,9 +396,11 @@ class LavarageTelemetry {
325
396
  url: typeof window !== 'undefined' ? window.location.href : '',
326
397
  });
327
398
  }
328
- setWallet(walletAddress) {
399
+ setWallet(walletAddress, walletPlatform) {
329
400
  try {
330
401
  this.wallet = walletAddress;
402
+ // Detect wallet platform if not provided
403
+ const detectedPlatform = walletPlatform || this.detectWalletPlatform();
331
404
  // Track login event
332
405
  this.enqueue({
333
406
  type: 'login',
@@ -336,6 +409,7 @@ class LavarageTelemetry {
336
409
  timestamp: Date.now(),
337
410
  sessionId: this.sessionId,
338
411
  userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',
412
+ walletPlatform: detectedPlatform || null,
339
413
  });
340
414
  }
341
415
  catch (error) {
@@ -610,6 +684,36 @@ class LavarageTelemetry {
610
684
  // Silently fail
611
685
  }
612
686
  }
687
+ /**
688
+ * Track a React state change
689
+ * Use this method to manually log state changes, or use the useTrackedState/useTrackedReducer hooks for automatic tracking
690
+ * @param stateName - Name/identifier for the state (e.g., 'userPreferences', 'cartItems', 'theme')
691
+ * @param previousValue - Previous state value (optional, will be sanitized)
692
+ * @param newValue - New state value (optional, will be sanitized)
693
+ * @param action - Action that caused the change (e.g., 'SET_USER', 'ADD_TO_CART', 'TOGGLE_THEME')
694
+ * @param metadata - Additional context about the state change
695
+ */
696
+ trackStateChange(stateName, previousValue, newValue, action, metadata) {
697
+ try {
698
+ const event = {
699
+ type: 'state_change',
700
+ wallet: this.wallet,
701
+ platform: this.platform,
702
+ stateName,
703
+ previousValue: previousValue !== undefined ? this.sanitizePayload(previousValue) : undefined,
704
+ newValue: newValue !== undefined ? this.sanitizePayload(newValue) : undefined,
705
+ action: action || 'STATE_CHANGE',
706
+ metadata: metadata ? this.sanitizePayload(metadata) : undefined,
707
+ timestamp: Date.now(),
708
+ sessionId: this.sessionId,
709
+ url: typeof window !== 'undefined' ? window.location.href : '',
710
+ };
711
+ this.enqueue(event);
712
+ }
713
+ catch (error) {
714
+ // Silently fail
715
+ }
716
+ }
613
717
  destroy() {
614
718
  // Restore original functions
615
719
  if (this.originalFetch && typeof window !== 'undefined') {
@@ -0,0 +1,73 @@
1
+ /**
2
+ * React hooks for automatic state change tracking
3
+ *
4
+ * These hooks wrap React's useState and useReducer to automatically
5
+ * track state changes to the telemetry system.
6
+ */
7
+ import { Dispatch, SetStateAction, Reducer } from 'react';
8
+ import { LavarageTelemetry } from './index';
9
+ /**
10
+ * Configuration for tracked state
11
+ */
12
+ export interface TrackedStateConfig {
13
+ /** Name/identifier for this state (e.g., 'userPreferences', 'cartItems') */
14
+ stateName: string;
15
+ /** Telemetry instance to use for tracking */
16
+ telemetry: LavarageTelemetry;
17
+ /** Whether to track the initial state value (default: false) */
18
+ trackInitial?: boolean;
19
+ /** Action name to use when tracking (default: 'STATE_CHANGE') */
20
+ action?: string;
21
+ /** Additional metadata to include with each state change */
22
+ metadata?: object;
23
+ /** Function to determine if a state change should be tracked (useful for filtering) */
24
+ shouldTrack?: (previousValue: any, newValue: any) => boolean;
25
+ }
26
+ /**
27
+ * Hook that wraps useState and automatically tracks state changes
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * const [count, setCount] = useTrackedState(0, {
32
+ * stateName: 'counter',
33
+ * telemetry: telemetryInstance,
34
+ * action: 'SET_COUNT'
35
+ * });
36
+ * ```
37
+ */
38
+ export declare function useTrackedState<T>(initialState: T | (() => T), config: TrackedStateConfig): [T, Dispatch<SetStateAction<T>>];
39
+ /**
40
+ * Action type for tracked reducer
41
+ */
42
+ export interface TrackedReducerAction {
43
+ type: string;
44
+ payload?: any;
45
+ [key: string]: any;
46
+ }
47
+ /**
48
+ * Hook that wraps useReducer and automatically tracks state changes
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * const [state, dispatch] = useTrackedReducer(reducer, initialState, {
53
+ * stateName: 'cart',
54
+ * telemetry: telemetryInstance
55
+ * });
56
+ * ```
57
+ */
58
+ export declare function useTrackedReducer<TState, TAction extends TrackedReducerAction>(reducer: Reducer<TState, TAction>, initialState: TState, config: TrackedStateConfig): [TState, Dispatch<TAction>];
59
+ /**
60
+ * Hook to manually track state changes with more control
61
+ * Useful when you want to track state changes but don't want to use the tracked hooks
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const trackState = useStateTracker(telemetryInstance);
66
+ *
67
+ * const handleChange = (newValue) => {
68
+ * trackState('userPreferences', currentValue, newValue, 'UPDATE_PREFERENCES');
69
+ * setCurrentValue(newValue);
70
+ * };
71
+ * ```
72
+ */
73
+ export declare function useStateTracker(telemetry: LavarageTelemetry): (stateName: string, previousValue?: any, newValue?: any, action?: string, metadata?: object) => void;
package/dist/react.js ADDED
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ /**
3
+ * React hooks for automatic state change tracking
4
+ *
5
+ * These hooks wrap React's useState and useReducer to automatically
6
+ * track state changes to the telemetry system.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.useTrackedState = useTrackedState;
10
+ exports.useTrackedReducer = useTrackedReducer;
11
+ exports.useStateTracker = useStateTracker;
12
+ const react_1 = require("react");
13
+ /**
14
+ * Hook that wraps useState and automatically tracks state changes
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const [count, setCount] = useTrackedState(0, {
19
+ * stateName: 'counter',
20
+ * telemetry: telemetryInstance,
21
+ * action: 'SET_COUNT'
22
+ * });
23
+ * ```
24
+ */
25
+ function useTrackedState(initialState, config) {
26
+ const { stateName, telemetry, trackInitial = false, action, metadata, shouldTrack } = config;
27
+ const [state, setState] = (0, react_1.useState)(initialState);
28
+ const previousStateRef = (0, react_1.useRef)(state);
29
+ const isInitialMount = (0, react_1.useRef)(true);
30
+ // Track initial state if configured
31
+ (0, react_1.useEffect)(() => {
32
+ if (trackInitial && isInitialMount.current) {
33
+ isInitialMount.current = false;
34
+ telemetry.trackStateChange(stateName, undefined, state, action || 'INITIAL_STATE', metadata);
35
+ }
36
+ }, []);
37
+ // Track state changes
38
+ (0, react_1.useEffect)(() => {
39
+ // Skip tracking on initial mount unless trackInitial is true
40
+ if (isInitialMount.current && !trackInitial) {
41
+ isInitialMount.current = false;
42
+ previousStateRef.current = state;
43
+ return;
44
+ }
45
+ if (isInitialMount.current) {
46
+ isInitialMount.current = false;
47
+ previousStateRef.current = state;
48
+ return;
49
+ }
50
+ const previousValue = previousStateRef.current;
51
+ // Check if we should track this change
52
+ if (shouldTrack && !shouldTrack(previousValue, state)) {
53
+ previousStateRef.current = state;
54
+ return;
55
+ }
56
+ // Track the state change
57
+ telemetry.trackStateChange(stateName, previousValue, state, action, metadata);
58
+ previousStateRef.current = state;
59
+ }, [state, stateName, telemetry, action, metadata, shouldTrack]);
60
+ // Wrapper for setState that ensures tracking
61
+ const trackedSetState = (value) => {
62
+ setState(value);
63
+ };
64
+ return [state, trackedSetState];
65
+ }
66
+ /**
67
+ * Hook that wraps useReducer and automatically tracks state changes
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * const [state, dispatch] = useTrackedReducer(reducer, initialState, {
72
+ * stateName: 'cart',
73
+ * telemetry: telemetryInstance
74
+ * });
75
+ * ```
76
+ */
77
+ function useTrackedReducer(reducer, initialState, config) {
78
+ const { stateName, telemetry, trackInitial = false, metadata, shouldTrack } = config;
79
+ const [state, dispatch] = (0, react_1.useReducer)(reducer, initialState);
80
+ const previousStateRef = (0, react_1.useRef)(state);
81
+ const isInitialMount = (0, react_1.useRef)(true);
82
+ // Track initial state if configured
83
+ (0, react_1.useEffect)(() => {
84
+ if (trackInitial && isInitialMount.current) {
85
+ isInitialMount.current = false;
86
+ telemetry.trackStateChange(stateName, undefined, state, 'INITIAL_STATE', metadata);
87
+ }
88
+ }, []);
89
+ // Track state changes
90
+ (0, react_1.useEffect)(() => {
91
+ // Skip tracking on initial mount unless trackInitial is true
92
+ if (isInitialMount.current && !trackInitial) {
93
+ isInitialMount.current = false;
94
+ previousStateRef.current = state;
95
+ return;
96
+ }
97
+ if (isInitialMount.current) {
98
+ isInitialMount.current = false;
99
+ previousStateRef.current = state;
100
+ return;
101
+ }
102
+ const previousValue = previousStateRef.current;
103
+ // Check if we should track this change
104
+ if (shouldTrack && !shouldTrack(previousValue, state)) {
105
+ previousStateRef.current = state;
106
+ return;
107
+ }
108
+ // Track the state change (action type will be included in the reducer action)
109
+ telemetry.trackStateChange(stateName, previousValue, state, undefined, // Action will be determined by the reducer
110
+ metadata);
111
+ previousStateRef.current = state;
112
+ }, [state, stateName, telemetry, metadata, shouldTrack]);
113
+ // Wrapper for dispatch that tracks the action
114
+ const trackedDispatch = (action) => {
115
+ // Track before dispatching
116
+ const previousValue = previousStateRef.current;
117
+ dispatch(action);
118
+ // Note: The actual state change will be tracked in the useEffect above
119
+ // But we can also track the action here for immediate feedback
120
+ if (!shouldTrack || shouldTrack(previousValue, state)) {
121
+ telemetry.trackStateChange(stateName, previousValue, state, // This will be the old state, useEffect will track the new one
122
+ action.type || 'REDUCER_ACTION', {
123
+ ...metadata,
124
+ actionPayload: action.payload,
125
+ ...action
126
+ });
127
+ }
128
+ };
129
+ return [state, trackedDispatch];
130
+ }
131
+ /**
132
+ * Hook to manually track state changes with more control
133
+ * Useful when you want to track state changes but don't want to use the tracked hooks
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * const trackState = useStateTracker(telemetryInstance);
138
+ *
139
+ * const handleChange = (newValue) => {
140
+ * trackState('userPreferences', currentValue, newValue, 'UPDATE_PREFERENCES');
141
+ * setCurrentValue(newValue);
142
+ * };
143
+ * ```
144
+ */
145
+ function useStateTracker(telemetry) {
146
+ return (stateName, previousValue, newValue, action, metadata) => {
147
+ telemetry.trackStateChange(stateName, previousValue, newValue, action, metadata);
148
+ };
149
+ }
package/dist/types.d.ts CHANGED
@@ -17,7 +17,7 @@ export interface ErrorFilterConfig {
17
17
  exclude?: string[];
18
18
  }
19
19
  export interface TelemetryEvent {
20
- type: 'login' | 'pair_view' | 'error' | 'request' | 'system_event';
20
+ type: 'login' | 'pair_view' | 'error' | 'request' | 'system_event' | 'state_change';
21
21
  wallet: string | null;
22
22
  platform: string;
23
23
  timestamp: number;
@@ -53,3 +53,10 @@ export interface RequestEvent {
53
53
  export interface BatchIngestRequest {
54
54
  events: TelemetryEvent[];
55
55
  }
56
+ export interface StateChangeEvent {
57
+ stateName: string;
58
+ previousValue?: any;
59
+ newValue?: any;
60
+ action?: string;
61
+ metadata?: object;
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lavarage/telemetry",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Production telemetry SDK for Lavarage and partner applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -10,29 +10,56 @@
10
10
  "node-fetch": false
11
11
  },
12
12
  "scripts": {
13
- "build": "tsc",
13
+ "build": "tsc && tsc --project tsconfig.react.json",
14
+ "build:main": "tsc",
15
+ "build:react": "tsc --project tsconfig.react.json",
14
16
  "dev": "tsc --watch",
15
17
  "prepublishOnly": "npm run build",
16
18
  "test": "jest"
17
19
  },
18
- "keywords": ["telemetry", "analytics", "monitoring", "lavarage"],
20
+ "keywords": [
21
+ "telemetry",
22
+ "analytics",
23
+ "monitoring",
24
+ "lavarage"
25
+ ],
19
26
  "author": "Lavarage",
20
27
  "license": "MIT",
21
28
  "peerDependencies": {
22
- "node-fetch": "^2.6.0 || ^3.0.0"
29
+ "node-fetch": "^2.6.0 || ^3.0.0",
30
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
23
31
  },
24
32
  "peerDependenciesMeta": {
25
33
  "node-fetch": {
26
34
  "optional": true
35
+ },
36
+ "react": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "exports": {
41
+ ".": {
42
+ "import": "./dist/index.js",
43
+ "require": "./dist/index.js",
44
+ "types": "./dist/index.d.ts"
45
+ },
46
+ "./react": {
47
+ "import": "./dist/react.js",
48
+ "require": "./dist/react.js",
49
+ "types": "./dist/react.d.ts"
27
50
  }
28
51
  },
29
52
  "devDependencies": {
53
+ "@types/jest": "^29.0.0",
30
54
  "@types/node": "^20.0.0",
31
- "typescript": "^5.0.0",
55
+ "@types/react": "^19.2.8",
56
+ "@types/react-dom": "^19.2.3",
32
57
  "jest": "^29.0.0",
33
- "@types/jest": "^29.0.0",
34
- "ts-jest": "^29.0.0"
58
+ "ts-jest": "^29.0.0",
59
+ "typescript": "^5.0.0"
35
60
  },
36
- "files": ["dist", "README.md"]
61
+ "files": [
62
+ "dist",
63
+ "README.md"
64
+ ]
37
65
  }
38
-