@senzops/web 1.0.0 → 1.1.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 +22 -21
- package/dist/index.d.ts +22 -21
- package/dist/index.global.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/src/index.ts +46 -38
package/dist/index.d.mts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
interface Config {
|
|
2
|
-
webId: string;
|
|
3
|
-
endpoint?: string;
|
|
4
|
-
}
|
|
5
|
-
declare class SenzorWebAgent {
|
|
6
|
-
private config;
|
|
7
|
-
private startTime;
|
|
8
|
-
private endpoint;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
private
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
interface Config {
|
|
2
|
+
webId: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
}
|
|
5
|
+
declare class SenzorWebAgent {
|
|
6
|
+
private config;
|
|
7
|
+
private startTime;
|
|
8
|
+
private endpoint;
|
|
9
|
+
private initialized;
|
|
10
|
+
constructor();
|
|
11
|
+
init(config: Config): void;
|
|
12
|
+
private checkSession;
|
|
13
|
+
private getIds;
|
|
14
|
+
private trackPageView;
|
|
15
|
+
private trackPing;
|
|
16
|
+
private send;
|
|
17
|
+
private fallbackSend;
|
|
18
|
+
private setupListeners;
|
|
19
|
+
}
|
|
20
|
+
declare const Senzor: SenzorWebAgent;
|
|
21
|
+
|
|
22
|
+
export { Senzor };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
interface Config {
|
|
2
|
-
webId: string;
|
|
3
|
-
endpoint?: string;
|
|
4
|
-
}
|
|
5
|
-
declare class SenzorWebAgent {
|
|
6
|
-
private config;
|
|
7
|
-
private startTime;
|
|
8
|
-
private endpoint;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
private
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
interface Config {
|
|
2
|
+
webId: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
}
|
|
5
|
+
declare class SenzorWebAgent {
|
|
6
|
+
private config;
|
|
7
|
+
private startTime;
|
|
8
|
+
private endpoint;
|
|
9
|
+
private initialized;
|
|
10
|
+
constructor();
|
|
11
|
+
init(config: Config): void;
|
|
12
|
+
private checkSession;
|
|
13
|
+
private getIds;
|
|
14
|
+
private trackPageView;
|
|
15
|
+
private trackPing;
|
|
16
|
+
private send;
|
|
17
|
+
private fallbackSend;
|
|
18
|
+
private setupListeners;
|
|
19
|
+
}
|
|
20
|
+
declare const Senzor: SenzorWebAgent;
|
|
21
|
+
|
|
22
|
+
export { Senzor };
|
package/dist/index.global.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{function
|
|
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);})();
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
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});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
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};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Remove the external uuid import causing the crash
|
|
2
|
-
// import { v4 as uuidv4 } from 'uuid';
|
|
3
|
-
|
|
4
1
|
interface Config {
|
|
5
2
|
webId: string;
|
|
6
3
|
endpoint?: string;
|
|
@@ -16,16 +13,14 @@ interface Payload {
|
|
|
16
13
|
referrer: string;
|
|
17
14
|
width: number;
|
|
18
15
|
timezone: string;
|
|
19
|
-
duration?: number;
|
|
16
|
+
duration?: number;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
//
|
|
19
|
+
// Browser-Native UUID (No Node.js dependencies)
|
|
23
20
|
function generateUUID(): string {
|
|
24
|
-
// Use native crypto API if available (Modern Browsers)
|
|
25
21
|
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
26
22
|
return crypto.randomUUID();
|
|
27
23
|
}
|
|
28
|
-
// Fallback for older environments
|
|
29
24
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
30
25
|
const r = (Math.random() * 16) | 0;
|
|
31
26
|
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
@@ -37,58 +32,76 @@ class SenzorWebAgent {
|
|
|
37
32
|
private config: Config;
|
|
38
33
|
private startTime: number;
|
|
39
34
|
private endpoint: string;
|
|
35
|
+
private initialized: boolean;
|
|
40
36
|
|
|
41
37
|
constructor() {
|
|
42
38
|
this.config = { webId: '', endpoint: 'https://api.senzor.dev/api/ingest/web' };
|
|
43
39
|
this.startTime = Date.now();
|
|
44
40
|
this.endpoint = '';
|
|
41
|
+
this.initialized = false;
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
public init(config: Config) {
|
|
45
|
+
if (this.initialized) {
|
|
46
|
+
console.warn('[Senzor] Agent already initialized.');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this.initialized = true;
|
|
50
|
+
|
|
48
51
|
this.config = { ...this.config, ...config };
|
|
49
|
-
// Allow overriding endpoint for self-hosting or dev
|
|
50
52
|
this.endpoint = this.config.endpoint || 'https://api.senzor.dev/api/ingest/web';
|
|
51
53
|
|
|
52
54
|
if (!this.config.webId) {
|
|
53
|
-
console.error('[Senzor] WebId is required
|
|
55
|
+
console.error('[Senzor] WebId is required.');
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
// 1.
|
|
58
|
-
this.
|
|
59
|
+
// 1. Manage Session State
|
|
60
|
+
this.checkSession();
|
|
59
61
|
|
|
60
|
-
// 2. Track
|
|
62
|
+
// 2. Track initial load
|
|
61
63
|
this.trackPageView();
|
|
62
64
|
|
|
63
65
|
// 3. Setup Listeners
|
|
64
66
|
this.setupListeners();
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
// --- Standard Analytics Session Logic ---
|
|
70
|
+
// A session ends after 30 minutes of inactivity.
|
|
71
|
+
private checkSession() {
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
const lastActivity = parseInt(localStorage.getItem('senzor_last_activity') || '0', 10);
|
|
74
|
+
const sessionTimeout = 30 * 60 * 1000; // 30 mins
|
|
75
|
+
|
|
76
|
+
// 1. Visitor ID (Persistent 1 Year)
|
|
77
|
+
if (!localStorage.getItem('senzor_vid')) {
|
|
78
|
+
localStorage.setItem('senzor_vid', generateUUID());
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
// Session ID
|
|
76
|
-
|
|
77
|
-
if (!
|
|
78
|
-
|
|
79
|
-
sessionStorage.setItem('senzor_sid', sid!);
|
|
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());
|
|
80
85
|
}
|
|
86
|
+
|
|
87
|
+
// Update Activity
|
|
88
|
+
localStorage.setItem('senzor_last_activity', now.toString());
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
private getIds() {
|
|
92
|
+
// Refresh activity timestamp on every hit
|
|
93
|
+
localStorage.setItem('senzor_last_activity', Date.now().toString());
|
|
84
94
|
return {
|
|
85
95
|
visitorId: localStorage.getItem('senzor_vid') || 'unknown',
|
|
86
|
-
sessionId:
|
|
96
|
+
sessionId: localStorage.getItem('senzor_sid') || 'unknown'
|
|
87
97
|
};
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
private trackPageView() {
|
|
91
|
-
|
|
101
|
+
// Ensure session is valid before tracking
|
|
102
|
+
this.checkSession();
|
|
103
|
+
this.startTime = Date.now();
|
|
104
|
+
|
|
92
105
|
const payload: Payload = {
|
|
93
106
|
type: 'pageview',
|
|
94
107
|
webId: this.config.webId,
|
|
@@ -103,10 +116,9 @@ class SenzorWebAgent {
|
|
|
103
116
|
this.send(payload);
|
|
104
117
|
}
|
|
105
118
|
|
|
106
|
-
// Captures time spent on page when user leaves or hides tab
|
|
107
119
|
private trackPing() {
|
|
108
120
|
const duration = Math.floor((Date.now() - this.startTime) / 1000);
|
|
109
|
-
if (duration < 1) return;
|
|
121
|
+
if (duration < 1) return;
|
|
110
122
|
|
|
111
123
|
const payload: Payload = {
|
|
112
124
|
type: 'ping',
|
|
@@ -124,10 +136,8 @@ class SenzorWebAgent {
|
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
private send(data: Payload) {
|
|
127
|
-
// Use sendBeacon for reliability during unload, fallback to fetch
|
|
128
139
|
if (navigator.sendBeacon) {
|
|
129
140
|
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
|
|
130
|
-
// sendBeacon returns false if it fails (e.g. payload too large)
|
|
131
141
|
const success = navigator.sendBeacon(this.endpoint, blob);
|
|
132
142
|
if (!success) this.fallbackSend(data);
|
|
133
143
|
} else {
|
|
@@ -141,16 +151,16 @@ class SenzorWebAgent {
|
|
|
141
151
|
body: JSON.stringify(data),
|
|
142
152
|
keepalive: true,
|
|
143
153
|
headers: { 'Content-Type': 'application/json' }
|
|
144
|
-
}).catch(err => console.error('[Senzor]
|
|
154
|
+
}).catch(err => console.error('[Senzor] Telemetry Error:', err));
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
private setupListeners() {
|
|
148
|
-
//
|
|
158
|
+
// SPA Support
|
|
149
159
|
const originalPushState = history.pushState;
|
|
150
160
|
history.pushState = (...args) => {
|
|
151
|
-
this.trackPing(); //
|
|
161
|
+
this.trackPing(); // End previous page
|
|
152
162
|
originalPushState.apply(history, args);
|
|
153
|
-
this.trackPageView(); //
|
|
163
|
+
this.trackPageView(); // Start new page
|
|
154
164
|
};
|
|
155
165
|
|
|
156
166
|
window.addEventListener('popstate', () => {
|
|
@@ -158,27 +168,25 @@ class SenzorWebAgent {
|
|
|
158
168
|
this.trackPageView();
|
|
159
169
|
});
|
|
160
170
|
|
|
161
|
-
//
|
|
171
|
+
// Visibility & Unload
|
|
162
172
|
document.addEventListener('visibilitychange', () => {
|
|
163
173
|
if (document.visibilityState === 'hidden') {
|
|
164
174
|
this.trackPing();
|
|
165
175
|
} else {
|
|
166
|
-
// User
|
|
176
|
+
// User returned, restart timer (don't count background time)
|
|
167
177
|
this.startTime = Date.now();
|
|
178
|
+
this.checkSession(); // Verify session hasn't expired while tab was hidden
|
|
168
179
|
}
|
|
169
180
|
});
|
|
170
181
|
|
|
171
|
-
// 3. Before Unload (Closing tab)
|
|
172
182
|
window.addEventListener('beforeunload', () => {
|
|
173
183
|
this.trackPing();
|
|
174
184
|
});
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
187
|
|
|
178
|
-
// Export Singleton
|
|
179
188
|
export const Senzor = new SenzorWebAgent();
|
|
180
189
|
|
|
181
|
-
// Allow window access for script tag usage
|
|
182
190
|
if (typeof window !== 'undefined') {
|
|
183
191
|
(window as any).Senzor = Senzor;
|
|
184
192
|
}
|