npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pond-ts

v0.14.0

Published

TypeScript-first time series primitives

Readme

pond-ts - A modern typescript timeseries library

TypeScript-first time series primitives built around typed events, typed schemas, and explicit temporal keys.

pond-ts is the successor to pondjs, rewritten from scratch in TypeScript with a focus on performance, type safety, and composable live streaming.

  • typed TimeSeries construction and immutable Event objects
  • Time, TimeRange, and Interval temporal keys
  • alignment, aggregation, joins, rolling windows, and smoothing
  • LiveSeries with push-based ingestion, retention policies, and subscriptions
  • LiveView, LiveAggregation, and LiveRollingAggregation for streaming composition
  • timezone-aware calendar sequences and ingest helpers

The package is intended to work in modern Node and frontend projects.

Performance

pond-ts is 7.6x faster than pondjs on average across all comparable operations, with no regressions. The advantage grows with data size.

| Category | Speedup (N=16k) | Notes | | ----------------- | --------------- | --------------------------------------------- | | Aggregation | 25–32x | O(N+B) bucketing vs O(N×B) Pipeline | | Alignment | 32x | Forward cursor vs repeated binary search | | Rate/diff | 18x | Direct array walk vs Pipeline materialization | | Fill | 10–11x | Single-pass vs Pipeline per strategy | | Transforms | 3–16x | Pre-validated constructor skips re-validation | | Construction | 7x | Plain objects vs ImmutableJS wrapping | | Statistics | 7–9x | Direct computation vs ImmutableJS iteration | | Serialization | 4x | Simpler internal representation | | Event access | 23x | Array indexing vs ImmutableJS get() |

See the full benchmark results for detailed numbers. Run locally: npm run build && node packages/core/bench/vs-pondjs.cjs

Install

npm install pond-ts

Build

The repo toolchain should work on Node 18, but use nvm to verify against newer stable Node releases when needed.

npm run build

Format

npm run format

Test

npm test

Verify

npm run verify

Docs site

The documentation website lives in website/ and is built with Docusaurus.

Examples

  • pond-ts-dashboard — a working React dashboard that streams synthetic per-host CPU/request metrics, computes per-host rolling baselines, flags anomalies against ±σ bands, and renders everything as live line and bar charts (~600 lines of TypeScript). The repo's README is also published as a docs- site guide: Building a dashboard.

License

MIT

Core model

The key types are:

  • Time: a point in time
  • TimeRange: an unlabeled interval
  • Interval: a labeled interval

An Event is a key plus typed data.

A TimeSeries is an ordered immutable collection of events sharing one schema.

Quick start

import { TimeSeries } from 'pond-ts';

const schema = [
  { name: 'time', kind: 'time' },
  { name: 'cpu', kind: 'number' },
  { name: 'host', kind: 'string' },
  { name: 'healthy', kind: 'boolean' },
] as const;

const series = new TimeSeries({
  name: 'cpu',
  schema,
  rows: [
    [new Date('2025-01-01T00:00:00.000Z'), 0.42, 'api-1', true],
    [new Date('2025-01-01T00:01:00.000Z'), 0.51, 'api-2', true],
  ],
});

const event = series.at(1);
if (!event) {
  throw new Error('missing event');
}

event.key();
event.timeRange();
event.get('cpu');
event.data().host;

Worked example

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.

import { Sequence, TimeSeries } from 'pond-ts';

const schema = [
  { name: 'time', kind: 'time' },
  { name: 'cpu', kind: 'number' },
  { name: 'requests', kind: 'number' },
  { name: 'host', kind: 'string' },
] as const;

const cpu = TimeSeries.fromJSON({
  name: 'cpu',
  schema,
  rows: [
    ['2025-01-01T00:00:00Z', 0.31, 120, 'api-1'],
    ['2025-01-01T00:01:00Z', 0.44, 135, 'api-1'],
    ['2025-01-01T00:02:00Z', 0.52, 141, 'api-1'],
    ['2025-01-01T00:03:00Z', 0.48, 128, 'api-1'],
    ['2025-01-01T00:04:00Z', 0.63, 166, 'api-1'],
  ],
});

const perMinute = cpu.align(Sequence.every('1m'), {
  method: 'hold',
});

const fiveMinute = cpu.aggregate(Sequence.every('5m'), {
  cpu: 'avg',
  requests: 'sum',
  host: 'last',
});

const rolling = cpu.rolling('3m', {
  cpu: 'avg',
  requests: 'sum',
});

const smoothed = cpu.smooth('cpu', 'ema', {
  alpha: 0.35,
  output: 'cpuTrend',
});

console.log(perMinute.first()?.key().asString());
console.log(fiveMinute.first()?.data());
console.log(rolling.last()?.data());
console.log(smoothed.last()?.get('cpuTrend'));

From one typed source series, you can derive:

  • aligned interval views for dashboards and joins
  • bucketed aggregates for reporting
  • rolling metrics for short-term behavior
  • smoothed trends for visualization or alerting

All of those remain fully typed and immutable.

JSON ingest

Use TimeSeries.fromJSON(...) for external data and ambiguous local timestamps.

import { TimeSeries } from 'pond-ts';

const schema = [
  { name: 'time', kind: 'time' },
  { name: 'value', kind: 'number' },
  { name: 'status', kind: 'string', required: false },
] as const;

const series = TimeSeries.fromJSON({
  name: 'cpu',
  schema,
  rows: [
    ['2025-01-01T09:00', 0.42, 'ok'],
    ['2025-01-01T10:00', 0.51, null],
  ],
  parse: { timeZone: 'Europe/Madrid' },
});

Export back into the same JSON-friendly shape:

const rows = series.toJSON();
const objectRows = series.toJSON({ rowFormat: 'object' });

For normalized in-memory export helpers:

const normalizedRows = series.toRows();
const normalizedObjects = series.toObjects();

Event and series transforms

Event-level transforms:

  • get(...)
  • set(...)
  • merge(...)
  • select(...)
  • rename(...)
  • collapse(...)
  • asTime(...)
  • asTimeRange()
  • asInterval(...)

Series-level transforms:

  • map(...)
  • select(...)
  • rename(...)
  • collapse(...)
  • asTime(...)
  • asTimeRange()
  • asInterval(...)

Example:

const renamed = series.rename({ cpu: 'usage' });
const selected = renamed.select('usage', 'healthy');

Temporal selection

TimeSeries includes both positional and temporal selection methods:

  • slice(...)
  • filter(...)
  • find(...)
  • first()
  • last()
  • before(...)
  • after(...)
  • within(...)
  • overlapping(...)
  • containedBy(...)
  • trim(...)
  • includesKey(...)
  • bisect(...)
  • atOrBefore(...)
  • atOrAfter(...)

Vocabulary is intentionally distinct:

  • within(...): fully contained
  • overlapping(...): intersects without clipping
  • trim(...): intersects and clips event extents

Sequences

Use Sequence for unbounded grids and BoundedSequence for explicit finite interval lists.

Fixed-step sequences:

import { Sequence } from 'pond-ts';

const minuteGrid = Sequence.every('1m');
const hourlyGrid = Sequence.hourly();

Calendar-aware sequences:

const localDays = Sequence.calendar('day', {
  timeZone: 'America/New_York',
});

Explicit bounded sequences:

import { BoundedSequence, Interval } from 'pond-ts';

const buckets = new BoundedSequence([
  new Interval({ value: 'a', start: 0, end: 10 }),
  new Interval({ value: 'b', start: 20, end: 30 }),
]);

Alignment and aggregation

Align onto a sequence:

const aligned = series.align(Sequence.every('1m'), {
  method: 'hold',
});

Aggregate into buckets:

const aggregated = series.aggregate(Sequence.every('5m'), {
  cpu: 'avg',
  host: 'last',
});

Built-in aggregations:

  • sum
  • avg
  • min
  • max
  • count
  • first
  • last

Joins

Join two aligned or bucketed series:

const joined = left.join(right, { type: 'outer' });

Supported join types:

  • outer
  • left
  • right
  • inner

Join many:

const wide = TimeSeries.joinMany([cpu, memory, errors], {
  type: 'outer',
});

Conflict handling:

  • default: onConflict: "error"
  • optional prefixing:
const joined = left.join(right, {
  onConflict: 'prefix',
  prefixes: ['left', 'right'] as const,
});

Rolling windows

Event-driven rolling:

const rolled = series.rolling('5m', {
  cpu: 'avg',
  host: 'last',
});

Sequence-driven rolling:

const rolledOnGrid = series.rolling(Sequence.every('1m'), '5m', { cpu: 'avg' });

Rolling alignment options:

  • trailing
  • centered
  • leading

Smoothing

Smoothing targets one numeric column at a time.

Replace the source column:

const smoothed = series.smooth('cpu', 'ema', { alpha: 0.2 });

Append the smoothed output:

const smoothed = series.smooth('cpu', 'movingAverage', {
  window: '5m',
  alignment: 'centered',
  output: 'cpuAvg',
});

Supported smoothing methods:

  • ema
  • movingAverage
  • loess

For interval-like keys, smoothing uses the key midpoint as the internal anchor.

Calendar-aware helpers

Primitive helpers normalize local calendar inputs into absolute time:

import { Interval, Time, TimeRange } from 'pond-ts';

const time = Time.parse('2025-01-01T09:00', { timeZone: 'Europe/Madrid' });
const day = TimeRange.fromDate('2025-01-01', { timeZone: 'UTC' });
const month = Interval.fromCalendar('month', '2025-01', {
  timeZone: 'UTC',
  value: '2025-01',
});

Current scope

The library provides both batch analytics (TimeSeries) and live streaming (LiveSeries, LiveView, LiveAggregation, LiveRollingAggregation).

  • type-safe construction with schema types that flow through every operation
  • temporal modeling with Time, TimeRange, and Interval keys
  • composable batch analytics (aggregate, align, join, rolling, smooth, fill, diff, rate, groupBy)
  • push-based live ingestion with retention policies and subscriptions
  • live composition: filter, map, select, window, diff, rate, fill, cumulative, aggregate, rolling

License

MIT