pond-ts 0.3.0 → 0.4.1
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/dist/LiveAggregation.d.ts +54 -0
- package/dist/LiveAggregation.d.ts.map +1 -0
- package/dist/LiveAggregation.js +240 -0
- package/dist/LiveAggregation.js.map +1 -0
- package/dist/LiveRollingAggregation.d.ts +43 -0
- package/dist/LiveRollingAggregation.d.ts.map +1 -0
- package/dist/LiveRollingAggregation.js +178 -0
- package/dist/LiveRollingAggregation.js.map +1 -0
- package/dist/LiveSeries.d.ts +63 -0
- package/dist/LiveSeries.d.ts.map +1 -0
- package/dist/LiveSeries.js +340 -0
- package/dist/LiveSeries.js.map +1 -0
- package/dist/LiveView.d.ts +59 -0
- package/dist/LiveView.d.ts.map +1 -0
- package/dist/LiveView.js +337 -0
- package/dist/LiveView.js.map +1 -0
- package/dist/Sequence.d.ts +2 -2
- package/dist/Sequence.d.ts.map +1 -1
- package/dist/Sequence.js +4 -57
- package/dist/Sequence.js.map +1 -1
- package/dist/TimeSeries.d.ts +1 -1
- package/dist/TimeSeries.d.ts.map +1 -1
- package/dist/TimeSeries.js +3 -26
- package/dist/TimeSeries.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/duration.d.ts +3 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +25 -0
- package/dist/utils/duration.js.map +1 -0
- package/package.json +5 -2
- package/LICENSE +0 -21
- package/README.md +0 -429
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.
|