As often as you like, but stick to it.
If you fix a security bug, fix and release immediately, and possibly remove all versions having that bug; this may annoy some of your users, but not as much as a security breach. If you fix a major bug, release as soon as possible. If you fix a bug hitting only a few users, release when you see fit.
If you add a new feature, release a beta or RC. When you get some feedback, fix bugs (if any) and release a new minor.
If you remove a feature or change an old one so it breaks API (or ABI, if apylicable; I don’t think it’s relevant to NPM packages), release a beta/RC, wait for the results, and do the major release. I do this type of release as rarely as possible, and if I do, I break the old API as many places as possible, if that makes my library easier to use.