Heap's Algorithm generates each permutation by swapping two elements of a
working array in place, and that working array was the argument itself, so
permutationsHeap([1, 2, 3]) left the caller holding [3, 2, 1]. Two calls on
one array returned different lists, and a frozen array threw.
Copy the input up front and swap on the copy, as shuffle and numericSort
already do. The returned permutations are unchanged.
combinationsReplacement recurses on x.slice(i, x.length), which at i = 0 is
as long as the input, so the only exit left is the k === 1 base case. For
k <= 0 the counter starts below that case and moves away from it, and the
recursion overflows the stack. The sibling combinations recurses on
x.slice(i + 1, x.length), runs out of input and returns [] for the same
arguments.
Guard the non-positive group size up front so both functions agree.