/** * Generate a RFC 4122 v4 UUID. * * `crypto.randomUUID()` is only exposed on secure contexts (HTTPS, localhost, * 127.0.0.1, file://). The app is served over bare LAN IPs for on-device * phone previews (e.g. http://192.168.1.167:5173) โ€” a non-secure origin โ€” * where `randomUUID` is undefined and every optimistic-id call site crashed * with `TypeError: crypto.randomUUID is not a function`. * * `crypto.getRandomValues` IS available on insecure contexts (unlike * `crypto.subtle` which is gated), so the fallback here is v4-compliant and * cryptographically acceptable for client-side optimistic ids. * * Background: https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID */ export function generateId(): string { // Secure-context fast path. if (typeof globalThis.crypto?.randomUUID === 'function') { return globalThis.crypto.randomUUID(); } // Fallback: 16 random bytes โ†’ format per RFC 4122 ยง4.4. const bytes = new Uint8Array(16); globalThis.crypto.getRandomValues(bytes); // Set version (4) and variant (10xx) bits. bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; const hex: string[] = []; for (let i = 0; i < 16; i++) hex.push(bytes[i].toString(16).padStart(2, '0')); return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`; }