'use client'; import { useEffect, useState } from 'react'; const DEFAULT_STORAGE_KEY = 'chat.visitor.fingerprint'; function generate(): string { if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) { return crypto.randomUUID(); } return `v-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; } export interface UseVisitorFingerprintOptions { /** localStorage key. @default 'chat.visitor.fingerprint' */ storageKey?: string; } /** * Persistent anonymous visitor id, kept in `localStorage`. * * Returns `null` on the first render (SSR-safe) and the stable id from * the second render onwards. Use as `fingerprint` for public chat * transports that need to dedupe sessions per visitor without auth. */ export function useVisitorFingerprint( opts: UseVisitorFingerprintOptions = {}, ): string | null { const storageKey = opts.storageKey ?? DEFAULT_STORAGE_KEY; const [fp, setFp] = useState(null); useEffect(() => { let value: string | null = null; try { value = window.localStorage.getItem(storageKey); if (!value) { value = generate(); window.localStorage.setItem(storageKey, value); } } catch { // Private mode / storage disabled — fall back to ephemeral. value = generate(); } setFp(value); }, [storageKey]); return fp; }