@pond-ts/react 0.11.6 → 0.11.7
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 +34 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/useEventRate.d.ts +40 -0
- package/dist/useEventRate.d.ts.map +1 -0
- package/dist/useEventRate.js +75 -0
- package/dist/useEventRate.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,43 @@ 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.11.
|
|
10
|
+
[Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.11.7...HEAD
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.11.7] — 2026-04-29
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`LiveView.count()` and `LiveView.eventRate()` terminal accessors.**
|
|
19
|
+
Read the current event count and events-per-second over a windowed
|
|
20
|
+
view directly — closes the
|
|
21
|
+
`useCurrent(live, { cpu: 'count' }, { tail: '1m' }).cpu / 60`
|
|
22
|
+
boilerplate surfaced by the gRPC experiment.
|
|
23
|
+
```ts
|
|
24
|
+
const eventsPerSec = live.window('1m').eventRate(); // events/sec
|
|
25
|
+
const eventsInWindow = live.window('1m').count();
|
|
26
|
+
```
|
|
27
|
+
`eventRate()` requires a time-based window (`window('1m')`) and
|
|
28
|
+
throws on count-based windows (`window(100)`) — there's no
|
|
29
|
+
denominator to use. Distinct from `LiveView.rate(columns)`,
|
|
30
|
+
which is the per-column derivative operator (rate-of-change of
|
|
31
|
+
values).
|
|
32
|
+
- `LiveView.{filter,map,select}` now propagate the parent's window
|
|
33
|
+
duration to the child view, so chains like
|
|
34
|
+
`live.window('1m').filter(...).eventRate()` work as expected.
|
|
35
|
+
- `@pond-ts/react` ships **`useEventRate(source, '1m')`** — a
|
|
36
|
+
reactive hook returning the events-per-second number, throttled
|
|
37
|
+
on `'event'` like `useSnapshot`. Hooks mounted on already-
|
|
38
|
+
populated sources render the actual rate on first paint via
|
|
39
|
+
lazy `useState` init.
|
|
40
|
+
```tsx
|
|
41
|
+
const eventsPerSec = useEventRate(liveSeries, '1m');
|
|
42
|
+
// <div>EVENT RATE {eventsPerSec.toFixed(1)}/s</div>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
[0.11.7]: https://github.com/pjm17971/pond-ts/compare/v0.11.6...v0.11.7
|
|
46
|
+
|
|
14
47
|
## [0.11.6] — 2026-04-29
|
|
15
48
|
|
|
16
49
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ export { useDerived } from './useDerived.js';
|
|
|
6
6
|
export { useLiveQuery } from './useLiveQuery.js';
|
|
7
7
|
export { useLatest } from './useLatest.js';
|
|
8
8
|
export { useCurrent, type UseCurrentOptions } from './useCurrent.js';
|
|
9
|
+
export { useEventRate } from './useEventRate.js';
|
|
9
10
|
export { takeSnapshot } from './takeSnapshot.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,5 +7,6 @@ export { useDerived } from './useDerived.js';
|
|
|
7
7
|
export { useLiveQuery } from './useLiveQuery.js';
|
|
8
8
|
export { useLatest } from './useLatest.js';
|
|
9
9
|
export { useCurrent } from './useCurrent.js';
|
|
10
|
+
export { useEventRate } from './useEventRate.js';
|
|
10
11
|
export { takeSnapshot } from './takeSnapshot.js';
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,WAAW,GAGZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,WAAW,GAGZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { LiveSource, RollingWindow, SeriesSchema } from 'pond-ts';
|
|
2
|
+
import type { UseSnapshotOptions } from './useSnapshot.js';
|
|
3
|
+
/**
|
|
4
|
+
* `LiveSource` that supports `.window(duration)` — `LiveSeries`,
|
|
5
|
+
* `LiveView`, and accumulators.
|
|
6
|
+
*/
|
|
7
|
+
type Windowable<S extends SeriesSchema> = LiveSource<S> & {
|
|
8
|
+
window(size: RollingWindow): LiveSource<S> & {
|
|
9
|
+
count(): number;
|
|
10
|
+
eventRate(): number;
|
|
11
|
+
on(type: 'event', fn: (...args: any[]) => void): () => void;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Subscribe to a live source, maintain a `.window(duration)` view
|
|
16
|
+
* over it, and return its events-per-second as a reactive number.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const eventRate = useEventRate(liveSeries, '1m');
|
|
21
|
+
* // <div>EVENT RATE {eventRate.toFixed(1)}/s</div>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Sugar over `live.window(duration).eventRate()` plus a subscription
|
|
25
|
+
* that re-reads on each push and throttles updates. Closes the
|
|
26
|
+
* boilerplate that the gRPC experiment's M1 friction notes
|
|
27
|
+
* surfaced — `useCurrent(live, { cpu: 'count' }, { tail: '1m' }).cpu / 60`
|
|
28
|
+
* collapses to one hook.
|
|
29
|
+
*
|
|
30
|
+
* Throttling matches `useSnapshot`: `throttle: 100` ms by default.
|
|
31
|
+
* Returns `0` until the first event lands.
|
|
32
|
+
*
|
|
33
|
+
* @throws TypeError if `windowDuration` is a count-based window (a
|
|
34
|
+
* number) — `eventRate` requires a time denominator. Pass a
|
|
35
|
+
* duration string like `'1m'`, `'30s'`, or a number-of-ms via
|
|
36
|
+
* `DurationInput` instead.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useEventRate<S extends SeriesSchema>(source: Windowable<S>, windowDuration: RollingWindow, options?: UseSnapshotOptions): number;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=useEventRate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEventRate.d.ts","sourceRoot":"","sources":["../src/useEventRate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D;;;GAGG;AACH,KAAK,UAAU,CAAC,CAAC,SAAS,YAAY,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IACxD,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG;QAC3C,KAAK,IAAI,MAAM,CAAC;QAChB,SAAS,IAAI,MAAM,CAAC;QACpB,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;KAC7D,CAAC;CACH,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,YAAY,EACjD,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EACrB,cAAc,EAAE,aAAa,EAC7B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,MAAM,CAuDR"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Subscribe to a live source, maintain a `.window(duration)` view
|
|
4
|
+
* over it, and return its events-per-second as a reactive number.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const eventRate = useEventRate(liveSeries, '1m');
|
|
9
|
+
* // <div>EVENT RATE {eventRate.toFixed(1)}/s</div>
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Sugar over `live.window(duration).eventRate()` plus a subscription
|
|
13
|
+
* that re-reads on each push and throttles updates. Closes the
|
|
14
|
+
* boilerplate that the gRPC experiment's M1 friction notes
|
|
15
|
+
* surfaced — `useCurrent(live, { cpu: 'count' }, { tail: '1m' }).cpu / 60`
|
|
16
|
+
* collapses to one hook.
|
|
17
|
+
*
|
|
18
|
+
* Throttling matches `useSnapshot`: `throttle: 100` ms by default.
|
|
19
|
+
* Returns `0` until the first event lands.
|
|
20
|
+
*
|
|
21
|
+
* @throws TypeError if `windowDuration` is a count-based window (a
|
|
22
|
+
* number) — `eventRate` requires a time denominator. Pass a
|
|
23
|
+
* duration string like `'1m'`, `'30s'`, or a number-of-ms via
|
|
24
|
+
* `DurationInput` instead.
|
|
25
|
+
*/
|
|
26
|
+
export function useEventRate(source, windowDuration, options) {
|
|
27
|
+
const throttleMs = options?.throttle ?? 100;
|
|
28
|
+
// Lazy initial value: take a one-shot read off a temporary view so a
|
|
29
|
+
// hook mounted on an already-populated source renders the correct
|
|
30
|
+
// rate on the first paint, not 0. The temporary view is GC'd when
|
|
31
|
+
// the effect creates the long-lived view; no leak because we don't
|
|
32
|
+
// subscribe.
|
|
33
|
+
const [rate, setRate] = useState(() => {
|
|
34
|
+
const initialView = source.window(windowDuration);
|
|
35
|
+
const r = initialView.eventRate();
|
|
36
|
+
if ('dispose' in initialView &&
|
|
37
|
+
typeof initialView.dispose === 'function') {
|
|
38
|
+
initialView.dispose();
|
|
39
|
+
}
|
|
40
|
+
return r;
|
|
41
|
+
});
|
|
42
|
+
const sourceRef = useRef(source);
|
|
43
|
+
sourceRef.current = source;
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const view = source.window(windowDuration);
|
|
46
|
+
setRate(view.eventRate());
|
|
47
|
+
let timer = null;
|
|
48
|
+
let pending = false;
|
|
49
|
+
const flush = () => {
|
|
50
|
+
timer = null;
|
|
51
|
+
pending = false;
|
|
52
|
+
setRate(view.eventRate());
|
|
53
|
+
};
|
|
54
|
+
const unsub = view.on('event', () => {
|
|
55
|
+
if (throttleMs === 0) {
|
|
56
|
+
setRate(view.eventRate());
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!pending) {
|
|
60
|
+
pending = true;
|
|
61
|
+
timer = setTimeout(flush, throttleMs);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return () => {
|
|
65
|
+
unsub();
|
|
66
|
+
if (timer !== null)
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
if ('dispose' in view && typeof view.dispose === 'function') {
|
|
69
|
+
view.dispose();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}, [source, windowDuration, throttleMs]);
|
|
73
|
+
return rate;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=useEventRate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEventRate.js","sourceRoot":"","sources":["../src/useEventRate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAgBpD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAqB,EACrB,cAA6B,EAC7B,OAA4B;IAE5B,MAAM,UAAU,GAAG,OAAO,EAAE,QAAQ,IAAI,GAAG,CAAC;IAC5C,qEAAqE;IACrE,kEAAkE;IAClE,kEAAkE;IAClE,mEAAmE;IACnE,aAAa;IACb,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE;QACpC,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,CAAC,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC;QAClC,IACE,SAAS,IAAI,WAAW;YACxB,OAAQ,WAAmB,CAAC,OAAO,KAAK,UAAU,EAClD,CAAC;YACA,WAAmB,CAAC,OAAO,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAE3B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAE1B,IAAI,KAAK,GAAyC,IAAI,CAAC;QACvD,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,KAAK,GAAG,GAAG,EAAE;YACjB,KAAK,GAAG,IAAI,CAAC;YACb,OAAO,GAAG,KAAK,CAAC;YAChB,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAClC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;gBACrB,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC1B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,IAAI;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,SAAS,IAAI,IAAI,IAAI,OAAQ,IAAY,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBACpE,IAAY,CAAC,OAAO,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC;IAEzC,OAAO,IAAI,CAAC;AACd,CAAC"}
|