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 🙏

© 2026 – Pkg Stats / Ryan Hefner

kareem

v3.2.0

Published

Next-generation take on pre/post function hooks

Readme

kareem

Build Status

Re-imagined take on the hooks module, meant to offer additional flexibility in allowing you to execute hooks whenever necessary, as opposed to simply wrapping a single function.

Named for the NBA's 2nd all-time leading scorer Kareem Abdul-Jabbar, known for his mastery of the hook shot

API

pre hooks

Much like hooks, kareem lets you define pre and post hooks: pre hooks are called before a given function executes. Unlike hooks, kareem stores hooks and other internal state in a separate object, rather than relying on inheritance. Furthermore, kareem exposes an execPre() function that allows you to execute your pre hooks when appropriate, giving you more fine-grained control over your function hooks.

It runs without any hooks specified

await hooks.execPre('cook', null);

It runs basic serial pre hooks

pre hook functions can return a promise that resolves when finished.

let count = 0;

hooks.pre('cook', function() {
  ++count;
  return Promise.resolve();
});

await hooks.execPre('cook', null);
assert.equal(1, count);

It can run multiple pre hooks

let count1 = 0;
let count2 = 0;

hooks.pre('cook', function() {
  ++count1;
  return Promise.resolve();
});

hooks.pre('cook', function() {
  ++count2;
  return Promise.resolve();
});

await hooks.execPre('cook', null);
assert.equal(1, count1);
assert.equal(1, count2);

It can run fully synchronous pre hooks

If your pre hook function takes no parameters, its assumed to be fully synchronous.

let count1 = 0;
let count2 = 0;

hooks.pre('cook', function() {
  ++count1;
});

hooks.pre('cook', function() {
  ++count2;
});

await hooks.execPre('cook', null);
assert.equal(1, count1);
assert.equal(1, count2);

It properly attaches context to pre hooks

Pre save hook functions are bound to the second parameter to execPre()

hooks.pre('cook', function() {
  this.bacon = 3;
});

hooks.pre('cook', function() {
  this.eggs = 4;
});

const obj = { bacon: 0, eggs: 0 };

// In the pre hooks, `this` will refer to `obj`
await hooks.execPre('cook', obj);
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);

It supports returning a promise

You can also return a promise from your pre hooks instead of calling next(). When the returned promise resolves, kareem will kick off the next middleware.

hooks.pre('cook', function() {
  return new Promise(resolve => {
    setTimeout(() => {
      this.bacon = 3;
      resolve();
    }, 100);
  });
});

const obj = { bacon: 0 };

await hooks.execPre('cook', obj);
assert.equal(3, obj.bacon);

It supports filtering which hooks to run

You can pass a filter option to execPre() to select which hooks to run. The filter function receives each hook object and should return true to run the hook or false to skip it.

const execed = [];

const fn1 = function() { execed.push('first'); };
fn1.skipMe = true;
hooks.pre('cook', fn1);

const fn2 = function() { execed.push('second'); };
hooks.pre('cook', fn2);

// Only runs fn2, skips fn1 because fn1.skipMe is true
await hooks.execPre('cook', null, [], {
  filter: hook => !hook.fn.skipMe
});

assert.deepStrictEqual(execed, ['second']);

post hooks

It runs without any hooks specified

const [eggs] = await hooks.execPost('cook', null, [1]);
assert.equal(eggs, 1);

It executes with parameters passed in

hooks.post('cook', function(eggs, bacon, callback) {
  assert.equal(eggs, 1);
  assert.equal(bacon, 2);
  callback();
});

const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]);
assert.equal(eggs, 1);
assert.equal(bacon, 2);

It can use synchronous post hooks

const execed = {};

hooks.post('cook', function(eggs, bacon) {
  execed.first = true;
  assert.equal(eggs, 1);
  assert.equal(bacon, 2);
});

hooks.post('cook', function(eggs, bacon, callback) {
  execed.second = true;
  assert.equal(eggs, 1);
  assert.equal(bacon, 2);
  callback();
});

const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]);
assert.equal(Object.keys(execed).length, 2);
assert.ok(execed.first);
assert.ok(execed.second);
assert.equal(eggs, 1);
assert.equal(bacon, 2);

It supports returning a promise

You can also return a promise from your post hooks instead of calling next(). When the returned promise resolves, kareem will kick off the next middleware.

hooks.post('cook', function() {
  return new Promise(resolve => {
    setTimeout(() => {
      this.bacon = 3;
      resolve();
    }, 100);
  });
});

const obj = { bacon: 0 };

await hooks.execPost('cook', obj, [obj]);
assert.equal(obj.bacon, 3);

It supports filtering which hooks to run

You can pass a filter option to execPost() to select which hooks to run. The filter function receives each hook object and should return true to run the hook or false to skip it.

const execed = [];

const fn1 = function() { execed.push('first'); };
fn1.skipMe = true;
hooks.post('cook', fn1);

const fn2 = function() { execed.push('second'); };
hooks.post('cook', fn2);

// Only runs fn2, skips fn1 because fn1.skipMe is true
await hooks.execPost('cook', null, [], {
  filter: hook => !hook.fn.skipMe
});

assert.deepStrictEqual(execed, ['second']);

wrap()

It wraps pre and post calls into one call

hooks.pre('cook', function() {
  return new Promise(resolve => {
    this.bacon = 3;
    setTimeout(() => {
      resolve();
    }, 5);
  });
});

hooks.pre('cook', function() {
  this.eggs = 4;
  return Promise.resolve();
});

hooks.pre('cook', function() {
  this.waffles = false;
  return Promise.resolve();
});

hooks.post('cook', function(obj) {
  obj.tofu = 'no';
});

const obj = { bacon: 0, eggs: 0 };

const args = [obj];

const result = await hooks.wrap(
  'cook',
  function(o) {
    assert.equal(obj.bacon, 3);
    assert.equal(obj.eggs, 4);
    assert.equal(obj.waffles, false);
    assert.equal(obj.tofu, undefined);
    return o;
  },
  obj,
  args);

assert.equal(obj.bacon, 3);
assert.equal(obj.eggs, 4);
assert.equal(obj.waffles, false);
assert.equal(obj.tofu, 'no');
assert.equal(result, obj);

createWrapper()

It wraps wrap() into a callable function

hooks.pre('cook', function() {
  this.bacon = 3;
  return Promise.resolve();
});

hooks.pre('cook', function() {
  return new Promise(resolve => {
    this.eggs = 4;
    setTimeout(function() {
      resolve();
    }, 10);
  });
});

hooks.pre('cook', function() {
  this.waffles = false;
  return Promise.resolve();
});

hooks.post('cook', function(obj) {
  obj.tofu = 'no';
});

const obj = { bacon: 0, eggs: 0 };

const cook = hooks.createWrapper(
  'cook',
  function(o) {
    assert.equal(3, obj.bacon);
    assert.equal(4, obj.eggs);
    assert.equal(false, obj.waffles);
    assert.equal(undefined, obj.tofu);
    return o;
  },
  obj);

const result = await cook(obj);
assert.equal(obj.bacon, 3);
assert.equal(obj.eggs, 4);
assert.equal(obj.waffles, false);
assert.equal(obj.tofu, 'no');

assert.equal(result, obj);

clone()

It clones a Kareem object

const k1 = new Kareem();
k1.pre('cook', function() {});
k1.post('cook', function() {});

const k2 = k1.clone();
assert.deepEqual(Array.from(k2._pres.keys()), ['cook']);
assert.deepEqual(Array.from(k2._posts.keys()), ['cook']);

merge()

It pulls hooks from another Kareem object

const k1 = new Kareem();
const test1 = function() {};
k1.pre('cook', test1);
k1.post('cook', function() {});

const k2 = new Kareem();
const test2 = function() {};
k2.pre('cook', test2);
const k3 = k2.merge(k1);
assert.equal(k3._pres.get('cook').length, 2);
assert.equal(k3._pres.get('cook')[0].fn, test2);
assert.equal(k3._pres.get('cook')[1].fn, test1);
assert.equal(k3._posts.get('cook').length, 1);