| /** | |
| * UUID v4 generator. | |
| * | |
| * Uses crypto.getRandomValues when available (React Native / modern browsers) | |
| * and falls back to Math.random otherwise. | |
| */ | |
| export function uuid(): string { | |
| if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') { | |
| // Native crypto path | |
| const bytes = new Uint8Array(16); | |
| crypto.getRandomValues(bytes); | |
| // Set version 4 bits | |
| bytes[6] = (bytes[6] & 0x0f) | 0x40; | |
| // Set variant bits (RFC 4122) | |
| bytes[8] = (bytes[8] & 0x3f) | 0x80; | |
| return formatBytes(bytes); | |
| } | |
| // Fallback: Math.random based | |
| return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { | |
| const r = (Math.random() * 16) | 0; | |
| const v = c === 'x' ? r : (r & 0x3) | 0x8; | |
| return v.toString(16); | |
| }); | |
| } | |
| function formatBytes(bytes: Uint8Array): string { | |
| const hex: string[] = []; | |
| for (let i = 0; i < bytes.length; 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(''), | |
| ].join('-'); | |
| } | |