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

regxa

v0.1.6

Published

Universal package registry client. Query npm, PyPI, crates.io, RubyGems, Packagist and more with a single PURL-native API.

Readme

regxa

npm version npm downloads license Ask DeepWiki

Query npm, PyPI, crates.io, RubyGems, Packagist, and Arch Linux with one API. PURL-native, typed, cached.

Why?

If you need package metadata from multiple registries, you currently have two options: call each registry's REST API yourself (they all work differently), or depend on a third-party aggregation service.

There is no embeddable TypeScript library that normalizes across registries. The closest thing is git-pkgs/registries in Go, which covers 25 ecosystems but is Go-only. Aggregation APIs like ecosyste.ms and deps.dev exist, but they are external services you can't bundle into your own tool.

regxa fills that gap. One fetchPackage call, same response shape, regardless of whether the package lives on npm or Packagist. Uses PURL (ECMA-427) for addressing, so pkg:npm/lodash and pkg:cargo/serde resolve through the same code path. Storage-backed caching with a lockfile keeps things fast on repeated lookups.

Features

  • 🔍 Single API, six registries - npm, PyPI, crates.io, RubyGems, Packagist, Arch Linux (official + AUR)
  • 📦 PURL-native - ECMA-427 identifiers as first-class input
  • 🏷️ Normalized data model - same Package, Version, Dependency, Maintainer types everywhere
  • 💾 Storage-backed cache + lockfile - unstorage-native, sha256 integrity checks, configurable TTL
  • ⌨️ CLI included - regxa info npm/lodash, regxa versions cargo/serde, regxa deps pypi/[email protected]
  • 🔁 Retry + backoff - exponential backoff with jitter, rate limiter interface
  • 🪶 ESM-only, zero CJS - built with obuild

Install

pnpm add regxa

For the AI SDK tool (regxa/ai subpath), also install ai and zod:

pnpm add ai zod

Quick start

API

import { fetchPackageFromPURL } from "regxa";

const pkg = await fetchPackageFromPURL("pkg:npm/lodash");

console.log(pkg.name); // "lodash"
console.log(pkg.latestVersion); // "4.17.23"
console.log(pkg.licenses); // "MIT"
console.log(pkg.repository); // "https://github.com/lodash/lodash"

Works the same for any supported registry:

await fetchPackageFromPURL("pkg:cargo/serde");
await fetchPackageFromPURL("pkg:pypi/flask");
await fetchPackageFromPURL("pkg:gem/rails");
await fetchPackageFromPURL("pkg:composer/laravel/framework");
await fetchPackageFromPURL("pkg:alpm/arch/pacman");

CLI

The pkg: prefix is optional in the CLI. npm/lodash works just as well:

regxa info npm/lodash
regxa versions cargo/serde
regxa deps pypi/[email protected]
regxa maintainers gem/rails
regxa deps alpm/aur/paru

Add --json for machine-readable output, --no-cache to skip the cache.

AI SDK tool

regxa/ai exports a ready-made tool for AI SDK apps:

import { generateText } from "ai";
import { packageTool } from "regxa/ai";

const { text } = await generateText({
  model: yourModel,
  tools: { packageRegistry: packageTool },
  prompt: "Show me the latest metadata for pkg:npm/lodash and then list its maintainers.",
});

The tool supports these operations through one input schema:

// { operation: 'package', purl: 'pkg:npm/lodash' }
// { operation: 'versions', purl: 'pkg:cargo/serde' }
// { operation: 'dependencies', purl: 'pkg:pypi/[email protected]' }
// { operation: 'maintainers', purl: 'pkg:gem/rails' }
// { operation: 'bulk-packages', purls: ['pkg:npm/lodash', 'pkg:cargo/serde'], concurrency?: number }

Registries

| Ecosystem | PURL type | Registry | | ---------- | ------------------ | -------------------------------- | | npm | pkg:npm/... | registry.npmjs.org | | Cargo | pkg:cargo/... | crates.io | | PyPI | pkg:pypi/... | pypi.org | | RubyGems | pkg:gem/... | rubygems.org | | Packagist | pkg:composer/... | packagist.org | | Arch Linux | pkg:alpm/... | archlinux.org, aur.archlinux.org |

Scoped packages work as expected: pkg:npm/%40vue/core or npm/@vue/core in the CLI.

Arch Linux packages use a namespace: pkg:alpm/arch/pacman (or just pkg:alpm/pacman) for official repos, pkg:alpm/aur/paru for AUR. Official packages default to arch when the namespace is omitted; AUR requires the explicit aur namespace.

API reference

PURL helpers

import {
  fetchPackageFromPURL,
  fetchVersionsFromPURL,
  fetchDependenciesFromPURL,
  fetchMaintainersFromPURL,
  bulkFetchPackages,
} from "regxa";

// Single lookups
const pkg = await fetchPackageFromPURL("pkg:npm/lodash");
const versions = await fetchVersionsFromPURL("pkg:cargo/serde");
const deps = await fetchDependenciesFromPURL("pkg:pypi/[email protected]");
const maintainers = await fetchMaintainersFromPURL("pkg:gem/rails");

// Bulk - fetches up to 15 packages concurrently
const packages = await bulkFetchPackages(["pkg:npm/lodash", "pkg:cargo/serde", "pkg:pypi/flask"]);

Direct registry access

For more control, create a registry instance directly:

import { create } from "regxa";

const npm = create("npm");
const pkg = await npm.fetchPackage("lodash");
const versions = await npm.fetchVersions("lodash");
const deps = await npm.fetchDependencies("lodash", "4.17.21");

Cached registry

Wrap any registry with caching:

import { createCached } from "regxa";

const npm = createCached("npm");

// First call hits the network and writes to cache
const pkg = await npm.fetchPackage("lodash");

// Second call reads from cache (if TTL hasn't expired)
const same = await npm.fetchPackage("lodash");

By default, regxa uses filesystem storage and follows platform cache conventions: ~/.cache/regxa on Linux (XDG), ~/Library/Caches/regxa on macOS, %LOCALAPPDATA%\regxa\cache on Windows. Override with REGXA_CACHE_DIR env var.

For edge/serverless runtimes, configure a custom unstorage driver (example: Cloudflare KV binding):

import { configureStorage, createCached } from "regxa";
import { createStorage } from "unstorage";
import cloudflareKVBindingDriver from "unstorage/drivers/cloudflare-kv-binding";
import "regxa/registries";

configureStorage(
  createStorage({
    driver: cloudflareKVBindingDriver({ binding: "REGXA_CACHE" }),
  }),
);

const npm = createCached("npm");
const pkg = await npm.fetchPackage("lodash");

PURL parsing

import { parsePURL, buildPURL, fullName } from "regxa";

const parsed = parsePURL("pkg:npm/%40vue/[email protected]");
// { type: 'npm', namespace: '@vue', name: 'core', version: '3.5.0', qualifiers: {}, subpath: '' }

fullName(parsed); // "@vue/core"

buildPURL({ type: "cargo", name: "serde", version: "1.0.0" });
// "pkg:cargo/[email protected]"

Types

import type { Package, Version, Dependency, Maintainer, Registry, ParsedPURL } from "regxa";

CLI

regxa <command> [options]

| Command | Description | | -------------------------- | ------------------------------------------------------ | | regxa info <purl> | Package metadata (name, license, repo, latest version) | | regxa versions <purl> | List all published versions | | regxa deps <purl> | Dependencies for a specific version | | regxa maintainers <purl> | Package maintainers / authors | | regxa cache status | Show cache stats (entries, freshness) | | regxa cache path | Print cache directory path | | regxa cache clear | Remove all cached data | | regxa cache prune | Remove stale entries |

Options

| Flag | Description | | ------------ | ---------------------------------------- | | --json | Output as JSON | | --no-cache | Bypass cache, always fetch from registry |

Caching

regxa stores fetched data and freshness metadata in unstorage. Default TTLs:

| Data type | TTL | | ---------------- | ---------- | | Package metadata | 1 hour | | Version list | 30 minutes | | Dependencies | 24 hours | | Maintainers | 24 hours |

Each cached entry has a sha256 integrity hash. If the stored data doesn't match the hash, regxa refetches automatically.

Data model

Every registry returns the same normalized types:

interface Package {
  name: string;
  description: string;
  homepage: string;
  documentation: string; // docs URL (docs.rs, readthedocs, rubydoc, etc.)
  repository: string;
  licenses: string; // SPDX-normalized
  keywords: string[];
  namespace: string; // e.g. "@vue" for npm scoped packages
  latestVersion: string;
  metadata: Record<string, unknown>;
}

interface Version {
  number: string;
  publishedAt: Date | null;
  licenses: string;
  integrity: string;
  status: "" | "yanked" | "deprecated" | "retracted";
  metadata: Record<string, unknown>;
}

interface Dependency {
  name: string;
  requirements: string; // version constraint
  scope: "runtime" | "development" | "test" | "build" | "optional";
  optional: boolean;
}

interface Maintainer {
  uuid: string;
  login: string;
  name: string;
  email: string;
  url: string;
  role: string;
}

License

MIT