Object.defineProperty(exports, '__esModule', { value: true });

const core = require('@sentry/core');
const utils = require('@sentry/utils');
const errorhandling = require('./utils/errorhandling.js');

/** Global Exception handler */
class OnUncaughtException  {
  /**
   * @inheritDoc
   */
   static __initStatic() {this.id = 'OnUncaughtException';}

  /**
   * @inheritDoc
   */
   __init() {this.name = OnUncaughtException.id;}

  /**
   * @inheritDoc
   */
    __init2() {this.handler = this._makeErrorHandler();}

  // CAREFUL: Please think twice before updating the way _options looks because the Next.js SDK depends on it in `index.server.ts`

  /**
   * @inheritDoc
   */
   constructor(options = {}) {OnUncaughtException.prototype.__init.call(this);OnUncaughtException.prototype.__init2.call(this);
    this._options = {
      exitEvenIfOtherHandlersAreRegistered: true,
      ...options,
    };
  }

  /**
   * @inheritDoc
   */
   setupOnce() {
    global.process.on('uncaughtException', this.handler);
  }

  /**
   * @hidden
   */
   _makeErrorHandler() {
    const timeout = 2000;
    let caughtFirstError = false;
    let caughtSecondError = false;
    let calledFatalError = false;
    let firstError;

    return (error) => {
      let onFatalError = errorhandling.logAndExitProcess;
      const client = core.getCurrentHub().getClient();

      if (this._options.onFatalError) {
        onFatalError = this._options.onFatalError;
      } else if (client && client.getOptions().onFatalError) {
        onFatalError = client.getOptions().onFatalError ;
      }

      // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not
      // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust
      // exit behaviour of the SDK accordingly:
      // - If other listeners are attached, do not exit.
      // - If the only listener attached is ours, exit.
      const userProvidedListenersCount = (
        global.process.listeners('uncaughtException')
      ).reduce((acc, listener) => {
        if (
          // There are 3 listeners we ignore:
          listener.name === 'domainUncaughtExceptionClear' || // as soon as we're using domains this listener is attached by node itself
          (listener.tag && listener.tag === 'sentry_tracingErrorCallback') || // the handler we register for tracing
          listener === this.handler // the handler we register in this integration
        ) {
          return acc;
        } else {
          return acc + 1;
        }
      }, 0);

      const processWouldExit = userProvidedListenersCount === 0;
      const shouldApplyFatalHandlingLogic = this._options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;

      if (!caughtFirstError) {
        const hub = core.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.getIntegration(OnUncaughtException)) {
          hub.withScope((scope) => {
            scope.setLevel('fatal');
            hub.captureException(error, {
              originalException: error,
              data: { mechanism: { handled: false, type: 'onuncaughtexception' } },
            });
            if (!calledFatalError && shouldApplyFatalHandlingLogic) {
              calledFatalError = true;
              onFatalError(error);
            }
          });
        } else {
          if (!calledFatalError && shouldApplyFatalHandlingLogic) {
            calledFatalError = true;
            onFatalError(error);
          }
        }
      } else {
        if (shouldApplyFatalHandlingLogic) {
          if (calledFatalError) {
            // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down
            (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
              utils.logger.warn(
                'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',
              );
            errorhandling.logAndExitProcess(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(() => {
              if (!calledFatalError) {
                // it was probably case 1, let's treat err as the sendErr and call onFatalError
                calledFatalError = true;
                onFatalError(firstError, error);
              }
            }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc
          }
        }
      }
    };
  }
} OnUncaughtException.__initStatic();

exports.OnUncaughtException = OnUncaughtException;
//# sourceMappingURL=onuncaughtexception.js.map
