@faststats/web 0.2.5 → 0.2.6

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @faststats/web
2
2
 
3
+ ## 0.2.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 7d57d50: fix: replay flushing issues
8
+
3
9
  ## 0.2.5
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1 @@
1
+ import{a as e,c as t,d as n,f as r,i,l as a,m as o,n as s,o as c,r as l,s as u,t as d,u as f}from"./types-Df70G0eM.js";async function p(e,t){if(t.projectToken&&t.projectId)throw Error(`provide either projectToken or projectId, not both`);if(t.identifier&&t.externalId)throw Error(`provide either serverId or externalId, not both`);let n=t.projectToken,r=t.projectId,i=t.identifier,o=t.externalId;if(!n&&!r)throw Error(`feature flag check requires projectToken or projectId`);if(!i&&!o)throw Error(`feature flag check requires serverId or externalId`);let s={key:e,...r?{projectId:r}:{},...i?{identifier:i}:{externalId:o}};t.attributes&&Object.keys(t.attributes).length>0&&(s.attributes=t.attributes);let c={"Content-Type":`application/json`};n&&(c.Authorization=`Bearer ${n}`);let l=a(t.baseUrl),u=await fetch(l,{method:`POST`,headers:c,body:JSON.stringify(s),credentials:`omit`,signal:t.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()}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:d(r.samplingPercentage??e.sessionReplays?.sampling?.percentage)}}let g=null,_;function v(){return g}function y(e,t){typeof window>`u`||E()||g?.track(e,t??{})}function b(e,t,n){typeof window>`u`||E()||g?.identify(e,t,n??{})}function x(e=!0){typeof window>`u`||E()||g?.logout(e)}function S(e){if(g){g.setConsentMode(e);return}_=e}function C(){S(`granted`)}function w(){S(`denied`)}function T(e){typeof window>`u`||E()||g?.reportError(e)}function E(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function D(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 O(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function k(){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 A=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(e){this.options=e,this.baseUrl=n(e.baseUrl),this.featureFlagsBaseUrl=r(e.featureFlagsBaseUrl),this.webEndpoint=o(this.baseUrl),this.debug=e.debug??!1,this.consentMode=e.consent?.mode??`granted`,this.cookielessWhilePending=e.consent?.cookielessWhilePending??!0,_!==void 0&&(this.consentMode=_,_=void 0),(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(E()){this.log(`disabled`);return}g=this,t(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||E()?!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!==this?(e.stop?.(),!1):(this.childTrackers.push(e),!0)}async startErrorTracker(){try{let{default:e}=await import(`./error-Df3cDcoP.js`);if(!this.started||this.destroyed||g!==this)return;let t=new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug});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-gcUR-TX_.js`);if(!this.started||this.destroyed||g!==this)return;let t=new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:d(this.options.webVitals?.sampling?.percentage)});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-alCILAnP.js`);if(!this.started||this.destroyed||g!==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(g&&g!==this){this.log(`already started by another instance`);return}if(E()){this.log(`disabled`);return}this.started=!0,g=this,t(this.isCookielessMode()),l();let e=this.options,n=h(e,this.baseUrl,this.debug);n&&this.startReplayTracker(n),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===this&&(g=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,n={}){if(!this.ensureStarted()||this.isCookielessMode())return;let r=e.trim(),i=t.trim();!r||!i||D({url:f(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:s(!1),externalId:r,email:i,name:n.name?.trim()||void 0,phone:n.phone?.trim()||void 0,avatarUrl:n.avatarUrl?.trim()||void 0,traits:n.traits??{}}),contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] identify`})}logout(e=!0){this.ensureStarted()&&(e&&c(this.isCookielessMode()),u())}setConsentMode(e){this.consentMode=e,t(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`||E())return{value:`false`};let r=n?.externalId?.trim();if(r)return p(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,externalId:r,attributes:t,signal:n?.signal});let i=this.getAnonymousId();return i?p(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,identifier:i,attributes:t,signal:n?.signal}):{value:`false`}}reportError(e){if(this.destroyed||typeof window>`u`||E()||!(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||E())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,...k(),...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-i()})}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}e()},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=O(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{x as a,T as c,y as d,p as f,E as i,D as l,v as n,C as o,b as r,w as s,A as t,S as u};
@@ -1,2 +1,2 @@
1
- import{m as e,n as t,r as n}from"./types-Df70G0eM.js";import{l as r}from"./analytics-CEej2XLw.js";const i=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//,a=e=>e?.split(`
1
+ import{m as e,n as t,r as n}from"./types-Df70G0eM.js";import{l as r}from"./analytics-D_dgxSVU.js";const i=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//,a=e=>e?.split(`
2
2
  `).map(e=>e.trim()).filter(Boolean);function o(e){if(e instanceof Error)return{error:e.name?.trim()||`Error`,message:e.message,stack:a(e.stack),cause:o(e.cause)};if(typeof e==`string`)return{error:`Error`,message:e}}function s(e){return e?`${e.error}\0${e.message??``}\0${s(e.cause)}`:``}function c(e){let t=[e.kind,e.handled?`handled`:`unhandled`,e.message,e.filename??``,e.lineno??``,s(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 l=class{endpoint;handled=new WeakSet;queue=new Map;timer=null;started=!1;constructor(t){this.options=t,this.endpoint=e(t.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:o(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?o(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:o(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(i.test(`${e.filename??``}\n${e.stack??``}`))return;let n=c(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:a(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 i=t(),a=globalThis.__SOURCEMAPS_BUILD__,o=typeof a?.buildId==`string`&&a.buildId.trim()?a.buildId:void 0,s=JSON.stringify({token:this.options.siteKey,...i?{userId:i}:{},sessionId:n(),...o?{buildId:o}:{},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),r({url:this.endpoint,data:s,debug:this.debug,debugPrefix:`[ErrorTracker]`})}};export{l as default};
package/dist/module.d.ts CHANGED
@@ -374,6 +374,7 @@ interface ReplayTrackerOptions {
374
374
  siteKey: string;
375
375
  baseUrl?: string;
376
376
  debug?: boolean;
377
+ compress?: boolean;
377
378
  samplingPercentage?: number;
378
379
  flushInterval?: number;
379
380
  maxEvents?: number;
@@ -492,6 +493,10 @@ declare class WebAnalytics {
492
493
  private stopHeartbeat;
493
494
  private stopNavigationTimer;
494
495
  private stopChildTrackers;
496
+ private registerChildTracker;
497
+ private startErrorTracker;
498
+ private startWebVitalsTracker;
499
+ private startReplayTracker;
495
500
  start(): Promise<void>;
496
501
  destroy(): void;
497
502
  pageview(extra?: Dict): void;
package/dist/module.js CHANGED
@@ -1 +1 @@
1
- import{f as e,l as t}from"./types-Df70G0eM.js";import{a as n,c as r,d as i,f as a,i as o,l as s,n as c,o as l,r as u,s as d,t as f,u as p}from"./analytics-CEej2XLw.js";export{f as WebAnalytics,t as featureFlagsCheckUrl,a as fetchFeatureFlagEvaluation,c as getInstance,u as identify,o as isTrackingDisabled,n as logout,e as normalizeFeatureFlagsBaseUrl,l as optIn,d as optOut,r as reportError,s as sendData,p as setConsentMode,i as trackEvent};
1
+ import{f as e,l as t}from"./types-Df70G0eM.js";import{a as n,c as r,d as i,f as a,i as o,l as s,n as c,o as l,r as u,s as d,t as f,u as p}from"./analytics-D_dgxSVU.js";export{f as WebAnalytics,t as featureFlagsCheckUrl,a as fetchFeatureFlagEvaluation,c as getInstance,u as identify,o as isTrackingDisabled,n as logout,e as normalizeFeatureFlagsBaseUrl,l as optIn,d as optOut,r as reportError,s as sendData,p as setConsentMode,i as trackEvent};
@@ -0,0 +1 @@
1
+ import{i as e,n as t,p as n,r,t as i}from"./types-Df70G0eM.js";import{l as a}from"./analytics-D_dgxSVU.js";import{getRecordConsolePlugin as o}from"@rrweb/rrweb-plugin-console-record";import{getRecordSequentialIdPlugin as s}from"@rrweb/rrweb-plugin-sequential-id-record";import{EventType as c,record as l}from"rrweb";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=[];started=!1;startTime=0;sequence=0;intervalId=null;flushTask=null;retryTask=null;minLengthFlushTask=null;stopRecording;sending=!1;constructor(e){this.options=e,this.endpoint=n(e.baseUrl),this.sampled=Math.random()*100<i(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 minReplayLengthMs(){return this.options.minReplayLengthMs??3e3}get shouldCompress(){return this.options.compress??!1}log(...e){this.debug&&console.log(`[Replay]`,...e)}start(){this.started||typeof window>`u`||!this.sampled||(this.started=!0,r(),this.startTime=e(),this.stopRecording=l({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:[s({key:`_faststatsSeqId`}),...this.options.recordConsole??!0?[o()]:[]]}),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`))}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.log(`Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush(!0),this.log(`Recording stopped`)}}getSessionId(){return 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 n=t();return{token:this.options.siteKey,sessionId:r(),...n?{identifier:n}:{},sequence:this.sequence++,timestamp:Date.now(),url:window.location.href,events:e}}async encodeBatch(e,t){let n=JSON.stringify(e);if(!this.shouldCompress||!this.compressionSupported||t)return{data:n,isCompressed:!1};try{return{data:await this.compress(n),isCompressed:!0}}catch{return this.log(`Compression failed, using uncompressed`),{data:new Blob([n],{type:`application/json`}),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.pending.push(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,e);if(!await this.send(n.data,n.isCompressed,e)){this.pending.length>=this.maxPendingBatches&&(this.log(`Pending buffer full, dropping batch ${t.sequence}`),this.pending.shift()),this.log(`Failed to send replay batch ${t.sequence}, retrying`),this.scheduleRetry();break}this.pending.shift(),e=!1}}finally{this.sending=!1}}}}scheduleRetry(){this.retryTask||=setTimeout(()=>{this.retryTask=null,this.flush(!1)},1e3)}async compress(e){let t=new TextEncoder().encode(e),n=new CompressionStream(`gzip`),r=n.writable.getWriter();await r.write(t),await r.close();let i=[],a=n.readable.getReader();for(;;){let{done:e,value:t}=await a.read();if(e)break;t&&i.push(t)}let o=i.reduce((e,t)=>e+t.length,0),s=new Uint8Array(o),c=0;for(let e of i)s.set(e,c),c+=e.length;return new Blob([s],{type:`application/octet-stream`})}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{f as default};
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "publishConfig": {
20
20
  "access": "public"
21
21
  },
22
- "version": "0.2.5",
22
+ "version": "0.2.6",
23
23
  "scripts": {
24
24
  "build": "tsdown",
25
25
  "dev": "bun run build",
package/src/analytics.ts CHANGED
@@ -407,6 +407,81 @@ export class WebAnalytics {
407
407
  this.errorTracker = null;
408
408
  }
409
409
 
410
+ private registerChildTracker(tracker: ChildTracker): boolean {
411
+ if (!this.started || this.destroyed || _instance !== this) {
412
+ tracker.stop?.();
413
+ return false;
414
+ }
415
+
416
+ this.childTrackers.push(tracker);
417
+ return true;
418
+ }
419
+
420
+ private async startErrorTracker(): Promise<void> {
421
+ try {
422
+ const { default: ErrorTrackerClass } = await import("./error");
423
+ if (!this.started || this.destroyed || _instance !== this) return;
424
+
425
+ const errorTracker: ErrorTracker = new ErrorTrackerClass({
426
+ siteKey: this.options.siteKey,
427
+ baseUrl: this.baseUrl,
428
+ debug: this.debug,
429
+ });
430
+ errorTracker.start();
431
+ if (!this.registerChildTracker(errorTracker)) return;
432
+
433
+ this.errorTracker = errorTracker;
434
+ while (this.pendingReportedErrors.length > 0) {
435
+ const pending = this.pendingReportedErrors.shift();
436
+ if (pending) errorTracker.captureError(pending);
437
+ }
438
+ this.log("error loaded");
439
+ } catch (error) {
440
+ this.log(`failed to initialize error tracker: ${String(error)}`);
441
+ }
442
+ }
443
+
444
+ private async startWebVitalsTracker(): Promise<void> {
445
+ try {
446
+ const { default: WebVitalsTrackerClass } = await import("./web-vitals");
447
+ if (!this.started || this.destroyed || _instance !== this) return;
448
+
449
+ const webVitalsTracker: WebVitalsTracker = new WebVitalsTrackerClass({
450
+ siteKey: this.options.siteKey,
451
+ baseUrl: this.baseUrl,
452
+ debug: this.debug,
453
+ samplingPercentage: normalizeSamplingPercentage(
454
+ this.options.webVitals?.sampling?.percentage,
455
+ ),
456
+ });
457
+ webVitalsTracker.start();
458
+ if (!this.registerChildTracker(webVitalsTracker)) return;
459
+
460
+ this.log("web-vitals loaded");
461
+ } catch (error) {
462
+ this.log(`failed to initialize web-vitals tracker: ${String(error)}`);
463
+ }
464
+ }
465
+
466
+ private async startReplayTracker(
467
+ replayOptions: ReplayTrackerOptions,
468
+ ): Promise<void> {
469
+ try {
470
+ const { default: ReplayTrackerClass } = await import("./replay");
471
+ if (!this.started || this.destroyed || _instance !== this) return;
472
+
473
+ const replayTracker: ReplayTracker = new ReplayTrackerClass(
474
+ replayOptions,
475
+ );
476
+ replayTracker.start();
477
+ if (!this.registerChildTracker(replayTracker)) return;
478
+
479
+ this.log("replay loaded");
480
+ } catch (error) {
481
+ this.log(`failed to initialize replay tracker: ${String(error)}`);
482
+ }
483
+ }
484
+
410
485
  async start(): Promise<void> {
411
486
  if (this.started || this.destroyed || typeof window === "undefined") return;
412
487
  if (_instance && _instance !== this) {
@@ -421,63 +496,22 @@ export class WebAnalytics {
421
496
  this.started = true;
422
497
  _instance = this;
423
498
  setCookielessMode(this.isCookielessMode());
499
+ getOrCreateSessionId();
424
500
 
425
501
  const opts = this.options;
426
- if (opts.trackErrors) {
427
- try {
428
- const { default: ErrorTrackerClass } = await import("./error");
429
- const errorTracker: ErrorTracker = new ErrorTrackerClass({
430
- siteKey: opts.siteKey,
431
- baseUrl: this.baseUrl,
432
- debug: this.debug,
433
- });
434
- errorTracker.start();
435
- this.errorTracker = errorTracker;
436
- this.childTrackers.push(errorTracker);
437
- while (this.pendingReportedErrors.length > 0) {
438
- const pending = this.pendingReportedErrors.shift();
439
- if (pending) errorTracker.captureError(pending);
440
- }
441
- this.log("error loaded");
442
- } catch (error) {
443
- this.log(`failed to initialize error tracker: ${String(error)}`);
444
- }
445
- }
446
- if (opts.trackWebVitals) {
447
- try {
448
- const { default: WebVitalsTrackerClass } = await import("./web-vitals");
449
- const webVitalsTracker: WebVitalsTracker = new WebVitalsTrackerClass({
450
- siteKey: opts.siteKey,
451
- baseUrl: this.baseUrl,
452
- debug: this.debug,
453
- samplingPercentage: normalizeSamplingPercentage(
454
- opts.webVitals?.sampling?.percentage,
455
- ),
456
- });
457
- webVitalsTracker.start();
458
- this.childTrackers.push(webVitalsTracker);
459
- this.log("web-vitals loaded");
460
- } catch (error) {
461
- this.log(`failed to initialize web-vitals tracker: ${String(error)}`);
462
- }
463
- }
464
502
  const replayOptions = resolveReplayTrackerOptions(
465
503
  opts,
466
504
  this.baseUrl,
467
505
  this.debug,
468
506
  );
469
507
  if (replayOptions) {
470
- try {
471
- const { default: ReplayTrackerClass } = await import("./replay");
472
- const replayTracker: ReplayTracker = new ReplayTrackerClass(
473
- replayOptions,
474
- );
475
- replayTracker.start();
476
- this.childTrackers.push(replayTracker);
477
- this.log("replay loaded");
478
- } catch (error) {
479
- this.log(`failed to initialize replay tracker: ${String(error)}`);
480
- }
508
+ void this.startReplayTracker(replayOptions);
509
+ }
510
+ if (opts.trackErrors) {
511
+ void this.startErrorTracker();
512
+ }
513
+ if (opts.trackWebVitals) {
514
+ void this.startWebVitalsTracker();
481
515
  }
482
516
 
483
517
  this.enterPage();
package/src/replay.ts CHANGED
@@ -6,15 +6,21 @@ import type { recordOptions } from "rrweb/typings/types";
6
6
  import type { SlimDOMOptions } from "rrweb-snapshot";
7
7
  import { sendData } from "./analytics";
8
8
  import { replayEventsUrl } from "./utils/api-urls";
9
- import { getAnonymousId, getOrCreateSessionId } from "./utils/identifiers";
9
+ import {
10
+ getAnonymousId,
11
+ getOrCreateSessionId,
12
+ getSessionStart,
13
+ } from "./utils/identifiers";
10
14
  import { normalizeSamplingPercentage } from "./utils/types";
11
15
 
12
16
  const RRWEB_SEQUENTIAL_ID_KEY = "_faststatsSeqId";
17
+ const MAX_TIMEOUT_MS = 2_147_483_647;
13
18
 
14
19
  export interface ReplayTrackerOptions {
15
20
  siteKey: string;
16
21
  baseUrl?: string;
17
22
  debug?: boolean;
23
+ compress?: boolean;
18
24
  samplingPercentage?: number;
19
25
  flushInterval?: number;
20
26
  maxEvents?: number;
@@ -78,6 +84,7 @@ export default class ReplayTracker {
78
84
  private intervalId: ReturnType<typeof setInterval> | null = null;
79
85
  private flushTask: ReturnType<typeof setTimeout> | null = null;
80
86
  private retryTask: ReturnType<typeof setTimeout> | null = null;
87
+ private minLengthFlushTask: ReturnType<typeof setTimeout> | null = null;
81
88
  private stopRecording?: listenerHandler;
82
89
  private sending = false;
83
90
 
@@ -108,6 +115,10 @@ export default class ReplayTracker {
108
115
  return this.options.minReplayLengthMs ?? 3000;
109
116
  }
110
117
 
118
+ private get shouldCompress(): boolean {
119
+ return this.options.compress ?? false;
120
+ }
121
+
111
122
  private log(...args: unknown[]): void {
112
123
  if (this.debug) console.log("[Replay]", ...args);
113
124
  }
@@ -116,7 +127,8 @@ export default class ReplayTracker {
116
127
  if (this.started || typeof window === "undefined" || !this.sampled) return;
117
128
 
118
129
  this.started = true;
119
- this.startTime = Date.now();
130
+ getOrCreateSessionId();
131
+ this.startTime = getSessionStart();
120
132
 
121
133
  this.stopRecording = record({
122
134
  emit: this.onEvent,
@@ -161,10 +173,12 @@ export default class ReplayTracker {
161
173
  if (this.intervalId) clearInterval(this.intervalId);
162
174
  if (this.flushTask) clearTimeout(this.flushTask);
163
175
  if (this.retryTask) clearTimeout(this.retryTask);
176
+ if (this.minLengthFlushTask) clearTimeout(this.minLengthFlushTask);
164
177
 
165
178
  this.intervalId = null;
166
179
  this.flushTask = null;
167
180
  this.retryTask = null;
181
+ this.minLengthFlushTask = null;
168
182
 
169
183
  window.removeEventListener("beforeunload", this.onUnload);
170
184
  window.removeEventListener("pagehide", this.onUnload);
@@ -195,7 +209,10 @@ export default class ReplayTracker {
195
209
  (event.type === EventType.FullSnapshot && this.hasReachedMinLength())
196
210
  ) {
197
211
  this.requestFlush();
212
+ return;
198
213
  }
214
+
215
+ this.scheduleMinLengthFlush();
199
216
  };
200
217
 
201
218
  private onUnload = (): void => {
@@ -218,12 +235,38 @@ export default class ReplayTracker {
218
235
  private requestFlush = (): void => {
219
236
  if (this.flushTask || this.sending || this.events.length === 0) return;
220
237
 
238
+ if (this.minLengthFlushTask) {
239
+ clearTimeout(this.minLengthFlushTask);
240
+ this.minLengthFlushTask = null;
241
+ }
242
+
221
243
  this.flushTask = setTimeout(() => {
222
244
  this.flushTask = null;
223
245
  void this.flush(false);
224
246
  }, 0);
225
247
  };
226
248
 
249
+ private scheduleMinLengthFlush(): void {
250
+ if (
251
+ this.minLengthFlushTask ||
252
+ this.events.length === 0 ||
253
+ this.hasReachedMinLength()
254
+ ) {
255
+ return;
256
+ }
257
+
258
+ const remainingMs = Math.max(
259
+ 0,
260
+ this.minReplayLengthMs - (Date.now() - this.startTime),
261
+ );
262
+ const timeoutMs = Math.min(remainingMs, MAX_TIMEOUT_MS);
263
+
264
+ this.minLengthFlushTask = setTimeout(() => {
265
+ this.minLengthFlushTask = null;
266
+ this.requestFlush();
267
+ }, timeoutMs);
268
+ }
269
+
227
270
  private createBatch(events: eventWithTime[]): ReplayBatch {
228
271
  const identifier = getAnonymousId();
229
272
 
@@ -241,12 +284,12 @@ export default class ReplayTracker {
241
284
  private async encodeBatch(
242
285
  batch: ReplayBatch,
243
286
  lowLatency: boolean,
244
- ): Promise<{ data: Blob; isCompressed: boolean }> {
287
+ ): Promise<{ data: string | Blob; isCompressed: boolean }> {
245
288
  const json = JSON.stringify(batch);
246
289
 
247
- if (!this.compressionSupported || lowLatency) {
290
+ if (!this.shouldCompress || !this.compressionSupported || lowLatency) {
248
291
  return {
249
- data: new Blob([json], { type: "application/json" }),
292
+ data: json,
250
293
  isCompressed: false,
251
294
  };
252
295
  }
@@ -270,11 +313,10 @@ export default class ReplayTracker {
270
313
 
271
314
  if (this.events.length > 0) {
272
315
  if (!this.hasReachedMinLength()) {
273
- this.log(
274
- `Too short (${Date.now() - this.startTime}ms), skipping flush`,
275
- );
316
+ this.scheduleMinLengthFlush();
276
317
  } else {
277
- this.pending.push(this.createBatch(this.events.splice(0)));
318
+ const batch = this.createBatch(this.events.splice(0));
319
+ this.pending.push(batch);
278
320
  }
279
321
  }
280
322
 
@@ -298,11 +340,10 @@ export default class ReplayTracker {
298
340
  this.log(`Pending buffer full, dropping batch ${batch.sequence}`);
299
341
  this.pending.shift();
300
342
  }
343
+ this.log(`Failed to send replay batch ${batch.sequence}, retrying`);
301
344
  this.scheduleRetry();
302
345
  break;
303
346
  }
304
-
305
- this.log(`Sent ${batch.events.length} events (seq: ${batch.sequence})`);
306
347
  this.pending.shift();
307
348
  lowLatency = false;
308
349
  }
@@ -313,7 +354,6 @@ export default class ReplayTracker {
313
354
 
314
355
  private scheduleRetry(): void {
315
356
  if (this.retryTask) return;
316
-
317
357
  this.retryTask = setTimeout(() => {
318
358
  this.retryTask = null;
319
359
  void this.flush(false);
@@ -346,17 +386,11 @@ export default class ReplayTracker {
346
386
  offset += chunk.length;
347
387
  }
348
388
 
349
- this.log(
350
- `Compressed: ${input.length} → ${output.length} bytes (${(
351
- (output.length / input.length) * 100
352
- ).toFixed(1)}%)`,
353
- );
354
-
355
389
  return new Blob([output], { type: "application/octet-stream" });
356
390
  }
357
391
 
358
392
  private send(
359
- data: Blob,
393
+ data: string | Blob,
360
394
  isCompressed: boolean,
361
395
  lowLatency: boolean,
362
396
  ): Promise<boolean> {
@@ -367,9 +401,8 @@ export default class ReplayTracker {
367
401
  contentType: isCompressed
368
402
  ? "application/octet-stream"
369
403
  : "application/json",
370
- debug: this.debug,
371
- debugPrefix: `[Replay] ${(data.size / 1024).toFixed(1)}KB`,
372
- useBeacon: lowLatency,
404
+ debug: false,
405
+ useBeacon: false,
373
406
  keepalive: lowLatency,
374
407
  }) ?? Promise.resolve(false)
375
408
  );
@@ -36,12 +36,13 @@ type ReplayBatch = {
36
36
  type ReplayTrackerInternals = {
37
37
  events: ReplayEvent[];
38
38
  pending: ReplayBatch[];
39
+ minLengthFlushTask: ReturnType<typeof setTimeout> | null;
39
40
  retryTask: ReturnType<typeof setTimeout> | null;
40
41
  startTime: number;
41
42
  onEvent: (event: ReplayEvent, isCheckout?: boolean) => void;
42
43
  flush: (lowLatency: boolean) => Promise<void>;
43
44
  send: (
44
- data: Blob,
45
+ data: string | Blob,
45
46
  isCompressed: boolean,
46
47
  lowLatency: boolean,
47
48
  ) => Promise<boolean>;
@@ -136,6 +137,28 @@ describe("ReplayTracker", () => {
136
137
  expect(sent).toBe(0);
137
138
  expect(tracker.events.length).toBe(1);
138
139
  expect(tracker.pending.length).toBe(0);
140
+
141
+ if (tracker.minLengthFlushTask) clearTimeout(tracker.minLengthFlushTask);
142
+ });
143
+
144
+ test("flushes buffered events once minimum replay length is reached", async () => {
145
+ const tracker = createTracker({
146
+ minReplayLengthMs: 20,
147
+ });
148
+
149
+ let sent = 0;
150
+ tracker.send = async () => {
151
+ sent++;
152
+ return true;
153
+ };
154
+ tracker.startTime = Date.now();
155
+ tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
156
+
157
+ await new Promise((resolve) => setTimeout(resolve, 40));
158
+
159
+ expect(sent).toBe(1);
160
+ expect(tracker.events.length).toBe(0);
161
+ expect(tracker.pending.length).toBe(0);
139
162
  });
140
163
 
141
164
  test("emits replay payload when flushed", async () => {
@@ -146,7 +169,9 @@ describe("ReplayTracker", () => {
146
169
  let payload: ReplayBatch | null = null;
147
170
  let lowLatency = false;
148
171
  tracker.send = async (data, _isCompressed, nextLowLatency) => {
149
- payload = JSON.parse(await data.text()) as ReplayBatch;
172
+ payload = JSON.parse(
173
+ typeof data === "string" ? data : await data.text(),
174
+ ) as ReplayBatch;
150
175
  lowLatency = nextLowLatency;
151
176
  return true;
152
177
  };
@@ -163,6 +188,24 @@ describe("ReplayTracker", () => {
163
188
  expect(tracker.pending.length).toBe(0);
164
189
  });
165
190
 
191
+ test("sends replay as plain JSON by default", async () => {
192
+ const tracker = createTracker({
193
+ minReplayLengthMs: 0,
194
+ });
195
+
196
+ let bodyType: "string" | "blob" | null = null;
197
+ tracker.send = async (data) => {
198
+ bodyType = typeof data === "string" ? "string" : "blob";
199
+ return true;
200
+ };
201
+ tracker.startTime = Date.now() - 1000;
202
+ tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
203
+
204
+ await tracker.flush(false);
205
+
206
+ expect(bodyType).toBe("string");
207
+ });
208
+
166
209
  test("keeps failed batches queued for retry", async () => {
167
210
  const tracker = createTracker({
168
211
  minReplayLengthMs: 0,
@@ -1 +0,0 @@
1
- import{a as e,c as t,d as n,f as r,i,l as a,m as o,n as s,o as c,r as l,s as u,t as d,u as f}from"./types-Df70G0eM.js";async function p(e,t){if(t.projectToken&&t.projectId)throw Error(`provide either projectToken or projectId, not both`);if(t.identifier&&t.externalId)throw Error(`provide either serverId or externalId, not both`);let n=t.projectToken,r=t.projectId,i=t.identifier,o=t.externalId;if(!n&&!r)throw Error(`feature flag check requires projectToken or projectId`);if(!i&&!o)throw Error(`feature flag check requires serverId or externalId`);let s={key:e,...r?{projectId:r}:{},...i?{identifier:i}:{externalId:o}};t.attributes&&Object.keys(t.attributes).length>0&&(s.attributes=t.attributes);let c={"Content-Type":`application/json`};n&&(c.Authorization=`Bearer ${n}`);let l=a(t.baseUrl),u=await fetch(l,{method:`POST`,headers:c,body:JSON.stringify(s),credentials:`omit`,signal:t.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()}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:d(r.samplingPercentage??e.sessionReplays?.sampling?.percentage)}}let g=null,_;function v(){return g}function y(e,t){typeof window>`u`||E()||g?.track(e,t??{})}function b(e,t,n){typeof window>`u`||E()||g?.identify(e,t,n??{})}function x(e=!0){typeof window>`u`||E()||g?.logout(e)}function S(e){if(g){g.setConsentMode(e);return}_=e}function C(){S(`granted`)}function w(){S(`denied`)}function T(e){typeof window>`u`||E()||g?.reportError(e)}function E(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function D(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 O(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function k(){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 A=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(e){this.options=e,this.baseUrl=n(e.baseUrl),this.featureFlagsBaseUrl=r(e.featureFlagsBaseUrl),this.webEndpoint=o(this.baseUrl),this.debug=e.debug??!1,this.consentMode=e.consent?.mode??`granted`,this.cookielessWhilePending=e.consent?.cookielessWhilePending??!0,_!==void 0&&(this.consentMode=_,_=void 0),(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(E()){this.log(`disabled`);return}g=this,t(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||E()?!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}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(g&&g!==this){this.log(`already started by another instance`);return}if(E()){this.log(`disabled`);return}this.started=!0,g=this,t(this.isCookielessMode());let e=this.options;if(e.trackErrors)try{let{default:t}=await import(`./error-uRd9K5Py.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug});for(n.start(),this.errorTracker=n,this.childTrackers.push(n);this.pendingReportedErrors.length>0;){let e=this.pendingReportedErrors.shift();e&&n.captureError(e)}this.log(`error loaded`)}catch(e){this.log(`failed to initialize error tracker: ${String(e)}`)}if(e.trackWebVitals)try{let{default:t}=await import(`./web-vitals-gcUR-TX_.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:d(e.webVitals?.sampling?.percentage)});n.start(),this.childTrackers.push(n),this.log(`web-vitals loaded`)}catch(e){this.log(`failed to initialize web-vitals tracker: ${String(e)}`)}let n=h(e,this.baseUrl,this.debug);if(n)try{let{default:e}=await import(`./replay-DkZI1bwa.js`),t=new e(n);t.start(),this.childTrackers.push(t),this.log(`replay loaded`)}catch(e){this.log(`failed to initialize replay tracker: ${String(e)}`)}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===this&&(g=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,n={}){if(!this.ensureStarted()||this.isCookielessMode())return;let r=e.trim(),i=t.trim();!r||!i||D({url:f(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:s(!1),externalId:r,email:i,name:n.name?.trim()||void 0,phone:n.phone?.trim()||void 0,avatarUrl:n.avatarUrl?.trim()||void 0,traits:n.traits??{}}),contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] identify`})}logout(e=!0){this.ensureStarted()&&(e&&c(this.isCookielessMode()),u())}setConsentMode(e){this.consentMode=e,t(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`||E())return{value:`false`};let r=n?.externalId?.trim();if(r)return p(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,externalId:r,attributes:t,signal:n?.signal});let i=this.getAnonymousId();return i?p(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,identifier:i,attributes:t,signal:n?.signal}):{value:`false`}}reportError(e){if(this.destroyed||typeof window>`u`||E()||!(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||E())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,...k(),...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-i()})}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}e()},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=O(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{x as a,T as c,y as d,p as f,E as i,D as l,v as n,C as o,b as r,w as s,A as t,S as u};
@@ -1 +0,0 @@
1
- import{n as e,p as t,r as n,t as r}from"./types-Df70G0eM.js";import{l as i}from"./analytics-CEej2XLw.js";import{getRecordConsolePlugin as a}from"@rrweb/rrweb-plugin-console-record";import{getRecordSequentialIdPlugin as o}from"@rrweb/rrweb-plugin-sequential-id-record";import{EventType as s,record as c}from"rrweb";const l={mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},u={script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0};var d=class{endpoint;compressionSupported=typeof window<`u`&&`CompressionStream`in window;sampled;events=[];pending=[];started=!1;startTime=0;sequence=0;intervalId=null;flushTask=null;retryTask=null;stopRecording;sending=!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}get flushInterval(){return this.options.flushInterval??1e4}get maxEvents(){return this.options.maxEvents??500}get maxPendingBatches(){return this.options.maxPendingBatches??30}get minReplayLengthMs(){return this.options.minReplayLengthMs??3e3}log(...e){this.debug&&console.log(`[Replay]`,...e)}start(){this.started||typeof window>`u`||!this.sampled||(this.started=!0,this.startTime=Date.now(),this.stopRecording=c({emit:this.onEvent,sampling:this.options.sampling??l,slimDOMOptions:this.options.slimDOMOptions??u,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:[o({key:`_faststatsSeqId`}),...this.options.recordConsole??!0?[a()]:[]]}),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`))}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.intervalId=null,this.flushTask=null,this.retryTask=null,window.removeEventListener(`beforeunload`,this.onUnload),window.removeEventListener(`pagehide`,this.onUnload),document.removeEventListener(`visibilitychange`,this.onVisibilityChange),!this.hasReachedMinLength()){this.events.length=0,this.log(`Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush(!0),this.log(`Recording stopped`)}}getSessionId(){return n()}onEvent=(e,t)=>{this.events.push(e),(t||this.events.length>=this.maxEvents||e.type===s.FullSnapshot&&this.hasReachedMinLength())&&this.requestFlush()};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.flushTask=setTimeout(()=>{this.flushTask=null,this.flush(!1)},0))};createBatch(t){let r=e();return{token:this.options.siteKey,sessionId:n(),...r?{identifier:r}:{},sequence:this.sequence++,timestamp:Date.now(),url:window.location.href,events:t}}async encodeBatch(e,t){let n=JSON.stringify(e);if(!this.compressionSupported||t)return{data:new Blob([n],{type:`application/json`}),isCompressed:!1};try{return{data:await this.compress(n),isCompressed:!0}}catch{return this.log(`Compression failed, using uncompressed`),{data:new Blob([n],{type:`application/json`}),isCompressed:!1}}}async flush(e){if(!this.sending&&(this.events.length>0&&(this.hasReachedMinLength()?this.pending.push(this.createBatch(this.events.splice(0))):this.log(`Too short (${Date.now()-this.startTime}ms), skipping flush`)),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,e);if(!await this.send(n.data,n.isCompressed,e)){this.pending.length>=this.maxPendingBatches&&(this.log(`Pending buffer full, dropping batch ${t.sequence}`),this.pending.shift()),this.scheduleRetry();break}this.log(`Sent ${t.events.length} events (seq: ${t.sequence})`),this.pending.shift(),e=!1}}finally{this.sending=!1}}}scheduleRetry(){this.retryTask||=setTimeout(()=>{this.retryTask=null,this.flush(!1)},1e3)}async compress(e){let t=new TextEncoder().encode(e),n=new CompressionStream(`gzip`),r=n.writable.getWriter();await r.write(t),await r.close();let i=[],a=n.readable.getReader();for(;;){let{done:e,value:t}=await a.read();if(e)break;t&&i.push(t)}let o=i.reduce((e,t)=>e+t.length,0),s=new Uint8Array(o),c=0;for(let e of i)s.set(e,c),c+=e.length;return this.log(`Compressed: ${t.length} → ${s.length} bytes (${(s.length/t.length*100).toFixed(1)}%)`),new Blob([s],{type:`application/octet-stream`})}send(e,t,n){return i({url:t?`${this.endpoint}?encoding=gzip`:this.endpoint,data:e,contentType:t?`application/octet-stream`:`application/json`,debug:this.debug,debugPrefix:`[Replay] ${(e.size/1024).toFixed(1)}KB`,useBeacon:n,keepalive:n})??Promise.resolve(!1)}};export{d as default};