# Browser Event Loop Lag Monitor

A lightweight, TypeScript-first utility for monitoring JavaScript event loop lag in real-time. Perfect for performance monitoring, debugging, and alerting on event loop blocking in both browser and Node.js environments.

[![npm version](https://badge.fury.io/js/lag-monitor.svg)](https://badge.fury.io/js/lag-monitor)
[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 🚀 **Lightweight**: Minimal overhead with efficient circular buffer implementation
- 📊 **Real-time Monitoring**: Track event loop lag as it happens
- 🎯 **Multiple Metrics**: Built-in support for latest, average, min, max, and custom metrics
- 🔧 **Flexible Configuration**: Customizable sample rates, buffer sizes, and measurement methods
- 🌐 **Universal**: Works in browsers and Node.js environments
- 📝 **TypeScript First**: Full type safety with comprehensive TypeScript definitions
- 🧪 **Well Tested**: Comprehensive test suite with high coverage
- 📈 **Custom Metrics**: Define your own metrics for specialized monitoring needs

## Installation

```bash
npm install lag-monitor
```

## Quick Start

```typescript
import LagMonitor from 'lag-monitor';

// Basic usage with default settings
const monitor = new LagMonitor();

// Get current lag metrics
console.log(monitor.snapshot());
// Output: { latest: 2.1, average: 1.8, min: 0.5, max: 5.2, samples: 10 }

// Stop monitoring when done
monitor.stop();
```

## API Reference

### Constructor

```typescript
new LagMonitor(options?: LagMonitorOptions)
```

#### Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `lagType` | `'setTimeout' \| 'setInterval' \| 'requestAnimationFrame'` | `'setTimeout'` | Method used for scheduling measurements |
| `sampleRate` | `number` | `100` | Interval between measurements in milliseconds |
| `sampleCount` | `number` | `10` | Number of samples to keep for calculations |
| `callback` | `LagCallback` | `undefined` | Optional callback invoked on each measurement |
| `autoStart` | `boolean` | `true` | Whether to start monitoring immediately |
| `userMetrics` | `Record<string, (samples: number[]) => number>` | `{}` | Custom metrics to compute |

### Methods

#### `start(): void`
Start lag monitoring.

#### `stop(): void`
Stop lag monitoring.

#### `restart(): void`
Reset and restart monitoring.

#### `reset(): void`
Clear all collected samples and stop monitoring.

#### `isRunning(): boolean`
Check if monitoring is currently active.

#### `snapshot(metric?: string): LagData | { [key: string]: number }`
Get current lag statistics. If `metric` is specified, returns only that metric.

#### `getAvailableMetrics(): string[]`
Get list of all available metrics (built-in + user-defined).

### Properties

#### `latest: number`
Most recent lag measurement in milliseconds.

#### `average: number`
Average lag over the current sample window.

#### `min: number`
Minimum lag in the current sample window.

#### `max: number`
Maximum lag in the current sample window.

## Usage Examples

### Basic Monitoring

```typescript
import LagMonitor from 'lag-monitor';

const monitor = new LagMonitor({
  sampleRate: 50,    // Check every 50ms
  sampleCount: 20    // Keep last 20 samples
});

// Check lag periodically
setInterval(() => {
  const lag = monitor.latest;
  if (lag > 16) {
    console.warn(`High lag detected: ${lag.toFixed(2)}ms`);
  }
}, 1000);
```

### With Callback

```typescript
const monitor = new LagMonitor({
  callback: (currentLag, lagData) => {
    if (currentLag > 16) {
      console.warn(`Frame drop detected: ${currentLag.toFixed(2)}ms`);
      console.log('Current stats:', lagData);
    }
  }
});
```

### Custom Metrics

```typescript
const monitor = new LagMonitor({
  userMetrics: {
    median: (samples) => {
      const sorted = [...samples].sort((a, b) => a - b);
      const mid = Math.floor(sorted.length / 2);
      return sorted.length % 2 === 0 
        ? (sorted[mid - 1] + sorted[mid]) / 2 
        : sorted[mid];
    },
    p95: (samples) => {
      const sorted = [...samples].sort((a, b) => a - b);
      const index = Math.ceil(sorted.length * 0.95) - 1;
      return sorted[index];
    }
  }
});

console.log(monitor.snapshot());
// Output includes: { latest: 2.1, average: 1.8, median: 1.9, p95: 4.2, ... }
```

### Manual Control

```typescript
const monitor = new LagMonitor({ autoStart: false });

// Start monitoring when needed
monitor.start();

// Get specific metrics
console.log('Current lag:', monitor.latest);
console.log('Average lag:', monitor.average);

// Get all metrics at once (atomic snapshot)
const stats = monitor.snapshot();
console.log('All metrics:', stats);

// Stop when done
monitor.stop();
```

### Performance Dashboard

```typescript
class PerformanceDashboard {
  private monitor: LagMonitor;
  private alertThreshold = 16; // 60fps threshold

  constructor() {
    this.monitor = new LagMonitor({
      sampleRate: 16, // Check every frame at 60fps
      callback: this.onLagMeasurement.bind(this)
    });
  }

  private onLagMeasurement(lag: number, data: any) {
    this.updateUI(data);
    
    if (lag > this.alertThreshold) {
      this.triggerAlert(lag);
    }
  }

  private updateUI(data: any) {
    // Update your dashboard UI
    document.getElementById('current-lag').textContent = `${data.latest.toFixed(1)}ms`;
    document.getElementById('avg-lag').textContent = `${data.average.toFixed(1)}ms`;
    document.getElementById('max-lag').textContent = `${data.max.toFixed(1)}ms`;
  }

  private triggerAlert(lag: number) {
    console.warn(`Performance alert: ${lag.toFixed(2)}ms lag detected`);
    // Send to monitoring service, show notification, etc.
  }
}
```

## Browser vs Node.js

The library works in both environments with some considerations:

### Browser
- All lag types supported: `setTimeout`, `setInterval`, `requestAnimationFrame`
- Uses `performance.now()` for high-precision timing
- Ideal for monitoring UI responsiveness

### Node.js
- Supports `setTimeout` and `setInterval`
- `requestAnimationFrame` will throw an error (not available)
- Uses `performance.now()` for timing
- Great for monitoring server-side event loop health

```typescript
// Node.js specific configuration
const monitor = new LagMonitor({
  lagType: 'setTimeout', // Avoid requestAnimationFrame
  sampleRate: 100
});
```

## Performance Considerations

- **Minimal Overhead**: The monitor itself adds minimal overhead (~0.1ms per measurement)
- **Efficient Storage**: Uses circular buffer to maintain constant memory usage
- **Configurable Impact**: Adjust `sampleRate` to balance accuracy vs. performance
- **Smart Scheduling**: Different lag types for different use cases:
  - `setTimeout`: General purpose, works everywhere
  - `setInterval`: More consistent timing, good for steady monitoring
  - `requestAnimationFrame`: Browser only, synced with display refresh

## TypeScript Support

Full TypeScript support with comprehensive type definitions:

```typescript
import LagMonitor, { LagMonitorOptions, LagData, LagCallback } from 'lag-monitor';

const options: LagMonitorOptions = {
  sampleRate: 100,
  callback: (lag: number, data: LagData) => {
    console.log(`Lag: ${lag}ms`, data);
  }
};

const monitor = new LagMonitor(options);
```

## Contributing

Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes.

## Support

- 📖 [Documentation](https://github.com/yourusername/lag-monitor#readme)
- 🐛 [Issue Tracker](https://github.com/yourusername/lag-monitor/issues)
- 💬 [Discussions](https://github.com/yourusername/lag-monitor/discussions)

---

Made with ❤️ for the JavaScript performance monitoring community.
