Node secure randomness implementations
secureRandomIntRange

Scheme

Basically what you generated with painstaking work is to create an implementation of the Complex Discard Method described in section A.5.2. That's a known secure scheme, so kudos for that.

I don't like the way bytes is calculated in the secureRandomIntRange function. It probably uses floating point and that may introduce rounding errors.  It took quite a search, but it is easy to calculate the bit length of the integer using Math.clz32(x) where x is the number:

sizeBytes = ((32 - Math.clz32(range)) + 7) / 8;


Note the name change from bytes to sizeBytes.

If you look closely you'll notice that this creates a problem with the value 0, as that is now represented by 0 bytes. Best not to allow ranges of zero elements. And for one element, you may just want to return that element. It also shows that going to 2^32 and over will represent problems, so that's a maximum for the range in this case (which is different from MAX_SAFE_INTEGER because that's 2^53).

Performance

If the range is somewhat higher than half of maxValueOfBytes then the scheme will loop with a 50% certainty. You can somewhat reduce that by deliberately generating a higher amount of bytes. That way you don't need as many bytes from the random number generator in the worst case scenario (at the obvious disadvantage of requiring more bytes for the good scenario). Note thought that you can get very unlucky tossing coins, and you might want to avoid such hickups where the while loop keeps on running.

One little trick that is sometimes performed is to check if range has just one bit set, i.e. is a power of two. In that case you can just randomly generate bits and return those. For instance, if the range is 256, then you can just return a single byte. If the range is 128 then you can generate a single byte, set the most significant bit to 0 and return the result. Basically, if you use precisely as many bits as required then your method degenerates to the Simple Discard Method.



secureRandomFloat

Random float is designed well, except that you might want to check how it handles precision for larger byte sizes. There is likely a maximum integer size as well, so rounding will probably get problematic for larger values of parameter bytes.



secureRandomChoice

It does seem to pick a random from a list of options. So sure, fine.



Code


Already mentioned in passing: there should be more guard checks on the input, especially on the secureRandomIntRange low end values.
Guard statements should result in an exception, not in a function returning null. You're not just propagating the billion dollar mistake, you're adding to it.
Try to avoid floating point operations. I know, I know, that's a stupid requirement for JavaScript, but really it is important because proving that your JavaScript doesn't have any rounding issues might be tricky. Hence the integer calculations performed for sizeBytes above: I'm sure that they work as long as everything is within the maximum size of a 32 bit integer.


That last note is probably going to be contested, especially since floats should not loose precision for calculations that stay within [0, 2^53). So you may want to keep an open mind to different arguments about it.