const re = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; /** * Convert a byte array to an hexadecimal string. */ function toHexString(a) { return Array.from(a, b => b.toString(16).padStart(2, '0')).join(''); } /** * Generate a UUIDv7 (Time-Sortable Identifier). * @returns {string} * The generated UUIDv7 (`00000000-0000-0000-0000-000000000000`). * @example * let id; console.log(id = generateId()); * console.log(new Date(validateId(id)).toLocaleString()); */ export function generateId() { const timestamp = Date.now(); // Convert the timestamp to a 6-byte hexadecimal string. const timestampHex = timestamp.toString(16).padStart(12, '0'); // Generate 10 random bytes for the remaining parts. const randomBytes = new Uint8Array(10); crypto.getRandomValues(randomBytes); const randomPart1 = randomBytes.subarray(0, 2); const randomPart2 = randomBytes.subarray(2, 4); const randomPart3 = randomBytes.subarray(4); randomPart1[0] = (randomPart1[0] & 0x0F) | 0x70; // version randomPart2[0] = (randomPart2[0] & 0x3F) | 0x80; // variant return [ timestampHex.slice(0, 8), // first 4 bytes of timestamp timestampHex.slice(8, 12), // last 2 bytes of timestamp toHexString(randomPart1), // version 7 (2 bytes) toHexString(randomPart2), // variant+random (2 bytes) toHexString(randomPart3), // remaining 6 bytes ].join('-'); // 00000000-0000-0000-0000-000000000000 } /** * Validate a UUIDv7 (Time-Sortable Identifier). * @param {string} id * @returns {number} * The Unix timestamp encoded in the UUIDv7. */ export function validateId(id) { return parseInt(id.match(re).slice(1, 3).join(''), 16); }