Resolution starts at your package.json and works outward. For each dependency the manager fetches the package's metadata (How Registries Work) and picks the highest published version that satisfies the range, then repeats for that version's own dependencies until every edge points at an exact version, reusing any version already chosen that fits. Range syntax is MERN Stack Development, Semantic Versioning; here it is applied to BookNest's content-type conflict from npm Install.
cd ~/v5-ch1/hoist-demo # a copy of BookNest's package.json and lockfile
jq -r '.packages | to_entries[] | select(.value.dependencies["content-type"])
| "\(.key) wants \(.value.dependencies["content-type"])"' package-lock.json
npx -y semver -r '^1.0.5' 1.0.4 1.0.5 2.0.0 2.1.0
npx -y semver -r '^2.0.0' 1.0.5 2.0.0 2.1.0node_modules/body-parser wants ^2.0.0 node_modules/express wants ^1.0.5 node_modules/negotiator wants ^2.1.0 node_modules/type-is wants ^2.0.0 1.0.5 2.0.0 2.1.0
The semver command (the library npm 2,036 uses) prints every candidate that satisfies a range, and the resolver takes the last. ^1.0.5 allows only 1.0.5, since a caret never crosses a major version, and ^2.0.0 gets 2.1.0 (content-type 3.1.1 exists, but nothing asks for it). No version satisfies both express and body-parser, so the graph holds two versions of one package and the linking step must decide where each copy goes. pnpm 69,400 , Yarn 11,798 and Bun 73,307 apply the same highest-satisfying rule, except that pnpm 11 and later skip versions less than a day old (minimumReleaseAge, Install Scripts and Audits).