@faststats/web 0.2.3 → 0.2.5
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 +12 -0
- package/dist/analytics-CEej2XLw.js +1 -0
- package/dist/{error-DmR1BucX.js → error-uRd9K5Py.js} +1 -1
- package/dist/module.d.ts +1 -0
- package/dist/module.js +1 -1
- package/dist/{replay-B2VT_kl_.js → replay-DkZI1bwa.js} +1 -1
- package/package.json +1 -1
- package/src/analytics.ts +48 -12
- package/tests/analytics.test.ts +22 -0
- package/tests/replay.test.ts +84 -149
- package/dist/analytics-CMMFYUFq.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -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}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,2 +1,2 @@
|
|
|
1
|
-
import{m as e,n as t,r as n}from"./types-Df70G0eM.js";import{l as r}from"./analytics-
|
|
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(`
|
|
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
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-
|
|
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 +1 @@
|
|
|
1
|
-
import{n as e,p as t,r as n,t as r}from"./types-Df70G0eM.js";import{l as i}from"./analytics-
|
|
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};
|
package/package.json
CHANGED
package/src/analytics.ts
CHANGED
|
@@ -44,6 +44,7 @@ interface WebVitalsConfig {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
interface SessionReplayConfig {
|
|
47
|
+
enabled?: boolean;
|
|
47
48
|
sampling?: SamplingOptions;
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -78,6 +79,39 @@ export interface WebAnalyticsOptions {
|
|
|
78
79
|
replayOptions?: Partial<ReplayTrackerOptions>;
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
function hasReplaySamplingConfig(options: WebAnalyticsOptions): boolean {
|
|
83
|
+
return (
|
|
84
|
+
options.replayOptions?.samplingPercentage !== undefined ||
|
|
85
|
+
options.sessionReplays?.sampling?.percentage !== undefined
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function resolveReplayTrackerOptions(
|
|
90
|
+
options: WebAnalyticsOptions,
|
|
91
|
+
baseUrl: string,
|
|
92
|
+
debug: boolean,
|
|
93
|
+
): ReplayTrackerOptions | null {
|
|
94
|
+
const replayEnabled =
|
|
95
|
+
options.sessionReplays?.enabled ??
|
|
96
|
+
options.trackReplay ??
|
|
97
|
+
hasReplaySamplingConfig(options);
|
|
98
|
+
|
|
99
|
+
if (!replayEnabled) return null;
|
|
100
|
+
|
|
101
|
+
const replayOptions = options.replayOptions ?? {};
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
siteKey: options.siteKey,
|
|
105
|
+
baseUrl,
|
|
106
|
+
debug,
|
|
107
|
+
...replayOptions,
|
|
108
|
+
samplingPercentage: normalizeSamplingPercentage(
|
|
109
|
+
replayOptions.samplingPercentage ??
|
|
110
|
+
options.sessionReplays?.sampling?.percentage,
|
|
111
|
+
),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
81
115
|
let _instance: WebAnalytics | null = null;
|
|
82
116
|
let pendingConsentMode: ConsentMode | undefined;
|
|
83
117
|
|
|
@@ -427,15 +461,17 @@ export class WebAnalytics {
|
|
|
427
461
|
this.log(`failed to initialize web-vitals tracker: ${String(error)}`);
|
|
428
462
|
}
|
|
429
463
|
}
|
|
430
|
-
|
|
464
|
+
const replayOptions = resolveReplayTrackerOptions(
|
|
465
|
+
opts,
|
|
466
|
+
this.baseUrl,
|
|
467
|
+
this.debug,
|
|
468
|
+
);
|
|
469
|
+
if (replayOptions) {
|
|
431
470
|
try {
|
|
432
471
|
const { default: ReplayTrackerClass } = await import("./replay");
|
|
433
|
-
const replayTracker: ReplayTracker = new ReplayTrackerClass(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
debug: this.debug,
|
|
437
|
-
...opts.replayOptions,
|
|
438
|
-
});
|
|
472
|
+
const replayTracker: ReplayTracker = new ReplayTrackerClass(
|
|
473
|
+
replayOptions,
|
|
474
|
+
);
|
|
439
475
|
replayTracker.start();
|
|
440
476
|
this.childTrackers.push(replayTracker);
|
|
441
477
|
this.log("replay loaded");
|
|
@@ -573,7 +609,7 @@ export class WebAnalytics {
|
|
|
573
609
|
opts?: { externalId?: string; signal?: AbortSignal },
|
|
574
610
|
): Promise<FeatureFlagEvaluation> {
|
|
575
611
|
if (typeof window === "undefined" || isTrackingDisabled()) {
|
|
576
|
-
return {
|
|
612
|
+
return { value: "false" };
|
|
577
613
|
}
|
|
578
614
|
const externalId = opts?.externalId?.trim();
|
|
579
615
|
if (externalId) {
|
|
@@ -585,14 +621,14 @@ export class WebAnalytics {
|
|
|
585
621
|
signal: opts?.signal,
|
|
586
622
|
});
|
|
587
623
|
}
|
|
588
|
-
const
|
|
589
|
-
if (!
|
|
590
|
-
return {
|
|
624
|
+
const identifier = this.getAnonymousId();
|
|
625
|
+
if (!identifier) {
|
|
626
|
+
return { value: "false" };
|
|
591
627
|
}
|
|
592
628
|
return fetchFeatureFlagEvaluation(key, {
|
|
593
629
|
baseUrl: this.featureFlagsBaseUrl,
|
|
594
630
|
projectToken: this.options.siteKey,
|
|
595
|
-
|
|
631
|
+
identifier,
|
|
596
632
|
attributes,
|
|
597
633
|
signal: opts?.signal,
|
|
598
634
|
});
|
package/tests/analytics.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
getInstance,
|
|
4
|
+
resolveReplayTrackerOptions,
|
|
4
5
|
sendData,
|
|
5
6
|
setConsentMode,
|
|
6
7
|
WebAnalytics,
|
|
@@ -358,4 +359,25 @@ describe("WebAnalytics lifecycle", () => {
|
|
|
358
359
|
),
|
|
359
360
|
).toBe(true);
|
|
360
361
|
});
|
|
362
|
+
|
|
363
|
+
test("resolveReplayTrackerOptions honors sessionReplays config", () => {
|
|
364
|
+
const options = resolveReplayTrackerOptions(
|
|
365
|
+
{
|
|
366
|
+
siteKey: "site_test",
|
|
367
|
+
sessionReplays: {
|
|
368
|
+
enabled: true,
|
|
369
|
+
sampling: { percentage: 37 },
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
"https://metrics.example.com",
|
|
373
|
+
true,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
expect(options).toMatchObject({
|
|
377
|
+
siteKey: "site_test",
|
|
378
|
+
baseUrl: "https://metrics.example.com",
|
|
379
|
+
debug: true,
|
|
380
|
+
samplingPercentage: 37,
|
|
381
|
+
});
|
|
382
|
+
});
|
|
361
383
|
});
|
package/tests/replay.test.ts
CHANGED
|
@@ -17,6 +17,36 @@ class MockStorage {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
type ReplayEvent = {
|
|
21
|
+
type: number;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
data?: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type ReplayBatch = {
|
|
27
|
+
token: string;
|
|
28
|
+
sessionId?: string;
|
|
29
|
+
identifier?: string;
|
|
30
|
+
sequence: number;
|
|
31
|
+
timestamp: number;
|
|
32
|
+
url: string;
|
|
33
|
+
events: ReplayEvent[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type ReplayTrackerInternals = {
|
|
37
|
+
events: ReplayEvent[];
|
|
38
|
+
pending: ReplayBatch[];
|
|
39
|
+
retryTask: ReturnType<typeof setTimeout> | null;
|
|
40
|
+
startTime: number;
|
|
41
|
+
onEvent: (event: ReplayEvent, isCheckout?: boolean) => void;
|
|
42
|
+
flush: (lowLatency: boolean) => Promise<void>;
|
|
43
|
+
send: (
|
|
44
|
+
data: Blob,
|
|
45
|
+
isCompressed: boolean,
|
|
46
|
+
lowLatency: boolean,
|
|
47
|
+
) => Promise<boolean>;
|
|
48
|
+
};
|
|
49
|
+
|
|
20
50
|
function setGlobal(name: keyof typeof globalThis, value: unknown): void {
|
|
21
51
|
Object.defineProperty(globalThis, name, {
|
|
22
52
|
configurable: true,
|
|
@@ -66,86 +96,32 @@ afterEach(() => {
|
|
|
66
96
|
setGlobal("sessionStorage", original.sessionStorage);
|
|
67
97
|
});
|
|
68
98
|
|
|
99
|
+
function createTracker(
|
|
100
|
+
options: Partial<ConstructorParameters<typeof ReplayTracker>[0]> = {},
|
|
101
|
+
): ReplayTrackerInternals {
|
|
102
|
+
return new ReplayTracker({
|
|
103
|
+
siteKey: "site_test",
|
|
104
|
+
...options,
|
|
105
|
+
}) as unknown as ReplayTrackerInternals;
|
|
106
|
+
}
|
|
107
|
+
|
|
69
108
|
describe("ReplayTracker", () => {
|
|
70
|
-
test("
|
|
71
|
-
const tracker =
|
|
72
|
-
siteKey: "site_test",
|
|
109
|
+
test("buffers replay events", () => {
|
|
110
|
+
const tracker = createTracker({
|
|
73
111
|
maxEvents: 1000,
|
|
74
112
|
minReplayLengthMs: Number.MAX_SAFE_INTEGER,
|
|
75
|
-
}) as unknown as {
|
|
76
|
-
handleEvent: (event: unknown, isCheckout?: boolean) => void;
|
|
77
|
-
events: unknown[];
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
for (let i = 0; i < 12; i++) {
|
|
81
|
-
tracker.handleEvent({ id: i, timestamp: i }, false);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
expect(tracker.events.length).toBe(12);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("keeps pending batches for retry", () => {
|
|
88
|
-
const tracker = new ReplayTracker({
|
|
89
|
-
siteKey: "site_test",
|
|
90
|
-
}) as unknown as {
|
|
91
|
-
pendingBatches: unknown[];
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
tracker.pendingBatches.push({
|
|
95
|
-
batch: {
|
|
96
|
-
token: "site_test",
|
|
97
|
-
sessionId: "s1",
|
|
98
|
-
sequence: 1,
|
|
99
|
-
timestamp: 1,
|
|
100
|
-
url: "https://example.com/",
|
|
101
|
-
events: [],
|
|
102
|
-
},
|
|
103
|
-
isCompressed: false,
|
|
104
|
-
retries: 0,
|
|
105
113
|
});
|
|
106
|
-
expect(tracker.pendingBatches.length).toBe(1);
|
|
107
|
-
});
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
setGlobal("window", {
|
|
112
|
-
location: { href: "https://example.com/" },
|
|
113
|
-
addEventListener: () => {},
|
|
114
|
-
removeEventListener: () => {},
|
|
115
|
-
requestIdleCallback: () => {
|
|
116
|
-
idleCallbackRuns++;
|
|
117
|
-
return 1;
|
|
118
|
-
},
|
|
119
|
-
cancelIdleCallback: () => {},
|
|
120
|
-
} as unknown as Window);
|
|
121
|
-
|
|
122
|
-
const tracker = new ReplayTracker({
|
|
123
|
-
siteKey: "site_test",
|
|
124
|
-
}) as unknown as {
|
|
125
|
-
events: unknown[];
|
|
126
|
-
scheduleFlush: () => void;
|
|
127
|
-
};
|
|
115
|
+
tracker.onEvent({ type: 2, timestamp: 1, data: {} }, false);
|
|
116
|
+
tracker.onEvent({ type: 3, timestamp: 2, data: {} }, false);
|
|
128
117
|
|
|
129
|
-
tracker.events
|
|
130
|
-
tracker.scheduleFlush();
|
|
131
|
-
tracker.scheduleFlush();
|
|
132
|
-
expect(idleCallbackRuns).toBe(1);
|
|
118
|
+
expect(tracker.events.length).toBe(2);
|
|
133
119
|
});
|
|
134
120
|
|
|
135
|
-
test("
|
|
136
|
-
const tracker =
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}) as unknown as {
|
|
140
|
-
events: unknown[];
|
|
141
|
-
send: (
|
|
142
|
-
data: Blob,
|
|
143
|
-
isCompressed: boolean,
|
|
144
|
-
options?: { useBeacon?: boolean },
|
|
145
|
-
) => Promise<boolean>;
|
|
146
|
-
scheduleMinLengthFlush: () => void;
|
|
147
|
-
startTime: number;
|
|
148
|
-
};
|
|
121
|
+
test("does not flush before minimum replay length", async () => {
|
|
122
|
+
const tracker = createTracker({
|
|
123
|
+
minReplayLengthMs: Number.MAX_SAFE_INTEGER,
|
|
124
|
+
});
|
|
149
125
|
|
|
150
126
|
let sent = 0;
|
|
151
127
|
tracker.send = async () => {
|
|
@@ -153,95 +129,54 @@ describe("ReplayTracker", () => {
|
|
|
153
129
|
return true;
|
|
154
130
|
};
|
|
155
131
|
tracker.startTime = Date.now();
|
|
156
|
-
tracker.
|
|
132
|
+
tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
|
|
157
133
|
|
|
158
|
-
tracker.
|
|
159
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
134
|
+
await tracker.flush(false);
|
|
160
135
|
|
|
161
|
-
expect(sent).toBe(
|
|
162
|
-
expect(tracker.events.length).toBe(
|
|
136
|
+
expect(sent).toBe(0);
|
|
137
|
+
expect(tracker.events.length).toBe(1);
|
|
138
|
+
expect(tracker.pending.length).toBe(0);
|
|
163
139
|
});
|
|
164
140
|
|
|
165
|
-
test("
|
|
166
|
-
const tracker =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
141
|
+
test("emits replay payload when flushed", async () => {
|
|
142
|
+
const tracker = createTracker({
|
|
143
|
+
minReplayLengthMs: 0,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
let payload: ReplayBatch | null = null;
|
|
147
|
+
let lowLatency = false;
|
|
148
|
+
tracker.send = async (data, _isCompressed, nextLowLatency) => {
|
|
149
|
+
payload = JSON.parse(await data.text()) as ReplayBatch;
|
|
150
|
+
lowLatency = nextLowLatency;
|
|
151
|
+
return true;
|
|
172
152
|
};
|
|
153
|
+
tracker.startTime = Date.now() - 1000;
|
|
154
|
+
tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
|
|
155
|
+
|
|
156
|
+
await tracker.flush(true);
|
|
173
157
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
timestamp: i,
|
|
181
|
-
url: "https://example.com/",
|
|
182
|
-
events: [],
|
|
183
|
-
},
|
|
184
|
-
isCompressed: false,
|
|
185
|
-
retries: 0,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
expect(tracker.pendingBatches.length).toBe(3);
|
|
158
|
+
expect(payload).not.toBeNull();
|
|
159
|
+
expect(payload?.token).toBe("site_test");
|
|
160
|
+
expect(payload?.sessionId).toBeTruthy();
|
|
161
|
+
expect(payload?.events).toHaveLength(1);
|
|
162
|
+
expect(lowLatency).toBe(true);
|
|
163
|
+
expect(tracker.pending.length).toBe(0);
|
|
190
164
|
});
|
|
191
165
|
|
|
192
|
-
test("
|
|
193
|
-
const tracker =
|
|
194
|
-
siteKey: "site_test",
|
|
166
|
+
test("keeps failed batches queued for retry", async () => {
|
|
167
|
+
const tracker = createTracker({
|
|
195
168
|
minReplayLengthMs: 0,
|
|
196
|
-
}) as unknown as {
|
|
197
|
-
events: unknown[];
|
|
198
|
-
pendingBatches: Array<{
|
|
199
|
-
batch: {
|
|
200
|
-
token: string;
|
|
201
|
-
sessionId: string;
|
|
202
|
-
sequence: number;
|
|
203
|
-
timestamp: number;
|
|
204
|
-
url: string;
|
|
205
|
-
events: unknown[];
|
|
206
|
-
};
|
|
207
|
-
isCompressed: boolean;
|
|
208
|
-
retries: number;
|
|
209
|
-
}>;
|
|
210
|
-
sequenceNumber: number;
|
|
211
|
-
flush: (options?: { lowLatency?: boolean }) => Promise<void>;
|
|
212
|
-
send: (
|
|
213
|
-
data: Blob,
|
|
214
|
-
isCompressed: boolean,
|
|
215
|
-
options?: { useBeacon?: boolean },
|
|
216
|
-
) => Promise<boolean>;
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
tracker.sequenceNumber = 2;
|
|
220
|
-
tracker.pendingBatches.push({
|
|
221
|
-
batch: {
|
|
222
|
-
token: "site_test",
|
|
223
|
-
sessionId: "s1",
|
|
224
|
-
sequence: 1,
|
|
225
|
-
timestamp: 1,
|
|
226
|
-
url: "https://example.com/",
|
|
227
|
-
events: [{ id: "older" }],
|
|
228
|
-
},
|
|
229
|
-
isCompressed: false,
|
|
230
|
-
retries: 0,
|
|
231
169
|
});
|
|
232
|
-
tracker.events = [{ id: "newer" }];
|
|
233
170
|
|
|
234
|
-
|
|
235
|
-
tracker.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
};
|
|
171
|
+
tracker.send = async () => false;
|
|
172
|
+
tracker.startTime = Date.now() - 1000;
|
|
173
|
+
tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
|
|
174
|
+
|
|
175
|
+
await tracker.flush(false);
|
|
240
176
|
|
|
241
|
-
|
|
242
|
-
|
|
177
|
+
expect(tracker.pending.length).toBe(1);
|
|
178
|
+
expect(tracker.retryTask).not.toBeNull();
|
|
243
179
|
|
|
244
|
-
|
|
245
|
-
expect(tracker.pendingBatches.length).toBe(0);
|
|
180
|
+
if (tracker.retryTask) clearTimeout(tracker.retryTask);
|
|
246
181
|
});
|
|
247
182
|
});
|
|
@@ -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()}let m=null,h;function g(){return m}function _(e,t){typeof window>`u`||w()||m?.track(e,t??{})}function v(e,t,n){typeof window>`u`||w()||m?.identify(e,t,n??{})}function y(e=!0){typeof window>`u`||w()||m?.logout(e)}function b(e){if(m){m.setConsentMode(e);return}h=e}function x(){b(`granted`)}function S(){b(`denied`)}function C(e){typeof window>`u`||w()||m?.reportError(e)}function w(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function T(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 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`?(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,h!==void 0&&(this.consentMode=h,h=void 0),(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(w()){this.log(`disabled`);return}m=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||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}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(m&&m!==this){this.log(`already started by another instance`);return}if(w()){this.log(`disabled`);return}this.started=!0,m=this,t(this.isCookielessMode());let e=this.options;if(e.trackErrors)try{let{default:t}=await import(`./error-DmR1BucX.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)}`)}if(e.trackReplay)try{let{default:t}=await import(`./replay-B2VT_kl_.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug,...e.replayOptions});n.start(),this.childTrackers.push(n),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(),m===this&&(m=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||T({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`||w())return{enabled:!1};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,serverId:i,attributes:t,signal:n?.signal}):{enabled:!1}}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=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,...D(),...t}});this.log(e),T({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=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{y as a,C as c,_ as d,p as f,w as i,T as l,g as n,x as o,v as r,S as s,O as t,b as u};
|