@oxog/emitter
v1.1.0
Published
Type-safe event emitter with wildcard support, async emit, and configurable error handling
Maintainers
Readme
@oxog/emitter
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 withemitParallel() - 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
npm install @oxog/emitterQuick Start
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
// Factory function
const emitter = createEmitter<MyEvents>(options?);
// Or extend the class
class MyService extends Emitter<MyEvents> {
doSomething() {
this.emit('message', 'Done!');
}
}Subscribing to Events
// 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
// 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
// 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
// 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
emitter.listenerCount('message'); // Number of listeners
emitter.listeners('message'); // Array of handlers
emitter.eventNames(); // All event names with listeners
emitter.hasListeners('message'); // Boolean checkConfiguration
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
// 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
import type {
EventMap,
EventHandler,
Unsubscribe,
MaybePromise,
EmitterOptions,
EmitterInstance,
EmitterLogger,
ErrorHandling,
Handler,
WildcardHandler,
PatternHandler,
} from '@oxog/emitter';Examples
See the examples directory for complete usage examples:
- Basic Emitter
- Typed Events
- Subscribe Once
- Wildcard All
- Wildcard Pattern
- Async Emit
- Error Handling
- Max Listeners
- Listener Inspection
- Prepend Listeners
- Extend Class
- Debug Mode
- Cleanup Patterns
- Configuration
- 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ç
