'use strict'; /* Expose. */ module.exports = trough; /* Methods. */ var slice = [].slice; /* Create new middleware. */ function trough() { var fns = []; var middleware = {}; middleware.run = run; middleware.use = use; return middleware; /* Run `fns`. Last argument must be * a completion handler. */ function run() { var index = -1; var input = slice.call(arguments, 0, -1); var done = arguments[arguments.length - 1]; if (typeof done !== 'function') { throw new Error('Expected function as last argument, not ' + done); } next.apply(null, [null].concat(input)); /* Run the next `fn`, if any. */ function next(err) { var fn = fns[++index]; var params = slice.call(arguments, 0); var values = params.slice(1); var length = input.length; var pos = -1; if (err) { done(err); return; } /* Copy non-nully input into values. */ while (++pos < length) { if (values[pos] === null || values[pos] === undefined) { values[pos] = input[pos]; } } input = values; /* Next or done. */ if (fn) { wrap(fn, next).apply(null, input); } else { done.apply(null, [null].concat(input)); } } } /* Add `fn` to the list. */ function use(fn) { if (typeof fn !== 'function') { throw new Error('Expected `fn` to be a function, not ' + fn); } fns.push(fn); return middleware; } } /* Wrap `fn`. Can be sync or async; return a promise, * receive a completion handler, return new values and * errors. */ function wrap(fn, next) { var invoked; return wrapped; function wrapped() { var params = slice.call(arguments, 0); var callback = fn.length > params.length; var result; if (callback) { params.push(done); } try { result = fn.apply(null, params); } catch (err) { /* Well, this is quite the pickle. `fn` received * a callback and invoked it (thus continuing the * pipeline), but later also threw an error. * We’re not about to restart the pipeline again, * so the only thing left to do is to throw the * thing instea. */ if (callback && invoked) { throw err; } return done(err); } if (!callback) { if (result && typeof result.then === 'function') { result.then(then, done); } else if (result instanceof Error) { done(result); } else { then(result); } } } /* Invoke `next`, only once. */ function done() { if (!invoked) { invoked = true; next.apply(null, arguments); } } /* Invoke `done` with one value. * Tracks if an error is passed, too. */ function then(value) { done(null, value); } }