shuffrand
Version:
Cryptographically secure randomness and shuffling — with soul.
10 lines (9 loc) • 3.14 kB
JavaScript
;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const O=require("./types.cjs.js"),h=require("./constants.cjs.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 E(s={}){const b=s.maxFracDigits??3;if(typeof b!="number"||!Number.isInteger(b)||b<h.Constants.MIN_FRACTIONAL_DIGITS||b>h.Constants.MAX_FRACTIONAL_DIGITS)throw new TypeError(`maxFracDigits (currently ${b}) must be an integer between ${h.Constants.MIN_FRACTIONAL_DIGITS} and ${h.Constants.MAX_FRACTIONAL_DIGITS} (inclusive) to ensure reliable precision.`);O.randomParamsSchema.assert(s);const w={lowerBound:s.lowerBound??0,upperBound:s.upperBound??2,typeOfNum:s.typeOfNum??"integer",exclusion:s.exclusion??"none",maxFracDigits:s.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:I}=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,y=0;const T=h.Constants.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 N=m-r+1,l=Math.ceil(Math.log2(N)/8),p=Math.pow(256,l)-Math.pow(256,l)%N;let a;const c=new Uint8Array(l);do{globalThis.crypto.getRandomValues(c),a=0;for(let o=0;o<l;o++)a=a*256+c[o]}while(a>=p);e=r+a%N}else{const l=18446744073709552e3;let p;const a=new Uint8Array(8);do{globalThis.crypto.getRandomValues(a);let o=0;for(let M=0;M<8;M++)o=o*256+a[M];p=o/l}while(p===1);e=r+p*(m-r);const c=Number(I);if(!isNaN(c)&&c>=0){const o=Math.pow(10,c);e=Math.round(e*o)/o}}let u=!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))&&(u=!0):(t==="lower bound"&&e===n||t==="upper bound"&&e===i||t==="both"&&(e===n||e===i))&&(u=!0),u){y++;continue}if(d==="double"&&Number.isInteger(e)){y++;continue}break}while(y<T);if(y>=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}exports.cryptoRandom=E;