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

@time-provider/addon-eta

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@time-provider/addon-eta

Progress and ETA addon for Time-Provider. Inject time. Control it in tests.

latest
Source
npmnpm
Version
0.5.0
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
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 ~ ETA Addon

Time-Provider

Description

This is the ETA (estimated time of arrival) addon for Time-Provider. Extends the library with an .eta facade that periodically reports progress and a projected completion time for a long-running task.

Three mutually exclusive ways to start a schedule:

  • withKnownTotal(total) - report progress toward a single known total (bytes downloaded, rows processed, ...). The completion rate is estimated from reported progress over time, and the ETA is projected from that rate.
  • withStages(stages) - the same idea across several weighted stages of one job (e.g. download, then process, then finalize), reported as a single overall schedule.
  • withEstimatedDuration(expectedDurationMilliseconds) - no progress signal at all, just a rough prior of how long the job usually takes. The ETA is startTime + expectedDurationMilliseconds, fixed for the life of the schedule.

Progress reporting (progress()/progressTo()) is pure, O(1) bookkeeping - it never triggers a notification by itself. Notifications are delivered strictly on the configured interval (withNotificationInterval, defaulting to 1000ms), decoupling how often progress is reported from how often a consumer is notified.

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-eta - for a system (real time) Time-Provider created via @time-provider/core. Notifications run on real native timers.
  • @time-provider/addon-eta/deterministic - for a deterministic Time-Provider (fixed/manual/sequential) created via @time-provider/core/deterministic. Notifications run against that runtime's own simulated clock.

Usage

import { createTimeProvider } from "@time-provider/core";
import { plugin } from "@time-provider/plugin-native";
import { addon } from "@time-provider/addon-eta";

const timeProvider = createTimeProvider.for(plugin).use(addon).create();

const tracker = timeProvider.eta
  .estimate()
  .withKnownTotal(1_000_000) // bytes
  .withNotificationInterval(500)
  .start((snapshot) => {
    console.log(`${snapshot.percentage.toFixed(1)}% - ETA ${snapshot.eta}`);
  });

tracker.progress(65_536); // a chunk just arrived
// ...
tracker.done(); // one final notification, snapped to 100%

Multi-stage schedules

withStages tracks several stages of one job as a single overall schedule. Each stage declares its own total (in whatever unit fits that stage - bytes, then rows, then steps) and a weight relative to the other stages, normalized by dividing by their sum (not required to sum to any particular total):

const tracker = timeProvider.eta
  .estimate()
  .withStages([
    { weight: 2, total: downloadBytes }, // twice as significant as what follows
    { weight: 1, total: processRows },
  ])
  .start((snapshot) => {
    // stageCompleted/stageTotal/stagePercentage are local to the current stage (its own unit) -
    // a per-stage progress bar. rate/eta/remainingMilliseconds are overall, weighted across every
    // stage.
    console.log(`stage ${snapshot.currentStageIndex + 1}/${snapshot.stageCount}`);
  });

tracker.progressTo(downloadBytes); // finish the download stage
tracker.nextStage(); // move on to processing, progress resets to 0
tracker.progress(1);

Different stages can use different raw units, which can't be meaningfully summed into one figure - so a staged snapshot's fields deliberately don't share names with a non-staged one: stageCompleted/stageTotal/stageRemaining/stagePercentage stay local to whichever stage is current, while rate/eta/remainingMilliseconds are weighted across every stage (unit-less/normalized figures, which can be combined), answering "when does the whole job finish" rather than just the current stage.

Estimating a completion rate

withAlgorithm picks how the completion rate is derived from reported progress (defaults to "windowed"):

  • "complete" - averaged over the entire tracked history, from the start. Simple and stable, but a slow start (or a mid-run change of pace) permanently drags the estimate.
  • "windowed" - averaged over only the most recently reported progress, discarding older samples. Reacts to a change in pace faster than "complete".
  • "smoothed" - a continuously blended running average, weighted toward more recent reports without discarding older ones outright.

rate itself is always a fraction of the whole job (0-1) per millisecond, never scaled to the tracked unit - that's what lets a multi-stage schedule combine stages using different units into one figure.

Ending a schedule

  • done() - marks the job complete. The final notification reports 100% regardless of the last reported amount, and no further notifications follow.
  • abandon() - calls off tracking without completing it. The final notification reports whatever progress was last recorded, as-is, with eta/remainingMilliseconds unset - there's nothing left to project forward. rate stays set to the last measured pace.

Both are idempotent: calling either again after a schedule has already ended is a no-op.

License

MIT

Keywords

clock

FAQs

Package last updated on 28 Sep 2026

Related posts