"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var core_1 = require("@sentry/core");
var types_1 = require("@sentry/types");
var logger_1 = require("@sentry/utils/logger");
var handlers_1 = require("../handlers");
/** Global Promise Rejection handler */
var OnUncaughtException = /** @class */ (function () {
    /**
     * @inheritDoc
     */
    function OnUncaughtException(options) {
        if (options === void 0) { options = {}; }
        this.options = options;
        /**
         * @inheritDoc
         */
        this.name = OnUncaughtException.id;
        /**
         * @inheritDoc
         */
        this.handler = makeErrorHandler(
        // tslint:disable-next-line
        this.options.onFatalError);
    }
    /**
     * @inheritDoc
     */
    OnUncaughtException.prototype.setupOnce = function () {
        global.process.on('uncaughtException', this.handler.bind(this));
    };
    /**
     * @inheritDoc
     */
    OnUncaughtException.id = 'OnUncaughtException';
    return OnUncaughtException;
}());
exports.OnUncaughtException = OnUncaughtException;
/** JSDoc */
function makeErrorHandler(onFatalError) {
    var _this = this;
    if (onFatalError === void 0) { onFatalError = handlers_1.defaultOnFatalError; }
    var timeout = 2000;
    var caughtFirstError = false;
    var caughtSecondError = false;
    var calledFatalError = false;
    var firstError;
    return function (error) {
        if (!caughtFirstError) {
            var hub_1 = core_1.getCurrentHub();
            // this is the first uncaught error and the ultimate reason for shutting down
            // we want to do absolutely everything possible to ensure it gets captured
            // also we want to make sure we don't go recursion crazy if more errors happen after this one
            firstError = error;
            caughtFirstError = true;
            if (hub_1.getIntegration(OnUncaughtException)) {
                hub_1.withScope(function (scope) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                    return tslib_1.__generator(this, function (_a) {
                        scope.setLevel(types_1.Severity.Fatal);
                        hub_1.captureException(error, { originalException: error });
                        if (!calledFatalError) {
                            calledFatalError = true;
                            onFatalError(error);
                        }
                        return [2 /*return*/];
                    });
                }); });
            }
            else {
                if (!calledFatalError) {
                    calledFatalError = true;
                    onFatalError(error);
                }
            }
        }
        else if (calledFatalError) {
            // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down
            logger_1.logger.warn('uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown');
            handlers_1.defaultOnFatalError(error);
        }
        else if (!caughtSecondError) {
            // two cases for how we can hit this branch:
            //   - capturing of first error blew up and we just caught the exception from that
            //     - quit trying to capture, proceed with shutdown
            //   - a second independent error happened while waiting for first error to capture
            //     - want to avoid causing premature shutdown before first error capture finishes
            // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff
            // so let's instead just delay a bit before we proceed with our action here
            // in case 1, we just wait a bit unnecessarily but ultimately do the same thing
            // in case 2, the delay hopefully made us wait long enough for the capture to finish
            // two potential nonideal outcomes:
            //   nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError
            //   nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error
            // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)
            //   we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish
            caughtSecondError = true;
            setTimeout(function () {
                if (!calledFatalError) {
                    // it was probably case 1, let's treat err as the sendErr and call onFatalError
                    calledFatalError = true;
                    onFatalError(firstError, error);
                }
                else {
                    // it was probably case 2, our first error finished capturing while we waited, cool, do nothing
                }
            }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc
        }
    };
}
exports.makeErrorHandler = makeErrorHandler;
//# sourceMappingURL=onuncaughtexception.js.map