@pond-ts/fit 0.31.0 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -2
- package/README.md +38 -198
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,8 +8,9 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`, and
|
|
|
8
8
|
them all. Pre-1.0: minor bumps may include new features and type-level changes;
|
|
9
9
|
patch bumps are strictly additive.
|
|
10
10
|
|
|
11
|
-
[Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.31.
|
|
12
|
-
[0.31.
|
|
11
|
+
[Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.31.1...HEAD
|
|
12
|
+
[0.31.1]: https://github.com/pjm17971/pond-ts/compare/v0.30.0...v0.31.1
|
|
13
|
+
[0.31.0]: https://github.com/pjm17971/pond-ts/compare/v0.30.0...3c4e8bd
|
|
13
14
|
[0.30.0]: https://github.com/pjm17971/pond-ts/compare/v0.29.0...v0.30.0
|
|
14
15
|
[0.29.0]: https://github.com/pjm17971/pond-ts/compare/v0.28.0...v0.29.0
|
|
15
16
|
[0.28.0]: https://github.com/pjm17971/pond-ts/compare/v0.27.0...v0.28.0
|
|
@@ -24,6 +25,15 @@ patch bumps are strictly additive.
|
|
|
24
25
|
[0.19.0]: https://github.com/pjm17971/pond-ts/compare/v0.18.0...v0.19.0
|
|
25
26
|
[0.18.0]: https://github.com/pjm17971/pond-ts/compare/v0.17.1...v0.18.0
|
|
26
27
|
|
|
28
|
+
## [0.31.1] — 2026-06-28
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **`@pond-ts/charts` and `@pond-ts/fit` now ship their own README** on npm.
|
|
33
|
+
0.31.0 inadvertently published the `pond-ts` core README on every package
|
|
34
|
+
(each `prepack` copied the repo-root README); charts and fit now carry their
|
|
35
|
+
own. No code or API changes.
|
|
36
|
+
|
|
27
37
|
## [0.31.0] — 2026-06-28
|
|
28
38
|
|
|
29
39
|
First published release of **`@pond-ts/charts`** and **`@pond-ts/fit`** (both were
|
package/README.md
CHANGED
|
@@ -1,228 +1,68 @@
|
|
|
1
|
-
# pond-ts
|
|
1
|
+
# @pond-ts/fit
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Fitness & activity analytics on [pond-ts](https://www.npmjs.com/package/pond-ts).**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**pond-ts** is the TypeScript-first successor to
|
|
10
|
-
[pondjs](https://github.com/esnet/pond), rewritten from scratch with a
|
|
11
|
-
focus on type safety, composability, and the live-streaming patterns
|
|
12
|
-
that pondjs never grew.
|
|
5
|
+
Turn raw activity streams (GPS, power, heart rate, cadence, …) into a typed,
|
|
6
|
+
immutable activity model with unit-safe quantities and the analytics you'd
|
|
7
|
+
expect — splits, power (NP / IF / TSS / curve), zones, elevation, best efforts —
|
|
8
|
+
behind a small `Activity` / `Section` façade.
|
|
13
9
|
|
|
14
10
|
```sh
|
|
15
|
-
npm install pond-ts
|
|
16
|
-
npm install @pond-ts/react # React hooks (optional)
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
- **Typed schemas** — declare once, every transform downstream narrows
|
|
20
|
-
off it. `event.get('cpu')` returns `number | undefined` straight from
|
|
21
|
-
the schema; no `as` casts.
|
|
22
|
-
- **Batch + streaming with the same vocabulary** — `filter`, `map`,
|
|
23
|
-
`aggregate`, `rolling`, `diff`, `rate`, `fill`, `cumulative`,
|
|
24
|
-
`sample`, `reduce` all exist on both `TimeSeries` and `LiveSeries`.
|
|
25
|
-
- **Multi-entity by construction** — `partitionBy('host')` routes per
|
|
26
|
-
entity; `rolling` / `aggregate` / `fill` / `sample` over a partitioned
|
|
27
|
-
view all become per-entity automatically.
|
|
28
|
-
- **Bounded-memory streaming** — retention policies, eviction-aware
|
|
29
|
-
views, and sampling decouple downstream window length
|
|
30
|
-
from event rate at firehose loads (up to 500k events/sec on a
|
|
31
|
-
single node.js instance.)
|
|
32
|
-
- **Triggers** — for control of rolling emission cadences. Synchronised
|
|
33
|
-
partitioned rolling fires across partitions on every boundary.
|
|
34
|
-
- **Typed column extraction** — `series.column('cpu')` returns a
|
|
35
|
-
schema-narrowed typed column with single-pass reductions
|
|
36
|
-
(`min`/`max`/`sum`/`mean`/`stdev`/`median`/`percentile`/`minMax`),
|
|
37
|
-
index downsampling (`bin`), and a zero-copy `toFloat64Array()` for
|
|
38
|
-
canvas / WebGL draw loops — no per-event allocation on the hot path.
|
|
39
|
-
- **No legacy baggage**
|
|
40
|
-
|
|
41
|
-
## Quick start: batch
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
import { Sequence, TimeSeries } from 'pond-ts';
|
|
45
|
-
|
|
46
|
-
const schema = [
|
|
47
|
-
{ name: 'time', kind: 'time' },
|
|
48
|
-
{ name: 'cpu', kind: 'number' },
|
|
49
|
-
{ name: 'requests', kind: 'number' },
|
|
50
|
-
{ name: 'host', kind: 'string' },
|
|
51
|
-
] as const;
|
|
52
|
-
|
|
53
|
-
const cpu = TimeSeries.fromJSON({
|
|
54
|
-
name: 'cpu',
|
|
55
|
-
schema,
|
|
56
|
-
rows: [
|
|
57
|
-
['2025-01-01T00:00:00Z', 0.31, 120, 'host1'],
|
|
58
|
-
['2025-01-01T00:01:00Z', 0.44, 135, 'host2'],
|
|
59
|
-
['2025-01-01T00:02:00Z', 0.52, 141, 'host1'],
|
|
60
|
-
['2025-01-01T00:03:00Z', 0.48, 128, 'host1'],
|
|
61
|
-
['2025-01-01T00:04:00Z', 0.63, 166, 'host3'],
|
|
62
|
-
],
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const byMinute = cpu.aggregate(Sequence.every('1m'), {
|
|
66
|
-
cpu: 'avg',
|
|
67
|
-
requests: 'sum',
|
|
68
|
-
host: 'last',
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const bands = cpu.baseline('cpu', { window: '2m', sigma: 2 });
|
|
72
|
-
// ^ appends rolling avg / sd / upper / lower in one pass.
|
|
73
|
-
|
|
74
|
-
const anomalies = cpu.outliers('cpu', { window: '2m', sigma: 2 });
|
|
75
|
-
// ^ schema-preserving filter — same columns, just the spikes.
|
|
11
|
+
npm install @pond-ts/fit pond-ts
|
|
76
12
|
```
|
|
77
13
|
|
|
78
|
-
|
|
79
|
-
`reduce`, `diff`, `rate`, `fill`, `dedupe`, `materialize`, `sample`,
|
|
80
|
-
`partitionBy`, `pivotByGroup`, …) follows the same shape: TimeSeries
|
|
81
|
-
in, TimeSeries out, schema preserved.
|
|
14
|
+
`pond-ts` is a peer dependency.
|
|
82
15
|
|
|
83
|
-
## Quick start
|
|
16
|
+
## Quick start
|
|
84
17
|
|
|
85
18
|
```ts
|
|
86
|
-
import {
|
|
19
|
+
import { Activity, Distance } from '@pond-ts/fit';
|
|
87
20
|
|
|
88
|
-
//
|
|
89
|
-
const
|
|
90
|
-
name: 'cpu',
|
|
91
|
-
schema,
|
|
92
|
-
retention: { maxAge: '10m' }, // keep only the last 10 minutes
|
|
93
|
-
});
|
|
21
|
+
// `imported` is an ImportedActivity — your raw streams (time, lat/lon, power, hr, …)
|
|
22
|
+
const activity = Activity.fromStreams(imported);
|
|
94
23
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// 3. Compose live views — incremental, push-driven, eviction-aware.
|
|
99
|
-
const recentAvg = live.rolling('5m', { cpu: 'avg' });
|
|
100
|
-
recentAvg.on('event', (e) => render(e.get('cpu')));
|
|
101
|
-
|
|
102
|
-
// 4. Snapshot to a TimeSeries for batch analytics at any time.
|
|
103
|
-
const snap = live.toTimeSeries();
|
|
24
|
+
activity.summary(); // { distance, duration, elevation, avg/max metrics, … }
|
|
25
|
+
activity.splits(Distance.km(1)); // per-kilometre Section[]
|
|
26
|
+
activity.power(260); // PowerSummary at FTP 260 W — NP, IF, TSS, curve, zones
|
|
104
27
|
```
|
|
105
28
|
|
|
106
|
-
|
|
107
|
-
`
|
|
108
|
-
|
|
109
|
-
memory.
|
|
110
|
-
|
|
111
|
-
## Quick start: multi-entity
|
|
112
|
-
|
|
113
|
-
`partitionBy` routes events into per-key buffers. Every stateful
|
|
114
|
-
operator downstream of `partitionBy` runs per-partition automatically:
|
|
29
|
+
Attach an athlete `Profile` for zone-aware, type-safe analytics — the
|
|
30
|
+
`ProfiledActivity` knows the athlete, so power/zone calls need no thresholds
|
|
31
|
+
passed in:
|
|
115
32
|
|
|
116
33
|
```ts
|
|
117
|
-
|
|
118
|
-
.partitionBy('host')
|
|
119
|
-
.rolling('5m', { cpu: 'avg', cpu_sd: 'stdev' });
|
|
34
|
+
import { Profile } from '@pond-ts/fit';
|
|
120
35
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
36
|
+
const profiled = activity.usingProfile(
|
|
37
|
+
Profile.of({ ftpWatts: 260, weightKg: 72 }),
|
|
38
|
+
);
|
|
39
|
+
profiled.power(); // zone-aware — FTP comes from the profile
|
|
40
|
+
profiled.splits(Distance.km(1)); // ProfiledSection[] — slices carry the profile through
|
|
124
41
|
```
|
|
125
42
|
|
|
126
|
-
|
|
127
|
-
`LivePartitionedSeries` whose `rolling` / `fill` / `diff` / `sample`
|
|
128
|
-
methods all maintain per-partition state.
|
|
129
|
-
|
|
130
|
-
## Quick start: bounded-memory sampling
|
|
131
|
-
|
|
132
|
-
At firehose rates, a long rolling baseline blows the heap. `sample({
|
|
133
|
-
stride: N })` decouples baseline length from event rate; chain it
|
|
134
|
-
between `partitionBy` and `rolling`:
|
|
43
|
+
Quantities are unit-safe and format themselves:
|
|
135
44
|
|
|
136
45
|
```ts
|
|
137
|
-
|
|
138
|
-
live
|
|
139
|
-
.partitionBy('host')
|
|
140
|
-
.sample({ stride: 10 })
|
|
141
|
-
.rolling('5m', { cpu_avg: 'avg', cpu_sd: 'stdev' });
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
For visualization, the snapshot side ships reservoir sampling too —
|
|
145
|
-
single-pass Algorithm R, sorted by key, fixed point count regardless of
|
|
146
|
-
source size:
|
|
46
|
+
import { Speed } from '@pond-ts/fit';
|
|
147
47
|
|
|
148
|
-
|
|
149
|
-
const points = series.sample({ reservoir: { size: 500 } }).toRows();
|
|
150
|
-
// 500 uncorrelated points drawn uniformly from the source.
|
|
48
|
+
Speed.mps(5.5).format('imperial'); // "12.3 mph"
|
|
151
49
|
```
|
|
152
50
|
|
|
153
|
-
##
|
|
154
|
-
|
|
155
|
-
pond-ts is faster on **every** comparable operation, with no regressions —
|
|
156
|
-
a **~17x** geometric-mean speedup across the measurable ops, plus a handful
|
|
157
|
-
of transforms (`select` / `rename`) that are **effectively instant** (O(1)
|
|
158
|
-
column rebinds, below the timer's resolution). The advantage grows with data
|
|
159
|
-
size.
|
|
160
|
-
|
|
161
|
-
| Category | Speedup (N=16k) | Notes |
|
|
162
|
-
| ----------------- | ---------------------------------------------------- | --------------------------------------------- |
|
|
163
|
-
| **Rate** | ~120x | Single columnar walk vs Pipeline |
|
|
164
|
-
| **Fill** | 77–87x | Single columnar pass vs Pipeline per strategy |
|
|
165
|
-
| **Aggregation** | 57–82x | O(N+B) bucketing vs O(N×B) Pipeline |
|
|
166
|
-
| **Statistics** | 18–80x | Typed-array reduce vs ImmutableJS iteration |
|
|
167
|
-
| **Alignment** | 42x | Forward cursor vs repeated binary search |
|
|
168
|
-
| **Construction** | 13x | Columnar intake vs ImmutableJS wrapping |
|
|
169
|
-
| **Chained** | 8x | Derived constructors vs per-step Pipeline |
|
|
170
|
-
| **Transforms** | `select`/`rename` instant; `collapse` 30x; `map` ~4x | Column reshapes vs Pipeline |
|
|
171
|
-
| **Event access** | 6x | Array indexing vs ImmutableJS `get()` |
|
|
172
|
-
| **Serialization** | 4x | Lightweight columnar representation |
|
|
173
|
-
|
|
174
|
-
See the [full benchmark results](website/docs/reference/benchmarks.mdx)
|
|
175
|
-
for detailed numbers. Run locally:
|
|
51
|
+
## What's in the box
|
|
176
52
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
53
|
+
- **Façade** — `Activity` / `Section`: load once, then ask for summary / splits /
|
|
54
|
+
laps / ranges.
|
|
55
|
+
- **Quantities** — `Distance` / `Speed` / `Pace` / `Power` / `HeartRate` /
|
|
56
|
+
`Cadence` / … with unit conversions and `.format()`.
|
|
57
|
+
- **Analytics** — geo (distance, elevation, best efforts), power (NP / IF / TSS /
|
|
58
|
+
curve), and zone distributions.
|
|
59
|
+
- **Profiles** — `Profile` + `usingProfile()` → type-safe `ProfiledActivity` /
|
|
60
|
+
`ProfiledSection`.
|
|
180
61
|
|
|
181
62
|
## Documentation
|
|
182
63
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
- **[Start here](https://pjm17971.github.io/pond-ts/docs/)**
|
|
186
|
-
— five-minute walkthrough with batch, live, and React examples.
|
|
187
|
-
- **[Concepts](https://pjm17971.github.io/pond-ts/docs/start-here/concepts)**
|
|
188
|
-
— temporal keys, sequences, windowing, partitioning, triggers, late
|
|
189
|
-
data.
|
|
190
|
-
- **[Transforms reference](https://pjm17971.github.io/pond-ts/docs/pond-ts/transforms/queries)**
|
|
191
|
-
— every batch operator (queries, aggregation, alignment, rolling,
|
|
192
|
-
smoothing, sampling, cleaning, reshape, anomaly detection).
|
|
193
|
-
- **[Live reference](https://pjm17971.github.io/pond-ts/docs/pond-ts/live/live-series)**
|
|
194
|
-
— `LiveSeries`, live transforms, triggering.
|
|
195
|
-
- **[How-to guides](https://pjm17971.github.io/pond-ts/docs/how-to-guides)**
|
|
196
|
-
— building a dashboard, ingesting messy data.
|
|
197
|
-
- **[API reference (auto-generated)](https://pjm17971.github.io/pond-ts/generated-api/core/)**
|
|
198
|
-
— TypeDoc output, every public class and method.
|
|
199
|
-
- **[CHANGELOG](./CHANGELOG.md)** — what shipped in each release.
|
|
200
|
-
|
|
201
|
-
## Examples
|
|
202
|
-
|
|
203
|
-
- **[pond-ts-dashboard](https://github.com/pjm17971/pond-ts-dashboard)**
|
|
204
|
-
— a working React dashboard that streams synthetic per-host CPU /
|
|
205
|
-
request metrics, computes per-host rolling baselines, flags anomalies
|
|
206
|
-
against ±σ bands, and renders everything as live line and bar charts
|
|
207
|
-
(~600 lines of TypeScript). Walked through end-to-end in
|
|
208
|
-
[Building a dashboard](website/docs/how-to-guides/dashboard-guide.mdx).
|
|
209
|
-
|
|
210
|
-
## Develop
|
|
211
|
-
|
|
212
|
-
The repo is an npm-workspaces monorepo with two published packages
|
|
213
|
-
(`pond-ts`, `@pond-ts/react`). Node 18+ for runtime; Node 20+ for the
|
|
214
|
-
docs site (Docusaurus).
|
|
215
|
-
|
|
216
|
-
```sh
|
|
217
|
-
npm install # one-time, hoists deps for both packages
|
|
218
|
-
npm run build # build both packages
|
|
219
|
-
npm test # runtime + type-level tests on both packages
|
|
220
|
-
npm run format # prettier write across the repo
|
|
221
|
-
npm run verify # format check + build + test (CI parity)
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
`packages/core/` is the `pond-ts` package; `packages/react/` is
|
|
225
|
-
`@pond-ts/react`. Docs live in `website/`.
|
|
64
|
+
Guides and the full API live at **<https://pjm17971.github.io/pond-ts/>**.
|
|
65
|
+
Source and issues: [github.com/pjm17971/pond-ts](https://github.com/pjm17971/pond-ts).
|
|
226
66
|
|
|
227
67
|
## License
|
|
228
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/fit",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Fitness & activity domain library on pond-ts — quantities, canonical activity series, and analytics (geo, power, zones, splits)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"build": "tsc -p tsconfig.json",
|
|
31
31
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
32
32
|
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
33
|
-
"prepack": "cp ../../
|
|
33
|
+
"prepack": "cp ../../LICENSE ./LICENSE && cp ../../CHANGELOG.md ./CHANGELOG.md && npm run build && cp cjs-fallback.cjs dist/cjs-fallback.cjs && find dist -name '*.map' -delete",
|
|
34
34
|
"test": "npm run test:type && npm run test:runtime",
|
|
35
35
|
"test:type": "tsc -p tsconfig.types.json",
|
|
36
36
|
"test:runtime": "vitest run",
|