npm package: @oxog/emitter
Description: Type-safe event emitter with wildcard support, async emit, and configurable error handling
Author: Ersin Koç
Latest version: 1.1.0

# @oxog/emitter

[![npm version](https://img.shields.io/npm/v/@oxog/emitter.svg)](https://www.npmjs.com/package/@oxog/emitter)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)

Type-safe event emitter with wildcard support, async emit, and configurable error handling.

## Features

- **Full TypeScript Support** - Complete type safety for events and payloads
- **Wildcard Events** - Listen to all events (`*`) or patterns (`user:*`)
- **Async Emit** - Sequential with `emitAsync()` or parallel with `emitParallel()`
- **Configurable Error Handling** - emit, throw, or silent strategies
- **Memory Leak Detection** - Warns when exceeding max listeners
- **Node.js Compatible** - Standard aliases (`addListener`, `removeListener`, `removeAllListeners`)
- **High Performance** - O(1) pattern matching, zero-allocation hot paths
- **Small Bundle** - < 3KB gzipped
- **Universal** - Works in Node.js and browsers

## Installation

```bash
npm install @oxog/emitter
```

## Quick Start

```typescript
import { createEmitter } from '@oxog/emitter';

// Define your events
interface MyEvents {
  'user:login': { userId: string; timestamp: number };
  'user:logout': { userId: string };
  'message': string;
  'error': Error;
}

// Create typed emitter
const emitter = createEmitter<MyEvents>();

// Subscribe to events
emitter.on('user:login', (payload) => {
  console.log(`User ${payload.userId} logged in`);
});

// Emit events
emitter.emit('user:login', { userId: 'user_123', timestamp: Date.now() });
```

## API Reference

### Creating an Emitter

```typescript
// Factory function
const emitter = createEmitter<MyEvents>(options?);

// Or extend the class
class MyService extends Emitter<MyEvents> {
  doSomething() {
    this.emit('message', 'Done!');
  }
}
```

### Subscribing to Events

```typescript
// Basic subscription (returns unsubscribe function)
const unsubscribe = emitter.on('message', (payload) => {
  console.log(payload);
});
unsubscribe(); // Remove subscription

// Node.js style alias
emitter.addListener('message', handler);

// Subscribe once (auto-unsubscribes after first emit)
emitter.once('message', (payload) => {
  console.log('First message:', payload);
});

// Prepend listener (runs before ALL other handlers, including wildcards)
emitter.prependListener('message', (payload) => {
  console.log('Runs first');
});
```

### Wildcard Events

```typescript
// Listen to ALL events
emitter.on('*', (eventName, payload) => {
  console.log(`[${eventName}]`, payload);
});

// Listen to events matching pattern
emitter.on('user:*', (eventName, payload) => {
  console.log(`User event: ${eventName}`, payload);
});
```

### Emitting Events

```typescript
// Sync emit (fire and forget)
emitter.emit('message', 'Hello!');

// Async emit - sequential (handlers run one after another)
const results = await emitter.emitAsync('message', 'Hello!');

// Async emit - parallel (all handlers run concurrently)
// 10 handlers × 100ms = ~100ms total (vs 1000ms with emitAsync)
const results = await emitter.emitParallel('message', 'Hello!');
```

### Removing Handlers

```typescript
// Remove specific handler
emitter.off('message', handler);
// or Node.js style:
emitter.removeListener('message', handler);

// Remove all handlers for event
emitter.offAll('message');
// or Node.js style:
emitter.removeAllListeners('message');

// Remove all handlers
emitter.clear();
// or:
emitter.removeAllListeners();
```

### Inspecting Listeners

```typescript
emitter.listenerCount('message');  // Number of listeners
emitter.listeners('message');      // Array of handlers
emitter.eventNames();              // All event names with listeners
emitter.hasListeners('message');   // Boolean check
```

### Configuration

```typescript
const emitter = createEmitter<MyEvents>({
  // Error handling: 'emit' (default), 'throw', or 'silent'
  errorHandling: 'emit',

  // Custom error handler
  onError: (error, eventName) => {
    console.error(`Error in ${eventName}:`, error);
  },

  // Max listeners before warning (default: 10, 0 = disabled)
  maxListeners: 10,

  // Enable debug logging
  debug: false,

  // Custom logger
  logger: console,
});

// Runtime configuration
emitter.setMaxListeners(50);
emitter.setDebug(true);
```

## Error Handling

```typescript
// Strategy 1: 'emit' (default) - errors emitted to 'error' event
const emitter1 = createEmitter({ errorHandling: 'emit' });
emitter1.on('error', (error) => console.error(error));

// Strategy 2: 'throw' - errors re-thrown
const emitter2 = createEmitter({ errorHandling: 'throw' });
try {
  emitter2.emit('event', data);
} catch (error) {
  console.error(error);
}

// Strategy 3: 'silent' - errors ignored
const emitter3 = createEmitter({ errorHandling: 'silent' });

// Strategy 4: Custom handler
const emitter4 = createEmitter({
  onError: (error, eventName) => {
    reportToSentry(error, { eventName });
  },
});
```

## TypeScript Types

```typescript
import type {
  EventMap,
  EventHandler,
  Unsubscribe,
  MaybePromise,
  EmitterOptions,
  EmitterInstance,
  EmitterLogger,
  ErrorHandling,
  Handler,
  WildcardHandler,
  PatternHandler,
} from '@oxog/emitter';
```

## Examples

See the [examples](./examples) directory for complete usage examples:

1. [Basic Emitter](./examples/01-basic-emitter)
2. [Typed Events](./examples/02-typed-events)
3. [Subscribe Once](./examples/03-subscribe-once)
4. [Wildcard All](./examples/04-wildcard-all)
5. [Wildcard Pattern](./examples/05-wildcard-pattern)
6. [Async Emit](./examples/06-async-emit)
7. [Error Handling](./examples/07-error-handling)
8. [Max Listeners](./examples/08-max-listeners)
9. [Listener Inspection](./examples/09-listener-inspection)
10. [Prepend Listeners](./examples/10-prepend-listeners)
11. [Extend Class](./examples/11-extend-class)
12. [Debug Mode](./examples/12-debug-mode)
13. [Cleanup Patterns](./examples/13-cleanup-patterns)
14. [Configuration](./examples/14-configuration)
15. [Real-World Chat](./examples/15-real-world-chat)

## Comparison

| Feature | @oxog/emitter | EventEmitter3 | mitt | nanoevents |
|---------|---------------|---------------|------|------------|
| TypeScript | Full | Partial | Full | Full |
| Wildcards | `*`, `prefix:*` | No | `*` only | No |
| Async emit | Sequential + Parallel | No | No | No |
| Error handling | Configurable | No | No | No |
| Max listeners | Warning | No | No | No |
| Once | Yes | Yes | No | Yes |
| Prepend | Global priority | Yes | No | No |
| Node.js compat | Full | Full | No | No |
| Size | ~3KB | ~1KB | ~200B | ~200B |

## License

MIT © [Ersin Koç](https://github.com/ersinkoc)

## Links

- [Documentation](https://emitter.oxog.dev)
- [GitHub](https://github.com/ersinkoc/emitter)
- [npm](https://www.npmjs.com/package/@oxog/emitter)