@microstack-dev/ms-events
v1.0.0
Published
Ultra type-safe, zero-dependency event emitter for TS
Maintainers
Readme
ms-events
Ultra type-safe, zero-dependency event emitter for TypeScript. Supports CJS, ESM, browser, Bun, Deno, and Node.js. A production-ready alternative to Node.js EventEmitter with advanced features.
✨ Features
- 🔒 Type-Safe: Full TypeScript inference for event handlers
- 📦 Zero Dependencies: Pure TypeScript, no runtime dependencies
- ⚡ High Performance: Optimized for speed with fast paths and pooling
- 🔄 Async Support:
emitAsync,emitAsyncSerial,emitAsyncParallel - 🌟 Wildcards: Subscribe to all events with
* - 🏷️ Namespaces: Scoped emitters with
scope()and prefix matching - ⏱️ Rate Limiting: Built-in throttling and debouncing
- 📼 Buffering: Replay events when listeners are added
- 🛑 AbortSignal: Auto-cleanup with AbortController
- 🔑 Symbols: Private symbol-based events
- 🐛 Debugging: Comprehensive logging and tracing
- 🧵 Microtask/Tick:
emitNextTick,emitMicrotask
📦 Installation
npm install ms-eventsyarn add ms-eventspnpm add ms-events🚀 Quick Start
import { EventEmitter } from 'ms-events';
type Events = {
ready: [];
message: [string, number];
error: [Error];
};
const emitter = new EventEmitter<Events>();
// Type-safe listeners
emitter.on('message', (text, count) => {
console.log(text, count);
});
emitter.emit('message', 'hello', 42); // ✅ OK
// emitter.emit('message', 'hello', 'world'); // ❌ TypeScript error📚 Examples
Async Events
emitter.on('ready', async () => {
await fetchData();
console.log('Ready!');
});
await emitter.emitAsync('ready');Wildcards
emitter.on('*', (event, ...args) => {
console.log(`Event ${String(event)} fired with:`, args);
});
emitter.emit('message', 'hi', 1);
// Output: Event message fired with: ['hi', 1]Namespaces
const userEmitter = emitter.scope('user');
userEmitter.on('login:*', (event, ...args) => {
console.log(`User event: ${String(event)}`);
});
userEmitter.emit('login:success', 'alice');Throttling
emitter.onThrottled('scroll', (position) => {
console.log('Throttled scroll:', position);
}, { delay: 100 });AbortSignal Cleanup
const controller = new AbortController();
emitter.on('update', handler, { signal: controller.signal });
controller.abort(); // Auto-removes listenerError Handling
emitter.on('error', (error) => {
console.error('Emitter error:', error);
});
emitter.on('fail', () => {
throw new Error('Something went wrong');
});
emitter.emit('fail'); // Caught by error handler🔧 API Reference
Constructor
const emitter = new EventEmitter<Events>(options?);Options:
maxListeners?: number- Max listeners per eventstrictMaxListeners?: boolean- Throw on limit exceededdebug?: DebugOptions- Enable loggingbuffer?: BufferOptions- Enable event buffering
Core Methods
on(event, handler, options?)
Add a listener. Returns cleanup function.
const cleanup = emitter.on('event', handler, { signal: abortSignal });
cleanup(); // Remove listeneronce(event, handler, options?)
Add one-time listener.
emit(event, ...args)
Emit event synchronously.
emitAsync(event, ...args)
Emit event asynchronously, waiting for all listeners.
emitAsyncSerial(event, ...args)
Emit serially (one after another).
emitAsyncParallel(event, ...args)
Emit in parallel (default for emitAsync).
off(event, handler)
Remove specific listener.
Advanced Methods
Listeners
prependListener(event, handler)- Add to front of queueappendListener(event, handler)- Add to end (same ason)prependOnceListener(event, handler)- Prepend one-timeonThrottled(event, handler, options)- Throttled listeneronDebounced(event, handler, options)- Debounced listener
Inspection
listeners(event)- Get listeners (defensive copy)rawListeners(event)- Get listeners (mutable)listenerCount(event)- Count listenershasListeners(event)- Check if has listenerseventNames()- Get all event names
Control
setMaxListeners(n)- Set global maxsetMaxListenersPerEvent(event, n)- Per-event maxsetStrictMaxListeners(strict)- Throw on exceedclear(event?)- Remove all listenersremoveAllListeners(event?)- Alias forclear
Scoping
scope(namespace)- Create scoped emitter
Timing
emitNextTick(event, ...args)- Emit on next tickemitMicrotask(event, ...args)- Emit on microtask
Debugging
setDebug(options)- Enable loggingsetBuffering(options)- Enable buffering
⚡ Performance
- Fast Path: Optimized for single listener
- Minimal Allocations: Reuse objects where possible
- Defensive Copies: Only when necessary
- Zero GC Pressure: For high-frequency events
🔍 Comparison
| Feature | Node.js EventEmitter | ms-events | |---------|---------------------|-----------| | Type Safety | ❌ | ✅ Full inference | | Zero Runtime Deps | ❌ | ✅ | | Async Emit | ❌ | ✅ | | Wildcards | ❌ | ✅ | | Namespaces | ❌ | ✅ | | Throttling | ❌ | ✅ | | Debouncing | ❌ | ✅ | | Symbols | ❌ | ✅ | | AbortSignal | ❌ | ✅ | | Buffering | ❌ | ✅ | | Performance | Good | Optimized |
🛠️ Development
# Install deps
npm install
# Build
npm run build
# Test
npm test
# Type check
npm run typecheck
# Lint
npm run lint📄 License
MIT © microstack-dev
🤝 Contributing
PRs welcome! Please read our contributing guide.
