@faststats/web 0.2.10 → 0.2.12
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 +13 -0
- package/dist/chunks/error-CttYL43D.js +2 -0
- package/dist/chunks/identifiers-CQeWm7wi.js +1 -0
- package/dist/chunks/replay-BrMLCiBF.js +1 -0
- package/dist/chunks/{replay-IhsP2Ab4.d.ts → replay-BuX3_0xs.d.ts} +1 -0
- package/dist/chunks/send-data-DL_GlsQw.js +1 -0
- package/dist/chunks/types-CYzR5xtT.js +1 -0
- package/dist/chunks/web-vitals-CjA1bFLG.js +1 -0
- package/dist/error.d.ts +3 -0
- package/dist/error.js +1 -1
- package/dist/feature-flags.d.ts +1 -1
- package/dist/feature-flags.js +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +1 -1
- package/dist/replay.d.ts +1 -1
- package/dist/replay.js +1 -1
- package/dist/web-vitals.d.ts +13 -3
- package/dist/web-vitals.js +1 -1
- package/package.json +1 -1
- package/src/analytics.ts +57 -115
- package/src/error.ts +38 -16
- package/src/replay.ts +13 -2
- package/src/utils/identifiers.ts +38 -26
- package/src/utils/send-data.ts +52 -0
- package/src/web-vitals.ts +157 -49
- package/tests/analytics.test.ts +35 -0
- package/tests/identifiers.test.ts +18 -0
- package/tests/replay.test.ts +25 -0
- package/tests/web-vitals.test.ts +208 -0
- package/dist/chunks/analytics-ur06JW5D.js +0 -1
- package/dist/chunks/error-ClMugucs.js +0 -2
- package/dist/chunks/replay-DQHUrcod.js +0 -1
- package/dist/chunks/types-C3vW7XGe.js +0 -1
- package/dist/chunks/web-vitals-BooBPWJC.js +0 -1
- /package/dist/chunks/{feature-flags-6rZlmhfu.d.ts → feature-flags-BxZz_lNm.d.ts} +0 -0
- /package/dist/chunks/{feature-flags-CqVtrpX2.js → feature-flags-CjnLZGxp.js} +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import WebVitalsTracker from "../src/web-vitals";
|
|
3
|
+
|
|
4
|
+
type FetchCall = {
|
|
5
|
+
url: string;
|
|
6
|
+
body?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type Harness = {
|
|
10
|
+
fetchCalls: FetchCall[];
|
|
11
|
+
fetchStatuses: number[];
|
|
12
|
+
restore(): void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function setGlobal(name: keyof typeof globalThis, value: unknown): void {
|
|
16
|
+
Object.defineProperty(globalThis, name, {
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function setupBrowser(): Harness {
|
|
24
|
+
const originalValues = {
|
|
25
|
+
window: globalThis.window,
|
|
26
|
+
document: globalThis.document,
|
|
27
|
+
location: globalThis.location,
|
|
28
|
+
fetch: globalThis.fetch,
|
|
29
|
+
sessionStorage: globalThis.sessionStorage,
|
|
30
|
+
};
|
|
31
|
+
const fetchCalls: FetchCall[] = [];
|
|
32
|
+
const fetchStatuses: number[] = [];
|
|
33
|
+
const listeners = new Map<string, Set<EventListenerOrEventListenerObject>>();
|
|
34
|
+
const addEventListener = (
|
|
35
|
+
type: string,
|
|
36
|
+
listener: EventListenerOrEventListenerObject,
|
|
37
|
+
): void => {
|
|
38
|
+
const existing = listeners.get(type) ?? new Set();
|
|
39
|
+
existing.add(listener);
|
|
40
|
+
listeners.set(type, existing);
|
|
41
|
+
};
|
|
42
|
+
const removeEventListener = (
|
|
43
|
+
type: string,
|
|
44
|
+
listener: EventListenerOrEventListenerObject,
|
|
45
|
+
): void => {
|
|
46
|
+
listeners.get(type)?.delete(listener);
|
|
47
|
+
};
|
|
48
|
+
const location = {
|
|
49
|
+
href: "https://example.com/",
|
|
50
|
+
};
|
|
51
|
+
const storage = new Map<string, string>();
|
|
52
|
+
const sessionStorage = {
|
|
53
|
+
getItem: (key: string) => storage.get(key) ?? null,
|
|
54
|
+
setItem: (key: string, value: string) => storage.set(key, value),
|
|
55
|
+
removeItem: (key: string) => storage.delete(key),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
setGlobal("location", location as Location);
|
|
59
|
+
setGlobal("document", {
|
|
60
|
+
addEventListener,
|
|
61
|
+
removeEventListener,
|
|
62
|
+
visibilityState: "visible",
|
|
63
|
+
} as unknown as Document);
|
|
64
|
+
setGlobal("window", {
|
|
65
|
+
addEventListener,
|
|
66
|
+
removeEventListener,
|
|
67
|
+
location,
|
|
68
|
+
setTimeout,
|
|
69
|
+
clearTimeout,
|
|
70
|
+
} as unknown as Window & typeof globalThis);
|
|
71
|
+
setGlobal("sessionStorage", sessionStorage);
|
|
72
|
+
setGlobal("fetch", (async (
|
|
73
|
+
url: string | URL | Request,
|
|
74
|
+
init?: RequestInit,
|
|
75
|
+
): Promise<Response> => {
|
|
76
|
+
fetchCalls.push({
|
|
77
|
+
url: String(url),
|
|
78
|
+
body: typeof init?.body === "string" ? init.body : undefined,
|
|
79
|
+
});
|
|
80
|
+
return new Response("", { status: fetchStatuses.shift() ?? 204 });
|
|
81
|
+
}) as typeof fetch);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
fetchCalls,
|
|
85
|
+
fetchStatuses,
|
|
86
|
+
restore() {
|
|
87
|
+
setGlobal("window", originalValues.window);
|
|
88
|
+
setGlobal("document", originalValues.document);
|
|
89
|
+
setGlobal("location", originalValues.location);
|
|
90
|
+
setGlobal("fetch", originalValues.fetch);
|
|
91
|
+
setGlobal("sessionStorage", originalValues.sessionStorage);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function captureMetric(
|
|
97
|
+
tracker: WebVitalsTracker,
|
|
98
|
+
metric: Parameters<(metric: unknown) => void>[0],
|
|
99
|
+
): void {
|
|
100
|
+
(
|
|
101
|
+
tracker as unknown as {
|
|
102
|
+
captureMetric(metric: unknown): void;
|
|
103
|
+
}
|
|
104
|
+
).captureMetric(metric);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let harness: Harness;
|
|
108
|
+
|
|
109
|
+
beforeEach(() => {
|
|
110
|
+
harness = setupBrowser();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
afterEach(() => {
|
|
114
|
+
harness.restore();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("WebVitalsTracker", () => {
|
|
118
|
+
test("flushes the current vitals bucket when the SPA route changes", async () => {
|
|
119
|
+
const tracker = new WebVitalsTracker({
|
|
120
|
+
siteKey: "site_test",
|
|
121
|
+
baseUrl: "https://analytics.example.com",
|
|
122
|
+
samplingPercentage: 100,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
tracker.start();
|
|
126
|
+
captureMetric(tracker, {
|
|
127
|
+
name: "CLS",
|
|
128
|
+
value: 0.12,
|
|
129
|
+
id: "v1",
|
|
130
|
+
rating: "needs-improvement",
|
|
131
|
+
delta: 0.12,
|
|
132
|
+
navigationType: "navigate",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
tracker.trackPageChange("https://example.com/about");
|
|
136
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
137
|
+
|
|
138
|
+
expect(harness.fetchCalls.length).toBe(1);
|
|
139
|
+
const firstPayload = JSON.parse(harness.fetchCalls[0]?.body ?? "{}") as {
|
|
140
|
+
metadata?: { url?: string };
|
|
141
|
+
vitals?: Array<{ metric?: string }>;
|
|
142
|
+
};
|
|
143
|
+
expect(firstPayload.metadata?.url).toBe("https://example.com/");
|
|
144
|
+
expect(firstPayload.vitals?.[0]?.metric).toBe("CLS");
|
|
145
|
+
|
|
146
|
+
captureMetric(tracker, {
|
|
147
|
+
name: "INP",
|
|
148
|
+
value: 32,
|
|
149
|
+
id: "v2",
|
|
150
|
+
rating: "good",
|
|
151
|
+
delta: 32,
|
|
152
|
+
navigationType: "navigate",
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
tracker.stop();
|
|
156
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
157
|
+
|
|
158
|
+
expect(harness.fetchCalls.length).toBe(2);
|
|
159
|
+
const secondPayload = JSON.parse(harness.fetchCalls[1]?.body ?? "{}") as {
|
|
160
|
+
metadata?: { url?: string };
|
|
161
|
+
vitals?: Array<{ metric?: string }>;
|
|
162
|
+
};
|
|
163
|
+
expect(secondPayload.metadata?.url).toBe("https://example.com/about");
|
|
164
|
+
expect(secondPayload.vitals?.[0]?.metric).toBe("INP");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("keeps metrics buffered when a route flush fails", async () => {
|
|
168
|
+
harness.fetchStatuses.push(500, 204, 204);
|
|
169
|
+
const tracker = new WebVitalsTracker({
|
|
170
|
+
siteKey: "site_test",
|
|
171
|
+
baseUrl: "https://analytics.example.com",
|
|
172
|
+
samplingPercentage: 100,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
tracker.start();
|
|
176
|
+
captureMetric(tracker, {
|
|
177
|
+
name: "CLS",
|
|
178
|
+
value: 0.12,
|
|
179
|
+
id: "v1",
|
|
180
|
+
rating: "needs-improvement",
|
|
181
|
+
delta: 0.12,
|
|
182
|
+
navigationType: "navigate",
|
|
183
|
+
});
|
|
184
|
+
tracker.trackPageChange("https://example.com/about");
|
|
185
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
186
|
+
|
|
187
|
+
expect(harness.fetchCalls.length).toBe(1);
|
|
188
|
+
|
|
189
|
+
captureMetric(tracker, {
|
|
190
|
+
name: "INP",
|
|
191
|
+
value: 32,
|
|
192
|
+
id: "v2",
|
|
193
|
+
rating: "good",
|
|
194
|
+
delta: 32,
|
|
195
|
+
navigationType: "navigate",
|
|
196
|
+
});
|
|
197
|
+
tracker.trackPageChange("https://example.com/contact");
|
|
198
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
199
|
+
|
|
200
|
+
expect(harness.fetchCalls.length).toBe(3);
|
|
201
|
+
const retriedPayload = JSON.parse(harness.fetchCalls[1]?.body ?? "{}") as {
|
|
202
|
+
metadata?: { url?: string };
|
|
203
|
+
vitals?: Array<{ metric?: string }>;
|
|
204
|
+
};
|
|
205
|
+
expect(retriedPayload.metadata?.url).toBe("https://example.com/");
|
|
206
|
+
expect(retriedPayload.vitals?.[0]?.metric).toBe("CLS");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e,n as t,o as n,r}from"./api-urls-BrkcoElX.js";import{a as i,c as a,i as o,n as s,o as c,r as l,s as u,t as d}from"./types-C3vW7XGe.js";function f(e){return e.replayOptions?.samplingPercentage!==void 0||e.sessionReplays?.sampling?.percentage!==void 0}function p(e,t,n){if(!(e.sessionReplays?.enabled??e.trackReplay??f(e)))return null;let r=e.replayOptions??{};return{siteKey:e.siteKey,baseUrl:t,debug:n,...r,samplingPercentage:d(r.samplingPercentage??e.sessionReplays?.sampling?.percentage)}}const m={instance:null,pendingConsentMode:void 0};function h(){return m.instance}function g(e,t){typeof window>`u`||C()||m.instance?.track(e,t??{})}function _(e,t,n){return typeof window>`u`||C()?Promise.resolve(!1):m.instance?.identify(e,t,n??{})??Promise.resolve(!1)}function v(e=!0){typeof window>`u`||C()||m.instance?.logout(e)}function y(e){if(m.instance){m.instance.setConsentMode(e);return}m.pendingConsentMode=e}function b(){y(`granted`)}function x(){y(`denied`)}function S(e){typeof window>`u`||C()||m.instance?.reportError(e)}function C(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function w(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`,useBeacon:s=!0,keepalive:c=!0}=e;if(s&&typeof navigator<`u`&&typeof navigator.sendBeacon==`function`)try{let e=n instanceof Blob?n:typeof Blob<`u`?new Blob([n],{type:r}):n;if(navigator.sendBeacon(t,e))return a&&console.log(`${o} Sent via beacon`),!0}catch{}if(typeof fetch!=`function`)return a&&console.warn(`${o} Failed to send`),!1;try{let e=await fetch(t,{method:`POST`,body:n,headers:{"Content-Type":r,...i},keepalive:c}),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 T(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function E(){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 D=class{webEndpoint;baseUrl;featureFlagsBaseUrl;debug;started=!1;destroyed=!1;pageKey=``;navTimer=null;heartbeatTimer=null;scrollDepth=0;pageEntryTime=0;pagePath=``;pageUrl=``;pageHash=``;hasLeftCurrentPage=!1;scrollHandler=null;consentMode;cookielessWhilePending;cleanupCallbacks=[];childTrackers=[];pendingReportedErrors=[];errorTracker=null;handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.leavePage(),this.stopHeartbeat()):this.startHeartbeat()};handlePageHide=()=>{this.leavePage()};handlePopState=()=>{this.navigate()};handleHashChange=()=>{this.navigate()};constructor(t){this.options=t,this.baseUrl=r(t.baseUrl),this.featureFlagsBaseUrl=e(t.featureFlagsBaseUrl),this.webEndpoint=n(this.baseUrl),this.debug=t.debug??!1,this.consentMode=t.consent?.mode??`granted`,this.cookielessWhilePending=t.consent?.cookielessWhilePending??!0,m.pendingConsentMode!==void 0&&(this.consentMode=m.pendingConsentMode,m.pendingConsentMode=void 0),(t.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(C()){this.log(`disabled`);return}m.instance=this,a(this.isCookielessMode()),setTimeout(()=>void this.start(),0)}}registerCleanup(e){this.cleanupCallbacks.push(e)}addWindowListener(e,t){window.addEventListener(e,t),this.registerCleanup(()=>window.removeEventListener(e,t))}addDocumentListener(e,t){document.addEventListener(e,t),this.registerCleanup(()=>document.removeEventListener(e,t))}patchHistory(){let e=history.pushState.bind(history),t=history.replaceState.bind(history);history.pushState=(t,n,r)=>{e(t,n,r),this.navigate()},history.replaceState=(e,n,r)=>{t(e,n,r),this.navigate()},this.registerCleanup(()=>{history.pushState=e,history.replaceState=t})}ensureStarted(){return typeof window>`u`||this.destroyed||C()?!1:(this.started||this.start(),!0)}stopHeartbeat(){this.heartbeatTimer&&=(clearInterval(this.heartbeatTimer),null)}stopNavigationTimer(){this.navTimer&&=(clearTimeout(this.navTimer),null)}stopChildTrackers(){for(let e of this.childTrackers.splice(0))e.stop?.();this.errorTracker=null}registerChildTracker(e){return!this.started||this.destroyed||m.instance!==this?(e.stop?.(),!1):(this.childTrackers.push(e),!0)}async startErrorTracker(){try{let{default:e}=await import(`./error-ClMugucs.js`).then(e=>e.n);if(!this.started||this.destroyed||m.instance!==this)return;let t=new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,sdkName:this.options.sdkName,sdkVersion:this.options.sdkVersion});if(t.start(),!this.registerChildTracker(t))return;for(this.errorTracker=t;this.pendingReportedErrors.length>0;){let e=this.pendingReportedErrors.shift();e&&t.captureError(e)}this.log(`error loaded`)}catch(e){this.log(`failed to initialize error tracker: ${String(e)}`)}}async startWebVitalsTracker(){try{let{default:e}=await import(`./web-vitals-BooBPWJC.js`).then(e=>e.n);if(!this.started||this.destroyed||m.instance!==this)return;let t=new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:d(this.options.webVitals?.sampling?.percentage),attribution:this.options.webVitals?.attribution??!1});if(t.start(),!this.registerChildTracker(t))return;this.log(`web-vitals loaded`)}catch(e){this.log(`failed to initialize web-vitals tracker: ${String(e)}`)}}async startReplayTracker(e){try{let{default:t}=await import(`./replay-DQHUrcod.js`).then(e=>e.n);if(!this.started||this.destroyed||m.instance!==this)return;let n=new t(e);if(n.start(),!this.registerChildTracker(n))return;this.log(`replay loaded`)}catch(e){this.log(`failed to initialize replay tracker: ${String(e)}`)}}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(m.instance&&m.instance!==this){this.log(`already started by another instance`);return}if(C()){this.log(`disabled`);return}this.started=!0,m.instance=this,a(this.isCookielessMode()),l();let e=this.options,t=p(e,this.baseUrl,this.debug);t&&this.startReplayTracker(t),e.trackErrors&&this.startErrorTracker(),e.trackWebVitals&&this.startWebVitalsTracker(),this.enterPage(),this.pageview({trigger:`load`}),this.links(),this.trackScroll(),this.startHeartbeat(),this.addDocumentListener(`visibilitychange`,this.handleVisibilityChange),this.addWindowListener(`pagehide`,this.handlePageHide),this.addWindowListener(`popstate`,this.handlePopState),e.trackHash&&this.addWindowListener(`hashchange`,this.handleHashChange),this.patchHistory()}destroy(){if(!this.destroyed){for(this.started&&typeof window<`u`&&this.leavePage(),this.pendingReportedErrors.length=0,this.stopNavigationTimer(),this.stopHeartbeat(),this.scrollHandler&&typeof window<`u`&&(window.removeEventListener(`scroll`,this.scrollHandler),this.scrollHandler=null);this.cleanupCallbacks.length>0;)this.cleanupCallbacks.pop()?.();this.stopChildTrackers(),m.instance===this&&(m.instance=null),this.started=!1,this.destroyed=!0}}pageview(e={}){if(!this.ensureStarted())return;let t=`${location.pathname}|${this.options.trackHash??!1?location.hash:``}`;t!==this.pageKey&&(this.pageKey=t,this.send(`pageview`,e))}track(e,t={}){this.ensureStarted()&&this.send(e,t)}identify(e,n,r={}){if(!this.ensureStarted()||this.isCookielessMode())return Promise.resolve(!1);let i=e.trim(),a=n.trim();return!i||!a?Promise.resolve(!1):w({url:t(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:s(!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`,useBeacon:!1})}logout(e=!0){this.ensureStarted()&&(e&&c(this.isCookielessMode()),u())}setConsentMode(e){this.consentMode=e,a(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return s(this.isCookielessMode())}getSessionId(){return l()}async checkFeatureFlag(e,t,n){if(typeof window>`u`||C())return{value:`false`};let r=n?.externalId?.trim(),i=this.getAnonymousId();if(!r&&!i)return{value:`false`};let{fetchFeatureFlagEvaluation:a}=await import(`./feature-flags-CqVtrpX2.js`).then(e=>e.t);return a(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,...i?{identifier:i}:{},...r?{externalId:r}:{},attributes:t,signal:n?.signal})}reportError(e){if(this.destroyed||typeof window>`u`||C()||!(this.options.trackErrors??!1)||!this.ensureStarted())return;let t=this.errorTracker;if(t){t.captureError(e);return}this.pendingReportedErrors.length>=50&&this.pendingReportedErrors.shift(),this.pendingReportedErrors.push(e)}isCookielessMode(){return this.options.cookieless||this.consentMode===`denied`?!0:this.consentMode===`pending`?this.cookielessWhilePending:!1}send(e,t={}){if(typeof window>`u`||this.destroyed||C())return;let n=s(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:l(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...E(),...t}});this.log(e),w({url:this.webEndpoint,data:r,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.destroyed||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-o()})}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.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(document.visibilityState===`hidden`){this.stopHeartbeat();return}i()},300*1e3)}navigate(){!this.started||this.destroyed||(this.stopNavigationTimer(),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))}links(){let e=e=>{let t=T(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{v as a,S as c,g as d,C as i,w as l,h as n,b as o,_ as r,x as s,D as t,y as u};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{o as t}from"./api-urls-BrkcoElX.js";import{n,r}from"./types-C3vW7XGe.js";import{l as i}from"./analytics-ur06JW5D.js";var a=e({default:()=>d});const o=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//,s=e=>e?.split(`
|
|
2
|
-
`).map(e=>e.trim()).filter(Boolean);function c(e){if(e instanceof Error)return{error:e.name?.trim()||`Error`,message:e.message,stack:s(e.stack),cause:c(e.cause)};if(typeof e==`string`)return{error:`Error`,message:e}}function l(e){return e?`${e.error}\0${e.message??``}\0${l(e.cause)}`:``}function u(e){let t=[e.kind,e.handled?`handled`:`unhandled`,e.message,e.filename??``,e.lineno??``,l(e.cause)].join(`\0`),n=2166136261,r=3598710387;for(let e=0;e<t.length;e++){let i=t.charCodeAt(e);n=Math.imul(n^i,16777619),r=Math.imul(r^i,2246822519)}return`err_${(n>>>0).toString(16).padStart(8,`0`)}${(r>>>0).toString(16).padStart(8,`0`)}`}var d=class{endpoint;handled=new WeakSet;queue=new Map;timer=null;started=!1;constructor(e){this.options=e,this.endpoint=t(e.baseUrl)}get debug(){return this.options.debug??!1}get flushInterval(){return this.options.flushInterval??5e3}get maxQueueSize(){return this.options.maxQueueSize??50}log(...e){this.debug&&console.log(`[ErrorTracker]`,...e)}start(){this.started||typeof window>`u`||(this.started=!0,window.addEventListener(`error`,this.onError),window.addEventListener(`unhandledrejection`,this.onRejection),document.addEventListener(`visibilitychange`,this.onVisibilityChange),window.addEventListener(`pagehide`,this.flush),this.timer=setInterval(this.flush,this.flushInterval),this.log(`Started listening for errors`))}stop(){!this.started||typeof window>`u`||(this.started=!1,window.removeEventListener(`error`,this.onError),window.removeEventListener(`unhandledrejection`,this.onRejection),document.removeEventListener(`visibilitychange`,this.onVisibilityChange),window.removeEventListener(`pagehide`,this.flush),this.timer&&clearInterval(this.timer),this.timer=null,this.flush(),this.log(`Stopped listening for errors`))}captureError(e){this.record({kind:`error`,message:e.message,stack:e.stack,handled:!0,cause:c(e.cause)},e)}onError=e=>{let t=e.error;this.record({kind:`error`,message:e.message||`Unknown error`,filename:e.filename||void 0,lineno:e.lineno||void 0,stack:t instanceof Error?t.stack:void 0,handled:!1,cause:t instanceof Error?c(t.cause):void 0},t)};onRejection=e=>{let t=e.reason;this.record({kind:`unhandledrejection`,message:t.message||`Unhandled promise rejection`,stack:t.stack||void 0,handled:!1,cause:c(t.cause)},t)};onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush()};record(e,t){if(t instanceof Error){if(this.handled.has(t)){this.log(`Skipping duplicate:`,t.message);return}this.handled.add(t)}if(o.test(`${e.filename??``}\n${e.stack??``}`))return;let n=u(e),r=this.queue.get(n);r?r.count+=1:this.queue.set(n,{hash:n,count:1,error:e.kind===`unhandledrejection`?`UnhandledRejection`:`Error`,message:e.message,stack:s(e.stack),handled:e.handled,...e.cause?{cause:e.cause}:{}}),this.log(`Captured error:`,e),this.queue.size>=this.maxQueueSize&&this.flush()}flush=()=>{if(this.queue.size===0)return;let e=[...this.queue.values()];this.queue.clear();let t=n(),a=globalThis.__SOURCEMAPS_BUILD__,o=typeof a?.buildId==`string`&&a.buildId.trim()?a.buildId:void 0,s=JSON.stringify({token:this.options.siteKey,...t?{userId:t}:{},sessionId:r(),...o?{buildId:o}:{},sdkName:this.options.sdkName??`@faststats/web`,sdkVersion:this.options.sdkVersion??`0.2.10`,data:{url:location.href,page:location.pathname,referrer:document.referrer||null,title:document.title},errors:e});this.log(`Flushing errors:`,e),this.log(`Payload:`,s),i({url:this.endpoint,data:s,debug:this.debug,debugPrefix:`[ErrorTracker]`})}};export{a as n,d as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{a as t}from"./api-urls-BrkcoElX.js";import{i as n,n as r,r as i,t as a}from"./types-C3vW7XGe.js";import{l as o}from"./analytics-ur06JW5D.js";import{EventType as s}from"@rrweb/types";import{record as c}from"rrweb";var l=e({default:()=>f});const u={mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},d={script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0};var f=class{endpoint;compressionSupported=typeof window<`u`&&`CompressionStream`in window;sampled;events=[];pending=[];pendingSizeBytes=0;sessionId;started=!1;startTime=0;sequence=0;intervalId=null;flushTask=null;retryTask=null;minLengthFlushTask=null;stopRecording;sending=!1;constructor(e){this.options=e,this.endpoint=t(e.baseUrl),this.sampled=Math.random()*100<a(e.samplingPercentage)}get debug(){return this.options.debug??!1}get flushInterval(){return this.options.flushInterval??1e4}get maxEvents(){return this.options.maxEvents??500}get maxPendingBatches(){return this.options.maxPendingBatches??30}get maxQueueSizeBytes(){return this.options.maxQueueSizeBytes??2097152}get minReplayLengthMs(){return this.options.minReplayLengthMs??3e3}get shouldCompress(){return this.options.compress??!0}log(...e){this.debug&&console.log(`[Replay]`,...e)}start(){this.started||typeof window>`u`||!this.sampled||(this.started=!0,this.sessionId=i(),this.startTime=n(),this.beginRecording(),this.intervalId=setInterval(this.requestFlush,this.flushInterval),window.addEventListener(`beforeunload`,this.onUnload),window.addEventListener(`pagehide`,this.onUnload),document.addEventListener(`visibilitychange`,this.onVisibilityChange),this.log(`Recording started`))}async beginRecording(){let e=this.options.recordConsole??!0,[{getRecordSequentialIdPlugin:t},n]=await Promise.all([import(`@rrweb/rrweb-plugin-sequential-id-record`),e?import(`@rrweb/rrweb-plugin-console-record`):Promise.resolve(null)]);if(!this.started)return;let r=[t({key:`_faststatsSeqId`})];n&&r.push(n.getRecordConsolePlugin()),this.stopRecording=c({emit:this.onEvent,sampling:this.options.sampling??u,slimDOMOptions:this.options.slimDOMOptions??d,maskAllInputs:this.options.maskAllInputs??!0,maskInputOptions:this.options.maskInputOptions??{password:!0,email:!0,tel:!0},blockClass:this.options.blockClass,blockSelector:this.options.blockSelector,maskTextClass:this.options.maskTextClass,maskTextSelector:this.options.maskTextSelector,checkoutEveryNms:this.options.checkoutEveryNms??6e4,checkoutEveryNth:this.options.checkoutEveryNth,plugins:r})}stop(){if(this.started){if(this.started=!1,this.stopRecording?.(),this.stopRecording=void 0,this.intervalId&&clearInterval(this.intervalId),this.flushTask&&clearTimeout(this.flushTask),this.retryTask&&clearTimeout(this.retryTask),this.minLengthFlushTask&&clearTimeout(this.minLengthFlushTask),this.intervalId=null,this.flushTask=null,this.retryTask=null,this.minLengthFlushTask=null,window.removeEventListener(`beforeunload`,this.onUnload),window.removeEventListener(`pagehide`,this.onUnload),document.removeEventListener(`visibilitychange`,this.onVisibilityChange),!this.hasReachedMinLength()){this.events.length=0,this.sessionId=void 0,this.log(`Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush(!0),this.sessionId=void 0,this.log(`Recording stopped`)}}getSessionId(){return this.sessionId??i()}onEvent=(e,t)=>{if(this.events.push(e),t||this.events.length>=this.maxEvents||e.type===s.FullSnapshot&&this.hasReachedMinLength()){this.requestFlush();return}this.scheduleMinLengthFlush()};onUnload=()=>{this.flush(!0)};onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush(!0)};hasReachedMinLength(){return this.minReplayLengthMs<=0||Date.now()-this.startTime>=this.minReplayLengthMs}requestFlush=()=>{this.flushTask||this.sending||this.events.length===0||(this.minLengthFlushTask&&=(clearTimeout(this.minLengthFlushTask),null),this.flushTask=setTimeout(()=>{this.flushTask=null,this.flush(!1)},0))};scheduleMinLengthFlush(){if(this.minLengthFlushTask||this.events.length===0||this.hasReachedMinLength())return;let e=Math.max(0,this.minReplayLengthMs-(Date.now()-this.startTime)),t=Math.min(e,2147483647);this.minLengthFlushTask=setTimeout(()=>{this.minLengthFlushTask=null,this.requestFlush()},t)}createBatch(e){let t=r();return{token:this.options.siteKey,sessionId:this.getSessionId(),...t?{identifier:t}:{},sequence:this.sequence++,timestamp:Date.now(),url:window.location.href,events:e}}getBatchSizeBytes(e){return new TextEncoder().encode(JSON.stringify(e)).byteLength}dropOldestPendingBatch(e){let t=this.pending.shift();t&&(this.pendingSizeBytes=Math.max(0,this.pendingSizeBytes-this.getBatchSizeBytes(t)),this.log(`${e}, dropping batch ${t.sequence}`))}enqueueBatch(e){let t=this.getBatchSizeBytes(e);if(t>this.maxQueueSizeBytes){this.log(`Replay batch ${e.sequence} is ${t}B, exceeding ${this.maxQueueSizeBytes}B queue limit; dropping`);return}for(;this.pending.length>0&&this.pendingSizeBytes+t>this.maxQueueSizeBytes;)this.dropOldestPendingBatch(`Pending queue size limit reached`);for(;this.pending.length>=this.maxPendingBatches;)this.dropOldestPendingBatch(`Pending batch limit reached`);this.pending.push(e),this.pendingSizeBytes+=t}async encodeBatch(e){let t=JSON.stringify(e);if(!this.shouldCompress||!this.compressionSupported)return{data:t,isCompressed:!1};try{let e=await this.compress(t);return this.log(`Compressed ${t.length}B -> ${e.byteLength}B (${Math.round(e.byteLength/t.length*100)}%)`),{data:e,isCompressed:!0}}catch{return this.log(`Compression failed, using uncompressed`),{data:t,isCompressed:!1}}}async flush(e){if(!this.sending){if(this.events.length>0)if(!this.hasReachedMinLength())this.scheduleMinLengthFlush();else{let e=this.createBatch(this.events.splice(0));this.enqueueBatch(e)}if(this.pending.length!==0){this.sending=!0;try{for(;this.pending.length>0;){let t=this.pending[0];if(!t)break;let n=await this.encodeBatch(t);if(!await this.send(n.data,n.isCompressed,e)){this.log(`Failed to send replay batch ${t.sequence}, retrying`),this.scheduleRetry();break}this.dropOldestPendingBatch(`Sent replay batch`),e=!1}}finally{this.sending=!1}}}}scheduleRetry(){this.retryTask||=setTimeout(()=>{this.retryTask=null,this.flush(!1)},1e3)}async compress(e){let t=new Blob([e]).stream().pipeThrough(new CompressionStream(`gzip`)),n=await new Response(t).arrayBuffer();return new Uint8Array(n)}send(e,t,n){return o({url:t?`${this.endpoint}?encoding=gzip`:this.endpoint,data:e,contentType:t?`application/octet-stream`:`application/json`,debug:!1,useBeacon:!1,keepalive:n})??Promise.resolve(!1)}};export{l as n,f as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
let e=!1;function t(t){e=t}function n(t){return t??e?``:r()}function r(){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 i(e){return e||typeof localStorage>`u`?``:(localStorage.removeItem(`faststats_anon_id`),r())}function a(){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 o(){return typeof sessionStorage>`u`?``:(sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`),a())}function s(){typeof sessionStorage>`u`||sessionStorage.getItem(`session_id`)&&sessionStorage.setItem(`session_timestamp`,Date.now().toString())}function c(){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()}function l(e){return typeof e!=`number`||!Number.isFinite(e)?100:Math.max(0,Math.min(100,e))}export{s as a,t as c,c as i,n,i as o,a as r,o as s,l as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{s as t}from"./api-urls-BrkcoElX.js";import{r as n,t as r}from"./types-C3vW7XGe.js";var i=e({default:()=>o});async function a(e){if(e){let{onCLS:e,onFCP:t,onINP:n,onLCP:r,onTTFB:i}=await import(`web-vitals/attribution`);return[e,t,n,r,i]}let{onCLS:t,onFCP:n,onINP:r,onLCP:i,onTTFB:a}=await import(`web-vitals`);return[t,n,r,i,a]}var o=class{endpoint;metrics=new Map;sampled;started=!1;flushed=!1;constructor(e){this.options=e,this.endpoint=t(e.baseUrl),this.sampled=Math.random()*100<r(e.samplingPercentage)}get debug(){return this.options.debug??!1}log(...e){this.debug&&console.log(`[WebVitals]`,...e)}start(){this.started||typeof window>`u`||(this.started=!0,this.flushed=!1,document.addEventListener(`visibilitychange`,this.onVisibilityChange),window.addEventListener(`pagehide`,this.flush),this.log(`Tracking started`),this.observe())}stop(){!this.started||typeof window>`u`||(this.started=!1,document.removeEventListener(`visibilitychange`,this.onVisibilityChange),window.removeEventListener(`pagehide`,this.flush),this.flush())}async observe(){try{let e=await a(this.options.attribution??!1);if(!this.started)return;for(let t of e)t(this.captureMetric)}catch(e){this.log(`Failed to load web-vitals`,e)}}onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush()};captureMetric=e=>{if(this.flushed||!this.sampled)return;let t=e.name,n=e.attribution??void 0;this.metrics.set(t,{value:e.value,attributes:{id:e.id,rating:e.rating,delta:e.delta,navigationType:e.navigationType,...n??{}}}),this.log(`${t} captured: ${e.value}`)};flush=()=>{if(this.flushed||this.metrics.size===0)return;this.flushed=!0;let e=[...this.metrics.entries()].map(([e,t])=>({metric:e,value:t.value,attributes:t.attributes})),t=e.map(e=>e.metric).join(`, `);this.metrics.clear();let r=JSON.stringify({sessionId:n(),vitals:e,metadata:{url:window.location.href}});this.log(`Sending final metrics (${e.length}): ${t}`),typeof fetch==`function`&&fetch(this.endpoint,{method:`POST`,body:r,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.options.siteKey}`},keepalive:!0}).catch(()=>{this.log(`Failed to send metrics`)})}};export{i as n,o as t};
|
|
File without changes
|
|
File without changes
|