Architecture

Deep dive into how Pastoralist works, including overrides, resolutions, patches, and the object anatomy

How Pastoralist Works

Pastoralist reads the root package.json, maps each override or resolution into a pastoralist.appendix entry, and records when the entry was created in its ledger. Patches created by tools such as patch-package are detected and tracked on the same entry.

If an override or resolution is no longer needed, Pastoralist marks the appendix entry as unused and prints a cleanup notice. The override and its appendix entry are removed only when you run with --remove-unused. Patch files are reported as potentially unused; Pastoralist does not delete patch files for you.

You manage the override or resolution field; Pastoralist manages the appendix.

Workspace Support

In workspace/monorepo setups, Pastoralist:

  • Reads the root package.json or project manifest file
  • Maps overrides, resolutions, and patches to the pastoralist.appendix, with a ledger entry recording when each override was added
  • Reads workspace package manifests when depPaths or workspaces are configured
  • Writes the consolidated appendix to the target package.json, usually the root

Simple Project Architecture

Standard single-package project with overrides:

Monorepo Architecture

Complex workspace setup with shared overrides:

What Are Overrides, Resolutions, and Patches?

Overrides (npm)

Overrides allow you to replace a package version in your dependency tree with a different version. This is npm's way of handling dependency conflicts:

{
  "overrides": {
    "foo": "1.0.0",
    "bar": {
      "baz": "1.0.0"
    }
  }
}

Resolutions (Yarn)

Resolutions serve the same purpose for Yarn users, allowing you to force specific versions:

{
  "resolutions": {
    "foo": "1.0.0",
    "**/bar/baz": "1.0.0"
  }
}

Patches

Patches are custom modifications to node_modules packages, typically created with tools like patch-package. Pastoralist automatically detects and tracks these patches.

Object Anatomy

The Pastoralist object in your package.json provides full transparency into what's being managed:

{
  "overrides": {
    "minimist": "1.2.8"
  },
  "pastoralist": {
    "appendix": {
      "[email protected]": {
        "dependents": {
          "my-app": "minimist@^1.2.6",
          "mkdirp": "minimist@^1.2.5"
        },
        "ledger": {
          "addedDate": "2026-05-30T00:00:00.000Z",
          "reason": "Pin minimist while upstream packages adopt the patched version.",
          "source": "manual"
        }
      }
    }
  }
}

Appendix Properties

  • appendix key: The package and override version, such as [email protected]
  • dependents: Direct, workspace, or transitive packages that still require the override
  • patches: Patch files associated with the package, when any are detected
  • ledger: Always present on entries written by current Pastoralist. Holds addedDate, optional reason and source, security metadata (securityProvider, cves, cveDetails, severity, vulnerableRange, patchedVersion), and optional keep constraints

Nested Override Architecture

How nested overrides work for transitive dependencies:

Design Decisions

Synchronous I/O

Pastoralist uses sync file I/O intentionally. As a CLI tool, predictable execution and simple debugging outweigh async benefits.

Caching

Two caches avoid redundant work: jsonCache (parsed package.json files) and dependencyTreeCache (npm ls output). Caches persist across update() calls - pass clearCache: true to reset.

Rate Limiting

npm registry requests are limited to 5 concurrent to avoid rate limits during security scans.

Dependency Resolution Flow

Complete flow of how dependencies are resolved with overrides:

Copyright © 2026 - All rights reserved

Pastoralist Logo