@pond-ts/fit
A fitness / activity domain library on pond-ts. The name reads fitness
(with the happy bonus that it parses .fit files); it is not a FIT-format
parser — file parsing is just one input. Its job is "Strava as an API": free,
ergonomic tools to work with your own activity data — parse it, analyze it,
slice it, convert units — with a fluent object model over pond's generic
TimeSeries.
Install — npm install pond-ts @pond-ts/fit. Peer-depends on pond-ts.
import { Activity, Distance, Duration } from '@pond-ts/fit';
const activity = Activity.fromStreams(imported); // .fromFit / .fromGpx land with ingest
activity.meta.sportType; // "Ride"
activity.distance().miles; // Distance — .miles / .km / .meters
activity.movingTime().format(); // "7:00:54"
const splits = activity.splits(Distance.miles(1)); // Section[]
splits[3].normalizedPower()?.watts; // 291
splits[3].avgSpeed()?.pace().asMinsPerMile(); // "10:31/mi"
const window = activity.range(Duration.minutes(20), Duration.minutes(40));
window.elevationGain().feet;
activity.power(ftp); // PowerSummary | undefined (no power recorded)
activity.bestEfforts({ weightKg }); // PowerEffort[] (+ W/kg)
activity.at(Duration.minutes(20)); // interpolated channel Sample
The stack (three layers)
pond-ts generic TimeSeries + operators (rolling, byColumn, fill, validity)
└─ @pond-ts/fit the FITNESS domain:
• quantity types Distance, Duration, Speed/Pace, Power, HeartRate, Cadence, Elevation
• canonical series the activity TimeSeries schema (one SI-normalized column vocabulary)
• operators (pure) geo (track/splits/profiles/best-efforts), power (NP/curve/zones), zones
• Activity / Section a thin ERGONOMIC façade over the operators + quantities
The OO façade is a thin skin over a functional core. activity.powerCurve()
delegates to a pure powerCurve(series, …); section.normalizedPower() to
normalizedPower(series). Logic lives in composable, tested functions;
Activity/Section add ergonomics + memoization. Drop to the functional layer
(activity.timeSeries()) or stay fluent — both first-class.
Design tenets
- Profile-agnostic. Anything needing FTP / body weight / zone definitions
takes them as arguments (the caller resolves them from the athlete
profile as-of the activity date).
Activitywraps only the activity's own evidence. - Gap-honest. Sustained coasts/dropouts break the line (NaN), never interpolate across a hole; missing channels are absent, not zero.
- Ingest membrane. Messy formats in (Strava exports,
.fit/.gpx/.tcx, dropped samples,-1/255sentinels, unit quirks), one clean canonical activity out — wrangled once, here, at ingest.
Where to start
- API reference — the generated full-width
reference: every quantity,
Activity/Section, and thegeo/power/zones/profile/summaryoperator surfaces.
This package is young — the docs here are an introduction plus the generated type reference. Guides (quantities, the canonical series, the operator core) follow as the API settles.