You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					513 lines
				
				13 KiB
			
		
		
			
		
	
	
					513 lines
				
				13 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function Kareem() {
							 | 
						||
| 
								 | 
							
								  this._pres = new Map();
							 | 
						||
| 
								 | 
							
								  this._posts = new Map();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.execPre = function(name, context, args, callback) {
							 | 
						||
| 
								 | 
							
								  if (arguments.length === 3) {
							 | 
						||
| 
								 | 
							
								    callback = args;
							 | 
						||
| 
								 | 
							
								    args = [];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  var pres = get(this._pres, name, []);
							 | 
						||
| 
								 | 
							
								  var numPres = pres.length;
							 | 
						||
| 
								 | 
							
								  var numAsyncPres = pres.numAsync || 0;
							 | 
						||
| 
								 | 
							
								  var currentPre = 0;
							 | 
						||
| 
								 | 
							
								  var asyncPresLeft = numAsyncPres;
							 | 
						||
| 
								 | 
							
								  var done = false;
							 | 
						||
| 
								 | 
							
								  var $args = args;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!numPres) {
							 | 
						||
| 
								 | 
							
								    return process.nextTick(function() {
							 | 
						||
| 
								 | 
							
								      callback(null);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var next = function() {
							 | 
						||
| 
								 | 
							
								    if (currentPre >= numPres) {
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var pre = pres[currentPre];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (pre.isAsync) {
							 | 
						||
| 
								 | 
							
								      var args = [
							 | 
						||
| 
								 | 
							
								        decorateNextFn(_next),
							 | 
						||
| 
								 | 
							
								        decorateNextFn(function(error) {
							 | 
						||
| 
								 | 
							
								          if (error) {
							 | 
						||
| 
								 | 
							
								            if (done) {
							 | 
						||
| 
								 | 
							
								              return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            done = true;
							 | 
						||
| 
								 | 
							
								            return callback(error);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          if (--asyncPresLeft === 0 && currentPre >= numPres) {
							 | 
						||
| 
								 | 
							
								            return callback(null);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								      ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      callMiddlewareFunction(pre.fn, context, args, args[0]);
							 | 
						||
| 
								 | 
							
								    } else if (pre.fn.length > 0) {
							 | 
						||
| 
								 | 
							
								      var args = [decorateNextFn(_next)];
							 | 
						||
| 
								 | 
							
								      var _args = arguments.length >= 2 ? arguments : [null].concat($args);
							 | 
						||
| 
								 | 
							
								      for (var i = 1; i < _args.length; ++i) {
							 | 
						||
| 
								 | 
							
								        args.push(_args[i]);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      callMiddlewareFunction(pre.fn, context, args, args[0]);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      let maybePromise = null;
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        maybePromise = pre.fn.call(context);
							 | 
						||
| 
								 | 
							
								      } catch (err) {
							 | 
						||
| 
								 | 
							
								        if (err != null) {
							 | 
						||
| 
								 | 
							
								          return callback(err);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (isPromise(maybePromise)) {
							 | 
						||
| 
								 | 
							
								        maybePromise.then(() => _next(), err => _next(err));
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        if (++currentPre >= numPres) {
							 | 
						||
| 
								 | 
							
								          if (asyncPresLeft > 0) {
							 | 
						||
| 
								 | 
							
								            // Leave parallel hooks to run
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            return process.nextTick(function() {
							 | 
						||
| 
								 | 
							
								              callback(null);
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        next();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  next.apply(null, [null].concat(args));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function _next(error) {
							 | 
						||
| 
								 | 
							
								    if (error) {
							 | 
						||
| 
								 | 
							
								      if (done) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      done = true;
							 | 
						||
| 
								 | 
							
								      return callback(error);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (++currentPre >= numPres) {
							 | 
						||
| 
								 | 
							
								      if (asyncPresLeft > 0) {
							 | 
						||
| 
								 | 
							
								        // Leave parallel hooks to run
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        return callback(null);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    next.apply(context, arguments);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.execPreSync = function(name, context, args) {
							 | 
						||
| 
								 | 
							
								  var pres = get(this._pres, name, []);
							 | 
						||
| 
								 | 
							
								  var numPres = pres.length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (var i = 0; i < numPres; ++i) {
							 | 
						||
| 
								 | 
							
								    pres[i].fn.apply(context, args || []);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.execPost = function(name, context, args, options, callback) {
							 | 
						||
| 
								 | 
							
								  if (arguments.length < 5) {
							 | 
						||
| 
								 | 
							
								    callback = options;
							 | 
						||
| 
								 | 
							
								    options = null;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  var posts = get(this._posts, name, []);
							 | 
						||
| 
								 | 
							
								  var numPosts = posts.length;
							 | 
						||
| 
								 | 
							
								  var currentPost = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var firstError = null;
							 | 
						||
| 
								 | 
							
								  if (options && options.error) {
							 | 
						||
| 
								 | 
							
								    firstError = options.error;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!numPosts) {
							 | 
						||
| 
								 | 
							
								    return process.nextTick(function() {
							 | 
						||
| 
								 | 
							
								      callback.apply(null, [firstError].concat(args));
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var next = function() {
							 | 
						||
| 
								 | 
							
								    var post = posts[currentPost].fn;
							 | 
						||
| 
								 | 
							
								    var numArgs = 0;
							 | 
						||
| 
								 | 
							
								    var argLength = args.length;
							 | 
						||
| 
								 | 
							
								    var newArgs = [];
							 | 
						||
| 
								 | 
							
								    for (var i = 0; i < argLength; ++i) {
							 | 
						||
| 
								 | 
							
								      numArgs += args[i] && args[i]._kareemIgnore ? 0 : 1;
							 | 
						||
| 
								 | 
							
								      if (!args[i] || !args[i]._kareemIgnore) {
							 | 
						||
| 
								 | 
							
								        newArgs.push(args[i]);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (firstError) {
							 | 
						||
| 
								 | 
							
								      if (post.length === numArgs + 2) {
							 | 
						||
| 
								 | 
							
								        var _cb = decorateNextFn(function(error) {
							 | 
						||
| 
								 | 
							
								          if (error) {
							 | 
						||
| 
								 | 
							
								            firstError = error;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          if (++currentPost >= numPosts) {
							 | 
						||
| 
								 | 
							
								            return callback.call(null, firstError);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          next();
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        callMiddlewareFunction(post, context,
							 | 
						||
| 
								 | 
							
								          [firstError].concat(newArgs).concat([_cb]), _cb);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        if (++currentPost >= numPosts) {
							 | 
						||
| 
								 | 
							
								          return callback.call(null, firstError);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        next();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      const _cb = decorateNextFn(function(error) {
							 | 
						||
| 
								 | 
							
								        if (error) {
							 | 
						||
| 
								 | 
							
								          firstError = error;
							 | 
						||
| 
								 | 
							
								          return next();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (++currentPost >= numPosts) {
							 | 
						||
| 
								 | 
							
								          return callback.apply(null, [null].concat(args));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        next();
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (post.length === numArgs + 2) {
							 | 
						||
| 
								 | 
							
								        // Skip error handlers if no error
							 | 
						||
| 
								 | 
							
								        if (++currentPost >= numPosts) {
							 | 
						||
| 
								 | 
							
								          return callback.apply(null, [null].concat(args));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return next();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (post.length === numArgs + 1) {
							 | 
						||
| 
								 | 
							
								        callMiddlewareFunction(post, context, newArgs.concat([_cb]), _cb);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        let error;
							 | 
						||
| 
								 | 
							
								        let maybePromise;
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          maybePromise = post.apply(context, newArgs);
							 | 
						||
| 
								 | 
							
								        } catch (err) {
							 | 
						||
| 
								 | 
							
								          error = err;
							 | 
						||
| 
								 | 
							
								          firstError = err;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (isPromise(maybePromise)) {
							 | 
						||
| 
								 | 
							
								          return maybePromise.then(() => _cb(), err => _cb(err));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (++currentPost >= numPosts) {
							 | 
						||
| 
								 | 
							
								          return callback.apply(null, [error].concat(args));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        next(error);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  next();
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.execPostSync = function(name, context, args) {
							 | 
						||
| 
								 | 
							
								  const posts = get(this._posts, name, []);
							 | 
						||
| 
								 | 
							
								  const numPosts = posts.length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < numPosts; ++i) {
							 | 
						||
| 
								 | 
							
								    posts[i].fn.apply(context, args || []);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.createWrapperSync = function(name, fn) {
							 | 
						||
| 
								 | 
							
								  var kareem = this;
							 | 
						||
| 
								 | 
							
								  return function syncWrapper() {
							 | 
						||
| 
								 | 
							
								    kareem.execPreSync(name, this, arguments);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var toReturn = fn.apply(this, arguments);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    kareem.execPostSync(name, this, [toReturn]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return toReturn;
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function _handleWrapError(instance, error, name, context, args, options, callback) {
							 | 
						||
| 
								 | 
							
								  if (options.useErrorHandlers) {
							 | 
						||
| 
								 | 
							
								    var _options = { error: error };
							 | 
						||
| 
								 | 
							
								    return instance.execPost(name, context, args, _options, function(error) {
							 | 
						||
| 
								 | 
							
								      return typeof callback === 'function' && callback(error);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return typeof callback === 'function' ?
							 | 
						||
| 
								 | 
							
								      callback(error) :
							 | 
						||
| 
								 | 
							
								      undefined;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.wrap = function(name, fn, context, args, options) {
							 | 
						||
| 
								 | 
							
								  const lastArg = (args.length > 0 ? args[args.length - 1] : null);
							 | 
						||
| 
								 | 
							
								  const argsWithoutCb = typeof lastArg === 'function' ?
							 | 
						||
| 
								 | 
							
								    args.slice(0, args.length - 1) :
							 | 
						||
| 
								 | 
							
								    args;
							 | 
						||
| 
								 | 
							
								  const _this = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  options = options || {};
							 | 
						||
| 
								 | 
							
								  const checkForPromise = options.checkForPromise;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.execPre(name, context, args, function(error) {
							 | 
						||
| 
								 | 
							
								    if (error) {
							 | 
						||
| 
								 | 
							
								      const numCallbackParams = options.numCallbackParams || 0;
							 | 
						||
| 
								 | 
							
								      const errorArgs = options.contextParameter ? [context] : [];
							 | 
						||
| 
								 | 
							
								      for (var i = errorArgs.length; i < numCallbackParams; ++i) {
							 | 
						||
| 
								 | 
							
								        errorArgs.push(null);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return _handleWrapError(_this, error, name, context, errorArgs,
							 | 
						||
| 
								 | 
							
								        options, lastArg);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const end = (typeof lastArg === 'function' ? args.length - 1 : args.length);
							 | 
						||
| 
								 | 
							
								    const numParameters = fn.length;
							 | 
						||
| 
								 | 
							
								    const ret = fn.apply(context, args.slice(0, end).concat(_cb));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (checkForPromise) {
							 | 
						||
| 
								 | 
							
								      if (ret != null && typeof ret.then === 'function') {
							 | 
						||
| 
								 | 
							
								        // Thenable, use it
							 | 
						||
| 
								 | 
							
								        return ret.then(
							 | 
						||
| 
								 | 
							
								          res => _cb(null, res),
							 | 
						||
| 
								 | 
							
								          err => _cb(err)
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // If `fn()` doesn't have a callback argument and doesn't return a
							 | 
						||
| 
								 | 
							
								      // promise, assume it is sync
							 | 
						||
| 
								 | 
							
								      if (numParameters < end + 1) {
							 | 
						||
| 
								 | 
							
								        return _cb(null, ret);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function _cb() {
							 | 
						||
| 
								 | 
							
								      const args = arguments;
							 | 
						||
| 
								 | 
							
								      const argsWithoutError = Array.prototype.slice.call(arguments, 1);
							 | 
						||
| 
								 | 
							
								      if (options.nullResultByDefault && argsWithoutError.length === 0) {
							 | 
						||
| 
								 | 
							
								        argsWithoutError.push(null);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (arguments[0]) {
							 | 
						||
| 
								 | 
							
								        // Assume error
							 | 
						||
| 
								 | 
							
								        return _handleWrapError(_this, arguments[0], name, context,
							 | 
						||
| 
								 | 
							
								          argsWithoutError, options, lastArg);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        _this.execPost(name, context, argsWithoutError, function() {
							 | 
						||
| 
								 | 
							
								          if (arguments[0]) {
							 | 
						||
| 
								 | 
							
								            return typeof lastArg === 'function' ?
							 | 
						||
| 
								 | 
							
								              lastArg(arguments[0]) :
							 | 
						||
| 
								 | 
							
								              undefined;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          return typeof lastArg === 'function' ?
							 | 
						||
| 
								 | 
							
								            lastArg.apply(context, arguments) :
							 | 
						||
| 
								 | 
							
								            undefined;
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.filter = function(fn) {
							 | 
						||
| 
								 | 
							
								  const clone = this.clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const pres = Array.from(clone._pres.keys());
							 | 
						||
| 
								 | 
							
								  for (const name of pres) {
							 | 
						||
| 
								 | 
							
								    const hooks = this._pres.get(name).
							 | 
						||
| 
								 | 
							
								      map(h => Object.assign({}, h, { name: name })).
							 | 
						||
| 
								 | 
							
								      filter(fn);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (hooks.length === 0) {
							 | 
						||
| 
								 | 
							
								      clone._pres.delete(name);
							 | 
						||
| 
								 | 
							
								      continue;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    hooks.numAsync = hooks.filter(h => h.isAsync).length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    clone._pres.set(name, hooks);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const posts = Array.from(clone._posts.keys());
							 | 
						||
| 
								 | 
							
								  for (const name of posts) {
							 | 
						||
| 
								 | 
							
								    const hooks = this._posts.get(name).
							 | 
						||
| 
								 | 
							
								      map(h => Object.assign({}, h, { name: name })).
							 | 
						||
| 
								 | 
							
								      filter(fn);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (hooks.length === 0) {
							 | 
						||
| 
								 | 
							
								      clone._posts.delete(name);
							 | 
						||
| 
								 | 
							
								      continue;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    clone._posts.set(name, hooks);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return clone;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.hasHooks = function(name) {
							 | 
						||
| 
								 | 
							
								  return this._pres.has(name) || this._posts.has(name);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.createWrapper = function(name, fn, context, options) {
							 | 
						||
| 
								 | 
							
								  var _this = this;
							 | 
						||
| 
								 | 
							
								  if (!this.hasHooks(name)) {
							 | 
						||
| 
								 | 
							
								    // Fast path: if there's no hooks for this function, just return the
							 | 
						||
| 
								 | 
							
								    // function wrapped in a nextTick()
							 | 
						||
| 
								 | 
							
								    return function() {
							 | 
						||
| 
								 | 
							
								      process.nextTick(() => fn.apply(this, arguments));
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return function() {
							 | 
						||
| 
								 | 
							
								    var _context = context || this;
							 | 
						||
| 
								 | 
							
								    var args = Array.prototype.slice.call(arguments);
							 | 
						||
| 
								 | 
							
								    _this.wrap(name, fn, _context, args, options);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.pre = function(name, isAsync, fn, error, unshift) {
							 | 
						||
| 
								 | 
							
								  let options = {};
							 | 
						||
| 
								 | 
							
								  if (typeof isAsync === 'object' && isAsync != null) {
							 | 
						||
| 
								 | 
							
								    options = isAsync;
							 | 
						||
| 
								 | 
							
								    isAsync = options.isAsync;
							 | 
						||
| 
								 | 
							
								  } else if (typeof arguments[1] !== 'boolean') {
							 | 
						||
| 
								 | 
							
								    error = fn;
							 | 
						||
| 
								 | 
							
								    fn = isAsync;
							 | 
						||
| 
								 | 
							
								    isAsync = false;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const pres = get(this._pres, name, []);
							 | 
						||
| 
								 | 
							
								  this._pres.set(name, pres);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (isAsync) {
							 | 
						||
| 
								 | 
							
								    pres.numAsync = pres.numAsync || 0;
							 | 
						||
| 
								 | 
							
								    ++pres.numAsync;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof fn !== 'function') {
							 | 
						||
| 
								 | 
							
								    throw new Error('pre() requires a function, got "' + typeof fn + '"');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (unshift) {
							 | 
						||
| 
								 | 
							
								    pres.unshift(Object.assign({}, options, { fn: fn, isAsync: isAsync }));
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    pres.push(Object.assign({}, options, { fn: fn, isAsync: isAsync }));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.post = function(name, options, fn, unshift) {
							 | 
						||
| 
								 | 
							
								  const hooks = get(this._posts, name, []);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof options === 'function') {
							 | 
						||
| 
								 | 
							
								    unshift = !!fn;
							 | 
						||
| 
								 | 
							
								    fn = options;
							 | 
						||
| 
								 | 
							
								    options = {};
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof fn !== 'function') {
							 | 
						||
| 
								 | 
							
								    throw new Error('post() requires a function, got "' + typeof fn + '"');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (unshift) {
							 | 
						||
| 
								 | 
							
								    hooks.unshift(Object.assign({}, options, { fn: fn }));
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    hooks.push(Object.assign({}, options, { fn: fn }));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  this._posts.set(name, hooks);
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.clone = function() {
							 | 
						||
| 
								 | 
							
								  const n = new Kareem();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (let key of this._pres.keys()) {
							 | 
						||
| 
								 | 
							
								    const clone = this._pres.get(key).slice();
							 | 
						||
| 
								 | 
							
								    clone.numAsync = this._pres.get(key).numAsync;
							 | 
						||
| 
								 | 
							
								    n._pres.set(key, clone);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  for (let key of this._posts.keys()) {
							 | 
						||
| 
								 | 
							
								    n._posts.set(key, this._posts.get(key).slice());
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return n;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Kareem.prototype.merge = function(other, clone) {
							 | 
						||
| 
								 | 
							
								  clone = arguments.length === 1 ? true : clone;
							 | 
						||
| 
								 | 
							
								  var ret = clone ? this.clone() : this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (let key of other._pres.keys()) {
							 | 
						||
| 
								 | 
							
								    const sourcePres = get(ret._pres, key, []);
							 | 
						||
| 
								 | 
							
								    const deduplicated = other._pres.get(key).
							 | 
						||
| 
								 | 
							
								      // Deduplicate based on `fn`
							 | 
						||
| 
								 | 
							
								      filter(p => sourcePres.map(_p => _p.fn).indexOf(p.fn) === -1);
							 | 
						||
| 
								 | 
							
								    const combined = sourcePres.concat(deduplicated);
							 | 
						||
| 
								 | 
							
								    combined.numAsync = sourcePres.numAsync || 0;
							 | 
						||
| 
								 | 
							
								    combined.numAsync += deduplicated.filter(p => p.isAsync).length;
							 | 
						||
| 
								 | 
							
								    ret._pres.set(key, combined);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  for (let key of other._posts.keys()) {
							 | 
						||
| 
								 | 
							
								    const sourcePosts = get(ret._posts, key, []);
							 | 
						||
| 
								 | 
							
								    const deduplicated = other._posts.get(key).
							 | 
						||
| 
								 | 
							
								      filter(p => sourcePosts.indexOf(p) === -1);
							 | 
						||
| 
								 | 
							
								    ret._posts.set(key, sourcePosts.concat(deduplicated));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return ret;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function get(map, key, def) {
							 | 
						||
| 
								 | 
							
								  if (map.has(key)) {
							 | 
						||
| 
								 | 
							
								    return map.get(key);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return def;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function callMiddlewareFunction(fn, context, args, next) {
							 | 
						||
| 
								 | 
							
								  let maybePromise;
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    maybePromise = fn.apply(context, args);
							 | 
						||
| 
								 | 
							
								  } catch (error) {
							 | 
						||
| 
								 | 
							
								    return next(error);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (isPromise(maybePromise)) {
							 | 
						||
| 
								 | 
							
								    maybePromise.then(() => next(), err => next(err));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isPromise(v) {
							 | 
						||
| 
								 | 
							
								  return v != null && typeof v.then === 'function';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function decorateNextFn(fn) {
							 | 
						||
| 
								 | 
							
								  var called = false;
							 | 
						||
| 
								 | 
							
								  var _this = this;
							 | 
						||
| 
								 | 
							
								  return function() {
							 | 
						||
| 
								 | 
							
								    // Ensure this function can only be called once
							 | 
						||
| 
								 | 
							
								    if (called) {
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    called = true;
							 | 
						||
| 
								 | 
							
								    // Make sure to clear the stack so try/catch doesn't catch errors
							 | 
						||
| 
								 | 
							
								    // in subsequent middleware
							 | 
						||
| 
								 | 
							
								    return process.nextTick(() => fn.apply(_this, arguments));
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Kareem;
							 |