@time-provider/addon-eta
v0.3.1
Published
Time-Provider : ETA estimation addon ~ Your single time interface for all your JavaScript / TypeScript projects.
Maintainers
Readme
Time-Provider ~ ETA Addon
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 isstartTime + 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, witheta/remainingMillisecondsunset - there's nothing left to project forward.ratestays set to the last measured pace.
Both are idempotent: calling either again after a schedule has already ended is a no-op.
License
MIT
