@timekeeper-countdown/core
v0.1.1
Published
Lightweight countdown engine that powers the Timekeeper packages. Pure TypeScript with no runtime dependencies.
Maintainers
Readme
@timekeeper-countdown/core
Lightweight countdown engine used by Timekeeper Countdown. This package exposes the finite-state machine, snapshot APIs, formatting helpers, and testing utilities that power the React hook and future framework adapters.
- Written in TypeScript with zero runtime dependencies.
- Ships modern ESM bundles and type definitions.
- Designed to run in browsers, Node.js, or custom runtimes.
- Tested with deterministic fake timers for reliable behavior.
Looking for the React hook? Install
@timekeeper-countdown/reactfor an idiomatic React API that wraps this engine.
Installation
npm install @timekeeper-countdown/coreThe published bundle is pure ESM. When targeting CommonJS environments use a bundler that understands type: "module" packages.
Supported runtimes:
- Node.js 18+
- Modern browsers (ES2022 modules)
Quick Start
High-level helper (Countdown)
import { Countdown, TimerState } from '@timekeeper-countdown/core';
const countdown = Countdown(300, {
onUpdate: (minutes, seconds) => {
timerElement.textContent = `${minutes}:${seconds}`;
},
onStateChange: state => {
if (state === TimerState.STOPPED) {
console.log('Finished!');
}
},
});
countdown.start(); // Begin the countdownCountdown wraps the lower-level engine and returns convenient methods:
countdown.start(); // boolean (false when invalid transition)
countdown.pause();
countdown.resume();
countdown.reset(nextInitialSeconds?);
countdown.stop();
countdown.destroy(); // dispose timers and listeners
countdown.getSnapshot(); // CountdownSnapshot
countdown.getCurrentState(); // TimerState
countdown.getMinutes(); // string e.g. "05"Low-level engine (CountdownEngine)
import { CountdownEngine, TimerState } from '@timekeeper-countdown/core';
const engine = CountdownEngine(90, {
tickIntervalMs: 50,
onSnapshot: snapshot => {
console.log(snapshot.totalSeconds);
},
onStateChange: (state, snapshot) => {
if (state === TimerState.STOPPED && snapshot.isCompleted) {
console.log('Done!');
}
},
onError: error => {
console.error('Timer failure', error);
},
});
engine.start();CountdownEngine exposes fine-grained control:
start,pause,resume,reset(nextInitialSeconds?),stop,setSeconds(value),destroygetSnapshot()returns the latest snapshot.subscribe(listener)emits the current snapshot immediately and on every tick.
Snapshot structure:
interface CountdownSnapshot {
initialSeconds: number; // starting value
totalSeconds: number; // remaining seconds, floor-clamped
parts: {
years: number;
weeks: number;
days: number;
hours: number;
minutes: number;
seconds: number;
totalDays: number;
totalHours: number;
totalMinutes: number;
};
state: TimerState; // IDLE | RUNNING | PAUSED | STOPPED
isRunning: boolean;
isCompleted: boolean;
}Formatting Helpers
Use the helpers exported at @timekeeper-countdown/core/format to avoid reimplementing padStart logic.
import { formatTime, formatMinutes, formatSeconds, Formatter } from '@timekeeper-countdown/core/format';
const snapshot = engine.getSnapshot();
formatTime(snapshot); // { minutes: "01", seconds: "30" }
formatMinutes(snapshot); // "01"
const formatter = Formatter();
formatter.formatHours(snapshot); // memoised string helpersAll helpers accept either a snapshot or any object that exposes totalSeconds.
Testing Utilities
The package includes utilities under @timekeeper-countdown/core/testing-utils to make unit tests deterministic.
import {
createFakeTimeProvider,
toTimeProvider,
buildSnapshot,
assertSnapshotState,
} from '@timekeeper-countdown/core/testing-utils';
const fake = createFakeTimeProvider({ startMs: 0, tickMs: 1000 });
const engine = CountdownEngine(5, {
timeProvider: toTimeProvider(fake),
tickIntervalMs: 5,
});
engine.start();
fake.advance(3000);
expect(engine.getSnapshot().totalSeconds).toBe(2);
const snapshot = buildSnapshot({ totalSeconds: 42 });
assertSnapshotState(snapshot, 'RUNNING');Utilities include:
createFakeTimeProvider/toTimeProviderfor manual clock control.buildSnapshot,assertSnapshotStatehelpers for quick snapshot fabrication.
Custom Time Providers
CountdownEngine accepts either:
- A function:
() => numberreturning milliseconds. - An object implementing the
TimeProviderinterface:{ now(): number; isHighResolution: boolean; type: string }.
This allows plugging in custom schedulers or synchronizing multiple engines.
const provider = {
now: () => performance.now(),
isHighResolution: true,
type: 'custom',
};
CountdownEngine(60, { timeProvider: provider });TypeScript Support
All exports are fully typed. Useful entry points:
import type {
CountdownSnapshot,
CountdownEngineOptions,
CountdownEngineInstance,
TimerState,
} from '@timekeeper-countdown/core';Documentation & Examples
- Monorepo overview: GitHub repository
- React hook guide:
@timekeeper-countdown/reactREADME - Complete docs site (guides, API reference, roadmap): https://eagle-head.github.io/timekeeper-countdown/
Contributing
Issues and pull requests are welcome. Please review the repository guidelines for more details and run:
npm run lint --workspaces
npm run test --workspaces
npm run typecheck --workspacesbefore submitting changes.
License
MIT © Eduardo Kohn
