@senzops/web 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -9,7 +9,9 @@ declare class SenzorWebAgent {
9
9
  private initialized;
10
10
  constructor();
11
11
  init(config: Config): void;
12
- private checkSession;
12
+ private normalizeUrl;
13
+ private manageSession;
14
+ private determineReferrer;
13
15
  private getIds;
14
16
  private trackPageView;
15
17
  private trackPing;
package/dist/index.d.ts CHANGED
@@ -9,7 +9,9 @@ declare class SenzorWebAgent {
9
9
  private initialized;
10
10
  constructor();
11
11
  init(config: Config): void;
12
- private checkSession;
12
+ private normalizeUrl;
13
+ private manageSession;
14
+ private determineReferrer;
13
15
  private getIds;
14
16
  private trackPageView;
15
17
  private trackPing;
@@ -1 +1 @@
1
- (()=>{function r(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let t=Math.random()*16|0;return(n==="x"?t:t&3|8).toString(16)})}var i=class{config;startTime;endpoint;initialized;constructor(){this.config={webId:"",endpoint:"https://api.senzor.dev/api/ingest/web"},this.startTime=Date.now(),this.endpoint="",this.initialized=!1}init(t){if(this.initialized){console.warn("[Senzor] Agent already initialized.");return}if(this.initialized=!0,this.config={...this.config,...t},this.endpoint=this.config.endpoint||"https://api.senzor.dev/api/ingest/web",!this.config.webId){console.error("[Senzor] WebId is required.");return}this.checkSession(),this.trackPageView(),this.setupListeners()}checkSession(){let t=Date.now(),e=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),o=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",r()),(!localStorage.getItem("senzor_sid")||t-e>o)&&localStorage.setItem("senzor_sid",r()),localStorage.setItem("senzor_last_activity",t.toString())}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:localStorage.getItem("senzor_sid")||"unknown"}}trackPageView(){this.checkSession(),this.startTime=Date.now();let t={type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,referrer:document.referrer,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone};this.send(t)}trackPing(){let t=Math.floor((Date.now()-this.startTime)/1e3);if(t<1)return;let e={type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,referrer:document.referrer,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,duration:t};this.send(e)}send(t){if(navigator.sendBeacon){let e=new Blob([JSON.stringify(t)],{type:"application/json"});navigator.sendBeacon(this.endpoint,e)||this.fallbackSend(t)}else this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(e=>console.error("[Senzor] Telemetry Error:",e))}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.checkSession())}),window.addEventListener("beforeunload",()=>{this.trackPing()})}},s=new i;typeof window<"u"&&(window.Senzor=s);})();
1
+ (()=>{function d(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}var o=class{config;startTime;endpoint;initialized;constructor(){this.config={webId:"",endpoint:"https://api.senzor.dev/api/ingest/web"},this.startTime=Date.now(),this.endpoint="",this.initialized=!1}init(e){if(this.initialized){console.warn("[Senzor] Agent already initialized.");return}if(this.initialized=!0,this.config={...this.config,...e},this.endpoint=this.config.endpoint||"https://api.senzor.dev/api/ingest/web",!this.config.webId){console.error("[Senzor] WebId is required.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}normalizeUrl(e){return e?e.replace(/^https?:\/\//,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",d());let i=sessionStorage.getItem("senzor_sid"),n=e-t>r;!i||n?(i=d(),sessionStorage.setItem("senzor_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,i=sessionStorage.getItem("senzor_ref"),n=!1;if(t)try{new URL(t).hostname!==r&&(n=!0)}catch{n=!0}if(n){let s=this.normalizeUrl(t);s!==i&&sessionStorage.setItem("senzor_ref",s)}else e&&!i&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now();let e={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};this.send(e)}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);if(e<1)return;let t={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};this.send(t)}send(e){if(navigator.sendBeacon){let t=new Blob([JSON.stringify(e)],{type:"application/json"});navigator.sendBeacon(this.endpoint,t)||this.fallbackSend(e)}else this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(t=>console.error("[Senzor] Telemetry Error:",t))}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()})}},c=new o;typeof window<"u"&&(window.Senzor=c);})();
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var r=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var g=(i,t)=>{for(var e in t)r(i,e,{get:t[e],enumerable:!0})},p=(i,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of l(t))!h.call(i,o)&&o!==e&&r(i,o,{get:()=>t[o],enumerable:!(n=c(t,o))||n.enumerable});return i};var w=i=>p(r({},"__esModule",{value:!0}),i);var f={};g(f,{Senzor:()=>d});module.exports=w(f);function a(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let t=Math.random()*16|0;return(i==="x"?t:t&3|8).toString(16)})}var s=class{config;startTime;endpoint;initialized;constructor(){this.config={webId:"",endpoint:"https://api.senzor.dev/api/ingest/web"},this.startTime=Date.now(),this.endpoint="",this.initialized=!1}init(t){if(this.initialized){console.warn("[Senzor] Agent already initialized.");return}if(this.initialized=!0,this.config={...this.config,...t},this.endpoint=this.config.endpoint||"https://api.senzor.dev/api/ingest/web",!this.config.webId){console.error("[Senzor] WebId is required.");return}this.checkSession(),this.trackPageView(),this.setupListeners()}checkSession(){let t=Date.now(),e=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),n=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",a()),(!localStorage.getItem("senzor_sid")||t-e>n)&&localStorage.setItem("senzor_sid",a()),localStorage.setItem("senzor_last_activity",t.toString())}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:localStorage.getItem("senzor_sid")||"unknown"}}trackPageView(){this.checkSession(),this.startTime=Date.now();let t={type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,referrer:document.referrer,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone};this.send(t)}trackPing(){let t=Math.floor((Date.now()-this.startTime)/1e3);if(t<1)return;let e={type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,referrer:document.referrer,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,duration:t};this.send(e)}send(t){if(navigator.sendBeacon){let e=new Blob([JSON.stringify(t)],{type:"application/json"});navigator.sendBeacon(this.endpoint,e)||this.fallbackSend(t)}else this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(e=>console.error("[Senzor] Telemetry Error:",e))}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.checkSession())}),window.addEventListener("beforeunload",()=>{this.trackPing()})}},d=new s;typeof window<"u"&&(window.Senzor=d);0&&(module.exports={Senzor});
1
+ var a=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var f=(n,e)=>{for(var t in e)a(n,t,{get:e[t],enumerable:!0})},w=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of h(e))!p.call(n,i)&&i!==t&&a(n,i,{get:()=>e[i],enumerable:!(r=g(e,i))||r.enumerable});return n};var m=n=>w(a({},"__esModule",{value:!0}),n);var u={};f(u,{Senzor:()=>l});module.exports=m(u);function c(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let e=Math.random()*16|0;return(n==="x"?e:e&3|8).toString(16)})}var d=class{config;startTime;endpoint;initialized;constructor(){this.config={webId:"",endpoint:"https://api.senzor.dev/api/ingest/web"},this.startTime=Date.now(),this.endpoint="",this.initialized=!1}init(e){if(this.initialized){console.warn("[Senzor] Agent already initialized.");return}if(this.initialized=!0,this.config={...this.config,...e},this.endpoint=this.config.endpoint||"https://api.senzor.dev/api/ingest/web",!this.config.webId){console.error("[Senzor] WebId is required.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}normalizeUrl(e){return e?e.replace(/^https?:\/\//,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",c());let i=sessionStorage.getItem("senzor_sid"),s=e-t>r;!i||s?(i=c(),sessionStorage.setItem("senzor_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,i=sessionStorage.getItem("senzor_ref"),s=!1;if(t)try{new URL(t).hostname!==r&&(s=!0)}catch{s=!0}if(s){let o=this.normalizeUrl(t);o!==i&&sessionStorage.setItem("senzor_ref",o)}else e&&!i&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now();let e={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};this.send(e)}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);if(e<1)return;let t={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};this.send(t)}send(e){if(navigator.sendBeacon){let t=new Blob([JSON.stringify(e)],{type:"application/json"});navigator.sendBeacon(this.endpoint,t)||this.fallbackSend(e)}else this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(t=>console.error("[Senzor] Telemetry Error:",t))}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()})}},l=new d;typeof window<"u"&&(window.Senzor=l);0&&(module.exports={Senzor});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- function r(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let t=Math.random()*16|0;return(n==="x"?t:t&3|8).toString(16)})}var i=class{config;startTime;endpoint;initialized;constructor(){this.config={webId:"",endpoint:"https://api.senzor.dev/api/ingest/web"},this.startTime=Date.now(),this.endpoint="",this.initialized=!1}init(t){if(this.initialized){console.warn("[Senzor] Agent already initialized.");return}if(this.initialized=!0,this.config={...this.config,...t},this.endpoint=this.config.endpoint||"https://api.senzor.dev/api/ingest/web",!this.config.webId){console.error("[Senzor] WebId is required.");return}this.checkSession(),this.trackPageView(),this.setupListeners()}checkSession(){let t=Date.now(),e=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),o=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",r()),(!localStorage.getItem("senzor_sid")||t-e>o)&&localStorage.setItem("senzor_sid",r()),localStorage.setItem("senzor_last_activity",t.toString())}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:localStorage.getItem("senzor_sid")||"unknown"}}trackPageView(){this.checkSession(),this.startTime=Date.now();let t={type:"pageview",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,referrer:document.referrer,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone};this.send(t)}trackPing(){let t=Math.floor((Date.now()-this.startTime)/1e3);if(t<1)return;let e={type:"ping",webId:this.config.webId,...this.getIds(),url:window.location.href,path:window.location.pathname,referrer:document.referrer,width:window.innerWidth,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,duration:t};this.send(e)}send(t){if(navigator.sendBeacon){let e=new Blob([JSON.stringify(t)],{type:"application/json"});navigator.sendBeacon(this.endpoint,e)||this.fallbackSend(t)}else this.fallbackSend(t)}fallbackSend(t){fetch(this.endpoint,{method:"POST",body:JSON.stringify(t),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(e=>console.error("[Senzor] Telemetry Error:",e))}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.checkSession())}),window.addEventListener("beforeunload",()=>{this.trackPing()})}},s=new i;typeof window<"u"&&(window.Senzor=s);export{s as Senzor};
1
+ function d(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}var o=class{config;startTime;endpoint;initialized;constructor(){this.config={webId:"",endpoint:"https://api.senzor.dev/api/ingest/web"},this.startTime=Date.now(),this.endpoint="",this.initialized=!1}init(e){if(this.initialized){console.warn("[Senzor] Agent already initialized.");return}if(this.initialized=!0,this.config={...this.config,...e},this.endpoint=this.config.endpoint||"https://api.senzor.dev/api/ingest/web",!this.config.webId){console.error("[Senzor] WebId is required.");return}this.manageSession(),this.trackPageView(),this.setupListeners()}normalizeUrl(e){return e?e.replace(/^https?:\/\//,""):""}manageSession(){let e=Date.now(),t=parseInt(localStorage.getItem("senzor_last_activity")||"0",10),r=1800*1e3;localStorage.getItem("senzor_vid")||localStorage.setItem("senzor_vid",d());let i=sessionStorage.getItem("senzor_sid"),n=e-t>r;!i||n?(i=d(),sessionStorage.setItem("senzor_sid",i),this.determineReferrer(!0)):this.determineReferrer(!1),localStorage.setItem("senzor_last_activity",e.toString())}determineReferrer(e){let t=document.referrer,r=window.location.hostname,i=sessionStorage.getItem("senzor_ref"),n=!1;if(t)try{new URL(t).hostname!==r&&(n=!0)}catch{n=!0}if(n){let s=this.normalizeUrl(t);s!==i&&sessionStorage.setItem("senzor_ref",s)}else e&&!i&&sessionStorage.setItem("senzor_ref","Direct")}getIds(){return localStorage.setItem("senzor_last_activity",Date.now().toString()),{visitorId:localStorage.getItem("senzor_vid")||"unknown",sessionId:sessionStorage.getItem("senzor_sid")||"unknown",referrer:sessionStorage.getItem("senzor_ref")||"Direct"}}trackPageView(){this.manageSession(),this.startTime=Date.now();let e={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};this.send(e)}trackPing(){let e=Math.floor((Date.now()-this.startTime)/1e3);if(e<1)return;let t={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};this.send(t)}send(e){if(navigator.sendBeacon){let t=new Blob([JSON.stringify(e)],{type:"application/json"});navigator.sendBeacon(this.endpoint,t)||this.fallbackSend(e)}else this.fallbackSend(e)}fallbackSend(e){fetch(this.endpoint,{method:"POST",body:JSON.stringify(e),keepalive:!0,headers:{"Content-Type":"application/json"}}).catch(t=>console.error("[Senzor] Telemetry Error:",t))}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()})}},c=new o;typeof window<"u"&&(window.Senzor=c);export{c as Senzor};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@senzops/web",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Senzor Web Analytics SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ interface Payload {
10
10
  sessionId: string;
11
11
  url: string;
12
12
  path: string;
13
+ title: string;
13
14
  referrer: string;
14
15
  width: number;
15
16
  timezone: string;
@@ -56,50 +57,86 @@ class SenzorWebAgent {
56
57
  return;
57
58
  }
58
59
 
59
- // 1. Manage Session State
60
- this.checkSession();
61
-
62
- // 2. Track initial load
60
+ this.manageSession();
63
61
  this.trackPageView();
64
-
65
- // 3. Setup Listeners
66
62
  this.setupListeners();
67
63
  }
68
64
 
69
- // --- Standard Analytics Session Logic ---
70
- // A session ends after 30 minutes of inactivity.
71
- private checkSession() {
65
+ // Helper to normalize referrer (strip protocol)
66
+ private normalizeUrl(url: string): string {
67
+ if (!url) return '';
68
+ return url.replace(/^https?:\/\//, '');
69
+ }
70
+
71
+ private manageSession() {
72
72
  const now = Date.now();
73
73
  const lastActivity = parseInt(localStorage.getItem('senzor_last_activity') || '0', 10);
74
74
  const sessionTimeout = 30 * 60 * 1000; // 30 mins
75
75
 
76
- // 1. Visitor ID (Persistent 1 Year)
76
+ // Visitor ID (Persistent 1 Year)
77
77
  if (!localStorage.getItem('senzor_vid')) {
78
78
  localStorage.setItem('senzor_vid', generateUUID());
79
79
  }
80
80
 
81
- // 2. Session ID
82
- // Create new if missing OR expired
83
- if (!localStorage.getItem('senzor_sid') || (now - lastActivity > sessionTimeout)) {
84
- localStorage.setItem('senzor_sid', generateUUID());
81
+ // Session ID
82
+ let sessionId = sessionStorage.getItem('senzor_sid');
83
+ const isExpired = (now - lastActivity > sessionTimeout);
84
+
85
+ // Session logic
86
+ if (!sessionId || isExpired) {
87
+ sessionId = generateUUID();
88
+ sessionStorage.setItem('senzor_sid', sessionId);
89
+ this.determineReferrer(true);
90
+ } else {
91
+ // Ongoing session: Check if external source changed
92
+ this.determineReferrer(false);
85
93
  }
86
94
 
87
- // Update Activity
88
95
  localStorage.setItem('senzor_last_activity', now.toString());
89
96
  }
90
97
 
98
+ private determineReferrer(isNewSession: boolean) {
99
+ const rawReferrer = document.referrer;
100
+ const currentHost = window.location.hostname;
101
+ let storedReferrer = sessionStorage.getItem('senzor_ref');
102
+
103
+ let isExternal = false;
104
+ if (rawReferrer) {
105
+ try {
106
+ const refUrl = new URL(rawReferrer);
107
+ // Compare hosts
108
+ if (refUrl.hostname !== currentHost) {
109
+ isExternal = true;
110
+ }
111
+ } catch (e) {
112
+ isExternal = true;
113
+ }
114
+ }
115
+
116
+ if (isExternal) {
117
+ // Always overwrite if it's a new external source
118
+ const cleanRef = this.normalizeUrl(rawReferrer);
119
+ // Only update if different to avoid redundant writes
120
+ if (cleanRef !== storedReferrer) {
121
+ sessionStorage.setItem('senzor_ref', cleanRef);
122
+ }
123
+ } else if (isNewSession && !storedReferrer) {
124
+ // New session with internal/no referrer = Direct
125
+ sessionStorage.setItem('senzor_ref', 'Direct');
126
+ }
127
+ }
128
+
91
129
  private getIds() {
92
- // Refresh activity timestamp on every hit
93
130
  localStorage.setItem('senzor_last_activity', Date.now().toString());
94
131
  return {
95
132
  visitorId: localStorage.getItem('senzor_vid') || 'unknown',
96
- sessionId: localStorage.getItem('senzor_sid') || 'unknown'
133
+ sessionId: sessionStorage.getItem('senzor_sid') || 'unknown',
134
+ referrer: sessionStorage.getItem('senzor_ref') || 'Direct'
97
135
  };
98
136
  }
99
137
 
100
138
  private trackPageView() {
101
- // Ensure session is valid before tracking
102
- this.checkSession();
139
+ this.manageSession();
103
140
  this.startTime = Date.now();
104
141
 
105
142
  const payload: Payload = {
@@ -108,9 +145,10 @@ class SenzorWebAgent {
108
145
  ...this.getIds(),
109
146
  url: window.location.href,
110
147
  path: window.location.pathname,
111
- referrer: document.referrer,
148
+ title: document.title,
112
149
  width: window.innerWidth,
113
150
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
151
+ referrer: this.getIds().referrer
114
152
  };
115
153
 
116
154
  this.send(payload);
@@ -126,9 +164,10 @@ class SenzorWebAgent {
126
164
  ...this.getIds(),
127
165
  url: window.location.href,
128
166
  path: window.location.pathname,
129
- referrer: document.referrer,
167
+ title: document.title,
130
168
  width: window.innerWidth,
131
169
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
170
+ referrer: this.getIds().referrer,
132
171
  duration: duration
133
172
  };
134
173
 
@@ -158,9 +197,9 @@ class SenzorWebAgent {
158
197
  // SPA Support
159
198
  const originalPushState = history.pushState;
160
199
  history.pushState = (...args) => {
161
- this.trackPing(); // End previous page
200
+ this.trackPing();
162
201
  originalPushState.apply(history, args);
163
- this.trackPageView(); // Start new page
202
+ this.trackPageView();
164
203
  };
165
204
 
166
205
  window.addEventListener('popstate', () => {
@@ -175,7 +214,7 @@ class SenzorWebAgent {
175
214
  } else {
176
215
  // User returned, restart timer (don't count background time)
177
216
  this.startTime = Date.now();
178
- this.checkSession(); // Verify session hasn't expired while tab was hidden
217
+ this.manageSession();
179
218
  }
180
219
  });
181
220