Skip to content

Latest commit

 

History

History
1166 lines (738 loc) · 25.1 KB

api.md

File metadata and controls

1166 lines (738 loc) · 25.1 KB

API Reference

Complete API documentation for @socketregistry/packageurl-js.

The root package exports PURL parsing, construction, comparison, normalization, VERS matching, and ecosystem helpers. Registry existence checks use the separate @socketregistry/packageurl-js/exists entrypoint so consumers of the core parser do not include the HTTP dependencies used for registry requests.

Table of Contents


Modular Functions

Tree-shakeable functions for optimal bundle size.

parseNpmSpecifier(specifier)

Parse npm package specifier into components.

Parameters:

  • specifier: unknown - npm package specifier string

Returns: NpmPackageComponents

The returned component shape, examples for a plain and a scoped specifier, and the range-prefix and dist-tag handling notes
{
  namespace: string | undefined // e.g., '@babel' for scoped packages
  name: string // package name
  version: string | undefined // version without range prefixes
}

Example:

import { parseNpmSpecifier } from '@socketregistry/packageurl-js'

parseNpmSpecifier('lodash@4.17.21')
// -> { namespace: undefined, name: 'lodash', version: '4.17.21' }

parseNpmSpecifier('@babel/core@^7.0.0')
// -> { namespace: '@babel', name: 'core', version: '7.0.0' }

Features:

  • Strips version range prefixes (^, ~, >=, etc.)
  • Handles dist-tags (latest, next, beta)
  • Supports scoped packages

stringify(purl)

Convert PackageURL instance to canonical PURL string.

Parameters:

  • purl: PackageURL - PackageURL instance

Returns: string - Canonical PURL string

Example:

import { stringify } from '@socketregistry/packageurl-js'

const purl = new PackageURL('npm', undefined, 'lodash', '4.17.21')
stringify(purl)
// -> 'pkg:npm/lodash@4.17.21'

equals(a, b)

Compare two PackageURLs for equality.

Parameters:

  • a: PackageURL - First PURL
  • b: PackageURL - Second PURL

Returns: boolean - true if equal

Example:

import { equals } from '@socketregistry/packageurl-js'

const purl1 = PackageURL.fromString('pkg:npm/lodash@4.17.21')
const purl2 = PackageURL.fromString('pkg:npm/lodash@4.17.21')
equals(purl1, purl2) // -> true

compare(a, b)

Compare two PackageURLs for sorting.

Parameters:

  • a: PackageURL - First PURL
  • b: PackageURL - Second PURL

Returns: -1 | 0 | 1 - Sort order indicator

Example:

import { compare } from '@socketregistry/packageurl-js'

const purls = [purl3, purl1, purl2]
purls.sort(compare) // Sort alphabetically

PackageURL Class

Main class for parsing and constructing Package URLs.

Constructor

new PackageURL(type, namespace, name, version, qualifiers, subpath)

Parameters:

  • type: unknown - Package ecosystem type (e.g., 'npm', 'pypi')
  • namespace: unknown - Optional namespace/scope
  • name: unknown - Package name (required)
  • version: unknown - Optional version
  • qualifiers: unknown - Optional key-value pairs
  • subpath: unknown - Optional subpath

Example:

new PackageURL('npm', '@babel', 'core', '7.20.0', null, null)
// -> 'pkg:npm/%40babel/core@7.20.0'

Static Methods

PackageURL.fromString, fromNpm, fromSpec, fromJSON and parseString: parameters, return type and a call example for each

PackageURL.fromString(purlStr)

Parse PURL string into PackageURL instance.

Parameters:

  • purlStr: unknown - PURL string

Returns: PackageURL

Example:

PackageURL.fromString('pkg:npm/lodash@4.17.21')

PackageURL.fromNpm(specifier)

Create PackageURL from npm package specifier.

Parameters:

  • specifier: unknown - npm package specifier

Returns: PackageURL

Example:

PackageURL.fromNpm('lodash@4.17.21')
PackageURL.fromNpm('@types/node@^18.0.0')

PackageURL.fromSpec(type, specifier)

Create PackageURL from ecosystem-specific specifier.

Parameters:

  • type: string - Package type ('npm', etc.)
  • specifier: unknown - Ecosystem-specific specifier

Returns: PackageURL

Example:

PackageURL.fromSpec('npm', 'lodash@4.17.21')

Currently supported: npm only


PackageURL.fromJSON(json)

Parse PackageURL from JSON string.

Parameters:

  • json: unknown - JSON string

Returns: PackageURL

Example:

const json = '{"type":"npm","name":"lodash","version":"4.17.21"}'
PackageURL.fromJSON(json)

PackageURL.parseString(purlStr)

Parse PURL string into component array.

Parameters:

  • purlStr: unknown - PURL string

Returns: ParsedPurlComponents - Tuple of components

Example:

const [type, namespace, name, version, qualifiers, subpath] =
  PackageURL.parseString('pkg:npm/lodash@4.17.21')

Instance Methods

toString, toJSON, toObject, equals and compare: parameters and return types

toString()

Convert to canonical PURL string.

Returns: string


toJSON()

Convert to JSON-serializable object.

Returns: string - JSON string


toObject()

Convert to plain object.

Returns: PackageURLObject


equals(other)

Compare with another PackageURL.

Parameters:

  • other: PackageURL

Returns: boolean


compare(other)

Compare for sorting.

Parameters:

  • other: PackageURL

Returns: -1 | 0 | 1

Properties

All properties are string | undefined:

  • type - Package ecosystem type
  • namespace - Optional namespace/scope
  • name - Package name
  • version - Optional version
  • qualifiers - Optional key-value pairs
  • subpath - Optional subpath

PurlBuilder Class

Fluent builder API for constructing PURLs.

Constructor

new PurlBuilder()

Static Factory Methods

PurlBuilder.create, PurlBuilder.from for copying an existing PackageURL, and the type-preset factories npm, pypi, maven, gem, cargo, composer, golang, nuget and 30+ more

PurlBuilder.create()

Create a new empty builder instance.

Returns: PurlBuilder

Example:

const builder = PurlBuilder.create().type('npm').name('lodash').build()

PurlBuilder.from(purl)

Create a builder from an existing PackageURL instance.

Parameters:

  • purl: PackageURL - Existing PackageURL to copy properties from

Returns: PurlBuilder - Builder with all properties pre-populated

Example:

const originalPurl = PackageURL.fromString('pkg:npm/lodash@4.17.21')

const modifiedPurl = PurlBuilder.from(originalPurl)
  .version('4.18.0')
  .qualifier('arch', 'x64')
  .build()

console.log(modifiedPurl.toString())
// -> 'pkg:npm/lodash@4.18.0?arch=x64'

Use cases:

  • Modify existing PURLs while preserving most properties
  • Create variants of a PURL with different versions or qualifiers
  • Clone and customize PURLs

Type-Specific Factory Methods

Create builders with type preset:

PurlBuilder.npm()
PurlBuilder.pypi()
PurlBuilder.maven()
PurlBuilder.gem()
PurlBuilder.cargo()
PurlBuilder.composer()
PurlBuilder.golang()
PurlBuilder.nuget()
// ... and 30+ more ecosystems

Example:

const purl = PurlBuilder.npm().name('lodash').version('4.17.21').build()

Builder Methods

All methods return this for chaining:

The chainable setters: type, namespace, name, version, qualifier, qualifiers, subpath and build

type(value)

Set package type.


namespace(value)

Set namespace/scope.


name(value)

Set package name (required).


version(value)

Set version.


qualifier(key, value)

Add a qualifier key-value pair.


qualifiers(object)

Set all qualifiers at once.


subpath(value)

Set subpath.


build()

Build and return PackageURL instance.

Returns: PackageURL

Example

import { PurlBuilder } from '@socketregistry/packageurl-js'

const purl = PurlBuilder.maven()
  .namespace('org.springframework')
  .name('spring-core')
  .version('5.3.21')
  .qualifier('classifier', 'sources')
  .build()

console.log(purl.toString())
// -> 'pkg:maven/org.springframework/spring-core@5.3.21?classifier=sources'

UrlConverter Class

Convert PURLs to repository and download URLs.

Static Methods

UrlConverter.toRepositoryUrl and UrlConverter.toDownloadUrl: parameters, the nullable return shapes and an example of each

UrlConverter.toRepositoryUrl(purl)

Convert to repository URL.

Parameters:

  • purl: PackageURL

Returns: RepositoryUrl | null

Example:

import { UrlConverter } from '@socketregistry/packageurl-js'

const purl = PackageURL.fromString('pkg:npm/lodash@4.17.21')
UrlConverter.toRepositoryUrl(purl)
// -> { type: 'git', url: 'https://github.com/lodash/lodash' }

UrlConverter.toDownloadUrl(purl)

Convert to download URL.

Parameters:

  • purl: PackageURL

Returns: DownloadUrl | null

Example:

UrlConverter.toDownloadUrl(purl)
// -> { type: 'tarball', url: 'https://registry.npmjs.org/...' }

Registry Existence Checks

Modular functions to verify package existence across 14 package registries. Each function queries the official registry API to check if a package exists and optionally validates a specific version.

purlExists(purl, options?)

Generic wrapper that dispatches to type-specific existence checks based on the PackageURL type.

Parameters, the ExistsResult shape, a plain and a cached example, and the list of supported types

Parameters:

  • purl: PackageURL - PackageURL instance to check
  • options?: ExistsOptions - Optional configuration
    • cache?: TtlCache - Optional TTL cache for caching responses

Returns: Promise<ExistsResult>

{
  exists: boolean            // Whether the package exists
  latestVersion?: string     // Latest version from registry
  error?: string            // Error message if check failed
}

Example:

import { purlExists } from '@socketregistry/packageurl-js/exists'
import { PackageURL } from '@socketregistry/packageurl-js'

const purl = PackageURL.fromString('pkg:npm/lodash@4.17.21')
const result = await purlExists(purl)
// -> { exists: true, latestVersion: '4.17.21' }

// With caching
import { createTtlCache } from '@socketsecurity/lib/cache-with-ttl'
const cache = createTtlCache({ ttl: 5 * 60 * 1000 })
await purlExists(purl, { cache })

Supported types: npm, pypi, cargo, gem, maven, nuget, golang, composer, cocoapods, pub, hex, cpan, cran, hackage, conda, docker, vscode-extension


Type-Specific Functions

All registry functions follow the same signature pattern and return ExistsResult.

The 17 per-registry checkers: npmExists, pypiExists, cargoExists, gemExists, mavenExists, nugetExists, golangExists, packagistExists, cocoapodsExists, pubExists, hexExists, cpanExists, cranExists, hackageExists, condaExists, dockerExists and vscodeExtensionExists, each with its registry host, its required options, and an await example

npmExists(name, options?)

Check npm packages from registry.npmjs.org. options accepts namespace, version, and cache.

Example:

import { npmExists } from '@socketregistry/packageurl-js/exists'

await npmExists('lodash')
// -> { exists: true, latestVersion: '4.17.21' }

await npmExists('core', { namespace: '@babel' }) // scoped package
// -> { exists: true, latestVersion: '7.23.0' }

await npmExists('lodash', { version: '4.17.21' }) // validate version
// -> { exists: true, latestVersion: '4.17.21' }

pypiExists(name, version?, options?)

Check Python packages from pypi.org.

Example:

import { pypiExists } from '@socketregistry/packageurl-js/exists'

await pypiExists('requests')
// -> { exists: true, latestVersion: '2.31.0' }

cargoExists(name, version?, options?)

Check Rust crates from crates.io.

Example:

import { cargoExists } from '@socketregistry/packageurl-js/exists'

await cargoExists('serde')
// -> { exists: true, latestVersion: '1.0.197' }

gemExists(name, version?, options?)

Check Ruby gems from rubygems.org.

Example:

import { gemExists } from '@socketregistry/packageurl-js/exists'

await gemExists('rails')
// -> { exists: true, latestVersion: '7.1.3' }

mavenExists(name, options?)

Check Java packages from Maven Central. options.namespace (group ID) is required.

Example:

import { mavenExists } from '@socketregistry/packageurl-js/exists'

await mavenExists('commons-lang3', { namespace: 'org.apache.commons' })
// -> { exists: true, latestVersion: '3.12.0' }

nugetExists(name, version?, options?)

Check .NET packages from nuget.org.

Example:

import { nugetExists } from '@socketregistry/packageurl-js/exists'

await nugetExists('Newtonsoft.Json')
// -> { exists: true, latestVersion: '13.0.3' }

golangExists(name, options?)

Check Go modules from proxy.golang.org. options accepts namespace, version, and cache.

Example:

import { golangExists } from '@socketregistry/packageurl-js/exists'

await golangExists('github.com/gorilla/mux')
// -> { exists: true, latestVersion: 'v1.8.0' }

await golangExists('mux', { namespace: 'github.com/gorilla' }) // with namespace
// -> { exists: true, latestVersion: 'v1.8.0' }

packagistExists(name, options?)

Check PHP/Composer packages from packagist.org. options.namespace (vendor) is required.

Example:

import { packagistExists } from '@socketregistry/packageurl-js/exists'

await packagistExists('http-foundation', { namespace: 'symfony' })
// -> { exists: true, latestVersion: 'v6.3.0' }

cocoapodsExists(name, version?, options?)

Check iOS/macOS pods from trunk.cocoapods.org.

Example:

import { cocoapodsExists } from '@socketregistry/packageurl-js/exists'

await cocoapodsExists('Alamofire')
// -> { exists: true, latestVersion: '5.8.1' }

pubExists(name, version?, options?)

Check Dart/Flutter packages from pub.dev.

Example:

import { pubExists } from '@socketregistry/packageurl-js/exists'

await pubExists('flutter_bloc')
// -> { exists: true, latestVersion: '8.1.3' }

hexExists(name, version?, options?)

Check Elixir/Erlang packages from hex.pm.

Example:

import { hexExists } from '@socketregistry/packageurl-js/exists'

await hexExists('phoenix')
// -> { exists: true, latestVersion: '1.7.10' }

cpanExists(name, version?, options?)

Check Perl modules from metacpan.org.

Example:

import { cpanExists } from '@socketregistry/packageurl-js/exists'

await cpanExists('Moose')
// -> { exists: true, latestVersion: '2.2206' }

cranExists(name, version?, options?)

Check R packages from cran.r-universe.dev.

Example:

import { cranExists } from '@socketregistry/packageurl-js/exists'

await cranExists('ggplot2')
// -> { exists: true, latestVersion: '3.4.4' }

hackageExists(name, version?, options?)

Check Haskell packages from hackage.haskell.org.

Example:

import { hackageExists } from '@socketregistry/packageurl-js/exists'

await hackageExists('aeson')
// -> { exists: true, latestVersion: '2.2.0.0' }

condaExists(name, options?)

Check Conda packages from an Anaconda channel (defaults to conda-forge). options accepts version, channel, and cache.

Example:

import { condaExists } from '@socketregistry/packageurl-js/exists'

await condaExists('numpy')
// -> { exists: true, latestVersion: '1.26.0' }

await condaExists('pytorch', { channel: 'pytorch' })
// -> { exists: true, latestVersion: '2.1.0' }

dockerExists(name, options?)

Check Docker images from Docker Hub. Omit options.namespace for official images (resolves to library/<name>); options.version is the tag.

Example:

import { dockerExists } from '@socketregistry/packageurl-js/exists'

await dockerExists('nginx')
// -> { exists: true, latestVersion: 'latest' }

await dockerExists('postgres', { version: '16' })
// -> { exists: true, latestVersion: '16' }

vscodeExtensionExists(name, options?)

Check Visual Studio Code extensions from the VS Code Marketplace. options.namespace (publisher) is required.

Example:

import { vscodeExtensionExists } from '@socketregistry/packageurl-js/exists'

await vscodeExtensionExists('vscode-eslint', { namespace: 'dbaeumer' })
// -> { exists: true, latestVersion: '3.0.10' }

Type Constants

PURL_Type

Enum-like object with all supported PURL types:

import { PURL_Type } from '@socketregistry/packageurl-js'

PURL_Type.NPM // 'npm'
PURL_Type.PYPI // 'pypi'
PURL_Type.MAVEN // 'maven'
PURL_Type.CARGO // 'cargo'
// ... 40+ types

PurlQualifierNames

Constants for standard PURL qualifier keys as defined in the purl-spec.

import { PurlQualifierNames } from '@socketregistry/packageurl-js'

PurlQualifierNames.RepositoryUrl // 'repository_url'
PurlQualifierNames.DownloadUrl // 'download_url'
PurlQualifierNames.VcsUrl // 'vcs_url'
PurlQualifierNames.FileName // 'file_name'
PurlQualifierNames.Checksum // 'checksum'

Usage:

A PurlBuilder example setting checksum and download_url, plus what each known qualifier means: repository_url, download_url, vcs_url, file_name and checksum
const purl = PurlBuilder.maven()
  .namespace('org.apache.commons')
  .name('commons-lang3')
  .version('3.12.0')
  .qualifier(PurlQualifierNames.Checksum, 'sha256:abc123')
  .qualifier(PurlQualifierNames.DownloadUrl, 'https://repo1.maven.org/...')
  .build()

Known qualifiers:

  • repository_url - URL to package repository homepage
  • download_url - Direct download URL for the package artifact
  • vcs_url - Version control system URL (git, svn, etc.)
  • file_name - Name of the package file or artifact
  • checksum - Checksum value in the format algorithm:value (e.g., sha256:abc123)

PurlType

Object with type-specific normalization and validation:

import { PurlType } from '@socketregistry/packageurl-js'

PurlType.npm.normalize(purl)
PurlType.npm.validate(purl)

Type-specific rules:

Normalization and validation rules per ecosystem: npm, pypi, maven and cargo

npm:

  • Name: Lowercased, namespace preserved with @ prefix
  • Version: Requires valid semver format
  • Namespace: Used for scoped packages (e.g., @types)

pypi:

  • Name: Lowercased, underscores replaced with dashes
  • Namespace: Must be empty
  • Version: Normalized according to PEP 440

maven:

  • Name: Preserved as-is
  • Namespace: Required (groupId)
  • Case-sensitive

cargo:

  • Name: Lowercased
  • Namespace: Must be empty
  • Version: Follows semver

PurlComponent

Component encoding/decoding utilities:

import { PurlComponent } from '@socketregistry/packageurl-js'

PurlComponent.name.encode('my-package')
PurlComponent.name.decode('my-package')

Available components:

  • type - Package ecosystem type
  • namespace - Optional namespace/scope
  • name - Package name (required)
  • version - Package version
  • qualifiers - Key-value pairs
  • subpath - Path within package

Encoding rules:

  • UTF-8 characters are percent-encoded
  • Forward slashes in namespaces become encoded
  • @ in npm scopes is encoded as %40
  • Special characters in names/versions are encoded per spec

Error Handling

PurlError

Custom error class for PURL-specific errors.

import { PurlError } from '@socketregistry/packageurl-js'

try {
  PackageURL.fromString('invalid')
} catch (error) {
  if (error instanceof PurlError) {
    console.error('PURL error:', error.message)
  }
}

Validation Rules

The library performs strict validation on all components:

Rules and the exact error text for each component: type, name, namespace, version, qualifiers and subpath

Type validation:

  • Required for all PURLs
  • Must be lowercase letters, numbers, ., +, or -
  • Cannot be empty
  • Example errors:
    • 'type' is a required component
    • 'type' component must be lowercase

Name validation:

  • Required for all PURLs
  • Cannot contain whitespace (some types)
  • Cannot be empty
  • Type-specific rules apply (e.g., npm name validation)
  • Example errors:
    • 'name' is a required component
    • npm 'name' component cannot contain whitespace

Namespace validation:

  • Optional for most types, required for some (e.g., maven)
  • Type-specific rules apply
  • Cannot contain whitespace (some types)
  • Example errors:
    • maven requires a 'namespace' component
    • npm 'namespace' component cannot contain whitespace

Version validation:

  • Optional for most types
  • Type-specific format requirements (e.g., semver for npm)
  • Example errors:
    • npm 'version' component must be valid semver
    • 'version' component cannot be empty

Qualifiers validation:

  • Keys must be lowercase alphanumeric with ., -, or _
  • Values must not be empty strings
  • Example errors:
    • qualifier 'Arch' must be lowercase
    • qualifier 'os' must not be empty

Subpath validation:

  • Must not start with /
  • Cannot contain .. segments
  • Example errors:
    • 'subpath' component cannot start with '/'
    • 'subpath' component cannot contain '..' segments

Common Error Scenarios

Three failing cases with the PurlError each raises: an invalid PURL string, type-specific violations for npm semver and maven namespace, and an uppercase qualifier key

Invalid PURL string:

// Missing 'pkg:' scheme
PackageURL.fromString('npm/lodash')
// -> PurlError: missing required "pkg" scheme component

// Missing type
PackageURL.fromString('pkg:/lodash')
// -> PurlError: 'type' is a required component

// Missing name
PackageURL.fromString('pkg:npm/')
// -> PurlError: 'name' is a required component

Type-specific violations:

// npm requires valid semver
new PackageURL('npm', undefined, 'lodash', 'latest')
// -> PurlError: npm 'version' component must be valid semver

// maven requires namespace
new PackageURL('maven', undefined, 'commons-lang3', '3.12.0')
// -> PurlError: maven requires a 'namespace' component

Invalid qualifiers:

const purl = PurlBuilder.npm()
  .name('lodash')
  .qualifier('Arch', 'x64') // Uppercase not allowed
  .build()
// -> PurlError: qualifier 'Arch' must be lowercase

Result Type

Functional error handling with Result<T, E> type:

import { ok, err, ResultUtils } from '@socketregistry/packageurl-js'

const result = ok('success')
result.isOk() // -> true
result.unwrap() // -> 'success'

const failure = err(new Error('failed'))
failure.isErr() // -> true
failure.unwrap() // throws Error

See types.md for complete type definitions.

Entry Points and Integration

Import path Contents Runtime boundary
@socketregistry/packageurl-js PackageURL, PurlBuilder, UrlConverter, PURL helpers, Vers, and core types. Core parser and builder entrypoint.
@socketregistry/packageurl-js/exists purlExists and type-specific registry functions such as npmExists, mavenExists, and vscodeExtensionExists. Separate entrypoint that includes registry HTTP dependencies.

The default export map is defined in package.json. See types.md for the PackageURLObject, parsed tuple, NpmPackageComponents, and result contracts. See vers.md for supported VERS schemes, matching examples, and pre-standard limitations.

The core PurlType registry defines normalization and validation hooks for ecosystem-specific PURLs. Existing examples cover npm, PyPI, Maven, and Cargo. The implementation for each registered type is under src/purl-types.