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 πŸ™

Β© 2024 – Pkg Stats / Ryan Hefner

eventhoven

v1.0.0

Published

Event manager that composes events effortlessly 🎡

Downloads

29

Readme


Table of Contents

What is this?

It's a tiny and simple type-safe event manager library for browser and node, ~1KB (gzipped, tree-shakeable - essentials are less than 500B).

It provides a powerful set of tools for creating and composing event managers.
In other words, it manages event managers!

Main list of features includes (but is not limited to):

  • Full tree-shaking
  • Functional-style API
  • Multiple event arguments
  • Event names can also be symbols (private events)
  • Soft error-handling - no unexpected runtime errors!
  • Versatile plugin system (using meta-events)
  • Fully type-safe - each event remembers its name and type signature
  • All functions are curried and point-free, which makes them easier to use in a functional environment (for example, with ramda and similar tools)
  • SOLID code
    • SRP - every function does only one thing
    • OCP - HOFs allow to change certain behaviours without the need to rewrite code
    • LSP - all functions are easily substitutable as long as they adhere to the same API
    • ISP - all data types are the least specific versions of them
    • DIP - API depends only on abstractions
  • Code-generation-friendly:
    Due to the SRP, all functions have a very limited number of ways of invocation.
    This allows to automatically generate efficient code (for example, CRUD events) for this library without concerns about its stability.
  • KISS and DRY code

Something's missing or found a bug?
Feel free to create an issue! πŸ˜‰


Disclaimer

and some ground principles

TypeScript

eventhoven's main concern is type-safety at every step, so all the code examples will be written in typescript.

It was written in a "type-first, implementation-later" way, and will be kept that way to ensure that runtime types always match the static ones.

Currying

"Why curry functions?" you may ask. Great question! It has many answers on the web already, but I'd recommend reading this and this.

eventhoven uses the concept of currying to elevate the abstraction and allow for a much wider and precise usage of it's API in return for sometimes writing )( instead of usual , , which is not too much of a price to pay for this feature.

It also allows eventhoven to be used effortlessly with other functional libraries like ramda and many others.

Not all eventhoven function are curried. Those, which are, however, will have the following disclaimer:

Note, that the function is curried, which means that it must be called partially

External state >>> Internal state

eventhoven doesn't store anything internally, it's a completely stateless, pure and side-effect-free library.
It only has side-effects from closures on an external state that a user provides.
So, there it is - no private fields, no hidden implementation details, no complications.
This allows for easier testing and simpler usage.

Thanks to this rule, eventhoven is a higher abstraction above other event-managers. A Higher-Order-Event-Manager, if you like.
That is, any other event manager's API can be built on top of what eventhoven gives you, providing a nearly endless set of possibilities.

OK, but why not %event-manager-package%?

eventhoven is not in direct comparison to other event managers. As stated in the main description - its main purpose is to compose events and event managers.

In production, it's a fairly typical scenario that multiple libraries with multiple event systems exist and function in the same project at the same time.
Front-end libraries do that all the time - vue, react, angular - all have own separate event systems - even from the DOM! eventhoven aims to provide a connecting bridge for different event managing strategies, by providing instruments for unifying the event management API.
In other words, it allows to unify event management.

It just so happens that it can do event management very efficiently too. πŸ˜‰


Installation

npm:

npm i -S eventhoven

browser:

<!-- ES2015 -->
<script type="module">
  import { eventMap, emit, on, off } from 'https://unpkg.com/eventhoven';

  // use it here
</script>

<!-- ES5 with IE11+ general syntax polyfills, global object - `eventhoven` -->
<!-- Polyfill `window.Promise` and `Object.assign` yourself! -->
<script src="https://unpkg.com/eventhoven/dist/umd.js"></script>

Importing

// TS-module (pure typescript),
// allows compilation settings to be set from the project config
import { eventMap, emit, on, off } from 'eventhoven/src';

// ES-module (npm/node, typescript)
import { eventMap, emit, on, off } from 'eventhoven';

// ESNext (no polyfills for esnext)
import { eventMap, emit, on, off } from 'eventhoven/dist/esnext';

// ES-module (browser, node)
import { eventMap, emit, on, off } from 'https://unpkg.com/eventhoven';

// Classic node commonjs
const { eventMap, emit, on, off } = require('eventhoven/dist/js');

Simple usage examples

// Essential imports
import { eventMap, emit, on, off } from 'eventhoven';

// Event-map declaration
const emojiEvents = eventMap({
  // key - event name,
  // function arguments - event arguments,
  // function body - default handler for the event
  // (leave emtpy if you need to just declare the event)
  'πŸ‘©'(context, emoji: 'πŸ‘¨' | 'πŸ‘©') {},
  '🌈'(context, emoji: 'πŸ¦„' | '🌧') {},
  '🎌'(context, emoji: 'πŸ‘˜' | '🍣' | 'πŸš—', amount: number) {},
});

on(emojiEvents)('🎌')(
  (context, emoji, amount) => console.log(`Yay!, ${amount} ${emoji}-s from ${context.event}!`)
);

on(emojiEvents)('🎌')(
  // Returning promises is also allowed (example API from http://www.sushicount.com/api)
  (context, emoji, amount) => fetch('http://api.sushicount.com/add-piece-of-sushi/')
    .then(_ => _.json())
    .then(resp => console.log(`Yay!, ${resp.pieces_of_sushi} ${emoji}-s loaded from sushicount!`))
);

// It's possible to await execution of all event handlers too
await emit(emojiEvents)(
  // Autocomplete for event names here!
  '🎌'
)(
  // Autocomplete for arguments here!
  '🍣', 10
);
// Console output:
// => Yay!, 10 🍣-s from 🎌!
// => Yay!, 11 🍣-s loaded from sushicount!
import { eventMap, emit, on, off } from 'eventhoven';

type Todo = { done: boolean; text: string; };

const todos: Todo[] = [];

// Event-map declaration
const todoEvents = eventMap({
  // key - event name,
  // first argument (context) - event contxext
  // function arguments - event arguments,
  // function body - default handler for the event
  // (leave emtpy if you need to just declare the event)
  'add-todos'(context, todos: Todo[], ...newTodos: Todo[]) {
    // Typically, a default handler is used to compose events from other event managers here.
    // But we'll just implement a simple todo-app for now
    todos.push(...newTodos);
  },
  'done-change'(context, todo: Todo, newDone: boolean) {
    todo.done = newDone;
  },
  'text-change'(context, todo: Todo, newText: string) {
    todo.text = newText;
  },
});

const emitTodo = emit(todoEvents);

const unsubFromAddTodos = on(todoEvents)('add-todos')(
  // Parameter types are inferred here
  (context, todos, ...newTodos) => newTodos.forEach(todo => console.log(
    `Wow, new todo added - "${todo.text}"!`,
    todo.done ? `And it's done already!` : 'Need to do it!'
  ))
);

const addTodos = emitTodo('add-todos');

addTodos(
  todos,
  { text: 'learn fp', done: true },
  { text: 'publish a cool event manager', done: true },
);
// => Wow, new todo added - "learn fp"! And it's done already!
// => Wow, new todo added - "publish a cool event manager"! And it's done already!

// Unsubbed the wow-ing console.log from the event
unsubFromAddTodos();

addTodos(
  todos,
  { text: 'buy milk', done: false }
);
// nothing happens in the console now

console.log(todos);
// => [
//   { text: 'learn fp', done: true },
//   { text: 'publish a cool event manager', done: true },
//   { text: 'buy milk', done: false }
// ]

const changeText = emitTodo('text-change');
const changeDone = emitTodo('done-change');

changeText(todos[2], 'write documentation');

changeDone(todos[2], true);

console.log(todos);
// => [
//   { text: 'learn fp', done: true },
//   { text: 'publish a cool event manager', done: true },
//   { text: 'buy milk', done: true }
// ]

Code Pen Playground


API

There are only 4 essential exports that are needed to use this library:

name | type | description -----|------|-------------------- eventMap | function | Event-map factory emit | function | Event emitter factory on | function | Event subscriber factory off | function | Event unsubscriber factory

Together they add up to less than 500 Bytes (gzipped)!

Everything else is just syntax sugar and boilerplate reduction.

name | type | description -----|------|-------------------- eventMap | function | Event-map factory emit | function | Event emitter factory subscribe | function | Event subscriber factory subscribeToAll | function | Event subscriber factory for all events in a collection on | function | Alias for subscribe onAll | function | Alias for subscribeToAll unsubscribe | function | Event unsubscriber factory unsubscribeFromAll | function | Event unsubscriber factory off | function | Alias for unsubscribe offAll | function | Alias for unsubscribeFromAll once | function | Makes a handler be executed only once emitCollection | function | Creates a collection of event-emitters from an event-map subscribeCollection | function | Creates a collection of event-subscribers from an event-map unsubscribeCollection | function | Creates a collection of event-unsubscribers from an event-map eventCollection | function | Creates a collection of the three previous collections from an event-map wait | function | Waits for an event to be executed harmonicWait | function | Same as wait but has an arity of 3, just as all the other event-handling functions debug | function | Sets the debug mode (if enabled - logs all events to the console) customDebug | function | Creates a custom debugger based on a function passed to it metaEvents | object | A meta-event-map. Can be used to subscribe to the internal eventhoven's events emitMeta | function | A meta-event emitter. An emit function created for metaEvents Eventhoven | class | A class wrapper for eventMap and eventCollection.


eventMap(events)

Creates an event-map from event signatures.

Parameters:

name | type | description -----|------|--------------- events | TEventSignatures | a collection of event signatures

Returns: TEventMap

This function is the main "entry point" to the whole event management pipeline. It constructs a base storage for events and their handlers, which is then used by all of the other functions.

In other words, to start working with events in eventhoven you start by creating an event-map:

import { eventMap } from 'eventhoven';

// `keyboardEvents` should now be used for all event interactions
const keyboardEvents = eventMap({
  keyup(context, e: KeyboardEvent) {},
  keydown(context, e: KeyboardEvent) {},
  keypress(context, e: KeyboardEvent, modifier?: string) {
    // This is a default handler for the event,
    // it's always executed when the event is invoked
    console.log('modifier:', modifier);
  },
});

In this example, keys in keyboardEvents correspond to event names ('keyup', 'keydown', etc.) and values contain handler maps and amount (and types) of arguments for a given event.

The decision to use plain functions as event signatures comes down to 3 advantages:

  1. It's easier to make type inference that way.
    Since a handler and the event signature are both functions,
    there's no need to convert types from event signature to event handler.

  2. It allows to compose event managers easier.
    Having an event signature be a default event handler
    allows to emit other managers' events directly in the event declaration, for example:

     import { eventMap } from 'eventhoven';
     import { VueApp } from './app.vue';
    
     const someEventMap = eventMap({
       someEvent() {
         // Emitting a Vue event
         VueApp.$emit('someEvent');
       }
     });

    Also, it is a common practice to add a default handler for the event
    right after its declaration, which produces lines like this:

     import { Manager } from 'some-event-manager';
    
     const manager = new Manager([ 'event' ]);
     // Boilerplate code:
     manager.on('event', () => console.log('I want to debug all invocations of this event!'));

    eventhoven allows to never have to do that.

  3. It allows code analysis tools detect which events are never being emitted.
    Since the event signatures are executable pieces of code, which are always executed on their respective events,
    code analysis tools (code coverages, testing frameworks) can detect and count the amount of executions
    of these signatures. And this amount is equivalent to the amount of emits of a particular event.
    This allows, in turn, to always know which events are never emitted and should be deleted from the event-map.

  • by adding an event to a generic map:

    import { eventMap, TEventSignatures } from 'eventhoven';
    
                       // Makes event-map accept any event into itself
    const someEventMap = eventMap<TEventSignatures>({
      someEvent() {}
    });
    
    // Adding a new event to the map! (notice, no default handler)
    someEventMap['newEvent'] = new Map();
    
    // And now we can use it:
    emit(someEventMap)('newEvent')();
  • by creating a new event-map 😁:

    const inputEvents = {
      ...keyboardEvents,
      ...eventMap({
        'mouse-click'(context, e: MouseEvent) {},
      }),
    }
    
    // Still have type inference here!
    emit(inputEvents)('mouse-click')

Event handler

As stated earlier, event signatures are read from the default event handlers in the event-map.

Any event handler has a following signature:

(context: TEventContext, ...args: any[]) => unknown | Promise<unknown>

Parameters:

name | type | description ------|-----|-------------- context | TEventContext | a context given by eventhovent for every event ...args | any[] (contextual) | an event-specific array of arguments

Returns: any value or a promise with any value.
The type of said value is determined by the default handler in the event-map, or remains unknown.

const map = eventMap({
  // An event handler that returns a number
  numberEvent(context): number {
    // The default is 42
    return 42;
  }
});

// Type is usually inferred automatically
const result: number[] = await emit(map)('numberEvent')();

console.log(result); // => [42]

// Let's add another handler to the mix
on(map)('numberEvent')(ctx => 43);

const result2 = await emit(map)('numberEvent')();

console.log(result2); // => [42, 43]

Event context

You've probably noticed by now, that all event handlers have a first context parameter.

This is the event context that's provided by eventhoven, and it is an object of the following signature:

key | type | description ----|------|----------------- event | PropertyKey | An event that triggered this handler. unsubscribe | () => void | A function that unsubscribes the current handler from the event.

const map = eventMap({
  eventName(context) {
    console.log(context.event); // => "eventName"
    console.log(typeof context.unsubscribe); // => "function"
  }
});

emit(eventMap)(event)(...args): Promise<void>

Creates event emitters for an event-map.
If an event does not exist, it will be ignored.

Note, that the function is curried, which means that it must be called partially

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to emit events from event | PropertyKey | An event name to emit for a given event-map (can be a symbol too) ...args | any (contextual) | Arguments for the specific event, spread

Returns: Promise<void> - a promise that is resolved when all event handlers have finished their execution


emitAll(eventMap)(eventArgs): object

Emits all events in an event map.

Note, that the function is curried, which means that it must be called partially

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to subscribe to. eventArgs | TEventParamsMap | Parameters for all events in an event map.

Returns: Record<keyof M, Promise<void>> - a map for all events' emits promises (each will resolve upon all event handlers' resolution).


subscribe(eventMap)(event)(...handlers): () => void

Creates event subscribers for an event in an event-map.
If an event does not exist, it will be ignored.

Note, that the function is curried, which means that it must be called partially

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to get events from. event | PropertyKey | An event name to subscribe to for a given event-map (can be a symbol too). ...handlers | function[] | Handlers to execute on the event, spread. If emtpy, no subscribing is done.

Returns: () => void - a function that unsubscribes the handler from the event

Alias: on

subscribeToAll(eventMap)(...handlers)

Subscribes handler(s) to all events in an event map.

Note, that the function is curried, which means that it must be called partially

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to subscribe to. ...handlers | function[] | Handlers to execute on the events, spread. If emtpy, no subscribing is done.

Returns: void

Alias: onAll


unsubscribe(eventMap)(event)(...handlers)

Unsubscribes handlers from events of an event-map.
If an event does not exist, it will be ignored.

Note, that the function is curried, which means that it must be called partially

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to unsubscribe handlers from. event | PropertyKey | An event name to unsub from for a given event-map (can be a symbol too). ...handlers | function[] | Handlers to unsubscribe from the event, spread. If empty - all currently subbed handlers will be unsubscribed.

Returns: void

Alias: off

unsubscribeFromAll(eventMap)(...handlers)

Unsubscribes handler(s) from all events in an event map.

Note, that the function is curried, which means that it must be called partially

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to unsubscribe from. ...handlers | function[] | Handlers to unsubscribe from the events, spread. If empty - all currently subbed handlers will be unsubscribed.

Returns: void

Alias: offAll


once(handler): handler

Makes a handler being called only once upon the subscribed event invocation.
Should be used with subscribe to reduce boilerplate for one-time handlers.

Parameters:

name | type | description -----|------|--------------- handler | function | An event handler to be once-d

Returns: handler - a changed handler that was passed in

Usage example:

on(eventmap)('some-event')(once((ctx, arg) => {
  console.log('This handler will only be called once!');
}));

wait(eventMap)(event): Promise<args[]>

Allows to wait for an event without the need for callbacks.

Note, that the function is curried, which means that it must be called partially

Basically, promise-style subscribe with the once flag.
It is a way to block execution flow until some event occurs.

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to wait events from. event | PropertyKey | An event name to wait for in a given event-map (can be a symbol too).

Returns: Promise<Array<unknown>> (contextual) - a promise with array of parameters passed to the event.

import { wait } from 'eventhoven';

const keydown = wait(keyboardEvents)('keydown');

//... some time later in async context

// Resolves upon the first 'keydown' event emit
// Returns a tuple of arguments that would otherwise go to the handler
// (excluding the context)
const [e] = await keydown;
console.log(e);
// => KeyboardEvent {}

harmonicWait(eventMap)(event)(): Promise<args[]>

Same as wait, but returns a promise factory instead of a plain promise.

Note, that the function is curried, which means that it must be called partially

Useful due to having the same signature as emit, subscribe and unsubscribe, which allows for an easier composition of waiters.

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to wait events from. event | PropertyKey | An event name to wait for in a given event-map (can be a symbol too).

Returns: () => Promise<Array<unknown>> (contextual) - a promise factory with array of parameters passed to the event.

import { harmonicWait } from 'eventhoven';

// Function that initiates a waiter
const waitForKeydown = harmonicWait(keyboardEvents)('keydown');

//... some time later in async context

// Resolves upon the first 'keydown' event emit
// since the call of the `waitForKeydown`
const [e] = await waitForKeydown();
console.log(e);
// => KeyboardEvent {}

debug(isEnabled)

Sets the debug mode.

Parameters:

name | type | description -----|------|--------------- enabled | boolean | Whether to enable the debug mode or disable it.

Returns: void

When debug mode is enabled, all emits, subscribes and unsubscribes are logged to the console in a following format (default):

MM:SS.fff [{event-type} {event-name}]: {event-handler-or-params}

Where:

  • {event-type} - type of the event
  • {event-name} - name of the event
  • {event-handler-or-params}
    • the handler for the event (when subscribing or unsubscribing)
    • params of the event (when emitting)

Example:

import { emit, debug } from 'eventhoven';

debug(true);

emit(emojiEvents)('🎌')('🍣', 10);

// logs:
// 59:05.512 [EMIT "🎌"]: [🍣, 10]

If an event does not exist in an event-map, the log will contain (INVALID) mark:

import { emit, debug } from 'eventhoven';

debug(true);

// event "😎" doesn't exist in the `emojiEvents` map
emit(emojiEvents)('😎')('🍣', 10);

// logs:
// 59:05.512 [EMIT "😎" (INVALID)]: [🍣, 10]

Custom logging function

If you want coloring or some other features - pass a custom logging function to the customDebug factory. It accepts a function of the same signature as any other event handler:

import { customDebug, TLogHandler, emit } from 'eventhoven';

// Let's say we want warnings instead of logs
const customLogFunction: TLogHandler = (ctx, ...args) => console.warn('custom!', ctx.event, ...args);

const debug = customDebug(customLog);

// Then use your custom debug function exactly like the default one:
debug(true);

emit(emojiEvents)('🎌')('🍣', 10);

// logs:
// custom! EMIT [object Object] 🎌 [🍣, 10]

Collections

eventhoven provides a way to group your event-managing needs using collections.

Parameters:

name | type | description -----|------|--------------- eventMap | TEventMap | An event-map to wait events from.

Return: A map of event names to the action for that event name.

Currently available collections are:

name | action | description -----|--------|------------------ emitCollection(eventMap) | emit | Creates an object, where each property is a function that emits a prescribed event subscribeCollection(eventMap) | subscribe | Creates an object, where each property is a function that subscribes to a prescribed event unsubscribeCollection(eventMap) | unsubscribe | Creates an object, where each property is a function that unsubscribes from a prescribed event eventCollection(eventMap) | all of the above | Creates an object that contains all three collections in itself. Can be used to create a singleton that manages all events in an event-map.

import {
  eventMap,
  emitCollection,
  subscribeCollection,
  unsubscribeCollection,
  eventCollection
} from 'eventhoven';

const myEvents = eventMap({
  event1() {},
  event2(ctx, arg1: number, arg2: string) {},
  event3(ctx, arg: boolean) {},
});

const emit = emitCollection(myEvents);

emit.event1();// => Promise<void>
emit.event2(12, 'some string');// => Promise<void>
emit.event3(true);// => Promise<void>

const on = subscribeCollection(myEvents);
const handler = (ctx, arg: boolean) => console.log(arg);

on.event3(handler);

const off = unsubscribeCollection(myEvents);

off.event3(handler);

const myCollection = eventCollection(myEvents);
// The same as
/*
const myCollection = {
  emit: emitCollection(myEvents),
  subscribe: subscribeCollection(myEvents),
  unsubscribe: unsubscribeCollection(myEvents)
};
*/

myCollection.emit.event1();// => Promise<void>
myCollection.emit.event2(12, 'some string');// => Promise<void>
myCollection.emit.event3(true);// => Promise<void>

myCollection.subscribe.event3(handler);
myCollection.unsubscribe.event3(handler);

Meta-Events (Plugin API)

It's also possible to write custom plugins for eventhoven thanks to meta-events!

Meta-events is a simple event-map with events for internal eventhoven actions, like emit.
One can subscribe to these events to execute some actions or emit these events to emulate them for the eventhoven.

The simplest possible plugin is already written for you - the debug plugin.
It can be used as an example for writing your own plugins for eventhoven!

Note:

  • A meta-event is always emitted before the event itself happens.
  • Any meta-event that returns a promise is going to be executed in parallel with the handlers for the event itself.

Current list of all meta-events is as follows:

name | emitted when -------|------------------ EMIT | Any event is emitted, except itself. SUBSCRIBE | Any event is subscribed to, except itself. UNSUBSCRIBE | Any event is unsubscribed from, except itself.

This list is also described as a const enum EMetaEvents.

Simple example:

import { metaEvents, EMetaEvents, on } from 'eventhoven';

on(metaEvents)(EMetaEvents.EMIT)(
  (ctx, eventMap, eventName, eventArgs) => console.log(
    `This handler will be executed when ANY event is emitted, for example ${eventName}!`
  )
);

Class API

Even though eventhoven is functional in its nature, nothing prohibits to use it in Object-Oriented way.
Class API can help with this:

import { Eventhoven } from 'eventhoven';

const myEventManager = new Eventhoven({
  myEvent1(ctx, arg: string) {
    console.log('yay, my oop event!', arg);
  }
});

myEventManager.emit('myEvent1', 'first emit');
// => yay, my oop event! first emit

myEventManager.on('myEvent1', (ctx, arg) => {
  console.log('another handler!', arg);
});

myEventManager.emit('myEvent1', 'second emit');
// => yay, my oop event! second emit
// => another handler! second emit

console.log(Object.keys(myEventManager.map));
// => ['myEvent1']

Basically, the Eventhoven class is a wrapper of eventCollection with the following properties:

visibility | name | type | description ------|------|---------|----------------- public | map | TEventMap | An event map that contains all of the instance events public | emit | (event, ...args) => Promise<any[]> | An event emitter function. Basically, an uncurried version of emit public | on | (event, ...handlers) => () => void | An event subscriber function. Basically, an uncurried version of subscribe. public | off | (event, ...handlers) => void | An event unsubscriber function. Basically, an uncurried version of unsubscribe. static | emit | emit | The emit function, with all of its args flattened static | on | subscribe | The subscribe function, with all of its args flattened static | off | unsubscribe | The unsubscribe function, with all of its args flattened

It's also possible to utilize static methods of the Eventhovent class in order to use a shorter version of main API:

import { Eventhoven } from 'eventhoven';

const myEventManager = new Eventhoven({
  myEvent1(ctx, arg: string) {
    console.log('yay, my oop event!', arg);
  }
});

const myEvents = eventMap({
  someEvent(ctx, arg: boolean) {
    console.log('functional event -', arg);
  },
});

// All three methods accept an event-map or an Eventhoven instance as a first argument

// Accepting a class
Eventhoven.on(myEventManager, 'myEvent1', (ctx, arg) => {
  console.log('another handler!', arg);
});
Eventhoven.emit(myEventManager, 'myEvent1', 'static emit');
// => yay, my oop event! static emit
// => another handler! static emit

// Accepting an event-map
Eventhoven.on(myEvents, 'someEvent', (ctx, arg) => {
  console.log('another handler:', arg);
});
Eventhoven.emit(myEvents, 'someEvent', true);
// => functional event - true
// => another handler: true

Contribute

First, fork the repo and clone it:

git clone https://github.com/%your-github-username%/eventhoven.git

Then:

npm install

Then:

npm run dev

Then introduce changes and propose a PR!
I'll be happy to review it!


Something's missing or found a bug?
Feel free to create an issue! πŸ˜‰