File size: 1,294 Bytes
3c22061
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f9220c
3c22061
7f9220c
 
3c22061
 
 
 
 
7f9220c
3c22061
 
 
 
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
import { randomBytes } from 'node:crypto';

const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
const TIME_LEN = 10;
const RANDOM_LEN = 16;

function encodeTime(ms: number): string {
	let out = '';
	let t = ms;
	for (let i = 0; i < TIME_LEN; i++) {
		out = ENCODING[t % 32] + out;
		t = Math.floor(t / 32);
	}
	return out;
}

function randomDigits(): number[] {
	const bytes = randomBytes(RANDOM_LEN);
	return Array.from(bytes, (b) => b % 32);
}

function incrementDigits(digits: number[]): void {
	for (let i = digits.length - 1; i >= 0; i--) {
		if (digits[i] < 31) {
			digits[i]++;
			return;
		}
		digits[i] = 0;
	}
	throw new Error('ULID random component overflow');
}

/**
 * Returns a monotonic ULID generator: ids created in the same millisecond
 * strictly increase, so lexical order equals creation order within a process.
 */
export function createUlidFactory(now: () => number = Date.now): () => string {
	let lastTime = -1;
	let lastRandom: number[] | null = null;
	return () => {
		const t = Math.floor(now());
		if (lastRandom !== null && t <= lastTime) {
			incrementDigits(lastRandom);
		} else {
			lastTime = t;
			lastRandom = randomDigits();
		}
		return encodeTime(lastTime) + lastRandom.map((d) => ENCODING[d]).join('');
	};
}

export const ulid = createUlidFactory();