@senzops/web 1.3.4 → 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 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
 
@@ -1,2 +1,2 @@
1
- (()=>{function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}function I(a){let e="";for(;e.length<a;)e+=Math.random().toString(16).slice(2);return e.slice(0,a)}var _=()=>{var a;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((a=navigator.connection)==null?void 0:a.effectiveType)||void 0}},T=a=>{if(!a)return 0;if(typeof a=="string")return a.length;if(a instanceof Blob||a instanceof File)return a.size;if(a instanceof ArrayBuffer)return a.byteLength},k=a=>{let e={};return a&&(a instanceof Headers?a.forEach((t,r)=>e[r]=t):Array.isArray(a)?a.forEach(([t,r])=>e[t]=r):typeof a=="object"&&Object.assign(e,a)),e};var x=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 i=sessionStorage.getItem("senzor_sid"),o=e-t>r;!i||o?(i=v(),sessionStorage.setItem("senzor_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,i=sessionStorage.getItem("senzor_ref"),o=!1;if(t)try{new URL(t).hostname!==r&&(o=!0)}catch{o=!0}if(o){let n=this.normalizeUrl(t);n!==i&&sessionStorage.setItem("senzor_ref",n)}else e&&!i&&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",{file:e.filename,line:e.lineno,column:e.colno});return}if(e.target&&e.target!==window){let t=e.target;this.capture(new Error("Resource failed to load"),"Resource Error",{file:(t==null?void 0:t.src)||(t==null?void 0:t.href)||"unknown",tag:t==null?void 0:t.tagName})}},!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.frustrations.errorCount++;let i=this.extractTopFrame(e),o=this.getLastUserInteraction(),n;try{n=this.deps.getLastNetworkSpan?this.deps.getLastNetworkSpan():void 0}catch{n=void 0}let s={type:t,path:location.pathname,referrer:document.referrer||void 0,traceId:this.deps.isSampled?this.deps.traceId():void 0,sessionId:this.deps.sessionId,file:(r==null?void 0:r.file)||(i==null?void 0:i.file),line:(r==null?void 0:r.line)||(i==null?void 0:i.line),column:(r==null?void 0:r.column)||(i==null?void 0:i.column),topFrame:i,lastInteraction:o,lastNetworkSpan:n,frustrations:{...this.deps.frustrations},..._(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",context:s,timestamp:new Date().toISOString()}),this.deps.flush()}extractTopFrame(e){if(!e.stack)return;let t=e.stack.split(`
2
- `);for(let r=1;r<t.length;r++){let i=t[r],o=i.match(/\(?(.+):(\d+):(\d+)\)?/);if(o)return{file:o[1],line:Number(o[2]),column:Number(o[3]),raw:i.trim()}}}getLastUserInteraction(){if(this.deps.breadcrumbs.length)for(let e=this.deps.breadcrumbs.length-1;e>=0;e--){let t=this.deps.breadcrumbs[e];if(t.type==="click")return t}}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,i,...o)=>{if(t)return t.apply(e,[r,i,...o])}}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",i=0;console.error=(...o)=>{try{if(!o||!o.length)return t.apply(console,o);let n=o[0];if(typeof n=="string"&&n.includes("The above error occurred")){let s=Date.now();if(n===r&&s-i<2e3)return t.apply(console,o);r=n,i=s;let c=new Error("React component crash");this.capture(c,"React Error")}}catch{}return t.apply(console,o)}}};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(),getLastNetworkSpan:()=>{var t;return(t=this.spanQueue)!=null&&t.length?this.spanQueue[this.spanQueue.length-1]:void 0}}),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(s=>n-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],c=!0;for(let u=1;u<this.clickHistory.length;u++){let h=Math.abs(this.clickHistory[u].x-s.x),p=Math.abs(this.clickHistory[u].y-s.y);(h>50||p>50)&&(c=!1)}c&&(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(),i=r[r.length-1];i&&(this.vitals.lcp=i.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 i=r,o=i.duration||(i.processingStart&&i.startTime?i.processingStart-i.startTime:0);(!this.vitals.inp||o>this.vitals.inp)&&(this.vitals.inp=o)}}).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,i=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,s,...c){return this.__szMethod=n.toUpperCase(),this.__szUrl=s,this.__szHeaders={},t.apply(this,[n,s,...c])},XMLHttpRequest.prototype.setRequestHeader=function(n,s){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=s,i.apply(this,[n,s])},XMLHttpRequest.prototype.send=function(n){let s=this,c=I(16),u=Date.now()-e.traceStartTime,h=s.__szMethod,p=s.__szUrl;try{p=new URL(s.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(p)&&s.setRequestHeader("traceparent",`00-${e.traceId}-${c}-01`),s.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-u,m={};try{m=s.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((d,S)=>{let b=S.split(": "),f=b.shift(),z=b.join(": ");return f&&(d[f]=z),d},{})}catch{}let y={url:p,method:h,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:T(n),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:c,name:`${h} ${new URL(p,window.location.origin).pathname}`,type:"http",startTime:u,duration:g,status:s.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),r.call(this,n)};let o=window.fetch;window.fetch=async function(...n){let s=n[0],c=n[1],u="",h="GET";typeof s=="string"||s instanceof URL?(u=s.toString(),h=((c==null?void 0:c.method)||"GET").toUpperCase()):s instanceof Request&&(u=s.url,h=s.method.toUpperCase());let p=u;try{p=new URL(u,window.location.origin).toString()}catch{}let g=I(16),m=Date.now()-e.traceStartTime,y=k((c==null?void 0:c.headers)||(s instanceof Request?s.headers:{}));if(e.shouldAttachTraceHeader(p)){let l=`00-${e.traceId}-${g}-01`;if(s instanceof Request){let d=new Headers(s.headers);d.set("traceparent",l),n[1]={...c||{},headers:d}}else{let d=new Headers((c==null?void 0:c.headers)||{});d.set("traceparent",l),n[1]={...c||{},headers:d}}y.traceparent=l}let w=(l,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:p,method:h,library:"fetch",status:l,requestPayloadSize:T(c==null?void 0:c.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=k(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${h} ${new URL(p,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 o.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},..._(),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 i=new Blob([JSON.stringify(r)],{type:"application/json"}),o=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${o}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&i.size<6e4?navigator.sendBeacon(n,i):fetch(n,{method:"POST",body:i,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var H=new x,L=new R,C={init:a=>H.init(a),initRum:a=>L.init(a)};typeof window<"u"&&(window.Senzor=C);})();
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,2 +1,2 @@
1
- var T=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var M=(n,e)=>{for(var t in e)T(n,t,{get:e[t],enumerable:!0})},N=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of P(e))!A.call(n,i)&&i!==t&&T(n,i,{get:()=>e[i],enumerable:!(r=D(e,i))||r.enumerable});return n};var O=n=>N(T({},"__esModule",{value:!0}),n);var q={};M(q,{Analytics:()=>H,RUM:()=>L,Senzor:()=>C});module.exports=O(q);function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let e=Math.random()*16|0;return(n==="x"?e:e&3|8).toString(16)})}function I(n){let e="";for(;e.length<n;)e+=Math.random().toString(16).slice(2);return e.slice(0,n)}var _=()=>{var n;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((n=navigator.connection)==null?void 0:n.effectiveType)||void 0}},k=n=>{if(!n)return 0;if(typeof n=="string")return n.length;if(n instanceof Blob||n instanceof File)return n.size;if(n instanceof ArrayBuffer)return n.byteLength},z=n=>{let e={};return n&&(n instanceof Headers?n.forEach((t,r)=>e[r]=t):Array.isArray(n)?n.forEach(([t,r])=>e[t]=r):typeof n=="object"&&Object.assign(e,n)),e};var x=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 i=sessionStorage.getItem("senzor_sid"),o=e-t>r;!i||o?(i=v(),sessionStorage.setItem("senzor_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,i=sessionStorage.getItem("senzor_ref"),o=!1;if(t)try{new URL(t).hostname!==r&&(o=!0)}catch{o=!0}if(o){let a=this.normalizeUrl(t);a!==i&&sessionStorage.setItem("senzor_ref",a)}else e&&!i&&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",{file:e.filename,line:e.lineno,column:e.colno});return}if(e.target&&e.target!==window){let t=e.target;this.capture(new Error("Resource failed to load"),"Resource Error",{file:(t==null?void 0:t.src)||(t==null?void 0:t.href)||"unknown",tag:t==null?void 0:t.tagName})}},!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.frustrations.errorCount++;let i=this.extractTopFrame(e),o=this.getLastUserInteraction(),a;try{a=this.deps.getLastNetworkSpan?this.deps.getLastNetworkSpan():void 0}catch{a=void 0}let s={type:t,path:location.pathname,referrer:document.referrer||void 0,traceId:this.deps.isSampled?this.deps.traceId():void 0,sessionId:this.deps.sessionId,file:(r==null?void 0:r.file)||(i==null?void 0:i.file),line:(r==null?void 0:r.line)||(i==null?void 0:i.line),column:(r==null?void 0:r.column)||(i==null?void 0:i.column),topFrame:i,lastInteraction:o,lastNetworkSpan:a,frustrations:{...this.deps.frustrations},..._(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",context:s,timestamp:new Date().toISOString()}),this.deps.flush()}extractTopFrame(e){if(!e.stack)return;let t=e.stack.split(`
2
- `);for(let r=1;r<t.length;r++){let i=t[r],o=i.match(/\(?(.+):(\d+):(\d+)\)?/);if(o)return{file:o[1],line:Number(o[2]),column:Number(o[3]),raw:i.trim()}}}getLastUserInteraction(){if(this.deps.breadcrumbs.length)for(let e=this.deps.breadcrumbs.length-1;e>=0;e--){let t=this.deps.breadcrumbs[e];if(t.type==="click")return t}}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,i,...o)=>{if(t)return t.apply(e,[r,i,...o])}}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",i=0;console.error=(...o)=>{try{if(!o||!o.length)return t.apply(console,o);let a=o[0];if(typeof a=="string"&&a.includes("The above error occurred")){let s=Date.now();if(a===r&&s-i<2e3)return t.apply(console,o);r=a,i=s;let c=new Error("React component crash");this.capture(c,"React Error")}}catch{}return t.apply(console,o)}}};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(),getLastNetworkSpan:()=>{var t;return(t=this.spanQueue)!=null&&t.length?this.spanQueue[this.spanQueue.length-1]:void 0}}),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],c=!0;for(let u=1;u<this.clickHistory.length;u++){let h=Math.abs(this.clickHistory[u].x-s.x),p=Math.abs(this.clickHistory[u].y-s.y);(h>50||p>50)&&(c=!1)}c&&(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(),i=r[r.length-1];i&&(this.vitals.lcp=i.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 i=r,o=i.duration||(i.processingStart&&i.startTime?i.processingStart-i.startTime:0);(!this.vitals.inp||o>this.vitals.inp)&&(this.vitals.inp=o)}}).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,i=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(a,s,...c){return this.__szMethod=a.toUpperCase(),this.__szUrl=s,this.__szHeaders={},t.apply(this,[a,s,...c])},XMLHttpRequest.prototype.setRequestHeader=function(a,s){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[a]=s,i.apply(this,[a,s])},XMLHttpRequest.prototype.send=function(a){let s=this,c=I(16),u=Date.now()-e.traceStartTime,h=s.__szMethod,p=s.__szUrl;try{p=new URL(s.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(p)&&s.setRequestHeader("traceparent",`00-${e.traceId}-${c}-01`),s.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-u,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:p,method:h,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:k(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:c,name:`${h} ${new URL(p,window.location.origin).pathname}`,type:"http",startTime:u,duration:g,status:s.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),r.call(this,a)};let o=window.fetch;window.fetch=async function(...a){let s=a[0],c=a[1],u="",h="GET";typeof s=="string"||s instanceof URL?(u=s.toString(),h=((c==null?void 0:c.method)||"GET").toUpperCase()):s instanceof Request&&(u=s.url,h=s.method.toUpperCase());let p=u;try{p=new URL(u,window.location.origin).toString()}catch{}let g=I(16),m=Date.now()-e.traceStartTime,y=z((c==null?void 0:c.headers)||(s instanceof Request?s.headers:{}));if(e.shouldAttachTraceHeader(p)){let l=`00-${e.traceId}-${g}-01`;if(s instanceof Request){let d=new Headers(s.headers);d.set("traceparent",l),a[1]={...c||{},headers:d}}else{let d=new Headers((c==null?void 0:c.headers)||{});d.set("traceparent",l),a[1]={...c||{},headers:d}}y.traceparent=l}let w=(l,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:p,method:h,library:"fetch",status:l,requestPayloadSize:k(c==null?void 0:c.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(p,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 o.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},..._(),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 i=new Blob([JSON.stringify(r)],{type:"application/json"}),o=this.endpoint.includes("?")?"&":"?",a=`${this.endpoint}${o}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&i.size<6e4?navigator.sendBeacon(a,i):fetch(a,{method:"POST",body:i,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var H=new x,L=new R,C={init:n=>H.init(n),initRum:n=>L.init(n)};typeof window<"u"&&(window.Senzor=C);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,2 +1,2 @@
1
- function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}function I(a){let e="";for(;e.length<a;)e+=Math.random().toString(16).slice(2);return e.slice(0,a)}var _=()=>{var a;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((a=navigator.connection)==null?void 0:a.effectiveType)||void 0}},T=a=>{if(!a)return 0;if(typeof a=="string")return a.length;if(a instanceof Blob||a instanceof File)return a.size;if(a instanceof ArrayBuffer)return a.byteLength},k=a=>{let e={};return a&&(a instanceof Headers?a.forEach((t,r)=>e[r]=t):Array.isArray(a)?a.forEach(([t,r])=>e[t]=r):typeof a=="object"&&Object.assign(e,a)),e};var x=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 i=sessionStorage.getItem("senzor_sid"),o=e-t>r;!i||o?(i=v(),sessionStorage.setItem("senzor_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,i=sessionStorage.getItem("senzor_ref"),o=!1;if(t)try{new URL(t).hostname!==r&&(o=!0)}catch{o=!0}if(o){let n=this.normalizeUrl(t);n!==i&&sessionStorage.setItem("senzor_ref",n)}else e&&!i&&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",{file:e.filename,line:e.lineno,column:e.colno});return}if(e.target&&e.target!==window){let t=e.target;this.capture(new Error("Resource failed to load"),"Resource Error",{file:(t==null?void 0:t.src)||(t==null?void 0:t.href)||"unknown",tag:t==null?void 0:t.tagName})}},!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.frustrations.errorCount++;let i=this.extractTopFrame(e),o=this.getLastUserInteraction(),n;try{n=this.deps.getLastNetworkSpan?this.deps.getLastNetworkSpan():void 0}catch{n=void 0}let s={type:t,path:location.pathname,referrer:document.referrer||void 0,traceId:this.deps.isSampled?this.deps.traceId():void 0,sessionId:this.deps.sessionId,file:(r==null?void 0:r.file)||(i==null?void 0:i.file),line:(r==null?void 0:r.line)||(i==null?void 0:i.line),column:(r==null?void 0:r.column)||(i==null?void 0:i.column),topFrame:i,lastInteraction:o,lastNetworkSpan:n,frustrations:{...this.deps.frustrations},..._(),breadcrumbs:[...this.deps.breadcrumbs]};this.deps.errorQueue.push({errorClass:e.name||"Error",message:e.message||String(e),stackTrace:e.stack||"",context:s,timestamp:new Date().toISOString()}),this.deps.flush()}extractTopFrame(e){if(!e.stack)return;let t=e.stack.split(`
2
- `);for(let r=1;r<t.length;r++){let i=t[r],o=i.match(/\(?(.+):(\d+):(\d+)\)?/);if(o)return{file:o[1],line:Number(o[2]),column:Number(o[3]),raw:i.trim()}}}getLastUserInteraction(){if(this.deps.breadcrumbs.length)for(let e=this.deps.breadcrumbs.length-1;e>=0;e--){let t=this.deps.breadcrumbs[e];if(t.type==="click")return t}}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,i,...o)=>{if(t)return t.apply(e,[r,i,...o])}}setupReactConsolePatch(){let e=console;if(e.__senzor_react_patch)return;e.__senzor_react_patch=!0;let t=console.error,r="",i=0;console.error=(...o)=>{try{if(!o||!o.length)return t.apply(console,o);let n=o[0];if(typeof n=="string"&&n.includes("The above error occurred")){let s=Date.now();if(n===r&&s-i<2e3)return t.apply(console,o);r=n,i=s;let c=new Error("React component crash");this.capture(c,"React Error")}}catch{}return t.apply(console,o)}}};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(),getLastNetworkSpan:()=>{var t;return(t=this.spanQueue)!=null&&t.length?this.spanQueue[this.spanQueue.length-1]:void 0}}),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(s=>n-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],c=!0;for(let u=1;u<this.clickHistory.length;u++){let h=Math.abs(this.clickHistory[u].x-s.x),p=Math.abs(this.clickHistory[u].y-s.y);(h>50||p>50)&&(c=!1)}c&&(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(),i=r[r.length-1];i&&(this.vitals.lcp=i.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 i=r,o=i.duration||(i.processingStart&&i.startTime?i.processingStart-i.startTime:0);(!this.vitals.inp||o>this.vitals.inp)&&(this.vitals.inp=o)}}).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,i=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,s,...c){return this.__szMethod=n.toUpperCase(),this.__szUrl=s,this.__szHeaders={},t.apply(this,[n,s,...c])},XMLHttpRequest.prototype.setRequestHeader=function(n,s){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=s,i.apply(this,[n,s])},XMLHttpRequest.prototype.send=function(n){let s=this,c=I(16),u=Date.now()-e.traceStartTime,h=s.__szMethod,p=s.__szUrl;try{p=new URL(s.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(p)&&s.setRequestHeader("traceparent",`00-${e.traceId}-${c}-01`),s.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-u,m={};try{m=s.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((d,S)=>{let b=S.split(": "),f=b.shift(),z=b.join(": ");return f&&(d[f]=z),d},{})}catch{}let y={url:p,method:h,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:T(n),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:c,name:`${h} ${new URL(p,window.location.origin).pathname}`,type:"http",startTime:u,duration:g,status:s.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),r.call(this,n)};let o=window.fetch;window.fetch=async function(...n){let s=n[0],c=n[1],u="",h="GET";typeof s=="string"||s instanceof URL?(u=s.toString(),h=((c==null?void 0:c.method)||"GET").toUpperCase()):s instanceof Request&&(u=s.url,h=s.method.toUpperCase());let p=u;try{p=new URL(u,window.location.origin).toString()}catch{}let g=I(16),m=Date.now()-e.traceStartTime,y=k((c==null?void 0:c.headers)||(s instanceof Request?s.headers:{}));if(e.shouldAttachTraceHeader(p)){let l=`00-${e.traceId}-${g}-01`;if(s instanceof Request){let d=new Headers(s.headers);d.set("traceparent",l),n[1]={...c||{},headers:d}}else{let d=new Headers((c==null?void 0:c.headers)||{});d.set("traceparent",l),n[1]={...c||{},headers:d}}y.traceparent=l}let w=(l,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:p,method:h,library:"fetch",status:l,requestPayloadSize:T(c==null?void 0:c.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=k(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${h} ${new URL(p,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 o.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},..._(),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 i=new Blob([JSON.stringify(r)],{type:"application/json"}),o=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${o}apiKey=${this.config.apiKey}`;navigator.sendBeacon&&i.size<6e4?navigator.sendBeacon(n,i):fetch(n,{method:"POST",body:i,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var H=new x,L=new R,C={init:a=>H.init(a),initRum:a=>L.init(a)};typeof window<"u"&&(window.Senzor=C);export{H as Analytics,L as RUM,C 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@senzops/web",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "Senzor Web Analytics and RUM SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/error.ts CHANGED
@@ -7,8 +7,8 @@ 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
- getLastNetworkSpan?: () => any; // optional correlation hook
12
12
  }
13
13
 
14
14
  export class ErrorEngine {
@@ -21,7 +21,6 @@ export class ErrorEngine {
21
21
  public setup() {
22
22
  this.setupGlobalErrors();
23
23
  this.setupPromiseErrors();
24
- this.setupReactIntegration();
25
24
  this.setupReactConsolePatch();
26
25
  }
27
26
 
@@ -31,19 +30,12 @@ export class ErrorEngine {
31
30
  (event: ErrorEvent | any) => {
32
31
  // JS runtime errors
33
32
  if (event.error) {
34
- this.capture(event.error, "Uncaught Exception", {
35
- file: event.filename,
36
- line: event.lineno,
37
- column: event.colno,
38
- });
33
+ this.capture(event.error, "Uncaught Exception");
39
34
  return;
40
- } // Resource errors (script, css, img, font etc)
35
+ }
36
+ // Resource errors (script, css, img, font etc)
41
37
  if (event.target && event.target !== window) {
42
- const el: any = event.target;
43
- this.capture(new Error("Resource failed to load"), "Resource Error", {
44
- file: el?.src || el?.href || "unknown",
45
- tag: el?.tagName,
46
- });
38
+ this.capture(new Error("Resource failed to load"), "Resource Error");
47
39
  }
48
40
  },
49
41
  true,
@@ -59,72 +51,32 @@ export class ErrorEngine {
59
51
 
60
52
  private capture(errorObj: Error, type: string, extra?: any) {
61
53
  if (this.shouldIgnore(errorObj)) return;
62
- this.deps.frustrations.errorCount++;
63
- const topFrame = this.extractTopFrame(errorObj);
64
- const lastInteraction = this.getLastUserInteraction();
65
- let lastNetwork;
66
- try {
67
- lastNetwork = this.deps.getLastNetworkSpan
68
- ? this.deps.getLastNetworkSpan()
69
- : undefined;
70
- } catch {
71
- lastNetwork = undefined;
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
72
58
  }
59
+
73
60
  const context = {
74
61
  type, // location
75
62
  path: location.pathname,
76
- referrer: document.referrer || undefined, // tracing
77
- traceId: this.deps.isSampled ? this.deps.traceId() : undefined,
78
- sessionId: this.deps.sessionId, // source location
79
- file: extra?.file || topFrame?.file,
80
- line: extra?.line || topFrame?.line,
81
- column: extra?.column || topFrame?.column,
82
- topFrame, // correlations
83
- lastInteraction,
84
- lastNetworkSpan: lastNetwork, // UX
85
- frustrations: { ...this.deps.frustrations }, // browser
86
- ...getBrowserContext(), // breadcrumbs
63
+ referrer: document.referrer || undefined,
64
+ ...getBrowserContext(), // userAgent, url, etc.
87
65
  breadcrumbs: [...this.deps.breadcrumbs],
88
66
  };
67
+
89
68
  this.deps.errorQueue.push({
90
69
  errorClass: errorObj.name || "Error",
91
70
  message: errorObj.message || String(errorObj),
92
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,
93
74
  context,
94
75
  timestamp: new Date().toISOString(),
95
76
  });
96
- this.deps.flush();
97
- }
98
-
99
- // --- Stack intelligence ---
100
- private extractTopFrame(error: Error) {
101
- if (!error.stack) return undefined;
102
- const lines = error.stack.split("\n");
103
- for (let i = 1; i < lines.length; i++) {
104
- const line = lines[i];
105
- const match = line.match(/\(?(.+):(\d+):(\d+)\)?/);
106
- if (match) {
107
- return {
108
- file: match[1],
109
- line: Number(match[2]),
110
- column: Number(match[3]),
111
- raw: line.trim(),
112
- };
113
- }
114
- }
115
- return undefined;
116
- }
117
77
 
118
- // --- User interaction intelligence ---
119
- private getLastUserInteraction() {
120
- if (!this.deps.breadcrumbs.length) return undefined;
121
- for (let i = this.deps.breadcrumbs.length - 1; i >= 0; i--) {
122
- const crumb = this.deps.breadcrumbs[i];
123
- if (crumb.type === "click") {
124
- return crumb;
125
- }
126
- }
127
- return undefined;
78
+ // This triggers the debounced flush in rum.ts
79
+ this.deps.flush();
128
80
  }
129
81
 
130
82
  private normalizeError(reason: any): Error {
@@ -139,60 +91,61 @@ export class ErrorEngine {
139
91
  }
140
92
 
141
93
  private shouldIgnore(error: Error) {
142
- const stack = error.stack || ""; // Ignore browser extensions noise
94
+ const stack = error.stack || "";
95
+ // Ignore browser extensions noise to keep dashboard clean
143
96
  if (stack.includes("chrome-extension://")) return true;
144
97
  if (stack.includes("moz-extension://")) return true;
145
98
  if (stack.includes("safari-extension://")) return true;
146
99
  return false;
147
100
  }
148
101
 
149
- private setupReactIntegration() {
150
- const hook = (window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__;
151
- if (!hook) return;
152
- // Prevent multiple patches
153
- if (hook.__senzor_patched) return;
154
- hook.__senzor_patched = true;
155
- const orig = hook.onCommitFiberRoot;
156
- hook.onCommitFiberRoot = (id: any, root: any, ...rest: any[]) => {
157
- try {
158
- // We don't depend on React internals.
159
- // Only detecting React presence safely.
160
- } catch {}
161
- if (orig) {
162
- return orig.apply(hook, [id, root, ...rest]);
163
- }
164
- };
165
- }
166
-
167
102
  private setupReactConsolePatch() {
168
103
  const consoleAny: any = console;
169
- // Prevent multiple patches
104
+ // Prevent multiple patches from hot-reloading
170
105
  if (consoleAny.__senzor_react_patch) return;
171
106
  consoleAny.__senzor_react_patch = true;
107
+
172
108
  const original = console.error;
109
+
173
110
  // Prevent duplicate React StrictMode errors
174
111
  let lastReactError = "";
175
112
  let lastReactErrorTime = 0;
113
+
176
114
  console.error = (...args: any[]) => {
177
115
  try {
178
116
  if (!args || !args.length) return original.apply(console, args);
179
117
  const first = args[0];
180
- // React component crash pattern
118
+
119
+ // React component crash pattern string matching
181
120
  if (typeof first === "string") {
182
- if (first.includes("The above error occurred")) {
121
+ if (
122
+ first.includes("The above error occurred") ||
123
+ first.includes("A cross-origin error was thrown")
124
+ ) {
183
125
  const now = Date.now();
184
- // Prevent duplicates (React strict mode)
126
+
127
+ // Prevent duplicates (React strict mode throws twice)
185
128
  if (first === lastReactError && now - lastReactErrorTime < 2000) {
186
129
  return original.apply(console, args);
187
130
  }
131
+
188
132
  lastReactError = first;
189
133
  lastReactErrorTime = now;
190
- const error = new Error("React component crash");
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
+
191
144
  this.capture(error, "React Error");
192
145
  }
193
146
  }
194
147
  } catch {
195
- // Never break console
148
+ // Never break the user's console logging
196
149
  }
197
150
  return original.apply(console, args);
198
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,11 +68,8 @@ export class SenzorRumAgent {
66
68
  breadcrumbs: this.breadcrumbs,
67
69
  frustrations: this.frustrations,
68
70
  errorQueue: this.errorQueue,
69
- flush: () => this.flush(),
70
- getLastNetworkSpan: () =>
71
- this.spanQueue?.length
72
- ? this.spanQueue[this.spanQueue.length - 1]
73
- : undefined,
71
+ queueLimit: this.MAX_QUEUE_MEMORY,
72
+ flush: () => this.debouncedFlush(),
74
73
  });
75
74
  this.errorEngine.setup();
76
75
 
@@ -130,6 +129,7 @@ export class SenzorRumAgent {
130
129
  target.closest("a") ||
131
130
  target.hasAttribute("role") ||
132
131
  target.onclick;
132
+
133
133
  if (!isInteractive) this.frustrations.deadClicks++;
134
134
 
135
135
  const now = Date.now();
@@ -225,7 +225,7 @@ export class SenzorRumAgent {
225
225
  };
226
226
  }
227
227
 
228
- // --- 3. Distributed Tracing & Verbose Network Meta ---
228
+ // --- 3. Distributed Tracing & Network Patching ---
229
229
  private shouldAttachTraceHeader(url: string): boolean {
230
230
  if (!this.config.allowedOrigins || this.config.allowedOrigins.length === 0)
231
231
  return false;
@@ -242,6 +242,12 @@ export class SenzorRumAgent {
242
242
  }
243
243
  }
244
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
+
245
251
  private patchNetwork() {
246
252
  const self = this;
247
253
 
@@ -322,8 +328,7 @@ export class SenzorRumAgent {
322
328
  }
323
329
  } catch (e) {}
324
330
 
325
- // Queue Span
326
- self.spanQueue.push({
331
+ self.pushSpan({
327
332
  spanId,
328
333
  name: `${method} ${new URL(fullUrl, window.location.origin).pathname}`,
329
334
  type: "http",
@@ -332,8 +337,6 @@ export class SenzorRumAgent {
332
337
  status: xhr.status,
333
338
  meta,
334
339
  });
335
-
336
- if (self.spanQueue.length >= self.MAX_BATCH_SIZE) self.flush();
337
340
  });
338
341
 
339
342
  return originalXhrSend.call(this, body);
@@ -408,7 +411,7 @@ export class SenzorRumAgent {
408
411
 
409
412
  if (errorMsg) meta.error = errorMsg;
410
413
 
411
- self.spanQueue.push({
414
+ self.pushSpan({
412
415
  spanId,
413
416
  name: `${method} ${new URL(fullUrl, window.location.origin).pathname}`,
414
417
  type: "http",
@@ -417,8 +420,6 @@ export class SenzorRumAgent {
417
420
  status,
418
421
  meta,
419
422
  });
420
-
421
- if (self.spanQueue.length >= self.MAX_BATCH_SIZE) self.flush();
422
423
  };
423
424
 
424
425
  try {
@@ -459,6 +460,11 @@ export class SenzorRumAgent {
459
460
  window.addEventListener("pagehide", () => this.flush());
460
461
  }
461
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
+
462
468
  private flush() {
463
469
  if (
464
470
  this.spanQueue.length === 0 &&
@@ -467,6 +473,7 @@ export class SenzorRumAgent {
467
473
  )
468
474
  return;
469
475
 
476
+ // Drain Queue up to batch limit to ensure payload fits in Beacon API limits
470
477
  const spansToSend = this.spanQueue.splice(0, this.MAX_BATCH_SIZE);
471
478
  const errorsToSend = this.errorQueue.splice(0, 20);
472
479
 
@@ -500,6 +507,7 @@ export class SenzorRumAgent {
500
507
  const authUrl = `${this.endpoint}${separator}apiKey=${this.config.apiKey}`;
501
508
 
502
509
  if (navigator.sendBeacon && blob.size < 60000) {
510
+ // Safety check against 64k beacon limit
503
511
  navigator.sendBeacon(authUrl, blob);
504
512
  } else {
505
513
  fetch(authUrl, {