Made on your device
Every character comes from crypto.getRandomValues, the browser's cryptographic random generator.
Numbers that would make some characters likelier are thrown away, so every password is equally likely. Without secure randomness, nothing is generated.
export function randomInt(n: number): number {
if (!Number.isSafeInteger(n) || n < 1 || n > UINT32_RANGE) {
throw new RangeError(`randomInt needs an integer range between 1 and 2^32, got ${n}`);
}
const source = secureSource();
// 2^32 is rarely a multiple of n, so reducing every draw modulo n would make the low results
// slightly more likely. Draws at or above the largest multiple of n are discarded and redrawn;
// even in the worst case under half of all draws are rejected, so the loop ends quickly.
const limit = Math.floor(UINT32_RANGE / n) * n;
const draw = new Uint32Array(1);
for (;;) {
source.getRandomValues(draw);
const value = draw[0] ?? limit;
if (value < limit) return value % n;
}
}