@expofp/floorplan
Version:
Interactive floor plan library for expos and events
2 lines (1 loc) • 2.71 kB
JavaScript
const p="efp-telemetry",X=3e5,Y=1e3,x=63e3;function b(e){return e?`${p}:${e}`:p}function k(e=p){return{get(){try{const r=localStorage.getItem(e);return r?JSON.parse(r):null}catch{return null}},set(r){try{localStorage.setItem(e,JSON.stringify(r))}catch(t){console.error("Failed to persist telemetry data",t)}},clear(){try{localStorage.removeItem(e)}catch(r){console.error("Failed to clear telemetry data",r)}}}}function $(e,r){if(!r)return e;const t=e.indexOf("#"),o=t===-1?e:e.slice(0,t),i=t===-1?"":e.slice(t);let l="?";return o.includes("?")&&(l=o.endsWith("?")||o.endsWith("&")?"":"&"),`${o}${l}expo=${encodeURIComponent(r)}${i}`}function w(e){const r=new TextEncoder,t=[];let o=r.encode(JSON.stringify({...e,items:[]})).byteLength;for(const l of e.items){const u=r.encode(JSON.stringify(l)).byteLength,a=t.length>0?u+1:u;if(t.length>0&&o+a>63e3)break;o+=a,t.push(l)}const i=t.length===e.items.length?e:{...e,items:t};return{payload:i,body:JSON.stringify(i),bytes:o}}function D(e){return e>=400&&e<500&&e!==408&&e!==429}function A(e){return e.length<=1e3?e:(console.warn(`Telemetry queue full: dropping ${e.length-1e3} oldest events`),e.slice(-1e3))}function F(e){const r=e.headers.get("Retry-After");if(!r)return 0;let t=0;const o=Number(r);if(Number.isFinite(o))t=o*1e3;else{const i=Date.parse(r);isNaN(i)||(t=Math.max(0,i-Date.now()))}return Math.min(t,3e5)}export function createTelemetry(e){const{url:r,expo:t,expoId:o,storage:i=k(b(t))}=e,l=$(r,t);let u=e.kioskId??"";function a(){const n=i.get();return n?(n.expo=t,n.expoId=o,n.kioskId=u,n):{expo:t,expoId:o,kioskId:u,items:[]}}function M(n){u=n}let h=!1,m=!1,T=0;async function E(){if(h){m=!0;return}h=!0;try{do{if(m=!1,performance.now()<T)break;let n=null;try{let s=a().items.length;for(;s>0;){const f=a();if(f.items.length===0)break;const{payload:g,body:I,bytes:N}=w(f),d=g.items.length;y(f,f.items.slice(d)),n=g.items,s-=d;const c=await fetch(l,{method:"POST",body:I,headers:{"Content-Type":"application/json"},credentials:"omit",keepalive:N<=63e3});if(await c.text().catch(()=>{}),n=null,!c.ok){if(D(c.status)){console.error(`Telemetry batch refused by the endpoint: HTTP ${c.status} (dropped: ${d})`);continue}if(c.status===429||c.status===503){const R=F(c);R>0&&(T=performance.now()+R)}S(g.items),console.error(`Failed to send telemetry: HTTP ${c.status} (requeued: ${d})`);break}}}catch(s){n&&S(n),console.error("Failed to send telemetry",s)}}while(m)}finally{h=!1}}function y(n,s){s.length===0?i.clear():i.set({...n,items:s})}function S(n){if(n.length===0)return;const s=a();y(s,A([...n,...s.items]))}function _(n){const s=a();s.items.push(n),y(s,A(s.items))}async function O(n){_(n),await E()}return{enqueue:_,send:O,flush:E,setKioskId:M}}