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

@kree4js/commons-performance

v1.0.1

Published

Commons Performance, used inside kree4js

Readme

@kree4js/commons-performance

A high-performance, tag-based metrics and timing framework for Node.js and browsers. Provides synchronous low-overhead measurement (Timeline) and asynchronous periodic statistical analysis (Analyzer) for performance monitoring, SLA tracking, and business metrics collection.

Table of Contents

Installation

npm install @kree4js/commons-performance
import { Analyzer, Timeline } from '@kree4js/commons-performance'

Quick Start

import { Analyzer } from '@kree4js/commons-performance'

const analyzer = Analyzer.from('api-server')
  .addSpan('Request.Start', 'Request.End', 'route:users').as('usersLatency')
  .addCounter('Request.End', 'route:users').as('usersCount')
  .addReportPeriod('minute')
  .addReportPeriod('hour')
  .start()

analyzer.mark('Request.Start', 'route:users')
await handleRequest()
analyzer.mark('Request.End', 'route:users')

analyzer.on('report', (report) => {
  console.log(report.type, report.tags, report.metrics)
})
// Example report output:
// {
//   type: "Analyzer-minute",
//   tags: "route:users",
//   metrics: {
//     usersLatency: { count: 150, max: 45, min: 12, mean: 28.5, sum: 4275, lastTs: 1715423400000, lastValue: 31 },
//     usersCount: 150
//   }
// }

Core Concepts

| Concept | Description | |---------|-------------| | Marker | A timestamped event point. Has a name, optional value, and optional tag. | | Span | Calculates the time delta between two Markers. Produces count/max/min/mean/sum/lastTs/lastValue. | | Counter | Counts how many times a specific Marker name occurred. Produces a single number. | | Scalar | Tracks raw numeric values and computes statistics. Produces count/max/min/mean/sum. | | Rate | Computes the ratio of two Counters or Scalars. Produces a single number. | | Tag | A filter string in key:value format. Enables multi-dimensional metric breakdowns. | | Timeline | Synchronous measurement engine. Records Markers and computes Spans on demand, zero async overhead. | | Analyzer | Asynchronous periodic analysis engine. Buffers Markers, processes via Calculators, and emits report events. | | snapshot() | Returns a three-level keyed object: __main__ (L2), tag names (L1), full tag strings (L0). |

Metric Types

Span (Time Interval)

Calculates the time difference between a startMarker and an endMarker. Each Span instance tracks one specific metric (determined by Marker names and Tags).

measure() output fields:

| Field | Type | Description | |-------|------|-------------| | count | number | Number of measurements | | max | number | Maximum time delta (milliseconds) | | min | number | Minimum time delta (milliseconds) | | mean | number | Arithmetic mean (sum / count) | | sum | number | Cumulative time delta (milliseconds) | | lastTs | number\|undefined | Timestamp of the last measurement | | lastValue | number\|undefined | Time delta of the last measurement (milliseconds) |

const analyzer = Analyzer.from('example')
  .addSpan('Start', 'End').as('latency')
  .addSpan('DB.Start', 'DB.End', 'db:mysql').as('dbLatency')
  .start()

analyzer.mark('Start')
analyzer.mark('DB.Start', 'db:mysql')
// ... database query ...
analyzer.mark('DB.End', 'db:mysql')
analyzer.mark('End')

const r = analyzer.snapshot()
// r.__main__.latency → { count: 1, max: N, min: N, mean: N, sum: N, lastTs: N, lastValue: N }
// r['db:mysql'].dbLatency → { count: 1, max: N, min: N, mean: N, sum: N, lastTs: N, lastValue: N }

Note: The endMarker must be marked after the corresponding startMarker. If an endMarker appears before the startMarker, that measurement is ignored.

Counter

Counts how many times a marker name occurs. Each mark(name) call increments the counter by 1.

const analyzer = Analyzer.from('traffic')
  .addCounter('Request.End', 'route:users').as('usersCount')
  .addCounter('Error.Occurred', 'route:users').as('usersErrors')
  .addReportPeriod('minute')
  .start()

analyzer.mark('Request.End', 'route:users')
analyzer.mark('Request.End', 'route:users')
analyzer.mark('Error.Occurred', 'route:users')

const r = analyzer.snapshot()
// r['route:users'].usersCount → 2
// r['route:users'].usersErrors → 1

Counter.measure() returns a single number (the count), not an object.

Scalar

Tracks a sequence of raw numeric values and computes statistics. Unlike Span, Scalar does not compute time deltas — it tracks arbitrary values you pass in.

const analyzer = Analyzer.from('resources')
  .addScalar('Queue.Depth', 'queue:order').as('orderQueueDepth')
  .addScalar('Memory.Used', 'server:prod').as('memoryUsage')
  .addReportPeriod('minute')
  .start()

// The second argument is the numeric value
analyzer.mark('Queue.Depth', 10, 'queue:order')
analyzer.mark('Queue.Depth', 25, 'queue:order')
analyzer.mark('Queue.Depth', 15, 'queue:order')

const r = analyzer.snapshot()
// r['queue:order'].orderQueueDepth → { count: 3, max: 25, min: 10, mean: 16.67, sum: 50 }

Rate

Computes a ratio by referencing existing Counters or Scalars. Rate does not create new Marker listeners — it reads data from the referenced metrics.

addRate() has four call forms:

// 1. addRate(numeratorName, denominatorName)
//    Both numerator and denominator are un-tagged Counters/Scalars
analyzer.addRate('success', 'total')

// 2. addRate(numeratorName, denominatorName, rateTag)
//    Both numerator and denominator must have Counters/Scalars with rateTag
analyzer.addRate('success', 'total', 'route:users')

// 3. addRate(numeratorName, denominatorName, rateTag, metricTag)
//    Both numerator and denominator must have metricTag, Rate itself gets rateTag
analyzer.addRate('success', 'total', 'good', 'math:a')

// 4. addRate(numeratorName, denominatorName, rateTag, numeratorTag, denominatorTag)
//    Numerator has numeratorTag, denominator has denominatorTag, Rate gets rateTag
analyzer.addRate('success', 'total', 'good', 'math:a', 'special:b')

Important: The referenced Counter/Scalar must be added via addCounter() / addScalar() before calling addRate().

const analyzer = Analyzer.from('reliability')
  .addCounter('Request.Success', 'route:users')
  .addCounter('Request.End', 'route:users')
  .addRate('Request.Success', 'Request.End', 'route:users').as('successRate')
  .start()

analyzer.mark('Request.Success', 'route:users')
analyzer.mark('Request.Success', 'route:users')
analyzer.mark('Request.End', 'route:users')
analyzer.mark('Request.End', 'route:users')
analyzer.mark('Request.End', 'route:users')

const r = analyzer.snapshot()
// r['route:users'].successRate → 0.666...

Tag System

Syntax

Tags use key:value string format. key is the tag name, value is the tag value.

"route:users"      → name="route", value="users"
"env:prod"         → name="env", value="prod"
"math"             → name="math", value=undefined (name-only tag)

Compound Tags: Use ; as an AND separator to combine multiple sub-tags. Each sub-tag is separated by semicolon and processed together.

"call:123;dst:nodeB"  → [{ name: "call", value: "123" }, { name: "dst", value: "nodeB" }]

Tags serve two purposes:

  • Match: Determines which Markers are processed by which Spans/Counters/Scalars
  • Group: Determines how metric data aggregates into different levels

Three-Level Aggregation (L0 / L1 / L2)

snapshot() returns a keyed object with three aggregation levels:

| Level | Key Example | Meaning | |-------|------------|---------| | L0 | "route:users", "env:prod" | Metrics for the exact tag string | | L1 | "route", "env" | All L0 entries merged by tag name | | L2 | "__main__" | All data (including un-tagged) fully aggregated |

Example:

const analyzer = Analyzer.from('example')
  .addSpan('A', 'B')
  .addSpan('A', 'B', 'route:users')
  .addSpan('A', 'B', 'route:orders')
  .addCounter('req.count')

analyzer.mark('A')
analyzer.mark('B')
analyzer.mark('A', 'route:users')
analyzer.mark('B', 'route:users')
analyzer.mark('A', 'route:orders')
analyzer.mark('B', 'route:orders')
analyzer.mark('req.count')
analyzer.mark('req.count')

const r = analyzer.snapshot()
// r.__main__             → { "A-B": spanStats, "req.count": 2 }      // L2: full aggregation
// r.route            → { "A-B": mergedSpanStats }                 // L1: merged by tag name
// r["route:users"]   → { "A-B": spanStats }                       // L0: exact tag
// r["route:orders"]  → { "A-B": spanStats }                       // L0: exact tag

"main" is not a Tag — it is a reserved key representing the L2 full aggregation: including un-tagged data plus all L0/L1 data. Using __main__ as a tag name is prohibited and will throw an error.

Compound Tags and L1 Aggregation: When using compound tags, data is aggregated into multiple L1 groups simultaneously — one for each sub-tag name.

const analyzer = Analyzer.from('example')
  .addSpan('A', 'B', 'call:123;dst:nodeB').as('latency')
  .start()

analyzer.mark('A', 'call:123;dst:nodeB')
analyzer.mark('B', 'call:123;dst:nodeB')

const r = analyzer.snapshot()
// r["call:123;dst:nodeB"].latency → { count: 1, ... }  // L0: exact compound tag
// r.call.latency                  → { count: 1, ... }  // L1: aggregated by "call"
// r.dst.latency                   → { count: 1, ... }  // L1: aggregated by "dst"
// r.__main__.latency               → { count: 1, ... }  // L2: full aggregation

Tag Canonicalization: Compound tags are automatically canonicalized — sub-tags are sorted by name. This ensures call:123;dst:nodeB and dst:nodeB;call:123 are treated as identical.

analyzer.mark('A', 'dst:nodeB;call:123')  // Order doesn't matter
analyzer.mark('B', 'call:123;dst:nodeB')  // Same as above, canonicalized to same key

Tag Matching Rules

The tag specified when defining a Span determines which Markers trigger that Span:

// Only matches Markers of A and B that carry the "route:users" tag
analyzer.addSpan('A', 'B', 'route:users')

// Works: Markers with "route:users" tag
analyzer.mark('A', 'route:users')
analyzer.mark('B', 'route:users')

// Does NOT work: this Marker lacks "route:users" tag, will not be processed
analyzer.mark('A', 'route:orders')

Span tag source precedence:

  1. If spanTag is defined, use spanTag
  2. If startTag === endTag, use startTag
  3. If startTag and endTag differ, join as "startTag-endTag"
  4. If no tags at all, returns undefined

API Reference

Analyzer

Asynchronous periodic statistical analysis engine. Extends EventEmitter.

import { Analyzer } from '@kree4js/commons-performance'

const analyzer = Analyzer.from('app-name', { slideWindowSize: 1024 })

Constructor options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | slideWindowSize | number | 1024 | Marker buffer capacity; old data is automatically discarded when full |

Fluent API:

analyzer
  .addSpan(startMarker, endMarker, [startTag], [endTag])  // Define time delta measurement
  .as('name')                                              // Rename the last added metric
  .addCounter(marker, [tag])                               // Define a counter
  .addScalar(marker, [tag])                                // Define a scalar
  .addRate(num, denom, [rateTag], [numTag], [denomTag])   // Define a rate
  .addReportPeriod('minute')                               // Enable minute reports
  .addReportPeriod('hour')                                 // Enable hour reports
  .addReportPeriod('day')                                  // Enable day reports
  .start()                                                 // Start all Calculators

mark(name, valueOrTag, ...tags):

mark() is the core method with multiple overload forms:

// 1. Marker name only (for Span and Counter)
analyzer.mark('Request.Start')

// 2. Marker + single tag
analyzer.mark('Request.Start', 'route:users')

// 3. Marker + multiple tags (marks multiple tags simultaneously)
analyzer.mark('Request.Start', 'route:users', 'env:prod')

// 4. Marker + numeric value (for Scalar)
analyzer.mark('Queue.Depth', 42)

// 5. Marker + value + single tag
analyzer.mark('Queue.Depth', 42, 'queue:order')

// 6. Marker + value + multiple tags
analyzer.mark('Queue.Depth', 42, 'queue:order', 'env:prod')

Multi-tag semantics: When multiple tags are passed to mark(), one Marker is created for each tag. For example, mark('A', 'x:1', 'y:2') creates two Markers: one tagged "x:1" and another tagged "y:2". This means a single mark call can feed multiple dimensional Spans/Counters simultaneously.

const analyzer = Analyzer.from('example')
  .addSpan('A', 'B', 'x:1')
  .addSpan('A', 'B', 'y:2')
  .start()

// One mark call satisfies both x:1 and y:2 Spans
analyzer.mark('A', 'x:1', 'y:2')
analyzer.mark('B', 'x:1', 'y:2')

const r = analyzer.snapshot()
// Both r['x:1']['A-B'] and r['y:2']['A-B'] have data

snapshot(deadline?):

Returns a three-level keyed object snapshot of all current metrics. Automatically flushes all unprocessed Markers.

const r = analyzer.snapshot()
// r = {
//   "route:users": { "A-B": spanStats, "req.count": 5 },
//   "route:orders": { "A-B": spanStats, "req.count": 3 },
//   route: { "A-B": mergedSpanStats, "req.count": 8 },
//   __main__: { "A-B": mergedSpanStats, "req.count": 8 }
// }

An optional deadline parameter specifies the timestamp cutoff (defaults to Date.now() + 1). Only Markers before the deadline are flushed.

const r = analyzer.snapshot(Date.now() + 60_000) // Only flush data older than 60 seconds

start() / stop() / reset():

analyzer.start()   // Start all Calculators, begin emitting report events
analyzer.stop()    // Stop all Calculators
analyzer.reset()   // Reset all metrics (Span/Counter/Scalar), does not clear the Marker queue

Events:

analyzer.on('report', (report) => {
  // Example report output:
  // {
  //   type: "Analyzer-minute",
  //   tags: "route:users",
  //   metrics: {
  //     usersLatency: { count: 150, max: 45, min: 12, mean: 28.5, sum: 4275, lastTs: 1715423400000, lastValue: 31 },
  //     usersCount: 150
  //   }
  // }
})

Timeline

Synchronous low-overhead measurement engine. Records Markers and computes Spans on demand with zero async overhead, suitable for hot paths.

import { Timeline } from '@kree4js/commons-performance'

const timeline = Timeline.from('http-request')
  .addSpan('Request.Start', 'Request.End').as('totalLatency')
  .addSpan('DB.Start', 'DB.End').as('dbLatency')

timeline.mark('Request.Start')
// ... process ...
timeline.mark('DB.Start')
// ... database query ...
timeline.mark('DB.End')
timeline.mark('Request.End')

const total = timeline.measureNamed('totalLatency')
// { count: 1, max: N, min: N, mean: N, sum: N, lastTs: N, lastValue: N }

const db = timeline.measureNamed('dbLatency')
// { count: 1, max: N, min: N, mean: N, sum: N, lastTs: N, lastValue: N }

timeline.reset() // Reset all Spans for the next measurement cycle

Timeline with Tags:

const timeline = Timeline.from('multi-route')
  .addSpan('Request.Start', 'Request.End', 'route:users').as('usersLatency')
  .addSpan('Request.Start', 'Request.End', 'route:orders').as('ordersLatency')

timeline.mark('Request.Start', 'route:users')
timeline.mark('Request.End', 'route:users')

timeline.mark('Request.Start', 'route:orders')
timeline.mark('Request.End', 'route:orders')

timeline.measureNamed('usersLatency', 'route:users')    // { ... }
timeline.measureNamed('ordersLatency', 'route:orders')  // { ... }

Measure by Marker names (no alias needed):

timeline.measureMarker('Request.Start', 'Request.End', 'route:users')
// Equivalent to measureNamed('usersLatency', 'route:users') (when .as() is used)

.as(name, tag?):

timeline
  .addSpan('A', 'B')
  .as('mySpan', 'custom:tag')  // Rename + optional Span tag

Span

A metric type that calculates the time difference between two Markers.

import { Measure } from '@kree4js/commons-performance'
// Measure is the public export alias for Span

// Internal usage:
span.mark(marker)              // Process a single Marker
span.mark([marker1, marker2])  // Process Markers in batch
span.measure()                 // Get SpanMeasureResult
span.reset()                   // Reset the Span

Counter

A metric type that counts Marker occurrences.

import { Counter } from '@kree4js/commons-performance'

const counter = new Counter('Request.End', 'myName', 'route:users')
counter.update(3)         // +3
counter.update()           // +1
counter.measure()          // → 4 (single number)
counter.reset()            // → 0

Scalar

A metric type that tracks a sequence of raw numeric values.

import { Scalar } from '@kree4js/commons-performance'

const scalar = new Scalar('Queue.Depth', 'queue:order', 'myScalar')
scalar.update(10)
scalar.update(20)
scalar.update(30)
scalar.measure()           // → { count: 3, max: 30, min: 10, mean: 20, sum: 60 }
scalar.reset()

Rate

A metric type that computes the ratio of two metrics.

import { Rate, Counter } from '@kree4js/commons-performance'

const success = new Counter('success')
const total = new Counter('total')
const rate = new Rate(success, total, 'route:users')
rate.measure()             // → 0 (total is 0)
success.update(3)
total.update(5)
rate.measure()             // → 0.6
rate.reset()               // Resets both numerator and denominator