@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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/README.md +38 -198
  3. 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.0...HEAD
12
- [0.31.0]: https://github.com/pjm17971/pond-ts/compare/v0.30.0...v0.31.0
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
- **Highly optimised, fully typed Timeseries library for TypeScript**
3
+ **Fitness & activity analytics on [pond-ts](https://www.npmjs.com/package/pond-ts).**
4
4
 
5
- Schema-driven events, composable batch transforms, push-based streaming
6
- ingest, multi-entity partitioning, and an optional React integration —
7
- all strict TypeScript end to end, all immutable.
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 # core
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
- The full batch surface (`align`, `rolling`, `smooth`, `groupBy`, `join`,
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: live (streaming)
16
+ ## Quick start
84
17
 
85
18
  ```ts
86
- import { LiveSeries, Sequence } from 'pond-ts';
19
+ import { Activity, Distance } from '@pond-ts/fit';
87
20
 
88
- // 1. Same schema; this is a live append buffer with retention.
89
- const live = new LiveSeries({
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
- // 2. Push as events arrive. Each push is validated against the schema.
96
- live.push([Date.now(), 0.45, 128, 'api-1']);
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
- The full live surface (`filter`, `map`, `select`, `window`, `aggregate`,
107
- `rolling`, `reduce`, `diff`, `rate`, `pctChange`, `fill`, `cumulative`,
108
- `sample`) is incremental — events flow, views emit, retention bounds
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
- const perHost = cpu
118
- .partitionBy('host')
119
- .rolling('5m', { cpu: 'avg', cpu_sd: 'stdev' });
34
+ import { Profile } from '@pond-ts/fit';
120
35
 
121
- // .collect() fans the per-partition outputs back into a flat TimeSeries
122
- // with the partition key auto-injected as a column.
123
- const flat = perHost.collect();
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
- Same shape on the live side — `live.partitionBy('host')` returns a
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
- // Per-host 1-in-10 stride feeding a per-host 5m baseline.
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
- ```ts
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
- ## Performance
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
- ```sh
178
- npm run build && node packages/core/bench/vs-pondjs.cjs
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
- The full guide is at **<https://pjm17971.github.io/pond-ts/>**.
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.0",
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 ../../README.md ./README.md && cp ../../LICENSE ./LICENSE && cp ../../CHANGELOG.md ./CHANGELOG.md && npm run build && cp cjs-fallback.cjs dist/cjs-fallback.cjs && find dist -name '*.map' -delete",
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",