UNPKG

@datocms/cma-client

Version:
89 lines (68 loc) 2.39 kB
import { v4 } from 'uuid'; function fromUint8ArrayToUrlSafeBase64(bytes: Uint8Array) { const base64 = typeof Buffer === 'undefined' ? btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join('')) : Buffer.from(bytes).toString('base64'); // Convert to URL-safe format (see RFC 4648, sec. 5) return ( base64 // Replace + with - .replace(/\+/g, '-') // Replace / with _ .replace(/\//g, '_') // Drop '==' padding .substring(0, 22) ); } function fromUrlSafeBase64toUint8Array(urlSafeBase64: string): Uint8Array { // Convert from URL-safe format (see RFC 4648, sec. 5) const base64 = urlSafeBase64 // Replace - with + .replace(/-/g, '+') // Replace _ with / .replace(/_/g, '/'); return typeof Buffer === 'undefined' ? Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)) : new Uint8Array(Buffer.from(base64, 'base64')); } export function isValidId(id: string) { // For backward compatibility, first check to see if this is an older-style integer ID formerly used by Dato if (/^\d+$/.test(id)) { const intId = BigInt(id); const maxDatoIntegerId = 281474976710655; // Max 6-byte/48-bit unsigned int return intId <= maxDatoIntegerId; } const bytes = fromUrlSafeBase64toUint8Array(id); // UUIDs are 16 bytes if (bytes.length !== 16) { return false; } // The variant field determines the layout of the UUID // (see RFC 4122, sec. 4.1.1) const variant = bytes.at(8)!; // Variant must be the one described in RFC 4122 if ((variant & 0b11000000) !== 0b10000000) { return false; } // The version number is in the most significant 4 bits // of the time stamp (see RFC 4122, sec. 4.1.3) const version = bytes.at(6)! >> 4; // Version number must be 4 (randomly generated) if (version !== 0x4) { return false; } return true; } export function generateId() { const bytes = v4(null, new Uint8Array(16)); // Here we unset the first bit to ensure [A-Za-f] as the first char. // // If we didn't do this, we would generate IDs that, once encoded // in base64, could start with a '+' or a '/'. This makes them less // easy to copy/paste, with bad DX. // This choice is purely aesthetic: definitely non-mandatory! bytes[0] = bytes[0]! & 0x7f; const base64 = fromUint8ArrayToUrlSafeBase64(bytes); return base64; }