UNPKG

cypress

Version:

Cypress is a next generation front end testing tool built for the modern web

1,377 lines 58.2 kB
'use strict';

var xvfb = require('./xvfb-BKRGwxlS.js');
var _ = require('lodash');
var os = require('os');
var cp = require('child_process');
var path = require('path');
var Debug = require('debug');
var chalk = require('chalk');
var listr2 = require('listr2');
var commonTags = require('common-tags');
var logSymbols = require('log-symbols');
var readline = require('readline');
var process$1 = require('process');
var require$$0 = require('stream');
var require$$1 = require('string_decoder');
var require$$1$1 = require('node:string_decoder');

const debug$1 = Debug('cypress:cli');
const verifyTestRunnerTimeoutMs = () => {
    const verifyTimeout = +((xvfb.util === null || xvfb.util === void 0 ? void 0 : xvfb.util.getEnv('CYPRESS_VERIFY_TIMEOUT')) || 'NaN');
    if (_.isNumber(verifyTimeout) && !_.isNaN(verifyTimeout)) {
        return verifyTimeout;
    }
    return 30000;
};
const checkExecutable = (binaryDir) => xvfb.__awaiter(void 0, void 0, void 0, function* () {
    const executable = xvfb.stateModule.getPathToExecutable(binaryDir);
    debug$1('checking if executable exists', executable);
    try {
        const isExecutable = yield xvfb.util.isExecutableAsync(executable);
        debug$1('Binary is executable? :', isExecutable);
        if (!isExecutable) {
            return xvfb.throwFormErrorText(xvfb.errors.binaryNotExecutable(executable))();
        }
    }
    catch (err) {
        if (err.code === 'ENOENT') {
            if (xvfb.util.isCi()) {
                return xvfb.throwFormErrorText(xvfb.errors.notInstalledCI(executable))();
            }
            return xvfb.throwFormErrorText(xvfb.errors.missingApp(binaryDir))(commonTags.stripIndent `
        Cypress executable not found at: ${chalk.cyan(executable)}
      `);
        }
        throw err;
    }
});
const runSmokeTest = (binaryDir, options) => {
    let executable = xvfb.stateModule.getPathToExecutable(binaryDir);
    const needsXvfb = xvfb.xvfb.isNeeded();
    debug$1('needs Xvfb?', needsXvfb);
    /**
     * Spawn Cypress running smoke test to check if all operating system
     * dependencies are good.
     */
    const spawn = (linuxWithDisplayEnv) => xvfb.__awaiter(void 0, void 0, void 0, function* () {
        const random = _.random(0, 1000);
        const args = ['--smoke-test', `--ping=${random}`];
        if (needsSandbox()) {
            // electron requires --no-sandbox to run as root
            debug$1('disabling Electron sandbox');
            args.unshift('--no-sandbox');
        }
        if (options.dev) {
            executable = 'node';
            const startScriptPath = xvfb.relativeToRepoRoot('scripts/start.js');
            if (!startScriptPath) {
                throw new Error(`Cypress start script (scripts/start.js) not found in parent directory of ${__dirname}`);
            }
            args.unshift(startScriptPath);
        }
        const smokeTestCommand = `${executable} ${args.join(' ')}`;
        debug$1('running smoke test');
        debug$1('using Cypress executable %s', executable);
        debug$1('smoke test command:', smokeTestCommand);
        debug$1('smoke test timeout %d ms', options.smokeTestTimeout);
        const stdioOptions = _.extend({}, {
            env: Object.assign(Object.assign({}, process.env), { FORCE_COLOR: '0' }),
            timeout: options.smokeTestTimeout,
        });
        try {
            const result = yield xvfb.util.exec(executable, args, stdioOptions);
            // TODO: when execa > 1.1 is released
            // change this to `result.all` for both stderr and stdout
            // use lodash to be robust during tests against null result or missing stdout
            const smokeTestStdout = _.get(result, 'stdout', '');
            debug$1('smoke test stdout "%s"', smokeTestStdout);
            if (!xvfb.util.stdoutLineMatches(String(random), smokeTestStdout)) {
                debug$1('Smoke test failed because could not find %d in:', random, result);
                const smokeTestStderr = _.get(result, 'stderr', '');
                const errorText = smokeTestStderr || smokeTestStdout;
                return xvfb.throwFormErrorText(xvfb.errors.smokeTestFailure(smokeTestCommand, false))(errorText);
            }
        }
        catch (err) {
            debug$1('Smoke test failed:', err);
            let errMessage = err.stderr || err.message;
            debug$1('error message:', errMessage);
            if (err.timedOut) {
                debug$1('error timedOut is true');
                return xvfb.throwFormErrorText(xvfb.errors.smokeTestFailure(smokeTestCommand, true))(errMessage);
            }
            if (linuxWithDisplayEnv && xvfb.util.isBrokenGtkDisplay(errMessage)) {
                xvfb.util.logBrokenGtkDisplayWarning();
                return xvfb.throwFormErrorText(xvfb.errors.invalidSmokeTestDisplayError)(errMessage);
            }
            return xvfb.throwFormErrorText(xvfb.errors.missingDependency)(errMessage);
        }
    });
    const spawnInXvfb = (linuxWithDisplayEnv) => xvfb.__awaiter(void 0, void 0, void 0, function* () {
        yield xvfb.xvfb.start();
        return spawn(linuxWithDisplayEnv || false).finally(() => xvfb.__awaiter(void 0, void 0, void 0, function* () {
            yield xvfb.xvfb.stop();
        }));
    });
    const userFriendlySpawn = (linuxWithDisplayEnv) => xvfb.__awaiter(void 0, void 0, void 0, function* () {
        debug$1('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv));
        try {
            yield spawn(linuxWithDisplayEnv);
        }
        catch (err) {
            if (err.code === 'INVALID_SMOKE_TEST_DISPLAY_ERROR') {
                return spawnInXvfb(linuxWithDisplayEnv);
            }
            throw err;
        }
    });
    if (needsXvfb) {
        return spawnInXvfb();
    }
    // if we are on linux and there's already a DISPLAY
    // set, then we may need to rerun cypress after
    // spawning our own Xvfb server
    const linuxWithDisplayEnv = xvfb.util.isPossibleLinuxWithIncorrectDisplay();
    return userFriendlySpawn(linuxWithDisplayEnv);
};
function logVersionMismatch(binaryVersion, binaryDir, packageVersion) {
    xvfb.loggerModule.log(`Found binary version ${chalk.green(binaryVersion)} installed in: ${chalk.cyan(binaryDir)}`);
    xvfb.loggerModule.log();
    xvfb.loggerModule.warn(commonTags.stripIndent `


  ${logSymbols.warning} Warning: Binary version ${chalk.green(binaryVersion)} does not match the expected package version ${chalk.green(packageVersion)}

    These versions may not work properly together.
  `);
    xvfb.loggerModule.log();
}
function verifyBinary(installedVersion, binaryDir, options) {
    return xvfb.__awaiter(this, void 0, void 0, function* () {
        debug$1('running binary verification check', installedVersion);
        // if running from 'cypress verify', don't print this message
        if (!options.force) {
            xvfb.loggerModule.log(commonTags.stripIndent `
    It looks like this is your first time using Cypress: ${chalk.cyan(installedVersion)}
    `);
        }
        xvfb.loggerModule.log();
        const verifyTaskRunner = new listr2.Listr([{
                title: xvfb.util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)),
                task: (ctx, task) => xvfb.__awaiter(this, void 0, void 0, function* () {
                    debug$1('clearing out the verified version');
                    yield xvfb.stateModule.clearBinaryStateAsync(binaryDir);
                    yield runSmokeTest(binaryDir, options);
                    debug$1('write verified: true');
                    yield xvfb.stateModule.writeBinaryVerifiedAsync(true, binaryDir);
                    task.title = xvfb.util.titleize(chalk.green('Verified Cypress!'), chalk.gray(binaryDir));
                }),
            }], Object.assign(Object.assign({ 
            // In CI we want timestamped, line-per-event output. Locally,
            // the default in-place spinner is the better experience.
            renderer: xvfb.util.isCi() ? 'verbose' : 'default' }, (xvfb.util.isCi() && { rendererOptions: { timestamp: listr2.PRESET_TIMESTAMP } })), { silentRendererCondition: () => xvfb.loggerModule.logLevel() === 'silent' }));
        yield verifyTaskRunner.run();
        if (options.welcomeMessage) {
            xvfb.loggerModule.log();
            xvfb.loggerModule.log('Opening Cypress...');
        }
    });
}
const start$1 = (...args_1) => xvfb.__awaiter(void 0, [...args_1], void 0, function* (options = {}) {
    debug$1('verifying Cypress app with options %j', options);
    _.defaults(options, {
        dev: false,
        force: false,
        welcomeMessage: true,
        smokeTestTimeout: verifyTestRunnerTimeoutMs(),
        skipVerify: xvfb.util.getEnv('CYPRESS_SKIP_VERIFY') === 'true',
    });
    if (options.skipVerify) {
        debug$1('skipping verification of the Cypress app');
        return Promise.resolve();
    }
    const packageVersion = xvfb.util.pkgVersion();
    let binaryDir = xvfb.stateModule.getBinaryDir(packageVersion);
    if (options.dev) {
        return runSmokeTest('', options);
    }
    const parseBinaryEnvVar = () => xvfb.__awaiter(void 0, void 0, void 0, function* () {
        const envBinaryPath = xvfb.util.getEnv('CYPRESS_RUN_BINARY');
        debug$1('CYPRESS_RUN_BINARY exists, =', envBinaryPath);
        xvfb.loggerModule.log(commonTags.stripIndent `
      ${chalk.yellow('Note:')} You have set the environment variable:

      ${chalk.white('CYPRESS_RUN_BINARY=')}${chalk.cyan(envBinaryPath)}

      This overrides the default Cypress binary path used.
    `);
        xvfb.loggerModule.log();
        try {
            const isExecutable = yield xvfb.util.isExecutableAsync(envBinaryPath);
            debug$1('CYPRESS_RUN_BINARY is executable? :', isExecutable);
            if (!isExecutable) {
                return xvfb.throwFormErrorText(xvfb.errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(commonTags.stripIndent `
        The supplied binary path is not executable
        `);
            }
            const envBinaryDir = yield xvfb.stateModule.parseRealPlatformBinaryFolderAsync(envBinaryPath);
            if (!envBinaryDir) {
                return xvfb.throwFormErrorText(xvfb.errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))();
            }
            debug$1('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir);
            binaryDir = envBinaryDir;
        }
        catch (err) {
            if (err.code === 'ENOENT') {
                return xvfb.throwFormErrorText(xvfb.errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message);
            }
            throw err;
        }
    });
    try {
        debug$1('checking environment variables');
        if (xvfb.util.getEnv('CYPRESS_RUN_BINARY')) {
            yield parseBinaryEnvVar();
        }
        yield checkExecutable(binaryDir);
        debug$1('binaryDir is ', binaryDir);
        const pkg = yield xvfb.stateModule.getBinaryPkgAsync(binaryDir);
        const binaryVersion = xvfb.stateModule.getBinaryPkgVersion(pkg);
        if (!binaryVersion) {
            debug$1('no Cypress binary found for cli version ', packageVersion);
            return xvfb.throwFormErrorText(xvfb.errors.missingApp(binaryDir))(`
      Cannot read binary version from: ${chalk.cyan(xvfb.stateModule.getBinaryPkgPath(binaryDir))}
    `);
        }
        debug$1(`Found binary version ${chalk.green(binaryVersion)} installed in: ${chalk.cyan(binaryDir)}`);
        if (binaryVersion !== packageVersion) {
            // warn if we installed with CYPRESS_INSTALL_BINARY or changed version
            // in the package.json
            logVersionMismatch(binaryVersion, binaryDir, packageVersion);
        }
        const isVerified = options.force ?
            false :
            yield xvfb.stateModule.getBinaryVerifiedAsync(binaryDir);
        debug$1('is Verified ?', isVerified);
        if (!isVerified) {
            yield verifyBinary(binaryVersion, binaryDir, options);
        }
    }
    catch (err) {
        if (err.known) {
            throw err;
        }
        return xvfb.throwFormErrorText(xvfb.errors.unexpected)(err.stack);
    }
});
const isLinuxLike = () => os.platform() !== 'win32';
/**
 * Returns true if running on a system where Electron needs "--no-sandbox" flag.
 * @see https://crbug.com/638180
 *
 * On Debian we had problems running in sandbox even for non-root users.
 * @see https://github.com/cypress-io/cypress/issues/5434
 * Seems there is a lot of discussion around this issue among Electron users
 * @see https://github.com/electron/electron/issues/17972
*/
const needsSandbox = () => isLinuxLike();

var dist = {};

var FilterTaggedContent = {};

var LineDecoder = {};

var constants = {};

var hasRequiredConstants;

function requireConstants () {
	if (hasRequiredConstants) return constants;
	hasRequiredConstants = 1;
	Object.defineProperty(constants, "__esModule", { value: true });
	constants.DEBUG_PREFIX = constants.END_TAG = constants.START_TAG = void 0;
	/**
	 * These tags are used to mark the beginning and end of error content that should
	 * be filtered from stderr output. The tags are designed to be unique and easily
	 * identifiable in log output.
	 */
	constants.START_TAG = '<<<CYPRESS.STDERR.START>>>';
	/**
	 * Marks the end of error content that should be filtered from stderr output.
	 */
	constants.END_TAG = '<<<CYPRESS.STDERR.END>>>';
	/**
	 * A regex that will match output from the 'debug' package
	 */
	// this regexp needs to match control characters
	// eslint-disable-next-line no-control-regex
	constants.DEBUG_PREFIX = /^\s+(?:\u001b\[[0-9;]*m)*((\S+):)+/u;
	
	return constants;
}

var hasRequiredLineDecoder;

function requireLineDecoder () {
	if (hasRequiredLineDecoder) return LineDecoder;
	hasRequiredLineDecoder = 1;
	/**
	 * Decodes incoming string chunks into complete lines, handling partial lines across chunk boundaries.
	 *
	 * This class buffers incoming string data and provides an iterator interface to yield complete
	 * lines. It handles the case where a line might be split across multiple chunks by maintaining
	 * an internal buffer. The end() method should be called to flush any remaining buffered content
	 * when processing is complete.
	 */
	var __importDefault = (LineDecoder && LineDecoder.__importDefault) || function (mod) {
	    return (mod && mod.__esModule) ? mod : { "default": mod };
	};
	Object.defineProperty(LineDecoder, "__esModule", { value: true });
	LineDecoder.LineDecoder = void 0;
	const debug_1 = __importDefault(Debug);
	const constants_1 = requireConstants();
	const debugVerbose = (0, debug_1.default)(`cypress-verbose:stderr-filtering:LineDecoder:${process.pid}`);
	let LineDecoder$1 = class LineDecoder {
	    constructor(overrideToken = constants_1.END_TAG) {
	        this.overrideToken = overrideToken;
	        this.buffer = '';
	    }
	    /**
	     * Adds a chunk of string data to the internal buffer.
	     *
	     * @param chunk The string chunk to add to the buffer
	     */
	    write(chunk) {
	        debugVerbose('writing chunk to line decoder', { chunk });
	        this.buffer += chunk;
	    }
	    /**
	     * Iterates over complete lines in the current buffer.
	     *
	     * This generator yields complete lines from the buffer, splitting on newline characters.
	     * Any incomplete line at the end of the buffer is kept for the next iteration.
	     * Handles both Windows (\r\n) and Unix (\n) line endings.
	     *
	     * @yields Complete lines with newline characters preserved
	     */
	    *[Symbol.iterator]() {
	        debugVerbose('iterating over lines in line decoder');
	        let nextLine = undefined;
	        do {
	            nextLine = this.nextLine();
	            if (nextLine) {
	                debugVerbose('yielding line:', nextLine);
	                debugVerbose('buffer size:', this.buffer.length);
	                yield nextLine;
	            }
	        } while (nextLine);
	    }
	    /**
	     * Flushes the remaining buffer content and yields all remaining lines.
	     *
	     * This method should be called when processing is complete to ensure all buffered
	     * content is yielded. It processes any remaining content in the buffer plus an
	     * optional final chunk. Handles both Windows (\r\n) and Unix (\n) line endings.
	     *
	     * @param chunk Optional final chunk to process along with the buffer
	     * @yields All remaining lines from the buffer and final chunk
	     */
	    *end(chunk) {
	        this.buffer = `${this.buffer}${(chunk || '')}`;
	        let nextLine = undefined;
	        do {
	            nextLine = this.nextLine();
	            if (nextLine) {
	                yield nextLine;
	            }
	        } while (nextLine);
	    }
	    nextLine() {
	        const [newlineIndex, length] = [this.buffer.indexOf('\n'), 1];
	        const endsWithOverrideToken = newlineIndex < 0 ? this.buffer.endsWith(this.overrideToken) : false;
	        if (endsWithOverrideToken) {
	            debugVerbose('ends with override token');
	            const line = this.buffer;
	            this.buffer = '';
	            return line;
	        }
	        if (newlineIndex >= 0) {
	            debugVerbose('contains a newline');
	            const line = this.buffer.slice(0, newlineIndex + length);
	            this.buffer = this.buffer.slice(newlineIndex + length);
	            return line;
	        }
	        return undefined;
	    }
	};
	LineDecoder.LineDecoder = LineDecoder$1;
	
	return LineDecoder;
}

var writeWithBackpressure = {};

var hasRequiredWriteWithBackpressure;

function requireWriteWithBackpressure () {
	if (hasRequiredWriteWithBackpressure) return writeWithBackpressure;
	hasRequiredWriteWithBackpressure = 1;
	Object.defineProperty(writeWithBackpressure, "__esModule", { value: true });
	writeWithBackpressure.writeWithBackpressure = writeWithBackpressure$1;
	async function writeWithBackpressure$1(toStream, chunk) {
	    return new Promise((resolve, reject) => {
	        try {
	            const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
	            const ret = toStream.write(buffer);
	            if (ret) {
	                resolve();
	            }
	            else {
	                toStream.once('drain', () => {
	                    resolve();
	                });
	            }
	        }
	        catch (err) {
	            reject(err);
	        }
	    });
	}
	
	return writeWithBackpressure;
}

var hasRequiredFilterTaggedContent;

function requireFilterTaggedContent () {
	if (hasRequiredFilterTaggedContent) return FilterTaggedContent;
	hasRequiredFilterTaggedContent = 1;
	var __importDefault = (FilterTaggedContent && FilterTaggedContent.__importDefault) || function (mod) {
	    return (mod && mod.__esModule) ? mod : { "default": mod };
	};
	Object.defineProperty(FilterTaggedContent, "__esModule", { value: true });
	FilterTaggedContent.FilterTaggedContent = void 0;
	const stream_1 = require$$0;
	const string_decoder_1 = require$$1;
	const LineDecoder_1 = requireLineDecoder();
	const debug_1 = __importDefault(Debug);
	const writeWithBackpressure_1 = requireWriteWithBackpressure();
	const debugVerbose = (0, debug_1.default)('cypress-verbose:stderr-filtering:FilterTaggedContent');
	/**
	 * Filters content based on start and end tags, supporting multi-line tagged content.
	 *
	 * This transform stream processes incoming data and routes content between two output streams
	 * based on tag detection. Content between start and end tags is sent to the filtered stream,
	 * while content outside tags is sent to the main output stream. The class handles cases where
	 * tags span multiple lines by maintaining state across line boundaries.
	 *
	 * Example usage:
	 * ```typescript
	 * const filter = new FilterTaggedContent('<secret>', '</secret>', filteredStream)
	 * inputStream.pipe(filter).pipe(outputStream)
	 * ```
	 */
	let FilterTaggedContent$1 = class FilterTaggedContent extends stream_1.Transform {
	    /**
	     * Creates a new FilterTaggedContent instance.
	     *
	     * @param startTag The string that marks the beginning of content to filter
	     * @param endTag The string that marks the end of content to filter
	     * @param filtered The writable stream for filtered content
	     */
	    constructor(startTag, endTag, wasteStream) {
	        super({
	            transform: (chunk, encoding, next) => this.transform(chunk, encoding, next),
	            flush: (callback) => this.flush(callback),
	        });
	        this.startTag = startTag;
	        this.endTag = endTag;
	        this.wasteStream = wasteStream;
	        this.inTaggedContent = false;
	        /**
	         * Processes incoming chunks and routes content based on tag detection.
	         *
	         * @param chunk The buffer chunk to process
	         * @param encoding The encoding of the chunk
	         * @param next Callback to call when processing is complete
	         */
	        this.transform = async (chunk, encoding, next) => {
	            var _a, _b, _c;
	            try {
	                this.ensureDecoders(encoding);
	                const str = (_b = (_a = this.strDecoder) === null || _a === void 0 ? void 0 : _a.write(chunk)) !== null && _b !== void 0 ? _b : '';
	                (_c = this.lineDecoder) === null || _c === void 0 ? void 0 : _c.write(str);
	                debugVerbose('processing str for tags: "%s"', str);
	                for (const line of Array.from(this.lineDecoder || [])) {
	                    await this.processLine(line);
	                }
	                next();
	            }
	            catch (err) {
	                next(err);
	            }
	        };
	        /**
	         * Flushes any remaining buffered content when the stream ends.
	         *
	         * @param callback Callback to call when flushing is complete
	         */
	        this.flush = async (callback) => {
	            var _a;
	            debugVerbose('flushing');
	            this.ensureDecoders();
	            try {
	                for (const line of Array.from(((_a = this.lineDecoder) === null || _a === void 0 ? void 0 : _a.end()) || [])) {
	                    await this.processLine(line);
	                }
	                callback();
	            }
	            catch (err) {
	                callback(err);
	            }
	        };
	    }
	    ensureDecoders(encoding) {
	        var _a;
	        const enc = (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8';
	        if (!this.lineDecoder) {
	            this.lineDecoder = new LineDecoder_1.LineDecoder();
	        }
	        if (!this.strDecoder) {
	            this.strDecoder = new string_decoder_1.StringDecoder(enc);
	        }
	    }
	    /**
	     * Processes a single line and routes content based on tag positions.
	     *
	     * This method handles the complex logic of detecting start and end tags within a line,
	     * maintaining state across lines, and routing content to the appropriate streams.
	     * It supports cases where both tags appear on the same line, only one tag appears,
	     * or no tags appear but the line is part of ongoing tagged content.
	     *
	     * @param line The line to process
	     */
	    async processLine(line) {
	        const startPos = line.indexOf(this.startTag);
	        const endPos = line.lastIndexOf(this.endTag);
	        if (startPos >= 0 && endPos >= 0) {
	            // Both tags on same line
	            if (startPos > 0) {
	                await this.pass(line.slice(0, startPos));
	            }
	            await this.writeToWasteStream(line.slice(startPos + this.startTag.length, endPos));
	            if (endPos + this.endTag.length < line.length) {
	                await this.pass(line.slice(endPos + this.endTag.length));
	            }
	        }
	        else if (startPos >= 0) {
	            // Start tag found
	            if (startPos > 0) {
	                await this.pass(line.slice(0, startPos));
	            }
	            await this.writeToWasteStream(line.slice(startPos + this.startTag.length));
	            this.inTaggedContent = true;
	        }
	        else if (endPos >= 0) {
	            // End tag found
	            await this.writeToWasteStream(line.slice(0, endPos));
	            if (endPos + this.endTag.length < line.length) {
	                await this.pass(line.slice(endPos + this.endTag.length));
	            }
	            this.inTaggedContent = false;
	        }
	        else if (this.inTaggedContent) {
	            // Currently in tagged content
	            await this.writeToWasteStream(line);
	        }
	        else {
	            // Not in tagged content
	            await this.pass(line);
	        }
	    }
	    async writeToWasteStream(line, encoding) {
	        var _a;
	        debugVerbose('writing to waste stream: "%s"', line);
	        await (0, writeWithBackpressure_1.writeWithBackpressure)(this.wasteStream, Buffer.from(line, (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8'));
	    }
	    async pass(line, encoding) {
	        var _a;
	        debugVerbose('passing: "%s"', line);
	        this.push(Buffer.from(line, (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8'));
	    }
	};
	FilterTaggedContent.FilterTaggedContent = FilterTaggedContent$1;
	
	return FilterTaggedContent;
}

var FilterPrefixedContent = {};

var hasRequiredFilterPrefixedContent;

function requireFilterPrefixedContent () {
	if (hasRequiredFilterPrefixedContent) return FilterPrefixedContent;
	hasRequiredFilterPrefixedContent = 1;
	var __importDefault = (FilterPrefixedContent && FilterPrefixedContent.__importDefault) || function (mod) {
	    return (mod && mod.__esModule) ? mod : { "default": mod };
	};
	Object.defineProperty(FilterPrefixedContent, "__esModule", { value: true });
	FilterPrefixedContent.FilterPrefixedContent = void 0;
	const stream_1 = require$$0;
	const string_decoder_1 = require$$1;
	const LineDecoder_1 = requireLineDecoder();
	const debug_1 = __importDefault(Debug);
	const debugStderr = (0, debug_1.default)('cypress:stderr');
	const writeWithBackpressure_1 = requireWriteWithBackpressure();
	/**
	 * Filters content based on a prefix pattern, routing matching lines to a filtered stream.
	 *
	 * This transform stream processes incoming data line by line and routes content between two
	 * output streams based on a regular expression prefix test. Lines that match the prefix pattern
	 * are sent to the filtered stream, while non-matching lines are sent to the main output stream.
	 *
	 * Example usage:
	 * ```typescript
	 * const errorStream = new Writable()
	 * const filter = new FilterPrefixedContent(/^ERROR:/, errorStream)
	 * inputStream.pipe(filter).pipe(outputStream)
	 * ```
	 */
	let FilterPrefixedContent$1 = class FilterPrefixedContent extends stream_1.Transform {
	    /**
	     * Creates a new FilterPrefixedContent instance.
	     *
	     * @param prefix The regular expression pattern to test against the beginning of each line
	     * @param filtered The writable stream for lines that match the prefix pattern
	     */
	    constructor(prefix, wasteStream) {
	        super(({
	            transform: (chunk, encoding, next) => this.transform(chunk, encoding, next),
	            flush: (callback) => this.flush(callback),
	        }));
	        this.prefix = prefix;
	        this.wasteStream = wasteStream;
	        /**
	         * Processes incoming chunks and routes lines based on prefix matching.
	         *
	         * @param chunk The buffer chunk to process
	         * @param encoding The encoding of the chunk
	         * @param next Callback to call when processing is complete
	         */
	        this.transform = async (chunk, encoding, next) => {
	            try {
	                if (!this.strDecoder) {
	                    // @ts-expect-error type here is not correct, 'buffer' is not a valid encoding but it does get passed in
	                    this.strDecoder = new string_decoder_1.StringDecoder(encoding === 'buffer' ? 'utf8' : encoding);
	                }
	                if (!this.lineDecoder) {
	                    this.lineDecoder = new LineDecoder_1.LineDecoder();
	                }
	                const str = this.strDecoder.write(chunk);
	                this.lineDecoder.write(str);
	                for (const line of Array.from(this.lineDecoder || [])) {
	                    await this.writeLine(line, encoding);
	                }
	                next();
	            }
	            catch (err) {
	                debugStderr('error in transform', err);
	                next(err);
	            }
	        };
	        /**
	         * Flushes any remaining buffered content when the stream ends.
	         *
	         * @param callback Callback to call when flushing is complete
	         */
	        this.flush = async (callback) => {
	            var _a;
	            try {
	                if (!this.strDecoder) {
	                    this.strDecoder = new string_decoder_1.StringDecoder();
	                }
	                if (!this.lineDecoder) {
	                    this.lineDecoder = new LineDecoder_1.LineDecoder();
	                }
	                if (this.lineDecoder) {
	                    for (const line of Array.from(((_a = this.lineDecoder) === null || _a === void 0 ? void 0 : _a.end()) || [])) {
	                        await this.writeLine(line);
	                    }
	                }
	                callback();
	            }
	            catch (err) {
	                callback(err);
	            }
	        };
	    }
	    /**
	     * Routes a single line to the appropriate stream based on prefix matching.
	     *
	     * Tests the line against the prefix regular expression and routes it to either
	     * the filtered stream (if it matches) or the main output stream (if it doesn't match).
	     *
	     * @param line The line to test and route
	     */
	    async writeLine(line, encoding) {
	        var _a, _b;
	        if (this.prefix.test(line)) {
	            await (0, writeWithBackpressure_1.writeWithBackpressure)(this.wasteStream, Buffer.from(line, (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8'));
	        }
	        else {
	            const canWrite = this.push(Buffer.from(line, (_b = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _b !== void 0 ? _b : 'utf8'));
	            if (!canWrite) {
	                await new Promise((resolve) => this.once('drain', resolve));
	            }
	        }
	    }
	};
	FilterPrefixedContent.FilterPrefixedContent = FilterPrefixedContent$1;
	
	return FilterPrefixedContent;
}

var TagStream = {};

var tagsDisabled = {};

var hasRequiredTagsDisabled;

function requireTagsDisabled () {
	if (hasRequiredTagsDisabled) return tagsDisabled;
	hasRequiredTagsDisabled = 1;
	Object.defineProperty(tagsDisabled, "__esModule", { value: true });
	tagsDisabled.tagsDisabled = tagsDisabled$1;
	function tagsDisabled$1() {
	    return process.env.ELECTRON_ENABLE_LOGGING === '1' || process.env.CYPRESS_INTERNAL_ENV === 'development';
	}
	
	return tagsDisabled;
}

var hasRequiredTagStream;

function requireTagStream () {
	if (hasRequiredTagStream) return TagStream;
	hasRequiredTagStream = 1;
	var __importDefault = (TagStream && TagStream.__importDefault) || function (mod) {
	    return (mod && mod.__esModule) ? mod : { "default": mod };
	};
	Object.defineProperty(TagStream, "__esModule", { value: true });
	TagStream.TagStream = void 0;
	const stream_1 = require$$0;
	const constants_1 = requireConstants();
	const string_decoder_1 = require$$1;
	const debug_1 = __importDefault(Debug);
	const tagsDisabled_1 = requireTagsDisabled();
	const debug = (0, debug_1.default)('cypress:stderr-filtering:TagStream');
	const debugVerbose = (0, debug_1.default)('cypress-verbose:stderr-filtering:TagStream');
	/**
	 * A Transform stream that wraps input data with start and end tags.
	 *
	 * This stream processes incoming chunks and wraps them with configurable
	 * start and end tags before passing them downstream. It handles both
	 * Buffer and string inputs, using a StringDecoder for proper encoding
	 * when processing Buffer chunks.
	 *
	 * By default, the start and end tags are the constants exported by this package:
	 *  - START_TAG
	 *  - END_TAG
	 *
	 * @example
	 * ```typescript
	 * const tagStream = new TagStream('[START]', '[END]');
	 * tagStream.pipe(process.stdout);
	 * tagStream.write('Hello World'); // Outputs: [START]Hello World[END]
	 * ```
	 */
	let TagStream$1 = class TagStream extends stream_1.Transform {
	    get initializedDecoder() {
	        debug('initializedDecoder', !!this.decoder);
	        if (!this.decoder) {
	            this.decoder = new string_decoder_1.StringDecoder();
	        }
	        return this.decoder;
	    }
	    /**
	     * Creates a new TagStream instance.
	     *
	     * @param startTag - The tag to prepend to each chunk. Defaults to START_TAG.
	     * @param endTag - The tag to append to each chunk. Defaults to END_TAG.
	     */
	    constructor(startTag = constants_1.START_TAG, endTag = constants_1.END_TAG) {
	        super({
	            transform: (...args) => this.transform(...args),
	        });
	        this.startTag = startTag;
	        this.endTag = endTag;
	    }
	    /**
	     * Transforms incoming chunks by wrapping them with start and end tags.
	     *
	     * Processes the input chunk, handles both Buffer and string inputs,
	     * and wraps the result with the configured start and end tags.
	     * Implements backpressure handling by waiting for the 'drain' event
	     * when the downstream stream cannot accept more data.
	     *
	     * @param chunk - The input chunk to transform. Can be Buffer, string, or any other type.
	     * @param encoding - The encoding of the chunk (used by Transform stream).
	     * @param callback - Callback function to signal completion of transformation.
	     * @returns Promise that resolves when transformation is complete.
	     */
	    async transform(chunk, encoding, callback) {
	        try {
	            const out = chunk instanceof Buffer ?
	                this.initializedDecoder.write(chunk) :
	                chunk;
	            const transformed = out ? this.tag(out) : Buffer.from('');
	            debugVerbose(`transformed: "${transformed.toString().replaceAll('\n', '\\n')}"`);
	            const canWrite = this.push(transformed);
	            if (!canWrite) {
	                debugVerbose('waiting for drain');
	                await new Promise((resolve) => this.once('drain', resolve));
	            }
	            callback();
	        }
	        catch (err) {
	            debug('error', err);
	            callback(err);
	        }
	    }
	    /**
	     * Flushes any remaining buffered data and wraps it with tags.
	     *
	     * Called when the stream is ending to process any remaining
	     * data in the StringDecoder buffer.
	     *
	     * @param callback - Callback function to signal completion of flush operation.
	     */
	    flush(callback) {
	        debug('flushing');
	        const out = this.initializedDecoder.end();
	        callback(undefined, out ? this.tag(out) : Buffer.from(''));
	    }
	    tag(str) {
	        return (0, tagsDisabled_1.tagsDisabled)() ? Buffer.from(str) : Buffer.from(`${this.startTag}${str}${this.endTag}`);
	    }
	};
	TagStream.TagStream = TagStream$1;
	
	return TagStream;
}

var WriteToDebug = {};

var hasRequiredWriteToDebug;

function requireWriteToDebug () {
	if (hasRequiredWriteToDebug) return WriteToDebug;
	hasRequiredWriteToDebug = 1;
	Object.defineProperty(WriteToDebug, "__esModule", { value: true });
	WriteToDebug.WriteToDebug = void 0;
	const stream_1 = require$$0;
	const node_string_decoder_1 = require$$1$1;
	const LineDecoder_1 = requireLineDecoder();
	/**
	 * A writable stream that routes incoming data to a debug logger.
	 *
	 * This class extends Writable to provide a stream interface that processes incoming
	 * data and forwards it to a debug logger. It handles line-by-line processing and
	 * automatically manages string decoding and line buffering. The stream is useful
	 * for debugging purposes where you want to log stream data with proper line handling.
	 *
	 * Example usage:
	 * ```typescript
	 * const debug = require('debug')('myapp:stream')
	 * const debugStream = new WriteToDebug(debug)
	 * someStream.pipe(debugStream)
	 * ```
	 */
	let WriteToDebug$1 = class WriteToDebug extends stream_1.Writable {
	    /**
	     * Creates a new WriteToDebug instance.
	     *
	     * @param debug The debug logger instance to write output to
	     */
	    constructor(debug) {
	        super({
	            write: (chunk, encoding, next) => {
	                if (!this.strDecoder) {
	                    // @ts-expect-error type here is not correct, 'buffer' is not a valid encoding but it does get passed in
	                    this.strDecoder = new node_string_decoder_1.StringDecoder(encoding === 'buffer' ? 'utf8' : encoding);
	                }
	                if (!this.lineDecoder) {
	                    this.lineDecoder = new LineDecoder_1.LineDecoder();
	                }
	                const str = this.strDecoder.write(chunk);
	                this.lineDecoder.write(str);
	                for (const line of this.lineDecoder) {
	                    this.debugLine(line);
	                }
	                next();
	            },
	            final: (callback) => {
	                if (!this.strDecoder) {
	                    this.strDecoder = new node_string_decoder_1.StringDecoder();
	                }
	                if (!this.lineDecoder) {
	                    this.lineDecoder = new LineDecoder_1.LineDecoder();
	                }
	                for (const line of this.lineDecoder.end()) {
	                    this.debugLine(line);
	                }
	                this.strDecoder = undefined;
	                this.lineDecoder = undefined;
	                callback();
	            },
	        });
	        this.debug = debug;
	    }
	    /**
	     * Processes a single line and sends it to the debug logger.
	     *
	     * This method cleans the line by removing trailing newlines while preserving
	     * intentional whitespace, then sends non-empty lines to the debug logger.
	     * Empty lines are filtered out to avoid cluttering the debug output.
	     *
	     * @param line The line to process and log
	     */
	    debugLine(line) {
	        // Remove trailing newline but preserve intentional whitespace
	        const clean = line.endsWith('\n') ? line.slice(0, -1) : line;
	        if (clean) {
	            this.debug(clean);
	        }
	    }
	};
	WriteToDebug.WriteToDebug = WriteToDebug$1;
	
	return WriteToDebug;
}

var Filter = {};

var hasRequiredFilter;

function requireFilter () {
	if (hasRequiredFilter) return Filter;
	hasRequiredFilter = 1;
	Object.defineProperty(Filter, "__esModule", { value: true });
	Filter.filter = filter;
	const constants_1 = requireConstants();
	const FilterPrefixedContent_1 = requireFilterPrefixedContent();
	const FilterTaggedContent_1 = requireFilterTaggedContent();
	const WriteToDebug_1 = requireWriteToDebug();
	const tagsDisabled_1 = requireTagsDisabled();
	function filter(stderr, debug, prefix) {
	    const prefixTx = new FilterPrefixedContent_1.FilterPrefixedContent(prefix, stderr);
	    const debugWriter = new WriteToDebug_1.WriteToDebug(debug);
	    if ((0, tagsDisabled_1.tagsDisabled)()) {
	        prefixTx.pipe(debugWriter);
	    }
	    else {
	        const tagTx = new FilterTaggedContent_1.FilterTaggedContent(constants_1.START_TAG, constants_1.END_TAG, stderr);
	        prefixTx.pipe(tagTx).pipe(debugWriter);
	    }
	    return prefixTx;
	}
	
	return Filter;
}

var logError = {};

var hasRequiredLogError;

function requireLogError () {
	if (hasRequiredLogError) return logError;
	hasRequiredLogError = 1;
	Object.defineProperty(logError, "__esModule", { value: true });
	logError.logError = void 0;
	/**
	 * Standard error logging tags used for stderr filtering
	 */
	const constants_1 = requireConstants();
	const tagsDisabled_1 = requireTagsDisabled();
	/**
	 * Logs error messages with special tags for stderr filtering.
	 *
	 * This function wraps console.error calls with start and end tags that can be
	 * used by FilterTaggedContent to identify and filter error messages from stderr
	 * output. The tags allow for precise control over which error messages are
	 * filtered while preserving the original error content.
	 *
	 * @param args The arguments to log as an error message
	 */
	const logError$1 = (...args) => {
	    // When electron debug is enabled, the output will not be filtered, so
	    // these tags are not needed.
	    if ((0, tagsDisabled_1.tagsDisabled)()) {
	        // eslint-disable-next-line no-console
	        console.error(...args);
	    }
	    else {
	        // eslint-disable-next-line no-console
	        console.error(constants_1.START_TAG, ...args, constants_1.END_TAG);
	    }
	};
	logError.logError = logError$1;
	
	return logError;
}

var hasRequiredDist;

function requireDist () {
	if (hasRequiredDist) return dist;
	hasRequiredDist = 1;
	(function (exports$1) {
		Object.defineProperty(exports$1, "__esModule", { value: true });
		exports$1.DEBUG_PREFIX = exports$1.END_TAG = exports$1.START_TAG = exports$1.logError = exports$1.filter = exports$1.WriteToDebug = exports$1.TagStream = exports$1.FilterPrefixedContent = exports$1.FilterTaggedContent = void 0;
		var FilterTaggedContent_1 = requireFilterTaggedContent();
		Object.defineProperty(exports$1, "FilterTaggedContent", { enumerable: true, get: function () { return FilterTaggedContent_1.FilterTaggedContent; } });
		var FilterPrefixedContent_1 = requireFilterPrefixedContent();
		Object.defineProperty(exports$1, "FilterPrefixedContent", { enumerable: true, get: function () { return FilterPrefixedContent_1.FilterPrefixedContent; } });
		var TagStream_1 = requireTagStream();
		Object.defineProperty(exports$1, "TagStream", { enumerable: true, get: function () { return TagStream_1.TagStream; } });
		var WriteToDebug_1 = requireWriteToDebug();
		Object.defineProperty(exports$1, "WriteToDebug", { enumerable: true, get: function () { return WriteToDebug_1.WriteToDebug; } });
		var Filter_1 = requireFilter();
		Object.defineProperty(exports$1, "filter", { enumerable: true, get: function () { return Filter_1.filter; } });
		var logError_1 = requireLogError();
		Object.defineProperty(exports$1, "logError", { enumerable: true, get: function () { return logError_1.logError; } });
		var constants_1 = requireConstants();
		Object.defineProperty(exports$1, "START_TAG", { enumerable: true, get: function () { return constants_1.START_TAG; } });
		Object.defineProperty(exports$1, "END_TAG", { enumerable: true, get: function () { return constants_1.END_TAG; } });
		Object.defineProperty(exports$1, "DEBUG_PREFIX", { enumerable: true, get: function () { return constants_1.DEBUG_PREFIX; } });
		
	} (dist));
	return dist;
}

var distExports = requireDist();

const debug = Debug('cypress:cli');
const debugElectron = Debug('cypress:electron');
const debugStderr = Debug('cypress:internal-stderr');
// Must match CYPRESS_OPEN_READY_MESSAGE in packages/server/lib/modes/interactive.ts
const CYPRESS_OPEN_READY_MESSAGE = 'Cypress is ready';
function isPlatform(platform) {
    return os.platform() === platform;
}
function needsStderrPiped(needsXvfb) {
    return _.some([
        isPlatform('darwin'),
        (needsXvfb && isPlatform('linux')),
        xvfb.util.isPossibleLinuxWithIncorrectDisplay(),
    ]);
}
function needsEverythingPipedDirectly() {
    return isPlatform('win32');
}
function getStdioStrategy(needsXvfb) {
    if (needsEverythingPipedDirectly()) {
        return 'pipe';
    }
    // https://github.com/cypress-io/cypress/issues/921
    // https://github.com/cypress-io/cypress/issues/1143
    // https://github.com/cypress-io/cypress/issues/1745
    if (needsStderrPiped(needsXvfb)) {
        // returning pipe here so we can massage stderr
        // and remove garbage from Xlib and libuv
        // due to starting the Xvfb process on linux
        return ['inherit', 'inherit', 'pipe'];
    }
    return 'inherit';
}
function createSpawnFunction(executable, args, options) {
    return (overrides = {}) => {
        return new Promise((resolve, reject) => xvfb.__awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            _.defaults(overrides, {
                onStderrData: false,
            });
            const { onStderrData } = overrides;
            const envOverrides = xvfb.util.getEnvOverrides(options);
            const electronArgs = [];
            const node11WindowsFix = isPlatform('win32');
            let startScriptPath;
            if (options.dev) {
                executable = 'node';
                // if we're in dev then reset
                // the launch cmd to be 'npm run dev'
                // This path is correct in the build output, but not the source code. This file gets bundled into
                // `dist/spawn-<hash>.js`, which makes this resolution appear incorrect at first glance.
                startScriptPath = xvfb.relativeToRepoRoot('scripts/start.js');
                if (!startScriptPath) {
                    throw new Error(`Cypress start script (scripts/start.js) not found in parent directory of ${__dirname}`);
                }
            }
            if (!options.dev && needsSandbox()) {
                electronArgs.push('--no-sandbox');
            }
            // strip dev out of child process options
            /**
               * @type {import('child_process').ForkOptions}
               */
            let stdioOptions = _.pick(options, 'env', 'detached', 'stdio');
            // figure out if we're going to be force enabling or disabling colors.
            // also figure out whether we should force stdout and stderr into thinking
            // it is a tty as opposed to a pipe.
            stdioOptions.env = _.extend({}, stdioOptions.env, envOverrides);
            if (node11WindowsFix) {
                stdioOptions = _.extend({}, stdioOptions, { windowsHide: false });
            }
            if (xvfb.util.isPossibleLinuxWithIncorrectDisplay()) {
                // make sure we use the latest DISPLAY variable if any
                debug('passing DISPLAY', process$1.env.DISPLAY);
                stdioOptions.env.DISPLAY = process$1.env.DISPLAY;
            }
            if (stdioOptions.detached) {
                // Ask interactive mode to print a ready sentinel on stdout; pipe stdio during
                // startup so errors are visible and we can detect it. Streams are destroyed
                // once it arrives so they don't keep the parent event loop alive.
                args.push('--emit-when-ready');
                stdioOptions.stdio = ['ignore', 'pipe', 'pipe'];
            }
            if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
                // Since we are running electron as node, we need to add an entry point file.
                startScriptPath = path.join(xvfb.stateModule.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js');
            }
            else {
                // Start arguments with "--" so Electron knows these are OUR
                // arguments and does not try to sanitize them. Otherwise on Windows
                // an url in one of the arguments crashes it :(
                // https://github.com/cypress-io/cypress/issues/5466
                args = [...electronArgs, '--', ...args];
            }
            if (startScriptPath) {
                args.unshift(startScriptPath);
            }
            if (process$1.env.CYPRESS_INTERNAL_DEV_DEBUG) {
                args.unshift(process$1.env.CYPRESS_INTERNAL_DEV_DEBUG);
            }
            debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'));
            debug('spawning Cypress with executable: %s', executable);
            const platform = yield xvfb.util.getPlatformInfo().catch((e) => reject(e));
            if (!platform) {
                return;
            }
            function resolveOn(event) {
                return function (code, signal) {
                    debug('child event fired %o', { event, code, signal });
                    if (signal) {
                        if (signal === 'SIGINT') {
                            resolve(0);
                        }
                        else {
                            resolve(128 + os.constants.signals[signal]);
                        }
                        return;
                    }
                    resolve(code !== null && code !== void 0 ? code : 1);
                };
            }
            const child = cp.spawn(executable, args, stdioOptions);
            child.on('close', resolveOn('close'));
            child.on('exit', resolveOn('exit'));
            child.on('error', reject);
            if (isPlatform('win32')) {
                const rl = readline.createInterface({
                    input: process$1.stdin,
                    output: process$1.stdout,
                });
                // on windows, SIGINT does not propagate to the child process when ctrl+c is pressed
                // this makes sure all nested processes are closed(ex: firefox inside the server)
                rl.on('SIGINT', function () {
                    return xvfb.__awaiter(this, void 0, void 0, function* () {
                        const kill = (yield import('tree-kill')).default;
                        kill(child.pid, 'SIGINT');
                    });
                });
            }
            else {
                // Adding listeners here prevents immediate process.exit() for these signals.
                // Exiting when the child process exits instead will allow the child process
                // to log during the exit process.
                // Unlike in windows, we do not need to propagate these signals to the child process
                // tree.
                for (const signal of ['SIGINT', 'SIGTERM']) {
                    debug('adding message for signal listener for %s', signal);
                    process$1.once(signal, function () {
                        return xvfb.__awaiter(this, void 0, void 0, function* () {
                            console.log(`\n\n${signal} received; Attempting to exit gracefully. Force exit with ^C again if needed.\n\n`);
                            if (process$1.stdin.isTTY) {
                                process$1.stdin.setRawMode(false);
                            }
                        });
                    });
                }
            }
            if (stdioOptions.detached) {
                child.stdout.on('data', (data) => {
                    const str = data.toString();
                    const readyMessageIndex = str.indexOf(CYPRESS_OPEN_READY_MESSAGE);
                    const isReady = readyMessageIndex !== -1;
                    if (isReady) {
                        const outputBeforeReady = str.slice(0, readyMessageIndex);
                        if (outputBeforeReady)
                            process$1.stdout.write(outputBeforeReady);
                        child.stdout.destroy();
                        child.stderr.destroy();
                        child.unref();
                        resolve(0);
                        return;
                    }
                    process$1.stdout.write(data);
                });
                child.stderr.pipe(process$1.stderr, { end: false });
                return;
            }
            // if stdio options is set to 'pipe', then
            //   we should set up pipes:
            //  process STDIN (read stream) => child STDIN (writeable)
            //  child STDOUT => process STDOUT
            //  child STDERR => process STDERR with additional filtering
            if (child.stdin) {
                debug('piping process STDIN into child STDIN');
                process$1.stdin.pipe(child.stdin);
            }
            if (child.stdout) {
                debug('piping child STDOUT to process STDOUT');
                child.stdout.pipe(process$1.stdout);
            }
            // if this is defined then we are manually piping for linux
            // to filter out the garbage
            if (child.stderr) {
                debug('piping child STDERR to process STDERR');
                const sourceStream = new require$$0.PassThrough();
                child.on('close', () => {
                    sourceStream.end();
                });
                child.stderr.on('data', (data) => {
                    const str = data.toString();
                    if (onStderrData && onStderrData(str)) {
                        return;
                    }
                    if (sourceStream.writable) {
                        sourceStream.write(data);
                    }
                });
                if (((_a = process$1.env.ELECTRON_ENABLE_LOGGING) !== null && _a !== void 0 ? _a : '') === '1' ||
                    debugElectron.enabled ||
                    ((_b = process$1.env.CYPRESS_INTERNAL_ENV) !== null && _b !== void 0 ? _b : '') === 'development') {
                    sourceStream.pipe(process$1.stderr, { end: false });
                }
                else {
                    sourceStream.pipe(distExports.filter(process$1.stderr, debugStderr, distExports.DEBUG_PREFIX));
                }
            }
            // https://github.com/cypress-io/cypress/issues/1841
            // https://github.com/cypress-io/cypress/issues/5241
            // In some versions of node, it will throw on windows
            // when you close the parent process after piping
            // into the child process. unpiping does not seem
            // to have any effect. so we're just catching the
            // error here and not doing anything.
            process$1.stdin.on('error', (err) => {
                debug('error on stdin', err);
                if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
                    return;
                }
                reject(err);
            });
        }));
    };
}
function spawnInXvfb(spawn) {
    return xvfb.__awaiter(this, void 0, void 0, function* () {
        try {
            yield xvfb.xvfb.start();
            debug('xvfb started');
            const code = yield userFriendlySpawn(spawn);
            return code;
        }
        finally {
            yield xvfb.xvfb.stop();
        }
    });
}
function userFriendlySpawn(spawn, linuxWithDisplayEnv) {
    return xvfb.__awaiter(this, void 0, void 0, function* () {
        debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv));
        let brokenGtkDisplay = false;
        const overrides = {};
        if (linuxWithDisplayEnv) {
            _.extend(overrides, {
                electronLogging: true,
                onStderrData(str) {
                    // if we receive a broken pipe anywhere
                    // then we know that's why cypress exited early
                    if (xvfb.util.isBrokenGtkDisplay(str)) {
                        brokenGtkDisplay = true;
                    }
                },
            });
        }
        try {
            const code = yield spawn(overrides);
            debug('tried spawning without xvfb, code', code, brokenGtkDisplay);
            if (code !== 0 && brokenGtkDisplay) {
                xvfb.util.logBrokenGtkDisplayWarning();
                return spawnInXvfb(spawn);
            }
            return code;
        }
        catch (error) {
            debug('error in userFriendlySpawn', error);
            // we can format and handle an error message from the code above
            // prevent wrapping error again by using "known: undefined" filter
            if (error.known === undefined) {
                const raiseErrorFn = xvfb.throwFormErrorText(xvfb.errors.unexpected);
                yield raiseErrorFn(error.message);
            }
            throw error;
        }
    });
}
function start(args_1) {
    return xvfb.__awaiter(this, arguments, void 0, function* (args, options = {}) {
        var _a, _b, _c, _d;
        let executable = xvfb.util.getEnv('CYPRESS_RUN_BINARY') ?
            path.resolve(xvfb.util.getEnv('CYPRESS_RUN_BINARY')) :
            xvfb.stateModule.getPathToExecutable(xvfb.stateModule.getBinaryDir());
        // Always push cwd into the args
        // which additionally acts as a signal to the
        // binary that it was invoked through the NPM module
        const baseArgs = args ? (typeof args === 'string' ? [args] : args) : [];
        const decoratedArgs = baseArgs.concat([
            '--cwd', process$1.cwd(),
            '--userNodePath', process$1.execPath,
            '--userNodeVersion', process$1.versions.node,
        ]);
        const needsXvfb = xvfb.xvfb.isNeeded();
        debug('needs to start own Xvfb?', needsXvfb);
        const stdio = (_a = options.stdio) !== null && _a !== void 0 ? _a : getStdioStrategy(needsXvfb);
        const dev = (_b = options.dev) !== null && _b !== void 0 ? _b : false;
        const detached = (_c = options.detached) !== null && _c !== void 0 ? _c : false;
        const env = (_d = options.env) !== null && _d !== void 0 ? _d : process$1.env;
        const spawn = createSpawnFunction(executable, decoratedArgs, { stdio, dev, detached, env });
        if (needsXvfb) {
            debug('starting xvfb');
            return spawnInXvfb(spawn);
        }
        // if we are on linux and there's already a DISPLAY
        // set, then we may need to rerun cypress after
        // spawning our own Xvfb server
        const linuxWithDisplayEnv = xvfb.util.isPossibleLinuxWithIncorrectDisplay();
        debug('linuxWithDisplayEnv', linuxWithDisplayEnv);
        return userFriendlySpawn(spawn, linuxWithDisplayEnv);
    });
}

exports.start = start$1;
exports.start$1 = start;