@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.
- package/CHANGELOG.md +18 -0
- package/dist/npm/index.d.cts +3 -1
- package/dist/npm/index.d.mts +3 -1
- package/dist/npm/legacy/bundled/cjs/index.cjs +270 -237
- package/dist/npm/legacy/bundled/esm/index.mjs +270 -237
- package/dist/npm/legacy/bundled/umd/index.js +270 -237
- package/dist/npm/legacy/cjs/index.cjs +270 -237
- package/dist/npm/legacy/content-script/cjs/index.cjs +270 -237
- package/dist/npm/legacy/content-script/esm/index.mjs +270 -237
- package/dist/npm/legacy/content-script/umd/index.js +270 -237
- package/dist/npm/legacy/esm/index.mjs +270 -237
- package/dist/npm/legacy/umd/index.js +270 -237
- package/dist/npm/modern/bundled/cjs/index.cjs +124 -91
- package/dist/npm/modern/bundled/esm/index.mjs +124 -91
- package/dist/npm/modern/bundled/umd/index.js +124 -91
- package/dist/npm/modern/cjs/index.cjs +77 -84
- package/dist/npm/modern/content-script/cjs/index.cjs +124 -91
- package/dist/npm/modern/content-script/esm/index.mjs +124 -91
- package/dist/npm/modern/content-script/umd/index.js +124 -91
- package/dist/npm/modern/esm/index.mjs +77 -84
- package/dist/npm/modern/umd/index.js +77 -84
- package/package.json +1 -1
@@ -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.
|
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(
|
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{
|
780
|
-
constructor(httpClient,logger){this.httpClient=httpClient;this.logger=logger;
|
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));}}}
|
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}
|
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
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
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
|
946
|
-
if(
|
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
|
-
|
961
|
-
|
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,
|
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
|
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
|
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();
|
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
|
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();}
|
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,
|
1247
|
-
if(isUndefined(details?.error)||!isRetryable||
|
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:
|
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,
|
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,
|
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,
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
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=
|
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
|
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
|
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.
|
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
|