| 'use strict'; | 
|   | 
| function isPromise(obj) { | 
|   return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; | 
| } | 
|   | 
| /** | 
|  * Return a function that will run a function asynchronously or synchronously | 
|  * | 
|  * example: | 
|  * runAsync(wrappedFunction, callback)(...args); | 
|  * | 
|  * @param   {Function} func  Function to run | 
|  * @param   {Function} cb    Callback function passed the `func` returned value | 
|  * @return  {Function(arguments)} Arguments to pass to `func`. This function will in turn | 
|  *                                return a Promise (Node >= 0.12) or call the callbacks. | 
|  */ | 
|   | 
| var runAsync = module.exports = function (func, cb) { | 
|   cb = cb || function () {}; | 
|   | 
|   return function () { | 
|   | 
|     var args = arguments; | 
|   | 
|     var promise = new Promise(function (resolve, reject) { | 
|       var resolved = false; | 
|       const wrappedResolve = function (value) { | 
|         if (resolved) { | 
|           console.warn('Run-async promise already resolved.') | 
|         } | 
|         resolved = true; | 
|         resolve(value); | 
|       } | 
|   | 
|       var rejected = false; | 
|       const wrappedReject = function (value) { | 
|         if (rejected) { | 
|           console.warn('Run-async promise already rejected.') | 
|         } | 
|         rejected = true; | 
|         reject(value); | 
|       } | 
|   | 
|       var usingCallback = false; | 
|       var callbackConflict = false; | 
|       var contextEnded = false; | 
|   | 
|       var answer = func.apply({ | 
|         async: function () { | 
|           if (contextEnded) { | 
|             console.warn('Run-async async() called outside a valid run-async context, callback will be ignored.'); | 
|             return function() {}; | 
|           } | 
|           if (callbackConflict) { | 
|             console.warn('Run-async wrapped function (async) returned a promise.\nCalls to async() callback can have unexpected results.'); | 
|           } | 
|           usingCallback = true; | 
|           return function (err, value) { | 
|             if (err) { | 
|               wrappedReject(err); | 
|             } else { | 
|               wrappedResolve(value); | 
|             } | 
|           }; | 
|         } | 
|       }, Array.prototype.slice.call(args)); | 
|   | 
|       if (usingCallback) { | 
|         if (isPromise(answer)) { | 
|           console.warn('Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve.'); | 
|         } | 
|       } else { | 
|         if (isPromise(answer)) { | 
|           callbackConflict = true; | 
|           answer.then(wrappedResolve, wrappedReject); | 
|         } else { | 
|           wrappedResolve(answer); | 
|         } | 
|       } | 
|       contextEnded = true; | 
|     }); | 
|   | 
|     promise.then(cb.bind(null, null), cb); | 
|   | 
|     return promise; | 
|   } | 
| }; | 
|   | 
| runAsync.cb = function (func, cb) { | 
|   return runAsync(function () { | 
|     var args = Array.prototype.slice.call(arguments); | 
|     if (args.length === func.length - 1) { | 
|       args.push(this.async()); | 
|     } | 
|     return func.apply(this, args); | 
|   }, cb); | 
| }; |