Skip to content

feat: Add setDisableFakeTimersDetection to opt out of fake-timer fingerprinting#532

Open
nev21 wants to merge 1 commit into
mainfrom
nev21/FakeTimerDetect
Open

feat: Add setDisableFakeTimersDetection to opt out of fake-timer fingerprinting#532
nev21 wants to merge 1 commit into
mainfrom
nev21/FakeTimerDetect

Conversation

@nev21

@nev21 nev21 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

_isFakeTimersEnabled() detects Sinon-style fake timers by checking for a patched setTimeout.clock property, and uses that to decide whether deferred promise continuations hop via a 0ms timeout (fake-timer-friendly) or a real microtask. This fingerprint is always active in production, so any code that patches setTimeout and happens to expose a .clock property (not necessarily Sinon) silently changes the library's internal scheduling.

Rather than stripping the check via a #ifdef DEBUG build flag, add setDisableFakeTimersDetection(disable?: boolean) so consumers can opt out at runtime while keeping the affordance available by default. Passing undefined restores the default (detection enabled); any other value is coerced via !!.

…erprinting

_isFakeTimersEnabled() detects Sinon-style fake timers by checking for a patched setTimeout.clock property, and uses that to decide whether deferred promise continuations hop via a 0ms timeout (fake-timer-friendly) or a real microtask. This fingerprint is always active in production, so any code that patches setTimeout and happens to expose a .clock property (not necessarily Sinon) silently changes the library's internal scheduling.

Rather than stripping the check via a #ifdef DEBUG build flag, add setDisableFakeTimersDetection(disable?: boolean) so consumers can opt out at runtime while keeping the affordance available by default. Passing undefined restores the default (detection enabled); any other value is coerced via !!.
@nev21 nev21 added this to the 0.7.0 milestone Jul 10, 2026
@nev21 nev21 requested a review from a team as a code owner July 10, 2026 13:51
Copilot AI review requested due to automatic review settings July 10, 2026 13:51
@nev21 nev21 requested a review from a team as a code owner July 10, 2026 13:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a runtime opt-out for the library’s Sinon-style fake-timer detection (setTimeout.clock fingerprint) so consumers can force deferred continuations to use real microtasks even when a patched setTimeout.clock exists. This extends the existing internal scheduling behavior while keeping default behavior unchanged unless the new setter is called.

Changes:

  • Added setDisableFakeTimersDetection(disable?: boolean) and integrated it into _isFakeTimersEnabled() so detection can be disabled at runtime.
  • Exported the new API from the package root and added a dedicated test suite validating default/disabled/restored behavior under Sinon fake timers.
  • Documented the behavior and side effects in both README.md and docs/README.md, and added an unreleased changelog entry.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md Adds a top-level section pointing readers to the new fake-timer detection docs and opt-out API.
lib/test/src/promise/fakeTimersDetection.test.ts Adds tests covering default detection behavior, opt-out behavior, and restoring defaults.
lib/src/promise/itemProcessor.ts Implements the opt-out flag and exposes setDisableFakeTimersDetection(); updates fake-timer detection path.
lib/src/index.ts Exports setDisableFakeTimersDetection from the package root entrypoint.
docs/README.md Adds detailed documentation on fake timer detection, usage, and side effects.
CHANGELIST.md Adds an Unreleased feature entry describing the new API and its behavior.

Comment on lines +102 to +104
* @param disable - When `true` (or any other truthy value), disables fake timer detection so
* {@link _isFakeTimersEnabled} always returns `false`. When `false` (or any other falsy value),
* (re-)enables detection. When `undefined`, restores the default (detection enabled).
Comment thread docs/README.md
| `setDisableFakeTimersDetection(true)` | Disables the check -- the library always behaves as though fake timers are **not** present and always uses real microtask scheduling to defer work, even if a patched `setTimeout.clock` is present. |
| `setDisableFakeTimersDetection(false)` | Explicitly (re-)enables the check (same as the default). |
| `setDisableFakeTimersDetection()` / `setDisableFakeTimersDetection(undefined)` | Restores the default (detection enabled). |
| Any other value | Coerced to a boolean via `!!value`, so e.g. `setDisableFakeTimersDetection(1)` behaves the same as passing `true`. |
Comment thread CHANGELIST.md
- `createAsyncPromise` was re-wrapping the raw (potentially array) timeout as the chained promise's additional args on every `then()`/`catch()`/`finally()` link, growing an extra array-nesting level per link. Combined with the new depth cap this silently dropped an explicit timeout after ~5-6 chained calls. Fixed by normalizing the timeout once before it is stored, matching the existing `createIdlePromise` pattern, so nesting never exceeds one level regardless of chain length.
- [Feature] Add `setDisableFakeTimersDetection` to allow opting out of Sinon fake-timer fingerprinting
- New exported function `setDisableFakeTimersDetection(disable?: boolean)` allows a consumer to force the library's internal Sinon-fake-timer detection (a patched `setTimeout.clock` fingerprint used to decide whether deferred continuations use a `0ms` timeout or a microtask) to always report "not present", so deferred work always uses a real microtask.
- Default behavior (detection enabled) is unchanged when never called, or called with `undefined`. Any other value is coerced via `!!`.
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.66%. Comparing base (efbcfb2) to head (fe14baf).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #532      +/-   ##
==========================================
+ Coverage   97.57%   97.66%   +0.08%     
==========================================
  Files          28       28              
  Lines        1527     1540      +13     
  Branches      358      362       +4     
==========================================
+ Hits         1490     1504      +14     
+ Misses         37       36       -1     
Files with missing lines Coverage Δ
lib/src/promise/itemProcessor.ts 100.00% <100.00%> (+1.33%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nevware21-bot nevware21-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved by nevware21-bot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants