npm package: @lavarage/telemetry
Description: Production telemetry SDK for Lavarage and partner applications
Author: Lavarage
Latest version: 1.2.2

# @lavarage/telemetry

Production telemetry SDK for Lavarage and partner applications. Track user activity, network requests, and errors with comprehensive filtering and privacy controls.

## Installation

```bash
npm install @lavarage/telemetry
```

## Quick Start

```typescript
import { LavarageTelemetry } from '@lavarage/telemetry';

const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
});

// Intercept network requests
telemetry.interceptFetch();

// Connect wallet (triggers login event)
// Wallet platform is automatically detected (MetaMask, Phantom, etc.)
// or you can specify it manually:
telemetry.setWallet('YourWalletAddress123...', 'MetaMask');

// Track trading pair views
telemetry.trackPairView('SOL/USDC', { source: 'homepage' });
```

## Configuration

### Basic Configuration

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: string;        // Required: Backend API endpoint
  platform: string;           // Required: Platform identifier (e.g., 'lavarage-web')
  captureHosts?: HostFilterInput;  // Optional: Host filtering configuration
  errorFilters?: ErrorFilterConfig; // Optional: Error filtering configuration
  batchSize?: number;         // Optional: Max events per batch (default: 50)
  flushInterval?: number;     // Optional: Flush interval in ms (default: 5000)
});
```

## Host Filtering

Control which network requests are captured using flexible host filtering.

### String Configuration

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
  captureHosts: 'api.lavarage.com', // Only capture requests to this host
});
```

### Array Configuration

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
  captureHosts: [
    'api.lavarage.com',
    'partner-api.com',
    '*.example.com' // Wildcard: matches any subdomain
  ],
});
```

### Object Configuration

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
  captureHosts: {
    mode: 'include', // 'all' | 'none' | 'include' | 'exclude'
    hosts: ['api.lavarage.com', '*.partner.com'],
    patterns: ['^api\\..*\\.lavarage\\.com$'] // Regex patterns
  },
});
```

### Wildcard Matching

- `*.example.com` - Matches any subdomain (e.g., `api.example.com`, `www.example.com`)
- `example.com` - Matches the domain and all subdomains

### Update Host Filter at Runtime

```typescript
telemetry.updateHostFilter({
  mode: 'exclude',
  hosts: ['analytics.google.com', '*.tracking.com']
});
```

## Error Filtering

Filter which errors are captured using include/exclude patterns.

### Example 1: Only Capture Specific Errors

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
  errorFilters: {
    include: [
      'Failed to fetch',
      'Network error',
      'Transaction.*failed',
      'RPC.*error'
    ]
  }
});
```

### Example 2: Exclude Noisy Errors

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
  errorFilters: {
    exclude: [
      'ResizeObserver',
      'Extension context invalidated',
      'Script error',
      'Non-Error promise rejection'
    ]
  }
});
```

### Example 3: Combined Filtering

```typescript
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
  errorFilters: {
    include: ['.*'], // Capture everything
    exclude: [
      'chrome-extension://',
      'moz-extension://',
      'webkit-masked-url://',
      'ResizeObserver loop',
      'Script error\\.'
    ]
  }
});
```

**Filter Logic:**
- If `include` patterns are provided, ONLY errors matching those patterns are captured
- If `exclude` patterns are provided, all errors EXCEPT those matching patterns are captured
- If both are provided, `include` is applied first, then `exclude`

## Axios Integration

If you're using Axios, you can intercept Axios requests:

```typescript
import axios from 'axios';
import { LavarageTelemetry } from '@lavarage/telemetry';

const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
});

const axiosInstance = axios.create({
  baseURL: 'https://api.lavarage.com',
});

// Intercept Axios requests
telemetry.interceptAxios(axiosInstance);
```

## Security Features

### Automatic Data Sanitization

The SDK automatically sanitizes sensitive data in all payloads and responses:

- `privateKey`
- `mnemonic`
- `password`
- `secret`
- `token`
- `apiKey`
- `authorization`

Sensitive fields are replaced with `[REDACTED]` recursively in nested objects and arrays.

### Silent Error Handling

All telemetry operations are wrapped in try-catch blocks. Telemetry errors will never disrupt your application.

## API Reference

### Methods

#### `setWallet(walletAddress: string, walletPlatform?: string)`

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.

**Supported auto-detected platforms:**
- MetaMask
- Coinbase Wallet
- Trust Wallet
- WalletConnect
- Phantom (Solana)
- Solflare (Solana)
- Generic Ethereum/Solana providers

```typescript
// Auto-detect wallet platform
telemetry.setWallet('YourWalletAddress123...');

// Or specify manually
telemetry.setWallet('YourWalletAddress123...', 'MetaMask');
```

#### `interceptFetch()`

Intercept the global `fetch` API to capture network requests.

```typescript
telemetry.interceptFetch();
```

#### `interceptAxios(axiosInstance: any)`

Intercept an Axios instance to capture network requests.

```typescript
telemetry.interceptAxios(axiosInstance);
```

#### `trackPairView(pair: string, metadata?: object)`

Track a trading pair view event.

```typescript
telemetry.trackPairView('SOL/USDC', {
  source: 'homepage',
  category: 'trending'
});
```

#### `trackSystemEvent(category: string, message: string, level?: string, metadata?: object)`

Track a system event (not tied to any wallet address). System events are displayed in a separate panel in the dashboard.

```typescript
// Track app startup
telemetry.trackSystemEvent('app_start', 'Application initialized', 'info');
```

#### `trackStateChange(stateName: string, previousValue?: any, newValue?: any, action?: string, metadata?: object)`

Track a React state change. Useful for debugging and understanding user interactions.

```typescript
// Track a state change manually
telemetry.trackStateChange(
  'userPreferences',
  { theme: 'light' },
  { theme: 'dark' },
  'TOGGLE_THEME',
  { source: 'settings-panel' }
);
```

// Track feature usage
telemetry.trackSystemEvent('feature_used', 'User enabled dark mode', 'info', {
  feature: 'dark_mode',
  version: '1.2.3'
});

// Track configuration changes
telemetry.trackSystemEvent('config_change', 'API endpoint updated', 'warning', {
  old_endpoint: 'https://api.example.com',
  new_endpoint: 'https://api2.example.com'
});

// Track errors
telemetry.trackSystemEvent('system_error', 'Failed to load configuration', 'error', {
  error_code: 'CONFIG_LOAD_FAILED'
});
```

**Parameters:**
- `category` - Event category (e.g., 'app_start', 'feature_used', 'config_change')
- `message` - Event message
- `level` - Event level: 'info' | 'warning' | 'error' | 'debug' (default: 'info')
- `metadata` - Optional additional metadata object

#### `updateHostFilter(captureHosts: HostFilterInput)`

Update the host filter configuration at runtime.

```typescript
telemetry.updateHostFilter({
  mode: 'exclude',
  hosts: ['analytics.google.com']
});
```

#### `logWalletEvent(walletAddress: string, eventType: string, message: string, metadata?: object)`

Log a custom event with wallet address (backend-friendly method). Events are queued and sent in batches.

```typescript
telemetry.logWalletEvent(
  'WalletAddress123...',
  'transaction',
  'Transaction completed',
  { txHash: '0x...', amount: '100' }
);
```

#### `sendLog(walletAddress: string, eventType: string, message: string, metadata?: object): Promise<void>`

Send a log event immediately, bypassing the batch queue. Useful for critical logs.

```typescript
await telemetry.sendLog(
  'WalletAddress123...',
  'critical',
  'Security alert',
  { alertType: 'suspicious_activity' }
);
```

#### `destroy()`

Clean up the telemetry instance, restore original functions, and flush remaining events.

```typescript
telemetry.destroy();
```

## React State Tracking

The SDK provides React hooks for automatically tracking state changes in your React components.

### Installation

```bash
# Install the SDK
npm install @lavarage/telemetry

# If you want to use React hooks, also install React (peer dependency)
npm install react
```

### Quick Start

```typescript
import { LavarageTelemetry } from '@lavarage/telemetry';
// Import React hooks from the separate entry point
import { useTrackedState } from '@lavarage/telemetry/react';

const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'lavarage-web',
});

function MyComponent() {
  // Automatically tracks all state changes
  const [count, setCount] = useTrackedState(0, {
    stateName: 'counter',
    telemetry: telemetry,
    action: 'SET_COUNT'
  });

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}
```

### Available Hooks

#### `useTrackedState(initialState, config)`

Wraps React's `useState` and automatically tracks state changes.

```typescript
const [state, setState] = useTrackedState(initialValue, {
  stateName: 'userPreferences',  // Required: name for this state
  telemetry: telemetryInstance,   // Required: telemetry instance
  trackInitial: false,             // Optional: track initial state (default: false)
  action: 'UPDATE_PREFERENCES',    // Optional: action name
  metadata: { source: 'settings' }, // Optional: additional metadata
  shouldTrack: (prev, next) => {   // Optional: filter which changes to track
    return prev !== next;          // Only track if values actually changed
  }
});
```

#### `useTrackedReducer(reducer, initialState, config)`

Wraps React's `useReducer` and automatically tracks state changes with action information.

```typescript
const [state, dispatch] = useTrackedReducer(reducer, initialState, {
  stateName: 'cart',
  telemetry: telemetryInstance,
  trackInitial: false,
  metadata: { userId: '123' }
});

// Dispatch actions normally - they'll be tracked automatically
dispatch({ type: 'ADD_ITEM', payload: { id: 1, name: 'Product' } });
```

#### `useStateTracker(telemetry)`

Returns a function to manually track state changes with more control.

```typescript
const trackState = useStateTracker(telemetry);

const handleChange = (newValue) => {
  trackState('theme', currentValue, newValue, 'SET_THEME', {
    source: 'user-action'
  });
  setCurrentValue(newValue);
};
```

### Configuration Options

- **stateName** (required): A unique identifier for this state (e.g., 'cart', 'userPreferences', 'theme')
- **telemetry** (required): Your telemetry instance
- **trackInitial** (optional): Whether to track the initial state value (default: false)
- **action** (optional): Action name to use when tracking (default: 'STATE_CHANGE')
- **metadata** (optional): Additional context to include with each state change
- **shouldTrack** (optional): Function to filter which changes to track - useful for avoiding noise

### Example: Shopping Cart

```typescript
import { useTrackedState } from '@lavarage/telemetry/react';

function ShoppingCart() {
  const [items, setItems] = useTrackedState([], {
    stateName: 'cartItems',
    telemetry: telemetry,
    action: 'CART_UPDATE',
    shouldTrack: (prev, next) => {
      // Only track if cart actually changed (not just reference)
      return JSON.stringify(prev) !== JSON.stringify(next);
    }
  });

  const addItem = (item) => {
    setItems([...items, item]);
    // State change is automatically tracked!
  };

  return (
    <div>
      {items.map(item => <div key={item.id}>{item.name}</div>)}
      <button onClick={() => addItem({ id: 1, name: 'Product' })}>
        Add Item
      </button>
    </div>
  );
}
```

### Example: User Preferences with useReducer

```typescript
import { useTrackedReducer } from '@lavarage/telemetry/react';

function preferencesReducer(state, action) {
  switch (action.type) {
    case 'SET_THEME':
      return { ...state, theme: action.payload };
    case 'SET_LANGUAGE':
      return { ...state, language: action.payload };
    default:
      return state;
  }
}

function UserPreferences() {
  const [prefs, dispatch] = useTrackedReducer(
    preferencesReducer,
    { theme: 'light', language: 'en' },
    {
      stateName: 'userPreferences',
      telemetry: telemetry,
      metadata: { userId: currentUser.id }
    }
  );

  return (
    <div>
      <button onClick={() => dispatch({ type: 'SET_THEME', payload: 'dark' })}>
        Dark Mode
      </button>
      {/* State changes are automatically tracked with action type! */}
    </div>
  );
}
```

## Event Types

The SDK tracks the following event types:

- **login**: Triggered when `setWallet()` is called
- **pair_view**: Trading pair view events
- **error**: Console errors, uncaught exceptions, and unhandled promise rejections
- **request**: Network requests (fetch/Axios)
- **system_event**: System-level events not tied to any wallet address (displayed in separate dashboard panel)
- **log**: Custom log events (via `logWalletEvent()` or `sendLog()`)
- **state_change**: React state changes (via `trackStateChange()` or React hooks)

## Batching

Events are automatically batched and sent to the backend:

- **Default batch size**: 50 events
- **Default flush interval**: 5 seconds
- **Automatic flush**: On page unload (using `keepalive` for reliable delivery)

## Backend Usage

The SDK works in both browser and Node.js environments. For backend applications, you can use a subset of features focused on manual logging.

### Installation for Backend

```bash
npm install @lavarage/telemetry
# For Node.js < 18, also install node-fetch:
npm install node-fetch@2
```

### Backend Quick Start

```typescript
import { LavarageTelemetry } from '@lavarage/telemetry';

// Initialize telemetry for backend
const telemetry = new LavarageTelemetry({
  apiEndpoint: 'https://telemetry.lavarage.com',
  platform: 'my-backend-service',
  batchSize: 100, // Larger batches for backend
  flushInterval: 10000, // Flush every 10 seconds
});

// Log events related to a wallet address
telemetry.logWalletEvent(
  'DummyWallet123456789',
  'transaction',
  'Transaction completed successfully',
  {
    txHash: '0x123...',
    amount: '100.5',
    token: 'USDC'
  }
);

// Log errors with wallet context
telemetry.logWalletEvent(
  'DummyWallet123456789',
  'error',
  'Failed to process transaction',
  {
    errorCode: 'INSUFFICIENT_FUNDS',
    attemptedAmount: '200.0'
  }
);

// Send critical logs immediately (bypasses batching)
await telemetry.sendLog(
  'DummyWallet123456789',
  'critical',
  'Security alert: suspicious activity detected',
  {
    alertType: 'unusual_pattern',
    severity: 'high'
  }
);
```

### Backend API Methods

#### `logWalletEvent(walletAddress: string, eventType: string, message: string, metadata?: object)`

Queue a log event with a wallet address. Events are batched and sent automatically.

```typescript
telemetry.logWalletEvent(
  'WalletAddress123...',
  'transaction', // Event type (e.g., 'transaction', 'error', 'action')
  'Transaction completed', // Log message
  { txHash: '0x...', amount: '100' } // Optional metadata
);
```

#### `sendLog(walletAddress: string, eventType: string, message: string, metadata?: object): Promise<void>`

Send a log event immediately, bypassing the batch queue. Useful for critical logs that need immediate delivery.

```typescript
await telemetry.sendLog(
  'WalletAddress123...',
  'critical',
  'Security alert',
  { alertType: 'suspicious_activity' }
);
```

#### `setWallet(walletAddress: string, walletPlatform?: string)`

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.

```typescript
telemetry.setWallet('WalletAddress123...', 'MetaMask');
// Now all events will use this wallet by default
telemetry.logWalletEvent(
  null, // Will use the wallet set above
  'action',
  'User performed action'
);
```

### Backend Example: Express.js Middleware

```typescript
import express from 'express';
import { LavarageTelemetry } from '@lavarage/telemetry';

const telemetry = new LavarageTelemetry({
  apiEndpoint: process.env.TELEMETRY_ENDPOINT!,
  platform: 'api-server',
});

const app = express();

// Middleware to log wallet-related requests
app.use('/api/wallet/:walletAddress', (req, res, next) => {
  const walletAddress = req.params.walletAddress;
  
  // Log the request
  telemetry.logWalletEvent(
    walletAddress,
    'api_request',
    `${req.method} ${req.path}`,
    {
      method: req.method,
      path: req.path,
      userAgent: req.get('user-agent'),
    }
  );
  
  next();
});

// Log transaction events
app.post('/api/transactions', async (req, res) => {
  const { walletAddress, txHash } = req.body;
  
  try {
    // Process transaction...
    
    telemetry.logWalletEvent(
      walletAddress,
      'transaction',
      'Transaction processed successfully',
      { txHash, status: 'success' }
    );
    
    res.json({ success: true });
  } catch (error) {
    telemetry.logWalletEvent(
      walletAddress,
      'error',
      'Transaction failed',
      { txHash, error: error.message }
    );
    
    res.status(500).json({ error: error.message });
  }
});
```

### Backend Example: Error Handler

```typescript
import { LavarageTelemetry } from '@lavarage/telemetry';

const telemetry = new LavarageTelemetry({
  apiEndpoint: process.env.TELEMETRY_ENDPOINT!,
  platform: 'api-server',
});

// Global error handler
process.on('uncaughtException', (error) => {
  // Extract wallet from error context if available
  const walletAddress = (error as any).walletAddress || null;
  
  if (walletAddress) {
    telemetry.logWalletEvent(
      walletAddress,
      'error',
      `Uncaught exception: ${error.message}`,
      {
        stack: error.stack,
        name: error.name,
      }
    );
  }
});
```

## Browser Support

- Modern browsers with ES2020 support
- Node.js 18+ (native fetch support)
- Node.js < 18 requires `node-fetch` package

## License

MIT