Yes I think you are right when NPM first introduced it, which was in NPM 5 [1]. At that time package locks were new.
Node 16 shipped with NPM 8, and by then this was not the behavior any more. I don't remember exactly when, but I believe it might've been in NPM 7 when this changed. Node 15 would have been the first release to ship with at least NPM 7 [2]
Honestly no guarantees it worked as intended right at that release, but the intention is clear.
EDIT 4: I realize that the following may present retort but it matches my mental model perfectly:
> If you manually edit your package.json to have different ranges and run npm i and those ranges aren't compatible with your package-lock.json then the latter will be updated with version that are compatible with your package.json. Further runs of npm i will be as with 2 above.
This is what I've mentioned elsewhere: You should not update package.json's versions and deploy it and expect that versions won't change when NPM install runs. In fact, npm ci will just fail in this situation, which is a Good Thing (TM), and is documented as such.
LAST EDIT SRSLY:
It is conceivable, especially given OPs other red flags, that perhaps they were on an older Node/NPM like Node 14.
That's another practice that I regularly do, pin NPM itself in deploy scripts. While I was using Node 14, I was using NPM 8, because you can just "npm i npm@8 -g" to upgrade your NPM regardless of Node version.
Node 16 shipped with NPM 8, and by then this was not the behavior any more. I don't remember exactly when, but I believe it might've been in NPM 7 when this changed. Node 15 would have been the first release to ship with at least NPM 7 [2]
EDIT: Node 16.0.0 shipped with NPM 7.8.0
https://github.com/nodejs/node/blob/main/doc/changelogs/CHAN...
EDIT 2: A StackOverflow confirming NPM 5 behaved the way you remember: https://stackoverflow.com/questions/45022048/why-does-npm-in...
EDIT 3: And, as that SO documents, 5.4.2 changed the behavior to prevent this, here's the GitHub issue: https://github.com/npm/npm/issues/17979#issuecomment-3327012...
Honestly no guarantees it worked as intended right at that release, but the intention is clear.
EDIT 4: I realize that the following may present retort but it matches my mental model perfectly:
> If you manually edit your package.json to have different ranges and run npm i and those ranges aren't compatible with your package-lock.json then the latter will be updated with version that are compatible with your package.json. Further runs of npm i will be as with 2 above.
This is what I've mentioned elsewhere: You should not update package.json's versions and deploy it and expect that versions won't change when NPM install runs. In fact, npm ci will just fail in this situation, which is a Good Thing (TM), and is documented as such.
LAST EDIT SRSLY:
It is conceivable, especially given OPs other red flags, that perhaps they were on an older Node/NPM like Node 14.
That's another practice that I regularly do, pin NPM itself in deploy scripts. While I was using Node 14, I was using NPM 8, because you can just "npm i npm@8 -g" to upgrade your NPM regardless of Node version.
[1] https://blog.npmjs.org/post/171556855892/introducing-npm-ci-...
[2] https://nodejs.org/en/about/previous-releases