pond-ts 0.20.0 → 0.22.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 (45) hide show
  1. package/CHANGELOG.md +104 -1
  2. package/dist/batch/aggregate-columns.d.ts +29 -0
  3. package/dist/batch/aggregate-columns.d.ts.map +1 -1
  4. package/dist/batch/aggregate-columns.js +63 -0
  5. package/dist/batch/aggregate-columns.js.map +1 -1
  6. package/dist/batch/operators/collapse.d.ts +42 -0
  7. package/dist/batch/operators/collapse.d.ts.map +1 -0
  8. package/dist/batch/operators/collapse.js +83 -0
  9. package/dist/batch/operators/collapse.js.map +1 -0
  10. package/dist/batch/operators/cumulative.d.ts +40 -0
  11. package/dist/batch/operators/cumulative.d.ts.map +1 -0
  12. package/dist/batch/operators/cumulative.js +74 -0
  13. package/dist/batch/operators/cumulative.js.map +1 -0
  14. package/dist/batch/operators/diff-rate.d.ts +52 -0
  15. package/dist/batch/operators/diff-rate.d.ts.map +1 -0
  16. package/dist/batch/operators/diff-rate.js +99 -0
  17. package/dist/batch/operators/diff-rate.js.map +1 -0
  18. package/dist/batch/operators/fill.d.ts +66 -0
  19. package/dist/batch/operators/fill.d.ts.map +1 -0
  20. package/dist/batch/operators/fill.js +219 -0
  21. package/dist/batch/operators/fill.js.map +1 -0
  22. package/dist/batch/operators/map.d.ts +56 -0
  23. package/dist/batch/operators/map.d.ts.map +1 -0
  24. package/dist/batch/operators/map.js +109 -0
  25. package/dist/batch/operators/map.js.map +1 -0
  26. package/dist/batch/operators/shift.d.ts +31 -0
  27. package/dist/batch/operators/shift.d.ts.map +1 -0
  28. package/dist/batch/operators/shift.js +56 -0
  29. package/dist/batch/operators/shift.js.map +1 -0
  30. package/dist/batch/time-series.d.ts +36 -5
  31. package/dist/batch/time-series.d.ts.map +1 -1
  32. package/dist/batch/time-series.js +244 -387
  33. package/dist/batch/time-series.js.map +1 -1
  34. package/dist/columnar/index.d.ts +1 -1
  35. package/dist/columnar/index.d.ts.map +1 -1
  36. package/dist/columnar/index.js +1 -1
  37. package/dist/columnar/index.js.map +1 -1
  38. package/dist/columnar/view.d.ts +57 -1
  39. package/dist/columnar/view.d.ts.map +1 -1
  40. package/dist/columnar/view.js +88 -0
  41. package/dist/columnar/view.js.map +1 -1
  42. package/dist/reducers/stdev.d.ts.map +1 -1
  43. package/dist/reducers/stdev.js +81 -59
  44. package/dist/reducers/stdev.js.map +1 -1
  45. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,13 +7,116 @@ 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.20.0...HEAD
10
+ [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.22.0...HEAD
11
+ [0.22.0]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...v0.22.0
12
+ [0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
11
13
  [0.20.0]: https://github.com/pjm17971/pond-ts/compare/v0.19.0...v0.20.0
12
14
  [0.19.0]: https://github.com/pjm17971/pond-ts/compare/v0.18.0...v0.19.0
13
15
  [0.18.0]: https://github.com/pjm17971/pond-ts/compare/v0.17.1...v0.18.0
14
16
 
15
17
  ## [Unreleased]
16
18
 
19
+ ## [0.22.0] — 2026-06-12
20
+
21
+ ### Changed
22
+
23
+ - **`asTime` / `asTimeRange` / `asInterval` are now column-native.** They
24
+ reinterpret the key's kind (a "rekey") straight off the existing key's
25
+ `begin` / `end` buffers instead of materializing events — value columns pass
26
+ through by reference. `asTimeRange` and `asTime` with `begin` / `end` reuse
27
+ the key buffer zero-copy (≈ **9×** faster on a build → rekey → read pipeline);
28
+ `asTime({ at: 'center' })` adds one midpoint pass; `asInterval` builds the
29
+ label column (string → `StringColumn`, number → `Float64Column`, inferred
30
+ from the first label and required consistent across rows). `asTime` with
31
+ `center` / `end` throws if anchoring a source with overlapping extents would
32
+ produce a non-monotonic time axis (preserving the prior validation — `begin`
33
+ is always sorted and is exempt).
34
+ - **Breaking: `asInterval`'s label function now receives the interval's
35
+ `TimeRange` (its `[begin, end]` extent) and index — not the whole `Event`.**
36
+ The canonical form is unchanged: `series.asInterval(range => range.begin())`
37
+ works exactly as before (both `Event` and `TimeRange` expose `begin()` /
38
+ `end()`). Only a label fn that read a _value column_ off the event (e.g.
39
+ `event => event.get('label')`) needs rewriting — compute the label before
40
+ `asInterval`, or derive it from the extent. The constant form
41
+ (`asInterval('bucket')` / `asInterval(42)`) is unaffected. (Pre-1.0 minor;
42
+ this is the change that lets the function form stay on the columnar path.)
43
+
44
+ ### Fixed
45
+
46
+ - **`mapColumns` rejects a non-finite numeric result at write.** A mapper on a
47
+ `number` column that returns `NaN` or `±Infinity` now throws a `RangeError`,
48
+ consistent with construction intake (which already rejects non-finite
49
+ numbers). Previously the value was packed into the column, where the reduce
50
+ fast path and the row path could disagree on the same bucket (e.g.
51
+ `aggregate('min')` returning a different result depending on which path ran).
52
+ A stored `NaN` is still a defined value the mapper sees — map it to a finite
53
+ number, or to `undefined` (missing), to clean it. (Closes a hole introduced
54
+ alongside `mapColumns` in 0.21.0.)
55
+ - **`aggregate('stdev')` is now numerically stable and path-independent.** The
56
+ bucketed row path (`bucketState`) used a one-pass `sq/n − mean²` accumulator
57
+ that cancels catastrophically on near-equal large-magnitude values —
58
+ returning `0` (e.g. `[1e10, 1e10+1, 1e10+2, 1e10+3]` → `0` instead of
59
+ `≈1.118`), or a negative variance whose `sqrt` is `NaN` that the validating
60
+ constructor then rejected with a throw. Because the columnar fast path is
61
+ all-or-nothing, an unrelated mapping (e.g. a `count` over a string column)
62
+ could silently flip the _same_ series' stdev. All three batch paths (`reduce`,
63
+ `reduceColumn`, `bucketState`) now share **one Welford recurrence** — O(1) per
64
+ element, no buffer (so the live aggregation path that shares `bucketState`
65
+ stays O(1)), `m2 ≥ 0` by construction — so they agree regardless of magnitude.
66
+ (Even the prior two-pass `Σv/n`-then-deviations drifted ~8.7% from the true
67
+ value at `2^52`, where the summed mean rounds — so unifying on Welford, not
68
+ two-pass, was necessary.) **Correction:** 0.21.0's columnar `aggregate()` fast
69
+ path (#186) was described as "signature + semantics unchanged", but it did
70
+ change released `stdev` output for fast-path-qualifying aggregates; this fix
71
+ makes every path agree. (`rolling`/`smooth` stdev keep the one-pass form for
72
+ now — a separate, deferred item.)
73
+
74
+ ## [0.21.0] — 2026-06-11
75
+
76
+ ### Added
77
+
78
+ - **`TimeSeries.mapColumns({ col: (value) => newValue })`** — a per-cell column
79
+ value transform. The column-scoped counterpart of the event-based `map()`:
80
+ where `map(schema, event => newEvent)` rebuilds whole rows through an
81
+ arbitrary closure (and can change the schema/key), `mapColumns` transforms
82
+ individual columns' values in place, reading the columns directly (no
83
+ per-row `Event`) so it stays on the fast columnar path. Same kind in/out
84
+ (number→number, string→string, …), so the schema is unchanged; missing cells
85
+ carry (the mapper isn't called on `undefined`). ~5–6× faster than the
86
+ `map()` workaround on a build → transform → read pipeline.
87
+
88
+ ### Changed
89
+
90
+ - **`select` / `rename` / `slice` / `cumulative` / `diff` / `rate` /
91
+ `pctChange` / `fill` / `shift` / `collapse` are now column-native.** They
92
+ reshape the columnar store directly instead of materializing events, so the
93
+ columnar construction win is preserved through these transforms — build →
94
+ transform → read pipelines run several× faster (~7–10× for `select` /
95
+ `rename` / `slice`; ~5–7× for the `cumulative` / `diff` / `rate` / `fill` /
96
+ `shift` / `collapse` folds). No API change for type-correct callers (one
97
+ narrow `fill` behavior change is noted under Fixed). `cumulative` / `diff` /
98
+ `rate` / `pctChange` / `fill` / `shift` / `collapse` are also the first
99
+ operators extracted into `batch/operators/` (internal refactor); `fill`
100
+ rebuilds only the columns it actually changes; `slice` normalizes
101
+ `Array.prototype.slice` semantics onto a zero-copy `withRowRange` reshape;
102
+ `collapse` reads only the keyed columns and passes the kept columns through
103
+ by reference.
104
+
105
+ ### Fixed
106
+
107
+ - **`rename` now rejects target-name collisions** (e.g. renaming `a` → `b`
108
+ when `b` already exists) with a clear error, instead of silently producing a
109
+ duplicate-named schema. Also fixes a prototype-chain bug where a column named
110
+ `toString` (or another `Object.prototype` member) could be corrupted during
111
+ a rename.
112
+ - **`fill` now throws on a kind-mismatched literal** (e.g.
113
+ `fill({ value: 'banana' })` where `value` is numeric — type-allowed because
114
+ mapping values are the broad `FillStrategy | ScalarValue`) with a clear
115
+ `RangeError` naming the column, instead of silently producing an
116
+ internally-inconsistent series (the old events path returned the literal
117
+ from `.get()` while the numeric column read `NaN`). The throw is
118
+ gap-dependent — it only fires when the literal would actually be placed.
119
+
17
120
  ## [0.20.0] — 2026-06-04
18
121
 
19
122
  Two internal performance improvements driven by the dashboard experiment at
@@ -1,3 +1,4 @@
1
+ import type { Column } from '../columnar/index.js';
1
2
  import type { AggregateMap, AggregateOutputMap, AggregateReducer, ScalarKind, SeriesSchema } from '../schema/index.js';
2
3
  /**
3
4
  * Normalised column spec used by both batch and live aggregation paths.
@@ -33,4 +34,32 @@ export type AggregateColumnSpec = {
33
34
  * same `mapping` shape produces the same schema.
34
35
  */
35
36
  export declare function normalizeAggregateColumns<S extends SeriesSchema>(schema: S, mapping: AggregateMap<S> | AggregateOutputMap<S>): AggregateColumnSpec[];
37
+ /**
38
+ * **Phase 4.7 step 3B — columnar fast path for time-keyed `aggregate()`.**
39
+ * On sorted time-keyed data each bucket is a contiguous index range, so
40
+ * when every mapped column is a built-in numeric reducer with a
41
+ * `reduceColumn` fast path over a **packed `Float64Column`** source, each
42
+ * bucket reduces straight off the typed-array slice — skipping the
43
+ * `series.events` materialization and the per-cell `state.add` walk the
44
+ * row path pays. Reuses the shipped step-3A `reduceColumn` kernels
45
+ * (sum/min/max/avg 59–73×, stdev 35×, median/p95 3.4×) per bucket.
46
+ *
47
+ * Returns `null` — caller takes the unchanged row path — when any column
48
+ * doesn't qualify: a custom-function reducer, a reducer with no
49
+ * `reduceColumn` (`first` / `last` / `unique` / `top` / `samples`), or a
50
+ * non-numeric / chunked / missing source column. All-or-nothing per call
51
+ * keeps the bucket walk single-pass; mixed mappings fall back wholesale.
52
+ *
53
+ * `begins` is the key column's begin axis (sorted, identical row order to
54
+ * the value columns — both read straight off the store). Bucketing
55
+ * replicates the row path exactly: `cursor` carries across buckets, and a
56
+ * bucket owns `[start, scan)` where `begins[i] ∈ [bucket.begin,
57
+ * bucket.end)`. Empty buckets reduce an empty slice — the reducer's
58
+ * empty-input result (the step-3A parity contract guarantees this matches
59
+ * a zero-`add` bucket snapshot).
60
+ */
61
+ export declare function tryAggregateColumnarTimeKeyed<B extends {
62
+ begin(): number;
63
+ end(): number;
64
+ }>(begins: Float64Array, getColumn: (name: string) => Column | undefined, buckets: ReadonlyArray<B>, columns: ReadonlyArray<AggregateColumnSpec>): Array<ReadonlyArray<unknown>> | null;
36
65
  //# sourceMappingURL=aggregate-columns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-columns.d.ts","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAElB,gBAAgB,EAChB,UAAU,EACV,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAkBF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,YAAY,EAC9D,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAC/C,mBAAmB,EAAE,CAuDvB"}
1
+ {"version":3,"file":"aggregate-columns.d.ts","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAElB,gBAAgB,EAEhB,UAAU,EACV,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAkBF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,YAAY,EAC9D,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAC/C,mBAAmB,EAAE,CAuDvB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,CAC3C,CAAC,SAAS;IAAE,KAAK,IAAI,MAAM,CAAC;IAAC,GAAG,IAAI,MAAM,CAAA;CAAE,EAE5C,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAC/C,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAC1C,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAyCtC"}
@@ -73,4 +73,67 @@ export function normalizeAggregateColumns(schema, mapping) {
73
73
  }
74
74
  return normalized;
75
75
  }
76
+ /**
77
+ * **Phase 4.7 step 3B — columnar fast path for time-keyed `aggregate()`.**
78
+ * On sorted time-keyed data each bucket is a contiguous index range, so
79
+ * when every mapped column is a built-in numeric reducer with a
80
+ * `reduceColumn` fast path over a **packed `Float64Column`** source, each
81
+ * bucket reduces straight off the typed-array slice — skipping the
82
+ * `series.events` materialization and the per-cell `state.add` walk the
83
+ * row path pays. Reuses the shipped step-3A `reduceColumn` kernels
84
+ * (sum/min/max/avg 59–73×, stdev 35×, median/p95 3.4×) per bucket.
85
+ *
86
+ * Returns `null` — caller takes the unchanged row path — when any column
87
+ * doesn't qualify: a custom-function reducer, a reducer with no
88
+ * `reduceColumn` (`first` / `last` / `unique` / `top` / `samples`), or a
89
+ * non-numeric / chunked / missing source column. All-or-nothing per call
90
+ * keeps the bucket walk single-pass; mixed mappings fall back wholesale.
91
+ *
92
+ * `begins` is the key column's begin axis (sorted, identical row order to
93
+ * the value columns — both read straight off the store). Bucketing
94
+ * replicates the row path exactly: `cursor` carries across buckets, and a
95
+ * bucket owns `[start, scan)` where `begins[i] ∈ [bucket.begin,
96
+ * bucket.end)`. Empty buckets reduce an empty slice — the reducer's
97
+ * empty-input result (the step-3A parity contract guarantees this matches
98
+ * a zero-`add` bucket snapshot).
99
+ */
100
+ export function tryAggregateColumnarTimeKeyed(begins, getColumn, buckets, columns) {
101
+ const plans = [];
102
+ for (const spec of columns) {
103
+ if (typeof spec.reducer !== 'string')
104
+ return null; // custom function
105
+ const def = resolveReducer(spec.reducer);
106
+ if (def.reduceColumn === undefined)
107
+ return null; // first/last/unique/...
108
+ const source = getColumn(spec.source);
109
+ if (source === undefined ||
110
+ source.kind !== 'number' ||
111
+ source.storage !== 'packed') {
112
+ return null; // non-numeric / chunked / missing source
113
+ }
114
+ plans.push({ column: source, reduce: def.reduceColumn });
115
+ }
116
+ const n = begins.length;
117
+ let cursor = 0;
118
+ const rows = new Array(buckets.length);
119
+ for (let b = 0; b < buckets.length; b += 1) {
120
+ const bucket = buckets[b];
121
+ const bucketBegin = bucket.begin();
122
+ const bucketEnd = bucket.end();
123
+ while (cursor < n && begins[cursor] < bucketBegin)
124
+ cursor += 1;
125
+ const start = cursor;
126
+ let scan = start;
127
+ while (scan < n && begins[scan] < bucketEnd)
128
+ scan += 1;
129
+ cursor = scan;
130
+ const reduced = new Array(plans.length);
131
+ for (let p = 0; p < plans.length; p += 1) {
132
+ const plan = plans[p];
133
+ reduced[p] = plan.reduce(plan.column.sliceByRange(start, scan));
134
+ }
135
+ rows[b] = Object.freeze([bucket, ...reduced]);
136
+ }
137
+ return rows;
138
+ }
76
139
  //# sourceMappingURL=aggregate-columns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-columns.js","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA6BtD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAc;IAEd,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAO,IAAI,KAAK,CACjB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAS,EACT,OAAgD;IAEhD,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAU,CAAC,CAChE,CAAC;IACF,MAAM,UAAU,GAA0B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,qBAAqB,CAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;QACzE,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,SAAS,CACjB,uDAAuD,UAAU,GAAG,CACrE,CAAC;QACJ,CAAC;QACD,IACE,YAAY,CAAC,IAAI,KAAK,QAAQ;YAC9B,YAAY,CAAC,IAAI,KAAK,QAAQ;YAC9B,YAAY,CAAC,IAAI,KAAK,SAAS;YAC/B,YAAY,CAAC,IAAI,KAAK,OAAO,EAC7B,CAAC;YACD,MAAM,IAAI,SAAS,CACjB,4BAA4B,UAAU,0BAA0B,CACjE,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,qBAAqB,CAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;QAChE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YACjE,MAAM,IAAI,SAAS,CACjB,0BAA0B,UAAU,uCAAuC,CAC5E,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,qBAAqB,CAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1E,IAAI,YAAwB,CAAC;QAC7B,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,YAAY,GAAG,YAAY,CAAC;QAC9B,CAAC;aAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;YACxC,IAAI,OAAO,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACpC,YAAY,GAAG,QAAQ,CAAC;YAC1B,CAAC;iBAAM,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;gBAC1C,YAAY,GAAG,OAAO,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC;YACnC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC;QACnC,CAAC;QACD,UAAU,CAAC,IAAI,CAAC;YACd,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,UAAU;YAClB,OAAO;YACP,IAAI,EAAE,YAAY;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"aggregate-columns.js","sourceRoot":"","sources":["../../src/batch/aggregate-columns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { type ColumnarStore } from '../../columnar/index.js';
2
+ import type { ScalarValue, SeriesSchema } from '../../schema/index.js';
3
+ /** The reducer's row input — the keyed columns' values for one row. */
4
+ export type CollapseReducer = (values: Record<string, unknown>) => ScalarValue;
5
+ /**
6
+ * **Step 4 — column-native `collapse` (extracted operator).** Reduces
7
+ * the `keys` columns into a single `output` column, one value per row,
8
+ * reading **only the keyed columns** off the store (storage-agnostic
9
+ * `read(i)`) — no `series.events` materialization, no per-row `Event`.
10
+ * The kept columns + the key axis pass through by reference.
11
+ *
12
+ * Unlike the pure folds (`cumulative`, `diff`, `shift`), `collapse`
13
+ * still calls the user reducer per row over a small `{ key: value }`
14
+ * object, so the win is the more modest "read only the keyed columns +
15
+ * drop the Event allocation + share the kept columns" rather than a
16
+ * full vectorization — but the materialization tax is still removed.
17
+ *
18
+ * Semantics (matching the row path):
19
+ * - the reducer receives the keyed columns' values for the row
20
+ * (`undefined` for a missing cell), and returns a `ScalarValue`;
21
+ * - the output column's kind is inferred from the **first row's**
22
+ * result (`number` / `boolean` / else `string`) — an empty series
23
+ * yields a `string` output column, matching the old `nextEvents[0]?`
24
+ * inference;
25
+ * - **`append: false`** (default) drops the keyed columns and appends
26
+ * `output`; **`append: true`** keeps every value column and appends
27
+ * `output`.
28
+ *
29
+ * Returns the reshaped store + the output schema; the result-schema
30
+ * cast is the single trust boundary, and the `TimeSeries.collapse`
31
+ * method wraps the store via `#fromTrustedStore`. An `output` name
32
+ * that collides with a kept column throws via `withColumnAppended`.
33
+ *
34
+ * NB: `buildScalarColumn` overlaps `fillOp`'s `buildFilledColumn` and
35
+ * `mapOp`'s `columnFromValuesByKind` — three callers now; a shared
36
+ * `columnFromValuesByKind` helper is the flagged follow-up.
37
+ */
38
+ export declare function collapseOp<S extends SeriesSchema, OutSchema extends SeriesSchema>(store: ColumnarStore<S>, schema: S, keys: readonly string[], output: string, reducer: CollapseReducer, append: boolean): {
39
+ store: ColumnarStore<OutSchema>;
40
+ schema: OutSchema;
41
+ };
42
+ //# sourceMappingURL=collapse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapse.d.ts","sourceRoot":"","sources":["../../../src/batch/operators/collapse.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAOnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEvE,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC;AAiB/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,CACxB,CAAC,SAAS,YAAY,EACtB,SAAS,SAAS,YAAY,EAE9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,OAAO,GACd;IAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CA8CxD"}
@@ -0,0 +1,83 @@
1
+ import { booleanColumnFromArray, float64ColumnFromArray, stringColumnFromArray, withColumnAppended, withColumnsSelected, } from '../../columnar/index.js';
2
+ /** Builds the single output column from the reducer results + inferred kind. */
3
+ function buildScalarColumn(kind, values) {
4
+ switch (kind) {
5
+ case 'number':
6
+ return float64ColumnFromArray(values);
7
+ case 'boolean':
8
+ return booleanColumnFromArray(values);
9
+ case 'string':
10
+ return stringColumnFromArray(values);
11
+ }
12
+ }
13
+ /**
14
+ * **Step 4 — column-native `collapse` (extracted operator).** Reduces
15
+ * the `keys` columns into a single `output` column, one value per row,
16
+ * reading **only the keyed columns** off the store (storage-agnostic
17
+ * `read(i)`) — no `series.events` materialization, no per-row `Event`.
18
+ * The kept columns + the key axis pass through by reference.
19
+ *
20
+ * Unlike the pure folds (`cumulative`, `diff`, `shift`), `collapse`
21
+ * still calls the user reducer per row over a small `{ key: value }`
22
+ * object, so the win is the more modest "read only the keyed columns +
23
+ * drop the Event allocation + share the kept columns" rather than a
24
+ * full vectorization — but the materialization tax is still removed.
25
+ *
26
+ * Semantics (matching the row path):
27
+ * - the reducer receives the keyed columns' values for the row
28
+ * (`undefined` for a missing cell), and returns a `ScalarValue`;
29
+ * - the output column's kind is inferred from the **first row's**
30
+ * result (`number` / `boolean` / else `string`) — an empty series
31
+ * yields a `string` output column, matching the old `nextEvents[0]?`
32
+ * inference;
33
+ * - **`append: false`** (default) drops the keyed columns and appends
34
+ * `output`; **`append: true`** keeps every value column and appends
35
+ * `output`.
36
+ *
37
+ * Returns the reshaped store + the output schema; the result-schema
38
+ * cast is the single trust boundary, and the `TimeSeries.collapse`
39
+ * method wraps the store via `#fromTrustedStore`. An `output` name
40
+ * that collides with a kept column throws via `withColumnAppended`.
41
+ *
42
+ * NB: `buildScalarColumn` overlaps `fillOp`'s `buildFilledColumn` and
43
+ * `mapOp`'s `columnFromValuesByKind` — three callers now; a shared
44
+ * `columnFromValuesByKind` helper is the flagged follow-up.
45
+ */
46
+ export function collapseOp(store, schema, keys, output, reducer, append) {
47
+ const n = store.length;
48
+ const keyedCols = keys.map((k) => store.columns.get(k));
49
+ const out = new Array(n);
50
+ for (let i = 0; i < n; i += 1) {
51
+ const values = {};
52
+ for (let j = 0; j < keys.length; j += 1) {
53
+ values[keys[j]] = keyedCols[j].read(i);
54
+ }
55
+ out[i] = reducer(values);
56
+ }
57
+ // Output kind from the first row's result (matches the old
58
+ // `typeof nextEvents[0]?.get(output)` inference; empty ⇒ 'string').
59
+ const first = n > 0 ? out[0] : undefined;
60
+ const outKind = typeof first === 'number'
61
+ ? 'number'
62
+ : typeof first === 'boolean'
63
+ ? 'boolean'
64
+ : 'string';
65
+ const outColumn = buildScalarColumn(outKind, out);
66
+ const keptDefs = append
67
+ ? schema.slice(1)
68
+ : schema.slice(1).filter((c) => !keys.includes(c.name));
69
+ const keptNames = keptDefs.map((c) => c.name);
70
+ // key + kept value columns (zero-copy), then append the output column.
71
+ const selected = withColumnsSelected(store, keptNames);
72
+ const result = withColumnAppended(selected, output, outColumn);
73
+ const outSchema = Object.freeze([
74
+ schema[0],
75
+ ...keptDefs,
76
+ { name: output, kind: outKind },
77
+ ]);
78
+ return {
79
+ store: result,
80
+ schema: outSchema,
81
+ };
82
+ }
83
+ //# sourceMappingURL=collapse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapse.js","sourceRoot":"","sources":["../../../src/batch/operators/collapse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type ColumnarStore } from '../../columnar/index.js';
2
+ import type { SeriesSchema } from '../../schema/index.js';
3
+ /**
4
+ * A cumulative accumulator — a built-in name or a custom fold
5
+ * `(acc, value) => next`. Mirrors `TimeSeries.cumulative`'s spec values.
6
+ */
7
+ export type CumulativeReducer = 'sum' | 'max' | 'min' | 'count' | ((acc: number, value: number) => number);
8
+ /**
9
+ * **Step 4 — column-native `cumulative` (extracted operator).** Running
10
+ * accumulation per target column, computed straight off the columnar store:
11
+ * read each target's cells (storage-agnostic `read(i)`), fold a running
12
+ * accumulator, replace the column — no `series.events` materialization, no
13
+ * per-row `Event`. Non-target columns + the key axis pass through untouched
14
+ * (`withColumnReplaced` references the unchanged columns + keys zero-copy).
15
+ *
16
+ * Matches the row path's semantics exactly: a defined numeric cell updates
17
+ * the accumulator; a missing / undefined cell **carries** the current
18
+ * accumulator (does not reset it), and the output is `undefined` only until
19
+ * the first defined value (`float64ColumnFromArray` derives validity from the
20
+ * `undefined`s). A stored NaN is a defined number — applied, matching the old
21
+ * `typeof raw === 'number'` check.
22
+ *
23
+ * Returns the reshaped store + the output schema (targets widened to optional
24
+ * `number`). The result-schema cast is the single trust boundary; the
25
+ * `TimeSeries.cumulative` method wraps the store via `#fromTrustedStore`.
26
+ *
27
+ * A non-numeric target name is unreachable through the typed surface
28
+ * (`cumulative<Targets extends NumericColumnNameForSchema<S>>`); it can only
29
+ * arrive by defeating that constraint (`(series as any).cumulative(...)`).
30
+ * On such a type-illegal input the all-`undefined` replacement column is
31
+ * `kind: 'number'`, so `withColumnReplaced`'s kind guard throws a `RangeError`
32
+ * naming the column. This is a deliberate improvement over the old events path,
33
+ * which silently overwrote the column with all-`undefined` — fail-fast beats
34
+ * silent corruption on input the type already forbids.
35
+ */
36
+ export declare function cumulativeOp<S extends SeriesSchema, OutSchema extends SeriesSchema>(store: ColumnarStore<S>, schema: S, spec: Readonly<Record<string, CumulativeReducer>>): {
37
+ store: ColumnarStore<OutSchema>;
38
+ schema: OutSchema;
39
+ };
40
+ //# sourceMappingURL=cumulative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cumulative.d.ts","sourceRoot":"","sources":["../../../src/batch/operators/cumulative.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAInB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GACzB,KAAK,GACL,KAAK,GACL,KAAK,GACL,OAAO,GACP,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAoB7C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,YAAY,EACtB,SAAS,SAAS,YAAY,EAE9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,GAChD;IAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAgCxD"}
@@ -0,0 +1,74 @@
1
+ import { float64ColumnFromArray, withColumnReplaced, } from '../../columnar/index.js';
2
+ function buildApply(reducer) {
3
+ if (typeof reducer === 'function') {
4
+ return (acc, v) => (acc === undefined ? v : reducer(acc, v));
5
+ }
6
+ switch (reducer) {
7
+ case 'sum':
8
+ return (acc, v) => (acc ?? 0) + v;
9
+ case 'count':
10
+ return (acc) => (acc ?? 0) + 1;
11
+ case 'max':
12
+ return (acc, v) => (acc === undefined || v > acc ? v : acc);
13
+ case 'min':
14
+ return (acc, v) => (acc === undefined || v < acc ? v : acc);
15
+ }
16
+ }
17
+ /**
18
+ * **Step 4 — column-native `cumulative` (extracted operator).** Running
19
+ * accumulation per target column, computed straight off the columnar store:
20
+ * read each target's cells (storage-agnostic `read(i)`), fold a running
21
+ * accumulator, replace the column — no `series.events` materialization, no
22
+ * per-row `Event`. Non-target columns + the key axis pass through untouched
23
+ * (`withColumnReplaced` references the unchanged columns + keys zero-copy).
24
+ *
25
+ * Matches the row path's semantics exactly: a defined numeric cell updates
26
+ * the accumulator; a missing / undefined cell **carries** the current
27
+ * accumulator (does not reset it), and the output is `undefined` only until
28
+ * the first defined value (`float64ColumnFromArray` derives validity from the
29
+ * `undefined`s). A stored NaN is a defined number — applied, matching the old
30
+ * `typeof raw === 'number'` check.
31
+ *
32
+ * Returns the reshaped store + the output schema (targets widened to optional
33
+ * `number`). The result-schema cast is the single trust boundary; the
34
+ * `TimeSeries.cumulative` method wraps the store via `#fromTrustedStore`.
35
+ *
36
+ * A non-numeric target name is unreachable through the typed surface
37
+ * (`cumulative<Targets extends NumericColumnNameForSchema<S>>`); it can only
38
+ * arrive by defeating that constraint (`(series as any).cumulative(...)`).
39
+ * On such a type-illegal input the all-`undefined` replacement column is
40
+ * `kind: 'number'`, so `withColumnReplaced`'s kind guard throws a `RangeError`
41
+ * naming the column. This is a deliberate improvement over the old events path,
42
+ * which silently overwrote the column with all-`undefined` — fail-fast beats
43
+ * silent corruption on input the type already forbids.
44
+ */
45
+ export function cumulativeOp(store, schema, spec) {
46
+ const entries = Object.entries(spec);
47
+ if (entries.length === 0) {
48
+ throw new Error('cumulative() requires at least one column');
49
+ }
50
+ const targetSet = new Set(entries.map(([name]) => name));
51
+ const outSchema = Object.freeze(schema.map((col, i) => i === 0 || !targetSet.has(col.name)
52
+ ? col
53
+ : { ...col, kind: 'number', required: false }));
54
+ const n = store.length;
55
+ let result = store;
56
+ for (const [name, reducer] of entries) {
57
+ const col = store.columns.get(name);
58
+ const apply = buildApply(reducer);
59
+ const out = new Array(n);
60
+ let acc;
61
+ for (let i = 0; i < n; i += 1) {
62
+ const raw = col.read(i);
63
+ if (typeof raw === 'number')
64
+ acc = apply(acc, raw);
65
+ out[i] = acc;
66
+ }
67
+ result = withColumnReplaced(result, name, float64ColumnFromArray(out));
68
+ }
69
+ return {
70
+ store: result,
71
+ schema: outSchema,
72
+ };
73
+ }
74
+ //# sourceMappingURL=cumulative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cumulative.js","sourceRoot":"","sources":["../../../src/batch/operators/cumulative.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { type ColumnarStore } from '../../columnar/index.js';
2
+ import type { SeriesSchema } from '../../schema/index.js';
3
+ /**
4
+ * Successive-difference family selector. `diff` is the raw delta
5
+ * `curr - prev`; `rate` divides by the elapsed seconds between the
6
+ * two rows' begins; `pctChange` divides by the previous value.
7
+ */
8
+ export type DiffRateMode = 'diff' | 'rate' | 'pctChange';
9
+ /**
10
+ * **Step 4 — column-native `diff` / `rate` / `pctChange` (extracted
11
+ * operator).** Successive differences per target column, computed
12
+ * straight off the columnar store: read each target's cells
13
+ * (storage-agnostic `read(i)`), fold the per-row difference, replace
14
+ * the column — no `series.events` materialization, no per-row
15
+ * `Event`. Non-target columns + the key axis pass through untouched
16
+ * (`withColumnReplaced` references the unchanged columns + keys
17
+ * zero-copy).
18
+ *
19
+ * Matches the row path's semantics exactly:
20
+ * - Row 0 has no predecessor, so each target is `undefined` there.
21
+ * - Row `i ≥ 1`: with both `prev` and `curr` defined numbers,
22
+ * `delta = curr - prev`; `diff` emits `delta`, `rate` emits
23
+ * `delta / dt` (`dt` = `(begin[i] − begin[i−1]) / 1000` seconds,
24
+ * `undefined` when `dt === 0`), `pctChange` emits `delta / prev`
25
+ * (`undefined` when `prev === 0`). If either side is missing /
26
+ * non-numeric, the output is `undefined`. A stored `NaN` is a
27
+ * defined number (`typeof raw === 'number'`), so it participates.
28
+ *
29
+ * **`drop`.** `drop: false` (default) keeps the predecessor-less
30
+ * first row (its targets `undefined`, its other columns + key
31
+ * intact). `drop: true` removes that row entirely — every column
32
+ * **and** the key slice to `[1, n)` via `withRowRange`, the row-range
33
+ * substrate built for exactly this. The full-length result is built
34
+ * first (the `drop: false` shape); `drop: true` just appends the
35
+ * trailing one-row slice, so the two paths share all the fold logic.
36
+ *
37
+ * Returns the reshaped store + the output schema (targets widened to
38
+ * optional `number`). The result-schema cast is the single trust
39
+ * boundary; the `TimeSeries` method wraps the store via
40
+ * `#fromTrustedStore`.
41
+ *
42
+ * A non-numeric target name is unreachable through the typed surface
43
+ * (`NumericColumnNameForSchema<S>`); defeating that constraint makes
44
+ * the all-`undefined` replacement column `kind: 'number'`, so
45
+ * `withColumnReplaced`'s kind guard throws a `RangeError` naming the
46
+ * column — fail-fast over the old path's silent all-`undefined`.
47
+ */
48
+ export declare function diffRateOp<S extends SeriesSchema, OutSchema extends SeriesSchema>(store: ColumnarStore<S>, schema: S, mode: DiffRateMode, cols: readonly string[], drop: boolean): {
49
+ store: ColumnarStore<OutSchema>;
50
+ schema: OutSchema;
51
+ };
52
+ //# sourceMappingURL=diff-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-rate.d.ts","sourceRoot":"","sources":["../../../src/batch/operators/diff-rate.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAKnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,UAAU,CACxB,CAAC,SAAS,YAAY,EACtB,SAAS,SAAS,YAAY,EAE9B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,OAAO,GACZ;IAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CA8DxD"}