const tslib = require('tslib');
const child = require('child_process');
const electron = require('electron');
const os = require('os');
const path = require('path');
const electronNormalize = require('./electron-normalize.js');
const fs = require('./fs.js');
const version = require('./version.js');

function _interopNamespaceDefault(e) {
    const n = Object.create(null);
    if (e) {
        for (const k in e) {
            n[k] = e[k];
        }
    }
    n.default = e;
    return n;
}

const child__namespace = /*#__PURE__*/_interopNamespaceDefault(child);

const SDK_NAME = 'sentry.javascript.electron';
/** Mapping of Node's platform names to actual OS names. */
const PLATFORM_NAMES = {
    aix: 'IBM AIX',
    freebsd: 'FreeBSD',
    openbsd: 'OpenBSD',
    sunos: 'SunOS',
    win32: 'Windows',
};
/** Mapping of linux release files located in /etc to distributions. */
const LINUX_DISTROS = [
    { name: 'fedora-release', distros: ['Fedora'] },
    { name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },
    { name: 'redhat_version', distros: ['Red Hat Linux'] },
    { name: 'SuSE-release', distros: ['SUSE Linux'] },
    { name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },
    { name: 'debian_version', distros: ['Debian'] },
    { name: 'debian_release', distros: ['Debian'] },
    { name: 'arch-release', distros: ['Arch Linux'] },
    { name: 'gentoo-release', distros: ['Gentoo Linux'] },
    { name: 'novell-release', distros: ['SUSE Linux'] },
    { name: 'alpine-release', distros: ['Alpine Linux'] },
];
/** Functions to extract the OS version from Linux release files. */
const LINUX_VERSIONS = {
    alpine: (content) => content,
    arch: (content) => matchFirst(/distrib_release=(.*)/, content),
    centos: (content) => matchFirst(/release ([^ ]+)/, content),
    debian: (content) => content,
    fedora: (content) => matchFirst(/release (..)/, content),
    mint: (content) => matchFirst(/distrib_release=(.*)/, content),
    red: (content) => matchFirst(/release ([^ ]+)/, content),
    suse: (content) => matchFirst(/VERSION = (.*)\n/, content),
    ubuntu: (content) => matchFirst(/distrib_release=(.*)/, content),
};
/**
 * Executes a regular expression with one capture group.
 *
 * @param regex A regular expression to execute.
 * @param text Content to execute the RegEx on.
 * @returns The captured string if matched; otherwise undefined.
 */
function matchFirst(regex, text) {
    const match = regex.exec(text);
    return match ? match[1] : undefined;
}
/** Loads the macOS operating system context. */
function getDarwinInfo() {
    return tslib.__awaiter(this, void 0, void 0, function* () {
        // Default values that will be used in case no operating system information
        // can be loaded. The default version is computed via heuristics from the
        // kernel version, but the build ID is missing.
        const darwinInfo = {
            kernel_version: os.release(),
            name: 'Mac OS X',
            version: `10.${Number(os.release().split('.')[0]) - 4}`,
        };
        try {
            // We try to load the actual macOS version by executing the `sw_vers` tool.
            // This tool should be available on every standard macOS installation. In
            // case this fails, we stick with the values computed above.
            const output = yield new Promise((resolve, reject) => {
                child__namespace.execFile('/usr/bin/sw_vers', (error, stdout) => {
                    if (error) {
                        reject(error);
                        return;
                    }
                    resolve(stdout);
                });
            });
            darwinInfo.name = matchFirst(/^ProductName:\s+(.*)$/m, output);
            darwinInfo.version = matchFirst(/^ProductVersion:\s+(.*)$/m, output);
            darwinInfo.build = matchFirst(/^BuildVersion:\s+(.*)$/m, output);
        }
        catch (e) {
            // ignore
        }
        return darwinInfo;
    });
}
/** Returns a distribution identifier to look up version callbacks. */
function getLinuxDistroId(name) {
    return name.split(' ')[0].toLowerCase();
}
/** Loads the Linux operating system context. */
function getLinuxInfo() {
    return tslib.__awaiter(this, void 0, void 0, function* () {
        // By default, we cannot assume anything about the distribution or Linux
        // version. `os.release()` returns the kernel version and we assume a generic
        // "Linux" name, which will be replaced down below.
        const linuxInfo = {
            kernel_version: os.release(),
            name: 'Linux',
        };
        try {
            // We start guessing the distribution by listing files in the /etc
            // directory. This is were most Linux distributions (except Knoppix) store
            // release files with certain distribution-dependent meta data. We search
            // for exactly one known file defined in `LINUX_DISTROS` and exit if none
            // are found. In case there are more than one file, we just stick with the
            // first one.
            const etcFiles = yield fs.readDirAsync('/etc');
            const distroFile = LINUX_DISTROS.find((file) => etcFiles.includes(file.name));
            if (!distroFile) {
                return linuxInfo;
            }
            // Once that file is known, load its contents. To make searching in those
            // files easier, we lowercase the file contents. Since these files are
            // usually quite small, this should not allocate too much memory and we only
            // hold on to it for a very short amount of time.
            const distroPath = path.join('/etc', distroFile.name);
            const contents = (yield fs.readFileAsync(distroPath, { encoding: 'utf-8' })).toLowerCase();
            // Some Linux distributions store their release information in the same file
            // (e.g. RHEL and Centos). In those cases, we scan the file for an
            // identifier, that basically consists of the first word of the linux
            // distribution name (e.g. "red" for Red Hat). In case there is no match, we
            // just assume the first distribution in our list.
            const { distros } = distroFile;
            linuxInfo.name = distros.find((d) => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];
            // Based on the found distribution, we can now compute the actual version
            // number. This is different for every distribution, so several strategies
            // are computed in `LINUX_VERSIONS`.
            const id = getLinuxDistroId(linuxInfo.name);
            linuxInfo.version = LINUX_VERSIONS[id](contents);
        }
        catch (e) {
            // ignore
        }
        return linuxInfo;
    });
}
/**
 * Returns the operating system context.
 *
 * Based on the current platform, this uses a different strategy to provide the
 * most accurate OS information. Since this might involve spawning subprocesses
 * or accessing the file system, this should only be executed lazily and cached.
 *
 *  - On macOS (Darwin), this will execute the `sw_vers` utility. The context
 *    has a `name`, `version`, `build` and `kernel_version` set.
 *  - On Linux, this will try to load a distribution release from `/etc` and set
 *    the `name`, `version` and `kernel_version` fields.
 *  - On all other platforms, only a `name` and `version` will be returned. Note
 *    that `version` might actually be the kernel version.
 */
function getOsContext() {
    return tslib.__awaiter(this, void 0, void 0, function* () {
        const platformId = os.platform();
        switch (platformId) {
            case 'darwin':
                return getDarwinInfo();
            case 'linux':
                return getLinuxInfo();
            default:
                return {
                    name: PLATFORM_NAMES[platformId] || platformId,
                    version: os.release(),
                };
        }
    });
}
/** Gets SDK info */
function getSdkInfo() {
    return {
        name: SDK_NAME,
        packages: [
            {
                name: 'npm:@sentry/electron',
                version: version.SDK_VERSION,
            },
        ],
        version: version.SDK_VERSION,
    };
}
/** Gets the app context */
function getAppContext() {
    const appCtx = {
        app_name: electron.app.name || electron.app.getName(),
        app_version: electron.app.getVersion(),
        app_start_time: new Date(Date.now() - process.uptime() * 1000).toISOString(),
    };
    if (process.mas) {
        appCtx.build_type = 'app-store';
    }
    if (process.windowsStore) {
        appCtx.build_type = 'windows-store';
    }
    return appCtx;
}
/** Gets the app contexts */
function getContexts() {
    return tslib.__awaiter(this, void 0, void 0, function* () {
        const contexts = {
            app: getAppContext(),
            os: (yield getOsContext()),
            browser: {
                name: 'Chrome',
            },
            chrome: {
                name: 'Chrome',
                type: 'runtime',
                version: process.versions.chrome,
            },
            device: {
                arch: process.arch,
                family: 'Desktop',
            },
            node: {
                name: 'Node',
                type: 'runtime',
                version: process.versions.node,
            },
            runtime: {
                name: 'Electron',
                version: process.versions.electron,
            },
        };
        return contexts;
    });
}
/** Gets the default release name */
function getDefaultReleaseName() {
    const app_name = electron.app.name || electron.app.getName();
    return `${app_name.replace(/\W/g, '-')}@${electron.app.getVersion()}`;
}
/** Gets the default environment */
function getDefaultEnvironment() {
    return electronNormalize.isPackaged ? 'production' : 'development';
}
/**
 * Computes Electron-specific default fields for events.
 *
 * The event defaults include contexts for the Electron, Node and Chrome
 * runtimes, limited device information, operating system context and defaults
 * for the release and environment.
 */
function _getEventDefaults(release, environment) {
    return tslib.__awaiter(this, void 0, void 0, function* () {
        return {
            contexts: yield getContexts(),
            environment: environment || getDefaultEnvironment(),
            release: release || getDefaultReleaseName(),
            user: { ip_address: '{{auto}}' },
            tags: {
                'event.origin': 'electron',
                'event.environment': 'javascript',
                'event.process': 'browser',
                // Legacy way of filtering native vs JavaScript events
                event_type: 'javascript',
            },
        };
    });
}
/** Cached event prototype with default values. */
let cachedDefaultsPromise;
/**
 * Computes and caches Electron-specific default fields for events.
 *
 * The event defaults include contexts for the Electron, Node and Chrome
 * runtimes, limited device information, operating system context and defaults
 * for the release and environment.
 */
function getEventDefaults(release, environment) {
    return tslib.__awaiter(this, void 0, void 0, function* () {
        // The event defaults are cached as long as the app is running. We create the
        // promise here synchronously to avoid multiple events computing them at the
        // same time.
        if (!cachedDefaultsPromise) {
            cachedDefaultsPromise = _getEventDefaults(release, environment);
        }
        return cachedDefaultsPromise;
    });
}

exports.SDK_NAME = SDK_NAME;
exports.getDefaultEnvironment = getDefaultEnvironment;
exports.getDefaultReleaseName = getDefaultReleaseName;
exports.getEventDefaults = getEventDefaults;
exports.getSdkInfo = getSdkInfo;
//# sourceMappingURL=context.js.map
