vitest 5.0.0-beta.2 → 5.0.0-beta.3

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 (36) 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.Bay6B1Dz.js} +4 -4
  4. package/dist/chunks/{browser.d.BUhkKcDl.d.ts → browser.d.DM1g8UNp.d.ts} +2 -2
  5. package/dist/chunks/{cac.8N4bOkkB.js → cac.DoK9yX-i.js} +8 -6
  6. package/dist/chunks/{cli-api.B0RFke2g.js → cli-api.BCY9ylNq.js} +140 -68
  7. package/dist/chunks/{config.d.D91DHYaD.d.ts → config.d.C0UMwus7.d.ts} +86 -63
  8. package/dist/chunks/{defaults.szbHWQun.js → defaults.DVfzlTkU.js} +1 -1
  9. package/dist/chunks/{env.D4Lgay0q.js → env.BKKtU2WC.js} +2 -1
  10. package/dist/chunks/{global.d.DhbKSQoV.d.ts → global.d.DZbA5YnY.d.ts} +4 -2
  11. package/dist/chunks/{globals.EHmmu0nC.js → globals.8_qjZdeE.js} +1 -1
  12. package/dist/chunks/{index.CViWo__T.js → index.PuMGMNHF.js} +2 -2
  13. package/dist/chunks/{index.D_7-4CaB.js → index.ukHtlBbI.js} +1305 -555
  14. package/dist/chunks/{init-forks.DMge3WTt.js → init-forks.OoZmDo1g.js} +1 -1
  15. package/dist/chunks/{init-threads.eIoyCTon.js → init-threads.eSHAowcx.js} +1 -1
  16. package/dist/chunks/{init.BVd7SaCA.js → init.YjNsCb-_.js} +1 -1
  17. package/dist/chunks/{plugin.d.cIKZEZ16.d.ts → plugin.d.C00LxKL6.d.ts} +35 -9
  18. package/dist/chunks/{setup-common.Hpq30zVk.js → setup-common.eQsbxe88.js} +1 -1
  19. package/dist/chunks/{vm.2okbRRME.js → vm.BE_VOfSs.js} +1 -1
  20. package/dist/chunks/{worker.d.Bu1kXGw4.d.ts → worker.d.Dv3hDCFf.d.ts} +1 -1
  21. package/dist/cli.js +2 -2
  22. package/dist/config.d.ts +6 -7
  23. package/dist/config.js +2 -2
  24. package/dist/index.d.ts +8 -8
  25. package/dist/index.js +1 -1
  26. package/dist/module-evaluator.js +1 -1
  27. package/dist/node.d.ts +7 -8
  28. package/dist/node.js +5 -5
  29. package/dist/worker.d.ts +2 -2
  30. package/dist/worker.js +5 -5
  31. package/dist/workers/forks.js +6 -6
  32. package/dist/workers/runVmTests.js +3 -3
  33. package/dist/workers/threads.js +6 -6
  34. package/dist/workers/vmForks.js +3 -3
  35. package/dist/workers/vmThreads.js +3 -3
  36. package/package.json +19 -20
@@ -2529,12 +2529,12 @@ function requireFakeTimersSrc () {
2529
2529
  if (typeof __vitest_required__ !== 'undefined') {
2530
2530
  try {
2531
2531
  timersModule = __vitest_required__.timers;
2532
- } catch (e) {
2532
+ } catch {
2533
2533
  // ignored
2534
2534
  }
2535
2535
  try {
2536
2536
  timersPromisesModule = __vitest_required__.timersPromises;
2537
- } catch (e) {
2537
+ } catch {
2538
2538
  // ignored
2539
2539
  }
2540
2540
  }
@@ -2545,18 +2545,18 @@ function requireFakeTimersSrc () {
2545
2545
 
2546
2546
  /**
2547
2547
  * @typedef {object} NextAsyncTickMode
2548
- * @property {"nextAsync"} mode
2548
+ * @property {"nextAsync"} mode - runs timers one macrotask at a time
2549
2549
  */
2550
2550
 
2551
2551
  /**
2552
2552
  * @typedef {object} ManualTickMode
2553
- * @property {"manual"} mode
2553
+ * @property {"manual"} mode - advances only when the caller explicitly ticks
2554
2554
  */
2555
2555
 
2556
2556
  /**
2557
2557
  * @typedef {object} IntervalTickMode
2558
- * @property {"interval"} mode
2559
- * @property {number} [delta]
2558
+ * @property {"interval"} mode - advances automatically on a native interval
2559
+ * @property {number} [delta] - interval duration in milliseconds
2560
2560
  */
2561
2561
 
2562
2562
  /**
@@ -2564,134 +2564,447 @@ function requireFakeTimersSrc () {
2564
2564
  */
2565
2565
 
2566
2566
  /**
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
2567
+ * @callback FakeTimersFunction
2568
+ * @param {...unknown[]} args
2569
+ * @returns {unknown}
2570
2570
  */
2571
2571
 
2572
2572
  /**
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
2573
+ * @callback VoidVarArgsFunc
2574
+ * @param {...unknown[]} args - optional arguments to call the callback with
2575
+ * @returns {void}
2578
2576
  */
2579
2577
 
2580
2578
  /**
2581
2579
  * @callback NextTick
2582
2580
  * @param {VoidVarArgsFunc} callback - the callback to run
2583
- * @param {...*} args - optional arguments to call the callback with
2581
+ * @param {...unknown[]} args - optional arguments to call the callback with
2584
2582
  * @returns {void}
2585
2583
  */
2586
2584
 
2587
2585
  /**
2588
2586
  * @callback SetImmediate
2589
2587
  * @param {VoidVarArgsFunc} callback - the callback to run
2590
- * @param {...*} args - optional arguments to call the callback with
2588
+ * @param {...unknown[]} args - optional arguments to call the callback with
2591
2589
  * @returns {NodeImmediate}
2592
2590
  */
2593
2591
 
2594
2592
  /**
2595
- * @callback VoidVarArgsFunc
2596
- * @param {...*} callback - the callback to run
2593
+ * @callback SetTimeout
2594
+ * @param {VoidVarArgsFunc} callback - the callback to run
2595
+ * @param {number} [delay] - optional delay in milliseconds
2596
+ * @param {...unknown[]} args - optional arguments to call the callback with
2597
+ * @returns {TimerId} - the timeout identifier
2598
+ */
2599
+
2600
+ /**
2601
+ * @callback ClearTimeout
2602
+ * @param {TimerId} [id] - the timeout identifier to clear
2603
+ * @returns {void}
2604
+ */
2605
+
2606
+ /**
2607
+ * @callback SetInterval
2608
+ * @param {VoidVarArgsFunc} callback - the callback to run
2609
+ * @param {number} [delay] - optional delay in milliseconds
2610
+ * @param {...unknown[]} args - optional arguments to call the callback with
2611
+ * @returns {TimerId} - the interval identifier
2612
+ */
2613
+
2614
+ /**
2615
+ * @callback ClearInterval
2616
+ * @param {TimerId} [id] - the interval identifier to clear
2617
+ * @returns {void}
2618
+ */
2619
+
2620
+ /**
2621
+ * @callback QueueMicrotask
2622
+ * @param {VoidVarArgsFunc} callback - the callback to run
2623
+ * @returns {void}
2624
+ */
2625
+
2626
+ /**
2627
+ * @callback TimeRemaining
2628
+ * @returns {number}
2629
+ */
2630
+
2631
+ /**
2632
+ * @typedef {object} IdleDeadline
2633
+ * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout
2634
+ * @property {TimeRemaining} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period
2635
+ */
2636
+
2637
+ /**
2638
+ * @callback RequestIdleCallbackCallback
2639
+ * @param {IdleDeadline} deadline
2640
+ */
2641
+
2642
+ /**
2643
+ * Queues a function to be called during a browser's idle periods
2644
+ * @callback RequestIdleCallback
2645
+ * @param {RequestIdleCallbackCallback} callback
2646
+ * @param {{timeout?: number}} [options] - an options object
2647
+ * @returns {number} the id
2648
+ */
2649
+
2650
+ /**
2651
+ * @callback AnimationFrameCallback
2652
+ * @param {number} timestamp
2653
+ */
2654
+
2655
+ /**
2656
+ * @callback RequestAnimationFrame
2657
+ * @param {AnimationFrameCallback} callback
2658
+ * @returns {TimerId} - the request id
2659
+ */
2660
+
2661
+ /**
2662
+ * @callback CancelAnimationFrame
2663
+ * @param {TimerId} id - cancels a frame callback
2597
2664
  * @returns {void}
2598
2665
  */
2599
2666
 
2600
2667
  /**
2601
- * @typedef RequestAnimationFrame
2602
- * @property {function(number):void} requestAnimationFrame
2603
- * @returns {number} - the id
2668
+ * @callback CancelIdleCallback
2669
+ * @param {TimerId} id - cancels a scheduled idle callback
2670
+ * @returns {void}
2671
+ */
2672
+
2673
+ /**
2674
+ * @callback ClearImmediate
2675
+ * @param {NodeImmediate} id - faked `clearImmediate`
2676
+ * @returns {void}
2604
2677
  */
2605
2678
 
2606
2679
  /**
2607
- * @typedef Performance
2608
- * @property {function(): number} now
2680
+ * @callback CountTimers
2681
+ * @returns {number}
2682
+ */
2683
+
2684
+ /**
2685
+ * @callback RunMicrotasks
2686
+ * @returns {void}
2687
+ */
2688
+
2689
+ /**
2690
+ * @callback Tick
2691
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
2692
+ * @returns {number} will return the new `now` value
2693
+ */
2694
+
2695
+ /**
2696
+ * @callback TickAsync
2697
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
2698
+ * @returns {Promise<number>}
2699
+ */
2700
+
2701
+ /**
2702
+ * @callback Next
2703
+ * @returns {number}
2704
+ */
2705
+
2706
+ /**
2707
+ * @callback NextAsync
2708
+ * @returns {Promise<number>}
2709
+ */
2710
+
2711
+ /**
2712
+ * @callback RunAll
2713
+ * @returns {number}
2714
+ */
2715
+
2716
+ /**
2717
+ * @callback RunToFrame
2718
+ * @returns {number}
2719
+ */
2720
+
2721
+ /**
2722
+ * @callback RunAllAsync
2723
+ * @returns {Promise<number>}
2724
+ */
2725
+
2726
+ /**
2727
+ * @callback RunToLast
2728
+ * @returns {number}
2729
+ */
2730
+
2731
+ /**
2732
+ * @callback RunToLastAsync
2733
+ * @returns {Promise<number>}
2734
+ */
2735
+
2736
+ /**
2737
+ * @callback Reset
2738
+ * @returns {void}
2739
+ */
2740
+
2741
+ /**
2742
+ * @callback SetSystemTime
2743
+ * @param {number|Date} [now] initial mocked time, as milliseconds since epoch or a Date
2744
+ * @returns {void}
2745
+ */
2746
+
2747
+ /**
2748
+ * @callback Jump
2749
+ * @param {number|string} tickValue milliseconds or a human-readable value like "01:11:15"
2750
+ * @returns {number}
2751
+ */
2752
+
2753
+ /**
2754
+ * @callback Uninstall
2755
+ * @returns {void}
2756
+ */
2757
+
2758
+ /**
2759
+ * @callback SetTickMode
2760
+ * @param {SetTickModeConfig} tickModeConfig - The new configuration for how the clock should tick.
2761
+ * @returns {void}
2762
+ */
2763
+
2764
+ /**
2765
+ * @callback Hrtime
2766
+ * @param {Array<number>} [prev]
2767
+ * @returns {Array<number>}
2768
+ */
2769
+
2770
+ /**
2771
+ * @callback WithGlobal
2772
+ * @param {object} _global Namespace to mock (e.g. `window`)
2773
+ * @returns {FakeTimers}
2774
+ */
2775
+
2776
+ /**
2777
+ * @typedef {"setTimeout" | "clearTimeout" | "setImmediate" | "clearImmediate" | "setInterval" | "clearInterval" | "Date" | "nextTick" | "hrtime" | "requestAnimationFrame" | "cancelAnimationFrame" | "requestIdleCallback" | "cancelIdleCallback" | "performance" | "queueMicrotask"} FakeMethod
2778
+ */
2779
+
2780
+ /**
2781
+ * @typedef {number | NodeImmediate | Timer} TimerId
2782
+ */
2783
+
2784
+ /* eslint-disable jsdoc/reject-any-type */
2785
+ /**
2786
+ * @typedef {Record<string, any> & {
2787
+ * setTimeout?: SetTimeout,
2788
+ * clearTimeout?: ClearTimeout,
2789
+ * setInterval?: SetInterval,
2790
+ * clearInterval?: ClearInterval,
2791
+ * setImmediate?: SetImmediate,
2792
+ * clearImmediate?: ClearImmediate,
2793
+ * queueMicrotask?: QueueMicrotask,
2794
+ * requestAnimationFrame?: RequestAnimationFrame,
2795
+ * cancelAnimationFrame?: CancelAnimationFrame,
2796
+ * requestIdleCallback?: RequestIdleCallback,
2797
+ * cancelIdleCallback?: CancelIdleCallback,
2798
+ * process?: any,
2799
+ * performance?: any,
2800
+ * Performance?: any,
2801
+ * Intl?: any,
2802
+ * Promise?: typeof Promise,
2803
+ * Date: typeof Date & { isFake?: boolean, toSource?: () => string, clock?: any }
2804
+ * }} GlobalObject
2805
+ */
2806
+
2807
+ /**
2808
+ * @typedef {object} TimerHeap
2809
+ * @property {Timer[]} timers - the heap-ordered timers
2810
+ * @property {() => Timer | undefined} peek - returns the next timer without removing it
2811
+ * @property {(timer: Timer) => void} push - adds a timer to the heap
2812
+ * @property {() => Timer | undefined} pop - removes and returns the next timer
2813
+ * @property {(timer: Timer) => void} remove - removes a specific timer
2814
+ */
2815
+
2816
+ /**
2817
+ * @typedef {object} ClockTickMode
2818
+ * @property {TickMode} mode - active tick mode
2819
+ * @property {number} counter - increments whenever the mode changes
2820
+ * @property {number} [delta] - interval length in milliseconds
2821
+ */
2822
+
2823
+ /**
2824
+ * @typedef {object} SetTickModeConfig
2825
+ * @property {TickMode} mode - desired tick mode
2826
+ * @property {number} [delta] - interval length in milliseconds
2827
+ */
2828
+
2829
+ /**
2830
+ * @typedef {Record<string, any> & { clock: Clock }} IntlWithClock
2831
+ */
2832
+
2833
+ /**
2834
+ * @typedef {object} Timers
2835
+ * @property {SetTimeout} setTimeout - native `setTimeout`
2836
+ * @property {ClearTimeout} clearTimeout - native `clearTimeout`
2837
+ * @property {SetInterval} setInterval - native `setInterval`
2838
+ * @property {ClearInterval} clearInterval - native `clearInterval`
2839
+ * @property {typeof Date} Date - native `Date`
2840
+ * @property {typeof Intl} [Intl] - native `Intl`
2841
+ * @property {SetImmediate} [setImmediate] - native `setImmediate`, if available
2842
+ * @property {ClearImmediate} [clearImmediate] - native `clearImmediate`, if available
2843
+ * @property {Hrtime} [hrtime] - native `process.hrtime`, if available
2844
+ * @property {NextTick} [nextTick] - native `process.nextTick`, if available
2845
+ * @property {Performance} [performance] - native `performance`, if available
2846
+ * @property {RequestAnimationFrame} [requestAnimationFrame] - native `requestAnimationFrame`, if available
2847
+ * @property {QueueMicrotask} [queueMicrotask] - whether `queueMicrotask` exists
2848
+ * @property {CancelAnimationFrame} [cancelAnimationFrame] - native `cancelAnimationFrame`, if available
2849
+ * @property {RequestIdleCallback} [requestIdleCallback] - native `requestIdleCallback`, if available
2850
+ * @property {CancelIdleCallback} [cancelIdleCallback] - native `cancelIdleCallback`, if available
2851
+ */
2852
+
2853
+ /**
2854
+ * @typedef {object} ClockState
2855
+ * @property {number} tickFrom - lower bound of the current tick range
2856
+ * @property {number} tickTo - upper bound of the current tick range
2857
+ * @property {number} [previous] - previous timer time used during ticking
2858
+ * @property {number | null} [oldNow] - previous value of `now`
2859
+ * @property {Timer} [timer] - timer currently being processed
2860
+ * @property {unknown} [firstException] - first exception raised while processing timers
2861
+ * @property {number} [nanosTotal] - accumulated nanoseconds from fractional ticks
2862
+ * @property {number} [msFloat] - accumulated fractional milliseconds
2863
+ * @property {number} [ms] - accumulated whole milliseconds
2864
+ */
2865
+
2866
+ /**
2867
+ * @typedef {object} TimerInitialProps
2868
+ * @property {VoidVarArgsFunc} func - callback or string to execute
2869
+ * @property {unknown[]} [args] - arguments passed to the callback
2870
+ * @property {'Timeout' | 'Interval' | 'Immediate' | 'AnimationFrame' | 'IdleCallback'} [type] - timer kind
2871
+ * @property {number} [delay] - requested delay in milliseconds
2872
+ * @property {number} [callAt] - scheduled execution time
2873
+ * @property {number} [createdAt] - time at which the timer was created
2874
+ * @property {boolean} [immediate] - whether this timer should run before non-immediate timers at the same time
2875
+ * @property {number} [id] - unique timer identifier
2876
+ * @property {Error} [error] - captured stack for loop diagnostics
2877
+ * @property {number} [interval] - interval for repeated timers
2878
+ * @property {boolean} [animation] - whether this is an animation frame timer
2879
+ * @property {boolean} [requestIdleCallback] - whether this is an idle callback timer
2880
+ * @property {number} [order] - execution order for timers at the same time
2881
+ * @property {number} [heapIndex] - index in the timer heap
2882
+ */
2883
+
2884
+ /**
2885
+ * @callback CreateClockCallback
2886
+ * @param {number|Date} [start] initial mocked time, as milliseconds since epoch or a Date
2887
+ * @param {number} [loopLimit] maximum number of timers run before aborting with an infinite-loop error
2888
+ * @returns {Clock}
2889
+ */
2890
+
2891
+ /**
2892
+ * @callback InstallCallback
2893
+ * @param {Config} [config] Optional config
2894
+ * @returns {Clock}
2895
+ */
2896
+
2897
+ /**
2898
+ * @typedef {object} FakeTimers
2899
+ * @property {Timers} timers - the native timer APIs saved for later restoration
2900
+ * @property {CreateClockCallback} createClock - creates a new fake clock
2901
+ * @property {InstallCallback} install - installs the fake timers onto the default global object
2902
+ * @property {WithGlobal} withGlobal - creates a fake-timers instance for a provided global object
2609
2903
  */
2610
2904
 
2611
- /* eslint-disable jsdoc/require-property-description */
2612
2905
  /**
2613
2906
  * @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
2907
+ * @property {number} now - current mocked time in milliseconds
2908
+ * @property {typeof Date & {clock?: Clock, isFake?: boolean, toSource?: () => string}} Date - fake Date constructor bound to this clock
2909
+ * @property {number} loopLimit - maximum number of timers before assuming an infinite loop
2910
+ * @property {RequestIdleCallback} requestIdleCallback - schedules an idle callback
2911
+ * @property {CancelIdleCallback} cancelIdleCallback - cancels a scheduled idle callback
2912
+ * @property {SetTimeout} setTimeout - faked `setTimeout`
2913
+ * @property {ClearTimeout} clearTimeout - faked `clearTimeout`
2914
+ * @property {NextTick} nextTick - faked `process.nextTick`
2915
+ * @property {QueueMicrotask} queueMicrotask - faked `queueMicrotask`
2916
+ * @property {SetInterval} setInterval - faked `setInterval`
2917
+ * @property {ClearInterval} clearInterval - faked `clearInterval`
2918
+ * @property {SetImmediate} setImmediate - faked `setImmediate`
2919
+ * @property {ClearImmediate} clearImmediate - faked `clearImmediate`
2920
+ * @property {CountTimers} countTimers - counts scheduled timers
2921
+ * @property {RequestAnimationFrame} requestAnimationFrame - schedules a frame callback
2922
+ * @property {CancelAnimationFrame} cancelAnimationFrame - cancels a frame callback
2923
+ * @property {RunMicrotasks} runMicrotasks - drains microtasks
2924
+ * @property {Tick} tick - advances fake time synchronously
2925
+ * @property {TickAsync} tickAsync - advances fake time asynchronously
2926
+ * @property {Next} next - runs the next scheduled timer
2927
+ * @property {NextAsync} nextAsync - runs the next scheduled timer asynchronously
2928
+ * @property {RunAll} runAll - runs all scheduled timers
2929
+ * @property {RunToFrame} runToFrame - runs timers up to the next animation frame
2930
+ * @property {RunAllAsync} runAllAsync - runs all scheduled timers asynchronously
2931
+ * @property {RunToLast} runToLast - runs timers up to the last scheduled timer
2932
+ * @property {RunToLastAsync} runToLastAsync - runs timers up to the last scheduled timer asynchronously
2933
+ * @property {Reset} reset - clears all timers and resets the clock
2934
+ * @property {SetSystemTime} setSystemTime - sets the clock to a specific wall-clock time
2935
+ * @property {Jump} jump - advances time and returns the new `now`
2936
+ * @property {any} performance - fake performance object
2937
+ * @property {Hrtime} hrtime - faked `process.hrtime`
2938
+ * @property {Uninstall} uninstall - restores native timers
2939
+ * @property {string[]} methods - names of faked methods
2940
+ * @property {boolean} [shouldClearNativeTimers] - inherited from config
2941
+ * @property {{methodName:string, original:unknown}[] | undefined} timersModuleMethods - saved Node timers module methods
2942
+ * @property {{methodName:string, original:unknown}[] | undefined} timersPromisesModuleMethods - saved Node timers/promises methods
2943
+ * @property {Map<VoidVarArgsFunc, AbortSignal>} abortListenerMap - active abort listeners
2944
+ * @property {SetTickMode} setTickMode - switches the auto-tick mode
2945
+ * @property {Map<number, Timer>} [timers] - internal timer storage
2946
+ * @property {TimerHeap} [timerHeap] - internal timer heap
2947
+ * @property {boolean} [duringTick] - internal flag
2948
+ * @property {boolean} isNearInfiniteLimit - internal flag indicating the loop limit is nearly reached
2949
+ * @property {TimerId} [attachedInterval] - internal flag
2950
+ * @property {ClockTickMode} [tickMode] - internal flag
2951
+ * @property {Timer[]} [jobs] - internal flag
2952
+ * @property {IntlWithClock} [Intl] - fake Intl object
2652
2953
  */
2653
- /* eslint-enable jsdoc/require-property-description */
2954
+ /* eslint-enable jsdoc/reject-any-type */
2654
2955
 
2655
2956
  /**
2656
2957
  * Configuration object for the `install` method.
2657
2958
  * @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
2959
+ * @property {number|Date} [now] initial mocked time, as milliseconds since epoch or a Date
2960
+ * @property {FakeMethod[]} [toFake] method names that should be faked
2961
+ * @property {FakeMethod[]} [toNotFake] method names that should remain native
2962
+ * @property {number} [loopLimit] maximum number of timers run before aborting with an infinite-loop error
2963
+ * @property {boolean} [shouldAdvanceTime] automatically increments mocked time while the clock is installed
2964
+ * @property {number} [advanceTimeDelta] interval in milliseconds used when `shouldAdvanceTime` is enabled
2965
+ * @property {boolean} [shouldClearNativeTimers] forwards clear calls to native methods when the timer is not fake
2966
+ * @property {boolean} [ignoreMissingTimers] suppresses errors when a requested timer is missing from the global object
2967
+ * @property {GlobalObject} [target] global object to install onto
2665
2968
  */
2666
2969
 
2667
- /* eslint-disable jsdoc/require-property-description */
2668
2970
  /**
2669
2971
  * 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]
2972
+ * @typedef {TimerInitialProps} Timer
2973
+ * @property {unknown[]} args - arguments passed to the callback
2974
+ * @property {number} callAt - scheduled execution time
2975
+ * @property {number} createdAt - time at which the timer was created
2976
+ * @property {number} id - unique timer identifier
2977
+ * @property {'Timeout' | 'Interval' | 'Immediate' | 'AnimationFrame' | 'IdleCallback'} type - timer kind
2978
+ */
2979
+
2980
+ /**
2981
+ * @callback NodeImmediateHasRef
2982
+ * @returns {boolean}
2983
+ */
2984
+
2985
+ /**
2986
+ * @callback NodeImmediateRef
2987
+ * @returns {NodeImmediate}
2988
+ */
2989
+
2990
+ /**
2991
+ * @callback NodeImmediateUnref
2992
+ * @returns {NodeImmediate}
2679
2993
  */
2680
2994
 
2681
2995
  /**
2682
2996
  * A Node timer
2683
2997
  * @typedef {object} NodeImmediate
2684
- * @property {function(): boolean} hasRef
2685
- * @property {function(): NodeImmediate} ref
2686
- * @property {function(): NodeImmediate} unref
2998
+ * @property {NodeImmediateHasRef} hasRef - reports whether the timer keeps the event loop alive
2999
+ * @property {NodeImmediateRef} ref - marks the timer as referenced
3000
+ * @property {NodeImmediateUnref} unref - marks the timer as unreferenced
2687
3001
  */
2688
- /* eslint-enable jsdoc/require-property-description */
2689
3002
 
2690
3003
  /* eslint-disable complexity */
2691
3004
 
2692
3005
  /**
2693
3006
  * Mocks available features in the specified global namespace.
2694
- * @param {*} _global Namespace to mock (e.g. `window`)
3007
+ * @param {GlobalObject} _global Namespace to mock (e.g. `window`)
2695
3008
  * @returns {FakeTimers}
2696
3009
  */
2697
3010
  function withGlobal(_global) {
@@ -2731,7 +3044,10 @@ function requireFakeTimersSrc () {
2731
3044
  _global.performance &&
2732
3045
  _global.performance.constructor &&
2733
3046
  _global.performance.constructor.prototype;
2734
- isPresent.queueMicrotask = _global.hasOwnProperty("queueMicrotask");
3047
+ isPresent.queueMicrotask = Object.prototype.hasOwnProperty.call(
3048
+ _global,
3049
+ "queueMicrotask",
3050
+ );
2735
3051
  isPresent.requestAnimationFrame =
2736
3052
  _global.requestAnimationFrame &&
2737
3053
  typeof _global.requestAnimationFrame === "function";
@@ -2741,7 +3057,7 @@ function requireFakeTimersSrc () {
2741
3057
  isPresent.requestIdleCallback =
2742
3058
  _global.requestIdleCallback &&
2743
3059
  typeof _global.requestIdleCallback === "function";
2744
- isPresent.cancelIdleCallbackPresent =
3060
+ isPresent.cancelIdleCallback =
2745
3061
  _global.cancelIdleCallback &&
2746
3062
  typeof _global.cancelIdleCallback === "function";
2747
3063
  isPresent.setImmediate =
@@ -2762,6 +3078,8 @@ function requireFakeTimersSrc () {
2762
3078
  )
2763
3079
  : undefined;
2764
3080
  let uniqueTimerId = idCounterStart;
3081
+ /** @type {number} */
3082
+ let uniqueTimerOrder = 0;
2765
3083
 
2766
3084
  if (NativeDate === undefined) {
2767
3085
  throw new Error(
@@ -2802,23 +3120,23 @@ function requireFakeTimersSrc () {
2802
3120
  return isFinite(num);
2803
3121
  }
2804
3122
 
2805
- let isNearInfiniteLimit = false;
2806
-
2807
3123
  /**
2808
3124
  * @param {Clock} clock
2809
3125
  * @param {number} i
2810
3126
  */
2811
3127
  function checkIsNearInfiniteLimit(clock, i) {
2812
3128
  if (clock.loopLimit && i === clock.loopLimit - 1) {
2813
- isNearInfiniteLimit = true;
3129
+ clock.isNearInfiniteLimit = true;
2814
3130
  }
2815
3131
  }
2816
3132
 
2817
3133
  /**
2818
- *
3134
+ * @param {Clock} clock
2819
3135
  */
2820
- function resetIsNearInfiniteLimit() {
2821
- isNearInfiniteLimit = false;
3136
+ function resetIsNearInfiniteLimit(clock) {
3137
+ if (clock) {
3138
+ clock.isNearInfiniteLimit = false;
3139
+ }
2822
3140
  }
2823
3141
 
2824
3142
  /**
@@ -2883,12 +3201,12 @@ function requireFakeTimersSrc () {
2883
3201
  if (!epoch) {
2884
3202
  return 0;
2885
3203
  }
2886
- if (typeof epoch.getTime === "function") {
2887
- return epoch.getTime();
2888
- }
2889
3204
  if (typeof epoch === "number") {
2890
3205
  return epoch;
2891
3206
  }
3207
+ if (typeof epoch.getTime === "function") {
3208
+ return epoch.getTime();
3209
+ }
2892
3210
  throw new TypeError("now should be milliseconds since UNIX epoch");
2893
3211
  }
2894
3212
 
@@ -2905,6 +3223,7 @@ function requireFakeTimersSrc () {
2905
3223
  /**
2906
3224
  * @param {Clock} clock
2907
3225
  * @param {Timer} job
3226
+ * @returns {Error}
2908
3227
  */
2909
3228
  function getInfiniteLoopError(clock, job) {
2910
3229
  const infiniteLoopError = new Error(
@@ -2964,34 +3283,29 @@ function requireFakeTimersSrc () {
2964
3283
  Object.defineProperty(infiniteLoopError, "stack", {
2965
3284
  value: stack,
2966
3285
  });
2967
- } catch (e) {
3286
+ } catch {
2968
3287
  // noop
2969
3288
  }
2970
3289
 
2971
3290
  return infiniteLoopError;
2972
3291
  }
2973
3292
 
2974
- //eslint-disable-next-line jsdoc/require-jsdoc
3293
+ /**
3294
+ * @returns {typeof Date & { clock: Clock }}
3295
+ */
2975
3296
  function createDate() {
2976
3297
  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) {
3298
+ /** @type {Clock} */
3299
+ static clock;
3300
+
3301
+ constructor(...args) {
3302
+ // Preserve fake time when Date is called without arguments.
3303
+ if (args.length === 0) {
2992
3304
  super(ClockDate.clock.now);
2993
3305
  } else {
2994
- super(...arguments);
3306
+ // The subclass is intentionally thin for explicit args.
3307
+ // @ts-expect-error Date constructor overloads are intentionally dynamic.
3308
+ super(...args);
2995
3309
  }
2996
3310
 
2997
3311
  // ensures identity checks using the constructor prop still works
@@ -3015,21 +3329,26 @@ function requireFakeTimersSrc () {
3015
3329
  };
3016
3330
  }
3017
3331
 
3018
- if (NativeDate.toSource) {
3332
+ const NativeDateWithToSource =
3333
+ /** @type {typeof Date & { toSource?: () => string }} */ (
3334
+ NativeDate
3335
+ );
3336
+
3337
+ if (NativeDateWithToSource.toSource) {
3019
3338
  ClockDate.toSource = function toSource() {
3020
- return NativeDate.toSource();
3339
+ return NativeDateWithToSource.toSource();
3021
3340
  };
3022
3341
  }
3023
3342
 
3024
3343
  ClockDate.toString = function toString() {
3025
- return NativeDate.toString();
3344
+ return NativeDateWithToSource.toString();
3026
3345
  };
3027
3346
 
3028
3347
  // noinspection UnnecessaryLocalVariableJS
3029
3348
  /**
3030
3349
  * A normal Class constructor cannot be called without `new`, but Date can, so we need
3031
3350
  * to wrap it in a Proxy in order to ensure this functionality of Date is kept intact
3032
- * @type {ClockDate}
3351
+ * @type {typeof ClockDate}
3033
3352
  */
3034
3353
  const ClockDateProxy = new Proxy(ClockDate, {
3035
3354
  // handler for [[Call]] invocations (i.e. not using `new`)
@@ -3046,7 +3365,9 @@ function requireFakeTimersSrc () {
3046
3365
  },
3047
3366
  });
3048
3367
 
3049
- return ClockDateProxy;
3368
+ return /** @type {typeof Date & { clock: Clock }} */ (
3369
+ /** @type {unknown} */ (ClockDateProxy)
3370
+ );
3050
3371
  }
3051
3372
 
3052
3373
  /**
@@ -3055,19 +3376,21 @@ function requireFakeTimersSrc () {
3055
3376
  * Most of the properties are the original native ones,
3056
3377
  * but we need to take control of those that have a
3057
3378
  * dependency on the current clock.
3058
- * @returns {object} the partly fake Intl implementation
3379
+ * @param {Clock} clock
3380
+ * @returns {IntlWithClock} the partly fake Intl implementation
3059
3381
  */
3060
- function createIntl() {
3061
- const ClockIntl = {};
3382
+ function createIntl(clock) {
3383
+ /** @type {IntlWithClock} */
3384
+ const IntlWithClock = { clock: clock };
3062
3385
  /*
3063
3386
  * All properties of Intl are non-enumerable, so we need
3064
3387
  * to do a bit of work to get them out.
3065
3388
  */
3066
3389
  Object.getOwnPropertyNames(NativeIntl).forEach(
3067
- (property) => (ClockIntl[property] = NativeIntl[property]),
3390
+ (property) => (IntlWithClock[property] = NativeIntl[property]),
3068
3391
  );
3069
3392
 
3070
- ClockIntl.DateTimeFormat = function (...args) {
3393
+ IntlWithClock.DateTimeFormat = function (...args) {
3071
3394
  const realFormatter = new NativeIntl.DateTimeFormat(...args);
3072
3395
  const formatter = {};
3073
3396
 
@@ -3080,21 +3403,23 @@ function requireFakeTimersSrc () {
3080
3403
 
3081
3404
  ["format", "formatToParts"].forEach((method) => {
3082
3405
  formatter[method] = function (date) {
3083
- return realFormatter[method](date || ClockIntl.clock.now);
3406
+ return realFormatter[method](
3407
+ date || IntlWithClock.clock.now,
3408
+ );
3084
3409
  };
3085
3410
  });
3086
3411
 
3087
3412
  return formatter;
3088
3413
  };
3089
3414
 
3090
- ClockIntl.DateTimeFormat.prototype = Object.create(
3415
+ IntlWithClock.DateTimeFormat.prototype = Object.create(
3091
3416
  NativeIntl.DateTimeFormat.prototype,
3092
3417
  );
3093
3418
 
3094
- ClockIntl.DateTimeFormat.supportedLocalesOf =
3419
+ IntlWithClock.DateTimeFormat.supportedLocalesOf =
3095
3420
  NativeIntl.DateTimeFormat.supportedLocalesOf;
3096
3421
 
3097
- return ClockIntl;
3422
+ return IntlWithClock;
3098
3423
  }
3099
3424
 
3100
3425
  //eslint-disable-next-line jsdoc/require-jsdoc
@@ -3112,6 +3437,7 @@ function requireFakeTimersSrc () {
3112
3437
  if (!clock.jobs) {
3113
3438
  return;
3114
3439
  }
3440
+ const wasNearLimit = clock.isNearInfiniteLimit;
3115
3441
  for (let i = 0; i < clock.jobs.length; i++) {
3116
3442
  const job = clock.jobs[i];
3117
3443
  job.func.apply(null, job.args);
@@ -3121,38 +3447,275 @@ function requireFakeTimersSrc () {
3121
3447
  throw getInfiniteLoopError(clock, job);
3122
3448
  }
3123
3449
  }
3124
- resetIsNearInfiniteLimit();
3450
+ if (!wasNearLimit) {
3451
+ resetIsNearInfiniteLimit(clock);
3452
+ }
3125
3453
  clock.jobs = [];
3126
3454
  }
3127
3455
 
3456
+ /**
3457
+ * A compact "soonest timer first" container.
3458
+ *
3459
+ * Think of this as a waiting room for scheduled callbacks where the next
3460
+ * callback to run is always kept at the front of the list. The internal
3461
+ * array is arranged so we can find, add, remove, and reorder timers
3462
+ * efficiently without sorting the whole list every time something changes.
3463
+ *
3464
+ * The important idea is not the data structure name, but the behavior:
3465
+ * the timer that should run next stays near the front, and when one timer
3466
+ * moves, the rest are shifted just enough to keep that promise true.
3467
+ */
3468
+ class TimerHeap {
3469
+ constructor() {
3470
+ this.timers = [];
3471
+ }
3472
+
3473
+ /**
3474
+ * Look at the next timer without removing it.
3475
+ * This is the timer the clock would run first if time advanced now.
3476
+ * @returns {Timer}
3477
+ */
3478
+ peek() {
3479
+ return this.timers[0];
3480
+ }
3481
+
3482
+ /**
3483
+ * Add a timer to the waiting room, then move it upward until it is in
3484
+ * the right place relative to the timers it should run before and after.
3485
+ * @param {Timer} timer
3486
+ */
3487
+ push(timer) {
3488
+ this.timers.push(timer);
3489
+ this.bubbleUp(this.timers.length - 1);
3490
+ }
3491
+
3492
+ /**
3493
+ * Remove and return the next timer to run.
3494
+ *
3495
+ * We pull the front timer out, move the last timer into the empty spot,
3496
+ * and then shift that replacement down until the ordering is correct
3497
+ * again. That avoids rebuilding the whole list from scratch.
3498
+ * @returns {Timer|undefined}
3499
+ */
3500
+ pop() {
3501
+ if (this.timers.length === 0) {
3502
+ return undefined;
3503
+ }
3504
+ const first = this.timers[0];
3505
+ const last = this.timers.pop();
3506
+ if (this.timers.length > 0) {
3507
+ this.timers[0] = last;
3508
+ last.heapIndex = 0;
3509
+ this.bubbleDown(0);
3510
+ }
3511
+ delete first.heapIndex;
3512
+ return first;
3513
+ }
3514
+
3515
+ /**
3516
+ * Remove a specific timer from the waiting room.
3517
+ *
3518
+ * The heap stores timers in a shape that lets us jump directly to the
3519
+ * timer's current position, replace it with the last timer, and then
3520
+ * move that replacement up or down until the ordering is correct again.
3521
+ * @param {Timer} timer
3522
+ * @returns {boolean}
3523
+ */
3524
+ remove(timer) {
3525
+ const index = timer.heapIndex;
3526
+ if (index === undefined || this.timers[index] !== timer) {
3527
+ return false;
3528
+ }
3529
+ const last = this.timers.pop();
3530
+ if (timer !== last) {
3531
+ this.timers[index] = last;
3532
+ last.heapIndex = index;
3533
+ if (compareTimers(last, timer) < 0) {
3534
+ this.bubbleUp(index);
3535
+ } else {
3536
+ this.bubbleDown(index);
3537
+ }
3538
+ }
3539
+ delete timer.heapIndex;
3540
+ return true;
3541
+ }
3542
+
3543
+ /**
3544
+ * Move a timer toward the front until it is no longer "earlier" than
3545
+ * the timer above it.
3546
+ *
3547
+ * Conceptually, this is what happens when something newly scheduled
3548
+ * turns out to belong ahead of its parent in the waiting room. We keep
3549
+ * swapping it upward until it is no longer out of place.
3550
+ * @param {number} index
3551
+ */
3552
+ bubbleUp(index) {
3553
+ const timer = this.timers[index];
3554
+ let currentIndex = index;
3555
+ while (currentIndex > 0) {
3556
+ const parentIndex = Math.floor((currentIndex - 1) / 2);
3557
+ const parent = this.timers[parentIndex];
3558
+ if (compareTimers(timer, parent) < 0) {
3559
+ this.timers[currentIndex] = parent;
3560
+ parent.heapIndex = currentIndex;
3561
+ currentIndex = parentIndex;
3562
+ } else {
3563
+ break;
3564
+ }
3565
+ }
3566
+ this.timers[currentIndex] = timer;
3567
+ timer.heapIndex = currentIndex;
3568
+ }
3569
+
3570
+ /**
3571
+ * Move a timer away from the front until the timer below it is no
3572
+ * longer supposed to run after it.
3573
+ *
3574
+ * This is the opposite of `bubbleUp`: when a timer at the front is
3575
+ * removed or moved, the replacement may be too far ahead, so we
3576
+ * repeatedly swap it downward with the best child until the waiting
3577
+ * room is ordered again.
3578
+ * @param {number} index
3579
+ */
3580
+ bubbleDown(index) {
3581
+ const timer = this.timers[index];
3582
+ let currentIndex = index;
3583
+ const halfLength = Math.floor(this.timers.length / 2);
3584
+ while (currentIndex < halfLength) {
3585
+ const leftIndex = currentIndex * 2 + 1;
3586
+ const rightIndex = leftIndex + 1;
3587
+ let bestChildIndex = leftIndex;
3588
+ let bestChild = this.timers[leftIndex];
3589
+
3590
+ if (
3591
+ rightIndex < this.timers.length &&
3592
+ compareTimers(this.timers[rightIndex], bestChild) < 0
3593
+ ) {
3594
+ bestChildIndex = rightIndex;
3595
+ bestChild = this.timers[rightIndex];
3596
+ }
3597
+
3598
+ if (compareTimers(bestChild, timer) < 0) {
3599
+ this.timers[currentIndex] = bestChild;
3600
+ bestChild.heapIndex = currentIndex;
3601
+ currentIndex = bestChildIndex;
3602
+ } else {
3603
+ break;
3604
+ }
3605
+ }
3606
+ this.timers[currentIndex] = timer;
3607
+ timer.heapIndex = currentIndex;
3608
+ }
3609
+ }
3610
+
3611
+ /**
3612
+ * Ensure timer storage and heap stay in sync even if a clear path touches
3613
+ * timer state before anything has been scheduled.
3614
+ *
3615
+ * Why do we need two data structures to keep tabs on timers?
3616
+ * 1. Fast ID Lookup (clock.timers): This is a Map from timer IDs to their respective timer objects. It allows clearTimeout(id) and
3617
+ * clearInterval(id) to be $O(1)$ operations. Without this map, finding a specific timer in the heap to remove it would require a linear
3618
+ * $O(n)$ search, which would significantly degrade performance as the number of active timers grows.
3619
+ * 2. Efficient Scheduling (clock.timerHeap): This is a priority queue (min-heap) that keeps timers ordered by their execution time (callAt). It
3620
+ * 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
3621
+ * removed ($O(\log n)$).
3622
+ *
3623
+ * In short: clock.timers provides fast access by ID, while clock.timerHeap provides fast access by Time. Removing either one would make common
3624
+ * operations (like clearing or finding the next timer) much slower.
3625
+ * @param {Clock} clock
3626
+ */
3627
+ function ensureTimerState(clock) {
3628
+ if (!clock.timers) {
3629
+ clock.timers = new Map();
3630
+ clock.timerHeap = new TimerHeap();
3631
+ }
3632
+ }
3633
+
3634
+ /**
3635
+ * @param {Clock} clock
3636
+ * @param {number} id
3637
+ * @returns {boolean}
3638
+ */
3639
+ function hasTimer(clock, id) {
3640
+ return clock.timers ? clock.timers.has(id) : false;
3641
+ }
3642
+
3643
+ /**
3644
+ * @param {Clock} clock
3645
+ * @param {number} id
3646
+ * @returns {Timer}
3647
+ */
3648
+ function getTimer(clock, id) {
3649
+ return clock.timers ? clock.timers.get(id) : undefined;
3650
+ }
3651
+
3128
3652
  /**
3129
3653
  * @param {Clock} clock
3130
3654
  * @param {Timer} timer
3131
- * @returns {number} id of the created timer
3655
+ */
3656
+ function setTimer(clock, timer) {
3657
+ ensureTimerState(clock);
3658
+ clock.timers.set(timer.id, timer);
3659
+ }
3660
+
3661
+ /**
3662
+ * @param {Clock} clock
3663
+ * @param {number} id
3664
+ * @returns {boolean}
3665
+ */
3666
+ function deleteTimer(clock, id) {
3667
+ return clock.timers ? clock.timers.delete(id) : false;
3668
+ }
3669
+
3670
+ /**
3671
+ * @param {Clock} clock
3672
+ * @param {(timer: Timer) => void} callback
3673
+ */
3674
+ function forEachActiveTimer(clock, callback) {
3675
+ if (!clock.timers) {
3676
+ return;
3677
+ }
3678
+
3679
+ for (const timer of clock.timers.values()) {
3680
+ callback(timer);
3681
+ }
3682
+ }
3683
+
3684
+ /**
3685
+ * @param {Clock} clock
3686
+ */
3687
+ function rebuildTimerHeap(clock) {
3688
+ clock.timerHeap = new TimerHeap();
3689
+ forEachActiveTimer(clock, (timer) => {
3690
+ clock.timerHeap.push(timer);
3691
+ });
3692
+ }
3693
+
3694
+ /**
3695
+ * @param {Clock} clock
3696
+ * @param {TimerInitialProps} timer
3697
+ * @returns {TimerId} id of the created timer
3132
3698
  */
3133
3699
  function addTimer(clock, timer) {
3134
3700
  if (timer.func === undefined) {
3135
3701
  throw new Error("Callback must be provided to timer calls");
3136
3702
  }
3137
3703
 
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
- }
3704
+ if (typeof timer.func !== "function") {
3705
+ throw new TypeError(
3706
+ `[ERR_INVALID_CALLBACK]: Callback must be a function. Received ${
3707
+ timer.func
3708
+ } of type ${typeof timer.func}`,
3709
+ );
3147
3710
  }
3148
3711
 
3149
- if (isNearInfiniteLimit) {
3712
+ if (clock.isNearInfiniteLimit) {
3150
3713
  timer.error = new Error();
3151
3714
  }
3152
3715
 
3153
3716
  timer.type = timer.immediate ? "Immediate" : "Timeout";
3154
3717
 
3155
- if (timer.hasOwnProperty("delay")) {
3718
+ if (Object.prototype.hasOwnProperty.call(timer, "delay")) {
3156
3719
  if (typeof timer.delay !== "number") {
3157
3720
  timer.delay = parseInt(timer.delay, 10);
3158
3721
  }
@@ -3164,42 +3727,60 @@ function requireFakeTimersSrc () {
3164
3727
  timer.delay = Math.max(0, timer.delay);
3165
3728
  }
3166
3729
 
3167
- if (timer.hasOwnProperty("interval")) {
3730
+ if (Object.prototype.hasOwnProperty.call(timer, "interval")) {
3168
3731
  timer.type = "Interval";
3169
3732
  timer.interval = timer.interval > maxTimeout ? 1 : timer.interval;
3170
3733
  }
3171
3734
 
3172
- if (timer.hasOwnProperty("animation")) {
3735
+ if (Object.prototype.hasOwnProperty.call(timer, "animation")) {
3173
3736
  timer.type = "AnimationFrame";
3174
3737
  timer.animation = true;
3175
3738
  }
3176
3739
 
3177
- if (timer.hasOwnProperty("idleCallback")) {
3178
- timer.type = "IdleCallback";
3179
- timer.idleCallback = true;
3740
+ if (
3741
+ Object.prototype.hasOwnProperty.call(timer, "requestIdleCallback")
3742
+ ) {
3743
+ // mark timer as IdleCallback type if it has no delay, otherwise it'd be of type timeout
3744
+ // this way we are able to sort such that the timer only gets called when there's truly no pending task to run
3745
+ if (!timer.delay) {
3746
+ timer.type = "IdleCallback";
3747
+ }
3748
+ timer.requestIdleCallback = true;
3180
3749
  }
3181
3750
 
3182
- if (!clock.timers) {
3183
- clock.timers = {};
3751
+ ensureTimerState(clock);
3752
+
3753
+ while (hasTimer(clock, uniqueTimerId)) {
3754
+ uniqueTimerId++;
3755
+ if (uniqueTimerId >= Number.MAX_SAFE_INTEGER) {
3756
+ uniqueTimerId = idCounterStart;
3757
+ }
3184
3758
  }
3185
3759
 
3186
3760
  timer.id = uniqueTimerId++;
3761
+ if (uniqueTimerId >= Number.MAX_SAFE_INTEGER) {
3762
+ uniqueTimerId = idCounterStart;
3763
+ }
3764
+
3765
+ timer.order = uniqueTimerOrder++;
3187
3766
  timer.createdAt = clock.now;
3188
3767
  timer.callAt =
3189
- clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
3768
+ clock.now +
3769
+ (parseInt(String(timer.delay)) || (clock.duringTick ? 1 : 0));
3190
3770
 
3191
- clock.timers[timer.id] = timer;
3771
+ setTimer(clock, timer);
3772
+ clock.timerHeap.push(timer);
3192
3773
 
3193
3774
  if (addTimerReturnsObject) {
3194
3775
  const res = {
3195
3776
  refed: true,
3196
3777
  ref: function () {
3197
3778
  this.refed = true;
3198
- return res;
3779
+ return this;
3199
3780
  },
3200
3781
  unref: function () {
3201
3782
  this.refed = false;
3202
- return res;
3783
+ return this;
3203
3784
  },
3204
3785
  hasRef: function () {
3205
3786
  return this.refed;
@@ -3207,12 +3788,15 @@ function requireFakeTimersSrc () {
3207
3788
  refresh: function () {
3208
3789
  timer.callAt =
3209
3790
  clock.now +
3210
- (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
3791
+ (parseInt(String(timer.delay)) ||
3792
+ (clock.duringTick ? 1 : 0));
3211
3793
 
3212
- // it _might_ have been removed, but if not the assignment is perfectly fine
3213
- clock.timers[timer.id] = timer;
3794
+ clock.timerHeap.remove(timer);
3795
+ timer.order = uniqueTimerOrder++;
3796
+ setTimer(clock, timer);
3797
+ clock.timerHeap.push(timer);
3214
3798
 
3215
- return res;
3799
+ return this;
3216
3800
  },
3217
3801
  [Symbol.toPrimitive]: function () {
3218
3802
  return timer.id;
@@ -3226,12 +3810,20 @@ function requireFakeTimersSrc () {
3226
3810
 
3227
3811
  /* eslint consistent-return: "off" */
3228
3812
  /**
3229
- * Timer comparitor
3813
+ * Timer comparator
3230
3814
  * @param {Timer} a
3231
3815
  * @param {Timer} b
3232
3816
  * @returns {number}
3233
3817
  */
3234
3818
  function compareTimers(a, b) {
3819
+ // Sort IdleCallback timers to the bottom when scheduled for the same time
3820
+ if (a.type === "IdleCallback" && b.type !== "IdleCallback") {
3821
+ return 1;
3822
+ }
3823
+ if (a.type !== "IdleCallback" && b.type === "IdleCallback") {
3824
+ return -1;
3825
+ }
3826
+
3235
3827
  // Sort first by absolute timing
3236
3828
  if (a.callAt < b.callAt) {
3237
3829
  return -1;
@@ -3248,6 +3840,13 @@ function requireFakeTimersSrc () {
3248
3840
  return 1;
3249
3841
  }
3250
3842
 
3843
+ if (a.order < b.order) {
3844
+ return -1;
3845
+ }
3846
+ if (a.order > b.order) {
3847
+ return 1;
3848
+ }
3849
+
3251
3850
  // Sort next by creation time, earlier-created timers take precedence
3252
3851
  if (a.createdAt < b.createdAt) {
3253
3852
  return -1;
@@ -3265,6 +3864,7 @@ function requireFakeTimersSrc () {
3265
3864
  }
3266
3865
 
3267
3866
  // As timer ids are unique, no fallback `0` is necessary
3867
+ return 0;
3268
3868
  }
3269
3869
 
3270
3870
  /**
@@ -3274,20 +3874,31 @@ function requireFakeTimersSrc () {
3274
3874
  * @returns {Timer}
3275
3875
  */
3276
3876
  function firstTimerInRange(clock, from, to) {
3277
- const timers = clock.timers;
3278
- let timer = null;
3279
- let id, isInRange;
3877
+ if (!clock.timerHeap) {
3878
+ return null;
3879
+ }
3280
3880
 
3281
- for (id in timers) {
3282
- if (timers.hasOwnProperty(id)) {
3283
- isInRange = inRange(from, to, timers[id]);
3881
+ const timers = clock.timerHeap.timers;
3882
+ if (timers.length === 1 && timers[0].requestIdleCallback) {
3883
+ return timers[0];
3884
+ }
3284
3885
 
3285
- if (
3286
- isInRange &&
3287
- (!timer || compareTimers(timer, timers[id]) === 1)
3288
- ) {
3289
- timer = timers[id];
3290
- }
3886
+ const first = clock.timerHeap.peek();
3887
+ if (first && inRange(from, to, first)) {
3888
+ return first;
3889
+ }
3890
+
3891
+ /**
3892
+ * @type {?Timer}
3893
+ */
3894
+ let timer = null;
3895
+
3896
+ for (let i = 0; i < timers.length; i++) {
3897
+ if (
3898
+ inRange(from, to, timers[i]) &&
3899
+ (!timer || compareTimers(timer, timers[i]) === 1)
3900
+ ) {
3901
+ timer = timers[i];
3291
3902
  }
3292
3903
  }
3293
3904
 
@@ -3299,19 +3910,10 @@ function requireFakeTimersSrc () {
3299
3910
  * @returns {Timer}
3300
3911
  */
3301
3912
  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
- }
3913
+ if (!clock.timerHeap) {
3914
+ return null;
3312
3915
  }
3313
-
3314
- return timer;
3916
+ return clock.timerHeap.peek() || null;
3315
3917
  }
3316
3918
 
3317
3919
  /**
@@ -3319,15 +3921,15 @@ function requireFakeTimersSrc () {
3319
3921
  * @returns {Timer}
3320
3922
  */
3321
3923
  function lastTimer(clock) {
3322
- const timers = clock.timers;
3924
+ if (!clock.timerHeap) {
3925
+ return null;
3926
+ }
3927
+ const timers = clock.timerHeap.timers;
3323
3928
  let timer = null;
3324
- let id;
3325
3929
 
3326
- for (id in timers) {
3327
- if (timers.hasOwnProperty(id)) {
3328
- if (!timer || compareTimers(timer, timers[id]) === -1) {
3329
- timer = timers[id];
3330
- }
3930
+ for (let i = 0; i < timers.length; i++) {
3931
+ if (!timer || compareTimers(timer, timers[i]) === -1) {
3932
+ timer = timers[i];
3331
3933
  }
3332
3934
  }
3333
3935
 
@@ -3340,25 +3942,27 @@ function requireFakeTimersSrc () {
3340
3942
  */
3341
3943
  function callTimer(clock, timer) {
3342
3944
  if (typeof timer.interval === "number") {
3343
- clock.timers[timer.id].callAt += timer.interval;
3945
+ clock.timerHeap.remove(timer);
3946
+ timer.callAt += timer.interval;
3947
+ timer.order = uniqueTimerOrder++;
3948
+ if (clock.isNearInfiniteLimit) {
3949
+ timer.error = new Error();
3950
+ }
3951
+ clock.timerHeap.push(timer);
3344
3952
  } else {
3345
- delete clock.timers[timer.id];
3953
+ deleteTimer(clock, timer.id);
3954
+ clock.timerHeap.remove(timer);
3346
3955
  }
3347
3956
 
3348
3957
  if (typeof timer.func === "function") {
3349
3958
  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
3959
  }
3357
3960
  }
3358
3961
 
3359
3962
  /**
3360
3963
  * Gets clear handler name for a given timer type
3361
3964
  * @param {string} ttype
3965
+ * @returns {string}
3362
3966
  */
3363
3967
  function getClearHandler(ttype) {
3364
3968
  if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
@@ -3370,6 +3974,7 @@ function requireFakeTimersSrc () {
3370
3974
  /**
3371
3975
  * Gets schedule handler name for a given timer type
3372
3976
  * @param {string} ttype
3977
+ * @returns {string}
3373
3978
  */
3374
3979
  function getScheduleHandler(ttype) {
3375
3980
  if (ttype === "IdleCallback" || ttype === "AnimationFrame") {
@@ -3380,6 +3985,7 @@ function requireFakeTimersSrc () {
3380
3985
 
3381
3986
  /**
3382
3987
  * Creates an anonymous function to warn only once
3988
+ * @returns {(msg: string) => void}
3383
3989
  */
3384
3990
  function createWarnOnce() {
3385
3991
  let calls = 0;
@@ -3392,8 +3998,9 @@ function requireFakeTimersSrc () {
3392
3998
 
3393
3999
  /**
3394
4000
  * @param {Clock} clock
3395
- * @param {number} timerId
4001
+ * @param {TimerId} timerId
3396
4002
  * @param {string} ttype
4003
+ * @returns {void}
3397
4004
  */
3398
4005
  function clearTimer(clock, timerId, ttype) {
3399
4006
  if (!timerId) {
@@ -3402,10 +4009,6 @@ function requireFakeTimersSrc () {
3402
4009
  return;
3403
4010
  }
3404
4011
 
3405
- if (!clock.timers) {
3406
- clock.timers = {};
3407
- }
3408
-
3409
4012
  // in Node, the ID is stored as the primitive value for `Timeout` objects
3410
4013
  // for `Immediate` objects, no ID exists, so it gets coerced to NaN
3411
4014
  const id = Number(timerId);
@@ -3419,21 +4022,30 @@ function requireFakeTimersSrc () {
3419
4022
  ? nativeHandler(timerId)
3420
4023
  : undefined;
3421
4024
  }
4025
+
4026
+ // Include the stacktrace, excluding the 'error' line
4027
+ const stackTrace = new Error().stack
4028
+ .split("\n")
4029
+ .slice(1)
4030
+ .join("\n");
4031
+
3422
4032
  warnOnce(
3423
4033
  `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`.",
4034
+ "\nTo automatically clean-up native timers, use `shouldClearNativeTimers`." +
4035
+ `\n${stackTrace}`,
3425
4036
  );
3426
4037
  }
3427
4038
 
3428
- if (clock.timers.hasOwnProperty(id)) {
4039
+ if (hasTimer(clock, id)) {
3429
4040
  // check that the ID matches a timer of the correct type
3430
- const timer = clock.timers[id];
4041
+ const timer = getTimer(clock, id);
3431
4042
  if (
3432
4043
  timer.type === ttype ||
3433
4044
  (timer.type === "Timeout" && ttype === "Interval") ||
3434
4045
  (timer.type === "Interval" && ttype === "Timeout")
3435
4046
  ) {
3436
- delete clock.timers[id];
4047
+ deleteTimer(clock, id);
4048
+ clock.timerHeap.remove(timer);
3437
4049
  } else {
3438
4050
  const clear = getClearHandler(ttype);
3439
4051
  const schedule = getScheduleHandler(timer.type);
@@ -3478,12 +4090,12 @@ function requireFakeTimersSrc () {
3478
4090
  _global[method] = clock[`_${method}`];
3479
4091
  }
3480
4092
  } else {
3481
- if (_global[method] && _global[method].hadOwnProperty) {
4093
+ if (clock[method] && clock[method].hasOwnProperty) {
3482
4094
  _global[method] = clock[`_${method}`];
3483
4095
  } else {
3484
4096
  try {
3485
4097
  delete _global[method];
3486
- } catch (ignore) {
4098
+ } catch {
3487
4099
  /* eslint no-empty: "off" */
3488
4100
  }
3489
4101
  }
@@ -3506,7 +4118,7 @@ function requireFakeTimersSrc () {
3506
4118
  }
3507
4119
  }
3508
4120
 
3509
- clock.setTickMode("manual");
4121
+ clock.setTickMode({ mode: "manual" });
3510
4122
 
3511
4123
  // Prevent multiple executions which will completely remove these props
3512
4124
  clock.methods = [];
@@ -3517,12 +4129,10 @@ function requireFakeTimersSrc () {
3517
4129
  }
3518
4130
 
3519
4131
  // return pending timers, to enable checking what timers remained on uninstall
3520
- if (!clock.timers) {
4132
+ if (!clock.timerHeap) {
3521
4133
  return [];
3522
4134
  }
3523
- return Object.keys(clock.timers).map(function mapper(key) {
3524
- return clock.timers[key];
3525
- });
4135
+ return clock.timerHeap.timers.slice();
3526
4136
  }
3527
4137
 
3528
4138
  /**
@@ -3531,7 +4141,7 @@ function requireFakeTimersSrc () {
3531
4141
  * @param {Clock} clock
3532
4142
  */
3533
4143
  function hijackMethod(target, method, clock) {
3534
- clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(
4144
+ clock[method].hasOwnProperty = Object.prototype.hasOwnProperty.call(
3535
4145
  target,
3536
4146
  method,
3537
4147
  );
@@ -3588,26 +4198,6 @@ function requireFakeTimersSrc () {
3588
4198
  clock.tick(advanceTimeDelta);
3589
4199
  }
3590
4200
 
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
4201
  /** @type {Timers} */
3612
4202
  const timers = {
3613
4203
  setTimeout: _global.setTimeout,
@@ -3671,30 +4261,37 @@ function requireFakeTimersSrc () {
3671
4261
  * @returns {Clock}
3672
4262
  */
3673
4263
  function createClock(start, loopLimit) {
4264
+ /** @type {number} */
3674
4265
  // eslint-disable-next-line no-param-reassign
3675
4266
  start = Math.floor(getEpoch(start));
4267
+ const startTimestamp = start;
3676
4268
  // eslint-disable-next-line no-param-reassign
3677
4269
  loopLimit = loopLimit || 1000;
4270
+ /** @type {number} */
3678
4271
  let nanos = 0;
4272
+ /** @type {number[]} */
3679
4273
  const adjustedSystemTime = [0, 0]; // [millis, nanoremainder]
3680
4274
 
3681
- const clock = {
4275
+ /** @type {Clock} */
4276
+ const clock = /** @type {Clock} */ ({
3682
4277
  now: start,
3683
4278
  Date: createDate(),
3684
4279
  loopLimit: loopLimit,
4280
+ isNearInfiniteLimit: false,
3685
4281
  tickMode: { mode: "manual", counter: 0, delta: undefined },
3686
- };
4282
+ });
3687
4283
 
3688
4284
  clock.Date.clock = clock;
3689
4285
 
3690
4286
  //eslint-disable-next-line jsdoc/require-jsdoc
3691
4287
  function getTimeToNextFrame() {
3692
- return 16 - ((clock.now - start) % 16);
4288
+ return 16 - ((clock.now - startTimestamp) % 16);
3693
4289
  }
3694
4290
 
3695
4291
  //eslint-disable-next-line jsdoc/require-jsdoc
3696
4292
  function hrtime(prev) {
3697
- const millisSinceStart = clock.now - adjustedSystemTime[0] - start;
4293
+ const millisSinceStart =
4294
+ clock.now - adjustedSystemTime[0] - startTimestamp;
3698
4295
  const secsSinceStart = Math.floor(millisSinceStart / 1000);
3699
4296
  const remainderInNanos =
3700
4297
  (millisSinceStart - secsSinceStart * 1e3) * 1e6 +
@@ -3740,20 +4337,21 @@ function requireFakeTimersSrc () {
3740
4337
  if (isPresent.hrtimeBigint) {
3741
4338
  hrtime.bigint = function () {
3742
4339
  const parts = hrtime();
3743
- return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]); // eslint-disable-line
4340
+ return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]);
3744
4341
  };
3745
4342
  }
3746
4343
 
3747
4344
  if (isPresent.Intl) {
3748
- clock.Intl = createIntl();
4345
+ clock.Intl = createIntl(clock);
3749
4346
  clock.Intl.clock = clock;
3750
4347
  }
3751
4348
 
3752
4349
  /**
3753
- * @param {TimerTickMode} tickModeConfig - The new configuration for how the clock should tick.
4350
+ * @param {SetTickModeConfig} tickModeConfig - The new configuration for how the clock should tick.
3754
4351
  */
3755
4352
  clock.setTickMode = function (tickModeConfig) {
3756
- const { mode: newMode, delta: newDelta } = tickModeConfig;
4353
+ const { mode: newMode, delta: newDelta } =
4354
+ /** @type {SetTickModeConfig} */ (tickModeConfig);
3757
4355
  const { mode: oldMode, delta: oldDelta } = clock.tickMode;
3758
4356
  if (newMode === oldMode && newDelta === oldDelta) {
3759
4357
  return;
@@ -3776,7 +4374,15 @@ function requireFakeTimersSrc () {
3776
4374
  }
3777
4375
  };
3778
4376
 
4377
+ /**
4378
+ * Keeps advancing the native event loop until the tick mode changes.
4379
+ * @returns {Promise<void>}
4380
+ */
3779
4381
  async function advanceUntilModeChanges() {
4382
+ /**
4383
+ * Waits for one native macrotask and then one microtask turn.
4384
+ * @returns {Promise<void>}
4385
+ */
3780
4386
  async function newMacrotask() {
3781
4387
  // MessageChannel ensures that setTimeout is not throttled to 4ms.
3782
4388
  // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
@@ -3784,7 +4390,7 @@ function requireFakeTimersSrc () {
3784
4390
  const channel = new MessageChannel();
3785
4391
  await new Promise((resolve) => {
3786
4392
  channel.port1.onmessage = () => {
3787
- resolve();
4393
+ resolve(undefined);
3788
4394
  channel.port1.close();
3789
4395
  };
3790
4396
  channel.port2.postMessage(undefined);
@@ -3807,6 +4413,11 @@ function requireFakeTimersSrc () {
3807
4413
  }
3808
4414
  }
3809
4415
 
4416
+ /**
4417
+ * Temporarily pauses nextAsync auto-ticking while an async operation runs.
4418
+ * @param {Promise<unknown>} promise
4419
+ * @returns {Promise<unknown>}
4420
+ */
3810
4421
  function pauseAutoTickUntilFinished(promise) {
3811
4422
  if (clock.tickMode.mode !== "nextAsync") {
3812
4423
  return promise;
@@ -3817,24 +4428,37 @@ function requireFakeTimersSrc () {
3817
4428
  });
3818
4429
  }
3819
4430
 
3820
- clock.requestIdleCallback = function requestIdleCallback(
3821
- func,
3822
- timeout,
3823
- ) {
4431
+ /**
4432
+ * Returns the remaining time in the current idle window.
4433
+ * @returns {number}
4434
+ */
4435
+ function getTimeToNextIdlePeriod() {
3824
4436
  let timeToNextIdlePeriod = 0;
3825
4437
 
3826
4438
  if (clock.countTimers() > 0) {
3827
4439
  timeToNextIdlePeriod = 50; // const for now
3828
4440
  }
3829
4441
 
4442
+ return timeToNextIdlePeriod;
4443
+ }
4444
+
4445
+ clock.requestIdleCallback = function requestIdleCallback(
4446
+ func,
4447
+ { timeout } = /** @type {{ timeout?: number }} */ ({}),
4448
+ ) {
4449
+ /**
4450
+ * @type {IdleDeadline}
4451
+ */
4452
+ const idleDeadline = {
4453
+ didTimeout: true,
4454
+ timeRemaining: getTimeToNextIdlePeriod,
4455
+ };
4456
+
3830
4457
  const result = addTimer(clock, {
3831
4458
  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,
4459
+ args: [idleDeadline],
4460
+ delay: timeout,
4461
+ requestIdleCallback: true,
3838
4462
  });
3839
4463
 
3840
4464
  return Number(result);
@@ -3874,7 +4498,7 @@ function requireFakeTimersSrc () {
3874
4498
  return enqueueJob(clock, {
3875
4499
  func: func,
3876
4500
  args: Array.prototype.slice.call(arguments, 1),
3877
- error: isNearInfiniteLimit ? new Error() : null,
4501
+ error: clock.isNearInfiniteLimit ? new Error() : null,
3878
4502
  });
3879
4503
  };
3880
4504
 
@@ -3884,7 +4508,7 @@ function requireFakeTimersSrc () {
3884
4508
 
3885
4509
  clock.setInterval = function setInterval(func, timeout) {
3886
4510
  // eslint-disable-next-line no-param-reassign
3887
- timeout = parseInt(timeout, 10);
4511
+ timeout = parseInt(String(timeout), 10);
3888
4512
  return addTimer(clock, {
3889
4513
  func: func,
3890
4514
  args: Array.prototype.slice.call(arguments, 2),
@@ -3898,13 +4522,15 @@ function requireFakeTimersSrc () {
3898
4522
  };
3899
4523
 
3900
4524
  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
- };
4525
+ clock.setImmediate = /** @type {SetImmediate} */ (
4526
+ function setImmediate(func) {
4527
+ return addTimer(clock, {
4528
+ func: func,
4529
+ args: Array.prototype.slice.call(arguments, 1),
4530
+ immediate: true,
4531
+ });
4532
+ }
4533
+ );
3908
4534
 
3909
4535
  if (typeof _global.Promise !== "undefined" && utilPromisify) {
3910
4536
  clock.setImmediate[utilPromisify.custom] =
@@ -3928,7 +4554,7 @@ function requireFakeTimersSrc () {
3928
4554
 
3929
4555
  clock.countTimers = function countTimers() {
3930
4556
  return (
3931
- Object.keys(clock.timers || {}).length +
4557
+ (clock.timerHeap ? clock.timerHeap.timers.length : 0) +
3932
4558
  (clock.jobs || []).length
3933
4559
  );
3934
4560
  };
@@ -3956,12 +4582,9 @@ function requireFakeTimersSrc () {
3956
4582
 
3957
4583
  /**
3958
4584
  * @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
4585
+ * @returns {ClockState} a mutable state object for the tick execution
3963
4586
  */
3964
- function doTick(tickValue, isAsync, resolve, reject) {
4587
+ function createTickState(tickValue) {
3965
4588
  const msFloat =
3966
4589
  typeof tickValue === "number"
3967
4590
  ? tickValue
@@ -3981,122 +4604,202 @@ function requireFakeTimersSrc () {
3981
4604
  nanosTotal -= 1e6;
3982
4605
  }
3983
4606
 
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;
4607
+ return /** @type {ClockState} */ ({
4608
+ msFloat: msFloat,
4609
+ ms: ms,
4610
+ nanosTotal: nanosTotal,
4611
+ tickFrom: clock.now,
4612
+ tickTo: tickTo,
4613
+ previous: clock.now,
4614
+ timer: null,
4615
+ firstException: null,
4616
+ oldNow: null,
4617
+ });
4618
+ }
3998
4619
 
3999
- // perform microtasks
4000
- oldNow = clock.now;
4001
- runJobs(clock);
4620
+ /**
4621
+ * @param {ClockState} state mutable tick state
4622
+ * @param {number} oldNow the clock.now before some action
4623
+ * @param {object} [options] compensation options
4624
+ * @param {boolean} [options.includePrevious] whether to also update state.previous
4625
+ */
4626
+ function applyClockChangeCompensation(state, oldNow, options) {
4002
4627
  if (oldNow !== clock.now) {
4003
- // compensate for any setSystemTime() call during microtask callback
4004
- tickFrom += clock.now - oldNow;
4005
- tickTo += clock.now - oldNow;
4628
+ const difference = clock.now - oldNow;
4629
+ state.tickFrom += difference;
4630
+ state.tickTo += difference;
4631
+ if (options && options.includePrevious) {
4632
+ state.previous += difference;
4633
+ }
4006
4634
  }
4635
+ }
4007
4636
 
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
- }
4637
+ /**
4638
+ * @param {ClockState} state mutable tick state
4639
+ */
4640
+ function runInitialJobs(state) {
4641
+ state.oldNow = clock.now;
4642
+ runJobs(clock);
4643
+ applyClockChangeCompensation(state, state.oldNow);
4644
+ }
4032
4645
 
4033
- compensationCheck();
4034
- }
4646
+ /**
4647
+ * @param {ClockState} state mutable tick state
4648
+ */
4649
+ function runPostLoopJobs(state) {
4650
+ state.oldNow = clock.now;
4651
+ runJobs(clock);
4652
+ applyClockChangeCompensation(state, state.oldNow);
4653
+ }
4035
4654
 
4036
- postTimerCall();
4037
- }
4655
+ /**
4656
+ * @param {ClockState} state mutable tick state
4657
+ */
4658
+ function selectNextTimerInRange(state) {
4659
+ state.timer = firstTimerInRange(
4660
+ clock,
4661
+ state.previous,
4662
+ state.tickTo,
4663
+ );
4664
+ state.previous = state.tickFrom;
4665
+ }
4038
4666
 
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;
4667
+ /**
4668
+ * @param {ClockState} state mutable tick state
4669
+ * @param {boolean} isAsync whether this is an async tick
4670
+ * @param {FakeTimersFunction} nextPromiseTick callback for async promise settlement
4671
+ * @param {FakeTimersFunction} compensationCheck callback for clock change compensation
4672
+ * @returns {boolean} whether an early return was triggered (async mode)
4673
+ */
4674
+ function runTimersInRange(
4675
+ state,
4676
+ isAsync,
4677
+ nextPromiseTick,
4678
+ compensationCheck,
4679
+ ) {
4680
+ state.timer = firstTimerInRange(
4681
+ clock,
4682
+ state.tickFrom,
4683
+ state.tickTo,
4684
+ );
4048
4685
 
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) {
4686
+ while (state.timer && state.tickFrom <= state.tickTo) {
4687
+ if (hasTimer(clock, state.timer.id)) {
4688
+ state.tickFrom = state.timer.callAt;
4689
+ clock.now = state.timer.callAt;
4690
+ state.oldNow = clock.now;
4052
4691
  try {
4053
- clock.tick(tickTo - clock.now); // do it all again - for the remainder of the requested range
4692
+ runJobs(clock);
4693
+ callTimer(clock, state.timer);
4054
4694
  } catch (e) {
4055
- firstException = firstException || e;
4695
+ state.firstException = state.firstException || e;
4056
4696
  }
4057
- } else {
4058
- // no timers remaining in the requested range: move the clock all the way to the end
4059
- clock.now = tickTo;
4060
4697
 
4061
- // update nanos
4062
- nanos = nanosTotal;
4063
- }
4064
- if (firstException) {
4065
- throw firstException;
4698
+ if (isAsync) {
4699
+ // finish up after native setImmediate callback to allow
4700
+ // all native es6 promises to process their callbacks after
4701
+ // each timer fires.
4702
+ originalSetTimeout(nextPromiseTick);
4703
+ return true;
4704
+ }
4705
+
4706
+ compensationCheck();
4066
4707
  }
4067
4708
 
4068
- if (isAsync) {
4069
- resolve(clock.now);
4070
- } else {
4071
- return clock.now;
4709
+ selectNextTimerInRange(state);
4710
+ }
4711
+ return false;
4712
+ }
4713
+
4714
+ /**
4715
+ * @param {ClockState} state mutable tick state
4716
+ * @param {boolean} isAsync whether this is an async tick
4717
+ * @param {FakeTimersFunction} resolve promise resolve function
4718
+ * @returns {number|undefined} the new clock.now or nothing for async
4719
+ */
4720
+ function finalizeTick(state, isAsync, resolve) {
4721
+ // corner case: during runJobs new timers were scheduled which could be in the range [clock.now, tickTo]
4722
+ state.timer = firstTimerInRange(
4723
+ clock,
4724
+ state.tickFrom,
4725
+ state.tickTo,
4726
+ );
4727
+ if (state.timer) {
4728
+ try {
4729
+ clock.tick(state.tickTo - clock.now); // do it all again - for the remainder of the requested range
4730
+ } catch (e) {
4731
+ state.firstException = state.firstException || e;
4072
4732
  }
4733
+ } else {
4734
+ // no timers remaining in the requested range: move the clock all the way to the end
4735
+ clock.now = state.tickTo;
4736
+
4737
+ // update nanos
4738
+ nanos = state.nanosTotal;
4739
+ }
4740
+ if (state.firstException) {
4741
+ throw state.firstException;
4073
4742
  }
4074
4743
 
4075
- nextPromiseTick =
4744
+ if (isAsync) {
4745
+ resolve(clock.now);
4746
+ } else {
4747
+ return clock.now;
4748
+ }
4749
+ }
4750
+
4751
+ /**
4752
+ * @param {number|string} tickValue milliseconds or a string parseable by parseTime
4753
+ * @param {boolean} isAsync whether this is an async tick
4754
+ * @param {FakeTimersFunction} [resolve] promise resolve function
4755
+ * @param {FakeTimersFunction} [reject] promise reject function
4756
+ * @returns {number|undefined} the new clock.now or nothing for async
4757
+ */
4758
+ function doTick(tickValue, isAsync, resolve, reject) {
4759
+ /** @type {ClockState} */
4760
+ const state = createTickState(tickValue);
4761
+
4762
+ nanos = state.nanosTotal;
4763
+ clock.duringTick = true;
4764
+
4765
+ runInitialJobs(state);
4766
+
4767
+ const compensationCheck = function () {
4768
+ applyClockChangeCompensation(state, state.oldNow, {
4769
+ includePrevious: true,
4770
+ });
4771
+ };
4772
+
4773
+ const nextPromiseTick =
4076
4774
  isAsync &&
4077
4775
  function () {
4078
4776
  try {
4079
4777
  compensationCheck();
4080
- postTimerCall();
4778
+ selectNextTimerInRange(state);
4081
4779
  doTickInner();
4082
4780
  } catch (e) {
4083
4781
  reject(e);
4084
4782
  }
4085
4783
  };
4086
4784
 
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;
4785
+ //eslint-disable-next-line jsdoc/require-jsdoc
4786
+ function doTickInner() {
4787
+ if (
4788
+ runTimersInRange(
4789
+ state,
4790
+ isAsync,
4791
+ nextPromiseTick,
4792
+ compensationCheck,
4793
+ )
4794
+ ) {
4795
+ return;
4093
4796
  }
4094
- };
4095
4797
 
4096
- postTimerCall = function () {
4097
- timer = firstTimerInRange(clock, previous, tickTo);
4098
- previous = tickFrom;
4099
- };
4798
+ runPostLoopJobs(state);
4799
+ clock.duringTick = false;
4800
+
4801
+ return finalizeTick(state, isAsync, resolve);
4802
+ }
4100
4803
 
4101
4804
  return doTickInner();
4102
4805
  }
@@ -4109,26 +4812,6 @@ function requireFakeTimersSrc () {
4109
4812
  return doTick(tickValue, false);
4110
4813
  };
4111
4814
 
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
4815
  clock.next = function next() {
4133
4816
  runJobs(clock);
4134
4817
  const timer = firstTimer(clock);
@@ -4147,61 +4830,40 @@ function requireFakeTimersSrc () {
4147
4830
  }
4148
4831
  };
4149
4832
 
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
- };
4833
+ /**
4834
+ * @param {(resolve: (value: unknown) => void, reject: (reason?: unknown) => void) => void} callback function to run inside native setTimeout
4835
+ * @returns {Promise}
4836
+ */
4837
+ function runAsyncWithNativeTimeout(callback) {
4838
+ return pauseAutoTickUntilFinished(
4839
+ new _global.Promise(function (resolve, reject) {
4840
+ originalSetTimeout(function () {
4841
+ try {
4842
+ callback(resolve, reject);
4843
+ } catch (e) {
4844
+ reject(e);
4845
+ }
4846
+ });
4847
+ }),
4848
+ );
4186
4849
  }
4187
4850
 
4188
4851
  clock.runAll = function runAll() {
4189
- let numTimers, i;
4190
4852
  runJobs(clock);
4191
- for (i = 0; i < clock.loopLimit; i++) {
4853
+ for (let i = 0; i < clock.loopLimit; i++) {
4192
4854
  if (!clock.timers) {
4193
- resetIsNearInfiniteLimit();
4855
+ resetIsNearInfiniteLimit(clock);
4194
4856
  return clock.now;
4195
4857
  }
4196
4858
 
4197
- numTimers = Object.keys(clock.timers).length;
4859
+ const numTimers = clock.timerHeap.timers.length;
4198
4860
  if (numTimers === 0) {
4199
- resetIsNearInfiniteLimit();
4861
+ resetIsNearInfiniteLimit(clock);
4200
4862
  return clock.now;
4201
4863
  }
4202
4864
 
4203
- clock.next();
4204
4865
  checkIsNearInfiniteLimit(clock, i);
4866
+ clock.next();
4205
4867
  }
4206
4868
 
4207
4869
  const excessJob = firstTimer(clock);
@@ -4212,60 +4874,6 @@ function requireFakeTimersSrc () {
4212
4874
  return clock.tick(getTimeToNextFrame());
4213
4875
  };
4214
4876
 
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
4877
  clock.runToLast = function runToLast() {
4270
4878
  const timer = lastTimer(clock);
4271
4879
  if (!timer) {
@@ -4277,32 +4885,110 @@ function requireFakeTimersSrc () {
4277
4885
  };
4278
4886
 
4279
4887
  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
- }
4888
+ /**
4889
+ * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
4890
+ * @returns {Promise}
4891
+ */
4892
+ clock.tickAsync = function tickAsync(tickValue) {
4893
+ return runAsyncWithNativeTimeout(function (resolve, reject) {
4894
+ doTick(tickValue, true, resolve, reject);
4895
+ });
4896
+ };
4290
4897
 
4291
- resolve(
4292
- clock.tickAsync(timer.callAt - clock.now),
4293
- );
4294
- } catch (e) {
4295
- reject(e);
4898
+ clock.nextAsync = function nextAsync() {
4899
+ return runAsyncWithNativeTimeout(function (resolve, reject) {
4900
+ const timer = firstTimer(clock);
4901
+ if (!timer) {
4902
+ resolve(clock.now);
4903
+ return;
4904
+ }
4905
+
4906
+ let err;
4907
+ clock.duringTick = true;
4908
+ clock.now = timer.callAt;
4909
+ try {
4910
+ callTimer(clock, timer);
4911
+ } catch (e) {
4912
+ err = e;
4913
+ }
4914
+ clock.duringTick = false;
4915
+
4916
+ originalSetTimeout(function () {
4917
+ if (err) {
4918
+ reject(err);
4919
+ } else {
4920
+ resolve(clock.now);
4921
+ }
4922
+ });
4923
+ });
4924
+ };
4925
+
4926
+ clock.runAllAsync = function runAllAsync() {
4927
+ let i = 0;
4928
+ /**
4929
+ * @param {(value: unknown) => void} resolve promise resolve function
4930
+ * @param {(reason?: unknown) => void} reject promise reject function
4931
+ */
4932
+ function doRun(resolve, reject) {
4933
+ try {
4934
+ runJobs(clock);
4935
+
4936
+ let numTimers;
4937
+ if (i < clock.loopLimit) {
4938
+ if (!clock.timerHeap) {
4939
+ resetIsNearInfiniteLimit(clock);
4940
+ resolve(clock.now);
4941
+ return;
4296
4942
  }
4297
- });
4298
- }),
4299
- );
4943
+
4944
+ numTimers = clock.timerHeap.timers.length;
4945
+ if (numTimers === 0) {
4946
+ resetIsNearInfiniteLimit(clock);
4947
+ resolve(clock.now);
4948
+ return;
4949
+ }
4950
+
4951
+ checkIsNearInfiniteLimit(clock, i);
4952
+ clock.next();
4953
+
4954
+ i++;
4955
+
4956
+ originalSetTimeout(function () {
4957
+ doRun(resolve, reject);
4958
+ });
4959
+ return;
4960
+ }
4961
+
4962
+ const excessJob = firstTimer(clock);
4963
+ reject(getInfiniteLoopError(clock, excessJob));
4964
+ } catch (e) {
4965
+ reject(e);
4966
+ }
4967
+ }
4968
+
4969
+ return runAsyncWithNativeTimeout(function (resolve, reject) {
4970
+ doRun(resolve, reject);
4971
+ });
4972
+ };
4973
+
4974
+ clock.runToLastAsync = function runToLastAsync() {
4975
+ return runAsyncWithNativeTimeout(function (resolve) {
4976
+ const timer = lastTimer(clock);
4977
+ if (!timer) {
4978
+ runJobs(clock);
4979
+ resolve(clock.now);
4980
+ return;
4981
+ }
4982
+
4983
+ resolve(clock.tickAsync(timer.callAt - clock.now));
4984
+ });
4300
4985
  };
4301
4986
  }
4302
4987
 
4303
4988
  clock.reset = function reset() {
4304
4989
  nanos = 0;
4305
- clock.timers = {};
4990
+ clock.timers = new Map();
4991
+ clock.timerHeap = new TimerHeap();
4306
4992
  clock.jobs = [];
4307
4993
  clock.now = start;
4308
4994
  };
@@ -4311,7 +4997,6 @@ function requireFakeTimersSrc () {
4311
4997
  // determine time difference
4312
4998
  const newNow = getEpoch(systemTime);
4313
4999
  const difference = newNow - clock.now;
4314
- let id, timer;
4315
5000
 
4316
5001
  adjustedSystemTime[0] = adjustedSystemTime[0] + difference;
4317
5002
  adjustedSystemTime[1] = adjustedSystemTime[1] + nanos;
@@ -4320,18 +5005,15 @@ function requireFakeTimersSrc () {
4320
5005
  nanos = 0;
4321
5006
 
4322
5007
  // 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
- }
5008
+ forEachActiveTimer(clock, (timer) => {
5009
+ timer.createdAt += difference;
5010
+ timer.callAt += difference;
5011
+ });
4330
5012
  };
4331
5013
 
4332
5014
  /**
4333
5015
  * @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
5016
+ * @returns {number} the new `now` value
4335
5017
  */
4336
5018
  clock.jump = function jump(tickValue) {
4337
5019
  const msFloat =
@@ -4340,12 +5022,17 @@ function requireFakeTimersSrc () {
4340
5022
  : parseTime(tickValue);
4341
5023
  const ms = Math.floor(msFloat);
4342
5024
 
4343
- for (const timer of Object.values(clock.timers)) {
5025
+ forEachActiveTimer(clock, (timer) => {
4344
5026
  if (clock.now + ms > timer.callAt) {
4345
5027
  timer.callAt = clock.now + ms;
4346
5028
  }
4347
- }
5029
+ });
5030
+
5031
+ // Rebuild heap as order might have changed
5032
+ rebuildTimerHeap(clock);
5033
+
4348
5034
  clock.tick(ms);
5035
+ return clock.now;
4349
5036
  };
4350
5037
 
4351
5038
  if (isPresent.performance) {
@@ -4360,6 +5047,11 @@ function requireFakeTimersSrc () {
4360
5047
  return clock;
4361
5048
  }
4362
5049
 
5050
+ /**
5051
+ * Starts the interval used to advance the clock automatically.
5052
+ * @param {Clock} clock
5053
+ * @param {number} delta
5054
+ */
4363
5055
  function createIntervalTick(clock, delta) {
4364
5056
  const intervalTick = doIntervalTick.bind(null, clock, delta);
4365
5057
  const intervalId = originalSetInterval(intervalTick, delta);
@@ -4401,6 +5093,21 @@ function requireFakeTimersSrc () {
4401
5093
  config.shouldClearNativeTimers =
4402
5094
  config.shouldClearNativeTimers || false;
4403
5095
 
5096
+ const hasToFake = Object.prototype.hasOwnProperty.call(
5097
+ config,
5098
+ "toFake",
5099
+ );
5100
+ const hasToNotFake = Object.prototype.hasOwnProperty.call(
5101
+ config,
5102
+ "toNotFake",
5103
+ );
5104
+
5105
+ if (hasToFake && hasToNotFake) {
5106
+ throw new TypeError(
5107
+ "config.toFake and config.toNotFake cannot be used together",
5108
+ );
5109
+ }
5110
+
4404
5111
  if (config.target) {
4405
5112
  throw new TypeError(
4406
5113
  "config.target is no longer supported. Use `withGlobal(target)` instead.",
@@ -4408,8 +5115,8 @@ function requireFakeTimersSrc () {
4408
5115
  }
4409
5116
 
4410
5117
  /**
4411
- * @param {string} timer/object the name of the thing that is not present
4412
- * @param timer
5118
+ * Handles a missing timer or API name during installation.
5119
+ * @param {string} timer - the name of the missing timer or object
4413
5120
  */
4414
5121
  function handleMissingTimer(timer) {
4415
5122
  if (config.ignoreMissingTimers) {
@@ -4431,10 +5138,24 @@ function requireFakeTimersSrc () {
4431
5138
 
4432
5139
  clock.abortListenerMap = new Map();
4433
5140
 
4434
- clock.methods = config.toFake || [];
4435
-
4436
- if (clock.methods.length === 0) {
4437
- clock.methods = Object.keys(timers);
5141
+ if (hasToFake) {
5142
+ clock.methods = /** @type {FakeMethod[]} */ (config.toFake || []);
5143
+ if (clock.methods.length === 0) {
5144
+ clock.methods = /** @type {FakeMethod[]} */ (
5145
+ Object.keys(timers)
5146
+ );
5147
+ }
5148
+ } else if (hasToNotFake) {
5149
+ const methodsToNotFake = /** @type {string[]} */ (
5150
+ config.toNotFake || []
5151
+ );
5152
+ clock.methods = /** @type {FakeMethod[]} */ (
5153
+ Object.keys(timers).filter(
5154
+ (method) => !methodsToNotFake.includes(method),
5155
+ )
5156
+ );
5157
+ } else {
5158
+ clock.methods = /** @type {FakeMethod[]} */ (Object.keys(timers));
4438
5159
  }
4439
5160
 
4440
5161
  if (config.shouldAdvanceTime === true) {
@@ -4471,7 +5192,7 @@ function requireFakeTimersSrc () {
4471
5192
  // (or the Worker was installed)
4472
5193
  clock.performance.timeOrigin = getEpoch(config.now);
4473
5194
  } else if ((config.toFake || []).includes("performance")) {
4474
- return handleMissingTimer("performance");
5195
+ handleMissingTimer("performance");
4475
5196
  }
4476
5197
  }
4477
5198
  if (_global === globalObject && timersModule) {
@@ -4635,10 +5356,13 @@ function requireFakeTimersSrc () {
4635
5356
  [Symbol.asyncIterator]: () => {
4636
5357
  const createResolvable = () => {
4637
5358
  let resolve, reject;
4638
- const promise = new Promise((res, rej) => {
4639
- resolve = res;
4640
- reject = rej;
4641
- });
5359
+ const promise =
5360
+ /** @type {Promise<unknown> & { resolve: (value: unknown) => void; reject: (reason: unknown) => void }} */ (
5361
+ new Promise((res, rej) => {
5362
+ resolve = res;
5363
+ reject = rej;
5364
+ })
5365
+ );
4642
5366
  promise.resolve = resolve;
4643
5367
  promise.reject = reject;
4644
5368
  return promise;
@@ -4766,22 +5490,13 @@ function requireFakeTimersSrc () {
4766
5490
  };
4767
5491
  }
4768
5492
 
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
5493
  /** @type {FakeTimers} */
4780
5494
  const defaultImplementation = withGlobal(globalObject);
4781
5495
 
4782
5496
  fakeTimersSrc.timers = defaultImplementation.timers;
4783
5497
  fakeTimersSrc.createClock = defaultImplementation.createClock;
4784
5498
  fakeTimersSrc.install = defaultImplementation.install;
5499
+ /** @type {WithGlobal} */
4785
5500
  fakeTimersSrc.withGlobal = withGlobal;
4786
5501
  return fakeTimersSrc;
4787
5502
  }
@@ -4872,12 +5587,18 @@ class FakeTimers {
4872
5587
  this._fakingDate = null;
4873
5588
  }
4874
5589
  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");
5590
+ let toFake = this._userConfig?.toFake;
5591
+ if (isChildProcess() && toFake?.includes("nextTick")) throw new Error("process.nextTick cannot be mocked inside child_process");
5592
+ let toNotFake = this._userConfig?.toNotFake;
5593
+ if (toFake === void 0 && toNotFake === void 0)
5594
+ // Do not mock timers internally used by node by default. It can still be mocked through userConfig.
5595
+ toFake = Object.keys(this._fakeTimers.timers).filter((timer) => timer !== "nextTick" && timer !== "queueMicrotask");
5596
+ if (isChildProcess() && toNotFake && !toNotFake.includes("nextTick")) toNotFake = [...toNotFake, "nextTick"];
4877
5597
  this._clock = this._fakeTimers.install({
4878
5598
  now: fakeDate,
4879
5599
  ...this._userConfig,
4880
- toFake: this._userConfig?.toFake || toFake,
5600
+ ...toFake && { toFake },
5601
+ ...toNotFake && { toNotFake },
4881
5602
  ignoreMissingTimers: true
4882
5603
  });
4883
5604
  this._fakingTime = true;
@@ -5325,7 +6046,9 @@ function createExpectPoll(expect) {
5325
6046
  const promise = async () => {
5326
6047
  chai.util.flag(assertion, "_name", key);
5327
6048
  chai.util.flag(assertion, "error", STACK_TRACE_ERROR);
6049
+ const onStart = chai.util.flag(assertion, "_poll.onStart");
5328
6050
  const onSettled = chai.util.flag(assertion, "_poll.onSettled");
6051
+ await onStart?.({ assertion });
5329
6052
  if (Object.getOwnPropertyDescriptor(assertionFunction, "__vitest_poll_takeover__")?.value) try {
5330
6053
  const output = await assertionFunction.call(assertion, ...args);
5331
6054
  await onSettled?.({
@@ -5341,41 +6064,53 @@ function createExpectPoll(expect) {
5341
6064
  throwWithCause(err, STACK_TRACE_ERROR);
5342
6065
  }
5343
6066
  const { setTimeout, clearTimeout } = getSafeTimers();
5344
- let executionPhase = "fn";
5345
- let hasTimedOut = false;
5346
- const timerId = setTimeout(() => {
5347
- hasTimedOut = true;
5348
- }, timeout);
6067
+ let timerId;
6068
+ const timeoutController = new AbortController();
6069
+ const timeoutPromise = new Promise((resolve) => {
6070
+ timerId = setTimeout(() => {
6071
+ timeoutController.abort();
6072
+ resolve();
6073
+ }, timeout);
6074
+ });
6075
+ let lastError;
5349
6076
  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);
6077
+ while (true) try {
6078
+ const fnResult = await raceWith$1(Promise.resolve().then(() => fn({ signal: timeoutController.signal })), timeoutPromise);
6079
+ if (!fnResult.ok) {
6080
+ lastError ??= /* @__PURE__ */ new Error(`expect.poll() function didn't resolve in time.`);
6081
+ break;
5374
6082
  }
6083
+ const obj = fnResult.value;
6084
+ chai.util.flag(assertion, "object", obj);
6085
+ const assertionResult = await raceWith$1(Promise.resolve().then(() => assertionFunction.apply(assertion, args)), timeoutPromise);
6086
+ if (!assertionResult.ok) {
6087
+ lastError ??= /* @__PURE__ */ new Error(`expect.poll() assertion didn't resolve in time.`);
6088
+ break;
6089
+ }
6090
+ const output = assertionResult.value;
6091
+ await onSettled?.({
6092
+ assertion,
6093
+ status: "pass"
6094
+ });
6095
+ return output;
6096
+ } catch (err) {
6097
+ lastError = err;
6098
+ // no retry for toMatchScreenshot since
6099
+ // it owns retry/stability after the first element resolution
6100
+ if (key === "toMatchScreenshot") break;
6101
+ if (!(await raceWith$1(delay(interval, setTimeout), timeoutPromise)).ok) break;
6102
+ if (vi.isFakeTimers()) vi.advanceTimersByTime(interval);
5375
6103
  }
5376
6104
  } finally {
5377
6105
  clearTimeout(timerId);
5378
6106
  }
6107
+ if (lastError) {
6108
+ await onSettled?.({
6109
+ assertion,
6110
+ status: "fail"
6111
+ });
6112
+ throwWithCause(lastError, STACK_TRACE_ERROR);
6113
+ }
5379
6114
  };
5380
6115
  let awaited = false;
5381
6116
  test.onFinished ??= [];
@@ -5413,6 +6148,17 @@ function copyStackTrace(target, source) {
5413
6148
  if (source.stack !== void 0) target.stack = source.stack.replace(source.message, target.message);
5414
6149
  return target;
5415
6150
  }
6151
+ function raceWith$1(promise, other) {
6152
+ const left = promise.then((value) => ({
6153
+ ok: true,
6154
+ value
6155
+ }));
6156
+ if (!other) return left;
6157
+ return Promise.race([left, other.then((value) => ({
6158
+ ok: false,
6159
+ value
6160
+ }))]);
6161
+ }
5416
6162
 
5417
6163
  var naturalCompare$1 = {exports: {}};
5418
6164
 
@@ -6303,11 +7049,15 @@ class SnapshotClient {
6303
7049
  inlineSnapshot
6304
7050
  });
6305
7051
  const reference = expectedSnapshot.data !== void 0 && snapshotState.snapshotUpdateState !== "all" ? adapter.parseExpected(expectedSnapshot.data) : void 0;
7052
+ const timeoutController = new AbortController();
6306
7053
  const stableResult = await getStableSnapshot({
6307
7054
  adapter,
6308
- poll,
7055
+ poll: () => poll({ signal: timeoutController.signal }),
6309
7056
  interval,
6310
- timedOut: timeout > 0 ? new Promise((r) => setTimeout(r, timeout)) : void 0,
7057
+ timedOut: timeout > 0 ? new Promise((r) => setTimeout(() => {
7058
+ timeoutController.abort();
7059
+ r();
7060
+ }, timeout)) : void 0,
6311
7061
  match: reference ? (captured) => adapter.match(captured, reference).pass : void 0
6312
7062
  });
6313
7063
  expectedSnapshot.markAsChecked();