UNPKG

@tinytapanalytics/sdk

Version:

Behavioral psychology platform that detects visitor frustration, predicts abandonment, and helps you save at-risk conversions in real-time

1 lines 6.45 kB
var e={d:function(t,i){for(var s in i)e.o(i,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:i[s]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{h:function(){return i}});class i{constructor(e,t){this.metrics={},this.isActive=!1,this.observers=new Set,this.config=e,this.sdk=t}start(){!this.isActive&&this.isPerformanceAPIAvailable()&&(this.isActive=!0,this.collectInitialMetrics(),this.setupCoreWebVitalsObservers(),this.setupResourceTimingObserver(),this.setupNavigationTimingObserver(),this.setupUserTimingObserver(),this.scheduleMetricsCollection(),this.config.debug)}stop(){this.isActive&&(this.isActive=!1,this.observers.forEach(e=>e.disconnect()),this.observers.clear(),this.sendPerformanceData(),this.config.debug)}collectInitialMetrics(){if(!window.performance||!window.performance.timing)return;const e=window.performance.timing,t=e.navigationStart;this.metrics={domInteractive:e.domInteractive>0?e.domInteractive-t:void 0,dnsLookup:e.domainLookupEnd-e.domainLookupStart,tcpConnect:e.connectEnd-e.connectStart,ttfb:e.responseStart>0?e.responseStart-t:void 0}}setupCoreWebVitalsObservers(){this.observeMetric("largest-contentful-paint",e=>{const t=e[e.length-1];this.metrics.lcp=t.startTime}),this.observeMetric("first-input",e=>{const t=e[0];this.metrics.fid=t.processingStart-t.startTime}),this.observeMetric("layout-shift",e=>{let t=0;e.forEach(e=>{e.hadRecentInput||(t+=e.value)}),this.metrics.cls=t}),this.observeMetric("paint",e=>{e.forEach(e=>{"first-contentful-paint"===e.name&&(this.metrics.fcp=e.startTime)})})}setupResourceTimingObserver(){this.observeMetric("resource",e=>{const t=[];let i=0;e.forEach(e=>{const s={name:e.name,type:this.getResourceType(e.name),size:e.transferSize||0,duration:e.duration,startTime:e.startTime};t.push(s),i+=s.size}),this.metrics.resourceCount=(this.metrics.resourceCount||0)+t.length,this.metrics.totalResourceSize=(this.metrics.totalResourceSize||0)+i;const s=t.filter(e=>e.duration>1e3);s.length>0&&this.sdk.track("slow_resources",{count:s.length,resources:s.map(e=>({name:e.name,type:e.type,duration:e.duration}))})})}setupNavigationTimingObserver(){this.observeMetric("navigation",e=>{const t=e[0];t&&(this.metrics.dnsLookup=t.domainLookupEnd-t.domainLookupStart,this.metrics.tcpConnect=t.connectEnd-t.connectStart,this.metrics.ttfb=t.responseStart-t.startTime)})}setupUserTimingObserver(){this.observeMetric("measure",e=>{this.metrics.customMeasures||(this.metrics.customMeasures={}),e.forEach(e=>{this.metrics.customMeasures[e.name]=e.duration})}),this.observeMetric("mark",e=>{this.metrics.customMarks||(this.metrics.customMarks={}),e.forEach(e=>{this.metrics.customMarks[e.name]=e.startTime})})}observeMetric(e,t){try{const i=new PerformanceObserver(e=>{t(e.getEntries())});i.observe({entryTypes:[e]}),this.observers.add(i)}catch(i){this.config.debug}}scheduleMetricsCollection(){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{this.collectDOMContentLoadedMetric()}):this.collectDOMContentLoadedMetric(),"complete"===document.readyState?(this.collectLoadCompleteMetric(),setTimeout(()=>this.sendPerformanceData(),1e3)):window.addEventListener("load",()=>{this.collectLoadCompleteMetric(),setTimeout(()=>this.sendPerformanceData(),1e3)}),window.addEventListener("beforeunload",()=>{this.sendPerformanceData()}),document.addEventListener("visibilitychange",()=>{document.hidden&&this.sendPerformanceData()})}collectDOMContentLoadedMetric(){if(window.performance&&window.performance.timing){const e=window.performance.timing,t=e.navigationStart;e.domContentLoadedEventEnd>0&&(this.metrics.domContentLoaded=e.domContentLoadedEventEnd-t)}}collectLoadCompleteMetric(){if(window.performance&&window.performance.timing){const e=window.performance.timing,t=e.navigationStart;e.loadEventEnd>0&&(this.metrics.loadComplete=e.loadEventEnd-t)}}sendPerformanceData(){const e={metrics:this.metrics,url:window.location.href,userAgent:navigator.userAgent,timestamp:Date.now(),viewport:{width:window.innerWidth,height:window.innerHeight},connection:this.getConnectionInfo()};this.sdk.track("performance_metrics",e),this.checkPerformanceThresholds(),this.config.debug}checkPerformanceThresholds(){const e=[];this.metrics.lcp&&this.metrics.lcp>2500&&e.push("LCP > 2.5s"),this.metrics.fid&&this.metrics.fid>100&&e.push("FID > 100ms"),this.metrics.cls&&this.metrics.cls>.1&&e.push("CLS > 0.1"),this.metrics.fcp&&this.metrics.fcp>1800&&e.push("FCP > 1.8s"),this.metrics.ttfb&&this.metrics.ttfb>600&&e.push("TTFB > 600ms"),e.length>0&&this.sdk.track("performance_issues",{issues:e,metrics:this.metrics,severity:e.length>2?"high":"medium"})}mark(e){if(this.isActive&&window.performance&&window.performance.mark)try{window.performance.mark(e),this.config.debug}catch(t){this.config.debug}}measure(e,t,i){if(this.isActive&&window.performance&&window.performance.measure)try{t&&i?window.performance.measure(e,t,i):t?window.performance.measure(e,t):window.performance.measure(e),this.config.debug}catch(s){this.config.debug}}getMetrics(){return{...this.metrics}}getCoreWebVitalsScore(){const e=this.metrics.lcp?this.metrics.lcp<=2500?"good":this.metrics.lcp<=4e3?"needs-improvement":"poor":"unknown",t=this.metrics.fid?this.metrics.fid<=100?"good":this.metrics.fid<=300?"needs-improvement":"poor":"unknown",i=this.metrics.cls?this.metrics.cls<=.1?"good":this.metrics.cls<=.25?"needs-improvement":"poor":"unknown",s=[e,t,i].filter(e=>"unknown"!==e),r=s.filter(e=>"good"===e).length,n=s.filter(e=>"poor"===e).length;let o;return o=0===s.length?"poor":r===s.length?"good":n>0?"poor":"needs-improvement",{lcp:e,fid:t,cls:i,overall:o}}getResourceType(e){const t=e.split(".").pop()?.toLowerCase();return["js","mjs"].includes(t||"")?"script":["css"].includes(t||"")?"stylesheet":["jpg","jpeg","png","gif","webp","svg"].includes(t||"")?"image":["woff","woff2","ttf","otf"].includes(t||"")?"font":["mp4","webm","ogg"].includes(t||"")?"video":["mp3","wav","ogg"].includes(t||"")?"audio":e.includes("/api/")||e.includes(".json")?"xhr":"other"}getConnectionInfo(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return e?{effectiveType:e.effectiveType,downlink:e.downlink,rtt:e.rtt,saveData:e.saveData}:null}isPerformanceAPIAvailable(){return"undefined"!=typeof window&&"performance"in window&&"PerformanceObserver"in window}clearData(){this.metrics={},this.config.debug}}var s=t.h;export{s as PerformanceMonitoring};