web-vitals
Version:
Easily measure performance metrics in JavaScript
2 lines (1 loc) • 11.6 kB
JavaScript
class t{t;o=0;i=[];u(t){if(t.hadRecentInput)return;const e=this.i[0],n=this.i.at(-1);this.o&&e&&n&&t.startTime-n.startTime<1e3&&t.startTime-e.startTime<5e3?(this.o+=t.value,this.i.push(t)):(this.o=t.value,this.i=[t]),this.t?.(t)}}const e=()=>{const t=performance.getEntriesByType("navigation")[0];if(t&&t.responseStart>0&&t.responseStart<performance.now())return t},n=t=>{if("loading"===document.readyState)return"loading";{const n=e();if(n){if(t<n.domInteractive)return"loading";if(0===n.domContentLoadedEventStart||t<n.domContentLoadedEventStart)return"dom-interactive";if(0===n.domComplete||t<n.domComplete)return"dom-content-loaded"}}return"complete"},o=t=>{const e=t.nodeName;return 1===t.nodeType?e.toLowerCase():e.toUpperCase().replace(/^#/,"")},i=t=>{let e="";try{for(;9!==t?.nodeType;){const n=t,i=n.id?"#"+n.id:[o(n),...Array.from(n.classList).sort()].join(".");if(e.length+i.length>99)return e||i;if(e=e?i+">"+e:i,n.id)break;t=n.parentNode}}catch{}return e},r=new WeakMap;function s(t,e){return r.get(t)||r.set(t,new e),r.get(t)}let a=-1;const c=()=>a,u=t=>{addEventListener("pageshow",(e=>{e.persisted&&(a=e.timeStamp,t(e))}),!0)},d=(t,e,n,o)=>{let i,r;return s=>{e.value>=0&&(s||o)&&(r=e.value-(i??0),(r||void 0===i)&&(i=e.value,e.delta=r,e.rating=((t,e)=>t>e[1]?"poor":t>e[0]?"needs-improvement":"good")(e.value,n),t(e)))}},f=t=>{requestAnimationFrame((()=>requestAnimationFrame((()=>t()))))},l=()=>{const t=e();return t?.activationStart??0},h=(t,n=-1)=>{const o=e();let i="navigate";c()>=0?i="back-forward-cache":o&&(document.prerendering||l()>0?i="prerender":document.wasDiscarded?i="restore":o.type&&(i=o.type.replace(/_/g,"-")));return{name:t,value:n,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:i}},m=(t,e,n={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(t)){const o=new PerformanceObserver((t=>{Promise.resolve().then((()=>{e(t.getEntries())}))}));return o.observe({type:t,buffered:!0,...n}),o}}catch{}},g=t=>{let e=!1;return()=>{e||(t(),e=!0)}};let p=-1;const y=()=>"hidden"!==document.visibilityState||document.prerendering?1/0:0,v=t=>{"hidden"===document.visibilityState&&p>-1&&(p="visibilitychange"===t.type?t.timeStamp:0,M())},b=()=>{addEventListener("visibilitychange",v,!0),addEventListener("prerenderingchange",v,!0)},M=()=>{removeEventListener("visibilitychange",v,!0),removeEventListener("prerenderingchange",v,!0)},T=()=>{if(p<0){const t=l(),e=document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((e=>"hidden"===e.name&&e.startTime>t))[0]?.startTime;p=e??y(),b(),u((()=>{setTimeout((()=>{p=y(),b()}))}))}return{get firstHiddenTime(){return p}}},D=t=>{document.prerendering?addEventListener("prerenderingchange",(()=>t()),!0):t()},E=[1800,3e3],P=(t,e={})=>{D((()=>{const n=T();let o,i=h("FCP");const r=m("paint",(t=>{for(const e of t)"first-contentful-paint"===e.name&&(r.disconnect(),e.startTime<n.firstHiddenTime&&(i.value=Math.max(e.startTime-l(),0),i.entries.push(e),o(!0)))}));r&&(o=d(t,i,E,e.reportAllChanges),u((n=>{i=h("FCP"),o=d(t,i,E,e.reportAllChanges),f((()=>{i.value=performance.now()-n.timeStamp,o(!0)}))})))}))},L=[.1,.25],S=t=>t.find((t=>1===t.node?.nodeType))||t[0],_=(e,o={})=>{const r=s(o=Object.assign({},o),t),a=new WeakMap;r.t=t=>{if(t?.sources?.length){const e=S(t.sources);if(e){const t=(o.generateTarget??i)(e.node);a.set(e,t)}}};((e,n={})=>{P(g((()=>{let o,i=h("CLS",0);const r=s(n,t),a=t=>{for(const e of t)r.u(e);r.o>i.value&&(i.value=r.o,i.entries=r.i,o())},c=m("layout-shift",a);c&&(o=d(e,i,L,n.reportAllChanges),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState&&(a(c.takeRecords()),o(!0))})),u((()=>{r.o=0,i=h("CLS",0),o=d(e,i,L,n.reportAllChanges),f((()=>o()))})),setTimeout(o))})))})((t=>{const o=(t=>{let e={};if(t.entries.length){const o=t.entries.reduce(((t,e)=>t.value>e.value?t:e));if(o?.sources?.length){const t=S(o.sources);t&&(e={largestShiftTarget:a.get(t),largestShiftTime:o.startTime,largestShiftValue:o.value,largestShiftSource:t,largestShiftEntry:o,loadState:n(o.startTime)})}}return Object.assign(t,{attribution:e})})(t);e(o)}),o)},w=(t,o={})=>{P((o=>{const i=(t=>{let o={timeToFirstByte:0,firstByteToFCP:t.value,loadState:n(c())};if(t.entries.length){const i=e(),r=t.entries.at(-1);if(i){const e=i.activationStart||0,s=Math.max(0,i.responseStart-e);o={timeToFirstByte:s,firstByteToFCP:t.value-s,loadState:n(t.entries[0].startTime),navigationEntry:i,fcpEntry:r}}}return Object.assign(t,{attribution:o})})(o);t(i)}),o)};let k=0,F=1/0,B=0;const C=t=>{for(const e of t)e.interactionId&&(F=Math.min(F,e.interactionId),B=Math.max(B,e.interactionId),k=B?(B-F)/7+1:0)};let O;const j=()=>O?k:performance.interactionCount??0,I=()=>{"interactionCount"in performance||O||(O=m("event",C,{type:"event",buffered:!0,durationThreshold:0}))};let A=0;class W{l=[];h=new Map;m;p;v(){A=j(),this.l.length=0,this.h.clear()}M(){const t=Math.min(this.l.length-1,Math.floor((j()-A)/50));return this.l[t]}u(t){if(this.m?.(t),!t.interactionId&&"first-input"!==t.entryType)return;const e=this.l.at(-1);let n=this.h.get(t.interactionId);if(n||this.l.length<10||t.duration>e.T){if(n?t.duration>n.T?(n.entries=[t],n.T=t.duration):t.duration===n.T&&t.startTime===n.entries[0].startTime&&n.entries.push(t):(n={id:t.interactionId,entries:[t],T:t.duration},this.h.set(n.id,n),this.l.push(n)),this.l.sort(((t,e)=>e.T-t.T)),this.l.length>10){const t=this.l.splice(10);for(const e of t)this.h.delete(e.id)}this.p?.(n)}}}const q=t=>{const e=globalThis.requestIdleCallback||setTimeout;"hidden"===document.visibilityState?t():(t=g(t),document.addEventListener("visibilitychange",t,{once:!0}),e((()=>{t(),document.removeEventListener("visibilitychange",t)})))},x=[200,500],N=(t,e={})=>{const o=s(e=Object.assign({},e),W);let r=[],a=[],c=0;const f=new WeakMap,l=new WeakMap;let g=!1;const p=()=>{g||(q(y),g=!0)},y=()=>{const t=o.l.map((t=>f.get(t.entries[0]))),e=a.length-50;a=a.filter(((n,o)=>o>=e||t.includes(n)));const n=new Set;for(const t of a){const e=v(t.startTime,t.processingEnd);for(const t of e)n.add(t)}const i=r.length-1-50;r=r.filter(((t,e)=>t.startTime>c&&e>i||n.has(t))),g=!1};o.m=t=>{const e=t.startTime+t.duration;let n;c=Math.max(c,t.processingEnd);for(let o=a.length-1;o>=0;o--){const i=a[o];if(Math.abs(e-i.renderTime)<=8){n=i,n.startTime=Math.min(t.startTime,n.startTime),n.processingStart=Math.min(t.processingStart,n.processingStart),n.processingEnd=Math.max(t.processingEnd,n.processingEnd),n.entries.push(t);break}}n||(n={startTime:t.startTime,processingStart:t.processingStart,processingEnd:t.processingEnd,renderTime:e,entries:[t]},a.push(n)),(t.interactionId||"first-input"===t.entryType)&&f.set(t,n),p()},o.p=t=>{if(!l.get(t)){const n=(e.generateTarget??i)(t.entries[0].target);l.set(t,n)}};const v=(t,e)=>{const n=[];for(const o of r)if(!(o.startTime+o.duration<t)){if(o.startTime>e)break;n.push(o)}return n},b=t=>{const e=t.entries[0],i=f.get(e),r=e.processingStart,s=Math.max(e.startTime+e.duration,r),a=Math.min(i.processingEnd,s),c=i.entries.sort(((t,e)=>t.processingStart-e.processingStart)),u=v(e.startTime,a),d=o.h.get(e.interactionId),h={interactionTarget:l.get(d),interactionType:e.name.startsWith("key")?"keyboard":"pointer",interactionTime:e.startTime,nextPaintTime:s,processedEventEntries:c,longAnimationFrameEntries:u,inputDelay:r-e.startTime,processingDuration:a-r,presentationDelay:s-a,loadState:n(e.startTime),longestScript:void 0,totalScriptDuration:void 0,totalStyleAndLayoutDuration:void 0,totalPaintDuration:void 0,totalUnattributedDuration:void 0};(t=>{if(!t.longAnimationFrameEntries?.length)return;const e=t.interactionTime,n=t.inputDelay,o=t.processingDuration;let i,r,s=0,a=0,c=0,u=0;for(const c of t.longAnimationFrameEntries){a=a+c.startTime+c.duration-c.styleAndLayoutStart;for(const t of c.scripts){const c=t.startTime+t.duration;if(c<e)continue;const d=c-Math.max(e,t.startTime),f=t.duration?d/t.duration*t.forcedStyleAndLayoutDuration:0;s+=d-f,a+=f,d>u&&(r=t.startTime<e+n?"input-delay":t.startTime>=e+n+o?"presentation-delay":"processing-duration",i=t,u=d)}}const d=t.longAnimationFrameEntries.at(-1),f=d?d.startTime+d.duration:0;f>=e+n+o&&(c=t.nextPaintTime-f),i&&r&&(t.longestScript={entry:i,subpart:r,intersectingDuration:u}),t.totalScriptDuration=s,t.totalStyleAndLayoutDuration=a,t.totalPaintDuration=c,t.totalUnattributedDuration=t.nextPaintTime-e-s-a-c})(h);return Object.assign(t,{attribution:h})};m("long-animation-frame",(t=>{r=r.concat(t),p()})),((t,e={})=>{globalThis.PerformanceEventTiming&&"interactionId"in PerformanceEventTiming.prototype&&D((()=>{I();let n,o=h("INP");const i=s(e,W),r=t=>{q((()=>{for(const e of t)i.u(e);const e=i.M();e&&e.T!==o.value&&(o.value=e.T,o.entries=e.entries,n())}))},a=m("event",r,{durationThreshold:e.durationThreshold??40});n=d(t,o,x,e.reportAllChanges),a&&(a.observe({type:"first-input",buffered:!0}),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState&&(r(a.takeRecords()),n(!0))})),u((()=>{i.v(),o=h("INP"),n=d(t,o,x,e.reportAllChanges)})))}))})((e=>{const n=b(e);t(n)}),e)};class R{m;u(t){this.m?.(t)}}const U=[2500,4e3],V=(t,n={})=>{const o=s(n=Object.assign({},n),R),r=new WeakMap;o.m=t=>{if(t.element){const e=(n.generateTarget??i)(t.element);r.set(t,e)}};((t,e={})=>{D((()=>{const n=T();let o,i=h("LCP");const r=s(e,R),a=t=>{e.reportAllChanges||(t=t.slice(-1));for(const e of t)r.u(e),e.startTime<n.firstHiddenTime&&(i.value=Math.max(e.startTime-l(),0),i.entries=[e],o())},c=m("largest-contentful-paint",a);if(c){o=d(t,i,U,e.reportAllChanges);const n=g((()=>{a(c.takeRecords()),c.disconnect(),o(!0)}));for(const t of["keydown","click","visibilitychange"])addEventListener(t,(()=>q(n)),{capture:!0,once:!0});u((n=>{i=h("LCP"),o=d(t,i,U,e.reportAllChanges),f((()=>{i.value=performance.now()-n.timeStamp,o(!0)}))}))}}))})((n=>{const o=(t=>{let n={timeToFirstByte:0,resourceLoadDelay:0,resourceLoadDuration:0,elementRenderDelay:t.value};if(t.entries.length){const o=e();if(o){const e=o.activationStart||0,i=t.entries.at(-1),s=i.url&&performance.getEntriesByType("resource").filter((t=>t.name===i.url))[0],a=Math.max(0,o.responseStart-e),c=Math.max(a,s?(s.requestStart||s.startTime)-e:0),u=Math.min(t.value,Math.max(c,s?s.responseEnd-e:0));n={target:r.get(i),timeToFirstByte:a,resourceLoadDelay:c-a,resourceLoadDuration:u-c,elementRenderDelay:t.value-u,navigationEntry:o,lcpEntry:i},i.url&&(n.url=i.url),s&&(n.lcpResourceEntry=s)}}return Object.assign(t,{attribution:n})})(n);t(o)}),n)},$=[800,1800],H=t=>{document.prerendering?D((()=>H(t))):"complete"!==document.readyState?addEventListener("load",(()=>H(t)),!0):setTimeout(t)},z=(t,n={})=>{((t,n={})=>{let o=h("TTFB"),i=d(t,o,$,n.reportAllChanges);H((()=>{const r=e();r&&(o.value=Math.max(r.responseStart-l(),0),o.entries=[r],i(!0),u((()=>{o=h("TTFB",0),i=d(t,o,$,n.reportAllChanges),i(!0)})))}))})((e=>{const n=(t=>{let e={waitingDuration:0,cacheDuration:0,dnsDuration:0,connectionDuration:0,requestDuration:0};if(t.entries.length){const n=t.entries[0],o=n.activationStart||0,i=Math.max((n.workerStart||n.fetchStart)-o,0),r=Math.max(n.domainLookupStart-o,0),s=Math.max(n.connectStart-o,0),a=Math.max(n.connectEnd-o,0);e={waitingDuration:i,cacheDuration:r-i,dnsDuration:s-r,connectionDuration:a-s,requestDuration:t.value-a,navigationEntry:n}}return Object.assign(t,{attribution:e})})(e);t(n)}),n)};export{L as CLSThresholds,E as FCPThresholds,x as INPThresholds,U as LCPThresholds,$ as TTFBThresholds,_ as onCLS,w as onFCP,N as onINP,V as onLCP,z as onTTFB};