pond-ts 0.1.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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -0
  3. package/dist/BoundedSequence.d.ts +28 -0
  4. package/dist/BoundedSequence.d.ts.map +1 -0
  5. package/dist/BoundedSequence.js +70 -0
  6. package/dist/BoundedSequence.js.map +1 -0
  7. package/dist/Event.d.ts +84 -0
  8. package/dist/Event.d.ts.map +1 -0
  9. package/dist/Event.js +162 -0
  10. package/dist/Event.js.map +1 -0
  11. package/dist/Interval.d.ts +51 -0
  12. package/dist/Interval.d.ts.map +1 -0
  13. package/dist/Interval.js +130 -0
  14. package/dist/Interval.js.map +1 -0
  15. package/dist/Sequence.d.ts +80 -0
  16. package/dist/Sequence.d.ts.map +1 -0
  17. package/dist/Sequence.js +197 -0
  18. package/dist/Sequence.js.map +1 -0
  19. package/dist/Time.d.ts +43 -0
  20. package/dist/Time.d.ts.map +1 -0
  21. package/dist/Time.js +78 -0
  22. package/dist/Time.js.map +1 -0
  23. package/dist/TimeRange.d.ts +45 -0
  24. package/dist/TimeRange.d.ts.map +1 -0
  25. package/dist/TimeRange.js +144 -0
  26. package/dist/TimeRange.js.map +1 -0
  27. package/dist/TimeSeries.d.ts +337 -0
  28. package/dist/TimeSeries.d.ts.map +1 -0
  29. package/dist/TimeSeries.js +1217 -0
  30. package/dist/TimeSeries.js.map +1 -0
  31. package/dist/calendar.d.ts +24 -0
  32. package/dist/calendar.d.ts.map +1 -0
  33. package/dist/calendar.js +96 -0
  34. package/dist/calendar.js.map +1 -0
  35. package/dist/errors.d.ts +4 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +7 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +9 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/temporal.d.ts +37 -0
  44. package/dist/temporal.d.ts.map +1 -0
  45. package/dist/temporal.js +29 -0
  46. package/dist/temporal.js.map +1 -0
  47. package/dist/types.d.ts +175 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/validate.d.ts +3 -0
  52. package/dist/validate.d.ts.map +1 -0
  53. package/dist/validate.js +145 -0
  54. package/dist/validate.js.map +1 -0
  55. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peter Murphy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,348 @@
1
+ # pond-ts
2
+
3
+ TypeScript-first time series primitives built around typed events, typed schemas, and explicit temporal keys.
4
+
5
+ The package is intended to work in modern Node and frontend projects. The repo toolchain should work on Node 18, and we can use `nvm` to verify against newer stable Node releases when needed.
6
+
7
+ The library is currently focused on non-streaming analytics:
8
+
9
+ - typed `TimeSeries` construction
10
+ - `Time`, `TimeRange`, and `Interval` keys
11
+ - immutable `Event` objects
12
+ - alignment, aggregation, joins, rolling windows, and smoothing
13
+ - timezone-aware calendar sequences and ingest helpers
14
+
15
+ ## Install
16
+
17
+ ```sh
18
+ npm install pond-ts
19
+ ```
20
+
21
+ ## Build
22
+
23
+ ```sh
24
+ npm run build
25
+ ```
26
+
27
+ ## Format
28
+
29
+ ```sh
30
+ npm run format
31
+ ```
32
+
33
+ ## Test
34
+
35
+ ```sh
36
+ npm test
37
+ ```
38
+
39
+ ## Verify
40
+
41
+ ```sh
42
+ npm run verify
43
+ ```
44
+
45
+ ## License
46
+
47
+ MIT
48
+
49
+ ## Core model
50
+
51
+ The key types are:
52
+
53
+ - `Time`: a point in time
54
+ - `TimeRange`: an unlabeled interval
55
+ - `Interval`: a labeled interval
56
+
57
+ An `Event` is a key plus typed data.
58
+
59
+ A `TimeSeries` is an ordered immutable collection of events sharing one schema.
60
+
61
+ ## Quick start
62
+
63
+ ```ts
64
+ import { TimeSeries } from 'pond-ts';
65
+
66
+ const schema = [
67
+ { name: 'time', kind: 'time' },
68
+ { name: 'cpu', kind: 'number' },
69
+ { name: 'host', kind: 'string' },
70
+ { name: 'healthy', kind: 'boolean' },
71
+ ] as const;
72
+
73
+ const series = new TimeSeries({
74
+ name: 'cpu',
75
+ schema,
76
+ rows: [
77
+ [new Date('2025-01-01T00:00:00.000Z'), 0.42, 'api-1', true],
78
+ [new Date('2025-01-01T00:01:00.000Z'), 0.51, 'api-2', true],
79
+ ],
80
+ });
81
+
82
+ const event = series.at(1);
83
+ if (!event) {
84
+ throw new Error('missing event');
85
+ }
86
+
87
+ event.key();
88
+ event.timeRange();
89
+ event.get('cpu');
90
+ event.data().host;
91
+ ```
92
+
93
+ ## JSON ingest
94
+
95
+ Use `TimeSeries.fromJSON(...)` for external data and ambiguous local timestamps.
96
+
97
+ ```ts
98
+ import { TimeSeries } from 'pond-ts';
99
+
100
+ const schema = [
101
+ { name: 'time', kind: 'time' },
102
+ { name: 'value', kind: 'number' },
103
+ { name: 'status', kind: 'string', required: false },
104
+ ] as const;
105
+
106
+ const series = TimeSeries.fromJSON({
107
+ name: 'cpu',
108
+ schema,
109
+ rows: [
110
+ ['2025-01-01T09:00', 0.42, 'ok'],
111
+ ['2025-01-01T10:00', 0.51, null],
112
+ ],
113
+ parse: { timeZone: 'Europe/Madrid' },
114
+ });
115
+ ```
116
+
117
+ ## Event and series transforms
118
+
119
+ Event-level transforms:
120
+
121
+ - `get(...)`
122
+ - `set(...)`
123
+ - `merge(...)`
124
+ - `select(...)`
125
+ - `rename(...)`
126
+ - `collapse(...)`
127
+ - `asTime(...)`
128
+ - `asTimeRange()`
129
+ - `asInterval(...)`
130
+
131
+ Series-level transforms:
132
+
133
+ - `map(...)`
134
+ - `select(...)`
135
+ - `rename(...)`
136
+ - `collapse(...)`
137
+ - `asTime(...)`
138
+ - `asTimeRange()`
139
+ - `asInterval(...)`
140
+
141
+ Example:
142
+
143
+ ```ts
144
+ const renamed = series.rename({ cpu: 'usage' });
145
+ const selected = renamed.select('usage', 'healthy');
146
+ ```
147
+
148
+ ## Temporal selection
149
+
150
+ `TimeSeries` includes both positional and temporal selection methods:
151
+
152
+ - `slice(...)`
153
+ - `filter(...)`
154
+ - `find(...)`
155
+ - `first()`
156
+ - `last()`
157
+ - `before(...)`
158
+ - `after(...)`
159
+ - `within(...)`
160
+ - `overlapping(...)`
161
+ - `containedBy(...)`
162
+ - `trim(...)`
163
+ - `includesKey(...)`
164
+ - `bisect(...)`
165
+ - `atOrBefore(...)`
166
+ - `atOrAfter(...)`
167
+
168
+ Vocabulary is intentionally distinct:
169
+
170
+ - `within(...)`: fully contained
171
+ - `overlapping(...)`: intersects without clipping
172
+ - `trim(...)`: intersects and clips event extents
173
+
174
+ ## Sequences
175
+
176
+ Use `Sequence` for unbounded grids and `BoundedSequence` for explicit finite interval lists.
177
+
178
+ Fixed-step sequences:
179
+
180
+ ```ts
181
+ import { Sequence } from 'pond-ts';
182
+
183
+ const minuteGrid = Sequence.every('1m');
184
+ const hourlyGrid = Sequence.hourly();
185
+ ```
186
+
187
+ Calendar-aware sequences:
188
+
189
+ ```ts
190
+ const localDays = Sequence.calendar('day', {
191
+ timeZone: 'America/New_York',
192
+ });
193
+ ```
194
+
195
+ Explicit bounded sequences:
196
+
197
+ ```ts
198
+ import { BoundedSequence, Interval } from 'pond-ts';
199
+
200
+ const buckets = new BoundedSequence([
201
+ new Interval({ value: 'a', start: 0, end: 10 }),
202
+ new Interval({ value: 'b', start: 20, end: 30 }),
203
+ ]);
204
+ ```
205
+
206
+ ## Alignment and aggregation
207
+
208
+ Align onto a sequence:
209
+
210
+ ```ts
211
+ const aligned = series.align(Sequence.every('1m'), {
212
+ method: 'hold',
213
+ });
214
+ ```
215
+
216
+ Aggregate into buckets:
217
+
218
+ ```ts
219
+ const aggregated = series.aggregate(Sequence.every('5m'), {
220
+ cpu: 'avg',
221
+ host: 'last',
222
+ });
223
+ ```
224
+
225
+ Built-in aggregations:
226
+
227
+ - `sum`
228
+ - `avg`
229
+ - `min`
230
+ - `max`
231
+ - `count`
232
+ - `first`
233
+ - `last`
234
+
235
+ ## Joins
236
+
237
+ Join two aligned or bucketed series:
238
+
239
+ ```ts
240
+ const joined = left.join(right, { type: 'outer' });
241
+ ```
242
+
243
+ Supported join types:
244
+
245
+ - `outer`
246
+ - `left`
247
+ - `right`
248
+ - `inner`
249
+
250
+ Join many:
251
+
252
+ ```ts
253
+ const wide = TimeSeries.joinMany([cpu, memory, errors], {
254
+ type: 'outer',
255
+ });
256
+ ```
257
+
258
+ Conflict handling:
259
+
260
+ - default: `onConflict: "error"`
261
+ - optional prefixing:
262
+
263
+ ```ts
264
+ const joined = left.join(right, {
265
+ onConflict: 'prefix',
266
+ prefixes: ['left', 'right'] as const,
267
+ });
268
+ ```
269
+
270
+ ## Rolling windows
271
+
272
+ Event-driven rolling:
273
+
274
+ ```ts
275
+ const rolled = series.rolling('5m', {
276
+ cpu: 'avg',
277
+ host: 'last',
278
+ });
279
+ ```
280
+
281
+ Sequence-driven rolling:
282
+
283
+ ```ts
284
+ const rolledOnGrid = series.rolling(Sequence.every('1m'), '5m', { cpu: 'avg' });
285
+ ```
286
+
287
+ Rolling alignment options:
288
+
289
+ - `trailing`
290
+ - `centered`
291
+ - `leading`
292
+
293
+ ## Smoothing
294
+
295
+ Smoothing targets one numeric column at a time.
296
+
297
+ Replace the source column:
298
+
299
+ ```ts
300
+ const smoothed = series.smooth('cpu', 'ema', { alpha: 0.2 });
301
+ ```
302
+
303
+ Append the smoothed output:
304
+
305
+ ```ts
306
+ const smoothed = series.smooth('cpu', 'movingAverage', {
307
+ window: '5m',
308
+ alignment: 'centered',
309
+ output: 'cpuAvg',
310
+ });
311
+ ```
312
+
313
+ Supported smoothing methods:
314
+
315
+ - `ema`
316
+ - `movingAverage`
317
+ - `loess`
318
+
319
+ For interval-like keys, smoothing uses the key midpoint as the internal anchor.
320
+
321
+ ## Calendar-aware helpers
322
+
323
+ Primitive helpers normalize local calendar inputs into absolute time:
324
+
325
+ ```ts
326
+ import { Interval, Time, TimeRange } from 'pond-ts';
327
+
328
+ const time = Time.parse('2025-01-01T09:00', { timeZone: 'Europe/Madrid' });
329
+ const day = TimeRange.fromDate('2025-01-01', { timeZone: 'UTC' });
330
+ const month = Interval.fromCalendar('month', '2025-01', {
331
+ timeZone: 'UTC',
332
+ value: '2025-01',
333
+ });
334
+ ```
335
+
336
+ ## Current scope
337
+
338
+ This package is batch-oriented and immutable.
339
+
340
+ It does not yet provide a dedicated live/streaming ingestion layer. The current focus is:
341
+
342
+ - type-safe construction
343
+ - temporal modeling
344
+ - composable non-streaming analytics
345
+
346
+ ## License
347
+
348
+ No license file is included yet.
@@ -0,0 +1,28 @@
1
+ import { Interval } from './Interval.js';
2
+ import { TimeRange } from './TimeRange.js';
3
+ /**
4
+ * A finite ordered list of `Interval` buckets.
5
+ *
6
+ * Use `BoundedSequence` when you already have an explicit interval list, or use
7
+ * `Sequence.bounded(...)` to realize a finite run from an unbounded grid definition.
8
+ */
9
+ export declare class BoundedSequence {
10
+ #private;
11
+ /** Example: `new BoundedSequence([intervalA, intervalB])`. Creates a finite interval sequence from an explicit list. */
12
+ constructor(intervals: ReadonlyArray<Interval>);
13
+ /** Example: `bounded.length`. Returns the number of intervals in the bounded sequence. */
14
+ get length(): number;
15
+ /** Example: `bounded.at(0)`. Returns the interval at the supplied position, if present. */
16
+ at(index: number): Interval | undefined;
17
+ /** Example: `bounded.first()`. Returns the first interval, if present. */
18
+ first(): Interval | undefined;
19
+ /** Example: `bounded.last()`. Returns the last interval, if present. */
20
+ last(): Interval | undefined;
21
+ /** Example: `bounded.timeRange()`. Returns the finite temporal extent of this bounded sequence, if any. */
22
+ timeRange(): TimeRange | undefined;
23
+ /** Example: `bounded.slice(0, 10)`. Returns a positional half-open slice of the bounded sequence. */
24
+ slice(beginIndex?: number, endIndex?: number): BoundedSequence;
25
+ /** Example: `bounded.intervals()`. Returns the finite explicit intervals represented by this bounded sequence. */
26
+ intervals(): ReadonlyArray<Interval>;
27
+ }
28
+ //# sourceMappingURL=BoundedSequence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoundedSequence.d.ts","sourceRoot":"","sources":["../src/BoundedSequence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA6B3C;;;;;GAKG;AACH,qBAAa,eAAe;;IAG1B,wHAAwH;gBAC5G,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC;IAM9C,0FAA0F;IAC1F,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,2FAA2F;IAC3F,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,0EAA0E;IAC1E,KAAK,IAAI,QAAQ,GAAG,SAAS;IAI7B,wEAAwE;IACxE,IAAI,IAAI,QAAQ,GAAG,SAAS;IAM5B,2GAA2G;IAC3G,SAAS,IAAI,SAAS,GAAG,SAAS;IASlC,qGAAqG;IACrG,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe;IAI9D,kHAAkH;IAClH,SAAS,IAAI,aAAa,CAAC,QAAQ,CAAC;CAGrC"}
@@ -0,0 +1,70 @@
1
+ import { TimeRange } from './TimeRange.js';
2
+ function validateIntervals(intervals) {
3
+ for (let index = 0; index < intervals.length; index += 1) {
4
+ const current = intervals[index];
5
+ if (current.end() <= current.begin()) {
6
+ throw new TypeError('bounded sequence intervals must have positive duration');
7
+ }
8
+ if (index === 0) {
9
+ continue;
10
+ }
11
+ const previous = intervals[index - 1];
12
+ if (current.begin() < previous.begin()) {
13
+ throw new TypeError('bounded sequence intervals must be sorted by start time');
14
+ }
15
+ if (current.begin() < previous.end()) {
16
+ throw new TypeError('bounded sequence intervals must not overlap');
17
+ }
18
+ }
19
+ }
20
+ /**
21
+ * A finite ordered list of `Interval` buckets.
22
+ *
23
+ * Use `BoundedSequence` when you already have an explicit interval list, or use
24
+ * `Sequence.bounded(...)` to realize a finite run from an unbounded grid definition.
25
+ */
26
+ export class BoundedSequence {
27
+ #intervals;
28
+ /** Example: `new BoundedSequence([intervalA, intervalB])`. Creates a finite interval sequence from an explicit list. */
29
+ constructor(intervals) {
30
+ validateIntervals(intervals);
31
+ this.#intervals = Object.freeze(intervals.slice());
32
+ Object.freeze(this);
33
+ }
34
+ /** Example: `bounded.length`. Returns the number of intervals in the bounded sequence. */
35
+ get length() {
36
+ return this.#intervals.length;
37
+ }
38
+ /** Example: `bounded.at(0)`. Returns the interval at the supplied position, if present. */
39
+ at(index) {
40
+ return this.#intervals[index];
41
+ }
42
+ /** Example: `bounded.first()`. Returns the first interval, if present. */
43
+ first() {
44
+ return this.at(0);
45
+ }
46
+ /** Example: `bounded.last()`. Returns the last interval, if present. */
47
+ last() {
48
+ return this.#intervals.length === 0
49
+ ? undefined
50
+ : this.#intervals[this.#intervals.length - 1];
51
+ }
52
+ /** Example: `bounded.timeRange()`. Returns the finite temporal extent of this bounded sequence, if any. */
53
+ timeRange() {
54
+ const first = this.first();
55
+ const last = this.last();
56
+ if (!first || !last) {
57
+ return undefined;
58
+ }
59
+ return new TimeRange({ start: first.begin(), end: last.end() });
60
+ }
61
+ /** Example: `bounded.slice(0, 10)`. Returns a positional half-open slice of the bounded sequence. */
62
+ slice(beginIndex, endIndex) {
63
+ return new BoundedSequence(this.#intervals.slice(beginIndex, endIndex));
64
+ }
65
+ /** Example: `bounded.intervals()`. Returns the finite explicit intervals represented by this bounded sequence. */
66
+ intervals() {
67
+ return this.#intervals;
68
+ }
69
+ }
70
+ //# sourceMappingURL=BoundedSequence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoundedSequence.js","sourceRoot":"","sources":["../src/BoundedSequence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,SAAS,iBAAiB,CAAC,SAAkC;IAC3D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACzD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAE,CAAC;QAElC,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACrC,MAAM,IAAI,SAAS,CACjB,wDAAwD,CACzD,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,GAAG,CAAC,CAAE,CAAC;QACvC,IAAI,OAAO,CAAC,KAAK,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;YACvC,MAAM,IAAI,SAAS,CACjB,yDAAyD,CAC1D,CAAC;QACJ,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC;YACrC,MAAM,IAAI,SAAS,CAAC,6CAA6C,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IACjB,UAAU,CAA0B;IAE7C,wHAAwH;IACxH,YAAY,SAAkC;QAC5C,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,0FAA0F;IAC1F,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;IAChC,CAAC;IAED,2FAA2F;IAC3F,EAAE,CAAC,KAAa;QACd,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,0EAA0E;IAC1E,KAAK;QACH,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,wEAAwE;IACxE,IAAI;QACF,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;YACjC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,2GAA2G;IAC3G,SAAS;QACP,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,qGAAqG;IACrG,KAAK,CAAC,UAAmB,EAAE,QAAiB;QAC1C,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,kHAAkH;IAClH,SAAS;QACP,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,84 @@
1
+ import { Interval } from './Interval.js';
2
+ import { Time } from './Time.js';
3
+ import { TimeRange } from './TimeRange.js';
4
+ import type { EventKey, IntervalValue, TemporalLike } from './temporal.js';
5
+ type ScalarValue = number | string | boolean;
6
+ type CollapseData<D, Keys extends keyof D, Name extends string, R extends ScalarValue, Append extends boolean = false> = Append extends true ? Readonly<D & Record<Name, R>> : Readonly<Omit<D, Keys> & Record<Name, R>>;
7
+ type SelectData<D, Keys extends keyof D> = Readonly<Pick<D, Keys>>;
8
+ type RenameMap<D> = Partial<{
9
+ [K in keyof D & string]: string;
10
+ }>;
11
+ type RenameData<D, Mapping extends RenameMap<D>> = Readonly<{
12
+ [Name in keyof D & string as Name extends keyof Mapping ? Mapping[Name] extends string ? Mapping[Name] : Name : Name]: D[Name];
13
+ }>;
14
+ /**
15
+ * An immutable event made of a temporal key and typed payload data.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const event = new Event(
20
+ * new Time(new Date("2025-01-01T00:00:00.000Z")),
21
+ * { cpu: 0.42, host: "api-1" },
22
+ * );
23
+ *
24
+ * event.get("cpu"); // 0.42
25
+ * event.timeRange(); // TimeRange for the event extent
26
+ * ```
27
+ */
28
+ export declare class Event<K extends EventKey, D> {
29
+ #private;
30
+ /** Example: `new Event(new Time(Date.now()), { value: 1 })`. Creates an immutable event from a key and typed payload object. */
31
+ constructor(key: K, data: D);
32
+ /** Example: `event.key()`. Returns the event key. */
33
+ key(): K;
34
+ /** Example: `event.withKey(new Time(Date.now()))`. Returns a new event with the same payload and a different key. */
35
+ withKey<NextKey extends EventKey>(key: NextKey): Event<NextKey, D>;
36
+ /** Example: `event.type()`. Returns the underlying key kind. */
37
+ type(): K['kind'];
38
+ /** Example: `event.data()`. Returns the immutable event payload. */
39
+ data(): Readonly<D>;
40
+ /** Example: `event.get("value")`. Returns a single payload field by name. */
41
+ get<Field extends keyof D>(field: Field): Readonly<D>[Field];
42
+ /** Example: `event.set("value", 2)`. Returns a new event with one payload field replaced. */
43
+ set<Field extends keyof D>(field: Field, value: D[Field]): Event<K, D>;
44
+ /** Example: `event.merge({ host: "api-1" })`. Returns a new event with a shallow payload merge applied. */
45
+ merge<U extends object>(patch: U): Event<K, Readonly<D & U>>;
46
+ /** Example: `event.select("cpu", "healthy")`. Returns a new event containing only the selected payload fields. */
47
+ select<const Keys extends readonly (keyof D)[]>(...keys: Keys): Event<K, SelectData<D, Keys[number]>>;
48
+ /** Example: `event.rename({ cpu: "usage" })`. Returns a new event with payload fields renamed according to the supplied mapping. */
49
+ rename<const Mapping extends RenameMap<D>>(mapping: Mapping): Event<K, RenameData<D, Mapping>>;
50
+ /** Example: `event.collapse(["in", "out"], "avg", fn)`. Collapses selected payload fields into a single derived field using the supplied reducer. */
51
+ collapse<const Keys extends readonly (keyof D)[], Name extends string, R extends ScalarValue>(keys: Keys, output: Name, reducer: (values: Pick<D, Keys[number]>) => R): Event<K, CollapseData<D, Keys[number], Name, R>>;
52
+ collapse<const Keys extends readonly (keyof D)[], Name extends string, R extends ScalarValue>(keys: Keys, output: Name, reducer: (values: Pick<D, Keys[number]>) => R, options: {
53
+ append: true;
54
+ }): Event<K, CollapseData<D, Keys[number], Name, R, true>>;
55
+ /** Example: `event.timeRange()`. Returns the event extent as a `TimeRange`. */
56
+ timeRange(): TimeRange;
57
+ /** Example: `event.begin()`. Returns the inclusive event start in milliseconds since epoch. */
58
+ begin(): number;
59
+ /** Example: `event.end()`. Returns the inclusive event end in milliseconds since epoch. */
60
+ end(): number;
61
+ /** Example: `event.overlaps(range)`. Returns `true` when the event extent overlaps the supplied temporal value. */
62
+ overlaps(other: TemporalLike): boolean;
63
+ /** Example: `event.contains(time)`. Returns `true` when the event extent fully contains the supplied temporal value. */
64
+ contains(other: TemporalLike): boolean;
65
+ /** Example: `event.isBefore(range)`. Returns `true` when the event ends strictly before the supplied temporal value begins. */
66
+ isBefore(other: TemporalLike): boolean;
67
+ /** Example: `event.isAfter(range)`. Returns `true` when the event begins strictly after the supplied temporal value ends. */
68
+ isAfter(other: TemporalLike): boolean;
69
+ /** Example: `event.intersection(range)`. Returns the temporal intersection of the event extent and the supplied value, if any. */
70
+ intersection(other: TemporalLike): TimeRange | undefined;
71
+ /** Example: `event.trim(range)`. Returns a new event clipped to the supplied temporal value, if the event overlaps it. */
72
+ trim(other: TemporalLike): Event<K, D> | undefined;
73
+ /** Example: `event.asTime({ at: "center" })`. Converts the event key to a point-in-time key using the supplied anchor within the current extent. */
74
+ asTime(options?: {
75
+ at?: 'begin' | 'center' | 'end';
76
+ }): Event<Time, D>;
77
+ /** Example: `event.asTimeRange()`. Converts the event key to an unlabeled `TimeRange` covering the same extent. */
78
+ asTimeRange(): Event<TimeRange, D>;
79
+ /** Example: `event.asInterval("bucket-a")`. Converts the event key to a labeled `Interval` covering the same extent. */
80
+ asInterval(value: IntervalValue): Event<Interval, D>;
81
+ asInterval(getValue: (event: Event<K, D>) => IntervalValue): Event<Interval, D>;
82
+ }
83
+ export {};
84
+ //# sourceMappingURL=Event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Event.d.ts","sourceRoot":"","sources":["../src/Event.ts"],"names":[],"mappings":"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"}