// A local random guest key restores this browser's history, never customer order identity. var VisitorSession = (function(){ const storageName = 'baiyue-visitor-key'; const nativeFetch = typeof window.fetch === 'function' ? window.fetch.bind(window) : fetch; let ready = null, established = false; function localKey(){ try{ const saved = localStorage.getItem(storageName); if(saved && /^[0-9a-f]{64}$/.test(saved)) return saved; const crypto = window.crypto; if(!crypto || !crypto.getRandomValues) return null; const bytes = crypto.getRandomValues(new Uint8Array(32)); const key = Array.from(bytes, value => value.toString(16).padStart(2, '0')).join(''); localStorage.setItem(storageName, key); return key; }catch(error){ return null; } // HttpOnly cookie can still retain this guest when storage is disabled. } function ensure(){ if(ready) return ready; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 10000); ready = nativeFetch('/api/visitor/session', {method:'POST',credentials:'same-origin', headers:{'Content-Type':'application/json'},body:JSON.stringify({key:localKey()}),signal:controller.signal}) .then(async response => { const data = await response.json(); if(!response.ok || !data.ok || typeof data.key !== 'string' || !/^[0-9a-f]{64}$/.test(data.key)){ throw new Error('访客会话暂时无法建立,请刷新后重试。'); } try{ localStorage.setItem(storageName, data.key); }catch(error){} established = true; }).catch(error => { ready = null; established = false; throw error; }).finally(() => clearTimeout(timer)); return ready; } function request(path, options){ const init = Object.assign({}, options, {credentials:'same-origin'}); function send(){ if(init.signal && init.signal.aborted) return Promise.reject(Object.assign(new Error('Aborted'), {name:'AbortError'})); const session = ready; return nativeFetch(path, init).then(async response => { if(response.status !== 401) return response; if(ready === session){ ready = null; established = false; } await ensure(); return nativeFetch(path, init); }); } return established ? send() : ensure().then(send); } return {fetch:request}; })();