shuffrand
Version:
Cryptographically secure randomness and shuffling — with soul.
104 lines (103 loc) • 3.65 kB
JavaScript
import { shuffleParamsSchema as y } from "./types.es.js";
import { cryptoRandom as x } from "./random.es.js";
/**
* shuffrand - Cryptographically Secure Array Shuffling
*
* This file contains the core logic for shuffling arrays using a cryptographically secure method,
* adhering to a flat, dot-categorized structure for clarity.
*
* @author Doron Brayer <doronbrayer@outlook.com>
* @license MIT
*/
function g(I = [], e = {}) {
if (e === null)
throw new TypeError(
"Invalid cryptoShuffle parameters: 'options' cannot be null. Please provide an object or omit it."
);
if (e.startIndex !== void 0) {
if (typeof e.startIndex != "number")
throw new TypeError(
`Invalid cryptoShuffle parameters: startIndex must be a number (was ${typeof e.startIndex})`
);
if (!Number.isInteger(e.startIndex))
throw new TypeError(
`Invalid cryptoShuffle parameters: startIndex must be an integer (was ${e.startIndex})`
);
}
if (e.endIndex !== void 0) {
if (typeof e.endIndex != "number")
throw new TypeError(
`Invalid cryptoShuffle parameters: endIndex must be a number (was ${typeof e.endIndex})`
);
if (!Number.isInteger(e.endIndex))
throw new TypeError(
`Invalid cryptoShuffle parameters: endIndex must be an integer (was ${e.endIndex})`
);
}
const n = {
arr: I,
isDestructive: e.isDestructive ?? !1,
preventIdentical: e.preventIdentical ?? !1,
startIndex: e.startIndex,
// Will be undefined if not provided, which is valid per schema
endIndex: e.endIndex
// Will be undefined if not provided, which is valid per schema
};
let c;
try {
y.assert(n), c = {
arr: n.arr ?? [],
isDestructive: n.isDestructive ?? !1,
preventIdentical: n.preventIdentical ?? !1,
// Apply defaults for startIndex and endIndex
startIndex: n.startIndex ?? 0,
endIndex: n.endIndex ?? n.arr?.length ?? 0
// Default to array length or 0 if arr is undefined/empty
};
} catch (r) {
throw new TypeError(`Invalid cryptoShuffle parameters: ${r.summary || r.message}`);
}
const { arr: d, isDestructive: h, preventIdentical: m, startIndex: l, endIndex: s } = c, i = d.length;
if (l < 0 || l > i)
throw new TypeError(
`Invalid cryptoShuffle parameters: 'startIndex' (${l}) must be between 0 and the array length (${i}), inclusive.`
);
if (s < 0 || s > i)
throw new TypeError(
`Invalid cryptoShuffle parameters: 'endIndex' (${s}) must be between 0 and the array length (${i}), inclusive.`
);
const t = h ? d : [...d], a = t.length, o = Math.max(0, Math.min(l, a)), p = Math.max(o, Math.min(s, a));
let u = null;
if (m)
try {
u = JSON.stringify(d);
} catch (r) {
throw new TypeError(
`Invalid cryptoShuffle parameters: Array elements cannot be serialized for 'preventIdentical' comparison. Ensure all elements are JSON-serializable. Original error: ${r.message}`
);
}
for (let r = p - 1; r > o; r--) {
const f = x({
lowerBound: o,
upperBound: r,
typeOfNum: "integer",
exclusion: "none"
});
[t[r], t[f]] = [t[f], t[r]];
}
if (m && u !== null && a > 1) {
let r;
try {
r = JSON.stringify(t);
} catch (f) {
throw new TypeError(
`Invalid cryptoShuffle parameters: Shuffled array elements cannot be serialized for 'preventIdentical' comparison. Ensure all elements are JSON-serializable. Original error: ${f.message}`
);
}
r === u && ([t[0], t[a - 1]] = [t[a - 1], t[0]]);
}
return t;
}
export {
g as cryptoShuffle
};