vitest 5.0.0-beta.2 → 5.0.0-beta.4

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.
Files changed (44) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{base.Opc_YHkk.js → base.BEGVMQrS.js} +6 -6
  4. package/dist/chunks/{browser.d.BUhkKcDl.d.ts → browser.d.BGxB4Xum.d.ts} +5 -26
  5. package/dist/chunks/{cac.8N4bOkkB.js → cac.CyXAEMkE.js} +26 -33
  6. package/dist/chunks/{cli-api.B0RFke2g.js → cli-api.DJMXq34b.js} +640 -615
  7. package/dist/chunks/{config.d.D91DHYaD.d.ts → config.d.DXq1aBpy.d.ts} +93 -89
  8. package/dist/chunks/{creator.BqL2U_x4.js → creator.D66cVXYh.js} +2 -2
  9. package/dist/chunks/{defaults.szbHWQun.js → defaults.CUUnbOrq.js} +6 -4
  10. package/dist/chunks/{env.D4Lgay0q.js → env.BKKtU2WC.js} +2 -1
  11. package/dist/chunks/global.d.BtKPuz2X.d.ts +194 -0
  12. package/dist/chunks/{globals.EHmmu0nC.js → globals.BuY-yD0m.js} +2 -1
  13. package/dist/chunks/{index.D_7-4CaB.js → index.CE58PZNH.js} +1660 -701
  14. package/dist/chunks/{index.CViWo__T.js → index.CcluKS59.js} +4 -4
  15. package/dist/chunks/{index.CbgUM9E5.js → index.nQFVd50u.js} +2 -1
  16. package/dist/chunks/{init-forks.DMge3WTt.js → init-forks.Ce3vGWgL.js} +1 -1
  17. package/dist/chunks/{init-threads.eIoyCTon.js → init-threads.8e1OLv5v.js} +1 -1
  18. package/dist/chunks/{init.BVd7SaCA.js → init.6qx-LaHs.js} +31 -3
  19. package/dist/chunks/{nativeModuleMocker.DKpFw0pk.js → nativeModuleMocker.DDZfQXLs.js} +1 -1
  20. package/dist/chunks/{plugin.d.cIKZEZ16.d.ts → plugin.d.B7MTG_Fe.d.ts} +103 -88
  21. package/dist/chunks/{rpc.d.7JZuxZ8u.d.ts → rpc.d.OQ_EZi1Z.d.ts} +18 -2
  22. package/dist/chunks/{setup-common.Hpq30zVk.js → setup-common.DdEF_hkE.js} +1 -1
  23. package/dist/chunks/{vm.2okbRRME.js → vm.Bu7mmcZq.js} +2 -2
  24. package/dist/chunks/{worker.d.Bu1kXGw4.d.ts → worker.d.yR22cs6X.d.ts} +3 -2
  25. package/dist/cli.js +2 -2
  26. package/dist/config.cjs +1 -1
  27. package/dist/config.d.ts +7 -10
  28. package/dist/config.js +2 -2
  29. package/dist/index.d.ts +24 -41
  30. package/dist/index.js +2 -1
  31. package/dist/module-evaluator.d.ts +4 -1
  32. package/dist/module-evaluator.js +15 -20
  33. package/dist/node.d.ts +20 -13
  34. package/dist/node.js +6 -6
  35. package/dist/runtime.js +2 -2
  36. package/dist/worker.d.ts +3 -3
  37. package/dist/worker.js +7 -6
  38. package/dist/workers/forks.js +8 -7
  39. package/dist/workers/runVmTests.js +4 -3
  40. package/dist/workers/threads.js +8 -7
  41. package/dist/workers/vmForks.js +4 -4
  42. package/dist/workers/vmThreads.js +4 -4
  43. package/package.json +20 -21
  44. package/dist/chunks/global.d.DhbKSQoV.d.ts +0 -99
@@ -1,22 +1,23 @@
1
- import { TestSyntaxError, getCurrentTest, getCurrentSuite, updateTask, createTaskCollector, getHooks, getFn, afterAll, afterEach, aroundAll, aroundEach, beforeAll, beforeEach, describe, it, onTestFailed, onTestFinished, recordArtifact, suite, test } from '@vitest/runner';
1
+ import { TestSyntaxError, getCurrentTest, createTaskCollector, getCurrentSuite, getHooks, getFn, afterAll, afterEach, aroundAll, aroundEach, beforeAll, beforeEach, describe, it, onTestFailed, onTestFinished, recordArtifact, suite, test } from '@vitest/runner';
2
2
  import { i as isChildProcess, w as waitForImportsToResolve, r as resetModules, g as getWorkerState } from './utils.BX5Fg8C4.js';
3
3
  import { getSafeTimers, delay } from '@vitest/utils/timers';
4
4
  import { isMockFunction, fn, spyOn, restoreAllMocks, resetAllMocks, clearAllMocks } from '@vitest/spy';
5
- import { getType, isObject, noop, assertTypes, ordinal, createSimpleStackTrace, getCallLastIndex, createDefer } from '@vitest/utils/helpers';
5
+ import { getType, isObject, noop, assertTypes, ordinal, createSimpleStackTrace, getCallLastIndex } from '@vitest/utils/helpers';
6
6
  import { positionToOffset, offsetToLineNumber, lineSplitRE } from '@vitest/utils/offset';
7
7
  import { parseSingleStack, parseErrorStacktrace } from '@vitest/utils/source-map';
8
8
  import { c as commonjsGlobal, g as getDefaultExportFromCjs } from './_commonjsHelpers.D26ty3Ew.js';
9
9
  import { R as RealDate, b as resetDate, m as mockDate, r as rpc, V as VitestEvaluatedModules } from './rpc.DFRWVnRh.js';
10
10
  import * as chai from 'chai';
11
11
  import { use, util } from 'chai';
12
- import { getNames, createChainable, getTests, getTestName, matchesTags } from '@vitest/runner/utils';
12
+ import { getNames, getTests, getTestName, createChainable, matchesTags } from '@vitest/runner/utils';
13
13
  import { processError } from '@vitest/utils/error';
14
14
  import { g as getSerializers, a as addSerializer } from './plugins.DrsmdUE2.js';
15
15
  import { format } from '@vitest/pretty-format';
16
16
  import { printDiffOrStringify, diff } from '@vitest/utils/diff';
17
17
  import { stringify, inspect } from '@vitest/utils/display';
18
18
  import c from 'tinyrainbow';
19
- import { normalize } from 'pathe';
19
+ import { isAbsolute, relative, normalize } from 'pathe';
20
+ import { Bench } from 'tinybench';
20
21
  import { expectTypeOf } from 'expect-type';
21
22
 
22
23
  const ChaiStyleAssertions = (chai, utils) => {
@@ -1590,6 +1591,48 @@ const JestExtend = (chai, utils) => {
1590
1591
  });
1591
1592
  };
1592
1593
 
1594
+ function isBenchResult(value) {
1595
+ return typeof value === "object" && value !== null && "latency" in value && typeof value.latency?.mean === "number";
1596
+ }
1597
+ function formatOps(ops) {
1598
+ return ops.toLocaleString("en-US", {
1599
+ minimumFractionDigits: 2,
1600
+ maximumFractionDigits: 2
1601
+ });
1602
+ }
1603
+ const benchMatchers = {
1604
+ toBeFasterThan(actual, expected, options) {
1605
+ const { matcherHint, RECEIVED_COLOR, EXPECTED_COLOR } = this.utils;
1606
+ const delta = options?.delta ?? 0;
1607
+ if (!isBenchResult(actual)) throw new TypeError(`${matcherHint(".toBeFasterThan")} expects the actual value to be a benchmark result.`);
1608
+ if (!isBenchResult(expected)) throw new TypeError(`${matcherHint(".toBeFasterThan")} expects the expected value to be a benchmark result.`);
1609
+ const threshold = expected.latency.mean * (1 - delta);
1610
+ const pass = actual.latency.mean < threshold;
1611
+ return {
1612
+ pass,
1613
+ message: () => {
1614
+ const relation = ((actual.latency.mean - expected.latency.mean) / expected.latency.mean * 100).toFixed(2);
1615
+ return pass ? `${matcherHint(".not.toBeFasterThan")}\n\nExpected to not be faster, but was ${Math.abs(Number(relation))}% faster.\n\nReceived: ${RECEIVED_COLOR(formatOps(actual.throughput.mean))} ops/sec\nExpected: ${EXPECTED_COLOR(formatOps(expected.throughput.mean))} ops/sec\n` : `${matcherHint(".toBeFasterThan")}\n\nExpected to be faster${delta > 0 ? ` by at least ${(delta * 100).toFixed(0)}%` : ""}, but was ${Number(relation) > 0 ? `${relation}% slower` : `only ${Math.abs(Number(relation))}% faster`}.\n\nReceived: ${RECEIVED_COLOR(formatOps(actual.throughput.mean))} ops/sec\nExpected: ${EXPECTED_COLOR(formatOps(expected.throughput.mean))} ops/sec\n`;
1616
+ }
1617
+ };
1618
+ },
1619
+ toBeSlowerThan(actual, expected, options) {
1620
+ const { matcherHint, RECEIVED_COLOR, EXPECTED_COLOR } = this.utils;
1621
+ const delta = options?.delta ?? 0;
1622
+ if (!isBenchResult(actual)) throw new TypeError(`${matcherHint(".toBeSlowerThan")} expects the actual value to be a benchmark result.`);
1623
+ if (!isBenchResult(expected)) throw new TypeError(`${matcherHint(".toBeSlowerThan")} expects the expected value to be a benchmark result.`);
1624
+ const threshold = expected.latency.mean * (1 + delta);
1625
+ const pass = actual.latency.mean > threshold;
1626
+ return {
1627
+ pass,
1628
+ message: () => {
1629
+ const relation = ((actual.latency.mean - expected.latency.mean) / expected.latency.mean * 100).toFixed(2);
1630
+ return pass ? `${matcherHint(".not.toBeSlowerThan")}\n\nExpected to not be slower, but was ${relation}% slower.\n\nReceived: ${RECEIVED_COLOR(formatOps(actual.throughput.mean))} ops/sec\nExpected: ${EXPECTED_COLOR(formatOps(expected.throughput.mean))} ops/sec\n` : `${matcherHint(".toBeSlowerThan")}\n\nExpected to be slower${delta > 0 ? ` by at least ${(delta * 100).toFixed(0)}%` : ""}, but was ${Number(relation) < 0 ? `${Math.abs(Number(relation))}% faster` : `only ${relation}% slower`}.\n\nReceived: ${RECEIVED_COLOR(formatOps(actual.throughput.mean))} ops/sec\nExpected: ${EXPECTED_COLOR(formatOps(expected.throughput.mean))} ops/sec\n`;
1631
+ }
1632
+ };
1633
+ }
1634
+ };
1635
+
1593
1636
  var fakeTimersSrc = {};
1594
1637
 
1595
1638
  var global;
@@ -2529,12 +2572,12 @@ function requireFakeTimersSrc () {
2529
2572
  if (typeof __vitest_required__ !== 'undefined') {
2530
2573
  try {
2531
2574
  timersModule = __vitest_required__.timers;
2532
- } catch (e) {
2575
+ } catch {
2533
2576
  // ignored
2534
2577
  }
2535
2578
  try {
2536
2579
  timersPromisesModule = __vitest_required__.timersPromises;
2537
- } catch (e) {
2580
+ } catch {
2538
2581
  // ignored
2539
2582
  }
2540
2583
  }
@@ -2545,18 +2588,18 @@ function requireFakeTimersSrc () {
2545
2588
 
2546
2589
  /**
2547
2590
  * @typedef {object} NextAsyncTickMode
2548
- * @property {"nextAsync"} mode
2591
+ * @property {"nextAsync"} mode - runs timers one macrotask at a time
2549
2592
  */
2550
2593
 
2551
2594
  /**
2552
2595
  * @typedef {object} ManualTickMode
2553
- * @property {"manual"} mode
2596
+ * @property {"manual"} mode - advances only when the caller explicitly ticks
2554
2597
  */
2555
2598
 
2556
2599
  /**
2557
2600
  * @typedef {object} IntervalTickMode
2558
- * @property {"interval"} mode
2559
- * @property {number} [delta]
2601
+ * @property {"interval"} mode - advances automatically on a native interval
2602
+ * @property {number} [delta] - interval duration in milliseconds
2560
2603
  */
2561
2604
 
2562
2605
  /**
@@ -2564,134 +2607,447 @@ function requireFakeTimersSrc () {
2564
2607
  */
2565
2608
 
2566
2609
  /**
2567
- * @typedef {object} IdleDeadline
2568
- * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout
2569
- * @property {function():number} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period
2610
+ * @callback FakeTimersFunction
2611
+ * @param {...unknown[]} args
2612
+ * @returns {unknown}
2570
2613
  */
2571
2614
 
2572
2615
  /**
2573
- * Queues a function to be called during a browser's idle periods
2574
- * @callback RequestIdleCallback
2575
- * @param {function(IdleDeadline)} callback
2576
- * @param {{timeout: number}} options - an options object
2577
- * @returns {number} the id
2616
+ * @callback VoidVarArgsFunc
2617
+ * @param {...unknown[]} args - optional arguments to call the callback with
2618
+ * @returns {void}
2578
2619
  */
2579
2620
 
2580
2621
  /**
2581
2622
  * @callback NextTick
2582
2623
  * @param {VoidVarArgsFunc} callback - the callback to run
2583
- * @param {...*} args - optional arguments to call the callback with
2624
+ * @param {...unknown[]} args - optional arguments to call the callback with
2584
2625
  * @returns {void}
2585
2626
  */
2586
2627
 
2587
2628
  /**
2588
2629
  * @callback SetImmediate
2589
2630
  * @param {VoidVarArgsFunc} callback - the callback to run
2590
- * @param {...*} args - optional arguments to call the callback with
2631
+ * @param {...unknown[]} args - optional arguments to call the callback with
2591
2632
  * @returns {NodeImmediate}
2592
2633
  */
2593
2634
 
2594
2635
  /**
2595
- * @callback VoidVarArgsFunc
2596
- * @param {...*} callback - the callback to run
2636
+ * @callback SetTimeout
2637
+ * @param {VoidVarArgsFunc} callback - the callback to run
2638
+ * @param {number} [delay] - optional delay in milliseconds
2639
+ * @param {...unknown[]} args - optional arguments to call the callback with
2640
+ * @returns {TimerId} - the timeout identifier
2641
+ */
2642
+
2643
+ /**
2644
+ * @callback ClearTimeout
2645
+ * @param {TimerId} [id] - the timeout identifier to clear
2646
+ * @returns {void}
2647
+ */
2648
+
2649
+ /**
2650
+ * @callback SetInterval
2651
+ * @param {VoidVarArgsFunc} callback - the callback to run
2652
+ * @param {number} [delay] - optional delay in milliseconds
2653
+ * @param {...unknown[]} args - optional arguments to call the callback with
2654
+ * @returns {TimerId} - the interval identifier
2655
+ */
2656
+
2657
+ /**
2658
+ * @callback ClearInterval
2659
+ * @param {TimerId} [id] - the interval identifier to clear
2660
+ * @returns {void}
2661
+ */
2662
+
2663
+ /**
2664
+ * @callback QueueMicrotask
2665
+ * @param {VoidVarArgsFunc} callback - the callback to run
2666
+ * @returns {void}
2667
+ */
2668
+
2669
+ /**
2670
+ * @callback TimeRemaining
2671
+ * @returns {number}
2672
+ */
2673
+
2674
+ /**
2675
+ * @typedef {object} IdleDeadline
2676
+ * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout
2677
+ * @property {TimeRemaining} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period
2678
+ */
2679
+
2680
+ /**
2681
+ * @callback RequestIdleCallbackCallback
2682
+ * @param {IdleDeadline} deadline
2683
+ */
2684
+
2685
+ /**
2686
+ * Queues a function to be called during a browser's idle periods
2687
+ * @callback RequestIdleCallback
2688
+ * @param {RequestIdleCallbackCallback} callback
2689
+ * @param {{timeout?: number}} [options] - an options object
2690
+ * @returns {number} the id
2691
+ */
2692
+
2693
+ /**
2694
+ * @callback AnimationFrameCallback
2695
+ * @param {number} timestamp
2696
+ */
2697
+
2698
+ /**
2699
+ * @callback RequestAnimationFrame
2700
+ * @param {AnimationFrameCallback} callback
2701
+ * @returns {TimerId} - the request id
2702
+ */
2703
+
2704
+ /**
2705
+ * @callback CancelAnimationFrame
2706
+ * @param {TimerId} id - cancels a frame callback
2707
+ * @returns {void}
2708
+ */
2709
+
2710
+ /**
2711
+ * @callback CancelIdleCallback
2712
+ * @param {TimerId} id - cancels a scheduled idle callback
2713
+ * @returns {void}
2714
+ */
2715
+
2716
+ /**
2717
+ * @callback ClearImmediate
2718
+ * @param {NodeImmediate} id - faked `clearImmediate`
2719
+ * @returns {void}
2720
+ */
2721
+
2722
+ /**
2723
+ * @callback CountTimers
2724
+ * @returns {number}
2725
+ */
2726
+
2727
+ /**
2728
+ * @callback RunMicrotasks
2729
+ * @returns {void}
2730
+ */
2731
+
2732
+ /**
2733
+ * @callback Tick
2734
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
2735
+ * @returns {number} will return the new `now` value
2736
+ */
2737
+
2738
+ /**
2739
+ * @callback TickAsync
2740
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
2741
+ * @returns {Promise<number>}
2742
+ */
2743
+
2744
+ /**
2745
+ * @callback Next
2746
+ * @returns {number}
2747
+ */
2748
+
2749
+ /**
2750
+ * @callback NextAsync
2751
+ * @returns {Promise<number>}
2752
+ */
2753
+
2754
+ /**
2755
+ * @callback RunAll
2756
+ * @returns {number}
2757
+ */
2758
+
2759
+ /**
2760
+ * @callback RunToFrame
2761
+ * @returns {number}
2762
+ */
2763
+
2764
+ /**
2765
+ * @callback RunAllAsync
2766
+ * @returns {Promise<number>}
2767
+ */
2768
+
2769
+ /**
2770
+ * @callback RunToLast
2771
+ * @returns {number}
2772
+ */
2773
+
2774
+ /**
2775
+ * @callback RunToLastAsync
2776
+ * @returns {Promise<number>}
2777
+ */
2778
+
2779
+ /**
2780
+ * @callback Reset
2597
2781
  * @returns {void}
2598
2782
  */
2599
2783
 
2600
2784
  /**
2601
- * @typedef RequestAnimationFrame
2602
- * @property {function(number):void} requestAnimationFrame
2603
- * @returns {number} - the id
2785
+ * @callback SetSystemTime
2786
+ * @param {number|Date} [now] initial mocked time, as milliseconds since epoch or a Date
2787
+ * @returns {void}
2788
+ */
2789
+
2790
+ /**
2791
+ * @callback Jump
2792
+ * @param {number|string} tickValue milliseconds or a human-readable value like "01:11:15"
2793
+ * @returns {number}
2794
+ */
2795
+
2796
+ /**
2797
+ * @callback Uninstall
2798
+ * @returns {void}
2799
+ */
2800
+
2801
+ /**
2802
+ * @callback SetTickMode
2803
+ * @param {SetTickModeConfig} tickModeConfig - The new configuration for how the clock should tick.
2804
+ * @returns {void}
2805
+ */
2806
+
2807
+ /**
2808
+ * @callback Hrtime
2809
+ * @param {Array<number>} [prev]
2810
+ * @returns {Array<number>}
2811
+ */
2812
+
2813
+ /**
2814
+ * @callback WithGlobal
2815
+ * @param {object} _global Namespace to mock (e.g. `window`)
2816
+ * @returns {FakeTimers}
2817
+ */
2818
+
2819
+ /**
2820
+ * @typedef {"setTimeout" | "clearTimeout" | "setImmediate" | "clearImmediate" | "setInterval" | "clearInterval" | "Date" | "nextTick" | "hrtime" | "requestAnimationFrame" | "cancelAnimationFrame" | "requestIdleCallback" | "cancelIdleCallback" | "performance" | "queueMicrotask"} FakeMethod
2821
+ */
2822
+
2823
+ /**
2824
+ * @typedef {number | NodeImmediate | Timer} TimerId
2825
+ */
2826
+
2827
+ /* eslint-disable jsdoc/reject-any-type */
2828
+ /**
2829
+ * @typedef {Record<string, any> & {
2830
+ * setTimeout?: SetTimeout,
2831
+ * clearTimeout?: ClearTimeout,
2832
+ * setInterval?: SetInterval,
2833
+ * clearInterval?: ClearInterval,
2834
+ * setImmediate?: SetImmediate,
2835
+ * clearImmediate?: ClearImmediate,
2836
+ * queueMicrotask?: QueueMicrotask,
2837
+ * requestAnimationFrame?: RequestAnimationFrame,
2838
+ * cancelAnimationFrame?: CancelAnimationFrame,
2839
+ * requestIdleCallback?: RequestIdleCallback,
2840
+ * cancelIdleCallback?: CancelIdleCallback,
2841
+ * process?: any,
2842
+ * performance?: any,
2843
+ * Performance?: any,
2844
+ * Intl?: any,
2845
+ * Promise?: typeof Promise,
2846
+ * Date: typeof Date & { isFake?: boolean, toSource?: () => string, clock?: any }
2847
+ * }} GlobalObject
2848
+ */
2849
+
2850
+ /**
2851
+ * @typedef {object} TimerHeap
2852
+ * @property {Timer[]} timers - the heap-ordered timers
2853
+ * @property {() => Timer | undefined} peek - returns the next timer without removing it
2854
+ * @property {(timer: Timer) => void} push - adds a timer to the heap
2855
+ * @property {() => Timer | undefined} pop - removes and returns the next timer
2856
+ * @property {(timer: Timer) => void} remove - removes a specific timer
2857
+ */
2858
+
2859
+ /**
2860
+ * @typedef {object} ClockTickMode
2861
+ * @property {TickMode} mode - active tick mode
2862
+ * @property {number} counter - increments whenever the mode changes
2863
+ * @property {number} [delta] - interval length in milliseconds
2864
+ */
2865
+
2866
+ /**
2867
+ * @typedef {object} SetTickModeConfig
2868
+ * @property {TickMode} mode - desired tick mode
2869
+ * @property {number} [delta] - interval length in milliseconds
2870
+ */
2871
+
2872
+ /**
2873
+ * @typedef {Record<string, any> & { clock: Clock }} IntlWithClock
2874
+ */
2875
+
2876
+ /**
2877
+ * @typedef {object} Timers
2878
+ * @property {SetTimeout} setTimeout - native `setTimeout`
2879
+ * @property {ClearTimeout} clearTimeout - native `clearTimeout`
2880
+ * @property {SetInterval} setInterval - native `setInterval`
2881
+ * @property {ClearInterval} clearInterval - native `clearInterval`
2882
+ * @property {typeof Date} Date - native `Date`
2883
+ * @property {typeof Intl} [Intl] - native `Intl`
2884
+ * @property {SetImmediate} [setImmediate] - native `setImmediate`, if available
2885
+ * @property {ClearImmediate} [clearImmediate] - native `clearImmediate`, if available
2886
+ * @property {Hrtime} [hrtime] - native `process.hrtime`, if available
2887
+ * @property {NextTick} [nextTick] - native `process.nextTick`, if available
2888
+ * @property {Performance} [performance] - native `performance`, if available
2889
+ * @property {RequestAnimationFrame} [requestAnimationFrame] - native `requestAnimationFrame`, if available
2890
+ * @property {QueueMicrotask} [queueMicrotask] - whether `queueMicrotask` exists
2891
+ * @property {CancelAnimationFrame} [cancelAnimationFrame] - native `cancelAnimationFrame`, if available
2892
+ * @property {RequestIdleCallback} [requestIdleCallback] - native `requestIdleCallback`, if available
2893
+ * @property {CancelIdleCallback} [cancelIdleCallback] - native `cancelIdleCallback`, if available
2894
+ */
2895
+
2896
+ /**
2897
+ * @typedef {object} ClockState
2898
+ * @property {number} tickFrom - lower bound of the current tick range
2899
+ * @property {number} tickTo - upper bound of the current tick range
2900
+ * @property {number} [previous] - previous timer time used during ticking
2901
+ * @property {number | null} [oldNow] - previous value of `now`
2902
+ * @property {Timer} [timer] - timer currently being processed
2903
+ * @property {unknown} [firstException] - first exception raised while processing timers
2904
+ * @property {number} [nanosTotal] - accumulated nanoseconds from fractional ticks
2905
+ * @property {number} [msFloat] - accumulated fractional milliseconds
2906
+ * @property {number} [ms] - accumulated whole milliseconds
2907
+ */
2908
+
2909
+ /**
2910
+ * @typedef {object} TimerInitialProps
2911
+ * @property {VoidVarArgsFunc} func - callback or string to execute
2912
+ * @property {unknown[]} [args] - arguments passed to the callback
2913
+ * @property {'Timeout' | 'Interval' | 'Immediate' | 'AnimationFrame' | 'IdleCallback'} [type] - timer kind
2914
+ * @property {number} [delay] - requested delay in milliseconds
2915
+ * @property {number} [callAt] - scheduled execution time
2916
+ * @property {number} [createdAt] - time at which the timer was created
2917
+ * @property {boolean} [immediate] - whether this timer should run before non-immediate timers at the same time
2918
+ * @property {number} [id] - unique timer identifier
2919
+ * @property {Error} [error] - captured stack for loop diagnostics
2920
+ * @property {number} [interval] - interval for repeated timers
2921
+ * @property {boolean} [animation] - whether this is an animation frame timer
2922
+ * @property {boolean} [requestIdleCallback] - whether this is an idle callback timer
2923
+ * @property {number} [order] - execution order for timers at the same time
2924
+ * @property {number} [heapIndex] - index in the timer heap
2925
+ */
2926
+
2927
+ /**
2928
+ * @callback CreateClockCallback
2929
+ * @param {number|Date} [start] initial mocked time, as milliseconds since epoch or a Date
2930
+ * @param {number} [loopLimit] maximum number of timers run before aborting with an infinite-loop error
2931
+ * @returns {Clock}
2932
+ */
2933
+
2934
+ /**
2935
+ * @callback InstallCallback
2936
+ * @param {Config} [config] Optional config
2937
+ * @returns {Clock}
2604
2938
  */
2605
2939
 
2606
2940
  /**
2607
- * @typedef Performance
2608
- * @property {function(): number} now
2941
+ * @typedef {object} FakeTimers
2942
+ * @property {Timers} timers - the native timer APIs saved for later restoration
2943
+ * @property {CreateClockCallback} createClock - creates a new fake clock
2944
+ * @property {InstallCallback} install - installs the fake timers onto the default global object
2945
+ * @property {WithGlobal} withGlobal - creates a fake-timers instance for a provided global object
2609
2946
  */
2610
2947
 
2611
- /* eslint-disable jsdoc/require-property-description */
2612
2948
  /**
2613
2949
  * @typedef {object} Clock
2614
- * @property {number} now - the current time
2615
- * @property {Date} Date - the Date constructor
2616
- * @property {number} loopLimit - the maximum number of timers before assuming an infinite loop
2617
- * @property {RequestIdleCallback} requestIdleCallback
2618
- * @property {function(number):void} cancelIdleCallback
2619
- * @property {setTimeout} setTimeout
2620
- * @property {clearTimeout} clearTimeout
2621
- * @property {NextTick} nextTick
2622
- * @property {queueMicrotask} queueMicrotask
2623
- * @property {setInterval} setInterval
2624
- * @property {clearInterval} clearInterval
2625
- * @property {SetImmediate} setImmediate
2626
- * @property {function(NodeImmediate):void} clearImmediate
2627
- * @property {function():number} countTimers
2628
- * @property {RequestAnimationFrame} requestAnimationFrame
2629
- * @property {function(number):void} cancelAnimationFrame
2630
- * @property {function():void} runMicrotasks
2631
- * @property {function(string | number): number} tick
2632
- * @property {function(string | number): Promise<number>} tickAsync
2633
- * @property {function(): number} next
2634
- * @property {function(): Promise<number>} nextAsync
2635
- * @property {function(): number} runAll
2636
- * @property {function(): number} runToFrame
2637
- * @property {function(): Promise<number>} runAllAsync
2638
- * @property {function(): number} runToLast
2639
- * @property {function(): Promise<number>} runToLastAsync
2640
- * @property {function(): void} reset
2641
- * @property {function(number | Date): void} setSystemTime
2642
- * @property {function(number): void} jump
2643
- * @property {Performance} performance
2644
- * @property {function(number[]): number[]} hrtime - process.hrtime (legacy)
2645
- * @property {function(): void} uninstall Uninstall the clock.
2646
- * @property {Function[]} methods - the methods that are faked
2647
- * @property {boolean} [shouldClearNativeTimers] inherited from config
2648
- * @property {{methodName:string, original:any}[] | undefined} timersModuleMethods
2649
- * @property {{methodName:string, original:any}[] | undefined} timersPromisesModuleMethods
2650
- * @property {Map<function(): void, AbortSignal>} abortListenerMap
2651
- * @property {function(TimerTickMode): void} setTickMode
2950
+ * @property {number} now - current mocked time in milliseconds
2951
+ * @property {typeof Date & {clock?: Clock, isFake?: boolean, toSource?: () => string}} Date - fake Date constructor bound to this clock
2952
+ * @property {number} loopLimit - maximum number of timers before assuming an infinite loop
2953
+ * @property {RequestIdleCallback} requestIdleCallback - schedules an idle callback
2954
+ * @property {CancelIdleCallback} cancelIdleCallback - cancels a scheduled idle callback
2955
+ * @property {SetTimeout} setTimeout - faked `setTimeout`
2956
+ * @property {ClearTimeout} clearTimeout - faked `clearTimeout`
2957
+ * @property {NextTick} nextTick - faked `process.nextTick`
2958
+ * @property {QueueMicrotask} queueMicrotask - faked `queueMicrotask`
2959
+ * @property {SetInterval} setInterval - faked `setInterval`
2960
+ * @property {ClearInterval} clearInterval - faked `clearInterval`
2961
+ * @property {SetImmediate} setImmediate - faked `setImmediate`
2962
+ * @property {ClearImmediate} clearImmediate - faked `clearImmediate`
2963
+ * @property {CountTimers} countTimers - counts scheduled timers
2964
+ * @property {RequestAnimationFrame} requestAnimationFrame - schedules a frame callback
2965
+ * @property {CancelAnimationFrame} cancelAnimationFrame - cancels a frame callback
2966
+ * @property {RunMicrotasks} runMicrotasks - drains microtasks
2967
+ * @property {Tick} tick - advances fake time synchronously
2968
+ * @property {TickAsync} tickAsync - advances fake time asynchronously
2969
+ * @property {Next} next - runs the next scheduled timer
2970
+ * @property {NextAsync} nextAsync - runs the next scheduled timer asynchronously
2971
+ * @property {RunAll} runAll - runs all scheduled timers
2972
+ * @property {RunToFrame} runToFrame - runs timers up to the next animation frame
2973
+ * @property {RunAllAsync} runAllAsync - runs all scheduled timers asynchronously
2974
+ * @property {RunToLast} runToLast - runs timers up to the last scheduled timer
2975
+ * @property {RunToLastAsync} runToLastAsync - runs timers up to the last scheduled timer asynchronously
2976
+ * @property {Reset} reset - clears all timers and resets the clock
2977
+ * @property {SetSystemTime} setSystemTime - sets the clock to a specific wall-clock time
2978
+ * @property {Jump} jump - advances time and returns the new `now`
2979
+ * @property {any} performance - fake performance object
2980
+ * @property {Hrtime} hrtime - faked `process.hrtime`
2981
+ * @property {Uninstall} uninstall - restores native timers
2982
+ * @property {string[]} methods - names of faked methods
2983
+ * @property {boolean} [shouldClearNativeTimers] - inherited from config
2984
+ * @property {{methodName:string, original:unknown}[] | undefined} timersModuleMethods - saved Node timers module methods
2985
+ * @property {{methodName:string, original:unknown}[] | undefined} timersPromisesModuleMethods - saved Node timers/promises methods
2986
+ * @property {Map<VoidVarArgsFunc, AbortSignal>} abortListenerMap - active abort listeners
2987
+ * @property {SetTickMode} setTickMode - switches the auto-tick mode
2988
+ * @property {Map<number, Timer>} [timers] - internal timer storage
2989
+ * @property {TimerHeap} [timerHeap] - internal timer heap
2990
+ * @property {boolean} [duringTick] - internal flag
2991
+ * @property {boolean} isNearInfiniteLimit - internal flag indicating the loop limit is nearly reached
2992
+ * @property {TimerId} [attachedInterval] - internal flag
2993
+ * @property {ClockTickMode} [tickMode] - internal flag
2994
+ * @property {Timer[]} [jobs] - internal flag
2995
+ * @property {IntlWithClock} [Intl] - fake Intl object
2652
2996
  */
2653
- /* eslint-enable jsdoc/require-property-description */
2997
+ /* eslint-enable jsdoc/reject-any-type */
2654
2998
 
2655
2999
  /**
2656
3000
  * Configuration object for the `install` method.
2657
3001
  * @typedef {object} Config
2658
- * @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch)
2659
- * @property {string[]} [toFake] names of the methods that should be faked.
2660
- * @property {number} [loopLimit] the maximum number of timers that will be run when calling runAll()
2661
- * @property {boolean} [shouldAdvanceTime] tells FakeTimers to increment mocked time automatically (default false)
2662
- * @property {number} [advanceTimeDelta] increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
2663
- * @property {boolean} [shouldClearNativeTimers] forwards clear timer calls to native functions if they are not fakes (default: false)
2664
- * @property {boolean} [ignoreMissingTimers] default is false, meaning asking to fake timers that are not present will throw an error
3002
+ * @property {number|Date} [now] initial mocked time, as milliseconds since epoch or a Date
3003
+ * @property {FakeMethod[]} [toFake] method names that should be faked
3004
+ * @property {FakeMethod[]} [toNotFake] method names that should remain native
3005
+ * @property {number} [loopLimit] maximum number of timers run before aborting with an infinite-loop error
3006
+ * @property {boolean} [shouldAdvanceTime] automatically increments mocked time while the clock is installed
3007
+ * @property {number} [advanceTimeDelta] interval in milliseconds used when `shouldAdvanceTime` is enabled
3008
+ * @property {boolean} [shouldClearNativeTimers] forwards clear calls to native methods when the timer is not fake
3009
+ * @property {boolean} [ignoreMissingTimers] suppresses errors when a requested timer is missing from the global object
3010
+ * @property {GlobalObject} [target] global object to install onto
2665
3011
  */
2666
3012
 
2667
- /* eslint-disable jsdoc/require-property-description */
2668
3013
  /**
2669
3014
  * The internal structure to describe a scheduled fake timer
2670
- * @typedef {object} Timer
2671
- * @property {Function} func
2672
- * @property {*[]} args
2673
- * @property {number} delay
2674
- * @property {number} callAt
2675
- * @property {number} createdAt
2676
- * @property {boolean} immediate
2677
- * @property {number} id
2678
- * @property {Error} [error]
3015
+ * @typedef {TimerInitialProps} Timer
3016
+ * @property {unknown[]} args - arguments passed to the callback
3017
+ * @property {number} callAt - scheduled execution time
3018
+ * @property {number} createdAt - time at which the timer was created
3019
+ * @property {number} id - unique timer identifier
3020
+ * @property {'Timeout' | 'Interval' | 'Immediate' | 'AnimationFrame' | 'IdleCallback'} type - timer kind
3021
+ */
3022
+
3023
+ /**
3024
+ * @callback NodeImmediateHasRef
3025
+ * @returns {boolean}
3026
+ */
3027
+
3028
+ /**
3029
+ * @callback NodeImmediateRef
3030
+ * @returns {NodeImmediate}
3031
+ */
3032
+
3033
+ /**
3034
+ * @callback NodeImmediateUnref
3035
+ * @returns {NodeImmediate}
2679
3036
  */
2680
3037
 
2681
3038
  /**
2682
3039
  * A Node timer
2683
3040
  * @typedef {object} NodeImmediate
2684
- * @property {function(): boolean} hasRef
2685
- * @property {function(): NodeImmediate} ref
2686
- * @property {function(): NodeImmediate} unref
3041
+ * @property {NodeImmediateHasRef} hasRef - reports whether the timer keeps the event loop alive
3042
+ * @property {NodeImmediateRef} ref - marks the timer as referenced
3043
+ * @property {NodeImmediateUnref} unref - marks the timer as unreferenced
2687
3044
  */
2688
- /* eslint-enable jsdoc/require-property-description */
2689
3045
 
2690
3046
  /* eslint-disable complexity */
2691
3047
 
2692
3048
  /**
2693
3049
  * Mocks available features in the specified global namespace.
2694
- * @param {*} _global Namespace to mock (e.g. `window`)
3050
+ * @param {GlobalObject} _global Namespace to mock (e.g. `window`)
2695
3051
  * @returns {FakeTimers}
2696
3052
  */
2697
3053
  function withGlobal(_global) {
@@ -2731,7 +3087,10 @@ function requireFakeTimersSrc () {
2731
3087
  _global.performance &&
2732
3088
  _global.performance.constructor &&
2733
3089
  _global.performance.constructor.prototype;
2734
- isPresent.queueMicrotask = _global.hasOwnProperty("queueMicrotask");
3090
+ isPresent.queueMicrotask = Object.prototype.hasOwnProperty.call(
3091
+ _global,
3092
+ "queueMicrotask",
3093
+ );
2735
3094
  isPresent.requestAnimationFrame =
2736
3095
  _global.requestAnimationFrame &&
2737
3096
  typeof _global.requestAnimationFrame === "function";
@@ -2741,7 +3100,7 @@ function requireFakeTimersSrc () {
2741
3100
  isPresent.requestIdleCallback =
2742
3101
  _global.requestIdleCallback &&
2743
3102
  typeof _global.requestIdleCallback === "function";
2744
- isPresent.cancelIdleCallbackPresent =
3103
+ isPresent.cancelIdleCallback =
2745
3104
  _global.cancelIdleCallback &&
2746
3105
  typeof _global.cancelIdleCallback === "function";
2747
3106
  isPresent.setImmediate =
@@ -2762,6 +3121,8 @@ function requireFakeTimersSrc () {
2762
3121
  )
2763
3122
  : undefined;
2764
3123
  let uniqueTimerId = idCounterStart;
3124
+ /** @type {number} */
3125
+ let uniqueTimerOrder = 0;
2765
3126
 
2766
3127
  if (NativeDate === undefined) {
2767
3128
  throw new Error(
@@ -2802,23 +3163,23 @@ function requireFakeTimersSrc () {
2802
3163
  return isFinite(num);
2803
3164
  }
2804
3165
 
2805
- let isNearInfiniteLimit = false;
2806
-
2807
3166
  /**
2808
3167
  * @param {Clock} clock
2809
3168
  * @param {number} i
2810
3169
  */
2811
3170
  function checkIsNearInfiniteLimit(clock, i) {
2812
3171
  if (clock.loopLimit && i === clock.loopLimit - 1) {
2813
- isNearInfiniteLimit = true;
3172
+ clock.isNearInfiniteLimit = true;
2814
3173
  }
2815
3174
  }
2816
3175
 
2817
3176
  /**
2818
- *
3177
+ * @param {Clock} clock
2819
3178
  */
2820
- function resetIsNearInfiniteLimit() {
2821
- isNearInfiniteLimit = false;
3179
+ function resetIsNearInfiniteLimit(clock) {
3180
+ if (clock) {
3181
+ clock.isNearInfiniteLimit = false;
3182
+ }
2822
3183
  }
2823
3184
 
2824
3185
  /**
@@ -2883,12 +3244,12 @@ function requireFakeTimersSrc () {
2883
3244
  if (!epoch) {
2884
3245
  return 0;
2885
3246
  }
2886
- if (typeof epoch.getTime === "function") {
2887
- return epoch.getTime();
2888
- }
2889
3247
  if (typeof epoch === "number") {
2890
3248
  return epoch;
2891
3249
  }
3250
+ if (typeof epoch.getTime === "function") {
3251
+ return epoch.getTime();
3252
+ }
2892
3253
  throw new TypeError("now should be milliseconds since UNIX epoch");
2893
3254
  }
2894
3255
 
@@ -2905,6 +3266,7 @@ function requireFakeTimersSrc () {
2905
3266
  /**
2906
3267
  * @param {Clock} clock
2907
3268
  * @param {Timer} job
3269
+ * @returns {Error}
2908
3270
  */
2909
3271
  function getInfiniteLoopError(clock, job) {
2910
3272
  const infiniteLoopError = new Error(
@@ -2964,34 +3326,29 @@ function requireFakeTimersSrc () {
2964
3326
  Object.defineProperty(infiniteLoopError, "stack", {
2965
3327
  value: stack,
2966
3328
  });
2967
- } catch (e) {
3329
+ } catch {
2968
3330
  // noop
2969
3331
  }
2970
3332
 
2971
3333
  return infiniteLoopError;
2972
3334
  }
2973
3335
 
2974
- //eslint-disable-next-line jsdoc/require-jsdoc
3336
+ /**
3337
+ * @returns {typeof Date & { clock: Clock }}
3338
+ */
2975
3339
  function createDate() {
2976
3340
  class ClockDate extends NativeDate {
2977
- /**
2978
- * @param {number} year
2979
- * @param {number} month
2980
- * @param {number} date
2981
- * @param {number} hour
2982
- * @param {number} minute
2983
- * @param {number} second
2984
- * @param {number} ms
2985
- * @returns void
2986
- */
2987
- // eslint-disable-next-line no-unused-vars
2988
- constructor(year, month, date, hour, minute, second, ms) {
2989
- // Defensive and verbose to avoid potential harm in passing
2990
- // explicit undefined when user does not pass argument
2991
- if (arguments.length === 0) {
3341
+ /** @type {Clock} */
3342
+ static clock;
3343
+
3344
+ constructor(...args) {
3345
+ // Preserve fake time when Date is called without arguments.
3346
+ if (args.length === 0) {
2992
3347
  super(ClockDate.clock.now);
2993
3348
  } else {
2994
- super(...arguments);
3349
+ // The subclass is intentionally thin for explicit args.
3350
+ // @ts-expect-error Date constructor overloads are intentionally dynamic.
3351
+ super(...args);
2995
3352
  }
2996
3353
 
2997
3354
  // ensures identity checks using the constructor prop still works
@@ -3015,21 +3372,26 @@ function requireFakeTimersSrc () {
3015
3372
  };
3016
3373
  }
3017
3374
 
3018
- if (NativeDate.toSource) {
3375
+ const NativeDateWithToSource =
3376
+ /** @type {typeof Date & { toSource?: () => string }} */ (
3377
+ NativeDate
3378
+ );
3379
+
3380
+ if (NativeDateWithToSource.toSource) {
3019
3381
  ClockDate.toSource = function toSource() {
3020
- return NativeDate.toSource();
3382
+ return NativeDateWithToSource.toSource();
3021
3383
  };
3022
3384
  }
3023
3385
 
3024
3386
  ClockDate.toString = function toString() {
3025
- return NativeDate.toString();
3387
+ return NativeDateWithToSource.toString();
3026
3388
  };
3027
3389
 
3028
3390
  // noinspection UnnecessaryLocalVariableJS
3029
3391
  /**
3030
3392
  * A normal Class constructor cannot be called without `new`, but Date can, so we need
3031
3393
  * to wrap it in a Proxy in order to ensure this functionality of Date is kept intact
3032
- * @type {ClockDate}
3394
+ * @type {typeof ClockDate}
3033
3395
  */
3034
3396
  const ClockDateProxy = new Proxy(ClockDate, {
3035
3397
  // handler for [[Call]] invocations (i.e. not using `new`)
@@ -3046,7 +3408,9 @@ function requireFakeTimersSrc () {
3046
3408
  },
3047
3409
  });
3048
3410
 
3049
- return ClockDateProxy;
3411
+ return /** @type {typeof Date & { clock: Clock }} */ (
3412
+ /** @type {unknown} */ (ClockDateProxy)
3413
+ );
3050
3414
  }
3051
3415
 
3052
3416
  /**
@@ -3055,19 +3419,21 @@ function requireFakeTimersSrc () {
3055
3419
  * Most of the properties are the original native ones,
3056
3420
  * but we need to take control of those that have a
3057
3421
  * dependency on the current clock.
3058
- * @returns {object} the partly fake Intl implementation
3422
+ * @param {Clock} clock
3423
+ * @returns {IntlWithClock} the partly fake Intl implementation
3059
3424
  */
3060
- function createIntl() {
3061
- const ClockIntl = {};
3425
+ function createIntl(clock) {
3426
+ /** @type {IntlWithClock} */
3427
+ const IntlWithClock = { clock: clock };
3062
3428
  /*
3063
3429
  * All properties of Intl are non-enumerable, so we need
3064
3430
  * to do a bit of work to get them out.
3065
3431
  */
3066
3432
  Object.getOwnPropertyNames(NativeIntl).forEach(
3067
- (property) => (ClockIntl[property] = NativeIntl[property]),
3433
+ (property) => (IntlWithClock[property] = NativeIntl[property]),
3068
3434
  );
3069
3435
 
3070
- ClockIntl.DateTimeFormat = function (...args) {
3436
+ IntlWithClock.DateTimeFormat = function (...args) {
3071
3437
  const realFormatter = new NativeIntl.DateTimeFormat(...args);
3072
3438
  const formatter = {};
3073
3439
 
@@ -3080,21 +3446,23 @@ function requireFakeTimersSrc () {
3080
3446
 
3081
3447
  ["format", "formatToParts"].forEach((method) => {
3082
3448
  formatter[method] = function (date) {
3083
- return realFormatter[method](date || ClockIntl.clock.now);
3449
+ return realFormatter[method](
3450
+ date || IntlWithClock.clock.now,
3451
+ );
3084
3452
  };
3085
3453
  });
3086
3454
 
3087
3455
  return formatter;
3088
3456
  };
3089
3457
 
3090
- ClockIntl.DateTimeFormat.prototype = Object.create(
3458
+ IntlWithClock.DateTimeFormat.prototype = Object.create(
3091
3459
  NativeIntl.DateTimeFormat.prototype,
3092
3460
  );
3093
3461
 
3094
- ClockIntl.DateTimeFormat.supportedLocalesOf =
3462
+ IntlWithClock.DateTimeFormat.supportedLocalesOf =
3095
3463
  NativeIntl.DateTimeFormat.supportedLocalesOf;
3096
3464
 
3097
- return ClockIntl;
3465
+ return IntlWithClock;
3098
3466
  }
3099
3467
 
3100
3468
  //eslint-disable-next-line jsdoc/require-jsdoc
@@ -3112,6 +3480,7 @@ function requireFakeTimersSrc () {
3112
3480
  if (!clock.jobs) {
3113
3481
  return;
3114
3482
  }
3483
+ const wasNearLimit = clock.isNearInfiniteLimit;
3115
3484
  for (let i = 0; i < clock.jobs.length; i++) {
3116
3485
  const job = clock.jobs[i];
3117
3486
  job.func.apply(null, job.args);
@@ -3121,38 +3490,275 @@ function requireFakeTimersSrc () {
3121
3490
  throw getInfiniteLoopError(clock, job);
3122
3491
  }
3123
3492
  }
3124
- resetIsNearInfiniteLimit();
3493
+ if (!wasNearLimit) {
3494
+ resetIsNearInfiniteLimit(clock);
3495
+ }
3125
3496
  clock.jobs = [];
3126
3497
  }
3127
3498
 
3499
+ /**
3500
+ * A compact "soonest timer first" container.
3501
+ *
3502
+ * Think of this as a waiting room for scheduled callbacks where the next
3503
+ * callback to run is always kept at the front of the list. The internal
3504
+ * array is arranged so we can find, add, remove, and reorder timers
3505
+ * efficiently without sorting the whole list every time something changes.
3506
+ *
3507
+ * The important idea is not the data structure name, but the behavior:
3508
+ * the timer that should run next stays near the front, and when one timer
3509
+ * moves, the rest are shifted just enough to keep that promise true.
3510
+ */
3511
+ class TimerHeap {
3512
+ constructor() {
3513
+ this.timers = [];
3514
+ }
3515
+
3516
+ /**
3517
+ * Look at the next timer without removing it.
3518
+ * This is the timer the clock would run first if time advanced now.
3519
+ * @returns {Timer}
3520
+ */
3521
+ peek() {
3522
+ return this.timers[0];
3523
+ }
3524
+
3525
+ /**
3526
+ * Add a timer to the waiting room, then move it upward until it is in
3527
+ * the right place relative to the timers it should run before and after.
3528
+ * @param {Timer} timer
3529
+ */
3530
+ push(timer) {
3531
+ this.timers.push(timer);
3532
+ this.bubbleUp(this.timers.length - 1);
3533
+ }
3534
+
3535
+ /**
3536
+ * Remove and return the next timer to run.
3537
+ *
3538
+ * We pull the front timer out, move the last timer into the empty spot,
3539
+ * and then shift that replacement down until the ordering is correct
3540
+ * again. That avoids rebuilding the whole list from scratch.
3541
+ * @returns {Timer|undefined}
3542
+ */
3543
+ pop() {
3544
+ if (this.timers.length === 0) {
3545
+ return undefined;
3546
+ }
3547
+ const first = this.timers[0];
3548
+ const last = this.timers.pop();
3549
+ if (this.timers.length > 0) {
3550
+ this.timers[0] = last;
3551
+ last.heapIndex = 0;
3552
+ this.bubbleDown(0);
3553
+ }
3554
+ delete first.heapIndex;
3555
+ return first;
3556
+ }
3557
+
3558
+ /**
3559
+ * Remove a specific timer from the waiting room.
3560
+ *
3561
+ * The heap stores timers in a shape that lets us jump directly to the
3562
+ * timer's current position, replace it with the last timer, and then
3563
+ * move that replacement up or down until the ordering is correct again.
3564
+ * @param {Timer} timer
3565
+ * @returns {boolean}
3566
+ */
3567
+ remove(timer) {
3568
+ const index = timer.heapIndex;
3569
+ if (index === undefined || this.timers[index] !== timer) {
3570
+ return false;
3571
+ }
3572
+ const last = this.timers.pop();
3573
+ if (timer !== last) {
3574
+ this.timers[index] = last;
3575
+ last.heapIndex = index;
3576
+ if (compareTimers(last, timer) < 0) {
3577
+ this.bubbleUp(index);
3578
+ } else {
3579
+ this.bubbleDown(index);
3580
+ }
3581
+ }
3582
+ delete timer.heapIndex;
3583
+ return true;
3584
+ }
3585
+
3586
+ /**
3587
+ * Move a timer toward the front until it is no longer "earlier" than
3588
+ * the timer above it.
3589
+ *
3590
+ * Conceptually, this is what happens when something newly scheduled
3591
+ * turns out to belong ahead of its parent in the waiting room. We keep
3592
+ * swapping it upward until it is no longer out of place.
3593
+ * @param {number} index
3594
+ */
3595
+ bubbleUp(index) {
3596
+ const timer = this.timers[index];
3597
+ let currentIndex = index;
3598
+ while (currentIndex > 0) {
3599
+ const parentIndex = Math.floor((currentIndex - 1) / 2);
3600
+ const parent = this.timers[parentIndex];
3601
+ if (compareTimers(timer, parent) < 0) {
3602
+ this.timers[currentIndex] = parent;
3603
+ parent.heapIndex = currentIndex;
3604
+ currentIndex = parentIndex;
3605
+ } else {
3606
+ break;
3607
+ }
3608
+ }
3609
+ this.timers[currentIndex] = timer;
3610
+ timer.heapIndex = currentIndex;
3611
+ }
3612
+
3613
+ /**
3614
+ * Move a timer away from the front until the timer below it is no
3615
+ * longer supposed to run after it.
3616
+ *
3617
+ * This is the opposite of `bubbleUp`: when a timer at the front is
3618
+ * removed or moved, the replacement may be too far ahead, so we
3619
+ * repeatedly swap it downward with the best child until the waiting
3620
+ * room is ordered again.
3621
+ * @param {number} index
3622
+ */
3623
+ bubbleDown(index) {
3624
+ const timer = this.timers[index];
3625
+ let currentIndex = index;
3626
+ const halfLength = Math.floor(this.timers.length / 2);
3627
+ while (currentIndex < halfLength) {
3628
+ const leftIndex = currentIndex * 2 + 1;
3629
+ const rightIndex = leftIndex + 1;
3630
+ let bestChildIndex = leftIndex;
3631
+ let bestChild = this.timers[leftIndex];
3632
+
3633
+ if (
3634
+ rightIndex < this.timers.length &&
3635
+ compareTimers(this.timers[rightIndex], bestChild) < 0
3636
+ ) {
3637
+ bestChildIndex = rightIndex;
3638
+ bestChild = this.timers[rightIndex];
3639
+ }
3640
+
3641
+ if (compareTimers(bestChild, timer) < 0) {
3642
+ this.timers[currentIndex] = bestChild;
3643
+ bestChild.heapIndex = currentIndex;
3644
+ currentIndex = bestChildIndex;
3645
+ } else {
3646
+ break;
3647
+ }
3648
+ }
3649
+ this.timers[currentIndex] = timer;
3650
+ timer.heapIndex = currentIndex;
3651
+ }
3652
+ }
3653
+
3654
+ /**
3655
+ * Ensure timer storage and heap stay in sync even if a clear path touches
3656
+ * timer state before anything has been scheduled.
3657
+ *
3658
+ * Why do we need two data structures to keep tabs on timers?
3659
+ * 1. Fast ID Lookup (clock.timers): This is a Map from timer IDs to their respective timer objects. It allows clearTimeout(id) and
3660
+ * clearInterval(id) to be $O(1)$ operations. Without this map, finding a specific timer in the heap to remove it would require a linear
3661
+ * $O(n)$ search, which would significantly degrade performance as the number of active timers grows.
3662
+ * 2. Efficient Scheduling (clock.timerHeap): This is a priority queue (min-heap) that keeps timers ordered by their execution time (callAt). It
3663
+ * allows the library to instantly find the next timer to run (peek() in $O(1)$) and efficiently update the schedule when timers are added or
3664
+ * removed ($O(\log n)$).
3665
+ *
3666
+ * In short: clock.timers provides fast access by ID, while clock.timerHeap provides fast access by Time. Removing either one would make common
3667
+ * operations (like clearing or finding the next timer) much slower.
3668
+ * @param {Clock} clock
3669
+ */
3670
+ function ensureTimerState(clock) {
3671
+ if (!clock.timers) {
3672
+ clock.timers = new Map();
3673
+ clock.timerHeap = new TimerHeap();
3674
+ }
3675
+ }
3676
+
3677
+ /**
3678
+ * @param {Clock} clock
3679
+ * @param {number} id
3680
+ * @returns {boolean}
3681
+ */
3682
+ function hasTimer(clock, id) {
3683
+ return clock.timers ? clock.timers.has(id) : false;
3684
+ }
3685
+
3686
+ /**
3687
+ * @param {Clock} clock
3688
+ * @param {number} id
3689
+ * @returns {Timer}
3690
+ */
3691
+ function getTimer(clock, id) {
3692
+ return clock.timers ? clock.timers.get(id) : undefined;
3693
+ }
3694
+
3128
3695
  /**
3129
3696
  * @param {Clock} clock
3130
3697
  * @param {Timer} timer
3131
- * @returns {number} id of the created timer
3698
+ */
3699
+ function setTimer(clock, timer) {
3700
+ ensureTimerState(clock);
3701
+ clock.timers.set(timer.id, timer);
3702
+ }
3703
+
3704
+ /**
3705
+ * @param {Clock} clock
3706
+ * @param {number} id
3707
+ * @returns {boolean}
3708
+ */
3709
+ function deleteTimer(clock, id) {
3710
+ return clock.timers ? clock.timers.delete(id) : false;
3711
+ }
3712
+
3713
+ /**
3714
+ * @param {Clock} clock
3715
+ * @param {(timer: Timer) => void} callback
3716
+ */
3717
+ function forEachActiveTimer(clock, callback) {
3718
+ if (!clock.timers) {
3719
+ return;
3720
+ }
3721
+
3722
+ for (const timer of clock.timers.values()) {
3723
+ callback(timer);
3724
+ }
3725
+ }
3726
+
3727
+ /**
3728
+ * @param {Clock} clock
3729
+ */
3730
+ function rebuildTimerHeap(clock) {
3731
+ clock.timerHeap = new TimerHeap();
3732
+ forEachActiveTimer(clock, (timer) => {
3733
+ clock.timerHeap.push(timer);
3734
+ });
3735
+ }
3736
+
3737
+ /**
3738
+ * @param {Clock} clock
3739
+ * @param {TimerInitialProps} timer
3740
+ * @returns {TimerId} id of the created timer
3132
3741
  */
3133
3742
  function addTimer(clock, timer) {
3134
3743
  if (timer.func === undefined) {
3135
3744
  throw new Error("Callback must be provided to timer calls");
3136
3745
  }
3137
3746
 
3138
- if (addTimerReturnsObject) {
3139
- // Node.js environment
3140
- if (typeof timer.func !== "function") {
3141
- throw new TypeError(
3142
- `[ERR_INVALID_CALLBACK]: Callback must be a function. Received ${
3143
- timer.func
3144
- } of type ${typeof timer.func}`,
3145
- );
3146
- }
3747
+ if (typeof timer.func !== "function") {
3748
+ throw new TypeError(
3749
+ `[ERR_INVALID_CALLBACK]: Callback must be a function. Received ${
3750
+ timer.func
3751
+ } of type ${typeof timer.func}`,
3752
+ );
3147
3753
  }
3148
3754
 
3149
- if (isNearInfiniteLimit) {
3755
+ if (clock.isNearInfiniteLimit) {
3150
3756
  timer.error = new Error();
3151
3757
  }
3152
3758
 
3153
3759
  timer.type = timer.immediate ? "Immediate" : "Timeout";
3154
3760
 
3155
- if (timer.hasOwnProperty("delay")) {
3761
+ if (Object.prototype.hasOwnProperty.call(timer, "delay")) {
3156
3762
  if (typeof timer.delay !== "number") {
3157
3763
  timer.delay = parseInt(timer.delay, 10);
3158
3764
  }
@@ -3164,42 +3770,60 @@ function requireFakeTimersSrc () {
3164
3770
  timer.delay = Math.max(0, timer.delay);
3165
3771
  }
3166
3772
 
3167
- if (timer.hasOwnProperty("interval")) {
3773
+ if (Object.prototype.hasOwnProperty.call(timer, "interval")) {
3168
3774
  timer.type = "Interval";
3169
3775
  timer.interval = timer.interval > maxTimeout ? 1 : timer.interval;
3170
3776
  }
3171
3777
 
3172
- if (timer.hasOwnProperty("animation")) {
3778
+ if (Object.prototype.hasOwnProperty.call(timer, "animation")) {
3173
3779
  timer.type = "AnimationFrame";
3174
3780
  timer.animation = true;
3175
3781
  }
3176
3782
 
3177
- if (timer.hasOwnProperty("idleCallback")) {
3178
- timer.type = "IdleCallback";
3179
- timer.idleCallback = true;
3783
+ if (
3784
+ Object.prototype.hasOwnProperty.call(timer, "requestIdleCallback")
3785
+ ) {
3786
+ // mark timer as IdleCallback type if it has no delay, otherwise it'd be of type timeout
3787
+ // this way we are able to sort such that the timer only gets called when there's truly no pending task to run
3788
+ if (!timer.delay) {
3789
+ timer.type = "IdleCallback";
3790
+ }
3791
+ timer.requestIdleCallback = true;
3180
3792
  }
3181
3793
 
3182
- if (!clock.timers) {
3183
- clock.timers = {};
3794
+ ensureTimerState(clock);
3795
+
3796
+ while (hasTimer(clock, uniqueTimerId)) {
3797
+ uniqueTimerId++;
3798
+ if (uniqueTimerId >= Number.MAX_SAFE_INTEGER) {
3799
+ uniqueTimerId = idCounterStart;
3800
+ }
3184
3801
  }
3185
3802
 
3186
3803
  timer.id = uniqueTimerId++;
3804
+ if (uniqueTimerId >= Number.MAX_SAFE_INTEGER) {
3805
+ uniqueTimerId = idCounterStart;
3806
+ }
3807
+
3808
+ timer.order = uniqueTimerOrder++;
3187
3809
  timer.createdAt = clock.now;
3188
3810
  timer.callAt =
3189
- clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
3811
+ clock.now +
3812
+ (parseInt(String(timer.delay)) || (clock.duringTick ? 1 : 0));
3190
3813
 
3191
- clock.timers[timer.id] = timer;
3814
+ setTimer(clock, timer);
3815
+ clock.timerHeap.push(timer);
3192
3816
 
3193
3817
  if (addTimerReturnsObject) {
3194
3818
  const res = {
3195
3819
  refed: true,
3196
3820
  ref: function () {
3197
3821
  this.refed = true;
3198
- return res;
3822
+ return this;
3199
3823
  },
3200
3824
  unref: function () {
3201
3825
  this.refed = false;
3202
- return res;
3826
+ return this;
3203
3827
  },
3204
3828
  hasRef: function () {
3205
3829
  return this.refed;
@@ -3207,12 +3831,15 @@ function requireFakeTimersSrc () {
3207
3831
  refresh: function () {
3208
3832
  timer.callAt =
3209
3833
  clock.now +
3210
- (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
3834
+ (parseInt(String(timer.delay)) ||
3835
+ (clock.duringTick ? 1 : 0));
3211
3836
 
3212
- // it _might_ have been removed, but if not the assignment is perfectly fine
3213
- clock.timers[timer.id] = timer;
3837
+ clock.timerHeap.remove(timer);
3838
+ timer.order = uniqueTimerOrder++;
3839
+ setTimer(clock, timer);
3840
+ clock.timerHeap.push(timer);
3214
3841
 
3215
- return res;
3842
+ return this;
3216
3843
  },
3217
3844
  [Symbol.toPrimitive]: function () {
3218
3845
  return timer.id;
@@ -3226,12 +3853,20 @@ function requireFakeTimersSrc () {
3226
3853
 
3227
3854
  /* eslint consistent-return: "off" */
3228
3855
  /**
3229
- * Timer comparitor
3856
+ * Timer comparator
3230
3857
  * @param {Timer} a
3231
3858
  * @param {Timer} b
3232
3859
  * @returns {number}
3233
3860
  */
3234
3861
  function compareTimers(a, b) {
3862
+ // Sort IdleCallback timers to the bottom when scheduled for the same time
3863
+ if (a.type === "IdleCallback" && b.type !== "IdleCallback") {
3864
+ return 1;
3865
+ }
3866
+ if (a.type !== "IdleCallback" && b.type === "IdleCallback") {
3867
+ return -1;
3868
+ }
3869
+
3235
3870
  // Sort first by absolute timing
3236
3871
  if (a.callAt < b.callAt) {
3237
3872
  return -1;
@@ -3248,6 +3883,13 @@ function requireFakeTimersSrc () {
3248
3883
  return 1;
3249
3884
  }
3250
3885
 
3886
+ if (a.order < b.order) {
3887
+ return -1;
3888
+ }
3889
+ if (a.order > b.order) {
3890
+ return 1;
3891
+ }
3892
+
3251
3893
  // Sort next by creation time, earlier-created timers take precedence
3252
3894
  if (a.createdAt < b.createdAt) {
3253
3895
  return -1;
@@ -3265,6 +3907,7 @@ function requireFakeTimersSrc () {
3265
3907
  }
3266
3908
 
3267
3909
  // As timer ids are unique, no fallback `0` is necessary
3910
+ return 0;
3268
3911
  }
3269
3912
 
3270
3913
  /**
@@ -3274,20 +3917,31 @@ function requireFakeTimersSrc () {
3274
3917
  * @returns {Timer}
3275
3918
  */
3276
3919
  function firstTimerInRange(clock, from, to) {
3277
- const timers = clock.timers;
3278
- let timer = null;
3279
- let id, isInRange;
3920
+ if (!clock.timerHeap) {
3921
+ return null;
3922
+ }
3280
3923
 
3281
- for (id in timers) {
3282
- if (timers.hasOwnProperty(id)) {
3283
- isInRange = inRange(from, to, timers[id]);
3924
+ const timers = clock.timerHeap.timers;
3925
+ if (timers.length === 1 && timers[0].requestIdleCallback) {
3926
+ return timers[0];
3927
+ }
3284
3928
 
3285
- if (
3286
- isInRange &&
3287
- (!timer || compareTimers(timer, timers[id]) === 1)
3288
- ) {
3289
- timer = timers[id];
3290
- }
3929
+ const first = clock.timerHeap.peek();
3930
+ if (first && inRange(from, to, first)) {
3931
+ return first;
3932
+ }
3933
+
3934
+ /**
3935
+ * @type {?Timer}
3936
+ */
3937
+ let timer = null;
3938
+
3939
+ for (let i = 0; i < timers.length; i++) {
3940
+ if (
3941
+ inRange(from, to, timers[i]) &&
3942
+ (!timer || compareTimers(timer, timers[i]) === 1)
3943
+ ) {
3944
+ timer = timers[i];
3291
3945
  }
3292
3946
  }
3293
3947
 
@@ -3299,19 +3953,10 @@ function requireFakeTimersSrc () {
3299
3953
  * @returns {Timer}
3300
3954
  */
3301
3955
  function firstTimer(clock) {
3302
- const timers = clock.timers;
3303
- let timer = null;
3304
- let id;
3305
-
3306
- for (id in timers) {
3307
- if (timers.hasOwnProperty(id)) {
3308
- if (!timer || compareTimers(timer, timers[id]) === 1) {
3309
- timer = timers[id];
3310
- }
3311
- }
3956
+ if (!clock.timerHeap) {
3957
+ return null;
3312
3958
  }
3313
-
3314
- return timer;
3959
+ return clock.timerHeap.peek() || null;
3315
3960
  }
3316
3961
 
3317
3962
  /**
@@ -3319,15 +3964,15 @@ function requireFakeTimersSrc () {
3319
3964
  * @returns {Timer}
3320
3965
  */
3321
3966
  function lastTimer(clock) {
3322
- const timers = clock.timers;
3967
+ if (!clock.timerHeap) {
3968
+ return null;
3969
+ }
3970
+ const timers = clock.timerHeap.timers;
3323
3971
  let timer = null;
3324
- let id;
3325
3972
 
3326
- for (id in timers) {
3327
- if (timers.hasOwnProperty(id)) {
3328
- if (!timer || compareTimers(timer, timers[id]) === -1) {
3329
- timer = timers[id];
3330
- }
3973
+ for (let i = 0; i < timers.length; i++) {
3974
+ if (!timer || compareTimers(timer, timers[i]) === -1) {
3975
+ timer = timers[i];
3331
3976
  }
3332
3977
  }
3333
3978
 
@@ -3340,25 +3985,27 @@ function requireFakeTimersSrc () {
3340
3985
  */
3341
3986
  function callTimer(clock, timer) {
3342
3987
  if (typeof timer.interval === "number") {
3343
- clock.timers[timer.id].callAt += timer.interval;
3988
+ clock.timerHeap.remove(timer);
3989
+ timer.callAt += timer.interval;
3990
+ timer.order = uniqueTimerOrder++;
3991
+ if (clock.isNearInfiniteLimit) {
3992
+ timer.error = new Error();
3993
+ }
3994
+ clock.timerHeap.push(timer);
3344
3995
  } else {
3345
- delete clock.timers[timer.id];
3996
+ deleteTimer(clock, timer.id);
3997
+ clock.timerHeap.remove(timer);
3346
3998
  }
3347
3999
 
3348
4000
  if (typeof timer.func === "function") {
3349
4001
  timer.func.apply(null, timer.args);
3350
- } else {
3351
- /* eslint no-eval: "off" */
3352
- const eval2 = eval;
3353
- (function () {
3354
- eval2(timer.func);
3355
- })();
3356
4002
  }
3357
4003
  }
3358
4004
 
3359
4005
  /**
3360
4006
  * Gets clear handler name for a given timer type
3361
4007
  * @param {string} ttype
4008
+ * @returns {string}
3362
4009
  */
3363
4010
  function getClearHandler(ttype) {
3364
4011
  if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
@@ -3370,6 +4017,7 @@ function requireFakeTimersSrc () {
3370
4017
  /**
3371
4018
  * Gets schedule handler name for a given timer type
3372
4019
  * @param {string} ttype
4020
+ * @returns {string}
3373
4021
  */
3374
4022
  function getScheduleHandler(ttype) {
3375
4023
  if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
@@ -3380,6 +4028,7 @@ function requireFakeTimersSrc () {
3380
4028
 
3381
4029
  /**
3382
4030
  * Creates an anonymous function to warn only once
4031
+ * @returns {(msg: string) => void}
3383
4032
  */
3384
4033
  function createWarnOnce() {
3385
4034
  let calls = 0;
@@ -3392,8 +4041,9 @@ function requireFakeTimersSrc () {
3392
4041
 
3393
4042
  /**
3394
4043
  * @param {Clock} clock
3395
- * @param {number} timerId
4044
+ * @param {TimerId} timerId
3396
4045
  * @param {string} ttype
4046
+ * @returns {void}
3397
4047
  */
3398
4048
  function clearTimer(clock, timerId, ttype) {
3399
4049
  if (!timerId) {
@@ -3402,10 +4052,6 @@ function requireFakeTimersSrc () {
3402
4052
  return;
3403
4053
  }
3404
4054
 
3405
- if (!clock.timers) {
3406
- clock.timers = {};
3407
- }
3408
-
3409
4055
  // in Node, the ID is stored as the primitive value for `Timeout` objects
3410
4056
  // for `Immediate` objects, no ID exists, so it gets coerced to NaN
3411
4057
  const id = Number(timerId);
@@ -3419,21 +4065,30 @@ function requireFakeTimersSrc () {
3419
4065
  ? nativeHandler(timerId)
3420
4066
  : undefined;
3421
4067
  }
4068
+
4069
+ // Include the stacktrace, excluding the 'error' line
4070
+ const stackTrace = new Error().stack
4071
+ .split("\n")
4072
+ .slice(1)
4073
+ .join("\n");
4074
+
3422
4075
  warnOnce(
3423
4076
  `FakeTimers: ${handlerName} was invoked to clear a native timer instead of one created by this library.` +
3424
- "\nTo automatically clean-up native timers, use `shouldClearNativeTimers`.",
4077
+ "\nTo automatically clean-up native timers, use `shouldClearNativeTimers`." +
4078
+ `\n${stackTrace}`,
3425
4079
  );
3426
4080
  }
3427
4081
 
3428
- if (clock.timers.hasOwnProperty(id)) {
4082
+ if (hasTimer(clock, id)) {
3429
4083
  // check that the ID matches a timer of the correct type
3430
- const timer = clock.timers[id];
4084
+ const timer = getTimer(clock, id);
3431
4085
  if (
3432
4086
  timer.type === ttype ||
3433
4087
  (timer.type === "Timeout" && ttype === "Interval") ||
3434
4088
  (timer.type === "Interval" && ttype === "Timeout")
3435
4089
  ) {
3436
- delete clock.timers[id];
4090
+ deleteTimer(clock, id);
4091
+ clock.timerHeap.remove(timer);
3437
4092
  } else {
3438
4093
  const clear = getClearHandler(ttype);
3439
4094
  const schedule = getScheduleHandler(timer.type);
@@ -3478,12 +4133,12 @@ function requireFakeTimersSrc () {
3478
4133
  _global[method] = clock[`_${method}`];
3479
4134
  }
3480
4135
  } else {
3481
- if (_global[method] && _global[method].hadOwnProperty) {
4136
+ if (clock[method] && clock[method].hasOwnProperty) {
3482
4137
  _global[method] = clock[`_${method}`];
3483
4138
  } else {
3484
4139
  try {
3485
4140
  delete _global[method];
3486
- } catch (ignore) {
4141
+ } catch {
3487
4142
  /* eslint no-empty: "off" */
3488
4143
  }
3489
4144
  }
@@ -3506,7 +4161,7 @@ function requireFakeTimersSrc () {
3506
4161
  }
3507
4162
  }
3508
4163
 
3509
- clock.setTickMode("manual");
4164
+ clock.setTickMode({ mode: "manual" });
3510
4165
 
3511
4166
  // Prevent multiple executions which will completely remove these props
3512
4167
  clock.methods = [];
@@ -3517,12 +4172,10 @@ function requireFakeTimersSrc () {
3517
4172
  }
3518
4173
 
3519
4174
  // return pending timers, to enable checking what timers remained on uninstall
3520
- if (!clock.timers) {
4175
+ if (!clock.timerHeap) {
3521
4176
  return [];
3522
4177
  }
3523
- return Object.keys(clock.timers).map(function mapper(key) {
3524
- return clock.timers[key];
3525
- });
4178
+ return clock.timerHeap.timers.slice();
3526
4179
  }
3527
4180
 
3528
4181
  /**
@@ -3531,7 +4184,7 @@ function requireFakeTimersSrc () {
3531
4184
  * @param {Clock} clock
3532
4185
  */
3533
4186
  function hijackMethod(target, method, clock) {
3534
- clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(
4187
+ clock[method].hasOwnProperty = Object.prototype.hasOwnProperty.call(
3535
4188
  target,
3536
4189
  method,
3537
4190
  );
@@ -3588,26 +4241,6 @@ function requireFakeTimersSrc () {
3588
4241
  clock.tick(advanceTimeDelta);
3589
4242
  }
3590
4243
 
3591
- /**
3592
- * @typedef {object} Timers
3593
- * @property {setTimeout} setTimeout
3594
- * @property {clearTimeout} clearTimeout
3595
- * @property {setInterval} setInterval
3596
- * @property {clearInterval} clearInterval
3597
- * @property {Date} Date
3598
- * @property {Intl} Intl
3599
- * @property {SetImmediate=} setImmediate
3600
- * @property {function(NodeImmediate): void=} clearImmediate
3601
- * @property {function(number[]):number[]=} hrtime
3602
- * @property {NextTick=} nextTick
3603
- * @property {Performance=} performance
3604
- * @property {RequestAnimationFrame=} requestAnimationFrame
3605
- * @property {boolean=} queueMicrotask
3606
- * @property {function(number): void=} cancelAnimationFrame
3607
- * @property {RequestIdleCallback=} requestIdleCallback
3608
- * @property {function(number): void=} cancelIdleCallback
3609
- */
3610
-
3611
4244
  /** @type {Timers} */
3612
4245
  const timers = {
3613
4246
  setTimeout: _global.setTimeout,
@@ -3671,30 +4304,37 @@ function requireFakeTimersSrc () {
3671
4304
  * @returns {Clock}
3672
4305
  */
3673
4306
  function createClock(start, loopLimit) {
4307
+ /** @type {number} */
3674
4308
  // eslint-disable-next-line no-param-reassign
3675
4309
  start = Math.floor(getEpoch(start));
4310
+ const startTimestamp = start;
3676
4311
  // eslint-disable-next-line no-param-reassign
3677
4312
  loopLimit = loopLimit || 1000;
4313
+ /** @type {number} */
3678
4314
  let nanos = 0;
4315
+ /** @type {number[]} */
3679
4316
  const adjustedSystemTime = [0, 0]; // [millis, nanoremainder]
3680
4317
 
3681
- const clock = {
4318
+ /** @type {Clock} */
4319
+ const clock = /** @type {Clock} */ ({
3682
4320
  now: start,
3683
4321
  Date: createDate(),
3684
4322
  loopLimit: loopLimit,
4323
+ isNearInfiniteLimit: false,
3685
4324
  tickMode: { mode: "manual", counter: 0, delta: undefined },
3686
- };
4325
+ });
3687
4326
 
3688
4327
  clock.Date.clock = clock;
3689
4328
 
3690
4329
  //eslint-disable-next-line jsdoc/require-jsdoc
3691
4330
  function getTimeToNextFrame() {
3692
- return 16 - ((clock.now - start) % 16);
4331
+ return 16 - ((clock.now - startTimestamp) % 16);
3693
4332
  }
3694
4333
 
3695
4334
  //eslint-disable-next-line jsdoc/require-jsdoc
3696
4335
  function hrtime(prev) {
3697
- const millisSinceStart = clock.now - adjustedSystemTime[0] - start;
4336
+ const millisSinceStart =
4337
+ clock.now - adjustedSystemTime[0] - startTimestamp;
3698
4338
  const secsSinceStart = Math.floor(millisSinceStart / 1000);
3699
4339
  const remainderInNanos =
3700
4340
  (millisSinceStart - secsSinceStart * 1e3) * 1e6 +
@@ -3740,20 +4380,21 @@ function requireFakeTimersSrc () {
3740
4380
  if (isPresent.hrtimeBigint) {
3741
4381
  hrtime.bigint = function () {
3742
4382
  const parts = hrtime();
3743
- return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]); // eslint-disable-line
4383
+ return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]);
3744
4384
  };
3745
4385
  }
3746
4386
 
3747
4387
  if (isPresent.Intl) {
3748
- clock.Intl = createIntl();
4388
+ clock.Intl = createIntl(clock);
3749
4389
  clock.Intl.clock = clock;
3750
4390
  }
3751
4391
 
3752
4392
  /**
3753
- * @param {TimerTickMode} tickModeConfig - The new configuration for how the clock should tick.
4393
+ * @param {SetTickModeConfig} tickModeConfig - The new configuration for how the clock should tick.
3754
4394
  */
3755
4395
  clock.setTickMode = function (tickModeConfig) {
3756
- const { mode: newMode, delta: newDelta } = tickModeConfig;
4396
+ const { mode: newMode, delta: newDelta } =
4397
+ /** @type {SetTickModeConfig} */ (tickModeConfig);
3757
4398
  const { mode: oldMode, delta: oldDelta } = clock.tickMode;
3758
4399
  if (newMode === oldMode && newDelta === oldDelta) {
3759
4400
  return;
@@ -3776,7 +4417,15 @@ function requireFakeTimersSrc () {
3776
4417
  }
3777
4418
  };
3778
4419
 
4420
+ /**
4421
+ * Keeps advancing the native event loop until the tick mode changes.
4422
+ * @returns {Promise<void>}
4423
+ */
3779
4424
  async function advanceUntilModeChanges() {
4425
+ /**
4426
+ * Waits for one native macrotask and then one microtask turn.
4427
+ * @returns {Promise<void>}
4428
+ */
3780
4429
  async function newMacrotask() {
3781
4430
  // MessageChannel ensures that setTimeout is not throttled to 4ms.
3782
4431
  // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
@@ -3784,7 +4433,7 @@ function requireFakeTimersSrc () {
3784
4433
  const channel = new MessageChannel();
3785
4434
  await new Promise((resolve) => {
3786
4435
  channel.port1.onmessage = () => {
3787
- resolve();
4436
+ resolve(undefined);
3788
4437
  channel.port1.close();
3789
4438
  };
3790
4439
  channel.port2.postMessage(undefined);
@@ -3807,6 +4456,11 @@ function requireFakeTimersSrc () {
3807
4456
  }
3808
4457
  }
3809
4458
 
4459
+ /**
4460
+ * Temporarily pauses nextAsync auto-ticking while an async operation runs.
4461
+ * @param {Promise<unknown>} promise
4462
+ * @returns {Promise<unknown>}
4463
+ */
3810
4464
  function pauseAutoTickUntilFinished(promise) {
3811
4465
  if (clock.tickMode.mode !== "nextAsync") {
3812
4466
  return promise;
@@ -3817,24 +4471,37 @@ function requireFakeTimersSrc () {
3817
4471
  });
3818
4472
  }
3819
4473
 
3820
- clock.requestIdleCallback = function requestIdleCallback(
3821
- func,
3822
- timeout,
3823
- ) {
4474
+ /**
4475
+ * Returns the remaining time in the current idle window.
4476
+ * @returns {number}
4477
+ */
4478
+ function getTimeToNextIdlePeriod() {
3824
4479
  let timeToNextIdlePeriod = 0;
3825
4480
 
3826
4481
  if (clock.countTimers() > 0) {
3827
4482
  timeToNextIdlePeriod = 50; // const for now
3828
4483
  }
3829
4484
 
4485
+ return timeToNextIdlePeriod;
4486
+ }
4487
+
4488
+ clock.requestIdleCallback = function requestIdleCallback(
4489
+ func,
4490
+ { timeout } = /** @type {{ timeout?: number }} */ ({}),
4491
+ ) {
4492
+ /**
4493
+ * @type {IdleDeadline}
4494
+ */
4495
+ const idleDeadline = {
4496
+ didTimeout: true,
4497
+ timeRemaining: getTimeToNextIdlePeriod,
4498
+ };
4499
+
3830
4500
  const result = addTimer(clock, {
3831
4501
  func: func,
3832
- args: Array.prototype.slice.call(arguments, 2),
3833
- delay:
3834
- typeof timeout === "undefined"
3835
- ? timeToNextIdlePeriod
3836
- : Math.min(timeout, timeToNextIdlePeriod),
3837
- idleCallback: true,
4502
+ args: [idleDeadline],
4503
+ delay: timeout,
4504
+ requestIdleCallback: true,
3838
4505
  });
3839
4506
 
3840
4507
  return Number(result);
@@ -3874,7 +4541,7 @@ function requireFakeTimersSrc () {
3874
4541
  return enqueueJob(clock, {
3875
4542
  func: func,
3876
4543
  args: Array.prototype.slice.call(arguments, 1),
3877
- error: isNearInfiniteLimit ? new Error() : null,
4544
+ error: clock.isNearInfiniteLimit ? new Error() : null,
3878
4545
  });
3879
4546
  };
3880
4547
 
@@ -3884,7 +4551,7 @@ function requireFakeTimersSrc () {
3884
4551
 
3885
4552
  clock.setInterval = function setInterval(func, timeout) {
3886
4553
  // eslint-disable-next-line no-param-reassign
3887
- timeout = parseInt(timeout, 10);
4554
+ timeout = parseInt(String(timeout), 10);
3888
4555
  return addTimer(clock, {
3889
4556
  func: func,
3890
4557
  args: Array.prototype.slice.call(arguments, 2),
@@ -3898,13 +4565,15 @@ function requireFakeTimersSrc () {
3898
4565
  };
3899
4566
 
3900
4567
  if (isPresent.setImmediate) {
3901
- clock.setImmediate = function setImmediate(func) {
3902
- return addTimer(clock, {
3903
- func: func,
3904
- args: Array.prototype.slice.call(arguments, 1),
3905
- immediate: true,
3906
- });
3907
- };
4568
+ clock.setImmediate = /** @type {SetImmediate} */ (
4569
+ function setImmediate(func) {
4570
+ return addTimer(clock, {
4571
+ func: func,
4572
+ args: Array.prototype.slice.call(arguments, 1),
4573
+ immediate: true,
4574
+ });
4575
+ }
4576
+ );
3908
4577
 
3909
4578
  if (typeof _global.Promise !== "undefined" && utilPromisify) {
3910
4579
  clock.setImmediate[utilPromisify.custom] =
@@ -3928,7 +4597,7 @@ function requireFakeTimersSrc () {
3928
4597
 
3929
4598
  clock.countTimers = function countTimers() {
3930
4599
  return (
3931
- Object.keys(clock.timers || {}).length +
4600
+ (clock.timerHeap ? clock.timerHeap.timers.length : 0) +
3932
4601
  (clock.jobs || []).length
3933
4602
  );
3934
4603
  };
@@ -3956,12 +4625,9 @@ function requireFakeTimersSrc () {
3956
4625
 
3957
4626
  /**
3958
4627
  * @param {number|string} tickValue milliseconds or a string parseable by parseTime
3959
- * @param {boolean} isAsync
3960
- * @param {Function} resolve
3961
- * @param {Function} reject
3962
- * @returns {number|undefined} will return the new `now` value or nothing for async
4628
+ * @returns {ClockState} a mutable state object for the tick execution
3963
4629
  */
3964
- function doTick(tickValue, isAsync, resolve, reject) {
4630
+ function createTickState(tickValue) {
3965
4631
  const msFloat =
3966
4632
  typeof tickValue === "number"
3967
4633
  ? tickValue
@@ -3981,122 +4647,202 @@ function requireFakeTimersSrc () {
3981
4647
  nanosTotal -= 1e6;
3982
4648
  }
3983
4649
 
3984
- nanos = nanosTotal;
3985
- let tickFrom = clock.now;
3986
- let previous = clock.now;
3987
- // ESLint fails to detect this correctly
3988
- /* eslint-disable prefer-const */
3989
- let timer,
3990
- firstException,
3991
- oldNow,
3992
- nextPromiseTick,
3993
- compensationCheck,
3994
- postTimerCall;
3995
- /* eslint-enable prefer-const */
3996
-
3997
- clock.duringTick = true;
4650
+ return /** @type {ClockState} */ ({
4651
+ msFloat: msFloat,
4652
+ ms: ms,
4653
+ nanosTotal: nanosTotal,
4654
+ tickFrom: clock.now,
4655
+ tickTo: tickTo,
4656
+ previous: clock.now,
4657
+ timer: null,
4658
+ firstException: null,
4659
+ oldNow: null,
4660
+ });
4661
+ }
3998
4662
 
3999
- // perform microtasks
4000
- oldNow = clock.now;
4001
- runJobs(clock);
4663
+ /**
4664
+ * @param {ClockState} state mutable tick state
4665
+ * @param {number} oldNow the clock.now before some action
4666
+ * @param {object} [options] compensation options
4667
+ * @param {boolean} [options.includePrevious] whether to also update state.previous
4668
+ */
4669
+ function applyClockChangeCompensation(state, oldNow, options) {
4002
4670
  if (oldNow !== clock.now) {
4003
- // compensate for any setSystemTime() call during microtask callback
4004
- tickFrom += clock.now - oldNow;
4005
- tickTo += clock.now - oldNow;
4671
+ const difference = clock.now - oldNow;
4672
+ state.tickFrom += difference;
4673
+ state.tickTo += difference;
4674
+ if (options && options.includePrevious) {
4675
+ state.previous += difference;
4676
+ }
4006
4677
  }
4678
+ }
4007
4679
 
4008
- //eslint-disable-next-line jsdoc/require-jsdoc
4009
- function doTickInner() {
4010
- // perform each timer in the requested range
4011
- timer = firstTimerInRange(clock, tickFrom, tickTo);
4012
- // eslint-disable-next-line no-unmodified-loop-condition
4013
- while (timer && tickFrom <= tickTo) {
4014
- if (clock.timers[timer.id]) {
4015
- tickFrom = timer.callAt;
4016
- clock.now = timer.callAt;
4017
- oldNow = clock.now;
4018
- try {
4019
- runJobs(clock);
4020
- callTimer(clock, timer);
4021
- } catch (e) {
4022
- firstException = firstException || e;
4023
- }
4024
-
4025
- if (isAsync) {
4026
- // finish up after native setImmediate callback to allow
4027
- // all native es6 promises to process their callbacks after
4028
- // each timer fires.
4029
- originalSetTimeout(nextPromiseTick);
4030
- return;
4031
- }
4680
+ /**
4681
+ * @param {ClockState} state mutable tick state
4682
+ */
4683
+ function runInitialJobs(state) {
4684
+ state.oldNow = clock.now;
4685
+ runJobs(clock);
4686
+ applyClockChangeCompensation(state, state.oldNow);
4687
+ }
4032
4688
 
4033
- compensationCheck();
4034
- }
4689
+ /**
4690
+ * @param {ClockState} state mutable tick state
4691
+ */
4692
+ function runPostLoopJobs(state) {
4693
+ state.oldNow = clock.now;
4694
+ runJobs(clock);
4695
+ applyClockChangeCompensation(state, state.oldNow);
4696
+ }
4035
4697
 
4036
- postTimerCall();
4037
- }
4698
+ /**
4699
+ * @param {ClockState} state mutable tick state
4700
+ */
4701
+ function selectNextTimerInRange(state) {
4702
+ state.timer = firstTimerInRange(
4703
+ clock,
4704
+ state.previous,
4705
+ state.tickTo,
4706
+ );
4707
+ state.previous = state.tickFrom;
4708
+ }
4038
4709
 
4039
- // perform process.nextTick()s again
4040
- oldNow = clock.now;
4041
- runJobs(clock);
4042
- if (oldNow !== clock.now) {
4043
- // compensate for any setSystemTime() call during process.nextTick() callback
4044
- tickFrom += clock.now - oldNow;
4045
- tickTo += clock.now - oldNow;
4046
- }
4047
- clock.duringTick = false;
4710
+ /**
4711
+ * @param {ClockState} state mutable tick state
4712
+ * @param {boolean} isAsync whether this is an async tick
4713
+ * @param {FakeTimersFunction} nextPromiseTick callback for async promise settlement
4714
+ * @param {FakeTimersFunction} compensationCheck callback for clock change compensation
4715
+ * @returns {boolean} whether an early return was triggered (async mode)
4716
+ */
4717
+ function runTimersInRange(
4718
+ state,
4719
+ isAsync,
4720
+ nextPromiseTick,
4721
+ compensationCheck,
4722
+ ) {
4723
+ state.timer = firstTimerInRange(
4724
+ clock,
4725
+ state.tickFrom,
4726
+ state.tickTo,
4727
+ );
4048
4728
 
4049
- // corner case: during runJobs new timers were scheduled which could be in the range [clock.now, tickTo]
4050
- timer = firstTimerInRange(clock, tickFrom, tickTo);
4051
- if (timer) {
4729
+ while (state.timer && state.tickFrom <= state.tickTo) {
4730
+ if (hasTimer(clock, state.timer.id)) {
4731
+ state.tickFrom = state.timer.callAt;
4732
+ clock.now = state.timer.callAt;
4733
+ state.oldNow = clock.now;
4052
4734
  try {
4053
- clock.tick(tickTo - clock.now); // do it all again - for the remainder of the requested range
4735
+ runJobs(clock);
4736
+ callTimer(clock, state.timer);
4054
4737
  } catch (e) {
4055
- firstException = firstException || e;
4738
+ state.firstException = state.firstException || e;
4739
+ }
4740
+
4741
+ if (isAsync) {
4742
+ // finish up after native setImmediate callback to allow
4743
+ // all native es6 promises to process their callbacks after
4744
+ // each timer fires.
4745
+ originalSetTimeout(nextPromiseTick);
4746
+ return true;
4056
4747
  }
4057
- } else {
4058
- // no timers remaining in the requested range: move the clock all the way to the end
4059
- clock.now = tickTo;
4060
4748
 
4061
- // update nanos
4062
- nanos = nanosTotal;
4063
- }
4064
- if (firstException) {
4065
- throw firstException;
4749
+ compensationCheck();
4066
4750
  }
4067
4751
 
4068
- if (isAsync) {
4069
- resolve(clock.now);
4070
- } else {
4071
- return clock.now;
4752
+ selectNextTimerInRange(state);
4753
+ }
4754
+ return false;
4755
+ }
4756
+
4757
+ /**
4758
+ * @param {ClockState} state mutable tick state
4759
+ * @param {boolean} isAsync whether this is an async tick
4760
+ * @param {FakeTimersFunction} resolve promise resolve function
4761
+ * @returns {number|undefined} the new clock.now or nothing for async
4762
+ */
4763
+ function finalizeTick(state, isAsync, resolve) {
4764
+ // corner case: during runJobs new timers were scheduled which could be in the range [clock.now, tickTo]
4765
+ state.timer = firstTimerInRange(
4766
+ clock,
4767
+ state.tickFrom,
4768
+ state.tickTo,
4769
+ );
4770
+ if (state.timer) {
4771
+ try {
4772
+ clock.tick(state.tickTo - clock.now); // do it all again - for the remainder of the requested range
4773
+ } catch (e) {
4774
+ state.firstException = state.firstException || e;
4072
4775
  }
4776
+ } else {
4777
+ // no timers remaining in the requested range: move the clock all the way to the end
4778
+ clock.now = state.tickTo;
4779
+
4780
+ // update nanos
4781
+ nanos = state.nanosTotal;
4782
+ }
4783
+ if (state.firstException) {
4784
+ throw state.firstException;
4785
+ }
4786
+
4787
+ if (isAsync) {
4788
+ resolve(clock.now);
4789
+ } else {
4790
+ return clock.now;
4073
4791
  }
4792
+ }
4793
+
4794
+ /**
4795
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
4796
+ * @param {boolean} isAsync whether this is an async tick
4797
+ * @param {FakeTimersFunction} [resolve] promise resolve function
4798
+ * @param {FakeTimersFunction} [reject] promise reject function
4799
+ * @returns {number|undefined} the new clock.now or nothing for async
4800
+ */
4801
+ function doTick(tickValue, isAsync, resolve, reject) {
4802
+ /** @type {ClockState} */
4803
+ const state = createTickState(tickValue);
4804
+
4805
+ nanos = state.nanosTotal;
4806
+ clock.duringTick = true;
4807
+
4808
+ runInitialJobs(state);
4809
+
4810
+ const compensationCheck = function () {
4811
+ applyClockChangeCompensation(state, state.oldNow, {
4812
+ includePrevious: true,
4813
+ });
4814
+ };
4074
4815
 
4075
- nextPromiseTick =
4816
+ const nextPromiseTick =
4076
4817
  isAsync &&
4077
4818
  function () {
4078
4819
  try {
4079
4820
  compensationCheck();
4080
- postTimerCall();
4821
+ selectNextTimerInRange(state);
4081
4822
  doTickInner();
4082
4823
  } catch (e) {
4083
4824
  reject(e);
4084
4825
  }
4085
4826
  };
4086
4827
 
4087
- compensationCheck = function () {
4088
- // compensate for any setSystemTime() call during timer callback
4089
- if (oldNow !== clock.now) {
4090
- tickFrom += clock.now - oldNow;
4091
- tickTo += clock.now - oldNow;
4092
- previous += clock.now - oldNow;
4828
+ //eslint-disable-next-line jsdoc/require-jsdoc
4829
+ function doTickInner() {
4830
+ if (
4831
+ runTimersInRange(
4832
+ state,
4833
+ isAsync,
4834
+ nextPromiseTick,
4835
+ compensationCheck,
4836
+ )
4837
+ ) {
4838
+ return;
4093
4839
  }
4094
- };
4095
4840
 
4096
- postTimerCall = function () {
4097
- timer = firstTimerInRange(clock, previous, tickTo);
4098
- previous = tickFrom;
4099
- };
4841
+ runPostLoopJobs(state);
4842
+ clock.duringTick = false;
4843
+
4844
+ return finalizeTick(state, isAsync, resolve);
4845
+ }
4100
4846
 
4101
4847
  return doTickInner();
4102
4848
  }
@@ -4109,26 +4855,6 @@ function requireFakeTimersSrc () {
4109
4855
  return doTick(tickValue, false);
4110
4856
  };
4111
4857
 
4112
- if (typeof _global.Promise !== "undefined") {
4113
- /**
4114
- * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
4115
- * @returns {Promise}
4116
- */
4117
- clock.tickAsync = function tickAsync(tickValue) {
4118
- return pauseAutoTickUntilFinished(
4119
- new _global.Promise(function (resolve, reject) {
4120
- originalSetTimeout(function () {
4121
- try {
4122
- doTick(tickValue, true, resolve, reject);
4123
- } catch (e) {
4124
- reject(e);
4125
- }
4126
- });
4127
- }),
4128
- );
4129
- };
4130
- }
4131
-
4132
4858
  clock.next = function next() {
4133
4859
  runJobs(clock);
4134
4860
  const timer = firstTimer(clock);
@@ -4147,61 +4873,40 @@ function requireFakeTimersSrc () {
4147
4873
  }
4148
4874
  };
4149
4875
 
4150
- if (typeof _global.Promise !== "undefined") {
4151
- clock.nextAsync = function nextAsync() {
4152
- return pauseAutoTickUntilFinished(
4153
- new _global.Promise(function (resolve, reject) {
4154
- originalSetTimeout(function () {
4155
- try {
4156
- const timer = firstTimer(clock);
4157
- if (!timer) {
4158
- resolve(clock.now);
4159
- return;
4160
- }
4161
-
4162
- let err;
4163
- clock.duringTick = true;
4164
- clock.now = timer.callAt;
4165
- try {
4166
- callTimer(clock, timer);
4167
- } catch (e) {
4168
- err = e;
4169
- }
4170
- clock.duringTick = false;
4171
-
4172
- originalSetTimeout(function () {
4173
- if (err) {
4174
- reject(err);
4175
- } else {
4176
- resolve(clock.now);
4177
- }
4178
- });
4179
- } catch (e) {
4180
- reject(e);
4181
- }
4182
- });
4183
- }),
4184
- );
4185
- };
4876
+ /**
4877
+ * @param {(resolve: (value: unknown) => void, reject: (reason?: unknown) => void) => void} callback function to run inside native setTimeout
4878
+ * @returns {Promise}
4879
+ */
4880
+ function runAsyncWithNativeTimeout(callback) {
4881
+ return pauseAutoTickUntilFinished(
4882
+ new _global.Promise(function (resolve, reject) {
4883
+ originalSetTimeout(function () {
4884
+ try {
4885
+ callback(resolve, reject);
4886
+ } catch (e) {
4887
+ reject(e);
4888
+ }
4889
+ });
4890
+ }),
4891
+ );
4186
4892
  }
4187
4893
 
4188
4894
  clock.runAll = function runAll() {
4189
- let numTimers, i;
4190
4895
  runJobs(clock);
4191
- for (i = 0; i < clock.loopLimit; i++) {
4896
+ for (let i = 0; i < clock.loopLimit; i++) {
4192
4897
  if (!clock.timers) {
4193
- resetIsNearInfiniteLimit();
4898
+ resetIsNearInfiniteLimit(clock);
4194
4899
  return clock.now;
4195
4900
  }
4196
4901
 
4197
- numTimers = Object.keys(clock.timers).length;
4902
+ const numTimers = clock.timerHeap.timers.length;
4198
4903
  if (numTimers === 0) {
4199
- resetIsNearInfiniteLimit();
4904
+ resetIsNearInfiniteLimit(clock);
4200
4905
  return clock.now;
4201
4906
  }
4202
4907
 
4203
- clock.next();
4204
4908
  checkIsNearInfiniteLimit(clock, i);
4909
+ clock.next();
4205
4910
  }
4206
4911
 
4207
4912
  const excessJob = firstTimer(clock);
@@ -4212,60 +4917,6 @@ function requireFakeTimersSrc () {
4212
4917
  return clock.tick(getTimeToNextFrame());
4213
4918
  };
4214
4919
 
4215
- if (typeof _global.Promise !== "undefined") {
4216
- clock.runAllAsync = function runAllAsync() {
4217
- return pauseAutoTickUntilFinished(
4218
- new _global.Promise(function (resolve, reject) {
4219
- let i = 0;
4220
- /**
4221
- *
4222
- */
4223
- function doRun() {
4224
- originalSetTimeout(function () {
4225
- try {
4226
- runJobs(clock);
4227
-
4228
- let numTimers;
4229
- if (i < clock.loopLimit) {
4230
- if (!clock.timers) {
4231
- resetIsNearInfiniteLimit();
4232
- resolve(clock.now);
4233
- return;
4234
- }
4235
-
4236
- numTimers = Object.keys(
4237
- clock.timers,
4238
- ).length;
4239
- if (numTimers === 0) {
4240
- resetIsNearInfiniteLimit();
4241
- resolve(clock.now);
4242
- return;
4243
- }
4244
-
4245
- clock.next();
4246
-
4247
- i++;
4248
-
4249
- doRun();
4250
- checkIsNearInfiniteLimit(clock, i);
4251
- return;
4252
- }
4253
-
4254
- const excessJob = firstTimer(clock);
4255
- reject(
4256
- getInfiniteLoopError(clock, excessJob),
4257
- );
4258
- } catch (e) {
4259
- reject(e);
4260
- }
4261
- });
4262
- }
4263
- doRun();
4264
- }),
4265
- );
4266
- };
4267
- }
4268
-
4269
4920
  clock.runToLast = function runToLast() {
4270
4921
  const timer = lastTimer(clock);
4271
4922
  if (!timer) {
@@ -4277,32 +4928,110 @@ function requireFakeTimersSrc () {
4277
4928
  };
4278
4929
 
4279
4930
  if (typeof _global.Promise !== "undefined") {
4280
- clock.runToLastAsync = function runToLastAsync() {
4281
- return pauseAutoTickUntilFinished(
4282
- new _global.Promise(function (resolve, reject) {
4283
- originalSetTimeout(function () {
4284
- try {
4285
- const timer = lastTimer(clock);
4286
- if (!timer) {
4287
- runJobs(clock);
4288
- resolve(clock.now);
4289
- }
4931
+ /**
4932
+ * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
4933
+ * @returns {Promise}
4934
+ */
4935
+ clock.tickAsync = function tickAsync(tickValue) {
4936
+ return runAsyncWithNativeTimeout(function (resolve, reject) {
4937
+ doTick(tickValue, true, resolve, reject);
4938
+ });
4939
+ };
4290
4940
 
4291
- resolve(
4292
- clock.tickAsync(timer.callAt - clock.now),
4293
- );
4294
- } catch (e) {
4295
- reject(e);
4941
+ clock.nextAsync = function nextAsync() {
4942
+ return runAsyncWithNativeTimeout(function (resolve, reject) {
4943
+ const timer = firstTimer(clock);
4944
+ if (!timer) {
4945
+ resolve(clock.now);
4946
+ return;
4947
+ }
4948
+
4949
+ let err;
4950
+ clock.duringTick = true;
4951
+ clock.now = timer.callAt;
4952
+ try {
4953
+ callTimer(clock, timer);
4954
+ } catch (e) {
4955
+ err = e;
4956
+ }
4957
+ clock.duringTick = false;
4958
+
4959
+ originalSetTimeout(function () {
4960
+ if (err) {
4961
+ reject(err);
4962
+ } else {
4963
+ resolve(clock.now);
4964
+ }
4965
+ });
4966
+ });
4967
+ };
4968
+
4969
+ clock.runAllAsync = function runAllAsync() {
4970
+ let i = 0;
4971
+ /**
4972
+ * @param {(value: unknown) => void} resolve promise resolve function
4973
+ * @param {(reason?: unknown) => void} reject promise reject function
4974
+ */
4975
+ function doRun(resolve, reject) {
4976
+ try {
4977
+ runJobs(clock);
4978
+
4979
+ let numTimers;
4980
+ if (i < clock.loopLimit) {
4981
+ if (!clock.timerHeap) {
4982
+ resetIsNearInfiniteLimit(clock);
4983
+ resolve(clock.now);
4984
+ return;
4296
4985
  }
4297
- });
4298
- }),
4299
- );
4986
+
4987
+ numTimers = clock.timerHeap.timers.length;
4988
+ if (numTimers === 0) {
4989
+ resetIsNearInfiniteLimit(clock);
4990
+ resolve(clock.now);
4991
+ return;
4992
+ }
4993
+
4994
+ checkIsNearInfiniteLimit(clock, i);
4995
+ clock.next();
4996
+
4997
+ i++;
4998
+
4999
+ originalSetTimeout(function () {
5000
+ doRun(resolve, reject);
5001
+ });
5002
+ return;
5003
+ }
5004
+
5005
+ const excessJob = firstTimer(clock);
5006
+ reject(getInfiniteLoopError(clock, excessJob));
5007
+ } catch (e) {
5008
+ reject(e);
5009
+ }
5010
+ }
5011
+
5012
+ return runAsyncWithNativeTimeout(function (resolve, reject) {
5013
+ doRun(resolve, reject);
5014
+ });
5015
+ };
5016
+
5017
+ clock.runToLastAsync = function runToLastAsync() {
5018
+ return runAsyncWithNativeTimeout(function (resolve) {
5019
+ const timer = lastTimer(clock);
5020
+ if (!timer) {
5021
+ runJobs(clock);
5022
+ resolve(clock.now);
5023
+ return;
5024
+ }
5025
+
5026
+ resolve(clock.tickAsync(timer.callAt - clock.now));
5027
+ });
4300
5028
  };
4301
5029
  }
4302
5030
 
4303
5031
  clock.reset = function reset() {
4304
5032
  nanos = 0;
4305
- clock.timers = {};
5033
+ clock.timers = new Map();
5034
+ clock.timerHeap = new TimerHeap();
4306
5035
  clock.jobs = [];
4307
5036
  clock.now = start;
4308
5037
  };
@@ -4311,7 +5040,6 @@ function requireFakeTimersSrc () {
4311
5040
  // determine time difference
4312
5041
  const newNow = getEpoch(systemTime);
4313
5042
  const difference = newNow - clock.now;
4314
- let id, timer;
4315
5043
 
4316
5044
  adjustedSystemTime[0] = adjustedSystemTime[0] + difference;
4317
5045
  adjustedSystemTime[1] = adjustedSystemTime[1] + nanos;
@@ -4320,18 +5048,15 @@ function requireFakeTimersSrc () {
4320
5048
  nanos = 0;
4321
5049
 
4322
5050
  // update timers and intervals to keep them stable
4323
- for (id in clock.timers) {
4324
- if (clock.timers.hasOwnProperty(id)) {
4325
- timer = clock.timers[id];
4326
- timer.createdAt += difference;
4327
- timer.callAt += difference;
4328
- }
4329
- }
5051
+ forEachActiveTimer(clock, (timer) => {
5052
+ timer.createdAt += difference;
5053
+ timer.callAt += difference;
5054
+ });
4330
5055
  };
4331
5056
 
4332
5057
  /**
4333
5058
  * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
4334
- * @returns {number} will return the new `now` value
5059
+ * @returns {number} the new `now` value
4335
5060
  */
4336
5061
  clock.jump = function jump(tickValue) {
4337
5062
  const msFloat =
@@ -4340,12 +5065,17 @@ function requireFakeTimersSrc () {
4340
5065
  : parseTime(tickValue);
4341
5066
  const ms = Math.floor(msFloat);
4342
5067
 
4343
- for (const timer of Object.values(clock.timers)) {
5068
+ forEachActiveTimer(clock, (timer) => {
4344
5069
  if (clock.now + ms > timer.callAt) {
4345
5070
  timer.callAt = clock.now + ms;
4346
5071
  }
4347
- }
5072
+ });
5073
+
5074
+ // Rebuild heap as order might have changed
5075
+ rebuildTimerHeap(clock);
5076
+
4348
5077
  clock.tick(ms);
5078
+ return clock.now;
4349
5079
  };
4350
5080
 
4351
5081
  if (isPresent.performance) {
@@ -4360,6 +5090,11 @@ function requireFakeTimersSrc () {
4360
5090
  return clock;
4361
5091
  }
4362
5092
 
5093
+ /**
5094
+ * Starts the interval used to advance the clock automatically.
5095
+ * @param {Clock} clock
5096
+ * @param {number} delta
5097
+ */
4363
5098
  function createIntervalTick(clock, delta) {
4364
5099
  const intervalTick = doIntervalTick.bind(null, clock, delta);
4365
5100
  const intervalId = originalSetInterval(intervalTick, delta);
@@ -4401,6 +5136,21 @@ function requireFakeTimersSrc () {
4401
5136
  config.shouldClearNativeTimers =
4402
5137
  config.shouldClearNativeTimers || false;
4403
5138
 
5139
+ const hasToFake = Object.prototype.hasOwnProperty.call(
5140
+ config,
5141
+ "toFake",
5142
+ );
5143
+ const hasToNotFake = Object.prototype.hasOwnProperty.call(
5144
+ config,
5145
+ "toNotFake",
5146
+ );
5147
+
5148
+ if (hasToFake && hasToNotFake) {
5149
+ throw new TypeError(
5150
+ "config.toFake and config.toNotFake cannot be used together",
5151
+ );
5152
+ }
5153
+
4404
5154
  if (config.target) {
4405
5155
  throw new TypeError(
4406
5156
  "config.target is no longer supported. Use `withGlobal(target)` instead.",
@@ -4408,8 +5158,8 @@ function requireFakeTimersSrc () {
4408
5158
  }
4409
5159
 
4410
5160
  /**
4411
- * @param {string} timer/object the name of the thing that is not present
4412
- * @param timer
5161
+ * Handles a missing timer or API name during installation.
5162
+ * @param {string} timer - the name of the missing timer or object
4413
5163
  */
4414
5164
  function handleMissingTimer(timer) {
4415
5165
  if (config.ignoreMissingTimers) {
@@ -4431,10 +5181,24 @@ function requireFakeTimersSrc () {
4431
5181
 
4432
5182
  clock.abortListenerMap = new Map();
4433
5183
 
4434
- clock.methods = config.toFake || [];
4435
-
4436
- if (clock.methods.length === 0) {
4437
- clock.methods = Object.keys(timers);
5184
+ if (hasToFake) {
5185
+ clock.methods = /** @type {FakeMethod[]} */ (config.toFake || []);
5186
+ if (clock.methods.length === 0) {
5187
+ clock.methods = /** @type {FakeMethod[]} */ (
5188
+ Object.keys(timers)
5189
+ );
5190
+ }
5191
+ } else if (hasToNotFake) {
5192
+ const methodsToNotFake = /** @type {string[]} */ (
5193
+ config.toNotFake || []
5194
+ );
5195
+ clock.methods = /** @type {FakeMethod[]} */ (
5196
+ Object.keys(timers).filter(
5197
+ (method) => !methodsToNotFake.includes(method),
5198
+ )
5199
+ );
5200
+ } else {
5201
+ clock.methods = /** @type {FakeMethod[]} */ (Object.keys(timers));
4438
5202
  }
4439
5203
 
4440
5204
  if (config.shouldAdvanceTime === true) {
@@ -4471,7 +5235,7 @@ function requireFakeTimersSrc () {
4471
5235
  // (or the Worker was installed)
4472
5236
  clock.performance.timeOrigin = getEpoch(config.now);
4473
5237
  } else if ((config.toFake || []).includes("performance")) {
4474
- return handleMissingTimer("performance");
5238
+ handleMissingTimer("performance");
4475
5239
  }
4476
5240
  }
4477
5241
  if (_global === globalObject && timersModule) {
@@ -4635,10 +5399,13 @@ function requireFakeTimersSrc () {
4635
5399
  [Symbol.asyncIterator]: () => {
4636
5400
  const createResolvable = () => {
4637
5401
  let resolve, reject;
4638
- const promise = new Promise((res, rej) => {
4639
- resolve = res;
4640
- reject = rej;
4641
- });
5402
+ const promise =
5403
+ /** @type {Promise<unknown> & { resolve: (value: unknown) => void; reject: (reason: unknown) => void }} */ (
5404
+ new Promise((res, rej) => {
5405
+ resolve = res;
5406
+ reject = rej;
5407
+ })
5408
+ );
4642
5409
  promise.resolve = resolve;
4643
5410
  promise.reject = reject;
4644
5411
  return promise;
@@ -4766,22 +5533,13 @@ function requireFakeTimersSrc () {
4766
5533
  };
4767
5534
  }
4768
5535
 
4769
- /**
4770
- * @typedef {object} FakeTimers
4771
- * @property {Timers} timers
4772
- * @property {createClock} createClock
4773
- * @property {Function} install
4774
- * @property {withGlobal} withGlobal
4775
- */
4776
-
4777
- /* eslint-enable complexity */
4778
-
4779
5536
  /** @type {FakeTimers} */
4780
5537
  const defaultImplementation = withGlobal(globalObject);
4781
5538
 
4782
5539
  fakeTimersSrc.timers = defaultImplementation.timers;
4783
5540
  fakeTimersSrc.createClock = defaultImplementation.createClock;
4784
5541
  fakeTimersSrc.install = defaultImplementation.install;
5542
+ /** @type {WithGlobal} */
4785
5543
  fakeTimersSrc.withGlobal = withGlobal;
4786
5544
  return fakeTimersSrc;
4787
5545
  }
@@ -4872,12 +5630,18 @@ class FakeTimers {
4872
5630
  this._fakingDate = null;
4873
5631
  }
4874
5632
  if (this._fakingTime) this._clock.uninstall();
4875
- const toFake = Object.keys(this._fakeTimers.timers).filter((timer) => timer !== "nextTick" && timer !== "queueMicrotask");
4876
- if (this._userConfig?.toFake?.includes("nextTick") && isChildProcess()) throw new Error("process.nextTick cannot be mocked inside child_process");
5633
+ let toFake = this._userConfig?.toFake;
5634
+ if (isChildProcess() && toFake?.includes("nextTick")) throw new Error("process.nextTick cannot be mocked inside child_process");
5635
+ let toNotFake = this._userConfig?.toNotFake;
5636
+ if (toFake === void 0 && toNotFake === void 0)
5637
+ // Do not mock timers internally used by node by default. It can still be mocked through userConfig.
5638
+ toFake = Object.keys(this._fakeTimers.timers).filter((timer) => timer !== "nextTick" && timer !== "queueMicrotask");
5639
+ if (isChildProcess() && toNotFake && !toNotFake.includes("nextTick")) toNotFake = [...toNotFake, "nextTick"];
4877
5640
  this._clock = this._fakeTimers.install({
4878
5641
  now: fakeDate,
4879
5642
  ...this._userConfig,
4880
- toFake: this._userConfig?.toFake || toFake,
5643
+ ...toFake && { toFake },
5644
+ ...toNotFake && { toNotFake },
4881
5645
  ignoreMissingTimers: true
4882
5646
  });
4883
5647
  this._fakingTime = true;
@@ -4928,7 +5692,7 @@ class FakeTimers {
4928
5692
  }
4929
5693
  }
4930
5694
 
4931
- function copyStackTrace$1(target, source) {
5695
+ function copyStackTrace$2(target, source) {
4932
5696
  if (source.stack !== void 0) target.stack = source.stack.replace(source.message, target.message);
4933
5697
  return target;
4934
5698
  }
@@ -4949,7 +5713,7 @@ function waitFor(callback, options = {}) {
4949
5713
  const handleTimeout = () => {
4950
5714
  if (intervalId) clearInterval(intervalId);
4951
5715
  let error = lastError;
4952
- if (!error) error = copyStackTrace$1(/* @__PURE__ */ new Error("Timed out in waitFor!"), STACK_TRACE_ERROR);
5716
+ if (!error) error = copyStackTrace$2(/* @__PURE__ */ new Error("Timed out in waitFor!"), STACK_TRACE_ERROR);
4953
5717
  reject(error);
4954
5718
  };
4955
5719
  const checkCallback = () => {
@@ -4990,7 +5754,7 @@ function waitUntil(callback, options = {}) {
4990
5754
  let intervalId;
4991
5755
  const onReject = (error) => {
4992
5756
  if (intervalId) clearInterval(intervalId);
4993
- if (!error) error = copyStackTrace$1(/* @__PURE__ */ new Error("Timed out in waitUntil!"), STACK_TRACE_ERROR);
5757
+ if (!error) error = copyStackTrace$2(/* @__PURE__ */ new Error("Timed out in waitUntil!"), STACK_TRACE_ERROR);
4994
5758
  reject(error);
4995
5759
  };
4996
5760
  const onResolve = (result) => {
@@ -5129,9 +5893,17 @@ function createVitest() {
5129
5893
  defineHelper: (fn) => {
5130
5894
  return function __VITEST_HELPER__(...args) {
5131
5895
  const result = fn.apply(this, args);
5132
- if (result && typeof result === "object" && typeof result.then === "function") return (async function __VITEST_HELPER__() {
5133
- return await result;
5134
- })();
5896
+ if (result && typeof result === "object" && typeof result.then === "function") {
5897
+ const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
5898
+ return (async function __VITEST_HELPER__() {
5899
+ try {
5900
+ return await result;
5901
+ } catch (error) {
5902
+ if (error instanceof Error && !error.stack?.includes("__VITEST_HELPER__")) copyStackTrace$1(error, stackTraceError);
5903
+ throw error;
5904
+ }
5905
+ })();
5906
+ }
5135
5907
  return result;
5136
5908
  };
5137
5909
  },
@@ -5258,6 +6030,10 @@ function getImporter(name) {
5258
6030
  return stack.includes(` at Object.${name}`) || stack.includes(`${name}@`) || stack.includes(` at ${name} (`);
5259
6031
  }) + 1])?.file || "";
5260
6032
  }
6033
+ function copyStackTrace$1(target, source) {
6034
+ if (source.stack !== void 0) target.stack = source.stack.replace(source.message, target.message);
6035
+ return target;
6036
+ }
5261
6037
 
5262
6038
  // these matchers are not supported because they don't make sense with poll
5263
6039
  const unsupported = [
@@ -5325,7 +6101,9 @@ function createExpectPoll(expect) {
5325
6101
  const promise = async () => {
5326
6102
  chai.util.flag(assertion, "_name", key);
5327
6103
  chai.util.flag(assertion, "error", STACK_TRACE_ERROR);
6104
+ const onStart = chai.util.flag(assertion, "_poll.onStart");
5328
6105
  const onSettled = chai.util.flag(assertion, "_poll.onSettled");
6106
+ await onStart?.({ assertion });
5329
6107
  if (Object.getOwnPropertyDescriptor(assertionFunction, "__vitest_poll_takeover__")?.value) try {
5330
6108
  const output = await assertionFunction.call(assertion, ...args);
5331
6109
  await onSettled?.({
@@ -5341,41 +6119,53 @@ function createExpectPoll(expect) {
5341
6119
  throwWithCause(err, STACK_TRACE_ERROR);
5342
6120
  }
5343
6121
  const { setTimeout, clearTimeout } = getSafeTimers();
5344
- let executionPhase = "fn";
5345
- let hasTimedOut = false;
5346
- const timerId = setTimeout(() => {
5347
- hasTimedOut = true;
5348
- }, timeout);
6122
+ let timerId;
6123
+ const timeoutController = new AbortController();
6124
+ const timeoutPromise = new Promise((resolve) => {
6125
+ timerId = setTimeout(() => {
6126
+ timeoutController.abort();
6127
+ resolve();
6128
+ }, timeout);
6129
+ });
6130
+ let lastError;
5349
6131
  try {
5350
- while (true) {
5351
- const isLastAttempt = hasTimedOut;
5352
- if (isLastAttempt) chai.util.flag(assertion, "_isLastPollAttempt", true);
5353
- try {
5354
- executionPhase = "fn";
5355
- const obj = await fn();
5356
- chai.util.flag(assertion, "object", obj);
5357
- executionPhase = "assertion";
5358
- const output = await assertionFunction.call(assertion, ...args);
5359
- await onSettled?.({
5360
- assertion,
5361
- status: "pass"
5362
- });
5363
- return output;
5364
- } catch (err) {
5365
- if (isLastAttempt || executionPhase === "assertion" && chai.util.flag(assertion, "_poll.assert_once")) {
5366
- await onSettled?.({
5367
- assertion,
5368
- status: "fail"
5369
- });
5370
- throwWithCause(err, STACK_TRACE_ERROR);
5371
- }
5372
- await delay(interval, setTimeout);
5373
- if (vi.isFakeTimers()) vi.advanceTimersByTime(interval);
6132
+ while (true) try {
6133
+ const fnResult = await raceWith$1(Promise.resolve().then(() => fn({ signal: timeoutController.signal })), timeoutPromise);
6134
+ if (!fnResult.ok) {
6135
+ lastError ??= /* @__PURE__ */ new Error(`expect.poll() function didn't resolve in time.`);
6136
+ break;
5374
6137
  }
6138
+ const obj = fnResult.value;
6139
+ chai.util.flag(assertion, "object", obj);
6140
+ const assertionResult = await raceWith$1(Promise.resolve().then(() => assertionFunction.apply(assertion, args)), timeoutPromise);
6141
+ if (!assertionResult.ok) {
6142
+ lastError ??= /* @__PURE__ */ new Error(`expect.poll() assertion didn't resolve in time.`);
6143
+ break;
6144
+ }
6145
+ const output = assertionResult.value;
6146
+ await onSettled?.({
6147
+ assertion,
6148
+ status: "pass"
6149
+ });
6150
+ return output;
6151
+ } catch (err) {
6152
+ lastError = err;
6153
+ // no retry for toMatchScreenshot since
6154
+ // it owns retry/stability after the first element resolution
6155
+ if (key === "toMatchScreenshot") break;
6156
+ if (!(await raceWith$1(delay(interval, setTimeout), timeoutPromise)).ok) break;
6157
+ if (vi.isFakeTimers()) vi.advanceTimersByTime(interval);
5375
6158
  }
5376
6159
  } finally {
5377
6160
  clearTimeout(timerId);
5378
6161
  }
6162
+ if (lastError) {
6163
+ await onSettled?.({
6164
+ assertion,
6165
+ status: "fail"
6166
+ });
6167
+ throwWithCause(lastError, STACK_TRACE_ERROR);
6168
+ }
5379
6169
  };
5380
6170
  let awaited = false;
5381
6171
  test.onFinished ??= [];
@@ -5413,6 +6203,17 @@ function copyStackTrace(target, source) {
5413
6203
  if (source.stack !== void 0) target.stack = source.stack.replace(source.message, target.message);
5414
6204
  return target;
5415
6205
  }
6206
+ function raceWith$1(promise, other) {
6207
+ const left = promise.then((value) => ({
6208
+ ok: true,
6209
+ value
6210
+ }));
6211
+ if (!other) return left;
6212
+ return Promise.race([left, other.then((value) => ({
6213
+ ok: false,
6214
+ value
6215
+ }))]);
6216
+ }
5416
6217
 
5417
6218
  var naturalCompare$1 = {exports: {}};
5418
6219
 
@@ -6303,11 +7104,15 @@ class SnapshotClient {
6303
7104
  inlineSnapshot
6304
7105
  });
6305
7106
  const reference = expectedSnapshot.data !== void 0 && snapshotState.snapshotUpdateState !== "all" ? adapter.parseExpected(expectedSnapshot.data) : void 0;
7107
+ const timeoutController = new AbortController();
6306
7108
  const stableResult = await getStableSnapshot({
6307
7109
  adapter,
6308
- poll,
7110
+ poll: () => poll({ signal: timeoutController.signal }),
6309
7111
  interval,
6310
- timedOut: timeout > 0 ? new Promise((r) => setTimeout(r, timeout)) : void 0,
7112
+ timedOut: timeout > 0 ? new Promise((r) => setTimeout(() => {
7113
+ timeoutController.abort();
7114
+ r();
7115
+ }, timeout)) : void 0,
6311
7116
  match: reference ? (captured) => adapter.match(captured, reference).pass : void 0
6312
7117
  });
6313
7118
  expectedSnapshot.markAsChecked();
@@ -6723,6 +7528,7 @@ function createExpect(test) {
6723
7528
  chai.util.addMethod(expect, "assertions", assertions);
6724
7529
  chai.util.addMethod(expect, "hasAssertions", hasAssertions);
6725
7530
  expect.extend(customMatchers);
7531
+ expect.extend(benchMatchers);
6726
7532
  return expect;
6727
7533
  }
6728
7534
  const globalExpect = createExpect();
@@ -6742,140 +7548,280 @@ function inject(key) {
6742
7548
  return getWorkerState().providedContext[key];
6743
7549
  }
6744
7550
 
6745
- const benchFns = /* @__PURE__ */ new WeakMap();
6746
- const benchOptsMap = /* @__PURE__ */ new WeakMap();
6747
- function getBenchOptions(key) {
6748
- return benchOptsMap.get(key);
6749
- }
6750
- function getBenchFn(key) {
6751
- return benchFns.get(key);
6752
- }
6753
- const bench = createBenchmark(function(name, fn = noop, options = {}) {
6754
- if (getWorkerState().config.mode !== "benchmark") throw new Error("`bench()` is only available in benchmark mode.");
6755
- const task = getCurrentSuite().task(formatName(name), {
6756
- ...this,
6757
- meta: { benchmark: true }
6758
- });
6759
- benchFns.set(task, fn);
6760
- benchOptsMap.set(task, options);
6761
- // vitest runner sets mode to `todo` if handler is not passed down
6762
- // but we store handler separately
6763
- if (!this.todo && task.mode === "todo") task.mode = "run";
6764
- });
6765
- function createBenchmark(fn) {
6766
- const benchmark = createChainable([
6767
- "skip",
6768
- "only",
6769
- "todo"
6770
- ], fn);
6771
- benchmark.skipIf = (condition) => condition ? benchmark.skip : benchmark;
6772
- benchmark.runIf = (condition) => condition ? benchmark : benchmark.skip;
6773
- return benchmark;
6774
- }
6775
- function formatName(name) {
6776
- return typeof name === "string" ? name : typeof name === "function" ? name.name || "<anonymous>" : String(name);
6777
- }
6778
-
6779
- function createBenchmarkResult(name) {
6780
- return {
7551
+ const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
7552
+ const kRegistration = Symbol("registration");
7553
+ const kFromSource = Symbol("fromSource");
7554
+ const kPerProject = Symbol("perProject");
7555
+ const kWriteResult = Symbol("writeResult");
7556
+ const kFinalize = Symbol("finalize");
7557
+ function isFromRegistration(reg) {
7558
+ return kFromSource in reg;
7559
+ }
7560
+ function substitutePath(template, projectName) {
7561
+ return template.replace(/\$\{projectName\}/g, projectName ?? "");
7562
+ }
7563
+ function createBench(test, config) {
7564
+ let benchIdx = 0;
7565
+ const pending = /* @__PURE__ */ new Set();
7566
+ const createTinybench = (options) => {
7567
+ const currentIndex = ++benchIdx;
7568
+ return new Bench({
7569
+ signal: test.context.signal,
7570
+ name: `${test.fullTestName} ${currentIndex}`,
7571
+ retainSamples: config.benchmark.retainSamples,
7572
+ ...options,
7573
+ now
7574
+ });
7575
+ };
7576
+ const resolveTemplate = (template) => substitutePath(template, config.benchmark.projectName);
7577
+ const resolveFromSource = async (source) => {
7578
+ if (typeof source === "function") return source();
7579
+ const resolved = resolveTemplate(source);
7580
+ const data = await rpc().readBenchmarkResult(resolved);
7581
+ if (data == null) throw new Error(`\`bench.from()\` could not find a result file at "${resolved}". Run the source benchmark first to create it.`);
7582
+ return data;
7583
+ };
7584
+ const taskFromBaseline = (name, data) => ({
6781
7585
  name,
7586
+ latency: data.latency,
7587
+ throughput: data.throughput,
7588
+ period: data.period,
7589
+ totalTime: data.totalTime,
6782
7590
  rank: 0,
6783
- rme: 0,
6784
- samples: []
7591
+ fromStore: true
7592
+ });
7593
+ const createCompareStorage = (bench, fromResults) => {
7594
+ return { get(name) {
7595
+ const stored = fromResults?.get(name);
7596
+ if (stored) return stored;
7597
+ const task = bench.getTask(name);
7598
+ if (!task) throw new Error(`task "${name}" was not defined`);
7599
+ return task.result;
7600
+ } };
6785
7601
  };
6786
- }
6787
- const benchmarkTasks = /* @__PURE__ */ new WeakMap();
6788
- async function runBenchmarkSuite(suite, runner) {
6789
- const { Task, Bench } = await runner.importTinybench();
6790
- const start = performance.now();
6791
- const benchmarkGroup = [];
6792
- const benchmarkSuiteGroup = [];
6793
- for (const task of suite.tasks) {
6794
- if (task.mode !== "run" && task.mode !== "queued") continue;
6795
- if (task.meta?.benchmark) benchmarkGroup.push(task);
6796
- else if (task.type === "suite") benchmarkSuiteGroup.push(task);
6797
- }
6798
- // run sub suites sequentially
6799
- for (const subSuite of benchmarkSuiteGroup) await runBenchmarkSuite(subSuite, runner);
6800
- if (benchmarkGroup.length) {
6801
- const defer = createDefer();
6802
- suite.result = {
6803
- state: "run",
6804
- startTime: start,
6805
- benchmark: createBenchmarkResult(suite.name)
7602
+ const serializeBenchmark = (tinybenchTasks, name, taskMeta, fromTasks) => {
7603
+ const tasks = tinybenchTasks.map((t) => {
7604
+ const result = t.result;
7605
+ if (result.state === "errored") throw result.error;
7606
+ if (result.state !== "completed") throw new Error(`task "${t.name}" did not complete: received "${result.state}"`);
7607
+ return {
7608
+ name: t.name,
7609
+ latency: result.latency,
7610
+ throughput: result.throughput,
7611
+ period: result.period,
7612
+ totalTime: result.totalTime,
7613
+ rank: 0,
7614
+ ...taskMeta?.get(t.name)
7615
+ };
7616
+ });
7617
+ if (fromTasks) tasks.push(...fromTasks);
7618
+ tasks.sort((a, b) => a.latency.mean - b.latency.mean);
7619
+ tasks.forEach((task, idx) => {
7620
+ task.rank = idx + 1;
7621
+ });
7622
+ return {
7623
+ name: name || test.fullTestName,
7624
+ tasks
7625
+ };
7626
+ };
7627
+ const recordBenchmark = async (tinybenchTasks, name, taskMeta, fromTasks) => {
7628
+ const serializedBenchmark = serializeBenchmark(tinybenchTasks, name, taskMeta, fromTasks);
7629
+ test.benchmarks.push(serializedBenchmark);
7630
+ await rpc().onTestBenchmark(test.id, serializedBenchmark);
7631
+ };
7632
+ const writeResultArtifact = async (template, result) => {
7633
+ const resolved = resolveTemplate(template);
7634
+ const data = {
7635
+ latency: result.latency,
7636
+ throughput: result.throughput,
7637
+ period: result.period,
7638
+ totalTime: result.totalTime
7639
+ };
7640
+ await rpc().writeBenchmarkResult(resolved, data);
7641
+ };
7642
+ const runBenchmarks = async (tinybench) => {
7643
+ const workerState = getWorkerState();
7644
+ const getterTracker = workerState.getterTracker;
7645
+ getterTracker?.resetInvocations();
7646
+ try {
7647
+ return await TestRunner.runBenchmarks(tinybench);
7648
+ } finally {
7649
+ const excessiveInvocations = config.benchmark.suppressExportGetterWarnings ? void 0 : getterTracker?.getExcessiveInvocations();
7650
+ if (excessiveInvocations?.length) {
7651
+ const entries = excessiveInvocations.map(({ moduleId, exportName }) => ` - ${formatModuleId(moduleId, workerState.config.root)} > ${exportName}`).join("\n");
7652
+ console.warn([
7653
+ c.yellow(c.bold("Benchmark Warning")),
7654
+ `Benchmark ${c.bold(`"${tinybench.name}"`)} accessed module export getters too many times.`,
7655
+ "",
7656
+ "This can make results unreliable because export getters add overhead.",
7657
+ "See https://vitest.dev/guide/benchmarking#module-runner-overhead",
7658
+ "",
7659
+ "Tracked exports:",
7660
+ entries
7661
+ ].join("\n"));
7662
+ }
7663
+ }
7664
+ };
7665
+ const runSingle = async (name, fn, fnOpts, options, meta, writeResult) => {
7666
+ const tinybench = createTinybench(options).add(name, fn, fnOpts);
7667
+ const tasks = await runBenchmarks(tinybench);
7668
+ const task = tinybench.getTask(name);
7669
+ if (task.result.state === "errored") throw task.result.error;
7670
+ await recordBenchmark(tasks, tinybench.name, meta ? new Map([[name, meta]]) : void 0);
7671
+ if (writeResult) await writeResultArtifact(writeResult, task.result);
7672
+ return task.result;
7673
+ };
7674
+ const runFrom = async (name, source) => {
7675
+ const data = await resolveFromSource(source);
7676
+ const benchmark = {
7677
+ name: test.fullTestName,
7678
+ tasks: [{
7679
+ ...taskFromBaseline(name, data),
7680
+ rank: 1
7681
+ }]
6806
7682
  };
6807
- updateTask$1("suite-prepare", suite);
6808
- const addBenchTaskListener = (task, benchmark) => {
6809
- task.addEventListener("complete", (e) => {
6810
- const taskRes = e.task.result;
6811
- const result = benchmark.result.benchmark;
6812
- benchmark.result.state = "pass";
6813
- Object.assign(result, taskRes);
6814
- // compute extra stats and free raw samples as early as possible
6815
- const samples = result.samples;
6816
- result.sampleCount = samples.length;
6817
- result.median = samples.length % 2 ? samples[Math.floor(samples.length / 2)] : (samples[samples.length / 2] + samples[samples.length / 2 - 1]) / 2;
6818
- if (!runner.config.benchmark?.includeSamples) result.samples.length = 0;
6819
- updateTask$1("test-finished", benchmark);
6820
- }, { once: true });
6821
- task.addEventListener("error", (e) => {
6822
- const task = e.task;
6823
- defer.reject(benchmark ? task.result.error : e);
6824
- }, { once: true });
7683
+ test.benchmarks.push(benchmark);
7684
+ await rpc().onTestBenchmark(test.id, benchmark);
7685
+ return data;
7686
+ };
7687
+ const bench = (nameOrFunction, a, b) => {
7688
+ validateBenchmarkProject(config);
7689
+ const { fn, fnOpts, writeResult, perProject } = normalizeBenchArgs(a, b);
7690
+ const name = typeof nameOrFunction === "function" ? nameOrFunction.name || "<anonymous>" : nameOrFunction;
7691
+ const meta = perProject ? { perProject: true } : void 0;
7692
+ const registration = {
7693
+ [kRegistration]: true,
7694
+ name,
7695
+ fn,
7696
+ fnOpts,
7697
+ run: (options) => {
7698
+ pending.delete(registration);
7699
+ return runSingle(name, fn, fnOpts, options, meta, writeResult);
7700
+ }
6825
7701
  };
6826
- benchmarkGroup.forEach((benchmark) => {
6827
- const benchmarkInstance = new Bench(getBenchOptions(benchmark));
6828
- const benchmarkFn = getBenchFn(benchmark);
6829
- benchmark.result = {
6830
- state: "run",
6831
- startTime: start,
6832
- benchmark: createBenchmarkResult(benchmark.name)
6833
- };
6834
- const task = new Task(benchmarkInstance, benchmark.name, benchmarkFn);
6835
- benchmarkTasks.set(benchmark, task);
6836
- addBenchTaskListener(task, benchmark);
7702
+ if (perProject) registration[kPerProject] = true;
7703
+ if (writeResult) registration[kWriteResult] = writeResult;
7704
+ pending.add(registration);
7705
+ return registration;
7706
+ };
7707
+ bench.from = (nameOrFunction, source) => {
7708
+ validateBenchmarkProject(config);
7709
+ if (typeof nameOrFunction !== "string" && typeof nameOrFunction !== "function") throw new TypeError("`bench.from()` requires a name (string or named function) as its first argument.");
7710
+ if (typeof source !== "string" && typeof source !== "function") throw new TypeError("`bench.from()` expects a string path or a function returning the result data as its second argument.");
7711
+ const name = typeof nameOrFunction === "function" ? nameOrFunction.name || "<anonymous>" : nameOrFunction;
7712
+ const registration = {
7713
+ [kRegistration]: true,
7714
+ [kFromSource]: source,
7715
+ name,
7716
+ run: () => {
7717
+ pending.delete(registration);
7718
+ return runFrom(name, source);
7719
+ }
7720
+ };
7721
+ pending.add(registration);
7722
+ return registration;
7723
+ };
7724
+ bench.compare = async (...args) => {
7725
+ validateBenchmarkProject(config);
7726
+ // extract optional trailing BenchCompareOptions argument
7727
+ const lastArg = args[args.length - 1];
7728
+ const benchOptions = lastArg != null && typeof lastArg === "object" && !(kRegistration in lastArg) ? args.pop() : void 0;
7729
+ const registrations = args;
7730
+ // Mark every passed-in registration as consumed before validation so a
7731
+ // throwing `bench.compare()` (wrong arity, wrong shape) doesn't also
7732
+ // trigger the unrun-bench warning — the user's intent was to consume them.
7733
+ for (const reg of registrations) if (reg != null && typeof reg === "object" && kRegistration in reg) pending.delete(reg);
7734
+ if (registrations.length < 2) throw new SyntaxError(`\`bench.compare()\` requires at least 2 benchmarks, received ${registrations.length} instead. ${registrations.length === 1 ? "Consider calling `bench().run()`. " : "Define benchmarks by calling `bench()`. "}See https://vitest.dev/guide/benchmarking#comparing-benchmarks`);
7735
+ for (const reg of registrations) if (reg == null || typeof reg !== "object" || !(kRegistration in reg)) throw new SyntaxError("`bench.compare()` expects every argument to be the return value of `bench` or `bench.from`.");
7736
+ const runnable = [];
7737
+ const fromEntries = [];
7738
+ for (const reg of registrations) if (isFromRegistration(reg)) fromEntries.push(reg);
7739
+ else runnable.push(reg);
7740
+ const taskMeta = /* @__PURE__ */ new Map();
7741
+ for (const reg of runnable) if (reg[kPerProject]) taskMeta.set(reg.name, { perProject: true });
7742
+ const fromResults = /* @__PURE__ */ new Map();
7743
+ const fromTasks = [];
7744
+ if (fromEntries.length > 0) {
7745
+ const resolved = await Promise.all(fromEntries.map(async (reg) => {
7746
+ return {
7747
+ reg,
7748
+ data: await resolveFromSource(reg[kFromSource])
7749
+ };
7750
+ }));
7751
+ for (const { reg, data } of resolved) {
7752
+ fromResults.set(reg.name, data);
7753
+ fromTasks.push(taskFromBaseline(reg.name, data));
7754
+ }
7755
+ }
7756
+ const tinybench = createTinybench(benchOptions);
7757
+ runnable.forEach((reg) => {
7758
+ tinybench.add(reg.name, reg.fn, reg.fnOpts);
6837
7759
  });
6838
- const { setTimeout } = getSafeTimers();
6839
- const tasks = [];
6840
- for (const benchmark of benchmarkGroup) {
6841
- const task = benchmarkTasks.get(benchmark);
6842
- updateTask$1("test-prepare", benchmark);
6843
- await task.warmup();
6844
- tasks.push([await new Promise((resolve) => setTimeout(async () => {
6845
- resolve(await task.run());
6846
- })), benchmark]);
7760
+ let tasks = [];
7761
+ if (runnable.length > 0) {
7762
+ tasks = await runBenchmarks(tinybench);
7763
+ const errors = tinybench.tasks.filter((task) => task.result.state === "errored").map((task) => task.result.error);
7764
+ if (errors.length > 0) throw new AggregateError(errors, "Some benchmarks failed");
6847
7765
  }
6848
- suite.result.duration = performance.now() - start;
6849
- suite.result.state = "pass";
6850
- updateTask$1("suite-finished", suite);
6851
- defer.resolve(null);
6852
- await defer;
6853
- }
6854
- function updateTask$1(event, task) {
6855
- updateTask(event, task, runner);
6856
- }
7766
+ await recordBenchmark(tasks, tinybench.name, taskMeta, fromTasks);
7767
+ // write artifacts for every runnable registration that requested it. We
7768
+ // do this after recording so a write failure can't be confused with a
7769
+ // benchmark failure in the reporter output.
7770
+ await Promise.all(runnable.filter((reg) => reg[kWriteResult] != null).map((reg) => {
7771
+ const task = tinybench.getTask(reg.name);
7772
+ return writeResultArtifact(reg[kWriteResult], task.result);
7773
+ }));
7774
+ return createCompareStorage(tinybench, fromResults);
7775
+ };
7776
+ bench[kFinalize] = () => {
7777
+ if (pending.size === 0) return;
7778
+ const names = [...pending].map((reg) => `"${reg.name}"`).join(", ");
7779
+ pending.clear();
7780
+ console.warn([
7781
+ c.yellow(c.bold("Benchmark Warning")),
7782
+ `Test ${c.bold(`"${test.fullTestName}"`)} registered benchmarks that never ran: ${names}.`,
7783
+ "",
7784
+ "Call `.run()` on the registration, or pass it to `bench.compare()`.",
7785
+ "See https://vitest.dev/guide/benchmarking#defining-a-benchmark"
7786
+ ].join("\n"));
7787
+ };
7788
+ return bench;
6857
7789
  }
6858
- class NodeBenchmarkRunner {
6859
- moduleRunner;
6860
- constructor(config) {
6861
- this.config = config;
6862
- }
6863
- async importTinybench() {
6864
- return await import('tinybench');
6865
- }
6866
- importFile(filepath, source) {
6867
- if (source === "setup") {
6868
- const moduleNode = getWorkerState().evaluatedModules.getModuleById(filepath);
6869
- if (moduleNode) getWorkerState().evaluatedModules.invalidateModule(moduleNode);
6870
- }
6871
- return this.moduleRunner.import(filepath);
6872
- }
6873
- async runSuite(suite) {
6874
- await runBenchmarkSuite(suite, this);
6875
- }
6876
- async runTask() {
6877
- throw new Error("`test()` and `it()` is only available in test mode.");
7790
+ function formatModuleId(moduleId, root) {
7791
+ if (!root || !isAbsolute(moduleId)) return moduleId;
7792
+ return relative(root, moduleId);
7793
+ }
7794
+ function normalizeBenchArgs(a, b) {
7795
+ if (typeof a === "function") {
7796
+ if (b !== void 0) throw new TypeError("`bench()` does not accept options as the third argument. Pass options as the second argument instead: `bench(name, options, fn)`.");
7797
+ return {
7798
+ fn: a,
7799
+ fnOpts: void 0,
7800
+ writeResult: void 0,
7801
+ perProject: false
7802
+ };
6878
7803
  }
7804
+ if (typeof b !== "function") throw new TypeError("`bench()` expects a benchmark function. Call `bench(name, fn)` or `bench(name, options, fn)`.");
7805
+ // Strip vitest-specific fields only when present so we don't allocate a new
7806
+ // object — preserving referential identity matters: users inspect
7807
+ // `registration.fnOpts` and tinybench's `add` sees the same object the
7808
+ // caller passed in.
7809
+ if (a.writeResult === void 0 && a.perProject === void 0) return {
7810
+ fn: b,
7811
+ fnOpts: a,
7812
+ writeResult: void 0,
7813
+ perProject: false
7814
+ };
7815
+ const { writeResult, perProject, ...fnOpts } = a;
7816
+ return {
7817
+ fn: b,
7818
+ fnOpts: Object.keys(fnOpts).length > 0 ? fnOpts : void 0,
7819
+ writeResult,
7820
+ perProject: perProject ?? false
7821
+ };
7822
+ }
7823
+ function validateBenchmarkProject(config) {
7824
+ if (!config.benchmark.enabled) throw new Error("Cannot use the `bench` test-context fixture within a regular test run. Benchmarks are inherently flaky, so Vitest runs them in a dedicated project based on the `benchmark.include` pattern (default `**/*.{bench,benchmark}.?(c|m)[jt]s?(x)`). Move this code to a file matched by `benchmark.include`, and make sure `bench` is destructured from the test context (`test('...', async ({ bench }) => { ... })`) — it is not a top-level export of `vitest`. See https://vitest.dev/guide/benchmarking#stability");
6879
7825
  }
6880
7826
 
6881
7827
  class TestRunner {
@@ -6884,7 +7830,11 @@ class TestRunner {
6884
7830
  moduleRunner;
6885
7831
  cancelRun = false;
6886
7832
  assertionsErrors = /* @__PURE__ */ new WeakMap();
7833
+ benchInstances = /* @__PURE__ */ new WeakMap();
6887
7834
  pool = this.workerState.ctx.pool;
7835
+ /**
7836
+ * @internal
7837
+ */
6888
7838
  _otel;
6889
7839
  viteEnvironment;
6890
7840
  viteModuleRunner;
@@ -6910,7 +7860,7 @@ class TestRunner {
6910
7860
  onCleanupWorkerContext(listener) {
6911
7861
  this.workerState.onCleanup(listener);
6912
7862
  }
6913
- onAfterRunFiles() {
7863
+ onAfterRunFiles(_files) {
6914
7864
  this.snapshotClient.clear();
6915
7865
  this.workerState.current = void 0;
6916
7866
  }
@@ -6957,7 +7907,7 @@ class TestRunner {
6957
7907
  if (suite.mode !== "skip" && "filepath" in suite) await this.snapshotClient.setup(suite.file.filepath, this.workerState.config.snapshotOptions);
6958
7908
  this.workerState.current = suite;
6959
7909
  }
6960
- onBeforeTryTask(test) {
7910
+ onBeforeTryTask(test, _options) {
6961
7911
  clearModuleMocks(this.config);
6962
7912
  this.snapshotClient.clearTest(test.file.filepath, test.id);
6963
7913
  setState({
@@ -6971,6 +7921,7 @@ class TestRunner {
6971
7921
  }, globalThis[GLOBAL_EXPECT]);
6972
7922
  }
6973
7923
  onAfterTryTask(test) {
7924
+ this.benchInstances.get(test)?.[kFinalize]();
6974
7925
  const { assertionCalls, expectedAssertionsNumber, expectedAssertionsNumberErrorGen, isExpectingAssertions, isExpectingAssertionsError } = test.context._local ? test.context.expect.getState() : getState(globalThis[GLOBAL_EXPECT]);
6975
7926
  if (expectedAssertionsNumber !== null && assertionCalls !== expectedAssertionsNumber) throw expectedAssertionsNumberErrorGen();
6976
7927
  if (isExpectingAssertions === true && assertionCalls === 0) throw isExpectingAssertionsError;
@@ -6987,6 +7938,16 @@ class TestRunner {
6987
7938
  Object.defineProperty(context, "_local", { get() {
6988
7939
  return _expect != null;
6989
7940
  } });
7941
+ let _bench;
7942
+ const runnerConfig = this.config;
7943
+ const benchInstances = this.benchInstances;
7944
+ Object.defineProperty(context, "bench", { get() {
7945
+ if (!_bench) {
7946
+ _bench = createBench(context.task, runnerConfig);
7947
+ benchInstances.set(context.task, _bench);
7948
+ }
7949
+ return _bench;
7950
+ } });
6990
7951
  return context;
6991
7952
  }
6992
7953
  getImportDurations() {
@@ -7021,13 +7982,13 @@ class TestRunner {
7021
7982
  static setTestFn = getFn;
7022
7983
  static matchesTags = matchesTags;
7023
7984
  /**
7024
- * @deprecated
7025
- */
7026
- static getBenchFn = getBenchFn;
7027
- /**
7028
- * @deprecated
7985
+ * @experimental
7986
+ * A function that runs tinybench tasks.
7987
+ * Can be overriden to run tasks in a special environment.
7029
7988
  */
7030
- static getBenchOptions = getBenchOptions;
7989
+ static async runBenchmarks(tinybench) {
7990
+ return await tinybench.run();
7991
+ }
7031
7992
  }
7032
7993
  function clearModuleMocks(config) {
7033
7994
  const { clearMocks, mockReset, restoreMocks, unstubEnvs, unstubGlobals } = config;
@@ -7042,7 +8003,6 @@ const assertType = function assertType() {};
7042
8003
 
7043
8004
  var index = /*#__PURE__*/Object.freeze({
7044
8005
  __proto__: null,
7045
- BenchmarkRunner: NodeBenchmarkRunner,
7046
8006
  EvaluatedModules: VitestEvaluatedModules,
7047
8007
  Snapshots: Snapshots,
7048
8008
  TestRunner: TestRunner,
@@ -7054,7 +8014,6 @@ var index = /*#__PURE__*/Object.freeze({
7054
8014
  assertType: assertType,
7055
8015
  beforeAll: beforeAll,
7056
8016
  beforeEach: beforeEach,
7057
- bench: bench,
7058
8017
  chai: chai,
7059
8018
  createExpect: createExpect,
7060
8019
  describe: describe,
@@ -7072,4 +8031,4 @@ var index = /*#__PURE__*/Object.freeze({
7072
8031
  vitest: vitest
7073
8032
  });
7074
8033
 
7075
- export { NodeBenchmarkRunner as N, Snapshots as S, TestRunner as T, assert as a, assertType as b, bench as c, createExpect as d, inject as e, vitest as f, globalExpect as g, index as i, should as s, vi as v };
8034
+ export { Snapshots as S, TestRunner as T, assert as a, assertType as b, createExpect as c, inject as d, vitest as e, globalExpect as g, index as i, should as s, vi as v };