@senzops/web 1.3.1 → 1.3.3
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 +7 -4
- package/dist/index.d.ts +7 -4
- 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 +158 -0
- package/src/index.ts +7 -519
- package/src/rum.ts +446 -0
- package/src/utils.ts +52 -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;
|
|
@@ -33,13 +34,14 @@ declare class SenzorRumAgent {
|
|
|
33
34
|
private traceId;
|
|
34
35
|
private traceStartTime;
|
|
35
36
|
private isInitialLoad;
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
private breadcrumbs;
|
|
37
|
+
private spanQueue;
|
|
38
|
+
private errorQueue;
|
|
39
39
|
private vitals;
|
|
40
|
+
private breadcrumbs;
|
|
40
41
|
private frustrations;
|
|
41
42
|
private clickHistory;
|
|
42
43
|
private flushInterval;
|
|
44
|
+
private readonly MAX_BATCH_SIZE;
|
|
43
45
|
init(config: RumConfig): void;
|
|
44
46
|
private manageSession;
|
|
45
47
|
private startNewTrace;
|
|
@@ -53,6 +55,7 @@ declare class SenzorRumAgent {
|
|
|
53
55
|
private setupRoutingListeners;
|
|
54
56
|
private flush;
|
|
55
57
|
}
|
|
58
|
+
|
|
56
59
|
declare const Analytics: SenzorAnalyticsAgent;
|
|
57
60
|
declare const RUM: SenzorRumAgent;
|
|
58
61
|
declare const Senzor: {
|
|
@@ -60,4 +63,4 @@ declare const Senzor: {
|
|
|
60
63
|
initRum: (config: RumConfig) => void;
|
|
61
64
|
};
|
|
62
65
|
|
|
63
|
-
export { Analytics, RUM, Senzor };
|
|
66
|
+
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;
|
|
@@ -33,13 +34,14 @@ declare class SenzorRumAgent {
|
|
|
33
34
|
private traceId;
|
|
34
35
|
private traceStartTime;
|
|
35
36
|
private isInitialLoad;
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
private breadcrumbs;
|
|
37
|
+
private spanQueue;
|
|
38
|
+
private errorQueue;
|
|
39
39
|
private vitals;
|
|
40
|
+
private breadcrumbs;
|
|
40
41
|
private frustrations;
|
|
41
42
|
private clickHistory;
|
|
42
43
|
private flushInterval;
|
|
44
|
+
private readonly MAX_BATCH_SIZE;
|
|
43
45
|
init(config: RumConfig): void;
|
|
44
46
|
private manageSession;
|
|
45
47
|
private startNewTrace;
|
|
@@ -53,6 +55,7 @@ declare class SenzorRumAgent {
|
|
|
53
55
|
private setupRoutingListeners;
|
|
54
56
|
private flush;
|
|
55
57
|
}
|
|
58
|
+
|
|
56
59
|
declare const Analytics: SenzorAnalyticsAgent;
|
|
57
60
|
declare const RUM: SenzorRumAgent;
|
|
58
61
|
declare const Senzor: {
|
|
@@ -60,4 +63,4 @@ declare const Senzor: {
|
|
|
60
63
|
initRum: (config: RumConfig) => void;
|
|
61
64
|
};
|
|
62
65
|
|
|
63
|
-
export { Analytics, RUM, Senzor };
|
|
66
|
+
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 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 H=()=>{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}},_=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},R=s=>{let e={};return s&&(s instanceof Headers?s.forEach((t,i)=>e[i]=t):Array.isArray(s)?s.forEach(([t,i])=>e[t]=i):typeof s=="object"&&Object.assign(e,s)),e};var I=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),i=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let a=sessionStorage.getItem("senzor_sid"),l=e-t>i;!a||l?(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,i=window.location.hostname,a=sessionStorage.getItem("senzor_ref"),l=!1;if(t)try{new URL(t).hostname!==i&&(l=!0)}catch{l=!0}if(l){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{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;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.setupErrorListeners(),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,i){this.breadcrumbs.push({type:e,message:t,data:i,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,i=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||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(r=>n-r.time<1e3),this.clickHistory.length>=3){let r=this.clickHistory[0],o=!0;for(let p=1;p<this.clickHistory.length;p++){let u=Math.abs(this.clickHistory[p].x-r.x),h=Math.abs(this.clickHistory[p].y-r.y);(u>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 i of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let i=t.getEntries(),a=i[i.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let i of t.getEntries())i.hadRecentInput||(e+=i.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let i of t.getEntries()){let a=i,l=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||l>this.vitals.inp)&&(this.vitals.inp=l)}}).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(i=>typeof i=="string"?t.origin.includes(i):i instanceof RegExp?i.test(t.origin):!1)}catch{return!1}}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,r,...o){return this.__szMethod=n.toUpperCase(),this.__szUrl=r,this.__szHeaders={},t.apply(this,[n,r,...o])},XMLHttpRequest.prototype.setRequestHeader=function(n,r){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=r,a.apply(this,[n,r])},XMLHttpRequest.prototype.send=function(n){let r=this,o=x(16),p=Date.now()-e.traceStartTime,u=r.__szMethod,h=r.__szUrl;try{h=new URL(r.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(h)&&r.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),r.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-p,m={};try{m=r.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:h,method:u,library:"xhr",status:r.status,responseType:r.responseType,requestPayloadSize:_(n),requestHeaders:r.__szHeaders,responseHeaders:m};try{(r.responseType===""||r.responseType==="text")&&(y.responsePayloadSize=(w=r.responseText)==null?void 0:w.length)}catch{}e.spanQueue.push({spanId:o,name:`${u} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:p,duration:g,status:r.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),i.call(this,n)};let l=window.fetch;window.fetch=async function(...n){let r=n[0],o=n[1],p="",u="GET";typeof r=="string"||r instanceof URL?(p=r.toString(),u=((o==null?void 0:o.method)||"GET").toUpperCase()):r instanceof Request&&(p=r.url,u=r.method.toUpperCase());let h=p;try{h=new URL(p,window.location.origin).toString()}catch{}let g=x(16),m=Date.now()-e.traceStartTime,y=R((o==null?void 0:o.headers)||(r instanceof Request?r.headers:{}));if(e.shouldAttachTraceHeader(h)){let c=`00-${e.traceId}-${g}-01`;if(r instanceof Request){let d=new Headers(r.headers);d.set("traceparent",c),n[1]={...o||{},headers:d}}else{let d=new Headers((o==null?void 0:o.headers)||{});d.set("traceparent",c),n[1]={...o||{},headers:d}}y.traceparent=c}let w=(c,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:h,method:u,library:"fetch",status:c,requestPayloadSize:_(o==null?void 0:o.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=R(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${u} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:c,meta:f}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()};try{let c=await l.apply(this,n);return w(c.status,c),c}catch(c){throw w(0,void 0,c instanceof Error?c.message:String(c)),c}}}setupErrorListeners(){let e=(t,i)=>{this.frustrations.errorCount++;let a=t.message||String(t);this.errorQueue.push({errorClass:t.name||"Error",message:a,stackTrace:t.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...H(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",t=>{t.error&&e(t.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",t=>{e(t.reason instanceof Error?t.reason:new Error(String(t.reason)),"Unhandled Promise Rejection")})}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),i={traces:[],errors:t};if(this.isSampled&&i.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},...H(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,i.traces.length>0||i.errors.length>0){let a=new Blob([JSON.stringify(i)],{type:"application/json"}),l=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${l}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 I,L=new T,E={init:s=>k.init(s),initRum:s=>L.init(s)};typeof window<"u"&&(window.Senzor=E);})();
|
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 H=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var D=(r,e)=>{for(var t in e)H(r,t,{get:e[t],enumerable:!0})},P=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of M(e))!A.call(r,n)&&n!==t&&H(r,n,{get:()=>e[n],enumerable:!(i=U(e,n))||i.enumerable});return r};var q=r=>P(H({},"__esModule",{value:!0}),r);var B={};D(B,{Analytics:()=>k,RUM:()=>L,Senzor:()=>E});module.exports=q(B);function v(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{let e=Math.random()*16|0;return(r==="x"?e:e&3|8).toString(16)})}function x(r){let e="";for(;e.length<r;)e+=Math.random().toString(16).slice(2);return e.slice(0,r)}var _=()=>{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}},R=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 ArrayBuffer)return r.byteLength},z=r=>{let e={};return r&&(r instanceof Headers?r.forEach((t,i)=>e[i]=t):Array.isArray(r)?r.forEach(([t,i])=>e[t]=i):typeof r=="object"&&Object.assign(e,r)),e};var I=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),i=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let n=sessionStorage.getItem("senzor_sid"),l=e-t>i;!n||l?(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,i=window.location.hostname,n=sessionStorage.getItem("senzor_ref"),l=!1;if(t)try{new URL(t).hostname!==i&&(l=!0)}catch{l=!0}if(l){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{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;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.setupErrorListeners(),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,i){this.breadcrumbs.push({type:e,message:t,data:i,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,i=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||t.closest("button")||t.closest("a")||t.hasAttribute("role")||t.onclick||this.frustrations.deadClicks++;let a=Date.now();if(this.clickHistory.push({x:e.clientX,y:e.clientY,time:a}),this.clickHistory=this.clickHistory.filter(s=>a-s.time<1e3),this.clickHistory.length>=3){let s=this.clickHistory[0],o=!0;for(let p=1;p<this.clickHistory.length;p++){let u=Math.abs(this.clickHistory[p].x-s.x),h=Math.abs(this.clickHistory[p].y-s.y);(u>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 i of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let i=t.getEntries(),n=i[i.length-1];n&&(this.vitals.lcp=n.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let i of t.getEntries())i.hadRecentInput||(e+=i.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let i of t.getEntries()){let n=i,l=n.duration||(n.processingStart&&n.startTime?n.processingStart-n.startTime:0);(!this.vitals.inp||l>this.vitals.inp)&&(this.vitals.inp=l)}}).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(i=>typeof i=="string"?t.origin.includes(i):i instanceof RegExp?i.test(t.origin):!1)}catch{return!1}}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,i=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),p=Date.now()-e.traceStartTime,u=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-p,m={};try{m=s.getAllResponseHeaders().trim().split(/[\r\n]+/).reduce((d,S)=>{let b=S.split(": "),f=b.shift(),C=b.join(": ");return f&&(d[f]=C),d},{})}catch{}let y={url:h,method:u,library:"xhr",status:s.status,responseType:s.responseType,requestPayloadSize:R(a),requestHeaders:s.__szHeaders,responseHeaders:m};try{(s.responseType===""||s.responseType==="text")&&(y.responsePayloadSize=(w=s.responseText)==null?void 0:w.length)}catch{}e.spanQueue.push({spanId:o,name:`${u} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:p,duration:g,status:s.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),i.call(this,a)};let l=window.fetch;window.fetch=async function(...a){let s=a[0],o=a[1],p="",u="GET";typeof s=="string"||s instanceof URL?(p=s.toString(),u=((o==null?void 0:o.method)||"GET").toUpperCase()):s instanceof Request&&(p=s.url,u=s.method.toUpperCase());let h=p;try{h=new URL(p,window.location.origin).toString()}catch{}let g=x(16),m=Date.now()-e.traceStartTime,y=z((o==null?void 0:o.headers)||(s instanceof Request?s.headers:{}));if(e.shouldAttachTraceHeader(h)){let c=`00-${e.traceId}-${g}-01`;if(s instanceof Request){let d=new Headers(s.headers);d.set("traceparent",c),a[1]={...o||{},headers:d}}else{let d=new Headers((o==null?void 0:o.headers)||{});d.set("traceparent",c),a[1]={...o||{},headers:d}}y.traceparent=c}let w=(c,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:h,method:u,library:"fetch",status:c,requestPayloadSize:R(o==null?void 0:o.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=z(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${u} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:c,meta:f}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()};try{let c=await l.apply(this,a);return w(c.status,c),c}catch(c){throw w(0,void 0,c instanceof Error?c.message:String(c)),c}}}setupErrorListeners(){let e=(t,i)=>{this.frustrations.errorCount++;let n=t.message||String(t);this.errorQueue.push({errorClass:t.name||"Error",message:n,stackTrace:t.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,..._(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",t=>{t.error&&e(t.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",t=>{e(t.reason instanceof Error?t.reason:new Error(String(t.reason)),"Unhandled Promise Rejection")})}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),i={traces:[],errors:t};if(this.isSampled&&i.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,i.traces.length>0||i.errors.length>0){let n=new Blob([JSON.stringify(i)],{type:"application/json"}),l=this.endpoint.includes("?")?"&":"?",a=`${this.endpoint}${l}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 I,L=new T,E={init:r=>k.init(r),initRum:r=>L.init(r)};typeof window<"u"&&(window.Senzor=E);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 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 H=()=>{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}},_=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},R=s=>{let e={};return s&&(s instanceof Headers?s.forEach((t,i)=>e[i]=t):Array.isArray(s)?s.forEach(([t,i])=>e[t]=i):typeof s=="object"&&Object.assign(e,s)),e};var I=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),i=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",v());let a=sessionStorage.getItem("senzor_sid"),l=e-t>i;!a||l?(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,i=window.location.hostname,a=sessionStorage.getItem("senzor_ref"),l=!1;if(t)try{new URL(t).hostname!==i&&(l=!0)}catch{l=!0}if(l){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{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;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.setupErrorListeners(),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,i){this.breadcrumbs.push({type:e,message:t,data:i,time:Date.now()}),this.breadcrumbs.length>20&&this.breadcrumbs.shift()}setupUXListeners(){document.addEventListener("click",e=>{let t=e.target,i=t.tagName?t.tagName.toLowerCase():"";this.addBreadcrumb("click",`Clicked ${i}${t.id?"#"+t.id:""}${t.className?"."+t.className.split(" ")[0]:""}`),["a","button","input","select","textarea","label"].includes(i)||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(r=>n-r.time<1e3),this.clickHistory.length>=3){let r=this.clickHistory[0],o=!0;for(let p=1;p<this.clickHistory.length;p++){let u=Math.abs(this.clickHistory[p].x-r.x),h=Math.abs(this.clickHistory[p].y-r.y);(u>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 i of t.getEntriesByName("first-contentful-paint"))this.vitals.fcp=i.startTime}).observe({type:"paint",buffered:!0}),new PerformanceObserver(t=>{let i=t.getEntries(),a=i[i.length-1];a&&(this.vitals.lcp=a.startTime)}).observe({type:"largest-contentful-paint",buffered:!0});let e=0;new PerformanceObserver(t=>{for(let i of t.getEntries())i.hadRecentInput||(e+=i.value,this.vitals.cls=e)}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{for(let i of t.getEntries()){let a=i,l=a.duration||(a.processingStart&&a.startTime?a.processingStart-a.startTime:0);(!this.vitals.inp||l>this.vitals.inp)&&(this.vitals.inp=l)}}).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(i=>typeof i=="string"?t.origin.includes(i):i instanceof RegExp?i.test(t.origin):!1)}catch{return!1}}patchNetwork(){let e=this,t=XMLHttpRequest.prototype.open,i=XMLHttpRequest.prototype.send,a=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.open=function(n,r,...o){return this.__szMethod=n.toUpperCase(),this.__szUrl=r,this.__szHeaders={},t.apply(this,[n,r,...o])},XMLHttpRequest.prototype.setRequestHeader=function(n,r){return this.__szHeaders||(this.__szHeaders={}),this.__szHeaders[n]=r,a.apply(this,[n,r])},XMLHttpRequest.prototype.send=function(n){let r=this,o=x(16),p=Date.now()-e.traceStartTime,u=r.__szMethod,h=r.__szUrl;try{h=new URL(r.__szUrl,window.location.origin).toString()}catch{}return e.shouldAttachTraceHeader(h)&&r.setRequestHeader("traceparent",`00-${e.traceId}-${o}-01`),r.addEventListener("loadend",()=>{var w;let g=Date.now()-e.traceStartTime-p,m={};try{m=r.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:h,method:u,library:"xhr",status:r.status,responseType:r.responseType,requestPayloadSize:_(n),requestHeaders:r.__szHeaders,responseHeaders:m};try{(r.responseType===""||r.responseType==="text")&&(y.responsePayloadSize=(w=r.responseText)==null?void 0:w.length)}catch{}e.spanQueue.push({spanId:o,name:`${u} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:p,duration:g,status:r.status,meta:y}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()}),i.call(this,n)};let l=window.fetch;window.fetch=async function(...n){let r=n[0],o=n[1],p="",u="GET";typeof r=="string"||r instanceof URL?(p=r.toString(),u=((o==null?void 0:o.method)||"GET").toUpperCase()):r instanceof Request&&(p=r.url,u=r.method.toUpperCase());let h=p;try{h=new URL(p,window.location.origin).toString()}catch{}let g=x(16),m=Date.now()-e.traceStartTime,y=R((o==null?void 0:o.headers)||(r instanceof Request?r.headers:{}));if(e.shouldAttachTraceHeader(h)){let c=`00-${e.traceId}-${g}-01`;if(r instanceof Request){let d=new Headers(r.headers);d.set("traceparent",c),n[1]={...o||{},headers:d}}else{let d=new Headers((o==null?void 0:o.headers)||{});d.set("traceparent",c),n[1]={...o||{},headers:d}}y.traceparent=c}let w=(c,d,S)=>{let b=Date.now()-e.traceStartTime-m,f={url:h,method:u,library:"fetch",status:c,requestPayloadSize:_(o==null?void 0:o.body),requestHeaders:y};d&&(f.statusText=d.statusText,f.type=d.type,f.redirected=d.redirected,f.responseHeaders=R(d.headers)),S&&(f.error=S),e.spanQueue.push({spanId:g,name:`${u} ${new URL(h,window.location.origin).pathname}`,type:"http",startTime:m,duration:b,status:c,meta:f}),e.spanQueue.length>=e.MAX_BATCH_SIZE&&e.flush()};try{let c=await l.apply(this,n);return w(c.status,c),c}catch(c){throw w(0,void 0,c instanceof Error?c.message:String(c)),c}}}setupErrorListeners(){let e=(t,i)=>{this.frustrations.errorCount++;let a=t.message||String(t);this.errorQueue.push({errorClass:t.name||"Error",message:a,stackTrace:t.stack||"",traceId:this.isSampled?this.traceId:void 0,context:{type:i,...H(),breadcrumbs:[...this.breadcrumbs]},timestamp:new Date().toISOString()}),this.flush()};window.addEventListener("error",t=>{t.error&&e(t.error,"Uncaught Exception")}),window.addEventListener("unhandledrejection",t=>{e(t.reason instanceof Error?t.reason:new Error(String(t.reason)),"Unhandled Promise Rejection")})}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),i={traces:[],errors:t};if(this.isSampled&&i.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},...H(),spans:e,duration:Date.now()-this.traceStartTime,timestamp:new Date(this.traceStartTime).toISOString()}),this.isInitialLoad=!1,i.traces.length>0||i.errors.length>0){let a=new Blob([JSON.stringify(i)],{type:"application/json"}),l=this.endpoint.includes("?")?"&":"?",n=`${this.endpoint}${l}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 I,L=new T,E={init:s=>k.init(s),initRum:s=>L.init(s)};typeof window<"u"&&(window.Senzor=E);export{k as Analytics,L as RUM,E as Senzor};
|
package/package.json
CHANGED
package/src/analytics.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
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?:\/\//, '').replace(/^www\./, '') : '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private manageSession() {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const lastActivity = parseInt(localStorage.getItem('senzor_last_activity') || '0', 10);
|
|
37
|
+
const sessionTimeout = 30 * 60 * 1000; // 30 minutes
|
|
38
|
+
|
|
39
|
+
if (!localStorage.getItem('senzor_vid')) localStorage.setItem('senzor_vid', generateUUID());
|
|
40
|
+
|
|
41
|
+
let sessionId = sessionStorage.getItem('senzor_sid');
|
|
42
|
+
const isExpired = (now - lastActivity > sessionTimeout);
|
|
43
|
+
|
|
44
|
+
if (!sessionId || isExpired) {
|
|
45
|
+
sessionId = generateUUID();
|
|
46
|
+
sessionStorage.setItem('senzor_sid', sessionId);
|
|
47
|
+
this.determineReferrer(true);
|
|
48
|
+
} else {
|
|
49
|
+
this.determineReferrer(false);
|
|
50
|
+
}
|
|
51
|
+
localStorage.setItem('senzor_last_activity', now.toString());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private determineReferrer(isNewSession: boolean) {
|
|
55
|
+
const rawReferrer = document.referrer;
|
|
56
|
+
const currentHost = window.location.hostname;
|
|
57
|
+
let storedReferrer = sessionStorage.getItem('senzor_ref');
|
|
58
|
+
|
|
59
|
+
let isExternal = false;
|
|
60
|
+
if (rawReferrer) {
|
|
61
|
+
try {
|
|
62
|
+
const refUrl = new URL(rawReferrer);
|
|
63
|
+
if (refUrl.hostname !== currentHost) isExternal = true;
|
|
64
|
+
} catch (e) { isExternal = true; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (isExternal) {
|
|
68
|
+
const cleanRef = this.normalizeUrl(rawReferrer);
|
|
69
|
+
if (cleanRef !== storedReferrer) sessionStorage.setItem('senzor_ref', cleanRef);
|
|
70
|
+
} else if (isNewSession && !storedReferrer) {
|
|
71
|
+
sessionStorage.setItem('senzor_ref', 'Direct');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private getIds() {
|
|
76
|
+
localStorage.setItem('senzor_last_activity', Date.now().toString());
|
|
77
|
+
return {
|
|
78
|
+
visitorId: localStorage.getItem('senzor_vid') || 'unknown',
|
|
79
|
+
sessionId: sessionStorage.getItem('senzor_sid') || 'unknown',
|
|
80
|
+
referrer: sessionStorage.getItem('senzor_ref') || 'Direct'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private trackPageView() {
|
|
85
|
+
this.manageSession();
|
|
86
|
+
this.startTime = Date.now();
|
|
87
|
+
this.send({
|
|
88
|
+
type: 'pageview',
|
|
89
|
+
webId: this.config.webId,
|
|
90
|
+
...this.getIds(),
|
|
91
|
+
url: window.location.href,
|
|
92
|
+
path: window.location.pathname,
|
|
93
|
+
title: document.title,
|
|
94
|
+
width: window.innerWidth,
|
|
95
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
96
|
+
referrer: this.getIds().referrer
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private trackPing() {
|
|
101
|
+
const duration = Math.floor((Date.now() - this.startTime) / 1000);
|
|
102
|
+
if (duration >= 1) {
|
|
103
|
+
this.send({
|
|
104
|
+
type: 'ping',
|
|
105
|
+
webId: this.config.webId,
|
|
106
|
+
...this.getIds(),
|
|
107
|
+
url: window.location.href,
|
|
108
|
+
path: window.location.pathname,
|
|
109
|
+
title: document.title,
|
|
110
|
+
width: window.innerWidth,
|
|
111
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
112
|
+
referrer: this.getIds().referrer,
|
|
113
|
+
duration
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private send(data: any) {
|
|
119
|
+
if (navigator.sendBeacon) {
|
|
120
|
+
if (!navigator.sendBeacon(this.endpoint, new Blob([JSON.stringify(data)], { type: 'application/json' }))) {
|
|
121
|
+
this.fallbackSend(data);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
this.fallbackSend(data);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private fallbackSend(data: any) {
|
|
129
|
+
fetch(this.endpoint, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
body: JSON.stringify(data),
|
|
132
|
+
keepalive: true,
|
|
133
|
+
headers: { 'Content-Type': 'application/json' }
|
|
134
|
+
}).catch(() => { });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private setupListeners() {
|
|
138
|
+
const originalPushState = history.pushState;
|
|
139
|
+
history.pushState = (...args) => {
|
|
140
|
+
this.trackPing();
|
|
141
|
+
originalPushState.apply(history, args);
|
|
142
|
+
this.trackPageView();
|
|
143
|
+
};
|
|
144
|
+
window.addEventListener('popstate', () => {
|
|
145
|
+
this.trackPing();
|
|
146
|
+
this.trackPageView();
|
|
147
|
+
});
|
|
148
|
+
document.addEventListener('visibilitychange', () => {
|
|
149
|
+
if (document.visibilityState === 'hidden') {
|
|
150
|
+
this.trackPing();
|
|
151
|
+
} else {
|
|
152
|
+
this.startTime = Date.now();
|
|
153
|
+
this.manageSession();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
window.addEventListener('beforeunload', () => this.trackPing());
|
|
157
|
+
}
|
|
158
|
+
}
|