From harness-claude
Teaches Node.js EventEmitter for typed pub-sub communication with memory leak prevention patterns.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:node-event-emitterThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Use Node.js EventEmitter for typed pub-sub communication with memory leak prevention
Use Node.js EventEmitter for typed pub-sub communication with memory leak prevention
import { EventEmitter } from 'node:events';
const emitter = new EventEmitter();
emitter.on('user:created', (user: { id: string; name: string }) => {
console.log(`User created: ${user.name}`);
});
emitter.emit('user:created', { id: '1', name: 'Alice' });
import { EventEmitter } from 'node:events';
interface AppEvents {
'user:created': [user: { id: string; name: string }];
'user:deleted': [userId: string];
error: [error: Error];
}
class AppEmitter extends EventEmitter<AppEvents> {}
const emitter = new AppEmitter();
emitter.on('user:created', (user) => {
// user is typed as { id: string; name: string }
console.log(user.name);
});
once:emitter.once('ready', () => {
console.log('System initialized');
});
const handler = (data: string) => console.log(data);
emitter.on('data', handler);
// Later: remove the specific listener
emitter.off('data', handler);
// Or remove all listeners for an event
emitter.removeAllListeners('data');
emitter.setMaxListeners(20); // Default is 10
// Or globally
EventEmitter.defaultMaxListeners = 20;
once as a Promise:import { once } from 'node:events';
const [data] = await once(emitter, 'data');
console.log(data);
'error' events:emitter.on('error', (err) => {
console.error('Emitter error:', err);
});
// Without an error listener, emitting 'error' throws and crashes the process
const ac = new AbortController();
emitter.on('data', handler, { signal: ac.signal });
// Automatically removes the listener
ac.abort();
EventEmitter is Node.js's built-in pub-sub mechanism. It is synchronous by default — emit() calls listeners in registration order and blocks until all complete.
Synchronous emission: emitter.emit('event', data) runs all listeners synchronously in the current tick. Long-running listeners block the event loop. Use setImmediate or queueMicrotask inside listeners for async work.
Memory leak warning: If more than maxListeners are registered for a single event, Node.js prints a warning. This usually indicates listeners being added in a loop without removal.
Event ordering: Listeners fire in registration order. prependListener adds to the front of the queue.
Trade-offs:
once as Promise is convenient — but only captures the first emissionhttps://nodejs.org/api/events.html
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeImplements publisher-subscriber communication with topic-based routing and fan-out delivery. Provides typed and wildcard pub/sub implementations.
Builds event-driven APIs with webhooks, Server-Sent Events, message brokers, event schemas, subscribers, retries, and dead-letter queues.
Provides decision trees for async programming, module systems (CJS/ESM), and the event loop in JavaScript and Node.js. Covers promises, streams, worker threads, and modern ES2024+ features.