npm package: @statedelta-libs/event-emitter
Description: High-performance EventEmitter with caching, wildcards and priority support
Author: Anderson D. Rosa
Latest version: 0.0.1

# @statedelta-libs/event-emitter

> High-performance EventEmitter with O(1) cached emit for hot paths.

## Philosophy

Emit é hot path. Em loops de tick (60+ fps), rules engines e resource stores, cada `emit()` conta.

**O problema:** EventEmitters tradicionais alocam array a cada emit.

```
Tradicional:  emit() → [...handlers] → allocation → GC pressure
Otimizado:    emit() → cache.get()   → O(1)       → zero allocation
```

**A solução:** Cache lazy com invalidação seletiva.

```
┌────────────────────────────────────────────────────────────────────────────┐
│                           EMIT HOT PATH                                     │
│                                                                             │
│   emit('tick')                                                              │
│       │                                                                     │
│       ▼                                                                     │
│   ┌────────┐     ┌────────────┐     ┌────────────┐                         │
│   │ CACHE  │────▶│  HANDLERS  │────▶│  EXECUTE   │                         │
│   │ LOOKUP │     │   ARRAY    │     │  INLINE    │                         │
│   │  O(1)  │     │  (cached)  │     │  1,2,N     │                         │
│   └────────┘     └────────────┘     └────────────┘                         │
│                                                                             │
│   Cache HIT: Zero allocation                                                │
│   Cache MISS: 1 array (cached for next emit)                                │
└────────────────────────────────────────────────────────────────────────────┘
```

## Installation

```bash
pnpm add @statedelta-libs/event-emitter
```

## Quick Start

```typescript
import { EventEmitter } from '@statedelta-libs/event-emitter';

// Define events
interface MyEvents {
  'tick': () => void;
  'user:login': (userId: string) => void;
  'data': (payload: { id: number }) => void;
}

// Create emitter
const emitter = new EventEmitter<MyEvents>();

// Register handlers
emitter.on('user:login', (userId) => {
  console.log('User logged in:', userId);
});

// Emit events
emitter.emit('user:login', 'user-123');
```

## Features

| Feature | Description | Default | Overhead |
|---------|-------------|---------|----------|
| **Cache** | Zero-allocation emit on cache hit | `true` | ~100 bytes/event |
| **Wildcards** | Pattern matching (`user:*`, `*`) | `false` | O(d) per emit |
| **Priority** | Handler execution order | `false` | Sort on cache miss |
| **Node.js API** | Compatible with EventEmitter | Yes | None |

## Constructor Options

```typescript
const emitter = new EventEmitter({
  cached: true,      // Enable handler caching (default: true)
  wildcards: false,  // Enable wildcard patterns (default: false)
  priority: false,   // Enable priority ordering (default: false)
  maxListeners: 10,  // Max listeners warning threshold (default: 10)
});
```

### Configuration Profiles

```typescript
// Hot path (tick, render loop, rules engine)
const emitter = new EventEmitter({
  cached: true,
  wildcards: false,
  priority: false,
});

// Flexible (plugins, logging, debugging)
const emitter = new EventEmitter({
  cached: true,
  wildcards: true,
  priority: true,
});

// Minimal (many on/off cycles, few emits)
const emitter = new EventEmitter({
  cached: false,
  wildcards: false,
  priority: false,
});
```

## Core API

### on / off / emit

```typescript
// Register handler
emitter.on('event', handler);
emitter.on('event', handler, { priority: 100, once: true, id: 'my-handler' });

// Remove handler
emitter.off('event', handler);   // Remove specific
emitter.off('event');            // Remove all for event

// One-time handler
emitter.once('event', handler);

// Emit event (returns true if had listeners)
const hadListeners = emitter.emit('event', ...args);
```

### Handler Options

```typescript
interface HandlerOptions {
  priority?: number;  // Execution order (higher first, default: 0)
  once?: boolean;     // Auto-remove after first call (default: false)
  id?: string;        // Identifier for debugging
}

emitter.on('tick', handler, {
  priority: 100,
  once: true,
  id: 'my-tick-handler'
});
```

### Inspection

```typescript
emitter.listenerCount('event');     // Number of handlers
emitter.hasListeners('event');      // Boolean check
emitter.eventNames();               // All event names with handlers
emitter.listeners('event');         // Handler functions
emitter.rawListeners('event');      // Handler entries with metadata
```

## Node.js Compatibility

Full compatibility with Node.js EventEmitter API.

```typescript
// Aliases
emitter.addListener('event', handler);     // = on()
emitter.removeListener('event', handler);  // = off()
emitter.removeAllListeners('event');       // Remove all

// Prepend (requires priority: true for effect)
emitter.prependListener('event', handler);
emitter.prependOnceListener('event', handler);

// Max listeners warning
emitter.setMaxListeners(20);
emitter.getMaxListeners();
```

## Caching

Cache eliminates array allocation on emit by storing sorted handler arrays.

```typescript
const emitter = new EventEmitter({ cached: true });

emitter.on('tick', handler1);
emitter.on('tick', handler2);

// First emit: cache MISS (builds cache)
emitter.emit('tick');

// Subsequent emits: cache HIT (zero allocation)
emitter.emit('tick');  // ~2.5M ops/s
emitter.emit('tick');
emitter.emit('tick');
```

### Cache Invalidation

Cache is automatically invalidated on `on()` and `off()` calls.

```typescript
emitter.on('tick', handler);   // Invalidates 'tick' cache
emitter.off('tick', handler);  // Invalidates 'tick' cache

// Other events unaffected
emitter.emit('other');  // Still cache HIT
```

### Cache Stats

```typescript
const stats = emitter.getCacheStats();
// {
//   size: 3,        // Cached event types
//   hits: 1000,     // Cache hits
//   misses: 3,      // Cache misses
//   hitRate: 0.997  // Hit rate (0-1)
// }

// Manual control
emitter.clearCache();
```

## Wildcards

Match events by pattern when wildcards are enabled.

```typescript
const emitter = new EventEmitter({ wildcards: true });

// Prefix wildcard - matches user:login, user:logout, user:profile:update
emitter.on('user:*', (data) => {
  console.log('User event:', data);
});

// Global wildcard - matches ALL events
emitter.onAny((event, ...args) => {
  console.log('Event:', event, args);
});

// Emit triggers exact + wildcards
emitter.emit('user:login', 'user-123');
// Triggers: 'user:login' handlers + 'user:*' handlers + onAny handlers
```

### Pattern Matching

| Pattern | Matches | Storage |
|---------|---------|---------|
| `user:login` | Exact match only | `_listeners` |
| `user:*` | `user:login`, `user:logout`, `user:profile:update` | `_wildcards['user']` |
| `*` | All events | `_anyHandlers` |

### Wildcard API

```typescript
// Register
emitter.on('user:*', handler);  // Prefix wildcard
emitter.onAny(handler);         // Global wildcard

// Remove
emitter.off('user:*', handler);
emitter.offAny(handler);
```

### Wildcard Matching Algorithm

```
emit('user:profile:update', data)
    │
    ├── 1. Exact: _listeners.get('user:profile:update')
    │
    ├── 2. Prefixes (bottom-up):
    │      'user' → _wildcards.get('user')           // user:*
    │      'user:profile' → _wildcards.get('user:profile')  // user:profile:*
    │
    └── 3. Any: _anyHandlers
```

## Priority

Execute handlers in priority order (higher first).

```typescript
const emitter = new EventEmitter({ priority: true });

emitter.on('tick', () => console.log('second'), { priority: 50 });
emitter.on('tick', () => console.log('first'), { priority: 100 });
emitter.on('tick', () => console.log('third'), { priority: 10 });
emitter.on('tick', () => console.log('fourth'));  // priority: 0 (default)

emitter.emit('tick');
// Output: first, second, third, fourth
```

### Priority Semantics

```
Priority 100  → executes first
Priority 50   → executes second
Priority 0    → default
Priority -10  → executes last
```

Sort happens only on cache build (not on every emit).

## Performance

Optimized for hot paths where emit is called frequently.

### Benchmarks (expected)

| Scenario | ops/s |
|----------|-------|
| emit (1 handler, cache hit) | ~3M |
| emit (3 handlers, cache hit) | ~2.5M |
| emit (3 handlers, no cache) | ~1.2M |
| emit (with priority) | ~2.2M |
| emit (with wildcards) | ~1.8M |
| on + off cycle | ~500K |

### Complexity

| Operation | Time | Allocation |
|-----------|------|------------|
| `on()` | O(1) | 1 entry |
| `off()` | O(h) | 0 |
| `emit()` cache hit | O(h) | 0 |
| `emit()` cache miss | O(h log h)* | 1 array |
| `listenerCount()` | O(1) | 0 |

*O(h log h) only if priority enabled (sort)

### Memory

| Component | Size |
|-----------|------|
| Empty EventEmitter | ~200 bytes |
| Per event (no handlers) | ~64 bytes |
| Per handler entry | ~32 bytes |
| Cache per event | ~24 bytes + array |

## TypeScript

Full type inference for events and handlers.

```typescript
interface Events {
  'user:login': (userId: string, timestamp: number) => void;
  'user:logout': (userId: string) => void;
  'error': (error: Error) => void;
}

const emitter = new EventEmitter<Events>();

// Type-safe handlers
emitter.on('user:login', (userId, timestamp) => {
  // userId: string, timestamp: number (inferred)
});

// Type-safe emit
emitter.emit('user:login', 'user-123', Date.now());  // OK
emitter.emit('user:login', 123);  // Error: expected string
```

### Exported Types

```typescript
import type {
  // Core types
  EventMap,
  EventHandler,
  EventParams,

  // Options
  HandlerOptions,
  EventEmitterOptions,

  // Internal
  HandlerEntry,
  CacheStats,
  AnyHandler,

  // Interfaces
  IEventEmitter,
  IFastEventEmitter,
} from '@statedelta-libs/event-emitter';

import {
  EventEmitter,
  createEventEmitter,
} from '@statedelta-libs/event-emitter';
```

## Factory Function

```typescript
import { createEventEmitter } from '@statedelta-libs/event-emitter';

const emitter = createEventEmitter<MyEvents>({
  cached: true,
  wildcards: true,
  priority: true,
});
```

## Integration with StateDelta

```typescript
import { EventEmitter } from '@statedelta-libs/event-emitter';

// TickManager - hot path (60+ fps)
class TickManager {
  private emitter = new EventEmitter<{
    'tick:before': (tick: number) => void;
    'tick:after': (tick: number) => void;
  }>({ cached: true });

  tick() {
    this.emitter.emit('tick:before', this.tickCount);
    // ... process
    this.emitter.emit('tick:after', this.tickCount);
  }
}

// RulesEngine - with wildcards for debugging
class RulesEngine {
  private emitter = new EventEmitter<{
    'rule:matched': (rule: Rule) => void;
    'rule:executed': (rule: Rule, result: any) => void;
  }>({ cached: true, wildcards: true });
}
```

## Scripts

```bash
pnpm build          # Build (ESM + CJS + .d.ts)
pnpm test           # Run tests
pnpm test:watch     # Watch mode
pnpm test:coverage  # Coverage report
pnpm typecheck      # Type checking
pnpm bench          # Benchmarks
```

## License

MIT