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

promtext

v0.1.0

Published

parse the prometheus text exposition format and openmetrics, zero deps

Readme

promtext

parse the prometheus text exposition format and openmetrics. zero deps.

npm i promtext

why

the package npm reaches for, parse-prometheus-text-format, does over 100k downloads a week. its last release was august 2019 and its repository has had no commit since january 2023. there is no maintained alternative.

the format itself is frozen and small, so this is not a hard problem — it has just been left alone. a metrics endpoint is also remote input you often parse on a schedule, so it is worth having a parser that is still maintained.

use

import { parse, value, histogram } from 'promtext'

const families = parse(await (await fetch('http://app:9090/metrics')).text())

value(families, 'http_requests_total', { method: 'post', code: '200' })
// 1027

histogram(families, 'http_request_duration_seconds')
// { buckets: [{ le: 0.05, count: 24054 }, ...], sum: 53423, count: 144320 }

parse returns families in the order they appear:

interface MetricFamily {
  name: string
  type: 'counter' | 'gauge' | 'histogram' | 'summary'
      | 'gaugehistogram' | 'stateset' | 'info' | 'unknown'
  help?: string
  unit?: string          // openmetrics
  samples: Sample[]
}

interface Sample {
  name: string           // including any _bucket, _sum, _count suffix
  labels: Map<string, string>
  value: number
  timestamp?: number     // always milliseconds
  exemplar?: Exemplar    // openmetrics
}

_bucket, _sum, _count, _total and _created are rolled back into the family that declared them, so a histogram arrives as one thing rather than five.

what it handles

  • # HELP, # TYPE, and openmetrics # UNIT and # EOF
  • escapes in help text and in label values — \\, \n, \"
  • +Inf, -Inf, NaN, exponent notation, negative timestamps
  • label values holding braces, commas and quotes
  • openmetrics exemplars, and fractional second timestamps converted to ms
  • colons in metric names, for recording rules
  • missing trailing newline, \r\n, blank lines, stray comments

timestamps are normalised to milliseconds whichever dialect wrote them: prometheus writes whole milliseconds, openmetrics writes fractional seconds.

strictness

by default a malformed line throws a ParseError carrying its line number. a scrape that is partly readable is often still worth having, so:

parse(text, { strict: false })   // skip bad lines, keep the rest
parse(text, { maxSamples: 50000 })

notes

there is no regex in the parser. it is a character scanner, so a hostile or merely strange endpoint cannot make it backtrack, and a 50,000 series scrape stays linear.

label and metric names come from the endpoint, so they are held in Maps throughout — a metric named __proto__ is an ordinary name.

helpers

  • parse(text, opts?)MetricFamily[]
  • value(families, name, labels?)number | undefined
  • find(families, name, labels?)Sample | undefined
  • samples(families) → every sample, flattened
  • histogram(families, name){ buckets, sum, count }

correctness

45 tests. the reference document from the prometheus docs is parsed field by field, and the interop tests parse real output from python's prometheus_client — counters, gauges, histograms with cumulative buckets, summaries, info metrics, escaped help text, its openmetrics exposition, and a 2,000 series scrape.

license

MIT