@pond-ts/react 0.69.0 → 0.70.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 (3) hide show
  1. package/API.md +25 -25
  2. package/CHANGELOG.md +67 -1
  3. package/package.json +2 -2
package/API.md CHANGED
@@ -716,31 +716,31 @@ Typed dataflow graphs for pipelines whose **shape is data** (runtime-assembled,
716
716
  user-edited, one computation fanned out to several consumers). Chaining stays
717
717
  the default for pipelines known at authoring time — see the package README.
718
718
 
719
- | Group | Exports | Source |
720
- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
721
- | Worker pool (Node) | `HostPool` (`start`, `run`, `close`, `size`, `inFlight`); types `HostPoolOptions`, `PoolSetup`, `PoolSetupConfig`; `toWire` / `fromWire`, types `WireResult`, `WireColumn` — subpath `@pond-ts/process/pool` | `packages/process/src/pool/index.ts` |
722
- | Ports | `Inlet`, `Outlet` (typed fields on `node.in` / `node.out`; `get()`, `peek()`, `version`, `connect`, `disconnect`) | `packages/process/src/port.ts` |
723
- | Nodes | `Node` (`in`, `out`, `dirty`, `error`, `invalidate()`), `defineNode` (reusable multi-output node type), `derive` (single-output, wired inline) | `packages/process/src/node.ts` |
724
- | Port declaration | `port<T>({ equals, defaultValue })`; types `PortSpec`, `PortSpecMap`, `PortValue`, `PortValues` | `packages/process/src/types.ts` |
725
- | Sources | `source<T>()` → `SourceNode` (`set()`), `fromLive(liveSource)` → `LiveSourceNode` (`dispose()`); `GraphSource` (bind contract — looser than core's `LiveSource`, accepts `LiveAggregation`), `SnapshotSource`, `NoInputs` | `packages/process/src/source.ts` |
726
- | Graph view | `Graph` (`Graph.from(...roots)`, `nodes`, `order()`, `edges()`, `toJSON()`); types `GraphEdge`, `GraphJson`, `GraphNodeJson`, `GraphEdgeJson` | `packages/process/src/graph.ts` |
727
- | Range output buffers | `prepareRange(length, keep, prior)` → `RangeOutput` (`values`, `bits`, `set`, `clear`) carrying `[0, keep)` forward as blocks — values **and** validity; `sealRange(out, length)` → `Float64Column`; `validityByteCount`. Reached from an op as `ctx.out[n]` | `packages/process/src/column.ts` |
728
- | Ranged recompute | `graph.setSourceFrom(series, changedFrom)` — declares which row first changed; `graph.recomputes` → `{ ranged, full }`. An op opts in with `OpDef.runRange(ctx)`, receiving `{ from, to, previous, previousView, out }` (type `RangeContext`) — write into `out` and return nothing for the block path alongside the usual context. Requires `lookback`. Falls back to a full `run` whenever anything is missing | `packages/process/src/plan/graph.ts` |
729
- | Node budget | `bind(series, { registry, budgetBytes })` — engine-wide cap on retained node values, LRU, enforced after each `run`; `graph.retainedBytes` / `graph.evictions` / `graph.enforceBudget()`. Unbounded when omitted. Skips a node whose consumer still holds its outlet | `packages/process/src/plan/graph.ts` |
730
- | Plan history | `requiredHistory(registry, plan)` → `{ known, rows?, undeclared, byOp }` — the minimum safe tail in rows, folded from per-op `OpDef.lookback`. Sums along nesting, maxes across siblings. `known: false` names ops with no declared lookback rather than defaulting to zero (type `HistoryResult`) | `packages/process/src/plan/history.ts` |
731
- | Column values | `packColumn` (values → packed `Float64Column`, NaN = missing), `columnBytes` (retained size, for a byte budget), `appendColumn` (column → series; boxing-free when gapless), `columnBuffers` / `columnFromBuffers` (the buffer pair a column is, for an isolate boundary; type `ColumnBuffers`), `columnView` (zero-copy borrowed read view for in-process folds; type `ColumnView`) | `packages/process/src/column.ts` |
732
- | Plan — registry | `createRegistry({ folds })` / `Registry` (`define`, `get`, `foldFor`, `outputsOf`, `resolveParams` (`{ validate: false }` applies defaults and skips every check), `byFamily`, `describe`, `toJsonSchema`), param builders `int` / `num` / `choice` / `flag`, `UnknownOpError`, `ParamError` | `packages/process/src/plan/registry.ts`, `params.ts` |
733
- | Plan — identity | `specId(registry, spec, { validate })` (content-addressed, param-order invariant, defaults materialized; `validate: false` is **total** — names a spec that would not compile in a separate `p1?:` namespace that cannot collide with a valid id; a valid spec's id is identical either way), `SpecIdOptions`, `refToId`, `explain`, `unitOf`, `columnsOf`, `dependsOn`, `outputKey` | `packages/process/src/plan/identity.ts` |
734
- | Plan — types | `Spec`, `Plan`, `Input` (column name \| `Spec` \| `PickedOutput`), `SpecRef`, `Def` (`OpDef` \| `FoldDef`), `OpContext`, `OpResult`, `FoldContext`, `FactBody`, `isFold`, `ParamDef`, `Params`, `Units`, `InputDef`, `OutputDef` | `packages/process/src/plan/types.ts` |
735
- | Plan — bind / run | `bind(series, { registry, units })` → `BoundGraph` (`compile`, `setSource`, `ids`, `series`, `columnOf`), `run(graph, { plan, select, onError })` → `RunResult`, `UnitError`, `UnknownColumnError` (a raw string input naming no column of the bound series, checked over the whole closure and re-checked on the warm path) | `packages/process/src/plan/graph.ts`, `run.ts` |
736
- | Plan — request/response | `RunRequest` (`PlanRequest` \| `SlotRequest`), `RunOptions`, `RunResult`, `Select` (`{ on, output?, name? }` — points at a node; what comes back is what that node produces), `ErrorPolicy`, `Fact` (carries `op`), `OutputInfo`, `Skipped` (`spec`, `select`, `reason`, `code` — the failure's kind, matching the error class a throw would have carried), `NodeTiming` (`slot`, `pulled`, `cached`, `ms`, `inputs`) | `packages/process/src/plan/run.ts` |
737
- | Plan — host | `createHost({ registry, units, sources })` → `Host` (`add`, `has`, `datasets`, `graphFor`, `run`, `runAsync`), `toWire`, `UnknownDatasetError`; local-string `Envelope` (`PlanEnvelope` \| `SlotEnvelope`), remote-capable `AsyncEnvelope` (`AsyncPlanEnvelope` \| `AsyncSlotEnvelope` \| `Envelope`), `DatasetInfo`, `WireResult` | `packages/process/src/plan/host.ts` |
738
- | Plan — slots | `expandSlots(slots, columns)` → `Map<slot, Spec>` (expands to the nested form, so ids match by construction; `slot#Output` picks one output), `SlotError`; types `SlotDef` (`{ op, params, in }`), `Slots` | `packages/process/src/plan/slots.ts` |
739
- | Plan — builder | `plan(from)` → low-level `PlanBuilder`; `process(registry, from)` → typed fluent `ProcessBuilder` (`column`, op methods, `outputs`), `BuilderError`; types `NodeHandle`, `OutputHandle`, `FluentColumnRef`, `SingleColumnNode`, `MultiColumnNode`, `ColumnSelection`, `FactRef`, `BuiltRequest` | `packages/process/src/plan/builder.ts`, `fluent.ts` |
740
- | Plan — async sources | `defineSource({ name, load })`, `createSourceRegistry()` / `SourceRegistry`, `sourceId`, `UnknownSourceError`; types `SourceRef`, `SourceParams`, `LoadedSource` (value + revision), `SourceLoadContext`, `SourceDef` | `packages/process/src/plan/source.ts` |
741
- | Plan — folds | `STANDARD_FOLDS` and the four it holds — `last`, `extremes`, `percentileRank`, `shape` — pre-registered by `createRegistry()`; each a plain `FoldDef`, so a consumer can `define` over one | `packages/process/src/plan/folds.ts` |
742
- | Errors | `ProcessError` (base; `code` — a stable per-class literal, minification-proof, also surfaced on `Skipped`), `CycleError`, `UnconnectedInputError`, `MissingOutputError`, `UnsetSourceError` | `packages/process/src/errors.ts` |
743
- | Node type helpers | `NodeSpec`, `NodeFactory`, `InletsFor`, `OutletsFor`, `OutletValue`, `SpecsForOutlets`, `DerivedOutput` | `packages/process/src/node.ts` |
719
+ | Group | Exports | Source |
720
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
721
+ | Worker pool (Node) | `HostPool` (`start`, `run`, `close`, `size`, `inFlight`); types `HostPoolOptions`, `PoolSetup`, `PoolSetupConfig`; `toWire` / `fromWire`, types `WireResult`, `WireColumn` — subpath `@pond-ts/process/pool` | `packages/process/src/pool/index.ts` |
722
+ | Ports | `Inlet`, `Outlet` (typed fields on `node.in` / `node.out`; `get()`, `peek()`, `version`, `connect`, `disconnect`) | `packages/process/src/port.ts` |
723
+ | Nodes | `Node` (`in`, `out`, `dirty`, `error`, `invalidate()`), `defineNode` (reusable multi-output node type), `derive` (single-output, wired inline) | `packages/process/src/node.ts` |
724
+ | Port declaration | `port<T>({ equals, defaultValue })`; types `PortSpec`, `PortSpecMap`, `PortValue`, `PortValues` | `packages/process/src/types.ts` |
725
+ | Sources | `source<T>()` → `SourceNode` (`set()`), `fromLive(liveSource)` → `LiveSourceNode` (`dispose()`); `GraphSource` (bind contract — looser than core's `LiveSource`, accepts `LiveAggregation`), `SnapshotSource`, `NoInputs` | `packages/process/src/source.ts` |
726
+ | Graph view | `Graph` (`Graph.from(...roots)`, `nodes`, `order()`, `edges()`, `toJSON()`); types `GraphEdge`, `GraphJson`, `GraphNodeJson`, `GraphEdgeJson` | `packages/process/src/graph.ts` |
727
+ | Range output buffers | `prepareRange(length, keep, prior)` → `RangeOutput` (`values`, `bits`, `set`, `clear`) carrying `[0, keep)` forward as blocks — values **and** validity; `sealRange(out, length)` → `Float64Column`; `validityByteCount`. Reached from an op as `ctx.out[n]` | `packages/process/src/column.ts` |
728
+ | Ranged recompute | `graph.setSourceFrom(series, changedFrom)` — declares which row first changed; `graph.recomputes` → `{ ranged, full }`. An op opts in with `OpDef.runRange(ctx)`, receiving `{ from, to, previous, previousView, out }` (type `RangeContext`) — write into `out` and return nothing for the block path alongside the usual context. Requires `lookback`. Falls back to a full `run` whenever anything is missing | `packages/process/src/plan/graph.ts` |
729
+ | Node budget | `bind(series, { registry, budgetBytes })` — engine-wide cap on retained node values, LRU, enforced after each `run`; `graph.retainedBytes` / `graph.evictions` / `graph.enforceBudget()`. Unbounded when omitted. Skips a node whose consumer still holds its outlet | `packages/process/src/plan/graph.ts` |
730
+ | Plan history | `requiredHistory(registry, plan)` → `{ known, rows?, undeclared, byOp }` — the minimum safe tail in rows, folded from per-op `OpDef.lookback`. Sums along nesting, maxes across siblings. `known: false` names ops with no declared lookback rather than defaulting to zero (type `HistoryResult`) | `packages/process/src/plan/history.ts` |
731
+ | Column values | `packColumn` (values → packed `Float64Column`, NaN = missing), `columnBytes` (retained size, for a byte budget), `appendColumn` (column → series; boxing-free when gapless), `columnBuffers` / `columnFromBuffers` (the buffer pair a column is, for an isolate boundary; type `ColumnBuffers`), `columnView` (zero-copy borrowed read view for in-process folds; type `ColumnView`) | `packages/process/src/column.ts` |
732
+ | Plan — registry | `createRegistry({ folds })` / `Registry` (`define`, `get`, `foldFor`, `outputsOf`, `resolveParams` (`{ validate: false }` applies defaults and skips every check), `byFamily`, `describe`, `toJsonSchema`, `checkArity`), param builders `int` / `num` / `choice` / `flag`, `UnknownOpError`, `ParamError`, `ArityError` | `packages/process/src/plan/registry.ts`, `params.ts` |
733
+ | Plan — identity | `specId(registry, spec, { validate })` (content-addressed, param-order invariant, defaults materialized; `validate: false` is **total over arbitrary JSON** — names a spec that would not compile, including malformed shapes, in a separate `p1?:` namespace that cannot collide with a valid id; a valid spec's id is identical either way. Judges op existence, params **and arity**), `SpecIdOptions`, `refToId`, `explain`, `unitOf`, `columnsOf`, `dependsOn`, `outputKey` | `packages/process/src/plan/identity.ts` |
734
+ | Plan — types | `Spec`, `Plan`, `Input` (column name \| `Spec` \| `PickedOutput`), `SpecRef`, `Def` (`OpDef` \| `FoldDef`), `OpContext`, `OpResult`, `FoldContext`, `FactBody`, `isFold`, `ParamDef`, `Params`, `Units`, `InputDef`, `OutputDef` | `packages/process/src/plan/types.ts` |
735
+ | Plan — bind / run | `bind(series, { registry, units })` → `BoundGraph` (`compile`, `setSource`, `ids`, `series`, `columnOf`), `run(graph, { plan, select, onError })` → `RunResult`, `UnitError`, `UnknownColumnError` (a raw string input naming no column of the bound series, checked over the whole closure and re-checked on the warm path) | `packages/process/src/plan/graph.ts`, `run.ts` |
736
+ | Plan — request/response | `RunRequest` (`PlanRequest` \| `SlotRequest`), `RunOptions`, `RunResult`, `Select` (`{ on, output?, name? }` — points at a node; what comes back is what that node produces), `ErrorPolicy`, `Fact` (carries `op`), `OutputInfo`, `Skipped` (`spec` — echoed verbatim, `params`/`inputs` typed `unknown`; `select`, `reason`, `code` — the failure's kind, matching the error class a throw would have carried), `NodeTiming` (`slot`, `pulled`, `cached`, `ms`, `inputs`) | `packages/process/src/plan/run.ts` |
737
+ | Plan — host | `createHost({ registry, units, sources })` → `Host` (`add`, `has`, `datasets`, `graphFor`, `run`, `runAsync`), `toWire`, `UnknownDatasetError`; local-string `Envelope` (`PlanEnvelope` \| `SlotEnvelope`), remote-capable `AsyncEnvelope` (`AsyncPlanEnvelope` \| `AsyncSlotEnvelope` \| `Envelope`), `DatasetInfo`, `WireResult` | `packages/process/src/plan/host.ts` |
738
+ | Plan — slots | `expandSlots(slots, columns)` → `Map<slot, Spec>` (expands to the nested form, so ids match by construction; `slot#Output` picks one output), `SlotError`; types `SlotDef` (`{ op, params, in }`), `Slots` | `packages/process/src/plan/slots.ts` |
739
+ | Plan — builder | `plan(from)` → low-level `PlanBuilder`; `process(registry, from)` → typed fluent `ProcessBuilder` (`column`, op methods, `outputs`), `BuilderError`; types `NodeHandle`, `OutputHandle`, `FluentColumnRef`, `SingleColumnNode`, `MultiColumnNode`, `ColumnSelection`, `FactRef`, `BuiltRequest` | `packages/process/src/plan/builder.ts`, `fluent.ts` |
740
+ | Plan — async sources | `defineSource({ name, load })`, `createSourceRegistry()` / `SourceRegistry`, `sourceId`, `UnknownSourceError`; types `SourceRef`, `SourceParams`, `LoadedSource` (value + revision), `SourceLoadContext`, `SourceDef` | `packages/process/src/plan/source.ts` |
741
+ | Plan — folds | `STANDARD_FOLDS` and the four it holds — `last`, `extremes`, `percentileRank`, `shape` — pre-registered by `createRegistry()`; each a plain `FoldDef`, so a consumer can `define` over one | `packages/process/src/plan/folds.ts` |
742
+ | Errors | `ProcessError` (base; `code` — a stable per-class literal, minification-proof, also surfaced on `Skipped`), `CycleError`, `UnconnectedInputError`, `MissingOutputError`, `UnsetSourceError` | `packages/process/src/errors.ts` |
743
+ | Node type helpers | `NodeSpec`, `NodeFactory`, `InletsFor`, `OutletsFor`, `OutletValue`, `SpecsForOutlets`, `DerivedOutput` | `packages/process/src/node.ts` |
744
744
 
745
745
  Note: this package's `npm test` includes a `test:dts` step that typechecks the
746
746
  **emitted** `dist/*.d.ts` from a consumer's perspective (`test-dts/`,
package/CHANGELOG.md CHANGED
@@ -8,7 +8,8 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`,
8
8
  under a single `v*` tag, so this file covers them all. Pre-1.0: minor bumps may
9
9
  include new features and type-level changes; patch bumps are strictly additive.
10
10
 
11
- [Unreleased]: https://github.com/pond-ts/pond/compare/v0.69.0...HEAD
11
+ [Unreleased]: https://github.com/pond-ts/pond/compare/v0.70.0...HEAD
12
+ [0.70.0]: https://github.com/pond-ts/pond/compare/v0.69.0...v0.70.0
12
13
  [0.69.0]: https://github.com/pond-ts/pond/compare/v0.68.0...v0.69.0
13
14
  [0.68.0]: https://github.com/pond-ts/pond/compare/v0.67.0...v0.68.0
14
15
  [0.67.0]: https://github.com/pond-ts/pond/compare/v0.66.0...v0.67.0
@@ -72,6 +73,71 @@ include new features and type-level changes; patch bumps are strictly additive.
72
73
 
73
74
  ## [Unreleased]
74
75
 
76
+ ## [0.70.0] — 2026-09-18
77
+
78
+ ### Added
79
+
80
+ - `@pond-ts/charts`: **`<BandChart sessionBreaks>`** — the same trading-axis
81
+ session break `<LineChart>` has had since v0.45.0. On a discontinuous
82
+ (`discontinuities` / `calendar`) axis the fill previously ran a near-vertical
83
+ sliver from one session's last sample to the next session's first, because
84
+ the collapsed overnight gap put them a pixel apart; `sessionBreaks` ends the
85
+ envelope at the close and re-starts it at the open, so a band and its centre
86
+ line break in step. A **scale** break, orthogonal to the NaN **data** gaps a
87
+ band always breaks at; default `false` (unchanged output). Decimation
88
+ composes: `decimateBand` folds each break instant into the pixel-column
89
+ edges and bakes a `NaN` sample at it, so no column merges two sessions'
90
+ envelopes. Stories `Axes/TradingTimeAxis / SessionBreaksBand` and
91
+ `Performance/Decimation / TradingSessionBreaksBand`.
92
+ - `@pond-ts/process`: **`ArityError`**, and **arity is now part of what `specId`
93
+ can judge**. A spec whose `inputs` count does not match the op's — including
94
+ one carrying no `inputs` at all — was named `p1:sma(;period=20)`, a _valid_
95
+ id for something that cannot compile, and then died at `compile` as a bare
96
+ `TypeError` reading `.length` of `undefined`, reaching the consumer as a
97
+ `Skipped` with **no `code`** (which under that contract means "op code
98
+ threw"). Arity is decidable from the registry alone, so strict `specId` now
99
+ raises `ArityError` and lenient mode marks the id `p1?:`.
100
+ `Registry.checkArity(op, inputs)` is the shared check `compile` uses too.
101
+
102
+ ### Changed
103
+
104
+ - `@pond-ts/financial`: **`StudyOutput.id` documents the `@pond-ts/process`
105
+ bridge**, and a new `test/catalog-process.test.ts` pins it. The field is a
106
+ *financial column suffix*; process's `OutputDef.id` is a *process outlet
107
+ id*; they share a name and are different namespaces. Process names its own
108
+ columns (`specId + OutputDef.id`) and matches an op's return to its outputs
109
+ positionally, so a study's own column names never reach it — which means a
110
+ registry bridging the two chooses its own suffixes, and for the **twelve**
111
+ multi-output studies that claim the bare prefix (`trix`, `superTrend`,
112
+ `klinger`, …) it must: process rejects `''` on a multi-output op, because
113
+ there the column would be named exactly the spec id, itself a legal column
114
+ reference. The map is `outputs.length > 1 && id === ''` → `'value'` — not
115
+ `id === ''`, which would rename all seventy-odd single-output columns for
116
+ nothing. Reported by a consumer that hit the throw at module load and
117
+ worked around it by hand. No API change: the descriptors, the studies and
118
+ the guard are all unchanged, and the round-trip test is what stops the two
119
+ packages drifting — `catalog.test.ts` validates a descriptor against its
120
+ *study*, so it is structurally blind to a cross-package disagreement.
121
+ - `@pond-ts/process`: **`Skipped.spec` echoes the request verbatim**, and its
122
+ `params` / `inputs` are typed `unknown` accordingly. The plan pass normalized
123
+ `params: null` to `{}`, so recomputing an id from the echo produced the
124
+ _defaulted spec's valid id_ — keying a broken persisted entry's report onto a
125
+ legitimate node. The selector pass echoed the original all along, so the two
126
+ passes disagreed. **Migration:** a consumer reading `entry.spec.params` now
127
+ narrows it (`entry.spec.params as Record<string, unknown>`, or a guard) —
128
+ which is the point, since the value may be exactly the malformed thing that
129
+ failed.
130
+ - `@pond-ts/process`: `specId(…, { validate: false })` is **total over
131
+ arbitrary JSON**, not just over well-typed specs. `params: null`, a
132
+ non-array `inputs`, and an input entry that is neither a column name nor a
133
+ spec each used to raise a `TypeError`; they are now named in the `p1?:`
134
+ namespace, distinctly enough that two differently-broken specs stay two ids.
135
+ Strict mode reports them as `ParamError` / `ArityError` / `ProcessError`
136
+ rather than crashing.
137
+
138
+ (All three reported by Tidal against 0.62.0, after adopting it.)
139
+
140
+
75
141
  ## [0.69.0] — 2026-09-13
76
142
 
77
143
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pond-ts/react",
3
- "version": "0.69.0",
3
+ "version": "0.70.0",
4
4
  "description": "React hooks for pond-ts: subscribe to LiveSeries and derived views with throttled snapshots",
5
5
  "keywords": [
6
6
  "time-series",
@@ -52,7 +52,7 @@
52
52
  "test:runtime": "vitest run"
53
53
  },
54
54
  "peerDependencies": {
55
- "pond-ts": "^0.69.0",
55
+ "pond-ts": "^0.70.0",
56
56
  "react": "^18.0.0 || ^19.0.0"
57
57
  },
58
58
  "devDependencies": {