@pond-ts/react 0.20.0 → 0.22.0

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 (2) hide show
  1. package/CHANGELOG.md +104 -1
  2. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,13 +7,116 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
7
7
  file covers both packages. Pre-1.0: minor bumps may include new features and
8
8
  type-level changes; patch bumps are strictly additive.
9
9
 
10
- [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...HEAD
10
+ [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.22.0...HEAD
11
+ [0.22.0]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...v0.22.0
12
+ [0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
11
13
  [0.20.0]: https://github.com/pjm17971/pond-ts/compare/v0.19.0...v0.20.0
12
14
  [0.19.0]: https://github.com/pjm17971/pond-ts/compare/v0.18.0...v0.19.0
13
15
  [0.18.0]: https://github.com/pjm17971/pond-ts/compare/v0.17.1...v0.18.0
14
16
 
15
17
  ## [Unreleased]
16
18
 
19
+ ## [0.22.0] — 2026-06-12
20
+
21
+ ### Changed
22
+
23
+ - **`asTime` / `asTimeRange` / `asInterval` are now column-native.** They
24
+ reinterpret the key's kind (a "rekey") straight off the existing key's
25
+ `begin` / `end` buffers instead of materializing events — value columns pass
26
+ through by reference. `asTimeRange` and `asTime` with `begin` / `end` reuse
27
+ the key buffer zero-copy (≈ **9×** faster on a build → rekey → read pipeline);
28
+ `asTime({ at: 'center' })` adds one midpoint pass; `asInterval` builds the
29
+ label column (string → `StringColumn`, number → `Float64Column`, inferred
30
+ from the first label and required consistent across rows). `asTime` with
31
+ `center` / `end` throws if anchoring a source with overlapping extents would
32
+ produce a non-monotonic time axis (preserving the prior validation — `begin`
33
+ is always sorted and is exempt).
34
+ - **Breaking: `asInterval`'s label function now receives the interval's
35
+ `TimeRange` (its `[begin, end]` extent) and index — not the whole `Event`.**
36
+ The canonical form is unchanged: `series.asInterval(range => range.begin())`
37
+ works exactly as before (both `Event` and `TimeRange` expose `begin()` /
38
+ `end()`). Only a label fn that read a _value column_ off the event (e.g.
39
+ `event => event.get('label')`) needs rewriting — compute the label before
40
+ `asInterval`, or derive it from the extent. The constant form
41
+ (`asInterval('bucket')` / `asInterval(42)`) is unaffected. (Pre-1.0 minor;
42
+ this is the change that lets the function form stay on the columnar path.)
43
+
44
+ ### Fixed
45
+
46
+ - **`mapColumns` rejects a non-finite numeric result at write.** A mapper on a
47
+ `number` column that returns `NaN` or `±Infinity` now throws a `RangeError`,
48
+ consistent with construction intake (which already rejects non-finite
49
+ numbers). Previously the value was packed into the column, where the reduce
50
+ fast path and the row path could disagree on the same bucket (e.g.
51
+ `aggregate('min')` returning a different result depending on which path ran).
52
+ A stored `NaN` is still a defined value the mapper sees — map it to a finite
53
+ number, or to `undefined` (missing), to clean it. (Closes a hole introduced
54
+ alongside `mapColumns` in 0.21.0.)
55
+ - **`aggregate('stdev')` is now numerically stable and path-independent.** The
56
+ bucketed row path (`bucketState`) used a one-pass `sq/n − mean²` accumulator
57
+ that cancels catastrophically on near-equal large-magnitude values —
58
+ returning `0` (e.g. `[1e10, 1e10+1, 1e10+2, 1e10+3]` → `0` instead of
59
+ `≈1.118`), or a negative variance whose `sqrt` is `NaN` that the validating
60
+ constructor then rejected with a throw. Because the columnar fast path is
61
+ all-or-nothing, an unrelated mapping (e.g. a `count` over a string column)
62
+ could silently flip the _same_ series' stdev. All three batch paths (`reduce`,
63
+ `reduceColumn`, `bucketState`) now share **one Welford recurrence** — O(1) per
64
+ element, no buffer (so the live aggregation path that shares `bucketState`
65
+ stays O(1)), `m2 ≥ 0` by construction — so they agree regardless of magnitude.
66
+ (Even the prior two-pass `Σv/n`-then-deviations drifted ~8.7% from the true
67
+ value at `2^52`, where the summed mean rounds — so unifying on Welford, not
68
+ two-pass, was necessary.) **Correction:** 0.21.0's columnar `aggregate()` fast
69
+ path (#186) was described as "signature + semantics unchanged", but it did
70
+ change released `stdev` output for fast-path-qualifying aggregates; this fix
71
+ makes every path agree. (`rolling`/`smooth` stdev keep the one-pass form for
72
+ now — a separate, deferred item.)
73
+
74
+ ## [0.21.0] — 2026-06-11
75
+
76
+ ### Added
77
+
78
+ - **`TimeSeries.mapColumns({ col: (value) => newValue })`** — a per-cell column
79
+ value transform. The column-scoped counterpart of the event-based `map()`:
80
+ where `map(schema, event => newEvent)` rebuilds whole rows through an
81
+ arbitrary closure (and can change the schema/key), `mapColumns` transforms
82
+ individual columns' values in place, reading the columns directly (no
83
+ per-row `Event`) so it stays on the fast columnar path. Same kind in/out
84
+ (number→number, string→string, …), so the schema is unchanged; missing cells
85
+ carry (the mapper isn't called on `undefined`). ~5–6× faster than the
86
+ `map()` workaround on a build → transform → read pipeline.
87
+
88
+ ### Changed
89
+
90
+ - **`select` / `rename` / `slice` / `cumulative` / `diff` / `rate` /
91
+ `pctChange` / `fill` / `shift` / `collapse` are now column-native.** They
92
+ reshape the columnar store directly instead of materializing events, so the
93
+ columnar construction win is preserved through these transforms — build →
94
+ transform → read pipelines run several× faster (~7–10× for `select` /
95
+ `rename` / `slice`; ~5–7× for the `cumulative` / `diff` / `rate` / `fill` /
96
+ `shift` / `collapse` folds). No API change for type-correct callers (one
97
+ narrow `fill` behavior change is noted under Fixed). `cumulative` / `diff` /
98
+ `rate` / `pctChange` / `fill` / `shift` / `collapse` are also the first
99
+ operators extracted into `batch/operators/` (internal refactor); `fill`
100
+ rebuilds only the columns it actually changes; `slice` normalizes
101
+ `Array.prototype.slice` semantics onto a zero-copy `withRowRange` reshape;
102
+ `collapse` reads only the keyed columns and passes the kept columns through
103
+ by reference.
104
+
105
+ ### Fixed
106
+
107
+ - **`rename` now rejects target-name collisions** (e.g. renaming `a` → `b`
108
+ when `b` already exists) with a clear error, instead of silently producing a
109
+ duplicate-named schema. Also fixes a prototype-chain bug where a column named
110
+ `toString` (or another `Object.prototype` member) could be corrupted during
111
+ a rename.
112
+ - **`fill` now throws on a kind-mismatched literal** (e.g.
113
+ `fill({ value: 'banana' })` where `value` is numeric — type-allowed because
114
+ mapping values are the broad `FillStrategy | ScalarValue`) with a clear
115
+ `RangeError` naming the column, instead of silently producing an
116
+ internally-inconsistent series (the old events path returned the literal
117
+ from `.get()` while the numeric column read `NaN`). The throw is
118
+ gap-dependent — it only fires when the literal would actually be placed.
119
+
17
120
  ## [0.20.0] — 2026-06-04
18
121
 
19
122
  Two internal performance improvements driven by the dashboard experiment at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pond-ts/react",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "React hooks for pond-ts live time series",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "test:runtime": "vitest run"
33
33
  },
34
34
  "peerDependencies": {
35
- "pond-ts": "^0.20.0",
35
+ "pond-ts": "^0.22.0",
36
36
  "react": "^18.0.0 || ^19.0.0"
37
37
  },
38
38
  "devDependencies": {