@rudderstack/analytics-js 3.15.2 → 3.16.0

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.
@@ -498,7 +498,7 @@
498
498
  error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
499
499
  error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
500
500
 
501
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.15.2';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';
501
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.16.0';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';
502
502
 
503
503
  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';
504
504
 
@@ -589,7 +589,7 @@
589
589
  * Handle errors
590
590
  */onError(error){this.errorHandler.onError(error,EXTERNAL_SRC_LOADER);}}
591
591
 
592
- var i=Symbol.for("preact-signals");function t(){if(!(s>1)){var i,t=false;while(undefined!==h){var r=h;h=undefined;f++;while(undefined!==r){var o=r.o;r.o=undefined;r.f&=-3;if(!(8&r.f)&&c(r))try{r.c();}catch(r){if(!t){i=r;t=true;}}r=o;}}f=0;s--;if(t)throw i;}else s--;}function r(i){if(s>0)return i();s++;try{return i();}finally{t();}}var o=undefined;var h=undefined,s=0,f=0,v=0;function e(i){if(undefined!==o){var t=i.n;if(undefined===t||t.t!==o){t={i:0,S:i,p:o.s,n:undefined,t:o,e:undefined,x:undefined,r:t};if(undefined!==o.s)o.s.n=t;o.s=t;i.n=t;if(32&o.f)i.S(t);return t;}else if(-1===t.i){t.i=0;if(undefined!==t.n){t.n.p=t.p;if(undefined!==t.p)t.p.n=t.n;t.p=o.s;t.n=undefined;o.s.n=t;o.s=t;}return t;}}}function u(i){this.v=i;this.i=0;this.n=undefined;this.t=undefined;}u.prototype.brand=i;u.prototype.h=function(){return true;};u.prototype.S=function(i){if(this.t!==i&&undefined===i.e){i.x=this.t;if(undefined!==this.t)this.t.e=i;this.t=i;}};u.prototype.U=function(i){if(undefined!==this.t){var t=i.e,r=i.x;if(undefined!==t){t.x=r;i.e=undefined;}if(undefined!==r){r.e=t;i.x=undefined;}if(i===this.t)this.t=r;}};u.prototype.subscribe=function(i){var t=this;return E(function(){var r=t.value,n=o;o=undefined;try{i(r);}finally{o=n;}});};u.prototype.valueOf=function(){return this.value;};u.prototype.toString=function(){return this.value+"";};u.prototype.toJSON=function(){return this.value;};u.prototype.peek=function(){var i=o;o=undefined;try{return this.value;}finally{o=i;}};Object.defineProperty(u.prototype,"value",{get:function(){var i=e(this);if(undefined!==i)i.i=this.i;return this.v;},set:function(i){if(i!==this.v){if(f>100)throw new Error("Cycle detected");this.v=i;this.i++;v++;s++;try{for(var r=this.t;void 0!==r;r=r.x)r.t.N();}finally{t();}}}});function d$1(i){return new u(i);}function c(i){for(var t=i.s;undefined!==t;t=t.n)if(t.S.i!==t.i||!t.S.h()||t.S.i!==t.i)return true;return false;}function a(i){for(var t=i.s;undefined!==t;t=t.n){var r=t.S.n;if(undefined!==r)t.r=r;t.S.n=t;t.i=-1;if(undefined===t.n){i.s=t;break;}}}function l(i){var t=i.s,r=undefined;while(undefined!==t){var o=t.p;if(-1===t.i){t.S.U(t);if(undefined!==o)o.n=t.n;if(undefined!==t.n)t.n.p=o;}else r=t;t.S.n=t.r;if(undefined!==t.r)t.r=undefined;t=o;}i.s=r;}function y(i){u.call(this,undefined);this.x=i;this.s=undefined;this.g=v-1;this.f=4;}(y.prototype=new u()).h=function(){this.f&=-3;if(1&this.f)return false;if(32==(36&this.f))return true;this.f&=-5;if(this.g===v)return true;this.g=v;this.f|=1;if(this.i>0&&!c(this)){this.f&=-2;return true;}var i=o;try{a(this);o=this;var t=this.x();if(16&this.f||this.v!==t||0===this.i){this.v=t;this.f&=-17;this.i++;}}catch(i){this.v=i;this.f|=16;this.i++;}o=i;l(this);this.f&=-2;return true;};y.prototype.S=function(i){if(undefined===this.t){this.f|=36;for(var t=this.s;undefined!==t;t=t.n)t.S.S(t);}u.prototype.S.call(this,i);};y.prototype.U=function(i){if(undefined!==this.t){u.prototype.U.call(this,i);if(undefined===this.t){this.f&=-33;for(var t=this.s;undefined!==t;t=t.n)t.S.U(t);}}};y.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var i=this.t;undefined!==i;i=i.x)i.t.N();}};Object.defineProperty(y.prototype,"value",{get:function(){if(1&this.f)throw new Error("Cycle detected");var i=e(this);this.h();if(undefined!==i)i.i=this.i;if(16&this.f)throw this.v;return this.v;}});function _(i){var r=i.u;i.u=undefined;if("function"==typeof r){s++;var n=o;o=undefined;try{r();}catch(t){i.f&=-2;i.f|=8;g(i);throw t;}finally{o=n;t();}}}function g(i){for(var t=i.s;undefined!==t;t=t.n)t.S.U(t);i.x=undefined;i.s=undefined;_(i);}function p(i){if(o!==this)throw new Error("Out-of-order effect");l(this);o=i;this.f&=-2;if(8&this.f)g(this);t();}function b(i){this.x=i;this.u=undefined;this.s=undefined;this.o=undefined;this.f=32;}b.prototype.c=function(){var i=this.S();try{if(8&this.f)return;if(void 0===this.x)return;var t=this.x();if("function"==typeof t)this.u=t;}finally{i();}};b.prototype.S=function(){if(1&this.f)throw new Error("Cycle detected");this.f|=1;this.f&=-9;_(this);a(this);s++;var i=o;o=this;return p.bind(this,i);};b.prototype.N=function(){if(!(2&this.f)){this.f|=2;this.o=h;h=this;}};b.prototype.d=function(){this.f|=8;if(!(1&this.f))g(this);};function E(i){var t=new b(i);try{t.c();}catch(i){t.d();throw i;}return t.d.bind(t);}
592
+ var i=Symbol.for("preact-signals");function t(){if(!(s>1)){var i,t=false;while(void 0!==h){var r=h;h=void 0;f++;while(void 0!==r){var o=r.o;r.o=void 0;r.f&=-3;if(!(8&r.f)&&c(r))try{r.c();}catch(r){if(!t){i=r;t=true;}}r=o;}}f=0;s--;if(t)throw i;}else s--;}function r(i){if(s>0)return i();s++;try{return i();}finally{t();}}var o=void 0;var h=void 0,s=0,f=0,v=0;function e(i){if(void 0!==o){var t=i.n;if(void 0===t||t.t!==o){t={i:0,S:i,p:o.s,n:void 0,t:o,e:void 0,x:void 0,r:t};if(void 0!==o.s)o.s.n=t;o.s=t;i.n=t;if(32&o.f)i.S(t);return t;}else if(-1===t.i){t.i=0;if(void 0!==t.n){t.n.p=t.p;if(void 0!==t.p)t.p.n=t.n;t.p=o.s;t.n=void 0;o.s.n=t;o.s=t;}return t;}}}function u(i){this.v=i;this.i=0;this.n=void 0;this.t=void 0;}u.prototype.brand=i;u.prototype.h=function(){return true;};u.prototype.S=function(i){if(this.t!==i&&void 0===i.e){i.x=this.t;if(void 0!==this.t)this.t.e=i;this.t=i;}};u.prototype.U=function(i){if(void 0!==this.t){var t=i.e,r=i.x;if(void 0!==t){t.x=r;i.e=void 0;}if(void 0!==r){r.e=t;i.x=void 0;}if(i===this.t)this.t=r;}};u.prototype.subscribe=function(i){var t=this;return E(function(){var r=t.value,n=o;o=void 0;try{i(r);}finally{o=n;}});};u.prototype.valueOf=function(){return this.value;};u.prototype.toString=function(){return this.value+"";};u.prototype.toJSON=function(){return this.value;};u.prototype.peek=function(){var i=o;o=void 0;try{return this.value;}finally{o=i;}};Object.defineProperty(u.prototype,"value",{get:function(){var i=e(this);if(void 0!==i)i.i=this.i;return this.v;},set:function(i){if(i!==this.v){if(f>100)throw new Error("Cycle detected");this.v=i;this.i++;v++;s++;try{for(var r=this.t;void 0!==r;r=r.x)r.t.N();}finally{t();}}}});function d$1(i){return new u(i);}function c(i){for(var t=i.s;void 0!==t;t=t.n)if(t.S.i!==t.i||!t.S.h()||t.S.i!==t.i)return true;return false;}function a(i){for(var t=i.s;void 0!==t;t=t.n){var r=t.S.n;if(void 0!==r)t.r=r;t.S.n=t;t.i=-1;if(void 0===t.n){i.s=t;break;}}}function l(i){var t=i.s,r=void 0;while(void 0!==t){var o=t.p;if(-1===t.i){t.S.U(t);if(void 0!==o)o.n=t.n;if(void 0!==t.n)t.n.p=o;}else r=t;t.S.n=t.r;if(void 0!==t.r)t.r=void 0;t=o;}i.s=r;}function y(i){u.call(this,void 0);this.x=i;this.s=void 0;this.g=v-1;this.f=4;}(y.prototype=new u()).h=function(){this.f&=-3;if(1&this.f)return false;if(32==(36&this.f))return true;this.f&=-5;if(this.g===v)return true;this.g=v;this.f|=1;if(this.i>0&&!c(this)){this.f&=-2;return true;}var i=o;try{a(this);o=this;var t=this.x();if(16&this.f||this.v!==t||0===this.i){this.v=t;this.f&=-17;this.i++;}}catch(i){this.v=i;this.f|=16;this.i++;}o=i;l(this);this.f&=-2;return true;};y.prototype.S=function(i){if(void 0===this.t){this.f|=36;for(var t=this.s;void 0!==t;t=t.n)t.S.S(t);}u.prototype.S.call(this,i);};y.prototype.U=function(i){if(void 0!==this.t){u.prototype.U.call(this,i);if(void 0===this.t){this.f&=-33;for(var t=this.s;void 0!==t;t=t.n)t.S.U(t);}}};y.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var i=this.t;void 0!==i;i=i.x)i.t.N();}};Object.defineProperty(y.prototype,"value",{get:function(){if(1&this.f)throw new Error("Cycle detected");var i=e(this);this.h();if(void 0!==i)i.i=this.i;if(16&this.f)throw this.v;return this.v;}});function _(i){var r=i.u;i.u=void 0;if("function"==typeof r){s++;var n=o;o=void 0;try{r();}catch(t){i.f&=-2;i.f|=8;g(i);throw t;}finally{o=n;t();}}}function g(i){for(var t=i.s;void 0!==t;t=t.n)t.S.U(t);i.x=void 0;i.s=void 0;_(i);}function p(i){if(o!==this)throw new Error("Out-of-order effect");l(this);o=i;this.f&=-2;if(8&this.f)g(this);t();}function b(i){this.x=i;this.u=void 0;this.s=void 0;this.o=void 0;this.f=32;}b.prototype.c=function(){var i=this.S();try{if(8&this.f)return;if(void 0===this.x)return;var t=this.x();if("function"==typeof t)this.u=t;}finally{i();}};b.prototype.S=function(){if(1&this.f)throw new Error("Cycle detected");this.f|=1;this.f&=-9;_(this);a(this);s++;var i=o;o=this;return p.bind(this,i);};b.prototype.N=function(){if(!(2&this.f)){this.f|=2;this.o=h;h=this;}};b.prototype.d=function(){this.f|=8;if(!(1&this.f))g(this);};function E(i){var t=new b(i);try{t.c();}catch(i){t.d();throw i;}return t.d.bind(t);}
593
593
 
594
594
  /**
595
595
  * A buffer queue to serve as a store for any type of data
@@ -776,8 +776,19 @@
776
776
 
777
777
  /**
778
778
  * A service to handle errors
779
- */class ErrorHandler{// If no logger is passed errors will be thrown as unhandled error
780
- constructor(httpClient,logger){this.httpClient=httpClient;this.logger=logger;this.attachErrorListeners();}attachErrorListeners(){globalThis.addEventListener('error',event=>{this.onError(event,ERROR_HANDLER,undefined,ErrorType.UNHANDLEDEXCEPTION);});globalThis.addEventListener('unhandledrejection',event=>{this.onError(event,ERROR_HANDLER,undefined,ErrorType.UNHANDLEDREJECTION);});}onError(error,context='',customMessage='',errorType=ErrorType.HANDLEDEXCEPTION){try{const errInstance=getErrInstance(error,errorType);const normalizedError=normalizeError(errInstance,this.logger);if(isUndefined(normalizedError)){return;}const customMsgVal=customMessage?`${customMessage} - `:'';const errorMsgPrefix=`${context}${LOG_CONTEXT_SEPARATOR}${customMsgVal}`;const bsException=createBugsnagException(normalizedError,errorMsgPrefix);const stacktrace=getStacktrace(normalizedError);const isSdkDispatched=stacktrace.includes(MANUAL_ERROR_IDENTIFIER);// Filter errors that are not originated in the SDK.
779
+ */class ErrorHandler{initialized=false;// If no logger is passed errors will be thrown as unhandled error
780
+ constructor(httpClient,logger){this.httpClient=httpClient;this.logger=logger;}/**
781
+ * Initializes the error handler by attaching global error listeners.
782
+ * This method should be called once after construction.
783
+ */init(){if(this.initialized){return;}this.attachErrorListeners();this.initialized=true;}/**
784
+ * Attach error listeners to the global window object
785
+ */attachErrorListeners(){globalThis.addEventListener('error',event=>{this.onError(event,ERROR_HANDLER,undefined,ErrorType.UNHANDLEDEXCEPTION);});globalThis.addEventListener('unhandledrejection',event=>{this.onError(event,ERROR_HANDLER,undefined,ErrorType.UNHANDLEDREJECTION);});}/**
786
+ * Handle errors
787
+ * @param error - The error to handle
788
+ * @param context - The context of where the error occurred
789
+ * @param customMessage - The custom message of the error
790
+ * @param errorType - The type of the error (handled or unhandled)
791
+ */onError(error,context='',customMessage='',errorType=ErrorType.HANDLEDEXCEPTION){try{const errInstance=getErrInstance(error,errorType);const normalizedError=normalizeError(errInstance,this.logger);if(isUndefined(normalizedError)){return;}const customMsgVal=customMessage?`${customMessage} - `:'';const errorMsgPrefix=`${context}${LOG_CONTEXT_SEPARATOR}${customMsgVal}`;const bsException=createBugsnagException(normalizedError,errorMsgPrefix);const stacktrace=getStacktrace(normalizedError);const isSdkDispatched=stacktrace.includes(MANUAL_ERROR_IDENTIFIER);// Filter errors that are not originated in the SDK.
781
792
  // In case of NPM installations, the unhandled errors from the SDK cannot be identified
782
793
  // and will NOT be reported unless they occur in plugins or integrations.
783
794
  if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value&&isAllowedToBeNotified(bsException)){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};// enrich error payload
@@ -789,7 +800,8 @@
789
800
  * occurred and send to external error monitoring service via a plugin
790
801
  *
791
802
  * @param {string} breadcrumb breadcrumbs message
792
- */leaveBreadcrumb(breadcrumb){try{state.reporting.breadcrumbs.value=[...state.reporting.breadcrumbs.value,createNewBreadcrumb(breadcrumb)];}catch(err){this.onError(err,BREADCRUMB_ERROR(ERROR_HANDLER));}}}const defaultErrorHandler=new ErrorHandler(defaultHttpClient,defaultLogger);
803
+ */leaveBreadcrumb(breadcrumb){try{state.reporting.breadcrumbs.value=[...state.reporting.breadcrumbs.value,createNewBreadcrumb(breadcrumb)];}catch(err){this.onError(err,BREADCRUMB_ERROR(ERROR_HANDLER));}}}// Note: Remember to call defaultErrorHandler.init() before using it
804
+ const defaultErrorHandler=new ErrorHandler(defaultHttpClient,defaultLogger);
793
805
 
794
806
  // to next or return the value if it is the last one instead of an array per
795
807
  // plugin that is the normal invoke
@@ -895,20 +907,23 @@
895
907
 
896
908
  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={};}}
897
909
 
898
- const RETRY_QUEUE_PROCESS_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Process function threw an error.`;const RETRY_QUEUE_ENTRY_REMOVE_ERROR=(context,entry,attempt)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to remove local storage entry "${entry}" (attempt: ${attempt}.`;
910
+ const RETRY_QUEUE_PROCESS_ERROR=(context,errMsg)=>`${context}${LOG_CONTEXT_SEPARATOR}An unknown error occurred while processing the queue item. ${errMsg}`;const RETRY_QUEUE_ENTRY_REMOVE_ERROR=(context,entry,attempt)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to remove local storage entry "${entry}" (attempt: ${attempt}.`;
899
911
 
900
912
  const DEFAULT_MIN_RETRY_DELAY_MS=1000;const DEFAULT_MAX_RETRY_DELAY_MS=30000;const DEFAULT_BACKOFF_FACTOR=2;const DEFAULT_BACKOFF_JITTER=0;const DEFAULT_MAX_RETRY_ATTEMPTS=Infinity;const DEFAULT_MAX_ITEMS=Infinity;const DEFAULT_ACK_TIMER_MS=1000;const DEFAULT_RECLAIM_TIMER_MS=3000;const DEFAULT_RECLAIM_TIMEOUT_MS=10000;const DEFAULT_RECLAIM_WAIT_MS=500;const MIN_TIMER_SCALE_FACTOR=1;const MAX_TIMER_SCALE_FACTOR=10;const DEFAULT_MAX_BATCH_SIZE_BYTES=512*1024;// 512 KB; this is also the max size of a batch
901
913
  const DEFAULT_MAX_BATCH_ITEMS=100;const DEFAULT_BATCH_FLUSH_INTERVAL_MS=60*1000;// 1 minutes
902
914
  const BATCH_QUEUE_ITEM_TYPE='Batch';const SINGLE_QUEUE_ITEM_TYPE='Single';
903
915
 
904
- const sortByTime=(a,b)=>a.time-b.time;const RETRY_QUEUE='RetryQueue';/**
905
- * Constructs a RetryQueue backed by localStorage
906
- *
907
- * @constructor
908
- * @param {String} name The name of the queue. Will be used to find abandoned queues and retry their items
909
- * @param {Object} [opts] Optional argument to override `maxItems`, `maxAttempts`, `minRetryDelay, `maxRetryDelay`, `backoffFactor` and `backoffJitter`.
910
- * @param {QueueProcessCallback} fn The function to call in order to process an item added to the queue
911
- */class RetryQueue{constructor(name,options,queueProcessCb,storeManager,storageType=LOCAL_STORAGE,logger,queueBatchItemsSizeCalculatorCb){this.storeManager=storeManager;this.logger=logger;this.name=name;this.id=generateUUID();this.processQueueCb=queueProcessCb;this.batchSizeCalcCb=queueBatchItemsSizeCalculatorCb;this.maxItems=options.maxItems||DEFAULT_MAX_ITEMS;this.maxAttempts=options.maxAttempts||DEFAULT_MAX_RETRY_ATTEMPTS;this.batch={enabled:false};this.configureBatchMode(options);this.backoff={minRetryDelay:options.minRetryDelay||DEFAULT_MIN_RETRY_DELAY_MS,maxRetryDelay:options.maxRetryDelay||DEFAULT_MAX_RETRY_DELAY_MS,factor:options.backoffFactor||DEFAULT_BACKOFF_FACTOR,jitter:options.backoffJitter||DEFAULT_BACKOFF_JITTER};// Limit the timer scale factor to the minimum value
916
+ const sortByTime=(a,b)=>a.time-b.time;const RETRY_QUEUE='RetryQueue';class RetryQueue{/**
917
+ * Constructs a RetryQueue backed by localStorage
918
+ *
919
+ * @param {String} name The name of the queue. Will be used to find abandoned queues and retry their items
920
+ * @param {QueueOpts} [options] Optional argument to override `maxItems`, `maxAttempts`, `minRetryDelay, `maxRetryDelay`, `backoffFactor` and `backoffJitter`.
921
+ * @param {QueueProcessCallback} queueProcessCb The function to call in order to process an item added to the queue
922
+ * @param {IStoreManager} storeManager The store manager instance to use
923
+ * @param {StorageType} [storageType] The storage type to use. Defaults to LOCAL_STORAGE
924
+ * @param {ILogger} [logger] The logger to use
925
+ * @param {QueueBatchItemsSizeCalculatorCallback} [queueBatchItemsSizeCalculatorCb] The callback to use to calculate the size of items in the batch queue
926
+ */constructor(name,options,queueProcessCb,storeManager,storageType=LOCAL_STORAGE,logger,queueBatchItemsSizeCalculatorCb){this.storeManager=storeManager;this.logger=logger;this.name=name;this.id=generateUUID();this.processQueueCb=queueProcessCb;this.batchSizeCalcCb=queueBatchItemsSizeCalculatorCb;this.maxItems=options.maxItems||DEFAULT_MAX_ITEMS;this.maxAttempts=options.maxAttempts||DEFAULT_MAX_RETRY_ATTEMPTS;this.batch={enabled:false};this.configureBatchMode(options);this.backoff={minRetryDelay:options.minRetryDelay||DEFAULT_MIN_RETRY_DELAY_MS,maxRetryDelay:options.maxRetryDelay||DEFAULT_MAX_RETRY_DELAY_MS,factor:options.backoffFactor||DEFAULT_BACKOFF_FACTOR,jitter:options.backoffJitter||DEFAULT_BACKOFF_JITTER};// Limit the timer scale factor to the minimum value
912
927
  let timerScaleFactor=Math.max(options.timerScaleFactor??MIN_TIMER_SCALE_FACTOR,MIN_TIMER_SCALE_FACTOR);// Limit the timer scale factor to the maximum value
913
928
  timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
914
929
  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
@@ -942,10 +957,10 @@
942
957
  * @param entry New item added to the retry queue
943
958
  * @returns Undefined or batch entry object
944
959
  */handleNewItemForBatch(entry){let curEntry;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(!this.batchingInProgress){this.batchingInProgress=true;batchQueue=batchQueue.slice(-batchQueue.length);batchQueue.push(entry);const batchDispatchInfo=this.getBatchDispatchInfo(batchQueue);// if batch criteria is met, queue the batch events to the main queue and clear batch queue
945
- if(batchDispatchInfo.criteriaMet||batchDispatchInfo.criteriaExceeded){let batchItems;if(batchDispatchInfo.criteriaExceeded){batchItems=batchQueue.slice(0,batchQueue.length-1).map(queueItem=>queueItem.item);batchQueue=[entry];}else {batchItems=batchQueue.map(queueItem=>queueItem.item);batchQueue=[];}// Don't make any batch request if there are no items
946
- if(batchItems.length>0){curEntry=this.genQueueItem(batchItems,BATCH_QUEUE_ITEM_TYPE);}// re-attach the timeout handler
960
+ if(batchDispatchInfo.criteriaMet||batchDispatchInfo.criteriaExceeded){let batchEntries;if(batchDispatchInfo.criteriaExceeded){batchEntries=batchQueue.slice(0,batchQueue.length-1);batchQueue=[entry];}else {batchEntries=batchQueue;batchQueue=[];}// Don't make any batch request if there are no items
961
+ if(batchEntries.length>0){const isReclaimed=batchEntries.every(queueItem=>queueItem.reclaimed);const batchItems=batchEntries.map(queueItem=>queueItem.item);if(isReclaimed){curEntry=this.genQueueItem(batchItems,BATCH_QUEUE_ITEM_TYPE,true);}else {curEntry=this.genQueueItem(batchItems,BATCH_QUEUE_ITEM_TYPE);}}// re-attach the timeout handler
947
962
  this.scheduleFlushBatch();}this.batchingInProgress=false;}else {batchQueue.push(entry);}// update the batch queue
948
- this.setStorageEntry(QueueStatuses.BATCH_QUEUE,batchQueue);return curEntry;}pushToMainQueue(curEntry){let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];queue=queue.slice(-(this.maxItems-1));queue.push(curEntry);queue=queue.sort(sortByTime);this.setStorageEntry(QueueStatuses.QUEUE,queue);if(this.scheduleTimeoutActive){this.processHead();}}/**
963
+ this.setStorageEntry(QueueStatuses.BATCH_QUEUE,batchQueue);return curEntry;}pushToMainQueue(curEntry){let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];if(this.maxItems>1){queue=queue.slice(-(this.maxItems-1));}else {queue=[];}queue.push(curEntry);queue=queue.sort(sortByTime);this.setStorageEntry(QueueStatuses.QUEUE,queue);if(this.scheduleTimeoutActive){this.processHead();}}/**
949
964
  * Adds an item to the queue
950
965
  *
951
966
  * @param {Object} itemData The item to process
@@ -953,38 +968,46 @@
953
968
  * Generates a queue item
954
969
  * @param itemData Queue item data
955
970
  * @returns Queue item
956
- */genQueueItem(itemData,type=SINGLE_QUEUE_ITEM_TYPE){return {item:itemData,attemptNumber:0,time:this.schedule.now(),id:generateUUID(),type};}/**
971
+ */genQueueItem(itemData,type=SINGLE_QUEUE_ITEM_TYPE,reclaimed){return {item:itemData,attemptNumber:0,time:this.schedule.now(),id:generateUUID(),type,...(isDefined(reclaimed)?{reclaimed}:{})};}/**
957
972
  * Adds an item to the retry queue
958
973
  *
959
974
  * @param {Object} qItem The item to process
960
- * @param {Error} [error] The error that occurred during processing
961
- */requeue(qItem,error){const{attemptNumber,item,type,id}=qItem;// Increment the attempt number as we're about to retry
962
- const attemptNumberToUse=attemptNumber+1;if(this.shouldRetry(item,attemptNumberToUse)){this.enqueue({item,attemptNumber:attemptNumberToUse,time:this.schedule.now()+this.getDelay(attemptNumberToUse),id:id??generateUUID(),type});}}/**
975
+ */requeue(qItem){const{attemptNumber,item,type,id,firstAttemptedAt,lastAttemptedAt,reclaimed}=qItem;// Increment the attempt number as we're about to retry
976
+ const attemptNumberToUse=attemptNumber+1;if(this.shouldRetry(item,attemptNumberToUse)){this.enqueue({item,attemptNumber:attemptNumberToUse,time:this.schedule.now()+this.getDelay(attemptNumberToUse),id:id??generateUUID(),type,firstAttemptedAt,lastAttemptedAt,reclaimed});}}/**
963
977
  * Returns the information about whether the batch criteria is met or exceeded
964
978
  * @param batchItems Prospective batch items
965
979
  * @returns Batch dispatch info
966
980
  */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
967
981
  this.schedule.cancel(this.processId);// Pop the head off the queue
968
982
  let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
969
- 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,err);}};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
983
+ const processItemCallback=(el,id)=>(err,res)=>{const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const inProgressItem=inProgress[id];const firstAttemptedAt=inProgressItem?.firstAttemptedAt;const lastAttemptedAt=inProgressItem?.lastAttemptedAt;delete inProgress[id];this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);if(err){this.requeue({...el,firstAttemptedAt,lastAttemptedAt});}};const enqueueItem=(el,id)=>{toRun.push({id,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
970
984
  // Otherwise, the next page load will retry the items which were in progress previously
971
985
  if(!this.isPageAccessible){inProgress={};}let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
972
986
  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
973
987
  if(this.isPageAccessible){// Save this to the in progress map
974
- inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now(),type:el.type};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
975
- 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
988
+ inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now(),type:el.type,firstAttemptedAt:el.firstAttemptedAt,lastAttemptedAt:el.lastAttemptedAt,reclaimed:el.reclaimed};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
989
+ try{const now=this.schedule.now();const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const inProgressItem=inProgress[el.id];const firstAttemptedAt=inProgressItem?.firstAttemptedAt??now;const lastAttemptedAt=inProgressItem?.lastAttemptedAt??now;// A decimal integer representing the seconds since the first attempt
990
+ const timeSinceFirstAttempt=Math.round((now-firstAttemptedAt)/1000);// A decimal integer representing the seconds since the last attempt
991
+ const timeSinceLastAttempt=Math.round((now-lastAttemptedAt)/1000);// Indicates if the item has been reclaimed from local storage
992
+ const reclaimed=inProgressItem?.reclaimed??false;// Update the first attempted at timestamp for the in progress item
993
+ inProgressItem.firstAttemptedAt=firstAttemptedAt;// Update the last attempted at to current timestamp for the in progress item
994
+ inProgressItem.lastAttemptedAt=now;inProgress[el.id]=inProgressItem;this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,{retryAttemptNumber:el.attemptNumber,maxRetryAttempts:this.maxAttempts,willBeRetried,timeSinceFirstAttempt,timeSinceLastAttempt,reclaimed});}catch(err){let errMsg='';if(el.attemptNumber<this.maxAttempts){errMsg='The item will be requeued.';if(el.attemptNumber>0){errMsg=`${errMsg} Retry attempt ${el.attemptNumber} of ${this.maxAttempts}.`;}// requeue the item to be retried
995
+ el.done(err);}else {errMsg=`Retries exhausted (${this.maxAttempts}). The item will be dropped.`;// drop the event as we're unable to process it
996
+ // after the max attempts are exhausted
997
+ el.done();}this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE,errMsg),err);}});// re-read the queue in case the process function finished immediately or added another item
976
998
  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
977
999
  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,errorHandler:this.storeManager.errorHandler,logger:this.storeManager.logger});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 {// Hack to determine the item type by the contents of the entry
978
1000
  // After some point, we can remove this hack as most of the stale data will have been processed
979
1001
  // and the new entries will have the type field set
980
- const type=Array.isArray(el.item)?BATCH_QUEUE_ITEM_TYPE:SINGLE_QUEUE_ITEM_TYPE;our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id,type:el.type??type});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#
1002
+ const type=Array.isArray(el.item)?BATCH_QUEUE_ITEM_TYPE:SINGLE_QUEUE_ITEM_TYPE;our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id,type:el.type??type,firstAttemptedAt:el.firstAttemptedAt,lastAttemptedAt:el.lastAttemptedAt,// Mark the item as reclaimed from local storage
1003
+ reclaimed:true});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#
981
1004
  addConcatQueue(their.queue,0);// Process batch queue items
982
- if(this.batch.enabled){their.batchQueue.forEach(el=>{const id=el.id??generateUUID();el.type=el.type??SINGLE_QUEUE_ITEM_TYPE;el.time=this.schedule.now();if(trackMessageIds.includes(id));else {this.enqueue(el);trackMessageIds.push(id);}});}else {// if batching is not enabled in the current instance, add those items to the main queue directly
1005
+ if(this.batch.enabled){their.batchQueue.forEach(el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {this.enqueue({...el,id,// Mark the item as reclaimed from local storage
1006
+ reclaimed:true,type:el.type??SINGLE_QUEUE_ITEM_TYPE,time:this.schedule.now()});trackMessageIds.push(id);}});}else {// if batching is not enabled in the current instance, add those items to the main queue directly
983
1007
  addConcatQueue(their.batchQueue,0);}// if the queue is abandoned, all the in-progress are failed. retry them immediately and increment the attempt#
984
- addConcatQueue(their.inProgress,1);our.queue=our.queue.sort(sortByTime);this.setStorageEntry(QueueStatuses.QUEUE,our.queue);// remove all keys one by on next tick to avoid NS_ERROR_STORAGE_BUSY error
1008
+ addConcatQueue(their.inProgress,1);our.queue.sort(sortByTime);this.setStorageEntry(QueueStatuses.QUEUE,our.queue);// remove all keys one by on next tick to avoid NS_ERROR_STORAGE_BUSY error
985
1009
  this.clearQueueEntries(other,1);// process the new items we claimed
986
- this.processHead();}// eslint-disable-next-line class-methods-use-this
987
- clearQueueEntries(other,localStorageBackoff){this.removeStorageEntry(other,0,localStorageBackoff);}removeStorageEntry(store,entryIdx,backoff,attempt=1){const maxAttempts=2;const queueEntryKeys=Object.keys(QueueStatuses);const entry=QueueStatuses[queueEntryKeys[entryIdx]];globalThis.setTimeout(()=>{try{store.remove(entry);// clear the next entry
1010
+ this.processHead();}clearQueueEntries(other,localStorageBackoff){this.removeStorageEntry(other,0,localStorageBackoff);}removeStorageEntry(store,entryIdx,backoff,attempt=1){const maxAttempts=2;const queueEntryKeys=Object.keys(QueueStatuses);const entry=QueueStatuses[queueEntryKeys[entryIdx]];globalThis.setTimeout(()=>{try{store.remove(entry);// clear the next entry
988
1011
  if(entryIdx+1<queueEntryKeys.length){this.removeStorageEntry(store,entryIdx+1,backoff);}}catch(err){const storageBusyErr='NS_ERROR_STORAGE_BUSY';const isLocalStorageBusy=err.name===storageBusyErr||err.code===storageBusyErr||err.code===0x80630001;if(isLocalStorageBusy&&attempt<maxAttempts){// Try clearing the same entry again with some extra delay
989
1012
  this.removeStorageEntry(store,entryIdx,backoff+40,attempt+1);}else {this.logger?.error(RETRY_QUEUE_ENTRY_REMOVE_ERROR(RETRY_QUEUE,entry,attempt),err);}// clear the next entry after we've exhausted our attempts
990
1013
  if(attempt===maxAttempts&&entryIdx+1<queueEntryKeys.length){this.removeStorageEntry(store,entryIdx+1,backoff);}}},backoff);}checkReclaim(){const createReclaimStartTask=store=>()=>{if(store.get(QueueStatuses.RECLAIM_END)!==this.id){return;}if(store.get(QueueStatuses.RECLAIM_START)!==this.id){return;}this.reclaim(store.id);};const createReclaimEndTask=store=>()=>{if(store.get(QueueStatuses.RECLAIM_START)!==this.id){return;}store.set(QueueStatuses.RECLAIM_END,this.id);this.schedule.run(createReclaimStartTask(store),this.timeouts.reclaimWait,ScheduleModes.ABANDON);};const tryReclaim=store=>{store.set(QueueStatuses.RECLAIM_START,this.id);store.set(QueueStatuses.ACK,this.schedule.now());this.schedule.run(createReclaimEndTask(store),this.timeouts.reclaimWait,ScheduleModes.ABANDON);};const findOtherQueues=name=>{const res=[];const storageEngine=this.store.getOriginalEngine();let storageKeys=[];// 'keys' API is not supported by all the core SDK versions
@@ -1243,8 +1266,8 @@
1243
1266
  case 404:{logger?.warn(DMT_SERVER_ACCESS_DENIED_WARNING(DMT_PLUGIN));eventsToSend.push(event);break;}default:{if(dest.propagateEventsUntransformedOnError===true){logger?.warn(DMT_REQUEST_FAILED_ERROR(DMT_PLUGIN,dest.displayName,status,ACTION_TO_SEND_UNTRANSFORMED_EVENT));eventsToSend.push(event);}else {logger?.error(DMT_REQUEST_FAILED_ERROR(DMT_PLUGIN,dest.displayName,status,ACTION_TO_DROP_EVENT));}break;}}eventsToSend?.forEach(tEvent=>{if(isNonEmptyObject(tEvent)){pluginsManager.invokeSingle(NATIVE_DEST_EXT_POINT,state,tEvent,dest,errorHandler,logger);}});}catch(e){errorHandler?.onError(e,DMT_PLUGIN,DMT_EXCEPTION(dest.displayName));}});};
1244
1267
 
1245
1268
  const pluginName$a='DeviceModeTransformation';const DeviceModeTransformation=()=>({name:pluginName$a,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$a];},transformEvent:{init(state,pluginsManager,httpClient,storeManager,errorHandler,logger){const writeKey=state.lifecycle.writeKey.value;httpClient.setAuthHeader(writeKey);const eventsQueue=new RetryQueue(// adding write key to the queue name to avoid conflicts
1246
- `${QUEUE_NAME$2}_${writeKey}` ,DEFAULT_TRANSFORMATION_QUEUE_OPTIONS,(item,done,attemptNumber,maxRetryAttempts)=>{const payload=createPayload(item.event,item.destinationIds,item.token);httpClient.getAsyncData({url:`${state.lifecycle.activeDataplaneUrl.value}/transform`,options:{method:'POST',data:getDMTDeliveryPayload(payload),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$1,callback:(result,details)=>{const isRetryable=isErrRetryable(details?.xhr?.status??0);// If there is no error, or the error is not retryable, or the attempt number is the max retry attempts, then attempt send the event to the destinations
1247
- if(isUndefined(details?.error)||!isRetryable||attemptNumber===maxRetryAttempts){sendTransformedEventToDestinations(state,pluginsManager,item.destinationIds,result,details?.xhr?.status,item.event,errorHandler,logger);done(null);}else {// Requeue the item as the error is retryable.
1269
+ `${QUEUE_NAME$2}_${writeKey}` ,DEFAULT_TRANSFORMATION_QUEUE_OPTIONS,(item,done,qItemProcessInfo)=>{const payload=createPayload(item.event,item.destinationIds,item.token);httpClient.getAsyncData({url:`${state.lifecycle.activeDataplaneUrl.value}/transform`,options:{method:'POST',data:getDMTDeliveryPayload(payload),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$1,callback:(result,details)=>{const isRetryable=isErrRetryable(details?.xhr?.status??0);// If there is no error, or the error is not retryable, or the attempt number is the max retry attempts, then attempt send the event to the destinations
1270
+ if(isUndefined(details?.error)||!isRetryable||qItemProcessInfo.retryAttemptNumber===qItemProcessInfo.maxRetryAttempts){sendTransformedEventToDestinations(state,pluginsManager,item.destinationIds,result,details?.xhr?.status,item.event,errorHandler,logger);done(null);}else {// Requeue the item as the error is retryable.
1248
1271
  done(details);}}});},storeManager,MEMORY_STORAGE);return eventsQueue;},enqueue(state,eventsQueue,event,destinations){const destinationIds=destinations.map(d=>d.id);eventsQueue.addItem({event,destinationIds,token:state.session.authToken.value});}}});
1249
1272
 
1250
1273
  const externallyLoadedSessionStorageKeys={segment:'ajs_anonymous_id'};
@@ -1490,7 +1513,7 @@
1490
1513
 
1491
1514
  const pluginName$3='StorageEncryption';const StorageEncryption=()=>({name:pluginName$3,initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$3];},storage:{encrypt(value){return encryptBrowser(value);},decrypt(value){return decryptBrowser(value);}}});
1492
1515
 
1493
- /* eslint-disable no-use-before-define */const crypto$1=(typeof globalThis!='undefined'?globalThis:undefined)?.crypto||(typeof global!='undefined'?global:undefined)?.crypto||(typeof window!='undefined'?window:undefined)?.crypto||(typeof self!='undefined'?self:undefined)?.crypto||(typeof frames!='undefined'?frames:undefined)?.[0]?.crypto;let randomWordArray;if(crypto$1){randomWordArray=nBytes=>{const words=[];for(let i=0,rcache;i<nBytes;i+=4){words.push(crypto$1.getRandomValues(new Uint32Array(1))[0]);}return new WordArray(words,nBytes);};}else {// Because there is no global crypto property in this context, cryptographically unsafe Math.random() is used.
1516
+ /* eslint-disable no-use-before-define */const crypto$1=(typeof globalThis!='undefined'?globalThis:void 0)?.crypto||(typeof global!='undefined'?global:void 0)?.crypto||(typeof window!='undefined'?window:void 0)?.crypto||(typeof self!='undefined'?self:void 0)?.crypto||(typeof frames!='undefined'?frames:void 0)?.[0]?.crypto;let randomWordArray;if(crypto$1){randomWordArray=nBytes=>{const words=[];for(let i=0,rcache;i<nBytes;i+=4){words.push(crypto$1.getRandomValues(new Uint32Array(1))[0]);}return new WordArray(words,nBytes);};}else {// Because there is no global crypto property in this context, cryptographically unsafe Math.random() is used.
1494
1517
  randomWordArray=nBytes=>{const words=[];const r=m_w=>{let _m_w=m_w;let _m_z=0x3ade68b1;const mask=0xffffffff;return ()=>{_m_z=0x9069*(_m_z&0xFFFF)+(_m_z>>0x10)&mask;_m_w=0x4650*(_m_w&0xFFFF)+(_m_w>>0x10)&mask;let result=(_m_z<<0x10)+_m_w&mask;result/=0x100000000;result+=0.5;return result*(Math.random()>0.5?1:-1);};};for(let i=0,rcache;i<nBytes;i+=4){const _r=r((rcache||Math.random())*0x100000000);rcache=_r()*0x3ade67b7;words.push(_r()*0x100000000|0);}return new WordArray(words,nBytes);};}/**
1495
1518
  * Base class for inheritance.
1496
1519
  */class Base{/**
@@ -2475,7 +2498,15 @@
2475
2498
 
2476
2499
  const EVENT_DELIVERY_FAILURE_ERROR_PREFIX=(context,originalErrorMsg)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to deliver event(s). Cause: ${originalErrorMsg}.`;
2477
2500
 
2478
- const getBatchDeliveryPayload=(events,currentTime,logger)=>{const batchPayload={batch:events,sentAt:currentTime};return stringifyWithoutCircular(batchPayload,true,undefined,logger);};const getNormalizedQueueOptions=queueOpts=>mergeDeepRight(DEFAULT_RETRY_QUEUE_OPTIONS,queueOpts);const getDeliveryUrl=(dataplaneUrl,endpoint)=>{const dpUrl=new URL(dataplaneUrl);return new URL(removeDuplicateSlashes([dpUrl.pathname,'/',DATA_PLANE_API_VERSION,'/',endpoint].join('')),dpUrl).href;};const getBatchDeliveryUrl=dataplaneUrl=>getDeliveryUrl(dataplaneUrl,'batch');const logErrorOnFailure=(details,isRetryable,willBeRetried,attemptNumber,maxRetryAttempts,logger)=>{let errMsg=EVENT_DELIVERY_FAILURE_ERROR_PREFIX(XHR_QUEUE_PLUGIN,details?.error?.message??'Unknown');const dropMsg=`The event(s) will be dropped.`;if(isRetryable){if(willBeRetried){errMsg=`${errMsg} The event(s) will be retried.`;if(attemptNumber>0){errMsg=`${errMsg} Retry attempt ${attemptNumber} of ${maxRetryAttempts}.`;}}else {errMsg=`${errMsg} Retries exhausted (${maxRetryAttempts}). ${dropMsg}`;}}else {errMsg=`${errMsg} ${dropMsg}`;}logger?.error(errMsg);};const getRequestInfo=(itemData,state,logger)=>{let data;let headers;let url;const currentTime=getCurrentTimeFormatted();if(Array.isArray(itemData)){const finalEvents=itemData.map(queueItemData=>getFinalEventForDeliveryMutator(queueItemData.event,currentTime));data=getBatchDeliveryPayload(finalEvents,currentTime,logger);headers=itemData[0]?clone(itemData[0].headers):{};url=getBatchDeliveryUrl(state.lifecycle.activeDataplaneUrl.value);}else {const{url:eventUrl,event,headers:eventHeaders}=itemData;const finalEvent=getFinalEventForDeliveryMutator(event,currentTime);data=getDeliveryPayload(finalEvent,logger);headers=clone(eventHeaders);url=eventUrl;}return {data,headers,url};};
2501
+ const getBatchDeliveryPayload=(events,currentTime,logger)=>{const batchPayload={batch:events,sentAt:currentTime};return stringifyWithoutCircular(batchPayload,true,undefined,logger);};const getNormalizedQueueOptions=queueOpts=>mergeDeepRight(DEFAULT_RETRY_QUEUE_OPTIONS,queueOpts);const getDeliveryUrl=(dataplaneUrl,endpoint)=>{const dpUrl=new URL(dataplaneUrl);return new URL(removeDuplicateSlashes([dpUrl.pathname,'/',DATA_PLANE_API_VERSION,'/',endpoint].join('')),dpUrl).href;};const getBatchDeliveryUrl=dataplaneUrl=>getDeliveryUrl(dataplaneUrl,'batch');const logErrorOnFailure=(details,isRetryable,qItemProcessInfo,logger)=>{let errMsg=EVENT_DELIVERY_FAILURE_ERROR_PREFIX(XHR_QUEUE_PLUGIN,details?.error?.message??'Unknown');const dropMsg=`The event(s) will be dropped.`;if(isRetryable){if(qItemProcessInfo.willBeRetried){errMsg=`${errMsg} The event(s) will be retried.`;if(qItemProcessInfo.retryAttemptNumber>0){errMsg=`${errMsg} Retry attempt ${qItemProcessInfo.retryAttemptNumber} of ${qItemProcessInfo.maxRetryAttempts}.`;}}else {errMsg=`${errMsg} Retries exhausted (${qItemProcessInfo.maxRetryAttempts}). ${dropMsg}`;}}else {errMsg=`${errMsg} ${dropMsg}`;}logger?.error(errMsg);};const getRequestInfo=(itemData,state,qItemProcessInfo,logger)=>{let data;let headers;let url;const currentTime=getCurrentTimeFormatted();if(Array.isArray(itemData)){const finalEvents=itemData.map(queueItemData=>getFinalEventForDeliveryMutator(queueItemData.event,currentTime));data=getBatchDeliveryPayload(finalEvents,currentTime,logger);headers=itemData[0]?clone(itemData[0].headers):{};url=getBatchDeliveryUrl(state.lifecycle.activeDataplaneUrl.value);}else {const{url:eventUrl,event,headers:eventHeaders}=itemData;const finalEvent=getFinalEventForDeliveryMutator(event,currentTime);data=getDeliveryPayload(finalEvent,logger);headers=clone(eventHeaders);url=eventUrl;}// Add current timestamp as sentAt header
2502
+ // The same value is added in the event payload as well
2503
+ headers.SentAt=currentTime;// Add a header to indicate if the item has been reclaimed from
2504
+ // local storage
2505
+ if(qItemProcessInfo.reclaimed){headers.Reclaimed='true';}// Add retry headers if the item is being retried for delivery
2506
+ if(qItemProcessInfo.retryAttemptNumber>0){// The number of times this item has been attempted to retry
2507
+ headers['Retry-Attempt']=qItemProcessInfo.retryAttemptNumber.toString();// The number of seconds since the last attempt
2508
+ headers['Retried-After']=qItemProcessInfo.timeSinceLastAttempt.toString();// The number of seconds since the first attempt
2509
+ headers['Retried-After-First']=qItemProcessInfo.timeSinceFirstAttempt.toString();}return {data,headers,url};};
2479
2510
 
2480
2511
  const pluginName='XhrQueue';const XhrQueue=()=>({name:pluginName,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName];},dataplaneEventsQueue:{/**
2481
2512
  * Initialize the queue for delivery
@@ -2486,9 +2517,9 @@
2486
2517
  * @param logger Logger instance
2487
2518
  * @returns RetryQueue instance
2488
2519
  */init(state,httpClient,storeManager,errorHandler,logger){const writeKey=state.lifecycle.writeKey.value;httpClient.setAuthHeader(writeKey);const finalQOpts=getNormalizedQueueOptions(state.loadOptions.value.queueOptions);const eventsQueue=new RetryQueue(// adding write key to the queue name to avoid conflicts
2489
- `${QUEUE_NAME}_${writeKey}` ,finalQOpts,(itemData,done,attemptNumber,maxRetryAttempts,willBeRetried)=>{const{data,url,headers}=getRequestInfo(itemData,state,logger);httpClient.getAsyncData({url,options:{method:'POST',headers,data:data,sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS,callback:(result,details)=>{// If there is no error, we can consider the item as delivered
2520
+ `${QUEUE_NAME}_${writeKey}` ,finalQOpts,(itemData,done,qItemProcessInfo)=>{const{data,url,headers}=getRequestInfo(itemData,state,qItemProcessInfo,logger);httpClient.getAsyncData({url,options:{method:'POST',headers,data:data,sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS,callback:(result,details)=>{// If there is no error, we can consider the item as delivered
2490
2521
  if(isUndefined(details?.error)){// null means item will not be processed further and will be removed from the queue (even from the storage)
2491
- done(null);return;}const isRetryable=isErrRetryable(details?.xhr?.status??0);logErrorOnFailure(details,isRetryable,willBeRetried,attemptNumber,maxRetryAttempts,logger);// null means item will not be processed further and will be removed from the queue (even from the storage)
2522
+ done(null);return;}const isRetryable=isErrRetryable(details?.xhr?.status??0);logErrorOnFailure(details,isRetryable,qItemProcessInfo,logger);// null means item will not be processed further and will be removed from the queue (even from the storage)
2492
2523
  const queueErrResp=isRetryable?details:null;done(queueErrResp);}});},storeManager,LOCAL_STORAGE,logger,itemData=>{const currentTime=getCurrentTimeFormatted();const events=itemData.map(queueItemData=>queueItemData.event);// type casting to string as we know that the event has already been validated prior to enqueue
2493
2524
  return getBatchDeliveryPayload(events,currentTime,logger)?.length;});return eventsQueue;},/**
2494
2525
  * Add event to the queue for delivery
@@ -2557,38 +2588,6 @@
2557
2588
 
2558
2589
  const storageClientDataStoreNameMap={[COOKIE_STORAGE]:CLIENT_DATA_STORE_COOKIE,[LOCAL_STORAGE]:CLIENT_DATA_STORE_LS,[MEMORY_STORAGE]:CLIENT_DATA_STORE_MEMORY,[SESSION_STORAGE]:CLIENT_DATA_STORE_SESSION};
2559
2590
 
2560
- const detectAdBlockers=httpClient=>{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
2561
- // Use source config URL here as it is very unlikely to be blocked by adblockers
2562
- // Only the extra query param should make it vulnerable to adblockers
2563
- // This will work even if the users proxies it.
2564
- // The edge case where this doesn't work is when HEAD method is not allowed by the server (user's)
2565
- const baseUrl=new URL(state.lifecycle.sourceConfigUrl.value);const url=`${baseUrl.origin}${baseUrl.pathname}?view=ad`;httpClient.getAsyncData({url,options:{// We actually don't need the response from the request, so we are using HEAD
2566
- method:'HEAD',headers:{'Content-Type':undefined}},isRawResponse:true,callback:(result,details)=>{// not ad blocked if the request is successful or it is not internally redirected on the client side
2567
- // Often adblockers instead of blocking the request, they redirect it to an internal URL
2568
- state.capabilities.isAdBlocked.value=details?.error!==undefined||details?.xhr?.responseURL!==url;}});};
2569
-
2570
- const hasCrypto=()=>!isNullOrUndefined(globalThis.crypto)&&isFunction(globalThis.crypto.getRandomValues);// eslint-disable-next-line compat/compat -- We are checking for the existence of navigator.userAgentData
2571
- const hasUAClientHints=()=>!isNullOrUndefined(globalThis.navigator.userAgentData);const hasBeacon=()=>!isNullOrUndefined(globalThis.navigator.sendBeacon)&&isFunction(globalThis.navigator.sendBeacon);const isIE11=()=>Boolean(globalThis.navigator.userAgent.match(/Trident.*rv:11\./));
2572
-
2573
- const getUserAgentClientHint=(callback,level='none')=>{if(level==='none'){callback(undefined);}if(level==='default'){callback(navigator.userAgentData);}if(level==='full'){navigator.userAgentData?.getHighEntropyValues(['architecture','bitness','brands','mobile','model','platform','platformVersion','uaFullVersion','fullVersionList','wow64']).then(ua=>{callback(ua);}).catch(()=>{callback();});}};
2574
-
2575
- const isDatasetAvailable=()=>{const testElement=globalThis.document.createElement('div');testElement.setAttribute('data-a-b','c');return testElement.dataset?testElement.dataset.aB==='c':false;};const legacyJSEngineRequiredPolyfills={// Ideally, we should separate the checks for URL and URLSearchParams but
2576
- // the polyfill service serves them under the same feature name, "URL".
2577
- URL:()=>!isFunction(globalThis.URL)||!isFunction(globalThis.URLSearchParams),Promise:()=>!isFunction(globalThis.Promise),'Number.isNaN':()=>!isFunction(globalThis.Number.isNaN),'Number.isInteger':()=>!isFunction(globalThis.Number.isInteger),'Array.from':()=>!isFunction(globalThis.Array.from),'Array.prototype.find':()=>!isFunction(globalThis.Array.prototype.find),'Array.prototype.includes':()=>!isFunction(globalThis.Array.prototype.includes),'String.prototype.endsWith':()=>!isFunction(globalThis.String.prototype.endsWith),'String.prototype.startsWith':()=>!isFunction(globalThis.String.prototype.startsWith),'String.prototype.includes':()=>!isFunction(globalThis.String.prototype.includes),'String.prototype.replaceAll':()=>!isFunction(globalThis.String.prototype.replaceAll),'String.fromCodePoint':()=>!isFunction(globalThis.String.fromCodePoint),'Object.entries':()=>!isFunction(globalThis.Object.entries),'Object.values':()=>!isFunction(globalThis.Object.values),'Object.assign':()=>!isFunction(globalThis.Object.assign),'Object.fromEntries':()=>!isFunction(globalThis.Object.fromEntries),'Element.prototype.dataset':()=>!isDatasetAvailable(),// Ideally, we should separate the checks for TextEncoder and TextDecoder but
2578
- // the polyfill service serves them under the same feature name, "TextEncoder".
2579
- TextEncoder:()=>!isFunction(globalThis.TextEncoder)||!isFunction(globalThis.TextDecoder),requestAnimationFrame:()=>!isFunction(globalThis.requestAnimationFrame)||!isFunction(globalThis.cancelAnimationFrame),CustomEvent:()=>!isFunction(globalThis.CustomEvent),'navigator.sendBeacon':()=>!isFunction(globalThis.navigator.sendBeacon),// Note, the polyfill service serves both ArrayBuffer and Uint8Array under the same feature name, "ArrayBuffer".
2580
- ArrayBuffer:()=>!isFunction(globalThis.Uint8Array),Set:()=>!isFunction(globalThis.Set),atob:()=>!isFunction(globalThis.atob)};const isLegacyJSEngine=()=>{const requiredCapabilitiesList=Object.keys(legacyJSEngineRequiredPolyfills);let needsPolyfill=false;/* eslint-disable-next-line unicorn/no-for-loop */for(let i=0;i<requiredCapabilitiesList.length;i++){const isCapabilityMissing=legacyJSEngineRequiredPolyfills[requiredCapabilitiesList[i]];if(isFunction(isCapabilityMissing)&&isCapabilityMissing()){needsPolyfill=true;break;}}return needsPolyfill;};
2581
-
2582
- const getScreenDetails=()=>{let screenDetails={density:0,width:0,height:0,innerWidth:0,innerHeight:0};screenDetails={width:globalThis.screen.width,height:globalThis.screen.height,density:globalThis.devicePixelRatio,innerWidth:globalThis.innerWidth,innerHeight:globalThis.innerHeight};return screenDetails;};
2583
-
2584
- const isStorageQuotaExceeded=e=>{const matchingNames=['QuotaExceededError','NS_ERROR_DOM_QUOTA_REACHED'];// [everything except Firefox, Firefox]
2585
- const matchingCodes=[22,1014];// [everything except Firefox, Firefox]
2586
- const isQuotaExceededError=matchingNames.includes(e.name)||matchingCodes.includes(e.code);return e instanceof DOMException&&isQuotaExceededError;};// TODO: also check for SecurityErrors
2587
- // https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage#exceptions
2588
- const isStorageAvailable=(type=LOCAL_STORAGE,storageInstance,logger)=>{let storage;let testData;const msgPrefix=STORAGE_UNAVAILABILITY_ERROR_PREFIX(CAPABILITIES_MANAGER,type);let reason='unavailable';let isAccessible=true;let errObj;try{switch(type){case MEMORY_STORAGE:return true;case COOKIE_STORAGE:storage=storageInstance;testData=STORAGE_TEST_COOKIE;break;case LOCAL_STORAGE:storage=storageInstance??globalThis.localStorage;testData=STORAGE_TEST_LOCAL_STORAGE;// was STORAGE_TEST_LOCAL_STORAGE in ours and generateUUID() in segment retry one
2589
- break;case SESSION_STORAGE:storage=storageInstance??globalThis.sessionStorage;testData=STORAGE_TEST_SESSION_STORAGE;break;default:return false;}if(storage){storage.setItem(testData,'true');if(storage.getItem(testData)){storage.removeItem(testData);return true;}}isAccessible=false;}catch(err){isAccessible=false;errObj=err;if(isStorageQuotaExceeded(err)){reason='full';}}if(!isAccessible){logger?.warn(`${msgPrefix}${reason}.`,errObj);}// if we've have reached here, it means the storage is not available
2590
- return false;};
2591
-
2592
2591
  const legacyGetHostname=href=>{const l=document.createElement('a');l.href=href;return l.hostname;};/**
2593
2592
  * Levels returns all levels of the given url
2594
2593
  *
@@ -2614,21 +2613,9 @@
2614
2613
 
2615
2614
  const getDefaultCookieOptions=()=>{const topDomain=`.${domain(globalThis.location.href)}`;return {maxage:DEFAULT_COOKIE_MAX_AGE_MS,path:'/',domain:!topDomain||topDomain==='.'?undefined:topDomain,samesite:'Lax',enabled:true};};const getDefaultLocalStorageOptions=()=>({enabled:true});const getDefaultSessionStorageOptions=()=>({enabled:true});const getDefaultInMemoryStorageOptions=()=>({enabled:true});
2616
2615
 
2617
- /**
2618
- * A storage utility to persist values in cookies via Storage interface
2619
- */class CookieStorage{static globalSingleton=null;isSupportAvailable=true;isEnabled=true;length=0;constructor(options={},logger){if(CookieStorage.globalSingleton){// eslint-disable-next-line no-constructor-return
2620
- return CookieStorage.globalSingleton;}this.options=getDefaultCookieOptions();this.logger=logger;this.configure(options);CookieStorage.globalSingleton=this;}configure(options){this.options=mergeDeepRight(this.options??{},options);if(options.sameDomainCookiesOnly){delete this.options.domain;}this.isSupportAvailable=isStorageAvailable(COOKIE_STORAGE,this);this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){cookie(key,value,this.options,this.logger);this.length=Object.keys(cookie()).length;return true;}// eslint-disable-next-line class-methods-use-this
2621
- getItem(key){const value=cookie(key);return isUndefined(value)?null:value;}removeItem(key){const result=this.setItem(key,null);this.length=Object.keys(cookie()).length;return result;}// eslint-disable-next-line class-methods-use-this
2622
- clear(){// Not implemented
2623
- // getting a list of all cookie storage keys and remove all values
2624
- // sounds risky to do as it will take on all top domain cookies
2625
- // better to explicitly clear specific ones if needed
2626
- }key(index){const curKeys=this.keys();return curKeys[index]??null;}// eslint-disable-next-line class-methods-use-this
2627
- keys(){return Object.keys(cookie());}}
2628
-
2629
2616
  /**
2630
2617
  * A storage utility to retain values in memory via Storage interface
2631
- */class InMemoryStorage{isEnabled=true;length=0;data={};constructor(options,logger){this.options=getDefaultInMemoryStorageOptions();this.logger=logger;this.configure(options??{});}configure(options){this.options=mergeDeepRight(this.options,options);this.isEnabled=Boolean(this.options.enabled);return this.options;}setItem(key,value){this.data[key]=value;this.length=Object.keys(this.data).length;return value;}getItem(key){if(key in this.data){return this.data[key];}return null;}removeItem(key){if(key in this.data){delete this.data[key];}this.length=Object.keys(this.data).length;return null;}clear(){this.data={};this.length=0;}key(index){const curKeys=this.keys();return curKeys[index]??null;}keys(){return Object.keys(this.data);}}const defaultInMemoryStorage=new InMemoryStorage({},defaultLogger);
2618
+ */class InMemoryStorage{isEnabled=true;length=0;data={};constructor(logger){this.options=getDefaultInMemoryStorageOptions();this.logger=logger;}configure(options){this.options=mergeDeepRight(this.options,options??{});this.isEnabled=Boolean(this.options.enabled);return this.options;}setItem(key,value){this.data[key]=value;this.length=Object.keys(this.data).length;return value;}getItem(key){if(key in this.data){return this.data[key];}return null;}removeItem(key){if(key in this.data){delete this.data[key];}this.length=Object.keys(this.data).length;return null;}clear(){this.data={};this.length=0;}key(index){const curKeys=this.keys();return curKeys[index]??null;}keys(){return Object.keys(this.data);}}const defaultInMemoryStorage=new InMemoryStorage(defaultLogger);
2632
2619
 
2633
2620
  var store$2 = {exports: {}};
2634
2621
 
@@ -2642,25 +2629,69 @@
2642
2629
  var storeExports = requireStore();
2643
2630
  const store = /*@__PURE__*/getDefaultExportFromCjs(storeExports);
2644
2631
 
2632
+ const detectAdBlockers=httpClient=>{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
2633
+ // Use source config URL here as it is very unlikely to be blocked by adblockers
2634
+ // Only the extra query param should make it vulnerable to adblockers
2635
+ // This will work even if the users proxies it.
2636
+ // The edge case where this doesn't work is when HEAD method is not allowed by the server (user's)
2637
+ const baseUrl=new URL(state.lifecycle.sourceConfigUrl.value);const url=`${baseUrl.origin}${baseUrl.pathname}?view=ad`;httpClient.getAsyncData({url,options:{// We actually don't need the response from the request, so we are using HEAD
2638
+ method:'HEAD',headers:{'Content-Type':undefined}},isRawResponse:true,callback:(result,details)=>{// not ad blocked if the request is successful or it is not internally redirected on the client side
2639
+ // Often adblockers instead of blocking the request, they redirect it to an internal URL
2640
+ state.capabilities.isAdBlocked.value=details?.error!==undefined||details?.xhr?.responseURL!==url;}});};
2641
+
2642
+ const hasCrypto=()=>!isNullOrUndefined(globalThis.crypto)&&isFunction(globalThis.crypto.getRandomValues);// eslint-disable-next-line compat/compat -- We are checking for the existence of navigator.userAgentData
2643
+ const hasUAClientHints=()=>!isNullOrUndefined(globalThis.navigator.userAgentData);const hasBeacon=()=>!isNullOrUndefined(globalThis.navigator.sendBeacon)&&isFunction(globalThis.navigator.sendBeacon);const isIE11=()=>Boolean(globalThis.navigator.userAgent.match(/Trident.*rv:11\./));
2644
+
2645
+ const getUserAgentClientHint=(callback,level='none')=>{if(level==='none'){callback(undefined);}if(level==='default'){callback(navigator.userAgentData);}if(level==='full'){navigator.userAgentData?.getHighEntropyValues(['architecture','bitness','brands','mobile','model','platform','platformVersion','uaFullVersion','fullVersionList','wow64']).then(ua=>{callback(ua);}).catch(()=>{callback();});}};
2646
+
2647
+ const isDatasetAvailable=()=>{const testElement=globalThis.document.createElement('div');testElement.setAttribute('data-a-b','c');return testElement.dataset?testElement.dataset.aB==='c':false;};const legacyJSEngineRequiredPolyfills={// Ideally, we should separate the checks for URL and URLSearchParams but
2648
+ // the polyfill service serves them under the same feature name, "URL".
2649
+ URL:()=>!isFunction(globalThis.URL)||!isFunction(globalThis.URLSearchParams),Promise:()=>!isFunction(globalThis.Promise),'Number.isNaN':()=>!isFunction(globalThis.Number.isNaN),'Number.isInteger':()=>!isFunction(globalThis.Number.isInteger),'Array.from':()=>!isFunction(globalThis.Array.from),'Array.prototype.find':()=>!isFunction(globalThis.Array.prototype.find),'Array.prototype.includes':()=>!isFunction(globalThis.Array.prototype.includes),'String.prototype.endsWith':()=>!isFunction(globalThis.String.prototype.endsWith),'String.prototype.startsWith':()=>!isFunction(globalThis.String.prototype.startsWith),'String.prototype.includes':()=>!isFunction(globalThis.String.prototype.includes),'String.prototype.replaceAll':()=>!isFunction(globalThis.String.prototype.replaceAll),'String.fromCodePoint':()=>!isFunction(globalThis.String.fromCodePoint),'Object.entries':()=>!isFunction(globalThis.Object.entries),'Object.values':()=>!isFunction(globalThis.Object.values),'Object.assign':()=>!isFunction(globalThis.Object.assign),'Object.fromEntries':()=>!isFunction(globalThis.Object.fromEntries),'Element.prototype.dataset':()=>!isDatasetAvailable(),// Ideally, we should separate the checks for TextEncoder and TextDecoder but
2650
+ // the polyfill service serves them under the same feature name, "TextEncoder".
2651
+ TextEncoder:()=>!isFunction(globalThis.TextEncoder)||!isFunction(globalThis.TextDecoder),requestAnimationFrame:()=>!isFunction(globalThis.requestAnimationFrame)||!isFunction(globalThis.cancelAnimationFrame),CustomEvent:()=>!isFunction(globalThis.CustomEvent),'navigator.sendBeacon':()=>!isFunction(globalThis.navigator.sendBeacon),// Note, the polyfill service serves both ArrayBuffer and Uint8Array under the same feature name, "ArrayBuffer".
2652
+ ArrayBuffer:()=>!isFunction(globalThis.Uint8Array),Set:()=>!isFunction(globalThis.Set),atob:()=>!isFunction(globalThis.atob)};const isLegacyJSEngine=()=>{const requiredCapabilitiesList=Object.keys(legacyJSEngineRequiredPolyfills);let needsPolyfill=false;/* eslint-disable-next-line unicorn/no-for-loop */for(let i=0;i<requiredCapabilitiesList.length;i++){const isCapabilityMissing=legacyJSEngineRequiredPolyfills[requiredCapabilitiesList[i]];if(isFunction(isCapabilityMissing)&&isCapabilityMissing()){needsPolyfill=true;break;}}return needsPolyfill;};
2653
+
2654
+ const getScreenDetails=()=>{let screenDetails={density:0,width:0,height:0,innerWidth:0,innerHeight:0};screenDetails={width:globalThis.screen.width,height:globalThis.screen.height,density:globalThis.devicePixelRatio,innerWidth:globalThis.innerWidth,innerHeight:globalThis.innerHeight};return screenDetails;};
2655
+
2656
+ const isStorageQuotaExceeded=e=>{const matchingNames=['QuotaExceededError','NS_ERROR_DOM_QUOTA_REACHED'];// [everything except Firefox, Firefox]
2657
+ const matchingCodes=[22,1014];// [everything except Firefox, Firefox]
2658
+ const isQuotaExceededError=matchingNames.includes(e.name)||matchingCodes.includes(e.code);return e instanceof DOMException&&isQuotaExceededError;};// TODO: also check for SecurityErrors
2659
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage#exceptions
2660
+ const isStorageAvailable=(type=LOCAL_STORAGE,storageInstance,logger)=>{let storage;let testData;const msgPrefix=STORAGE_UNAVAILABILITY_ERROR_PREFIX(CAPABILITIES_MANAGER,type);let reason='unavailable';let isAccessible=true;let errObj;try{switch(type){case MEMORY_STORAGE:return true;case COOKIE_STORAGE:storage=storageInstance;testData=STORAGE_TEST_COOKIE;break;case LOCAL_STORAGE:storage=storageInstance??globalThis.localStorage;testData=STORAGE_TEST_LOCAL_STORAGE;// was STORAGE_TEST_LOCAL_STORAGE in ours and generateUUID() in segment retry one
2661
+ break;case SESSION_STORAGE:storage=storageInstance??globalThis.sessionStorage;testData=STORAGE_TEST_SESSION_STORAGE;break;default:return false;}if(storage){storage.setItem(testData,'true');if(storage.getItem(testData)){storage.removeItem(testData);return true;}}isAccessible=false;}catch(err){isAccessible=false;errObj=err;if(isStorageQuotaExceeded(err)){reason='full';}}if(!isAccessible){logger?.warn(`${msgPrefix}${reason}.`,errObj);}// if we've have reached here, it means the storage is not available
2662
+ return false;};
2663
+
2645
2664
  // check if the get, set overloads and search methods are used at all
2646
2665
  // if we do, ensure we provide types to support overloads as per storejs docs
2647
2666
  // https://www.npmjs.com/package/storejs
2648
2667
  /**
2649
2668
  * A storage utility to persist values in localstorage via Storage interface
2650
- */class LocalStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(options={},logger){this.options=getDefaultLocalStorageOptions();this.logger=logger;this.configure(options);}configure(options){this.options=mergeDeepRight(this.options,options);this.isSupportAvailable=isStorageAvailable(LOCAL_STORAGE);this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){store.set(key,value);this.length=store.len();}// eslint-disable-next-line class-methods-use-this
2669
+ */class LocalStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(logger){this.options=getDefaultLocalStorageOptions();this.logger=logger;}configure(options){this.options=mergeDeepRight(this.options,options??{});this.isSupportAvailable=isStorageAvailable(LOCAL_STORAGE);this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){store.set(key,value);this.length=store.len();}// eslint-disable-next-line class-methods-use-this
2651
2670
  getItem(key){const value=store.get(key);return isUndefined(value)?null:value;}removeItem(key){store.remove(key);this.length=store.len();}clear(){store.clear();this.length=0;}key(index){const curKeys=this.keys();return curKeys[index]??null;}// eslint-disable-next-line class-methods-use-this
2652
- keys(){return store.keys();}}const defaultLocalStorage=new LocalStorage({},defaultLogger);
2671
+ keys(){return store.keys();}}const defaultLocalStorage=new LocalStorage(defaultLogger);
2653
2672
 
2654
2673
  /**
2655
2674
  * A storage utility to persist values in SessionStorage via Storage interface
2656
- */class SessionStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(options={},logger){this.options=getDefaultSessionStorageOptions();this.logger=logger;this.configure(options);}configure(options){this.options=mergeDeepRight(this.options,options);this.isSupportAvailable=isStorageAvailable(SESSION_STORAGE);// when storage is blocked by the user, even accessing the property throws an error
2657
- if(this.isSupportAvailable){this.store=globalThis.sessionStorage;}this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){if(!this.store){return;}this.store.setItem(key,value);this.length=this.store.length;}getItem(key){if(!this.store){return null;}const value=this.store.getItem(key);return isUndefined(value)?null:value;}removeItem(key){if(!this.store){return;}this.store.removeItem(key);this.length=this.store.length;}clear(){this.store?.clear();this.length=0;}key(index){return this.store?.key(index)??null;}keys(){const keys=[];if(!this.store){return keys;}for(let i=0;i<this.store.length;i+=1){const key=this.store.key(i);if(key!==null){keys.push(key);}}return keys;}}const defaultSessionStorage=new SessionStorage({},defaultLogger);
2675
+ */class SessionStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(logger){this.options=getDefaultSessionStorageOptions();this.logger=logger;}configure(options){this.options=mergeDeepRight(this.options,options??{});this.isSupportAvailable=isStorageAvailable(SESSION_STORAGE);// when storage is blocked by the user, even accessing the property throws an error
2676
+ if(this.isSupportAvailable){this.store=globalThis.sessionStorage;}this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){if(!this.store){return;}this.store.setItem(key,value);this.length=this.store.length;}getItem(key){if(!this.store){return null;}const value=this.store.getItem(key);return isUndefined(value)?null:value;}removeItem(key){if(!this.store){return;}this.store.removeItem(key);this.length=this.store.length;}clear(){this.store?.clear();this.length=0;}key(index){return this.store?.key(index)??null;}keys(){const keys=[];if(!this.store){return keys;}for(let i=0;i<this.store.length;i+=1){const key=this.store.key(i);if(key!==null){keys.push(key);}}return keys;}}const defaultSessionStorage=new SessionStorage(defaultLogger);
2677
+
2678
+ /**
2679
+ * A storage utility to persist values in cookies via Storage interface
2680
+ */class CookieStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(logger){this.options=getDefaultCookieOptions();this.logger=logger;}configure(options){this.options=mergeDeepRight(this.options,options??{});if(this.options.sameDomainCookiesOnly){delete this.options.domain;}this.isSupportAvailable=isStorageAvailable(COOKIE_STORAGE,this);this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){cookie(key,value,this.options,this.logger);this.length=Object.keys(cookie()).length;return true;}// eslint-disable-next-line class-methods-use-this
2681
+ getItem(key){const value=cookie(key);return isUndefined(value)?null:value;}removeItem(key){const result=this.setItem(key,null);this.length=Object.keys(cookie()).length;return result;}// eslint-disable-next-line class-methods-use-this
2682
+ clear(){// Not implemented
2683
+ // getting a list of all cookie storage keys and remove all values
2684
+ // sounds risky to do as it will take on all top domain cookies
2685
+ // better to explicitly clear specific ones if needed
2686
+ }key(index){const curKeys=this.keys();return curKeys[index]??null;}// eslint-disable-next-line class-methods-use-this
2687
+ keys(){return Object.keys(cookie());}}const defaultCookieStorage=new CookieStorage(defaultLogger);
2658
2688
 
2659
2689
  /**
2660
2690
  * A utility to retrieve the storage singleton instance by type
2661
- */const getStorageEngine=type=>{switch(type){case LOCAL_STORAGE:return defaultLocalStorage;case SESSION_STORAGE:return defaultSessionStorage;case MEMORY_STORAGE:return defaultInMemoryStorage;case COOKIE_STORAGE:return new CookieStorage({},defaultLogger);default:return defaultInMemoryStorage;}};/**
2691
+ */const getStorageEngine=type=>{switch(type){case LOCAL_STORAGE:return defaultLocalStorage;case SESSION_STORAGE:return defaultSessionStorage;case MEMORY_STORAGE:return defaultInMemoryStorage;case COOKIE_STORAGE:return defaultCookieStorage;default:return defaultInMemoryStorage;}};/**
2662
2692
  * Configure cookie storage singleton
2663
- */const configureCookieStorageEngine=options=>{const cookieStorageOptions=new CookieStorage({},defaultLogger).configure(options);state.storage.cookie.value={maxage:cookieStorageOptions.maxage,path:cookieStorageOptions.path,domain:cookieStorageOptions.domain,samesite:cookieStorageOptions.samesite,expires:cookieStorageOptions.expires,secure:cookieStorageOptions.secure};};/**
2693
+ */const configureCookieStorageEngine=options=>{const cookieStorageOptions=defaultCookieStorage.configure(options);// Update the state with the final cookie storage options
2694
+ state.storage.cookie.value={maxage:cookieStorageOptions.maxage,path:cookieStorageOptions.path,domain:cookieStorageOptions.domain,samesite:cookieStorageOptions.samesite,expires:cookieStorageOptions.expires,secure:cookieStorageOptions.secure};};/**
2664
2695
  * Configure local storage singleton
2665
2696
  */const configureLocalStorageEngine=options=>{defaultLocalStorage.configure(options);};/**
2666
2697
  * Configure in memory storage singleton
@@ -2672,7 +2703,7 @@
2672
2703
 
2673
2704
  /**
2674
2705
  * Store Implementation with dedicated storage
2675
- */class Store{constructor(config,engine,pluginsManager){this.id=config.id;this.name=config.name;this.isEncrypted=config.isEncrypted??false;this.validKeys=config.validKeys??{};this.engine=engine??getStorageEngine(LOCAL_STORAGE);this.noKeyValidation=Object.keys(this.validKeys).length===0;this.noCompoundKey=config.noCompoundKey;this.originalEngine=this.engine;this.errorHandler=config.errorHandler;this.logger=config.logger;this.pluginsManager=pluginsManager;}/**
2706
+ */class Store{constructor(config,engine,pluginsManager){this.id=config.id;this.name=config.name;this.isEncrypted=config.isEncrypted??false;this.validKeys=config.validKeys??{};this.engine=engine;this.noKeyValidation=Object.keys(this.validKeys).length===0;this.noCompoundKey=config.noCompoundKey;this.originalEngine=this.engine;this.errorHandler=config.errorHandler;this.logger=config.logger;this.pluginsManager=pluginsManager;}/**
2676
2707
  * Ensure the key is valid and with correct format
2677
2708
  */createValidKey(key){const{name,id,validKeys,noKeyValidation,noCompoundKey}=this;if(noKeyValidation){return noCompoundKey?key:[name,id,key].join('.');}// validate and return undefined if invalid key
2678
2709
  let compoundKey;Object.values(validKeys).forEach(validKeyName=>{if(validKeyName===key){compoundKey=noCompoundKey?key:[name,id,key].join('.');}});return compoundKey;}/**
@@ -2711,7 +2742,7 @@
2711
2742
  * A service to manage stores & available storage client configurations
2712
2743
  */class StoreManager{stores={};isInitialized=false;constructor(pluginsManager,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.pluginsManager=pluginsManager;}/**
2713
2744
  * Configure available storage client instances
2714
- */init(){if(this.isInitialized){return;}const loadOptions=state.loadOptions.value;const config={cookieStorageOptions:{samesite:loadOptions.sameSiteCookie,secure:loadOptions.secureCookie,domain:loadOptions.setCookieDomain,sameDomainCookiesOnly:loadOptions.sameDomainCookiesOnly,enabled:true},localStorageOptions:{enabled:true},inMemoryStorageOptions:{enabled:true},sessionStorageOptions:{enabled:true}};configureStorageEngines(removeUndefinedValues(mergeDeepRight(config.cookieStorageOptions,state.storage.cookie?.value??{})),removeUndefinedValues(config.localStorageOptions),removeUndefinedValues(config.inMemoryStorageOptions),removeUndefinedValues(config.sessionStorageOptions));this.initClientDataStores();this.isInitialized=true;}/**
2745
+ */init(){if(this.isInitialized){return;}const loadOptions=state.loadOptions.value;const config={cookieStorageOptions:{samesite:loadOptions.sameSiteCookie,secure:loadOptions.secureCookie,domain:loadOptions.setCookieDomain,sameDomainCookiesOnly:loadOptions.sameDomainCookiesOnly},localStorageOptions:{},inMemoryStorageOptions:{},sessionStorageOptions:{}};configureStorageEngines(removeUndefinedValues(mergeDeepRight(config.cookieStorageOptions??{},state.storage.cookie?.value??{})),removeUndefinedValues(config.localStorageOptions),removeUndefinedValues(config.inMemoryStorageOptions),removeUndefinedValues(config.sessionStorageOptions));this.initClientDataStores();this.isInitialized=true;}/**
2715
2746
  * Create store to persist data used by the SDK like session, used details etc
2716
2747
  */initClientDataStores(){this.initializeStorageState();// TODO: fill in extra config values and bring them in from StoreManagerOptions if needed
2717
2748
  // TODO: should we pass the keys for all in order to validate or leave free as v1.1?
@@ -3381,10 +3412,12 @@
3381
3412
  constructor(){try{if(RudderAnalytics.globalSingleton){// START-NO-SONAR-SCAN
3382
3413
  // eslint-disable-next-line no-constructor-return
3383
3414
  return RudderAnalytics.globalSingleton;// END-NO-SONAR-SCAN
3384
- }this.setDefaultInstanceKey=this.setDefaultInstanceKey.bind(this);this.getAnalyticsInstance=this.getAnalyticsInstance.bind(this);this.load=this.load.bind(this);this.ready=this.ready.bind(this);this.triggerBufferedLoadEvent=this.triggerBufferedLoadEvent.bind(this);this.page=this.page.bind(this);this.track=this.track.bind(this);this.identify=this.identify.bind(this);this.alias=this.alias.bind(this);this.group=this.group.bind(this);this.reset=this.reset.bind(this);this.getAnonymousId=this.getAnonymousId.bind(this);this.setAnonymousId=this.setAnonymousId.bind(this);this.getUserId=this.getUserId.bind(this);this.getUserTraits=this.getUserTraits.bind(this);this.getGroupId=this.getGroupId.bind(this);this.getGroupTraits=this.getGroupTraits.bind(this);this.startSession=this.startSession.bind(this);this.endSession=this.endSession.bind(this);this.getSessionId=this.getSessionId.bind(this);this.setAuthToken=this.setAuthToken.bind(this);this.consent=this.consent.bind(this);RudderAnalytics.globalSingleton=this;state.autoTrack.pageLifecycle.visitId.value=generateUUID();state.autoTrack.pageLifecycle.pageLoadedTimestamp.value=Date.now();// start loading if a load event was buffered or wait for explicit load call
3415
+ }RudderAnalytics.initializeGlobalResources();this.setDefaultInstanceKey=this.setDefaultInstanceKey.bind(this);this.getAnalyticsInstance=this.getAnalyticsInstance.bind(this);this.load=this.load.bind(this);this.ready=this.ready.bind(this);this.triggerBufferedLoadEvent=this.triggerBufferedLoadEvent.bind(this);this.page=this.page.bind(this);this.track=this.track.bind(this);this.identify=this.identify.bind(this);this.alias=this.alias.bind(this);this.group=this.group.bind(this);this.reset=this.reset.bind(this);this.getAnonymousId=this.getAnonymousId.bind(this);this.setAnonymousId=this.setAnonymousId.bind(this);this.getUserId=this.getUserId.bind(this);this.getUserTraits=this.getUserTraits.bind(this);this.getGroupId=this.getGroupId.bind(this);this.getGroupTraits=this.getGroupTraits.bind(this);this.startSession=this.startSession.bind(this);this.endSession=this.endSession.bind(this);this.getSessionId=this.getSessionId.bind(this);this.setAuthToken=this.setAuthToken.bind(this);this.consent=this.consent.bind(this);RudderAnalytics.globalSingleton=this;state.autoTrack.pageLifecycle.visitId.value=generateUUID();state.autoTrack.pageLifecycle.pageLoadedTimestamp.value=Date.now();// start loading if a load event was buffered or wait for explicit load call
3385
3416
  this.triggerBufferedLoadEvent();// Assign to global "rudderanalytics" object after processing the preload buffer (if any exists)
3386
3417
  // for CDN bundling IIFE exports covers this but for npm ESM and CJS bundling has to be done explicitly
3387
- globalThis.rudderanalytics=this;}catch(error){dispatchErrorEvent(error);}}/**
3418
+ globalThis.rudderanalytics=this;}catch(error){dispatchErrorEvent(error);}}static initializeGlobalResources(){// We need to initialize the error handler first to catch any unhandled errors occurring in this module as well
3419
+ defaultErrorHandler.init();// Initialize the storage engines with default options
3420
+ defaultCookieStorage.configure();defaultLocalStorage.configure();defaultSessionStorage.configure();defaultInMemoryStorage.configure();}/**
3388
3421
  * Set instance to use if no specific writeKey is provided in methods
3389
3422
  * automatically for the first created instance
3390
3423
  * TODO: to support multiple analytics instances in the near future
@@ -3404,7 +3437,7 @@
3404
3437
  // as the constructor must have already pushed the events to the global buffer
3405
3438
  const preloadedEventsArray=getExposedGlobal(GLOBAL_PRELOAD_BUFFER);// Track page loaded lifecycle event if enabled
3406
3439
  this.trackPageLifecycleEvents(preloadedEventsArray,loadOptions);// The array will be mutated in the below method
3407
- promotePreloadedConsentEventsToTop(preloadedEventsArray);setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone(preloadedEventsArray));this.analyticsInstances[writeKey]=new Analytics();this.getAnalyticsInstance(writeKey)?.load(writeKey,dataPlaneUrl,getSanitizedValue(loadOptions));}catch(error){dispatchErrorEvent(error);}}/**
3440
+ promotePreloadedConsentEventsToTop(preloadedEventsArray);setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone(preloadedEventsArray));this.getAnalyticsInstance(writeKey)?.load(writeKey,dataPlaneUrl,getSanitizedValue(loadOptions));}catch(error){dispatchErrorEvent(error);}}/**
3408
3441
  * A function to track page lifecycle events like page loaded and page unloaded
3409
3442
  * @param preloadedEventsArray
3410
3443
  * @param loadOptions