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 aledgerentry recording when each override was added - Reads workspace package manifests when
depPathsorworkspacesare 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, optionalreasonandsource, security metadata (securityProvider,cves,cveDetails,severity,vulnerableRange,patchedVersion), and optionalkeepconstraints
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: