pond-ts 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md DELETED
@@ -1,429 +0,0 @@
1
- # pond-ts - A modern typescript timeseries library
2
-
3
- TypeScript-first time series primitives built around typed events, typed schemas, and explicit temporal keys.
4
-
5
- The library is currently focused on non-streaming analytics:
6
-
7
- - typed `TimeSeries` construction
8
- - `Time`, `TimeRange`, and `Interval` keys
9
- - immutable `Event` objects
10
- - alignment, aggregation, joins, rolling windows, and smoothing
11
- - timezone-aware calendar sequences and ingest helpers
12
-
13
- The package is intended to work in modern Node and frontend projects.
14
-
15
- ## Install
16
-
17
- ```sh
18
- npm install pond-ts
19
- ```
20
-
21
- ## Build
22
-
23
- The repo toolchain should work on Node 18, but use `nvm` to verify against newer stable Node releases when needed.
24
-
25
- ```sh
26
- npm run build
27
- ```
28
-
29
- ## Format
30
-
31
- ```sh
32
- npm run format
33
- ```
34
-
35
- ## Test
36
-
37
- ```sh
38
- npm test
39
- ```
40
-
41
- ## Verify
42
-
43
- ```sh
44
- npm run verify
45
- ```
46
-
47
- ## Docs site
48
-
49
- The documentation website lives in [`website/`](./website) and is built with Docusaurus.
50
-
51
- ## License
52
-
53
- MIT
54
-
55
- ## Core model
56
-
57
- The key types are:
58
-
59
- - `Time`: a point in time
60
- - `TimeRange`: an unlabeled interval
61
- - `Interval`: a labeled interval
62
-
63
- An `Event` is a key plus typed data.
64
-
65
- A `TimeSeries` is an ordered immutable collection of events sharing one schema.
66
-
67
- ## Quick start
68
-
69
- ```ts
70
- import { TimeSeries } from 'pond-ts';
71
-
72
- const schema = [
73
- { name: 'time', kind: 'time' },
74
- { name: 'cpu', kind: 'number' },
75
- { name: 'host', kind: 'string' },
76
- { name: 'healthy', kind: 'boolean' },
77
- ] as const;
78
-
79
- const series = new TimeSeries({
80
- name: 'cpu',
81
- schema,
82
- rows: [
83
- [new Date('2025-01-01T00:00:00.000Z'), 0.42, 'api-1', true],
84
- [new Date('2025-01-01T00:01:00.000Z'), 0.51, 'api-2', true],
85
- ],
86
- });
87
-
88
- const event = series.at(1);
89
- if (!event) {
90
- throw new Error('missing event');
91
- }
92
-
93
- event.key();
94
- event.timeRange();
95
- event.get('cpu');
96
- event.data().host;
97
- ```
98
-
99
- ## Worked example
100
-
101
- This is the kind of flow `pond-ts` is built for: start with typed events, then derive aligned, aggregated, and smoothed analytical views without mutating the original series.
102
-
103
- ```ts
104
- import { Sequence, TimeSeries } from 'pond-ts';
105
-
106
- const schema = [
107
- { name: 'time', kind: 'time' },
108
- { name: 'cpu', kind: 'number' },
109
- { name: 'requests', kind: 'number' },
110
- { name: 'host', kind: 'string' },
111
- ] as const;
112
-
113
- const cpu = TimeSeries.fromJSON({
114
- name: 'cpu',
115
- schema,
116
- rows: [
117
- ['2025-01-01T00:00:00Z', 0.31, 120, 'api-1'],
118
- ['2025-01-01T00:01:00Z', 0.44, 135, 'api-1'],
119
- ['2025-01-01T00:02:00Z', 0.52, 141, 'api-1'],
120
- ['2025-01-01T00:03:00Z', 0.48, 128, 'api-1'],
121
- ['2025-01-01T00:04:00Z', 0.63, 166, 'api-1'],
122
- ],
123
- });
124
-
125
- const perMinute = cpu.align(Sequence.every('1m'), {
126
- method: 'hold',
127
- });
128
-
129
- const fiveMinute = cpu.aggregate(Sequence.every('5m'), {
130
- cpu: 'avg',
131
- requests: 'sum',
132
- host: 'last',
133
- });
134
-
135
- const rolling = cpu.rolling('3m', {
136
- cpu: 'avg',
137
- requests: 'sum',
138
- });
139
-
140
- const smoothed = cpu.smooth('cpu', 'ema', {
141
- alpha: 0.35,
142
- output: 'cpuTrend',
143
- });
144
-
145
- console.log(perMinute.first()?.key().asString());
146
- console.log(fiveMinute.first()?.data());
147
- console.log(rolling.last()?.data());
148
- console.log(smoothed.last()?.get('cpuTrend'));
149
- ```
150
-
151
- From one typed source series, you can derive:
152
-
153
- - aligned interval views for dashboards and joins
154
- - bucketed aggregates for reporting
155
- - rolling metrics for short-term behavior
156
- - smoothed trends for visualization or alerting
157
-
158
- All of those remain fully typed and immutable.
159
-
160
- ## JSON ingest
161
-
162
- Use `TimeSeries.fromJSON(...)` for external data and ambiguous local timestamps.
163
-
164
- ```ts
165
- import { TimeSeries } from 'pond-ts';
166
-
167
- const schema = [
168
- { name: 'time', kind: 'time' },
169
- { name: 'value', kind: 'number' },
170
- { name: 'status', kind: 'string', required: false },
171
- ] as const;
172
-
173
- const series = TimeSeries.fromJSON({
174
- name: 'cpu',
175
- schema,
176
- rows: [
177
- ['2025-01-01T09:00', 0.42, 'ok'],
178
- ['2025-01-01T10:00', 0.51, null],
179
- ],
180
- parse: { timeZone: 'Europe/Madrid' },
181
- });
182
- ```
183
-
184
- Export back into the same JSON-friendly shape:
185
-
186
- ```ts
187
- const rows = series.toJSON();
188
- const objectRows = series.toJSON({ rowFormat: 'object' });
189
- ```
190
-
191
- For normalized in-memory export helpers:
192
-
193
- ```ts
194
- const normalizedRows = series.toRows();
195
- const normalizedObjects = series.toObjects();
196
- ```
197
-
198
- ## Event and series transforms
199
-
200
- Event-level transforms:
201
-
202
- - `get(...)`
203
- - `set(...)`
204
- - `merge(...)`
205
- - `select(...)`
206
- - `rename(...)`
207
- - `collapse(...)`
208
- - `asTime(...)`
209
- - `asTimeRange()`
210
- - `asInterval(...)`
211
-
212
- Series-level transforms:
213
-
214
- - `map(...)`
215
- - `select(...)`
216
- - `rename(...)`
217
- - `collapse(...)`
218
- - `asTime(...)`
219
- - `asTimeRange()`
220
- - `asInterval(...)`
221
-
222
- Example:
223
-
224
- ```ts
225
- const renamed = series.rename({ cpu: 'usage' });
226
- const selected = renamed.select('usage', 'healthy');
227
- ```
228
-
229
- ## Temporal selection
230
-
231
- `TimeSeries` includes both positional and temporal selection methods:
232
-
233
- - `slice(...)`
234
- - `filter(...)`
235
- - `find(...)`
236
- - `first()`
237
- - `last()`
238
- - `before(...)`
239
- - `after(...)`
240
- - `within(...)`
241
- - `overlapping(...)`
242
- - `containedBy(...)`
243
- - `trim(...)`
244
- - `includesKey(...)`
245
- - `bisect(...)`
246
- - `atOrBefore(...)`
247
- - `atOrAfter(...)`
248
-
249
- Vocabulary is intentionally distinct:
250
-
251
- - `within(...)`: fully contained
252
- - `overlapping(...)`: intersects without clipping
253
- - `trim(...)`: intersects and clips event extents
254
-
255
- ## Sequences
256
-
257
- Use `Sequence` for unbounded grids and `BoundedSequence` for explicit finite interval lists.
258
-
259
- Fixed-step sequences:
260
-
261
- ```ts
262
- import { Sequence } from 'pond-ts';
263
-
264
- const minuteGrid = Sequence.every('1m');
265
- const hourlyGrid = Sequence.hourly();
266
- ```
267
-
268
- Calendar-aware sequences:
269
-
270
- ```ts
271
- const localDays = Sequence.calendar('day', {
272
- timeZone: 'America/New_York',
273
- });
274
- ```
275
-
276
- Explicit bounded sequences:
277
-
278
- ```ts
279
- import { BoundedSequence, Interval } from 'pond-ts';
280
-
281
- const buckets = new BoundedSequence([
282
- new Interval({ value: 'a', start: 0, end: 10 }),
283
- new Interval({ value: 'b', start: 20, end: 30 }),
284
- ]);
285
- ```
286
-
287
- ## Alignment and aggregation
288
-
289
- Align onto a sequence:
290
-
291
- ```ts
292
- const aligned = series.align(Sequence.every('1m'), {
293
- method: 'hold',
294
- });
295
- ```
296
-
297
- Aggregate into buckets:
298
-
299
- ```ts
300
- const aggregated = series.aggregate(Sequence.every('5m'), {
301
- cpu: 'avg',
302
- host: 'last',
303
- });
304
- ```
305
-
306
- Built-in aggregations:
307
-
308
- - `sum`
309
- - `avg`
310
- - `min`
311
- - `max`
312
- - `count`
313
- - `first`
314
- - `last`
315
-
316
- ## Joins
317
-
318
- Join two aligned or bucketed series:
319
-
320
- ```ts
321
- const joined = left.join(right, { type: 'outer' });
322
- ```
323
-
324
- Supported join types:
325
-
326
- - `outer`
327
- - `left`
328
- - `right`
329
- - `inner`
330
-
331
- Join many:
332
-
333
- ```ts
334
- const wide = TimeSeries.joinMany([cpu, memory, errors], {
335
- type: 'outer',
336
- });
337
- ```
338
-
339
- Conflict handling:
340
-
341
- - default: `onConflict: "error"`
342
- - optional prefixing:
343
-
344
- ```ts
345
- const joined = left.join(right, {
346
- onConflict: 'prefix',
347
- prefixes: ['left', 'right'] as const,
348
- });
349
- ```
350
-
351
- ## Rolling windows
352
-
353
- Event-driven rolling:
354
-
355
- ```ts
356
- const rolled = series.rolling('5m', {
357
- cpu: 'avg',
358
- host: 'last',
359
- });
360
- ```
361
-
362
- Sequence-driven rolling:
363
-
364
- ```ts
365
- const rolledOnGrid = series.rolling(Sequence.every('1m'), '5m', { cpu: 'avg' });
366
- ```
367
-
368
- Rolling alignment options:
369
-
370
- - `trailing`
371
- - `centered`
372
- - `leading`
373
-
374
- ## Smoothing
375
-
376
- Smoothing targets one numeric column at a time.
377
-
378
- Replace the source column:
379
-
380
- ```ts
381
- const smoothed = series.smooth('cpu', 'ema', { alpha: 0.2 });
382
- ```
383
-
384
- Append the smoothed output:
385
-
386
- ```ts
387
- const smoothed = series.smooth('cpu', 'movingAverage', {
388
- window: '5m',
389
- alignment: 'centered',
390
- output: 'cpuAvg',
391
- });
392
- ```
393
-
394
- Supported smoothing methods:
395
-
396
- - `ema`
397
- - `movingAverage`
398
- - `loess`
399
-
400
- For interval-like keys, smoothing uses the key midpoint as the internal anchor.
401
-
402
- ## Calendar-aware helpers
403
-
404
- Primitive helpers normalize local calendar inputs into absolute time:
405
-
406
- ```ts
407
- import { Interval, Time, TimeRange } from 'pond-ts';
408
-
409
- const time = Time.parse('2025-01-01T09:00', { timeZone: 'Europe/Madrid' });
410
- const day = TimeRange.fromDate('2025-01-01', { timeZone: 'UTC' });
411
- const month = Interval.fromCalendar('month', '2025-01', {
412
- timeZone: 'UTC',
413
- value: '2025-01',
414
- });
415
- ```
416
-
417
- ## Current scope
418
-
419
- This package is batch-oriented and immutable.
420
-
421
- It does not yet provide a dedicated live/streaming ingestion layer. The current focus is:
422
-
423
- - type-safe construction
424
- - temporal modeling
425
- - composable non-streaming analytics
426
-
427
- ## License
428
-
429
- No license file is included yet.