import { __extends } from "tslib";
import { eventFromException, eventFromMessage } from '@sentry/browser';
import { BaseBackend, getCurrentHub } from '@sentry/core';
import { walk as walkUtil } from '@sentry/utils';
import { getNameFallback, IPC } from '../common';
import { requiresNativeHandlerRenderer } from '../electron-version';
/** Walks an object to perform a normalization on it with a maximum depth of 50 */
function walk(key, value) {
    return walkUtil(key, value, 50);
}
/** Requires and returns electron or undefined if it's unavailable  */
function requireElectron() {
    try {
        return require('electron');
    }
    catch (e) {
        //
    }
    return undefined;
}
/** Timeout used for registering with the main process. */
var PING_TIMEOUT = 500;
/** Backend implementation for Electron renderer backends. */
var RendererBackend = /** @class */ (function (_super) {
    __extends(RendererBackend, _super);
    /** Creates a new Electron backend instance. */
    function RendererBackend(options) {
        var _this = this;
        // Disable session tracking until we've decided how this should work with Electron
        options.autoSessionTracking = false;
        if (options.enableJavaScript === false) {
            options.enabled = false;
        }
        _this = _super.call(this, options) || this;
        var electron = requireElectron();
        if (electron) {
            // We are either in a preload script or nodeIntegration is enabled
            if (_this._isNativeEnabled()) {
                _this._installNativeHandler(electron.crashReporter);
            }
            _this._hookIPC(electron.ipcRenderer, electron.contextBridge);
            _this._pingMainProcess();
        }
        else {
            // We are in a renderer with contextIsolation = true
            if (window.__SENTRY_IPC__ == undefined) {
                // eslint-disable-next-line no-console
                console.warn('contextIsolation is enabled but IPC has not been exposed. Did you call "init" in the preload script?');
            }
        }
        _this._setupScopeListener();
        return _this;
    }
    /**
     * @inheritDoc
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
    RendererBackend.prototype.eventFromException = function (exception, hint) {
        return eventFromException(this._options, exception, hint);
    };
    /**
     * @inheritDoc
     */
    RendererBackend.prototype.eventFromMessage = function (message, level, hint) {
        return eventFromMessage(this._options, message, level, hint);
    };
    /**
     * @inheritDoc
     */
    RendererBackend.prototype.sendEvent = function (event) {
        var _a;
        // Ensure breadcrumbs is not `undefined` as `walk` translates it into a string
        event.breadcrumbs = event.breadcrumbs || [];
        (_a = window.__SENTRY_IPC__) === null || _a === void 0 ? void 0 : _a.sendEvent(event);
    };
    /**
     * Attaches IPC methods to window and uses contextBridge when available
     */
    RendererBackend.prototype._hookIPC = function (ipcRenderer, contextBridge) {
        var ipcObject = {
            // We pass through JSON because in Electron >= 8, IPC uses v8's structured clone algorithm and throws errors if
            // objects have functions. Calling walk makes sure to break circular references.
            sendScope: function (scope) { return ipcRenderer.send(IPC.SCOPE, JSON.stringify(scope, walk)); },
            sendEvent: function (event) { return ipcRenderer.send(IPC.EVENT, JSON.stringify(event, walk)); },
            pingMain: function (success) {
                ipcRenderer.once(IPC.PING, function () {
                    success();
                });
                ipcRenderer.send(IPC.PING);
            },
        };
        window.__SENTRY_IPC__ = ipcObject;
        // We attempt to use contextBridge if it's available (Electron >= 6)
        if (contextBridge) {
            // This will fail if contextIsolation is not enabled but we have no other way to detect this from the renderer
            try {
                contextBridge.exposeInMainWorld('__SENTRY_IPC__', ipcObject);
            }
            catch (e) {
                //
            }
        }
    };
    /**
     * Sends the scope to the main process once it updates.
     */
    RendererBackend.prototype._setupScopeListener = function () {
        var scope = getCurrentHub().getScope();
        if (scope) {
            scope.addScopeListener(function (updatedScope) {
                var _a;
                (_a = window.__SENTRY_IPC__) === null || _a === void 0 ? void 0 : _a.sendScope(updatedScope);
                scope.clearBreadcrumbs();
            });
        }
    };
    /** Returns whether native reports are enabled. */
    RendererBackend.prototype._isNativeEnabled = function () {
        // On macOS, we should start the Electron CrashReporter only in the main
        // process. It uses Crashpad internally, which will catch errors from all
        // sub processes thanks to out-of-processes crash handling. On other
        // platforms we need to start the CrashReporter in every sub process. For
        // more information see: https://goo.gl/nhqqwD
        if (process.platform === 'darwin') {
            return false;
        }
        // Mac AppStore builds cannot run the crash reporter due to the sandboxing
        // requirements. In this case, we prevent enabling native crashes entirely.
        // https://electronjs.org/docs/tutorial/mac-app-store-submission-guide#limitations-of-mas-build
        if (process.mas) {
            return false;
        }
        return this._options.enableNative !== false;
    };
    /** Activates the Electron CrashReporter. */
    RendererBackend.prototype._installNativeHandler = function (crashReporter) {
        // this is only necessary for electron versions before 8
        if (!requiresNativeHandlerRenderer()) {
            return;
        }
        // We will manually submit errors, but CrashReporter requires a submitURL in
        // some versions. Also, provide a productName and companyName, which we will
        // add manually to the event's context during submission.
        crashReporter.start({
            companyName: '',
            ignoreSystemCrashHandler: true,
            productName: this._options.appName || getNameFallback(),
            submitURL: '',
            uploadToServer: false,
        });
    };
    /** Checks if the main processes is available and logs a warning if not. */
    RendererBackend.prototype._pingMainProcess = function () {
        // For whatever reason we have to wait PING_TIMEOUT until we send the ping
        // to main.
        setTimeout(function () {
            var _a;
            var timeout = setTimeout(function () {
                // eslint-disable-next-line no-console
                console.warn('Could not connect to Sentry main process. Did you call init in the Electron main process?');
            }, PING_TIMEOUT);
            (_a = window.__SENTRY_IPC__) === null || _a === void 0 ? void 0 : _a.pingMain(function () { return clearTimeout(timeout); });
        }, PING_TIMEOUT);
    };
    return RendererBackend;
}(BaseBackend));
export { RendererBackend };
//# sourceMappingURL=backend.js.map