-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Expand file tree
/
Copy pathrandom.ts
More file actions
78 lines (70 loc) · 2.61 KB
/
Copy pathrandom.ts
File metadata and controls
78 lines (70 loc) · 2.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
* Cryptographically secure random utilities built on `crypto.getRandomValues()`.
* Works in all contexts including non-secure (HTTP) browser environments.
*/
/** Lowercase alphanumeric characters used as the default alphabet for random strings. */
export const LOWERCASE_ALPHANUMERIC_ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789'
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
/**
* Generates cryptographically secure random bytes.
* @param length - Number of bytes to generate
* @returns Uint8Array of random bytes
*/
export function generateRandomBytes(length: number): Uint8Array {
return crypto.getRandomValues(new Uint8Array(length))
}
/**
* Generates a cryptographically secure random hex string.
* @param length - Number of hex characters (default: 16)
* @returns Lowercase hex string of the given length
*/
export function generateRandomHex(length = 16): string {
const bytes = generateRandomBytes(Math.ceil(length / 2))
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
.slice(0, length)
}
/**
* Generates a cryptographically secure random alphanumeric string.
* @param length - Number of characters (default: 16)
* @returns Random string composed of A-Z, a-z, 0-9
*/
export function generateRandomString(length = 16): string {
const bytes = generateRandomBytes(length)
return Array.from(bytes)
.map((b) => CHARS[b % CHARS.length])
.join('')
}
/**
* Returns a cryptographically secure random float in [0, 1).
* Drop-in replacement for `Math.random()`.
*/
export function randomFloat(): number {
const [value] = crypto.getRandomValues(new Uint32Array(1))
return value / 0x100000000
}
/**
* Returns a cryptographically secure random integer in [min, max).
* Uses rejection sampling for uniform distribution (no modulo bias).
* @param min - Inclusive lower bound
* @param max - Exclusive upper bound
*/
export function randomInt(min: number, max: number): number {
const range = max - min
if (range <= 0) throw new RangeError(`randomInt: max (${max}) must be greater than min (${min})`)
const threshold = (0x100000000 - (0x100000000 % range)) >>> 0
let value: number
do {
;[value] = crypto.getRandomValues(new Uint32Array(1))
} while (value >= threshold)
return min + (value % range)
}
/**
* Returns a uniformly random element from a non-empty array.
* @param items - Array to sample from (must have at least one element)
*/
export function randomItem<T>(items: readonly T[]): T {
if (items.length === 0) throw new RangeError('randomItem: array must not be empty')
return items[randomInt(0, items.length)]
}