pond-ts 0.67.0 → 0.68.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/AGENTS.md +244 -0
- package/API.md +10 -9
- package/CHANGELOG.md +29 -1
- package/README.md +20 -0
- package/dist/batch/partitioned-time-series.d.ts +37 -19
- package/dist/batch/time-series.d.ts +2 -2
- package/package.json +29 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Using pond from a coding agent
|
|
2
|
+
|
|
3
|
+
You are reading this because a project depends on `pond-ts` or one of the
|
|
4
|
+
`@pond-ts/*` packages, or because you are deciding whether it should. This
|
|
5
|
+
file is the shortest route to correct code. It ships inside every pond
|
|
6
|
+
tarball as `AGENTS.md`, next to `API.md` (every public export, one line
|
|
7
|
+
each, with its source file) and `CHANGELOG.md`.
|
|
8
|
+
|
|
9
|
+
Docs: <https://pond-ts.org> · index for agents: <https://pond-ts.org/llms.txt>
|
|
10
|
+
· source: <https://github.com/pond-ts/pond>.
|
|
11
|
+
|
|
12
|
+
## What pond is, in three lines
|
|
13
|
+
|
|
14
|
+
- A **typed, immutable time series** (`TimeSeries`) whose schema is declared
|
|
15
|
+
once `as const` and narrows every downstream transform — no casts.
|
|
16
|
+
- The **same operator vocabulary on a streaming buffer** (`LiveSeries`):
|
|
17
|
+
push events in, subscribe to incremental `rolling` / `aggregate` views,
|
|
18
|
+
bounded by retention.
|
|
19
|
+
- **Domain packages on top**: React hooks, canvas charts that read a series
|
|
20
|
+
directly, financial studies + trading calendars, fitness analytics, and an
|
|
21
|
+
experimental processing-graph runtime.
|
|
22
|
+
|
|
23
|
+
## Which package
|
|
24
|
+
|
|
25
|
+
| You need to… | Install | Import from |
|
|
26
|
+
| ----------------------------------------------------------------------- | ------------------------------------ | ----------------------------------------------------- |
|
|
27
|
+
| Load timestamped rows; bucket, regrid, roll, fill, join, partition them | `pond-ts` | `'pond-ts'` |
|
|
28
|
+
| Ingest a live feed and keep rolling stats over the last N minutes | `pond-ts` | `'pond-ts'` (`LiveSeries`) |
|
|
29
|
+
| Share a series' type across a wire boundary with zero runtime | `pond-ts` | `'pond-ts/types'` |
|
|
30
|
+
| Own / subscribe to a series inside React | `@pond-ts/react` | `'@pond-ts/react'` |
|
|
31
|
+
| Draw it (line, area, band, bar, scatter, box, candlestick, heat map) | `@pond-ts/charts` (+ react, pond-ts) | `'@pond-ts/charts'` |
|
|
32
|
+
| OHLCV bars, SMA/EMA/RSI/MACD/Bollinger/ATR/VWAP…, market-hours calendar | `@pond-ts/financial` | `'@pond-ts/financial'`, `'@pond-ts/financial/fluent'` |
|
|
33
|
+
| GPS / power / heart-rate activity analytics | `@pond-ts/fit` | `'@pond-ts/fit'` |
|
|
34
|
+
| Computations as JSON plans with caching + provenance (experimental) | `@pond-ts/process` | `'@pond-ts/process'` |
|
|
35
|
+
|
|
36
|
+
All six release together under one version and release often. Install with
|
|
37
|
+
`@latest` rather than a version written from memory (a cold-start agent once
|
|
38
|
+
wrote `^0.3.0` and spent turns on a 2024 API); keep their ranges in step — a
|
|
39
|
+
pre-1.0 caret (`^0.67.0`) does **not** span minors.
|
|
40
|
+
|
|
41
|
+
## The idioms that cover most jobs
|
|
42
|
+
|
|
43
|
+
### 1. Declare the schema, build the series
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { TimeSeries, Sequence } from 'pond-ts';
|
|
47
|
+
|
|
48
|
+
const schema = [
|
|
49
|
+
{ name: 'time', kind: 'time' },
|
|
50
|
+
{ name: 'host', kind: 'string' },
|
|
51
|
+
{ name: 'latencyMs', kind: 'number' },
|
|
52
|
+
] as const; // ← load-bearing. Without it every column widens to string.
|
|
53
|
+
|
|
54
|
+
const s = TimeSeries.fromJSON({
|
|
55
|
+
name: 'latency',
|
|
56
|
+
schema,
|
|
57
|
+
rows, // positional tuples [time, host, latencyMs] or objects { time, host, latencyMs }
|
|
58
|
+
sort: true, // input not already time-ordered? sort on construction (stable)
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Time cells accept ms-since-epoch numbers, `Date`s, or ISO strings **with an
|
|
63
|
+
offset** (`…Z`, `…+01:00`). A wall-clock string with no offset throws unless
|
|
64
|
+
you pass `parse: { timeZone: 'America/New_York' }`.
|
|
65
|
+
|
|
66
|
+
Other doors: `TimeSeries.fromPoints(points)` for wide `{ ts, a, b }` rows,
|
|
67
|
+
`fromColumns` for struct-of-arrays / `Float64Array`, `fromArrow` for an Arrow
|
|
68
|
+
table, `fromEvents`. `toJSON()` round-trips.
|
|
69
|
+
|
|
70
|
+
### 2. Downsample, regrid, slide — three different verbs
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// Fewer rows out than in: one row per bucket.
|
|
74
|
+
const perMin = s.aggregate(Sequence.every('1m'), {
|
|
75
|
+
latencyMs: 'avg', // reducer by column …
|
|
76
|
+
p95: { from: 'latencyMs', using: 'p95' }, // … or a named output; reducers: sum avg min max count first last median stdev pNN
|
|
77
|
+
host: 'last',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Same information, on a regular grid (hold / interpolate). No reduction.
|
|
81
|
+
const gridded = s.align(Sequence.every('10s'), { method: 'hold' });
|
|
82
|
+
|
|
83
|
+
// One output per input event, looking back over a window.
|
|
84
|
+
const rolled = s.rolling('5m', {
|
|
85
|
+
latencyMs: 'avg',
|
|
86
|
+
sd: { from: 'latencyMs', using: 'stdev' },
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`Sequence.every()` takes fixed durations only (`'10s'`, `'5m'`, `'1h'`,
|
|
91
|
+
`'1d'`). Months, weeks-in-a-zone, calendar days: `Sequence.calendar('month',
|
|
92
|
+
{ timeZone })`. Common shortcuts: `s.baseline('latencyMs', { window: '1h',
|
|
93
|
+
sigma: 2 })` appends avg / sd / upper / lower in one pass;
|
|
94
|
+
`s.outliers(col, { window, sigma })` keeps only the rows outside the band.
|
|
95
|
+
|
|
96
|
+
### 3. Per-entity, then flatten
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const perHost = s
|
|
100
|
+
.partitionBy('host') // every stateful operator below runs per host
|
|
101
|
+
.rolling('5m', { latencyMs: 'avg' })
|
|
102
|
+
.collect(); // one flat TimeSeries, `host` carried through (type and runtime)
|
|
103
|
+
// or .toMap() → Map<host, TimeSeries>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`aggregate` and `rolling` under `partitionBy` carry the partition column
|
|
107
|
+
through in both the runtime **and** the static type (since 0.68), so
|
|
108
|
+
`e.get('host')` works on the collected result without naming it. On 0.67 or
|
|
109
|
+
older, name it in the mapping — `{ host: 'first', … }`.
|
|
110
|
+
|
|
111
|
+
### 4. Clean, fill, join, read out
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const clean = s.dedupe().fill({ latencyMs: 'hold' }); // also 'linear', 'zero', gap caps
|
|
115
|
+
const joined = a.join(b); // on the time key; see API.md for options
|
|
116
|
+
clean.toPoints(); // [{ ts, host, latencyMs }, …] — chart-library friendly
|
|
117
|
+
clean.toRows(); // positional tuples
|
|
118
|
+
clean.column('latencyMs').mean(); // typed column: min/max/sum/mean/stdev/median/percentile
|
|
119
|
+
clean.column('latencyMs').toFloat64Array(); // zero-copy for canvas / WebGL loops
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Everything returns a **new** series. There is no `push` on a `TimeSeries`;
|
|
123
|
+
if you are appending, you want a `LiveSeries`.
|
|
124
|
+
|
|
125
|
+
### 5. Streaming
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { LiveSeries, Sequence } from 'pond-ts';
|
|
129
|
+
|
|
130
|
+
const live = new LiveSeries({
|
|
131
|
+
name: 'latency',
|
|
132
|
+
schema,
|
|
133
|
+
retention: { maxAge: '15m' }, // or { maxEvents: 10_000 }
|
|
134
|
+
ordering: 'reorder', // tolerate late rows …
|
|
135
|
+
graceWindow: '5s', // … up to this late
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const view = live.partitionBy('host').rolling('5m', { latencyMs: 'avg' });
|
|
139
|
+
const stop = view.on('event', (e) => render(e.get('host'), e.get('latencyMs')));
|
|
140
|
+
|
|
141
|
+
live.push([Date.now(), 'api-1', 42]); // validated against the schema
|
|
142
|
+
live.pushMany(batch);
|
|
143
|
+
const snapshot = live.toTimeSeries(); // immutable batch copy for analytics
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`live.aggregate(Sequence.every('1m'), …)` emits `'bucket'` (partial) and
|
|
147
|
+
`'close'` (final) events. Retention bounds memory; `sample({ stride })`
|
|
148
|
+
between `partitionBy` and a long `rolling` bounds it further at firehose
|
|
149
|
+
rates.
|
|
150
|
+
|
|
151
|
+
### React and charts
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { useLiveSeries } from '@pond-ts/react';
|
|
155
|
+
import {
|
|
156
|
+
ChartContainer,
|
|
157
|
+
ChartRow,
|
|
158
|
+
Layers,
|
|
159
|
+
LineChart,
|
|
160
|
+
YAxis,
|
|
161
|
+
} from '@pond-ts/charts';
|
|
162
|
+
|
|
163
|
+
const [live, snap] = useLiveSeries({
|
|
164
|
+
name: 'latency',
|
|
165
|
+
schema,
|
|
166
|
+
retention: { maxAge: '10m' },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
<ChartContainer width={800} cursor="crosshair" panZoom>
|
|
170
|
+
<ChartRow height={240}>
|
|
171
|
+
<YAxis id="ms" />
|
|
172
|
+
<Layers>
|
|
173
|
+
{snap && <LineChart series={snap} column="latencyMs" axis="ms" />}
|
|
174
|
+
</Layers>
|
|
175
|
+
</ChartRow>
|
|
176
|
+
</ChartContainer>;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Charts read a pond series directly — do the maths in pond (`rolling`,
|
|
180
|
+
`aggregate`, `align`) and hand the result to a layer. `useLiveSeries`'s snapshot is `null` before the first
|
|
181
|
+
push, hence the guard. `width` is a pixel
|
|
182
|
+
number or `'auto'` (the parent then needs a definite width, or nothing draws).
|
|
183
|
+
Hooks: `useTimeSeries`, `useLiveSeries`, `useSnapshot`, `useLiveQuery`,
|
|
184
|
+
`useDerived`, `useWindow`, `useCurrent`, `useLatest`.
|
|
185
|
+
|
|
186
|
+
### Financial
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import '@pond-ts/financial/fluent'; // once, anywhere: adds studies to TimeSeries
|
|
190
|
+
import { TradingCalendar } from '@pond-ts/financial';
|
|
191
|
+
|
|
192
|
+
const studied = bars
|
|
193
|
+
.sma({ period: 20 })
|
|
194
|
+
.rsi({ period: 14 })
|
|
195
|
+
.bollinger({ period: 20 });
|
|
196
|
+
// or, function form: sma(bars, { period: 20 })
|
|
197
|
+
const cal = TradingCalendar.fromRules(
|
|
198
|
+
{ timeZone: 'America/New_York', open: '09:30', close: '16:00' },
|
|
199
|
+
{ from: '2026-01-05', to: '2026-02-13' },
|
|
200
|
+
);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Studies read `'close'` by default, take **bar-count** periods, append
|
|
204
|
+
columns, preserve row count (warm-up rows are `undefined`). Sixty-plus of
|
|
205
|
+
them; `import { STUDIES } from '@pond-ts/financial/catalog'` lists them at runtime. Session-aligned bars: `ticks.aggregate(cal.barSequence('5m'), {...})`.
|
|
206
|
+
|
|
207
|
+
## Mistakes agents actually make
|
|
208
|
+
|
|
209
|
+
1. **Dropping `as const` on the schema.** Everything compiles and every
|
|
210
|
+
column is `string`. If `.get('x')` is not `number | undefined`, this is
|
|
211
|
+
why.
|
|
212
|
+
2. **`aggregate` when you meant `rolling`, or vice versa.** `aggregate`
|
|
213
|
+
changes the row count (one per bucket); `rolling` keeps it (one per
|
|
214
|
+
event); `align` puts rows on a grid without reducing.
|
|
215
|
+
3. **`Sequence.every('1M')` for months.** Not fixed-length → use
|
|
216
|
+
`Sequence.calendar('month', { timeZone })`.
|
|
217
|
+
4. **Wall-clock strings without a zone.** `'2025-01-01T09:00'` throws; add
|
|
218
|
+
`parse: { timeZone }` or use offset strings / ms numbers.
|
|
219
|
+
5. **Unsorted rows.** The constructor throws and names the row; pass
|
|
220
|
+
`sort: true` rather than sorting by hand.
|
|
221
|
+
6. **Mutating.** Nothing mutates. Capture the return value.
|
|
222
|
+
7. **Iterating events in a hot loop for a chart.** Use `column(name)` /
|
|
223
|
+
`toFloat64Array()` or hand the series to `@pond-ts/charts` — do not
|
|
224
|
+
rebuild point arrays per frame.
|
|
225
|
+
8. **Mismatched package versions.** All `pond-ts` / `@pond-ts/*` at the same
|
|
226
|
+
version, always.
|
|
227
|
+
9. **Reaching for a chart-library adapter first.** If the project uses React,
|
|
228
|
+
`@pond-ts/charts` consumes the series with no adapter; `toPoints()` is the
|
|
229
|
+
bridge for other libraries.
|
|
230
|
+
|
|
231
|
+
## Where to read next
|
|
232
|
+
|
|
233
|
+
- `API.md` (this folder) — find any export and its source file.
|
|
234
|
+
- <https://pond-ts.org/llms.txt> — every docs page with a one-line
|
|
235
|
+
description; `https://pond-ts.org/llms-<area>.txt` for a single-fetch dump
|
|
236
|
+
of one area (`pond-ts`, `charts`, `financial`, …).
|
|
237
|
+
- <https://pond-ts.org/docs/pond-ts/mental-model> — one picture, and the
|
|
238
|
+
pandas / pondjs translation tables.
|
|
239
|
+
- <https://pond-ts.org/docs/how-to-guides> — end-to-end builds with the
|
|
240
|
+
friction already ironed out (dashboard, messy CSV ingest, histograms,
|
|
241
|
+
large series).
|
|
242
|
+
- Claude Code users: `/plugin marketplace add pond-ts/pond` then
|
|
243
|
+
`/plugin install pond-ts@pond-ts` installs skills for core, charts and
|
|
244
|
+
financial.
|
package/API.md
CHANGED
|
@@ -40,13 +40,13 @@ next door is the point.
|
|
|
40
40
|
|
|
41
41
|
### Series classes & construction
|
|
42
42
|
|
|
43
|
-
| Export | Purpose
|
|
44
|
-
| ----------------------- |
|
|
45
|
-
| `TimeSeries` | Immutable time-indexed collection, columnar storage
|
|
46
|
-
| `ValueSeries` | Series keyed by a monotonic non-time value axis
|
|
47
|
-
| `PartitionedTimeSeries` | Scoped view for per-partition stateful transforms
|
|
48
|
-
| `Sequence` | Infinite grid of time buckets (daily, hourly, every N)
|
|
49
|
-
| `BoundedSequence` | Finite ordered list of explicit interval buckets
|
|
43
|
+
| Export | Purpose | Source |
|
|
44
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
45
|
+
| `TimeSeries` | Immutable time-indexed collection, columnar storage | `packages/core/src/batch/time-series.ts` |
|
|
46
|
+
| `ValueSeries` | Series keyed by a monotonic non-time value axis | `packages/core/src/batch/value-series.ts` |
|
|
47
|
+
| `PartitionedTimeSeries` | Scoped view for per-partition stateful transforms; `<S, K, By>` — `By` is the partition column names, carried into `aggregate` / `rolling` result types | `packages/core/src/batch/partitioned-time-series.ts` |
|
|
48
|
+
| `Sequence` | Infinite grid of time buckets (daily, hourly, every N) | `packages/core/src/sequence/sequence.ts` |
|
|
49
|
+
| `BoundedSequence` | Finite ordered list of explicit interval buckets | `packages/core/src/sequence/bounded-sequence.ts` |
|
|
50
50
|
|
|
51
51
|
Static constructors on `TimeSeries`: `fromJSON()` (row tuples/objects),
|
|
52
52
|
`fromColumns()` (struct-of-arrays; `number` + `string` value columns),
|
|
@@ -125,10 +125,11 @@ Value-axis wire types
|
|
|
125
125
|
`arrayContainsAny()`, `arrayAggregate()`, `arrayExplode()`
|
|
126
126
|
- **Gap fill / dedupe**: `fill()`, `materialize()`, `dedupe()`
|
|
127
127
|
- **Aggregate/group**: `aggregate(sequence, spec)`, `reduce()`, `groupBy()`,
|
|
128
|
-
`partitionBy()`, `byColumn()` (
|
|
128
|
+
`partitionBy()`, `byColumn(col, bins, mapping)` (numeric binning of a column into
|
|
129
|
+
fixed-`width` or explicit-`edges` bins, then reduce per bin — histograms),
|
|
129
130
|
`rollingByColumn()`, `byValue(axis)` (project onto a `ValueSeries`)
|
|
130
131
|
- **Windowing/smoothing**: `rolling(window, spec, opts)`, `smooth(column,
|
|
131
|
-
method)` (
|
|
132
|
+
method, opts)` (`'ema'` / `'movingAverage'` / `'loess'`), `align(method, opts)`
|
|
132
133
|
- **Differential/statistical**: `diff()`, `rate()`, `pctChange()`,
|
|
133
134
|
`cumulative()`, `scan()` (custom stateful reducer), `shift()`, `baseline()`
|
|
134
135
|
(rolling avg/sd/bands), `outliers()` (deviation from baseline)
|
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.
|
|
11
|
+
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.68.0...HEAD
|
|
12
|
+
[0.68.0]: https://github.com/pond-ts/pond/compare/v0.67.0...v0.68.0
|
|
12
13
|
[0.67.0]: https://github.com/pond-ts/pond/compare/v0.66.0...v0.67.0
|
|
13
14
|
[0.66.0]: https://github.com/pond-ts/pond/compare/v0.65.0...v0.66.0
|
|
14
15
|
[0.65.0]: https://github.com/pond-ts/pond/compare/v0.64.0...v0.65.0
|
|
@@ -70,6 +71,33 @@ include new features and type-level changes; patch bumps are strictly additive.
|
|
|
70
71
|
|
|
71
72
|
## [Unreleased]
|
|
72
73
|
|
|
74
|
+
## [0.68.0] — 2026-09-13
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **Agent adoption tranche ([PND-ADOPTMETA] / [PND-ADOPTLINKS] /
|
|
79
|
+
[PND-LLMSTXT] / [PND-AGENTGUIDE] / [PND-SKILL] / [PND-CONTEXT7]).** Every
|
|
80
|
+
package now declares `keywords`, `homepage` and `bugs` (there were none —
|
|
81
|
+
`pond-ts` ranked last in `npm search "time series"`). Every tarball ships an
|
|
82
|
+
`AGENTS.md` (source `docs/agents/USING_POND.md`): which package for which
|
|
83
|
+
task, the core idioms, the mistakes agents make. `pond-ts.org/llms.txt` is
|
|
84
|
+
now llmstxt.org-shaped (titles + descriptions per page, one section per
|
|
85
|
+
docs area, `Optional` links to `API.md` / the agent guide) with per-area
|
|
86
|
+
`llms-<area>.txt` dumps so a single fetch stays small. A Claude Code plugin
|
|
87
|
+
marketplace lives in the repo (`/plugin marketplace add pond-ts/pond`) with
|
|
88
|
+
`pond-ts`, `pond-charts` and `pond-financial` skills. `context7.json`
|
|
89
|
+
configures docs-MCP indexing. Plan and baseline:
|
|
90
|
+
`docs/plans/PND_ADOPTION_PLAN.md`.
|
|
91
|
+
|
|
92
|
+
- **Agent guide + skill hardened by the first cold-start run** (`docs/agents/USING_POND.md`, shipped as `AGENTS.md`; `plugins/pond-ts/skills/pond-ts`): install with `@latest` and the `.d.ts` paths that carry signatures. Cold-start harness committed at `docs/adoption/cold-start/`.
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
96
|
+
- **`pond-ts`: the partition column is now in the static type after a partitioned `aggregate` / `rolling` ([PND-PARTCOL]).** `series.partitionBy('host').aggregate(seq, { p95: { from: 'ms', using: 'p95' } }).collect()` always carried `host` at runtime (auto-injected as `'first'`) but the result type omitted it, so `e.get('host')` failed to compile — every fresh agent in the cold-start experiment hit or pre-empted it. `PartitionedTimeSeries` gains a third type parameter `By` (the partition column names, captured by `partitionBy`, default `never`), and the two schema-replacing operators are typed over `WithPartitionColumns<Mapping, By>` — the user's keys win, kind and all; missing partition columns are added as `'first'`. Composite partitions and typed `groups` carry through; `smooth` / `baseline` under `partitionBy` now also keep `K`. Additive: untyped views are unchanged.
|
|
97
|
+
- `@pond-ts/charts` and `@pond-ts/fit` READMEs (rendered on npm) and eight
|
|
98
|
+
docs pages pointed at the retired `pjm17971.github.io/pond-ts` site /
|
|
99
|
+
`pjm17971/pond-ts` repo; now `pond-ts.org` / `pond-ts/pond`.
|
|
100
|
+
|
|
73
101
|
## [0.67.0] — 2026-09-11
|
|
74
102
|
|
|
75
103
|
### Added
|
package/README.md
CHANGED
|
@@ -202,6 +202,26 @@ The full guide is at **<https://pond-ts.org/>**.
|
|
|
202
202
|
— TypeDoc output, every public class and method.
|
|
203
203
|
- **[CHANGELOG](./CHANGELOG.md)** — what shipped in each release.
|
|
204
204
|
|
|
205
|
+
## For coding agents
|
|
206
|
+
|
|
207
|
+
pond is built by agents and expects to be used by them. Three things exist so
|
|
208
|
+
an agent can go from "never heard of pond" to working code without a human in
|
|
209
|
+
the loop:
|
|
210
|
+
|
|
211
|
+
- **`AGENTS.md` + `API.md` ship inside every npm tarball** —
|
|
212
|
+
`node_modules/pond-ts/AGENTS.md` is a one-read guide (which package for
|
|
213
|
+
which task, the idioms, the mistakes agents make); `API.md` maps every
|
|
214
|
+
public export to its source file. Source:
|
|
215
|
+
[docs/agents/USING_POND.md](docs/agents/USING_POND.md), [API.md](API.md).
|
|
216
|
+
- **<https://pond-ts.org/llms.txt>** — every docs page with a one-line
|
|
217
|
+
description, plus `llms-<area>.txt` single-fetch dumps per package.
|
|
218
|
+
- **Claude Code plugin** — skills for core, charts and financial, versioned
|
|
219
|
+
with the library:
|
|
220
|
+
```
|
|
221
|
+
/plugin marketplace add pond-ts/pond
|
|
222
|
+
/plugin install pond-ts@pond-ts
|
|
223
|
+
```
|
|
224
|
+
|
|
205
225
|
## Examples
|
|
206
226
|
|
|
207
227
|
- **[pond-ts-dashboard](https://github.com/pjm17971/pond-ts-dashboard)**
|
|
@@ -9,6 +9,24 @@ import type { ScanStep } from './operators/scan.js';
|
|
|
9
9
|
type SequenceLike = Sequence | BoundedSequence;
|
|
10
10
|
type AlignMethod = 'hold' | 'linear';
|
|
11
11
|
type AlignSample = 'begin' | 'center' | 'end';
|
|
12
|
+
/**
|
|
13
|
+
* The mapping a partitioned `aggregate` / `rolling` actually runs with
|
|
14
|
+
* ([PND-PARTCOL]). The runtime (`augmentMappingWithPartitionCols`) appends
|
|
15
|
+
* every partition column the user's mapping does not already name as a
|
|
16
|
+
* `'first'` spec, so the collected series carries the partition key. This
|
|
17
|
+
* is the same rule at the type level: keys the user wrote win (kind and
|
|
18
|
+
* all — `host: 'count'` stays an optional number), the rest are added as
|
|
19
|
+
* `'first'` and so keep the source column's kind. `By` defaults to `never`
|
|
20
|
+
* on an untyped view, which leaves the mapping untouched; a widened
|
|
21
|
+
* `string` `By` does too (see the conditional). One knowing lie: when the
|
|
22
|
+
* partition column is a union-typed variable (`c: 'host' | 'region'`), the
|
|
23
|
+
* type names both as `string | undefined` while the runtime carries only
|
|
24
|
+
* the one actually passed — harmless because injected columns are already
|
|
25
|
+
* optional.
|
|
26
|
+
*/
|
|
27
|
+
export type WithPartitionColumns<Mapping, By extends string> = string extends By ? Mapping : Mapping & {
|
|
28
|
+
readonly [C in Exclude<By, keyof Mapping>]: 'first';
|
|
29
|
+
};
|
|
12
30
|
/**
|
|
13
31
|
* View over a `TimeSeries` that scopes stateful transforms to within
|
|
14
32
|
* each partition. Created by `TimeSeries.partitionBy(by)`.
|
|
@@ -52,7 +70,7 @@ type AlignSample = 'begin' | 'center' | 'end';
|
|
|
52
70
|
* );
|
|
53
71
|
* ```
|
|
54
72
|
*/
|
|
55
|
-
export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends string = string> {
|
|
73
|
+
export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends string = string, By extends string = never> {
|
|
56
74
|
#private;
|
|
57
75
|
readonly source: TimeSeries<S>;
|
|
58
76
|
readonly by: ReadonlyArray<keyof EventDataForSchema<S> & string>;
|
|
@@ -205,12 +223,12 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
|
|
|
205
223
|
* reservoir. Safe by construction; no `unsafeGlobal: true` token.
|
|
206
224
|
* See {@link TimeSeries.sample}.
|
|
207
225
|
*/
|
|
208
|
-
sample(strategy: BatchSampleStrategy): PartitionedTimeSeries<S, K>;
|
|
226
|
+
sample(strategy: BatchSampleStrategy): PartitionedTimeSeries<S, K, By>;
|
|
209
227
|
/** Per-partition `fill`. See {@link TimeSeries.fill}. */
|
|
210
228
|
fill(strategy: FillStrategy | FillMapping<S>, options?: {
|
|
211
229
|
limit?: number;
|
|
212
230
|
maxGap?: DurationInput;
|
|
213
|
-
}): PartitionedTimeSeries<S, K>;
|
|
231
|
+
}): PartitionedTimeSeries<S, K, By>;
|
|
214
232
|
/**
|
|
215
233
|
* Per-partition `dedupe`. The duplicate key becomes "same partition
|
|
216
234
|
* columns AND same timestamp" — `partitionBy` provides the partition
|
|
@@ -221,13 +239,13 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
|
|
|
221
239
|
*/
|
|
222
240
|
dedupe(options?: {
|
|
223
241
|
keep?: DedupeKeep<S>;
|
|
224
|
-
}): PartitionedTimeSeries<S, K>;
|
|
242
|
+
}): PartitionedTimeSeries<S, K, By>;
|
|
225
243
|
/** Per-partition `align`. See {@link TimeSeries.align}. */
|
|
226
244
|
align(sequence: SequenceLike, options?: {
|
|
227
245
|
method?: AlignMethod;
|
|
228
246
|
sample?: AlignSample;
|
|
229
247
|
range?: TemporalLike;
|
|
230
|
-
}): PartitionedTimeSeries<AlignSchema<S>, K>;
|
|
248
|
+
}): PartitionedTimeSeries<AlignSchema<S>, K, By>;
|
|
231
249
|
/**
|
|
232
250
|
* Per-partition `materialize`. See {@link TimeSeries.materialize}.
|
|
233
251
|
*
|
|
@@ -243,18 +261,18 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
|
|
|
243
261
|
sample?: AlignSample;
|
|
244
262
|
select?: 'first' | 'last' | 'nearest';
|
|
245
263
|
range?: TemporalLike;
|
|
246
|
-
}): PartitionedTimeSeries<MaterializeSchema<S>, K>;
|
|
264
|
+
}): PartitionedTimeSeries<MaterializeSchema<S>, K, By>;
|
|
247
265
|
/** Per-partition `rolling`. See {@link TimeSeries.rolling}. */
|
|
248
266
|
rolling<const Mapping extends ValidatedAggregateMap<S, Mapping>>(window: DurationInput, mapping: Mapping, options?: {
|
|
249
267
|
alignment?: RollingAlignment;
|
|
250
268
|
minSamples?: number;
|
|
251
|
-
}): PartitionedTimeSeries<RollingSchema<S, Mapping
|
|
269
|
+
}): PartitionedTimeSeries<RollingSchema<S, WithPartitionColumns<Mapping, By>>, K, By>;
|
|
252
270
|
rolling<const Mapping extends ValidatedAggregateMap<S, Mapping>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
|
|
253
271
|
alignment?: RollingAlignment;
|
|
254
272
|
sample?: AlignSample;
|
|
255
273
|
range?: TemporalLike;
|
|
256
274
|
minSamples?: number;
|
|
257
|
-
}): PartitionedTimeSeries<AggregateSchema<S, Mapping
|
|
275
|
+
}): PartitionedTimeSeries<AggregateSchema<S, WithPartitionColumns<Mapping, By>>, K, By>;
|
|
258
276
|
/** Per-partition `smooth`. See {@link TimeSeries.smooth}. */
|
|
259
277
|
smooth<const Target extends NumericColumnNameForSchema<S>, const Output extends string | undefined = undefined>(column: Target, method: SmoothMethod, options: {
|
|
260
278
|
alpha: number;
|
|
@@ -267,7 +285,7 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
|
|
|
267
285
|
} | {
|
|
268
286
|
span: number;
|
|
269
287
|
output?: Output;
|
|
270
|
-
}): PartitionedTimeSeries<Output extends string ? SmoothAppendSchema<S, Output> : SmoothSchema<S, Target
|
|
288
|
+
}): PartitionedTimeSeries<Output extends string ? SmoothAppendSchema<S, Output> : SmoothSchema<S, Target>, K, By>;
|
|
271
289
|
/** Per-partition `baseline`. See {@link TimeSeries.baseline}. */
|
|
272
290
|
baseline<const Col extends NumericColumnNameForSchema<S>, const AvgName extends string = 'avg', const SdName extends string = 'sd', const UpperName extends string = 'upper', const LowerName extends string = 'lower'>(col: Col, options: {
|
|
273
291
|
window: DurationInput;
|
|
@@ -280,41 +298,41 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
|
|
|
280
298
|
upper?: UpperName;
|
|
281
299
|
lower?: LowerName;
|
|
282
300
|
};
|
|
283
|
-
}): PartitionedTimeSeries<BaselineSchema<S, AvgName, SdName, UpperName, LowerName
|
|
301
|
+
}): PartitionedTimeSeries<BaselineSchema<S, AvgName, SdName, UpperName, LowerName>, K, By>;
|
|
284
302
|
/** Per-partition `outliers`. See {@link TimeSeries.outliers}. */
|
|
285
303
|
outliers<const Col extends NumericColumnNameForSchema<S>>(col: Col, options: {
|
|
286
304
|
window: DurationInput;
|
|
287
305
|
sigma: number;
|
|
288
306
|
alignment?: RollingAlignment;
|
|
289
307
|
minSamples?: number;
|
|
290
|
-
}): PartitionedTimeSeries<S, K>;
|
|
308
|
+
}): PartitionedTimeSeries<S, K, By>;
|
|
291
309
|
/** Per-partition `diff`. See {@link TimeSeries.diff}. */
|
|
292
310
|
diff<const Target extends NumericColumnNameForSchema<S>>(columns: Target | readonly Target[], options?: {
|
|
293
311
|
drop?: boolean;
|
|
294
|
-
}): PartitionedTimeSeries<DiffSchema<S, Target>, K>;
|
|
312
|
+
}): PartitionedTimeSeries<DiffSchema<S, Target>, K, By>;
|
|
295
313
|
/** Per-partition `rate`. See {@link TimeSeries.rate}. */
|
|
296
314
|
rate<const Target extends NumericColumnNameForSchema<S>>(columns: Target | readonly Target[], options?: {
|
|
297
315
|
drop?: boolean;
|
|
298
|
-
}): PartitionedTimeSeries<DiffSchema<S, Target>, K>;
|
|
316
|
+
}): PartitionedTimeSeries<DiffSchema<S, Target>, K, By>;
|
|
299
317
|
/** Per-partition `pctChange`. See {@link TimeSeries.pctChange}. */
|
|
300
318
|
pctChange<const Target extends NumericColumnNameForSchema<S>>(columns: Target | readonly Target[], options?: {
|
|
301
319
|
drop?: boolean;
|
|
302
|
-
}): PartitionedTimeSeries<DiffSchema<S, Target>, K>;
|
|
320
|
+
}): PartitionedTimeSeries<DiffSchema<S, Target>, K, By>;
|
|
303
321
|
/** Per-partition `cumulative`. See {@link TimeSeries.cumulative}. */
|
|
304
322
|
cumulative<const Targets extends NumericColumnNameForSchema<S>>(spec: {
|
|
305
323
|
[K in Targets]: 'sum' | 'max' | 'min' | 'count' | ((acc: number, value: number) => number);
|
|
306
|
-
}): PartitionedTimeSeries<DiffSchema<S, Targets>, K>;
|
|
324
|
+
}): PartitionedTimeSeries<DiffSchema<S, Targets>, K, By>;
|
|
307
325
|
/** Per-partition `scan`. See {@link TimeSeries.scan}. */
|
|
308
|
-
scan<const Source extends NumericColumnNameForSchema<S>, A>(source: Source, step: ScanStep<A>, init: A): PartitionedTimeSeries<DiffSchema<S, Source>, K>;
|
|
326
|
+
scan<const Source extends NumericColumnNameForSchema<S>, A>(source: Source, step: ScanStep<A>, init: A): PartitionedTimeSeries<DiffSchema<S, Source>, K, By>;
|
|
309
327
|
scan<const Source extends NumericColumnNameForSchema<S>, const Name extends string, A>(source: Source, step: ScanStep<A>, init: A, options: {
|
|
310
328
|
output: Name;
|
|
311
|
-
}): PartitionedTimeSeries<AppendColumn<S, Name, 'number'>, K>;
|
|
329
|
+
}): PartitionedTimeSeries<AppendColumn<S, Name, 'number'>, K, By>;
|
|
312
330
|
/** Per-partition `shift`. See {@link TimeSeries.shift}. */
|
|
313
|
-
shift<const Target extends NumericColumnNameForSchema<S>>(columns: Target | readonly Target[], n: number): PartitionedTimeSeries<DiffSchema<S, Target>, K>;
|
|
331
|
+
shift<const Target extends NumericColumnNameForSchema<S>>(columns: Target | readonly Target[], n: number): PartitionedTimeSeries<DiffSchema<S, Target>, K, By>;
|
|
314
332
|
/** Per-partition `aggregate`. See {@link TimeSeries.aggregate}. */
|
|
315
333
|
aggregate<const Mapping extends ValidatedAggregateMap<S, Mapping>>(sequence: SequenceLike, mapping: Mapping, options?: {
|
|
316
334
|
range?: TemporalLike;
|
|
317
|
-
}): PartitionedTimeSeries<AggregateSchema<S, Mapping
|
|
335
|
+
}): PartitionedTimeSeries<AggregateSchema<S, WithPartitionColumns<Mapping, By>>, K, By>;
|
|
318
336
|
}
|
|
319
337
|
export {};
|
|
320
338
|
//# sourceMappingURL=partitioned-time-series.d.ts.map
|
|
@@ -956,8 +956,8 @@ export declare class TimeSeries<S extends SeriesSchema> {
|
|
|
956
956
|
*/
|
|
957
957
|
partitionBy<Col extends keyof EventDataForSchema<S> & string, const Groups extends ReadonlyArray<string>>(by: Col | readonly [Col], options: {
|
|
958
958
|
groups: Groups;
|
|
959
|
-
}): PartitionedTimeSeries<S, Groups[number]>;
|
|
960
|
-
partitionBy
|
|
959
|
+
}): PartitionedTimeSeries<S, Groups[number], Col>;
|
|
960
|
+
partitionBy<const Col extends keyof EventDataForSchema<S> & string>(by: Col | ReadonlyArray<Col>): PartitionedTimeSeries<S, string, Col>;
|
|
961
961
|
/**
|
|
962
962
|
* Example: `series.pivotByGroup("host", "cpu")`.
|
|
963
963
|
* Reshapes long-form data into wide rows. Each distinct value of
|
package/package.json
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pond-ts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.68.0",
|
|
4
|
+
"description": "Typed time series for TypeScript: schema-driven TimeSeries + streaming LiveSeries with aggregate, rolling, align, fill, partitionBy and typed columns",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"time-series",
|
|
7
|
+
"timeseries",
|
|
8
|
+
"typescript",
|
|
9
|
+
"streaming",
|
|
10
|
+
"analytics",
|
|
11
|
+
"rolling-window",
|
|
12
|
+
"aggregate",
|
|
13
|
+
"resample",
|
|
14
|
+
"downsample",
|
|
15
|
+
"partition",
|
|
16
|
+
"live",
|
|
17
|
+
"realtime",
|
|
18
|
+
"metrics",
|
|
19
|
+
"telemetry",
|
|
20
|
+
"immutable",
|
|
21
|
+
"columnar",
|
|
22
|
+
"arrow",
|
|
23
|
+
"pondjs"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://pond-ts.org/docs/pond-ts/",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/pond-ts/pond/issues"
|
|
28
|
+
},
|
|
5
29
|
"license": "MIT",
|
|
6
30
|
"repository": {
|
|
7
31
|
"type": "git",
|
|
@@ -29,13 +53,14 @@
|
|
|
29
53
|
"files": [
|
|
30
54
|
"dist",
|
|
31
55
|
"CHANGELOG.md",
|
|
32
|
-
"API.md"
|
|
56
|
+
"API.md",
|
|
57
|
+
"AGENTS.md"
|
|
33
58
|
],
|
|
34
59
|
"scripts": {
|
|
35
60
|
"build": "tsc -p tsconfig.json",
|
|
36
61
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"test-d/**/*.ts\"",
|
|
37
62
|
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"test-d/**/*.ts\"",
|
|
38
|
-
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE && cp ../../CHANGELOG.md ./CHANGELOG.md && cp ../../API.md ./API.md && npm run build && cp cjs-fallback.cjs dist/cjs-fallback.cjs && find dist -name '*.map' -delete",
|
|
63
|
+
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE && cp ../../CHANGELOG.md ./CHANGELOG.md && cp ../../API.md ./API.md && cp ../../docs/agents/USING_POND.md ./AGENTS.md && npm run build && cp cjs-fallback.cjs dist/cjs-fallback.cjs && find dist -name '*.map' -delete",
|
|
39
64
|
"test": "npm run test:type && npm run test:runtime",
|
|
40
65
|
"test:runtime": "vitest run",
|
|
41
66
|
"test:type": "tsc -p tsconfig.types.json",
|