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

pnpm-airgap

v2.2.0

Published

Transfer pnpm project dependencies between online and offline environments

Downloads

126

Readme

pnpm-airgap

The complete solution for transferring pnpm dependencies to air-gapped environments.

The Problem

Getting pnpm projects into secure, offline, or air-gapped environments is challenging:

  • No tooling for pnpm lockfiles - Existing airgap tools only work with npm's package-lock.json
  • Complex dependency trees - pnpm's advanced resolution (peer deps, optionals, workspaces) makes manual approaches nearly impossible
  • Registry population gap - No automated way to populate offline registries with pnpm project dependencies

The Solution

pnpm-airgap is a standalone tool that:

  • Reads pnpm-lock.yaml and downloads ALL dependencies
  • Publishes packages to any npm-compatible registry (Verdaccio, Nexus, Artifactory)
  • Works as a single file - no npm install required in airgap
  • Supports pnpm lockfile versions 5.x, 6.x, and 9.x

Quick Start

1. Download the standalone CLI

The CLI is a single file (~1.1MB) that runs with just Node.js:

# From npm (online)
npm pack pnpm-airgap
tar -xzf pnpm-airgap-*.tgz
# Use: node package/dist/cli.cjs

# Or build from source
pnpm install && pnpm build
# Use: node dist/cli.cjs

2. Fetch dependencies (online)

node cli.cjs fetch -l ./pnpm-lock.yaml -o ./packages

3. Transfer to airgap

Copy the packages folder and cli.cjs to your air-gapped environment.

4. Publish to local registry (airgap)

# Start your registry (e.g., Verdaccio)
verdaccio &

# Login
npm login --registry http://localhost:4873

# Publish all packages
node cli.cjs publish -p ./packages -r http://localhost:4873

5. Install your project

pnpm install --registry http://localhost:4873

Interactive Mode

Run without arguments for a guided wizard:

node cli.cjs
┌───────────────────────────────────────┐
│  pnpm-airgap v2.0.0                   │
│  Transfer dependencies to air-gapped  │
│  environments with ease               │
└───────────────────────────────────────┘

? What would you like to do?
  ❯ 📦 Fetch dependencies from lockfile
    📤 Publish packages to registry
    🔄 Sync registries
    📊 Export registry state
    📖 Quick start guide
    ✖ Exit

Commands

fetch - Download packages from lockfile

node cli.cjs fetch [options]

Options:
  -l, --lockfile <path>      Path to pnpm-lock.yaml (default: ./pnpm-lock.yaml)
  -o, --output <path>        Output directory (default: ./airgap-packages)
  -r, --registry <url>       Source registry (default: https://registry.npmjs.org)
  --registry-state <path>    Registry state file for incremental fetching
  --skip-optional            Skip optional dependencies
  --concurrency <number>     Parallel downloads (default: 5)
  --debug                    Enable debug output

publish - Publish packages to registry

node cli.cjs publish [options]

Options:
  -p, --packages <path>      Packages directory (default: ./airgap-packages)
  -r, --registry <url>       Target registry (default: http://localhost:4873)
  --concurrency <number>     Parallel publishes (default: 3)
  --no-skip-existing         Publish all packages even if they exist
  --dry-run                  Preview without publishing
  --debug                    Enable debug output

sync - Sync between registries

node cli.cjs sync [options]

Options:
  -s, --source <url>         Source registry URL
  -d, --dest <url>           Destination registry URL
  -o, --output <path>        Output directory
  --scope <scope>            Only sync packages in this scope
  --download-only            Only download, don't publish
  --publish-only             Only publish existing packages
  --dry-run                  Preview without changes

registry-state export - Export for incremental sync

Export all packages from a registry to enable incremental fetching:

node cli.cjs registry-state export -r http://localhost:4873 -o registry-state.json

# Then use with fetch to skip existing packages
node cli.cjs fetch -l pnpm-lock.yaml --registry-state registry-state.json

info - Show bundle information

node cli.cjs info ./packages

init - Create config file

node cli.cjs init

Configuration

Create pnpm-airgap.config.json:

{
  "fetch": {
    "lockfilePath": "./pnpm-lock.yaml",
    "outputDir": "./airgap-packages",
    "concurrency": 5,
    "registryUrl": "https://registry.npmjs.org",
    "skipOptional": false
  },
  "publish": {
    "packagesDir": "./airgap-packages",
    "registryUrl": "http://localhost:4873",
    "concurrency": 3,
    "skipExisting": true
  },
  "sync": {
    "sourceRegistry": "",
    "destRegistry": "http://localhost:4873",
    "outputDir": "./sync-packages",
    "skipExisting": true
  }
}

Features

| Feature | Description | | --------------------- | ----------------------------------------------------------------- | | Standalone Binary | Single 1.1MB file, runs with just Node.js - no npm install needed | | Interactive Mode | Guided wizard for all commands | | Auto-detection | Finds lockfiles and package directories automatically | | Incremental Sync | Export registry state to skip already-synced packages | | Smart Tagging | Auto-detects prerelease tags, handles version conflicts | | Safety Blocks | Prevents accidental publish to public registries (npmjs.org) | | Rate Limiting | Automatic backoff for 429 errors | | Robust Parsing | Handles scoped packages, aliases, patches, peer deps |

Workflow Examples

Complete Airgap Transfer

Online Machine:

# Fetch all dependencies
node cli.cjs fetch -l pnpm-lock.yaml -o ./packages

# Create transfer archive
tar -czf transfer.tar.gz packages/ cli.cjs

Offline Machine:

# Extract
tar -xzf transfer.tar.gz

# Start registry and login
verdaccio &
npm login --registry http://localhost:4873

# Publish
node cli.cjs publish -p ./packages -r http://localhost:4873

# Install your project
echo "registry=http://localhost:4873" > .npmrc
pnpm install

Incremental Updates

Avoid re-downloading packages that already exist:

# Export state from airgap registry
node cli.cjs registry-state export -r http://verdaccio:4873 -o state.json

# Transfer state.json to online machine

# Fetch only missing packages
node cli.cjs fetch -l pnpm-lock.yaml --registry-state state.json -o ./packages
# Result: If lockfile needs 500 packages but 450 exist, only 50 are downloaded

Programmatic API

import { fetchDependencies, publishPackages } from 'pnpm-airgap';

// Fetch
await fetchDependencies({
  lockfilePath: './pnpm-lock.yaml',
  outputDir: './packages',
  registryUrl: 'https://registry.npmjs.org',
  concurrency: 5,
});

// Publish
await publishPackages({
  packagesDir: './packages',
  registryUrl: 'http://localhost:4873',
  concurrency: 3,
  skipExisting: true,
});

Compatibility

| Component | Supported Versions | | ----------------- | ------------------------------------------------- | | Node.js | 18.0.0 or higher | | pnpm lockfile | v5, v6, v9 | | Registries | Verdaccio, Nexus, Artifactory, any npm-compatible | | Platforms | Windows, Linux, macOS |

Reports

Both fetch and publish commands generate JSON reports:

  • metadata.json - Package list and metadata
  • bundle-info.json - Download statistics
  • publish-report.json - Publishing results

Troubleshooting

Authentication Issues

# Verify you're logged in
npm whoami --registry http://localhost:4873

# Re-login if needed
npm login --registry http://localhost:4873

Missing Packages

Check bundle-info.json for download failures and ensure lockfile is current.

Publishing Conflicts

The tool automatically handles:

  • Version conflicts (uses version-specific tags)
  • Prerelease versions (applies correct tags)
  • Already-existing packages (skips by default)

Development

# Install dependencies
pnpm install

# Build
pnpm build

# Run tests
pnpm test

# Lint
pnpm lint

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.