@senzops/web 1.3.1 → 1.3.2
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 +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.global.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/analytics.ts +148 -0
- package/src/index.ts +7 -519
- package/src/rum.ts +443 -0
- package/src/utils.ts +40 -0
package/dist/index.d.mts
CHANGED
|
@@ -18,6 +18,7 @@ declare class SenzorAnalyticsAgent {
|
|
|
18
18
|
private fallbackSend;
|
|
19
19
|
private setupListeners;
|
|
20
20
|
}
|
|
21
|
+
|
|
21
22
|
interface RumConfig {
|
|
22
23
|
apiKey: string;
|
|
23
24
|
endpoint?: string;
|
|
@@ -53,6 +54,7 @@ declare class SenzorRumAgent {
|
|
|
53
54
|
private setupRoutingListeners;
|
|
54
55
|
private flush;
|
|
55
56
|
}
|
|
57
|
+
|
|
56
58
|
declare const Analytics: SenzorAnalyticsAgent;
|
|
57
59
|
declare const RUM: SenzorRumAgent;
|
|
58
60
|
declare const Senzor: {
|
|
@@ -60,4 +62,4 @@ declare const Senzor: {
|
|
|
60
62
|
initRum: (config: RumConfig) => void;
|
|
61
63
|
};
|
|
62
64
|
|
|
63
|
-
export { Analytics, RUM, Senzor };
|
|
65
|
+
export { Analytics, type AnalyticsConfig, RUM, type RumConfig, Senzor };
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ declare class SenzorAnalyticsAgent {
|
|
|
18
18
|
private fallbackSend;
|
|
19
19
|
private setupListeners;
|
|
20
20
|
}
|
|
21
|
+
|
|
21
22
|
interface RumConfig {
|
|
22
23
|
apiKey: string;
|
|
23
24
|
endpoint?: string;
|
|
@@ -53,6 +54,7 @@ declare class SenzorRumAgent {
|
|
|
53
54
|
private setupRoutingListeners;
|
|
54
55
|
private flush;
|
|
55
56
|
}
|
|
57
|
+
|
|
56
58
|
declare const Analytics: SenzorAnalyticsAgent;
|
|
57
59
|
declare const RUM: SenzorRumAgent;
|
|
58
60
|
declare const Senzor: {
|
|
@@ -60,4 +62,4 @@ declare const Senzor: {
|
|
|
60
62
|
initRum: (config: RumConfig) => void;
|
|
61
63
|
};
|
|
62
64
|
|
|
63
|
-
export { Analytics, RUM, Senzor };
|
|
65
|
+
export { Analytics, type AnalyticsConfig, RUM, type RumConfig, Senzor };
|
package/dist/index.global.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{function f(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let t=Math.random()*16|0;return(a==="x"?t:t&3|8).toString(16)})}function u(a){let t="";for(;t.length<a;)t+=Math.random().toString(16).slice(2);return t.slice(0,a)}var w=()=>{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}},m=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(t){return t?t.replace(/^https?:\/\//,""):""}manageSession(){let t=Date.now(),e=parseInt(localStorage.getItem("sz_wa_last")||"0",10);localStorage.getItem("sz_wa_vid")||localStorage.setItem("sz_wa_vid",f());let i=sessionStorage.getItem("sz_wa_sid");!i||t-e>1800*1e3?(i=f(),sessionStorage.setItem("sz_wa_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("sz_wa_last",t.toString())}determineReferrer(t){let e=document.referrer,i=!1;if(e)try{i=new URL(e).hostname!==window.location.hostname}catch{i=!0}if(i){let n=this.normalizeUrl(e);n!==sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref",n)}else t&&!sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref","Direct")}getIds(){return localStorage.setItem("sz_wa_last",Date.now().toString()),{visitorId:localStorage.getItem("sz_wa_vid")||"unknown",sessionId:sessionStorage.getItem("sz_wa_sid")||"unknown",referrer:sessionStorage.getItem("sz_wa_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 t=Math.floor((Date.now()-this.startTime)/1e3);t>=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:t})}send(t){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(t)],{type:"application/json"}))||this.fallbackSend(t):this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let t=history.pushState;history.pushState=(...e)=>{this.trackPing(),t.apply(history,e),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())}},g=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spans=[];errors=[];breadcrumbs=[];vitals={};frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.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.setupErrorListeners(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),1e4),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",f()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(t){this.traceId=u(32),this.traceStartTime=Date.now(),this.isInitialLoad=t,this.spans=[],this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(t,e){this.breadcrumbs.push({type:t,message:e,time:Date.now()}),this.breadcrumbs.length>15&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",t=>{let e=t.target,i=e.tagName?e.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${e.id?"#"+e.id:""}${e.className?"."+e.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||e.closest("button")||e.closest("a")||e.hasAttribute("role")||e.onclick||this.frustrations.deadClicks++;let r=Date.now();if(this.clickHistory.push({x:t.clientX,y:t.clientY,time:r}),this.clickHistory=this.clickHistory.filter(o=>r-o.time<1e3),this.clickHistory.length>=3){let o=this.clickHistory[0],c=!0;for(let d=1;d<this.clickHistory.length;d++){let l=Math.abs(this.clickHistory[d].x-o.x),p=Math.abs(this.clickHistory[d].y-o.y);(l>50||p>50)&&(c=!1)}c&&(this.frustrations.rageClicks++,this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(e=>{for(let i of e.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(e=>{let i=e.getEntries(),n=i[i.length-1];n&&(this.vitals.lcp=n.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let t=0;new PerformanceObserver(e=>{for(let i of e.getEntries())i.hadRecentInput||(t+=i.value,this.vitals.cls=t)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{for(let i of e.getEntries()){let n=i,s=n.duration||(n.processingStart&&n.startTime?n.processingStart-n.startTime:0);(!this.vitals.inp||s>this.vitals.inp)&&(this.vitals.inp=s)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let t=performance.getEntriesByType("navigation")[0];return t?{dns:Math.max(0,t.domainLookupEnd-t.domainLookupStart),tcp:Math.max(0,t.connectEnd-t.connectStart),ssl:t.secureConnectionStart?Math.max(0,t.requestStart-t.secureConnectionStart):0,ttfb:Math.max(0,t.responseStart-t.requestStart),domInteractive:Math.max(0,t.domInteractive-t.startTime),domComplete:Math.max(0,t.domComplete-t.startTime)}:{}}shouldAttachTraceHeader(t){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let e=new URL(t,window.location.origin);return this.config.allowedOrigins.some(i=>typeof i=="string"?e.origin.includes(i):i instanceof RegExp?i.test(e.origin):!1)}catch{return!1}}patchNetwork(){let t=this,e=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(s,r,...o){return this.__szMethod=s,this.__szUrl=r,e.apply(this,[s,r,...o])},XMLHttpRequest.prototype.send=function(s){let r=this,o=u(16),c=Date.now()-t.traceStartTime;return t.shouldAttachTraceHeader(r.__szUrl)&&r.setRequestHeader("traceparent",`00-${t.traceId}-${o}-01`),r.addEventListener("loadend",()=>{t.spans.push({spanId:o,name:new URL(r.__szUrl,window.location.origin).pathname,type:"xhr",method:r.__szMethod,status:r.status,startTime:c,duration:Date.now()-t.traceStartTime-c})}),i.call(this,s)};let n=window.fetch;window.fetch=async function(...s){var l,p;let r=typeof s[0]=="string"?s[0]:s[0].url,o=(((l=s[1])==null?void 0:l.method)||s[0].method||"GET").toUpperCase(),c=u(16),d=Date.now()-t.traceStartTime;if(t.shouldAttachTraceHeader(r)){let h=new Headers(((p=s[1])==null?void 0:p.headers)||s[0].headers||{});h.set("traceparent",`00-${t.traceId}-${c}-01`),s[1]?s[1].headers=h:s[0]instanceof Request&&(s[0]=new Request(s[0],{headers:h}))}try{let h=await n.apply(this,s);return t.spans.push({spanId:c,name:new URL(r,window.location.origin).pathname,type:"fetch",method:o,status:h.status,startTime:d,duration:Date.now()-t.traceStartTime-d}),h}catch(h){throw t.spans.push({spanId:c,name:new URL(r,window.location.origin).pathname,type:"fetch",method:o,status:0,startTime:d,duration:Date.now()-t.traceStartTime-d}),h}}}setupErrorListeners(){let t=(e,i)=>{this.frustrations.errorCount++;let n=e.message||String(e);this.errors.push({errorClass:e.name||"Error",message:n,stackTrace:e.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...w(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",e=>{e.error&&t(e.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",e=>{t(e.reason instanceof Error?e.reason:new Error(String(e.reason)),"Unhandled Promise Rejection")})}setupRoutingListeners(){let t=history.pushState;history.pushState=(...e)=>{this.flush(),t.apply(history,e),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.spans.length===0&&this.errors.length===0&&!this.isInitialLoad)return;let t={traces:[],errors:this.errors};if(this.isSampled&&t.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},...w(),spans:[...this.spans],duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.spans=[],this.errors=[],this.frustrations={rageClicks:0,deadClicks:0,errorCount:0},this.isInitialLoad=!1,t.traces.length>0||t.errors.length>0){let e=new Blob([JSON.stringify(t)],{type:"application/json"}),i=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${i}apiKey=${this.config.apiKey}`;navigator.sendBeacon?navigator.sendBeacon(n,e):fetch(n,{method:"POST",body:e,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}},v=new m,y=new g,S={init:a=>v.init(a),initRum:a=>y.init(a)};typeof window<"u"&&(window.Senzor=S);})();
|
|
1
|
+
(()=>{function m(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{let t=Math.random()*16|0;return(r==="x"?t:t&3|8).toString(16)})}function y(r){let t="";for(;t.length<r;)t+=Math.random().toString(16).slice(2);return t.slice(0,r)}var b=()=>{var r;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((r=navigator.connection)==null?void 0:r.effectiveType)||void 0}},w=r=>{if(!r)return 0;if(typeof r=="string")return r.length;if(r instanceof Blob||r instanceof File)return r.size;if(!(r instanceof FormData)&&r instanceof ArrayBuffer)return r.byteLength};var v=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(t){return t?t.replace(/^https?:\/\//,""):""}manageSession(){let t=Date.now(),e=parseInt(localStorage.getItem("sz_wa_last")||"0",10);localStorage.getItem("sz_wa_vid")||localStorage.setItem("sz_wa_vid",m());let i=sessionStorage.getItem("sz_wa_sid");!i||t-e>1800*1e3?(i=m(),sessionStorage.setItem("sz_wa_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("sz_wa_last",t.toString())}determineReferrer(t){let e=document.referrer,i=!1;if(e)try{i=new URL(e).hostname!==window.location.hostname}catch{i=!0}if(i){let a=this.normalizeUrl(e);a!==sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref",a)}else t&&!sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref","Direct")}getIds(){return localStorage.setItem("sz_wa_last",Date.now().toString()),{visitorId:localStorage.getItem("sz_wa_vid")||"unknown",sessionId:sessionStorage.getItem("sz_wa_sid")||"unknown",referrer:sessionStorage.getItem("sz_wa_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 t=Math.floor((Date.now()-this.startTime)/1e3);t>=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:t})}send(t){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(t)],{type:"application/json"}))||this.fallbackSend(t):this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let t=history.pushState;history.pushState=(...e)=>{this.trackPing(),t.apply(history,e),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 S=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spans=[];errors=[];breadcrumbs=[];vitals={};frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.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.setupErrorListeners(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),1e4),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",m()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(t){this.traceId=y(32),this.traceStartTime=Date.now(),this.isInitialLoad=t,this.spans=[],this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(t,e,i){this.breadcrumbs.push({type:t,message:e,data:i,time:Date.now()}),this.breadcrumbs.length>15&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",t=>{let e=t.target,i=e.tagName?e.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${e.id?"#"+e.id:""}${e.className?"."+e.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||e.closest("button")||e.closest("a")||e.hasAttribute("role")||e.onclick||this.frustrations.deadClicks++;let c=Date.now();if(this.clickHistory.push({x:t.clientX,y:t.clientY,time:c}),this.clickHistory=this.clickHistory.filter(s=>c-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],n=!0;for(let l=1;l<this.clickHistory.length;l++){let h=Math.abs(this.clickHistory[l].x-s.x),d=Math.abs(this.clickHistory[l].y-s.y);(h>50||d>50)&&(n=!1)}n&&(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(e=>{for(let i of e.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(e=>{let i=e.getEntries(),a=i[i.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let t=0;new PerformanceObserver(e=>{for(let i of e.getEntries())i.hadRecentInput||(t+=i.value,this.vitals.cls=t)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{for(let i of e.getEntries()){let a=i,g=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||g>this.vitals.inp)&&(this.vitals.inp=g)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let t=performance.getEntriesByType("navigation")[0];return t?{dns:Math.max(0,t.domainLookupEnd-t.domainLookupStart),tcp:Math.max(0,t.connectEnd-t.connectStart),ssl:t.secureConnectionStart?Math.max(0,t.requestStart-t.secureConnectionStart):0,ttfb:Math.max(0,t.responseStart-t.requestStart),domInteractive:Math.max(0,t.domInteractive-t.startTime),domComplete:Math.max(0,t.domComplete-t.startTime)}:{}}shouldAttachTraceHeader(t){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let e=new URL(t,window.location.origin);return this.config.allowedOrigins.some(i=>typeof i=="string"?e.origin.includes(i):i instanceof RegExp?i.test(e.origin):!1)}catch{return!1}}patchNetwork(){let t=this,e=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(c,s,...n){return this.__szMethod=c.toUpperCase(),this.__szUrl=s,this.__szHeaders={},e.apply(this,[c,s,...n])},XMLHttpRequest.prototype.setRequestHeader=function(c,s){return this.__szHeaders[c]=s,a.call(this,c,s)},XMLHttpRequest.prototype.send=function(c){let s=this,n=y(16),l=Date.now()-t.traceStartTime,h=s.__szMethod,d=s.__szUrl;try{d=new URL(s.__szUrl,window.location.origin).toString()}catch{}return t.shouldAttachTraceHeader(d)&&s.setRequestHeader("traceparent",`00-${t.traceId}-${n}-01`),s.addEventListener("loadend",()=>{let f=Date.now()-t.traceStartTime-l,u={url:d,method:h,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:w(c)};try{let o=s.responseText?s.responseText.length:void 0;o&&(u.responsePayloadSize=o)}catch{}t.spans.push({spanId:n,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:l,duration:f,status:s.status,meta:u})}),i.call(this,c)};let g=window.fetch;window.fetch=async function(...c){let s=c[0],n=c[1],l="",h="GET";typeof s=="string"||s instanceof URL?(l=s.toString(),h=((n==null?void 0:n.method)||"GET").toUpperCase()):s instanceof Request&&(l=s.url,h=s.method.toUpperCase());let d=l;try{d=new URL(l,window.location.origin).toString()}catch{}let f=y(16),u=Date.now()-t.traceStartTime;if(t.shouldAttachTraceHeader(d)){let o=`00-${t.traceId}-${f}-01`;if(s instanceof Request){let p=new Headers(s.headers);p.set("traceparent",o),c[1]={...n||{},headers:p}}else{let p=new Headers((n==null?void 0:n.headers)||{});p.set("traceparent",o),c[1]={...n||{},headers:p}}}try{let o=await g.apply(this,c),p=Date.now()-t.traceStartTime-u;return t.spans.push({spanId:f,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:u,duration:p,status:o.status,meta:{url:d,method:h,library:"fetch",status:o.status,statusText:o.statusText,type:o.type,redirected:o.redirected,requestPayloadSize:w(n==null?void 0:n.body)}}),o}catch(o){let p=Date.now()-t.traceStartTime-u;throw t.spans.push({spanId:f,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:u,duration:p,status:0,meta:{url:d,method:h,library:"fetch",status:0,error:o instanceof Error?o.message:String(o),requestPayloadSize:w(n==null?void 0:n.body)}}),o}}}setupErrorListeners(){let t=(e,i)=>{this.frustrations.errorCount++;let a=e.message||String(e);this.errors.push({errorClass:e.name||"Error",message:a,stackTrace:e.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...b(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",e=>{e.error&&t(e.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",e=>{t(e.reason instanceof Error?e.reason:new Error(String(e.reason)),"Unhandled Promise Rejection")})}setupRoutingListeners(){let t=history.pushState;history.pushState=(...e)=>{this.flush(),t.apply(history,e),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.spans.length===0&&this.errors.length===0&&!this.isInitialLoad)return;let t={traces:[],errors:this.errors};if(this.isSampled&&t.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},...b(),spans:[...this.spans],duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.spans=[],this.errors=[],this.frustrations={rageClicks:0,deadClicks:0,errorCount:0},this.isInitialLoad=!1,t.traces.length>0||t.errors.length>0){let e=new Blob([JSON.stringify(t)],{type:"application/json"}),i=this.endpoint.includes("?")?"&":"?",a=`${this.endpoint}${i}apiKey=${this.config.apiKey}`;navigator.sendBeacon?navigator.sendBeacon(a,e):fetch(a,{method:"POST",body:e,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var x=new v,I=new S,T={init:r=>x.init(r),initRum:r=>I.init(r)};typeof window<"u"&&(window.Senzor=T);})();
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var u=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var k=(r,t)=>{for(var e in t)u(r,e,{get:t[e],enumerable:!0})},T=(r,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of x(t))!_.call(r,s)&&s!==e&&u(r,s,{get:()=>t[s],enumerable:!(i=I(t,s))||i.enumerable});return r};var L=r=>T(u({},"__esModule",{value:!0}),r);var R={};k(R,{Analytics:()=>y,RUM:()=>S,Senzor:()=>b});module.exports=L(R);function m(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{let t=Math.random()*16|0;return(r==="x"?t:t&3|8).toString(16)})}function f(r){let t="";for(;t.length<r;)t+=Math.random().toString(16).slice(2);return t.slice(0,r)}var v=()=>{var r;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((r=navigator.connection)==null?void 0:r.effectiveType)||void 0}},g=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(t){return t?t.replace(/^https?:\/\//,""):""}manageSession(){let t=Date.now(),e=parseInt(localStorage.getItem("sz_wa_last")||"0",10);localStorage.getItem("sz_wa_vid")||localStorage.setItem("sz_wa_vid",m());let i=sessionStorage.getItem("sz_wa_sid");!i||t-e>1800*1e3?(i=m(),sessionStorage.setItem("sz_wa_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("sz_wa_last",t.toString())}determineReferrer(t){let e=document.referrer,i=!1;if(e)try{i=new URL(e).hostname!==window.location.hostname}catch{i=!0}if(i){let s=this.normalizeUrl(e);s!==sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref",s)}else t&&!sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref","Direct")}getIds(){return localStorage.setItem("sz_wa_last",Date.now().toString()),{visitorId:localStorage.getItem("sz_wa_vid")||"unknown",sessionId:sessionStorage.getItem("sz_wa_sid")||"unknown",referrer:sessionStorage.getItem("sz_wa_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 t=Math.floor((Date.now()-this.startTime)/1e3);t>=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:t})}send(t){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(t)],{type:"application/json"}))||this.fallbackSend(t):this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let t=history.pushState;history.pushState=(...e)=>{this.trackPing(),t.apply(history,e),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())}},w=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spans=[];errors=[];breadcrumbs=[];vitals={};frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.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.setupErrorListeners(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),1e4),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",m()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(t){this.traceId=f(32),this.traceStartTime=Date.now(),this.isInitialLoad=t,this.spans=[],this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(t,e){this.breadcrumbs.push({type:t,message:e,time:Date.now()}),this.breadcrumbs.length>15&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",t=>{let e=t.target,i=e.tagName?e.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${e.id?"#"+e.id:""}${e.className?"."+e.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||e.closest("button")||e.closest("a")||e.hasAttribute("role")||e.onclick||this.frustrations.deadClicks++;let a=Date.now();if(this.clickHistory.push({x:t.clientX,y:t.clientY,time:a}),this.clickHistory=this.clickHistory.filter(o=>a-o.time<1e3),this.clickHistory.length>=3){let o=this.clickHistory[0],c=!0;for(let d=1;d<this.clickHistory.length;d++){let l=Math.abs(this.clickHistory[d].x-o.x),p=Math.abs(this.clickHistory[d].y-o.y);(l>50||p>50)&&(c=!1)}c&&(this.frustrations.rageClicks++,this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(e=>{for(let i of e.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(e=>{let i=e.getEntries(),s=i[i.length-1];s&&(this.vitals.lcp=s.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let t=0;new PerformanceObserver(e=>{for(let i of e.getEntries())i.hadRecentInput||(t+=i.value,this.vitals.cls=t)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{for(let i of e.getEntries()){let s=i,n=s.duration||(s.processingStart&&s.startTime?s.processingStart-s.startTime:0);(!this.vitals.inp||n>this.vitals.inp)&&(this.vitals.inp=n)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let t=performance.getEntriesByType("navigation")[0];return t?{dns:Math.max(0,t.domainLookupEnd-t.domainLookupStart),tcp:Math.max(0,t.connectEnd-t.connectStart),ssl:t.secureConnectionStart?Math.max(0,t.requestStart-t.secureConnectionStart):0,ttfb:Math.max(0,t.responseStart-t.requestStart),domInteractive:Math.max(0,t.domInteractive-t.startTime),domComplete:Math.max(0,t.domComplete-t.startTime)}:{}}shouldAttachTraceHeader(t){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let e=new URL(t,window.location.origin);return this.config.allowedOrigins.some(i=>typeof i=="string"?e.origin.includes(i):i instanceof RegExp?i.test(e.origin):!1)}catch{return!1}}patchNetwork(){let t=this,e=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(n,a,...o){return this.__szMethod=n,this.__szUrl=a,e.apply(this,[n,a,...o])},XMLHttpRequest.prototype.send=function(n){let a=this,o=f(16),c=Date.now()-t.traceStartTime;return t.shouldAttachTraceHeader(a.__szUrl)&&a.setRequestHeader("traceparent",`00-${t.traceId}-${o}-01`),a.addEventListener("loadend",()=>{t.spans.push({spanId:o,name:new URL(a.__szUrl,window.location.origin).pathname,type:"xhr",method:a.__szMethod,status:a.status,startTime:c,duration:Date.now()-t.traceStartTime-c})}),i.call(this,n)};let s=window.fetch;window.fetch=async function(...n){var l,p;let a=typeof n[0]=="string"?n[0]:n[0].url,o=(((l=n[1])==null?void 0:l.method)||n[0].method||"GET").toUpperCase(),c=f(16),d=Date.now()-t.traceStartTime;if(t.shouldAttachTraceHeader(a)){let h=new Headers(((p=n[1])==null?void 0:p.headers)||n[0].headers||{});h.set("traceparent",`00-${t.traceId}-${c}-01`),n[1]?n[1].headers=h:n[0]instanceof Request&&(n[0]=new Request(n[0],{headers:h}))}try{let h=await s.apply(this,n);return t.spans.push({spanId:c,name:new URL(a,window.location.origin).pathname,type:"fetch",method:o,status:h.status,startTime:d,duration:Date.now()-t.traceStartTime-d}),h}catch(h){throw t.spans.push({spanId:c,name:new URL(a,window.location.origin).pathname,type:"fetch",method:o,status:0,startTime:d,duration:Date.now()-t.traceStartTime-d}),h}}}setupErrorListeners(){let t=(e,i)=>{this.frustrations.errorCount++;let s=e.message||String(e);this.errors.push({errorClass:e.name||"Error",message:s,stackTrace:e.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...v(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",e=>{e.error&&t(e.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",e=>{t(e.reason instanceof Error?e.reason:new Error(String(e.reason)),"Unhandled Promise Rejection")})}setupRoutingListeners(){let t=history.pushState;history.pushState=(...e)=>{this.flush(),t.apply(history,e),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.spans.length===0&&this.errors.length===0&&!this.isInitialLoad)return;let t={traces:[],errors:this.errors};if(this.isSampled&&t.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},...v(),spans:[...this.spans],duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.spans=[],this.errors=[],this.frustrations={rageClicks:0,deadClicks:0,errorCount:0},this.isInitialLoad=!1,t.traces.length>0||t.errors.length>0){let e=new Blob([JSON.stringify(t)],{type:"application/json"}),i=this.endpoint.includes("?")?"&":"?",s=`${this.endpoint}${i}apiKey=${this.config.apiKey}`;navigator.sendBeacon?navigator.sendBeacon(s,e):fetch(s,{method:"POST",body:e,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}},y=new g,S=new w,b={init:r=>y.init(r),initRum:r=>S.init(r)};typeof window<"u"&&(window.Senzor=b);0&&(module.exports={Analytics,RUM,Senzor});
|
|
1
|
+
var b=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var L=(s,t)=>{for(var e in t)b(s,e,{get:t[e],enumerable:!0})},C=(s,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of k(t))!R.call(s,n)&&n!==e&&b(s,n,{get:()=>t[n],enumerable:!(i=z(t,n))||i.enumerable});return s};var U=s=>C(b({},"__esModule",{value:!0}),s);var E={};L(E,{Analytics:()=>I,RUM:()=>T,Senzor:()=>_});module.exports=U(E);function m(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let t=Math.random()*16|0;return(s==="x"?t:t&3|8).toString(16)})}function y(s){let t="";for(;t.length<s;)t+=Math.random().toString(16).slice(2);return t.slice(0,s)}var x=()=>{var s;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((s=navigator.connection)==null?void 0:s.effectiveType)||void 0}},w=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 FormData)&&s instanceof ArrayBuffer)return s.byteLength};var v=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(t){return t?t.replace(/^https?:\/\//,""):""}manageSession(){let t=Date.now(),e=parseInt(localStorage.getItem("sz_wa_last")||"0",10);localStorage.getItem("sz_wa_vid")||localStorage.setItem("sz_wa_vid",m());let i=sessionStorage.getItem("sz_wa_sid");!i||t-e>1800*1e3?(i=m(),sessionStorage.setItem("sz_wa_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("sz_wa_last",t.toString())}determineReferrer(t){let e=document.referrer,i=!1;if(e)try{i=new URL(e).hostname!==window.location.hostname}catch{i=!0}if(i){let n=this.normalizeUrl(e);n!==sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref",n)}else t&&!sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref","Direct")}getIds(){return localStorage.setItem("sz_wa_last",Date.now().toString()),{visitorId:localStorage.getItem("sz_wa_vid")||"unknown",sessionId:sessionStorage.getItem("sz_wa_sid")||"unknown",referrer:sessionStorage.getItem("sz_wa_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 t=Math.floor((Date.now()-this.startTime)/1e3);t>=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:t})}send(t){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(t)],{type:"application/json"}))||this.fallbackSend(t):this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let t=history.pushState;history.pushState=(...e)=>{this.trackPing(),t.apply(history,e),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 S=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spans=[];errors=[];breadcrumbs=[];vitals={};frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.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.setupErrorListeners(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),1e4),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",m()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(t){this.traceId=y(32),this.traceStartTime=Date.now(),this.isInitialLoad=t,this.spans=[],this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(t,e,i){this.breadcrumbs.push({type:t,message:e,data:i,time:Date.now()}),this.breadcrumbs.length>15&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",t=>{let e=t.target,i=e.tagName?e.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${e.id?"#"+e.id:""}${e.className?"."+e.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||e.closest("button")||e.closest("a")||e.hasAttribute("role")||e.onclick||this.frustrations.deadClicks++;let c=Date.now();if(this.clickHistory.push({x:t.clientX,y:t.clientY,time:c}),this.clickHistory=this.clickHistory.filter(r=>c-r.time<1e3),this.clickHistory.length>=3){let r=this.clickHistory[0],a=!0;for(let l=1;l<this.clickHistory.length;l++){let h=Math.abs(this.clickHistory[l].x-r.x),d=Math.abs(this.clickHistory[l].y-r.y);(h>50||d>50)&&(a=!1)}a&&(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(e=>{for(let i of e.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(e=>{let i=e.getEntries(),n=i[i.length-1];n&&(this.vitals.lcp=n.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let t=0;new PerformanceObserver(e=>{for(let i of e.getEntries())i.hadRecentInput||(t+=i.value,this.vitals.cls=t)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{for(let i of e.getEntries()){let n=i,g=n.duration||(n.processingStart&&n.startTime?n.processingStart-n.startTime:0);(!this.vitals.inp||g>this.vitals.inp)&&(this.vitals.inp=g)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let t=performance.getEntriesByType("navigation")[0];return t?{dns:Math.max(0,t.domainLookupEnd-t.domainLookupStart),tcp:Math.max(0,t.connectEnd-t.connectStart),ssl:t.secureConnectionStart?Math.max(0,t.requestStart-t.secureConnectionStart):0,ttfb:Math.max(0,t.responseStart-t.requestStart),domInteractive:Math.max(0,t.domInteractive-t.startTime),domComplete:Math.max(0,t.domComplete-t.startTime)}:{}}shouldAttachTraceHeader(t){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let e=new URL(t,window.location.origin);return this.config.allowedOrigins.some(i=>typeof i=="string"?e.origin.includes(i):i instanceof RegExp?i.test(e.origin):!1)}catch{return!1}}patchNetwork(){let t=this,e=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send,n=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(c,r,...a){return this.__szMethod=c.toUpperCase(),this.__szUrl=r,this.__szHeaders={},e.apply(this,[c,r,...a])},XMLHttpRequest.prototype.setRequestHeader=function(c,r){return this.__szHeaders[c]=r,n.call(this,c,r)},XMLHttpRequest.prototype.send=function(c){let r=this,a=y(16),l=Date.now()-t.traceStartTime,h=r.__szMethod,d=r.__szUrl;try{d=new URL(r.__szUrl,window.location.origin).toString()}catch{}return t.shouldAttachTraceHeader(d)&&r.setRequestHeader("traceparent",`00-${t.traceId}-${a}-01`),r.addEventListener("loadend",()=>{let f=Date.now()-t.traceStartTime-l,u={url:d,method:h,library:"xhr",status:r.status,responseType:r.responseType,requestPayloadSize:w(c)};try{let o=r.responseText?r.responseText.length:void 0;o&&(u.responsePayloadSize=o)}catch{}t.spans.push({spanId:a,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:l,duration:f,status:r.status,meta:u})}),i.call(this,c)};let g=window.fetch;window.fetch=async function(...c){let r=c[0],a=c[1],l="",h="GET";typeof r=="string"||r instanceof URL?(l=r.toString(),h=((a==null?void 0:a.method)||"GET").toUpperCase()):r instanceof Request&&(l=r.url,h=r.method.toUpperCase());let d=l;try{d=new URL(l,window.location.origin).toString()}catch{}let f=y(16),u=Date.now()-t.traceStartTime;if(t.shouldAttachTraceHeader(d)){let o=`00-${t.traceId}-${f}-01`;if(r instanceof Request){let p=new Headers(r.headers);p.set("traceparent",o),c[1]={...a||{},headers:p}}else{let p=new Headers((a==null?void 0:a.headers)||{});p.set("traceparent",o),c[1]={...a||{},headers:p}}}try{let o=await g.apply(this,c),p=Date.now()-t.traceStartTime-u;return t.spans.push({spanId:f,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:u,duration:p,status:o.status,meta:{url:d,method:h,library:"fetch",status:o.status,statusText:o.statusText,type:o.type,redirected:o.redirected,requestPayloadSize:w(a==null?void 0:a.body)}}),o}catch(o){let p=Date.now()-t.traceStartTime-u;throw t.spans.push({spanId:f,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:u,duration:p,status:0,meta:{url:d,method:h,library:"fetch",status:0,error:o instanceof Error?o.message:String(o),requestPayloadSize:w(a==null?void 0:a.body)}}),o}}}setupErrorListeners(){let t=(e,i)=>{this.frustrations.errorCount++;let n=e.message||String(e);this.errors.push({errorClass:e.name||"Error",message:n,stackTrace:e.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...x(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",e=>{e.error&&t(e.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",e=>{t(e.reason instanceof Error?e.reason:new Error(String(e.reason)),"Unhandled Promise Rejection")})}setupRoutingListeners(){let t=history.pushState;history.pushState=(...e)=>{this.flush(),t.apply(history,e),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.spans.length===0&&this.errors.length===0&&!this.isInitialLoad)return;let t={traces:[],errors:this.errors};if(this.isSampled&&t.traces.push({traceId:this.traceId,sessionId:this.sessionId,traceType:this.isInitialLoad?"initial_load":"route_change",path:window.location.pathname,referrer:document.referrer||"",vitals:{...this.vitals},timings:this.isInitialLoad?this.getNavigationTimings():{},frustration:{...this.frustrations},...x(),spans:[...this.spans],duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.spans=[],this.errors=[],this.frustrations={rageClicks:0,deadClicks:0,errorCount:0},this.isInitialLoad=!1,t.traces.length>0||t.errors.length>0){let e=new Blob([JSON.stringify(t)],{type:"application/json"}),i=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${i}apiKey=${this.config.apiKey}`;navigator.sendBeacon?navigator.sendBeacon(n,e):fetch(n,{method:"POST",body:e,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var I=new v,T=new S,_={init:s=>I.init(s),initRum:s=>T.init(s)};typeof window<"u"&&(window.Senzor=_);0&&(module.exports={Analytics,RUM,Senzor});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function f(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let t=Math.random()*16|0;return(a==="x"?t:t&3|8).toString(16)})}function u(a){let t="";for(;t.length<a;)t+=Math.random().toString(16).slice(2);return t.slice(0,a)}var w=()=>{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}},m=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(t){return t?t.replace(/^https?:\/\//,""):""}manageSession(){let t=Date.now(),e=parseInt(localStorage.getItem("sz_wa_last")||"0",10);localStorage.getItem("sz_wa_vid")||localStorage.setItem("sz_wa_vid",f());let i=sessionStorage.getItem("sz_wa_sid");!i||t-e>1800*1e3?(i=f(),sessionStorage.setItem("sz_wa_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("sz_wa_last",t.toString())}determineReferrer(t){let e=document.referrer,i=!1;if(e)try{i=new URL(e).hostname!==window.location.hostname}catch{i=!0}if(i){let n=this.normalizeUrl(e);n!==sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref",n)}else t&&!sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref","Direct")}getIds(){return localStorage.setItem("sz_wa_last",Date.now().toString()),{visitorId:localStorage.getItem("sz_wa_vid")||"unknown",sessionId:sessionStorage.getItem("sz_wa_sid")||"unknown",referrer:sessionStorage.getItem("sz_wa_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 t=Math.floor((Date.now()-this.startTime)/1e3);t>=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:t})}send(t){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(t)],{type:"application/json"}))||this.fallbackSend(t):this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let t=history.pushState;history.pushState=(...e)=>{this.trackPing(),t.apply(history,e),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())}},g=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spans=[];errors=[];breadcrumbs=[];vitals={};frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.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.setupErrorListeners(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),1e4),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",f()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(t){this.traceId=u(32),this.traceStartTime=Date.now(),this.isInitialLoad=t,this.spans=[],this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(t,e){this.breadcrumbs.push({type:t,message:e,time:Date.now()}),this.breadcrumbs.length>15&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",t=>{let e=t.target,i=e.tagName?e.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${e.id?"#"+e.id:""}${e.className?"."+e.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||e.closest("button")||e.closest("a")||e.hasAttribute("role")||e.onclick||this.frustrations.deadClicks++;let r=Date.now();if(this.clickHistory.push({x:t.clientX,y:t.clientY,time:r}),this.clickHistory=this.clickHistory.filter(o=>r-o.time<1e3),this.clickHistory.length>=3){let o=this.clickHistory[0],c=!0;for(let d=1;d<this.clickHistory.length;d++){let l=Math.abs(this.clickHistory[d].x-o.x),p=Math.abs(this.clickHistory[d].y-o.y);(l>50||p>50)&&(c=!1)}c&&(this.frustrations.rageClicks++,this.clickHistory=[])}},{capture:!0,passive:!0})}setupPerformanceObservers(){if(!(!this.isSampled||typeof PerformanceObserver>"u"))try{new PerformanceObserver(e=>{for(let i of e.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(e=>{let i=e.getEntries(),n=i[i.length-1];n&&(this.vitals.lcp=n.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let t=0;new PerformanceObserver(e=>{for(let i of e.getEntries())i.hadRecentInput||(t+=i.value,this.vitals.cls=t)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{for(let i of e.getEntries()){let n=i,s=n.duration||(n.processingStart&&n.startTime?n.processingStart-n.startTime:0);(!this.vitals.inp||s>this.vitals.inp)&&(this.vitals.inp=s)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let t=performance.getEntriesByType("navigation")[0];return t?{dns:Math.max(0,t.domainLookupEnd-t.domainLookupStart),tcp:Math.max(0,t.connectEnd-t.connectStart),ssl:t.secureConnectionStart?Math.max(0,t.requestStart-t.secureConnectionStart):0,ttfb:Math.max(0,t.responseStart-t.requestStart),domInteractive:Math.max(0,t.domInteractive-t.startTime),domComplete:Math.max(0,t.domComplete-t.startTime)}:{}}shouldAttachTraceHeader(t){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let e=new URL(t,window.location.origin);return this.config.allowedOrigins.some(i=>typeof i=="string"?e.origin.includes(i):i instanceof RegExp?i.test(e.origin):!1)}catch{return!1}}patchNetwork(){let t=this,e=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(s,r,...o){return this.__szMethod=s,this.__szUrl=r,e.apply(this,[s,r,...o])},XMLHttpRequest.prototype.send=function(s){let r=this,o=u(16),c=Date.now()-t.traceStartTime;return t.shouldAttachTraceHeader(r.__szUrl)&&r.setRequestHeader("traceparent",`00-${t.traceId}-${o}-01`),r.addEventListener("loadend",()=>{t.spans.push({spanId:o,name:new URL(r.__szUrl,window.location.origin).pathname,type:"xhr",method:r.__szMethod,status:r.status,startTime:c,duration:Date.now()-t.traceStartTime-c})}),i.call(this,s)};let n=window.fetch;window.fetch=async function(...s){var l,p;let r=typeof s[0]=="string"?s[0]:s[0].url,o=(((l=s[1])==null?void 0:l.method)||s[0].method||"GET").toUpperCase(),c=u(16),d=Date.now()-t.traceStartTime;if(t.shouldAttachTraceHeader(r)){let h=new Headers(((p=s[1])==null?void 0:p.headers)||s[0].headers||{});h.set("traceparent",`00-${t.traceId}-${c}-01`),s[1]?s[1].headers=h:s[0]instanceof Request&&(s[0]=new Request(s[0],{headers:h}))}try{let h=await n.apply(this,s);return t.spans.push({spanId:c,name:new URL(r,window.location.origin).pathname,type:"fetch",method:o,status:h.status,startTime:d,duration:Date.now()-t.traceStartTime-d}),h}catch(h){throw t.spans.push({spanId:c,name:new URL(r,window.location.origin).pathname,type:"fetch",method:o,status:0,startTime:d,duration:Date.now()-t.traceStartTime-d}),h}}}setupErrorListeners(){let t=(e,i)=>{this.frustrations.errorCount++;let n=e.message||String(e);this.errors.push({errorClass:e.name||"Error",message:n,stackTrace:e.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...w(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",e=>{e.error&&t(e.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",e=>{t(e.reason instanceof Error?e.reason:new Error(String(e.reason)),"Unhandled Promise Rejection")})}setupRoutingListeners(){let t=history.pushState;history.pushState=(...e)=>{this.flush(),t.apply(history,e),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.spans.length===0&&this.errors.length===0&&!this.isInitialLoad)return;let t={traces:[],errors:this.errors};if(this.isSampled&&t.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},...w(),spans:[...this.spans],duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.spans=[],this.errors=[],this.frustrations={rageClicks:0,deadClicks:0,errorCount:0},this.isInitialLoad=!1,t.traces.length>0||t.errors.length>0){let e=new Blob([JSON.stringify(t)],{type:"application/json"}),i=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${i}apiKey=${this.config.apiKey}`;navigator.sendBeacon?navigator.sendBeacon(n,e):fetch(n,{method:"POST",body:e,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}},v=new m,y=new g,S={init:a=>v.init(a),initRum:a=>y.init(a)};typeof window<"u"&&(window.Senzor=S);export{v as Analytics,y as RUM,S as Senzor};
|
|
1
|
+
function m(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{let t=Math.random()*16|0;return(r==="x"?t:t&3|8).toString(16)})}function y(r){let t="";for(;t.length<r;)t+=Math.random().toString(16).slice(2);return t.slice(0,r)}var b=()=>{var r;return{userAgent:navigator.userAgent,url:window.location.href,deviceMemory:navigator.deviceMemory||void 0,connectionType:((r=navigator.connection)==null?void 0:r.effectiveType)||void 0}},w=r=>{if(!r)return 0;if(typeof r=="string")return r.length;if(r instanceof Blob||r instanceof File)return r.size;if(!(r instanceof FormData)&&r instanceof ArrayBuffer)return r.byteLength};var v=class{config={webId:""};startTime=Date.now();endpoint="https://api.senzor.dev/api/ingest/web";initialized=!1;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.endpoint),!this.config.webId){console.error("[Senzor] webId is required for Analytics.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}}normalizeUrl(t){return t?t.replace(/^https?:\/\//,""):""}manageSession(){let t=Date.now(),e=parseInt(localStorage.getItem("sz_wa_last")||"0",10);localStorage.getItem("sz_wa_vid")||localStorage.setItem("sz_wa_vid",m());let i=sessionStorage.getItem("sz_wa_sid");!i||t-e>1800*1e3?(i=m(),sessionStorage.setItem("sz_wa_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("sz_wa_last",t.toString())}determineReferrer(t){let e=document.referrer,i=!1;if(e)try{i=new URL(e).hostname!==window.location.hostname}catch{i=!0}if(i){let a=this.normalizeUrl(e);a!==sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref",a)}else t&&!sessionStorage.getItem("sz_wa_ref")&&sessionStorage.setItem("sz_wa_ref","Direct")}getIds(){return localStorage.setItem("sz_wa_last",Date.now().toString()),{visitorId:localStorage.getItem("sz_wa_vid")||"unknown",sessionId:sessionStorage.getItem("sz_wa_sid")||"unknown",referrer:sessionStorage.getItem("sz_wa_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 t=Math.floor((Date.now()-this.startTime)/1e3);t>=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:t})}send(t){navigator.sendBeacon?navigator.sendBeacon(this.endpoint,new Blob([JSON.stringify(t)],{type:"application/json"}))||this.fallbackSend(t):this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(()=>{})}setupListeners(){let t=history.pushState;history.pushState=(...e)=>{this.trackPing(),t.apply(history,e),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 S=class{config={apiKey:"",sampleRate:1,allowedOrigins:[]};endpoint="https://api.senzor.dev/api/ingest/rum";initialized=!1;isSampled=!0;sessionId="";traceId="";traceStartTime=0;isInitialLoad=!0;spans=[];errors=[];breadcrumbs=[];vitals={};frustrations={rageClicks:0,deadClicks:0,errorCount:0};clickHistory=[];flushInterval;init(t){if(!this.initialized){if(this.initialized=!0,this.config={...this.config,...t},t.endpoint&&(this.endpoint=t.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.setupErrorListeners(),this.setupPerformanceObservers(),this.setupUXListeners(),this.isSampled&&this.patchNetwork(),this.flushInterval=setInterval(()=>this.flush(),1e4),this.setupRoutingListeners()}}manageSession(){sessionStorage.getItem("sz_rum_sid")||sessionStorage.setItem("sz_rum_sid",m()),this.sessionId=sessionStorage.getItem("sz_rum_sid")}startNewTrace(t){this.traceId=y(32),this.traceStartTime=Date.now(),this.isInitialLoad=t,this.spans=[],this.vitals={},this.frustrations={rageClicks:0,deadClicks:0,errorCount:0}}addBreadcrumb(t,e,i){this.breadcrumbs.push({type:t,message:e,data:i,time:Date.now()}),this.breadcrumbs.length>15&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",t=>{let e=t.target,i=e.tagName?e.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${e.id?"#"+e.id:""}${e.className?"."+e.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||e.closest("button")||e.closest("a")||e.hasAttribute("role")||e.onclick||this.frustrations.deadClicks++;let c=Date.now();if(this.clickHistory.push({x:t.clientX,y:t.clientY,time:c}),this.clickHistory=this.clickHistory.filter(s=>c-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],n=!0;for(let l=1;l<this.clickHistory.length;l++){let h=Math.abs(this.clickHistory[l].x-s.x),d=Math.abs(this.clickHistory[l].y-s.y);(h>50||d>50)&&(n=!1)}n&&(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(e=>{for(let i of e.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(e=>{let i=e.getEntries(),a=i[i.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let t=0;new PerformanceObserver(e=>{for(let i of e.getEntries())i.hadRecentInput||(t+=i.value,this.vitals.cls=t)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{for(let i of e.getEntries()){let a=i,g=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||g>this.vitals.inp)&&(this.vitals.inp=g)}}).observe({type:"event",buffered:!0,durationThreshold:40})}catch{}}getNavigationTimings(){if(typeof performance>"u")return{};let t=performance.getEntriesByType("navigation")[0];return t?{dns:Math.max(0,t.domainLookupEnd-t.domainLookupStart),tcp:Math.max(0,t.connectEnd-t.connectStart),ssl:t.secureConnectionStart?Math.max(0,t.requestStart-t.secureConnectionStart):0,ttfb:Math.max(0,t.responseStart-t.requestStart),domInteractive:Math.max(0,t.domInteractive-t.startTime),domComplete:Math.max(0,t.domComplete-t.startTime)}:{}}shouldAttachTraceHeader(t){if(!this.config.allowedOrigins||this.config.allowedOrigins.length===0)return!1;try{let e=new URL(t,window.location.origin);return this.config.allowedOrigins.some(i=>typeof i=="string"?e.origin.includes(i):i instanceof RegExp?i.test(e.origin):!1)}catch{return!1}}patchNetwork(){let t=this,e=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(c,s,...n){return this.__szMethod=c.toUpperCase(),this.__szUrl=s,this.__szHeaders={},e.apply(this,[c,s,...n])},XMLHttpRequest.prototype.setRequestHeader=function(c,s){return this.__szHeaders[c]=s,a.call(this,c,s)},XMLHttpRequest.prototype.send=function(c){let s=this,n=y(16),l=Date.now()-t.traceStartTime,h=s.__szMethod,d=s.__szUrl;try{d=new URL(s.__szUrl,window.location.origin).toString()}catch{}return t.shouldAttachTraceHeader(d)&&s.setRequestHeader("traceparent",`00-${t.traceId}-${n}-01`),s.addEventListener("loadend",()=>{let f=Date.now()-t.traceStartTime-l,u={url:d,method:h,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:w(c)};try{let o=s.responseText?s.responseText.length:void 0;o&&(u.responsePayloadSize=o)}catch{}t.spans.push({spanId:n,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:l,duration:f,status:s.status,meta:u})}),i.call(this,c)};let g=window.fetch;window.fetch=async function(...c){let s=c[0],n=c[1],l="",h="GET";typeof s=="string"||s instanceof URL?(l=s.toString(),h=((n==null?void 0:n.method)||"GET").toUpperCase()):s instanceof Request&&(l=s.url,h=s.method.toUpperCase());let d=l;try{d=new URL(l,window.location.origin).toString()}catch{}let f=y(16),u=Date.now()-t.traceStartTime;if(t.shouldAttachTraceHeader(d)){let o=`00-${t.traceId}-${f}-01`;if(s instanceof Request){let p=new Headers(s.headers);p.set("traceparent",o),c[1]={...n||{},headers:p}}else{let p=new Headers((n==null?void 0:n.headers)||{});p.set("traceparent",o),c[1]={...n||{},headers:p}}}try{let o=await g.apply(this,c),p=Date.now()-t.traceStartTime-u;return t.spans.push({spanId:f,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:u,duration:p,status:o.status,meta:{url:d,method:h,library:"fetch",status:o.status,statusText:o.statusText,type:o.type,redirected:o.redirected,requestPayloadSize:w(n==null?void 0:n.body)}}),o}catch(o){let p=Date.now()-t.traceStartTime-u;throw t.spans.push({spanId:f,name:`${h} ${new URL(d,window.location.origin).pathname}`,type:"http",startTime:u,duration:p,status:0,meta:{url:d,method:h,library:"fetch",status:0,error:o instanceof Error?o.message:String(o),requestPayloadSize:w(n==null?void 0:n.body)}}),o}}}setupErrorListeners(){let t=(e,i)=>{this.frustrations.errorCount++;let a=e.message||String(e);this.errors.push({errorClass:e.name||"Error",message:a,stackTrace:e.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...b(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",e=>{e.error&&t(e.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",e=>{t(e.reason instanceof Error?e.reason:new Error(String(e.reason)),"Unhandled Promise Rejection")})}setupRoutingListeners(){let t=history.pushState;history.pushState=(...e)=>{this.flush(),t.apply(history,e),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.spans.length===0&&this.errors.length===0&&!this.isInitialLoad)return;let t={traces:[],errors:this.errors};if(this.isSampled&&t.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},...b(),spans:[...this.spans],duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.spans=[],this.errors=[],this.frustrations={rageClicks:0,deadClicks:0,errorCount:0},this.isInitialLoad=!1,t.traces.length>0||t.errors.length>0){let e=new Blob([JSON.stringify(t)],{type:"application/json"}),i=this.endpoint.includes("?")?"&":"?",a=`${this.endpoint}${i}apiKey=${this.config.apiKey}`;navigator.sendBeacon?navigator.sendBeacon(a,e):fetch(a,{method:"POST",body:e,keepalive:!0,headers:{"x-service-api-key":this.config.apiKey}}).catch(()=>{})}}};var x=new v,I=new S,T={init:r=>x.init(r),initRum:r=>I.init(r)};typeof window<"u"&&(window.Senzor=T);export{x as Analytics,I as RUM,T as Senzor};
|
package/package.json
CHANGED
package/src/analytics.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { generateUUID } from './utils';
|
|
2
|
+
|
|
3
|
+
export interface AnalyticsConfig {
|
|
4
|
+
webId: string;
|
|
5
|
+
endpoint?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class SenzorAnalyticsAgent {
|
|
9
|
+
private config: AnalyticsConfig = { webId: '' };
|
|
10
|
+
private startTime: number = Date.now();
|
|
11
|
+
private endpoint: string = 'https://api.senzor.dev/api/ingest/web';
|
|
12
|
+
private initialized: boolean = false;
|
|
13
|
+
|
|
14
|
+
public init(config: AnalyticsConfig) {
|
|
15
|
+
if (this.initialized) return;
|
|
16
|
+
this.initialized = true;
|
|
17
|
+
this.config = { ...this.config, ...config };
|
|
18
|
+
if (config.endpoint) this.endpoint = config.endpoint;
|
|
19
|
+
|
|
20
|
+
if (!this.config.webId) {
|
|
21
|
+
console.error('[Senzor] webId is required for Analytics.');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.manageSession();
|
|
26
|
+
this.trackPageView();
|
|
27
|
+
this.setupListeners();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private normalizeUrl(url: string): string {
|
|
31
|
+
return url ? url.replace(/^https?:\/\//, '') : '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private manageSession() {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const lastActivity = parseInt(localStorage.getItem('sz_wa_last') || '0', 10);
|
|
37
|
+
if (!localStorage.getItem('sz_wa_vid')) localStorage.setItem('sz_wa_vid', generateUUID());
|
|
38
|
+
|
|
39
|
+
let sessionId = sessionStorage.getItem('sz_wa_sid');
|
|
40
|
+
if (!sessionId || (now - lastActivity > 30 * 60 * 1000)) {
|
|
41
|
+
sessionId = generateUUID();
|
|
42
|
+
sessionStorage.setItem('sz_wa_sid', sessionId);
|
|
43
|
+
this.determineReferrer(true);
|
|
44
|
+
} else {
|
|
45
|
+
this.determineReferrer(false);
|
|
46
|
+
}
|
|
47
|
+
localStorage.setItem('sz_wa_last', now.toString());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private determineReferrer(isNewSession: boolean) {
|
|
51
|
+
const rawReferrer = document.referrer;
|
|
52
|
+
let isExternal = false;
|
|
53
|
+
if (rawReferrer) {
|
|
54
|
+
try { isExternal = new URL(rawReferrer).hostname !== window.location.hostname; } catch (e) { isExternal = true; }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (isExternal) {
|
|
58
|
+
const cleanRef = this.normalizeUrl(rawReferrer);
|
|
59
|
+
if (cleanRef !== sessionStorage.getItem('sz_wa_ref')) sessionStorage.setItem('sz_wa_ref', cleanRef);
|
|
60
|
+
} else if (isNewSession && !sessionStorage.getItem('sz_wa_ref')) {
|
|
61
|
+
sessionStorage.setItem('sz_wa_ref', 'Direct');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private getIds() {
|
|
66
|
+
localStorage.setItem('sz_wa_last', Date.now().toString());
|
|
67
|
+
return {
|
|
68
|
+
visitorId: localStorage.getItem('sz_wa_vid') || 'unknown',
|
|
69
|
+
sessionId: sessionStorage.getItem('sz_wa_sid') || 'unknown',
|
|
70
|
+
referrer: sessionStorage.getItem('sz_wa_ref') || 'Direct'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private trackPageView() {
|
|
75
|
+
this.manageSession();
|
|
76
|
+
this.startTime = Date.now();
|
|
77
|
+
this.send({
|
|
78
|
+
type: 'pageview',
|
|
79
|
+
webId: this.config.webId,
|
|
80
|
+
...this.getIds(),
|
|
81
|
+
url: window.location.href,
|
|
82
|
+
path: window.location.pathname,
|
|
83
|
+
title: document.title,
|
|
84
|
+
width: window.innerWidth,
|
|
85
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
86
|
+
referrer: this.getIds().referrer
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private trackPing() {
|
|
91
|
+
const duration = Math.floor((Date.now() - this.startTime) / 1000);
|
|
92
|
+
if (duration >= 1) {
|
|
93
|
+
this.send({
|
|
94
|
+
type: 'ping',
|
|
95
|
+
webId: this.config.webId,
|
|
96
|
+
...this.getIds(),
|
|
97
|
+
url: window.location.href,
|
|
98
|
+
path: window.location.pathname,
|
|
99
|
+
title: document.title,
|
|
100
|
+
width: window.innerWidth,
|
|
101
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
102
|
+
referrer: this.getIds().referrer,
|
|
103
|
+
duration
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private send(data: any) {
|
|
109
|
+
if (navigator.sendBeacon) {
|
|
110
|
+
if (!navigator.sendBeacon(this.endpoint, new Blob([JSON.stringify(data)], { type: 'application/json' }))) {
|
|
111
|
+
this.fallbackSend(data);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
this.fallbackSend(data);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private fallbackSend(data: any) {
|
|
119
|
+
fetch(this.endpoint, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
body: JSON.stringify(data),
|
|
122
|
+
keepalive: true,
|
|
123
|
+
headers: { 'Content-Type': 'application/json' }
|
|
124
|
+
}).catch(() => { });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private setupListeners() {
|
|
128
|
+
const originalPushState = history.pushState;
|
|
129
|
+
history.pushState = (...args) => {
|
|
130
|
+
this.trackPing();
|
|
131
|
+
originalPushState.apply(history, args);
|
|
132
|
+
this.trackPageView();
|
|
133
|
+
};
|
|
134
|
+
window.addEventListener('popstate', () => {
|
|
135
|
+
this.trackPing();
|
|
136
|
+
this.trackPageView();
|
|
137
|
+
});
|
|
138
|
+
document.addEventListener('visibilitychange', () => {
|
|
139
|
+
if (document.visibilityState === 'hidden') {
|
|
140
|
+
this.trackPing();
|
|
141
|
+
} else {
|
|
142
|
+
this.startTime = Date.now();
|
|
143
|
+
this.manageSession();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
window.addEventListener('beforeunload', () => this.trackPing());
|
|
147
|
+
}
|
|
148
|
+
}
|