pond-ts 0.22.0 → 0.24.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 (198) hide show
  1. package/CHANGELOG.md +125 -1
  2. package/README.md +18 -14
  3. package/dist/batch/aggregate-columns.d.ts +0 -24
  4. package/dist/batch/aggregate-columns.js +47 -32
  5. package/dist/batch/partitioned-time-series.d.ts +4 -20
  6. package/dist/batch/partitioned-time-series.js +25 -67
  7. package/dist/batch/time-series.d.ts +11 -20
  8. package/dist/batch/time-series.js +154 -15
  9. package/dist/batch/validate.js +2 -2
  10. package/dist/cjs-fallback.cjs +15 -0
  11. package/dist/index.d.ts +1 -1
  12. package/dist/live/live-aggregation.d.ts +2 -2
  13. package/dist/live/live-partitioned-series.d.ts +8 -31
  14. package/dist/live/live-rolling-aggregation.d.ts +2 -2
  15. package/dist/live/live-series.d.ts +5 -10
  16. package/dist/live/live-series.js +2 -1
  17. package/dist/live/live-view.d.ts +5 -10
  18. package/dist/live/live-view.js +1 -0
  19. package/dist/reducers/first.js +1 -0
  20. package/dist/reducers/last.js +1 -0
  21. package/dist/reducers/types.d.ts +16 -0
  22. package/dist/schema/aggregate.d.ts +162 -64
  23. package/dist/schema/events.d.ts +10 -0
  24. package/dist/schema/index.d.ts +1 -2
  25. package/dist/schema/index.js +8 -0
  26. package/dist/schema/reduce.d.ts +34 -4
  27. package/dist/schema/rolling.d.ts +1 -1
  28. package/package.json +4 -3
  29. package/dist/batch/aggregate-columns.d.ts.map +0 -1
  30. package/dist/batch/aggregate-columns.js.map +0 -1
  31. package/dist/batch/index.d.ts.map +0 -1
  32. package/dist/batch/index.js.map +0 -1
  33. package/dist/batch/json.d.ts.map +0 -1
  34. package/dist/batch/json.js.map +0 -1
  35. package/dist/batch/operators/collapse.d.ts.map +0 -1
  36. package/dist/batch/operators/collapse.js.map +0 -1
  37. package/dist/batch/operators/cumulative.d.ts.map +0 -1
  38. package/dist/batch/operators/cumulative.js.map +0 -1
  39. package/dist/batch/operators/diff-rate.d.ts.map +0 -1
  40. package/dist/batch/operators/diff-rate.js.map +0 -1
  41. package/dist/batch/operators/fill.d.ts.map +0 -1
  42. package/dist/batch/operators/fill.js.map +0 -1
  43. package/dist/batch/operators/map.d.ts.map +0 -1
  44. package/dist/batch/operators/map.js.map +0 -1
  45. package/dist/batch/operators/shift.d.ts.map +0 -1
  46. package/dist/batch/operators/shift.js.map +0 -1
  47. package/dist/batch/partitioned-time-series.d.ts.map +0 -1
  48. package/dist/batch/partitioned-time-series.js.map +0 -1
  49. package/dist/batch/time-series.d.ts.map +0 -1
  50. package/dist/batch/time-series.js.map +0 -1
  51. package/dist/batch/validate.d.ts.map +0 -1
  52. package/dist/batch/validate.js.map +0 -1
  53. package/dist/column.d.ts.map +0 -1
  54. package/dist/column.js.map +0 -1
  55. package/dist/columnar/array-column.d.ts.map +0 -1
  56. package/dist/columnar/array-column.js.map +0 -1
  57. package/dist/columnar/builder.d.ts.map +0 -1
  58. package/dist/columnar/builder.js.map +0 -1
  59. package/dist/columnar/chunked-column.d.ts.map +0 -1
  60. package/dist/columnar/chunked-column.js.map +0 -1
  61. package/dist/columnar/column.d.ts.map +0 -1
  62. package/dist/columnar/column.js.map +0 -1
  63. package/dist/columnar/concat.d.ts.map +0 -1
  64. package/dist/columnar/concat.js.map +0 -1
  65. package/dist/columnar/index.d.ts.map +0 -1
  66. package/dist/columnar/index.js.map +0 -1
  67. package/dist/columnar/key-column.d.ts.map +0 -1
  68. package/dist/columnar/key-column.js.map +0 -1
  69. package/dist/columnar/ring-buffer.d.ts.map +0 -1
  70. package/dist/columnar/ring-buffer.js.map +0 -1
  71. package/dist/columnar/scatter.d.ts.map +0 -1
  72. package/dist/columnar/scatter.js.map +0 -1
  73. package/dist/columnar/store.d.ts.map +0 -1
  74. package/dist/columnar/store.js.map +0 -1
  75. package/dist/columnar/string-column.d.ts.map +0 -1
  76. package/dist/columnar/string-column.js.map +0 -1
  77. package/dist/columnar/types.d.ts.map +0 -1
  78. package/dist/columnar/types.js.map +0 -1
  79. package/dist/columnar/validity.d.ts.map +0 -1
  80. package/dist/columnar/validity.js.map +0 -1
  81. package/dist/columnar/view.d.ts.map +0 -1
  82. package/dist/columnar/view.js.map +0 -1
  83. package/dist/core/calendar.d.ts.map +0 -1
  84. package/dist/core/calendar.js.map +0 -1
  85. package/dist/core/duration.d.ts.map +0 -1
  86. package/dist/core/duration.js.map +0 -1
  87. package/dist/core/errors.d.ts.map +0 -1
  88. package/dist/core/errors.js.map +0 -1
  89. package/dist/core/event.d.ts.map +0 -1
  90. package/dist/core/event.js.map +0 -1
  91. package/dist/core/index.d.ts.map +0 -1
  92. package/dist/core/index.js.map +0 -1
  93. package/dist/core/interval.d.ts.map +0 -1
  94. package/dist/core/interval.js.map +0 -1
  95. package/dist/core/temporal.d.ts.map +0 -1
  96. package/dist/core/temporal.js.map +0 -1
  97. package/dist/core/time-range.d.ts.map +0 -1
  98. package/dist/core/time-range.js.map +0 -1
  99. package/dist/core/time.d.ts.map +0 -1
  100. package/dist/core/time.js.map +0 -1
  101. package/dist/index.d.ts.map +0 -1
  102. package/dist/index.js.map +0 -1
  103. package/dist/live/index.d.ts.map +0 -1
  104. package/dist/live/index.js.map +0 -1
  105. package/dist/live/live-aggregation.d.ts.map +0 -1
  106. package/dist/live/live-aggregation.js.map +0 -1
  107. package/dist/live/live-chunked-storage.d.ts.map +0 -1
  108. package/dist/live/live-chunked-storage.js.map +0 -1
  109. package/dist/live/live-fused-rolling.d.ts.map +0 -1
  110. package/dist/live/live-fused-rolling.js.map +0 -1
  111. package/dist/live/live-history.d.ts.map +0 -1
  112. package/dist/live/live-history.js.map +0 -1
  113. package/dist/live/live-partitioned-fused-rolling.d.ts.map +0 -1
  114. package/dist/live/live-partitioned-fused-rolling.js.map +0 -1
  115. package/dist/live/live-partitioned-series.d.ts.map +0 -1
  116. package/dist/live/live-partitioned-series.js.map +0 -1
  117. package/dist/live/live-partitioned-sync-rolling.d.ts.map +0 -1
  118. package/dist/live/live-partitioned-sync-rolling.js.map +0 -1
  119. package/dist/live/live-reduce.d.ts.map +0 -1
  120. package/dist/live/live-reduce.js.map +0 -1
  121. package/dist/live/live-rolling-aggregation.d.ts.map +0 -1
  122. package/dist/live/live-rolling-aggregation.js.map +0 -1
  123. package/dist/live/live-series.d.ts.map +0 -1
  124. package/dist/live/live-series.js.map +0 -1
  125. package/dist/live/live-storage.d.ts.map +0 -1
  126. package/dist/live/live-storage.js.map +0 -1
  127. package/dist/live/live-view.d.ts.map +0 -1
  128. package/dist/live/live-view.js.map +0 -1
  129. package/dist/live/series-store.d.ts.map +0 -1
  130. package/dist/live/series-store.js.map +0 -1
  131. package/dist/live/triggers.d.ts.map +0 -1
  132. package/dist/live/triggers.js.map +0 -1
  133. package/dist/reducers/avg.d.ts.map +0 -1
  134. package/dist/reducers/avg.js.map +0 -1
  135. package/dist/reducers/count.d.ts.map +0 -1
  136. package/dist/reducers/count.js.map +0 -1
  137. package/dist/reducers/difference.d.ts.map +0 -1
  138. package/dist/reducers/difference.js.map +0 -1
  139. package/dist/reducers/first.d.ts.map +0 -1
  140. package/dist/reducers/first.js.map +0 -1
  141. package/dist/reducers/index.d.ts.map +0 -1
  142. package/dist/reducers/index.js.map +0 -1
  143. package/dist/reducers/keep.d.ts.map +0 -1
  144. package/dist/reducers/keep.js.map +0 -1
  145. package/dist/reducers/last.d.ts.map +0 -1
  146. package/dist/reducers/last.js.map +0 -1
  147. package/dist/reducers/max.d.ts.map +0 -1
  148. package/dist/reducers/max.js.map +0 -1
  149. package/dist/reducers/median.d.ts.map +0 -1
  150. package/dist/reducers/median.js.map +0 -1
  151. package/dist/reducers/min.d.ts.map +0 -1
  152. package/dist/reducers/min.js.map +0 -1
  153. package/dist/reducers/percentile.d.ts.map +0 -1
  154. package/dist/reducers/percentile.js.map +0 -1
  155. package/dist/reducers/rolling.d.ts.map +0 -1
  156. package/dist/reducers/rolling.js.map +0 -1
  157. package/dist/reducers/samples.d.ts.map +0 -1
  158. package/dist/reducers/samples.js.map +0 -1
  159. package/dist/reducers/stdev.d.ts.map +0 -1
  160. package/dist/reducers/stdev.js.map +0 -1
  161. package/dist/reducers/sum.d.ts.map +0 -1
  162. package/dist/reducers/sum.js.map +0 -1
  163. package/dist/reducers/top.d.ts.map +0 -1
  164. package/dist/reducers/top.js.map +0 -1
  165. package/dist/reducers/types.d.ts.map +0 -1
  166. package/dist/reducers/types.js.map +0 -1
  167. package/dist/reducers/unique.d.ts.map +0 -1
  168. package/dist/reducers/unique.js.map +0 -1
  169. package/dist/schema/aggregate.d.ts.map +0 -1
  170. package/dist/schema/aggregate.js.map +0 -1
  171. package/dist/schema/diff.d.ts.map +0 -1
  172. package/dist/schema/diff.js.map +0 -1
  173. package/dist/schema/events.d.ts.map +0 -1
  174. package/dist/schema/events.js.map +0 -1
  175. package/dist/schema/index.d.ts.map +0 -1
  176. package/dist/schema/index.js.map +0 -1
  177. package/dist/schema/join.d.ts.map +0 -1
  178. package/dist/schema/join.js.map +0 -1
  179. package/dist/schema/json.d.ts.map +0 -1
  180. package/dist/schema/json.js.map +0 -1
  181. package/dist/schema/public.d.ts.map +0 -1
  182. package/dist/schema/public.js.map +0 -1
  183. package/dist/schema/reduce.d.ts.map +0 -1
  184. package/dist/schema/reduce.js.map +0 -1
  185. package/dist/schema/reshape.d.ts.map +0 -1
  186. package/dist/schema/reshape.js.map +0 -1
  187. package/dist/schema/rolling.d.ts.map +0 -1
  188. package/dist/schema/rolling.js.map +0 -1
  189. package/dist/schema/series.d.ts.map +0 -1
  190. package/dist/schema/series.js.map +0 -1
  191. package/dist/sequence/bounded-sequence.d.ts.map +0 -1
  192. package/dist/sequence/bounded-sequence.js.map +0 -1
  193. package/dist/sequence/index.d.ts.map +0 -1
  194. package/dist/sequence/index.js.map +0 -1
  195. package/dist/sequence/sample.d.ts.map +0 -1
  196. package/dist/sequence/sample.js.map +0 -1
  197. package/dist/sequence/sequence.d.ts.map +0 -1
  198. package/dist/sequence/sequence.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,7 +7,9 @@ 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.22.0...HEAD
10
+ [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.24.0...HEAD
11
+ [0.24.0]: https://github.com/pjm17971/pond-ts/compare/v0.23.0...v0.24.0
12
+ [0.23.0]: https://github.com/pjm17971/pond-ts/compare/v0.22.0...v0.23.0
11
13
  [0.22.0]: https://github.com/pjm17971/pond-ts/compare/v0.21.0...v0.22.0
12
14
  [0.21.0]: https://github.com/pjm17971/pond-ts/compare/v0.20.0...v0.21.0
13
15
  [0.20.0]: https://github.com/pjm17971/pond-ts/compare/v0.19.0...v0.20.0
@@ -16,6 +18,128 @@ type-level changes; patch bumps are strictly additive.
16
18
 
17
19
  ## [Unreleased]
18
20
 
21
+ ## [0.24.0] — 2026-06-14
22
+
23
+ ### Changed
24
+
25
+ - **`TimeSeries.timeRange()` is now a columnar key-axis read instead of a
26
+ reduce over materialized events.** Behavior is unchanged, but the old
27
+ implementation materialized every `Event` on its first call — and because
28
+ `aggregate()` defaults its `range` to `series.timeRange()`, a one-shot
29
+ `aggregate()` paid full event materialization before the columnar fast
30
+ path could run, erasing the win. The new path reads the key column's
31
+ begin/end axis directly: O(1) for time-keyed series, a typed-array scan
32
+ for range/interval-keyed series, with no event materialization. Measured
33
+ on 1M rows: `timeRange()` itself ~407 ms → ~0.002 ms (time-keyed); cold
34
+ `aggregate()` with a defaulted range ~387 ms → ~6 ms (~63×). Every
35
+ `timeRange()` / `overlaps` / `contains` / `intersection` caller benefits.
36
+ (Audit v2 §3.3.)
37
+ - **`aggregate()` now takes the columnar fast path when a mapping mixes
38
+ numeric reducers with `first` / `last`.** Previously a single `first` or
39
+ `last` column (they have no numeric `reduceColumn`) bailed the entire call
40
+ to the row path. They now qualify via a boundary scan — the first/last
41
+ _defined_ cell, on any column kind. Behavior is unchanged. The big
42
+ beneficiary is **partitioned `aggregate`**, which auto-injects a `'first'`
43
+ reducer for the partition column and so was excluded from the fast path on
44
+ every call (audit v2 §3.2/§3.3). Measured on 1M rows, flat
45
+ `{ cpu: 'avg', host: 'first' }`: ~37.7 ms → ~4.8 ms (~7.8×); the
46
+ pure-numeric path is unchanged. (The remaining `partitionBy` materialization
47
+ cost is addressed separately by the columnar `partitionBy` split.)
48
+ - **`partitionBy(...)` now splits the columnar store directly instead of
49
+ materializing events.** `collect()` / the per-partition sugar methods
50
+ (`fill` / `diff` / `rolling` / …) and `toMap()` previously walked
51
+ `this.events` to bucket rows, then rebuilt each partition via `fromEvents`
52
+ (re-validating + re-packing) — silently re-paying the event-materialization
53
+ tax the columnar wave removed, and making `partitionBy(host).fill().collect()`
54
+ the #1 batch hotspot. They now group row indices off the store and gather
55
+ each partition via a zero-materialization columnar selection. Behavior is
56
+ unchanged (partition order, the `' undefined'` missing-key bucket, composite
57
+ keys, and declared `groups` all preserved). Measured on 100k rows / 64
58
+ partitions: `toMap()` ~389 → ~25 ns/row (~15×, no event materialization at
59
+ all); `diff().collect()` ~2×; `fill(hold).collect()` ~1.7× (the residual is
60
+ `TimeSeries.concat` still materializing to re-sort — a separate follow-up).
61
+ Declared-`groups` membership is validated by the same columnar scan, so that
62
+ path is materialization-free too (~331 → ~33 ns/row). **Behavior note:**
63
+ per-partition sub-series from `toMap()` / `apply()` now lazily materialize
64
+ their own `Event` objects rather than reusing the source's instances — cell
65
+ values are identical; only object identity differs (`collect()`, which
66
+ returns the source unchanged, is unaffected). (Audit v2 §3.2.)
67
+
68
+ ## [0.23.0] — 2026-06-13
69
+
70
+ ### Added
71
+
72
+ - **`new TimeSeries({ …, sort: true })` (and `TimeSeries.fromJSON`) sort rows by
73
+ key on construction.** Pond requires rows in non-decreasing key order and
74
+ throws otherwise; `sort: true` accepts unsorted input (messy CSVs, merged
75
+ sources) and sorts it for you instead of forcing a manual pre-sort. The sort
76
+ is **stable** — rows with equal keys keep their input order — matching what
77
+ `TimeSeries.fromEvents` already does. The out-of-order error now names the
78
+ option. (Audit v2 §5 F3.)
79
+
80
+ ### Changed
81
+
82
+ - **CommonJS consumers now get a clear error instead of
83
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`.** Both `pond-ts` and `@pond-ts/react`
84
+ add a `require` condition to their `exports["."]` map pointing at a tiny
85
+ shipped CJS stub that throws an ESM-only message naming `import` as the
86
+ fix. The packages remain ES-module-only; this only improves the error a
87
+ `require('pond-ts')` caller sees. (Audit v2 §5 F6/F7/F9/F10/F11)
88
+ - **Published tarballs no longer ship `*.js.map` / `*.d.ts.map` source
89
+ maps.** The maps referenced a `../src` tree that was never included in
90
+ the tarball (`files: ["dist", …]`), so they were dead weight (~⅓ of the
91
+ unpacked size). A `prepack` step now strips them from the published
92
+ artifact for both packages; local `npm run build` still emits them, so
93
+ in-repo debugging is unaffected. (Audit v2 §5 F6/F7/F9/F10/F11)
94
+
95
+ ### Fixed
96
+
97
+ - **Shipped `.d.ts` now type-check under `skipLibCheck: false`.** The internal
98
+ `EMITS_EVICT` marker symbol was `@internal` (stripped from the emitted
99
+ `series.d.ts`) but still referenced by un-stripped public declarations — a
100
+ by-name re-export in `schema/index.d.ts` and the `[EMITS_EVICT]` brand members
101
+ on `LiveSeries` / `LiveView` — leaving dangling references that broke strict
102
+ consumer builds with **TS2305**. Those references are now `@internal` too, so
103
+ the symbol is fully stripped from the published types; runtime behavior is
104
+ unchanged. (Audit v2 §5 F2.)
105
+ - **`TimeSeries.at(-1)` counts from the end**, matching `LiveSeries.at` and
106
+ `Array.prototype.at` (it previously returned `undefined` for any negative
107
+ index). Deep underflow (e.g. `at(-100)` on a 3-event series) still returns
108
+ `undefined`, and the non-integer / `NaN` guard is unchanged. (Audit v2 §5 F8.)
109
+ - **Docs: corrected `Time.asString()` (does not exist), the missing
110
+ `aggregate`/`materialize` → `pivotByGroup` rekey pointer, and an
111
+ inaccurate `rolling().value()` return-type example.** The getting-started
112
+ example now calls `event.key().toDate().toISOString()`; the aggregation
113
+ and reshape pages note that interval-keyed output must be rekeyed with
114
+ `.asTime({ at: 'begin' })` before a time-keyed transform like
115
+ `pivotByGroup` (whose runtime error now says so too); the rolling page
116
+ documents `value()` as `Record<string, ColumnValue | undefined>`.
117
+ (Audit v2 §5 F6/F7/F9/F10/F11)
118
+ - **Mixed shorthand + `{ from, using }` mappings now keep every output
119
+ column in the result type (Audit v2 §5 F1).** Calling
120
+ `aggregate` / `rolling` / `reduce` with a mapping that mixes the
121
+ shorthand form (`cpu: 'avg'`) and the spec form
122
+ (`cpu_p95: { from: 'cpu', using: 'p95' }`) in one call — the
123
+ docs-blessed pattern — previously resolved to the shorthand overload
124
+ and **silently dropped every spec-keyed output column from the result
125
+ type** (`event.get('cpu_p95')` failed to compile with `TS2345`), even
126
+ though the runtime emitted the column. The two overloads
127
+ (`AggregateMap` shorthand + `AggregateOutputMap` spec) are now
128
+ collapsed into one unified mapping shape whose result schema dispatches
129
+ per output key, so all columns survive and each narrows to its
130
+ reducer's output kind. Runtime behavior is unchanged — this is a
131
+ types-only fix plus the tests that should have caught it.
132
+ - **The unified mapping keeps the shorthand compile-time guards.** A
133
+ shorthand reducer is still kind-checked against its source column
134
+ (`host: 'avg'` on a `string` column stays a compile error), and a bare
135
+ reducer on a key that is not a source column (`ghost: 'avg'` — a typo
136
+ the runtime rejects with "unknown source column") is now a compile
137
+ error too. Spec keys (`{ from, using }`) remain free output names.
138
+ Inline mapping literals get full validation; values pre-widened to
139
+ `AggregateMap<S>` and broad-schema (`TimeSeries<SeriesSchema>`)
140
+ callers keep the permissive shape. `AggregateOutputMap` is retained
141
+ as a back-compat alias of `AggregateMap`.
142
+
19
143
  ## [0.22.0] — 2026-06-12
20
144
 
21
145
  ### Changed
package/README.md CHANGED
@@ -152,20 +152,24 @@ const points = series.sample({ reservoir: { size: 500 } }).toRows();
152
152
 
153
153
  ## Performance
154
154
 
155
- pond-ts is **7.6x faster** than pondjs on average across all comparable
156
- operations, with no regressions. The advantage grows with data size.
157
-
158
- | Category | Speedup (N=16k) | Notes |
159
- | ----------------- | --------------- | --------------------------------------------- |
160
- | **Aggregation** | 25–32x | O(N+B) bucketing vs O(N×B) Pipeline |
161
- | **Alignment** | 32x | Forward cursor vs repeated binary search |
162
- | **Rate/diff** | 18x | Direct array walk vs Pipeline materialization |
163
- | **Fill** | 10–11x | Single-pass vs Pipeline per strategy |
164
- | **Transforms** | 316x | Pre-validated constructor skips re-validation |
165
- | **Construction** | 7x | Plain objects vs ImmutableJS wrapping |
166
- | **Statistics** | 79x | Direct computation vs ImmutableJS iteration |
167
- | **Serialization** | 4x | Simpler internal representation |
168
- | **Event access** | 23x | Array indexing vs ImmutableJS `get()` |
155
+ pond-ts is faster on **every** comparable operation, with no regressions
156
+ a **~17x** geometric-mean speedup across the measurable ops, plus a handful
157
+ of transforms (`select` / `rename`) that are **effectively instant** (O(1)
158
+ column rebinds, below the timer's resolution). The advantage grows with data
159
+ size.
160
+
161
+ | Category | Speedup (N=16k) | Notes |
162
+ | ----------------- | ---------------------------------------------------- | --------------------------------------------- |
163
+ | **Rate** | ~120x | Single columnar walk vs Pipeline |
164
+ | **Fill** | 7787x | Single columnar pass vs Pipeline per strategy |
165
+ | **Aggregation** | 57–82x | O(N+B) bucketing vs O(N×B) Pipeline |
166
+ | **Statistics** | 1880x | Typed-array reduce vs ImmutableJS iteration |
167
+ | **Alignment** | 42x | Forward cursor vs repeated binary search |
168
+ | **Construction** | 13x | Columnar intake vs ImmutableJS wrapping |
169
+ | **Chained** | 8x | Derived constructors vs per-step Pipeline |
170
+ | **Transforms** | `select`/`rename` instant; `collapse` 30x; `map` ~4x | Column reshapes vs Pipeline |
171
+ | **Event access** | 6x | Array indexing vs ImmutableJS `get()` |
172
+ | **Serialization** | 4x | Lightweight columnar representation |
169
173
 
170
174
  See the [full benchmark results](website/docs/reference/benchmarks.mdx)
171
175
  for detailed numbers. Run locally:
@@ -34,30 +34,6 @@ export type AggregateColumnSpec = {
34
34
  * same `mapping` shape produces the same schema.
35
35
  */
36
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
37
  export declare function tryAggregateColumnarTimeKeyed<B extends {
62
38
  begin(): number;
63
39
  end(): number;
@@ -73,45 +73,34 @@ 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
76
  export function tryAggregateColumnarTimeKeyed(begins, getColumn, buckets, columns) {
101
77
  const plans = [];
102
78
  for (const spec of columns) {
103
79
  if (typeof spec.reducer !== 'string')
104
80
  return null; // custom function
105
81
  const def = resolveReducer(spec.reducer);
106
- if (def.reduceColumn === undefined)
107
- return null; // first/last/unique/...
108
82
  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
83
+ if (source === undefined)
84
+ return null; // missing source
85
+ if (def.definedBoundary !== undefined) {
86
+ // `first` / `last`: pick the first/last *defined* cell in the bucket
87
+ // via a boundary scan over any column kind / storage (`col.read(i)`).
88
+ // This is what lets a partitioned `aggregate` — whose auto-injected
89
+ // partition-column reducer is `'first'` — take the fast path instead
90
+ // of bailing the whole call for lack of a numeric `reduceColumn`.
91
+ plans.push({
92
+ kind: 'boundary',
93
+ column: source,
94
+ which: def.definedBoundary,
95
+ });
96
+ continue;
113
97
  }
114
- plans.push({ column: source, reduce: def.reduceColumn });
98
+ if (def.reduceColumn === undefined)
99
+ return null; // unique / top / samples / keep
100
+ if (source.kind !== 'number' || source.storage !== 'packed') {
101
+ return null; // non-numeric / chunked numeric source
102
+ }
103
+ plans.push({ kind: 'reduce', column: source, reduce: def.reduceColumn });
115
104
  }
116
105
  const n = begins.length;
117
106
  let cursor = 0;
@@ -130,7 +119,33 @@ export function tryAggregateColumnarTimeKeyed(begins, getColumn, buckets, column
130
119
  const reduced = new Array(plans.length);
131
120
  for (let p = 0; p < plans.length; p += 1) {
132
121
  const plan = plans[p];
133
- reduced[p] = plan.reduce(plan.column.sliceByRange(start, scan));
122
+ if (plan.kind === 'reduce') {
123
+ reduced[p] = plan.reduce(plan.column.sliceByRange(start, scan));
124
+ }
125
+ else if (plan.which === 'first') {
126
+ // First defined cell in [start, scan); scans past missing cells.
127
+ let value;
128
+ for (let i = start; i < scan; i += 1) {
129
+ const cell = plan.column.read(i);
130
+ if (cell !== undefined) {
131
+ value = cell;
132
+ break;
133
+ }
134
+ }
135
+ reduced[p] = value;
136
+ }
137
+ else {
138
+ // Last defined cell in [start, scan); scans backward past missing.
139
+ let value;
140
+ for (let i = scan - 1; i >= start; i -= 1) {
141
+ const cell = plan.column.read(i);
142
+ if (cell !== undefined) {
143
+ value = cell;
144
+ break;
145
+ }
146
+ }
147
+ reduced[p] = value;
148
+ }
134
149
  }
135
150
  rows[b] = Object.freeze([bucket, ...reduced]);
136
151
  }
@@ -4,8 +4,7 @@ import { Sequence } from '../sequence/sequence.js';
4
4
  import type { DurationInput } from '../core/duration.js';
5
5
  import type { TemporalLike } from '../core/temporal.js';
6
6
  import type { BatchSampleStrategy } from '../sequence/sample.js';
7
- import type { AggregateMap, AggregateOutputMap, AggregateSchema, AlignSchema, BaselineSchema, DedupeKeep, DiffSchema, EventDataForSchema, FillMapping, FillStrategy, MaterializeSchema, NumericColumnNameForSchema, RollingAlignment, RollingSchema, SeriesSchema, SmoothAppendSchema, SmoothMethod, SmoothSchema } from '../schema/index.js';
8
- import type { AggregateOutputMapResultSchema, RollingOutputMapSchema } from '../schema/index.js';
7
+ import type { AggregateSchema, AlignSchema, BaselineSchema, DedupeKeep, DiffSchema, EventDataForSchema, FillMapping, FillStrategy, MaterializeSchema, NumericColumnNameForSchema, RollingAlignment, RollingSchema, SeriesSchema, SmoothAppendSchema, SmoothMethod, SmoothSchema, ValidatedAggregateMap } from '../schema/index.js';
9
8
  type SequenceLike = Sequence | BoundedSequence;
10
9
  type AlignMethod = 'hold' | 'linear';
11
10
  type AlignSample = 'begin' | 'center' | 'end';
@@ -197,8 +196,6 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
197
196
  toMap(): Map<K, TimeSeries<S>>;
198
197
  toMap<R extends SeriesSchema>(transform: (group: TimeSeries<S>) => TimeSeries<R>): Map<K, TimeSeries<R>>;
199
198
  toMap<R>(transform: (group: TimeSeries<S>) => R): Map<K, R>;
200
- private static partitionKeyOf;
201
- private static bucketByPartition;
202
199
  private static applyToSource;
203
200
  private rewrap;
204
201
  /**
@@ -247,26 +244,16 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
247
244
  range?: TemporalLike;
248
245
  }): PartitionedTimeSeries<MaterializeSchema<S>, K>;
249
246
  /** Per-partition `rolling`. See {@link TimeSeries.rolling}. */
250
- rolling<const Mapping extends AggregateMap<S>>(window: DurationInput, mapping: Mapping, options?: {
247
+ rolling<const Mapping extends ValidatedAggregateMap<S, Mapping>>(window: DurationInput, mapping: Mapping, options?: {
251
248
  alignment?: RollingAlignment;
252
249
  minSamples?: number;
253
250
  }): PartitionedTimeSeries<RollingSchema<S, Mapping>, K>;
254
- rolling<const Mapping extends AggregateOutputMap<S>>(window: DurationInput, mapping: Mapping, options?: {
255
- alignment?: RollingAlignment;
256
- minSamples?: number;
257
- }): PartitionedTimeSeries<RollingOutputMapSchema<S, Mapping>, K>;
258
- rolling<const Mapping extends AggregateMap<S>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
251
+ rolling<const Mapping extends ValidatedAggregateMap<S, Mapping>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
259
252
  alignment?: RollingAlignment;
260
253
  sample?: AlignSample;
261
254
  range?: TemporalLike;
262
255
  minSamples?: number;
263
256
  }): PartitionedTimeSeries<AggregateSchema<S, Mapping>, K>;
264
- rolling<const Mapping extends AggregateOutputMap<S>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
265
- alignment?: RollingAlignment;
266
- sample?: AlignSample;
267
- range?: TemporalLike;
268
- minSamples?: number;
269
- }): PartitionedTimeSeries<AggregateOutputMapResultSchema<S, Mapping>, K>;
270
257
  /** Per-partition `smooth`. See {@link TimeSeries.smooth}. */
271
258
  smooth<const Target extends NumericColumnNameForSchema<S>, const Output extends string | undefined = undefined>(column: Target, method: SmoothMethod, options: {
272
259
  alpha: number;
@@ -319,12 +306,9 @@ export declare class PartitionedTimeSeries<S extends SeriesSchema, K extends str
319
306
  /** Per-partition `shift`. See {@link TimeSeries.shift}. */
320
307
  shift<const Target extends NumericColumnNameForSchema<S>>(columns: Target | readonly Target[], n: number): PartitionedTimeSeries<DiffSchema<S, Target>, K>;
321
308
  /** Per-partition `aggregate`. See {@link TimeSeries.aggregate}. */
322
- aggregate<const Mapping extends AggregateMap<S>>(sequence: SequenceLike, mapping: Mapping, options?: {
309
+ aggregate<const Mapping extends ValidatedAggregateMap<S, Mapping>>(sequence: SequenceLike, mapping: Mapping, options?: {
323
310
  range?: TemporalLike;
324
311
  }): PartitionedTimeSeries<AggregateSchema<S, Mapping>, K>;
325
- aggregate<const Mapping extends AggregateOutputMap<S>>(sequence: SequenceLike, mapping: Mapping, options?: {
326
- range?: TemporalLike;
327
- }): PartitionedTimeSeries<AggregateOutputMapResultSchema<S, Mapping>, K>;
328
312
  }
329
313
  export {};
330
314
  //# sourceMappingURL=partitioned-time-series.d.ts.map
@@ -88,17 +88,17 @@ export class PartitionedTimeSeries {
88
88
  this.validateGroupMembership();
89
89
  }
90
90
  }
91
- // Validate that every event's partition value appears in the
92
- // declared groups. Mirrors the partition encoder so the comparison
93
- // accepts the same string forms toMap will produce as keys.
91
+ // Validate that every partition value appears in the declared groups.
92
+ // Scans the partition column columnar-natively via `_distinctPartitionKeys`
93
+ // (no event materialization — same encoding `toMap` produces), so the
94
+ // declared-`groups` path is materialization-free like the rest of the
95
+ // split.
94
96
  validateGroupMembership() {
95
97
  if (!this.groups)
96
98
  return;
97
99
  const col = this.by[0];
98
100
  const declared = new Set(this.groups);
99
- const keyOf = PartitionedTimeSeries.partitionKeyOf(this.by);
100
- for (const event of this.source.events) {
101
- const key = keyOf(event);
101
+ for (const key of this.source._distinctPartitionKeys(this.by)) {
102
102
  if (!declared.has(key)) {
103
103
  // Decode the encoder's leading-space sentinel so the message
104
104
  // shows the user-facing concept, not the internal encoding.
@@ -205,96 +205,54 @@ export class PartitionedTimeSeries {
205
205
  }
206
206
  toMap(transform) {
207
207
  const result = new Map();
208
- const buckets = this.source.events.length === 0
208
+ // Columnar partition split each sub-series is gathered straight off
209
+ // the store, no event materialization (the old path rebuilt every
210
+ // partition via `fromEvents`). Map order = first-encountered partition.
211
+ const partitions = this.source.length === 0
209
212
  ? new Map()
210
- : PartitionedTimeSeries.bucketByPartition(this.source, this.by);
213
+ : this.source._partitionByColumns(this.by);
211
214
  if (this.groups) {
212
215
  // Declared-order iteration. Empty groups produce empty
213
216
  // TimeSeries entries (consistent with pivotByGroup's typed
214
217
  // groups behavior, which emits a column for every declared
215
218
  // value even when no events match).
216
219
  for (const g of this.groups) {
217
- const events = buckets.get(g) ?? [];
218
- const sub = TimeSeries.fromEvents(events, {
219
- schema: this.source.schema,
220
- name: this.source.name,
221
- });
220
+ const sub = partitions.get(g) ??
221
+ TimeSeries.fromEvents([], {
222
+ schema: this.source.schema,
223
+ name: this.source.name,
224
+ });
222
225
  result.set(g, transform ? transform(sub) : sub);
223
226
  }
224
227
  return result;
225
228
  }
226
229
  // Insertion-order iteration (matches the order each partition was
227
230
  // first encountered in the source events).
228
- for (const [key, events] of buckets) {
229
- const sub = TimeSeries.fromEvents(events, {
230
- schema: this.source.schema,
231
- name: this.source.name,
232
- });
231
+ for (const [key, sub] of partitions) {
233
232
  result.set(key, transform ? transform(sub) : sub);
234
233
  }
235
234
  return result;
236
235
  }
237
- // Build the encoder that produces a string key for an event given
238
- // the partition columns. Single-column case avoids the JSON encoding
239
- // overhead. Multi-column uses JSON.stringify to guarantee no key
240
- // collisions on values containing separators (e.g. region names with
241
- // spaces) — a naive `parts.join('|')` would collide. `undefined` in a
242
- // single-column key becomes the literal `' undefined'` (with the
243
- // leading space ensuring it can never collide with a string column
244
- // whose value is the literal `'undefined'`).
245
- static partitionKeyOf(by) {
246
- if (by.length === 1) {
247
- const col = by[0];
248
- return (event) => {
249
- const v = event.data()[col];
250
- return v === undefined ? ' undefined' : `${String(v)}`;
251
- };
252
- }
253
- return (event) => {
254
- const data = event.data();
255
- const parts = new Array(by.length);
256
- for (let i = 0; i < by.length; i += 1) {
257
- parts[i] = data[by[i]] ?? null;
258
- }
259
- return JSON.stringify(parts);
260
- };
261
- }
262
- // Group source events into buckets keyed by partition value. Returned
263
- // Map iteration order = insertion order, which matches the order
264
- // partitions were first seen in the source events array.
265
- static bucketByPartition(source, by) {
266
- const keyOf = PartitionedTimeSeries.partitionKeyOf(by);
267
- const buckets = new Map();
268
- for (const event of source.events) {
269
- const key = keyOf(event);
270
- let bucket = buckets.get(key);
271
- if (!bucket) {
272
- bucket = [];
273
- buckets.set(key, bucket);
274
- }
275
- bucket.push(event);
276
- }
277
- return buckets;
278
- }
279
236
  // Internal helper used by both `apply` (terminal) and the sugar
280
237
  // methods (which re-wrap the result back into a partitioned view).
281
238
  static applyToSource(source, by, fn) {
282
239
  // Empty source: apply fn to an empty group so the output schema
283
240
  // and name come from fn, not from inferring R structurally.
284
- if (source.events.length === 0) {
241
+ if (source.length === 0) {
285
242
  const empty = TimeSeries.fromEvents([], {
286
243
  schema: source.schema,
287
244
  name: source.name,
288
245
  });
289
246
  return fn(empty);
290
247
  }
291
- const buckets = PartitionedTimeSeries.bucketByPartition(source, by);
248
+ // Columnar partition split — no event materialization. The old path
249
+ // walked `source.events` to bucket, then rebuilt each partition via
250
+ // `fromEvents` (re-validating + re-packing), silently re-paying the
251
+ // 495 ns/row tax the columnar wave removed. `_partitionByColumns`
252
+ // gathers each partition straight off the store (audit v2 §3.2).
253
+ const partitions = source._partitionByColumns(by);
292
254
  const transformed = [];
293
- for (const events of buckets.values()) {
294
- const sub = TimeSeries.fromEvents(events, {
295
- schema: source.schema,
296
- name: source.name,
297
- });
255
+ for (const sub of partitions.values()) {
298
256
  transformed.push(fn(sub));
299
257
  }
300
258
  return TimeSeries.concat(transformed);
@@ -1,5 +1,4 @@
1
- import type { AlignSchema, MaterializeSchema, ArrayAggregateAppendSchema, ArrayAggregateReplaceSchema, ArrayColumnNameForSchema, ArrayExplodeAppendSchema, ArrayExplodeReplaceSchema, BaselineSchema, AggregateReducer, AggregateOutputMap, AggregateMap, AggregateSchema, CollapseSchema, EventDataForSchema, EventForSchema, FirstColKind, IntervalKeyedSchema, JsonRowFormat, JoinManySchema, JoinSchema, JoinType, NumericColumnNameForSchema, NormalizedObjectRow, NormalizedRowForSchema, PivotByGroupSchema, PointRowForSchema, PrefixedJoinManySchema, PrefixedJoinSchema, ReduceResult, RenameMap } from '../schema/index.js';
2
- import type { AggregateOutputMapResultSchema, RollingOutputMapSchema } from '../schema/index.js';
1
+ import type { AlignSchema, MaterializeSchema, ArrayAggregateAppendSchema, ArrayAggregateReplaceSchema, ArrayColumnNameForSchema, ArrayExplodeAppendSchema, ArrayExplodeReplaceSchema, BaselineSchema, AggregateReducer, AggregateSchema, CollapseSchema, EventDataForSchema, EventForSchema, FirstColKind, IntervalKeyedSchema, JsonRowFormat, JoinManySchema, JoinSchema, JoinType, NumericColumnNameForSchema, NormalizedObjectRow, NormalizedRowForSchema, PivotByGroupSchema, PointRowForSchema, PrefixedJoinManySchema, PrefixedJoinSchema, ReduceResult, RenameMap, ValidatedAggregateMap } from '../schema/index.js';
3
2
  import type { RenameSchema, RollingAlignment, RollingSchema, ColumnValue, DedupeKeep, DiffSchema, FillMapping, FillStrategy, ScalarKind, ScalarValue, SmoothMethod, SmoothAppendSchema, SmoothSchema, SelectSchema, SeriesSchema, TimeKeyedSchema, TimeSeriesJsonInput, TimeSeriesInput, TimeRangeKeyedSchema, ValueColumnKindForName, ValueColumnNameForSchema, ValueColumnsForSchema } from '../schema/index.js';
4
3
  import { BoundedSequence } from '../sequence/bounded-sequence.js';
5
4
  import { type TimeZoneOptions } from '../core/calendar.js';
@@ -77,6 +76,12 @@ export declare class TimeSeries<S extends SeriesSchema> {
77
76
  */
78
77
  static fromJSON<S extends SeriesSchema>(input: TimeSeriesJsonInput<S> & {
79
78
  parse?: TimeZoneOptions;
79
+ /**
80
+ * Sort rows by key before construction (off by default; see
81
+ * `TimeSeriesInput.sort`). Useful when reviving a wire payload whose
82
+ * rows aren't guaranteed sorted — avoids a manual pre-sort.
83
+ */
84
+ sort?: boolean;
80
85
  }): TimeSeries<S>;
81
86
  /**
82
87
  * Example: `TimeSeries.fromEvents(events, { schema, name })`.
@@ -497,12 +502,9 @@ export declare class TimeSeries<S extends SeriesSchema> {
497
502
  * `series.partitionBy(col).aggregate(seq, mapping).collect()` to
498
503
  * aggregate per entity. See {@link TimeSeries.partitionBy}.
499
504
  */
500
- aggregate<const Mapping extends AggregateMap<S>>(sequence: SequenceLike, mapping: Mapping, options?: {
505
+ aggregate<const Mapping extends ValidatedAggregateMap<S, Mapping>>(sequence: SequenceLike, mapping: Mapping, options?: {
501
506
  range?: TemporalLike;
502
507
  }): TimeSeries<AggregateSchema<S, Mapping>>;
503
- aggregate<const Mapping extends AggregateOutputMap<S>>(sequence: SequenceLike, mapping: Mapping, options?: {
504
- range?: TemporalLike;
505
- }): TimeSeries<AggregateOutputMapResultSchema<S, Mapping>>;
506
508
  /**
507
509
  * Example: `series.reduce("value", "avg")`.
508
510
  * Collapses the entire series to a single scalar value using the specified reducer.
@@ -516,8 +518,7 @@ export declare class TimeSeries<S extends SeriesSchema> {
516
518
  * a plain value or record.
517
519
  */
518
520
  reduce(column: ValueColumnsForSchema<S>[number]['name'], reducer: AggregateReducer): ColumnValue | undefined;
519
- reduce<const Mapping extends AggregateMap<S>>(mapping: Mapping): ReduceResult<S, Mapping>;
520
- reduce<const Mapping extends AggregateOutputMap<S>>(mapping: Mapping): ReduceResult<S, Mapping>;
521
+ reduce<const Mapping extends ValidatedAggregateMap<S, Mapping>>(mapping: Mapping): ReduceResult<S, Mapping>;
521
522
  /**
522
523
  * Example: `series.groupBy("host")`.
523
524
  * Partitions the series into groups keyed by the distinct values of a payload column.
@@ -900,26 +901,16 @@ export declare class TimeSeries<S extends SeriesSchema> {
900
901
  * `series.partitionBy(col).rolling(...).collect()` to scope per
901
902
  * entity. See {@link TimeSeries.partitionBy}.
902
903
  */
903
- rolling<const Mapping extends AggregateMap<S>>(window: DurationInput, mapping: Mapping, options?: {
904
+ rolling<const Mapping extends ValidatedAggregateMap<S, Mapping>>(window: DurationInput, mapping: Mapping, options?: {
904
905
  alignment?: RollingAlignment;
905
906
  minSamples?: number;
906
907
  }): TimeSeries<RollingSchema<S, Mapping>>;
907
- rolling<const Mapping extends AggregateOutputMap<S>>(window: DurationInput, mapping: Mapping, options?: {
908
- alignment?: RollingAlignment;
909
- minSamples?: number;
910
- }): TimeSeries<RollingOutputMapSchema<S, Mapping>>;
911
- rolling<const Mapping extends AggregateMap<S>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
908
+ rolling<const Mapping extends ValidatedAggregateMap<S, Mapping>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
912
909
  alignment?: RollingAlignment;
913
910
  sample?: AlignSample;
914
911
  range?: TemporalLike;
915
912
  minSamples?: number;
916
913
  }): TimeSeries<AggregateSchema<S, Mapping>>;
917
- rolling<const Mapping extends AggregateOutputMap<S>>(sequence: SequenceLike, window: DurationInput, mapping: Mapping, options?: {
918
- alignment?: RollingAlignment;
919
- sample?: AlignSample;
920
- range?: TemporalLike;
921
- minSamples?: number;
922
- }): TimeSeries<AggregateOutputMapResultSchema<S, Mapping>>;
923
914
  /**
924
915
  * Example: `series.smooth("value", "ema", { alpha: 0.2 })`.
925
916
  * Applies a smoothing transform to one numeric payload column while preserving the original key