@faststats/web 0.1.0 → 0.1.1
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/CHANGELOG.md +6 -0
- package/dist/module.d.ts +0 -2
- package/dist/module.js +1 -1
- package/package.json +6 -3
- package/src/analytics.ts +6 -27
package/CHANGELOG.md
CHANGED
package/dist/module.d.ts
CHANGED
|
@@ -404,11 +404,9 @@ interface ErrorTrackingConfig {
|
|
|
404
404
|
enabled?: boolean;
|
|
405
405
|
}
|
|
406
406
|
interface WebVitalsConfig {
|
|
407
|
-
enabled?: boolean;
|
|
408
407
|
sampling?: SamplingOptions;
|
|
409
408
|
}
|
|
410
409
|
interface SessionReplayConfig {
|
|
411
|
-
enabled?: boolean;
|
|
412
410
|
sampling?: SamplingOptions;
|
|
413
411
|
}
|
|
414
412
|
type ConsentMode = "pending" | "granted" | "denied";
|
package/dist/module.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./types-E07i1PxI.js";function t(e){return e?``:n()}function n(){if(typeof localStorage>`u`)return``;let e=localStorage.getItem(`faststats_anon_id`);if(e)return e;let t=crypto.randomUUID();return localStorage.setItem(`faststats_anon_id`,t),t}function r(e){return e||typeof localStorage>`u`?``:(localStorage.removeItem(`faststats_anon_id`),n())}function i(){if(typeof sessionStorage>`u`)return``;let e=sessionStorage.getItem(`session_id`),t=sessionStorage.getItem(`session_timestamp`);if(e&&t){if(Date.now()-Number.parseInt(t,10)<18e5)return sessionStorage.setItem(`session_timestamp`,Date.now().toString()),e;sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`)}let n=Date.now().toString(),r=crypto.randomUUID();return sessionStorage.setItem(`session_id`,r),sessionStorage.setItem(`session_timestamp`,n),sessionStorage.setItem(`session_start`,n),r}function a(){return typeof sessionStorage>`u`?``:(sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`),i())}function o(){typeof sessionStorage>`u`||sessionStorage.getItem(`session_id`)&&sessionStorage.setItem(`session_timestamp`,Date.now().toString())}function s(){if(typeof sessionStorage>`u`)return Date.now();let e=sessionStorage.getItem(`session_start`);if(e)return Number.parseInt(e,10);let t=sessionStorage.getItem(`session_timestamp`);return t?Number.parseInt(t,10):Date.now()}let c=null;function l(){return c}function u(e,t){typeof window>`u`||g()||c?.track(e,t??{})}function d(e,t,n){typeof window>`u`||g()||c?.identify(e,t,n??{})}function f(e=!0){typeof window>`u`||g()||c?.logout(e)}function p(e){c?.setConsentMode(e)}function m(){p(`granted`)}function h(){p(`denied`)}function g(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function _(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`}=e,s=n instanceof Blob?n:new Blob([n],{type:r});if(navigator.sendBeacon?.(t,s))return a&&console.log(`${o} ✓ Sent via beacon`),!0;try{let e=await fetch(t,{method:`POST`,body:n,headers:{"Content-Type":r,...i},keepalive:!0}),s=e.ok;return a&&(s?console.log(`${o} ✓ Sent via fetch`):console.warn(`${o} ✗ Failed: ${e.status}`)),s}catch{return a&&console.warn(`${o} ✗ Failed to send`),!1}}function v(e){for(;e
|
|
1
|
+
import{t as e}from"./types-E07i1PxI.js";function t(e){return e?``:n()}function n(){if(typeof localStorage>`u`)return``;let e=localStorage.getItem(`faststats_anon_id`);if(e)return e;let t=crypto.randomUUID();return localStorage.setItem(`faststats_anon_id`,t),t}function r(e){return e||typeof localStorage>`u`?``:(localStorage.removeItem(`faststats_anon_id`),n())}function i(){if(typeof sessionStorage>`u`)return``;let e=sessionStorage.getItem(`session_id`),t=sessionStorage.getItem(`session_timestamp`);if(e&&t){if(Date.now()-Number.parseInt(t,10)<18e5)return sessionStorage.setItem(`session_timestamp`,Date.now().toString()),e;sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`)}let n=Date.now().toString(),r=crypto.randomUUID();return sessionStorage.setItem(`session_id`,r),sessionStorage.setItem(`session_timestamp`,n),sessionStorage.setItem(`session_start`,n),r}function a(){return typeof sessionStorage>`u`?``:(sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`),i())}function o(){typeof sessionStorage>`u`||sessionStorage.getItem(`session_id`)&&sessionStorage.setItem(`session_timestamp`,Date.now().toString())}function s(){if(typeof sessionStorage>`u`)return Date.now();let e=sessionStorage.getItem(`session_start`);if(e)return Number.parseInt(e,10);let t=sessionStorage.getItem(`session_timestamp`);return t?Number.parseInt(t,10):Date.now()}let c=null;function l(){return c}function u(e,t){typeof window>`u`||g()||c?.track(e,t??{})}function d(e,t,n){typeof window>`u`||g()||c?.identify(e,t,n??{})}function f(e=!0){typeof window>`u`||g()||c?.logout(e)}function p(e){c?.setConsentMode(e)}function m(){p(`granted`)}function h(){p(`denied`)}function g(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function _(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`}=e,s=n instanceof Blob?n:new Blob([n],{type:r});if(navigator.sendBeacon?.(t,s))return a&&console.log(`${o} ✓ Sent via beacon`),!0;try{let e=await fetch(t,{method:`POST`,body:n,headers:{"Content-Type":r,...i},keepalive:!0}),s=e.ok;return a&&(s?console.log(`${o} ✓ Sent via fetch`):console.warn(`${o} ✗ Failed: ${e.status}`)),s}catch{return a&&console.warn(`${o} ✗ Failed to send`),!1}}function v(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function y(){let e={};if(!location.search)return e;let t=new URLSearchParams(location.search);for(let n of[`utm_source`,`utm_medium`,`utm_campaign`,`utm_term`,`utm_content`]){let r=t.get(n);r&&(e[n]=r)}return e}var b=class{endpoint;debug;started=!1;pageKey=``;navTimer=null;heartbeatTimer=null;scrollDepth=0;pageEntryTime=0;pagePath=``;pageUrl=``;pageHash=``;hasLeftCurrentPage=!1;scrollHandler=null;consentMode;cookielessWhilePending;constructor(e){this.options=e,this.endpoint=e.endpoint??`https://metrics.faststats.dev/v1/web`,this.debug=e.debug??!1,this.consentMode=e.consent?.mode??`granted`,this.cookielessWhilePending=e.consent?.cookielessWhilePending??!0,(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(g()){this.log(`disabled`);return}c=this,setTimeout(()=>void this.start(),0)}}async start(){if(this.started||typeof window>`u`)return;if(c&&c!==this){this.log(`already started by another instance`);return}if(g()){this.log(`disabled`);return}this.started=!0,c=this;let n=()=>t(this.isCookielessMode()),r=this.options;if(r.errorTracking?.enabled??r.trackErrors){let{default:e}=await import(`./error-DtTBFJh_.js`);new e({siteKey:r.siteKey,endpoint:this.endpoint,debug:this.debug,getCommonData:()=>({url:location.href,page:location.pathname,referrer:document.referrer||null}),getAnonymousId:n,getSessionId:i,sendData:_}).start(),this.log(`error loaded`)}if(r.trackWebVitals){let{default:t}=await import(`./web-vitals-CUXDm9A3.js`);new t({siteKey:r.siteKey,endpoint:this.endpoint,debug:this.debug,samplingPercentage:e(r.webVitals?.sampling?.percentage),getSessionId:i}).start(),this.log(`web-vitals loaded`)}if(r.trackReplay){let{default:e}=await import(`./replay-ZmimkIBT.js`);new e({siteKey:r.siteKey,endpoint:this.endpoint,debug:this.debug,getAnonymousId:n,getSessionId:i,sendData:_}).start(),this.log(`replay loaded`)}this.enterPage(),this.pageview({trigger:`load`}),this.links(),this.trackScroll(),this.startHeartbeat(),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`?this.leavePage():this.startHeartbeat()}),window.addEventListener(`pagehide`,()=>this.leavePage()),window.addEventListener(`popstate`,()=>this.navigate()),r.trackHash&&window.addEventListener(`hashchange`,()=>this.navigate()),this.patch()}pageview(e={}){let t=`${location.pathname}|${this.options.trackHash??!1?location.hash:``}`;t!==this.pageKey&&(this.pageKey=t,this.send(`pageview`,e))}track(e,t={}){this.send(e,t)}identify(e,n,r={}){if(g()||this.isCookielessMode())return;let i=e.trim(),a=n.trim();!i||!a||_({url:this.endpoint.replace(/\/v1\/web$/,`/v1/identify`),data:JSON.stringify({token:this.options.siteKey,identifier:t(!1),externalId:i,email:a,name:r.name?.trim()||void 0,phone:r.phone?.trim()||void 0,avatarUrl:r.avatarUrl?.trim()||void 0,traits:r.traits??{}}),contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] identify`})}logout(e=!0){g()||(e&&r(this.isCookielessMode()),a())}setConsentMode(e){this.consentMode=e}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return t(this.isCookielessMode())}getSessionId(){return i()}isCookielessMode(){return this.options.cookieless||this.consentMode===`denied`?!0:this.consentMode===`pending`?this.cookielessWhilePending:!1}send(e,n={}){let r=t(this.isCookielessMode()),a=JSON.stringify({token:this.options.siteKey,...r?{userId:r}:{},sessionId:i(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...y(),...n}});this.log(e),_({url:this.endpoint,data:a,contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] ${e}`})}enterPage(){this.pageEntryTime=Date.now(),this.pagePath=location.pathname,this.pageUrl=location.href,this.pageHash=location.hash,this.scrollDepth=0,this.hasLeftCurrentPage=!1}leavePage(){if(this.hasLeftCurrentPage)return;this.hasLeftCurrentPage=!0;let e=Date.now();this.send(`page_leave`,{page:this.pagePath,url:this.pageUrl,time_on_page:e-this.pageEntryTime,scroll_depth:this.scrollDepth,session_duration:e-s()})}trackScroll(){this.scrollHandler&&window.removeEventListener(`scroll`,this.scrollHandler);let e=()=>{let e=document.documentElement,t=document.body,n=window.innerHeight,r=Math.max(e.scrollHeight,t.scrollHeight);if(r<=n){this.scrollDepth=100;return}let i=Math.min(100,Math.round(((window.scrollY||e.scrollTop)+n)/r*100));i>this.scrollDepth&&(this.scrollDepth=i)};this.scrollHandler=e,e(),window.addEventListener(`scroll`,e,{passive:!0})}startHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{if(document.visibilityState===`hidden`){clearInterval(this.heartbeatTimer),this.heartbeatTimer=null;return}o()},300*1e3)}navigate(){this.navTimer&&clearTimeout(this.navTimer),this.navTimer=setTimeout(()=>{this.navTimer=null;let e=location.pathname!==this.pagePath,t=(this.options.trackHash??!1)&&location.hash!==this.pageHash;!e&&!t||(this.leavePage(),this.enterPage(),this.trackScroll(),this.pageview({trigger:`navigation`}))},300)}patch(){let e=()=>this.navigate();for(let t of[`pushState`,`replaceState`]){let n=history[t];history[t]=function(...t){let r=n.apply(this,t);return e(),r}}}links(){let e=e=>{let t=v(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};document.addEventListener(`click`,e),document.addEventListener(`auxclick`,e)}};export{b as WebAnalytics,l as getInstance,d as identify,g as isTrackingDisabled,f as logout,m as optIn,h as optOut,_ as sendData,p as setConsentMode,u as trackEvent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststats/web",
|
|
3
|
-
"repository":
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "https://github.com/faststats-dev/web-analytics.git"
|
|
6
|
+
},
|
|
4
7
|
"main": "dist/module.js",
|
|
5
8
|
"module": "dist/module.js",
|
|
6
9
|
"types": "dist/module.d.ts",
|
|
@@ -12,11 +15,11 @@
|
|
|
12
15
|
}
|
|
13
16
|
},
|
|
14
17
|
"type": "module",
|
|
15
|
-
"sideEffects":
|
|
18
|
+
"sideEffects": true,
|
|
16
19
|
"publishConfig": {
|
|
17
20
|
"access": "public"
|
|
18
21
|
},
|
|
19
|
-
"version": "0.1.
|
|
22
|
+
"version": "0.1.1",
|
|
20
23
|
"scripts": {
|
|
21
24
|
"build": "tsdown",
|
|
22
25
|
"dev": "bun run build",
|
package/src/analytics.ts
CHANGED
|
@@ -108,13 +108,11 @@ export async function sendData(options: SendDataOptions): Promise<boolean> {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function getLinkEl(el: Node | null): HTMLAnchorElement | null {
|
|
111
|
-
while (
|
|
112
|
-
el &&
|
|
113
|
-
!((el as Element).tagName === "A" && (el as HTMLAnchorElement).href)
|
|
114
|
-
) {
|
|
111
|
+
while (el) {
|
|
112
|
+
if (el instanceof HTMLAnchorElement && el.href) return el;
|
|
115
113
|
el = el.parentNode;
|
|
116
114
|
}
|
|
117
|
-
return
|
|
115
|
+
return null;
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
function getUTM(): Record<string, string> {
|
|
@@ -145,12 +143,10 @@ interface ErrorTrackingConfig {
|
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
interface WebVitalsConfig {
|
|
148
|
-
enabled?: boolean;
|
|
149
146
|
sampling?: SamplingOptions;
|
|
150
147
|
}
|
|
151
148
|
|
|
152
149
|
interface SessionReplayConfig {
|
|
153
|
-
enabled?: boolean;
|
|
154
150
|
sampling?: SamplingOptions;
|
|
155
151
|
}
|
|
156
152
|
|
|
@@ -222,9 +218,7 @@ export class WebAnalytics {
|
|
|
222
218
|
return;
|
|
223
219
|
}
|
|
224
220
|
_instance = this;
|
|
225
|
-
|
|
226
|
-
window.requestIdleCallback(() => void this.start());
|
|
227
|
-
else setTimeout(() => void this.start(), 1);
|
|
221
|
+
setTimeout(() => void this.start(), 0);
|
|
228
222
|
}
|
|
229
223
|
|
|
230
224
|
async start(): Promise<void> {
|
|
@@ -242,11 +236,6 @@ export class WebAnalytics {
|
|
|
242
236
|
|
|
243
237
|
const getAnonymousIdFn = () => getAnonymousId(this.isCookielessMode());
|
|
244
238
|
const opts = this.options;
|
|
245
|
-
const hasWebVitalsSampling =
|
|
246
|
-
opts.webVitals?.sampling?.percentage !== undefined;
|
|
247
|
-
const hasReplaySampling =
|
|
248
|
-
opts.replayOptions?.samplingPercentage !== undefined ||
|
|
249
|
-
opts.sessionReplays?.sampling?.percentage !== undefined;
|
|
250
239
|
|
|
251
240
|
if (opts.errorTracking?.enabled ?? opts.trackErrors) {
|
|
252
241
|
const { default: ErrorTracker } = await import("./error");
|
|
@@ -265,11 +254,7 @@ export class WebAnalytics {
|
|
|
265
254
|
}).start();
|
|
266
255
|
this.log("error loaded");
|
|
267
256
|
}
|
|
268
|
-
if (
|
|
269
|
-
opts.trackWebVitals ??
|
|
270
|
-
opts.webVitals?.enabled ??
|
|
271
|
-
hasWebVitalsSampling
|
|
272
|
-
) {
|
|
257
|
+
if (opts.trackWebVitals) {
|
|
273
258
|
const { default: WebVitalsTracker } = await import("./web-vitals");
|
|
274
259
|
new WebVitalsTracker({
|
|
275
260
|
siteKey: opts.siteKey,
|
|
@@ -282,18 +267,12 @@ export class WebAnalytics {
|
|
|
282
267
|
}).start();
|
|
283
268
|
this.log("web-vitals loaded");
|
|
284
269
|
}
|
|
285
|
-
if (opts.
|
|
270
|
+
if (opts.trackReplay) {
|
|
286
271
|
const { default: ReplayTracker } = await import("./replay");
|
|
287
|
-
const replayOpts = opts.replayOptions ?? {};
|
|
288
272
|
new ReplayTracker({
|
|
289
273
|
siteKey: opts.siteKey,
|
|
290
274
|
endpoint: this.endpoint,
|
|
291
275
|
debug: this.debug,
|
|
292
|
-
...replayOpts,
|
|
293
|
-
samplingPercentage: normalizeSamplingPercentage(
|
|
294
|
-
replayOpts.samplingPercentage ??
|
|
295
|
-
opts.sessionReplays?.sampling?.percentage,
|
|
296
|
-
),
|
|
297
276
|
getAnonymousId: getAnonymousIdFn,
|
|
298
277
|
getSessionId: getOrCreateSessionId,
|
|
299
278
|
sendData,
|