@faststats/web 0.2.12 → 0.2.13
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/chunks/api-urls-DaeYkG0_.js +1 -0
- package/dist/chunks/error-CDVOMiI9.js +2 -0
- package/dist/chunks/{feature-flags-BxZz_lNm.d.ts → feature-flags-BClx56v5.d.ts} +1 -0
- package/dist/chunks/feature-flags-DSOCIZHK.js +1 -0
- package/dist/chunks/replay-CHFW2q4E.js +1 -0
- package/dist/chunks/web-vitals-GwPlL7Zy.js +1 -0
- package/dist/error.d.ts +5 -8
- 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 +1 -1
- package/dist/index.js +1 -1
- package/dist/replay.js +1 -1
- package/dist/web-vitals.d.ts +7 -15
- package/dist/web-vitals.js +1 -1
- package/package.json +1 -1
- package/src/analytics.ts +4 -4
- package/src/error.ts +104 -157
- package/src/feature-flags.ts +5 -2
- package/src/replay.ts +2 -2
- package/src/utils/api-urls.ts +7 -20
- package/src/web-vitals.ts +65 -167
- package/tests/analytics.test.ts +1 -0
- package/dist/chunks/api-urls-BrkcoElX.js +0 -1
- package/dist/chunks/error-CttYL43D.js +0 -2
- package/dist/chunks/feature-flags-CjnLZGxp.js +0 -1
- package/dist/chunks/replay-BrMLCiBF.js +0 -1
- package/dist/chunks/web-vitals-CjA1bFLG.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`https://metrics.faststats.dev`,t=`https://flags.faststats.dev`;function n(e){return e.replace(/\/+$/,``)}function r(t){return t===void 0||t===``?e:n(t)||e}const i={events:`/v1/web`,identify:`/v1/identify`,replay:`/v1/replay`,vitals:`/v1/vitals`,flags:`/v1/check`};function a(e){return e===void 0||e===``?t:n(e)||t}export{r as n,a as r,i as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{n as t,t as n}from"./api-urls-DaeYkG0_.js";import{n as r,t as i}from"./identifiers-CQeWm7wi.js";import{t as a}from"./send-data-DL_GlsQw.js";var o=e({default:()=>p});const s=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//,c=e=>e?.split(`
|
|
2
|
+
`).map(e=>e.trim()).filter(Boolean);function l(e){if(e instanceof Error)return{error:e.name?.trim()||`Error`,message:e.message,stack:c(e.stack),cause:l(e.cause)};if(typeof e==`string`)return{error:`Error`,message:e}}function u(e){let t=``;for(let n=e;n;n=n.cause)t+=`${n.error}\0${n.message??``}\0`;return t}function d(e){let t=2166136261,n=3598710387;for(let r=0;r<e.length;r++){let i=e.charCodeAt(r);t=Math.imul(t^i,16777619),n=Math.imul(n^i,2246822519)}return`err_${(t>>>0).toString(16).padStart(8,`0`)}${(n>>>0).toString(16).padStart(8,`0`)}`}function f(){let e=globalThis.__SOURCEMAPS_BUILD__?.buildId;return typeof e==`string`&&e.trim()?e:void 0}var p=class{endpoint;seen=new WeakSet;queue=new Map;timer=null;started=!1;flushing=!1;constructor(e){this.options=e,this.endpoint=`${t(e.baseUrl)}${n.events}`}log(...e){this.options.debug&&console.log(`[ErrorTracker]`,...e)}start(){this.started||typeof window>`u`||(this.started=!0,window.addEventListener(`error`,this.onError),window.addEventListener(`unhandledrejection`,this.onRejection),window.addEventListener(`pagehide`,this.scheduleFlush),document.addEventListener(`visibilitychange`,this.onVisibility),this.timer=setInterval(this.scheduleFlush,this.options.flushInterval??5e3),this.log(`Started`))}stop(){!this.started||typeof window>`u`||(this.started=!1,window.removeEventListener(`error`,this.onError),window.removeEventListener(`unhandledrejection`,this.onRejection),window.removeEventListener(`pagehide`,this.scheduleFlush),document.removeEventListener(`visibilitychange`,this.onVisibility),this.timer&&clearInterval(this.timer),this.timer=null,this.scheduleFlush(),this.log(`Stopped`))}captureError(e){this.capture(`error`,e.message,!0,e)}onError=e=>{this.capture(`error`,e.message||`Unknown error`,!1,e.error,e.filename||``,e.lineno)};onRejection=e=>{let t=e.reason,n=t instanceof Error?t.message:typeof t==`string`?t:`Unhandled promise rejection`;this.capture(`unhandledrejection`,n,!1,t)};onVisibility=()=>{document.visibilityState===`hidden`&&this.scheduleFlush()};scheduleFlush=()=>{this.flush()};capture(e,t,n,r,i=``,a=``){let o=r instanceof Error?r.stack??``:``;if(s.test(`${i}\n${o}`))return;if(typeof r==`object`&&r){if(this.seen.has(r)){this.log(`Skipping duplicate:`,t);return}this.seen.add(r)}let f={error:e===`unhandledrejection`?`UnhandledRejection`:`Error`,message:t,handled:n};if(r instanceof Error){f.stack=c(r.stack);let e=l(r.cause);e&&(f.cause=e)}let p=d(`${e}\0${n?`handled`:`unhandled`}\0${t}\0${i}\0${a}\0${u(f.cause)}`),m=this.queue.get(p);m?m.count++:this.queue.set(p,{hash:p,count:1,...f}),this.log(`Captured:`,f),this.queue.size>=(this.options.maxQueueSize??50)&&this.scheduleFlush()}async flush(){if(this.flushing||this.queue.size===0)return;let e=[...this.queue.values()];this.queue.clear(),this.flushing=!0;let t=i(),n=f(),o=JSON.stringify({token:this.options.siteKey,...t?{userId:t}:{},sessionId:r(),...n?{buildId:n}:{},sdkName:this.options.sdkName??`@faststats/web`,sdkVersion:this.options.sdkVersion??`0.2.13`,data:{url:location.href,page:location.pathname,referrer:document.referrer||null,title:document.title},errors:e});this.log(`Flushing:`,e);try{await a({url:this.endpoint,data:o,debug:this.options.debug,debugPrefix:`[ErrorTracker]`})||this.requeue(e)}catch{this.requeue(e)}finally{this.flushing=!1}}requeue(e){for(let t of e){let e=this.queue.get(t.hash);e?e.count+=t.count:this.queue.set(t.hash,t)}}};export{o as n,p as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{r as t,t as n}from"./api-urls-DaeYkG0_.js";var r=e({fetchFeatureFlagEvaluation:()=>i});async function i(e,r){if(r.projectToken&&r.projectId)throw Error(`provide either projectToken or projectId, not both`);let i=r.projectToken,a=r.projectId,o=r.identifier,s=r.externalId,c=r.sessionId;if(!i&&!a)throw Error(`feature flag check requires projectToken or projectId`);if(!o&&!s)throw Error(`feature flag check requires serverId or externalId`);let l={key:e,...a?{projectId:a}:{},...o?{identifier:o}:{},...s?{externalId:s}:{},...c?{sessionId:c}:{}};r.attributes&&Object.keys(r.attributes).length>0&&(l.attributes=r.attributes);let u={"Content-Type":`application/json`};i&&(u.Authorization=`Bearer ${i}`);let d=`${t(r.baseUrl)}${n.flags}`,f=await fetch(d,{method:`POST`,headers:u,body:JSON.stringify(l),credentials:`omit`,signal:r.signal});if(!f.ok){let e=``;try{let t=await f.json();typeof t.error==`string`&&t.error.length>0&&(e=` — ${t.error}`)}catch{}throw Error(`feature flag check failed: HTTP ${f.status}${e}`)}return await f.json()}export{i as n,r as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{n as t,t as n}from"./api-urls-DaeYkG0_.js";import{n as r,r as i,t as a}from"./identifiers-CQeWm7wi.js";import{t as o}from"./send-data-DL_GlsQw.js";import{t as s}from"./types-CYzR5xtT.js";import{EventType as c}from"@rrweb/types";import{record as l}from"rrweb";var u=e({default:()=>p});const d={mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},f={script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0};var p=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)}${n.replay}`,this.sampled=Math.random()*100<s(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=r(),this.startTime=i(),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=l({emit:this.onEvent,sampling:this.options.sampling??d,slimDOMOptions:this.options.slimDOMOptions??f,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??r()}onEvent=(e,t)=>{if(this.events.push(e),t||this.events.length>=this.maxEvents||e.type===c.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=a(),n=this.sequence++;return{token:this.options.siteKey,sessionId:this.getSessionId(),...t?{identifier:t}:{},batchId:this.createBatchId(n),sequence:n,timestamp:Date.now(),url:window.location.href,events:e}}createBatchId(e){let t=typeof crypto<`u`&&`randomUUID`in crypto?crypto.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;return`${this.getSessionId()??`unknown`}-${e}-${t}`}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{u as n,p as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{n as t,t as n}from"./api-urls-DaeYkG0_.js";import{n as r}from"./identifiers-CQeWm7wi.js";import{t as i}from"./types-CYzR5xtT.js";var a=e({default:()=>o}),o=class{endpoint;sampled;metrics=new Map;started=!1;url=``;constructor(e){this.options=e,this.endpoint=`${t(e.baseUrl)}${n.vitals}`,this.sampled=Math.random()*100<i(e.samplingPercentage)}get debug(){return this.options.debug??!1}log(...e){this.debug&&console.log(`[WebVitals]`,...e)}async start(){if(this.started||typeof window>`u`)return;this.started=!0,this.url=window.location.href,document.addEventListener(`visibilitychange`,this.onHidden,{passive:!0}),window.addEventListener(`pagehide`,this.onPageHide,{passive:!0});let e=this.options.attribution?await import(`web-vitals/attribution`):await import(`web-vitals`);this.started&&(e.onCLS(this.capture,{reportAllChanges:!0}),e.onINP(this.capture,{reportAllChanges:!0}),e.onLCP(this.capture,{reportAllChanges:!0}),e.onFCP(this.capture),e.onTTFB(this.capture),this.log(`Tracking started`))}stop(){!this.started||typeof window>`u`||(this.started=!1,this.removeListeners(),this.flush())}trackPageChange(e){if(!this.started||typeof window>`u`)return;let t=e??window.location.href;t!==this.url&&(this.flush(),this.url=t)}onHidden=()=>{document.visibilityState===`hidden`&&this.flush()};onPageHide=()=>{this.flush()};capture=e=>{if(!this.started||!this.sampled)return;let t=e.name,n=e.attribution;this.metrics.set(t,{metric:t,value:e.value,attributes:{id:e.id,rating:e.rating,delta:e.delta,navigationType:e.navigationType,...n??{}}})};async flush(){if(typeof window>`u`||typeof fetch!=`function`||!this.metrics.size)return;let e=Array.from(this.metrics.values()),t=this.url;this.metrics.clear();try{let n=new AbortController,i=window.setTimeout(()=>n.abort(),3e3),a=await fetch(this.endpoint,{method:`POST`,body:JSON.stringify({sessionId:r(),vitals:e,metadata:{url:t}}),headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.options.siteKey}`},keepalive:!0,signal:n.signal});if(clearTimeout(i),!a.ok)throw Error(`HTTP ${a.status}`)}catch(e){this.log(`Failed to send metrics`,e)}}removeListeners(){document.removeEventListener(`visibilitychange`,this.onHidden),window.removeEventListener(`pagehide`,this.onPageHide)}};export{a as n,o as t};
|
package/dist/error.d.ts
CHANGED
|
@@ -22,26 +22,23 @@ interface ErrorTrackingOptions {
|
|
|
22
22
|
declare class ErrorTracker {
|
|
23
23
|
private readonly options;
|
|
24
24
|
private readonly endpoint;
|
|
25
|
-
private readonly
|
|
25
|
+
private readonly seen;
|
|
26
26
|
private readonly queue;
|
|
27
27
|
private timer;
|
|
28
28
|
private started;
|
|
29
29
|
private flushing;
|
|
30
30
|
constructor(options: ErrorTrackingOptions);
|
|
31
|
-
private get debug();
|
|
32
|
-
private get flushInterval();
|
|
33
|
-
private get maxQueueSize();
|
|
34
31
|
private log;
|
|
35
32
|
start(): void;
|
|
36
33
|
stop(): void;
|
|
37
34
|
captureError(error: Error): void;
|
|
38
35
|
private onError;
|
|
39
36
|
private onRejection;
|
|
40
|
-
private
|
|
41
|
-
private
|
|
42
|
-
private
|
|
43
|
-
private record;
|
|
37
|
+
private onVisibility;
|
|
38
|
+
private scheduleFlush;
|
|
39
|
+
private capture;
|
|
44
40
|
private flush;
|
|
41
|
+
private requeue;
|
|
45
42
|
}
|
|
46
43
|
//#endregion
|
|
47
44
|
export { type ErrorEntry, type ErrorTracking, type ErrorTrackingOptions, ErrorTracker as default };
|
package/dist/error.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./chunks/error-
|
|
1
|
+
import{t as e}from"./chunks/error-CDVOMiI9.js";export{e as default};
|
package/dist/feature-flags.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as FeatureFlagEvaluation, r as fetchFeatureFlagEvaluation, t as FeatureFlagCheckContext } from "./chunks/feature-flags-
|
|
1
|
+
import { n as FeatureFlagEvaluation, r as fetchFeatureFlagEvaluation, t as FeatureFlagCheckContext } from "./chunks/feature-flags-BClx56v5.js";
|
|
2
2
|
export { type FeatureFlagCheckContext, type FeatureFlagEvaluation, fetchFeatureFlagEvaluation };
|
package/dist/feature-flags.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./chunks/feature-flags-
|
|
1
|
+
import{n as e}from"./chunks/feature-flags-DSOCIZHK.js";export{e as fetchFeatureFlagEvaluation};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as FeatureFlagEvaluation, r as fetchFeatureFlagEvaluation, t as FeatureFlagCheckContext } from "./chunks/feature-flags-
|
|
1
|
+
import { n as FeatureFlagEvaluation, r as fetchFeatureFlagEvaluation, t as FeatureFlagCheckContext } from "./chunks/feature-flags-BClx56v5.js";
|
|
2
2
|
import { n as ReplayTrackerOptions } from "./chunks/replay-BuX3_0xs.js";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/types.d.ts
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e,n as t,o as n,r}from"./chunks/api-urls-BrkcoElX.js";import{a as i,i as a,n as o,o as s,r as c,s as l,t as u}from"./chunks/identifiers-CQeWm7wi.js";import{t as d}from"./chunks/send-data-DL_GlsQw.js";import{t as f}from"./chunks/types-CYzR5xtT.js";import{n as p}from"./chunks/feature-flags-CjnLZGxp.js";function m(e){return e.replayOptions?.samplingPercentage!==void 0||e.sessionReplays?.sampling?.percentage!==void 0}function h(e,t,n){if(!(e.sessionReplays?.enabled??e.trackReplay??m(e)))return null;let r=e.replayOptions??{};return{siteKey:e.siteKey,baseUrl:t,debug:n,...r,samplingPercentage:f(r.samplingPercentage??e.sessionReplays?.sampling?.percentage)}}const g={instance:null,pendingConsentMode:void 0};function _(){return g.instance}function v(e,t){typeof window>`u`||T()||g.instance?.track(e,t??{})}function y(e,t,n){return typeof window>`u`||T()?Promise.resolve(!1):g.instance?.identify(e,t,n??{})??Promise.resolve(!1)}function b(e=!0){typeof window>`u`||T()||g.instance?.logout(e)}function x(e){if(g.instance){g.instance.setConsentMode(e);return}g.pendingConsentMode=e}function S(){x(`granted`)}function C(){x(`denied`)}function w(e){typeof window>`u`||T()||g.instance?.reportError(e)}function T(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}function E(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function D(){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 O=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`?(a(),this.leavePage(),this.stopHeartbeat()):(a(),this.enterPage(),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,g.pendingConsentMode!==void 0&&(this.consentMode=g.pendingConsentMode,g.pendingConsentMode=void 0),(t.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(T()){this.log(`disabled`);return}g.instance=this,l(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||T()?!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||g.instance!==this?(e.stop?.(),!1):(this.childTrackers.push(e),!0)}async startChildTracker(e,t,n){try{let r=await t();return!this.started||this.destroyed||g.instance!==this?(r.stop?.(),null):(r.start(),this.registerChildTracker(r)?(n?.(r),this.log(`${e} loaded`),r):null)}catch(t){return this.log(`failed to initialize ${e} tracker: ${String(t)}`),null}}async startErrorTracker(){await this.startChildTracker(`error`,async()=>{let{default:e}=await import(`./chunks/error-CttYL43D.js`).then(e=>e.n);return new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,sdkName:this.options.sdkName,sdkVersion:this.options.sdkVersion})},e=>{for(this.errorTracker=e;this.pendingReportedErrors.length>0;){let t=this.pendingReportedErrors.shift();t&&e.captureError(t)}})}async startWebVitalsTracker(){await this.startChildTracker(`web-vitals`,async()=>{let{default:e}=await import(`./chunks/web-vitals-CjA1bFLG.js`).then(e=>e.n);return new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:f(this.options.webVitals?.sampling?.percentage),attribution:this.options.webVitals?.attribution??!1})})}async startReplayTracker(e){await this.startChildTracker(`replay`,async()=>{let{default:t}=await import(`./chunks/replay-BrMLCiBF.js`).then(e=>e.n);return new t(e)})}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(g.instance&&g.instance!==this){this.log(`already started by another instance`);return}if(T()){this.log(`disabled`);return}this.started=!0,g.instance=this,l(this.isCookielessMode()),o();let e=this.options,t=h(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(),g.instance===this&&(g.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):d({url:t(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:u(!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&&i(this.isCookielessMode()),s())}setConsentMode(e){this.consentMode=e,l(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return u(this.isCookielessMode())}getSessionId(){return o()}async checkFeatureFlag(e,t,n){if(typeof window>`u`||T())return{value:`false`};let r=n?.externalId?.trim(),i=this.getAnonymousId();if(!r&&!i)return{value:`false`};let{fetchFeatureFlagEvaluation:a}=await import(`./chunks/feature-flags-CjnLZGxp.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`||T()||!(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||T())return;let n=u(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:o(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...D(),...t}});this.log(e),d({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-c()})}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}a()},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;if(!(!e&&!t)){this.leavePage(),this.enterPage();for(let e of this.childTrackers)e.trackPageChange?.(this.pageUrl);this.trackScroll(),this.pageview({trigger:`navigation`})}},300))}links(){let e=e=>{let t=E(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{O as WebAnalytics,p as fetchFeatureFlagEvaluation,_ as getInstance,y as identify,T as isTrackingDisabled,b as logout,S as optIn,C as optOut,w as reportError,d as sendData,x as setConsentMode,v as trackEvent};
|
|
1
|
+
import{n as e,r as t,t as n}from"./chunks/api-urls-DaeYkG0_.js";import{a as r,i,n as a,o,r as s,s as c,t as l}from"./chunks/identifiers-CQeWm7wi.js";import{t as u}from"./chunks/send-data-DL_GlsQw.js";import{t as d}from"./chunks/types-CYzR5xtT.js";import{n as f}from"./chunks/feature-flags-DSOCIZHK.js";function p(e){return e.replayOptions?.samplingPercentage!==void 0||e.sessionReplays?.sampling?.percentage!==void 0}function m(e,t,n){if(!(e.sessionReplays?.enabled??e.trackReplay??p(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 h={instance:null,pendingConsentMode:void 0};function g(){return h.instance}function _(e,t){typeof window>`u`||w()||h.instance?.track(e,t??{})}function v(e,t,n){return typeof window>`u`||w()?Promise.resolve(!1):h.instance?.identify(e,t,n??{})??Promise.resolve(!1)}function y(e=!0){typeof window>`u`||w()||h.instance?.logout(e)}function b(e){if(h.instance){h.instance.setConsentMode(e);return}h.pendingConsentMode=e}function x(){b(`granted`)}function S(){b(`denied`)}function C(e){typeof window>`u`||w()||h.instance?.reportError(e)}function w(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`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`?(i(),this.leavePage(),this.stopHeartbeat()):(i(),this.enterPage(),this.startHeartbeat())};handlePageHide=()=>{this.leavePage()};handlePopState=()=>{this.navigate()};handleHashChange=()=>{this.navigate()};constructor(r){this.options=r,this.baseUrl=e(r.baseUrl),this.featureFlagsBaseUrl=t(r.featureFlagsBaseUrl),this.webEndpoint=`${this.baseUrl}${n.events}`,this.debug=r.debug??!1,this.consentMode=r.consent?.mode??`granted`,this.cookielessWhilePending=r.consent?.cookielessWhilePending??!0,h.pendingConsentMode!==void 0&&(this.consentMode=h.pendingConsentMode,h.pendingConsentMode=void 0),(r.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(w()){this.log(`disabled`);return}h.instance=this,c(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||w()?!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||h.instance!==this?(e.stop?.(),!1):(this.childTrackers.push(e),!0)}async startChildTracker(e,t,n){try{let r=await t();return!this.started||this.destroyed||h.instance!==this?(r.stop?.(),null):(r.start(),this.registerChildTracker(r)?(n?.(r),this.log(`${e} loaded`),r):null)}catch(t){return this.log(`failed to initialize ${e} tracker: ${String(t)}`),null}}async startErrorTracker(){await this.startChildTracker(`error`,async()=>{let{default:e}=await import(`./chunks/error-CDVOMiI9.js`).then(e=>e.n);return new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,sdkName:this.options.sdkName,sdkVersion:this.options.sdkVersion})},e=>{for(this.errorTracker=e;this.pendingReportedErrors.length>0;){let t=this.pendingReportedErrors.shift();t&&e.captureError(t)}})}async startWebVitalsTracker(){await this.startChildTracker(`web-vitals`,async()=>{let{default:e}=await import(`./chunks/web-vitals-GwPlL7Zy.js`).then(e=>e.n);return 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})})}async startReplayTracker(e){await this.startChildTracker(`replay`,async()=>{let{default:t}=await import(`./chunks/replay-CHFW2q4E.js`).then(e=>e.n);return new t(e)})}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(h.instance&&h.instance!==this){this.log(`already started by another instance`);return}if(w()){this.log(`disabled`);return}this.started=!0,h.instance=this,c(this.isCookielessMode()),a();let e=this.options,t=m(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(),h.instance===this&&(h.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,t,r={}){if(!this.ensureStarted()||this.isCookielessMode())return Promise.resolve(!1);let i=e.trim(),a=t.trim();return!i||!a?Promise.resolve(!1):u({url:`${this.baseUrl}${n.identify}`,data:JSON.stringify({token:this.options.siteKey,identifier:l(!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&&r(this.isCookielessMode()),o())}setConsentMode(e){this.consentMode=e,c(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return l(this.isCookielessMode())}getSessionId(){return a()}async checkFeatureFlag(e,t,n){if(typeof window>`u`||w())return{value:`false`};let r=n?.externalId?.trim(),i=this.getAnonymousId();if(!r&&!i)return{value:`false`};let{fetchFeatureFlagEvaluation:a}=await import(`./chunks/feature-flags-DSOCIZHK.js`).then(e=>e.t);return a(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,...i?{identifier:i}:{},...r?{externalId:r}:{},sessionId:this.getSessionId(),attributes:t,signal:n?.signal})}reportError(e){if(this.destroyed||typeof window>`u`||w()||!(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||w())return;let n=l(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:a(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...E(),...t}});this.log(e),u({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-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.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;if(!(!e&&!t)){this.leavePage(),this.enterPage();for(let e of this.childTrackers)e.trackPageChange?.(this.pageUrl);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{D as WebAnalytics,f as fetchFeatureFlagEvaluation,g as getInstance,v as identify,w as isTrackingDisabled,y as logout,x as optIn,S as optOut,C as reportError,u as sendData,b as setConsentMode,_ as trackEvent};
|
package/dist/replay.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./chunks/replay-
|
|
1
|
+
import{t as e}from"./chunks/replay-CHFW2q4E.js";export{e as default};
|
package/dist/web-vitals.d.ts
CHANGED
|
@@ -9,29 +9,21 @@ interface WebVitalsOptions {
|
|
|
9
9
|
declare class WebVitalsTracker {
|
|
10
10
|
private readonly options;
|
|
11
11
|
private readonly endpoint;
|
|
12
|
-
private readonly metricsByUrl;
|
|
13
|
-
private readonly pendingFlushUrls;
|
|
14
12
|
private readonly sampled;
|
|
13
|
+
private readonly metrics;
|
|
15
14
|
private started;
|
|
16
|
-
private
|
|
17
|
-
private finalFlushRequested;
|
|
18
|
-
private currentUrl;
|
|
15
|
+
private url;
|
|
19
16
|
constructor(options: WebVitalsOptions);
|
|
20
17
|
private get debug();
|
|
21
18
|
private log;
|
|
22
|
-
start(): void
|
|
19
|
+
start(): Promise<void>;
|
|
23
20
|
stop(): void;
|
|
24
21
|
trackPageChange(url?: string): void;
|
|
25
|
-
private
|
|
26
|
-
private observe;
|
|
27
|
-
private onVisibilityChange;
|
|
22
|
+
private onHidden;
|
|
28
23
|
private onPageHide;
|
|
29
|
-
private
|
|
30
|
-
private
|
|
31
|
-
private
|
|
32
|
-
private flushPending;
|
|
33
|
-
private sendMetricsForUrl;
|
|
34
|
-
private finishFinalFlushIfNeeded;
|
|
24
|
+
private capture;
|
|
25
|
+
private flush;
|
|
26
|
+
private removeListeners;
|
|
35
27
|
}
|
|
36
28
|
//#endregion
|
|
37
29
|
export { type WebVitalsOptions, WebVitalsTracker as default };
|
package/dist/web-vitals.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./chunks/web-vitals-
|
|
1
|
+
import{t as e}from"./chunks/web-vitals-GwPlL7Zy.js";export{e as default};
|
package/package.json
CHANGED
package/src/analytics.ts
CHANGED
|
@@ -2,10 +2,9 @@ import type ErrorTracker from "./error";
|
|
|
2
2
|
import type { FeatureFlagEvaluation } from "./feature-flags";
|
|
3
3
|
import type { ReplayTrackerOptions } from "./replay";
|
|
4
4
|
import {
|
|
5
|
-
identifyEventsUrl,
|
|
6
5
|
normalizeAnalyticsBaseUrl,
|
|
7
6
|
normalizeFeatureFlagsBaseUrl,
|
|
8
|
-
|
|
7
|
+
URLS,
|
|
9
8
|
} from "./utils/api-urls";
|
|
10
9
|
import {
|
|
11
10
|
getAnonymousId,
|
|
@@ -256,7 +255,7 @@ export class WebAnalytics {
|
|
|
256
255
|
this.featureFlagsBaseUrl = normalizeFeatureFlagsBaseUrl(
|
|
257
256
|
options.featureFlagsBaseUrl,
|
|
258
257
|
);
|
|
259
|
-
this.webEndpoint =
|
|
258
|
+
this.webEndpoint = `${this.baseUrl}${URLS.events}`;
|
|
260
259
|
this.debug = options.debug ?? false;
|
|
261
260
|
this.consentMode = options.consent?.mode ?? "granted";
|
|
262
261
|
this.cookielessWhilePending =
|
|
@@ -540,7 +539,7 @@ export class WebAnalytics {
|
|
|
540
539
|
const trimmedEmail = email.trim();
|
|
541
540
|
if (!trimmedExternalId || !trimmedEmail) return Promise.resolve(false);
|
|
542
541
|
|
|
543
|
-
const identifyEndpoint =
|
|
542
|
+
const identifyEndpoint = `${this.baseUrl}${URLS.identify}`;
|
|
544
543
|
const payload = JSON.stringify({
|
|
545
544
|
token: this.options.siteKey,
|
|
546
545
|
identifier: getAnonymousId(false),
|
|
@@ -615,6 +614,7 @@ export class WebAnalytics {
|
|
|
615
614
|
projectToken: this.options.siteKey,
|
|
616
615
|
...(identifier ? { identifier } : {}),
|
|
617
616
|
...(externalId ? { externalId } : {}),
|
|
617
|
+
sessionId: this.getSessionId(),
|
|
618
618
|
attributes,
|
|
619
619
|
signal: opts?.signal,
|
|
620
620
|
});
|
package/src/error.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SDK_NAME, SDK_VERSION } from "./sdk";
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeAnalyticsBaseUrl, URLS } from "./utils/api-urls";
|
|
3
3
|
import { getAnonymousId, getOrCreateSessionId } from "./utils/identifiers";
|
|
4
4
|
import { sendData } from "./utils/send-data";
|
|
5
5
|
|
|
@@ -16,18 +16,6 @@ export type ErrorTracking = ErrorEntry & {
|
|
|
16
16
|
count: number;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
type ErrorKind = "error" | "unhandledrejection";
|
|
20
|
-
|
|
21
|
-
type NormalizedError = {
|
|
22
|
-
kind: ErrorKind;
|
|
23
|
-
message: string;
|
|
24
|
-
stack?: string;
|
|
25
|
-
filename?: string;
|
|
26
|
-
lineno?: number;
|
|
27
|
-
handled: boolean;
|
|
28
|
-
cause?: ErrorEntry;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
19
|
export interface ErrorTrackingOptions {
|
|
32
20
|
siteKey: string;
|
|
33
21
|
baseUrl?: string;
|
|
@@ -38,214 +26,176 @@ export interface ErrorTrackingOptions {
|
|
|
38
26
|
sdkVersion?: string;
|
|
39
27
|
}
|
|
40
28
|
|
|
41
|
-
|
|
29
|
+
type Kind = "error" | "unhandledrejection";
|
|
30
|
+
|
|
31
|
+
const EXTENSION_RE =
|
|
42
32
|
/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//;
|
|
43
33
|
|
|
44
34
|
const parseStack = (stack?: string): string[] | undefined =>
|
|
45
35
|
stack
|
|
46
36
|
?.split("\n")
|
|
47
|
-
.map((
|
|
37
|
+
.map((l) => l.trim())
|
|
48
38
|
.filter(Boolean);
|
|
49
39
|
|
|
50
|
-
function serializeCause(
|
|
51
|
-
if (
|
|
40
|
+
function serializeCause(value: unknown): ErrorEntry | undefined {
|
|
41
|
+
if (value instanceof Error) {
|
|
52
42
|
return {
|
|
53
|
-
error:
|
|
54
|
-
message:
|
|
55
|
-
stack: parseStack(
|
|
56
|
-
cause: serializeCause(
|
|
43
|
+
error: value.name?.trim() || "Error",
|
|
44
|
+
message: value.message,
|
|
45
|
+
stack: parseStack(value.stack),
|
|
46
|
+
cause: serializeCause(value.cause),
|
|
57
47
|
};
|
|
58
48
|
}
|
|
59
|
-
|
|
60
|
-
if (typeof cause === "string") {
|
|
61
|
-
return { error: "Error", message: cause };
|
|
62
|
-
}
|
|
49
|
+
if (typeof value === "string") return { error: "Error", message: value };
|
|
63
50
|
}
|
|
64
51
|
|
|
65
52
|
function fingerprintCause(cause?: ErrorEntry): string {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
let s = "";
|
|
54
|
+
for (let c = cause; c; c = c.cause) {
|
|
55
|
+
s += `${c.error}\0${c.message ?? ""}\0`;
|
|
56
|
+
}
|
|
57
|
+
return s;
|
|
69
58
|
}
|
|
70
59
|
|
|
71
|
-
function
|
|
72
|
-
const key = [
|
|
73
|
-
data.kind,
|
|
74
|
-
data.handled ? "handled" : "unhandled",
|
|
75
|
-
data.message,
|
|
76
|
-
data.filename ?? "",
|
|
77
|
-
data.lineno ?? "",
|
|
78
|
-
fingerprintCause(data.cause),
|
|
79
|
-
].join("\0");
|
|
80
|
-
|
|
60
|
+
function hashKey(key: string): string {
|
|
81
61
|
let a = 2166136261;
|
|
82
62
|
let b = 3598710387;
|
|
83
|
-
|
|
84
63
|
for (let i = 0; i < key.length; i++) {
|
|
85
64
|
const c = key.charCodeAt(i);
|
|
86
65
|
a = Math.imul(a ^ c, 16777619);
|
|
87
66
|
b = Math.imul(b ^ c, 2246822519);
|
|
88
67
|
}
|
|
68
|
+
return `err_${(a >>> 0).toString(16).padStart(8, "0")}${(b >>> 0).toString(16).padStart(8, "0")}`;
|
|
69
|
+
}
|
|
89
70
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.
|
|
71
|
+
function readBuildId(): string | undefined {
|
|
72
|
+
const v = (globalThis as { __SOURCEMAPS_BUILD__?: { buildId?: unknown } })
|
|
73
|
+
.__SOURCEMAPS_BUILD__?.buildId;
|
|
74
|
+
return typeof v === "string" && v.trim() ? v : undefined;
|
|
93
75
|
}
|
|
94
76
|
|
|
95
77
|
export default class ErrorTracker {
|
|
96
78
|
private readonly endpoint: string;
|
|
97
|
-
private readonly
|
|
79
|
+
private readonly seen = new WeakSet<object>();
|
|
98
80
|
private readonly queue = new Map<string, ErrorTracking>();
|
|
99
81
|
private timer: ReturnType<typeof setInterval> | null = null;
|
|
100
82
|
private started = false;
|
|
101
83
|
private flushing = false;
|
|
102
84
|
|
|
103
85
|
constructor(private readonly options: ErrorTrackingOptions) {
|
|
104
|
-
this.endpoint =
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private get debug(): boolean {
|
|
108
|
-
return this.options.debug ?? false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private get flushInterval(): number {
|
|
112
|
-
return this.options.flushInterval ?? 5000;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private get maxQueueSize(): number {
|
|
116
|
-
return this.options.maxQueueSize ?? 50;
|
|
86
|
+
this.endpoint = `${normalizeAnalyticsBaseUrl(options.baseUrl)}${URLS.events}`;
|
|
117
87
|
}
|
|
118
88
|
|
|
119
89
|
private log(...args: unknown[]): void {
|
|
120
|
-
if (this.debug) console.log("[ErrorTracker]", ...args);
|
|
90
|
+
if (this.options.debug) console.log("[ErrorTracker]", ...args);
|
|
121
91
|
}
|
|
122
92
|
|
|
123
93
|
start(): void {
|
|
124
94
|
if (this.started || typeof window === "undefined") return;
|
|
125
95
|
this.started = true;
|
|
126
|
-
|
|
127
96
|
window.addEventListener("error", this.onError);
|
|
128
97
|
window.addEventListener("unhandledrejection", this.onRejection);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
98
|
+
window.addEventListener("pagehide", this.scheduleFlush);
|
|
99
|
+
document.addEventListener("visibilitychange", this.onVisibility);
|
|
100
|
+
this.timer = setInterval(
|
|
101
|
+
this.scheduleFlush,
|
|
102
|
+
this.options.flushInterval ?? 5000,
|
|
103
|
+
);
|
|
104
|
+
this.log("Started");
|
|
134
105
|
}
|
|
135
106
|
|
|
136
107
|
stop(): void {
|
|
137
108
|
if (!this.started || typeof window === "undefined") return;
|
|
138
109
|
this.started = false;
|
|
139
|
-
|
|
140
110
|
window.removeEventListener("error", this.onError);
|
|
141
111
|
window.removeEventListener("unhandledrejection", this.onRejection);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
112
|
+
window.removeEventListener("pagehide", this.scheduleFlush);
|
|
113
|
+
document.removeEventListener("visibilitychange", this.onVisibility);
|
|
145
114
|
if (this.timer) clearInterval(this.timer);
|
|
146
115
|
this.timer = null;
|
|
147
|
-
|
|
148
|
-
this.
|
|
149
|
-
this.log("Stopped listening for errors");
|
|
116
|
+
this.scheduleFlush();
|
|
117
|
+
this.log("Stopped");
|
|
150
118
|
}
|
|
151
119
|
|
|
152
120
|
captureError(error: Error): void {
|
|
153
|
-
this.
|
|
154
|
-
{
|
|
155
|
-
kind: "error",
|
|
156
|
-
message: error.message,
|
|
157
|
-
stack: error.stack,
|
|
158
|
-
handled: true,
|
|
159
|
-
cause: serializeCause(error.cause),
|
|
160
|
-
},
|
|
161
|
-
error,
|
|
162
|
-
);
|
|
121
|
+
this.capture("error", error.message, true, error);
|
|
163
122
|
}
|
|
164
123
|
|
|
165
124
|
private onError = (event: ErrorEvent): void => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
lineno: event.lineno || undefined,
|
|
174
|
-
stack: err instanceof Error ? err.stack : undefined,
|
|
175
|
-
handled: false,
|
|
176
|
-
cause: err instanceof Error ? serializeCause(err.cause) : undefined,
|
|
177
|
-
},
|
|
178
|
-
err,
|
|
125
|
+
this.capture(
|
|
126
|
+
"error",
|
|
127
|
+
event.message || "Unknown error",
|
|
128
|
+
false,
|
|
129
|
+
event.error,
|
|
130
|
+
event.filename || "",
|
|
131
|
+
event.lineno,
|
|
179
132
|
);
|
|
180
133
|
};
|
|
181
134
|
|
|
182
135
|
private onRejection = (event: PromiseRejectionEvent): void => {
|
|
183
136
|
const reason = event.reason;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
cause: serializeCause(reason.cause),
|
|
192
|
-
},
|
|
193
|
-
reason,
|
|
194
|
-
);
|
|
137
|
+
const message =
|
|
138
|
+
reason instanceof Error
|
|
139
|
+
? reason.message
|
|
140
|
+
: typeof reason === "string"
|
|
141
|
+
? reason
|
|
142
|
+
: "Unhandled promise rejection";
|
|
143
|
+
this.capture("unhandledrejection", message, false, reason);
|
|
195
144
|
};
|
|
196
145
|
|
|
197
|
-
private
|
|
198
|
-
if (document.visibilityState === "hidden") this.
|
|
146
|
+
private onVisibility = (): void => {
|
|
147
|
+
if (document.visibilityState === "hidden") this.scheduleFlush();
|
|
199
148
|
};
|
|
200
149
|
|
|
201
|
-
private
|
|
150
|
+
private scheduleFlush = (): void => {
|
|
202
151
|
void this.flush();
|
|
203
152
|
};
|
|
204
153
|
|
|
205
|
-
private
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
154
|
+
private capture(
|
|
155
|
+
kind: Kind,
|
|
156
|
+
message: string,
|
|
157
|
+
handled: boolean,
|
|
158
|
+
source: unknown,
|
|
159
|
+
filename = "",
|
|
160
|
+
lineno: number | "" = "",
|
|
161
|
+
): void {
|
|
162
|
+
const stackText = source instanceof Error ? (source.stack ?? "") : "";
|
|
163
|
+
if (EXTENSION_RE.test(`${filename}\n${stackText}`)) return;
|
|
164
|
+
|
|
165
|
+
if (typeof source === "object" && source !== null) {
|
|
166
|
+
if (this.seen.has(source)) {
|
|
167
|
+
this.log("Skipping duplicate:", message);
|
|
218
168
|
return;
|
|
219
169
|
}
|
|
220
|
-
this.
|
|
170
|
+
this.seen.add(source);
|
|
221
171
|
}
|
|
222
172
|
|
|
223
|
-
|
|
224
|
-
|
|
173
|
+
const entry: ErrorEntry = {
|
|
174
|
+
error: kind === "unhandledrejection" ? "UnhandledRejection" : "Error",
|
|
175
|
+
message,
|
|
176
|
+
handled,
|
|
177
|
+
};
|
|
178
|
+
if (source instanceof Error) {
|
|
179
|
+
entry.stack = parseStack(source.stack);
|
|
180
|
+
const cause = serializeCause(source.cause);
|
|
181
|
+
if (cause) entry.cause = cause;
|
|
225
182
|
}
|
|
226
183
|
|
|
227
|
-
const
|
|
228
|
-
|
|
184
|
+
const key = hashKey(
|
|
185
|
+
`${kind}\0${handled ? "handled" : "unhandled"}\0${message}\0${filename}\0${lineno}\0${fingerprintCause(entry.cause)}`,
|
|
186
|
+
);
|
|
229
187
|
|
|
188
|
+
const existing = this.queue.get(key);
|
|
230
189
|
if (existing) {
|
|
231
|
-
existing.count
|
|
190
|
+
existing.count++;
|
|
232
191
|
} else {
|
|
233
|
-
this.queue.set(hash,
|
|
234
|
-
hash,
|
|
235
|
-
count: 1,
|
|
236
|
-
error:
|
|
237
|
-
data.kind === "unhandledrejection" ? "UnhandledRejection" : "Error",
|
|
238
|
-
message: data.message,
|
|
239
|
-
stack: parseStack(data.stack),
|
|
240
|
-
handled: data.handled,
|
|
241
|
-
...(data.cause ? { cause: data.cause } : {}),
|
|
242
|
-
});
|
|
192
|
+
this.queue.set(key, { hash: key, count: 1, ...entry });
|
|
243
193
|
}
|
|
244
194
|
|
|
245
|
-
this.log("Captured
|
|
195
|
+
this.log("Captured:", entry);
|
|
246
196
|
|
|
247
|
-
if (this.queue.size >= this.maxQueueSize) {
|
|
248
|
-
this.
|
|
197
|
+
if (this.queue.size >= (this.options.maxQueueSize ?? 50)) {
|
|
198
|
+
this.scheduleFlush();
|
|
249
199
|
}
|
|
250
200
|
}
|
|
251
201
|
|
|
@@ -256,22 +206,12 @@ export default class ErrorTracker {
|
|
|
256
206
|
this.queue.clear();
|
|
257
207
|
this.flushing = true;
|
|
258
208
|
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
globalThis as {
|
|
262
|
-
__SOURCEMAPS_BUILD__?: { buildId?: unknown };
|
|
263
|
-
}
|
|
264
|
-
).__SOURCEMAPS_BUILD__;
|
|
265
|
-
|
|
266
|
-
const buildId =
|
|
267
|
-
typeof sourcemapsBuild?.buildId === "string" &&
|
|
268
|
-
sourcemapsBuild.buildId.trim()
|
|
269
|
-
? sourcemapsBuild.buildId
|
|
270
|
-
: undefined;
|
|
209
|
+
const userId = getAnonymousId();
|
|
210
|
+
const buildId = readBuildId();
|
|
271
211
|
|
|
272
212
|
const payload = JSON.stringify({
|
|
273
213
|
token: this.options.siteKey,
|
|
274
|
-
...(
|
|
214
|
+
...(userId ? { userId } : {}),
|
|
275
215
|
sessionId: getOrCreateSessionId(),
|
|
276
216
|
...(buildId ? { buildId } : {}),
|
|
277
217
|
sdkName: this.options.sdkName ?? SDK_NAME,
|
|
@@ -285,21 +225,28 @@ export default class ErrorTracker {
|
|
|
285
225
|
errors,
|
|
286
226
|
});
|
|
287
227
|
|
|
288
|
-
this.log("Flushing
|
|
289
|
-
this.log("Payload:", payload);
|
|
228
|
+
this.log("Flushing:", errors);
|
|
290
229
|
|
|
291
230
|
try {
|
|
292
231
|
const sent = await sendData({
|
|
293
232
|
url: this.endpoint,
|
|
294
233
|
data: payload,
|
|
295
|
-
debug: this.debug,
|
|
234
|
+
debug: this.options.debug,
|
|
296
235
|
debugPrefix: "[ErrorTracker]",
|
|
297
236
|
});
|
|
298
|
-
if (!sent) this.
|
|
237
|
+
if (!sent) this.requeue(errors);
|
|
299
238
|
} catch {
|
|
300
|
-
this.
|
|
239
|
+
this.requeue(errors);
|
|
301
240
|
} finally {
|
|
302
241
|
this.flushing = false;
|
|
303
242
|
}
|
|
304
243
|
}
|
|
244
|
+
|
|
245
|
+
private requeue(errors: ErrorTracking[]): void {
|
|
246
|
+
for (const e of errors) {
|
|
247
|
+
const existing = this.queue.get(e.hash);
|
|
248
|
+
if (existing) existing.count += e.count;
|
|
249
|
+
else this.queue.set(e.hash, e);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
305
252
|
}
|
package/src/feature-flags.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeFeatureFlagsBaseUrl, URLS } from "./utils/api-urls";
|
|
2
2
|
|
|
3
3
|
export type FeatureFlagEvaluation = {
|
|
4
4
|
value: string;
|
|
@@ -12,6 +12,7 @@ export type FeatureFlagCheckContext = {
|
|
|
12
12
|
projectId?: string;
|
|
13
13
|
identifier?: string;
|
|
14
14
|
externalId?: string;
|
|
15
|
+
sessionId?: string;
|
|
15
16
|
attributes?: Record<string, unknown>;
|
|
16
17
|
signal?: AbortSignal;
|
|
17
18
|
};
|
|
@@ -28,6 +29,7 @@ export async function fetchFeatureFlagEvaluation(
|
|
|
28
29
|
const projectId = context.projectId;
|
|
29
30
|
const identifier = context.identifier;
|
|
30
31
|
const externalId = context.externalId;
|
|
32
|
+
const sessionId = context.sessionId;
|
|
31
33
|
|
|
32
34
|
if (!projectToken && !projectId) {
|
|
33
35
|
throw new Error("feature flag check requires projectToken or projectId");
|
|
@@ -41,6 +43,7 @@ export async function fetchFeatureFlagEvaluation(
|
|
|
41
43
|
...(projectId ? { projectId } : {}),
|
|
42
44
|
...(identifier ? { identifier } : {}),
|
|
43
45
|
...(externalId ? { externalId } : {}),
|
|
46
|
+
...(sessionId ? { sessionId } : {}),
|
|
44
47
|
};
|
|
45
48
|
|
|
46
49
|
if (context.attributes && Object.keys(context.attributes).length > 0) {
|
|
@@ -54,7 +57,7 @@ export async function fetchFeatureFlagEvaluation(
|
|
|
54
57
|
headers.Authorization = `Bearer ${projectToken}`;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const url =
|
|
60
|
+
const url = `${normalizeFeatureFlagsBaseUrl(context.baseUrl)}${URLS.flags}`;
|
|
58
61
|
const res = await fetch(url, {
|
|
59
62
|
method: "POST",
|
|
60
63
|
headers,
|
package/src/replay.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import { record } from "rrweb";
|
|
7
7
|
import type { recordOptions } from "rrweb/typings/types";
|
|
8
8
|
import type { SlimDOMOptions } from "rrweb-snapshot";
|
|
9
|
-
import {
|
|
9
|
+
import { normalizeAnalyticsBaseUrl, URLS } from "./utils/api-urls";
|
|
10
10
|
import {
|
|
11
11
|
getAnonymousId,
|
|
12
12
|
getOrCreateSessionId,
|
|
@@ -96,7 +96,7 @@ export default class ReplayTracker {
|
|
|
96
96
|
private sending = false;
|
|
97
97
|
|
|
98
98
|
constructor(private readonly options: ReplayTrackerOptions) {
|
|
99
|
-
this.endpoint =
|
|
99
|
+
this.endpoint = `${normalizeAnalyticsBaseUrl(options.baseUrl)}${URLS.replay}`;
|
|
100
100
|
this.sampled =
|
|
101
101
|
Math.random() * 100 <
|
|
102
102
|
normalizeSamplingPercentage(options.samplingPercentage);
|
package/src/utils/api-urls.ts
CHANGED
|
@@ -10,28 +10,15 @@ export function normalizeAnalyticsBaseUrl(input?: string): string {
|
|
|
10
10
|
return stripTrailingSlashes(input) || DEFAULT_BASE;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function replayEventsUrl(baseUrl?: string): string {
|
|
22
|
-
return `${normalizeAnalyticsBaseUrl(baseUrl)}/v1/replay`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function webVitalsEventsUrl(baseUrl?: string): string {
|
|
26
|
-
return `${normalizeAnalyticsBaseUrl(baseUrl)}/v1/vitals`;
|
|
27
|
-
}
|
|
13
|
+
export const URLS = {
|
|
14
|
+
events: "/v1/web",
|
|
15
|
+
identify: "/v1/identify",
|
|
16
|
+
replay: "/v1/replay",
|
|
17
|
+
vitals: "/v1/vitals",
|
|
18
|
+
flags: "/v1/check",
|
|
19
|
+
} as const;
|
|
28
20
|
|
|
29
21
|
export function normalizeFeatureFlagsBaseUrl(input?: string): string {
|
|
30
22
|
if (input === undefined || input === "") return DEFAULT_FEATURE_FLAGS_BASE;
|
|
31
23
|
return stripTrailingSlashes(input) || DEFAULT_FEATURE_FLAGS_BASE;
|
|
32
24
|
}
|
|
33
|
-
|
|
34
|
-
export function featureFlagsCheckUrl(baseUrl?: string): string {
|
|
35
|
-
const base = normalizeFeatureFlagsBaseUrl(baseUrl);
|
|
36
|
-
return `${base}/v1/check`;
|
|
37
|
-
}
|
package/src/web-vitals.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Metric, MetricWithAttribution } from "web-vitals";
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeAnalyticsBaseUrl, URLS } from "./utils/api-urls";
|
|
3
3
|
import { getOrCreateSessionId } from "./utils/identifiers";
|
|
4
4
|
import { normalizeSamplingPercentage } from "./utils/types";
|
|
5
5
|
|
|
@@ -12,64 +12,23 @@ export interface WebVitalsOptions {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
type MetricName = "CLS" | "INP" | "LCP" | "FCP" | "TTFB";
|
|
15
|
+
type AnyMetric = Metric | MetricWithAttribution;
|
|
15
16
|
|
|
16
|
-
type
|
|
17
|
+
type MetricPayload = {
|
|
18
|
+
metric: MetricName;
|
|
17
19
|
value: number;
|
|
18
20
|
attributes: Record<string, unknown>;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
|
-
type AnyMetric = Metric | MetricWithAttribution;
|
|
22
|
-
type ObserverOptions = {
|
|
23
|
-
reportAllChanges?: boolean;
|
|
24
|
-
};
|
|
25
|
-
type Observer = (
|
|
26
|
-
cb: (metric: AnyMetric) => void,
|
|
27
|
-
opts?: ObserverOptions,
|
|
28
|
-
) => void;
|
|
29
|
-
type ObserverTuple = [Observer, Observer, Observer, Observer, Observer];
|
|
30
|
-
type ObserverConfig = {
|
|
31
|
-
observe: Observer;
|
|
32
|
-
options?: ObserverOptions;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
async function loadObservers(attribution: boolean): Promise<ObserverConfig[]> {
|
|
36
|
-
const { onCLS, onFCP, onINP, onLCP, onTTFB } = attribution
|
|
37
|
-
? await import("web-vitals/attribution")
|
|
38
|
-
: await import("web-vitals");
|
|
39
|
-
return createObserverConfigs([onCLS, onFCP, onINP, onLCP, onTTFB]);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function createObserverConfigs([
|
|
43
|
-
onCLS,
|
|
44
|
-
onFCP,
|
|
45
|
-
onINP,
|
|
46
|
-
onLCP,
|
|
47
|
-
onTTFB,
|
|
48
|
-
]: ObserverTuple): ObserverConfig[] {
|
|
49
|
-
return [
|
|
50
|
-
{ observe: onCLS, options: { reportAllChanges: true } },
|
|
51
|
-
{ observe: onFCP },
|
|
52
|
-
{ observe: onINP, options: { reportAllChanges: true } },
|
|
53
|
-
{ observe: onLCP, options: { reportAllChanges: true } },
|
|
54
|
-
{ observe: onTTFB },
|
|
55
|
-
];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
23
|
export default class WebVitalsTracker {
|
|
59
24
|
private readonly endpoint: string;
|
|
60
|
-
private readonly metricsByUrl = new Map<
|
|
61
|
-
string,
|
|
62
|
-
Map<MetricName, MetricState>
|
|
63
|
-
>();
|
|
64
|
-
private readonly pendingFlushUrls = new Set<string>();
|
|
65
25
|
private readonly sampled: boolean;
|
|
26
|
+
private readonly metrics = new Map<MetricName, MetricPayload>();
|
|
66
27
|
private started = false;
|
|
67
|
-
private
|
|
68
|
-
private finalFlushRequested = false;
|
|
69
|
-
private currentUrl = "";
|
|
28
|
+
private url = "";
|
|
70
29
|
|
|
71
30
|
constructor(private readonly options: WebVitalsOptions) {
|
|
72
|
-
this.endpoint =
|
|
31
|
+
this.endpoint = `${normalizeAnalyticsBaseUrl(options.baseUrl)}${URLS.vitals}`;
|
|
73
32
|
this.sampled =
|
|
74
33
|
Math.random() * 100 <
|
|
75
34
|
normalizeSamplingPercentage(options.samplingPercentage);
|
|
@@ -83,77 +42,68 @@ export default class WebVitalsTracker {
|
|
|
83
42
|
if (this.debug) console.log("[WebVitals]", ...args);
|
|
84
43
|
}
|
|
85
44
|
|
|
86
|
-
start(): void {
|
|
45
|
+
async start(): Promise<void> {
|
|
87
46
|
if (this.started || typeof window === "undefined") return;
|
|
88
47
|
|
|
89
48
|
this.started = true;
|
|
90
|
-
this.
|
|
49
|
+
this.url = window.location.href;
|
|
91
50
|
|
|
92
|
-
document.addEventListener("visibilitychange", this.
|
|
51
|
+
document.addEventListener("visibilitychange", this.onHidden, {
|
|
93
52
|
passive: true,
|
|
94
53
|
});
|
|
95
54
|
window.addEventListener("pagehide", this.onPageHide, { passive: true });
|
|
96
55
|
|
|
56
|
+
const mod = this.options.attribution
|
|
57
|
+
? await import("web-vitals/attribution")
|
|
58
|
+
: await import("web-vitals");
|
|
59
|
+
|
|
60
|
+
if (!this.started) return;
|
|
61
|
+
|
|
62
|
+
mod.onCLS(this.capture, { reportAllChanges: true });
|
|
63
|
+
mod.onINP(this.capture, { reportAllChanges: true });
|
|
64
|
+
mod.onLCP(this.capture, { reportAllChanges: true });
|
|
65
|
+
mod.onFCP(this.capture);
|
|
66
|
+
mod.onTTFB(this.capture);
|
|
67
|
+
|
|
97
68
|
this.log("Tracking started");
|
|
98
|
-
void this.observe();
|
|
99
69
|
}
|
|
100
70
|
|
|
101
71
|
stop(): void {
|
|
102
72
|
if (!this.started || typeof window === "undefined") return;
|
|
103
73
|
|
|
104
74
|
this.started = false;
|
|
105
|
-
this.
|
|
106
|
-
this.
|
|
75
|
+
this.removeListeners();
|
|
76
|
+
void this.flush();
|
|
107
77
|
}
|
|
108
78
|
|
|
109
79
|
trackPageChange(url?: string): void {
|
|
110
80
|
if (!this.started || typeof window === "undefined") return;
|
|
111
|
-
const nextUrl = url ?? window.location.href;
|
|
112
|
-
if (nextUrl === this.currentUrl) return;
|
|
113
81
|
|
|
114
|
-
const
|
|
115
|
-
this.
|
|
116
|
-
this.queueFlush(previousUrl);
|
|
117
|
-
}
|
|
82
|
+
const nextUrl = url ?? window.location.href;
|
|
83
|
+
if (nextUrl === this.url) return;
|
|
118
84
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
window.removeEventListener("pagehide", this.onPageHide);
|
|
85
|
+
void this.flush();
|
|
86
|
+
this.url = nextUrl;
|
|
122
87
|
}
|
|
123
88
|
|
|
124
|
-
private
|
|
125
|
-
try {
|
|
126
|
-
const observers = await loadObservers(this.options.attribution ?? false);
|
|
127
|
-
|
|
128
|
-
if (!this.started) return;
|
|
129
|
-
|
|
130
|
-
for (const { observe, options } of observers) {
|
|
131
|
-
observe(this.captureMetric, options);
|
|
132
|
-
}
|
|
133
|
-
} catch (error) {
|
|
134
|
-
this.log("Failed to load web-vitals", error);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private onVisibilityChange = (): void => {
|
|
89
|
+
private onHidden = (): void => {
|
|
139
90
|
if (document.visibilityState === "hidden") {
|
|
140
|
-
this.
|
|
91
|
+
void this.flush();
|
|
141
92
|
}
|
|
142
93
|
};
|
|
143
94
|
|
|
144
95
|
private onPageHide = (): void => {
|
|
145
|
-
this.
|
|
96
|
+
void this.flush();
|
|
146
97
|
};
|
|
147
98
|
|
|
148
|
-
private
|
|
99
|
+
private capture = (metric: AnyMetric): void => {
|
|
149
100
|
if (!this.started || !this.sampled) return;
|
|
150
101
|
|
|
151
102
|
const name = metric.name as MetricName;
|
|
152
|
-
const attribution =
|
|
153
|
-
(metric as MetricWithAttribution).attribution ?? undefined;
|
|
154
|
-
const metrics = this.metricsForUrl(this.currentUrl);
|
|
103
|
+
const attribution = (metric as MetricWithAttribution).attribution;
|
|
155
104
|
|
|
156
|
-
metrics.set(name, {
|
|
105
|
+
this.metrics.set(name, {
|
|
106
|
+
metric: name,
|
|
157
107
|
value: metric.value,
|
|
158
108
|
attributes: {
|
|
159
109
|
id: metric.id,
|
|
@@ -163,99 +113,47 @@ export default class WebVitalsTracker {
|
|
|
163
113
|
...(attribution ?? {}),
|
|
164
114
|
},
|
|
165
115
|
});
|
|
166
|
-
|
|
167
|
-
this.log(`${name} captured: ${metric.value}`);
|
|
168
116
|
};
|
|
169
117
|
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
if (!metrics)
|
|
173
|
-
metrics = new Map();
|
|
174
|
-
this.metricsByUrl.set(url, metrics);
|
|
175
|
-
}
|
|
176
|
-
return metrics;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private queueFlush(url: string, final = false): void {
|
|
180
|
-
this.finalFlushRequested ||= final;
|
|
181
|
-
this.pendingFlushUrls.add(url);
|
|
182
|
-
void this.flushPending();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private async flushPending(): Promise<void> {
|
|
186
|
-
if (this.flushing) return;
|
|
187
|
-
if (typeof window === "undefined" || typeof fetch !== "function") {
|
|
188
|
-
this.finishFinalFlushIfNeeded();
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
this.flushing = true;
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
const urls = [...this.pendingFlushUrls];
|
|
196
|
-
this.pendingFlushUrls.clear();
|
|
197
|
-
for (const url of urls) {
|
|
198
|
-
if (!(await this.sendMetricsForUrl(url)))
|
|
199
|
-
this.pendingFlushUrls.add(url);
|
|
200
|
-
}
|
|
201
|
-
} finally {
|
|
202
|
-
this.flushing = false;
|
|
203
|
-
this.finishFinalFlushIfNeeded();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
private async sendMetricsForUrl(url: string): Promise<boolean> {
|
|
208
|
-
const metrics = this.metricsByUrl.get(url);
|
|
209
|
-
if (!metrics || metrics.size === 0) return true;
|
|
210
|
-
|
|
211
|
-
const vitals = [...metrics.entries()].map(([metric, data]) => ({
|
|
212
|
-
metric,
|
|
213
|
-
value: data.value,
|
|
214
|
-
attributes: data.attributes,
|
|
215
|
-
}));
|
|
216
|
-
const body = JSON.stringify({
|
|
217
|
-
sessionId: getOrCreateSessionId(),
|
|
218
|
-
vitals,
|
|
219
|
-
metadata: { url },
|
|
220
|
-
});
|
|
118
|
+
private async flush(): Promise<void> {
|
|
119
|
+
if (typeof window === "undefined" || typeof fetch !== "function") return;
|
|
120
|
+
if (!this.metrics.size) return;
|
|
221
121
|
|
|
222
|
-
|
|
122
|
+
const vitals = Array.from(this.metrics.values());
|
|
123
|
+
const url = this.url;
|
|
124
|
+
this.metrics.clear();
|
|
223
125
|
|
|
224
126
|
try {
|
|
225
127
|
const controller = new AbortController();
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
128
|
+
const timeoutId = window.setTimeout(() => controller.abort(), 3000);
|
|
129
|
+
|
|
130
|
+
const response = await fetch(this.endpoint, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
sessionId: getOrCreateSessionId(),
|
|
134
|
+
vitals,
|
|
135
|
+
metadata: { url },
|
|
136
|
+
}),
|
|
137
|
+
headers: {
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
Authorization: `Bearer ${this.options.siteKey}`,
|
|
140
|
+
},
|
|
141
|
+
keepalive: true,
|
|
142
|
+
signal: controller.signal,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
clearTimeout(timeoutId);
|
|
146
|
+
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
throw new Error(`HTTP ${response.status}`);
|
|
247
149
|
}
|
|
248
|
-
return true;
|
|
249
150
|
} catch (error) {
|
|
250
151
|
this.log("Failed to send metrics", error);
|
|
251
|
-
return false;
|
|
252
152
|
}
|
|
253
153
|
}
|
|
254
154
|
|
|
255
|
-
private
|
|
256
|
-
|
|
257
|
-
this.
|
|
258
|
-
this.started = false;
|
|
259
|
-
this.cleanupListeners();
|
|
155
|
+
private removeListeners(): void {
|
|
156
|
+
document.removeEventListener("visibilitychange", this.onHidden);
|
|
157
|
+
window.removeEventListener("pagehide", this.onPageHide);
|
|
260
158
|
}
|
|
261
159
|
}
|
package/tests/analytics.test.ts
CHANGED
|
@@ -362,6 +362,7 @@ describe("WebAnalytics lifecycle", () => {
|
|
|
362
362
|
expect(checkCall?.body).toContain('"key":"beta"');
|
|
363
363
|
expect(checkCall?.body).toContain('"externalId":"user_1"');
|
|
364
364
|
expect(checkCall?.body).toContain('"identifier":');
|
|
365
|
+
expect(checkCall?.body).toContain('"sessionId":');
|
|
365
366
|
});
|
|
366
367
|
|
|
367
368
|
test("navigation sends page leave and navigation pageview", async () => {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=`https://metrics.faststats.dev`,t=`https://flags.faststats.dev`;function n(e){return e.replace(/\/+$/,``)}function r(t){return t===void 0||t===``?e:n(t)||e}function i(e){return`${r(e)}/v1/web`}function a(e){return`${r(e)}/v1/identify`}function o(e){return`${r(e)}/v1/replay`}function s(e){return`${r(e)}/v1/vitals`}function c(e){return e===void 0||e===``?t:n(e)||t}function l(e){return`${c(e)}/v1/check`}export{o as a,c as i,a as n,i as o,r,s,l as t};
|
|
@@ -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,t as r}from"./identifiers-CQeWm7wi.js";import{t as i}from"./send-data-DL_GlsQw.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;flushing=!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.requestFlush),this.timer=setInterval(this.requestFlush,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.requestFlush),this.timer&&clearInterval(this.timer),this.timer=null,this.requestFlush(),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.requestFlush()};requestFlush=()=>{this.flush()};restoreErrors(e){for(let t of e)this.queue.set(t.hash,{...t,count:t.count+(this.queue.get(t.hash)?.count??0)})}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.requestFlush()}async flush(){if(this.flushing||this.queue.size===0)return;let e=[...this.queue.values()];this.queue.clear(),this.flushing=!0;let t=r(),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:n(),...o?{buildId:o}:{},sdkName:this.options.sdkName??`@faststats/web`,sdkVersion:this.options.sdkVersion??`0.2.12`,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);try{await i({url:this.endpoint,data:s,debug:this.debug,debugPrefix:`[ErrorTracker]`})||this.restoreErrors(e)}catch{this.restoreErrors(e)}finally{this.flushing=!1}}};export{a as n,d as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{t}from"./api-urls-BrkcoElX.js";var n=e({fetchFeatureFlagEvaluation:()=>r});async function r(e,n){if(n.projectToken&&n.projectId)throw Error(`provide either projectToken or projectId, not both`);let r=n.projectToken,i=n.projectId,a=n.identifier,o=n.externalId;if(!r&&!i)throw Error(`feature flag check requires projectToken or projectId`);if(!a&&!o)throw Error(`feature flag check requires serverId or externalId`);let s={key:e,...i?{projectId:i}:{},...a?{identifier:a}:{},...o?{externalId:o}:{}};n.attributes&&Object.keys(n.attributes).length>0&&(s.attributes=n.attributes);let c={"Content-Type":`application/json`};r&&(c.Authorization=`Bearer ${r}`);let l=t(n.baseUrl),u=await fetch(l,{method:`POST`,headers:c,body:JSON.stringify(s),credentials:`omit`,signal:n.signal});if(!u.ok){let e=``;try{let t=await u.json();typeof t.error==`string`&&t.error.length>0&&(e=` — ${t.error}`)}catch{}throw Error(`feature flag check failed: HTTP ${u.status}${e}`)}return await u.json()}export{r as n,n 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{n,r,t as i}from"./identifiers-CQeWm7wi.js";import{t as a}from"./send-data-DL_GlsQw.js";import{t as o}from"./types-CYzR5xtT.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<o(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=n(),this.startTime=r(),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??n()}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=i(),n=this.sequence++;return{token:this.options.siteKey,sessionId:this.getSessionId(),...t?{identifier:t}:{},batchId:this.createBatchId(n),sequence:n,timestamp:Date.now(),url:window.location.href,events:e}}createBatchId(e){let t=typeof crypto<`u`&&`randomUUID`in crypto?crypto.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;return`${this.getSessionId()??`unknown`}-${e}-${t}`}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 a({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
|
-
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{s as t}from"./api-urls-BrkcoElX.js";import{n}from"./identifiers-CQeWm7wi.js";import{t as r}from"./types-CYzR5xtT.js";var i=e({default:()=>s});async function a(e){let{onCLS:t,onFCP:n,onINP:r,onLCP:i,onTTFB:a}=e?await import(`web-vitals/attribution`):await import(`web-vitals`);return o([t,n,r,i,a])}function o([e,t,n,r,i]){return[{observe:e,options:{reportAllChanges:!0}},{observe:t},{observe:n,options:{reportAllChanges:!0}},{observe:r,options:{reportAllChanges:!0}},{observe:i}]}var s=class{endpoint;metricsByUrl=new Map;pendingFlushUrls=new Set;sampled;started=!1;flushing=!1;finalFlushRequested=!1;currentUrl=``;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.currentUrl=window.location.href,document.addEventListener(`visibilitychange`,this.onVisibilityChange,{passive:!0}),window.addEventListener(`pagehide`,this.onPageHide,{passive:!0}),this.log(`Tracking started`),this.observe())}stop(){!this.started||typeof window>`u`||(this.started=!1,this.cleanupListeners(),this.queueFlush(this.currentUrl,!0))}trackPageChange(e){if(!this.started||typeof window>`u`)return;let t=e??window.location.href;if(t===this.currentUrl)return;let n=this.currentUrl;this.currentUrl=t,this.queueFlush(n)}cleanupListeners(){document.removeEventListener(`visibilitychange`,this.onVisibilityChange),window.removeEventListener(`pagehide`,this.onPageHide)}async observe(){try{let e=await a(this.options.attribution??!1);if(!this.started)return;for(let{observe:t,options:n}of e)t(this.captureMetric,n)}catch(e){this.log(`Failed to load web-vitals`,e)}}onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.queueFlush(this.currentUrl,!0)};onPageHide=()=>{this.queueFlush(this.currentUrl,!0)};captureMetric=e=>{if(!this.started||!this.sampled)return;let t=e.name,n=e.attribution??void 0;this.metricsForUrl(this.currentUrl).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}`)};metricsForUrl(e){let t=this.metricsByUrl.get(e);return t||(t=new Map,this.metricsByUrl.set(e,t)),t}queueFlush(e,t=!1){this.finalFlushRequested||=t,this.pendingFlushUrls.add(e),this.flushPending()}async flushPending(){if(!this.flushing){if(typeof window>`u`||typeof fetch!=`function`){this.finishFinalFlushIfNeeded();return}this.flushing=!0;try{let e=[...this.pendingFlushUrls];this.pendingFlushUrls.clear();for(let t of e)await this.sendMetricsForUrl(t)||this.pendingFlushUrls.add(t)}finally{this.flushing=!1,this.finishFinalFlushIfNeeded()}}}async sendMetricsForUrl(e){let t=this.metricsByUrl.get(e);if(!t||t.size===0)return!0;let r=[...t.entries()].map(([e,t])=>({metric:e,value:t.value,attributes:t.attributes})),i=JSON.stringify({sessionId:n(),vitals:r,metadata:{url:e}});this.log(`Sending metrics for ${e} (${r.length})`);try{let t=new AbortController,n=window.setTimeout(()=>t.abort(),3e3);try{let n=await fetch(this.endpoint,{method:`POST`,body:i,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.options.siteKey}`},keepalive:!0,signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}`);this.metricsByUrl.delete(e)}finally{clearTimeout(n)}return!0}catch(e){return this.log(`Failed to send metrics`,e),!1}}finishFinalFlushIfNeeded(){this.finalFlushRequested&&(this.finalFlushRequested=!1,this.started=!1,this.cleanupListeners())}};export{i as n,s as t};
|