logo

Event Emitting and Middleware Hooks

tests GitHub license codecov npm jsDelivr hits npm

Features

Installation

npm install hookified --save

Usage

This was built because we constantly wanted hooks and events extended on libraires we are building such as Keyv and Cacheable. This is a simple way to add hooks and events to your classes.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World'); //using Emittery
  }

  //with hooks you can pass data in and if they are subscribed via onHook they can modify the data
  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

You can even pass in multiple arguments to the hooks:

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    let data2 = { some: 'data2' };
    // do something
    await this.hook('before:myMethod2', data, data2);

    return data;
  }
}

Using it in the Browser

<script type="module">
  import { Hookified } from 'https://cdn.jsdelivr.net/npm/hookified/dist/browser/index.js';

  class MyClass extends Hookified {
    constructor() {
      super();
    }

    async myMethodEmittingEvent() {
      this.emit('message', 'Hello World'); //using Emittery
    }

    //with hooks you can pass data in and if they are subscribed via onHook they can modify the data
    async myMethodWithHooks() Promise<any> {
      let data = { some: 'data' };
      // do something
      await this.hook('before:myMethod2', data);

      return data;
    }
  }
</script>

if you are not using ESM modules, you can use the following:

<script src="https://cdn.jsdelivr.net/npm/hookified/dist/browser/index.global.js"></script>
<script>
  class MyClass extends Hookified {
    constructor() {
      super();
    }

    async myMethodEmittingEvent() {
      this.emit('message', 'Hello World'); //using Emittery
    }

    //with hooks you can pass data in and if they are subscribed via onHook they can modify the data
    async myMethodWithHooks() Promise<any> {
      let data = { some: 'data' };
      // do something
      await this.hook('before:myMethod2', data);

      return data;
    }
  }
</script>

API - Hooks

.onHook(eventName, handler)

Subscribe to a hook event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

const myClass = new MyClass();
myClass.onHook('before:myMethod2', async (data) => {
  data.some = 'new data';
});

.onceHook(eventName, handler)

Subscribe to a hook event once.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

const myClass = new MyClass();

myClass.onHookOnce('before:myMethod2', async (data) => {
  data.some = 'new data';
});

myClass.myMethodWithHooks();

console.log(myClass.hooks.length); // 0

.removeHook(eventName)

Unsubscribe from a hook event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

const myClass = new MyClass();
const handler = async (data) => {
  data.some = 'new data';
};

myClass.onHook('before:myMethod2', handler);

myClass.removeHook('before:myMethod2', handler);

.hook(eventName, ...args)

Run a hook event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

in this example we are passing multiple arguments to the hook:

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    let data2 = { some: 'data2' };
    // do something
    await this.hook('before:myMethod2', data, data2);

    return data;
  }
}

const myClass = new MyClass();

myClass.onHook('before:myMethod2', async (data, data2) => {
  data.some = 'new data';
  data2.some = 'new data2';
});

await myClass.myMethodWithHooks();

.hooks

Get all hooks.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

const myClass = new MyClass();
myClass.onHook('before:myMethod2', async (data) => {
  data.some = 'new data';
});

console.log(myClass.hooks);

.getHooks(eventName)

Get all hooks for an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

const myClass = new MyClass();
myClass.onHook('before:myMethod2', async (data) => {
  data.some = 'new data';
});

console.log(myClass.getHooks('before:myMethod2'));

.clearHooks(eventName)

Clear all hooks for an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodWithHooks() Promise<any> {
    let data = { some: 'data' };
    // do something
    await this.hook('before:myMethod2', data);

    return data;
  }
}

const myClass = new MyClass();

myClass.onHook('before:myMethod2', async (data) => {
  data.some = 'new data';
});

myClass.clearHooks('before:myMethod2');

API - Events

.on(eventName, handler)

Subscribe to an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World');
  }
}

const myClass = new MyClass();

myClass.on('message', (message) => {
  console.log(message);
});

.off(eventName, handler)

Unsubscribe from an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World');
  }
}

const myClass = new MyClass();
myClass.on('message', (message) => {
  console.log(message);
});

myClass.off('message', (message) => {
  console.log(message);
});

.emit(eventName, ...args)

Emit an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World');
  }
}

.listeners(eventName)

Get all listeners for an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World');
  }
}

const myClass = new MyClass();

myClass.on('message', (message) => {
  console.log(message);
});

console.log(myClass.listeners('message'));

.removeAllListeners(eventName)

Remove all listeners for an event.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World');
  }
}

const myClass = new MyClass();

myClass.on('message', (message) => {
  console.log(message);
});

myClass.removeAllListeners('message');

.setMaxListeners(maxListeners: number)

Set the maximum number of listeners and will truncate if there are already too many.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }

  async myMethodEmittingEvent() {
    this.emit('message', 'Hello World');
  }
}

const myClass = new MyClass();

myClass.setMaxListeners(1);

myClass.on('message', (message) => {
  console.log(message);
});

myClass.on('message', (message) => {
  console.log(message);
}); // this will not be added and console warning

console.log(myClass.listenerCount('message')); // 1

.once(eventName, handler)

Subscribe to an event once.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }
}

const myClass = new MyClass();

myClass.once('message', (message) => {
  console.log(message);
});

myClass.emit('message', 'Hello World');

myClass.emit('message', 'Hello World'); // this will not be called

.prependListener(eventName, handler)

Prepend a listener to an event. This will be called before any other listeners.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }
}

const myClass = new MyClass();

myClass.prependListener('message', (message) => {
  console.log(message);
});

.prependOnceListener(eventName, handler)

Prepend a listener to an event once. This will be called before any other listeners.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }
}

const myClass = new MyClass();

myClass.prependOnceListener('message', (message) => {
  console.log(message);
});

myClass.emit('message', 'Hello World');

.eventNames()

Get all event names.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }
}

const myClass = new MyClass();

myClass.on('message', (message) => {
  console.log(message);
});

console.log(myClass.eventNames());

.listenerCount(eventName?)

Get the count of listeners for an event or all events if evenName not provided.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }
}

const myClass = new MyClass();

myClass.on('message', (message) => {
  console.log(message);
});

console.log(myClass.listenerCount('message')); // 1

.rawListeners(eventName?)

Get all listeners for an event or all events if evenName not provided.

import { Hookified } from 'hookified';

class MyClass extends Hookified {
  constructor() {
    super();
  }
}

const myClass = new MyClass();

myClass.on('message', (message) => {
  console.log(message);
});

console.log(myClass.rawListeners('message'));

Development and Testing

Hookified is written in TypeScript and tests are written in vitest. To run the tests, use the following command:

To setup the environment and run the tests:

npm i && npm test

To contribute follow the Contributing Guidelines and Code of Conduct.

License

MIT & © Jared Wray

Contributors

Latest's Releases

v1.5.1 November 25, 2024

What's Changed

Full Changelog: https://github.com/jaredwray/hookified/compare/v1.5.0...v1.5.1

v1.5.0 October 29, 2024

What's Changed

Full Changelog: https://github.com/jaredwray/hookified/compare/v1.4.0...v1.5.0

v1.4.0 October 15, 2024

What's Changed

Full Changelog: https://github.com/jaredwray/hookified/compare/v1.3.0...v1.4.0

All Releases