1const maxTimestamp = 2 ** 48 - 1
2const base32Chars = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
3
4/** @internal */
5export const ulidString = (timestampMillis: number, bytes: Uint8Array): string => {
6 if (bytes.length !== 10) {
7 throw new Error(`ULID randomness must be exactly 10 bytes, received ${bytes.length}`)
8 }
9
10 const timestamp = Math.min(Math.max(0, Math.trunc(timestampMillis)), maxTimestamp)
11 let out = ""
12 for (let shift = 45; shift >= 0; shift -= 5) {
13 // Take the low 5 bits and map NaN to zero, matching UUIDv7's byte encoding.
14 out += base32Chars[Math.floor(timestamp / 2 ** shift) & 0x1f]
15 }
16
17 let accumulator = 0
18 let bits = 0
19 for (const byte of bytes) {
20 accumulator = (accumulator << 8) | byte
21 bits += 8
22 while (bits >= 5) {
23 bits -= 5
24 out += base32Chars[(accumulator >>> bits) & 0x1f]
25 }
26 accumulator &= (1 << bits) - 1
27 }
28
29 return out
30}