New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@time-provider/addon-idle

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@time-provider/addon-idle

Idle callback addon for Time-Provider. Inject time. Control it in tests.

latest
Source
npmnpm
Version
0.3.0
Version published
Maintainers
1
Created
Source

NPM jsr jsr score types Node.js ^18.18 || >=20.4 Deno ~1.39.1 || >=1.43 module ESM + CJS CodeQL check codecov npm downloads dependencies unpacked-size minified size openssf best practices license

Time-Provider ~ Idle Addon

Time-Provider

Description

This is the Idle addon for Time-Provider.
It adds a scheduler.idle facade exposing the idle callback API (request, cancelled via dispose() on the returned handle), beside the scheduler.timers and scheduler.microtasks that core already provides.

Just like the plugin packages, this addon is tree-shakable.
It is split into a default (system/real-time) entry point and a deterministic one, so each import pulls in only the code it needs:

  • @time-provider/addon-idle - for a system (real time) Time-Provider created via @time-provider/core. .scheduler.idle.request passes through to the real requestIdleCallback, or throws a clear error when the host has no native equivalent (e.g. Safari) - cancelIdleCallback itself stays an internal detail; cancel by calling dispose() on the handle .scheduler.idle.request returns.
  • @time-provider/addon-idle/deterministic - for a deterministic Time-Provider (fixed/manual/sequential) created via @time-provider/core/deterministic. Requests made through .scheduler.idle.request stay pending until a test declares the runtime idle via .scheduler.idle.drain().

Usage

import { createTimeProvider } from "@time-provider/core";
import { createTimeProvider as createDeterministicTimeProvider } from "@time-provider/core/deterministic";
import { plugin } from "@time-provider/plugin-native";
import { plugin as deterministicPlugin } from "@time-provider/plugin-native/deterministic";
import { addon } from "@time-provider/addon-idle";
import { addon as deterministicAddon } from "@time-provider/addon-idle/deterministic";

// System: real requestIdleCallback under the hood (or a clear error if not available)
const timeProvider = createTimeProvider.for(plugin).use(addon).create();
timeProvider.scheduler.idle.request(() => console.log("Idle!"));

// Deterministic: requests stay pending until you declare the runtime idle
const manual = createDeterministicTimeProvider
  .for(deterministicPlugin)
  .use(deterministicAddon)
  .asManual()
  .withInitialTime(0)
  .create();
manual.scheduler.idle.request(() => console.log("Idle!"));
manual.scheduler.idle.drain(); // the idle callback runs here

// Like the native API, a callback receives a deadline and a request takes a timeout
manual.scheduler.idle.request((deadline) => console.log(deadline.didTimeout), { timeout: 1000 });
manual.clock.moveBy({ seconds: 1 }); // true: still pending at its timeout, so it ran anyway

Simulated idle periods

There is no such thing as a real idle period on a deterministic runtime - unlike a timeout, nothing about elapsed simulated time says the runtime has spare capacity - so request() just registers the callback under this addon's own tag in the runtime's shared due-heap. Clock moves and reads never fire it on their own; only drain() does, by retrieving up to maxCount pending requests (oldest first) directly through that tag - without scanning any other pending timer/interval/recurring entry sharing the heap:

manual.scheduler.timers.once({ milliseconds: 50 }, () => console.log("Busy!"));
manual.scheduler.idle.request(() => console.log("Idle!"));
manual.clock.moveBy({ milliseconds: 50 }); // "Busy!" - the idle request is still pending
manual.scheduler.idle.drain(); // "Idle!"

Omit maxCount to run everything currently pending, or pass it to cap how much idle work a single idle period allows through:

manual.scheduler.idle.request(() => console.log("first"));
manual.scheduler.idle.request(() => console.log("second"));
manual.scheduler.idle.drain(1); // "first" - "second" stays pending for the next drain

With the compat addon

Compose @time-provider/addon-compat before this addon and its .compat facade also gets requestIdleCallback/cancelIdleCallback, delegating to request and to the handle's dispose(). They are declared as an optional compat? on WithIdleApi, since they are only there when both addons are composed.

License

MIT

Keywords

clock

FAQs

Package last updated on 28 Sep 2026

Related posts