Each dependency edge carries a semantic-version range, not a fixed version. A package manager resolves it by picking the highest published version in the registry that satisfies the range:
SemVer precedence: compare major, then minor, then patch.
^X.Y.Z (X>0) → X.Y.Z ≤ v < (X+1).0.0
^0.Y.Z (Y>0) → 0.Y.Z ≤ v < 0.(Y+1).0
^0.0.Z → 0.0.Z ≤ v < 0.0.(Z+1)
~X.Y.Z → X.Y.Z ≤ v < X.(Y+1).0
=X.Y.Z → v == X.Y.Z (exact)
If every consumer's highest-satisfying version is the same published version, the resolver dedupes: one physical copy of D is installed and shared. If the ranges disagree, a flat (single-version) resolver reports a conflict; an npm-style resolver instead nests a second (or third) copy of D so each consumer still gets a version it is compatible with — at the cost of extra installs and no longer sharing state through D.
- Range type buttons — switch all three consumer requirements between caret, tilde and exact matching against the same base version.
- Sliders A/B/C — pick which published registry version each consumer's requirement is anchored to.
- Publish new D version — appends a new version to the registry (bumps patch, occasionally minor/major), which can change what the highest-satisfying match is.
- Nested installs toggle — off = strict single-version resolution (conflict halts the build); on = permissive resolution (extra copies keep the build green).
Real-world relevance: this is exactly how npm/yarn/pnpm and CocoaPods/Gradle resolve ^/~ ranges across a mobile app's dependency tree, and why "works on my machine" version drift happens the moment two consumers pin incompatible ranges on the same shared library.