@hyperfrontend/versioning/registry

registry/

Package registry abstractions for querying npm and other package registries.

Overview

Unified interface for querying package registries with in-memory caching. Validates all user inputs before shell execution to prevent injection attacks.

Usage Examples

Querying npm Registry

import { createNpmRegistry } from '@hyperfrontend/versioning'

const registry = createNpmRegistry()

// Get latest version
const latest = await registry.getLatestVersion('@hyperfrontend/utils')
console.log(latest) // '1.2.3'

// Check if version is published
const exists = await registry.isVersionPublished('lodash', '4.17.21')
console.log(exists) // true

// Get full package info
const info = await registry.getPackageInfo('typescript')
console.log(info?.versions) // ['4.0.0', '4.1.0', ...]
console.log(info?.latestVersion) // '5.4.0'

Using the Factory

import { createRegistry } from '@hyperfrontend/versioning'

// Create registry by type
const registry = createRegistry('npm', {
  timeout: 5000,
  cacheTtl: 30000,
})

const versions = await registry.listVersions('react')

Custom Cache Configuration

import { createNpmRegistry } from '@hyperfrontend/versioning'

// 5 minute cache, 30 second timeout
const registry = createNpmRegistry({
  cacheTtl: 300000,
  timeout: 30000,
})

Version-Specific Information

import { createNpmRegistry } from '@hyperfrontend/versioning'

const registry = createNpmRegistry()
const versionInfo = await registry.getVersionInfo('lodash', '4.17.21')

console.log(versionInfo?.publishedAt) // '2021-02-20T15:42:16.891Z'
console.log(versionInfo?.dependencies) // { ... }
console.log(versionInfo?.engines) // { node: '>=0.10.0' }

Security

Input Validation

All package names and versions are validated character-by-character before being used in shell commands:

  • Package names: Only a-z, A-Z, 0-9, @, /, -, _, . allowed
  • Versions: Only 0-9, a-z, A-Z, ., -, + allowed
  • Length limits: Package names max 214 chars, versions max 256 chars

Invalid characters throw descriptive errors:

import { escapePackageName } from '@hyperfrontend/versioning'

escapePackageName('lodash') // 'lodash'
escapePackageName('@scope/pkg') // '@scope/pkg'
escapePackageName('pkg; rm -rf /') // throws Error

Caching

Responses are cached in memory to reduce registry load:

  • Default TTL: 60 seconds
  • Expired entries are cleaned up on access
  • Cache can be configured per-client

See Also

API Reference§

ƒ Functions

§function

classifyNpmError(error: unknown): NpmLookupFailure

Decides whether a failed npm view proves absence or leaves the answer unknown.
Only a 404 from the registry proves absence, and npm reports both a missing package and a missing version that way. Every other outcome, including a timeout that produces no output at all, leaves the published state unknown and must not be read as "not published".
Parameters
NameTypeDescription
§error
unknownThe error thrown by the invocation
Returns
NpmLookupFailure
Whether the package is absent, or why the registry could not answer
Example

Classifying a lookup failure

try {
  execFileSync('npm', ['view', name, 'version'])
} catch (error) {
  const failure = classifyNpmError(error)
  if (failure.kind === 'absent') return null
  throw createRegistryUnavailableError({ ...failure, packageName: name })
}
§function

createCache(ttl: number): Cache

Creates a new cache instance.
Parameters
NameTypeDescription
§ttl
numberTime-to-live in milliseconds (default: 60000 = 1 minute)(default: 60000)
Returns
Cache
A new Cache instance
Example

Using a cache with custom TTL

const cache = createCache(30000) // 30 second TTL
cache.set('key', { data: 'value' })
const value = cache.get<{ data: string }>('key')
§function

createNpmRegistry(config: RegistryConfig): Registry

Creates an npm registry client.
Parameters
NameTypeDescription
§config
RegistryConfigRegistry configuration(default: {})
Returns
Registry
A Registry implementation for npm
Example
Creating an npm registry client
const registry = createNpmRegistry()
const version = await registry.getLatestVersion('@hyperfrontend/utils')
§function

createPackageInfo(options: CreatePackageInfoOptions): PackageInfo

Creates a new PackageInfo object.
Parameters
NameTypeDescription
§options
CreatePackageInfoOptionsConfiguration for the package info
Returns
PackageInfo
A new PackageInfo object
Example

Creating package info from registry data

const info = createPackageInfo({
  name: '@scope/my-package',
  latestVersion: '2.0.0',
  versions: ['1.0.0', '1.1.0', '2.0.0'],
  license: 'MIT'
})
§function

createRegistry(type: "npm", config: RegistryConfig): Registry

Creates a registry client.
Parameters
NameTypeDescription
§type
"npm"Type of registry to create(default: 'npm')
§config
RegistryConfigRegistry configuration(default: {})
Returns
Registry
A Registry instance
Example
Creating an npm registry client
const registry = createRegistry('npm')
const version = await registry.getLatestVersion('lodash')
§function

createRegistryUnavailableError(details: RegistryUnavailableDetails): RegistryUnavailableError

Creates an error describing a registry that could not answer a lookup.
Parameters
NameTypeDescription
§details
RegistryUnavailableDetailsWhich lookup failed, against which registry, and why
Returns
RegistryUnavailableError
An error carrying the failure details
Example

Reporting an unreachable registry

throw createRegistryUnavailableError({
  registry: 'npm',
  packageName: '@scope/pkg',
  operation: 'getLatestVersion',
  reason: 'network',
  detail: 'ECONNREFUSED',
})
§function

createVersionInfo(options: CreateVersionInfoOptions): VersionInfo

Creates a new VersionInfo object.
Parameters
NameTypeDescription
§options
CreateVersionInfoOptionsConfiguration for the release info
Returns
VersionInfo
A new VersionInfo object
Example

Creating version info from registry data

const release = createVersionInfo({
  version: '1.2.3',
  publishedAt: '2024-01-15T10:30:00Z',
  tarball: 'https://registry.npmjs.org/pkg/-/pkg-1.2.3.tgz'
})
§function

escapePackageName(name: string): string

Escapes a package name for safe use in shell commands. Uses character-by-character validation to prevent injection.
Parameters
NameTypeDescription
§name
stringPackage name to escape
Returns
string
Safe package name
Example

Validating package names for shell safety

escapePackageName('@scope/package') // => '@scope/package'
escapePackageName('simple-pkg') // => 'simple-pkg'
escapePackageName('pkg$invalid') // throws - '$' is invalid
§function

escapeVersion(version: string): string

Escapes a version string for safe use in shell commands.
Parameters
NameTypeDescription
§version
stringVersion string to escape
Returns
string
Safe version string
Example

Validating version strings for shell safety

escapeVersion('1.2.3') // => '1.2.3'
escapeVersion('1.0.0-beta.1') // => '1.0.0-beta.1'
escapeVersion('1.0.0+build') // => '1.0.0+build'
§function

isRegistryUnavailableError(value: unknown): unknown

Checks whether a value is a RegistryUnavailableError.
Parameters
NameTypeDescription
§value
unknownValue to test, typically a caught error
Returns
unknown
True when the value reports a registry that could not answer
Example

Distinguishing an unreachable registry from other failures

try {
  await registry.getLatestVersion('@scope/pkg')
} catch (error) {
  if (isRegistryUnavailableError(error)) {
    logger.error(error.message)
  }
}

◈ Interfaces

§interface

AbsentLookupFailure

The registry answered and the package or version is not there.
Properties
§readonly kind:"absent"Discriminant.
§interface

Cache

Simple in-memory cache with TTL support.
Properties
§interface

CacheEntry

Cache entry with data and expiration time.
Properties
§readonly data:TCached data
§readonly timestamp:numberTimestamp when entry was created
§interface

Maintainer

Maintainer information.
Properties
§readonly email?:stringMaintainer email
§readonly name:stringMaintainer name
§interface

PackageInfo

Package information from a registry.
Properties
§readonly description?:stringPackage description
§readonly homepage?:stringHomepage URL
§readonly keywords?:unknownKeywords
§readonly lastModified?:stringTime of last modification
§readonly latestVersion:stringLatest version
§readonly license?:stringSPDX license identifier
§readonly maintainers:unknownPackage maintainers
§readonly name:stringPackage name
§readonly repository?:stringRepository URL
§readonly versions:unknownAll published versions
§interface

Registry

Abstract interface for package registries.
Properties
§readonly name:stringRegistry name (e.g., "npm", "yarn")
§readonly url:stringRegistry URL
§interface

RegistryConfig

Registry configuration options.
Properties
§readonly authToken?:stringAuthentication token
§readonly cacheTtl?:numberCache TTL in milliseconds
§readonly timeout?:numberRequest timeout in milliseconds
§readonly url?:stringRegistry URL
§interface

RegistryUnavailableDetails

Details identifying the failed lookup.
Properties
§readonly detail?:stringDiagnostic text from the underlying client, included in the message.
§readonly operation:stringClient operation that failed, for example getLatestVersion.
§readonly packageName:stringPackage the lookup was for.
§readonly reason:RegistryFailureReasonWhy the registry could not answer.
§readonly registry:stringRegistry that was queried, for example npm.
§interface

RegistryUnavailableError

Error raised when a registry lookup could not be completed.
Distinct from a lookup that completed and found nothing: this means the registry did not give an answer, so no release decision may be derived from it.
Properties
§cause?:unknown
§message:string
§readonly name:stringAlways REGISTRY_UNAVAILABLE_ERROR.
§readonly operation:stringClient operation that failed, for example getLatestVersion.
§readonly packageName:stringPackage the lookup was for.
§readonly reason:RegistryFailureReasonWhy the registry could not answer.
§readonly registry:stringRegistry that was queried, for example npm.
§stack?:string
§interface

UnavailableLookupFailure

The registry did not answer, so nothing may be concluded.
Properties
§readonly detail:stringDiagnostic text pulled from the failed invocation.
§readonly kind:"unavailable"Discriminant.
§readonly reason:RegistryFailureReasonWhy the registry could not answer.
§interface

VersionInfo

Version-specific information from a registry.
Properties
§readonly dependencies?:Record<string, string>Runtime dependencies
§readonly devDependencies?:Record<string, string>Development dependencies
§readonly engines?:Record<string, string>Engine requirements
§readonly gitHead?:stringGit commit hash at publish time. Used to determine commit range for changelog generation.
NOTE: This value comes from npm registry, making it immutable and independent of local git state.
§readonly integrity?:stringSubresource integrity hash
§readonly nodeVersion?:stringNode.js version range
§readonly npmVersion?:stringnpm version used to publish
§readonly optionalDependencies?:Record<string, string>Optional peer dependencies
§readonly peerDependencies?:Record<string, string>Peer dependencies
§readonly publishedAt:stringISO date when published
§readonly tarball:stringTarball URL
§readonly version:stringVersion string

◆ Types

§type

NpmLookupFailure

Outcome of a failed npm view invocation.
§type

RegistryFailureReason

Why a registry could not answer a lookup.
Every value here means the answer is unknown, never that the package or version is absent. An absent package is a successful lookup with a negative answer, and is reported through the normal return value instead.
type RegistryFailureReason = "network" | "authentication" | "rate-limit" | "server" | "timeout" | "unknown"
§type

RegistryType

Supported registry types.
type RegistryType = "npm"

● Variables

§type

REGISTRY_UNAVAILABLE_ERROR

Name carried by every error this module raises, used to identify it across module boundaries.