1'use strict';
2
3var Promise = require('./core');
4
5var DEFAULT_WHITELIST = [
6  ReferenceError,
7  TypeError,
8  RangeError
9];
10
11var enabled = false;
12exports.disable = disable;
13function disable() {
14  enabled = false;
15  Promise._onHandle = null;
16  Promise._onReject = null;
17}
18
19exports.enable = enable;
20function enable(options) {
21  options = options || {};
22  if (enabled) disable();
23  enabled = true;
24  var id = 0;
25  var displayId = 0;
26  var rejections = {};
27  Promise._onHandle = function (promise) {
28    if (
29      promise._state === 2 && // IS REJECTED
30      rejections[promise._rejectionId]
31    ) {
32      if (rejections[promise._rejectionId].logged) {
33        onHandled(promise._rejectionId);
34      } else {
35        clearTimeout(rejections[promise._rejectionId].timeout);
36      }
37      delete rejections[promise._rejectionId];
38    }
39  };
40  Promise._onReject = function (promise, err) {
41    if (promise._deferredState === 0) { // not yet handled
42      promise._rejectionId = id++;
43      rejections[promise._rejectionId] = {
44        displayId: null,
45        error: err,
46        timeout: setTimeout(
47          onUnhandled.bind(null, promise._rejectionId),
48          // For reference errors and type errors, this almost always
49          // means the programmer made a mistake, so log them after just
50          // 100ms
51          // otherwise, wait 2 seconds to see if they get handled
52          matchWhitelist(err, DEFAULT_WHITELIST)
53            ? 100
54            : 2000
55        ),
56        logged: false
57      };
58    }
59  };
60  function onUnhandled(id) {
61    if (
62      options.allRejections ||
63      matchWhitelist(
64        rejections[id].error,
65        options.whitelist || DEFAULT_WHITELIST
66      )
67    ) {
68      rejections[id].displayId = displayId++;
69      if (options.onUnhandled) {
70        rejections[id].logged = true;
71        options.onUnhandled(
72          rejections[id].displayId,
73          rejections[id].error
74        );
75      } else {
76        rejections[id].logged = true;
77        logError(
78          rejections[id].displayId,
79          rejections[id].error
80        );
81      }
82    }
83  }
84  function onHandled(id) {
85    if (rejections[id].logged) {
86      if (options.onHandled) {
87        options.onHandled(rejections[id].displayId, rejections[id].error);
88      } else if (!rejections[id].onUnhandled) {
89        console.warn(
90          'Promise Rejection Handled (id: ' + rejections[id].displayId + '):'
91        );
92        console.warn(
93          '  This means you can ignore any previous messages of the form "Possible Unhandled Promise Rejection" with id ' +
94          rejections[id].displayId + '.'
95        );
96      }
97    }
98  }
99}
100
101function logError(id, error) {
102  console.warn('Possible Unhandled Promise Rejection (id: ' + id + '):');
103  var errStr = (error && (error.stack || error)) + '';
104  errStr.split('\n').forEach(function (line) {
105    console.warn('  ' + line);
106  });
107}
108
109function matchWhitelist(error, list) {
110  return list.some(function (cls) {
111    return error instanceof cls;
112  });
113}