const core = require('@sentry/core');
const node = require('@sentry/node');
const electron = require('electron');
const envelope = require('../common/envelope.js');
const ipc = require('../common/ipc.js');
const electronNormalize = require('./electron-normalize.js');
const rendererAnr = require('./integrations/renderer-anr.js');
const rendererProfiling = require('./integrations/renderer-profiling.js');
const log = require('./log.js');
const merge = require('./merge.js');
const normalize = require('./normalize.js');
const version = require('./version.js');

let KNOWN_RENDERERS;
let WINDOW_ID_TO_WEB_CONTENTS;
function newProtocolRenderer() {
    KNOWN_RENDERERS = KNOWN_RENDERERS || new Set();
    WINDOW_ID_TO_WEB_CONTENTS = WINDOW_ID_TO_WEB_CONTENTS || new Map();
    for (const wc of electron.webContents.getAllWebContents()) {
        const wcId = wc.id;
        if (KNOWN_RENDERERS.has(wcId)) {
            continue;
        }
        if (!wc.isDestroyed()) {
            wc.executeJavaScript('window.__SENTRY_RENDERER_ID__').then((windowId) => {
                if (windowId && KNOWN_RENDERERS && WINDOW_ID_TO_WEB_CONTENTS) {
                    KNOWN_RENDERERS.add(wcId);
                    WINDOW_ID_TO_WEB_CONTENTS.set(windowId, wcId);
                    wc.once('destroyed', () => {
                        KNOWN_RENDERERS?.delete(wcId);
                        WINDOW_ID_TO_WEB_CONTENTS?.delete(windowId);
                    });
                }
            }, core.debug.error);
        }
    }
}
function captureEventFromRenderer(options, event, dynamicSamplingContext, attachments, contents) {
    const process = contents ? options?.getRendererName?.(contents) || 'renderer' : 'renderer';
    // Ensure breadcrumbs are empty as they sent via scope updates
    event.breadcrumbs = event.breadcrumbs || [];
    // Remove the environment as it defaults to 'production' and overwrites the main process environment
    delete event.environment;
    // Remove the SDK info as we want the Electron SDK to be the one reporting the event
    delete event.sdk?.name;
    delete event.sdk?.version;
    delete event.sdk?.packages;
    if (dynamicSamplingContext) {
        event.sdkProcessingMetadata = { ...event.sdkProcessingMetadata, dynamicSamplingContext };
    }
    node.captureEvent(merge.mergeEvents(event, { tags: { 'event.process': process } }), { attachments });
}
let cached_public_key;
function handleEnvelope(client, options, env, contents) {
    const envelope$1 = core.parseEnvelope(env);
    const [envelopeHeader] = envelope$1;
    const dynamicSamplingContext = envelopeHeader.trace;
    if (dynamicSamplingContext) {
        if (!cached_public_key) {
            const dsn = client.getDsn();
            cached_public_key = dsn?.publicKey;
        }
        dynamicSamplingContext.release = options.release;
        dynamicSamplingContext.environment = options.environment;
        dynamicSamplingContext.public_key = cached_public_key;
    }
    const eventAndAttachments = envelope.eventFromEnvelope(envelope$1);
    if (eventAndAttachments) {
        const [event, attachments, profile] = eventAndAttachments;
        if (profile) {
            // We have a 'profile' item and there is no way for us to pass this through event capture
            // so store them in a cache and reattach them via the `beforeEnvelope` hook before sending
            rendererProfiling.rendererProfileFromIpc(event, profile);
        }
        captureEventFromRenderer(options, event, dynamicSamplingContext, attachments, contents);
    }
    else {
        const normalizedEnvelope = normalize.normalizeReplayEnvelope(options, envelope$1, electron.app.getAppPath());
        // Pass other types of envelope straight to the transport
        void node.getClient()?.getTransport()?.send(normalizedEnvelope);
    }
}
/** Is object defined and has keys */
function hasKeys(obj) {
    return obj != undefined && Object.keys(obj).length > 0;
}
/**
 * Handle scope updates from renderer processes
 */
function handleScope(options, jsonScope) {
    let sentScope;
    try {
        sentScope = JSON.parse(jsonScope);
    }
    catch {
        core.debug.warn('sentry-electron received an invalid scope message');
        return;
    }
    const scope = node.getCurrentScope();
    if (hasKeys(sentScope.user)) {
        scope.setUser(sentScope.user);
    }
    if (hasKeys(sentScope.tags)) {
        scope.setTags(sentScope.tags);
    }
    if (hasKeys(sentScope.extra)) {
        scope.setExtras(sentScope.extra);
    }
    for (const attachment of sentScope.attachments || []) {
        scope.addAttachment(attachment);
    }
    const breadcrumb = sentScope.breadcrumbs.pop();
    if (breadcrumb) {
        scope.addBreadcrumb(breadcrumb, options?.maxBreadcrumbs || 100);
    }
}
function handleLogFromRenderer(client, options, log$1, contents) {
    const process = contents ? options?.getRendererName?.(contents) || 'renderer' : 'renderer';
    log$1.attributes = log$1.attributes || {};
    if (options.release) {
        log$1.attributes['sentry.release'] = { value: options.release, type: 'string' };
    }
    if (options.environment) {
        log$1.attributes['sentry.environment'] = { value: options.environment, type: 'string' };
    }
    log$1.attributes['sentry.sdk.name'] = { value: 'sentry.javascript.electron', type: 'string' };
    log$1.attributes['sentry.sdk.version'] = { value: version.SDK_VERSION, type: 'string' };
    log$1.attributes['electron.process'] = { value: process, type: 'string' };
    const osDeviceAttributes = log.getOsDeviceLogAttributes(client);
    if (osDeviceAttributes['os.name']) {
        log$1.attributes['os.name'] = { value: osDeviceAttributes['os.name'], type: 'string' };
    }
    if (osDeviceAttributes['os.version']) {
        log$1.attributes['os.version'] = { value: osDeviceAttributes['os.version'], type: 'string' };
    }
    if (osDeviceAttributes['device.brand']) {
        log$1.attributes['device.brand'] = { value: osDeviceAttributes['device.brand'], type: 'string' };
    }
    if (osDeviceAttributes['device.model']) {
        log$1.attributes['device.model'] = { value: osDeviceAttributes['device.model'], type: 'string' };
    }
    if (osDeviceAttributes['device.family']) {
        log$1.attributes['device.family'] = { value: osDeviceAttributes['device.family'], type: 'string' };
    }
    core._INTERNAL_captureSerializedLog(client, log$1);
}
/** Enables Electron protocol handling */
function configureProtocol(client, ipcUtil, options) {
    if (electron.app.isReady()) {
        throw new Error("Sentry SDK should be initialized before the Electron app 'ready' event is fired");
    }
    const scheme = {
        scheme: ipcUtil.namespace,
        privileges: { bypassCSP: true, corsEnabled: true, supportFetchAPI: true, secure: true },
    };
    electron.protocol.registerSchemesAsPrivileged([scheme]);
    // We Proxy this function so that later user calls to registerSchemesAsPrivileged don't overwrite our custom scheme
    // eslint-disable-next-line @typescript-eslint/unbound-method
    electron.protocol.registerSchemesAsPrivileged = new Proxy(electron.protocol.registerSchemesAsPrivileged, {
        apply: (target, __, args) => {
            target([...args[0], scheme]);
        },
    });
    const rendererStatusChanged = rendererAnr.createRendererEventLoopBlockStatusHandler(client);
    electron.app
        .whenReady()
        .then(() => {
        for (const sesh of options.getSessions()) {
            electronNormalize.registerProtocol(sesh.protocol, ipcUtil.namespace, (request) => {
                const getWebContents = () => {
                    const webContentsId = request.windowId ? WINDOW_ID_TO_WEB_CONTENTS?.get(request.windowId) : undefined;
                    return webContentsId ? electron.webContents.fromId(webContentsId) : undefined;
                };
                const data = request.body;
                if (ipcUtil.urlMatches(request.url, 'start')) {
                    newProtocolRenderer();
                }
                else if (ipcUtil.urlMatches(request.url, 'scope') && data) {
                    handleScope(options, data.toString());
                }
                else if (ipcUtil.urlMatches(request.url, 'envelope') && data) {
                    handleEnvelope(client, options, data, getWebContents());
                }
                else if (ipcUtil.urlMatches(request.url, 'structured-log') && data) {
                    handleLogFromRenderer(client, options, JSON.parse(data.toString()), getWebContents());
                }
                else if (rendererStatusChanged && ipcUtil.urlMatches(request.url, 'status') && data) {
                    const contents = getWebContents();
                    if (contents) {
                        const status = JSON.parse(data.toString()).status;
                        rendererStatusChanged(status, contents);
                    }
                }
            });
        }
    })
        .catch((error) => core.debug.error(error));
}
/**
 * Hooks IPC for communication with the renderer processes
 */
function configureClassic(client, ipcUtil, options) {
    electron.ipcMain.on(ipcUtil.createKey('start'), ({ sender }) => {
        const id = sender.id;
        // Keep track of renderers that are using IPC
        KNOWN_RENDERERS = KNOWN_RENDERERS || new Set();
        if (KNOWN_RENDERERS.has(id)) {
            return;
        }
        // In older Electron, sender can be destroyed before this callback is called
        if (!sender.isDestroyed()) {
            KNOWN_RENDERERS.add(id);
            sender.once('destroyed', () => {
                KNOWN_RENDERERS?.delete(id);
            });
        }
    });
    electron.ipcMain.on(ipcUtil.createKey('scope'), (_, jsonScope) => handleScope(options, jsonScope));
    electron.ipcMain.on(ipcUtil.createKey('envelope'), ({ sender }, env) => handleEnvelope(client, options, env, sender));
    electron.ipcMain.on(ipcUtil.createKey('structured-log'), ({ sender }, log) => handleLogFromRenderer(client, options, log, sender));
    const rendererStatusChanged = rendererAnr.createRendererEventLoopBlockStatusHandler(client);
    if (rendererStatusChanged) {
        electron.ipcMain.on(ipcUtil.createKey('status'), ({ sender }, status) => rendererStatusChanged(status, sender));
    }
}
/** Sets up communication channels with the renderer */
function configureIPC(client, options) {
    const ipcUtil = ipc.ipcChannelUtils(options.ipcNamespace);
    // eslint-disable-next-line no-bitwise
    if ((options.ipcMode & ipc.IPCMode.Protocol) > 0) {
        configureProtocol(client, ipcUtil, options);
    }
    // eslint-disable-next-line no-bitwise
    if ((options.ipcMode & ipc.IPCMode.Classic) > 0) {
        configureClassic(client, ipcUtil, options);
    }
}

exports.configureIPC = configureIPC;//# sourceMappingURL=http://go/sourcemap/sourcemaps/31e8d61c448c7472e371505838a0fe34083dad50/node_modules/@sentry/electron/main/ipc.js.map
