cypress 15.15.0 → 15.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cypress CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- var cli = require('../cli-CQHngRhO.js');
5
- require('../xvfb-Csr_YzMk.js');
4
+ var cli = require('../cli-CqSCkgQ7.js');
5
+ require('../xvfb-CqCqXRC-.js');
6
6
  require('os');
7
7
  require('bluebird');
8
8
  require('@cypress/xvfb');
@@ -32,7 +32,7 @@ require('commander');
32
32
  require('cli-table3');
33
33
  require('dayjs');
34
34
  require('dayjs/plugin/relativeTime');
35
- require('../spawn-D9ARC26J.js');
35
+ require('../spawn-Cnrq9zm4.js');
36
36
  require('child_process');
37
37
  require('listr2');
38
38
  require('readline');
@@ -45,7 +45,7 @@ require('@cypress/request');
45
45
  require('request-progress');
46
46
  require('proxy-from-env');
47
47
  require('yauzl');
48
- require('extract-zip');
48
+ require('util');
49
49
  require('pretty-bytes');
50
50
 
51
51
  // declared here in order to avoid consumers who are looking for the binary to be available relative to the dist directory
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var xvfb = require('./xvfb-Csr_YzMk.js');
3
+ var xvfb = require('./xvfb-CqCqXRC-.js');
4
4
  var _ = require('lodash');
5
5
  var commander = require('commander');
6
6
  var commonTags = require('common-tags');
@@ -13,18 +13,20 @@ var dayjs = require('dayjs');
13
13
  var relativeTime = require('dayjs/plugin/relativeTime');
14
14
  var chalk = require('chalk');
15
15
  var Bluebird = require('bluebird');
16
- var spawn = require('./spawn-D9ARC26J.js');
16
+ var spawn = require('./spawn-Cnrq9zm4.js');
17
17
  var os = require('os');
18
18
  var listr2 = require('listr2');
19
19
  var timers = require('timers/promises');
20
- var promises = require('fs/promises');
20
+ var fsp = require('fs/promises');
21
21
  var assert = require('assert');
22
22
  var request = require('@cypress/request');
23
23
  var requestProgress = require('request-progress');
24
24
  var proxyFromEnv = require('proxy-from-env');
25
25
  var cp = require('child_process');
26
26
  var yauzl = require('yauzl');
27
- var extract = require('extract-zip');
27
+ var fs$1 = require('fs');
28
+ var util = require('util');
29
+ var require$$0 = require('stream');
28
30
  var readline = require('readline');
29
31
  var prettyBytes = require('pretty-bytes');
30
32
 
@@ -304,9 +306,154 @@ const downloadModule = {
304
306
  getCA,
305
307
  };
306
308
 
309
+ const pipelineAsync = util.promisify(require$$0.pipeline);
310
+ // Unix file mode masks for entries stored in zip's externalFileAttributes
311
+ // (the high 16 bits when the file was zipped on a Unix host).
312
+ const S_IFMT = 0o170000;
313
+ const S_IFDIR = 0o040000;
314
+ const S_IFLNK = 0o120000;
315
+ // PATH_MAX on Linux/macOS is 4096; symlink targets larger than this are not
316
+ // legal filesystem paths and almost certainly indicate a malformed or
317
+ // malicious archive. The cap also prevents reading an arbitrarily large
318
+ // "symlink" entry into memory.
319
+ const MAX_SYMLINK_TARGET_BYTES = 4096;
320
+ /**
321
+ * Extracts the contents of a zip archive into the given destination directory.
322
+ * Recreates directories, regular files, and symlinks while preserving Unix
323
+ * file modes encoded in each entry's external attributes. Calls `onEntry` once
324
+ * per archive entry processed. Refuses entries whose resolved path would
325
+ * escape the destination directory.
326
+ */
327
+ const extractWithYauzl = (zipFilePath, destDir, onEntry) => xvfb.__awaiter(void 0, void 0, void 0, function* () {
328
+ const resolvedDest = path.resolve(destDir);
329
+ yield new Promise((resolve, reject) => {
330
+ // autoClose: false — `finish` below owns closing the zipfile, so we don't
331
+ // want yauzl's internal end-listener closing it first and tripping a
332
+ // double-close (EBADF) when we do.
333
+ yauzl.open(zipFilePath, { lazyEntries: true, autoClose: false }, (err, zipFile) => {
334
+ if (err) {
335
+ return reject(err);
336
+ }
337
+ // `settled` guards against an in-flight `handleEntry` calling
338
+ // `zipFile.readEntry()` on a now-closed handle when extraction has
339
+ // already failed (e.g. yauzl emitted 'error' while we were writing
340
+ // an entry to disk).
341
+ let settled = false;
342
+ const finish = _.once((err) => {
343
+ var _a, _b;
344
+ settled = true;
345
+ (_a = zipFile.removeAllListeners) === null || _a === void 0 ? void 0 : _a.call(zipFile);
346
+ (_b = zipFile.close) === null || _b === void 0 ? void 0 : _b.call(zipFile);
347
+ if (err) {
348
+ return reject(err);
349
+ }
350
+ return resolve();
351
+ });
352
+ // Normalize any thrown / emitted value into a real Error so that a
353
+ // falsy rejection (e.g. `Promise.reject(undefined)`) doesn't get
354
+ // misread by `finish` as a successful completion.
355
+ const fail = (err) => {
356
+ finish(err instanceof Error ? err : new Error(typeof err === 'string' && err ? err : 'zip extraction failed'));
357
+ };
358
+ zipFile.on('error', fail);
359
+ zipFile.on('end', () => finish());
360
+ zipFile.on('entry', (entry) => {
361
+ handleEntry(zipFile, entry, resolvedDest)
362
+ .then(() => {
363
+ if (settled)
364
+ return;
365
+ onEntry();
366
+ zipFile.readEntry();
367
+ })
368
+ .catch(fail);
369
+ });
370
+ zipFile.readEntry();
371
+ });
372
+ });
373
+ });
374
+ const handleEntry = (zipFile, entry, resolvedDest) => xvfb.__awaiter(void 0, void 0, void 0, function* () {
375
+ const fileDest = path.resolve(resolvedDest, entry.fileName);
376
+ // refuse anything that would write outside the install dir
377
+ if (fileDest !== resolvedDest &&
378
+ !fileDest.startsWith(resolvedDest + path.sep)) {
379
+ throw new Error(`Refusing to extract entry outside of destination: ${entry.fileName}`);
380
+ }
381
+ const unixMode = (entry.externalFileAttributes >>> 16) & 0xffff;
382
+ // Some archivers mark directories by Unix mode bits instead of (or in
383
+ // addition to) a trailing slash; honor both so we don't extract a
384
+ // directory entry as a zero-byte file.
385
+ const isDir = /\/$/.test(entry.fileName) || (unixMode & S_IFMT) === S_IFDIR;
386
+ const isSymlink = (unixMode & S_IFMT) === S_IFLNK;
387
+ if (isDir) {
388
+ yield fsp.mkdir(fileDest, { recursive: true });
389
+ return;
390
+ }
391
+ yield fsp.mkdir(path.dirname(fileDest), { recursive: true });
392
+ if (isSymlink) {
393
+ if (entry.uncompressedSize > MAX_SYMLINK_TARGET_BYTES) {
394
+ throw new Error(`Refusing to extract symlink with target larger than ${MAX_SYMLINK_TARGET_BYTES} bytes: ${entry.fileName}`);
395
+ }
396
+ const linkTarget = yield readEntryAsString(zipFile, entry, MAX_SYMLINK_TARGET_BYTES);
397
+ const resolvedTarget = path.resolve(path.dirname(fileDest), linkTarget);
398
+ if (resolvedTarget !== resolvedDest &&
399
+ !resolvedTarget.startsWith(resolvedDest + path.sep)) {
400
+ throw new Error(`Refusing to extract symlink pointing outside of destination: ${entry.fileName} -> ${linkTarget}`);
401
+ }
402
+ yield fsp.rm(fileDest, { recursive: true, force: true });
403
+ yield fsp.symlink(linkTarget, fileDest);
404
+ return;
405
+ }
406
+ const readStream = yield new Promise((res, rej) => {
407
+ zipFile.openReadStream(entry, (err, rs) => {
408
+ if (err) {
409
+ return rej(err);
410
+ }
411
+ return res(rs);
412
+ });
413
+ });
414
+ // Preserve the Unix mode bits when present; otherwise fall back to a sane default.
415
+ const fileMode = (unixMode & 0o7777) || 0o644;
416
+ const writeStream = fs$1.createWriteStream(fileDest, { mode: fileMode });
417
+ yield pipelineAsync(readStream, writeStream);
418
+ });
419
+ const readEntryAsString = (zipFile, entry, maxBytes) => {
420
+ return new Promise((resolve, reject) => {
421
+ zipFile.openReadStream(entry, (err, rs) => {
422
+ if (err) {
423
+ return reject(err);
424
+ }
425
+ const chunks = [];
426
+ let received = 0;
427
+ let bailed = false;
428
+ const bail = (err) => {
429
+ var _a;
430
+ if (bailed)
431
+ return;
432
+ bailed = true;
433
+ (_a = rs.destroy) === null || _a === void 0 ? void 0 : _a.call(rs, err);
434
+ reject(err);
435
+ };
436
+ rs.on('data', (chunk) => {
437
+ received += chunk.length;
438
+ if (received > maxBytes) {
439
+ bail(new Error(`Refusing to read entry body larger than ${maxBytes} bytes: ${entry.fileName}`));
440
+ return;
441
+ }
442
+ chunks.push(chunk);
443
+ });
444
+ rs.on('end', () => {
445
+ if (bailed)
446
+ return;
447
+ resolve(Buffer.concat(chunks).toString('utf8'));
448
+ });
449
+ rs.on('error', bail);
450
+ });
451
+ });
452
+ };
453
+
307
454
  const debug$5 = Debug('cypress:cli:unzip');
308
455
  const unzipTools = {
309
- extract,
456
+ extractWithYauzl,
310
457
  };
311
458
  // expose this function for simple testing
312
459
  const unzip = (_a) => xvfb.__awaiter(void 0, [_a], void 0, function* ({ zipFilePath, installDir, progress }) {
@@ -319,7 +466,10 @@ const unzip = (_a) => xvfb.__awaiter(void 0, [_a], void 0, function* ({ zipFileP
319
466
  let yauzlDoneTime = 0;
320
467
  yield fs.ensureDir(installDir);
321
468
  yield new Promise((resolve, reject) => {
322
- return yauzl.open(zipFilePath, (err, zipFile) => {
469
+ // Open with lazyEntries so yauzl doesn't auto-emit entries (which would
470
+ // require the fd to stay open for the duration of the OS-tool extraction).
471
+ // We only need the entryCount here for the progress calculation.
472
+ return yauzl.open(zipFilePath, { lazyEntries: true }, (err, zipFile) => {
323
473
  yauzlDoneTime = Date.now();
324
474
  if (err) {
325
475
  debug$5('error using yauzl %s', err.message);
@@ -327,6 +477,8 @@ const unzip = (_a) => xvfb.__awaiter(void 0, [_a], void 0, function* ({ zipFileP
327
477
  }
328
478
  const total = zipFile.entryCount;
329
479
  debug$5('zipFile entries count', total);
480
+ // Close the count-only handle — the Node fallback re-opens the zip for extraction.
481
+ zipFile.close();
330
482
  const started = new Date();
331
483
  let percent = 0;
332
484
  let count = 0;
@@ -343,13 +495,8 @@ const unzip = (_a) => xvfb.__awaiter(void 0, [_a], void 0, function* ({ zipFileP
343
495
  };
344
496
  const unzipWithNode = () => xvfb.__awaiter(void 0, void 0, void 0, function* () {
345
497
  debug$5('unzipping with node.js (slow)');
346
- const opts = {
347
- dir: installDir,
348
- onEntry: tick,
349
- };
350
- debug$5('calling Node extract tool %s %o', zipFilePath, opts);
351
498
  try {
352
- yield unzipTools.extract(zipFilePath, opts);
499
+ yield unzipTools.extractWithYauzl(zipFilePath, installDir, tick);
353
500
  debug$5('node unzip finished');
354
501
  return resolve();
355
502
  }
@@ -569,7 +716,7 @@ const start$1 = (...args_1) => xvfb.__awaiter(void 0, [...args_1], void 0, funct
569
716
  if (!pkgPath) {
570
717
  return xvfb.throwFormErrorText('Could not find package.json for Cypress package to determine build information')();
571
718
  }
572
- const { buildInfo, version } = JSON.parse(yield promises.readFile(pkgPath, 'utf8'));
719
+ const { buildInfo, version } = JSON.parse(yield fsp.readFile(pkgPath, 'utf8'));
573
720
  _.defaults(options, {
574
721
  force: false,
575
722
  buildInfo,
@@ -673,9 +820,10 @@ const start$1 = (...args_1) => xvfb.__awaiter(void 0, [...args_1], void 0, funct
673
820
  // let the user know what version of cypress we're downloading!
674
821
  xvfb.loggerModule.log(`Installing Cypress ${chalk.gray(`(version: ${versionToInstall})`)}`);
675
822
  xvfb.loggerModule.log();
676
- const taskRunner = new listr2.Listr(tasks, {
677
- silentRendererCondition: () => xvfb.loggerModule.logLevel() === 'silent',
678
- });
823
+ const taskRunner = new listr2.Listr(tasks, Object.assign(Object.assign({
824
+ // In CI we want timestamped, line-per-event output. Locally,
825
+ // the default in-place spinner is the better experience.
826
+ renderer: xvfb.util.isCi() ? 'verbose' : 'default' }, (xvfb.util.isCi() && { rendererOptions: { timestamp: listr2.PRESET_TIMESTAMP } })), { silentRendererCondition: () => xvfb.loggerModule.logLevel() === 'silent' }));
679
827
  yield taskRunner.run();
680
828
  // delay 1 sec for UX, unless we are testing
681
829
  yield timers.setTimeout(1000);
@@ -1083,6 +1231,8 @@ function getSize(path$1) {
1083
1231
  }
1084
1232
 
1085
1233
  dayjs.extend(relativeTime);
1234
+ // Subdirs under the cache root that are not binary version dirs.
1235
+ const EXTERNAL_CACHE_ENTRIES = new Set(['bundles']);
1086
1236
  // output colors for the table
1087
1237
  const colors = {
1088
1238
  titles: chalk.white,
@@ -1104,6 +1254,8 @@ const prune = () => xvfb.__awaiter(void 0, void 0, void 0, function* () {
1104
1254
  try {
1105
1255
  const versions = yield fs.readdir(cacheDir);
1106
1256
  for (const version of versions) {
1257
+ if (EXTERNAL_CACHE_ENTRIES.has(version))
1258
+ continue;
1107
1259
  if (version !== checkedInBinaryVersion) {
1108
1260
  deletedBinary = true;
1109
1261
  const versionDir = path.join(cacheDir, version);
@@ -1423,6 +1575,8 @@ const descriptions = {
1423
1575
  group: 'a named group for recorded runs in Cypress Cloud',
1424
1576
  headed: 'displays the browser instead of running headlessly',
1425
1577
  headless: 'hide the browser instead of running headed (default for cypress run)',
1578
+ inspect: 'enable the Node.js inspector to debug the Cypress development process. only available when used with --dev',
1579
+ inspectBrk: 'enable the Node.js inspector and break before the Cypress development process starts. only available when used with --dev',
1426
1580
  key: 'your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.',
1427
1581
  parallel: 'enables concurrent runs and automatic load balancing of specs across multiple machines or processes',
1428
1582
  passWithNoTests: 'pass when no tests are found',
@@ -1551,8 +1705,7 @@ const addCypressRunCommand = (program) => {
1551
1705
  .option('--no-runner-ui', text('noRunnerUi'))
1552
1706
  .option('-o, --reporter-options <reporter-options>', text('reporterOptions'))
1553
1707
  .option('-s, --spec <spec>', text('spec'))
1554
- .option('-t, --tag <tag>', text('tag'))
1555
- .option('--dev', text('dev'), coerceFalse);
1708
+ .option('-t, --tag <tag>', text('tag'));
1556
1709
  };
1557
1710
  const addCypressOpenCommand = (program) => {
1558
1711
  return program
@@ -1569,14 +1722,24 @@ const addCypressOpenCommand = (program) => {
1569
1722
  .option('-x, --expose <expose>', text('expose'))
1570
1723
  .option('--global', text('global'))
1571
1724
  .option('-p, --port <port>', text('port'))
1572
- .option('-P, --project <project-path>', text('project'))
1573
- .option('--dev', text('dev'), coerceFalse);
1725
+ .option('-P, --project <project-path>', text('project'));
1726
+ };
1727
+ // `--dev`, `--inspect` and `--inspect-brk` are internal flags used when
1728
+ // developing Cypress itself. They are intentionally hidden from the public
1729
+ // `--help` output and only registered when `--dev` is actually passed, so that
1730
+ // released versions don't advertise flags that error for end users.
1731
+ // See https://github.com/cypress-io/cypress/issues/21320
1732
+ const maybeAddDevFlag = (program, args) => {
1733
+ if (args.includes('--dev')) {
1734
+ return program.option('--dev', text('dev'), coerceFalse);
1735
+ }
1736
+ return program;
1574
1737
  };
1575
- const maybeAddInspectFlags = (program) => {
1576
- if (process.argv.includes('--dev')) {
1738
+ const maybeAddInspectFlags = (program, args) => {
1739
+ if (args.includes('--dev')) {
1577
1740
  return program
1578
- .option('--inspect', 'Node option')
1579
- .option('--inspect-brk', 'Node option');
1741
+ .option('--inspect', text('inspect'))
1742
+ .option('--inspect-brk', text('inspectBrk'));
1580
1743
  }
1581
1744
  return program;
1582
1745
  };
@@ -1617,7 +1780,7 @@ const cliModule = {
1617
1780
  cliArgs.unshift(null, null);
1618
1781
  debug('creating program parser');
1619
1782
  const program = createProgram();
1620
- maybeAddInspectFlags(addCypressRunCommand(program))
1783
+ maybeAddInspectFlags(maybeAddDevFlag(addCypressRunCommand(program), cliArgs), cliArgs)
1621
1784
  .action((...fnArgs) => {
1622
1785
  debug('parsed Cypress run %o', fnArgs);
1623
1786
  const options = parseVariableOpts(fnArgs, cliArgs);
@@ -1649,7 +1812,7 @@ const cliModule = {
1649
1812
  cliArgs.unshift(null, null);
1650
1813
  debug('creating program parser');
1651
1814
  const program = createProgram();
1652
- maybeAddInspectFlags(addCypressOpenCommand(program))
1815
+ maybeAddInspectFlags(maybeAddDevFlag(addCypressOpenCommand(program), cliArgs), cliArgs)
1653
1816
  .action((...fnArgs) => {
1654
1817
  debug('parsed Cypress open %o', fnArgs);
1655
1818
  const options = parseVariableOpts(fnArgs, cliArgs);
@@ -1719,7 +1882,7 @@ const cliModule = {
1719
1882
  .option('-v, --version', text('version'))
1720
1883
  .command('version')
1721
1884
  .description(text('version')));
1722
- maybeAddInspectFlags(addCypressOpenCommand(program))
1885
+ maybeAddInspectFlags(maybeAddDevFlag(addCypressOpenCommand(program), args), args)
1723
1886
  .action((opts) => xvfb.__awaiter(this, void 0, void 0, function* () {
1724
1887
  debug('opening Cypress');
1725
1888
  try {
@@ -1730,7 +1893,7 @@ const cliModule = {
1730
1893
  xvfb.util.logErrorExit1(e);
1731
1894
  }
1732
1895
  }));
1733
- maybeAddInspectFlags(addCypressRunCommand(program))
1896
+ maybeAddInspectFlags(maybeAddDevFlag(addCypressRunCommand(program), args), args)
1734
1897
  .action((...fnArgs) => xvfb.__awaiter(this, void 0, void 0, function* () {
1735
1898
  debug('running Cypress with args %o', fnArgs);
1736
1899
  try {
@@ -1754,11 +1917,10 @@ const cliModule = {
1754
1917
  xvfb.util.logErrorExit1(e);
1755
1918
  }
1756
1919
  }));
1757
- program
1920
+ maybeAddDevFlag(program
1758
1921
  .command('verify')
1759
1922
  .usage('[options]')
1760
- .description('Verifies that Cypress is installed correctly and executable')
1761
- .option('--dev', text('dev'), coerceFalse)
1923
+ .description('Verifies that Cypress is installed correctly and executable'), args)
1762
1924
  .action((opts) => xvfb.__awaiter(this, void 0, void 0, function* () {
1763
1925
  const defaultOpts = { force: true, welcomeMessage: false };
1764
1926
  const parsedOpts = xvfb.util.parseOpts(opts);
@@ -1809,11 +1971,10 @@ const cliModule = {
1809
1971
  cacheModule[command]();
1810
1972
  });
1811
1973
  });
1812
- program
1974
+ maybeAddDevFlag(program
1813
1975
  .command('info')
1814
1976
  .usage('[command]')
1815
- .description('Prints Cypress and system information')
1816
- .option('--dev', text('dev'), coerceFalse)
1977
+ .description('Prints Cypress and system information'), args)
1817
1978
  .action((opts) => xvfb.__awaiter(this, void 0, void 0, function* () {
1818
1979
  try {
1819
1980
  const code = yield methods.start(opts);
package/dist/cli.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- require('./xvfb-Csr_YzMk.js');
5
+ require('./xvfb-CqCqXRC-.js');
6
6
  require('lodash');
7
7
  require('commander');
8
8
  require('common-tags');
9
9
  require('log-symbols');
10
10
  require('debug');
11
- var cli = require('./cli-CQHngRhO.js');
12
- require('./spawn-D9ARC26J.js');
11
+ var cli = require('./cli-CqSCkgQ7.js');
12
+ require('./spawn-Cnrq9zm4.js');
13
13
  require('os');
14
14
  require('bluebird');
15
15
  require('@cypress/xvfb');
@@ -42,10 +42,10 @@ require('request-progress');
42
42
  require('proxy-from-env');
43
43
  require('child_process');
44
44
  require('yauzl');
45
- require('extract-zip');
45
+ require('util');
46
+ require('stream');
46
47
  require('readline');
47
48
  require('pretty-bytes');
48
- require('stream');
49
49
  require('string_decoder');
50
50
  require('node:string_decoder');
51
51
 
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var xvfb = require('./xvfb-Csr_YzMk.js');
3
+ var xvfb = require('./xvfb-CqCqXRC-.js');
4
4
  var tmp = require('tmp');
5
5
  var fs = require('fs-extra');
6
- var cli$1 = require('./cli-CQHngRhO.js');
6
+ var cli$1 = require('./cli-CqSCkgQ7.js');
7
7
 
8
8
  /**
9
9
  * Opens Cypress GUI
package/dist/cypress.js CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- require('./xvfb-Csr_YzMk.js');
3
+ require('./xvfb-CqCqXRC-.js');
4
4
  require('tmp');
5
5
  require('fs-extra');
6
- require('./cli-CQHngRhO.js');
7
- var cypress = require('./cypress-BMOTlyGc.js');
6
+ require('./cli-CqSCkgQ7.js');
7
+ var cypress = require('./cypress-BZS7OvER.js');
8
8
  require('os');
9
9
  require('bluebird');
10
10
  require('@cypress/xvfb');
@@ -33,7 +33,7 @@ require('commander');
33
33
  require('cli-table3');
34
34
  require('dayjs');
35
35
  require('dayjs/plugin/relativeTime');
36
- require('./spawn-D9ARC26J.js');
36
+ require('./spawn-Cnrq9zm4.js');
37
37
  require('child_process');
38
38
  require('listr2');
39
39
  require('readline');
@@ -46,7 +46,7 @@ require('@cypress/request');
46
46
  require('request-progress');
47
47
  require('proxy-from-env');
48
48
  require('yauzl');
49
- require('extract-zip');
49
+ require('util');
50
50
  require('pretty-bytes');
51
51
 
52
52
 
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- require('../xvfb-Csr_YzMk.js');
3
+ require('../xvfb-CqCqXRC-.js');
4
4
  require('lodash');
5
5
  require('os');
6
6
  require('child_process');
7
7
  require('path');
8
8
  require('debug');
9
- var spawn = require('../spawn-D9ARC26J.js');
9
+ var spawn = require('../spawn-Cnrq9zm4.js');
10
10
  require('readline');
11
11
  require('process');
12
12
  require('stream');
package/dist/exec/xvfb.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var xvfb = require('../xvfb-Csr_YzMk.js');
5
+ var xvfb = require('../xvfb-CqCqXRC-.js');
6
6
  require('os');
7
7
  require('bluebird');
8
8
  require('@cypress/xvfb');
package/dist/index.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var xvfb = require('./xvfb-Csr_YzMk.js');
5
+ var xvfb = require('./xvfb-CqCqXRC-.js');
6
6
  var minimist = require('minimist');
7
7
  var Debug = require('debug');
8
- var cli$1 = require('./cli-CQHngRhO.js');
9
- var spawn = require('./spawn-D9ARC26J.js');
10
- var cypress = require('./cypress-BMOTlyGc.js');
8
+ var cli$1 = require('./cli-CqSCkgQ7.js');
9
+ var spawn = require('./spawn-Cnrq9zm4.js');
10
+ var cypress = require('./cypress-BZS7OvER.js');
11
11
  require('os');
12
12
  require('bluebird');
13
13
  require('@cypress/xvfb');
@@ -44,10 +44,10 @@ require('request-progress');
44
44
  require('proxy-from-env');
45
45
  require('child_process');
46
46
  require('yauzl');
47
- require('extract-zip');
47
+ require('util');
48
+ require('stream');
48
49
  require('readline');
49
50
  require('pretty-bytes');
50
- require('stream');
51
51
  require('string_decoder');
52
52
  require('node:string_decoder');
53
53
  require('tmp');
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var xvfb = require('./xvfb-Csr_YzMk.js');
3
+ var xvfb = require('./xvfb-CqCqXRC-.js');
4
4
  var _ = require('lodash');
5
5
  var os = require('os');
6
6
  var cp = require('child_process');
@@ -171,9 +171,10 @@ function verifyBinary(installedVersion, binaryDir, options) {
171
171
  yield xvfb.stateModule.writeBinaryVerifiedAsync(true, binaryDir);
172
172
  task.title = xvfb.util.titleize(chalk.green('Verified Cypress!'), chalk.gray(binaryDir));
173
173
  }),
174
- }], {
175
- silentRendererCondition: () => xvfb.loggerModule.logLevel() === 'silent',
176
- });
174
+ }], Object.assign(Object.assign({
175
+ // In CI we want timestamped, line-per-event output. Locally,
176
+ // the default in-place spinner is the better experience.
177
+ renderer: xvfb.util.isCi() ? 'verbose' : 'default' }, (xvfb.util.isCi() && { rendererOptions: { timestamp: listr2.PRESET_TIMESTAMP } })), { silentRendererCondition: () => xvfb.loggerModule.logLevel() === 'silent' }));
177
178
  yield verifyTaskRunner.run();
178
179
  if (options.welcomeMessage) {
179
180
  xvfb.loggerModule.log();
@@ -1059,6 +1060,8 @@ var distExports = requireDist();
1059
1060
  const debug = Debug('cypress:cli');
1060
1061
  const debugElectron = Debug('cypress:electron');
1061
1062
  const debugStderr = Debug('cypress:internal-stderr');
1063
+ // Must match CYPRESS_OPEN_READY_MESSAGE in packages/server/lib/modes/interactive.ts
1064
+ const CYPRESS_OPEN_READY_MESSAGE = 'Cypress is ready';
1062
1065
  function isPlatform(platform) {
1063
1066
  return os.platform() === platform;
1064
1067
  }
@@ -1127,8 +1130,15 @@ function createSpawnFunction(executable, args, options) {
1127
1130
  }
1128
1131
  if (xvfb.util.isPossibleLinuxWithIncorrectDisplay()) {
1129
1132
  // make sure we use the latest DISPLAY variable if any
1130
- debug('passing DISPLAY', process.env.DISPLAY);
1131
- stdioOptions.env.DISPLAY = process.env.DISPLAY;
1133
+ debug('passing DISPLAY', process$1.env.DISPLAY);
1134
+ stdioOptions.env.DISPLAY = process$1.env.DISPLAY;
1135
+ }
1136
+ if (stdioOptions.detached) {
1137
+ // Ask interactive mode to print a ready sentinel on stdout; pipe stdio during
1138
+ // startup so errors are visible and we can detect it. Streams are destroyed
1139
+ // once it arrives so they don't keep the parent event loop alive.
1140
+ args.push('--emit-when-ready');
1141
+ stdioOptions.stdio = ['ignore', 'pipe', 'pipe'];
1132
1142
  }
1133
1143
  if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
1134
1144
  // Since we are running electron as node, we need to add an entry point file.
@@ -1144,8 +1154,8 @@ function createSpawnFunction(executable, args, options) {
1144
1154
  if (startScriptPath) {
1145
1155
  args.unshift(startScriptPath);
1146
1156
  }
1147
- if (process.env.CYPRESS_INTERNAL_DEV_DEBUG) {
1148
- args.unshift(process.env.CYPRESS_INTERNAL_DEV_DEBUG);
1157
+ if (process$1.env.CYPRESS_INTERNAL_DEV_DEBUG) {
1158
+ args.unshift(process$1.env.CYPRESS_INTERNAL_DEV_DEBUG);
1149
1159
  }
1150
1160
  debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'));
1151
1161
  debug('spawning Cypress with executable: %s', executable);
@@ -1156,14 +1166,16 @@ function createSpawnFunction(executable, args, options) {
1156
1166
  function resolveOn(event) {
1157
1167
  return function (code, signal) {
1158
1168
  debug('child event fired %o', { event, code, signal });
1159
- if (code === null) {
1160
- const errorObject = xvfb.errors.childProcessKilled(event, signal);
1161
- errorObject.platform = platform;
1162
- const err = xvfb.getErrorSync(errorObject, platform);
1163
- reject(err);
1169
+ if (signal) {
1170
+ if (signal === 'SIGINT') {
1171
+ resolve(0);
1172
+ }
1173
+ else {
1174
+ resolve(128 + os.constants.signals[signal]);
1175
+ }
1164
1176
  return;
1165
1177
  }
1166
- resolve(code);
1178
+ resolve(code !== null && code !== void 0 ? code : 1);
1167
1179
  };
1168
1180
  }
1169
1181
  const child = cp.spawn(executable, args, stdioOptions);
@@ -1184,6 +1196,44 @@ function createSpawnFunction(executable, args, options) {
1184
1196
  });
1185
1197
  });
1186
1198
  }
1199
+ else {
1200
+ // Adding listeners here prevents immediate process.exit() for these signals.
1201
+ // Exiting when the child process exits instead will allow the child process
1202
+ // to log during the exit process.
1203
+ // Unlike in windows, we do not need to propagate these signals to the child process
1204
+ // tree.
1205
+ for (const signal of ['SIGINT', 'SIGTERM']) {
1206
+ debug('adding message for signal listener for %s', signal);
1207
+ process$1.once(signal, function () {
1208
+ return xvfb.__awaiter(this, void 0, void 0, function* () {
1209
+ console.log(`\n\n${signal} received; Attempting to exit gracefully. Force exit with ^C again if needed.\n\n`);
1210
+ if (process$1.stdin.isTTY) {
1211
+ process$1.stdin.setRawMode(false);
1212
+ }
1213
+ });
1214
+ });
1215
+ }
1216
+ }
1217
+ if (stdioOptions.detached) {
1218
+ child.stdout.on('data', (data) => {
1219
+ const str = data.toString();
1220
+ const readyMessageIndex = str.indexOf(CYPRESS_OPEN_READY_MESSAGE);
1221
+ const isReady = readyMessageIndex !== -1;
1222
+ if (isReady) {
1223
+ const outputBeforeReady = str.slice(0, readyMessageIndex);
1224
+ if (outputBeforeReady)
1225
+ process$1.stdout.write(outputBeforeReady);
1226
+ child.stdout.destroy();
1227
+ child.stderr.destroy();
1228
+ child.unref();
1229
+ resolve(0);
1230
+ return;
1231
+ }
1232
+ process$1.stdout.write(data);
1233
+ });
1234
+ child.stderr.pipe(process$1.stderr, { end: false });
1235
+ return;
1236
+ }
1187
1237
  // if stdio options is set to 'pipe', then
1188
1238
  // we should set up pipes:
1189
1239
  // process STDIN (read stream) => child STDIN (writeable)
@@ -1214,9 +1264,9 @@ function createSpawnFunction(executable, args, options) {
1214
1264
  sourceStream.write(data);
1215
1265
  }
1216
1266
  });
1217
- if (((_a = process.env.ELECTRON_ENABLE_LOGGING) !== null && _a !== void 0 ? _a : '') === '1' ||
1267
+ if (((_a = process$1.env.ELECTRON_ENABLE_LOGGING) !== null && _a !== void 0 ? _a : '') === '1' ||
1218
1268
  debugElectron.enabled ||
1219
- ((_b = process.env.CYPRESS_INTERNAL_ENV) !== null && _b !== void 0 ? _b : '') === 'development') {
1269
+ ((_b = process$1.env.CYPRESS_INTERNAL_ENV) !== null && _b !== void 0 ? _b : '') === 'development') {
1220
1270
  sourceStream.pipe(process$1.stderr, { end: false });
1221
1271
  }
1222
1272
  else {
@@ -1231,14 +1281,12 @@ function createSpawnFunction(executable, args, options) {
1231
1281
  // to have any effect. so we're just catching the
1232
1282
  // error here and not doing anything.
1233
1283
  process$1.stdin.on('error', (err) => {
1284
+ debug('error on stdin', err);
1234
1285
  if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
1235
1286
  return;
1236
1287
  }
1237
1288
  reject(err);
1238
1289
  });
1239
- if (stdioOptions.detached) {
1240
- child.unref();
1241
- }
1242
1290
  }));
1243
1291
  };
1244
1292
  }
@@ -1304,16 +1352,16 @@ function start(args_1) {
1304
1352
  // binary that it was invoked through the NPM module
1305
1353
  const baseArgs = args ? (typeof args === 'string' ? [args] : args) : [];
1306
1354
  const decoratedArgs = baseArgs.concat([
1307
- '--cwd', process.cwd(),
1308
- '--userNodePath', process.execPath,
1309
- '--userNodeVersion', process.versions.node,
1355
+ '--cwd', process$1.cwd(),
1356
+ '--userNodePath', process$1.execPath,
1357
+ '--userNodeVersion', process$1.versions.node,
1310
1358
  ]);
1311
1359
  const needsXvfb = xvfb.xvfb.isNeeded();
1312
1360
  debug('needs to start own Xvfb?', needsXvfb);
1313
1361
  const stdio = (_a = options.stdio) !== null && _a !== void 0 ? _a : getStdioStrategy(needsXvfb);
1314
1362
  const dev = (_b = options.dev) !== null && _b !== void 0 ? _b : false;
1315
1363
  const detached = (_c = options.detached) !== null && _c !== void 0 ? _c : false;
1316
- const env = (_d = options.env) !== null && _d !== void 0 ? _d : process.env;
1364
+ const env = (_d = options.env) !== null && _d !== void 0 ? _d : process$1.env;
1317
1365
  const spawn = createSpawnFunction(executable, decoratedArgs, { stdio, dev, detached, env });
1318
1366
  if (needsXvfb) {
1319
1367
  debug('starting xvfb');
@@ -593,8 +593,12 @@ const isInstallingFromPostinstallHook = () => {
593
593
  };
594
594
  const getCacheDir = () => {
595
595
  let cache_directory = util.getCacheDir();
596
- if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
597
- const envVarCacheDir = untildify(util.getEnv('CYPRESS_CACHE_FOLDER'));
596
+ // Pass trim=true so we strip surrounding double quotes and whitespace.
597
+ // Windows CMD's `set CYPRESS_CACHE_FOLDER="C:\path"` embeds literal quotes
598
+ // into the env value; without dequoting, the resolved cache directory ends
599
+ // up with quote chars in its name (see cypress-io/cypress#4506).
600
+ if (util.getEnv('CYPRESS_CACHE_FOLDER', true)) {
601
+ const envVarCacheDir = untildify(util.getEnv('CYPRESS_CACHE_FOLDER', true));
598
602
  debug$1('using environment variable CYPRESS_CACHE_FOLDER %s', envVarCacheDir);
599
603
  if (!path.isAbsolute(envVarCacheDir) && isInstallingFromPostinstallHook()) {
600
604
  const packageRootFolder = path.join('..', '..', envVarCacheDir);
@@ -903,19 +907,6 @@ const invalidConfigFile = {
903
907
  description: '`--config-file` cannot be false.',
904
908
  solution: 'Either pass a relative path to a valid Cypress config file or remove this option.',
905
909
  };
906
- /**
907
- * This error happens when CLI detects that the child Test Runner process
908
- * was killed with a signal, like SIGBUS
909
- * @see https://github.com/cypress-io/cypress/issues/5808
910
- * @param {'close'|'event'} eventName Child close event name
911
- * @param {string} signal Signal that closed the child process, like "SIGBUS"
912
- */
913
- const childProcessKilled = (eventName, signal) => {
914
- return {
915
- description: `The Test Runner unexpectedly exited via a ${chalk.cyan(eventName)} event with signal ${chalk.cyan(signal)}`,
916
- solution: solutionUnknown,
917
- };
918
- };
919
910
  const CYPRESS_RUN_BINARY = {
920
911
  notValid: (value) => {
921
912
  const properFormat = `**/${stateModule.getPlatformExecutable()}`;
@@ -925,12 +916,6 @@ const CYPRESS_RUN_BINARY = {
925
916
  };
926
917
  },
927
918
  };
928
- function getErrorSync(errorObject, platform) {
929
- const errorMessage = syncFormErrorText(Object.assign(Object.assign({}, errorObject), { platform }));
930
- const err = new Error(errorMessage);
931
- err.known = true;
932
- return err;
933
- }
934
919
  function formErrorText(info, msg, prevMessage) {
935
920
  return __awaiter(this, void 0, void 0, function* () {
936
921
  const platform = yield util.getPlatformInfo();
@@ -1035,7 +1020,6 @@ const errors = {
1035
1020
  invalidCacheDirectory,
1036
1021
  CYPRESS_RUN_BINARY,
1037
1022
  smokeTestFailure,
1038
- childProcessKilled,
1039
1023
  incompatibleHeadlessFlags,
1040
1024
  invalidRunProjectPath,
1041
1025
  invalidTestingType,
@@ -1154,7 +1138,6 @@ exports._xvfb = _xvfb;
1154
1138
  exports._xvfbOptions = _xvfbOptions;
1155
1139
  exports.errors = errors;
1156
1140
  exports.exitWithError = exitWithError;
1157
- exports.getErrorSync = getErrorSync;
1158
1141
  exports.isNeeded = isNeeded;
1159
1142
  exports.loggerModule = loggerModule;
1160
1143
  exports.relativeToRepoRoot = relativeToRepoRoot;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cypress",
3
- "version": "15.15.0",
3
+ "version": "15.17.0",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "postinstall": "node dist/index.js --exec install",
@@ -27,7 +27,6 @@
27
27
  "eventemitter2": "6.4.7",
28
28
  "execa": "4.1.0",
29
29
  "executable": "^4.1.1",
30
- "extract-zip": "2.0.1",
31
30
  "fs-extra": "^9.1.0",
32
31
  "hasha": "5.2.2",
33
32
  "is-installed-globally": "~0.4.0",
@@ -46,7 +45,7 @@
46
45
  "tslib": "1.14.1",
47
46
  "tree-kill": "1.2.2",
48
47
  "untildify": "^4.0.0",
49
- "yauzl": "^2.10.0"
48
+ "yauzl": "^3.3.1"
50
49
  },
51
50
  "files": [
52
51
  "bin",
@@ -139,8 +138,8 @@
139
138
  },
140
139
  "buildInfo": {
141
140
  "commitBranch": "develop",
142
- "commitSha": "914ad901d9d50e2c744769a445643702ceca774b",
143
- "commitDate": "2026-05-12T03:59:53.000Z",
141
+ "commitSha": "b22780a9bb118b86730e22243aba16e25e7a5bed",
142
+ "commitDate": "2026-06-09T13:13:52.000Z",
144
143
  "stable": true
145
144
  },
146
145
  "description": "Cypress is a next generation front end testing tool built for the modern web",
@@ -1486,6 +1486,22 @@ declare namespace Cypress {
1486
1486
  * cy.get('button').should('be.visible')
1487
1487
  */
1488
1488
  get<K extends keyof HTMLElementTagNameMap>(selector: K, options?: Partial<Loggable & Timeoutable & Withinable & Shadow>): Chainable<JQuery<HTMLElementTagNameMap[K]>>
1489
+ /**
1490
+ * Get aliased DOM elements, intercepts, spies, stubs, or wrapped values via `@` prefix.
1491
+ * Use the `S` type parameter to specify the type of the aliased subject.
1492
+ * @see https://on.cypress.io/get#Alias
1493
+ * @example
1494
+ * // Get the aliased 'todos' elements
1495
+ * cy.get('ul#todos').as('todos')
1496
+ * cy.get<JQuery<HTMLLIElement>>('@todos')
1497
+ *
1498
+ * // Alias a wrapped value and retrieve it with proper typing
1499
+ * cy.wrap({ name: 'Jane' }).as('user')
1500
+ * cy.get<{ name: string }>('@user').then((user) => {
1501
+ * // user is typed as { name: string }
1502
+ * })
1503
+ */
1504
+ get<S = any>(alias: `@${string}`, options?: Partial<Loggable & Timeoutable & Withinable & Shadow>): Chainable<S>
1489
1505
  /**
1490
1506
  * Get one or more DOM elements by selector.
1491
1507
  * The querying behavior of this command matches exactly how $(…) works in jQuery.
@@ -1496,17 +1512,6 @@ declare namespace Cypress {
1496
1512
  * cy.get('.dropdown-menu').click()
1497
1513
  */
1498
1514
  get<E extends Node = HTMLElement>(selector: string, options?: Partial<Loggable & Timeoutable & Withinable & Shadow>): Chainable<JQuery<E>>
1499
- /**
1500
- * Get one or more DOM elements by alias.
1501
- * @see https://on.cypress.io/get#Alias
1502
- * @example
1503
- * // Get the aliased 'todos' elements
1504
- * cy.get('ul#todos').as('todos')
1505
- * //...hack hack hack...
1506
- * //later retrieve the todos
1507
- * cy.get('@todos')
1508
- */
1509
- get<S = any>(alias: string, options?: Partial<Loggable & Timeoutable & Withinable & Shadow>): Chainable<S>
1510
1515
 
1511
1516
  /**
1512
1517
  * Get a browser cookie by its name.
@@ -3442,6 +3447,7 @@ declare namespace Cypress {
3442
3447
  >, Partial<Pick<ResolvedConfigOptions, 'baseUrl' | 'testIsolation'>> {
3443
3448
  browser?: IsBrowserMatcher | IsBrowserMatcher[]
3444
3449
  keystrokeDelay?: number
3450
+ expose?: Record<string, any>
3445
3451
  }
3446
3452
 
3447
3453
  interface TestConfigOverrides extends Partial<
@@ -3449,6 +3455,7 @@ declare namespace Cypress {
3449
3455
  >, Partial<Pick<ResolvedConfigOptions, 'baseUrl'>> {
3450
3456
  browser?: IsBrowserMatcher | IsBrowserMatcher[]
3451
3457
  keystrokeDelay?: number
3458
+ expose?: Record<string, any>
3452
3459
  }
3453
3460
 
3454
3461
  /**
@@ -548,7 +548,7 @@ declare global {
548
548
  })
549
549
  ```
550
550
  */
551
- wait<TRequest = any, TResponse = any>(alias: string, options?: Partial<WaitOptions>): Chainable<Interception<TRequest, TResponse>>
551
+ wait<TRequest = any, TResponse = any>(alias: `@${string}`, options?: Partial<WaitOptions>): Chainable<Interception<TRequest, TResponse>>
552
552
  /**
553
553
  * Wait for list of requests to complete.
554
554
  *
@@ -568,7 +568,7 @@ declare global {
568
568
  })
569
569
  ```
570
570
  */
571
- wait(aliases: string[], options?: Partial<WaitOptions>): Chainable<Interception[]>
571
+ wait(aliases: `@${string}`[], options?: Partial<WaitOptions>): Chainable<Interception[]>
572
572
  }
573
573
  }
574
574
  }
@@ -12,7 +12,9 @@
12
12
  // Roey Berman <https://github.com/bergundy>
13
13
  // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
14
14
 
15
- import * as FakeTimers from '@sinonjs/fake-timers';
15
+ // Import via the @types name so TS resolves to the bundled
16
+ // @types/sinonjs__fake-timers (cypress-io/cypress#33829).
17
+ import * as FakeTimers from 'sinonjs__fake-timers';
16
18
 
17
19
  // sinon uses DOM dependencies which are absent in browser-less environment like node.js
18
20
  // to avoid compiler errors this monkey patch is used