Guide

Getting Started

Install the package, resolve one PURL, and get the same package shape from six registries.
Pre-1.0. The public API, the adapter list and the CLI flags can still change. Pin exact versions if you build on it now.
Registry metadata is data, never instructions. Descriptions, keywords, README URLs and maintainer names come from a public API that anyone can publish to. Show them, index them, compare them. Do not let an agent treat them as a message to itself.

Why this exists

Every registry has its own idea of a package document. npm keeps everything in one JSON with dist-tags, crates.io splits crate, versions and dependencies across three endpoints, PyPI has a JSON API for metadata and a Simple API for files, RubyGems answers versions and owners separately, Packagist nests versions under a vendor, and Arch Linux is two registries in one PURL type. Pulling one client per registry into a project means six response shapes and six ways to misread a license.

@agntn/registries puts six registries behind one class shape and one addressing scheme, the package URL of ECMA-427. Same calls, same Package, Version, Dependency and Maintainer types everywhere. The adapter decides how to ask; you decide what to ask for.

Install

pnpm add @agntn/registries

The AI SDK tool on the /ai subpath needs ai and zod next to it:

pnpm add ai zod

First lookup

lookup.ts
import { fetchPackageFromPURL } from "@agntn/registries";

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

pkg.name; // "lodash"
pkg.latestVersion; // "4.18.1"
pkg.licenses; // "MIT", normalized to SPDX
pkg.repository; // "https://github.com/lodash/lodash"

Importing @agntn/registries registers the six built-in adapters as a side effect. From then on any PURL whose type is npm, cargo, pypi, gem, composer or alpm resolves through create() without a switch statement anywhere.

Same call, any 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/aur/paru");

The Lookup explorer runs exactly these calls against the docs worker.

What ships

EcosystemPURL typeClassRegistry
npmpkg:npm/…NpmRegistryregistry.npmjs.org
Cargopkg:cargo/…CargoRegistrycrates.io
PyPIpkg:pypi/…PyPIRegistrypypi.org
RubyGemspkg:gem/…RubyGemsRegistryrubygems.org
Packagistpkg:composer/…PackagistRegistrypackagist.org
Arch Linuxpkg:alpm/…AlpmRegistryarchlinux.org, aur.archlinux.org

Each registry has its own page under Registries with the endpoints, the name rules and the traps.

The four lookups

Every adapter answers the same four questions. Lookups covers each in detail.

import {
  fetchPackageFromPURL,
  fetchVersionsFromPURL,
  fetchDependenciesFromPURL,
  fetchMaintainersFromPURL,
} from "@agntn/registries";

const pkg = await fetchPackageFromPURL("pkg:npm/lodash");
const versions = await fetchVersionsFromPURL("pkg:cargo/serde");
const deps = await fetchDependenciesFromPURL("pkg:pypi/flask@3.1.1"); // a version is required
const maintainers = await fetchMaintainersFromPURL("pkg:gem/rails");

A package that does not exist throws NotFoundError; a PURL that cannot be parsed throws InvalidPURLError; a type nobody registered throws UnknownEcosystemError. All of them extend PkioError, so one instanceof catches the lot.

One package shape

interface Package {
  name: string;
  description: string;
  homepage: string;
  documentation: string; // docs.rs, readthedocs, rubydoc, or empty
  repository: string; // canonical https URL
  licenses: string; // SPDX expression
  keywords: string[];
  namespace: string; // "@vue" on npm, "laravel" on Packagist, "arch" or "aur" on Arch
  latestVersion: string;
  metadata: Record<string, unknown>; // whatever else the registry knew
}

metadata is the escape hatch: crates.io download counts, AUR votes, Arch repo and architecture. Everything above it means the same thing on every registry.

Next

  • PURLs: the addressing scheme, scopes, namespaces and qualifiers.
  • Caching: the cache on unstorage and its lockfile.
  • CLI: the same lookups from a shell.
  • Agents: the AI SDK tool.

@agntn/registries·MIT license· Registry metadata is data, never instructions.