@senzops/web 1.3.5 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.global.js +2 -1
- package/dist/index.js +2 -1
- package/dist/index.mjs +2 -1
- package/package.json +1 -1
- package/src/error.ts +42 -31
- package/src/rum.ts +22 -10
package/dist/index.d.mts
CHANGED
|
@@ -41,7 +41,9 @@ declare class SenzorRumAgent {
|
|
|
41
41
|
private frustrations;
|
|
42
42
|
private clickHistory;
|
|
43
43
|
private flushInterval;
|
|
44
|
+
private flushTimeout;
|
|
44
45
|
private readonly MAX_BATCH_SIZE;
|
|
46
|
+
private readonly MAX_QUEUE_MEMORY;
|
|
45
47
|
private errorEngine;
|
|
46
48
|
init(config: RumConfig): void;
|
|
47
49
|
private manageSession;
|
|
@@ -51,8 +53,10 @@ declare class SenzorRumAgent {
|
|
|
51
53
|
private setupPerformanceObservers;
|
|
52
54
|
private getNavigationTimings;
|
|
53
55
|
private shouldAttachTraceHeader;
|
|
56
|
+
private pushSpan;
|
|
54
57
|
private patchNetwork;
|
|
55
58
|
private setupRoutingListeners;
|
|
59
|
+
private debouncedFlush;
|
|
56
60
|
private flush;
|
|
57
61
|
}
|
|
58
62
|
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,9 @@ declare class SenzorRumAgent {
|
|
|
41
41
|
private frustrations;
|
|
42
42
|
private clickHistory;
|
|
43
43
|
private flushInterval;
|
|
44
|
+
private flushTimeout;
|
|
44
45
|
private readonly MAX_BATCH_SIZE;
|
|
46
|
+
private readonly MAX_QUEUE_MEMORY;
|
|
45
47
|
private errorEngine;
|
|
46
48
|
init(config: RumConfig): void;
|
|
47
49
|
private manageSession;
|
|
@@ -51,8 +53,10 @@ declare class SenzorRumAgent {
|
|
|
51
53
|
private setupPerformanceObservers;
|
|
52
54
|
private getNavigationTimings;
|
|
53
55
|
private shouldAttachTraceHeader;
|
|
56
|
+
private pushSpan;
|
|
54
57
|
private patchNetwork;
|
|
55
58
|
private setupRoutingListeners;
|
|
59
|
+
private debouncedFlush;
|
|
56
60
|
private flush;
|
|
57
61
|
}
|
|
58
62
|
|
package/dist/index.global.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
(()=>{function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let e=Math.random()*16|0;return(s==="x"?e:e&3|8).toString(16)})}function I(s){let e="";for(;e.length<s;)e+=Math.random().toString(16).slice(2);return e.slice(0,s)}var x=()=>{var s;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((s=navigator.connection)==null?void 0:s.effectiveType)||void 0}},T=s=>{if(!s)return 0;if(typeof s=="string")return s.length;if(s instanceof Blob||s instanceof File)return s.size;if(s instanceof ArrayBuffer)return s.byteLength},z=s=>{let e={};return s&&(s instanceof Headers?s.forEach((t,r)=>e[r]=t):Array.isArray(s)?s.forEach(([t,r])=>e[t]=r):typeof s=="object"&&Object.assign(e,s)),e};var _=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(e){return e?e.replace(/^https?:\/\//,"").replace(/^www\./,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let a=sessionStorage.getItem("senzor_sid"),c=e-t>r;!a||c?(a=v(),sessionStorage.setItem("senzor_sid",a),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,a=sessionStorage.getItem("senzor_ref"),c=!1;if(t)try{new URL(t).hostname!==r&&(c=!0)}catch{c=!0}if(c){let n=this.normalizeUrl(t);n!==a&&sessionStorage.setItem("senzor_ref",n)}else e&&!a&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now(),this.send({type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer})}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);e>=1&&this.send({type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer,duration:e})}send(e){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(e)],{type:"application/json"}))||this.fallbackSend(e):this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let e=history.pushState;history.pushState=(...t)=>{this.trackPing(),e.apply(history,t),this.trackPageView()},window.addEventListener("popstate",()=>{this.trackPing(),this.trackPageView()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.trackPing():(this.startTime=Date.now(),this.manageSession())}),window.addEventListener("beforeunload",()=>this.trackPing())}};var E=class{deps;constructor(e){this.deps=e}setup(){this.setupGlobalErrors(),this.setupPromiseErrors(),this.setupReactIntegration(),this.setupReactConsolePatch()}setupGlobalErrors(){window.addEventListener("error",e=>{if(e.error){this.capture(e.error,"Uncaught Exception");return}if(e.target&&e.target!==window){let t=e.target;this.capture(new Error("Resource failed to load"),"Resource Error")}},!0)}setupPromiseErrors(){window.addEventListener("unhandledrejection",e=>{let t=this.normalizeError(e.reason);this.capture(t,"Unhandled Promise Rejection")})}capture(e,t,r){if(this.shouldIgnore(e))return;let a={type:t,path:location.pathname,referrer:document.referrer||void 0,traceId:this.deps.isSampled?this.deps.traceId():void 0,...x(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",context:a,timestamp:new Date().toISOString()}),this.deps.flush()}normalizeError(e){if(e instanceof Error)return e;if(typeof e=="string")return new Error(e);if(e!=null&&e.message)return new Error(e.message);try{return new Error(JSON.stringify(e))}catch{return new Error("Unknown rejection")}}shouldIgnore(e){let t=e.stack||"";return!!(t.includes("chrome-extension://")||t.includes("moz-extension://")||t.includes("safari-extension://"))}setupReactIntegration(){let e=window.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!e||e.__senzor_patched)return;e.__senzor_patched=!0;let t=e.onCommitFiberRoot;e.onCommitFiberRoot=(r,a,...c)=>{if(t)return t.apply(e,[r,a,...c])}}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",a=0;console.error=(...c)=>{try{if(!c||!c.length)return t.apply(console,c);let n=c[0];if(typeof n=="string"&&n.includes("The above error occurred")){let i=Date.now();if(n===r&&i-a<2e3)return t.apply(console,c);r=n,a=i;let o=new Error("React component crash");this.capture(o,"React Error")}}catch{}return t.apply(console,c)}}};var R=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spanQueue=[];errorQueue=[];vitals={};breadcrumbs=[];frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;MAX_BATCH_SIZE=50;errorEngine;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.apiKey){console.error("[Senzor RUM] apiKey is required.");return}this.isSampled=Math.random()<=(this.config.sampleRate??1),this.manageSession(),this.startNewTrace(!0),this.errorEngine=new E({isSampled:this.isSampled,traceId:()=>this.traceId,sessionId:this.sessionId,breadcrumbs:this.breadcrumbs,frustrations:this.frustrations,errorQueue:this.errorQueue,flush:()=>this.flush()}),this.errorEngine.setup(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),5e3),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",v()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(e){this.traceId=I(32),this.traceStartTime=Date.now(),this.isInitialLoad=e,this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(e,t,r){this.breadcrumbs.push({type:e,message:t,data:r,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,r=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${r}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(r)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let n=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:n}),this.clickHistory=this.clickHistory.filter(i=>n-i.time<1e3),this.clickHistory.length>=3){let i=this.clickHistory[0],o=!0;for(let p=1;p<this.clickHistory.length;p++){let h=Math.abs(this.clickHistory[p].x-i.x),u=Math.abs(this.clickHistory[p].y-i.y);(h>50||u>50)&&(o=!1)}o&&(this.frustrations.rageClicks++,this.addBreadcrumb("frustration","Rage Click Detected"),this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(t=>{for(let r of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=r.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let r=t.getEntries(),a=r[r.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let r of t.getEntries())r.hadRecentInput||(e+=r.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let r of t.getEntries()){let a=r,c=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||c>this.vitals.inp)&&(this.vitals.inp=c)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let e=performance.getEntriesByType("navigation")[0];return e?{dns:Math.max(0,e.domainLookupEnd-e.domainLookupStart),tcp:Math.max(0,e.connectEnd-e.connectStart),ssl:e.secureConnectionStart?Math.max(0,e.requestStart-e.secureConnectionStart):0,ttfb:Math.max(0,e.responseStart-e.requestStart),domInteractive:Math.max(0,e.domInteractive-e.startTime),domComplete:Math.max(0,e.domComplete-e.startTime)}:{}}shouldAttachTraceHeader(e){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let t=new URL(e,window.location.origin);return this.config.allowedOrigins.some(r=>typeof r=="string"?t.origin.includes(r):r instanceof RegExp?r.test(t.origin):!1)}catch{return!1}}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,i,...o){return this.__szMethod=n.toUpperCase(),this.__szUrl=i,this.__szHeaders={},t.apply(this,[n,i,...o])},XMLHttpRequest.prototype.setRequestHeader=function(n,i){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=i,a.apply(this,[n,i])},XMLHttpRequest.prototype.send=function(n){let i=this,o=I(16),p=Date.now()-e.traceStartTime,h=i.__szMethod,u=i.__szUrl;try{u=new URL(i.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(u)&&i.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),i.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-p,m={};try{m=i.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((d,S)=>{let b=S.split(": "),f=b.shift(),H=b.join(": ");return f&&(d[f]=H),d},{})}catch{}let y={url:u,method:h,library:"xhr",status:i.status,responseType:i.responseType,requestPayloadSize:T(n),requestHeaders:i.__szHeaders,responseHeaders:m};try{(i.responseType===""||i.responseType==="text")&&(y.responsePayloadSize=(w=i.responseText)==null?void 0:w.length)}catch{}e.spanQueue.push({spanId:o,name:`${h} ${new URL(u,window.location.origin).pathname}`,type:"http",startTime:p,duration:g,status:i.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),r.call(this,n)};let c=window.fetch;window.fetch=async function(...n){let i=n[0],o=n[1],p="",h="GET";typeof i=="string"||i instanceof URL?(p=i.toString(),h=((o==null?void 0:o.method)||"GET").toUpperCase()):i instanceof Request&&(p=i.url,h=i.method.toUpperCase());let u=p;try{u=new URL(p,window.location.origin).toString()}catch{}let g=I(16),m=Date.now()-e.traceStartTime,y=z((o==null?void 0:o.headers)||(i instanceof Request?i.headers:{}));if(e.shouldAttachTraceHeader(u)){let l=`00-${e.traceId}-${g}-01`;if(i instanceof Request){let d=new Headers(i.headers);d.set("traceparent",l),n[1]={...o||{},headers:d}}else{let d=new Headers((o==null?void 0:o.headers)||{});d.set("traceparent",l),n[1]={...o||{},headers:d}}y.traceparent=l}let w=(l,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:u,method:h,library:"fetch",status:l,requestPayloadSize:T(o==null?void 0:o.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=z(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${h} ${new URL(u,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:l,meta:f}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()};try{let l=await c.apply(this,n);return w(l.status,l),l}catch(l){throw w(0,void 0,l instanceof Error?l.message:String(l)),l}}}setupRoutingListeners(){let e=history.pushState;history.pushState=(...t)=>{this.flush(),e.apply(history,t),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)},window.addEventListener("popstate",()=>{this.flush(),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),window.addEventListener("pagehide",()=>this.flush())}flush(){if(this.spanQueue.length===0&&this.errorQueue.length===0&&!this.isInitialLoad)return;let e=this.spanQueue.splice(0,this.MAX_BATCH_SIZE),t=this.errorQueue.splice(0,20),r={traces:[],errors:t};if(this.isSampled&&r.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...x(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,r.traces.length>0||r.errors.length>0){let a=new Blob([JSON.stringify(r)],{type:"application/json"}),c=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${c}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&a.size<6e4?navigator.sendBeacon(n,a):fetch(n,{method:"POST",body:a,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var k=new _,C=new R,L={init:s=>k.init(s),initRum:s=>C.init(s)};typeof window<"u"&&(window.Senzor=L);})();
|
|
1
|
+
(()=>{function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let e=Math.random()*16|0;return(s==="x"?e:e&3|8).toString(16)})}function x(s){let e="";for(;e.length<s;)e+=Math.random().toString(16).slice(2);return e.slice(0,s)}var I=()=>{var s;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((s=navigator.connection)==null?void 0:s.effectiveType)||void 0}},R=s=>{if(!s)return 0;if(typeof s=="string")return s.length;if(s instanceof Blob||s instanceof File)return s.size;if(s instanceof ArrayBuffer)return s.byteLength},z=s=>{let e={};return s&&(s instanceof Headers?s.forEach((t,r)=>e[r]=t):Array.isArray(s)?s.forEach(([t,r])=>e[t]=r):typeof s=="object"&&Object.assign(e,s)),e};var E=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(e){return e?e.replace(/^https?:\/\//,"").replace(/^www\./,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let a=sessionStorage.getItem("senzor_sid"),c=e-t>r;!a||c?(a=v(),sessionStorage.setItem("senzor_sid",a),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,a=sessionStorage.getItem("senzor_ref"),c=!1;if(t)try{new URL(t).hostname!==r&&(c=!0)}catch{c=!0}if(c){let n=this.normalizeUrl(t);n!==a&&sessionStorage.setItem("senzor_ref",n)}else e&&!a&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now(),this.send({type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer})}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);e>=1&&this.send({type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer,duration:e})}send(e){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(e)],{type:"application/json"}))||this.fallbackSend(e):this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let e=history.pushState;history.pushState=(...t)=>{this.trackPing(),e.apply(history,t),this.trackPageView()},window.addEventListener("popstate",()=>{this.trackPing(),this.trackPageView()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.trackPing():(this.startTime=Date.now(),this.manageSession())}),window.addEventListener("beforeunload",()=>this.trackPing())}};var T=class{deps;constructor(e){this.deps=e}setup(){this.setupGlobalErrors(),this.setupPromiseErrors(),this.setupReactConsolePatch()}setupGlobalErrors(){window.addEventListener("error",e=>{if(e.error){this.capture(e.error,"Uncaught Exception");return}e.target&&e.target!==window&&this.capture(new Error("Resource failed to load"),"Resource Error")},!0)}setupPromiseErrors(){window.addEventListener("unhandledrejection",e=>{let t=this.normalizeError(e.reason);this.capture(t,"Unhandled Promise Rejection")})}capture(e,t,r){if(this.shouldIgnore(e))return;this.deps.errorQueue.length>=this.deps.queueLimit&&this.deps.errorQueue.shift();let a={type:t,path:location.pathname,referrer:document.referrer||void 0,...I(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",traceId:this.deps.isSampled?this.deps.traceId():void 0,context:a,timestamp:new Date().toISOString()}),this.deps.flush()}normalizeError(e){if(e instanceof Error)return e;if(typeof e=="string")return new Error(e);if(e!=null&&e.message)return new Error(e.message);try{return new Error(JSON.stringify(e))}catch{return new Error("Unknown rejection")}}shouldIgnore(e){let t=e.stack||"";return!!(t.includes("chrome-extension://")||t.includes("moz-extension://")||t.includes("safari-extension://"))}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",a=0;console.error=(...c)=>{try{if(!c||!c.length)return t.apply(console,c);let n=c[0];if(typeof n=="string"&&(n.includes("The above error occurred")||n.includes("A cross-origin error was thrown"))){let i=Date.now();if(n===r&&i-a<2e3)return t.apply(console,c);r=n,a=i;let o=new Error("React Component Crash"),d=c.find(p=>typeof p=="string"&&p.includes(`
|
|
2
|
+
in `));d&&(o.stack=d),this.capture(o,"React Error")}}catch{}return t.apply(console,c)}}};var _=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spanQueue=[];errorQueue=[];vitals={};breadcrumbs=[];frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;flushTimeout=null;MAX_BATCH_SIZE=50;MAX_QUEUE_MEMORY=500;errorEngine;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.apiKey){console.error("[Senzor RUM] apiKey is required.");return}this.isSampled=Math.random()<=(this.config.sampleRate??1),this.manageSession(),this.startNewTrace(!0),this.errorEngine=new T({isSampled:this.isSampled,traceId:()=>this.traceId,sessionId:this.sessionId,breadcrumbs:this.breadcrumbs,frustrations:this.frustrations,errorQueue:this.errorQueue,queueLimit:this.MAX_QUEUE_MEMORY,flush:()=>this.debouncedFlush()}),this.errorEngine.setup(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),5e3),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",v()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(e){this.traceId=x(32),this.traceStartTime=Date.now(),this.isInitialLoad=e,this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(e,t,r){this.breadcrumbs.push({type:e,message:t,data:r,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,r=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${r}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(r)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let n=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:n}),this.clickHistory=this.clickHistory.filter(i=>n-i.time<1e3),this.clickHistory.length>=3){let i=this.clickHistory[0],o=!0;for(let d=1;d<this.clickHistory.length;d++){let p=Math.abs(this.clickHistory[d].x-i.x),h=Math.abs(this.clickHistory[d].y-i.y);(p>50||h>50)&&(o=!1)}o&&(this.frustrations.rageClicks++,this.addBreadcrumb("frustration","Rage Click Detected"),this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(t=>{for(let r of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=r.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let r=t.getEntries(),a=r[r.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let r of t.getEntries())r.hadRecentInput||(e+=r.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let r of t.getEntries()){let a=r,c=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||c>this.vitals.inp)&&(this.vitals.inp=c)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let e=performance.getEntriesByType("navigation")[0];return e?{dns:Math.max(0,e.domainLookupEnd-e.domainLookupStart),tcp:Math.max(0,e.connectEnd-e.connectStart),ssl:e.secureConnectionStart?Math.max(0,e.requestStart-e.secureConnectionStart):0,ttfb:Math.max(0,e.responseStart-e.requestStart),domInteractive:Math.max(0,e.domInteractive-e.startTime),domComplete:Math.max(0,e.domComplete-e.startTime)}:{}}shouldAttachTraceHeader(e){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let t=new URL(e,window.location.origin);return this.config.allowedOrigins.some(r=>typeof r=="string"?t.origin.includes(r):r instanceof RegExp?r.test(t.origin):!1)}catch{return!1}}pushSpan(e){this.spanQueue.length>=this.MAX_QUEUE_MEMORY&&this.spanQueue.shift(),this.spanQueue.push(e),this.spanQueue.length>=this.MAX_BATCH_SIZE&&this.debouncedFlush()}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,i,...o){return this.__szMethod=n.toUpperCase(),this.__szUrl=i,this.__szHeaders={},t.apply(this,[n,i,...o])},XMLHttpRequest.prototype.setRequestHeader=function(n,i){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=i,a.apply(this,[n,i])},XMLHttpRequest.prototype.send=function(n){let i=this,o=x(16),d=Date.now()-e.traceStartTime,p=i.__szMethod,h=i.__szUrl;try{h=new URL(i.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(h)&&i.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),i.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-d,m={};try{m=i.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((u,S)=>{let b=S.split(": "),f=b.shift(),H=b.join(": ");return f&&(u[f]=H),u},{})}catch{}let y={url:h,method:p,library:"xhr",status:i.status,responseType:i.responseType,requestPayloadSize:R(n),requestHeaders:i.__szHeaders,responseHeaders:m};try{(i.responseType===""||i.responseType==="text")&&(y.responsePayloadSize=(w=i.responseText)==null?void 0:w.length)}catch{}e.pushSpan({spanId:o,name:`${p} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:d,duration:g,status:i.status,meta:y})}),r.call(this,n)};let c=window.fetch;window.fetch=async function(...n){let i=n[0],o=n[1],d="",p="GET";typeof i=="string"||i instanceof URL?(d=i.toString(),p=((o==null?void 0:o.method)||"GET").toUpperCase()):i instanceof Request&&(d=i.url,p=i.method.toUpperCase());let h=d;try{h=new URL(d,window.location.origin).toString()}catch{}let g=x(16),m=Date.now()-e.traceStartTime,y=z((o==null?void 0:o.headers)||(i instanceof Request?i.headers:{}));if(e.shouldAttachTraceHeader(h)){let l=`00-${e.traceId}-${g}-01`;if(i instanceof Request){let u=new Headers(i.headers);u.set("traceparent",l),n[1]={...o||{},headers:u}}else{let u=new Headers((o==null?void 0:o.headers)||{});u.set("traceparent",l),n[1]={...o||{},headers:u}}y.traceparent=l}let w=(l,u,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:h,method:p,library:"fetch",status:l,requestPayloadSize:R(o==null?void 0:o.body),requestHeaders:y};u&&(f.statusText=u.statusText,f.type=u.type,f.redirected=u.redirected,f.responseHeaders=z(u.headers)),S&&(f.error=S),e.pushSpan({spanId:g,name:`${p} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:l,meta:f})};try{let l=await c.apply(this,n);return w(l.status,l),l}catch(l){throw w(0,void 0,l instanceof Error?l.message:String(l)),l}}}setupRoutingListeners(){let e=history.pushState;history.pushState=(...t)=>{this.flush(),e.apply(history,t),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)},window.addEventListener("popstate",()=>{this.flush(),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),window.addEventListener("pagehide",()=>this.flush())}debouncedFlush(){this.flushTimeout&&clearTimeout(this.flushTimeout),this.flushTimeout=setTimeout(()=>this.flush(),100)}flush(){if(this.spanQueue.length===0&&this.errorQueue.length===0&&!this.isInitialLoad)return;let e=this.spanQueue.splice(0,this.MAX_BATCH_SIZE),t=this.errorQueue.splice(0,20),r={traces:[],errors:t};if(this.isSampled&&r.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...I(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,r.traces.length>0||r.errors.length>0){let a=new Blob([JSON.stringify(r)],{type:"application/json"}),c=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${c}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&a.size<6e4?navigator.sendBeacon(n,a):fetch(n,{method:"POST",body:a,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var k=new E,C=new _,L={init:s=>k.init(s),initRum:s=>C.init(s)};typeof window<"u"&&(window.Senzor=L);})();
|
package/dist/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
var T=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var M=(i,e)=>{for(var t in e)T(i,t,{get:e[t],enumerable:!0})},O=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of P(e))!A.call(i,n)&&n!==t&&T(i,n,{get:()=>e[n],enumerable:!(r=D(e,n))||r.enumerable});return i};var q=i=>O(T({},"__esModule",{value:!0}),i);var B={};M(B,{Analytics:()=>k,RUM:()=>C,Senzor:()=>L});module.exports=q(B);function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let e=Math.random()*16|0;return(i==="x"?e:e&3|8).toString(16)})}function I(i){let e="";for(;e.length<i;)e+=Math.random().toString(16).slice(2);return e.slice(0,i)}var x=()=>{var i;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((i=navigator.connection)==null?void 0:i.effectiveType)||void 0}},z=i=>{if(!i)return 0;if(typeof i=="string")return i.length;if(i instanceof Blob||i instanceof File)return i.size;if(i instanceof ArrayBuffer)return i.byteLength},H=i=>{let e={};return i&&(i instanceof Headers?i.forEach((t,r)=>e[r]=t):Array.isArray(i)?i.forEach(([t,r])=>e[t]=r):typeof i=="object"&&Object.assign(e,i)),e};var _=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(e){return e?e.replace(/^https?:\/\//,"").replace(/^www\./,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let n=sessionStorage.getItem("senzor_sid"),c=e-t>r;!n||c?(n=v(),sessionStorage.setItem("senzor_sid",n),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,n=sessionStorage.getItem("senzor_ref"),c=!1;if(t)try{new URL(t).hostname!==r&&(c=!0)}catch{c=!0}if(c){let a=this.normalizeUrl(t);a!==n&&sessionStorage.setItem("senzor_ref",a)}else e&&!n&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now(),this.send({type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer})}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);e>=1&&this.send({type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer,duration:e})}send(e){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(e)],{type:"application/json"}))||this.fallbackSend(e):this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let e=history.pushState;history.pushState=(...t)=>{this.trackPing(),e.apply(history,t),this.trackPageView()},window.addEventListener("popstate",()=>{this.trackPing(),this.trackPageView()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.trackPing():(this.startTime=Date.now(),this.manageSession())}),window.addEventListener("beforeunload",()=>this.trackPing())}};var E=class{deps;constructor(e){this.deps=e}setup(){this.setupGlobalErrors(),this.setupPromiseErrors(),this.setupReactIntegration(),this.setupReactConsolePatch()}setupGlobalErrors(){window.addEventListener("error",e=>{if(e.error){this.capture(e.error,"Uncaught Exception");return}if(e.target&&e.target!==window){let t=e.target;this.capture(new Error("Resource failed to load"),"Resource Error")}},!0)}setupPromiseErrors(){window.addEventListener("unhandledrejection",e=>{let t=this.normalizeError(e.reason);this.capture(t,"Unhandled Promise Rejection")})}capture(e,t,r){if(this.shouldIgnore(e))return;let n={type:t,path:location.pathname,referrer:document.referrer||void 0,traceId:this.deps.isSampled?this.deps.traceId():void 0,...x(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",context:n,timestamp:new Date().toISOString()}),this.deps.flush()}normalizeError(e){if(e instanceof Error)return e;if(typeof e=="string")return new Error(e);if(e!=null&&e.message)return new Error(e.message);try{return new Error(JSON.stringify(e))}catch{return new Error("Unknown rejection")}}shouldIgnore(e){let t=e.stack||"";return!!(t.includes("chrome-extension://")||t.includes("moz-extension://")||t.includes("safari-extension://"))}setupReactIntegration(){let e=window.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!e||e.__senzor_patched)return;e.__senzor_patched=!0;let t=e.onCommitFiberRoot;e.onCommitFiberRoot=(r,n,...c)=>{if(t)return t.apply(e,[r,n,...c])}}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",n=0;console.error=(...c)=>{try{if(!c||!c.length)return t.apply(console,c);let a=c[0];if(typeof a=="string"&&a.includes("The above error occurred")){let s=Date.now();if(a===r&&s-n<2e3)return t.apply(console,c);r=a,n=s;let o=new Error("React component crash");this.capture(o,"React Error")}}catch{}return t.apply(console,c)}}};var R=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spanQueue=[];errorQueue=[];vitals={};breadcrumbs=[];frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;MAX_BATCH_SIZE=50;errorEngine;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.apiKey){console.error("[Senzor RUM] apiKey is required.");return}this.isSampled=Math.random()<=(this.config.sampleRate??1),this.manageSession(),this.startNewTrace(!0),this.errorEngine=new E({isSampled:this.isSampled,traceId:()=>this.traceId,sessionId:this.sessionId,breadcrumbs:this.breadcrumbs,frustrations:this.frustrations,errorQueue:this.errorQueue,flush:()=>this.flush()}),this.errorEngine.setup(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),5e3),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",v()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(e){this.traceId=I(32),this.traceStartTime=Date.now(),this.isInitialLoad=e,this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(e,t,r){this.breadcrumbs.push({type:e,message:t,data:r,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,r=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${r}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(r)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let a=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:a}),this.clickHistory=this.clickHistory.filter(s=>a-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],o=!0;for(let p=1;p<this.clickHistory.length;p++){let h=Math.abs(this.clickHistory[p].x-s.x),u=Math.abs(this.clickHistory[p].y-s.y);(h>50||u>50)&&(o=!1)}o&&(this.frustrations.rageClicks++,this.addBreadcrumb("frustration","Rage Click Detected"),this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(t=>{for(let r of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=r.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let r=t.getEntries(),n=r[r.length-1];n&&(this.vitals.lcp=n.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let r of t.getEntries())r.hadRecentInput||(e+=r.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let r of t.getEntries()){let n=r,c=n.duration||(n.processingStart&&n.startTime?n.processingStart-n.startTime:0);(!this.vitals.inp||c>this.vitals.inp)&&(this.vitals.inp=c)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let e=performance.getEntriesByType("navigation")[0];return e?{dns:Math.max(0,e.domainLookupEnd-e.domainLookupStart),tcp:Math.max(0,e.connectEnd-e.connectStart),ssl:e.secureConnectionStart?Math.max(0,e.requestStart-e.secureConnectionStart):0,ttfb:Math.max(0,e.responseStart-e.requestStart),domInteractive:Math.max(0,e.domInteractive-e.startTime),domComplete:Math.max(0,e.domComplete-e.startTime)}:{}}shouldAttachTraceHeader(e){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let t=new URL(e,window.location.origin);return this.config.allowedOrigins.some(r=>typeof r=="string"?t.origin.includes(r):r instanceof RegExp?r.test(t.origin):!1)}catch{return!1}}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send,n=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(a,s,...o){return this.__szMethod=a.toUpperCase(),this.__szUrl=s,this.__szHeaders={},t.apply(this,[a,s,...o])},XMLHttpRequest.prototype.setRequestHeader=function(a,s){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[a]=s,n.apply(this,[a,s])},XMLHttpRequest.prototype.send=function(a){let s=this,o=I(16),p=Date.now()-e.traceStartTime,h=s.__szMethod,u=s.__szUrl;try{u=new URL(s.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(u)&&s.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),s.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-p,m={};try{m=s.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((d,S)=>{let b=S.split(": "),f=b.shift(),U=b.join(": ");return f&&(d[f]=U),d},{})}catch{}let y={url:u,method:h,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:z(a),requestHeaders:s.__szHeaders,responseHeaders:m};try{(s.responseType===""||s.responseType==="text")&&(y.responsePayloadSize=(w=s.responseText)==null?void 0:w.length)}catch{}e.spanQueue.push({spanId:o,name:`${h} ${new URL(u,window.location.origin).pathname}`,type:"http",startTime:p,duration:g,status:s.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),r.call(this,a)};let c=window.fetch;window.fetch=async function(...a){let s=a[0],o=a[1],p="",h="GET";typeof s=="string"||s instanceof URL?(p=s.toString(),h=((o==null?void 0:o.method)||"GET").toUpperCase()):s instanceof Request&&(p=s.url,h=s.method.toUpperCase());let u=p;try{u=new URL(p,window.location.origin).toString()}catch{}let g=I(16),m=Date.now()-e.traceStartTime,y=H((o==null?void 0:o.headers)||(s instanceof Request?s.headers:{}));if(e.shouldAttachTraceHeader(u)){let l=`00-${e.traceId}-${g}-01`;if(s instanceof Request){let d=new Headers(s.headers);d.set("traceparent",l),a[1]={...o||{},headers:d}}else{let d=new Headers((o==null?void 0:o.headers)||{});d.set("traceparent",l),a[1]={...o||{},headers:d}}y.traceparent=l}let w=(l,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:u,method:h,library:"fetch",status:l,requestPayloadSize:z(o==null?void 0:o.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=H(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${h} ${new URL(u,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:l,meta:f}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()};try{let l=await c.apply(this,a);return w(l.status,l),l}catch(l){throw w(0,void 0,l instanceof Error?l.message:String(l)),l}}}setupRoutingListeners(){let e=history.pushState;history.pushState=(...t)=>{this.flush(),e.apply(history,t),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)},window.addEventListener("popstate",()=>{this.flush(),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),window.addEventListener("pagehide",()=>this.flush())}flush(){if(this.spanQueue.length===0&&this.errorQueue.length===0&&!this.isInitialLoad)return;let e=this.spanQueue.splice(0,this.MAX_BATCH_SIZE),t=this.errorQueue.splice(0,20),r={traces:[],errors:t};if(this.isSampled&&r.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...x(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,r.traces.length>0||r.errors.length>0){let n=new Blob([JSON.stringify(r)],{type:"application/json"}),c=this.endpoint.includes("?")?"&":"?",a=`${this.endpoint}${c}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&n.size<6e4?navigator.sendBeacon(a,n):fetch(a,{method:"POST",body:n,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var k=new _,C=new R,L={init:i=>k.init(i),initRum:i=>C.init(i)};typeof window<"u"&&(window.Senzor=L);0&&(module.exports={Analytics,RUM,Senzor});
|
|
1
|
+
var R=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var A=(i,e)=>{for(var t in e)R(i,t,{get:e[t],enumerable:!0})},q=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of D(e))!P.call(i,n)&&n!==t&&R(i,n,{get:()=>e[n],enumerable:!(r=M(e,n))||r.enumerable});return i};var O=i=>q(R({},"__esModule",{value:!0}),i);var B={};A(B,{Analytics:()=>k,RUM:()=>C,Senzor:()=>L});module.exports=O(B);function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let e=Math.random()*16|0;return(i==="x"?e:e&3|8).toString(16)})}function x(i){let e="";for(;e.length<i;)e+=Math.random().toString(16).slice(2);return e.slice(0,i)}var I=()=>{var i;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((i=navigator.connection)==null?void 0:i.effectiveType)||void 0}},z=i=>{if(!i)return 0;if(typeof i=="string")return i.length;if(i instanceof Blob||i instanceof File)return i.size;if(i instanceof ArrayBuffer)return i.byteLength},H=i=>{let e={};return i&&(i instanceof Headers?i.forEach((t,r)=>e[r]=t):Array.isArray(i)?i.forEach(([t,r])=>e[t]=r):typeof i=="object"&&Object.assign(e,i)),e};var E=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(e){return e?e.replace(/^https?:\/\//,"").replace(/^www\./,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let n=sessionStorage.getItem("senzor_sid"),c=e-t>r;!n||c?(n=v(),sessionStorage.setItem("senzor_sid",n),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,n=sessionStorage.getItem("senzor_ref"),c=!1;if(t)try{new URL(t).hostname!==r&&(c=!0)}catch{c=!0}if(c){let a=this.normalizeUrl(t);a!==n&&sessionStorage.setItem("senzor_ref",a)}else e&&!n&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now(),this.send({type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer})}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);e>=1&&this.send({type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer,duration:e})}send(e){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(e)],{type:"application/json"}))||this.fallbackSend(e):this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let e=history.pushState;history.pushState=(...t)=>{this.trackPing(),e.apply(history,t),this.trackPageView()},window.addEventListener("popstate",()=>{this.trackPing(),this.trackPageView()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.trackPing():(this.startTime=Date.now(),this.manageSession())}),window.addEventListener("beforeunload",()=>this.trackPing())}};var T=class{deps;constructor(e){this.deps=e}setup(){this.setupGlobalErrors(),this.setupPromiseErrors(),this.setupReactConsolePatch()}setupGlobalErrors(){window.addEventListener("error",e=>{if(e.error){this.capture(e.error,"Uncaught Exception");return}e.target&&e.target!==window&&this.capture(new Error("Resource failed to load"),"Resource Error")},!0)}setupPromiseErrors(){window.addEventListener("unhandledrejection",e=>{let t=this.normalizeError(e.reason);this.capture(t,"Unhandled Promise Rejection")})}capture(e,t,r){if(this.shouldIgnore(e))return;this.deps.errorQueue.length>=this.deps.queueLimit&&this.deps.errorQueue.shift();let n={type:t,path:location.pathname,referrer:document.referrer||void 0,...I(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",traceId:this.deps.isSampled?this.deps.traceId():void 0,context:n,timestamp:new Date().toISOString()}),this.deps.flush()}normalizeError(e){if(e instanceof Error)return e;if(typeof e=="string")return new Error(e);if(e!=null&&e.message)return new Error(e.message);try{return new Error(JSON.stringify(e))}catch{return new Error("Unknown rejection")}}shouldIgnore(e){let t=e.stack||"";return!!(t.includes("chrome-extension://")||t.includes("moz-extension://")||t.includes("safari-extension://"))}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",n=0;console.error=(...c)=>{try{if(!c||!c.length)return t.apply(console,c);let a=c[0];if(typeof a=="string"&&(a.includes("The above error occurred")||a.includes("A cross-origin error was thrown"))){let s=Date.now();if(a===r&&s-n<2e3)return t.apply(console,c);r=a,n=s;let o=new Error("React Component Crash"),d=c.find(p=>typeof p=="string"&&p.includes(`
|
|
2
|
+
in `));d&&(o.stack=d),this.capture(o,"React Error")}}catch{}return t.apply(console,c)}}};var _=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spanQueue=[];errorQueue=[];vitals={};breadcrumbs=[];frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;flushTimeout=null;MAX_BATCH_SIZE=50;MAX_QUEUE_MEMORY=500;errorEngine;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.apiKey){console.error("[Senzor RUM] apiKey is required.");return}this.isSampled=Math.random()<=(this.config.sampleRate??1),this.manageSession(),this.startNewTrace(!0),this.errorEngine=new T({isSampled:this.isSampled,traceId:()=>this.traceId,sessionId:this.sessionId,breadcrumbs:this.breadcrumbs,frustrations:this.frustrations,errorQueue:this.errorQueue,queueLimit:this.MAX_QUEUE_MEMORY,flush:()=>this.debouncedFlush()}),this.errorEngine.setup(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),5e3),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",v()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(e){this.traceId=x(32),this.traceStartTime=Date.now(),this.isInitialLoad=e,this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(e,t,r){this.breadcrumbs.push({type:e,message:t,data:r,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,r=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${r}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(r)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let a=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:a}),this.clickHistory=this.clickHistory.filter(s=>a-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],o=!0;for(let d=1;d<this.clickHistory.length;d++){let p=Math.abs(this.clickHistory[d].x-s.x),h=Math.abs(this.clickHistory[d].y-s.y);(p>50||h>50)&&(o=!1)}o&&(this.frustrations.rageClicks++,this.addBreadcrumb("frustration","Rage Click Detected"),this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(t=>{for(let r of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=r.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let r=t.getEntries(),n=r[r.length-1];n&&(this.vitals.lcp=n.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let r of t.getEntries())r.hadRecentInput||(e+=r.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let r of t.getEntries()){let n=r,c=n.duration||(n.processingStart&&n.startTime?n.processingStart-n.startTime:0);(!this.vitals.inp||c>this.vitals.inp)&&(this.vitals.inp=c)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let e=performance.getEntriesByType("navigation")[0];return e?{dns:Math.max(0,e.domainLookupEnd-e.domainLookupStart),tcp:Math.max(0,e.connectEnd-e.connectStart),ssl:e.secureConnectionStart?Math.max(0,e.requestStart-e.secureConnectionStart):0,ttfb:Math.max(0,e.responseStart-e.requestStart),domInteractive:Math.max(0,e.domInteractive-e.startTime),domComplete:Math.max(0,e.domComplete-e.startTime)}:{}}shouldAttachTraceHeader(e){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let t=new URL(e,window.location.origin);return this.config.allowedOrigins.some(r=>typeof r=="string"?t.origin.includes(r):r instanceof RegExp?r.test(t.origin):!1)}catch{return!1}}pushSpan(e){this.spanQueue.length>=this.MAX_QUEUE_MEMORY&&this.spanQueue.shift(),this.spanQueue.push(e),this.spanQueue.length>=this.MAX_BATCH_SIZE&&this.debouncedFlush()}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send,n=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(a,s,...o){return this.__szMethod=a.toUpperCase(),this.__szUrl=s,this.__szHeaders={},t.apply(this,[a,s,...o])},XMLHttpRequest.prototype.setRequestHeader=function(a,s){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[a]=s,n.apply(this,[a,s])},XMLHttpRequest.prototype.send=function(a){let s=this,o=x(16),d=Date.now()-e.traceStartTime,p=s.__szMethod,h=s.__szUrl;try{h=new URL(s.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(h)&&s.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),s.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-d,m={};try{m=s.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((u,S)=>{let b=S.split(": "),f=b.shift(),U=b.join(": ");return f&&(u[f]=U),u},{})}catch{}let y={url:h,method:p,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:z(a),requestHeaders:s.__szHeaders,responseHeaders:m};try{(s.responseType===""||s.responseType==="text")&&(y.responsePayloadSize=(w=s.responseText)==null?void 0:w.length)}catch{}e.pushSpan({spanId:o,name:`${p} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:d,duration:g,status:s.status,meta:y})}),r.call(this,a)};let c=window.fetch;window.fetch=async function(...a){let s=a[0],o=a[1],d="",p="GET";typeof s=="string"||s instanceof URL?(d=s.toString(),p=((o==null?void 0:o.method)||"GET").toUpperCase()):s instanceof Request&&(d=s.url,p=s.method.toUpperCase());let h=d;try{h=new URL(d,window.location.origin).toString()}catch{}let g=x(16),m=Date.now()-e.traceStartTime,y=H((o==null?void 0:o.headers)||(s instanceof Request?s.headers:{}));if(e.shouldAttachTraceHeader(h)){let l=`00-${e.traceId}-${g}-01`;if(s instanceof Request){let u=new Headers(s.headers);u.set("traceparent",l),a[1]={...o||{},headers:u}}else{let u=new Headers((o==null?void 0:o.headers)||{});u.set("traceparent",l),a[1]={...o||{},headers:u}}y.traceparent=l}let w=(l,u,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:h,method:p,library:"fetch",status:l,requestPayloadSize:z(o==null?void 0:o.body),requestHeaders:y};u&&(f.statusText=u.statusText,f.type=u.type,f.redirected=u.redirected,f.responseHeaders=H(u.headers)),S&&(f.error=S),e.pushSpan({spanId:g,name:`${p} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:l,meta:f})};try{let l=await c.apply(this,a);return w(l.status,l),l}catch(l){throw w(0,void 0,l instanceof Error?l.message:String(l)),l}}}setupRoutingListeners(){let e=history.pushState;history.pushState=(...t)=>{this.flush(),e.apply(history,t),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)},window.addEventListener("popstate",()=>{this.flush(),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),window.addEventListener("pagehide",()=>this.flush())}debouncedFlush(){this.flushTimeout&&clearTimeout(this.flushTimeout),this.flushTimeout=setTimeout(()=>this.flush(),100)}flush(){if(this.spanQueue.length===0&&this.errorQueue.length===0&&!this.isInitialLoad)return;let e=this.spanQueue.splice(0,this.MAX_BATCH_SIZE),t=this.errorQueue.splice(0,20),r={traces:[],errors:t};if(this.isSampled&&r.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...I(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,r.traces.length>0||r.errors.length>0){let n=new Blob([JSON.stringify(r)],{type:"application/json"}),c=this.endpoint.includes("?")?"&":"?",a=`${this.endpoint}${c}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&n.size<6e4?navigator.sendBeacon(a,n):fetch(a,{method:"POST",body:n,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var k=new E,C=new _,L={init:i=>k.init(i),initRum:i=>C.init(i)};typeof window<"u"&&(window.Senzor=L);0&&(module.exports={Analytics,RUM,Senzor});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let e=Math.random()*16|0;return(s==="x"?e:e&3|8).toString(16)})}function I(s){let e="";for(;e.length<s;)e+=Math.random().toString(16).slice(2);return e.slice(0,s)}var x=()=>{var s;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((s=navigator.connection)==null?void 0:s.effectiveType)||void 0}},T=s=>{if(!s)return 0;if(typeof s=="string")return s.length;if(s instanceof Blob||s instanceof File)return s.size;if(s instanceof ArrayBuffer)return s.byteLength},z=s=>{let e={};return s&&(s instanceof Headers?s.forEach((t,r)=>e[r]=t):Array.isArray(s)?s.forEach(([t,r])=>e[t]=r):typeof s=="object"&&Object.assign(e,s)),e};var _=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(e){return e?e.replace(/^https?:\/\//,"").replace(/^www\./,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let a=sessionStorage.getItem("senzor_sid"),c=e-t>r;!a||c?(a=v(),sessionStorage.setItem("senzor_sid",a),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,a=sessionStorage.getItem("senzor_ref"),c=!1;if(t)try{new URL(t).hostname!==r&&(c=!0)}catch{c=!0}if(c){let n=this.normalizeUrl(t);n!==a&&sessionStorage.setItem("senzor_ref",n)}else e&&!a&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now(),this.send({type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer})}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);e>=1&&this.send({type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer,duration:e})}send(e){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(e)],{type:"application/json"}))||this.fallbackSend(e):this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let e=history.pushState;history.pushState=(...t)=>{this.trackPing(),e.apply(history,t),this.trackPageView()},window.addEventListener("popstate",()=>{this.trackPing(),this.trackPageView()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.trackPing():(this.startTime=Date.now(),this.manageSession())}),window.addEventListener("beforeunload",()=>this.trackPing())}};var E=class{deps;constructor(e){this.deps=e}setup(){this.setupGlobalErrors(),this.setupPromiseErrors(),this.setupReactIntegration(),this.setupReactConsolePatch()}setupGlobalErrors(){window.addEventListener("error",e=>{if(e.error){this.capture(e.error,"Uncaught Exception");return}if(e.target&&e.target!==window){let t=e.target;this.capture(new Error("Resource failed to load"),"Resource Error")}},!0)}setupPromiseErrors(){window.addEventListener("unhandledrejection",e=>{let t=this.normalizeError(e.reason);this.capture(t,"Unhandled Promise Rejection")})}capture(e,t,r){if(this.shouldIgnore(e))return;let a={type:t,path:location.pathname,referrer:document.referrer||void 0,traceId:this.deps.isSampled?this.deps.traceId():void 0,...x(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",context:a,timestamp:new Date().toISOString()}),this.deps.flush()}normalizeError(e){if(e instanceof Error)return e;if(typeof e=="string")return new Error(e);if(e!=null&&e.message)return new Error(e.message);try{return new Error(JSON.stringify(e))}catch{return new Error("Unknown rejection")}}shouldIgnore(e){let t=e.stack||"";return!!(t.includes("chrome-extension://")||t.includes("moz-extension://")||t.includes("safari-extension://"))}setupReactIntegration(){let e=window.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!e||e.__senzor_patched)return;e.__senzor_patched=!0;let t=e.onCommitFiberRoot;e.onCommitFiberRoot=(r,a,...c)=>{if(t)return t.apply(e,[r,a,...c])}}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",a=0;console.error=(...c)=>{try{if(!c||!c.length)return t.apply(console,c);let n=c[0];if(typeof n=="string"&&n.includes("The above error occurred")){let i=Date.now();if(n===r&&i-a<2e3)return t.apply(console,c);r=n,a=i;let o=new Error("React component crash");this.capture(o,"React Error")}}catch{}return t.apply(console,c)}}};var R=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spanQueue=[];errorQueue=[];vitals={};breadcrumbs=[];frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;MAX_BATCH_SIZE=50;errorEngine;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.apiKey){console.error("[Senzor RUM] apiKey is required.");return}this.isSampled=Math.random()<=(this.config.sampleRate??1),this.manageSession(),this.startNewTrace(!0),this.errorEngine=new E({isSampled:this.isSampled,traceId:()=>this.traceId,sessionId:this.sessionId,breadcrumbs:this.breadcrumbs,frustrations:this.frustrations,errorQueue:this.errorQueue,flush:()=>this.flush()}),this.errorEngine.setup(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),5e3),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",v()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(e){this.traceId=I(32),this.traceStartTime=Date.now(),this.isInitialLoad=e,this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(e,t,r){this.breadcrumbs.push({type:e,message:t,data:r,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,r=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${r}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(r)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let n=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:n}),this.clickHistory=this.clickHistory.filter(i=>n-i.time<1e3),this.clickHistory.length>=3){let i=this.clickHistory[0],o=!0;for(let p=1;p<this.clickHistory.length;p++){let h=Math.abs(this.clickHistory[p].x-i.x),u=Math.abs(this.clickHistory[p].y-i.y);(h>50||u>50)&&(o=!1)}o&&(this.frustrations.rageClicks++,this.addBreadcrumb("frustration","Rage Click Detected"),this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(t=>{for(let r of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=r.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let r=t.getEntries(),a=r[r.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let r of t.getEntries())r.hadRecentInput||(e+=r.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let r of t.getEntries()){let a=r,c=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||c>this.vitals.inp)&&(this.vitals.inp=c)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let e=performance.getEntriesByType("navigation")[0];return e?{dns:Math.max(0,e.domainLookupEnd-e.domainLookupStart),tcp:Math.max(0,e.connectEnd-e.connectStart),ssl:e.secureConnectionStart?Math.max(0,e.requestStart-e.secureConnectionStart):0,ttfb:Math.max(0,e.responseStart-e.requestStart),domInteractive:Math.max(0,e.domInteractive-e.startTime),domComplete:Math.max(0,e.domComplete-e.startTime)}:{}}shouldAttachTraceHeader(e){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let t=new URL(e,window.location.origin);return this.config.allowedOrigins.some(r=>typeof r=="string"?t.origin.includes(r):r instanceof RegExp?r.test(t.origin):!1)}catch{return!1}}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,i,...o){return this.__szMethod=n.toUpperCase(),this.__szUrl=i,this.__szHeaders={},t.apply(this,[n,i,...o])},XMLHttpRequest.prototype.setRequestHeader=function(n,i){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=i,a.apply(this,[n,i])},XMLHttpRequest.prototype.send=function(n){let i=this,o=I(16),p=Date.now()-e.traceStartTime,h=i.__szMethod,u=i.__szUrl;try{u=new URL(i.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(u)&&i.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),i.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-p,m={};try{m=i.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((d,S)=>{let b=S.split(": "),f=b.shift(),H=b.join(": ");return f&&(d[f]=H),d},{})}catch{}let y={url:u,method:h,library:"xhr",status:i.status,responseType:i.responseType,requestPayloadSize:T(n),requestHeaders:i.__szHeaders,responseHeaders:m};try{(i.responseType===""||i.responseType==="text")&&(y.responsePayloadSize=(w=i.responseText)==null?void 0:w.length)}catch{}e.spanQueue.push({spanId:o,name:`${h} ${new URL(u,window.location.origin).pathname}`,type:"http",startTime:p,duration:g,status:i.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),r.call(this,n)};let c=window.fetch;window.fetch=async function(...n){let i=n[0],o=n[1],p="",h="GET";typeof i=="string"||i instanceof URL?(p=i.toString(),h=((o==null?void 0:o.method)||"GET").toUpperCase()):i instanceof Request&&(p=i.url,h=i.method.toUpperCase());let u=p;try{u=new URL(p,window.location.origin).toString()}catch{}let g=I(16),m=Date.now()-e.traceStartTime,y=z((o==null?void 0:o.headers)||(i instanceof Request?i.headers:{}));if(e.shouldAttachTraceHeader(u)){let l=`00-${e.traceId}-${g}-01`;if(i instanceof Request){let d=new Headers(i.headers);d.set("traceparent",l),n[1]={...o||{},headers:d}}else{let d=new Headers((o==null?void 0:o.headers)||{});d.set("traceparent",l),n[1]={...o||{},headers:d}}y.traceparent=l}let w=(l,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:u,method:h,library:"fetch",status:l,requestPayloadSize:T(o==null?void 0:o.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=z(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${h} ${new URL(u,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:l,meta:f}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()};try{let l=await c.apply(this,n);return w(l.status,l),l}catch(l){throw w(0,void 0,l instanceof Error?l.message:String(l)),l}}}setupRoutingListeners(){let e=history.pushState;history.pushState=(...t)=>{this.flush(),e.apply(history,t),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)},window.addEventListener("popstate",()=>{this.flush(),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),window.addEventListener("pagehide",()=>this.flush())}flush(){if(this.spanQueue.length===0&&this.errorQueue.length===0&&!this.isInitialLoad)return;let e=this.spanQueue.splice(0,this.MAX_BATCH_SIZE),t=this.errorQueue.splice(0,20),r={traces:[],errors:t};if(this.isSampled&&r.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...x(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,r.traces.length>0||r.errors.length>0){let a=new Blob([JSON.stringify(r)],{type:"application/json"}),c=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${c}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&a.size<6e4?navigator.sendBeacon(n,a):fetch(n,{method:"POST",body:a,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var k=new _,C=new R,L={init:s=>k.init(s),initRum:s=>C.init(s)};typeof window<"u"&&(window.Senzor=L);export{k as Analytics,C as RUM,L as Senzor};
|
|
1
|
+
function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let e=Math.random()*16|0;return(s==="x"?e:e&3|8).toString(16)})}function x(s){let e="";for(;e.length<s;)e+=Math.random().toString(16).slice(2);return e.slice(0,s)}var I=()=>{var s;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((s=navigator.connection)==null?void 0:s.effectiveType)||void 0}},R=s=>{if(!s)return 0;if(typeof s=="string")return s.length;if(s instanceof Blob||s instanceof File)return s.size;if(s instanceof ArrayBuffer)return s.byteLength},z=s=>{let e={};return s&&(s instanceof Headers?s.forEach((t,r)=>e[r]=t):Array.isArray(s)?s.forEach(([t,r])=>e[t]=r):typeof s=="object"&&Object.assign(e,s)),e};var E=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(e){return e?e.replace(/^https?:\/\//,"").replace(/^www\./,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let a=sessionStorage.getItem("senzor_sid"),c=e-t>r;!a||c?(a=v(),sessionStorage.setItem("senzor_sid",a),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,a=sessionStorage.getItem("senzor_ref"),c=!1;if(t)try{new URL(t).hostname!==r&&(c=!0)}catch{c=!0}if(c){let n=this.normalizeUrl(t);n!==a&&sessionStorage.setItem("senzor_ref",n)}else e&&!a&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now(),this.send({type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer})}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);e>=1&&this.send({type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,title:document.title,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,referrer:this.getIds().referrer,duration:e})}send(e){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(e)],{type:"application/json"}))||this.fallbackSend(e):this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let e=history.pushState;history.pushState=(...t)=>{this.trackPing(),e.apply(history,t),this.trackPageView()},window.addEventListener("popstate",()=>{this.trackPing(),this.trackPageView()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.trackPing():(this.startTime=Date.now(),this.manageSession())}),window.addEventListener("beforeunload",()=>this.trackPing())}};var T=class{deps;constructor(e){this.deps=e}setup(){this.setupGlobalErrors(),this.setupPromiseErrors(),this.setupReactConsolePatch()}setupGlobalErrors(){window.addEventListener("error",e=>{if(e.error){this.capture(e.error,"Uncaught Exception");return}e.target&&e.target!==window&&this.capture(new Error("Resource failed to load"),"Resource Error")},!0)}setupPromiseErrors(){window.addEventListener("unhandledrejection",e=>{let t=this.normalizeError(e.reason);this.capture(t,"Unhandled Promise Rejection")})}capture(e,t,r){if(this.shouldIgnore(e))return;this.deps.errorQueue.length>=this.deps.queueLimit&&this.deps.errorQueue.shift();let a={type:t,path:location.pathname,referrer:document.referrer||void 0,...I(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",traceId:this.deps.isSampled?this.deps.traceId():void 0,context:a,timestamp:new Date().toISOString()}),this.deps.flush()}normalizeError(e){if(e instanceof Error)return e;if(typeof e=="string")return new Error(e);if(e!=null&&e.message)return new Error(e.message);try{return new Error(JSON.stringify(e))}catch{return new Error("Unknown rejection")}}shouldIgnore(e){let t=e.stack||"";return!!(t.includes("chrome-extension://")||t.includes("moz-extension://")||t.includes("safari-extension://"))}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",a=0;console.error=(...c)=>{try{if(!c||!c.length)return t.apply(console,c);let n=c[0];if(typeof n=="string"&&(n.includes("The above error occurred")||n.includes("A cross-origin error was thrown"))){let i=Date.now();if(n===r&&i-a<2e3)return t.apply(console,c);r=n,a=i;let o=new Error("React Component Crash"),d=c.find(p=>typeof p=="string"&&p.includes(`
|
|
2
|
+
in `));d&&(o.stack=d),this.capture(o,"React Error")}}catch{}return t.apply(console,c)}}};var _=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spanQueue=[];errorQueue=[];vitals={};breadcrumbs=[];frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;flushTimeout=null;MAX_BATCH_SIZE=50;MAX_QUEUE_MEMORY=500;errorEngine;init(e){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...e},e.endpoint&&(this.endpoint=e.endpoint),!this.config.apiKey){console.error("[Senzor RUM] apiKey is required.");return}this.isSampled=Math.random()<=(this.config.sampleRate??1),this.manageSession(),this.startNewTrace(!0),this.errorEngine=new T({isSampled:this.isSampled,traceId:()=>this.traceId,sessionId:this.sessionId,breadcrumbs:this.breadcrumbs,frustrations:this.frustrations,errorQueue:this.errorQueue,queueLimit:this.MAX_QUEUE_MEMORY,flush:()=>this.debouncedFlush()}),this.errorEngine.setup(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),5e3),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",v()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(e){this.traceId=x(32),this.traceStartTime=Date.now(),this.isInitialLoad=e,this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(e,t,r){this.breadcrumbs.push({type:e,message:t,data:r,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,r=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${r}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(r)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let n=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:n}),this.clickHistory=this.clickHistory.filter(i=>n-i.time<1e3),this.clickHistory.length>=3){let i=this.clickHistory[0],o=!0;for(let d=1;d<this.clickHistory.length;d++){let p=Math.abs(this.clickHistory[d].x-i.x),h=Math.abs(this.clickHistory[d].y-i.y);(p>50||h>50)&&(o=!1)}o&&(this.frustrations.rageClicks++,this.addBreadcrumb("frustration","Rage Click Detected"),this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(t=>{for(let r of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=r.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let r=t.getEntries(),a=r[r.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let r of t.getEntries())r.hadRecentInput||(e+=r.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let r of t.getEntries()){let a=r,c=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||c>this.vitals.inp)&&(this.vitals.inp=c)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let e=performance.getEntriesByType("navigation")[0];return e?{dns:Math.max(0,e.domainLookupEnd-e.domainLookupStart),tcp:Math.max(0,e.connectEnd-e.connectStart),ssl:e.secureConnectionStart?Math.max(0,e.requestStart-e.secureConnectionStart):0,ttfb:Math.max(0,e.responseStart-e.requestStart),domInteractive:Math.max(0,e.domInteractive-e.startTime),domComplete:Math.max(0,e.domComplete-e.startTime)}:{}}shouldAttachTraceHeader(e){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let t=new URL(e,window.location.origin);return this.config.allowedOrigins.some(r=>typeof r=="string"?t.origin.includes(r):r instanceof RegExp?r.test(t.origin):!1)}catch{return!1}}pushSpan(e){this.spanQueue.length>=this.MAX_QUEUE_MEMORY&&this.spanQueue.shift(),this.spanQueue.push(e),this.spanQueue.length>=this.MAX_BATCH_SIZE&&this.debouncedFlush()}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,r=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,i,...o){return this.__szMethod=n.toUpperCase(),this.__szUrl=i,this.__szHeaders={},t.apply(this,[n,i,...o])},XMLHttpRequest.prototype.setRequestHeader=function(n,i){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=i,a.apply(this,[n,i])},XMLHttpRequest.prototype.send=function(n){let i=this,o=x(16),d=Date.now()-e.traceStartTime,p=i.__szMethod,h=i.__szUrl;try{h=new URL(i.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(h)&&i.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),i.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-d,m={};try{m=i.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((u,S)=>{let b=S.split(": "),f=b.shift(),H=b.join(": ");return f&&(u[f]=H),u},{})}catch{}let y={url:h,method:p,library:"xhr",status:i.status,responseType:i.responseType,requestPayloadSize:R(n),requestHeaders:i.__szHeaders,responseHeaders:m};try{(i.responseType===""||i.responseType==="text")&&(y.responsePayloadSize=(w=i.responseText)==null?void 0:w.length)}catch{}e.pushSpan({spanId:o,name:`${p} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:d,duration:g,status:i.status,meta:y})}),r.call(this,n)};let c=window.fetch;window.fetch=async function(...n){let i=n[0],o=n[1],d="",p="GET";typeof i=="string"||i instanceof URL?(d=i.toString(),p=((o==null?void 0:o.method)||"GET").toUpperCase()):i instanceof Request&&(d=i.url,p=i.method.toUpperCase());let h=d;try{h=new URL(d,window.location.origin).toString()}catch{}let g=x(16),m=Date.now()-e.traceStartTime,y=z((o==null?void 0:o.headers)||(i instanceof Request?i.headers:{}));if(e.shouldAttachTraceHeader(h)){let l=`00-${e.traceId}-${g}-01`;if(i instanceof Request){let u=new Headers(i.headers);u.set("traceparent",l),n[1]={...o||{},headers:u}}else{let u=new Headers((o==null?void 0:o.headers)||{});u.set("traceparent",l),n[1]={...o||{},headers:u}}y.traceparent=l}let w=(l,u,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:h,method:p,library:"fetch",status:l,requestPayloadSize:R(o==null?void 0:o.body),requestHeaders:y};u&&(f.statusText=u.statusText,f.type=u.type,f.redirected=u.redirected,f.responseHeaders=z(u.headers)),S&&(f.error=S),e.pushSpan({spanId:g,name:`${p} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:l,meta:f})};try{let l=await c.apply(this,n);return w(l.status,l),l}catch(l){throw w(0,void 0,l instanceof Error?l.message:String(l)),l}}}setupRoutingListeners(){let e=history.pushState;history.pushState=(...t)=>{this.flush(),e.apply(history,t),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)},window.addEventListener("popstate",()=>{this.flush(),this.startNewTrace(!1),this.addBreadcrumb("navigation",window.location.pathname)}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),window.addEventListener("pagehide",()=>this.flush())}debouncedFlush(){this.flushTimeout&&clearTimeout(this.flushTimeout),this.flushTimeout=setTimeout(()=>this.flush(),100)}flush(){if(this.spanQueue.length===0&&this.errorQueue.length===0&&!this.isInitialLoad)return;let e=this.spanQueue.splice(0,this.MAX_BATCH_SIZE),t=this.errorQueue.splice(0,20),r={traces:[],errors:t};if(this.isSampled&&r.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...I(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,r.traces.length>0||r.errors.length>0){let a=new Blob([JSON.stringify(r)],{type:"application/json"}),c=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${c}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&a.size<6e4?navigator.sendBeacon(n,a):fetch(n,{method:"POST",body:a,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var k=new E,C=new _,L={init:s=>k.init(s),initRum:s=>C.init(s)};typeof window<"u"&&(window.Senzor=L);export{k as Analytics,C as RUM,L as Senzor};
|
package/package.json
CHANGED
package/src/error.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface ErrorEngineDeps {
|
|
|
7
7
|
breadcrumbs: any[];
|
|
8
8
|
frustrations: { rageClicks: number; deadClicks: number; errorCount: number };
|
|
9
9
|
errorQueue: any[];
|
|
10
|
+
queueLimit: number;
|
|
10
11
|
flush: () => void;
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -20,7 +21,6 @@ export class ErrorEngine {
|
|
|
20
21
|
public setup() {
|
|
21
22
|
this.setupGlobalErrors();
|
|
22
23
|
this.setupPromiseErrors();
|
|
23
|
-
this.setupReactIntegration();
|
|
24
24
|
this.setupReactConsolePatch();
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -32,9 +32,9 @@ export class ErrorEngine {
|
|
|
32
32
|
if (event.error) {
|
|
33
33
|
this.capture(event.error, "Uncaught Exception");
|
|
34
34
|
return;
|
|
35
|
-
}
|
|
35
|
+
}
|
|
36
|
+
// Resource errors (script, css, img, font etc)
|
|
36
37
|
if (event.target && event.target !== window) {
|
|
37
|
-
const el: any = event.target;
|
|
38
38
|
this.capture(new Error("Resource failed to load"), "Resource Error");
|
|
39
39
|
}
|
|
40
40
|
},
|
|
@@ -51,21 +51,31 @@ export class ErrorEngine {
|
|
|
51
51
|
|
|
52
52
|
private capture(errorObj: Error, type: string, extra?: any) {
|
|
53
53
|
if (this.shouldIgnore(errorObj)) return;
|
|
54
|
+
|
|
55
|
+
// Prevent Memory Leaks on catastrophic infinite loops
|
|
56
|
+
if (this.deps.errorQueue.length >= this.deps.queueLimit) {
|
|
57
|
+
this.deps.errorQueue.shift(); // Drop oldest error
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
const context = {
|
|
55
61
|
type, // location
|
|
56
62
|
path: location.pathname,
|
|
57
|
-
referrer: document.referrer || undefined,
|
|
58
|
-
|
|
59
|
-
...getBrowserContext(), // breadcrumbs
|
|
63
|
+
referrer: document.referrer || undefined,
|
|
64
|
+
...getBrowserContext(), // userAgent, url, etc.
|
|
60
65
|
breadcrumbs: [...this.deps.breadcrumbs],
|
|
61
66
|
};
|
|
67
|
+
|
|
62
68
|
this.deps.errorQueue.push({
|
|
63
69
|
errorClass: errorObj.name || "Error",
|
|
64
70
|
message: errorObj.message || String(errorObj),
|
|
65
71
|
stackTrace: errorObj.stack || "",
|
|
72
|
+
// CRITICAL FIX: traceId MUST be at the root for backend polymorphic lookup to work
|
|
73
|
+
traceId: this.deps.isSampled ? this.deps.traceId() : undefined,
|
|
66
74
|
context,
|
|
67
75
|
timestamp: new Date().toISOString(),
|
|
68
76
|
});
|
|
77
|
+
|
|
78
|
+
// This triggers the debounced flush in rum.ts
|
|
69
79
|
this.deps.flush();
|
|
70
80
|
}
|
|
71
81
|
|
|
@@ -81,60 +91,61 @@ export class ErrorEngine {
|
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
private shouldIgnore(error: Error) {
|
|
84
|
-
const stack = error.stack || "";
|
|
94
|
+
const stack = error.stack || "";
|
|
95
|
+
// Ignore browser extensions noise to keep dashboard clean
|
|
85
96
|
if (stack.includes("chrome-extension://")) return true;
|
|
86
97
|
if (stack.includes("moz-extension://")) return true;
|
|
87
98
|
if (stack.includes("safari-extension://")) return true;
|
|
88
99
|
return false;
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
private setupReactIntegration() {
|
|
92
|
-
const hook = (window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
93
|
-
if (!hook) return;
|
|
94
|
-
// Prevent multiple patches
|
|
95
|
-
if (hook.__senzor_patched) return;
|
|
96
|
-
hook.__senzor_patched = true;
|
|
97
|
-
const orig = hook.onCommitFiberRoot;
|
|
98
|
-
hook.onCommitFiberRoot = (id: any, root: any, ...rest: any[]) => {
|
|
99
|
-
try {
|
|
100
|
-
// We don't depend on React internals.
|
|
101
|
-
// Only detecting React presence safely.
|
|
102
|
-
} catch {}
|
|
103
|
-
if (orig) {
|
|
104
|
-
return orig.apply(hook, [id, root, ...rest]);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
102
|
private setupReactConsolePatch() {
|
|
110
103
|
const consoleAny: any = console;
|
|
111
|
-
// Prevent multiple patches
|
|
104
|
+
// Prevent multiple patches from hot-reloading
|
|
112
105
|
if (consoleAny.__senzor_react_patch) return;
|
|
113
106
|
consoleAny.__senzor_react_patch = true;
|
|
107
|
+
|
|
114
108
|
const original = console.error;
|
|
109
|
+
|
|
115
110
|
// Prevent duplicate React StrictMode errors
|
|
116
111
|
let lastReactError = "";
|
|
117
112
|
let lastReactErrorTime = 0;
|
|
113
|
+
|
|
118
114
|
console.error = (...args: any[]) => {
|
|
119
115
|
try {
|
|
120
116
|
if (!args || !args.length) return original.apply(console, args);
|
|
121
117
|
const first = args[0];
|
|
122
|
-
|
|
118
|
+
|
|
119
|
+
// React component crash pattern string matching
|
|
123
120
|
if (typeof first === "string") {
|
|
124
|
-
if (
|
|
121
|
+
if (
|
|
122
|
+
first.includes("The above error occurred") ||
|
|
123
|
+
first.includes("A cross-origin error was thrown")
|
|
124
|
+
) {
|
|
125
125
|
const now = Date.now();
|
|
126
|
-
|
|
126
|
+
|
|
127
|
+
// Prevent duplicates (React strict mode throws twice)
|
|
127
128
|
if (first === lastReactError && now - lastReactErrorTime < 2000) {
|
|
128
129
|
return original.apply(console, args);
|
|
129
130
|
}
|
|
131
|
+
|
|
130
132
|
lastReactError = first;
|
|
131
133
|
lastReactErrorTime = now;
|
|
132
|
-
|
|
134
|
+
|
|
135
|
+
const error = new Error("React Component Crash");
|
|
136
|
+
// React usually passes the component stack in args[1] or args[2] depending on version
|
|
137
|
+
const componentStack = args.find(
|
|
138
|
+
(a) => typeof a === "string" && a.includes("\n in "),
|
|
139
|
+
);
|
|
140
|
+
if (componentStack) {
|
|
141
|
+
error.stack = componentStack;
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
this.capture(error, "React Error");
|
|
134
145
|
}
|
|
135
146
|
}
|
|
136
147
|
} catch {
|
|
137
|
-
// Never break console
|
|
148
|
+
// Never break the user's console logging
|
|
138
149
|
}
|
|
139
150
|
return original.apply(console, args);
|
|
140
151
|
};
|
package/src/rum.ts
CHANGED
|
@@ -30,7 +30,7 @@ export class SenzorRumAgent {
|
|
|
30
30
|
private traceStartTime: number = 0;
|
|
31
31
|
private isInitialLoad: boolean = true;
|
|
32
32
|
|
|
33
|
-
// --- BATCHING QUEUES ---
|
|
33
|
+
// --- BATCHING QUEUES & LIMITS ---
|
|
34
34
|
private spanQueue: any[] = [];
|
|
35
35
|
private errorQueue: any[] = [];
|
|
36
36
|
private vitals: any = {};
|
|
@@ -39,7 +39,9 @@ export class SenzorRumAgent {
|
|
|
39
39
|
private clickHistory: { x: number; y: number; time: number }[] = [];
|
|
40
40
|
|
|
41
41
|
private flushInterval: any;
|
|
42
|
+
private flushTimeout: any = null; // Used for debouncing burst errors
|
|
42
43
|
private readonly MAX_BATCH_SIZE = 50;
|
|
44
|
+
private readonly MAX_QUEUE_MEMORY = 500; // Prevent Out-of-Memory (OOM) crashes if offline
|
|
43
45
|
|
|
44
46
|
private errorEngine!: ErrorEngine;
|
|
45
47
|
|
|
@@ -66,7 +68,8 @@ export class SenzorRumAgent {
|
|
|
66
68
|
breadcrumbs: this.breadcrumbs,
|
|
67
69
|
frustrations: this.frustrations,
|
|
68
70
|
errorQueue: this.errorQueue,
|
|
69
|
-
|
|
71
|
+
queueLimit: this.MAX_QUEUE_MEMORY,
|
|
72
|
+
flush: () => this.debouncedFlush(),
|
|
70
73
|
});
|
|
71
74
|
this.errorEngine.setup();
|
|
72
75
|
|
|
@@ -126,6 +129,7 @@ export class SenzorRumAgent {
|
|
|
126
129
|
target.closest("a") ||
|
|
127
130
|
target.hasAttribute("role") ||
|
|
128
131
|
target.onclick;
|
|
132
|
+
|
|
129
133
|
if (!isInteractive) this.frustrations.deadClicks++;
|
|
130
134
|
|
|
131
135
|
const now = Date.now();
|
|
@@ -221,7 +225,7 @@ export class SenzorRumAgent {
|
|
|
221
225
|
};
|
|
222
226
|
}
|
|
223
227
|
|
|
224
|
-
// --- 3. Distributed Tracing &
|
|
228
|
+
// --- 3. Distributed Tracing & Network Patching ---
|
|
225
229
|
private shouldAttachTraceHeader(url: string): boolean {
|
|
226
230
|
if (!this.config.allowedOrigins || this.config.allowedOrigins.length === 0)
|
|
227
231
|
return false;
|
|
@@ -238,6 +242,12 @@ export class SenzorRumAgent {
|
|
|
238
242
|
}
|
|
239
243
|
}
|
|
240
244
|
|
|
245
|
+
private pushSpan(span: any) {
|
|
246
|
+
if (this.spanQueue.length >= this.MAX_QUEUE_MEMORY) this.spanQueue.shift(); // Drop oldest to prevent OOM
|
|
247
|
+
this.spanQueue.push(span);
|
|
248
|
+
if (this.spanQueue.length >= this.MAX_BATCH_SIZE) this.debouncedFlush();
|
|
249
|
+
}
|
|
250
|
+
|
|
241
251
|
private patchNetwork() {
|
|
242
252
|
const self = this;
|
|
243
253
|
|
|
@@ -318,8 +328,7 @@ export class SenzorRumAgent {
|
|
|
318
328
|
}
|
|
319
329
|
} catch (e) {}
|
|
320
330
|
|
|
321
|
-
|
|
322
|
-
self.spanQueue.push({
|
|
331
|
+
self.pushSpan({
|
|
323
332
|
spanId,
|
|
324
333
|
name: `${method} ${new URL(fullUrl, window.location.origin).pathname}`,
|
|
325
334
|
type: "http",
|
|
@@ -328,8 +337,6 @@ export class SenzorRumAgent {
|
|
|
328
337
|
status: xhr.status,
|
|
329
338
|
meta,
|
|
330
339
|
});
|
|
331
|
-
|
|
332
|
-
if (self.spanQueue.length >= self.MAX_BATCH_SIZE) self.flush();
|
|
333
340
|
});
|
|
334
341
|
|
|
335
342
|
return originalXhrSend.call(this, body);
|
|
@@ -404,7 +411,7 @@ export class SenzorRumAgent {
|
|
|
404
411
|
|
|
405
412
|
if (errorMsg) meta.error = errorMsg;
|
|
406
413
|
|
|
407
|
-
self.
|
|
414
|
+
self.pushSpan({
|
|
408
415
|
spanId,
|
|
409
416
|
name: `${method} ${new URL(fullUrl, window.location.origin).pathname}`,
|
|
410
417
|
type: "http",
|
|
@@ -413,8 +420,6 @@ export class SenzorRumAgent {
|
|
|
413
420
|
status,
|
|
414
421
|
meta,
|
|
415
422
|
});
|
|
416
|
-
|
|
417
|
-
if (self.spanQueue.length >= self.MAX_BATCH_SIZE) self.flush();
|
|
418
423
|
};
|
|
419
424
|
|
|
420
425
|
try {
|
|
@@ -455,6 +460,11 @@ export class SenzorRumAgent {
|
|
|
455
460
|
window.addEventListener("pagehide", () => this.flush());
|
|
456
461
|
}
|
|
457
462
|
|
|
463
|
+
private debouncedFlush() {
|
|
464
|
+
if (this.flushTimeout) clearTimeout(this.flushTimeout);
|
|
465
|
+
this.flushTimeout = setTimeout(() => this.flush(), 100); // 100ms debounce to prevent network spam
|
|
466
|
+
}
|
|
467
|
+
|
|
458
468
|
private flush() {
|
|
459
469
|
if (
|
|
460
470
|
this.spanQueue.length === 0 &&
|
|
@@ -463,6 +473,7 @@ export class SenzorRumAgent {
|
|
|
463
473
|
)
|
|
464
474
|
return;
|
|
465
475
|
|
|
476
|
+
// Drain Queue up to batch limit to ensure payload fits in Beacon API limits
|
|
466
477
|
const spansToSend = this.spanQueue.splice(0, this.MAX_BATCH_SIZE);
|
|
467
478
|
const errorsToSend = this.errorQueue.splice(0, 20);
|
|
468
479
|
|
|
@@ -496,6 +507,7 @@ export class SenzorRumAgent {
|
|
|
496
507
|
const authUrl = `${this.endpoint}${separator}apiKey=${this.config.apiKey}`;
|
|
497
508
|
|
|
498
509
|
if (navigator.sendBeacon && blob.size < 60000) {
|
|
510
|
+
// Safety check against 64k beacon limit
|
|
499
511
|
navigator.sendBeacon(authUrl, blob);
|
|
500
512
|
} else {
|
|
501
513
|
fetch(authUrl, {
|