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();