@faststats/web 0.1.6 → 0.1.8
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-C9dKSMp9.js +1 -0
- package/dist/{error-DP65ICdP.js → error-B84WzvRo.js} +1 -1
- package/dist/module.d.ts +2 -0
- package/dist/module.js +1 -1
- package/dist/replay-CE1ZpeYS.js +1 -0
- package/package.json +1 -1
- package/src/analytics.ts +4 -1
- package/src/replay.ts +60 -5
- package/src/utils/types.ts +2 -0
- package/tests/analytics.test.ts +11 -0
- package/tests/replay.test.ts +86 -0
- package/dist/analytics-c6ZVX6pa.js +0 -1
- package/dist/replay-DveDhM7v.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,f as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./types-8FXsUqbi.js";let d=null,f;function p(){return d}function m(e,t){typeof window>`u`||x()||d?.track(e,t??{})}function h(e,t,n){typeof window>`u`||x()||d?.identify(e,t,n??{})}function g(e=!0){typeof window>`u`||x()||d?.logout(e)}function _(e){if(d){d.setConsentMode(e);return}f=e}function v(){_(`granted`)}function y(){_(`denied`)}function b(e){typeof window>`u`||x()||d?.reportError(e)}function x(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function S(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 C(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function w(){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 T=class{webEndpoint;baseUrl;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=u(e.baseUrl),this.webEndpoint=n(this.baseUrl),this.debug=e.debug??!1,this.consentMode=e.consent?.mode??`granted`,this.cookielessWhilePending=e.consent?.cookielessWhilePending??!0,f!==void 0&&(this.consentMode=f,f=void 0),(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(x()){this.log(`disabled`);return}d=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||x()?!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}getErrorTracker(){return this.errorTracker}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(d&&d!==this){this.log(`already started by another instance`);return}if(x()){this.log(`disabled`);return}this.started=!0,d=this,t(this.isCookielessMode());let e=this.options;if(e.trackErrors)try{let{default:t}=await import(`./error-B84WzvRo.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-C7H69Xv3.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:l(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-CE1ZpeYS.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(),d===this&&(d=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(),o=t.trim();!r||!o||S({url:i(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:a(!1),externalId:r,email:o,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&&o(this.isCookielessMode()),c())}setConsentMode(e){this.consentMode=e,t(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return a(this.isCookielessMode())}getSessionId(){return s()}reportError(e){if(this.destroyed||typeof window>`u`||x()||!(this.options.trackErrors??!1)||!this.ensureStarted())return;let t=this.getErrorTracker();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||x())return;let n=a(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:s(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...w(),...t}});this.log(e),S({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-r()})}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=C(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{g as a,b as c,m as d,x as i,S as l,p as n,v as o,h as r,y as s,T as t,_ as u};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{f as e,n as t,r as n}from"./types-8FXsUqbi.js";import{l as r}from"./analytics-
|
|
1
|
+
import{f as e,n as t,r as n}from"./types-8FXsUqbi.js";import{l as r}from"./analytics-C9dKSMp9.js";const i=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//;function a(e){if(e)return e.split(`
|
|
2
2
|
`).map(e=>e.trim()).filter(e=>e.length>0)}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.type,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;siteKey;debug;flushInterval;maxQueueSize;handledErrors=new WeakSet;errorCounts=new Map;handleVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush()};handlePageHide=()=>{this.flush()};flushTimer=null;started=!1;constructor(t){this.siteKey=t.siteKey,this.endpoint=e(t.baseUrl),this.debug=t.debug??!1,this.flushInterval=t.flushInterval??5e3,this.maxQueueSize=t.maxQueueSize??50}start(){this.started||typeof window>`u`||(this.started=!0,window.addEventListener(`error`,this.handleErrorEvent),window.addEventListener(`unhandledrejection`,this.handleRejection),this.flushTimer=setInterval(()=>this.flush(),this.flushInterval),document.addEventListener(`visibilitychange`,this.handleVisibilityChange),window.addEventListener(`pagehide`,this.handlePageHide),this.debug&&console.log(`[ErrorTracker] Started listening for errors`))}stop(){!this.started||typeof window>`u`||(this.started=!1,window.removeEventListener(`error`,this.handleErrorEvent),window.removeEventListener(`unhandledrejection`,this.handleRejection),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),window.removeEventListener(`pagehide`,this.handlePageHide),this.flushTimer&&=(clearInterval(this.flushTimer),null),this.flush(),this.debug&&console.log(`[ErrorTracker] Stopped listening for errors`))}skipDuplicate(e){return e instanceof Error?this.handledErrors.has(e)?!0:(this.handledErrors.add(e),!1):!1}handleErrorEvent=e=>{let t=e.error;if(this.skipDuplicate(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate error:`,t.message);return}this.queueError({message:e.message||(t instanceof Error?t.message:`Unknown error`),filename:e.filename||void 0,lineno:e.lineno||void 0,colno:e.colno||void 0,stack:t instanceof Error?t.stack:void 0,type:`error`,handled:!1,cause:t instanceof Error?o(t.cause):void 0})};handleRejection=e=>{let t=e.reason;if(this.skipDuplicate(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate rejection:`,t instanceof Error?t.message:t);return}let n=t instanceof Error?t.message:typeof t==`string`?t:`Unhandled promise rejection`;this.queueError({message:n,stack:t instanceof Error?t.stack:void 0,type:`unhandledrejection`,handled:!1,cause:t instanceof Error?o(t.cause):void 0})};queueError(e){if(i.test(`${e.filename??``}\n${e.stack??``}`))return;this.debug&&console.log(`[ErrorTracker] Captured error:`,e);let t=c(e),n=this.errorCounts.get(t);if(n)n.count++,this.debug&&console.log(`[ErrorTracker] Incremented count for ${t} to ${n.count}`);else{let n={error:e.type===`unhandledrejection`?`UnhandledRejection`:`Error`,message:e.message,stack:a(e.stack),handled:e.handled,...e.cause?{cause:e.cause}:{}};this.errorCounts.set(t,{entry:n,count:1}),this.debug&&console.log(`[ErrorTracker] Queued new error: ${t}`)}this.errorCounts.size>=this.maxQueueSize&&this.flush()}captureError(e){if(this.skipDuplicate(e)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate:`,e.message);return}this.queueError({message:e.message,stack:e.stack,type:`error`,handled:!0,cause:o(e.cause)})}flush(){if(this.errorCounts.size===0)return;let e=[];for(let[t,{entry:n,count:r}]of this.errorCounts)e.push({...n,hash:t,count:r});this.errorCounts.clear(),this.debug&&console.log(`[ErrorTracker] Flushing errors:`,e);let i=t(),a=globalThis.__SOURCEMAPS_BUILD__,o=typeof a?.buildId==`string`&&a.buildId.trim().length>0?a.buildId:void 0,s=JSON.stringify({token:this.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.debug&&console.log(`[ErrorTracker] 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"./types-8FXsUqbi.js";import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./analytics-
|
|
1
|
+
import"./types-8FXsUqbi.js";import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./analytics-C9dKSMp9.js";export{l as WebAnalytics,a as getInstance,s as identify,r as isTrackingDisabled,e as logout,o as optIn,c as optOut,t as reportError,i as sendData,u as setConsentMode,n as trackEvent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as e,n as t,r as n,t as r}from"./types-8FXsUqbi.js";import{l as i}from"./analytics-C9dKSMp9.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";var l=class{endpoint;siteKey;debug;flushInterval;maxEvents;sampling;slimDOMOptions;maskAllInputs;maskInputOptions;blockClass;blockSelector;maskTextClass;maskTextSelector;checkoutEveryNms;checkoutEveryNth;samplingPercentage;recordConsole;events=[];flushTimer=null;stopRecording=void 0;started=!1;startTime=0;minLengthFlushScheduled=!1;sequenceNumber=0;pendingBatches=[];isFlushing=!1;compressionSupported=!1;sessionSamplingSeed;minReplayLengthMs;maxPendingBatches;flushScheduled=!1;flushTimeout=null;idleFlushId=null;minLengthFlushTimer=null;pendingRetryTimer=null;isProcessingPending=!1;constructor(t){this.siteKey=t.siteKey,this.endpoint=e(t.baseUrl),this.debug=t.debug??!1,this.samplingPercentage=r(t.samplingPercentage),this.flushInterval=t.flushInterval??1e4,this.maxEvents=t.maxEvents??500,this.sampling=t.sampling??{mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},this.slimDOMOptions=t.slimDOMOptions??{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0},this.maskAllInputs=t.maskAllInputs??!0,this.maskInputOptions=t.maskInputOptions??{password:!0,email:!0,tel:!0},this.blockClass=t.blockClass,this.blockSelector=t.blockSelector,this.maskTextClass=t.maskTextClass,this.maskTextSelector=t.maskTextSelector,this.checkoutEveryNms=t.checkoutEveryNms??6e4,this.checkoutEveryNth=t.checkoutEveryNth,this.recordConsole=t.recordConsole??!0,this.minReplayLengthMs=t.minReplayLengthMs??3e3,this.maxPendingBatches=t.maxPendingBatches??30,this.sessionSamplingSeed=Math.random()*100,typeof window<`u`&&(this.compressionSupported=`CompressionStream`in window)}start(){if(this.started||typeof window>`u`||this.samplingPercentage<100&&this.sessionSamplingSeed>=this.samplingPercentage)return;this.started=!0,this.startTime=Date.now(),this.debug&&console.log(`[Replay] Recording started`);let e={emit:(e,t)=>this.handleEvent(e,t),sampling:this.sampling,slimDOMOptions:this.slimDOMOptions,maskAllInputs:this.maskAllInputs,checkoutEveryNms:this.checkoutEveryNms};this.maskInputOptions&&(e.maskInputOptions=this.maskInputOptions),this.blockClass&&(e.blockClass=this.blockClass),this.blockSelector&&(e.blockSelector=this.blockSelector),this.maskTextClass&&(e.maskTextClass=this.maskTextClass),this.maskTextSelector&&(e.maskTextSelector=this.maskTextSelector),this.checkoutEveryNth&&(e.checkoutEveryNth=this.checkoutEveryNth),e.plugins=[o({key:`_faststatsSeqId`}),...this.recordConsole?[a()]:[]],this.stopRecording=c(e),this.flushTimer=setInterval(()=>{this.scheduleFlush()},this.flushInterval),window.addEventListener(`beforeunload`,this.handleUnload),window.addEventListener(`pagehide`,this.handleUnload),document.addEventListener(`visibilitychange`,this.handleVisibilityChange),this.scheduleMinLengthFlush()}stop(){if(this.started){if(this.started=!1,this.debug&&console.log(`[Replay] Recording stopped`),this.stopRecording?.(),this.stopRecording=void 0,this.clearScheduledFlush(),this.clearMinLengthFlushTimer(),this.clearPendingRetryTimer(),this.flushTimer&&=(clearInterval(this.flushTimer),null),window.removeEventListener(`beforeunload`,this.handleUnload),window.removeEventListener(`pagehide`,this.handleUnload),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),!this.hasReachedMinLength()){this.events=[],this.debug&&console.log(`[Replay] Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush()}}handleEvent(e,t){this.events.push(e),t||this.events.length>=this.maxEvents?this.scheduleFlush():(e.type===s.FullSnapshot&&this.hasReachedMinLength()||!this.minLengthFlushScheduled&&this.hasReachedMinLength())&&(this.minLengthFlushScheduled=!0,this.scheduleFlush())}hasReachedMinLength(){return this.minReplayLengthMs<=0?!0:Date.now()-this.startTime>=this.minReplayLengthMs}scheduleFlush(){if(this.isFlushing||this.events.length===0||this.flushScheduled)return;this.flushScheduled=!0;let e=()=>{this.flushScheduled=!1,this.flushTimeout=null,this.idleFlushId=null,this.flush()};if(typeof window<`u`&&`requestIdleCallback`in window){this.idleFlushId=window.requestIdleCallback(e,{timeout:2e3});return}this.flushTimeout=setTimeout(e,0)}handleUnload=()=>{this.clearScheduledFlush(),this.flush({lowLatency:!0})};handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.flushTimer&&=(clearInterval(this.flushTimer),null),this.clearScheduledFlush(),this.flush({lowLatency:!0})):document.visibilityState===`visible`&&this.started&&(this.flushTimer||=setInterval(()=>{this.scheduleFlush()},this.flushInterval))};clearScheduledFlush(){this.idleFlushId!==null&&typeof window<`u`&&`cancelIdleCallback`in window&&window.cancelIdleCallback(this.idleFlushId),this.idleFlushId=null,this.flushTimeout&&=(clearTimeout(this.flushTimeout),null),this.flushScheduled=!1}scheduleMinLengthFlush(){this.minReplayLengthMs<=0||(this.clearMinLengthFlushTimer(),this.minLengthFlushTimer=setTimeout(()=>{this.minLengthFlushTimer=null,this.events.length!==0&&(this.minLengthFlushScheduled=!0,this.scheduleFlush())},this.minReplayLengthMs))}clearMinLengthFlushTimer(){this.minLengthFlushTimer&&=(clearTimeout(this.minLengthFlushTimer),null)}clearPendingRetryTimer(){this.pendingRetryTimer&&=(clearTimeout(this.pendingRetryTimer),null)}queuePendingBatch(e){if(this.pendingBatches.length>=this.maxPendingBatches){this.debug&&console.warn(`[Replay] Pending batch buffer full, dropping batch ${e.batch.sequence}`);return}this.pendingBatches.push(e)}schedulePendingRetry(e){this.pendingRetryTimer||=setTimeout(()=>{this.pendingRetryTimer=null,this.processPendingBatches()},e)}async flush(e={}){if(this.events.length===0){this.processPendingBatches();return}if(!this.hasReachedMinLength()){this.debug&&console.log(`[Replay] Too short (${Date.now()-this.startTime}ms), skipping`),this.processPendingBatches();return}if(this.isFlushing)return;this.isFlushing=!0;let r=this.events;this.events=[];let i=t(),a={token:this.siteKey,sessionId:n(),...i?{identifier:i}:{},sequence:this.sequenceNumber++,timestamp:Date.now(),url:window.location.href,events:r};if(this.pendingBatches.length>0||this.isProcessingPending){this.queuePendingBatch({batch:a,isCompressed:this.compressionSupported&&!e.lowLatency,retries:0}),this.isFlushing=!1,this.processPendingBatches();return}this.debug&&console.log(`[Replay] Sending ${r.length} events (seq: ${a.sequence})`);try{let t,n=!1;if(this.compressionSupported&&!e.lowLatency)try{t=await this.compress(JSON.stringify(a)),n=!0}catch{this.debug&&console.warn(`[Replay] Compression failed, using uncompressed`),t=new Blob([JSON.stringify(a)],{type:`application/json`})}else t=new Blob([JSON.stringify(a)],{type:`application/json`});await this.send(t,n,{useBeacon:e.lowLatency===!0})||this.queuePendingBatch({batch:a,isCompressed:n,retries:0})}catch(e){this.debug&&console.warn(`[Replay] Flush error:`,e),this.queuePendingBatch({batch:a,isCompressed:!1,retries:0})}finally{this.isFlushing=!1,this.processPendingBatches()}}async processPendingBatches(){if(!(this.isProcessingPending||this.pendingBatches.length===0)){this.clearPendingRetryTimer(),this.isProcessingPending=!0;try{for(;this.pendingBatches.length>0;){let e=this.pendingBatches[0];if(!e)break;if(e.retries>=3){this.debug&&console.warn(`[Replay] Max retries reached, restoring batch ${e.batch.sequence} to buffer`),this.pendingBatches.shift(),this.events=[...e.batch.events,...this.events],this.scheduleFlush();continue}e.retries++;let t;if(e.isCompressed&&this.compressionSupported)try{t=await this.compress(JSON.stringify(e.batch))}catch{t=new Blob([JSON.stringify(e.batch)],{type:`application/json`}),e.isCompressed=!1}else t=new Blob([JSON.stringify(e.batch)],{type:`application/json`});if(await this.send(t,e.isCompressed,{useBeacon:!1})){this.pendingBatches.shift();continue}this.schedulePendingRetry(1e3*e.retries);break}}catch{let e=this.pendingBatches[0];this.debug&&e&&console.warn(`[Replay] Retry ${e.retries} failed`),this.schedulePendingRetry(e?1e3*e.retries:1e3)}finally{this.isProcessingPending=!1}}}async compress(e){if(!this.compressionSupported)throw Error(`Compression not supported`);let t=new TextEncoder().encode(e),n=new CompressionStream(`gzip`),r=n.writable.getWriter();r.write(t),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;if(this.debug){let e=(s.length/t.length*100).toFixed(1);console.log(`[Replay] Compressed: ${t.length} → ${s.length} bytes (${e}%)`)}return new Blob([s],{type:`application/octet-stream`})}async send(e,t,n={}){let r=t?`${this.endpoint}?encoding=gzip`:this.endpoint,a=(e.size/1024).toFixed(1);return i({url:r,data:e,contentType:t?`application/octet-stream`:`application/json`,headers:t?{"Content-Encoding":`gzip`}:void 0,debug:this.debug,debugPrefix:`[Replay] ${a}KB`,useBeacon:n.useBeacon??!1,keepalive:n.useBeacon===!0})??Promise.resolve(!1)}getSessionId(){return n()}};export{l as default};
|
package/package.json
CHANGED
package/src/analytics.ts
CHANGED
|
@@ -136,9 +136,12 @@ export async function sendData(options: SendDataOptions): Promise<boolean> {
|
|
|
136
136
|
headers = {},
|
|
137
137
|
debug = false,
|
|
138
138
|
debugPrefix = "[Analytics]",
|
|
139
|
+
useBeacon = true,
|
|
140
|
+
keepalive = true,
|
|
139
141
|
} = options;
|
|
140
142
|
|
|
141
143
|
const canUseBeacon =
|
|
144
|
+
useBeacon &&
|
|
142
145
|
typeof navigator !== "undefined" &&
|
|
143
146
|
typeof navigator.sendBeacon === "function";
|
|
144
147
|
if (canUseBeacon) {
|
|
@@ -173,7 +176,7 @@ export async function sendData(options: SendDataOptions): Promise<boolean> {
|
|
|
173
176
|
"Content-Type": contentType,
|
|
174
177
|
...headers,
|
|
175
178
|
},
|
|
176
|
-
keepalive
|
|
179
|
+
keepalive,
|
|
177
180
|
});
|
|
178
181
|
const success = response.ok;
|
|
179
182
|
if (debug) {
|
package/src/replay.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getRecordConsolePlugin } from "@rrweb/rrweb-plugin-console-record";
|
|
2
2
|
import { getRecordSequentialIdPlugin } from "@rrweb/rrweb-plugin-sequential-id-record";
|
|
3
3
|
import type { eventWithTime, listenerHandler } from "@rrweb/types";
|
|
4
|
-
import { record } from "rrweb";
|
|
4
|
+
import { EventType, record } from "rrweb";
|
|
5
5
|
import type { recordOptions } from "rrweb/typings/types";
|
|
6
6
|
import type { SlimDOMOptions } from "rrweb-snapshot";
|
|
7
7
|
import { sendData } from "./analytics";
|
|
@@ -81,6 +81,7 @@ class ReplayTracker {
|
|
|
81
81
|
private flushScheduled = false;
|
|
82
82
|
private flushTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
83
83
|
private idleFlushId: number | null = null;
|
|
84
|
+
private minLengthFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
84
85
|
private pendingRetryTimer: ReturnType<typeof setTimeout> | null = null;
|
|
85
86
|
private isProcessingPending = false;
|
|
86
87
|
|
|
@@ -191,6 +192,7 @@ class ReplayTracker {
|
|
|
191
192
|
window.addEventListener("beforeunload", this.handleUnload);
|
|
192
193
|
window.addEventListener("pagehide", this.handleUnload);
|
|
193
194
|
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
195
|
+
this.scheduleMinLengthFlush();
|
|
194
196
|
}
|
|
195
197
|
|
|
196
198
|
stop(): void {
|
|
@@ -204,6 +206,7 @@ class ReplayTracker {
|
|
|
204
206
|
this.stopRecording?.();
|
|
205
207
|
this.stopRecording = undefined;
|
|
206
208
|
this.clearScheduledFlush();
|
|
209
|
+
this.clearMinLengthFlushTimer();
|
|
207
210
|
this.clearPendingRetryTimer();
|
|
208
211
|
|
|
209
212
|
if (this.flushTimer) {
|
|
@@ -238,6 +241,12 @@ class ReplayTracker {
|
|
|
238
241
|
this.scheduleFlush();
|
|
239
242
|
} else if (this.events.length >= this.maxEvents) {
|
|
240
243
|
this.scheduleFlush();
|
|
244
|
+
} else if (
|
|
245
|
+
event.type === EventType.FullSnapshot &&
|
|
246
|
+
this.hasReachedMinLength()
|
|
247
|
+
) {
|
|
248
|
+
this.minLengthFlushScheduled = true;
|
|
249
|
+
this.scheduleFlush();
|
|
241
250
|
} else if (!this.minLengthFlushScheduled && this.hasReachedMinLength()) {
|
|
242
251
|
this.minLengthFlushScheduled = true;
|
|
243
252
|
this.scheduleFlush();
|
|
@@ -311,6 +320,26 @@ class ReplayTracker {
|
|
|
311
320
|
this.flushScheduled = false;
|
|
312
321
|
}
|
|
313
322
|
|
|
323
|
+
private scheduleMinLengthFlush(): void {
|
|
324
|
+
if (this.minReplayLengthMs <= 0) return;
|
|
325
|
+
|
|
326
|
+
this.clearMinLengthFlushTimer();
|
|
327
|
+
this.minLengthFlushTimer = setTimeout(() => {
|
|
328
|
+
this.minLengthFlushTimer = null;
|
|
329
|
+
if (this.events.length === 0) return;
|
|
330
|
+
|
|
331
|
+
this.minLengthFlushScheduled = true;
|
|
332
|
+
this.scheduleFlush();
|
|
333
|
+
}, this.minReplayLengthMs);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private clearMinLengthFlushTimer(): void {
|
|
337
|
+
if (this.minLengthFlushTimer) {
|
|
338
|
+
clearTimeout(this.minLengthFlushTimer);
|
|
339
|
+
this.minLengthFlushTimer = null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
314
343
|
private clearPendingRetryTimer(): void {
|
|
315
344
|
if (this.pendingRetryTimer) {
|
|
316
345
|
clearTimeout(this.pendingRetryTimer);
|
|
@@ -320,7 +349,12 @@ class ReplayTracker {
|
|
|
320
349
|
|
|
321
350
|
private queuePendingBatch(batch: PendingBatch): void {
|
|
322
351
|
if (this.pendingBatches.length >= this.maxPendingBatches) {
|
|
323
|
-
this.
|
|
352
|
+
if (this.debug) {
|
|
353
|
+
console.warn(
|
|
354
|
+
`[Replay] Pending batch buffer full, dropping batch ${batch.batch.sequence}`,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return;
|
|
324
358
|
}
|
|
325
359
|
this.pendingBatches.push(batch);
|
|
326
360
|
}
|
|
@@ -366,6 +400,17 @@ class ReplayTracker {
|
|
|
366
400
|
events: eventsToSend,
|
|
367
401
|
};
|
|
368
402
|
|
|
403
|
+
if (this.pendingBatches.length > 0 || this.isProcessingPending) {
|
|
404
|
+
this.queuePendingBatch({
|
|
405
|
+
batch,
|
|
406
|
+
isCompressed: this.compressionSupported && !options.lowLatency,
|
|
407
|
+
retries: 0,
|
|
408
|
+
});
|
|
409
|
+
this.isFlushing = false;
|
|
410
|
+
void this.processPendingBatches();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
369
414
|
if (this.debug) {
|
|
370
415
|
console.log(
|
|
371
416
|
`[Replay] Sending ${eventsToSend.length} events (seq: ${batch.sequence})`,
|
|
@@ -394,7 +439,9 @@ class ReplayTracker {
|
|
|
394
439
|
});
|
|
395
440
|
}
|
|
396
441
|
|
|
397
|
-
const success = await this.send(compressed, isCompressed
|
|
442
|
+
const success = await this.send(compressed, isCompressed, {
|
|
443
|
+
useBeacon: options.lowLatency === true,
|
|
444
|
+
});
|
|
398
445
|
if (!success) {
|
|
399
446
|
this.queuePendingBatch({
|
|
400
447
|
batch,
|
|
@@ -459,7 +506,9 @@ class ReplayTracker {
|
|
|
459
506
|
});
|
|
460
507
|
}
|
|
461
508
|
|
|
462
|
-
const success = await this.send(blob, batch.isCompressed
|
|
509
|
+
const success = await this.send(blob, batch.isCompressed, {
|
|
510
|
+
useBeacon: false,
|
|
511
|
+
});
|
|
463
512
|
if (success) {
|
|
464
513
|
this.pendingBatches.shift();
|
|
465
514
|
continue;
|
|
@@ -522,7 +571,11 @@ class ReplayTracker {
|
|
|
522
571
|
return new Blob([compressed], { type: "application/octet-stream" });
|
|
523
572
|
}
|
|
524
573
|
|
|
525
|
-
private async send(
|
|
574
|
+
private async send(
|
|
575
|
+
data: Blob,
|
|
576
|
+
isCompressed: boolean,
|
|
577
|
+
options: { useBeacon?: boolean } = {},
|
|
578
|
+
): Promise<boolean> {
|
|
526
579
|
const url = isCompressed ? `${this.endpoint}?encoding=gzip` : this.endpoint;
|
|
527
580
|
const sizeKB = (data.size / 1024).toFixed(1);
|
|
528
581
|
|
|
@@ -536,6 +589,8 @@ class ReplayTracker {
|
|
|
536
589
|
headers: isCompressed ? { "Content-Encoding": "gzip" } : undefined,
|
|
537
590
|
debug: this.debug,
|
|
538
591
|
debugPrefix: `[Replay] ${sizeKB}KB`,
|
|
592
|
+
useBeacon: options.useBeacon ?? false,
|
|
593
|
+
keepalive: options.useBeacon === true,
|
|
539
594
|
}) ?? Promise.resolve(false)
|
|
540
595
|
);
|
|
541
596
|
}
|
package/src/utils/types.ts
CHANGED
package/tests/analytics.test.ts
CHANGED
|
@@ -280,6 +280,17 @@ describe("WebAnalytics lifecycle", () => {
|
|
|
280
280
|
expect(sent).toBe(false);
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
+
test("sendData can skip beacon and use fetch", async () => {
|
|
284
|
+
harness.navigator.sendBeacon = () => true;
|
|
285
|
+
const sent = await sendData({
|
|
286
|
+
url: "https://example.com/v1/web",
|
|
287
|
+
data: '{"ok":true}',
|
|
288
|
+
useBeacon: false,
|
|
289
|
+
});
|
|
290
|
+
expect(sent).toBe(true);
|
|
291
|
+
expect(harness.fetchCalls.length).toBe(1);
|
|
292
|
+
});
|
|
293
|
+
|
|
283
294
|
test("module consent updates apply before instance starts", () => {
|
|
284
295
|
setConsentMode("denied");
|
|
285
296
|
const analytics = new WebAnalytics({
|
package/tests/replay.test.ts
CHANGED
|
@@ -132,6 +132,36 @@ describe("ReplayTracker", () => {
|
|
|
132
132
|
expect(idleCallbackRuns).toBe(1);
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
+
test("flushes buffered snapshot when minimum replay length is reached", async () => {
|
|
136
|
+
const tracker = new ReplayTracker({
|
|
137
|
+
siteKey: "site_test",
|
|
138
|
+
minReplayLengthMs: 1,
|
|
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
|
+
};
|
|
149
|
+
|
|
150
|
+
let sent = 0;
|
|
151
|
+
tracker.send = async () => {
|
|
152
|
+
sent++;
|
|
153
|
+
return true;
|
|
154
|
+
};
|
|
155
|
+
tracker.startTime = Date.now();
|
|
156
|
+
tracker.events = [{ type: 2, timestamp: Date.now(), data: {} }];
|
|
157
|
+
|
|
158
|
+
tracker.scheduleMinLengthFlush();
|
|
159
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
160
|
+
|
|
161
|
+
expect(sent).toBe(1);
|
|
162
|
+
expect(tracker.events.length).toBe(0);
|
|
163
|
+
});
|
|
164
|
+
|
|
135
165
|
test("caps pending batch buffer size", () => {
|
|
136
166
|
const tracker = new ReplayTracker({
|
|
137
167
|
siteKey: "site_test",
|
|
@@ -158,4 +188,60 @@ describe("ReplayTracker", () => {
|
|
|
158
188
|
|
|
159
189
|
expect(tracker.pendingBatches.length).toBe(3);
|
|
160
190
|
});
|
|
191
|
+
|
|
192
|
+
test("queues new flush behind pending batches to preserve order", async () => {
|
|
193
|
+
const tracker = new ReplayTracker({
|
|
194
|
+
siteKey: "site_test",
|
|
195
|
+
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
|
+
});
|
|
232
|
+
tracker.events = [{ id: "newer" }];
|
|
233
|
+
|
|
234
|
+
const sentSequences: number[] = [];
|
|
235
|
+
tracker.send = async (data) => {
|
|
236
|
+
const payload = JSON.parse(await data.text()) as { sequence: number };
|
|
237
|
+
sentSequences.push(payload.sequence);
|
|
238
|
+
return true;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
await tracker.flush();
|
|
242
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
243
|
+
|
|
244
|
+
expect(sentSequences).toEqual([1, 2]);
|
|
245
|
+
expect(tracker.pendingBatches.length).toBe(0);
|
|
246
|
+
});
|
|
161
247
|
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,c as t,f as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./types-8FXsUqbi.js";let d=null,f;function p(){return d}function m(e,t){typeof window>`u`||x()||d?.track(e,t??{})}function h(e,t,n){typeof window>`u`||x()||d?.identify(e,t,n??{})}function g(e=!0){typeof window>`u`||x()||d?.logout(e)}function _(e){if(d){d.setConsentMode(e);return}f=e}function v(){_(`granted`)}function y(){_(`denied`)}function b(e){typeof window>`u`||x()||d?.reportError(e)}function x(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function S(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`}=e;if(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:!0}),s=e.ok;return a&&(s?console.log(`${o} Sent via fetch`):console.warn(`${o} Failed: ${e.status}`)),s}catch{return a&&console.warn(`${o} Failed to send`),!1}}function C(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function w(){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 T=class{webEndpoint;baseUrl;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=u(e.baseUrl),this.webEndpoint=n(this.baseUrl),this.debug=e.debug??!1,this.consentMode=e.consent?.mode??`granted`,this.cookielessWhilePending=e.consent?.cookielessWhilePending??!0,f!==void 0&&(this.consentMode=f,f=void 0),(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(x()){this.log(`disabled`);return}d=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||x()?!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}getErrorTracker(){return this.errorTracker}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(d&&d!==this){this.log(`already started by another instance`);return}if(x()){this.log(`disabled`);return}this.started=!0,d=this,t(this.isCookielessMode());let e=this.options;if(e.trackErrors)try{let{default:t}=await import(`./error-DP65ICdP.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-C7H69Xv3.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:l(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-DveDhM7v.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(),d===this&&(d=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(),o=t.trim();!r||!o||S({url:i(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:a(!1),externalId:r,email:o,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&&o(this.isCookielessMode()),c())}setConsentMode(e){this.consentMode=e,t(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return a(this.isCookielessMode())}getSessionId(){return s()}reportError(e){if(this.destroyed||typeof window>`u`||x()||!(this.options.trackErrors??!1)||!this.ensureStarted())return;let t=this.getErrorTracker();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||x())return;let n=a(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:s(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...w(),...t}});this.log(e),S({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-r()})}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=C(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{g as a,b as c,m as d,x as i,S as l,p as n,v as o,h as r,y as s,T as t,_ as u};
|
package/dist/replay-DveDhM7v.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as e,n as t,r as n,t as r}from"./types-8FXsUqbi.js";import{l as i}from"./analytics-c6ZVX6pa.js";import{getRecordConsolePlugin as a}from"@rrweb/rrweb-plugin-console-record";import{getRecordSequentialIdPlugin as o}from"@rrweb/rrweb-plugin-sequential-id-record";import{record as s}from"rrweb";var c=class{endpoint;siteKey;debug;flushInterval;maxEvents;sampling;slimDOMOptions;maskAllInputs;maskInputOptions;blockClass;blockSelector;maskTextClass;maskTextSelector;checkoutEveryNms;checkoutEveryNth;samplingPercentage;recordConsole;events=[];flushTimer=null;stopRecording=void 0;started=!1;startTime=0;minLengthFlushScheduled=!1;sequenceNumber=0;pendingBatches=[];isFlushing=!1;compressionSupported=!1;sessionSamplingSeed;minReplayLengthMs;maxPendingBatches;flushScheduled=!1;flushTimeout=null;idleFlushId=null;pendingRetryTimer=null;isProcessingPending=!1;constructor(t){this.siteKey=t.siteKey,this.endpoint=e(t.baseUrl),this.debug=t.debug??!1,this.samplingPercentage=r(t.samplingPercentage),this.flushInterval=t.flushInterval??1e4,this.maxEvents=t.maxEvents??500,this.sampling=t.sampling??{mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},this.slimDOMOptions=t.slimDOMOptions??{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0},this.maskAllInputs=t.maskAllInputs??!0,this.maskInputOptions=t.maskInputOptions??{password:!0,email:!0,tel:!0},this.blockClass=t.blockClass,this.blockSelector=t.blockSelector,this.maskTextClass=t.maskTextClass,this.maskTextSelector=t.maskTextSelector,this.checkoutEveryNms=t.checkoutEveryNms??6e4,this.checkoutEveryNth=t.checkoutEveryNth,this.recordConsole=t.recordConsole??!0,this.minReplayLengthMs=t.minReplayLengthMs??3e3,this.maxPendingBatches=t.maxPendingBatches??30,this.sessionSamplingSeed=Math.random()*100,typeof window<`u`&&(this.compressionSupported=`CompressionStream`in window)}start(){if(this.started||typeof window>`u`||this.samplingPercentage<100&&this.sessionSamplingSeed>=this.samplingPercentage)return;this.started=!0,this.startTime=Date.now(),this.debug&&console.log(`[Replay] Recording started`);let e={emit:(e,t)=>this.handleEvent(e,t),sampling:this.sampling,slimDOMOptions:this.slimDOMOptions,maskAllInputs:this.maskAllInputs,checkoutEveryNms:this.checkoutEveryNms};this.maskInputOptions&&(e.maskInputOptions=this.maskInputOptions),this.blockClass&&(e.blockClass=this.blockClass),this.blockSelector&&(e.blockSelector=this.blockSelector),this.maskTextClass&&(e.maskTextClass=this.maskTextClass),this.maskTextSelector&&(e.maskTextSelector=this.maskTextSelector),this.checkoutEveryNth&&(e.checkoutEveryNth=this.checkoutEveryNth),e.plugins=[o({key:`_faststatsSeqId`}),...this.recordConsole?[a()]:[]],this.stopRecording=s(e),this.flushTimer=setInterval(()=>{this.scheduleFlush()},this.flushInterval),window.addEventListener(`beforeunload`,this.handleUnload),window.addEventListener(`pagehide`,this.handleUnload),document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}stop(){if(this.started){if(this.started=!1,this.debug&&console.log(`[Replay] Recording stopped`),this.stopRecording?.(),this.stopRecording=void 0,this.clearScheduledFlush(),this.clearPendingRetryTimer(),this.flushTimer&&=(clearInterval(this.flushTimer),null),window.removeEventListener(`beforeunload`,this.handleUnload),window.removeEventListener(`pagehide`,this.handleUnload),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),!this.hasReachedMinLength()){this.events=[],this.debug&&console.log(`[Replay] Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush()}}handleEvent(e,t){this.events.push(e),t||this.events.length>=this.maxEvents?this.scheduleFlush():!this.minLengthFlushScheduled&&this.hasReachedMinLength()&&(this.minLengthFlushScheduled=!0,this.scheduleFlush())}hasReachedMinLength(){return this.minReplayLengthMs<=0?!0:Date.now()-this.startTime>=this.minReplayLengthMs}scheduleFlush(){if(this.isFlushing||this.events.length===0||this.flushScheduled)return;this.flushScheduled=!0;let e=()=>{this.flushScheduled=!1,this.flushTimeout=null,this.idleFlushId=null,this.flush()};if(typeof window<`u`&&`requestIdleCallback`in window){this.idleFlushId=window.requestIdleCallback(e,{timeout:2e3});return}this.flushTimeout=setTimeout(e,0)}handleUnload=()=>{this.clearScheduledFlush(),this.flush({lowLatency:!0})};handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.flushTimer&&=(clearInterval(this.flushTimer),null),this.clearScheduledFlush(),this.flush({lowLatency:!0})):document.visibilityState===`visible`&&this.started&&(this.flushTimer||=setInterval(()=>{this.scheduleFlush()},this.flushInterval))};clearScheduledFlush(){this.idleFlushId!==null&&typeof window<`u`&&`cancelIdleCallback`in window&&window.cancelIdleCallback(this.idleFlushId),this.idleFlushId=null,this.flushTimeout&&=(clearTimeout(this.flushTimeout),null),this.flushScheduled=!1}clearPendingRetryTimer(){this.pendingRetryTimer&&=(clearTimeout(this.pendingRetryTimer),null)}queuePendingBatch(e){this.pendingBatches.length>=this.maxPendingBatches&&this.pendingBatches.shift(),this.pendingBatches.push(e)}schedulePendingRetry(e){this.pendingRetryTimer||=setTimeout(()=>{this.pendingRetryTimer=null,this.processPendingBatches()},e)}async flush(e={}){if(this.events.length===0){this.processPendingBatches();return}if(!this.hasReachedMinLength()){this.debug&&console.log(`[Replay] Too short (${Date.now()-this.startTime}ms), skipping`),this.processPendingBatches();return}if(this.isFlushing)return;this.isFlushing=!0;let r=this.events;this.events=[];let i=t(),a={token:this.siteKey,sessionId:n(),...i?{identifier:i}:{},sequence:this.sequenceNumber++,timestamp:Date.now(),url:window.location.href,events:r};this.debug&&console.log(`[Replay] Sending ${r.length} events (seq: ${a.sequence})`);try{let t,n=!1;if(this.compressionSupported&&!e.lowLatency)try{t=await this.compress(JSON.stringify(a)),n=!0}catch{this.debug&&console.warn(`[Replay] Compression failed, using uncompressed`),t=new Blob([JSON.stringify(a)],{type:`application/json`})}else t=new Blob([JSON.stringify(a)],{type:`application/json`});await this.send(t,n)||this.queuePendingBatch({batch:a,isCompressed:n,retries:0})}catch(e){this.debug&&console.warn(`[Replay] Flush error:`,e),this.queuePendingBatch({batch:a,isCompressed:!1,retries:0})}finally{this.isFlushing=!1,this.processPendingBatches()}}async processPendingBatches(){if(!(this.isProcessingPending||this.pendingBatches.length===0)){this.clearPendingRetryTimer(),this.isProcessingPending=!0;try{for(;this.pendingBatches.length>0;){let e=this.pendingBatches[0];if(!e)break;if(e.retries>=3){this.debug&&console.warn(`[Replay] Max retries reached, restoring batch ${e.batch.sequence} to buffer`),this.pendingBatches.shift(),this.events=[...e.batch.events,...this.events],this.scheduleFlush();continue}e.retries++;let t;if(e.isCompressed&&this.compressionSupported)try{t=await this.compress(JSON.stringify(e.batch))}catch{t=new Blob([JSON.stringify(e.batch)],{type:`application/json`}),e.isCompressed=!1}else t=new Blob([JSON.stringify(e.batch)],{type:`application/json`});if(await this.send(t,e.isCompressed)){this.pendingBatches.shift();continue}this.schedulePendingRetry(1e3*e.retries);break}}catch{let e=this.pendingBatches[0];this.debug&&e&&console.warn(`[Replay] Retry ${e.retries} failed`),this.schedulePendingRetry(e?1e3*e.retries:1e3)}finally{this.isProcessingPending=!1}}}async compress(e){if(!this.compressionSupported)throw Error(`Compression not supported`);let t=new TextEncoder().encode(e),n=new CompressionStream(`gzip`),r=n.writable.getWriter();r.write(t),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;if(this.debug){let e=(s.length/t.length*100).toFixed(1);console.log(`[Replay] Compressed: ${t.length} → ${s.length} bytes (${e}%)`)}return new Blob([s],{type:`application/octet-stream`})}async send(e,t){let n=t?`${this.endpoint}?encoding=gzip`:this.endpoint,r=(e.size/1024).toFixed(1);return i({url:n,data:e,contentType:t?`application/octet-stream`:`application/json`,headers:t?{"Content-Encoding":`gzip`}:void 0,debug:this.debug,debugPrefix:`[Replay] ${r}KB`})??Promise.resolve(!1)}getSessionId(){return n()}};export{c as default};
|