@pond-ts/react 0.17.1 → 0.19.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.
- package/CHANGELOG.md +147 -1
- package/README.md +6 -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/useLiveVersion.d.ts +41 -0
- package/dist/useLiveVersion.d.ts.map +1 -0
- package/dist/useLiveVersion.js +104 -0
- package/dist/useLiveVersion.js.map +1 -0
- package/dist/useTimeSeries.d.ts +15 -2
- package/dist/useTimeSeries.d.ts.map +1 -1
- package/dist/useTimeSeries.js +11 -0
- package/dist/useTimeSeries.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,156 @@ 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.19.0...HEAD
|
|
11
|
+
[0.19.0]: https://github.com/pjm17971/pond-ts/compare/v0.18.0...v0.19.0
|
|
12
|
+
[0.18.0]: https://github.com/pjm17971/pond-ts/compare/v0.17.1...v0.18.0
|
|
11
13
|
|
|
12
14
|
## [Unreleased]
|
|
13
15
|
|
|
16
|
+
## [0.19.0] — 2026-06-02
|
|
17
|
+
|
|
18
|
+
Adds an **experimental column-read surface to the live side** — read typed
|
|
19
|
+
columns straight off a `LiveView` without materializing a `TimeSeries`
|
|
20
|
+
snapshot — driven by the dashboard experiment's per-tick memo cost. Plus a
|
|
21
|
+
`useTimeSeries` schema-inference fix. The live column surface is
|
|
22
|
+
**experimental and expected to keep moving in 0.19.x**.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`LiveView` column-read surface (experimental).** Read columns directly
|
|
27
|
+
off a windowed live view, the column-API counterpart to the batch
|
|
28
|
+
`TimeSeries` surface ([#179](https://github.com/pjm17971/pond-ts/pull/179)):
|
|
29
|
+
- `liveView.column(name)` — a numeric value column gathered from the view's
|
|
30
|
+
current events (string / array columns are a compile error; read those as
|
|
31
|
+
scalars or snapshot via `toTimeSeries()`).
|
|
32
|
+
- `liveView.keyColumn()` — the time axis (`TimeKeyColumn`; time-keyed views
|
|
33
|
+
only, enforced at compile time).
|
|
34
|
+
- `liveView.partitionBy(col).toMap(fn)` — a walk-now per-partition read
|
|
35
|
+
returning `Map<string, R>`, mirroring `TimeSeries.partitionBy().toMap()`
|
|
36
|
+
but without per-partition `TimeSeries` construction. Distinct from
|
|
37
|
+
`LiveSeries.partitionBy` (which is subscription-oriented). Throws on a
|
|
38
|
+
missing / key partition column rather than silently merging.
|
|
39
|
+
- `LiveColumnGroup` — the per-partition view passed to the `toMap` callback.
|
|
40
|
+
- **`@pond-ts/react`: `useLiveVersion(source, { throttle })` (experimental)**
|
|
41
|
+
— a `useSyncExternalStore`-based change signal that bumps on append **and**
|
|
42
|
+
eviction, so a component can read columns off a live view each render
|
|
43
|
+
without manufacturing a `TimeSeries` snapshot. Closes the
|
|
44
|
+
render-before-subscribe gap; throttling bounds only the React notification
|
|
45
|
+
([#179](https://github.com/pjm17971/pond-ts/pull/179)).
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **`useTimeSeries` collapsed to a single generic** `<S extends SeriesSchema>`
|
|
50
|
+
so the schema infers from `input.schema`. The prior two-generic signature
|
|
51
|
+
lost `S` through the input-wrapper generic and resolved
|
|
52
|
+
`result.column('cpu')` to `never`; the accepted input type is unchanged, so
|
|
53
|
+
this is an inference fix — but a caller passing two explicit type arguments
|
|
54
|
+
must drop the second ([#176](https://github.com/pjm17971/pond-ts/pull/176)).
|
|
55
|
+
|
|
56
|
+
## [0.18.0] — 2026-05-30
|
|
57
|
+
|
|
58
|
+
This release graduates the **Phase 4.7 columnar substrate** from
|
|
59
|
+
framework-internal (shipped piecemeal to `main` since v0.17.1) to a
|
|
60
|
+
user-visible **public column API**, plus a column-native live buffer that
|
|
61
|
+
fixes a high-partition-count OOM. Everything is additive except one
|
|
62
|
+
documented breaking change (interval label kinds) and one documented
|
|
63
|
+
behavior change (chunked-backed `pushMany` commit semantics). Pre-1.0: the
|
|
64
|
+
column API is expected to keep moving toward its eventual shape.
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **Public column API (Phase 4.7 step 8).** A column-centric extraction
|
|
69
|
+
surface on `TimeSeries`, for high-throughput and charting consumers that
|
|
70
|
+
want typed-array access instead of per-`Event` iteration. Additive — every
|
|
71
|
+
existing row / `Event` API is unchanged.
|
|
72
|
+
- `series.column(name)` returns a schema-narrowed typed column view, with
|
|
73
|
+
public re-exports of the `Float64Column` / `BooleanColumn` /
|
|
74
|
+
`StringColumn` / `KeyColumn` (time / timeRange / interval) variants
|
|
75
|
+
([#154](https://github.com/pjm17971/pond-ts/pull/154),
|
|
76
|
+
[#155](https://github.com/pjm17971/pond-ts/pull/155)).
|
|
77
|
+
- `Float64Column`: scalar reductions (`min` / `max` / `sum` / `mean` /
|
|
78
|
+
`count` / …) and `scan`
|
|
79
|
+
([#155](https://github.com/pjm17971/pond-ts/pull/155)); `bin(...)` for
|
|
80
|
+
histogram / downsample bucketing
|
|
81
|
+
([#156](https://github.com/pjm17971/pond-ts/pull/156)); and
|
|
82
|
+
`toFloat64Array()` for a storage-agnostic gather into a dense array
|
|
83
|
+
([#165](https://github.com/pjm17971/pond-ts/pull/165)).
|
|
84
|
+
- `KeyColumn.at(i)` and `.slice(start, end)`
|
|
85
|
+
([#159](https://github.com/pjm17971/pond-ts/pull/159)).
|
|
86
|
+
- **Columnar substrate (Phase 4.7 step 1, framework layer).** All
|
|
87
|
+
eight sub-steps (1a–1h) shipped to main as PRs #132 / #133 /
|
|
88
|
+
#134 / #135 / #136 / #147 / #148 / #149. See `PLAN.md` and
|
|
89
|
+
[`packages/core/src/columnar/README.md`](packages/core/src/columnar/README.md)
|
|
90
|
+
for the full inventory. Framework-internal — surfaced behind the existing
|
|
91
|
+
`TimeSeries` API at step 2 (below) and the public column API at step 8
|
|
92
|
+
(above).
|
|
93
|
+
|
|
94
|
+
### Changed
|
|
95
|
+
|
|
96
|
+
- **Chunked columnar live backing for strict time-keyed `LiveSeries`**
|
|
97
|
+
([#170](https://github.com/pjm17971/pond-ts/pull/170)). A top-level
|
|
98
|
+
`LiveSeries` with `ordering: 'strict'` and a time key now backs its
|
|
99
|
+
retained window with batch-granular columnar chunks instead of an
|
|
100
|
+
`Event[]` window — each `pushMany` validates straight into typed columns,
|
|
101
|
+
retaining **zero `Event` objects** (~4.7× less retained heap in-pond; the
|
|
102
|
+
high-partition-count OOM fix). Two consequences:
|
|
103
|
+
- **`pushMany` commit semantics** on the chunked path: the batch is
|
|
104
|
+
appended atomically _before_ any `'event'` fires, so a listener observes
|
|
105
|
+
the full post-batch `length` (not a row-by-row `1, 2, 3`), and a listener
|
|
106
|
+
that throws mid-batch leaves the whole batch committed. The per-row
|
|
107
|
+
`Event[]` backing (`reorder` / `drop` / interval-keyed /
|
|
108
|
+
internally-created series) keeps per-row commit. Listener _values_ and
|
|
109
|
+
`event → batch → evict` ordering are unchanged.
|
|
110
|
+
- **`LiveReduce` eviction** resolves by event identity (primary) with a
|
|
111
|
+
FIFO-frontier fallback for the chunked backing's materialized evictions —
|
|
112
|
+
correct for both `reorder` and the chunked backing. `min` / `max` /
|
|
113
|
+
`first` / `last` / `samples` over a `reorder` source **with retention**
|
|
114
|
+
remain a documented limitation (see `LiveReduce` JSDoc and PLAN
|
|
115
|
+
"Deferred") — pre-existing, not introduced here.
|
|
116
|
+
- **Internal, behavior-preserving performance work.** Column-native intake
|
|
117
|
+
bypasses per-row `Event` allocation at `TimeSeries` construction
|
|
118
|
+
([#151](https://github.com/pjm17971/pond-ts/pull/151)); numeric reducers
|
|
119
|
+
(`min` / `max` / `sum` / `avg` / …) compute over typed-array columns where
|
|
120
|
+
available, with NaN parity preserved
|
|
121
|
+
([#153](https://github.com/pjm17971/pond-ts/pull/153)); the live storage
|
|
122
|
+
strategy was extracted behind an internal interface
|
|
123
|
+
([#168](https://github.com/pjm17971/pond-ts/pull/168)).
|
|
124
|
+
|
|
125
|
+
### Changed (BREAKING)
|
|
126
|
+
|
|
127
|
+
- **Interval-keyed series must use one label type throughout**
|
|
128
|
+
([#150](https://github.com/pjm17971/pond-ts/pull/150)). Pre-2a,
|
|
129
|
+
TimeSeries silently tolerated mixed-kind interval labels —
|
|
130
|
+
rows with `value: 'row-1'` (string) and `value: 2` (number) could
|
|
131
|
+
coexist in a single series because events were stored as a raw
|
|
132
|
+
array with no per-column type alignment. The columnar substrate
|
|
133
|
+
introduced at Phase 4.7 enforces one label kind per column via
|
|
134
|
+
`IntervalKeyColumn`, so mixed-kind labels now throw at series
|
|
135
|
+
construction with a row-pointed error message.
|
|
136
|
+
- **Affected:** Any series built via `new TimeSeries(...)`,
|
|
137
|
+
`TimeSeries.fromJSON(...)`, `TimeSeries.fromEvents(...)`, or
|
|
138
|
+
any transform that produces interval-keyed events, where the
|
|
139
|
+
`value` field of `IntervalInput` rows or `Interval` keys
|
|
140
|
+
mixes `string` and `number` types.
|
|
141
|
+
- **Migration:** Choose one label kind for the whole series.
|
|
142
|
+
Numeric labels can be stringified at intake (`String(label)`)
|
|
143
|
+
if the downstream consumer accepts string equality; string
|
|
144
|
+
labels parseable as integers can be converted to numbers at
|
|
145
|
+
intake. The error message names the first offending row so
|
|
146
|
+
the offending data is easy to find.
|
|
147
|
+
- **Rationale:** Aligns the row-API contract with the columnar
|
|
148
|
+
substrate's per-column kind discipline (matching Polars /
|
|
149
|
+
Arrow / Parquet). The previous behavior produced type-broken
|
|
150
|
+
events that worked only because TimeSeries didn't enforce
|
|
151
|
+
per-column alignment; downstream columnar operators (the
|
|
152
|
+
upcoming reducer adaptation in steps 3+) require it.
|
|
153
|
+
- **Affected types:** `IntervalValue` remains `string | number`
|
|
154
|
+
per the `Interval` class contract. The runtime restriction
|
|
155
|
+
is at the **series** level (all intervals within one series
|
|
156
|
+
must share a kind), not the per-interval level. Type-level
|
|
157
|
+
narrowing of `IntervalKeyedSchema<S>` over label kind is a
|
|
158
|
+
follow-up deferred to a later sub-step.
|
|
159
|
+
|
|
14
160
|
## [0.17.1] — 2026-05-11
|
|
15
161
|
|
|
16
162
|
Bug fix: `live.partitionBy()` now default-inherits `ordering`,
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pond-ts
|
|
2
2
|
|
|
3
3
|
**Highly optimised, fully typed Timeseries library for TypeScript**
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
Schema-driven events, composable batch transforms, push-based streaming
|
|
6
6
|
ingest, multi-entity partitioning, and an optional React integration —
|
|
7
7
|
all strict TypeScript end to end, all immutable.
|
|
@@ -31,6 +31,11 @@ npm install @pond-ts/react # React hooks (optional)
|
|
|
31
31
|
single node.js instance.)
|
|
32
32
|
- **Triggers** — for control of rolling emission cadences. Synchronised
|
|
33
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.
|
|
34
39
|
- **No legacy baggage**
|
|
35
40
|
|
|
36
41
|
## Quick start: batch
|
package/dist/index.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export { useLiveQuery } from './useLiveQuery.js';
|
|
|
7
7
|
export { useLatest } from './useLatest.js';
|
|
8
8
|
export { useCurrent, type UseCurrentOptions } from './useCurrent.js';
|
|
9
9
|
export { useEventRate } from './useEventRate.js';
|
|
10
|
+
export { useLiveVersion, type UseLiveVersionOptions, } from './useLiveVersion.js';
|
|
10
11
|
export { takeSnapshot } from './takeSnapshot.js';
|
|
11
12
|
//# 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;AACjD,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,EACL,cAAc,EACd,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,5 +8,6 @@ export { useLiveQuery } from './useLiveQuery.js';
|
|
|
8
8
|
export { useLatest } from './useLatest.js';
|
|
9
9
|
export { useCurrent } from './useCurrent.js';
|
|
10
10
|
export { useEventRate } from './useEventRate.js';
|
|
11
|
+
export { useLiveVersion, } from './useLiveVersion.js';
|
|
11
12
|
export { takeSnapshot } from './takeSnapshot.js';
|
|
12
13
|
//# 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;AACjD,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,EACL,cAAc,GAEf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { LiveSource, SeriesSchema } from 'pond-ts';
|
|
2
|
+
export interface UseLiveVersionOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Minimum interval between React notifications, in ms. The version
|
|
5
|
+
* counter bumps *immediately* on every source change (no buffering);
|
|
6
|
+
* `throttle` only bounds how often React is told to re-render.
|
|
7
|
+
* `0` notifies synchronously per change. Default 100.
|
|
8
|
+
*/
|
|
9
|
+
throttle?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The React change signal for reading columns off a live source without
|
|
13
|
+
* manufacturing a `TimeSeries` snapshot (§A pull/read, experimental).
|
|
14
|
+
*
|
|
15
|
+
* `LiveSeries` / `LiveView` mutate in place, so a `useMemo([liveView])`
|
|
16
|
+
* keyed on the view never re-runs. This hook gives React a
|
|
17
|
+
* monotonically-increasing version that changes (at most once per
|
|
18
|
+
* `throttle`) whenever the source mutates — the missing invalidation
|
|
19
|
+
* trigger. Read columns straight off the live view, keyed on the
|
|
20
|
+
* returned version:
|
|
21
|
+
*
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const view = useMemo(() => live.window('5m'), [live]);
|
|
24
|
+
* const v = useLiveVersion(view, { throttle: 200 });
|
|
25
|
+
* const series = useMemo(
|
|
26
|
+
* () => view.partitionBy('host').toMap((g) => ({
|
|
27
|
+
* ts: g.keyColumn().begin,
|
|
28
|
+
* cpu: g.column('cpu').toFloat64Array(),
|
|
29
|
+
* })),
|
|
30
|
+
* [view, v],
|
|
31
|
+
* );
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Tracks **both** append (`'event'`) and eviction (`'evict'`, on sources
|
|
35
|
+
* that emit it — e.g. `clear()` / retention prune), and advances the
|
|
36
|
+
* revision on subscribe so a change between render and the subscribe
|
|
37
|
+
* effect is still picked up (the `useSyncExternalStore` post-subscribe
|
|
38
|
+
* re-read). Experimental (0.19.0) — surface may change in 0.19.x.
|
|
39
|
+
*/
|
|
40
|
+
export declare function useLiveVersion<S extends SeriesSchema>(source: LiveSource<S>, options?: UseLiveVersionOptions): number;
|
|
41
|
+
//# sourceMappingURL=useLiveVersion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLiveVersion.d.ts","sourceRoot":"","sources":["../src/useLiveVersion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AASxD,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,YAAY,EACnD,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,CA4ER"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
// The capability marker pond-ts stamps on evict-emitting sources
|
|
3
|
+
// (`LiveSeries`, `LiveView`). It's a registered symbol
|
|
4
|
+
// (`Symbol.for('pond-ts:emitsEvict')` in pond-ts), so reconstructing it
|
|
5
|
+
// here yields the identical symbol — no import needed. The
|
|
6
|
+
// `clear-evicts-without-event` test guards against key drift.
|
|
7
|
+
const EMITS_EVICT = Symbol.for('pond-ts:emitsEvict');
|
|
8
|
+
/**
|
|
9
|
+
* The React change signal for reading columns off a live source without
|
|
10
|
+
* manufacturing a `TimeSeries` snapshot (§A pull/read, experimental).
|
|
11
|
+
*
|
|
12
|
+
* `LiveSeries` / `LiveView` mutate in place, so a `useMemo([liveView])`
|
|
13
|
+
* keyed on the view never re-runs. This hook gives React a
|
|
14
|
+
* monotonically-increasing version that changes (at most once per
|
|
15
|
+
* `throttle`) whenever the source mutates — the missing invalidation
|
|
16
|
+
* trigger. Read columns straight off the live view, keyed on the
|
|
17
|
+
* returned version:
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const view = useMemo(() => live.window('5m'), [live]);
|
|
21
|
+
* const v = useLiveVersion(view, { throttle: 200 });
|
|
22
|
+
* const series = useMemo(
|
|
23
|
+
* () => view.partitionBy('host').toMap((g) => ({
|
|
24
|
+
* ts: g.keyColumn().begin,
|
|
25
|
+
* cpu: g.column('cpu').toFloat64Array(),
|
|
26
|
+
* })),
|
|
27
|
+
* [view, v],
|
|
28
|
+
* );
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Tracks **both** append (`'event'`) and eviction (`'evict'`, on sources
|
|
32
|
+
* that emit it — e.g. `clear()` / retention prune), and advances the
|
|
33
|
+
* revision on subscribe so a change between render and the subscribe
|
|
34
|
+
* effect is still picked up (the `useSyncExternalStore` post-subscribe
|
|
35
|
+
* re-read). Experimental (0.19.0) — surface may change in 0.19.x.
|
|
36
|
+
*/
|
|
37
|
+
export function useLiveVersion(source, options) {
|
|
38
|
+
const throttleMs = options?.throttle ?? 100;
|
|
39
|
+
const store = useMemo(() => {
|
|
40
|
+
let version = 0; // bumped on every observed source mutation
|
|
41
|
+
let committed = 0; // last version React has been notified about
|
|
42
|
+
let timer = null;
|
|
43
|
+
let unsubSource = null;
|
|
44
|
+
const listeners = new Set();
|
|
45
|
+
const flush = () => {
|
|
46
|
+
timer = null;
|
|
47
|
+
committed = version;
|
|
48
|
+
for (const l of listeners)
|
|
49
|
+
l();
|
|
50
|
+
};
|
|
51
|
+
const onChange = () => {
|
|
52
|
+
version += 1;
|
|
53
|
+
if (throttleMs <= 0) {
|
|
54
|
+
flush();
|
|
55
|
+
}
|
|
56
|
+
else if (timer === null) {
|
|
57
|
+
timer = setTimeout(flush, throttleMs);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
subscribe(cb) {
|
|
62
|
+
listeners.add(cb);
|
|
63
|
+
if (unsubSource === null) {
|
|
64
|
+
const unsubEvent = source.on('event', onChange);
|
|
65
|
+
// Also track eviction (clear / retention prune) on sources that
|
|
66
|
+
// emit it — otherwise a `clear()` with no following append leaves
|
|
67
|
+
// a column reader stale.
|
|
68
|
+
let unsubEvict;
|
|
69
|
+
if (EMITS_EVICT in source) {
|
|
70
|
+
unsubEvict = source.on('evict', onChange);
|
|
71
|
+
}
|
|
72
|
+
unsubSource = () => {
|
|
73
|
+
unsubEvent();
|
|
74
|
+
unsubEvict?.();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Close the render-before-subscribe gap: a mutation between render
|
|
78
|
+
// and this effect fires no observed callback, so advance the
|
|
79
|
+
// revision now. useSyncExternalStore re-reads getSnapshot after
|
|
80
|
+
// subscribe; the bumped value forces one re-render that re-reads
|
|
81
|
+
// the (now-current) view.
|
|
82
|
+
version += 1;
|
|
83
|
+
committed = version;
|
|
84
|
+
return () => {
|
|
85
|
+
listeners.delete(cb);
|
|
86
|
+
if (listeners.size === 0) {
|
|
87
|
+
unsubSource?.();
|
|
88
|
+
unsubSource = null;
|
|
89
|
+
if (timer !== null) {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
timer = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
// Stable between notifications — only advances at flush / subscribe,
|
|
97
|
+
// exactly when React is told. (Returning the live `version` would
|
|
98
|
+
// violate the getSnapshot contract.)
|
|
99
|
+
getSnapshot: () => committed,
|
|
100
|
+
};
|
|
101
|
+
}, [source, throttleMs]);
|
|
102
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=useLiveVersion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLiveVersion.js","sourceRoot":"","sources":["../src/useLiveVersion.ts"],"names":[],"mappings":"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"}
|
package/dist/useTimeSeries.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TimeSeries } from 'pond-ts';
|
|
2
|
-
import type { SeriesSchema } from 'pond-ts';
|
|
2
|
+
import type { SeriesSchema, TimeSeriesJsonInput, TimeZoneOptions } from 'pond-ts';
|
|
3
3
|
/**
|
|
4
4
|
* Memoized `TimeSeries.fromJSON(...)` for static or fetched data.
|
|
5
5
|
*
|
|
@@ -7,6 +7,19 @@ import type { SeriesSchema } from 'pond-ts';
|
|
|
7
7
|
* serialized via `JSON.stringify` as the cache key — fine for small to
|
|
8
8
|
* moderate payloads. For large datasets, pass an explicit `key` (e.g. a fetch
|
|
9
9
|
* URL or ETag) to avoid the serialization cost.
|
|
10
|
+
*
|
|
11
|
+
* The schema generic `S` is inferred directly from `input.schema` (a plain
|
|
12
|
+
* structural position), so an `as const` schema narrows the returned
|
|
13
|
+
* `TimeSeries<S>` fully — `result.column('cpu')` resolves to `Float64Column`,
|
|
14
|
+
* `result.at(i)!.get('cpu')` to `number | undefined`, etc. An earlier
|
|
15
|
+
* two-generic `<S, I extends Parameters<...>[0]>` shape lost `S` through the
|
|
16
|
+
* input-wrapper generic and collapsed schema-narrowed accessors to `never`
|
|
17
|
+
* (the loose `.get(string)` path masked it); the column API surfaced it. The
|
|
18
|
+
* accepted input type is unchanged — `Parameters<fromJSON<S>>[0]` already
|
|
19
|
+
* resolved to `TimeSeriesJsonInput<S> & { parse? }` — so this is purely an
|
|
20
|
+
* inference fix, not a surface change.
|
|
10
21
|
*/
|
|
11
|
-
export declare function useTimeSeries<S extends SeriesSchema
|
|
22
|
+
export declare function useTimeSeries<S extends SeriesSchema>(input: TimeSeriesJsonInput<S> & {
|
|
23
|
+
parse?: TimeZoneOptions;
|
|
24
|
+
}, key?: string): TimeSeries<S>;
|
|
12
25
|
//# sourceMappingURL=useTimeSeries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTimeSeries.d.ts","sourceRoot":"","sources":["../src/useTimeSeries.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useTimeSeries.d.ts","sourceRoot":"","sources":["../src/useTimeSeries.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,YAAY,EAClD,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,eAAe,CAAA;CAAE,EAC3D,GAAG,CAAC,EAAE,MAAM,GACX,UAAU,CAAC,CAAC,CAAC,CAKf"}
|
package/dist/useTimeSeries.js
CHANGED
|
@@ -7,6 +7,17 @@ import { TimeSeries } from 'pond-ts';
|
|
|
7
7
|
* serialized via `JSON.stringify` as the cache key — fine for small to
|
|
8
8
|
* moderate payloads. For large datasets, pass an explicit `key` (e.g. a fetch
|
|
9
9
|
* URL or ETag) to avoid the serialization cost.
|
|
10
|
+
*
|
|
11
|
+
* The schema generic `S` is inferred directly from `input.schema` (a plain
|
|
12
|
+
* structural position), so an `as const` schema narrows the returned
|
|
13
|
+
* `TimeSeries<S>` fully — `result.column('cpu')` resolves to `Float64Column`,
|
|
14
|
+
* `result.at(i)!.get('cpu')` to `number | undefined`, etc. An earlier
|
|
15
|
+
* two-generic `<S, I extends Parameters<...>[0]>` shape lost `S` through the
|
|
16
|
+
* input-wrapper generic and collapsed schema-narrowed accessors to `never`
|
|
17
|
+
* (the loose `.get(string)` path masked it); the column API surfaced it. The
|
|
18
|
+
* accepted input type is unchanged — `Parameters<fromJSON<S>>[0]` already
|
|
19
|
+
* resolved to `TimeSeriesJsonInput<S> & { parse? }` — so this is purely an
|
|
20
|
+
* inference fix, not a surface change.
|
|
10
21
|
*/
|
|
11
22
|
export function useTimeSeries(input, key) {
|
|
12
23
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTimeSeries.js","sourceRoot":"","sources":["../src/useTimeSeries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"useTimeSeries.js","sourceRoot":"","sources":["../src/useTimeSeries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAOrC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAC3B,KAA2D,EAC3D,GAAY;IAEZ,uDAAuD;IACvD,MAAM,QAAQ,GAAG,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9C,uDAAuD;IACvD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAI,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "React hooks for pond-ts live time series",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsc -p tsconfig.json",
|
|
29
29
|
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE && cp ../../CHANGELOG.md ./CHANGELOG.md && npm run build",
|
|
30
|
-
"test": "
|
|
30
|
+
"test": "npm run test:type && npm run test:runtime",
|
|
31
|
+
"test:type": "tsc -p tsconfig.types.json",
|
|
31
32
|
"test:runtime": "vitest run"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
|
-
"pond-ts": "^0.
|
|
35
|
+
"pond-ts": "^0.19.0",
|
|
35
36
|
"react": "^18.0.0 || ^19.0.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|