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

@entelekheia/ref-id

v0.7.1

Published

The ref: identifier scheme — a specification published as data with conformance vectors, and the package that consumes it.

Readme

@entelekheia/ref-id

Parse, serialise, digest and validate ref: identifiers — against a specification shipped as data. The package embeds spec/ref-id.json and is held to it by that file's conformance vectors.

ref:[<version>:]<type>:<locator>[;<qualifier>=<value>]*[#<declared-name-path>[;<refinement>=<value>]*]
import { parse } from "@entelekheia/ref-id"

parse("ref:pkg:npm/@acme/[email protected]#Observation")
// => {
//   status: "ok",
//   type: "pkg",
//   locator: "npm/@acme/[email protected]",
//   fragment: { path: "Observation", refinements: [] },
//   delegated: "pkg:npm/@acme/[email protected]",
//   canonical: "pkg:npm/%40acme/[email protected]",
//   ...
// }

Why

Naming a thing by its file path breaks on the first move; naming it by a content hash breaks on the first edit. ref: names it by what its format declared — a Package URL when a manifest proves the name, a declared corpus name when nothing does — and keeps state (;state=), instrument (;by=) and population (;over=) as qualifiers rather than folding them into the name. One regular expression decomposes an identifier; each captured part is handed to the validator that already owns that format.

Install

npm install @entelekheia/ref-id

Usage

The entry points are parse, serialise, build, canonicalise, loadSpec, digest, validateEnvelope, sameIdentifier, canonicalIdentifier, samePackage, covers, relate and verdict; their contracts are the vectors in spec/ref-id.json (parse, roundtrip, build, digest, envelope, canonical, comparison, relate, verdict). An unknown locator type parses and degrades to uncovered; it never throws.

Three questions get three answers, and they are not interchangeable:

import { covers, sameIdentifier, samePackage } from "@entelekheia/ref-id"

sameIdentifier("ref:pkg:npm/[email protected]", "ref:pkg:npm/[email protected]") // false — two different releases, byte for byte
sameIdentifier("ref:pkg:npm/x;a=1;b=2", "ref:pkg:npm/x;b=2;a=1") // true  — qualifier order never distinguishes
samePackage("ref:pkg:npm/[email protected]", "ref:pkg:npm/[email protected]") // true  — one released thing, two versions
covers("ref:pkg:npm/x", "ref:pkg:npm/[email protected]") // true  — the general covers the specific
covers("ref:pkg:npm/[email protected]", "ref:pkg:npm/x") // false — and never the reverse

sameIdentifier is what a digest and an envelope are built on, so it stays strict. samePackage is symmetric and ignores the locator's version, and only where the type declares it carries one — a mailbox's @ and a served model's quantisation key are not versions. covers is asymmetric: what the first identifier leaves undeclared, the second may declare freely; what the first declares, the second must declare identically — except a qualifier whose value is a nested ref: identifier on both sides, which is compared with covers on the decoded pair instead, one level deep, so an unversioned instrument covers every release of it. Any other qualifier value — a digest, a timestamp, plain text, or a nested identifier on one side only — still must be identical, and so must a nested pair covers refuses (a scheme version this package does not implement). That is what makes a partial identifier a query over a store keyed by identifier.

relate(a, b) answers a different question: not whether one covers the other, but how the two relate in each dimension — type, version, locator stem, locator version, the declared name and its refinements, and each qualifier — as "equal" | "covers" | "coveredBy" | "differ", or null for a pair it refuses. covers, samePackage (and their mirror) are reductions of this same result, never a second computation that could disagree with it.

verdict(a, b) says what a difference means once some qualifiers are hints rather than identity: the location qualifiers path=, origin= and corpus= narrow where to search without deciding what is named. It returns an identity axis ("same" | "covers" | "coveredBy" | "distinct" | "undetermined"), a content axis read from state= ("same" | "different" | "unknown") and decidedBy, or null for a pair relate refuses. Two clones of one repository at two local paths come out undetermined, two origin= values distinct, and one content under two names distinct with content: "same".

An identifier is a reference, not a copy of the record. Read it as search(in: <type>:<locator>, for: #<declared name>, with: ;<qualifiers>): the first two are the pointer, and a qualifier is enrichment, added where it tells two records apart. A nested identifier is a bare pointer — a type, a locator and at most a fragment. Every other attribute goes in a column beside the identifier.

ref:ai-model:example-app/org/repo;thinking=no;by=ref:pkg:github/ggml-org/llama.cpp   ← a pointer
ref:ai-model:example-app/org/repo;by=ref:pkg:github/ggml-org/llama.cpp%3Bstate=none;latency-ms=812
                                                                                     ← a copy of the record

Both parse ok; write the first shape. The rules and their reasons are in What an identifier carries.

Environments

Node.js 22 or later, and any browser reached through a bundler. The package ships two builds of one source, and exports picks between them: a bundler that honours the browser condition takes the browser build, everything else takes the default. Nothing to configure, and the public API is the same either way.

| | Node | Browser | |---|---|---| | Where the specification comes from | spec/ref-id.json, read from disk on first use | a constant compiled into the build | | When its integrity is established | at load — the bytes are hashed and compared with the sidecar, and a mismatch throws | at build — the generator refuses to emit from a specification that fails its sidecar, and a staleness check refuses a compiled constant that no longer matches the file | | sha256 for digest() | Node's crypto | @noble/hashes (pure JavaScript, no dependencies) |

What the browser build gives up, stated rather than discovered:

  • No integrity check at runtime. There is no file to compare the constant against, and hashing it against a digest compiled from the same source in the same build would be a check that cannot fail — which reads as a guarantee and is a tautology. The build exports SPEC_DIGEST, the digest spec/ref-id.json carried when the constant was generated; it is a statement about provenance, not a verification, and it is labelled as one.
  • No loadSpecFrom. It takes a directory to read a specification and its sidecar from. A browser has neither, so the browser build does not export it; every other export is present and identical. TypeScript will not warn you about that on the default configuration. Under "moduleResolution": "bundler" without customConditions, the compiler resolves types through the default condition — which does declare loadSpecFrom — while the bundler picks the browser build at build time. tsc --noEmit then passes on an import that fails when the bundle is produced. Add "customConditions": ["browser"] to your tsconfig.json and the compiler sees the same surface your bundler does.
  • digest() is sha256 only. The algorithm is specification data, and the browser implementation honours sha256 alone: a specification naming another one throws a SpecVersionError naming it, where Node would honour whatever its own crypto honours.
  • The compiled-in specification costs bundle size. It is the whole published file, including the parts a given consumer never reads.

The two builds are held to each other by npm run test:differential, which runs the browser build as a fifth implementation beside Node, Rust, Swift and Python over every input the specification names, and fails on the first disagreement. npm run build additionally walks the emitted browser module graph and refuses it if any Node builtin — or anything resolving a path against a module's own location — survives into it.

Runtime dependencies: packageurl-js and @noble/hashes, both pure JavaScript. No native modules.

packageurl-js decides whether a Package URL locator is valid and how its canonical field is spelled, and the Rust, Swift and Python implementations use other validators. Where they differ is measured in Known differences between implementations.

License

Apache-2.0 — see the repository's LICENSE.