shuffrand
Version:
Cryptographically secure randomness and shuffling — with soul.
101 lines (100 loc) • 3.94 kB
JavaScript
import { randomParamsSchema as x } from "./types.es.js";
import { Constants as h } from "./constants.es.js";
/**
* shuffrand - Cryptographically Secure Random Number Generation
*
* This file contains the core logic for generating cryptographically secure random numbers,
* adhering to a flat, dot-categorized structure for clarity.
*
* @author Doron Brayer <doronbrayer@outlook.com>
* @license MIT
*/
function _(u = {}) {
const b = u.maxFracDigits ?? 3;
if (typeof b != "number" || !Number.isInteger(b) || b < h.MIN_FRACTIONAL_DIGITS || b > h.MAX_FRACTIONAL_DIGITS)
throw new TypeError(
`maxFracDigits (currently ${b}) must be an integer between ${h.MIN_FRACTIONAL_DIGITS} and ${h.MAX_FRACTIONAL_DIGITS} (inclusive) to ensure reliable precision.`
);
x.assert(u);
const w = {
lowerBound: u.lowerBound ?? 0,
upperBound: u.upperBound ?? 2,
// Corrected default upperBound to 2 as per test plan
typeOfNum: u.typeOfNum ?? "integer",
exclusion: u.exclusion ?? "none",
maxFracDigits: u.maxFracDigits ?? 3
};
if (w.typeOfNum === "double" && w.maxFracDigits === 0)
throw new TypeError(
`Invalid cryptoRandom parameters: 'maxFracDigits' cannot be 0 when 'typeOfNum' is 'double'. Use 'typeOfNum: "integer"' for whole numbers.`
);
if (typeof globalThis.crypto > "u" || !globalThis.crypto.getRandomValues)
throw new Error(
"Cryptographically secure random number generator (WebCrypto API) is not available in this environment."
);
const { lowerBound: f, upperBound: g, typeOfNum: d, exclusion: t, maxFracDigits: M } = w, n = Math.min(f, g), i = Math.max(f, g);
if (n === i) {
if (d === "double" && t === "both")
throw new TypeError(
`Invalid range for double with 'both' exclusion: lowerBound (${f}) equals upperBound (${g}).`
);
return n;
}
let e, N = 0;
const T = h.MAX_ATTEMPTS_TO_GENERATE_NUM;
do {
let r = n, m = i;
if (d === "integer") {
if (r = Math.ceil(n), m = Math.floor(i), (t === "lower bound" || t === "both") && r++, (t === "upper bound" || t === "both") && m--, r > m)
throw new TypeError(
`Invalid integer range after exclusions: the original range of [${f}–${g}] with exclusion '${t}' results in an empty integer range.`
);
const y = m - r + 1, s = Math.ceil(Math.log2(y) / 8), p = Math.pow(256, s) - Math.pow(256, s) % y;
let a;
const c = new Uint8Array(s);
do {
globalThis.crypto.getRandomValues(c), a = 0;
for (let o = 0; o < s; o++)
a = a * 256 + c[o];
} while (a >= p);
e = r + a % y;
} else {
const s = 18446744073709552e3;
let p;
const a = new Uint8Array(8);
do {
globalThis.crypto.getRandomValues(a);
let o = 0;
for (let I = 0; I < 8; I++)
o = o * 256 + a[I];
p = o / s;
} while (p === 1);
e = r + p * (m - r);
const c = Number(M);
if (!isNaN(c) && c >= 0) {
const o = Math.pow(10, c);
e = Math.round(e * o) / o;
}
}
let l = !1;
if (d === "double" ? (t === "lower bound" && Math.abs(e - n) < Number.EPSILON || t === "upper bound" && Math.abs(e - i) < Number.EPSILON || t === "both" && (Math.abs(e - n) < Number.EPSILON || Math.abs(e - i) < Number.EPSILON)) && (l = !0) : (t === "lower bound" && e === n || t === "upper bound" && e === i || t === "both" && (e === n || e === i)) && (l = !0), l) {
N++;
continue;
}
if (d === "double" && Number.isInteger(e)) {
N++;
continue;
}
break;
} while (N < T);
if (N >= T) {
let r = `the exclusion constraint: '${t}'`;
throw d === "double" && (r += " or the non-integer requirement"), new Error(
`Unable to generate a random number within the range [${n}–${i}] that satisfies ${r}. Max attempts (${T}) reached.`
);
}
return e;
}
export {
_ as cryptoRandom
};