npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

fastevent

v2.3.6

Published

The Ultimate Event Trigger Library – Fast, Feature-Rich, Battle-Tested!

Readme

FastEvent

WebSite

FastEvent is a well-designed, powerful, type-safe, and thoroughly tested event emitter that provides robust event subscription and publishing mechanisms, suitable for both nodejs/browser environments.

Installation

npm install fastevent
yarn add fastevent
pnpm add fastevent
bun add fastevent

Guide

Event Publishing and Subscription

FastEvent provides complete event emission and subscription functionality, with an API design inspired by eventemitter2.

import { FastEvent } from 'fastevent';
const events = new FastEvent();

// Basic event publishing
const results = events.emit('user/login', { id: 1 });

// Asynchronous event emission
const results = await events.emitAsync('data/process', { items: [...] });

// Event subscription
events.on('user/login', (message) => {
    console.log('User login:', message.payload);
});

// One-time listener
events.once('startup', () => console.log('Application has started'));

// Listener with options
events.on('data/update', handler, {
    count: 3,       // Maximum trigger count
    prepend: true,  // Add to the beginning of the queue
    filter: (msg) => msg.payload.important // Only process important updates
});

// Global listener
events.onAny((message) => {
    console.log('Event occurred:', message.type);
});

Event Messages

Listener functions receive a Message object that contains the following properties:

events.on('user/login', (message) => {
    // {
    //     type: 'user/login', // Event name
    //     payload: { id: 1 }, // Event data
    //     meta: {...}         // Event metadata
    // }
});

Retained Events

Retain the last event data, so subsequent subscribers can immediately receive the event value upon subscription:

const events = new FastEvent();

// Publish and retain event
events.emit('config/theme', { dark: true }, true);
// Equivalent to
events.emit('config/theme', { dark: true }, { retain: true });

// Subsequent subscribers immediately receive the retained value
events.on('config/theme', (message) => {
    console.log('Theme:', message.payload); // Immediately outputs: { dark: true }
});

Hierarchical Event Publishing

FastEvent supports hierarchical event publishing and subscription.

  • The default event hierarchy delimiter is /, which can be modified via options.delimiter
  • Two types of wildcards are supported when subscribing to events: * matches a single path level, ** matches multiple path levels (only used at the end of event names)
const events = new FastEvent();

// Match user/*/login
events.on('user/*/login', (message) => {
    console.log('Any user type login:', message.payload);
});

// Match all events under user
events.on('user/**', (message) => {
    console.log('All user-related events:', message.payload);
});

// Trigger events
events.emit('user/admin/login', { id: 1 }); // Both handlers will be called
events.emit('user/admin/profile/update', { name: 'New' }); // Only the ** handler will be called

Removing Listeners

FastEvent provides multiple ways to remove listeners:

// Return a subscriber object to remove the listener, recommended approach
const subscriber = events.on('user/login', handler);
subscriber.off();

// Remove a specific listener
events.off(listener);
// Remove all listeners for a specific event
events.off('user/login');
// Remove a specific listener for a specific event
events.off('user/login', listener);
// Remove listeners using wildcard patterns
events.off('user/*');
// Remove all listeners
events.offAll();
// Remove all listeners under a specific prefix
events.offAll('user');

Event Scopes

Scopes allow you to handle events within a specific namespace.

Note that scopes share the same listener table with the parent event emitter:

const events = new FastEvent();

// Create a user-related scope
const userScope = events.scope('user');

// The following two approaches are equivalent:
userScope.on('login', handler);
events.on('user/login', handler);

// The following two approaches are also equivalent:
userScope.emit('login', data);
events.emit('user/login', data);

// Clear all listeners in the scope
userScope.offAll(); // Equivalent to events.offAll('user')

Waiting for Events

Use waitFor to wait for a specific event to occur, with timeout support.

const events = new FastEvent();

async function waitForLogin() {
    try {
        // Wait for login event with a 5-second timeout
        const userData = await events.waitFor('user/login', 5000);
        console.log('User logged in:', userData);
    } catch (error) {
        console.log('Login wait timeout');
    }
}

waitForLogin();
// Later trigger the login event
events.emit('user/login', { id: 1, name: 'Alice' });

Event Hooks

FastEvent provides multiple hook functions for operations at different stages of the event emitter lifecycle.

const otherEvents = new FastEvent();
const events = new FastEvent({
    // Called when a new listener is added
    onAddListener: (type, listener, options) => {
        console.log('Added new listener:', type);
        // Return false to prevent the listener from being added
        return false;
        // Can directly return a FastEventSubscriber
        // For example: transfer events starting with `@` to another FastEvent
        if (type.startsWith('@')) {
            return otherEvents.on(type, listener, options);
        }
    },
    // Called when a listener is removed
    onRemoveListener: (type, listener) => {
        console.log('Removed listener:', type);
    },
    // Called when listeners are cleared
    onClearListeners: () => {
        console.log('All listeners cleared');
    },
    // Called when a listener throws an error
    onListenerError: (error, listener, message, args) => {
        console.error(`Error in listener for event ${message.type}:`, error);
    },
    // Called before a listener executes
    onBeforeExecuteListener: (message, args) => {
        console.log('Before executing event listener');
        // Return false to prevent listener execution
        return false;

        // Forward events to another FastEvent
        // For example: forward events starting with `@` to another FastEvent
        if (type.startsWith('@')) {
            return otherEvents.emit(message.type);
        }
    },
    // Called after a listener executes
    onAfterExecuteListener: (message, returns, listeners) => {
        console.log('After executing event listener');
        // Can intercept and modify return values here
    },
});

Executors

By default, all listeners are executed in parallel when an event is triggered.

FastEvent provides powerful listener execution mechanisms that allow developers to control how listeners are executed.

import { race } from 'fastevent/executors';
const events = new FastEvent({
    executor: race(),
});

events.on('task/start', async () => {
    /* Time-consuming operation 1 */
});
events.on('task/start', async () => {
    /* Time-consuming operation 2 */
});

// The two listeners will execute in parallel, returning the fastest result
await events.emitAsync('task/start');

Built-in Support:

| Executor | Description | | ----------------------------------------- | ------------------------------------------------------------------------- | | parallel | Default, concurrent execution | | race | Parallel executor, uses Promise.race for parallel execution | | balance | Evenly distributed executor | | first | Execute only the first listener | | last | Execute only the last listener | | random | Randomly select a listener | | series | Serial executor, execute listeners in sequence and return the last result | | waterfall | Execute listeners in sequence and return the last result, abort on error | | (listeners,message,args,execute)=>any[] | Custom executor |

Listener Pipes

Listener pipes are used to wrap listener functions during event subscription to implement various common advanced features.

import { queue } from 'fastevent/pipes';
const events = new FastEvent();

// default queue size is 10
events.on(
    'data/update',
    (data) => {
        console.log('Processing data:', data);
    },
    {
        pipes: [queue({ size: 10 })],
    },
);

Built-in Support:

| Pipe | Description | | ---------- | -------------------------------------------------------------------------------- | | queue | Queue listener, process messages in queue, supports priority and timeout control | | throttle | Throttle listener | | debounce | Debounce listener | | timeout | Timeout listener | | retry | Retry listener, for controlling retries after listener execution failure | | memorize | Cache listener, cache listener execution results |

Forwarding Publishing and Subscription

FastEvent can elegantly forward publishing and subscription to another FastEvent instance.

const otherEmitter = new FastEvent();
const emitter = new FastEvent({
    onAddListener: (type, listener, options) => {
        // Subscription forwarding rule: when event name starts with `@/`, forward subscription to another `FastEvent` instance
        if (type.startsWith('@/')) {
            return otherEmitter.on(type.substring(2), listener, options);
        }
    },
    onBeforeExecuteListener: (message, args) => {
        // Event forwarding rule: when event name starts with `@/`, publish to another `FastEvent` instance
        if (message.type.startsWith('@/')) {
            message.type = message.type.substring(2);
            return otherEmitter.emit(message, args);
        }
    },
});
const events: any[] = [];
otherEmitter.on('data', ({ payload }) => {
    events.push(payload);
});
// Subscribe to otherEmitter's data event
emitter.on('@/data', ({ payload }) => {
    expect(payload).toBe(1);
    events.push(payload);
});
// Publish data event to otherEmitter
const subscriber = emitter.emit('@/data', 1);
subscriber.off();

Metadata (Meta)

Metadata is a mechanism for providing additional contextual information for events.

You can set metadata at different levels: global, scope level, or event-specific level.

const events = new FastEvent({
    meta: {
        version: '1.0',
        environment: 'production',
    },
});

events.on('user/login', (message) => {
    console.log('Event data:', message.payload);
    console.log('Metadata:', message.meta); // Includes type, version, and environment
});

// Using scope-level metadata
const userScope = events.scope('user', {
    meta: { domain: 'user' },
});
// Add specific metadata when publishing events
userScope.emit(
    'login',
    { userId: '123' },
    {
        meta: { timestamp: Date.now() }, // Event-specific metadata
    },
);

// Listeners receive merged metadata
userScope.on('login', (message) => {
    console.log('Metadata:', message.meta);
});

Event Type Definitions

FastEvent has complete TypeScript type support.

// Define events with different payload types
interface ComplexEvents {
    'data/number': number;
    'data/string': string;
    'data/object': { value: any };
}

const events = new FastEvent<ComplexEvents>();

// TypeScript ensures type safety for each event
events.on('data/number', (message) => {
    const sum = message.payload + 1; // payload type is number
});

// All event emissions are type-checked
events.emit('data/number', 42);
events.emit('data/string', 'hello');
events.emit('data/object', { value: true });

Unit Testing

FastEvent has been thoroughly unit tested, with over 280+ cumulative test cases and 99%+ test coverage.

License

MIT

For more detailed documentation, see WebSite