CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pnpm--npm-resolver

Resolver for npm-hosted packages with caching, offline support, and registry authentication

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

metadata-management.mddocs/

Metadata Management

The metadata management system handles caching and retrieval of package information from npm registries, providing efficient storage and access to package metadata with support for offline operations.

Capabilities

Package Metadata Cache

The cache interface provides storage and retrieval of package metadata to minimize registry requests.

/**
 * Interface for caching package metadata
 */
interface PackageMetaCache {
  /** 
   * Retrieve cached metadata for a package
   * @param key - Cache key (typically package name)
   * @returns Cached metadata or undefined if not found
   */
  get(key: string): PackageMeta | undefined;
  
  /** 
   * Store metadata in cache
   * @param key - Cache key (typically package name)
   * @param meta - Package metadata to cache
   */
  set(key: string, meta: PackageMeta): void;
  
  /** 
   * Check if metadata exists in cache
   * @param key - Cache key to check
   * @returns True if metadata is cached
   */
  has(key: string): boolean;
}

Package Metadata Structure

Complete package metadata retrieved from npm registries.

/**
 * Complete package metadata from npm registry
 */
interface PackageMeta {
  /** Distribution tags mapping tag names to version numbers */
  'dist-tags': { [name: string]: string };
  /** All available versions with their manifests */
  versions: { [name: string]: PackageInRegistry };
  /** Timestamp when metadata was cached (optional) */
  cachedAt?: number;
}

/**
 * Package manifest with distribution information
 */
type PackageInRegistry = PackageManifest & {
  /** Distribution metadata for downloading */
  dist: {
    /** Subresource integrity hash (optional) */
    integrity?: string;
    /** SHA-1 hash of the tarball */
    shasum: string;
    /** URL to download the package tarball */
    tarball: string;
  };
};

Package Selection from Metadata

The resolver automatically selects appropriate package versions from cached metadata based on the specification requirements and any preferred version configuration. This selection process is handled internally when resolving packages.

Usage Examples

Basic Cache Implementation

import createResolveFromNpm, { PackageMetaCache, PackageMeta } from '@pnpm/npm-resolver';

// Simple Map-based cache
const cache: PackageMetaCache = new Map();

const resolve = createResolveFromNpm({
  metaCache: cache,
  store: './pnpm-store',
  rawNpmConfig: {
    registry: 'https://registry.npmjs.org/',
  },
});

// First resolution will fetch from registry and cache
const result1 = await resolve(
  { alias: 'lodash', pref: '^4.0.0' },
  { 
    registry: 'https://registry.npmjs.org/',
    prefix: process.cwd()
  }
);

// Second resolution will use cached metadata
const result2 = await resolve(
  { alias: 'lodash', pref: '4.17.20' },
  { 
    registry: 'https://registry.npmjs.org/',
    prefix: process.cwd()
  }
);

console.log(`Cache has lodash: ${cache.has('lodash')}`);

Custom Cache Implementation

class PersistentPackageCache implements PackageMetaCache {
  private cache = new Map<string, PackageMeta>();
  private cacheFile: string;

  constructor(cacheFile: string) {
    this.cacheFile = cacheFile;
    this.loadFromDisk();
  }

  get(key: string): PackageMeta | undefined {
    return this.cache.get(key);
  }

  set(key: string, meta: PackageMeta): void {
    meta.cachedAt = Date.now();
    this.cache.set(key, meta);
    this.saveToDisk();
  }

  has(key: string): boolean {
    return this.cache.has(key);
  }

  private loadFromDisk(): void {
    try {
      const data = fs.readFileSync(this.cacheFile, 'utf8');
      const entries = JSON.parse(data);
      this.cache = new Map(entries);
    } catch {
      // Cache file doesn't exist or is invalid
    }
  }

  private saveToDisk(): void {
    const entries = Array.from(this.cache.entries());
    fs.writeFileSync(this.cacheFile, JSON.stringify(entries));
  }
}

const persistentCache = new PersistentPackageCache('./package-cache.json');

Cache Inspection and Management

// Inspect cached metadata
const cachedMeta = cache.get('express');
if (cachedMeta) {
  console.log('Available versions:', Object.keys(cachedMeta.versions));
  console.log('Dist tags:', cachedMeta['dist-tags']);
  console.log('Latest version:', cachedMeta['dist-tags'].latest);
  
  // Check if cached recently (within 1 hour)
  const oneHourAgo = Date.now() - (60 * 60 * 1000);
  const isFresh = cachedMeta.cachedAt && cachedMeta.cachedAt > oneHourAgo;
  console.log(`Cache is fresh: ${isFresh}`);
}

Working with Package Metadata

// Inspect cached metadata
const expressMeta = cache.get('express');
if (expressMeta) {
  console.log('Available versions:', Object.keys(expressMeta.versions));
  console.log('Dist tags:', expressMeta['dist-tags']);
  console.log('Latest version:', expressMeta['dist-tags'].latest);
  
  // Get specific version information
  const latestVersion = expressMeta['dist-tags'].latest;
  const packageInfo = expressMeta.versions[latestVersion];
  
  if (packageInfo) {
    console.log(`Latest: ${packageInfo.name}@${packageInfo.version}`);
    console.log(`Tarball: ${packageInfo.dist.tarball}`);
    console.log(`Integrity: ${packageInfo.dist.integrity || packageInfo.dist.shasum}`);
  }
}

Offline-First Approach

// Create resolver that prefers cached data
const offlineFirstResolve = createResolveFromNpm({
  metaCache: cache,
  store: './pnpm-store',
  preferOffline: true, // Use cache when available
  rawNpmConfig: {
    registry: 'https://registry.npmjs.org/',
  },
});

// This will use cached metadata if available
const result = await offlineFirstResolve(
  { alias: 'react', pref: '^17.0.0' },
  { 
    registry: 'https://registry.npmjs.org/',
    prefix: process.cwd()
  }
);

Cache Warming

// Pre-populate cache with commonly used packages
const commonPackages = ['lodash', 'express', 'react', 'axios'];

for (const packageName of commonPackages) {
  try {
    await resolve(
      { alias: packageName, pref: 'latest' },
      { 
        registry: 'https://registry.npmjs.org/',
        prefix: process.cwd()
      }
    );
    console.log(`Cached metadata for ${packageName}`);
  } catch (error) {
    console.error(`Failed to cache ${packageName}:`, error.message);
  }
}

Cache Statistics

class StatisticalPackageCache implements PackageMetaCache {
  private cache = new Map<string, PackageMeta>();
  private hits = 0;
  private misses = 0;

  get(key: string): PackageMeta | undefined {
    const result = this.cache.get(key);
    if (result) {
      this.hits++;
    } else {
      this.misses++;
    }
    return result;
  }

  set(key: string, meta: PackageMeta): void {
    this.cache.set(key, meta);
  }

  has(key: string): boolean {
    return this.cache.has(key);
  }

  getStats() {
    const total = this.hits + this.misses;
    return {
      hits: this.hits,
      misses: this.misses,
      hitRate: total > 0 ? this.hits / total : 0,
      cacheSize: this.cache.size,
    };
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-pnpm--npm-resolver

docs

index.md

metadata-management.md

package-resolution.md

version-selection.md

tile.json