@rudderstack/analytics-js 3.7.9 → 3.7.11

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.
@@ -370,7 +370,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
370
370
 
371
371
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
372
372
 
373
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
373
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
374
374
 
375
375
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
376
376
 
@@ -717,14 +717,14 @@ const QueueStatuses={IN_PROGRESS:'inProgress',QUEUE:'queue',RECLAIM_START:'recla
717
717
 
718
718
  const onPageLeave=callback=>{// To ensure the callback is only called once even if more than one events
719
719
  // are fired at once.
720
- let pageLeft=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback();}// Catches the unloading of the page (e.g., closing the tab or navigating away).
720
+ let pageLeft=false;let isAccessible=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback(isAccessible);}// Catches the unloading of the page (e.g., closing the tab or navigating away).
721
721
  // Includes user actions like clicking a link, entering a new URL,
722
722
  // refreshing the page, or closing the browser tab
723
723
  // Note that 'pagehide' is not supported in IE.
724
724
  // So, this is a fallback.
725
- globalThis.addEventListener('beforeunload',handleOnLeave);// Catches the page being hidden, including scenarios like closing the tab.
726
- document.addEventListener('pagehide',handleOnLeave);// Catches visibility changes, such as switching tabs or minimizing the browser.
727
- document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
725
+ globalThis.addEventListener('beforeunload',()=>{isAccessible=false;handleOnLeave();});globalThis.addEventListener('blur',()=>{isAccessible=true;handleOnLeave();});globalThis.addEventListener('focus',()=>{pageLeft=false;});// Catches the page being hidden, including scenarios like closing the tab.
726
+ document.addEventListener('pagehide',()=>{isAccessible=document.visibilityState==='hidden';handleOnLeave();});// Catches visibility changes, such as switching tabs or minimizing the browser.
727
+ document.addEventListener('visibilitychange',()=>{isAccessible=true;if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
728
728
 
729
729
  let ScheduleModes=/*#__PURE__*/function(ScheduleModes){ScheduleModes[ScheduleModes["ASAP"]=1]="ASAP";ScheduleModes[ScheduleModes["RESCHEDULE"]=2]="RESCHEDULE";ScheduleModes[ScheduleModes["ABANDON"]=3]="ABANDON";return ScheduleModes;}({});const DEFAULT_CLOCK_LATE_FACTOR=2;const DEFAULT_CLOCK={setTimeout(fn,ms){return globalThis.setTimeout(fn,ms);},clearTimeout(id){return globalThis.clearTimeout(id);},Date:globalThis.Date,clockLateFactor:DEFAULT_CLOCK_LATE_FACTOR};class Schedule{constructor(){this.tasks={};this.nextId=1;this.clock=DEFAULT_CLOCK;}now(){return +new this.clock.Date();}run(task,timeout,mode){const id=(this.nextId+1).toString();this.tasks[id]=this.clock.setTimeout(this.handle(id,task,timeout,mode||ScheduleModes.ASAP),timeout);return id;}handle(id,callback,timeout,mode){const start=this.now();return ()=>{delete this.tasks[id];const elapsedTimeoutTime=start+timeout*(this.clock.clockLateFactor||DEFAULT_CLOCK_LATE_FACTOR);const currentTime=this.now();const notCompletedOrTimedOut=mode>=ScheduleModes.RESCHEDULE&&elapsedTimeoutTime<currentTime;if(notCompletedOrTimedOut){if(mode===ScheduleModes.RESCHEDULE){this.run(callback,timeout,mode);}return undefined;}return callback();};}cancel(id){if(this.tasks[id]){this.clock.clearTimeout(this.tasks[id]);delete this.tasks[id];}}cancelAll(){Object.values(this.tasks).forEach(this.clock.clearTimeout);this.tasks={};}}
730
730
 
@@ -745,7 +745,7 @@ let timerScaleFactor=Math.max(options.timerScaleFactor??MIN_TIMER_SCALE_FACTOR,M
745
745
  timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
746
746
  this.timeouts={ackTimer:Math.round(timerScaleFactor*DEFAULT_ACK_TIMER_MS),reclaimTimer:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMER_MS),reclaimTimeout:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMEOUT_MS),reclaimWait:Math.round(timerScaleFactor*DEFAULT_RECLAIM_WAIT_MS)};this.schedule=new Schedule();this.processId='0';// Set up our empty queues
747
747
  this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
748
- this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);// Flush the queue on page leave
748
+ this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
749
749
  this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
750
750
  this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
751
751
  setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name);}else {this.store.set(name,value);}}/**
@@ -756,7 +756,7 @@ setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name)
756
756
  * Configures the timeout handler for flushing the batch queue
757
757
  */scheduleFlushBatch(){if(this.batch.enabled&&this.batch?.flushInterval){if(this.flushBatchTaskId){this.schedule.cancel(this.flushBatchTaskId);}this.flushBatchTaskId=this.schedule.run(this.flushBatch,this.batch.flushInterval,ScheduleModes.ASAP);}}/**
758
758
  * Flushes the batch queue
759
- */flushBatch(){if(!this.batchingInProgress){this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
759
+ */flushBatch(isAccessible=true){if(!this.batchingInProgress){this.isPageAccessible=isAccessible;this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
760
760
  this.scheduleFlushBatch();}}/**
761
761
  * Decides whether to retry. Overridable.
762
762
  *
@@ -798,10 +798,13 @@ this.setStorageEntry(QueueStatuses.BATCH_QUEUE,batchQueue);return curEntry;}push
798
798
  * @returns Batch dispatch info
799
799
  */getBatchDispatchInfo(batchItems){let lengthCriteriaMet=false;let lengthCriteriaExceeded=false;const configuredBatchMaxItems=this.batch?.maxItems;if(isDefined(configuredBatchMaxItems)){lengthCriteriaMet=batchItems.length===configuredBatchMaxItems;lengthCriteriaExceeded=batchItems.length>configuredBatchMaxItems;}if(lengthCriteriaMet||lengthCriteriaExceeded){return {criteriaMet:lengthCriteriaMet,criteriaExceeded:lengthCriteriaExceeded};}let sizeCriteriaMet=false;let sizeCriteriaExceeded=false;const configuredBatchMaxSize=this.batch?.maxSize;if(isDefined(configuredBatchMaxSize)&&isDefined(this.batchSizeCalcCb)){const curBatchSize=this.batchSizeCalcCb(batchItems.map(queueItem=>queueItem.item));sizeCriteriaMet=curBatchSize===configuredBatchMaxSize;sizeCriteriaExceeded=curBatchSize>configuredBatchMaxSize;}return {criteriaMet:sizeCriteriaMet,criteriaExceeded:sizeCriteriaExceeded};}processHead(){// cancel the scheduled task if it exists
800
800
  this.schedule.cancel(this.processId);// Pop the head off the queue
801
- let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
802
- const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
803
- while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// Save this to the in progress map
804
- inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
801
+ let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
802
+ const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};// If the page is unloading, clear the previous in progress queue also to avoid any stale data
803
+ // Otherwise, the next page load will retry the items which were in progress previously
804
+ if(!this.isPageAccessible){inProgress={};}let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
805
+ while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// If the page is unloading, don't add items to the in progress queue
806
+ if(this.isPageAccessible){// Save this to the in progress map
807
+ inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
805
808
  try{const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,el.attemptNumber,this.maxAttempts,willBeRetried);}catch(err){this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE),err);}});// re-read the queue in case the process function finished immediately or added another item
806
809
  queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];this.schedule.cancel(this.processId);if(queue.length>0){const nextProcessExecutionTime=queue[0].time-now;this.processId=this.schedule.run(this.processHead,nextProcessExecutionTime,ScheduleModes.ASAP);}}// Ack continuously to prevent other tabs from claiming our queue
807
810
  ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id});trackMessageIds.push(id);}};if(Array.isArray(queue)){queue.forEach(concatIterator);}else if(queue){Object.values(queue).forEach(concatIterator);}};// add their queue to ours, resetting run-time to immediate and copying the attempt#
@@ -2485,7 +2488,7 @@ const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-T
2485
2488
  * this is not supported by our sourceConfig API
2486
2489
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
2487
2490
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
2488
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2491
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2489
2492
  // and the first call to the send method in legacy browsers
2490
2493
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
2491
2494
 
@@ -376,7 +376,7 @@
376
376
 
377
377
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
378
378
 
379
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
379
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
380
380
 
381
381
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
382
382
 
@@ -723,14 +723,14 @@
723
723
 
724
724
  const onPageLeave=callback=>{// To ensure the callback is only called once even if more than one events
725
725
  // are fired at once.
726
- let pageLeft=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback();}// Catches the unloading of the page (e.g., closing the tab or navigating away).
726
+ let pageLeft=false;let isAccessible=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback(isAccessible);}// Catches the unloading of the page (e.g., closing the tab or navigating away).
727
727
  // Includes user actions like clicking a link, entering a new URL,
728
728
  // refreshing the page, or closing the browser tab
729
729
  // Note that 'pagehide' is not supported in IE.
730
730
  // So, this is a fallback.
731
- globalThis.addEventListener('beforeunload',handleOnLeave);// Catches the page being hidden, including scenarios like closing the tab.
732
- document.addEventListener('pagehide',handleOnLeave);// Catches visibility changes, such as switching tabs or minimizing the browser.
733
- document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
731
+ globalThis.addEventListener('beforeunload',()=>{isAccessible=false;handleOnLeave();});globalThis.addEventListener('blur',()=>{isAccessible=true;handleOnLeave();});globalThis.addEventListener('focus',()=>{pageLeft=false;});// Catches the page being hidden, including scenarios like closing the tab.
732
+ document.addEventListener('pagehide',()=>{isAccessible=document.visibilityState==='hidden';handleOnLeave();});// Catches visibility changes, such as switching tabs or minimizing the browser.
733
+ document.addEventListener('visibilitychange',()=>{isAccessible=true;if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
734
734
 
735
735
  let ScheduleModes=/*#__PURE__*/function(ScheduleModes){ScheduleModes[ScheduleModes["ASAP"]=1]="ASAP";ScheduleModes[ScheduleModes["RESCHEDULE"]=2]="RESCHEDULE";ScheduleModes[ScheduleModes["ABANDON"]=3]="ABANDON";return ScheduleModes;}({});const DEFAULT_CLOCK_LATE_FACTOR=2;const DEFAULT_CLOCK={setTimeout(fn,ms){return globalThis.setTimeout(fn,ms);},clearTimeout(id){return globalThis.clearTimeout(id);},Date:globalThis.Date,clockLateFactor:DEFAULT_CLOCK_LATE_FACTOR};class Schedule{constructor(){this.tasks={};this.nextId=1;this.clock=DEFAULT_CLOCK;}now(){return +new this.clock.Date();}run(task,timeout,mode){const id=(this.nextId+1).toString();this.tasks[id]=this.clock.setTimeout(this.handle(id,task,timeout,mode||ScheduleModes.ASAP),timeout);return id;}handle(id,callback,timeout,mode){const start=this.now();return ()=>{delete this.tasks[id];const elapsedTimeoutTime=start+timeout*(this.clock.clockLateFactor||DEFAULT_CLOCK_LATE_FACTOR);const currentTime=this.now();const notCompletedOrTimedOut=mode>=ScheduleModes.RESCHEDULE&&elapsedTimeoutTime<currentTime;if(notCompletedOrTimedOut){if(mode===ScheduleModes.RESCHEDULE){this.run(callback,timeout,mode);}return undefined;}return callback();};}cancel(id){if(this.tasks[id]){this.clock.clearTimeout(this.tasks[id]);delete this.tasks[id];}}cancelAll(){Object.values(this.tasks).forEach(this.clock.clearTimeout);this.tasks={};}}
736
736
 
@@ -751,7 +751,7 @@
751
751
  timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
752
752
  this.timeouts={ackTimer:Math.round(timerScaleFactor*DEFAULT_ACK_TIMER_MS),reclaimTimer:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMER_MS),reclaimTimeout:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMEOUT_MS),reclaimWait:Math.round(timerScaleFactor*DEFAULT_RECLAIM_WAIT_MS)};this.schedule=new Schedule();this.processId='0';// Set up our empty queues
753
753
  this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
754
- this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);// Flush the queue on page leave
754
+ this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
755
755
  this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
756
756
  this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
757
757
  setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name);}else {this.store.set(name,value);}}/**
@@ -762,7 +762,7 @@
762
762
  * Configures the timeout handler for flushing the batch queue
763
763
  */scheduleFlushBatch(){if(this.batch.enabled&&this.batch?.flushInterval){if(this.flushBatchTaskId){this.schedule.cancel(this.flushBatchTaskId);}this.flushBatchTaskId=this.schedule.run(this.flushBatch,this.batch.flushInterval,ScheduleModes.ASAP);}}/**
764
764
  * Flushes the batch queue
765
- */flushBatch(){if(!this.batchingInProgress){this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
765
+ */flushBatch(isAccessible=true){if(!this.batchingInProgress){this.isPageAccessible=isAccessible;this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
766
766
  this.scheduleFlushBatch();}}/**
767
767
  * Decides whether to retry. Overridable.
768
768
  *
@@ -804,10 +804,13 @@
804
804
  * @returns Batch dispatch info
805
805
  */getBatchDispatchInfo(batchItems){let lengthCriteriaMet=false;let lengthCriteriaExceeded=false;const configuredBatchMaxItems=this.batch?.maxItems;if(isDefined(configuredBatchMaxItems)){lengthCriteriaMet=batchItems.length===configuredBatchMaxItems;lengthCriteriaExceeded=batchItems.length>configuredBatchMaxItems;}if(lengthCriteriaMet||lengthCriteriaExceeded){return {criteriaMet:lengthCriteriaMet,criteriaExceeded:lengthCriteriaExceeded};}let sizeCriteriaMet=false;let sizeCriteriaExceeded=false;const configuredBatchMaxSize=this.batch?.maxSize;if(isDefined(configuredBatchMaxSize)&&isDefined(this.batchSizeCalcCb)){const curBatchSize=this.batchSizeCalcCb(batchItems.map(queueItem=>queueItem.item));sizeCriteriaMet=curBatchSize===configuredBatchMaxSize;sizeCriteriaExceeded=curBatchSize>configuredBatchMaxSize;}return {criteriaMet:sizeCriteriaMet,criteriaExceeded:sizeCriteriaExceeded};}processHead(){// cancel the scheduled task if it exists
806
806
  this.schedule.cancel(this.processId);// Pop the head off the queue
807
- let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
808
- const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
809
- while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// Save this to the in progress map
810
- inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
807
+ let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
808
+ const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};// If the page is unloading, clear the previous in progress queue also to avoid any stale data
809
+ // Otherwise, the next page load will retry the items which were in progress previously
810
+ if(!this.isPageAccessible){inProgress={};}let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
811
+ while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// If the page is unloading, don't add items to the in progress queue
812
+ if(this.isPageAccessible){// Save this to the in progress map
813
+ inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
811
814
  try{const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,el.attemptNumber,this.maxAttempts,willBeRetried);}catch(err){this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE),err);}});// re-read the queue in case the process function finished immediately or added another item
812
815
  queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];this.schedule.cancel(this.processId);if(queue.length>0){const nextProcessExecutionTime=queue[0].time-now;this.processId=this.schedule.run(this.processHead,nextProcessExecutionTime,ScheduleModes.ASAP);}}// Ack continuously to prevent other tabs from claiming our queue
813
816
  ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id});trackMessageIds.push(id);}};if(Array.isArray(queue)){queue.forEach(concatIterator);}else if(queue){Object.values(queue).forEach(concatIterator);}};// add their queue to ours, resetting run-time to immediate and copying the attempt#
@@ -2491,7 +2494,7 @@
2491
2494
  * this is not supported by our sourceConfig API
2492
2495
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
2493
2496
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
2494
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2497
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2495
2498
  // and the first call to the send method in legacy browsers
2496
2499
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
2497
2500
 
@@ -365,7 +365,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
365
365
 
366
366
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
367
367
 
368
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
368
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
369
369
 
370
370
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
371
371
 
@@ -775,7 +775,7 @@ const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-T
775
775
  * this is not supported by our sourceConfig API
776
776
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
777
777
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
778
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
778
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
779
779
  // and the first call to the send method in legacy browsers
780
780
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
781
781
 
@@ -374,7 +374,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
374
374
 
375
375
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
376
376
 
377
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
377
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
378
378
 
379
379
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
380
380
 
@@ -721,14 +721,14 @@ const QueueStatuses={IN_PROGRESS:'inProgress',QUEUE:'queue',RECLAIM_START:'recla
721
721
 
722
722
  const onPageLeave=callback=>{// To ensure the callback is only called once even if more than one events
723
723
  // are fired at once.
724
- let pageLeft=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback();}// Catches the unloading of the page (e.g., closing the tab or navigating away).
724
+ let pageLeft=false;let isAccessible=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback(isAccessible);}// Catches the unloading of the page (e.g., closing the tab or navigating away).
725
725
  // Includes user actions like clicking a link, entering a new URL,
726
726
  // refreshing the page, or closing the browser tab
727
727
  // Note that 'pagehide' is not supported in IE.
728
728
  // So, this is a fallback.
729
- globalThis.addEventListener('beforeunload',handleOnLeave);// Catches the page being hidden, including scenarios like closing the tab.
730
- document.addEventListener('pagehide',handleOnLeave);// Catches visibility changes, such as switching tabs or minimizing the browser.
731
- document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
729
+ globalThis.addEventListener('beforeunload',()=>{isAccessible=false;handleOnLeave();});globalThis.addEventListener('blur',()=>{isAccessible=true;handleOnLeave();});globalThis.addEventListener('focus',()=>{pageLeft=false;});// Catches the page being hidden, including scenarios like closing the tab.
730
+ document.addEventListener('pagehide',()=>{isAccessible=document.visibilityState==='hidden';handleOnLeave();});// Catches visibility changes, such as switching tabs or minimizing the browser.
731
+ document.addEventListener('visibilitychange',()=>{isAccessible=true;if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
732
732
 
733
733
  let ScheduleModes=/*#__PURE__*/function(ScheduleModes){ScheduleModes[ScheduleModes["ASAP"]=1]="ASAP";ScheduleModes[ScheduleModes["RESCHEDULE"]=2]="RESCHEDULE";ScheduleModes[ScheduleModes["ABANDON"]=3]="ABANDON";return ScheduleModes;}({});const DEFAULT_CLOCK_LATE_FACTOR=2;const DEFAULT_CLOCK={setTimeout(fn,ms){return globalThis.setTimeout(fn,ms);},clearTimeout(id){return globalThis.clearTimeout(id);},Date:globalThis.Date,clockLateFactor:DEFAULT_CLOCK_LATE_FACTOR};class Schedule{constructor(){this.tasks={};this.nextId=1;this.clock=DEFAULT_CLOCK;}now(){return +new this.clock.Date();}run(task,timeout,mode){const id=(this.nextId+1).toString();this.tasks[id]=this.clock.setTimeout(this.handle(id,task,timeout,mode||ScheduleModes.ASAP),timeout);return id;}handle(id,callback,timeout,mode){const start=this.now();return ()=>{delete this.tasks[id];const elapsedTimeoutTime=start+timeout*(this.clock.clockLateFactor||DEFAULT_CLOCK_LATE_FACTOR);const currentTime=this.now();const notCompletedOrTimedOut=mode>=ScheduleModes.RESCHEDULE&&elapsedTimeoutTime<currentTime;if(notCompletedOrTimedOut){if(mode===ScheduleModes.RESCHEDULE){this.run(callback,timeout,mode);}return undefined;}return callback();};}cancel(id){if(this.tasks[id]){this.clock.clearTimeout(this.tasks[id]);delete this.tasks[id];}}cancelAll(){Object.values(this.tasks).forEach(this.clock.clearTimeout);this.tasks={};}}
734
734
 
@@ -749,7 +749,7 @@ let timerScaleFactor=Math.max(options.timerScaleFactor??MIN_TIMER_SCALE_FACTOR,M
749
749
  timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
750
750
  this.timeouts={ackTimer:Math.round(timerScaleFactor*DEFAULT_ACK_TIMER_MS),reclaimTimer:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMER_MS),reclaimTimeout:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMEOUT_MS),reclaimWait:Math.round(timerScaleFactor*DEFAULT_RECLAIM_WAIT_MS)};this.schedule=new Schedule();this.processId='0';// Set up our empty queues
751
751
  this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
752
- this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);// Flush the queue on page leave
752
+ this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
753
753
  this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
754
754
  this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
755
755
  setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name);}else {this.store.set(name,value);}}/**
@@ -760,7 +760,7 @@ setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name)
760
760
  * Configures the timeout handler for flushing the batch queue
761
761
  */scheduleFlushBatch(){if(this.batch.enabled&&this.batch?.flushInterval){if(this.flushBatchTaskId){this.schedule.cancel(this.flushBatchTaskId);}this.flushBatchTaskId=this.schedule.run(this.flushBatch,this.batch.flushInterval,ScheduleModes.ASAP);}}/**
762
762
  * Flushes the batch queue
763
- */flushBatch(){if(!this.batchingInProgress){this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
763
+ */flushBatch(isAccessible=true){if(!this.batchingInProgress){this.isPageAccessible=isAccessible;this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
764
764
  this.scheduleFlushBatch();}}/**
765
765
  * Decides whether to retry. Overridable.
766
766
  *
@@ -802,10 +802,13 @@ this.setStorageEntry(QueueStatuses.BATCH_QUEUE,batchQueue);return curEntry;}push
802
802
  * @returns Batch dispatch info
803
803
  */getBatchDispatchInfo(batchItems){let lengthCriteriaMet=false;let lengthCriteriaExceeded=false;const configuredBatchMaxItems=this.batch?.maxItems;if(isDefined(configuredBatchMaxItems)){lengthCriteriaMet=batchItems.length===configuredBatchMaxItems;lengthCriteriaExceeded=batchItems.length>configuredBatchMaxItems;}if(lengthCriteriaMet||lengthCriteriaExceeded){return {criteriaMet:lengthCriteriaMet,criteriaExceeded:lengthCriteriaExceeded};}let sizeCriteriaMet=false;let sizeCriteriaExceeded=false;const configuredBatchMaxSize=this.batch?.maxSize;if(isDefined(configuredBatchMaxSize)&&isDefined(this.batchSizeCalcCb)){const curBatchSize=this.batchSizeCalcCb(batchItems.map(queueItem=>queueItem.item));sizeCriteriaMet=curBatchSize===configuredBatchMaxSize;sizeCriteriaExceeded=curBatchSize>configuredBatchMaxSize;}return {criteriaMet:sizeCriteriaMet,criteriaExceeded:sizeCriteriaExceeded};}processHead(){// cancel the scheduled task if it exists
804
804
  this.schedule.cancel(this.processId);// Pop the head off the queue
805
- let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
806
- const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
807
- while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// Save this to the in progress map
808
- inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
805
+ let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
806
+ const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};// If the page is unloading, clear the previous in progress queue also to avoid any stale data
807
+ // Otherwise, the next page load will retry the items which were in progress previously
808
+ if(!this.isPageAccessible){inProgress={};}let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
809
+ while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// If the page is unloading, don't add items to the in progress queue
810
+ if(this.isPageAccessible){// Save this to the in progress map
811
+ inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
809
812
  try{const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,el.attemptNumber,this.maxAttempts,willBeRetried);}catch(err){this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE),err);}});// re-read the queue in case the process function finished immediately or added another item
810
813
  queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];this.schedule.cancel(this.processId);if(queue.length>0){const nextProcessExecutionTime=queue[0].time-now;this.processId=this.schedule.run(this.processHead,nextProcessExecutionTime,ScheduleModes.ASAP);}}// Ack continuously to prevent other tabs from claiming our queue
811
814
  ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id});trackMessageIds.push(id);}};if(Array.isArray(queue)){queue.forEach(concatIterator);}else if(queue){Object.values(queue).forEach(concatIterator);}};// add their queue to ours, resetting run-time to immediate and copying the attempt#
@@ -2463,7 +2466,7 @@ const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-T
2463
2466
  * this is not supported by our sourceConfig API
2464
2467
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
2465
2468
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
2466
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2469
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2467
2470
  // and the first call to the send method in legacy browsers
2468
2471
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
2469
2472
 
@@ -370,7 +370,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
370
370
 
371
371
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
372
372
 
373
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
373
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
374
374
 
375
375
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
376
376
 
@@ -717,14 +717,14 @@ const QueueStatuses={IN_PROGRESS:'inProgress',QUEUE:'queue',RECLAIM_START:'recla
717
717
 
718
718
  const onPageLeave=callback=>{// To ensure the callback is only called once even if more than one events
719
719
  // are fired at once.
720
- let pageLeft=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback();}// Catches the unloading of the page (e.g., closing the tab or navigating away).
720
+ let pageLeft=false;let isAccessible=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback(isAccessible);}// Catches the unloading of the page (e.g., closing the tab or navigating away).
721
721
  // Includes user actions like clicking a link, entering a new URL,
722
722
  // refreshing the page, or closing the browser tab
723
723
  // Note that 'pagehide' is not supported in IE.
724
724
  // So, this is a fallback.
725
- globalThis.addEventListener('beforeunload',handleOnLeave);// Catches the page being hidden, including scenarios like closing the tab.
726
- document.addEventListener('pagehide',handleOnLeave);// Catches visibility changes, such as switching tabs or minimizing the browser.
727
- document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
725
+ globalThis.addEventListener('beforeunload',()=>{isAccessible=false;handleOnLeave();});globalThis.addEventListener('blur',()=>{isAccessible=true;handleOnLeave();});globalThis.addEventListener('focus',()=>{pageLeft=false;});// Catches the page being hidden, including scenarios like closing the tab.
726
+ document.addEventListener('pagehide',()=>{isAccessible=document.visibilityState==='hidden';handleOnLeave();});// Catches visibility changes, such as switching tabs or minimizing the browser.
727
+ document.addEventListener('visibilitychange',()=>{isAccessible=true;if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
728
728
 
729
729
  let ScheduleModes=/*#__PURE__*/function(ScheduleModes){ScheduleModes[ScheduleModes["ASAP"]=1]="ASAP";ScheduleModes[ScheduleModes["RESCHEDULE"]=2]="RESCHEDULE";ScheduleModes[ScheduleModes["ABANDON"]=3]="ABANDON";return ScheduleModes;}({});const DEFAULT_CLOCK_LATE_FACTOR=2;const DEFAULT_CLOCK={setTimeout(fn,ms){return globalThis.setTimeout(fn,ms);},clearTimeout(id){return globalThis.clearTimeout(id);},Date:globalThis.Date,clockLateFactor:DEFAULT_CLOCK_LATE_FACTOR};class Schedule{constructor(){this.tasks={};this.nextId=1;this.clock=DEFAULT_CLOCK;}now(){return +new this.clock.Date();}run(task,timeout,mode){const id=(this.nextId+1).toString();this.tasks[id]=this.clock.setTimeout(this.handle(id,task,timeout,mode||ScheduleModes.ASAP),timeout);return id;}handle(id,callback,timeout,mode){const start=this.now();return ()=>{delete this.tasks[id];const elapsedTimeoutTime=start+timeout*(this.clock.clockLateFactor||DEFAULT_CLOCK_LATE_FACTOR);const currentTime=this.now();const notCompletedOrTimedOut=mode>=ScheduleModes.RESCHEDULE&&elapsedTimeoutTime<currentTime;if(notCompletedOrTimedOut){if(mode===ScheduleModes.RESCHEDULE){this.run(callback,timeout,mode);}return undefined;}return callback();};}cancel(id){if(this.tasks[id]){this.clock.clearTimeout(this.tasks[id]);delete this.tasks[id];}}cancelAll(){Object.values(this.tasks).forEach(this.clock.clearTimeout);this.tasks={};}}
730
730
 
@@ -745,7 +745,7 @@ let timerScaleFactor=Math.max(options.timerScaleFactor??MIN_TIMER_SCALE_FACTOR,M
745
745
  timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
746
746
  this.timeouts={ackTimer:Math.round(timerScaleFactor*DEFAULT_ACK_TIMER_MS),reclaimTimer:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMER_MS),reclaimTimeout:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMEOUT_MS),reclaimWait:Math.round(timerScaleFactor*DEFAULT_RECLAIM_WAIT_MS)};this.schedule=new Schedule();this.processId='0';// Set up our empty queues
747
747
  this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
748
- this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);// Flush the queue on page leave
748
+ this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
749
749
  this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
750
750
  this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
751
751
  setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name);}else {this.store.set(name,value);}}/**
@@ -756,7 +756,7 @@ setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name)
756
756
  * Configures the timeout handler for flushing the batch queue
757
757
  */scheduleFlushBatch(){if(this.batch.enabled&&this.batch?.flushInterval){if(this.flushBatchTaskId){this.schedule.cancel(this.flushBatchTaskId);}this.flushBatchTaskId=this.schedule.run(this.flushBatch,this.batch.flushInterval,ScheduleModes.ASAP);}}/**
758
758
  * Flushes the batch queue
759
- */flushBatch(){if(!this.batchingInProgress){this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
759
+ */flushBatch(isAccessible=true){if(!this.batchingInProgress){this.isPageAccessible=isAccessible;this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
760
760
  this.scheduleFlushBatch();}}/**
761
761
  * Decides whether to retry. Overridable.
762
762
  *
@@ -798,10 +798,13 @@ this.setStorageEntry(QueueStatuses.BATCH_QUEUE,batchQueue);return curEntry;}push
798
798
  * @returns Batch dispatch info
799
799
  */getBatchDispatchInfo(batchItems){let lengthCriteriaMet=false;let lengthCriteriaExceeded=false;const configuredBatchMaxItems=this.batch?.maxItems;if(isDefined(configuredBatchMaxItems)){lengthCriteriaMet=batchItems.length===configuredBatchMaxItems;lengthCriteriaExceeded=batchItems.length>configuredBatchMaxItems;}if(lengthCriteriaMet||lengthCriteriaExceeded){return {criteriaMet:lengthCriteriaMet,criteriaExceeded:lengthCriteriaExceeded};}let sizeCriteriaMet=false;let sizeCriteriaExceeded=false;const configuredBatchMaxSize=this.batch?.maxSize;if(isDefined(configuredBatchMaxSize)&&isDefined(this.batchSizeCalcCb)){const curBatchSize=this.batchSizeCalcCb(batchItems.map(queueItem=>queueItem.item));sizeCriteriaMet=curBatchSize===configuredBatchMaxSize;sizeCriteriaExceeded=curBatchSize>configuredBatchMaxSize;}return {criteriaMet:sizeCriteriaMet,criteriaExceeded:sizeCriteriaExceeded};}processHead(){// cancel the scheduled task if it exists
800
800
  this.schedule.cancel(this.processId);// Pop the head off the queue
801
- let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
802
- const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
803
- while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// Save this to the in progress map
804
- inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
801
+ let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
802
+ const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};// If the page is unloading, clear the previous in progress queue also to avoid any stale data
803
+ // Otherwise, the next page load will retry the items which were in progress previously
804
+ if(!this.isPageAccessible){inProgress={};}let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
805
+ while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// If the page is unloading, don't add items to the in progress queue
806
+ if(this.isPageAccessible){// Save this to the in progress map
807
+ inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
805
808
  try{const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,el.attemptNumber,this.maxAttempts,willBeRetried);}catch(err){this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE),err);}});// re-read the queue in case the process function finished immediately or added another item
806
809
  queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];this.schedule.cancel(this.processId);if(queue.length>0){const nextProcessExecutionTime=queue[0].time-now;this.processId=this.schedule.run(this.processHead,nextProcessExecutionTime,ScheduleModes.ASAP);}}// Ack continuously to prevent other tabs from claiming our queue
807
810
  ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id});trackMessageIds.push(id);}};if(Array.isArray(queue)){queue.forEach(concatIterator);}else if(queue){Object.values(queue).forEach(concatIterator);}};// add their queue to ours, resetting run-time to immediate and copying the attempt#
@@ -2459,7 +2462,7 @@ const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-T
2459
2462
  * this is not supported by our sourceConfig API
2460
2463
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
2461
2464
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
2462
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2465
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2463
2466
  // and the first call to the send method in legacy browsers
2464
2467
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
2465
2468
 
@@ -376,7 +376,7 @@
376
376
 
377
377
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
378
378
 
379
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
379
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
380
380
 
381
381
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
382
382
 
@@ -723,14 +723,14 @@
723
723
 
724
724
  const onPageLeave=callback=>{// To ensure the callback is only called once even if more than one events
725
725
  // are fired at once.
726
- let pageLeft=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback();}// Catches the unloading of the page (e.g., closing the tab or navigating away).
726
+ let pageLeft=false;let isAccessible=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback(isAccessible);}// Catches the unloading of the page (e.g., closing the tab or navigating away).
727
727
  // Includes user actions like clicking a link, entering a new URL,
728
728
  // refreshing the page, or closing the browser tab
729
729
  // Note that 'pagehide' is not supported in IE.
730
730
  // So, this is a fallback.
731
- globalThis.addEventListener('beforeunload',handleOnLeave);// Catches the page being hidden, including scenarios like closing the tab.
732
- document.addEventListener('pagehide',handleOnLeave);// Catches visibility changes, such as switching tabs or minimizing the browser.
733
- document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
731
+ globalThis.addEventListener('beforeunload',()=>{isAccessible=false;handleOnLeave();});globalThis.addEventListener('blur',()=>{isAccessible=true;handleOnLeave();});globalThis.addEventListener('focus',()=>{pageLeft=false;});// Catches the page being hidden, including scenarios like closing the tab.
732
+ document.addEventListener('pagehide',()=>{isAccessible=document.visibilityState==='hidden';handleOnLeave();});// Catches visibility changes, such as switching tabs or minimizing the browser.
733
+ document.addEventListener('visibilitychange',()=>{isAccessible=true;if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
734
734
 
735
735
  let ScheduleModes=/*#__PURE__*/function(ScheduleModes){ScheduleModes[ScheduleModes["ASAP"]=1]="ASAP";ScheduleModes[ScheduleModes["RESCHEDULE"]=2]="RESCHEDULE";ScheduleModes[ScheduleModes["ABANDON"]=3]="ABANDON";return ScheduleModes;}({});const DEFAULT_CLOCK_LATE_FACTOR=2;const DEFAULT_CLOCK={setTimeout(fn,ms){return globalThis.setTimeout(fn,ms);},clearTimeout(id){return globalThis.clearTimeout(id);},Date:globalThis.Date,clockLateFactor:DEFAULT_CLOCK_LATE_FACTOR};class Schedule{constructor(){this.tasks={};this.nextId=1;this.clock=DEFAULT_CLOCK;}now(){return +new this.clock.Date();}run(task,timeout,mode){const id=(this.nextId+1).toString();this.tasks[id]=this.clock.setTimeout(this.handle(id,task,timeout,mode||ScheduleModes.ASAP),timeout);return id;}handle(id,callback,timeout,mode){const start=this.now();return ()=>{delete this.tasks[id];const elapsedTimeoutTime=start+timeout*(this.clock.clockLateFactor||DEFAULT_CLOCK_LATE_FACTOR);const currentTime=this.now();const notCompletedOrTimedOut=mode>=ScheduleModes.RESCHEDULE&&elapsedTimeoutTime<currentTime;if(notCompletedOrTimedOut){if(mode===ScheduleModes.RESCHEDULE){this.run(callback,timeout,mode);}return undefined;}return callback();};}cancel(id){if(this.tasks[id]){this.clock.clearTimeout(this.tasks[id]);delete this.tasks[id];}}cancelAll(){Object.values(this.tasks).forEach(this.clock.clearTimeout);this.tasks={};}}
736
736
 
@@ -751,7 +751,7 @@
751
751
  timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
752
752
  this.timeouts={ackTimer:Math.round(timerScaleFactor*DEFAULT_ACK_TIMER_MS),reclaimTimer:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMER_MS),reclaimTimeout:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMEOUT_MS),reclaimWait:Math.round(timerScaleFactor*DEFAULT_RECLAIM_WAIT_MS)};this.schedule=new Schedule();this.processId='0';// Set up our empty queues
753
753
  this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
754
- this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);// Flush the queue on page leave
754
+ this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
755
755
  this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
756
756
  this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
757
757
  setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name);}else {this.store.set(name,value);}}/**
@@ -762,7 +762,7 @@
762
762
  * Configures the timeout handler for flushing the batch queue
763
763
  */scheduleFlushBatch(){if(this.batch.enabled&&this.batch?.flushInterval){if(this.flushBatchTaskId){this.schedule.cancel(this.flushBatchTaskId);}this.flushBatchTaskId=this.schedule.run(this.flushBatch,this.batch.flushInterval,ScheduleModes.ASAP);}}/**
764
764
  * Flushes the batch queue
765
- */flushBatch(){if(!this.batchingInProgress){this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
765
+ */flushBatch(isAccessible=true){if(!this.batchingInProgress){this.isPageAccessible=isAccessible;this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item));this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
766
766
  this.scheduleFlushBatch();}}/**
767
767
  * Decides whether to retry. Overridable.
768
768
  *
@@ -804,10 +804,13 @@
804
804
  * @returns Batch dispatch info
805
805
  */getBatchDispatchInfo(batchItems){let lengthCriteriaMet=false;let lengthCriteriaExceeded=false;const configuredBatchMaxItems=this.batch?.maxItems;if(isDefined(configuredBatchMaxItems)){lengthCriteriaMet=batchItems.length===configuredBatchMaxItems;lengthCriteriaExceeded=batchItems.length>configuredBatchMaxItems;}if(lengthCriteriaMet||lengthCriteriaExceeded){return {criteriaMet:lengthCriteriaMet,criteriaExceeded:lengthCriteriaExceeded};}let sizeCriteriaMet=false;let sizeCriteriaExceeded=false;const configuredBatchMaxSize=this.batch?.maxSize;if(isDefined(configuredBatchMaxSize)&&isDefined(this.batchSizeCalcCb)){const curBatchSize=this.batchSizeCalcCb(batchItems.map(queueItem=>queueItem.item));sizeCriteriaMet=curBatchSize===configuredBatchMaxSize;sizeCriteriaExceeded=curBatchSize>configuredBatchMaxSize;}return {criteriaMet:sizeCriteriaMet,criteriaExceeded:sizeCriteriaExceeded};}processHead(){// cancel the scheduled task if it exists
806
806
  this.schedule.cancel(this.processId);// Pop the head off the queue
807
- let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
808
- const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
809
- while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// Save this to the in progress map
810
- inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
807
+ let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
808
+ const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue(el.item,el.attemptNumber+1,err,el.id);}};const enqueueItem=(el,id)=>{toRun.push({item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};let inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};// If the page is unloading, clear the previous in progress queue also to avoid any stale data
809
+ // Otherwise, the next page load will retry the items which were in progress previously
810
+ if(!this.isPageAccessible){inProgress={};}let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
811
+ while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// If the page is unloading, don't add items to the in progress queue
812
+ if(this.isPageAccessible){// Save this to the in progress map
813
+ inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now()};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
811
814
  try{const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,el.attemptNumber,this.maxAttempts,willBeRetried);}catch(err){this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE),err);}});// re-read the queue in case the process function finished immediately or added another item
812
815
  queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];this.schedule.cancel(this.processId);if(queue.length>0){const nextProcessExecutionTime=queue[0].time-now;this.processId=this.schedule.run(this.processHead,nextProcessExecutionTime,ScheduleModes.ASAP);}}// Ack continuously to prevent other tabs from claiming our queue
813
816
  ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id});trackMessageIds.push(id);}};if(Array.isArray(queue)){queue.forEach(concatIterator);}else if(queue){Object.values(queue).forEach(concatIterator);}};// add their queue to ours, resetting run-time to immediate and copying the attempt#
@@ -2465,7 +2468,7 @@
2465
2468
  * this is not supported by our sourceConfig API
2466
2469
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
2467
2470
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
2468
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2471
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
2469
2472
  // and the first call to the send method in legacy browsers
2470
2473
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
2471
2474
 
@@ -361,7 +361,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
361
361
 
362
362
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
363
363
 
364
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
364
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
365
365
 
366
366
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
367
367
 
@@ -771,7 +771,7 @@ const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-T
771
771
  * this is not supported by our sourceConfig API
772
772
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
773
773
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
774
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
774
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
775
775
  // and the first call to the send method in legacy browsers
776
776
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
777
777
 
@@ -367,7 +367,7 @@
367
367
 
368
368
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
369
369
 
370
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
370
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.11';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
371
371
 
372
372
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
373
373
 
@@ -777,7 +777,7 @@
777
777
  * this is not supported by our sourceConfig API
778
778
  */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
779
779
  return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
780
- const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
780
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
781
781
  // and the first call to the send method in legacy browsers
782
782
  xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
783
783
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rudderstack/analytics-js",
3
- "version": "3.7.9",
3
+ "version": "3.7.11",
4
4
  "description": "RudderStack JavaScript SDK",
5
5
  "main": "dist/npm/modern/cjs/index.cjs",
6
6
  "module": "dist/npm/modern/esm/index.mjs",