@pond-ts/react 0.13.2 → 0.14.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 +193 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,202 @@ 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.
|
|
10
|
+
[Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.14.1...HEAD
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.14.1] — 2026-05-03
|
|
15
|
+
|
|
16
|
+
The "samples reducer + lifted custom-fn guard" release. Surfaced by
|
|
17
|
+
the gRPC experiment's step-4 (anomaly density) walkback: the use
|
|
18
|
+
case "compute counts of values exceeding `k·σ` from a baseline" needs
|
|
19
|
+
the **raw values** from the rolling window, but pond's existing
|
|
20
|
+
built-ins all collapse to scalars or deduplicate. Custom-function
|
|
21
|
+
reducers — which would cover the use case cleanly — worked on batch
|
|
22
|
+
but were rejected at runtime on live with a `TypeError` pointing at
|
|
23
|
+
`AggregateOutputMap` aliases (which don't actually solve "all values"
|
|
24
|
+
either). Two related changes ship together to close both gaps.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`'samples'` built-in reducer.** Returns the bucket's defined
|
|
29
|
+
values as an array, in arrival order, with duplicates preserved.
|
|
30
|
+
Sits beside `'unique'` (which deduplicates) and `'top${N}'` (which
|
|
31
|
+
bounds and frequency-orders) — same array-output kind, same
|
|
32
|
+
type-system narrowing through `AggregateOutputMap`. Library-
|
|
33
|
+
implemented; per-event cost is O(1) `add` / O(1) `remove`
|
|
34
|
+
(Map-keyed by event index); `snapshot` is O(N) array copy.
|
|
35
|
+
Memory O(window size).
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// Anomaly density: count samples > k·σ from a separate baseline.
|
|
39
|
+
const stats = live.rolling(
|
|
40
|
+
'1m',
|
|
41
|
+
{
|
|
42
|
+
mean: { from: 'cpu', using: 'avg' },
|
|
43
|
+
sd: { from: 'cpu', using: 'stdev' },
|
|
44
|
+
},
|
|
45
|
+
{ trigger: Trigger.every('30s') },
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const recent = live.rolling('200ms', {
|
|
49
|
+
vals: { from: 'cpu', using: 'samples' },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// At each tick, count threshold crossings against the baseline:
|
|
53
|
+
stats.on('event', (e) => {
|
|
54
|
+
const samples = recent.value().vals as ReadonlyArray<number>;
|
|
55
|
+
const counts = thresholds.map(
|
|
56
|
+
(k) => samples.filter((v) => v - e.get('mean') > k * e.get('sd')).length,
|
|
57
|
+
);
|
|
58
|
+
// ... emit anomaly density
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Like `unique`, `samples` flattens one level on array-kind source
|
|
63
|
+
columns. Returns `[]` for an empty bucket.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- **Custom-function reducers now work on live.** Removed the runtime
|
|
68
|
+
`TypeError` guards on `LiveAggregation`, `LiveRollingAggregation`,
|
|
69
|
+
and `LivePartitionedSyncRolling` that previously rejected
|
|
70
|
+
function-typed reducers. New `bucketStateFor` and `rollingStateFor`
|
|
71
|
+
helpers in `reducers/index.ts` route built-ins to their dedicated
|
|
72
|
+
O(1) machinery and wrap custom functions in a generic adapter:
|
|
73
|
+
|
|
74
|
+
- **Bucket adapter** (`LiveAggregation`): buffers values, calls
|
|
75
|
+
the function once at `snapshot()` time. O(N) per snapshot.
|
|
76
|
+
- **Rolling adapter** (`LiveRollingAggregation`,
|
|
77
|
+
`LivePartitionedSyncRolling`): Map-keyed by event index for O(1)
|
|
78
|
+
`add` / O(1) `remove`; `snapshot()` calls the function with
|
|
79
|
+
`Array.from(map.values())` in arrival order. **O(N) per
|
|
80
|
+
snapshot** — the function re-runs over the current window each
|
|
81
|
+
time the accumulator emits.
|
|
82
|
+
|
|
83
|
+
Documented as the explicit trade-off: convenience of writing
|
|
84
|
+
`(values) => ...` inline against the perf cliff at high event
|
|
85
|
+
rates. For high-throughput streams prefer built-ins or `'samples'`
|
|
86
|
+
(collapse the window once on the producer side, run custom logic
|
|
87
|
+
on the consumer). For low-rate dashboards / debug pipelines /
|
|
88
|
+
prototypes, the convenience usually wins.
|
|
89
|
+
|
|
90
|
+
Pre-v0.14.1, calling `live.rolling(...)` with a custom-function
|
|
91
|
+
reducer threw `TypeError: live rolling reducer for output 'X' must
|
|
92
|
+
be a built-in name; ...`. Post-v0.14.1, the same call constructs
|
|
93
|
+
successfully and runs.
|
|
94
|
+
|
|
95
|
+
### Tests
|
|
96
|
+
|
|
97
|
+
- 15 new tests in `test/samples-reducer.test.ts` covering: batch
|
|
98
|
+
reduce / aggregate / rolling (including the array-source
|
|
99
|
+
flattening); live aggregate (per-bucket arrays); live rolling
|
|
100
|
+
(window eviction, snapshot correctness through multiple cycles);
|
|
101
|
+
synced partitioned rolling with samples per partition; an
|
|
102
|
+
end-to-end anomaly-density-against-baseline scenario.
|
|
103
|
+
- 2 obsolete tests in `LiveAggregateOutputMap.test.ts` rewritten —
|
|
104
|
+
previously asserted the rejection error, now assert that custom
|
|
105
|
+
functions construct successfully and produce the right value.
|
|
106
|
+
- Total core tests: 1087 (was 1072).
|
|
107
|
+
|
|
108
|
+
### Docs
|
|
109
|
+
|
|
110
|
+
- `pond-ts/transforms/reducer-reference.mdx`: new `'samples'` entry
|
|
111
|
+
in the Array-producing reducers section; "Choosing a reducer"
|
|
112
|
+
matrix updated; empty-bucket and rolling-complexity tables
|
|
113
|
+
updated; Custom reducers section gained the live perf-cliff
|
|
114
|
+
callout.
|
|
115
|
+
- `pond-ts/transforms/rolling.mdx`: replaced the "Custom-function
|
|
116
|
+
reducers are batch-only" note with the new "O(N) per snapshot on
|
|
117
|
+
live" perf-cliff note pointing at the reducer reference.
|
|
118
|
+
|
|
119
|
+
[0.14.1]: https://github.com/pjm17971/pond-ts/compare/v0.14.0...v0.14.1
|
|
120
|
+
|
|
121
|
+
## [0.14.0] — 2026-05-01
|
|
122
|
+
|
|
123
|
+
Two perf wins driven by the gRPC experiment's V3 profiling pass
|
|
124
|
+
(PR #14 on `pond-grpc-experiment`): `estimateEventBytes` at 6.2%
|
|
125
|
+
self time and the partition router's `Event → row → Event`
|
|
126
|
+
round-trip (combined ~7% in `#validateRow` + `Event` constructor
|
|
127
|
+
re-allocations). Both root-caused, both fixed.
|
|
128
|
+
|
|
129
|
+
Benchmark deltas on `scripts/perf-live-partitioned.mjs`
|
|
130
|
+
(100k events, median ms):
|
|
131
|
+
|
|
132
|
+
| Scenario | Before | After | Δ |
|
|
133
|
+
| ------------------------------------- | -----: | -----: | ------: |
|
|
134
|
+
| bare `LiveSeries.push` | 41.11 | 30.08 | **−27%** |
|
|
135
|
+
| `partitionBy('host')` routing (10) | 83.14 | 39.10 | **−53%** |
|
|
136
|
+
| `partitionBy + collect()` | 124.82 | 49.96 | **−60%** |
|
|
137
|
+
| `partitionBy + apply(fill)` | 120.53 | 49.64 | **−59%** |
|
|
138
|
+
| `partitionBy('host')` routing (1000) | 105.92 | 43.23 | **−59%** |
|
|
139
|
+
|
|
140
|
+
The bare-push delta is from the byte-estimate removal; the
|
|
141
|
+
partition-routing deltas are from the trusted-pipeline path that
|
|
142
|
+
skips `Event → row → Event` reconstruction at every routing hop.
|
|
143
|
+
|
|
144
|
+
### Removed (breaking, pre-1.0)
|
|
145
|
+
|
|
146
|
+
- **`retention.maxBytes`** option on `LiveSeriesOptions`. Speculative
|
|
147
|
+
feature from pre-v0.10 that no real user has reached for. Use
|
|
148
|
+
`retention.maxEvents` for count-based caps; `maxBytes` was
|
|
149
|
+
approximate (rough per-event byte estimate) and the imprecision
|
|
150
|
+
meant it was rarely used as designed.
|
|
151
|
+
|
|
152
|
+
Migration: replace `{ retention: { maxBytes: N } }` with
|
|
153
|
+
`{ retention: { maxEvents: M } }` where M is your desired
|
|
154
|
+
upper bound on event count.
|
|
155
|
+
|
|
156
|
+
### Changed
|
|
157
|
+
|
|
158
|
+
- **`estimateEventBytes` and the `#byteEstimate` accumulator
|
|
159
|
+
removed** from `LiveSeries`. Closes the 6.2% per-push self-time
|
|
160
|
+
line the gRPC experiment surfaced. Bare push is now ~27% faster
|
|
161
|
+
for the typical case where `maxBytes` was never set.
|
|
162
|
+
|
|
163
|
+
- **Partition router uses a trusted-pipeline fast path.**
|
|
164
|
+
`LivePartitionedSeries.#routeEvent`, `collect()`, and `apply()`
|
|
165
|
+
previously round-tripped `Event → row → Event` at every routing
|
|
166
|
+
hop — re-validating and re-allocating Events that the source
|
|
167
|
+
pipeline had already constructed. New `_pushTrustedEvents` method
|
|
168
|
+
on `LiveSeries` accepts pre-validated Event references (under a
|
|
169
|
+
schema-identity contract; only used internally where the source
|
|
170
|
+
and target schemas are guaranteed identical). Closes the ~7%
|
|
171
|
+
combined self-time line in `#validateRow` (×2) and `Event`
|
|
172
|
+
constructor (×2) that the gRPC profile flagged.
|
|
173
|
+
|
|
174
|
+
Trusted-pipeline applies to: the source-to-partition route, the
|
|
175
|
+
per-partition replay-on-construct prefix, the unified-buffer
|
|
176
|
+
`collect()` subscriber, and `apply()`'s factory-output forwarding.
|
|
177
|
+
All four sites had identical schemas at both ends — the trust
|
|
178
|
+
contract holds without runtime re-checking.
|
|
179
|
+
|
|
180
|
+
`_pushTrustedEvents` is `@internal` and not exported from the
|
|
181
|
+
public type surface. Reach for `pushMany` from any external
|
|
182
|
+
context; the trusted variant skips schema validation and is
|
|
183
|
+
only safe for pond's own internal pipelines.
|
|
184
|
+
|
|
185
|
+
### Tests
|
|
186
|
+
|
|
187
|
+
- 4 new tests in `test/LiveSeries.test.ts` for the trusted-pipeline
|
|
188
|
+
path: insertion without re-validation, listener fan-out and
|
|
189
|
+
retention behaviour, ordering enforcement (strict still rejects
|
|
190
|
+
out-of-order on the trusted path — the trust contract is only
|
|
191
|
+
about validation/allocation, not insertion ordering), empty-array
|
|
192
|
+
no-op.
|
|
193
|
+
- Removed the `retention: maxBytes` describe block in
|
|
194
|
+
`test/LiveSeries.test.ts` and the `forwards retention.maxBytes`
|
|
195
|
+
assertion in `test/LiveSeries.snapshot-append.test.ts`.
|
|
196
|
+
- Total core tests: 1072 (was 1070; +4 new for the trusted path,
|
|
197
|
+
−2 for the removed maxBytes assertions).
|
|
198
|
+
|
|
199
|
+
### Docs
|
|
200
|
+
|
|
201
|
+
- `live-series.mdx`: retention table and example trimmed to
|
|
202
|
+
`maxEvents` + `maxAge` only. Removed the byte-estimate prose.
|
|
203
|
+
|
|
204
|
+
[0.14.0]: https://github.com/pjm17971/pond-ts/compare/v0.13.2...v0.14.0
|
|
205
|
+
|
|
14
206
|
## [0.13.2] — 2026-05-01
|
|
15
207
|
|
|
16
208
|
Strictly additive over v0.13.1. Adds `Trigger.count(n)` per the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "React hooks for pond-ts live time series",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"test:runtime": "vitest run"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"pond-ts": "^0.
|
|
34
|
+
"pond-ts": "^0.14.0",
|
|
35
35
|
"react": "^18.0.0 || ^19.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|