/** @license MIT License (c) copyright 2010-2014 original author or authors */ /** @author Brian Cavalier */ /** @author John Hann */ (function(define) { 'use strict'; define(function(require) { var defaultStackJumpSeparator = 'from execution context:'; var defaultStackFilter = /[\s\(\/\\](node|module|timers)\.js:|when([\/\\]{1,2}(lib|monitor|es6-shim)[\/\\]{1,2}|\.js)|(new\sPromise)\b|(\b(PromiseMonitor|ConsoleReporter|Scheduler|RunHandlerTask|ProgressTask|Promise|.*Handler)\.[\w_]\w\w+\b)|\b(tryCatch\w+|getHandler\w*)\b/i; var setTimer = require('../lib/env').setTimer; var error = require('./error'); var executionContext = []; function PromiseMonitor(reporter) { this.logDelay = 0; this.stackFilter = defaultStackFilter; this.stackJumpSeparator = defaultStackJumpSeparator; this.filterDuplicateFrames = true; this._reporter = reporter; if(typeof reporter.configurePromiseMonitor === 'function') { reporter.configurePromiseMonitor(this); } this._traces = []; this._traceTask = 0; var self = this; this._doLogTraces = function() { self._logTraces(); }; } PromiseMonitor.prototype.monitor = function(Promise) { var self = this; Promise.createContext = function(p, context) { p.context = self.createContext(p, context); }; Promise.enterContext = function(p) { executionContext.push(p.context); }; Promise.exitContext = function() { executionContext.pop(); }; Promise.onPotentiallyUnhandledRejection = function(rejection, extraContext) { return self.addTrace(rejection, extraContext); }; Promise.onPotentiallyUnhandledRejectionHandled = function(rejection) { return self.removeTrace(rejection); }; Promise.onFatalRejection = function(rejection, extraContext) { return self.fatal(rejection, extraContext); }; return this; }; PromiseMonitor.prototype.createContext = function(at, parentContext) { var context = { parent: parentContext || executionContext[executionContext.length - 1], stack: void 0 }; error.captureStack(context, at.constructor); return context; }; PromiseMonitor.prototype.addTrace = function(handler, extraContext) { var t, i; for(i = this._traces.length-1; i >= 0; --i) { t = this._traces[i]; if(t.handler === handler) { break; } } if(i >= 0) { t.extraContext = extraContext; } else { this._traces.push({ handler: handler, extraContext: extraContext }); } this.logTraces(); }; PromiseMonitor.prototype.removeTrace = function(/*handler*/) { this.logTraces(); }; PromiseMonitor.prototype.fatal = function(handler, extraContext) { var err = new Error(); err.stack = this._createLongTrace(handler.value, handler.context, extraContext).join('\n'); setTimer(function() { throw err; }, 0); }; PromiseMonitor.prototype.logTraces = function() { if(!this._traceTask) { this._traceTask = setTimer(this._doLogTraces, this.logDelay); } }; PromiseMonitor.prototype._logTraces = function() { this._traceTask = void 0; this._traces = this._traces.filter(filterHandled); this._reporter.log(this.formatTraces(this._traces)); }; PromiseMonitor.prototype.formatTraces = function(traces) { return traces.map(function(t) { return this._createLongTrace(t.handler.value, t.handler.context, t.extraContext); }, this); }; PromiseMonitor.prototype._createLongTrace = function(e, context, extraContext) { var trace = error.parse(e) || [String(e) + ' (WARNING: non-Error used)']; trace = filterFrames(this.stackFilter, trace, 0); this._appendContext(trace, context); this._appendContext(trace, extraContext); return this.filterDuplicateFrames ? this._removeDuplicates(trace) : trace; }; PromiseMonitor.prototype._removeDuplicates = function(trace) { var seen = {}; var sep = this.stackJumpSeparator; var count = 0; return trace.reduceRight(function(deduped, line, i) { if(i === 0) { deduped.unshift(line); } else if(line === sep) { if(count > 0) { deduped.unshift(line); count = 0; } } else if(!seen[line]) { seen[line] = true; deduped.unshift(line); ++count; } return deduped; }, []); }; PromiseMonitor.prototype._appendContext = function(trace, context) { trace.push.apply(trace, this._createTrace(context)); }; PromiseMonitor.prototype._createTrace = function(traceChain) { var trace = []; var stack; while(traceChain) { stack = error.parse(traceChain); if (stack) { stack = filterFrames(this.stackFilter, stack); appendStack(trace, stack, this.stackJumpSeparator); } traceChain = traceChain.parent; } return trace; }; function appendStack(trace, stack, separator) { if (stack.length > 1) { stack[0] = separator; trace.push.apply(trace, stack); } } function filterFrames(stackFilter, stack) { return stack.filter(function(frame) { return !stackFilter.test(frame); }); } function filterHandled(t) { return !t.handler.handled; } return PromiseMonitor; }); }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));