@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 +209 -6
- package/dist/index.d.ts +17 -1
- package/dist/index.js +105 -1
- package/dist/react.d.ts +73 -0
- package/dist/react.js +149 -0
- package/dist/types.d.ts +8 -1
- package/package.json +36 -9
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
|
-
|
|
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
|
-
|
|
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') {
|
package/dist/react.d.ts
ADDED
|
@@ -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
|
|
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": [
|
|
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
|
-
"
|
|
55
|
+
"@types/react": "^19.2.8",
|
|
56
|
+
"@types/react-dom": "^19.2.3",
|
|
32
57
|
"jest": "^29.0.0",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
58
|
+
"ts-jest": "^29.0.0",
|
|
59
|
+
"typescript": "^5.0.0"
|
|
35
60
|
},
|
|
36
|
-
"files": [
|
|
61
|
+
"files": [
|
|
62
|
+
"dist",
|
|
63
|
+
"README.md"
|
|
64
|
+
]
|
|
37
65
|
}
|
|
38
|
-
|