@rudderstack/analytics-js 3.28.1 → 3.29.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.
@@ -0,0 +1,2271 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.rudderanalytics = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ function _isPlaceholder(a){return a!=null&&typeof a==='object'&&a['@@functional/placeholder']===true;}
8
+
9
+ /**
10
+ * Optimized internal one-arity curry function.
11
+ *
12
+ * @private
13
+ * @category Function
14
+ * @param {Function} fn The function to curry.
15
+ * @return {Function} The curried function.
16
+ */function _curry1(fn){return function f1(a){if(arguments.length===0||_isPlaceholder(a)){return f1;}else {return fn.apply(this,arguments);}};}
17
+
18
+ /**
19
+ * Optimized internal two-arity curry function.
20
+ *
21
+ * @private
22
+ * @category Function
23
+ * @param {Function} fn The function to curry.
24
+ * @return {Function} The curried function.
25
+ */function _curry2(fn){return function f2(a,b){switch(arguments.length){case 0:return f2;case 1:return _isPlaceholder(a)?f2:_curry1(function(_b){return fn(a,_b);});default:return _isPlaceholder(a)&&_isPlaceholder(b)?f2:_isPlaceholder(a)?_curry1(function(_a){return fn(_a,b);}):_isPlaceholder(b)?_curry1(function(_b){return fn(a,_b);}):fn(a,b);}};}
26
+
27
+ /**
28
+ * Optimized internal three-arity curry function.
29
+ *
30
+ * @private
31
+ * @category Function
32
+ * @param {Function} fn The function to curry.
33
+ * @return {Function} The curried function.
34
+ */function _curry3(fn){return function f3(a,b,c){switch(arguments.length){case 0:return f3;case 1:return _isPlaceholder(a)?f3:_curry2(function(_b,_c){return fn(a,_b,_c);});case 2:return _isPlaceholder(a)&&_isPlaceholder(b)?f3:_isPlaceholder(a)?_curry2(function(_a,_c){return fn(_a,b,_c);}):_isPlaceholder(b)?_curry2(function(_b,_c){return fn(a,_b,_c);}):_curry1(function(_c){return fn(a,b,_c);});default:return _isPlaceholder(a)&&_isPlaceholder(b)&&_isPlaceholder(c)?f3:_isPlaceholder(a)&&_isPlaceholder(b)?_curry2(function(_a,_b){return fn(_a,_b,c);}):_isPlaceholder(a)&&_isPlaceholder(c)?_curry2(function(_a,_c){return fn(_a,b,_c);}):_isPlaceholder(b)&&_isPlaceholder(c)?_curry2(function(_b,_c){return fn(a,_b,_c);}):_isPlaceholder(a)?_curry1(function(_a){return fn(_a,b,c);}):_isPlaceholder(b)?_curry1(function(_b){return fn(a,_b,c);}):_isPlaceholder(c)?_curry1(function(_c){return fn(a,b,_c);}):fn(a,b,c);}};}
35
+
36
+ function _has(prop,obj){return Object.prototype.hasOwnProperty.call(obj,prop);}
37
+
38
+ /**
39
+ * Gives a single-word string description of the (native) type of a value,
40
+ * returning such answers as 'Object', 'Number', 'Array', or 'Null'. Does not
41
+ * attempt to distinguish user Object types any further, reporting them all as
42
+ * 'Object'.
43
+ *
44
+ * @func
45
+ * @memberOf R
46
+ * @since v0.8.0
47
+ * @category Type
48
+ * @sig * -> String
49
+ * @param {*} val The value to test
50
+ * @return {String}
51
+ * @example
52
+ *
53
+ * R.type({}); //=> "Object"
54
+ * R.type(new Map); //=> "Map"
55
+ * R.type(new Set); //=> "Set"
56
+ * R.type(1); //=> "Number"
57
+ * R.type(false); //=> "Boolean"
58
+ * R.type('s'); //=> "String"
59
+ * R.type(null); //=> "Null"
60
+ * R.type([]); //=> "Array"
61
+ * R.type(/[A-z]/); //=> "RegExp"
62
+ * R.type(() => {}); //=> "Function"
63
+ * R.type(async () => {}); //=> "AsyncFunction"
64
+ * R.type(undefined); //=> "Undefined"
65
+ * R.type(BigInt(123)); //=> "BigInt"
66
+ */var type=/*#__PURE__*/_curry1(function type(val){return val===null?'Null':val===undefined?'Undefined':Object.prototype.toString.call(val).slice(8,-1);});
67
+
68
+ function _isObject(x){return Object.prototype.toString.call(x)==='[object Object]';}
69
+
70
+ /**
71
+ * Determine if the passed argument is an integer.
72
+ *
73
+ * @private
74
+ * @param {*} n
75
+ * @category Type
76
+ * @return {Boolean}
77
+ */const _isInteger = Number.isInteger||function _isInteger(n){return n<<0===n;};
78
+
79
+ function _nth(offset,list){var idx=offset<0?list.length+offset:offset;return list[idx];}
80
+
81
+ function _cloneRegExp(pattern){return new RegExp(pattern.source,pattern.flags?pattern.flags:(pattern.global?'g':'')+(pattern.ignoreCase?'i':'')+(pattern.multiline?'m':'')+(pattern.sticky?'y':'')+(pattern.unicode?'u':'')+(pattern.dotAll?'s':''));}
82
+
83
+ /**
84
+ * Copies an object.
85
+ *
86
+ * @private
87
+ * @param {*} value The value to be copied
88
+ * @param {Boolean} deep Whether or not to perform deep cloning.
89
+ * @return {*} The copied value.
90
+ */function _clone(value,deep,map){map||(map=new _ObjectMap());// this avoids the slower switch with a quick if decision removing some milliseconds in each run.
91
+ if(_isPrimitive(value)){return value;}var copy=function copy(copiedValue){// Check for circular and same references on the object graph and return its corresponding clone.
92
+ var cachedCopy=map.get(value);if(cachedCopy){return cachedCopy;}map.set(value,copiedValue);for(var key in value){if(Object.prototype.hasOwnProperty.call(value,key)){copiedValue[key]=_clone(value[key],true,map);}}return copiedValue;};switch(type(value)){case 'Object':return copy(Object.create(Object.getPrototypeOf(value)));case 'Array':return copy(Array(value.length));case 'Date':return new Date(value.valueOf());case 'RegExp':return _cloneRegExp(value);case 'Int8Array':case 'Uint8Array':case 'Uint8ClampedArray':case 'Int16Array':case 'Uint16Array':case 'Int32Array':case 'Uint32Array':case 'Float32Array':case 'Float64Array':case 'BigInt64Array':case 'BigUint64Array':return value.slice();default:return value;}}function _isPrimitive(param){var type=typeof param;return param==null||type!='object'&&type!='function';}var _ObjectMap=/*#__PURE__*/function(){function _ObjectMap(){this.map={};this.length=0;}_ObjectMap.prototype.set=function(key,value){var hashedKey=this.hash(key);var bucket=this.map[hashedKey];if(!bucket){this.map[hashedKey]=bucket=[];}bucket.push([key,value]);this.length+=1;};_ObjectMap.prototype.hash=function(key){var hashedKey=[];for(var value in key){hashedKey.push(Object.prototype.toString.call(key[value]));}return hashedKey.join();};_ObjectMap.prototype.get=function(key){/**
93
+ * depending on the number of objects to be cloned is faster to just iterate over the items in the map just because the hash function is so costly,
94
+ * on my tests this number is 180, anything above that using the hash function is faster.
95
+ */if(this.length<=180){for(var p in this.map){var bucket=this.map[p];for(var i=0;i<bucket.length;i+=1){var element=bucket[i];if(element[0]===key){return element[1];}}}return;}var hashedKey=this.hash(key);var bucket=this.map[hashedKey];if(!bucket){return;}for(var i=0;i<bucket.length;i+=1){var element=bucket[i];if(element[0]===key){return element[1];}}};return _ObjectMap;}();
96
+
97
+ /**
98
+ * Creates a deep copy of the source that can be used in place of the source
99
+ * object without retaining any references to it.
100
+ * The source object may contain (nested) `Array`s and `Object`s,
101
+ * `Number`s, `String`s, `Boolean`s and `Date`s.
102
+ * `Function`s are assigned by reference rather than copied.
103
+ *
104
+ * Dispatches to a `clone` method if present.
105
+ *
106
+ * Note that if the source object has multiple nodes that share a reference,
107
+ * the returned object will have the same structure, but the references will
108
+ * be pointed to the location within the cloned value.
109
+ *
110
+ * @func
111
+ * @memberOf R
112
+ * @since v0.1.0
113
+ * @category Object
114
+ * @sig {*} -> {*}
115
+ * @param {*} value The object or array to clone
116
+ * @return {*} A deeply cloned copy of `val`
117
+ * @example
118
+ *
119
+ * const objects = [{}, {}, {}];
120
+ * const objectsClone = R.clone(objects);
121
+ * objects === objectsClone; //=> false
122
+ * objects[0] === objectsClone[0]; //=> false
123
+ */var clone=/*#__PURE__*/_curry1(function clone(value){return value!=null&&typeof value.clone==='function'?value.clone():_clone(value);});
124
+
125
+ function _path(pathAr,obj){var val=obj;for(var i=0;i<pathAr.length;i+=1){if(val==null){return undefined;}var p=pathAr[i];if(_isInteger(p)){val=_nth(p,val);}else {val=val[p];}}return val;}
126
+
127
+ /**
128
+ * Creates a new object with the own properties of the two provided objects. If
129
+ * a key exists in both objects, the provided function is applied to the key
130
+ * and the values associated with the key in each object, with the result being
131
+ * used as the value associated with the key in the returned object.
132
+ *
133
+ * @func
134
+ * @memberOf R
135
+ * @since v0.19.0
136
+ * @category Object
137
+ * @sig ((String, a, a) -> a) -> {a} -> {a} -> {a}
138
+ * @param {Function} fn
139
+ * @param {Object} l
140
+ * @param {Object} r
141
+ * @return {Object}
142
+ * @see R.mergeDeepWithKey, R.mergeWith
143
+ * @example
144
+ *
145
+ * let concatValues = (k, l, r) => k == 'values' ? R.concat(l, r) : r
146
+ * R.mergeWithKey(concatValues,
147
+ * { a: true, thing: 'foo', values: [10, 20] },
148
+ * { b: true, thing: 'bar', values: [15, 35] });
149
+ * //=> { a: true, b: true, thing: 'bar', values: [10, 20, 15, 35] }
150
+ * @symb R.mergeWithKey(f, { x: 1, y: 2 }, { y: 5, z: 3 }) = { x: 1, y: f('y', 2, 5), z: 3 }
151
+ */var mergeWithKey=/*#__PURE__*/_curry3(function mergeWithKey(fn,l,r){var result={};var k;l=l||{};r=r||{};for(k in l){if(_has(k,l)){result[k]=_has(k,r)?fn(k,l[k],r[k]):l[k];}}for(k in r){if(_has(k,r)&&!_has(k,result)){result[k]=r[k];}}return result;});
152
+
153
+ /**
154
+ * Creates a new object with the own properties of the two provided objects.
155
+ * If a key exists in both objects:
156
+ * - and both associated values are also objects then the values will be
157
+ * recursively merged.
158
+ * - otherwise the provided function is applied to the key and associated values
159
+ * using the resulting value as the new value associated with the key.
160
+ * If a key only exists in one object, the value will be associated with the key
161
+ * of the resulting object.
162
+ *
163
+ * @func
164
+ * @memberOf R
165
+ * @since v0.24.0
166
+ * @category Object
167
+ * @sig ((String, a, a) -> a) -> {a} -> {a} -> {a}
168
+ * @param {Function} fn
169
+ * @param {Object} lObj
170
+ * @param {Object} rObj
171
+ * @return {Object}
172
+ * @see R.mergeWithKey, R.mergeDeepWith
173
+ * @example
174
+ *
175
+ * let concatValues = (k, l, r) => k == 'values' ? R.concat(l, r) : r
176
+ * R.mergeDeepWithKey(concatValues,
177
+ * { a: true, c: { thing: 'foo', values: [10, 20] }},
178
+ * { b: true, c: { thing: 'bar', values: [15, 35] }});
179
+ * //=> { a: true, b: true, c: { thing: 'bar', values: [10, 20, 15, 35] }}
180
+ */var mergeDeepWithKey=/*#__PURE__*/_curry3(function mergeDeepWithKey(fn,lObj,rObj){return mergeWithKey(function(k,lVal,rVal){if(_isObject(lVal)&&_isObject(rVal)){return mergeDeepWithKey(fn,lVal,rVal);}else {return fn(k,lVal,rVal);}},lObj,rObj);});
181
+
182
+ /**
183
+ * Creates a new object with the own properties of the two provided objects.
184
+ * If a key exists in both objects:
185
+ * - and both associated values are also objects then the values will be
186
+ * recursively merged.
187
+ * - otherwise the provided function is applied to associated values using the
188
+ * resulting value as the new value associated with the key.
189
+ * If a key only exists in one object, the value will be associated with the key
190
+ * of the resulting object.
191
+ *
192
+ * @func
193
+ * @memberOf R
194
+ * @since v0.24.0
195
+ * @category Object
196
+ * @sig ((a, a) -> a) -> {a} -> {a} -> {a}
197
+ * @param {Function} fn
198
+ * @param {Object} lObj
199
+ * @param {Object} rObj
200
+ * @return {Object}
201
+ * @see R.mergeWith, R.mergeDeepWithKey
202
+ * @example
203
+ *
204
+ * R.mergeDeepWith(R.concat,
205
+ * { a: true, c: { values: [10, 20] }},
206
+ * { b: true, c: { values: [15, 35] }});
207
+ * //=> { a: true, b: true, c: { values: [10, 20, 15, 35] }}
208
+ */var mergeDeepWith=/*#__PURE__*/_curry3(function mergeDeepWith(fn,lObj,rObj){return mergeDeepWithKey(function(k,lVal,rVal){return fn(lVal,rVal);},lObj,rObj);});
209
+
210
+ /**
211
+ * Retrieves the value at a given path. The nodes of the path can be arbitrary strings or non-negative integers.
212
+ * For anything else, the value is unspecified. Integer paths are meant to index arrays, strings are meant for objects.
213
+ *
214
+ * @func
215
+ * @memberOf R
216
+ * @since v0.2.0
217
+ * @category Object
218
+ * @typedefn Idx = String | Int | Symbol
219
+ * @sig [Idx] -> {a} -> a | Undefined
220
+ * @sig Idx = String | NonNegativeInt
221
+ * @param {Array} path The path to use.
222
+ * @param {Object} obj The object or array to retrieve the nested property from.
223
+ * @return {*} The data at `path`.
224
+ * @see R.prop, R.nth, R.assocPath, R.dissocPath
225
+ * @example
226
+ *
227
+ * R.path(['a', 'b'], {a: {b: 2}}); //=> 2
228
+ * R.path(['a', 'b'], {c: {b: 2}}); //=> undefined
229
+ * R.path(['a', 'b', 0], {a: {b: [1, 2, 3]}}); //=> 1
230
+ * R.path(['a', 'b', -2], {a: {b: [1, 2, 3]}}); //=> 2
231
+ * R.path([2], {'2': 2}); //=> 2
232
+ * R.path([-2], {'-2': 'a'}); //=> undefined
233
+ */var path=/*#__PURE__*/_curry2(_path);
234
+
235
+ /**
236
+ * Returns a partial copy of an object containing only the keys that satisfy
237
+ * the supplied predicate.
238
+ *
239
+ * @func
240
+ * @memberOf R
241
+ * @since v0.8.0
242
+ * @category Object
243
+ * @sig ((v, k) -> Boolean) -> {k: v} -> {k: v}
244
+ * @param {Function} pred A predicate to determine whether or not a key
245
+ * should be included on the output object.
246
+ * @param {Object} obj The object to copy from
247
+ * @return {Object} A new object with only properties that satisfy `pred`
248
+ * on it.
249
+ * @see R.pick, R.filter
250
+ * @example
251
+ *
252
+ * const isUpperCase = (val, key) => key.toUpperCase() === key;
253
+ * R.pickBy(isUpperCase, {a: 1, b: 2, A: 3, B: 4}); //=> {A: 3, B: 4}
254
+ */var pickBy=/*#__PURE__*/_curry2(function pickBy(test,obj){var result={};for(var prop in obj){if(test(obj[prop],prop,obj)){result[prop]=obj[prop];}}return result;});
255
+
256
+ /**
257
+ * A function to check given value is a function
258
+ * @param value input value
259
+ * @returns boolean
260
+ */const isFunction=value=>typeof value==='function'&&Boolean(value.constructor&&value.call&&value.apply);/**
261
+ * A function to check given value is a string
262
+ * @param value input value
263
+ * @returns boolean
264
+ */const isString=value=>typeof value==='string';/**
265
+ * A function to check given value is null or not
266
+ * @param value input value
267
+ * @returns boolean
268
+ */const isNull=value=>value===null;/**
269
+ * A function to check given value is undefined
270
+ * @param value input value
271
+ * @returns boolean
272
+ */const isUndefined=value=>typeof value==='undefined';/**
273
+ * A function to check given value is null or undefined
274
+ * @param value input value
275
+ * @returns boolean
276
+ */const isNullOrUndefined=value=>isNull(value)||isUndefined(value);/**
277
+ * Checks if the input is a BigInt
278
+ * @param value input value
279
+ * @returns True if the input is a BigInt
280
+ */const isBigInt=value=>typeof value==='bigint';/**
281
+ * A function to check given value is defined
282
+ * @param value input value
283
+ * @returns boolean
284
+ */const isDefined=value=>!isUndefined(value);/**
285
+ * A function to check given value is defined and not null
286
+ * @param value input value
287
+ * @returns boolean
288
+ */const isDefinedAndNotNull=value=>!isNullOrUndefined(value);/**
289
+ * A function to check given value is defined and not null
290
+ * @param value input value
291
+ * @returns boolean
292
+ */const isDefinedNotNullAndNotEmptyString=value=>isDefinedAndNotNull(value)&&value!=='';/**
293
+ * Determines if the input is of type error
294
+ * @param value input value
295
+ * @returns true if the input is of type error else false
296
+ */const isTypeOfError=value=>{switch(Object.prototype.toString.call(value)){case '[object Error]':case '[object Exception]':case '[object DOMException]':return true;default:return value instanceof Error;}};/**
297
+ * A function to check given value is a boolean
298
+ * @param value input value
299
+ * @returns boolean
300
+ */const isBoolean=value=>typeof value==='boolean';
301
+
302
+ const getValueByPath=(obj,keyPath)=>{const pathParts=keyPath.split('.');return path(pathParts,obj);};const hasValueByPath=(obj,path)=>Boolean(getValueByPath(obj,path));const isObject=value=>typeof value==='object';/**
303
+ * Checks if the input is an object literal or built-in object type and not null
304
+ * @param value Input value
305
+ * @returns true if the input is an object and not null
306
+ */const isObjectAndNotNull=value=>!isNull(value)&&isObject(value)&&!Array.isArray(value);/**
307
+ * Checks if the input is an object literal and not null
308
+ * @param value Input value
309
+ * @returns true if the input is an object and not null
310
+ */const isObjectLiteralAndNotNull=value=>!isNull(value)&&Object.prototype.toString.call(value)==='[object Object]';/**
311
+ * Merges two arrays deeply, right-to-left
312
+ * In the case of conflicts, the right array's values replace the left array's values in the
313
+ * same index position
314
+ * @param leftValue - The left array
315
+ * @param rightValue - The right array
316
+ * @returns The merged array
317
+ */const mergeDeepRightObjectArrays=(leftValue,rightValue)=>{if(!Array.isArray(leftValue)||!Array.isArray(rightValue)){return clone(rightValue);}const mergedArray=clone(leftValue);rightValue.forEach((value,index)=>{mergedArray[index]=Array.isArray(value)||isObjectAndNotNull(value)?// eslint-disable-next-line @typescript-eslint/no-use-before-define
318
+ mergeDeepRight(mergedArray[index],value):value;});return mergedArray;};/**
319
+ * Merges two objects deeply, right-to-left.
320
+ * In the case of conflicts, the right object's values take precedence.
321
+ * For arrays, the right array's values replace the left array's values in the
322
+ * same index position keeping the remaining left array's values in the resultant array.
323
+ * @param leftObject - The left object
324
+ * @param rightObject - The right object
325
+ * @returns The merged object
326
+ */const mergeDeepRight=(leftObject,rightObject)=>mergeDeepWith(mergeDeepRightObjectArrays,leftObject,rightObject);/**
327
+ Checks if the input is a non-empty object literal type and not undefined or null
328
+ * @param value input any
329
+ * @returns boolean
330
+ */const isNonEmptyObject=value=>isObjectLiteralAndNotNull(value)&&Object.keys(value).length>0;/**
331
+ * A utility to recursively remove undefined values from an object
332
+ * @param obj input object
333
+ * @returns a new object
334
+ */const removeUndefinedValues=obj=>{const result=pickBy(isDefined,obj);Object.keys(result).forEach(key=>{const value=result[key];if(isObjectLiteralAndNotNull(value)){result[key]=removeUndefinedValues(value);}});return result;};/**
335
+ * A utility to recursively remove undefined and null values from an object
336
+ * @param obj input object
337
+ * @returns a new object
338
+ */const removeUndefinedAndNullValues=obj=>{const result=pickBy(isDefinedAndNotNull,obj);Object.keys(result).forEach(key=>{const value=result[key];if(isObjectLiteralAndNotNull(value)){result[key]=removeUndefinedAndNullValues(value);}});return result;};/**
339
+ * Normalizes an object by removing undefined and null values.
340
+ * @param val - The value to normalize
341
+ * @returns The normalized object, or undefined if input is not a non-empty object
342
+ * @example
343
+ * getNormalizedObjectValue({ a: 1, b: null, c: undefined }) // returns { a: 1 }
344
+ * getNormalizedObjectValue({}) // returns undefined
345
+ * getNormalizedObjectValue(null) // returns undefined
346
+ */const getNormalizedObjectValue=val=>{if(!isNonEmptyObject(val)){return undefined;}return removeUndefinedAndNullValues(val);};/**
347
+ * Normalizes a value to a boolean, with support for a default value
348
+ * @param val Input value
349
+ * @param defVal Default value
350
+ * @returns Returns the input value if it is a boolean, otherwise returns the default value
351
+ * @example
352
+ * getNormalizedBooleanValue(true, false) // returns true
353
+ */const getNormalizedBooleanValue=(val,defVal)=>typeof val==='boolean'?val:defVal;const deepFreeze=obj=>{Object.getOwnPropertyNames(obj).forEach(function(prop){if(obj[prop]&&typeof obj[prop]==='object'){deepFreeze(obj[prop]);}});return Object.freeze(obj);};
354
+
355
+ const trim=value=>value.replace(/^\s+|\s+$/gm,'');const removeLeadingPeriod=value=>value.replace(/^\.+/,'');/**
356
+ * A function to convert values to string
357
+ * @param val input value
358
+ * @returns stringified value
359
+ */const tryStringify=val=>{let retVal=val;if(!isString(val)&&!isNullOrUndefined(val)){try{retVal=JSON.stringify(val);}catch(e){retVal=null;}}return retVal;};// The following text encoding and decoding is done before base64 encoding to prevent
360
+ // https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
361
+ /**
362
+ * Converts a base64 encoded string to bytes array
363
+ * @param base64Str base64 encoded string
364
+ * @returns bytes array
365
+ */const base64ToBytes=base64Str=>{const binString=globalThis.atob(base64Str);const bytes=binString.split('').map(char=>char.charCodeAt(0));return new Uint8Array(bytes);};/**
366
+ * Converts a bytes array to base64 encoded string
367
+ * @param bytes bytes array to be converted to base64
368
+ * @returns base64 encoded string
369
+ */const bytesToBase64=bytes=>{const binString=Array.from(bytes,x=>String.fromCodePoint(x)).join('');return globalThis.btoa(binString);};/**
370
+ * Encodes a string to base64 even with unicode characters
371
+ * @param value input string
372
+ * @returns base64 encoded string
373
+ */const toBase64=value=>bytesToBase64(new TextEncoder().encode(value));/**
374
+ * Decodes a base64 encoded string
375
+ * @param value base64 encoded string
376
+ * @returns decoded string
377
+ */const fromBase64=value=>new TextDecoder().decode(base64ToBytes(value));
378
+
379
+ // if yes make them null instead of omitting in overloaded cases
380
+ /*
381
+ * Normalise the overloaded arguments of the page call facade
382
+ */const pageArgumentsToCallOptions=(category,name,properties,options,callback)=>{const payload={category:category,name:name,properties:properties,options:options,callback:undefined};if(isFunction(callback)){payload.callback=callback;}if(isFunction(options)){payload.category=category;payload.name=name;payload.properties=properties;payload.options=undefined;payload.callback=options;}if(isFunction(properties)){payload.category=category;payload.name=name;payload.properties=undefined;payload.options=undefined;payload.callback=properties;}if(isFunction(name)){payload.category=category;payload.name=undefined;payload.properties=undefined;payload.options=undefined;payload.callback=name;}if(isFunction(category)){payload.category=undefined;payload.name=undefined;payload.properties=undefined;payload.options=undefined;payload.callback=category;}if(isObjectLiteralAndNotNull(category)){payload.name=undefined;payload.category=undefined;payload.properties=category;if(!isFunction(name)){payload.options=name;}else {payload.options=undefined;}}else if(isObjectLiteralAndNotNull(name)){payload.name=undefined;payload.properties=name;if(!isFunction(properties)){payload.options=properties;}else {payload.options=undefined;}}// if the category argument alone is provided b/w category and name,
383
+ // use it as name and set category to undefined
384
+ if(isString(category)&&!isString(name)){payload.category=undefined;payload.name=category;}// Rest of the code is just to clean up undefined values
385
+ // and set some proper defaults
386
+ // Also, to clone the incoming object type arguments
387
+ if(!isDefined(payload.category)){payload.category=undefined;}if(!isDefined(payload.name)){payload.name=undefined;}payload.properties=payload.properties?clone(payload.properties):{};if(isDefined(payload.options)){payload.options=clone(payload.options);}else {payload.options=undefined;}const nameForProperties=isString(payload.name)?payload.name:payload.properties.name;const categoryForProperties=isString(payload.category)?payload.category:payload.properties.category;// add name and category to properties
388
+ payload.properties=mergeDeepRight(isObjectLiteralAndNotNull(payload.properties)?payload.properties:{},{...(nameForProperties&&{name:nameForProperties}),...(categoryForProperties&&{category:categoryForProperties})});return payload;};/*
389
+ * Normalise the overloaded arguments of the track call facade
390
+ */const trackArgumentsToCallOptions=(event,properties,options,callback)=>{const payload={name:event,properties:properties,options:options,callback:undefined};if(isFunction(callback)){payload.callback=callback;}if(isFunction(options)){payload.properties=properties;payload.options=undefined;payload.callback=options;}if(isFunction(properties)){payload.properties=undefined;payload.options=undefined;payload.callback=properties;}// Rest of the code is just to clean up undefined values
391
+ // and set some proper defaults
392
+ // Also, to clone the incoming object type arguments
393
+ payload.properties=isDefinedAndNotNull(payload.properties)?clone(payload.properties):{};if(isDefined(payload.options)){payload.options=clone(payload.options);}else {payload.options=undefined;}return payload;};/*
394
+ * Normalise the overloaded arguments of the identify call facade
395
+ */const identifyArgumentsToCallOptions=(userId,traits,options,callback)=>{const payload={userId:userId,traits:traits,options:options,callback:undefined};if(isFunction(callback)){payload.callback=callback;}if(isFunction(options)){payload.userId=userId;payload.traits=traits;payload.options=undefined;payload.callback=options;}if(isFunction(traits)){payload.userId=userId;payload.traits=undefined;payload.options=undefined;payload.callback=traits;}if(isObjectLiteralAndNotNull(userId)||isNull(userId)){// Explicitly set null to prevent resetting the existing value
396
+ // in the Analytics class
397
+ payload.userId=null;payload.traits=userId;if(!isFunction(traits)){payload.options=traits;}else {payload.options=undefined;}}// Rest of the code is just to clean up undefined values
398
+ // and set some proper defaults
399
+ // Also, to clone the incoming object type arguments
400
+ payload.userId=tryStringify(payload.userId);if(isObjectLiteralAndNotNull(payload.traits)){payload.traits=clone(payload.traits);}else {payload.traits=undefined;}if(isDefined(payload.options)){payload.options=clone(payload.options);}else {payload.options=undefined;}return payload;};/*
401
+ * Normalise the overloaded arguments of the alias call facade
402
+ */const aliasArgumentsToCallOptions=(to,from,options,callback)=>{const payload={to,from:from,options:options,callback:undefined};if(isFunction(callback)){payload.callback=callback;}if(isFunction(options)){payload.to=to;payload.from=from;payload.options=undefined;payload.callback=options;}if(isFunction(from)){payload.to=to;payload.from=undefined;payload.options=undefined;payload.callback=from;}else if(isObjectLiteralAndNotNull(from)||isNull(from)){payload.to=to;payload.from=undefined;payload.options=from;}// Rest of the code is just to clean up undefined values
403
+ // and set some proper defaults
404
+ // Also, to clone the incoming object type arguments
405
+ if(isDefined(payload.to)){payload.to=tryStringify(payload.to);}if(isDefined(payload.from)){payload.from=tryStringify(payload.from);}else {payload.from=undefined;}if(isDefined(payload.options)){payload.options=clone(payload.options);}else {payload.options=undefined;}return payload;};/*
406
+ * Normalise the overloaded arguments of the group call facade
407
+ */const groupArgumentsToCallOptions=(groupId,traits,options,callback)=>{const payload={groupId:groupId,traits:traits,options:options,callback:undefined};if(isFunction(callback)){payload.callback=callback;}if(isFunction(options)){payload.groupId=groupId;payload.traits=traits;payload.options=undefined;payload.callback=options;}if(isFunction(traits)){payload.groupId=groupId;payload.traits=undefined;payload.options=undefined;payload.callback=traits;}if(isObjectLiteralAndNotNull(groupId)||isNull(groupId)){// Explicitly set null to prevent resetting the existing value
408
+ // in the Analytics class
409
+ payload.groupId=null;payload.traits=groupId;if(!isFunction(traits)){payload.options=traits;}else {payload.options=undefined;}}// Rest of the code is just to clean up undefined values
410
+ // and set some proper defaults
411
+ // Also, to clone the incoming object type arguments
412
+ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(payload.traits)){payload.traits=clone(payload.traits);}else {payload.traits=undefined;}if(isDefined(payload.options)){payload.options=clone(payload.options);}else {payload.options=undefined;}return payload;};
413
+
414
+ /**
415
+ * Represents the options parameter for anonymousId
416
+ *//**
417
+ * Represents the beacon queue options parameter in loadOptions type
418
+ *//**
419
+ * Represents the queue options parameter in loadOptions type
420
+ *//**
421
+ * Represents the destinations queue options parameter in loadOptions type
422
+ */let PageLifecycleEvents=/*#__PURE__*/function(PageLifecycleEvents){PageLifecycleEvents["UNLOADED"]="Page Unloaded";return PageLifecycleEvents;}({});/**
423
+ * Represents the source configuration override options for destinations
424
+ *//**
425
+ * Represents the options parameter in the load API
426
+ */
427
+
428
+ const API_SUFFIX='API';const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API=`Ready${API_SUFFIX}`;const LOAD_API=`Load${API_SUFFIX}`;const HTTP_CLIENT='HttpClient';const RSA='RudderStackAnalytics';const ANALYTICS_CORE='AnalyticsCore';
429
+
430
+ function random(len){return crypto.getRandomValues(new Uint8Array(len));}
431
+
432
+ var SIZE=4096,HEX$1=[],IDX$1=0,BUFFER$1;for(;IDX$1<256;IDX$1++){HEX$1[IDX$1]=(IDX$1+256).toString(16).substring(1);}function v4$1(){if(!BUFFER$1||IDX$1+16>SIZE){BUFFER$1=random(SIZE);IDX$1=0;}var i=0,tmp,out='';for(;i<16;i++){tmp=BUFFER$1[IDX$1+i];if(i==6)out+=HEX$1[tmp&15|64];else if(i==8)out+=HEX$1[tmp&63|128];else out+=HEX$1[tmp];if(i&1&&i>1&&i<11)out+='-';}IDX$1+=16;return out;}
433
+
434
+ var IDX=256,HEX=[],BUFFER;while(IDX--)HEX[IDX]=(IDX+256).toString(16).substring(1);function v4(){var i=0,num,out='';if(!BUFFER||IDX+16>256){BUFFER=Array(i=256);while(i--)BUFFER[i]=256*Math.random()|0;i=IDX=0;}for(;i<16;i++){num=BUFFER[IDX+i];if(i==6)out+=HEX[num&15|64];else if(i==8)out+=HEX[num&63|128];else out+=HEX[num];if(i&1&&i>1&&i<11)out+='-';}IDX++;return out;}
435
+
436
+ const hasCrypto$1=()=>!isNullOrUndefined(globalThis.crypto)&&isFunction(globalThis.crypto.getRandomValues);
437
+
438
+ const generateUUID=()=>{if(hasCrypto$1()){return v4$1();}return v4();};
439
+
440
+ /**
441
+ * Determines if the SDK is running inside a chrome extension
442
+ * @returns boolean
443
+ */const isSDKRunningInChromeExtension=()=>!!window.chrome?.runtime?.id;const isIE11=()=>isString(globalThis.navigator.userAgent)&&/Trident.*rv:11\./.test(globalThis.navigator.userAgent);
444
+
445
+ /**
446
+ * Registers events to detect page leave scenarios
447
+ * @param callback Callback function
448
+ * @param avoidBfCacheOptimization When `true`, forcefully subscribes to beforeunload event, compromising on the bfcache optimization
449
+ */const onPageLeave=(callback,avoidBfCacheOptimization=false)=>{// To ensure the callback is only called once even if more than one events
450
+ // are fired at once.
451
+ let pageLeft=false;let isAccessible=false;function handleOnLeave(){if(pageLeft){return;}pageLeft=true;callback(isAccessible);// Reset pageLeft on the next tick
452
+ // to ensure callback executes for other listeners
453
+ // when closing an inactive browser tab.
454
+ setTimeout(()=>{pageLeft=false;},0);}// Catches the unloading of the page (e.g., closing the tab or navigating away).
455
+ // Includes user actions like clicking a link, entering a new URL,
456
+ // refreshing the page, or closing the browser tab
457
+ // Note that 'pagehide' is not supported in IE.
458
+ // Registering this event conditionally for IE11 also because
459
+ // it affects bfcache optimization on modern browsers otherwise.
460
+ // However, if optimization is disabled, force subscribe the event
461
+ if(avoidBfCacheOptimization||isIE11()){globalThis.addEventListener('beforeunload',()=>{isAccessible=false;handleOnLeave();});}// This is important for iOS Safari browser as it does not
462
+ // fire the regular pagehide and visibilitychange events
463
+ // when user goes to tablist view and closes the tab.
464
+ globalThis.addEventListener('blur',()=>{isAccessible=true;handleOnLeave();});globalThis.addEventListener('focus',()=>{pageLeft=false;});// Catches the page being hidden, including scenarios like closing the tab.
465
+ document.addEventListener('pagehide',()=>{isAccessible=document.visibilityState==='hidden';handleOnLeave();});// Catches visibility changes, such as switching tabs or minimizing the browser.
466
+ document.addEventListener('visibilitychange',()=>{isAccessible=true;if(document.visibilityState==='hidden'){handleOnLeave();}else {pageLeft=false;}});};
467
+
468
+ const getFormattedTimestamp=date=>date.toISOString();/**
469
+ * To get the current timestamp in ISO string format
470
+ * @returns ISO formatted timestamp string
471
+ */const getCurrentTimeFormatted=()=>getFormattedTimestamp(new Date());
472
+
473
+ const LOG_CONTEXT_SEPARATOR=':: ';const SCRIPT_ALREADY_EXISTS_ERROR=id=>`A script with the id "${id}" is already loaded. Skipping the loading of this script to prevent conflicts`;const SCRIPT_LOAD_ERROR=(id,url,ev)=>`Unable to load (${isString(ev)?ev:ev.type}) the script with the id "${id}" from URL "${url}"`;const SCRIPT_LOAD_TIMEOUT_ERROR=(id,url,timeout)=>`A timeout of ${timeout} ms occurred while trying to load the script with id "${id}" from URL "${url}"`;const CIRCULAR_REFERENCE_WARNING=(context,key)=>`${context}${LOG_CONTEXT_SEPARATOR}A circular reference has been detected in the object and the property "${key}" has been dropped from the output.`;const JSON_STRINGIFY_WARNING=`Failed to convert the value to a JSON string.`;const COOKIE_DATA_ENCODING_ERROR=`Failed to encode the cookie data.`;const STORAGE_UNAVAILABILITY_ERROR_PREFIX=(context,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${storageType}" storage type is `;
474
+
475
+ const JSON_STRINGIFY='JSONStringify';const BIG_INT_PLACEHOLDER='[BigInt]';const CIRCULAR_REFERENCE_PLACEHOLDER='[Circular Reference]';const getCircularReplacer=(excludeNull,excludeKeys,logger)=>{const ancestors=[];// Here we do not want to use arrow function to use "this" in function context
476
+ // eslint-disable-next-line func-names
477
+ return function(key,value){if(excludeKeys?.includes(key)){return undefined;}if(excludeNull&&isNullOrUndefined(value)){return undefined;}if(typeof value!=='object'||isNull(value)){return value;}// `this` is the object that value is contained in, i.e., its direct parent.
478
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
479
+ // @ts-ignore-next-line
480
+ while(ancestors.length>0&&ancestors[ancestors.length-1]!==this){ancestors.pop();}if(ancestors.includes(value)){logger?.warn(CIRCULAR_REFERENCE_WARNING(JSON_STRINGIFY,key));return CIRCULAR_REFERENCE_PLACEHOLDER;}ancestors.push(value);return value;};};/**
481
+ * Utility method for JSON stringify object excluding null values & circular references
482
+ *
483
+ * @param {*} value input
484
+ * @param {boolean} excludeNull if it should exclude nul or not
485
+ * @param {function} logger optional logger methods for warning
486
+ * @returns string
487
+ */const stringifyWithoutCircular=(value,excludeNull,excludeKeys,logger)=>{try{return JSON.stringify(value,getCircularReplacer(excludeNull,excludeKeys,logger));}catch(err){logger?.warn(JSON_STRINGIFY_WARNING,err);return null;}};const getReplacer=logger=>{const ancestors=[];// Array to track ancestor objects
488
+ // Using a regular function to use `this` for the parent context
489
+ return function replacer(key,value){if(isBigInt(value)){return BIG_INT_PLACEHOLDER;// Replace BigInt values
490
+ }// `this` is the object that value is contained in, i.e., its direct parent.
491
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
492
+ // @ts-ignore-next-line
493
+ while(ancestors.length>0&&ancestors[ancestors.length-1]!==this){ancestors.pop();// Remove ancestors that are no longer part of the chain
494
+ }// Check for circular references (if the value is already in the ancestors)
495
+ if(ancestors.includes(value)){return CIRCULAR_REFERENCE_PLACEHOLDER;}// Add current value to ancestors
496
+ ancestors.push(value);return value;};};const traverseWithThis=(obj,replacer)=>{// Create a new result object or array
497
+ const result=Array.isArray(obj)?[]:{};// Traverse object properties or array elements
498
+ // eslint-disable-next-line no-restricted-syntax
499
+ for(const key in obj){if(Object.hasOwnProperty.call(obj,key)){const value=obj[key];// Recursively apply the replacer and traversal
500
+ const sanitizedValue=replacer.call(obj,key,value);// If the value is an object or array, continue traversal
501
+ if(isObjectLiteralAndNotNull(sanitizedValue)||Array.isArray(sanitizedValue)){result[key]=traverseWithThis(sanitizedValue,replacer);}else {result[key]=sanitizedValue;}}}return result;};/**
502
+ * Recursively traverses an object similar to JSON.stringify,
503
+ * sanitizing BigInts and circular references
504
+ * @param value Input object
505
+ * @param logger Logger instance
506
+ * @returns Sanitized value
507
+ */const getSanitizedValue=(value,logger)=>{const replacer=getReplacer();// This is needed for registering the first ancestor
508
+ const newValue=replacer.call(value,'',value);if(isObjectLiteralAndNotNull(value)||Array.isArray(value)){return traverseWithThis(value,replacer);}return newValue;};
509
+
510
+ const MANUAL_ERROR_IDENTIFIER='[SDK DISPATCHED ERROR]';const getStacktrace=err=>{const{stack,stacktrace,'opera#sourceloc':operaSourceloc}=err;const stackString=stack??stacktrace??operaSourceloc;if(!!stackString&&typeof stackString==='string'){return stackString;}return undefined;};/**
511
+ * Get mutated error with issue prepended to error message
512
+ * @param err Original error
513
+ * @param issue Issue to prepend to error message
514
+ * @returns Instance of Error with message prepended with issue
515
+ */const getMutatedError=(err,issue)=>{if(!isTypeOfError(err)){return new Error(`${issue}: ${stringifyWithoutCircular(err)}`);}try{// Preserve the specific error type (TypeError, ReferenceError, etc.)
516
+ const ErrorConstructor=err.constructor;const newError=new ErrorConstructor(`${issue}: ${err.message}`);// Preserve stack trace
517
+ const stack=getStacktrace(err);if(stack){newError.stack=stack;}// Preserve any other enumerable properties
518
+ Object.getOwnPropertyNames(err).forEach(key=>{if(key!=='message'&&key!=='stack'&&key!=='name'){try{newError[key]=err[key];}catch{// Ignore if property is not writable
519
+ }}});return newError;}catch{return new Error(`${issue}: ${stringifyWithoutCircular(err)}`);}};const dispatchErrorEvent=error=>{if(isTypeOfError(error)){const errStack=getStacktrace(error);if(errStack){const{stack,stacktrace,'opera#sourceloc':operaSourceloc}=error;switch(errStack){case stack:// eslint-disable-next-line no-param-reassign
520
+ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// eslint-disable-next-line no-param-reassign
521
+ error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
522
+ error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
523
+
524
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.29.0';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const BUILD_VARIANT='lite';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';
525
+
526
+ 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';
527
+
528
+ const DEFAULT_XHR_TIMEOUT_MS=10*1000;// 10 seconds
529
+ const DEFAULT_COOKIE_MAX_AGE_MS=31536000*1000;// 1 year
530
+ const DEFAULT_SESSION_CUT_OFF_DURATION_MS=12*60*60*1000;// 12 hours
531
+ const DEFAULT_SESSION_TIMEOUT_MS=30*60*1000;// 30 minutes
532
+ const MIN_SESSION_TIMEOUT_MS=10*1000;// 10 seconds
533
+ const DEFAULT_DATA_PLANE_EVENTS_BUFFER_TIMEOUT_MS=10*1000;// 10 seconds
534
+ const DEBOUNCED_TIMEOUT_MS=250;// 250 milliseconds
535
+
536
+ /**
537
+ * Create globally accessible RudderStackGlobals object
538
+ */const createExposedGlobals=(analyticsInstanceId='app')=>{if(!globalThis.RudderStackGlobals){globalThis.RudderStackGlobals={};}if(!globalThis.RudderStackGlobals[analyticsInstanceId]){globalThis.RudderStackGlobals[analyticsInstanceId]={};}};/**
539
+ * Add move values to globally accessible RudderStackGlobals object per analytics instance
540
+ */const setExposedGlobal=(keyName,value,analyticsInstanceId='app')=>{createExposedGlobals(analyticsInstanceId);globalThis.RudderStackGlobals[analyticsInstanceId][keyName]=value;};/**
541
+ * Get values from globally accessible RudderStackGlobals object by analytics instance
542
+ */const getExposedGlobal=(keyName,analyticsInstanceId='app')=>{createExposedGlobals(analyticsInstanceId);return globalThis.RudderStackGlobals[analyticsInstanceId][keyName];};function debounce(func,thisArg,delay=DEBOUNCED_TIMEOUT_MS){let timeoutId;return (...args)=>{globalThis.clearTimeout(timeoutId);timeoutId=globalThis.setTimeout(()=>{func.apply(thisArg,args);},delay);};}
543
+
544
+ /**
545
+ * Parse query string params into object values for keys that start with a defined prefix
546
+ */const getEventDataFromQueryString=(params,dataTypeNamePrefix)=>{const data={};params.forEach((value,key)=>{if(key.startsWith(dataTypeNamePrefix)){// remove prefix from key name
547
+ const dataKey=key.substring(dataTypeNamePrefix.length);// add new key value pair in generated object
548
+ data[dataKey]=params.get(key);}});return data;};/**
549
+ * Parse query string into preload buffer events & push into existing array before any other events
550
+ */const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
551
+ const eventArgumentToQueryParamMap={trait:QUERY_PARAM_TRAIT_PREFIX,properties:QUERY_PARAM_PROPERTY_PREFIX};const queryObject=new URLSearchParams(globalThis.location.search);// Add track events with name and properties
552
+ if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}// Send identify event
553
+ const userId=queryObject.get(QUERY_PARAM_USER_ID_KEY);const userTraits=getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.trait);if(userId||isNonEmptyObject(userTraits)){// In identify API, user ID is optional
554
+ const identifyApiArgs=[...(userId?[userId]:[]),userTraits];argumentsArray.unshift(['identify',...identifyApiArgs]);}// Set anonymousID
555
+ if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
556
+ * Retrieve an existing buffered load method call and remove from the existing array
557
+ */const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
558
+ * Iterate the buffered API calls until we find load call and process it separately
559
+ */let i=0;while(i<preloadedEventsArray.length){if(preloadedEventsArray[i]&&preloadedEventsArray[i][0]===loadMethodName){loadEvent=clone(preloadedEventsArray[i]);preloadedEventsArray.splice(i,1);break;}i+=1;}return loadEvent;};/**
560
+ * Promote consent events to the top of the preloaded events array
561
+ * @param preloadedEventsArray Preloaded events array
562
+ * @returns None
563
+ */const promotePreloadedConsentEventsToTop=preloadedEventsArray=>{const consentMethodName='consent';const consentEvents=preloadedEventsArray.filter(bufferedEvent=>bufferedEvent[0]===consentMethodName);const nonConsentEvents=preloadedEventsArray.filter(bufferedEvent=>bufferedEvent[0]!==consentMethodName);// Remove all elements and add consent events first followed by non consent events
564
+ // eslint-disable-next-line unicorn/no-useless-spread
565
+ preloadedEventsArray.splice(0,preloadedEventsArray.length,...consentEvents,...nonConsentEvents);};/**
566
+ * Retrieve any existing events that were triggered before SDK load and enqueue in buffer
567
+ */const retrievePreloadBufferEvents=instance=>{const preloadedEventsArray=getExposedGlobal(GLOBAL_PRELOAD_BUFFER)||[];// Get events that are pre-populated via query string params
568
+ retrieveEventsFromQueryString(preloadedEventsArray);// Enqueue the non load events in the buffer of the global rudder analytics singleton
569
+ if(preloadedEventsArray.length>0){instance.enqueuePreloadBufferEvents(preloadedEventsArray);setExposedGlobal(GLOBAL_PRELOAD_BUFFER,[]);}};const consumePreloadBufferedEvent=(event,analyticsInstance)=>{const methodName=event.shift();let callOptions;if(isFunction(analyticsInstance[methodName])){switch(methodName){case 'page':callOptions=pageArgumentsToCallOptions(...event);break;case 'track':callOptions=trackArgumentsToCallOptions(...event);break;case 'identify':callOptions=identifyArgumentsToCallOptions(...event);break;case 'alias':callOptions=aliasArgumentsToCallOptions(...event);break;case 'group':callOptions=groupArgumentsToCallOptions(...event);break;default:analyticsInstance[methodName](...event);break;}if(callOptions){analyticsInstance[methodName](callOptions);}}};
570
+
571
+ const DEFAULT_EXT_SRC_LOAD_TIMEOUT_MS=10*1000;// 10 seconds
572
+
573
+ const EXTERNAL_SOURCE_LOAD_ORIGIN='RS_JS_SDK';
574
+
575
+ /**
576
+ * Create the DOM element to load a script marked as RS SDK originated
577
+ *
578
+ * @param {*} url The URL of the script to be loaded
579
+ * @param {*} id ID for the script tag
580
+ * @param {*} async Whether to load the script in async mode. Defaults to `true` [optional]
581
+ * @param {*} onload callback to invoke onload [optional]
582
+ * @param {*} onerror callback to invoke onerror [optional]
583
+ * @param {*} extraAttributes key/value pair with html attributes to add in html tag [optional]
584
+ *
585
+ * @returns HTMLScriptElement
586
+ */const createScriptElement=(url,id,async=true,onload=null,onerror=null,extraAttributes={})=>{const scriptElement=document.createElement('script');scriptElement.type='text/javascript';scriptElement.onload=onload;scriptElement.onerror=onerror;scriptElement.src=url;scriptElement.id=id;scriptElement.async=async;Object.keys(extraAttributes).forEach(attributeName=>{scriptElement.setAttribute(attributeName,extraAttributes[attributeName]);});scriptElement.setAttribute('data-loader',EXTERNAL_SOURCE_LOAD_ORIGIN);return scriptElement;};/**
587
+ * Add script DOM element to DOM
588
+ *
589
+ * @param {*} newScriptElement the script element to add
590
+ *
591
+ * @returns
592
+ */const insertScript=newScriptElement=>{// First try to add it to the head
593
+ const headElements=document.getElementsByTagName('head');if(headElements.length>0){headElements[0]?.insertBefore(newScriptElement,headElements[0]?.firstChild);return;}// Else wise add it before the first script tag
594
+ const scriptElements=document.getElementsByTagName('script');if(scriptElements.length>0&&scriptElements[0]?.parentNode){scriptElements[0]?.parentNode.insertBefore(newScriptElement,scriptElements[0]);return;}// Create a new head element and add the script as fallback
595
+ const headElement=document.createElement('head');headElement.appendChild(newScriptElement);const htmlElement=document.getElementsByTagName('html')[0];htmlElement?.insertBefore(headElement,htmlElement.firstChild);};/**
596
+ * Loads external js file as a script html tag
597
+ *
598
+ * @param {*} url The URL of the script to be loaded
599
+ * @param {*} id ID for the script tag
600
+ * @param {*} timeout loading timeout
601
+ * @param {*} async Whether to load the script in async mode. Defaults to `true` [optional]
602
+ * @param {*} extraAttributes key/value pair with html attributes to add in html tag [optional]
603
+ *
604
+ * @returns
605
+ */const jsFileLoader=(url,id,timeout,async=true,extraAttributes)=>new Promise((resolve,reject)=>{const scriptExists=document.getElementById(id);if(scriptExists){reject(new Error(SCRIPT_ALREADY_EXISTS_ERROR(id)));}try{let timeoutID;const onload=()=>{globalThis.clearTimeout(timeoutID);resolve(id);};const onerror=ev=>{globalThis.clearTimeout(timeoutID);reject(new Error(SCRIPT_LOAD_ERROR(id,url,ev)));};// Create the DOM element to load the script and add it to the DOM
606
+ insertScript(createScriptElement(url,id,async,onload,onerror,extraAttributes));// Reject on timeout
607
+ timeoutID=globalThis.setTimeout(()=>{reject(new Error(SCRIPT_LOAD_TIMEOUT_ERROR(id,url,timeout)));},timeout);}catch(err){reject(getMutatedError(err,SCRIPT_LOAD_ERROR(id,url,'unknown')));}});
608
+
609
+ /**
610
+ * Service to load external resources/files
611
+ */class ExternalSrcLoader{constructor(logger,timeout=DEFAULT_EXT_SRC_LOAD_TIMEOUT_MS){this.logger=logger;this.timeout=timeout;}/**
612
+ * Load external resource of type javascript
613
+ */loadJSFile(config){const{url,id,timeout,async,callback,extraAttributes}=config;const isFireAndForget=!isFunction(callback);jsFileLoader(url,id,timeout||this.timeout,async,extraAttributes).then(id=>{if(!isFireAndForget){callback(id);}}).catch(err=>{if(!isFireAndForget){callback(id,err);}});}}
614
+
615
+ var i=Symbol.for("preact-signals");function t(){if(!(s>1)){var i,t=false;while(void 0!==h){var n=h;h=void 0;v++;while(void 0!==n){var r=n.o;n.o=void 0;n.f&=-3;if(!(8&n.f)&&a(n))try{n.c();}catch(n){if(!t){i=n;t=true;}}n=r;}}v=0;s--;if(t)throw i;}else s--;}function n(i){if(s>0)return i();s++;try{return i();}finally{t();}}var r=void 0;function o(i){var t=r;r=void 0;try{return i();}finally{r=t;}}var h=void 0,s=0,v=0,u=0;function e(i){if(void 0!==r){var t=i.n;if(void 0===t||t.t!==r){t={i:0,S:i,p:r.s,n:void 0,t:r,e:void 0,x:void 0,r:t};if(void 0!==r.s)r.s.n=t;r.s=t;i.n=t;if(32&r.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=r.s;t.n=void 0;r.s.n=t;r.s=t;}return t;}}}function d(i,t){this.v=i;this.i=0;this.n=void 0;this.t=void 0;this.W=null==t?void 0:t.watched;this.Z=null==t?void 0:t.unwatched;this.name=null==t?void 0:t.name;}d.prototype.brand=i;d.prototype.h=function(){return true;};d.prototype.S=function(i){var t=this,n=this.t;if(n!==i&&void 0===i.e){i.x=n;this.t=i;if(void 0!==n)n.e=i;else o(function(){var i;null==(i=t.W)||i.call(t);});}};d.prototype.U=function(i){var t=this;if(void 0!==this.t){var n=i.e,r=i.x;if(void 0!==n){n.x=r;i.e=void 0;}if(void 0!==r){r.e=n;i.x=void 0;}if(i===this.t){this.t=r;if(void 0===r)o(function(){var i;null==(i=t.Z)||i.call(t);});}}};d.prototype.subscribe=function(i){var t=this;return m(function(){var n=t.value,o=r;r=void 0;try{i(n);}finally{r=o;}},{name:"sub"});};d.prototype.valueOf=function(){return this.value;};d.prototype.toString=function(){return this.value+"";};d.prototype.toJSON=function(){return this.value;};d.prototype.peek=function(){var i=r;r=void 0;try{return this.value;}finally{r=i;}};Object.defineProperty(d.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(v>100)throw new Error("Cycle detected");this.v=i;this.i++;u++;s++;try{for(var n=this.t;void 0!==n;n=n.x)n.t.N();}finally{t();}}}});function c(i,t){return new d(i,t);}function a(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 l(i){for(var t=i.s;void 0!==t;t=t.n){var n=t.S.n;if(void 0!==n)t.r=n;t.S.n=t;t.i=-1;if(void 0===t.n){i.s=t;break;}}}function y(i){var t=i.s,n=void 0;while(void 0!==t){var r=t.p;if(-1===t.i){t.S.U(t);if(void 0!==r)r.n=t.n;if(void 0!==t.n)t.n.p=r;}else n=t;t.S.n=t.r;if(void 0!==t.r)t.r=void 0;t=r;}i.s=n;}function w(i,t){d.call(this,void 0);this.x=i;this.s=void 0;this.g=u-1;this.f=4;this.W=null==t?void 0:t.watched;this.Z=null==t?void 0:t.unwatched;this.name=null==t?void 0:t.name;}w.prototype=new d();w.prototype.h=function(){this.f&=-3;if(1&this.f)return false;if(32==(36&this.f))return true;this.f&=-5;if(this.g===u)return true;this.g=u;this.f|=1;if(this.i>0&&!a(this)){this.f&=-2;return true;}var i=r;try{l(this);r=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++;}r=i;y(this);this.f&=-2;return true;};w.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);}d.prototype.S.call(this,i);};w.prototype.U=function(i){if(void 0!==this.t){d.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);}}};w.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(w.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 n=i.u;i.u=void 0;if("function"==typeof n){s++;var o=r;r=void 0;try{n();}catch(t){i.f&=-2;i.f|=8;p(i);throw t;}finally{r=o;t();}}}function p(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 g(i){if(r!==this)throw new Error("Out-of-order effect");y(this);r=i;this.f&=-2;if(8&this.f)p(this);t();}function S(i,t){this.x=i;this.u=void 0;this.s=void 0;this.o=void 0;this.f=32;this.name=null==t?void 0:t.name;}S.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();}};S.prototype.S=function(){if(1&this.f)throw new Error("Cycle detected");this.f|=1;this.f&=-9;_(this);l(this);s++;var i=r;r=this;return g.bind(this,i);};S.prototype.N=function(){if(!(2&this.f)){this.f|=2;this.o=h;h=this;}};S.prototype.d=function(){this.f|=8;if(!(1&this.f))p(this);};S.prototype.dispose=function(){this.d();};function m(i,t){var n=new S(i,t);try{n.c();}catch(i){n.d();throw i;}var r=n.d.bind(n);r[Symbol.dispose]=r;return r;}
616
+
617
+ /**
618
+ * A buffer queue to serve as a store for any type of data
619
+ */class BufferQueue{constructor(){this.items=[];}enqueue(item){this.items.push(item);}dequeue(){if(this.items.length===0){return null;}return this.items.shift();}isEmpty(){return this.items.length===0;}size(){return this.items.length;}clear(){this.items=[];}}
620
+
621
+ const LOG_LEVEL_MAP={LOG:0,INFO:1,DEBUG:2,WARN:3,ERROR:4,NONE:5};const DEFAULT_LOG_LEVEL='LOG';const POST_LOAD_LOG_LEVEL='ERROR';const LOG_MSG_PREFIX='RS SDK';const LOG_MSG_PREFIX_STYLE='font-weight: bold; background: black; color: white;';const LOG_MSG_STYLE='font-weight: normal;';/**
622
+ * Service to log messages/data to output provider, default is console
623
+ */class Logger{constructor(minLogLevel=DEFAULT_LOG_LEVEL,scope='',logProvider=console){this.minLogLevel=LOG_LEVEL_MAP[minLogLevel];this.scope=scope;this.logProvider=logProvider;}log(...data){this.outputLog('LOG',data);}info(...data){this.outputLog('INFO',data);}debug(...data){this.outputLog('DEBUG',data);}warn(...data){this.outputLog('WARN',data);}error(...data){this.outputLog('ERROR',data);}outputLog(logMethod,data){if(this.minLogLevel<=LOG_LEVEL_MAP[logMethod]){this.logProvider[logMethod.toLowerCase()]?.(...this.formatLogData(data));}}setScope(scopeVal){this.scope=scopeVal||this.scope;}// TODO: should we allow to change the level via global variable on run time
624
+ // to assist on the fly debugging?
625
+ setMinLogLevel(logLevel){this.minLogLevel=LOG_LEVEL_MAP[logLevel];if(isUndefined(this.minLogLevel)){this.minLogLevel=LOG_LEVEL_MAP[DEFAULT_LOG_LEVEL];}}/**
626
+ * Formats the console message using `scope` and styles
627
+ */formatLogData(data){if(Array.isArray(data)&&data.length>0){// prefix SDK identifier
628
+ let msg=`%c ${LOG_MSG_PREFIX}`;// format the log message using `scope`
629
+ if(this.scope){msg=`${msg} - ${this.scope}`;}// trim whitespaces for original message
630
+ const originalMsg=isString(data[0])?data[0].trim():'';// prepare the final message
631
+ msg=`${msg} %c ${originalMsg}`;const styledLogArgs=[msg,LOG_MSG_PREFIX_STYLE,// add style for the prefix
632
+ LOG_MSG_STYLE// reset the style for the actual message
633
+ ];// add first it if it was not a string msg
634
+ if(!isString(data[0])){styledLogArgs.push(data[0]);}// append rest of the original arguments
635
+ styledLogArgs.push(...data.slice(1));return styledLogArgs;}return data;}}const defaultLogger=new Logger();
636
+
637
+ let ErrorType=/*#__PURE__*/function(ErrorType){ErrorType["HANDLEDEXCEPTION"]="handledException";ErrorType["UNHANDLEDEXCEPTION"]="unhandledException";ErrorType["UNHANDLEDREJECTION"]="unhandledPromiseRejection";return ErrorType;}({});
638
+
639
+ // default is v3
640
+ const SUPPORTED_STORAGE_TYPES=['localStorage','memoryStorage','cookieStorage','sessionStorage','none'];const DEFAULT_STORAGE_TYPE='cookieStorage';
641
+
642
+ const SOURCE_CONFIG_RESOLUTION_ERROR=`Unable to process/parse source configuration response`;const SOURCE_DISABLED_ERROR=`The source is disabled. Please enable the source in the dashboard to send events.`;const XHR_PAYLOAD_PREP_ERROR=`Failed to prepare data for the request.`;const PLUGIN_EXT_POINT_MISSING_ERROR=`Failed to invoke plugin because the extension point name is missing.`;const PLUGIN_EXT_POINT_INVALID_ERROR=`Failed to invoke plugin because the extension point name is invalid.`;const SOURCE_CONFIG_OPTION_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The "getSourceConfig" load API option must be a function that returns valid source configuration data.`;const COMPONENT_BASE_URL_ERROR=(context,component,url)=>`${context}${LOG_CONTEXT_SEPARATOR}The base URL "${url}" for ${component} is not valid.`;// ERROR
643
+ const UNSUPPORTED_CONSENT_MANAGER_ERROR=(context,selectedConsentManager,consentManagersToPluginNameMap)=>`${context}${LOG_CONTEXT_SEPARATOR}The consent manager "${selectedConsentManager}" is not supported. Please choose one of the following supported consent managers: "${Object.keys(consentManagersToPluginNameMap)}".`;const NON_ERROR_WARNING=(context,errStr)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring a non-error: ${errStr}.`;const BREADCRUMB_ERROR=`Failed to log breadcrumb`;const HANDLE_ERROR_FAILURE=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to handle the error.`;const PLUGIN_NAME_MISSING_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin name is missing.`;const PLUGIN_ALREADY_EXISTS_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" already exists.`;const PLUGIN_NOT_FOUND_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found.`;const PLUGIN_ENGINE_BUG_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found in plugins but found in byName. This indicates a bug in the plugin engine. Please report this issue to the development team.`;const PLUGIN_DEPS_ERROR=(context,pluginName,notExistDeps)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" could not be loaded because some of its dependencies "${notExistDeps}" do not exist.`;const PLUGIN_INVOCATION_ERROR=(context,extPoint,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to invoke the "${extPoint}" extension point of plugin "${pluginName}".`;const SOURCE_CONFIG_FETCH_ERROR='Failed to fetch the source config';const WRITE_KEY_VALIDATION_ERROR=(context,writeKey)=>`${context}${LOG_CONTEXT_SEPARATOR}The write key "${writeKey}" is invalid. It must be a non-empty string. Please check that the write key is correct and try again.`;const DATA_PLANE_URL_VALIDATION_ERROR=(context,dataPlaneUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The data plane URL "${dataPlaneUrl}" is invalid. It must be a valid URL string. Please check that the data plane URL is correct and try again.`;const INVALID_CALLBACK_FN_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The provided callback parameter is not a function.`;const XHR_DELIVERY_ERROR=(prefix,status,statusText,url,response)=>`${prefix} with status ${status} (${statusText}) for URL: ${url}. Response: ${response.trim()}`;const XHR_REQUEST_ERROR=(prefix,e,url)=>`${prefix} due to timeout or no connection (${e?e.type:''}) at the client side for URL: ${url}`;const XHR_SEND_ERROR=(prefix,url)=>`${prefix} for URL: ${url}`;const STORE_DATA_SAVE_ERROR=key=>`Failed to save the value for "${key}" to storage`;const STORE_DATA_FETCH_ERROR=key=>`Failed to retrieve or parse data for "${key}" from storage`;const DATA_SERVER_REQUEST_FAIL_ERROR=status=>`The server responded with status ${status} while setting the cookies. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_ERROR=key=>`The server failed to set the ${key} cookie. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR=`Failed to set/remove cookies via server. As a fallback, the cookies will be managed client side.`;// WARNING
644
+ const STORAGE_TYPE_VALIDATION_WARNING=(context,storageType,defaultStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${storageType}" is not supported. Please choose one of the following supported types: "${SUPPORTED_STORAGE_TYPES}". The default type "${defaultStorageType}" will be used instead.`;const UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING=(context,selectedStorageEncryptionVersion,storageEncryptionVersionsToPluginNameMap,defaultVersion)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage encryption version "${selectedStorageEncryptionVersion}" is not supported. Please choose one of the following supported versions: "${Object.keys(storageEncryptionVersionsToPluginNameMap)}". The default version "${defaultVersion}" will be used instead.`;const STORAGE_DATA_MIGRATION_OVERRIDE_WARNING=(context,storageEncryptionVersion,defaultVersion)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage data migration has been disabled because the configured storage encryption version (${storageEncryptionVersion}) is not the latest (${defaultVersion}). To enable storage data migration, please update the storage encryption version to the latest version.`;const SERVER_SIDE_COOKIE_FEATURE_OVERRIDE_WARNING=(context,providedCookieDomain,currentCookieDomain)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided cookie domain (${providedCookieDomain}) does not match the current webpage's domain (${currentCookieDomain}). Hence, the cookies will be set client-side.`;const RESERVED_KEYWORD_WARNING=(context,property,parentKeyPath,reservedElements)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${property}" property defined under "${parentKeyPath}" is a reserved keyword. Please choose a different property name to avoid conflicts with reserved keywords (${reservedElements}).`;const INVALID_CONTEXT_OBJECT_WARNING=logContext=>`${logContext}${LOG_CONTEXT_SEPARATOR}Please make sure that the "context" property in the event API's "options" argument is a valid object literal with key-value pairs.`;const UNSUPPORTED_BEACON_API_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The Beacon API is not supported by your browser. The events will be sent using XHR instead.`;const TIMEOUT_NOT_NUMBER_WARNING=(context,timeout,defaultValue)=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value "${timeout}" is not a number. The default timeout of ${defaultValue} ms will be used instead.`;const CUT_OFF_DURATION_NOT_NUMBER_WARNING=(context,cutOffDuration,defaultValue)=>`${context}${LOG_CONTEXT_SEPARATOR}The session cut off duration value "${cutOffDuration}" is not a number. The default cut off duration of ${defaultValue} ms will be used instead.`;const CUT_OFF_DURATION_LESS_THAN_TIMEOUT_WARNING=(context,cutOffDuration,timeout)=>`${context}${LOG_CONTEXT_SEPARATOR}The session cut off duration value "${cutOffDuration}" ms is less than the session timeout value "${timeout}" ms. The cut off functionality will be disabled.`;const TIMEOUT_ZERO_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value is 0, which disables the automatic session tracking feature. If you want to enable session tracking, please provide a positive integer value for the timeout.`;const TIMEOUT_NOT_RECOMMENDED_WARNING=(context,timeout,minTimeout)=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value ${timeout} ms is less than the recommended minimum of ${minTimeout} ms. Please consider increasing the timeout value to ensure optimal performance and reliability.`;const INVALID_SESSION_ID_WARNING=(context,sessionId,minSessionIdLength)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided session ID (${sessionId}) is either invalid, not a positive integer, or not at least "${minSessionIdLength}" digits long. A new session ID will be auto-generated instead.`;const STORAGE_QUOTA_EXCEEDED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The storage is either full or unavailable, so the data will not be persisted. Switching to in-memory storage.`;const STORAGE_UNAVAILABLE_WARNING=(context,entry,selectedStorageType,finalStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${selectedStorageType}" is not available for entry "${entry}". The SDK will initialize the entry with "${finalStorageType}" storage type instead.`;const CALLBACK_INVOKE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The callback threw an exception`;const INVALID_CONFIG_URL_WARNING=(context,configUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided source config URL "${configUrl}" is invalid. Using the default source config URL instead.`;const POLYFILL_SCRIPT_LOAD_ERROR=(scriptId,url)=>`Failed to load the polyfill script with ID "${scriptId}" from URL ${url}.`;const UNSUPPORTED_PRE_CONSENT_STORAGE_STRATEGY=(context,selectedStrategy,defaultStrategy)=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent storage strategy "${selectedStrategy}" is not supported. Please choose one of the following supported strategies: "none, session, anonymousId". The default strategy "${defaultStrategy}" will be used instead.`;const UNSUPPORTED_PRE_CONSENT_EVENTS_DELIVERY_TYPE=(context,selectedDeliveryType,defaultDeliveryType)=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent events delivery type "${selectedDeliveryType}" is not supported. Please choose one of the following supported types: "immediate, buffer". The default type "${defaultDeliveryType}" will be used instead.`;const DEPRECATED_PLUGIN_WARNING=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}${pluginName} plugin is deprecated. Please exclude it from the load API options.`;const generateMisconfiguredPluginsWarning=(context,configurationStatus,missingPlugins,shouldAddMissingPlugins)=>{const isSinglePlugin=missingPlugins.length===1;const pluginsString=isSinglePlugin?` '${missingPlugins[0]}' plugin was`:` ['${missingPlugins.join("', '")}'] plugins were`;const baseWarning=`${context}${LOG_CONTEXT_SEPARATOR}${configurationStatus}, but${pluginsString} not configured to load.`;if(shouldAddMissingPlugins){return `${baseWarning} So, ${isSinglePlugin?'the plugin':'those plugins'} will be loaded automatically.`;}return `${baseWarning} Ignore if this was intentional. Otherwise, consider adding ${isSinglePlugin?'it':'them'} to the 'plugins' load API option.`;};const INVALID_POLYFILL_URL_WARNING=(context,customPolyfillUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided polyfill URL "${customPolyfillUrl}" is invalid. The default polyfill URL will be used instead.`;const PAGE_UNLOAD_ON_BEACON_DISABLED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}Page Unloaded event can only be tracked when the Beacon transport is active. Please enable "useBeacon" load API option.`;const UNKNOWN_PLUGINS_WARNING=(context,unknownPlugins)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring unknown plugins: ${unknownPlugins.join(', ')}.`;const UNAVAILABLE_PLUGINS_ERROR=(context,unavailablePlugins)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to load the following unavailable local plugins: ${unavailablePlugins.join(', ')}. Some features of the SDK may not work as expected. Make sure you are using the correct SDK bundle variant.`;const CUSTOM_INTEGRATION_CANNOT_BE_ADDED_ERROR=(context,destinationId)=>`${context}${LOG_CONTEXT_SEPARATOR}Cannot add custom integration for destination ID "${destinationId}" after the SDK is loaded.`;
645
+
646
+ const DEFAULT_INTEGRATIONS_CONFIG={All:true};
647
+
648
+ const CDN_INT_DIR='js-integrations';const CDN_PLUGINS_DIR='plugins';const URL_PATTERN=new RegExp('^(https?:\\/\\/)'+// protocol
649
+ '('+'((([a-zA-Z\\d]([a-zA-Z\\d-]*[a-zA-Z\\d])*)\\.)+[a-zA-Z]{2,}|'+// domain name
650
+ 'localhost|'+// localhost
651
+ '((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9]?)\\.){3}'+// OR IP (v4) address first 3 octets
652
+ '(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9]?))'+// last octet of IP address
653
+ ')'+'(\\:\\d+)?'+// port
654
+ '(\\/[-a-zA-Z\\d%_.~+]*)*'+// path
655
+ '(\\?[;&a-zA-Z\\d%_.~+=-]*)?'+// query string
656
+ '(\\#[-a-zA-Z\\d_]*)?$')// fragment locator
657
+ ;
658
+
659
+ const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';const CDN_ARCH_VERSION_DIR='v3';const DEFAULT_INTEGRATION_SDKS_URL=`${SDK_CDN_BASE_URL}/${CDN_ARCH_VERSION_DIR}/${BUILD_TYPE}/${CDN_INT_DIR}`;const DEFAULT_PLUGINS_URL=`${SDK_CDN_BASE_URL}/${CDN_ARCH_VERSION_DIR}/${BUILD_TYPE}/${CDN_PLUGINS_DIR}`;const DEFAULT_CONFIG_BE_URL='https://api.rudderstack.com';
660
+
661
+ const DEFAULT_STORAGE_ENCRYPTION_VERSION='v3';const DEFAULT_DATA_PLANE_EVENTS_TRANSPORT='xhr';const ConsentManagersToPluginNameMap={iubenda:'IubendaConsentManager',oneTrust:'OneTrustConsentManager',ketch:'KetchConsentManager',custom:'CustomConsentManager'};const StorageEncryptionVersionsToPluginNameMap={[DEFAULT_STORAGE_ENCRYPTION_VERSION]:'StorageEncryption',legacy:'StorageEncryptionLegacy'};const DataPlaneEventsTransportToPluginNameMap={[DEFAULT_DATA_PLANE_EVENTS_TRANSPORT]:'XhrQueue',beacon:'BeaconQueue'};const DEFAULT_DATA_SERVICE_ENDPOINT='rsaRequest';const METRICS_SERVICE_ENDPOINT='rsaMetrics';const CUSTOM_DEVICE_MODE_DESTINATION_DISPLAY_NAME='Custom Device Mode';
662
+
663
+ const defaultLoadOptions={configUrl:DEFAULT_CONFIG_BE_URL,loadIntegration:true,sessions:{autoTrack:true,timeout:DEFAULT_SESSION_TIMEOUT_MS,cutOff:{enabled:false}},sameSiteCookie:'Lax',polyfillIfRequired:true,integrations:DEFAULT_INTEGRATIONS_CONFIG,useBeacon:false,beaconQueueOptions:{},destinationsQueueOptions:{},queueOptions:{},lockIntegrationsVersion:true,lockPluginsVersion:true,uaChTrackLevel:'none',plugins:[],useGlobalIntegrationsConfigInEvents:false,bufferDataPlaneEventsUntilReady:false,dataPlaneEventsBufferTimeout:DEFAULT_DATA_PLANE_EVENTS_BUFFER_TIMEOUT_MS,storage:{encryption:{version:DEFAULT_STORAGE_ENCRYPTION_VERSION},migrate:false,cookie:{}},sendAdblockPage:false,sameDomainCookiesOnly:false,secureCookie:false,sendAdblockPageOptions:{},useServerSideCookies:false};const loadOptionsState=c(clone(defaultLoadOptions));
664
+
665
+ const DEFAULT_USER_SESSION_VALUES=deepFreeze({userId:'',userTraits:{},anonymousId:'',groupId:'',groupTraits:{},initialReferrer:'',initialReferringDomain:'',sessionInfo:{},authToken:null});const DEFAULT_RESET_OPTIONS=deepFreeze({entries:{userId:true,userTraits:true,groupId:true,groupTraits:true,sessionInfo:true,authToken:true,// These are not reset by default
666
+ anonymousId:false,initialReferrer:false,initialReferringDomain:false}});const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
667
+
668
+ const sessionState={userId:c(DEFAULT_USER_SESSION_VALUES.userId),userTraits:c(DEFAULT_USER_SESSION_VALUES.userTraits),anonymousId:c(DEFAULT_USER_SESSION_VALUES.anonymousId),groupId:c(DEFAULT_USER_SESSION_VALUES.groupId),groupTraits:c(DEFAULT_USER_SESSION_VALUES.groupTraits),initialReferrer:c(DEFAULT_USER_SESSION_VALUES.initialReferrer),initialReferringDomain:c(DEFAULT_USER_SESSION_VALUES.initialReferringDomain),sessionInfo:c(DEFAULT_USER_SESSION_VALUES.sessionInfo),authToken:c(DEFAULT_USER_SESSION_VALUES.authToken)};
669
+
670
+ const capabilitiesState={isOnline:c(true),storage:{isLocalStorageAvailable:c(false),isCookieStorageAvailable:c(false),isSessionStorageAvailable:c(false)},isBeaconAvailable:c(false),isLegacyDOM:c(false),isUaCHAvailable:c(false),isCryptoAvailable:c(false),isIE11:c(false),isAdBlockerDetectionInProgress:c(false),isAdBlocked:c(undefined),cspBlockedURLs:c([])};
671
+
672
+ const reportingState={isErrorReportingEnabled:c(false),isMetricsReportingEnabled:c(false),breadcrumbs:c([])};
673
+
674
+ const sourceConfigState=c(undefined);
675
+
676
+ const lifecycleState={activeDataplaneUrl:c(undefined),integrationsCDNPath:c(DEFAULT_INTEGRATION_SDKS_URL),pluginsCDNPath:c(DEFAULT_PLUGINS_URL),sourceConfigUrl:c(undefined),status:c(undefined),initialized:c(false),logLevel:c(POST_LOAD_LOG_LEVEL),loaded:c(false),readyCallbacks:c([]),writeKey:c(undefined),dataPlaneUrl:c(undefined),safeAnalyticsInstance:c(undefined)};
677
+
678
+ const consentsState={enabled:c(false),initialized:c(false),data:c({}),activeConsentManagerPluginName:c(undefined),preConsent:c({enabled:false}),postConsent:c({}),resolutionStrategy:c('and'),provider:c(undefined),metadata:c(undefined)};
679
+
680
+ const metricsState={retries:c(0),dropped:c(0),sent:c(0),queued:c(0),triggered:c(0),metricsServiceUrl:c(undefined)};
681
+
682
+ const getVariantValue=()=>{// For CDN builds, use runtime window.rudderAnalyticsBuildType
683
+ return BUILD_VARIANT;};const contextState={app:c({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION,installType:MODULE_TYPE}),traits:c(null),library:c({name:APP_NAME,version:APP_VERSION,snippetVersion:globalThis.RudderSnippetVersion,variant:getVariantValue()}),userAgent:c(null),device:c(null),network:c(null),os:c({name:'',version:''}),locale:c(null),screen:c({density:0,width:0,height:0,innerWidth:0,innerHeight:0}),'ua-ch':c(undefined),timezone:c(undefined)};
684
+
685
+ const nativeDestinationsState={configuredDestinations:c([]),activeDestinations:c([]),loadOnlyIntegrations:c({}),failedDestinations:c([]),loadIntegration:c(true),initializedDestinations:c([]),clientDestinationsReady:c(false),integrationsConfig:c({})};
686
+
687
+ const eventBufferState={toBeProcessedArray:c([]),readyCallbacksArray:c([])};
688
+
689
+ const pluginsState={ready:c(false),loadedPlugins:c([]),failedPlugins:c([]),pluginsToLoadFromConfig:c([]),activePlugins:c([]),totalPluginsToLoad:c(0)};
690
+
691
+ const storageState={encryptionPluginName:c(undefined),migrate:c(false),type:c(undefined),cookie:c(undefined),entries:c({}),trulyAnonymousTracking:c(false)};
692
+
693
+ const serverSideCookiesState={isEnabledServerSideCookies:c(false),dataServiceUrl:c(undefined)};
694
+
695
+ const dataPlaneEventsState={eventsQueuePluginName:c(undefined),deliveryEnabled:c(true)// Delivery should always happen
696
+ };
697
+
698
+ const autoTrackState={enabled:c(false),pageLifecycle:{enabled:c(false),pageViewId:c(undefined),pageLoadedTimestamp:c(undefined)}};
699
+
700
+ const defaultStateValues={capabilities:capabilitiesState,consents:consentsState,context:contextState,eventBuffer:eventBufferState,lifecycle:lifecycleState,loadOptions:loadOptionsState,metrics:metricsState,nativeDestinations:nativeDestinationsState,plugins:pluginsState,reporting:reportingState,session:sessionState,source:sourceConfigState,storage:storageState,serverCookies:serverSideCookiesState,dataPlaneEvents:dataPlaneEventsState,autoTrack:autoTrackState};const state={...clone(defaultStateValues)};
701
+
702
+ function getDefaultExportFromCjs (x) {
703
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
704
+ }
705
+
706
+ var errorStackParser$1 = {exports: {}};
707
+
708
+ var stackframe$1 = {exports: {}};
709
+
710
+ var stackframe=stackframe$1.exports;var hasRequiredStackframe;function requireStackframe(){if(hasRequiredStackframe)return stackframe$1.exports;hasRequiredStackframe=1;(function(module,exports$1){(function(root,factory){/* istanbul ignore next */{module.exports=factory();}})(stackframe,function(){function _isNumber(n){return !isNaN(parseFloat(n))&&isFinite(n);}function _capitalize(str){return str.charAt(0).toUpperCase()+str.substring(1);}function _getter(p){return function(){return this[p];};}var booleanProps=['isConstructor','isEval','isNative','isToplevel'];var numericProps=['columnNumber','lineNumber'];var stringProps=['fileName','functionName','source'];var arrayProps=['args'];var objectProps=['evalOrigin'];var props=booleanProps.concat(numericProps,stringProps,arrayProps,objectProps);function StackFrame(obj){if(!obj)return;for(var i=0;i<props.length;i++){if(obj[props[i]]!==undefined){this['set'+_capitalize(props[i])](obj[props[i]]);}}}StackFrame.prototype={getArgs:function(){return this.args;},setArgs:function(v){if(Object.prototype.toString.call(v)!=='[object Array]'){throw new TypeError('Args must be an Array');}this.args=v;},getEvalOrigin:function(){return this.evalOrigin;},setEvalOrigin:function(v){if(v instanceof StackFrame){this.evalOrigin=v;}else if(v instanceof Object){this.evalOrigin=new StackFrame(v);}else {throw new TypeError('Eval Origin must be an Object or StackFrame');}},toString:function(){var fileName=this.getFileName()||'';var lineNumber=this.getLineNumber()||'';var columnNumber=this.getColumnNumber()||'';var functionName=this.getFunctionName()||'';if(this.getIsEval()){if(fileName){return '[eval] ('+fileName+':'+lineNumber+':'+columnNumber+')';}return '[eval]:'+lineNumber+':'+columnNumber;}if(functionName){return functionName+' ('+fileName+':'+lineNumber+':'+columnNumber+')';}return fileName+':'+lineNumber+':'+columnNumber;}};StackFrame.fromString=function StackFrame$$fromString(str){var argsStartIndex=str.indexOf('(');var argsEndIndex=str.lastIndexOf(')');var functionName=str.substring(0,argsStartIndex);var args=str.substring(argsStartIndex+1,argsEndIndex).split(',');var locationString=str.substring(argsEndIndex+1);if(locationString.indexOf('@')===0){var parts=/@(.+?)(?::(\d+))?(?::(\d+))?$/.exec(locationString,'');var fileName=parts[1];var lineNumber=parts[2];var columnNumber=parts[3];}return new StackFrame({functionName:functionName,args:args||undefined,fileName:fileName,lineNumber:lineNumber||undefined,columnNumber:columnNumber||undefined});};for(var i=0;i<booleanProps.length;i++){StackFrame.prototype['get'+_capitalize(booleanProps[i])]=_getter(booleanProps[i]);StackFrame.prototype['set'+_capitalize(booleanProps[i])]=function(p){return function(v){this[p]=Boolean(v);};}(booleanProps[i]);}for(var j=0;j<numericProps.length;j++){StackFrame.prototype['get'+_capitalize(numericProps[j])]=_getter(numericProps[j]);StackFrame.prototype['set'+_capitalize(numericProps[j])]=function(p){return function(v){if(!_isNumber(v)){throw new TypeError(p+' must be a Number');}this[p]=Number(v);};}(numericProps[j]);}for(var k=0;k<stringProps.length;k++){StackFrame.prototype['get'+_capitalize(stringProps[k])]=_getter(stringProps[k]);StackFrame.prototype['set'+_capitalize(stringProps[k])]=function(p){return function(v){this[p]=String(v);};}(stringProps[k]);}return StackFrame;});})(stackframe$1);return stackframe$1.exports;}
711
+
712
+ var errorStackParser=errorStackParser$1.exports;var hasRequiredErrorStackParser;function requireErrorStackParser(){if(hasRequiredErrorStackParser)return errorStackParser$1.exports;hasRequiredErrorStackParser=1;(function(module,exports$1){(function(root,factory){/* istanbul ignore next */{module.exports=factory(requireStackframe());}})(errorStackParser,function ErrorStackParser(StackFrame){var FIREFOX_SAFARI_STACK_REGEXP=/(^|@)\S+:\d+/;var CHROME_IE_STACK_REGEXP=/^\s*at .*(\S+:\d+|\(native\))/m;var SAFARI_NATIVE_CODE_REGEXP=/^(eval@)?(\[native code])?$/;return {/**
713
+ * Given an Error object, extract the most information from it.
714
+ *
715
+ * @param {Error} error object
716
+ * @return {Array} of StackFrames
717
+ */parse:function ErrorStackParser$$parse(error){if(typeof error.stacktrace!=='undefined'||typeof error['opera#sourceloc']!=='undefined'){return this.parseOpera(error);}else if(error.stack&&error.stack.match(CHROME_IE_STACK_REGEXP)){return this.parseV8OrIE(error);}else if(error.stack){return this.parseFFOrSafari(error);}else {throw new Error('Cannot parse given Error object');}},// Separate line and column numbers from a string of the form: (URI:Line:Column)
718
+ extractLocation:function ErrorStackParser$$extractLocation(urlLike){// Fail-fast but return locations like "(native)"
719
+ if(urlLike.indexOf(':')===-1){return [urlLike];}var regExp=/(.+?)(?::(\d+))?(?::(\d+))?$/;var parts=regExp.exec(urlLike.replace(/[()]/g,''));return [parts[1],parts[2]||undefined,parts[3]||undefined];},parseV8OrIE:function ErrorStackParser$$parseV8OrIE(error){var filtered=error.stack.split('\n').filter(function(line){return !!line.match(CHROME_IE_STACK_REGEXP);},this);return filtered.map(function(line){if(line.indexOf('(eval ')>-1){// Throw away eval information until we implement stacktrace.js/stackframe#8
720
+ line=line.replace(/eval code/g,'eval').replace(/(\(eval at [^()]*)|(,.*$)/g,'');}var sanitizedLine=line.replace(/^\s+/,'').replace(/\(eval code/g,'(').replace(/^.*?\s+/,'');// capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
721
+ // case it has spaces in it, as the string is split on \s+ later on
722
+ var location=sanitizedLine.match(/ (\(.+\)$)/);// remove the parenthesized location from the line, if it was matched
723
+ sanitizedLine=location?sanitizedLine.replace(location[0],''):sanitizedLine;// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
724
+ // because this line doesn't have function name
725
+ var locationParts=this.extractLocation(location?location[1]:sanitizedLine);var functionName=location&&sanitizedLine||undefined;var fileName=['eval','<anonymous>'].indexOf(locationParts[0])>-1?undefined:locationParts[0];return new StackFrame({functionName:functionName,fileName:fileName,lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});},this);},parseFFOrSafari:function ErrorStackParser$$parseFFOrSafari(error){var filtered=error.stack.split('\n').filter(function(line){return !line.match(SAFARI_NATIVE_CODE_REGEXP);},this);return filtered.map(function(line){// Throw away eval information until we implement stacktrace.js/stackframe#8
726
+ if(line.indexOf(' > eval')>-1){line=line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,':$1');}if(line.indexOf('@')===-1&&line.indexOf(':')===-1){// Safari eval frames only have function names and nothing else
727
+ return new StackFrame({functionName:line});}else {var functionNameRegex=/((.*".+"[^@]*)?[^@]*)(?:@)/;var matches=line.match(functionNameRegex);var functionName=matches&&matches[1]?matches[1]:undefined;var locationParts=this.extractLocation(line.replace(functionNameRegex,''));return new StackFrame({functionName:functionName,fileName:locationParts[0],lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});}},this);},parseOpera:function ErrorStackParser$$parseOpera(e){if(!e.stacktrace||e.message.indexOf('\n')>-1&&e.message.split('\n').length>e.stacktrace.split('\n').length){return this.parseOpera9(e);}else if(!e.stack){return this.parseOpera10(e);}else {return this.parseOpera11(e);}},parseOpera9:function ErrorStackParser$$parseOpera9(e){var lineRE=/Line (\d+).*script (?:in )?(\S+)/i;var lines=e.message.split('\n');var result=[];for(var i=2,len=lines.length;i<len;i+=2){var match=lineRE.exec(lines[i]);if(match){result.push(new StackFrame({fileName:match[2],lineNumber:match[1],source:lines[i]}));}}return result;},parseOpera10:function ErrorStackParser$$parseOpera10(e){var lineRE=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;var lines=e.stacktrace.split('\n');var result=[];for(var i=0,len=lines.length;i<len;i+=2){var match=lineRE.exec(lines[i]);if(match){result.push(new StackFrame({functionName:match[3]||undefined,fileName:match[2],lineNumber:match[1],source:lines[i]}));}}return result;},// Opera 10.65+ Error.stack very similar to FF/Safari
728
+ parseOpera11:function ErrorStackParser$$parseOpera11(error){var filtered=error.stack.split('\n').filter(function(line){return !!line.match(FIREFOX_SAFARI_STACK_REGEXP)&&!line.match(/^Error created at/);},this);return filtered.map(function(line){var tokens=line.split('@');var locationParts=this.extractLocation(tokens.pop());var functionCall=tokens.shift()||'';var functionName=functionCall.replace(/<anonymous function(: (\w+))?>/,'$2').replace(/\([^)]*\)/g,'')||undefined;var argsRaw;if(functionCall.match(/\(([^)]*)\)/)){argsRaw=functionCall.replace(/^[^(]+\(([^)]*)\)$/,'$1');}var args=argsRaw===undefined||argsRaw==='[arguments not available]'?undefined:argsRaw.split(',');return new StackFrame({functionName:functionName,args:args,fileName:locationParts[0],lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});},this);}};});})(errorStackParser$1);return errorStackParser$1.exports;}
729
+
730
+ var errorStackParserExports = requireErrorStackParser();
731
+ const ErrorStackParser = /*@__PURE__*/getDefaultExportFromCjs(errorStackParserExports);
732
+
733
+ const GLOBAL_CODE='global code';const normalizeFunctionName=name=>{if(isDefined(name)){return /^global code$/i.test(name)?GLOBAL_CODE:name;}return name;};/**
734
+ * Takes a stacktrace.js style stackframe (https://github.com/stacktracejs/stackframe)
735
+ * and returns a Bugsnag compatible stackframe (https://docs.bugsnag.com/api/error-reporting/#json-payload)
736
+ * @param frame
737
+ * @returns
738
+ */const formatStackframe=frame=>{const f={file:frame.fileName,method:normalizeFunctionName(frame.functionName),lineNumber:frame.lineNumber,columnNumber:frame.columnNumber};// Some instances result in no file:
739
+ // - non-error exception thrown from global code in FF
740
+ // This adds one.
741
+ if(f.lineNumber&&f.lineNumber>-1&&!f.file&&!f.method){f.file=GLOBAL_CODE;}return f;};const ensureString=str=>isString(str)?str:'';function createException(errorClass,errorMessage,msgPrefix,stacktrace){return {errorClass:ensureString(errorClass),message:`${msgPrefix}${ensureString(errorMessage)}`,type:'browserjs',stacktrace:stacktrace.reduce((accum,frame)=>{const f=formatStackframe(frame);// don't include a stackframe if none of its properties are defined
742
+ try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch{return accum;}},[])};}const normalizeError=(maybeError,logger)=>{let error;if(isTypeOfError(maybeError)&&isString(getStacktrace(maybeError))){error=maybeError;}else {logger.warn(NON_ERROR_WARNING(ERROR_HANDLER,stringifyWithoutCircular(maybeError)));error=undefined;}return error;};const createBugsnagException=(error,msgPrefix)=>{try{const stacktrace=ErrorStackParser.parse(error);return createException(error.name,error.message,msgPrefix,stacktrace);}catch{return createException(error.name,error.message,msgPrefix,[]);}};
743
+
744
+ /**
745
+ * Utility to parse XHR JSON response
746
+ */const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
747
+
748
+ const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];const INTEGRATIONS_ERROR_CATEGORY='integrations';const SDK_ERROR_CATEGORY='sdk';const DEFAULT_ERROR_CATEGORY=SDK_ERROR_CATEGORY;
749
+
750
+ const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
751
+ * Utility to create request configuration based on default options
752
+ */const createXhrRequestOptions=(url,options,basicAuthHeader)=>{const requestOptions=mergeDeepRight(DEFAULT_XHR_REQUEST_OPTIONS,options||{});if(basicAuthHeader){requestOptions.headers=mergeDeepRight(requestOptions.headers,{Authorization:basicAuthHeader});}requestOptions.url=url;return requestOptions;};/**
753
+ * Utility implementation of XHR, fetch cannot be used as it requires explicit
754
+ * origin allowed values and not wildcard for CORS requests with credentials and
755
+ * this is not supported by our sourceConfig API
756
+ */const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
757
+ return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
758
+ const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url,xhr.responseText)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options,...(e?.type==='timeout'?{timedOut:true}:{})});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
759
+ // and the first call to the send method in legacy browsers
760
+ xhr.timeout=timeout;Object.keys(options.headers).forEach(headerName=>{if(options.headers[headerName]){xhr.setRequestHeader(headerName,options.headers[headerName]);}});try{xhr.send(payload);}catch(err){reject({error:getMutatedError(err,XHR_SEND_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,options.url)),xhr,options});}});
761
+
762
+ /**
763
+ * Service to handle data communication with APIs
764
+ */class HttpClient{constructor(logger){this.logger=logger;this.onError=this.onError.bind(this);}init(errorHandler){this.errorHandler=errorHandler;}/**
765
+ * Implement requests in a blocking way
766
+ */async getData(config){const{url,options,timeout,isRawResponse}=config;try{const data=await xhrRequest(createXhrRequestOptions(url,options,this.basicAuthHeader),timeout,this.logger);return {data:isRawResponse?data.response:responseTextToJson(data.response,this.onError),details:data};}catch(reason){return {data:undefined,details:reason};}}/**
767
+ * Implement requests in a non-blocking way
768
+ */getAsyncData(config){const{callback,url,options,timeout,isRawResponse}=config;const isFireAndForget=!isFunction(callback);xhrRequest(createXhrRequestOptions(url,options,this.basicAuthHeader),timeout,this.logger).then(data=>{if(!isFireAndForget){callback(isRawResponse?data.response:responseTextToJson(data.response,this.onError),data);}}).catch(data=>{if(!isFireAndForget){callback(undefined,data);}});}/**
769
+ * Handle errors
770
+ */onError(error,groupingHash){this.errorHandler?.onError({error,context:HTTP_CLIENT,groupingHash});}/**
771
+ * Set basic authentication header (eg writekey)
772
+ */setAuthHeader(value,noBtoa=false){const authVal=noBtoa?value:toBase64(`${value}:`);this.basicAuthHeader=`Basic ${authVal}`;}/**
773
+ * Clear basic authentication header
774
+ */resetAuthHeader(){this.basicAuthHeader=undefined;}}const defaultHttpClient=new HttpClient(defaultLogger);
775
+
776
+ const METRICS_PAYLOAD_VERSION='1';
777
+
778
+ // Errors from the below scripts are NOT allowed to reach Bugsnag
779
+ const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
780
+ ];const DEV_HOSTS=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
781
+ // Potential PII or sensitive data
782
+ const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
783
+ 'eventBuffer',// pre-load event buffer (may contain PII)
784
+ 'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='git+https://github.com/rudderlabs/rudder-sdk-js.git';const SOURCE_NAME='js';
785
+
786
+ const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
787
+ // Use source config URL here as it is very unlikely to be blocked by adblockers
788
+ // Only the extra query param should make it vulnerable to adblockers
789
+ // This will work even if the users proxies it.
790
+ // The edge case where this doesn't work is when HEAD method is not allowed by the server (user's)
791
+ 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
792
+ method:'HEAD',headers:{'Content-Type':undefined}},isRawResponse:true,callback:(result,details)=>{state.capabilities.isAdBlockerDetectionInProgress.value=false;// not ad blocked if the request is successful or it is not internally redirected on the client side
793
+ // Often adblockers instead of blocking the request, they redirect it to an internal URL
794
+ state.capabilities.isAdBlocked.value=details?.error!==undefined||details?.xhr?.responseURL!==url;}});}catch(err){// Reset the flag to prevent blocking future detection attempts
795
+ state.capabilities.isAdBlockerDetectionInProgress.value=false;// Re-throw the error to be handled by the caller
796
+ throw err;}};
797
+
798
+ const getErrInstance=(err,errorType)=>{switch(errorType){case ErrorType.UNHANDLEDEXCEPTION:{const{error}=err;return error||err;}case ErrorType.UNHANDLEDREJECTION:{return err.reason;}case ErrorType.HANDLEDEXCEPTION:default:return err;}};const createNewBreadcrumb=message=>({type:'manual',name:message,timestamp:new Date(),metaData:{}});/**
799
+ * A function to get the Bugsnag release stage for the current environment
800
+ * @param getHostName Optional function to get the hostname (primarily for testing)
801
+ * @returns 'development' if the host is empty (for file:// protocol etc.) or a dev host (localhost, 127.0.0.1, etc.), otherwise ''production'' (it'll be replaced with the actual release stage during the build)
802
+ */const getReleaseStage=(getHostName=()=>window.location.hostname)=>{const host=getHostName();return !host||host&&DEV_HOSTS.includes(host)?'development':'production';};const getAppStateForMetadata=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS);return stateStr!==null?JSON.parse(stateStr):{};};const getURLWithoutQueryString=()=>{const url=globalThis.location.href.split('?');return url[0];};const getUserDetails=(source,session,lifecycle,autoTrack)=>({id:`${source.value?.id??lifecycle.writeKey.value}..${session.sessionInfo.value.id??'NA'}..${autoTrack.pageLifecycle.pageViewId.value??'NA'}`,name:source.value?.name??'NA'});const getDeviceDetails=(locale,userAgent)=>({locale:locale.value??'NA',userAgent:userAgent.value??'NA',time:new Date()});const getBugsnagErrorEvent=(exception,errorState,state,groupingHash)=>{const{context,lifecycle,session,source,reporting,autoTrack}=state;const{app,locale,userAgent,timezone,screen,library}=context;return {payloadVersion:'5',notifier:{name:NOTIFIER_NAME,version:app.value.version,url:SDK_GITHUB_URL},events:[{exceptions:[clone(exception)],severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:app.value.version,releaseStage:getReleaseStage(),type:app.value.installType},device:getDeviceDetails(locale,userAgent),request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(reporting.breadcrumbs.value),context:exception.message,groupingHash,metaData:{app:{snippetVersion:library.value.snippetVersion},device:{...screen.value,timezone:timezone.value},// Add rest of the state groups as metadata
803
+ // so that they show up as separate tabs in the dashboard
804
+ ...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
805
+ * A function to check if adblockers are active. The promise's resolve function
806
+ * is invoked with true if adblockers are not detected and false otherwise.
807
+ * @param {ApplicationState} state The application state
808
+ * @param {IHttpClient} httpClient The HTTP client instance
809
+ * @param {Function} resolve The promise's resolve function
810
+ */const checkIfAdBlockersAreActive=(state,httpClient,resolve)=>{// Initiate ad blocker detection if not done previously and not already in progress.
811
+ if(isUndefined(state.capabilities.isAdBlocked.value)){if(state.capabilities.isAdBlockerDetectionInProgress.value===false){detectAdBlockers(httpClient);}// Wait for the detection to complete.
812
+ const detectionDisposer=m(()=>{if(isDefined(state.capabilities.isAdBlocked.value)){// If ad blocker is not detected, notify.
813
+ resolve(state.capabilities.isAdBlocked.value===false);// Cleanup the effect.
814
+ detectionDisposer();}});}else {// If ad blocker is not detected, notify.
815
+ resolve(state.capabilities.isAdBlocked.value===false);}};/**
816
+ * A function to determine whether the error should be promoted to notify or not.
817
+ * For plugin and integration errors from RS CDN, if it is due to CSP blocked URLs or AdBlockers,
818
+ * it will not be promoted to notify.
819
+ * If it is due to other reasons, it will be promoted to notify.
820
+ * @param {Error} exception The error object
821
+ * @param {ApplicationState} state The application state
822
+ * @param {IHttpClient} httpClient The HTTP client instance
823
+ * @returns A promise that resolves to a boolean indicating whether the error should be promoted to notify or not
824
+ */const checkIfAllowedToBeNotified=(exception,state,httpClient)=>{const errMsg=exception.message;return new Promise(resolve=>{// Filter out script load failures that are not from the RS CDN.
825
+ if(SCRIPT_LOAD_FAILURE_MESSAGES.some(regex=>regex.test(errMsg))){const extractedURL=/https?:\/\/[^\s"'(),;<>[\]{}]+/.exec(errMsg)?.[0];if(isString(extractedURL)){if(extractedURL.startsWith(SDK_CDN_BASE_URL)){// Filter out errors that are from CSP blocked URLs.
826
+ if(state.capabilities.cspBlockedURLs.value.includes(extractedURL)){resolve(false);}else {// Filter out errors if adblockers are detected.
827
+ checkIfAdBlockersAreActive(state,httpClient,resolve);}}else {// Filter out errors that are not from the RS CDN.
828
+ resolve(false);}}else {// Allow the error to be notified if no URL could be extracted from the error message
829
+ resolve(true);}}else {resolve(!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg)));}});};/**
830
+ * A function to get the directory name from a file path.
831
+ * @param {string} filePath The file path
832
+ * @returns The directory name or undefined if the file path is invalid
833
+ */const getDirectoryName=filePath=>{if(!filePath){return undefined;}const paths=filePath.split('/');return paths.at(-2);};/**
834
+ * A function to get the top stack path from the exception.
835
+ * @param {Exception} exception The exception object
836
+ * @returns The top stack path or undefined if the exception is invalid
837
+ */const getTopStackPath=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return undefined;}return errorOrigin;};/**
838
+ * A function to determine if the error is from Rudder SDK
839
+ * @param {Error} exception
840
+ * @returns
841
+ */const isSDKError=exception=>{const errorOrigin=getTopStackPath(exception);if(!errorOrigin){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const parentFolderName=getDirectoryName(errorOrigin);return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorCategory=(exception,category)=>{if(category){return category;}const errorOrigin=getTopStackPath(exception);const directoryName=getDirectoryName(errorOrigin);if(directoryName===CDN_INT_DIR){return INTEGRATIONS_ERROR_CATEGORY;}return DEFAULT_ERROR_CATEGORY;};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category},errors:payload};return stringifyWithoutCircular(data);};/**
842
+ * A function to get the grouping hash value to be used for the error event.
843
+ * If the grouping hash is an error instance, the normalized error message is used as the grouping hash.
844
+ * If the grouping hash is an empty string or not specified, the default grouping hash is used.
845
+ * If the grouping hash is a string, it is used as is.
846
+ * @param curErrGroupingHash The grouping hash value part of the error event
847
+ * @param defaultGroupingHash The default grouping hash value. It is the error message.
848
+ * @param logger The logger instance
849
+ * @returns The final grouping hash value to be used for the error event
850
+ */const getErrorGroupingHash=(curErrGroupingHash,defaultGroupingHash,logger)=>{let normalizedGroupingHash;if(!isDefined(curErrGroupingHash)){normalizedGroupingHash=defaultGroupingHash;}else if(isString(curErrGroupingHash)){normalizedGroupingHash=curErrGroupingHash;}else {const normalizedErrorInstance=normalizeError(curErrGroupingHash,logger);if(isDefined(normalizedErrorInstance)){normalizedGroupingHash=normalizedErrorInstance.message;}else {normalizedGroupingHash=defaultGroupingHash;}}return normalizedGroupingHash;};
851
+
852
+ /**
853
+ * A service to handle errors
854
+ */class ErrorHandler{initialized=false;// If no logger is passed errors will be thrown as unhandled error
855
+ constructor(httpClient,logger){this.httpClient=httpClient;this.logger=logger;}/**
856
+ * Initializes the error handler by attaching global error listeners.
857
+ * This method should be called once after construction.
858
+ */init(){if(this.initialized){return;}this.attachErrorListeners();this.initialized=true;}/**
859
+ * Attach error listeners to the global window object
860
+ */attachErrorListeners(){globalThis.addEventListener('error',event=>{this.onError({error:event,context:ERROR_HANDLER,errorType:ErrorType.UNHANDLEDEXCEPTION});});globalThis.addEventListener('unhandledrejection',event=>{this.onError({error:event,context:ERROR_HANDLER,errorType:ErrorType.UNHANDLEDREJECTION});});// Listen to CSP violations and add the blocked URL to the state
861
+ // if those URLs are from RS CDN.
862
+ document.addEventListener('securitypolicyviolation',event=>{const blockedURL=isString(event.blockedURI)?event.blockedURI:'';if(event.disposition==='enforce'&&blockedURL.startsWith(SDK_CDN_BASE_URL)&&!state.capabilities.cspBlockedURLs.value.includes(blockedURL)){state.capabilities.cspBlockedURLs.value=[...state.capabilities.cspBlockedURLs.value,blockedURL];}});}/**
863
+ * Handle errors
864
+ * @param errorInfo - The error information
865
+ * @param errorInfo.error - The error to handle
866
+ * @param errorInfo.context - The context of where the error occurred
867
+ * @param errorInfo.customMessage - The custom message of the error
868
+ * @param errorInfo.errorType - The type of the error (handled or unhandled)
869
+ * @param errorInfo.groupingHash - The grouping hash of the error
870
+ * @param errorInfo.category - The category of the error (sdk or integrations)
871
+ */async onError(errorInfo){try{const{error,context,customMessage,groupingHash,category}=errorInfo;const errorType=errorInfo.errorType??ErrorType.HANDLEDEXCEPTION;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.
872
+ // In case of NPM installations, the unhandled errors from the SDK cannot be identified
873
+ // and will NOT be reported unless they occur in plugins or integrations.
874
+ if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value){const isAllowed=await checkIfAllowedToBeNotified(bsException,state,this.httpClient);if(isAllowed){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};// This will allow custom grouping of errors.
875
+ // In case of NPM installations, the default grouping by surrounding code
876
+ // does not make sense as each user application is different and will create a lot of noise in the alerts.
877
+ // References:
878
+ // https://docs.bugsnag.com/platforms/javascript/customizing-error-reports/#groupinghash
879
+ // https://docs.bugsnag.com/product/error-grouping/#user_defined
880
+ const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,this.logger);// Get the final payload to be sent to the metrics service
881
+ const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);const errorCategory=getErrorCategory(bsException,category);// send it to metrics service
882
+ this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,errorCategory),sendRawData:true},isRawResponse:true});}}// Log handled errors and errors dispatched by the SDK
883
+ if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
884
+ this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
885
+ * Add breadcrumbs to add insight of a user's journey before an error
886
+ * occurred and send to external error monitoring service via a plugin
887
+ *
888
+ * @param {string} breadcrumb breadcrumbs message
889
+ */leaveBreadcrumb(breadcrumb){try{state.reporting.breadcrumbs.value=[...state.reporting.breadcrumbs.value,createNewBreadcrumb(breadcrumb)];}catch(err){this.onError({error:err,context:ERROR_HANDLER,customMessage:BREADCRUMB_ERROR,groupingHash:BREADCRUMB_ERROR});}}}// Note: Remember to call defaultErrorHandler.init() before using it
890
+ const defaultErrorHandler=new ErrorHandler(defaultHttpClient,defaultLogger);
891
+
892
+ // to next or return the value if it is the last one instead of an array per
893
+ // plugin that is the normal invoke
894
+ // TODO: add invoke method for extension point that we know only one plugin can be used. add invokeMultiple and invokeSingle methods
895
+ class PluginEngine{plugins=[];byName={};cache={};config={throws:true};constructor(logger,options={}){this.config={throws:true,...options};this.logger=logger;}register(plugin,state){if(!plugin.name){const errorMessage=PLUGIN_NAME_MISSING_ERROR(PLUGIN_ENGINE);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage,plugin);return;}}if(this.byName[plugin.name]){const errorMessage=PLUGIN_ALREADY_EXISTS_ERROR(PLUGIN_ENGINE,plugin.name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage);return;}}this.cache={};this.plugins=this.plugins.slice();let pos=this.plugins.length;this.plugins.forEach((pluginItem,index)=>{if(pluginItem.deps?.includes(plugin.name)){pos=Math.min(pos,index);}});this.plugins.splice(pos,0,plugin);this.byName[plugin.name]=plugin;if(isFunction(plugin.initialize)){plugin.initialize(state);}}unregister(name){const plugin=this.byName[name];if(!plugin){const errorMessage=PLUGIN_NOT_FOUND_ERROR(PLUGIN_ENGINE,name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage);return;}}const index=this.plugins.indexOf(plugin);if(index===-1){const errorMessage=PLUGIN_ENGINE_BUG_ERROR(PLUGIN_ENGINE,name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage);return;}}this.cache={};delete this.byName[name];this.plugins=this.plugins.slice();this.plugins.splice(index,1);}getPlugin(name){return this.byName[name];}getPlugins(extPoint){const lifeCycleName=extPoint??'.';if(!this.cache[lifeCycleName]){this.cache[lifeCycleName]=this.plugins.filter(plugin=>{if(plugin.deps?.some(dependency=>!this.byName[dependency])){// If deps not exist, then not load it.
896
+ const notExistDeps=plugin.deps.filter(dependency=>!this.byName[dependency]);this.logger.error(PLUGIN_DEPS_ERROR(PLUGIN_ENGINE,plugin.name,notExistDeps));return false;}return lifeCycleName==='.'?true:hasValueByPath(plugin,lifeCycleName);});}return this.cache[lifeCycleName];}// This method allows to process this.plugins so that it could
897
+ // do some unified pre-process before application starts.
898
+ processRawPlugins(callback){callback(this.plugins);this.cache={};}invoke(extPoint,allowMultiple=true,...args){let extensionPointName=extPoint;if(!extensionPointName){throw new Error(PLUGIN_EXT_POINT_MISSING_ERROR);}const noCall=extensionPointName.startsWith('!');const throws=this.config.throws??extensionPointName.endsWith('!');// eslint-disable-next-line unicorn/better-regex
899
+ extensionPointName=extensionPointName.replace(/(^!|!$)/g,'');if(!extensionPointName){throw new Error(PLUGIN_EXT_POINT_INVALID_ERROR);}const extensionPointNameParts=extensionPointName.split('.');extensionPointNameParts.pop();const pluginMethodPath=extensionPointNameParts.join('.');const pluginsToInvoke=allowMultiple?this.getPlugins(extensionPointName):[this.getPlugins(extensionPointName)[0]];return pluginsToInvoke.map(plugin=>{const method=getValueByPath(plugin,extensionPointName);if(!isFunction(method)||noCall){return method;}try{return method.apply(getValueByPath(plugin,pluginMethodPath),args);}catch(err){// When a plugin failed, doesn't break the app
900
+ if(throws){throw err;}else {this.logger.error(PLUGIN_INVOCATION_ERROR(PLUGIN_ENGINE,extensionPointName,plugin.name),err);}}return null;});}invokeSingle(extPoint,...args){return this.invoke(extPoint,false,...args)[0];}invokeMultiple(extPoint,...args){return this.invoke(extPoint,true,...args);}}const defaultPluginEngine=new PluginEngine(defaultLogger,{throws:true});
901
+
902
+ /**
903
+ * A function to filter and return non cloud mode destinations
904
+ * A destination is considered non cloud mode if it is not a cloud mode destination or if it is a hybrid mode destination
905
+ * @param destination
906
+ *
907
+ * @returns boolean
908
+ */const isNonCloudDestination=destination=>Boolean(destination.config.connectionMode!=='cloud'||destination.config.useNativeSDKToSend===true||// this is the older flag for hybrid mode destinations
909
+ destination.config.useNativeSDK===true);const isHybridModeDestination=destination=>Boolean(destination.config.connectionMode==='hybrid'||destination.config.useNativeSDKToSend===true);/**
910
+ * A function to filter and return non cloud mode destinations
911
+ * @param destinations
912
+ *
913
+ * @returns destinations
914
+ */const getNonCloudDestinations=destinations=>destinations.filter(isNonCloudDestination);/**
915
+ * A function to get the user friendly id for a destination
916
+ * Replaces all spaces with hyphens and appends the id to the display name
917
+ * @param displayName The display name of the destination
918
+ * @param id The id of the destination
919
+ *
920
+ * @returns the user friendly id
921
+ */const getDestinationUserFriendlyId=(displayName,id)=>`${displayName.replaceAll(' ','-')}___${id}`;
922
+
923
+ /**
924
+ * List of plugin names that are loaded as dynamic imports in modern builds
925
+ */const pluginNamesList=['BeaconQueue','CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];const deprecatedPluginsList=['Bugsnag','ErrorReporting'];
926
+
927
+ const CUSTOM_CONSENT_MANAGER_PLUGIN='CustomConsentManagerPlugin';
928
+
929
+ const DESTINATION_CONSENT_STATUS_ERROR$3=`Failed to determine the consent status for the destination. Please check the destination configuration and try again.`;
930
+
931
+ const pluginName$6='CustomConsentManager';const CustomConsentManager=()=>({name:pluginName$6,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$6];},consentManager:{init(state,logger){// Nothing to initialize
932
+ },updateConsentsInfo(state,storeManager,logger){// Nothing to update. Already provided by the user
933
+ },isDestinationConsented(state,destConfig,errorHandler,logger){if(!state.consents.initialized.value){return true;}const allowedConsentIds=state.consents.data.value.allowedConsentIds;try{const{consentManagement}=destConfig;// If the destination does not have consent management config, events should be sent.
934
+ if(!consentManagement){return true;}// Get the corresponding consents for the destination
935
+ const cmpConfig=consentManagement.find(c=>c.provider===state.consents.provider.value);// If there are no consents configured for the destination for the current provider, events should be sent.
936
+ if(!cmpConfig?.consents){return true;}const configuredConsents=cmpConfig.consents.map(c=>c.consent.trim()).filter(n=>n);const resolutionStrategy=cmpConfig.resolutionStrategy??state.consents.resolutionStrategy.value;// match the configured consents with user provided consents as per
937
+ // the configured resolution strategy
938
+ const matchPredicate=consent=>allowedConsentIds.includes(consent);switch(resolutionStrategy){case 'or':return configuredConsents.some(matchPredicate)||configuredConsents.length===0;case 'and':default:return configuredConsents.every(matchPredicate);}}catch(err){errorHandler?.onError({error:err,context:CUSTOM_CONSENT_MANAGER_PLUGIN,customMessage:DESTINATION_CONSENT_STATUS_ERROR$3});return true;}}}});
939
+
940
+ const externallyLoadedSessionStorageKeys={segment:'ajs_anonymous_id'};
941
+
942
+ const COOKIE_STORAGE='cookieStorage';const LOCAL_STORAGE='localStorage';const SESSION_STORAGE='sessionStorage';const MEMORY_STORAGE='memoryStorage';const NO_STORAGE='none';const STORAGE_TEST_COOKIE='test_rudder_cookie';const STORAGE_TEST_LOCAL_STORAGE='test_rudder_ls';const STORAGE_TEST_SESSION_STORAGE='test_rudder_ss';
943
+
944
+ const removeDuplicateSlashes=str=>str.replace(/\/{2,}/g,'/');/**
945
+ * Checks if provided url is valid or not
946
+ * @param url
947
+ * @returns true if `url` is valid and false otherwise
948
+ */const isValidURL=url=>{if(!isString(url)){return false;}try{// If URL is supported by the browser, we can use it to validate the URL
949
+ // Otherwise, we can at least check if the URL matches the pattern
950
+ if(isFunction(globalThis.URL)){// eslint-disable-next-line no-new
951
+ new URL(url);}return URL_PATTERN.test(url);}catch(e){return false;}};
952
+
953
+ /**
954
+ * This function is used to determine if the input status code is retryable.
955
+ * @param status - The status code.
956
+ * @returns True if the status code is not 4xx (except 429), false otherwise.
957
+ */const isErrRetryable=status=>{if(status===429){return true;}return !(status>=400&&status<500);};
958
+
959
+ /**
960
+ * Encode.
961
+ */const encode=(value,logger)=>{try{return encodeURIComponent(value);}catch(err){logger?.error(COOKIE_DATA_ENCODING_ERROR,err);return undefined;}};/**
962
+ * Decode
963
+ */const decode=value=>{try{return decodeURIComponent(value);}catch(err){// Do nothing as non-RS SDK cookies may not be URI encoded
964
+ return undefined;}};/**
965
+ * Parse cookie `str`
966
+ */const parse=str=>{const obj={};const pairs=str.split(/\s*;\s*/);let pair;if(!pairs[0]){return obj;}// TODO: Decode only the cookies that are needed by the SDK
967
+ pairs.forEach(pairItem=>{pair=pairItem.split('=');const keyName=pair[0]?decode(pair[0]):undefined;if(keyName){obj[keyName]=pair[1]?decode(pair[1]):undefined;}});return obj;};/**
968
+ * Set cookie `name` to `value`
969
+ */const set=(name,value,optionsConfig,logger)=>{const options={...(optionsConfig||{})};let cookieString=`${encode(name,logger)}=${encode(value,logger)}`;if(isNull(value)){options.maxage=-1;}if(options.maxage){options.expires=new Date(+new Date()+options.maxage);}if(options.path){cookieString+=`; path=${options.path}`;}if(options.domain){cookieString+=`; domain=${options.domain}`;}if(options.expires){cookieString+=`; expires=${options.expires.toUTCString()}`;}if(options.samesite){cookieString+=`; samesite=${options.samesite}`;}if(options.secure){cookieString+=`; secure`;}globalThis.document.cookie=cookieString;};/**
970
+ * Return all cookies
971
+ */const all=()=>{const cookieStringValue=globalThis.document.cookie;return parse(cookieStringValue);};/**
972
+ * Get cookie `name`
973
+ */const get=name=>all()[name];/**
974
+ * Set or get cookie `name` with `value` and `options` object
975
+ */// eslint-disable-next-line func-names
976
+ const cookie=function(name,value,options,logger){switch(arguments.length){case 4:case 3:case 2:return set(name,value,options,logger);case 1:if(name){return get(name);}return all();default:return all();}};
977
+
978
+ const userIdKey='rl_user_id';const userTraitsKey='rl_trait';const anonymousUserIdKey='rl_anonymous_id';const groupIdKey='rl_group_id';const groupTraitsKey='rl_group_trait';const pageInitialReferrerKey='rl_page_init_referrer';const pageInitialReferringDomainKey='rl_page_init_referring_domain';const sessionInfoKey='rl_session';const authTokenKey='rl_auth_token';const COOKIE_KEYS={userId:userIdKey,userTraits:userTraitsKey,anonymousId:anonymousUserIdKey,groupId:groupIdKey,groupTraits:groupTraitsKey,initialReferrer:pageInitialReferrerKey,initialReferringDomain:pageInitialReferringDomainKey,sessionInfo:sessionInfoKey,authToken:authTokenKey};const ENCRYPTION_PREFIX_V3='RS_ENC_v3_';
979
+
980
+ const encryptBrowser=value=>`${ENCRYPTION_PREFIX_V3}${toBase64(value)}`;const decryptBrowser=value=>{if(value?.startsWith(ENCRYPTION_PREFIX_V3)){return fromBase64(value.substring(ENCRYPTION_PREFIX_V3.length));}return value;};
981
+
982
+ const EVENT_PAYLOAD_SIZE_BYTES_LIMIT=32*1024;// 32 KB
983
+ const RETRY_REASON_CLIENT_NETWORK='client-network';const RETRY_REASON_CLIENT_TIMEOUT='client-timeout';const DEFAULT_RETRY_REASON=RETRY_REASON_CLIENT_NETWORK;
984
+
985
+ const isStorageQuotaExceeded=e=>{const matchingNames=['QuotaExceededError','NS_ERROR_DOM_QUOTA_REACHED'];// Everything except Firefox, Firefox
986
+ const matchingCodes=[22,1014];if(e instanceof DOMException){return matchingNames.includes(e.name)||matchingCodes.includes(e.code);}return false;};// TODO: also check for SecurityErrors
987
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage#exceptions
988
+ 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
989
+ 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
990
+ return false;};
991
+
992
+ const EVENT_PAYLOAD_SIZE_CHECK_FAIL_WARNING=(context,payloadSize,sizeLimit)=>`${context}${LOG_CONTEXT_SEPARATOR}The size of the event payload (${payloadSize} bytes) exceeds the maximum limit of ${sizeLimit} bytes. Events with large payloads may be dropped in the future. Please review your instrumentation to ensure that event payloads are within the size limit.`;const EVENT_PAYLOAD_SIZE_VALIDATION_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to validate event payload size. Please make sure that the event payload is within the size limit and is a valid JSON object.`;const QUEUE_UTILITIES='QueueUtilities';/**
993
+ * Utility to get the stringified event payload
994
+ * @param event RudderEvent object
995
+ * @param logger Logger instance
996
+ * @returns stringified event payload. Empty string if error occurs.
997
+ */const getDeliveryPayload=(event,logger)=>stringifyWithoutCircular(event,true,undefined,logger);/**
998
+ * Utility to validate final payload size before sending to server
999
+ * @param event RudderEvent object
1000
+ * @param logger Logger instance
1001
+ */const validateEventPayloadSize=(event,logger)=>{const payloadStr=getDeliveryPayload(event,logger);if(payloadStr){const payloadSize=payloadStr.length;if(payloadSize>EVENT_PAYLOAD_SIZE_BYTES_LIMIT){logger?.warn(EVENT_PAYLOAD_SIZE_CHECK_FAIL_WARNING(QUEUE_UTILITIES,payloadSize,EVENT_PAYLOAD_SIZE_BYTES_LIMIT));}}else {logger?.warn(EVENT_PAYLOAD_SIZE_VALIDATION_WARNING(QUEUE_UTILITIES));}};/**
1002
+ * Mutates the event and return final event for delivery
1003
+ * Updates certain parameters like sentAt timestamp, integrations config etc.
1004
+ * @param event RudderEvent object
1005
+ * @returns Final event ready to be delivered
1006
+ */const getFinalEventForDeliveryMutator=(event,currentTime)=>{const finalEvent=clone(event);// Update sentAt timestamp to the latest timestamp
1007
+ finalEvent.sentAt=currentTime;return finalEvent;};/**
1008
+ * Utility to get the storage type for events persistence
1009
+ * If local storage is available, use it; else, fall back to session storage; else, fall back to memory storage.
1010
+ * On some browsers, persistence storage is blocked for the JS SDK that is loaded from RS domains.
1011
+ * In such cases, we need to verify all the storage types and use the first one that is available.
1012
+ * @param logger Logger instance for logging storage availability warnings
1013
+ * @returns StorageType
1014
+ */const getStorageTypeForEventsPersistence=logger=>{if(isStorageAvailable(LOCAL_STORAGE,undefined,logger)){return LOCAL_STORAGE;}if(isStorageAvailable(SESSION_STORAGE,undefined,logger)){return SESSION_STORAGE;}// Don't use cookie storage as it can potentially cause overhead with the network requests.
1015
+ // Note that events will not be persisted across page reloads in this case.
1016
+ return MEMORY_STORAGE;};
1017
+
1018
+ const QueueStatuses={IN_PROGRESS:'inProgress',QUEUE:'queue',RECLAIM_START:'reclaimStart',RECLAIM_END:'reclaimEnd',ACK:'ack',BATCH_QUEUE:'batchQueue'};
1019
+
1020
+ const getSegmentAnonymousId=getStorageEngine=>{let anonymousId;/**
1021
+ * First check the local storage for anonymousId
1022
+ * Ref: https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/#identify
1023
+ */const lsEngine=getStorageEngine(LOCAL_STORAGE);if(lsEngine?.isEnabled){anonymousId=lsEngine.getItem(externallyLoadedSessionStorageKeys.segment);}// If anonymousId is not present in local storage and find it in cookies
1024
+ const csEngine=getStorageEngine(COOKIE_STORAGE);if(!anonymousId&&csEngine?.isEnabled){anonymousId=csEngine.getItem(externallyLoadedSessionStorageKeys.segment);}return anonymousId;};
1025
+
1026
+ const pluginName$5='ExternalAnonymousId';const ExternalAnonymousId=()=>({name:pluginName$5,initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$5];},storage:{getAnonymousId(getStorageEngine,options){let anonymousId;if(options?.autoCapture?.enabled&&options.autoCapture.source){const source=options.autoCapture.source.toLowerCase();if(!Object.keys(externallyLoadedSessionStorageKeys).includes(source)){return anonymousId;}// eslint-disable-next-line sonarjs/no-small-switch
1027
+ switch(source){case 'segment':anonymousId=getSegmentAnonymousId(getStorageEngine);break;}}return anonymousId;}}});
1028
+
1029
+ const IUBENDA_CONSENT_COOKIE_READ_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to read the consent cookie.`;const IUBENDA_CONSENT_COOKIE_PARSE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to parse the consent cookie.`;const DESTINATION_CONSENT_STATUS_ERROR$2=`Failed to determine the consent status for the destination. Please check the destination configuration and try again.`;
1030
+
1031
+ const IUBENDA_CONSENT_MANAGER_PLUGIN='IubendaConsentManagerPlugin';const IUBENDA_CONSENT_COOKIE_NAME_PATTERN=/^_iub_cs-\d+$/;
1032
+
1033
+ const getIubendaCookieName=logger=>{try{// Retrieve cookies as a string and split them into an array
1034
+ const cookies=document.cookie.split('; ');// Find the cookie that matches the iubenda cookie pattern
1035
+ const matchedCookie=cookies.find(cookie=>{const[name]=cookie.split('=');return IUBENDA_CONSENT_COOKIE_NAME_PATTERN.test((name||'').trim());});if(!matchedCookie){throw new Error('Iubenda Cookie not found with the specified pattern.');}// Extract and return the cookie name
1036
+ const[name]=matchedCookie.split('=');return name||'';}catch(err){logger?.error(IUBENDA_CONSENT_COOKIE_READ_ERROR(IUBENDA_CONSENT_MANAGER_PLUGIN),err);return '';}};/**
1037
+ * Gets the consent data from the Iubenda's consent cookie
1038
+ * @param storeManager Store manager instance
1039
+ * @param logger Logger instance
1040
+ * @returns Consent data from the consent cookie
1041
+ */const getIubendaConsentData=(storeManager,logger)=>{let rawConsentCookieData=null;try{const dataStore=storeManager?.setStore({id:IUBENDA_CONSENT_MANAGER_PLUGIN,name:IUBENDA_CONSENT_MANAGER_PLUGIN,type:COOKIE_STORAGE,errorHandler:storeManager?.errorHandler,logger:storeManager?.logger});rawConsentCookieData=dataStore?.engine.getItem(getIubendaCookieName(logger));}catch(err){logger?.error(IUBENDA_CONSENT_COOKIE_READ_ERROR(IUBENDA_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(isNullOrUndefined(rawConsentCookieData)){return undefined;}// Decode and parse the cookie data to JSON
1042
+ let consentCookieData;try{consentCookieData=JSON.parse(decodeURIComponent(rawConsentCookieData));}catch(err){logger?.error(IUBENDA_CONSENT_COOKIE_PARSE_ERROR(IUBENDA_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(!consentCookieData){return undefined;}// Convert the cookie data to consent data
1043
+ const consentPurposes=consentCookieData.purposes;return consentPurposes;};/**
1044
+ * Gets the consent data in the format expected by the application state
1045
+ * @param iubendaConsentData Consent data derived from the consent cookie
1046
+ * @returns Consent data
1047
+ */const getConsentData$1=iubendaConsentData=>{const allowedConsentIds=[];const deniedConsentIds=[];if(iubendaConsentData){Object.entries(iubendaConsentData).forEach(e=>{const purposeId=e[0];const isConsented=e[1];if(isConsented){allowedConsentIds.push(purposeId);}else {deniedConsentIds.push(purposeId);}});}return {allowedConsentIds,deniedConsentIds};};const updateConsentStateFromData$1=(state,iubendaConsentData)=>{const consentData=getConsentData$1(iubendaConsentData);state.consents.initialized.value=isDefined(iubendaConsentData);state.consents.data.value=consentData;};
1048
+
1049
+ const pluginName$4='IubendaConsentManager';const IubendaConsentManager=()=>({name:pluginName$4,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$4];},consentManager:{init(state,logger){// getIubendaUserConsentedPurposes returns current iubenda opted-in purposes
1050
+ // This will be helpful for debugging
1051
+ globalThis.getIubendaUserConsentedPurposes=()=>state.consents.data.value.allowedConsentIds?.slice();// getIubendaUserDeniedPurposes returns current Iubenda opted-out purposes
1052
+ // This will be helpful for debugging
1053
+ globalThis.getIubendaUserDeniedPurposes=()=>state.consents.data.value.deniedConsentIds?.slice();// updateIubendaConsent callback function to update current consent purpose state
1054
+ globalThis.updateIubendaConsent=iubendaConsentData=>{updateConsentStateFromData$1(state,iubendaConsentData);};},updateConsentsInfo(state,storeManager,logger){// retrieve consent data and update the state
1055
+ let iubendaConsentData;// From window
1056
+ if(!isUndefined(globalThis._iub.cs.consent.purposes)){iubendaConsentData=globalThis._iub.cs.consent.purposes;// From cookie
1057
+ }else {iubendaConsentData=getIubendaConsentData(storeManager,logger);}updateConsentStateFromData$1(state,iubendaConsentData);},isDestinationConsented(state,destConfig,errorHandler,logger){if(!state.consents.initialized.value){return true;}const allowedConsentIds=state.consents.data.value.allowedConsentIds;const matchPredicate=consent=>allowedConsentIds.includes(consent);try{const{consentManagement}=destConfig;// If the destination does not have consent management config, events should be sent.
1058
+ if(consentManagement){// Get the corresponding consents for the destination
1059
+ const cmpConfig=consentManagement.find(c=>c.provider===state.consents.provider.value);// If there are no consents configured for the destination for the current provider, events should be sent.
1060
+ if(!cmpConfig?.consents){return true;}const configuredConsents=cmpConfig.consents.map(c=>c.consent.trim()).filter(n=>n);const resolutionStrategy=cmpConfig.resolutionStrategy??state.consents.resolutionStrategy.value;// match the configured consents with user provided consents as per
1061
+ // the configured resolution strategy
1062
+ switch(resolutionStrategy){case 'or':return configuredConsents.some(matchPredicate)||configuredConsents.length===0;case 'and':default:return configuredConsents.every(matchPredicate);}}return true;}catch(err){errorHandler?.onError({error:err,context:IUBENDA_CONSENT_MANAGER_PLUGIN,customMessage:DESTINATION_CONSENT_STATUS_ERROR$2});return true;}}}});
1063
+
1064
+ const KETCH_CONSENT_COOKIE_READ_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to read the consent cookie.`;const KETCH_CONSENT_COOKIE_PARSE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to parse the consent cookie.`;const DESTINATION_CONSENT_STATUS_ERROR$1=`Failed to determine the consent status for the destination. Please check the destination configuration and try again.`;
1065
+
1066
+ const KETCH_CONSENT_MANAGER_PLUGIN='KetchConsentManagerPlugin';const KETCH_CONSENT_COOKIE_NAME_V1='_ketch_consent_v1_';
1067
+
1068
+ /**
1069
+ * Gets the consent data from the Ketch's consent cookie
1070
+ * @param storeManager Store manager instance
1071
+ * @param logger Logger instance
1072
+ * @returns Consent data from the consent cookie
1073
+ */const getKetchConsentData=(storeManager,logger)=>{let rawConsentCookieData=null;try{// Create a data store instance to read the consent cookie
1074
+ const dataStore=storeManager?.setStore({id:KETCH_CONSENT_MANAGER_PLUGIN,name:KETCH_CONSENT_MANAGER_PLUGIN,type:COOKIE_STORAGE,errorHandler:storeManager?.errorHandler,logger:storeManager?.logger});rawConsentCookieData=dataStore?.engine.getItem(KETCH_CONSENT_COOKIE_NAME_V1);}catch(err){logger?.error(KETCH_CONSENT_COOKIE_READ_ERROR(KETCH_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(isNullOrUndefined(rawConsentCookieData)){return undefined;}// Decode and parse the cookie data to JSON
1075
+ let consentCookieData;try{consentCookieData=JSON.parse(fromBase64(rawConsentCookieData));}catch(err){logger?.error(KETCH_CONSENT_COOKIE_PARSE_ERROR(KETCH_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(!consentCookieData){return undefined;}// Convert the cookie data to consent data
1076
+ const consentPurposes={};Object.entries(consentCookieData).forEach(pEntry=>{const purposeCode=pEntry[0];const purposeValue=pEntry[1];consentPurposes[purposeCode]=purposeValue?.status==='granted';});return consentPurposes;};/**
1077
+ * Gets the consent data in the format expected by the application state
1078
+ * @param ketchConsentData Consent data derived from the consent cookie
1079
+ * @returns Consent data
1080
+ */const getConsentData=ketchConsentData=>{const allowedConsentIds=[];const deniedConsentIds=[];if(ketchConsentData){Object.entries(ketchConsentData).forEach(e=>{const purposeCode=e[0];const isConsented=e[1];if(isConsented){allowedConsentIds.push(purposeCode);}else {deniedConsentIds.push(purposeCode);}});}return {allowedConsentIds,deniedConsentIds};};const updateConsentStateFromData=(state,ketchConsentData)=>{const consentData=getConsentData(ketchConsentData);state.consents.initialized.value=isDefined(ketchConsentData);state.consents.data.value=consentData;};
1081
+
1082
+ const pluginName$3='KetchConsentManager';const KetchConsentManager=()=>({name:pluginName$3,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$3];},consentManager:{init(state,logger){// getKetchUserConsentedPurposes returns current ketch opted-in purposes
1083
+ // This will be helpful for debugging
1084
+ globalThis.getKetchUserConsentedPurposes=()=>state.consents.data.value.allowedConsentIds?.slice();// getKetchUserDeniedPurposes returns current ketch opted-out purposes
1085
+ // This will be helpful for debugging
1086
+ globalThis.getKetchUserDeniedPurposes=()=>state.consents.data.value.deniedConsentIds?.slice();// updateKetchConsent callback function to update current consent purpose state
1087
+ // this will be called from ketch rudderstack plugin
1088
+ globalThis.updateKetchConsent=ketchConsentData=>{updateConsentStateFromData(state,ketchConsentData);};},updateConsentsInfo(state,storeManager,logger){// retrieve consent data and update the state
1089
+ let ketchConsentData;if(!isUndefined(globalThis.ketchConsent)){ketchConsentData=globalThis.ketchConsent;}else {ketchConsentData=getKetchConsentData(storeManager,logger);}updateConsentStateFromData(state,ketchConsentData);},isDestinationConsented(state,destConfig,errorHandler,logger){if(!state.consents.initialized.value){return true;}const allowedConsentIds=state.consents.data.value.allowedConsentIds;try{const{ketchConsentPurposes,consentManagement}=destConfig;const matchPredicate=consent=>allowedConsentIds.includes(consent);// Generic consent management
1090
+ if(consentManagement){// Get the corresponding consents for the destination
1091
+ const cmpConsents=consentManagement.find(c=>c.provider===state.consents.provider.value)?.consents;// If there are no consents configured for the destination for the current provider, events should be sent.
1092
+ if(!cmpConsents){return true;}const configuredConsents=cmpConsents.map(c=>c.consent.trim()).filter(n=>n);// match the configured consents with user provided consents as per
1093
+ // the configured resolution strategy
1094
+ switch(state.consents.resolutionStrategy.value){case 'or':return configuredConsents.some(matchPredicate)||configuredConsents.length===0;case 'and':default:return configuredConsents.every(matchPredicate);}// Legacy cookie consent management
1095
+ // TODO: To be removed once the source config API is updated to support generic consent management
1096
+ }else if(ketchConsentPurposes){const configuredConsents=ketchConsentPurposes.map(p=>p.purpose.trim()).filter(n=>n);// Check if any of the destination's mapped ketch purposes are consented by the user in the browser.
1097
+ return configuredConsents.some(matchPredicate)||configuredConsents.length===0;}// If there are no consents configured for the destination for the current provider, events should be sent.
1098
+ return true;}catch(err){errorHandler?.onError({error:err,context:KETCH_CONSENT_MANAGER_PLUGIN,customMessage:DESTINATION_CONSENT_STATUS_ERROR$1});return true;}}}});
1099
+
1100
+ const ONETRUST_ACCESS_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to access OneTrust SDK resources. Please ensure that the OneTrust SDK is loaded successfully before RudderStack SDK.`;const DESTINATION_CONSENT_STATUS_ERROR=`Failed to determine the consent status for the destination. Please check the destination configuration and try again.`;
1101
+
1102
+ const ONETRUST_CONSENT_MANAGER_PLUGIN='OneTrustConsentManagerPlugin';
1103
+
1104
+ const pluginName$2='OneTrustConsentManager';const OneTrustConsentManager=()=>({name:pluginName$2,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$2];},consentManager:{init(state,logger){// Nothing to initialize
1105
+ },updateConsentsInfo(state,storeManager,logger){if(!globalThis.OneTrust||!globalThis.OnetrustActiveGroups){logger?.error(ONETRUST_ACCESS_ERROR(ONETRUST_CONSENT_MANAGER_PLUGIN));state.consents.initialized.value=false;return;}// Get the groups (cookie categorization), user has created in OneTrust account.
1106
+ const oneTrustAllGroupsInfo=globalThis.OneTrust.GetDomainData().Groups;// OneTrustConsentManager SDK populates a data layer object OnetrustActiveGroups with
1107
+ // the cookie categories Ids that the user has consented to.
1108
+ // Eg: ',C0001,C0003,'
1109
+ // We split it and save it as an array.
1110
+ const allowedConsentIds=globalThis.OnetrustActiveGroups.split(',').filter(n=>n);const deniedConsentIds=[];oneTrustAllGroupsInfo.forEach(({CustomGroupId})=>{if(!allowedConsentIds.includes(CustomGroupId)){deniedConsentIds.push(CustomGroupId);}});state.consents.initialized.value=true;state.consents.data.value={allowedConsentIds,deniedConsentIds};},isDestinationConsented(state,destConfig,errorHandler,logger){if(!state.consents.initialized.value){return true;}const allowedConsentIds=state.consents.data.value.allowedConsentIds;try{// mapping of the destination with the consent group name
1111
+ const{oneTrustCookieCategories,consentManagement}=destConfig;const matchPredicate=consent=>allowedConsentIds.includes(consent);// Generic consent management
1112
+ if(consentManagement){// Get the corresponding consents for the destination
1113
+ const cmpConsents=consentManagement.find(c=>c.provider===state.consents.provider.value)?.consents;// If there are no consents configured for the destination for the current provider, events should be sent.
1114
+ if(!cmpConsents){return true;}const configuredConsents=cmpConsents.map(c=>c.consent.trim()).filter(n=>n);// match the configured consents with user provided consents as per
1115
+ // the configured resolution strategy
1116
+ switch(state.consents.resolutionStrategy.value){case 'or':return configuredConsents.some(matchPredicate)||configuredConsents.length===0;case 'and':default:return configuredConsents.every(matchPredicate);}// Legacy cookie consent management
1117
+ // TODO: To be removed once the source config API is updated to support generic consent management
1118
+ }else if(oneTrustCookieCategories){// Change the structure of oneTrustConsentGroup as an array and filter values if empty string
1119
+ // Eg:
1120
+ // ["Performance Cookies", "Functional Cookies"]
1121
+ const configuredConsents=oneTrustCookieCategories.map(c=>c.oneTrustCookieCategory.trim()).filter(n=>n);// Check if all the destination's mapped cookie categories are consented by the user in the browser.
1122
+ return configuredConsents.every(matchPredicate);}// If there are no consents configured for the destination for the current provider, events should be sent.
1123
+ return true;}catch(err){errorHandler?.onError({error:err,context:ONETRUST_CONSENT_MANAGER_PLUGIN,customMessage:DESTINATION_CONSENT_STATUS_ERROR});return true;}}}});
1124
+
1125
+ const pluginName$1='StorageEncryption';const StorageEncryption=()=>({name:pluginName$1,initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$1];},storage:{encrypt(value){return encryptBrowser(value);},decrypt(value){return decryptBrowser(value);}}});
1126
+
1127
+ const DEFAULT_RETRY_QUEUE_OPTIONS={maxRetryDelay:360000,minRetryDelay:1000,backoffFactor:2,maxAttempts:10,maxItems:100};const REQUEST_TIMEOUT_MS=30*1000;// 30 seconds
1128
+ const DATA_PLANE_API_VERSION='v1';const QUEUE_NAME='rudder';const XHR_QUEUE_PLUGIN='XhrQueuePlugin';
1129
+
1130
+ const EVENT_DELIVERY_FAILURE_ERROR_PREFIX=(context,originalErrorMsg)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to deliver event(s). Cause: ${originalErrorMsg}.`;
1131
+
1132
+ 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 logMessageOnFailure=(details,isRetryable,qItemProcessInfo,logger)=>{if(!logger){return;}let logMsg=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){logMsg=`${logMsg} The event(s) will be retried.`;if(qItemProcessInfo.retryAttemptNumber>0){logMsg=`${logMsg} Retry attempt ${qItemProcessInfo.retryAttemptNumber} of ${qItemProcessInfo.maxRetryAttempts}.`;}// Use warning for retryable failures that will be retried
1133
+ logger.warn(logMsg);}else {logger.error(`${logMsg} Retries exhausted (${qItemProcessInfo.maxRetryAttempts}). ${dropMsg}`);}}else {// Use error for non-retryable failures
1134
+ logger.error(`${logMsg} ${dropMsg}`);}};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
1135
+ // The same value is added in the event payload as well
1136
+ headers.SentAt=currentTime;// Add a header to indicate if the item has been reclaimed from
1137
+ // local storage
1138
+ if(qItemProcessInfo.reclaimed){headers['Rsa-Recovered']='true';}// Add retry headers if the item is being retried for delivery
1139
+ if(qItemProcessInfo.retryAttemptNumber>0){// The number of times this item has been attempted to retry
1140
+ headers['Rsa-Retry-Attempt']=qItemProcessInfo.retryAttemptNumber.toString();// The number of milliseconds since the last attempt
1141
+ headers['Rsa-Since-Last-Attempt']=qItemProcessInfo.timeSinceLastAttempt.toString();// The number of milliseconds since the first attempt
1142
+ headers['Rsa-Since-First-Attempt']=qItemProcessInfo.timeSinceFirstAttempt.toString();// The reason for the retry
1143
+ headers['Rsa-Retry-Reason']=qItemProcessInfo.retryReason;}return {data,headers,url};};
1144
+
1145
+ 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){if(this.nextId===Number.MAX_SAFE_INTEGER){this.nextId=1;}const id=(this.nextId++).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={};}}
1146
+
1147
+ 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}.`;
1148
+
1149
+ 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
1150
+ const DEFAULT_MAX_BATCH_ITEMS=100;const DEFAULT_BATCH_FLUSH_INTERVAL_MS=60*1000;// 1 minutes
1151
+ const BATCH_QUEUE_ITEM_TYPE='Batch';const SINGLE_QUEUE_ITEM_TYPE='Single';
1152
+
1153
+ const sortByTime=(a,b)=>a.time-b.time;const RETRY_QUEUE='RetryQueue';class RetryQueue{/**
1154
+ * Constructs a RetryQueue backed by localStorage
1155
+ *
1156
+ * @param {String} name The name of the queue. Will be used to find abandoned queues and retry their items
1157
+ * @param {QueueOpts} [options] Optional argument to override `maxItems`, `maxAttempts`, `minRetryDelay, `maxRetryDelay`, `backoffFactor` and `backoffJitter`.
1158
+ * @param {QueueProcessCallback} queueProcessCb The function to call in order to process an item added to the queue
1159
+ * @param {IStoreManager} storeManager The store manager instance to use
1160
+ * @param {StorageType} [storageType] The storage type to use. Defaults to LOCAL_STORAGE
1161
+ * @param {ILogger} [logger] The logger to use
1162
+ * @param {QueueBatchItemsSizeCalculatorCallback} [queueBatchItemsSizeCalculatorCb] The callback to use to calculate the size of items in the batch queue
1163
+ */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
1164
+ let timerScaleFactor=Math.max(options.timerScaleFactor??MIN_TIMER_SCALE_FACTOR,MIN_TIMER_SCALE_FACTOR);// Limit the timer scale factor to the maximum value
1165
+ timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
1166
+ 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
1167
+ this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType,errorHandler:this.storeManager.errorHandler,logger:this.storeManager.logger});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
1168
+ this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
1169
+ this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
1170
+ this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
1171
+ setStorageEntry(name,value){if(isNullOrUndefined(value)){this.store.remove(name);}else {this.store.set(name,value);}}/**
1172
+ * Stops processing the queue
1173
+ */stop(){this.schedule.cancelAll();this.scheduleTimeoutActive=false;}/**
1174
+ * Starts processing the queue
1175
+ */start(){if(this.scheduleTimeoutActive){this.stop();}this.scheduleTimeoutActive=true;this.scheduleFlushBatch();this.ack();this.checkReclaim();this.processHead();}/**
1176
+ * Configures the timeout handler for flushing the batch queue
1177
+ */scheduleFlushBatch(){if(this.batch.enabled&&this.batch?.flushInterval){if(this.flushBatchTaskId){this.schedule.cancel(this.flushBatchTaskId);}this.flushBatchTaskId=this.schedule.run(this.flushBatch,this.batch.flushInterval,ScheduleModes.ASAP);}}/**
1178
+ * Flushes the batch queue
1179
+ */flushBatch(isAccessible=true){if(!this.batchingInProgress){this.isPageAccessible=isAccessible;this.batchingInProgress=true;let batchQueue=this.getStorageEntry(QueueStatuses.BATCH_QUEUE)??[];if(batchQueue.length>0){batchQueue=batchQueue.slice(-batchQueue.length);const batchEntry=this.genQueueItem(batchQueue.map(queueItem=>queueItem.item),BATCH_QUEUE_ITEM_TYPE);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);this.pushToMainQueue(batchEntry);}this.batchingInProgress=false;// Re-schedule the flush task
1180
+ this.scheduleFlushBatch();}}/**
1181
+ * Decides whether to retry. Overridable.
1182
+ *
1183
+ * @param {Object} item The item being processed
1184
+ * @param {Number} attemptNumber The attemptNumber (1 for first retry)
1185
+ * @return {Boolean} Whether to requeue the message
1186
+ */shouldRetry(item,attemptNumber){return attemptNumber<=this.maxAttempts;}/**
1187
+ * Calculates the delay (in ms) for a retry attempt
1188
+ *
1189
+ * @param {Number} attemptNumber The attemptNumber (1 for first retry)
1190
+ * @return {Number} The delay in milliseconds to wait before attempting a retry
1191
+ */getDelay(attemptNumber){let ms=this.backoff.minRetryDelay*this.backoff.factor**attemptNumber;if(this.backoff.jitter){const rand=Math.random();const deviation=Math.floor(rand*this.backoff.jitter*ms);if(Math.floor(rand*10)<5){ms-=deviation;}else {ms+=deviation;}}return Number(Math.min(ms,this.backoff.maxRetryDelay).toPrecision(1));}enqueue(entry){let curEntry;if(this.batch.enabled&&entry.type===SINGLE_QUEUE_ITEM_TYPE){curEntry=this.handleNewItemForBatch(entry);}else {curEntry=entry;}// when batching is enabled, `curEntry` could be `undefined` if the batch criteria is not met
1192
+ if(curEntry){this.pushToMainQueue(curEntry);}}/**
1193
+ * Handles a new item added to the retry queue when batching is enabled
1194
+ * @param entry New item added to the retry queue
1195
+ * @returns Undefined or batch entry object
1196
+ */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
1197
+ 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
1198
+ 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
1199
+ this.scheduleFlushBatch();}this.batchingInProgress=false;}else {batchQueue.push(entry);}// update the batch queue
1200
+ 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();}}/**
1201
+ * Adds an item to the queue
1202
+ *
1203
+ * @param {Object} itemData The item to process
1204
+ */addItem(itemData){this.enqueue(this.genQueueItem(itemData));}/**
1205
+ * Generates a queue item
1206
+ * @param itemData Queue item data
1207
+ * @returns Queue item
1208
+ */genQueueItem(itemData,type=SINGLE_QUEUE_ITEM_TYPE,reclaimed){return {item:itemData,attemptNumber:0,time:this.schedule.now(),id:generateUUID(),type,...(isDefined(reclaimed)?{reclaimed}:{})};}/**
1209
+ * Adds an item to the retry queue
1210
+ *
1211
+ * @param {Object} qItem The item to process
1212
+ */requeue(qItem){const{attemptNumber,item,type,id,firstAttemptedAt,lastAttemptedAt,reclaimed,retryReason}=qItem;// Increment the attempt number as we're about to retry
1213
+ 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,retryReason});}}/**
1214
+ * Returns the information about whether the batch criteria is met or exceeded
1215
+ * @param batchItems Prospective batch items
1216
+ * @returns Batch dispatch info
1217
+ */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
1218
+ this.schedule.cancel(this.processId);// Pop the head off the queue
1219
+ let queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];const now=this.schedule.now();const toRun=[];// eslint-disable-next-line @typescript-eslint/no-unused-vars
1220
+ 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,retryReason:res?.retryReason??DEFAULT_RETRY_REASON});}};const enqueueItem=(el,id)=>{toRun.push({id,item:el.item,done:processItemCallback(el,id),attemptNumber:el.attemptNumber});};const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};let inProgressSize=Object.keys(inProgress).length;// eslint-disable-next-line no-plusplus
1221
+ while(queue.length>0&&queue[0].time<=now&&inProgressSize++<this.maxItems){const el=queue.shift();if(el){const id=generateUUID();// Save this to the in progress map
1222
+ 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
1223
+ try{const now=this.schedule.now();const inProgress=this.getStorageEntry(QueueStatuses.IN_PROGRESS)??{};const inProgressItem=inProgress[el.id];let firstAttemptedAt=now;let lastAttemptedAt=now;let reclaimed=false;let retryReason=DEFAULT_RETRY_REASON;if(inProgressItem){retryReason=inProgressItem.retryReason??retryReason;firstAttemptedAt=inProgressItem.firstAttemptedAt??firstAttemptedAt;lastAttemptedAt=inProgressItem.lastAttemptedAt??lastAttemptedAt;// Indicates if the item has been reclaimed from local storage
1224
+ reclaimed=inProgressItem.reclaimed??reclaimed;// Update the first attempted at timestamp for the in progress item
1225
+ inProgressItem.firstAttemptedAt=firstAttemptedAt;// Update the last attempted at to current timestamp for the in progress item
1226
+ inProgressItem.lastAttemptedAt=now;inProgress[el.id]=inProgressItem;this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);}// A decimal integer representing the milliseconds since the first attempt
1227
+ const timeSinceFirstAttempt=now-firstAttemptedAt;// A decimal integer representing the milliseconds since the last attempt
1228
+ const timeSinceLastAttempt=now-lastAttemptedAt;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,isPageAccessible:this.isPageAccessible,retryReason});}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
1229
+ el.done(err);}else {errMsg=`Retries exhausted (${this.maxAttempts}). The item will be dropped.`;// drop the event as we're unable to process it
1230
+ // after the max attempts are exhausted
1231
+ 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
1232
+ 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
1233
+ 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
1234
+ // After some point, we can remove this hack as most of the stale data will have been processed
1235
+ // and the new entries will have the type field set
1236
+ 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,retryReason:el.retryReason,// Mark the item as reclaimed from local storage
1237
+ 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#
1238
+ addConcatQueue(their.queue,0);// Process batch queue items
1239
+ if(this.batch.enabled){their.batchQueue.forEach(el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {this.enqueue({...el,id,retryReason:el.retryReason,// Mark the item as reclaimed from local storage
1240
+ 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
1241
+ addConcatQueue(their.batchQueue,0);}// if the queue is abandoned, all the in-progress are failed. retry them immediately and increment the attempt#
1242
+ 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
1243
+ this.clearQueueEntries(other,1);// process the new items we claimed
1244
+ 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
1245
+ 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
1246
+ 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
1247
+ 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
1248
+ // Hence, we need this backward compatibility check
1249
+ if(isFunction(storageEngine.keys)){storageKeys=storageEngine.keys();}else {for(let i=0;i<storageEngine.length;i++){const key=storageEngine.key(i);if(key){storageKeys.push(key);}}}storageKeys.forEach(k=>{const keyParts=k?k.split('.'):[];if(keyParts.length>=3&&keyParts[0]===name&&keyParts[1]!==this.id&&keyParts[2]===QueueStatuses.ACK){res.push(this.storeManager.setStore({id:keyParts[1],name,validKeys:QueueStatuses,type:LOCAL_STORAGE,errorHandler:this.storeManager.errorHandler,logger:this.storeManager.logger}));}});return res;};findOtherQueues(this.name).forEach(store=>{if(this.schedule.now()-store.get(QueueStatuses.ACK)<this.timeouts.reclaimTimeout){return;}tryReclaim(store);});this.schedule.run(this.checkReclaim,this.timeouts.reclaimTimer,ScheduleModes.RESCHEDULE);}clear(){this.schedule.cancelAll();this.setDefaultQueueEntries();}}
1250
+
1251
+ const pluginName='XhrQueue';const XhrQueue=()=>({name:pluginName,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName];},dataplaneEventsQueue:{/**
1252
+ * Initialize the queue for delivery
1253
+ * @param state Application state
1254
+ * @param httpClient http client instance
1255
+ * @param storeManager Store Manager instance
1256
+ * @param errorHandler Error handler instance
1257
+ * @param logger Logger instance
1258
+ * @returns RetryQueue instance
1259
+ */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
1260
+ `${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
1261
+ if(isUndefined(details?.error)){// null means item will not be processed further and will be removed from the queue (even from the storage)
1262
+ done(null);return;}const isRetryable=isErrRetryable(details?.xhr?.status??0);logMessageOnFailure(details,isRetryable,qItemProcessInfo,logger);if(isRetryable){let retryReason=DEFAULT_RETRY_REASON;if(details?.timedOut){retryReason=RETRY_REASON_CLIENT_TIMEOUT;}else if(isDefined(details?.xhr?.status)){retryReason=`server-${details.xhr.status}`;}done(details,{retryReason});}else {// null means item will not be processed further and will be removed from the queue (even from the storage)
1263
+ done(null);}}});},storeManager,getStorageTypeForEventsPersistence(logger),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
1264
+ return getBatchDeliveryPayload(events,currentTime,logger)?.length;});return eventsQueue;},/**
1265
+ * Add event to the queue for delivery
1266
+ * @param state Application state
1267
+ * @param eventsQueue RetryQueue instance
1268
+ * @param event RudderEvent object
1269
+ * @param errorHandler Error handler instance
1270
+ * @param logger Logger instance
1271
+ * @returns none
1272
+ */enqueue(state,eventsQueue,event,errorHandler,logger){// sentAt is only added here for the validation step
1273
+ // It'll be updated to the latest timestamp during actual delivery
1274
+ event.sentAt=getCurrentTimeFormatted();validateEventPayloadSize(event,logger);const dataplaneUrl=state.lifecycle.activeDataplaneUrl.value;const url=getDeliveryUrl(dataplaneUrl,event.type);// Other default headers are added by the HttpClient
1275
+ // Auth header is added during initialization
1276
+ const headers={// To maintain event ordering while using the HTTP API as per is documentation,
1277
+ // make sure to include anonymousId as a header
1278
+ AnonymousId:toBase64(event.anonymousId)};eventsQueue.addItem({url,headers,event});}}});
1279
+
1280
+ /**
1281
+ * Map plugin names to direct code imports from plugins package
1282
+ */const getBundledBuildPluginImports=()=>({BeaconQueue: {}.BeaconQueue,CustomConsentManager,DeviceModeDestinations: {}.DeviceModeDestinations,DeviceModeTransformation: {}.DeviceModeTransformation,ExternalAnonymousId,GoogleLinker: {}.GoogleLinker,IubendaConsentManager,KetchConsentManager,NativeDestinationQueue: {}.NativeDestinationQueue,OneTrustConsentManager,StorageEncryption,StorageEncryptionLegacy: {}.StorageEncryptionLegacy,StorageMigrator: {}.StorageMigrator,XhrQueue});
1283
+
1284
+ /**
1285
+ * Map of mandatory plugin names and direct imports
1286
+ */const getMandatoryPluginsMap=()=>({});/**
1287
+ * Map of optional plugin names and direct imports for legacy builds
1288
+ */const getOptionalPluginsMap=()=>{return getBundledBuildPluginImports();};/**
1289
+ * Map of optional plugin names and dynamic imports for modern builds
1290
+ */const getRemotePluginsMap=activePluginNames=>{{return {};}};const pluginsInventory={...getMandatoryPluginsMap(),...getOptionalPluginsMap()};const remotePluginsInventory=activePluginNames=>({...getRemotePluginsMap()});
1291
+
1292
+ // TODO: add retry mechanism for getting remote plugins
1293
+ // TODO: add timeout error mechanism for marking remote plugins that failed to load as failed in state
1294
+ class PluginsManager{constructor(engine,errorHandler,logger){this.engine=engine;this.errorHandler=errorHandler;this.logger=logger;this.onError=this.onError.bind(this);}/**
1295
+ * Orchestrate the plugin loading and registering
1296
+ */init(){state.lifecycle.status.value='pluginsLoading';// Expose pluginsCDNPath to global object, so it can be used in the promise that determines
1297
+ this.setActivePlugins();this.registerLocalPlugins();this.registerRemotePlugins();this.attachEffects();}/**
1298
+ * Update state based on plugin loaded status
1299
+ */// eslint-disable-next-line class-methods-use-this
1300
+ attachEffects(){m(()=>{const isAllPluginsReady=state.plugins.activePlugins.value.length===0||state.plugins.loadedPlugins.value.length+state.plugins.failedPlugins.value.length===state.plugins.totalPluginsToLoad.value;if(isAllPluginsReady){n(()=>{state.plugins.ready.value=true;// TODO: decide what to do if a plugin fails to load for any reason.
1301
+ // Should we stop here or should we progress?
1302
+ state.lifecycle.status.value='pluginsReady';});}});}/**
1303
+ * Determine the list of plugins that should be loaded based on sourceConfig & load options
1304
+ */// eslint-disable-next-line class-methods-use-this
1305
+ getPluginsToLoadBasedOnConfig(){// This contains the default plugins if load option has been omitted by user
1306
+ let pluginsToLoadFromConfig=state.plugins.pluginsToLoadFromConfig.value;if(!pluginsToLoadFromConfig){return [];}// Filter deprecated plugins
1307
+ pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>{if(deprecatedPluginsList.includes(pluginName)){this.logger.warn(DEPRECATED_PLUGIN_WARNING(PLUGINS_MANAGER,pluginName));return false;}return true;});const pluginGroupsToProcess=[{configurationStatus:()=>isDefined(state.dataPlaneEvents.eventsQueuePluginName.value),configurationStatusStr:'Data plane events delivery is enabled',activePluginName:state.dataPlaneEvents.eventsQueuePluginName.value,supportedPlugins:Object.values(DataPlaneEventsTransportToPluginNameMap),shouldAddMissingPlugins:true},{configurationStatus:()=>getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value).length>0,configurationStatusStr:'Device mode destinations are connected to the source',supportedPlugins:['DeviceModeDestinations','NativeDestinationQueue']},{configurationStatus:()=>getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value).some(destination=>destination.shouldApplyDeviceModeTransformation),configurationStatusStr:'Device mode transformations are enabled for at least one destination',supportedPlugins:['DeviceModeTransformation']},{configurationStatus:()=>isDefined(state.consents.activeConsentManagerPluginName.value),configurationStatusStr:'Consent management is enabled',activePluginName:state.consents.activeConsentManagerPluginName.value,supportedPlugins:Object.values(ConsentManagersToPluginNameMap)},{configurationStatus:()=>isDefined(state.storage.encryptionPluginName.value),configurationStatusStr:'Storage encryption is enabled',activePluginName:state.storage.encryptionPluginName.value,supportedPlugins:Object.values(StorageEncryptionVersionsToPluginNameMap)},{configurationStatus:()=>state.storage.migrate.value,configurationStatusStr:'Storage migration is enabled',supportedPlugins:['StorageMigrator']}];const addMissingPlugins=false;pluginGroupsToProcess.forEach(group=>{if(group.configurationStatus()){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(group.activePluginName?pluginName=>!(pluginName!==group.activePluginName&&group.supportedPlugins.includes(pluginName)):pluginName=>isDefined(pluginName)// pass through
1308
+ );this.addMissingPlugins(group,addMissingPlugins,pluginsToLoadFromConfig);}else {pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(group.basePlugins!==undefined?pluginName=>!(group.basePlugins.includes(pluginName)||group.supportedPlugins.includes(pluginName)):pluginName=>!group.supportedPlugins.includes(pluginName));}});return [...Object.keys(getMandatoryPluginsMap()),...pluginsToLoadFromConfig];}addMissingPlugins(group,addMissingPlugins,pluginsToLoadFromConfig){const shouldAddMissingPlugins=group.shouldAddMissingPlugins||addMissingPlugins;let pluginsToConfigure;if(group.activePluginName){pluginsToConfigure=[...(group.basePlugins||[]),group.activePluginName];}else {pluginsToConfigure=[...group.supportedPlugins];}const missingPlugins=pluginsToConfigure.filter(pluginName=>!pluginsToLoadFromConfig.includes(pluginName));if(missingPlugins.length>0){if(shouldAddMissingPlugins){pluginsToLoadFromConfig.push(...missingPlugins);}this.logger.warn(generateMisconfiguredPluginsWarning(PLUGINS_MANAGER,group.configurationStatusStr,missingPlugins,shouldAddMissingPlugins));}}/**
1309
+ * Determine the list of plugins that should be activated
1310
+ */setActivePlugins(){const pluginsToLoad=this.getPluginsToLoadBasedOnConfig();// Merging available mandatory and optional plugin name list
1311
+ const availablePlugins=[...Object.keys(pluginsInventory),...pluginNamesList];const activePlugins=[];const failedPlugins=[];pluginsToLoad.forEach(pluginName=>{if(availablePlugins.includes(pluginName)){activePlugins.push(pluginName);}else {failedPlugins.push(pluginName);}});if(failedPlugins.length>0){this.logger.warn(UNKNOWN_PLUGINS_WARNING(PLUGINS_MANAGER,failedPlugins));}n(()=>{state.plugins.totalPluginsToLoad.value=pluginsToLoad.length;state.plugins.activePlugins.value=activePlugins;state.plugins.failedPlugins.value=failedPlugins;});}/**
1312
+ * Register plugins that are direct imports to PluginEngine
1313
+ */registerLocalPlugins(){const unavailablePlugins=[];Object.entries(pluginsInventory).forEach(([pluginName,localPlugin])=>{if(state.plugins.activePlugins.value.includes(pluginName)){if(isFunction(localPlugin)){this.register([localPlugin()]);}else {unavailablePlugins.push(pluginName);}}});if(unavailablePlugins.length>0){state.plugins.failedPlugins.value=[...state.plugins.failedPlugins.value,...unavailablePlugins];this.logger.error(UNAVAILABLE_PLUGINS_ERROR(PLUGINS_MANAGER,unavailablePlugins));}}/**
1314
+ * Register plugins that are dynamic imports to PluginEngine
1315
+ */registerRemotePlugins(){const remotePluginsList=remotePluginsInventory(state.plugins.activePlugins.value);Promise.all(Object.keys(remotePluginsList).map(async remotePluginKey=>{await remotePluginsList[remotePluginKey]().then(remotePluginModule=>this.register([remotePluginModule.default()])).catch(err=>{// TODO: add retry here if dynamic import fails
1316
+ state.plugins.failedPlugins.value=[...state.plugins.failedPlugins.value,remotePluginKey];this.onError(err,`Failed to load plugin "${remotePluginKey}"`,err);});})).catch(err=>{this.onError(err);});}/**
1317
+ * Extension point invoke that allows multiple plugins to be registered to it with error handling
1318
+ */invokeMultiple(extPoint,...args){try{return this.engine.invokeMultiple(extPoint,...args);}catch(e){this.onError(e,extPoint);return [];}}/**
1319
+ * Extension point invoke that allows a single plugin to be registered to it with error handling
1320
+ */invokeSingle(extPoint,...args){try{return this.engine.invokeSingle(extPoint,...args);}catch(e){this.onError(e,extPoint);return null;}}/**
1321
+ * Plugin engine register with error handling
1322
+ */register(plugins){plugins.forEach(plugin=>{try{this.engine.register(plugin,state);}catch(e){state.plugins.failedPlugins.value=[...state.plugins.failedPlugins.value,plugin.name];this.onError(e,`Failed to register plugin "${plugin.name}"`);}});}// TODO: Implement reset API instead
1323
+ unregisterLocalPlugins(){Object.keys(pluginsInventory).forEach(pluginKey=>{try{this.engine.unregister(pluginKey);}catch(e){this.onError(e,`Failed to unregister plugin "${pluginKey}"`);}});}/**
1324
+ * Handle errors
1325
+ */onError(error,customMessage,groupingHash){this.errorHandler.onError({error,context:PLUGINS_MANAGER,customMessage,groupingHash});}}
1326
+
1327
+ const STORAGE_TEST_TOP_LEVEL_DOMAIN='__tld__';const CLIENT_DATA_STORE_COOKIE='clientDataInCookie';const CLIENT_DATA_STORE_LS='clientDataInLocalStorage';const CLIENT_DATA_STORE_MEMORY='clientDataInMemory';const CLIENT_DATA_STORE_SESSION='clientDataInSessionStorage';const USER_SESSION_KEYS=['userId','userTraits','anonymousId','groupId','groupTraits','initialReferrer','initialReferringDomain','sessionInfo','authToken'];
1328
+
1329
+ 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};
1330
+
1331
+ const legacyGetHostname=href=>{const l=document.createElement('a');l.href=href;return l.hostname;};/**
1332
+ * Levels returns all levels of the given url
1333
+ *
1334
+ * The method returns an empty array when the hostname is an ip.
1335
+ */const levelsFunc=url=>{// This is called before the polyfills load thus new URL cannot be used
1336
+ const host=typeof globalThis.URL!=='function'?legacyGetHostname(url):new URL(url).hostname;const parts=host?.split('.')??[];const last=parts[parts.length-1];const levels=[];// Ip address.
1337
+ if(parts.length===4&&last&&last===parseInt(last,10).toString()){return levels;}// Localhost.
1338
+ if(parts.length<=1){// Fix to support localhost
1339
+ if(parts[0]&&parts[0].indexOf('localhost')!==-1){return ['localhost'];}return levels;}// Create levels.
1340
+ for(let i=parts.length-2;i>=0;i-=1){levels.push(parts.slice(i).join('.'));}return levels;};/**
1341
+ * Get the top domain.
1342
+ *
1343
+ * The function constructs the levels of domain and attempts to set a global
1344
+ * cookie on each one when it succeeds it returns the top level domain.
1345
+ *
1346
+ * The method returns an empty string when the hostname is an ip.
1347
+ */const domain=url=>{const levels=levelsFunc(url);// Lookup the real top level one.
1348
+ // eslint-disable-next-line unicorn/no-for-loop
1349
+ for(let i=0;i<levels.length;i+=1){const domain=levels[i];const cname=STORAGE_TEST_TOP_LEVEL_DOMAIN;const opts={domain:`${domain.indexOf('localhost')!==-1?'':'.'}${domain}`};try{// Set cookie on domain
1350
+ cookie(cname,1,opts);// If successful
1351
+ if(cookie(cname)){// Remove cookie from domain
1352
+ cookie(cname,null,opts);return domain;}}catch{// Silently continue to next domain level if cookie access is restricted or setting fails
1353
+ // Best-effort cleanup to avoid leaking the test cookie
1354
+ try{cookie(cname,null,opts);}catch{// Ignore if we are unable to delete the cookie
1355
+ }}}return '';};
1356
+
1357
+ 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});
1358
+
1359
+ /**
1360
+ * A storage utility to retain values in memory via Storage interface
1361
+ */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);
1362
+
1363
+ var store$2 = {exports: {}};
1364
+
1365
+ var store$1=store$2.exports;var hasRequiredStore;function requireStore(){if(hasRequiredStore)return store$2.exports;hasRequiredStore=1;(function(module,exports$1){(function(global,factory){module.exports=factory();})(store$1,function(){function isJSON(obj){obj=JSON.stringify(obj);if(!/^\{[\s\S]*\}$/.test(obj)){return false;}return true;}function stringify(val){return val===undefined||typeof val==="function"?val+'':JSON.stringify(val);}function deserialize(value){if(typeof value!=='string'){return undefined;}try{return JSON.parse(value);}catch(e){return value;}}function isFunction(value){return {}.toString.call(value)==="[object Function]";}function isArray(value){return Object.prototype.toString.call(value)==="[object Array]";}// https://github.com/jaywcjlove/store.js/pull/8
1366
+ // Error: QuotaExceededError
1367
+ function dealIncognito(storage){var _KEY='_Is_Incognit',_VALUE='yes';try{// NOTE: set default storage when not passed in
1368
+ if(!storage){storage=window.localStorage;}storage.setItem(_KEY,_VALUE);storage.removeItem(_KEY);}catch(e){var inMemoryStorage={};inMemoryStorage._data={};inMemoryStorage.setItem=function(id,val){return inMemoryStorage._data[id]=String(val);};inMemoryStorage.getItem=function(id){return inMemoryStorage._data.hasOwnProperty(id)?inMemoryStorage._data[id]:undefined;};inMemoryStorage.removeItem=function(id){return delete inMemoryStorage._data[id];};inMemoryStorage.clear=function(){return inMemoryStorage._data={};};storage=inMemoryStorage;}finally{if(storage.getItem(_KEY)===_VALUE)storage.removeItem(_KEY);}return storage;}// deal QuotaExceededError if user use incognito mode in browser
1369
+ var storage=dealIncognito();function Store(){if(!(this instanceof Store)){return new Store();}}Store.prototype={set:function set(key,val){if(key&&!isJSON(key)){storage.setItem(key,stringify(val));}else if(isJSON(key)){for(var a in key)this.set(a,key[a]);}return this;},get:function get(key){// Return all entries if no key
1370
+ if(key===undefined){var ret={};this.forEach(function(key,val){return ret[key]=val;});return ret;}if(key.charAt(0)==='?'){return this.has(key.substr(1));}var args=arguments;if(args.length>1){var dt={};for(var i=0,len=args.length;i<len;i++){var value=deserialize(storage.getItem(args[i]));if(this.has(args[i])){dt[args[i]]=value;}}return dt;}return deserialize(storage.getItem(key));},clear:function clear(){storage.clear();return this;},remove:function remove(key){var val=this.get(key);storage.removeItem(key);return val;},has:function has(key){return {}.hasOwnProperty.call(this.get(),key);},keys:function keys(){var d=[];this.forEach(function(k){d.push(k);});return d;},forEach:function forEach(callback){for(var i=0,len=storage.length;i<len;i++){var key=storage.key(i);callback(key,this.get(key));}return this;},search:function search(str){var arr=this.keys(),dt={};for(var i=0,len=arr.length;i<len;i++){if(arr[i].indexOf(str)>-1)dt[arr[i]]=this.get(arr[i]);}return dt;},len:function len(){return storage.length;}};var _Store=null;function store(key,data){var argm=arguments;var dt=null;if(!_Store)_Store=Store();if(argm.length===0)return _Store.get();if(argm.length===1){if(typeof key==="string")return _Store.get(key);if(isJSON(key))return _Store.set(key);}if(argm.length===2&&typeof key==="string"){if(!data)return _Store.remove(key);if(data&&typeof data==="string")return _Store.set(key,data);if(data&&isFunction(data)){dt=null;dt=data(key,_Store.get(key));store.set(key,dt);}}if(argm.length===2&&isArray(key)&&isFunction(data)){for(var i=0,len=key.length;i<len;i++){dt=data(key[i],_Store.get(key[i]));store.set(key[i],dt);}}return store;}for(var a in Store.prototype)store[a]=Store.prototype[a];return store;});})(store$2);return store$2.exports;}
1371
+
1372
+ var storeExports = requireStore();
1373
+ const store = /*@__PURE__*/getDefaultExportFromCjs(storeExports);
1374
+
1375
+ // check if the get, set overloads and search methods are used at all
1376
+ // if we do, ensure we provide types to support overloads as per storejs docs
1377
+ // https://www.npmjs.com/package/storejs
1378
+ /**
1379
+ * A storage utility to persist values in localstorage via Storage interface
1380
+ */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
1381
+ 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
1382
+ keys(){return store.keys();}}const defaultLocalStorage=new LocalStorage(defaultLogger);
1383
+
1384
+ /**
1385
+ * A storage utility to persist values in SessionStorage via Storage interface
1386
+ */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
1387
+ 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);
1388
+
1389
+ /**
1390
+ * A storage utility to persist values in cookies via Storage interface
1391
+ */class CookieStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(logger){this.logger=logger;}configure(options){if(!this.options){this.options=getDefaultCookieOptions();}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
1392
+ 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
1393
+ clear(){// Not implemented
1394
+ // getting a list of all cookie storage keys and remove all values
1395
+ // sounds risky to do as it will take on all top domain cookies
1396
+ // better to explicitly clear specific ones if needed
1397
+ }key(index){const curKeys=this.keys();return curKeys[index]??null;}// eslint-disable-next-line class-methods-use-this
1398
+ keys(){return Object.keys(cookie());}}const defaultCookieStorage=new CookieStorage(defaultLogger);
1399
+
1400
+ /**
1401
+ * A utility to retrieve the storage singleton instance by type
1402
+ */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;}};/**
1403
+ * Configure cookie storage singleton
1404
+ */const configureCookieStorageEngine=options=>{const cookieStorageOptions=defaultCookieStorage.configure(options);// Update the state with the final cookie storage options
1405
+ state.storage.cookie.value={maxage:cookieStorageOptions.maxage,path:cookieStorageOptions.path,domain:cookieStorageOptions.domain,samesite:cookieStorageOptions.samesite,expires:cookieStorageOptions.expires,secure:cookieStorageOptions.secure};};/**
1406
+ * Configure local storage singleton
1407
+ */const configureLocalStorageEngine=options=>{defaultLocalStorage.configure(options);};/**
1408
+ * Configure in memory storage singleton
1409
+ */const configureInMemoryStorageEngine=options=>{defaultInMemoryStorage.configure(options);};/**
1410
+ * Configure session storage singleton
1411
+ */const configureSessionStorageEngine=options=>{defaultSessionStorage.configure(options);};/**
1412
+ * Configure all storage singleton instances
1413
+ */const configureStorageEngines=(cookieStorageOptions={},localStorageOptions={},inMemoryStorageOptions={},sessionStorageOptions={})=>{configureCookieStorageEngine(cookieStorageOptions);configureLocalStorageEngine(localStorageOptions);configureInMemoryStorageEngine(inMemoryStorageOptions);configureSessionStorageEngine(sessionStorageOptions);};
1414
+
1415
+ /**
1416
+ * Store Implementation with dedicated storage
1417
+ */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;}/**
1418
+ * Ensure the key is valid and with correct format
1419
+ */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
1420
+ let compoundKey;Object.values(validKeys).forEach(validKeyName=>{if(validKeyName===key){compoundKey=noCompoundKey?key:[name,id,key].join('.');}});return compoundKey;}/**
1421
+ * Switch to inMemoryEngine, bringing any existing data with.
1422
+ */swapQueueStoreToInMemoryEngine(){const{name,id,validKeys,noCompoundKey}=this;const inMemoryStorage=getStorageEngine(MEMORY_STORAGE);// grab existing data, but only for this page's queue instance, not all
1423
+ // better to keep other queues in localstorage to be flushed later
1424
+ // than to pull them into memory and remove them from durable storage
1425
+ Object.keys(validKeys).forEach(key=>{const value=this.get(validKeys[key]);const validKey=noCompoundKey?key:[name,id,key].join('.');inMemoryStorage.setItem(validKey,value);// TODO: are we sure we want to drop clientData
1426
+ // if cookies are not available and localstorage is full?
1427
+ this.remove(key);});this.engine=inMemoryStorage;}/**
1428
+ * Set value by key.
1429
+ */set(key,value){const validKey=this.createValidKey(key);if(!validKey){return;}try{// storejs that is used in localstorage engine already stringifies json
1430
+ this.engine.setItem(validKey,this.encrypt(stringifyWithoutCircular(value,false,[],this.logger)));}catch(err){if(isStorageQuotaExceeded(err)){this.logger.warn(STORAGE_QUOTA_EXCEEDED_WARNING(`Store ${this.id}`));// switch to inMemory engine
1431
+ this.swapQueueStoreToInMemoryEngine();// and save it there
1432
+ this.set(key,value);}else {const customMessage=STORE_DATA_SAVE_ERROR(key);this.onError(err,customMessage,customMessage);}}}/**
1433
+ * Get by Key.
1434
+ */get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)||decryptedValue===''){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
1435
+ return JSON.parse(decryptedValue);}catch(err){const encryptionPluginName=state.storage.encryptionPluginName.value;// Skip error reporting only when the encryption plugin is configured but failed to load
1436
+ const shouldReportError=!encryptionPluginName||!state.plugins.failedPlugins.value.includes(encryptionPluginName);if(shouldReportError){const customMessage=STORE_DATA_FETCH_ERROR(key);this.onError(err,customMessage,customMessage);}return null;}}/**
1437
+ * Remove by Key.
1438
+ */remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
1439
+ * Get original engine
1440
+ */getOriginalEngine(){return this.originalEngine;}/**
1441
+ * Decrypt values
1442
+ */decrypt(value){if(isNullOrUndefined(value)){return null;}return this.crypto(value,'decrypt');}/**
1443
+ * Encrypt value
1444
+ */encrypt(value){return this.crypto(value,'encrypt');}/**
1445
+ * Extension point to use with encryption plugins
1446
+ */crypto(value,mode){const noEncryption=!this.isEncrypted||!value||typeof value!=='string'||trim(value)==='';if(noEncryption){return value;}const extensionPointName=`storage.${mode}`;const formattedValue=this.pluginsManager?this.pluginsManager.invokeSingle(extensionPointName,value):value;return typeof formattedValue==='undefined'?value:formattedValue??'';}/**
1447
+ * Handle errors
1448
+ */onError(error,customMessage,groupingHash){this.errorHandler.onError({error,context:`Store ${this.id}`,customMessage,groupingHash});}}
1449
+
1450
+ const getStorageTypeFromPreConsentIfApplicable=(state,sessionKey)=>{let overriddenStorageType;if(state.consents.preConsent.value.enabled){switch(state.consents.preConsent.value.storage?.strategy){case 'none':overriddenStorageType=NO_STORAGE;break;case 'session':if(sessionKey!=='sessionInfo'){overriddenStorageType=NO_STORAGE;}break;case 'anonymousId':if(sessionKey!=='anonymousId'){overriddenStorageType=NO_STORAGE;}break;}}return overriddenStorageType;};
1451
+
1452
+ /**
1453
+ * A service to manage stores & available storage client configurations
1454
+ */class StoreManager{stores={};isInitialized=false;constructor(pluginsManager,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.pluginsManager=pluginsManager;}/**
1455
+ * Configure available storage client instances
1456
+ */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;}/**
1457
+ * Create store to persist data used by the SDK like session, used details etc
1458
+ */initClientDataStores(){this.initializeStorageState();// TODO: fill in extra config values and bring them in from StoreManagerOptions if needed
1459
+ // TODO: should we pass the keys for all in order to validate or leave free as v1.1?
1460
+ // Initializing all the enabled store because previous user data might be in different storage
1461
+ // that needs auto migration
1462
+ const storageTypes=[MEMORY_STORAGE,LOCAL_STORAGE,COOKIE_STORAGE,SESSION_STORAGE];storageTypes.forEach(storageType=>{if(getStorageEngine(storageType)?.isEnabled){this.setStore({id:storageClientDataStoreNameMap[storageType],name:storageClientDataStoreNameMap[storageType],isEncrypted:true,noCompoundKey:true,type:storageType,errorHandler:this.errorHandler,logger:this.logger});}});}initializeStorageState(){let globalStorageType=state.storage.type.value;let entriesOptions=state.loadOptions.value.storage?.entries;// Use the storage options from post consent if anything is defined
1463
+ const postConsentStorageOpts=state.consents.postConsent.value.storage;if(isDefined(postConsentStorageOpts?.type)||isDefined(postConsentStorageOpts?.entries)){globalStorageType=postConsentStorageOpts?.type;entriesOptions=postConsentStorageOpts?.entries;}let trulyAnonymousTracking=true;let storageEntries={};USER_SESSION_KEYS.forEach(sessionKey=>{const key=sessionKey;const storageKey=sessionKey;const configuredStorageType=entriesOptions?.[key]?.type;const preConsentStorageType=getStorageTypeFromPreConsentIfApplicable(state,sessionKey);// Storage type precedence order: pre-consent strategy > entry type > global type > default
1464
+ const storageType=preConsentStorageType??configuredStorageType??globalStorageType??DEFAULT_STORAGE_TYPE;const finalStorageType=this.getResolvedStorageTypeForEntry(storageType,sessionKey);if(finalStorageType!==NO_STORAGE){trulyAnonymousTracking=false;}storageEntries={...storageEntries,[sessionKey]:{type:finalStorageType,key:COOKIE_KEYS[storageKey]}};});n(()=>{state.storage.type.value=globalStorageType;state.storage.entries.value=storageEntries;state.storage.trulyAnonymousTracking.value=trulyAnonymousTracking;});}getResolvedStorageTypeForEntry(storageType,sessionKey){let finalStorageType=storageType;switch(storageType){case LOCAL_STORAGE:if(!getStorageEngine(LOCAL_STORAGE)?.isEnabled){finalStorageType=MEMORY_STORAGE;}break;case SESSION_STORAGE:if(!getStorageEngine(SESSION_STORAGE)?.isEnabled){finalStorageType=MEMORY_STORAGE;}break;case MEMORY_STORAGE:case NO_STORAGE:break;case COOKIE_STORAGE:default:// First try setting the storage to cookie else to local storage
1465
+ if(getStorageEngine(COOKIE_STORAGE)?.isEnabled){finalStorageType=COOKIE_STORAGE;}else if(getStorageEngine(LOCAL_STORAGE)?.isEnabled){finalStorageType=LOCAL_STORAGE;}else if(getStorageEngine(SESSION_STORAGE)?.isEnabled){finalStorageType=SESSION_STORAGE;}else {finalStorageType=MEMORY_STORAGE;}break;}if(finalStorageType!==storageType){this.logger.warn(STORAGE_UNAVAILABLE_WARNING(STORE_MANAGER,sessionKey,storageType,finalStorageType));}return finalStorageType;}/**
1466
+ * Create a new store
1467
+ */setStore(storeConfig){const storageEngine=getStorageEngine(storeConfig.type);this.stores[storeConfig.id]=new Store(storeConfig,storageEngine,this.pluginsManager);return this.stores[storeConfig.id];}/**
1468
+ * Retrieve a store
1469
+ */getStore(id){return this.stores[id];}}
1470
+
1471
+ const isValidSourceConfig=res=>isObjectLiteralAndNotNull(res)&&isObjectLiteralAndNotNull(res.source)&&!isNullOrUndefined(res.source.id)&&isObjectLiteralAndNotNull(res.source.config)&&Array.isArray(res.source.destinations);const isValidStorageType=storageType=>typeof storageType==='string'&&SUPPORTED_STORAGE_TYPES.includes(storageType);const getTopDomain=url=>{// Create a URL object
1472
+ const urlObj=new URL(url);// Extract the host and protocol
1473
+ const{host,protocol}=urlObj;// Split the host into parts
1474
+ const parts=host.split('.');let topDomain;// Handle different cases, especially for co.uk or similar TLDs
1475
+ if(parts.length>2){// Join the last two parts for the top-level domain
1476
+ topDomain=`${parts[parts.length-2]}.${parts[parts.length-1]}`;}else {// If only two parts or less, return as it is
1477
+ topDomain=host;}return {topDomain,protocol};};const getTopDomainUrl=url=>{const{topDomain,protocol}=getTopDomain(url);return `${protocol}//${topDomain}`;};const getDataServiceUrl=(endpoint,useExactDomain)=>{const url=useExactDomain?window.location.origin:getTopDomainUrl(window.location.href);const formattedEndpoint=endpoint.startsWith('/')?endpoint.substring(1):endpoint;return `${url}/${formattedEndpoint}`;};const isWebpageTopLevelDomain=providedDomain=>{const{topDomain}=getTopDomain(window.location.href);return topDomain===providedDomain;};
1478
+
1479
+ /**
1480
+ * Removes trailing slash from url
1481
+ * @param url
1482
+ * @returns url
1483
+ */const removeTrailingSlashes=url=>url?.endsWith('/')?removeTrailingSlashes(url.substring(0,url.length-1)):url;const getDomain=url=>{try{const urlObj=new URL(url);return urlObj.host;}catch(error){return null;}};/**
1484
+ * Get the referring domain from the referrer URL
1485
+ * @param referrer Page referrer
1486
+ * @returns Page referring domain
1487
+ */const getReferringDomain=referrer=>getDomain(referrer)??'';/**
1488
+ * Extracts UTM parameters from the URL
1489
+ * @param url Page URL
1490
+ * @returns UTM parameters
1491
+ */const extractUTMParameters=url=>{const result={};try{const urlObj=new URL(url);const UTM_PREFIX='utm_';urlObj.searchParams.forEach((value,sParam)=>{if(sParam.startsWith(UTM_PREFIX)){let utmParam=sParam.substring(UTM_PREFIX.length);// Not sure why we're doing this
1492
+ if(utmParam==='campaign'){utmParam='name';}result[utmParam]=value;}});}catch(error){// Do nothing
1493
+ }return result;};/**
1494
+ * To get the URL until the hash
1495
+ * @param url The input URL
1496
+ * @returns URL until the hash
1497
+ */const getUrlWithoutHash=url=>{let urlWithoutHash=url;try{const urlObj=new URL(url);urlWithoutHash=urlObj.origin+urlObj.pathname+urlObj.search;}catch(error){// Do nothing
1498
+ }return urlWithoutHash;};
1499
+
1500
+ const DEFAULT_PRE_CONSENT_STORAGE_STRATEGY='none';const DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE='immediate';
1501
+
1502
+ const isErrorReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.errors?.enabled===true;const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
1503
+
1504
+ /**
1505
+ * Validates and normalizes the consent options provided by the user
1506
+ * @param options Consent options provided by the user
1507
+ * @returns Validated and normalized consent options
1508
+ */const getValidPostConsentOptions=options=>{const validOptions={sendPageEvent:false,trackConsent:false,discardPreConsentEvents:false};if(isObjectLiteralAndNotNull(options)){const clonedOptions=clone(options);validOptions.storage=clonedOptions.storage;if(isNonEmptyObject(clonedOptions.integrations)){validOptions.integrations=clonedOptions.integrations;}validOptions.discardPreConsentEvents=clonedOptions.discardPreConsentEvents===true;validOptions.sendPageEvent=clonedOptions.sendPageEvent===true;validOptions.trackConsent=clonedOptions.trackConsent===true;if(isNonEmptyObject(clonedOptions.consentManagement)){// Override enabled value with the current state value
1509
+ validOptions.consentManagement=mergeDeepRight(clonedOptions.consentManagement,{enabled:state.consents.enabled.value});}}return validOptions;};/**
1510
+ * Validates if the input is a valid consents data
1511
+ * @param value Input consents data
1512
+ * @returns true if the input is a valid consents data else false
1513
+ */const isValidConsentsData=value=>isNonEmptyObject(value)||Array.isArray(value);/**
1514
+ * Retrieves the corresponding provider and plugin name of the selected consent manager from the supported consent managers
1515
+ * @param consentManagementOpts consent management options
1516
+ * @param logger logger instance
1517
+ * @returns Corresponding provider and plugin name of the selected consent manager from the supported consent managers
1518
+ */const getConsentManagerInfo=(consentManagementOpts,logger)=>{let{provider}=consentManagementOpts;const consentManagerPluginName=provider?ConsentManagersToPluginNameMap[provider]:undefined;if(provider&&!consentManagerPluginName){logger.error(UNSUPPORTED_CONSENT_MANAGER_ERROR(CONFIG_MANAGER,provider,ConsentManagersToPluginNameMap));// Reset the provider value
1519
+ provider=undefined;}return {provider,consentManagerPluginName};};/**
1520
+ * Validates and converts the consent management options into a normalized format
1521
+ * @param consentManagementOpts Consent management options provided by the user
1522
+ * @param logger logger instance
1523
+ * @returns An object containing the consent manager plugin name, initialized, enabled and consents data
1524
+ */const getConsentManagementData=(consentManagementOpts,logger)=>{let consentManagerPluginName;let allowedConsentIds=[];let deniedConsentIds=[];let initialized=false;let provider;let enabled=consentManagementOpts?.enabled===true;if(isNonEmptyObject(consentManagementOpts)&&enabled){// Get the corresponding plugin name of the selected consent manager from the supported consent managers
1525
+ ({provider,consentManagerPluginName}=getConsentManagerInfo(consentManagementOpts,logger));if(isValidConsentsData(consentManagementOpts.allowedConsentIds)){allowedConsentIds=consentManagementOpts.allowedConsentIds;initialized=true;}if(isValidConsentsData(consentManagementOpts.deniedConsentIds)){deniedConsentIds=consentManagementOpts.deniedConsentIds;initialized=true;}}const consentsData={allowedConsentIds,deniedConsentIds};// Enable consent management only if consent manager is supported
1526
+ enabled=enabled&&Boolean(consentManagerPluginName);return {provider,consentManagerPluginName,initialized,enabled,consentsData};};
1527
+
1528
+ /**
1529
+ * Determines the SDK URL
1530
+ * @returns sdkURL
1531
+ */const getSDKUrl=()=>{// First try the new method of getting the SDK URL
1532
+ // from the script tag
1533
+ const scriptTag=document.querySelector('script[data-rsa-write-key]');if(scriptTag&&scriptTag.dataset.rsaWriteKey===state.lifecycle.writeKey.value){return scriptTag.src;}// If the new method fails, try the old method
1534
+ // TODO: We need to remove this once all the customers upgrade to the
1535
+ // latest SDK loading snippet
1536
+ const scripts=document.getElementsByTagName('script');const sdkFileNameRegex=/(?:^|\/)rsa(\.min)?\.js$/;// eslint-disable-next-line no-restricted-syntax
1537
+ for(const script of scripts){const src=script.getAttribute('src');if(src&&sdkFileNameRegex.test(src)){return src;}}return undefined;};/**
1538
+ * Updates the reporting state variables from the source config data
1539
+ * @param res Source config
1540
+ * @param logger Logger instance
1541
+ */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=isErrorReportingEnabled(res.source.config)&&!isSDKRunningInChromeExtension();state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const getServerSideCookiesStateData=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let cookieOptions=storageOptsFromLoad?.cookie;let sscEnabled=false;let finalDataServiceUrl;if(useServerSideCookies){sscEnabled=useServerSideCookies;const providedCookieDomain=cookieOptions.domain??setCookieDomain;/**
1542
+ * Based on the following conditions, we decide whether to use the exact domain or not to determine the data service URL:
1543
+ * 1. If the cookie domain is provided and it is not a top-level domain, then use the exact domain
1544
+ * 2. If the sameDomainCookiesOnly flag is set to true, then use the exact domain
1545
+ */const useExactDomain=isDefined(providedCookieDomain)&&!isWebpageTopLevelDomain(removeLeadingPeriod(providedCookieDomain))||sameDomainCookiesOnly;const dataServiceUrl=getDataServiceUrl(dataServiceEndpoint??DEFAULT_DATA_SERVICE_ENDPOINT,useExactDomain);if(isValidURL(dataServiceUrl)){finalDataServiceUrl=removeTrailingSlashes(dataServiceUrl);const curHost=getDomain(window.location.href);const dataServiceHost=getDomain(dataServiceUrl);// If the current host is different from the data service host, then it is a cross-site request
1546
+ // For server-side cookies to work, we need to set the SameSite=None and Secure attributes
1547
+ // One round of cookie options manipulation is taking place here
1548
+ // Based on these(setCookieDomain/storage.cookie or sameDomainCookiesOnly) two load-options, final cookie options are set in the storage module
1549
+ // TODO: Refactor the cookie options manipulation logic in one place
1550
+ if(curHost!==dataServiceHost){cookieOptions={...cookieOptions,samesite:'None',secure:true};}/**
1551
+ * If the sameDomainCookiesOnly flag is not set and the cookie domain is provided(not top level domain),
1552
+ * and the data service host is different from the provided cookie domain, then we disable server-side cookies
1553
+ * ex: provided cookie domain: 'random.com', data service host: 'sub.example.com'
1554
+ */if(!sameDomainCookiesOnly&&useExactDomain&&dataServiceHost!==removeLeadingPeriod(providedCookieDomain)){sscEnabled=false;logger.warn(SERVER_SIDE_COOKIE_FEATURE_OVERRIDE_WARNING(CONFIG_MANAGER,providedCookieDomain,dataServiceHost));}}else {sscEnabled=false;}}return {sscEnabled,cookieOptions,finalDataServiceUrl};};const updateStorageStateFromLoadOptions=logger=>{const{storage:storageOptsFromLoad}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
1555
+ logger.warn(UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING(CONFIG_MANAGER,storageEncryptionVersion,StorageEncryptionVersionsToPluginNameMap,DEFAULT_STORAGE_ENCRYPTION_VERSION));storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}else if(isUndefined(storageEncryptionVersion)){storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}// Allow migration only if the configured encryption version is the default encryption version
1556
+ const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}const{sscEnabled,finalDataServiceUrl,cookieOptions}=getServerSideCookiesStateData(logger);n(()=>{state.storage.type.value=storageType;state.storage.cookie.value=cookieOptions;state.serverCookies.isEnabledServerSideCookies.value=sscEnabled;state.serverCookies.dataServiceUrl.value=finalDataServiceUrl;state.storage.encryptionPluginName.value=StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];state.storage.migrate.value=finalMigrationVal;});};const updateConsentsStateFromLoadOptions=logger=>{const{provider,consentManagerPluginName,initialized,enabled,consentsData}=getConsentManagementData(state.loadOptions.value.consentManagement,logger);// Pre-consent
1557
+ const preConsentOpts=state.loadOptions.value.preConsent;let storageStrategy=preConsentOpts?.storage?.strategy??DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;const StorageStrategies=['none','session','anonymousId'];if(isDefined(storageStrategy)&&!StorageStrategies.includes(storageStrategy)){storageStrategy=DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;logger.warn(UNSUPPORTED_PRE_CONSENT_STORAGE_STRATEGY(CONFIG_MANAGER,preConsentOpts?.storage?.strategy,DEFAULT_PRE_CONSENT_STORAGE_STRATEGY));}let eventsDeliveryType=preConsentOpts?.events?.delivery??DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE;const deliveryTypes=['immediate','buffer'];if(isDefined(eventsDeliveryType)&&!deliveryTypes.includes(eventsDeliveryType)){eventsDeliveryType=DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE;logger.warn(UNSUPPORTED_PRE_CONSENT_EVENTS_DELIVERY_TYPE(CONFIG_MANAGER,preConsentOpts?.events?.delivery,DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE));}n(()=>{state.consents.activeConsentManagerPluginName.value=consentManagerPluginName;state.consents.initialized.value=initialized;state.consents.enabled.value=enabled;state.consents.data.value=consentsData;state.consents.provider.value=provider;state.consents.preConsent.value={// Only enable pre-consent if it is explicitly enabled and
1558
+ // if it is not already initialized and
1559
+ // if consent management is enabled
1560
+ enabled:state.loadOptions.value.preConsent?.enabled===true&&initialized===false&&enabled===true,storage:{strategy:storageStrategy},events:{delivery:eventsDeliveryType}};});};/**
1561
+ * Determines the consent management state variables from the source config data
1562
+ * @param resp Source config response
1563
+ * @param logger Logger instance
1564
+ */const updateConsentsState=resp=>{let resolutionStrategy=state.consents.resolutionStrategy.value;let cmpMetadata;if(isObjectLiteralAndNotNull(resp.consentManagementMetadata)){if(state.consents.provider.value){resolutionStrategy=resp.consentManagementMetadata.providers.find(p=>p.provider===state.consents.provider.value)?.resolutionStrategy??state.consents.resolutionStrategy.value;}cmpMetadata=resp.consentManagementMetadata;}// If the provider is custom, then the resolution strategy is not applicable
1565
+ if(state.consents.provider.value==='custom'){resolutionStrategy=undefined;}n(()=>{state.consents.metadata.value=clone(cmpMetadata);state.consents.resolutionStrategy.value=resolutionStrategy;});};const updateDataPlaneEventsStateFromLoadOptions=logger=>{if(state.dataPlaneEvents.deliveryEnabled.value){const defaultEventsQueuePluginName='XhrQueue';let eventsQueuePluginName=defaultEventsQueuePluginName;if(state.loadOptions.value.useBeacon){if(state.capabilities.isBeaconAvailable.value){eventsQueuePluginName='BeaconQueue';}else {eventsQueuePluginName=defaultEventsQueuePluginName;logger.warn(UNSUPPORTED_BEACON_API_WARNING(CONFIG_MANAGER));}}n(()=>{state.dataPlaneEvents.eventsQueuePluginName.value=eventsQueuePluginName;});}};const getSourceConfigURL=(configUrl,writeKey,lockIntegrationsVersion,lockPluginsVersion,logger)=>{const defSearchParams=new URLSearchParams({p:MODULE_TYPE,v:APP_VERSION,build:BUILD_TYPE,writeKey,lockIntegrationsVersion:lockIntegrationsVersion.toString(),lockPluginsVersion:lockPluginsVersion.toString()});let origin=DEFAULT_CONFIG_BE_URL;let searchParams=defSearchParams;let pathname='/sourceConfig/';let hash='';if(isValidURL(configUrl)){const configUrlInstance=new URL(configUrl);if(!removeTrailingSlashes(configUrlInstance.pathname).endsWith('/sourceConfig')){configUrlInstance.pathname=`${removeTrailingSlashes(configUrlInstance.pathname)}/sourceConfig/`;}configUrlInstance.pathname=removeDuplicateSlashes(configUrlInstance.pathname);defSearchParams.forEach((value,key)=>{if(configUrlInstance.searchParams.get(key)===null){configUrlInstance.searchParams.set(key,value);}});origin=configUrlInstance.origin;pathname=configUrlInstance.pathname;searchParams=configUrlInstance.searchParams;hash=configUrlInstance.hash;}else {logger.warn(INVALID_CONFIG_URL_WARNING(CONFIG_MANAGER,configUrl));}return `${origin}${pathname}?${searchParams}${hash}`;};/**
1566
+ * Transforms destinations config from source config response to Destination format
1567
+ * @param destinations Array of destination items from config response
1568
+ * @returns Array of transformed Destination objects
1569
+ */const getDestinationsFromConfig=destinations=>destinations.map(destination=>({id:destination.id,displayName:destination.destinationDefinition.displayName,enabled:destination.enabled,config:destination.config,shouldApplyDeviceModeTransformation:destination.shouldApplyDeviceModeTransformation??false,propagateEventsUntransformedOnError:destination.propagateEventsUntransformedOnError??false,userFriendlyId:getDestinationUserFriendlyId(destination.destinationDefinition.displayName,destination.id),isCustomIntegration:destination.destinationDefinition.displayName===CUSTOM_DEVICE_MODE_DESTINATION_DISPLAY_NAME}));
1570
+
1571
+ /**
1572
+ * A function that determines the base URL for the integrations or plugins SDK
1573
+ * @param componentType The type of the component (integrations or plugins)
1574
+ * @param pathSuffix The path suffix to be appended to the base URL (js-integrations or plugins)
1575
+ * @param defaultComponentUrl The default URL to be used if the user has not provided a custom URL
1576
+ * @param currentSdkVersion The current version of the SDK
1577
+ * @param lockVersion Flag to lock the version of the component
1578
+ * @param urlFromLoadOptions The URL provided by the user in the load options
1579
+ * @param logger Logger instance
1580
+ * @returns The base URL for the integrations or plugins SDK
1581
+ */const getSDKComponentBaseURL=(componentType,pathSuffix,defaultComponentUrl,currentSdkVersion,lockVersion,urlFromLoadOptions,logger)=>{let sdkComponentBaseURL;// If the user has provided a custom URL, then validate, clean up and use it
1582
+ if(urlFromLoadOptions){if(!isValidURL(urlFromLoadOptions)){logger.error(COMPONENT_BASE_URL_ERROR(CONFIG_MANAGER,componentType,urlFromLoadOptions));return null;}sdkComponentBaseURL=removeTrailingSlashes(urlFromLoadOptions);}else {sdkComponentBaseURL=defaultComponentUrl;// We can automatically determine the base URL only for CDN installations
1583
+ if(state.context.app.value.installType==='cdn'){const sdkURL=getSDKUrl();if(sdkURL){// Extract the base URL from the core SDK file URL
1584
+ // and append the path suffix to it
1585
+ sdkComponentBaseURL=sdkURL.split('/').slice(0,-1).concat(pathSuffix).join('/');}}}// If the version needs to be locked, then replace the major version in the URL
1586
+ // with the current version of the SDK
1587
+ if(lockVersion){sdkComponentBaseURL=sdkComponentBaseURL.replace(new RegExp(`/${CDN_ARCH_VERSION_DIR}/${BUILD_TYPE}/${pathSuffix}$`),`/${currentSdkVersion}/${BUILD_TYPE}/${pathSuffix}`);}return sdkComponentBaseURL;};/**
1588
+ * A function that determines integration SDK loading path
1589
+ * @param currentSdkVersion Current SDK version
1590
+ * @param lockIntegrationsVersion Flag to lock the integrations version
1591
+ * @param integrationsUrlFromLoadOptions URL to load the integrations from as provided by the user
1592
+ * @param logger Logger instance
1593
+ * @returns
1594
+ */const getIntegrationsCDNPath=(currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions,logger)=>getSDKComponentBaseURL('integrations',CDN_INT_DIR,DEFAULT_INTEGRATION_SDKS_URL,currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions,logger);
1595
+
1596
+ class ConfigManager{constructor(httpClient,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.httpClient=httpClient;this.onError=this.onError.bind(this);this.processConfig=this.processConfig.bind(this);}attachEffects(){m(()=>{this.logger.setMinLogLevel(state.lifecycle.logLevel.value);});}/**
1597
+ * A function to validate, construct and store loadOption, lifecycle, source and destination
1598
+ * config related information in global state
1599
+ */init(){const{logLevel,configUrl,lockIntegrationsVersion,lockPluginsVersion,destSDKBaseURL,pluginsSDKBaseURL,integrations}=state.loadOptions.value;// determine the path to fetch integration SDK from
1600
+ const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,destSDKBaseURL,this.logger);if(isNull(intgCdnUrl)){return;}let pluginsCDNPath;this.attachEffects();state.lifecycle.activeDataplaneUrl.value=removeTrailingSlashes(state.lifecycle.dataPlaneUrl.value);updateStorageStateFromLoadOptions(this.logger);updateConsentsStateFromLoadOptions(this.logger);updateDataPlaneEventsStateFromLoadOptions(this.logger);// set application lifecycle state in global state
1601
+ n(()=>{state.lifecycle.integrationsCDNPath.value=intgCdnUrl;state.lifecycle.pluginsCDNPath.value=pluginsCDNPath;if(logLevel){state.lifecycle.logLevel.value=logLevel;}state.lifecycle.sourceConfigUrl.value=getSourceConfigURL(configUrl,state.lifecycle.writeKey.value,lockIntegrationsVersion,lockPluginsVersion,this.logger);state.metrics.metricsServiceUrl.value=`${state.lifecycle.activeDataplaneUrl.value}/${METRICS_SERVICE_ENDPOINT}`;// Data in the loadOptions state is already normalized
1602
+ state.nativeDestinations.loadOnlyIntegrations.value=integrations;});this.getConfig();}/**
1603
+ * Handle errors
1604
+ */onError(error,customMessage,groupingHash){this.errorHandler.onError({error,context:CONFIG_MANAGER,customMessage,groupingHash});}/**
1605
+ * A callback function that is executed once we fetch the source config response.
1606
+ * Use to construct and store information that are dependent on the sourceConfig.
1607
+ */processConfig(response,details){// TODO: add retry logic with backoff based on rejectionDetails.xhr.status
1608
+ // We can use isErrRetryable utility method
1609
+ if(!isDefined(response)){if(isDefined(details)){this.onError(details.error,SOURCE_CONFIG_FETCH_ERROR);}else {this.onError(new Error(SOURCE_CONFIG_FETCH_ERROR));}return;}let res;try{if(isString(response)){res=JSON.parse(response);}else {res=response;}}catch(err){this.onError(err,SOURCE_CONFIG_RESOLUTION_ERROR);return;}if(!isValidSourceConfig(res)){this.onError(new Error(SOURCE_CONFIG_RESOLUTION_ERROR));return;}// Log error and abort if source is disabled
1610
+ if(res.source.enabled===false){this.logger.error(SOURCE_DISABLED_ERROR);return;}// set the values in state for reporting slice
1611
+ updateReportingState(res);const nativeDestinations=res.source.destinations.length>0?getDestinationsFromConfig(res.source.destinations):[];// set in the state --> source, destination, lifecycle, reporting
1612
+ n(()=>{// set source related information in state
1613
+ state.source.value={config:res.source.config,name:res.source.name,id:res.source.id,workspaceId:res.source.workspaceId};// set device mode destination related information in state
1614
+ state.nativeDestinations.configuredDestinations.value=nativeDestinations;// set the desired optional plugins
1615
+ state.plugins.pluginsToLoadFromConfig.value=state.loadOptions.value.plugins??[];updateConsentsState(res);// set application lifecycle state
1616
+ state.lifecycle.status.value='configured';});}/**
1617
+ * A function to fetch source config either from /sourceConfig endpoint
1618
+ * or from getSourceConfig load option
1619
+ * @returns
1620
+ */getConfig(){const sourceConfigFunc=state.loadOptions.value.getSourceConfig;if(sourceConfigFunc){if(!isFunction(sourceConfigFunc)){this.logger.error(SOURCE_CONFIG_OPTION_ERROR(CONFIG_MANAGER));return;}// Fetch source config from the function
1621
+ const res=sourceConfigFunc();if(res instanceof Promise){res.then(pRes=>this.processConfig(pRes)).catch(err=>{this.onError(err,'SourceConfig');});}else {this.processConfig(res);}}else {// Fetch source configuration from the configured URL
1622
+ this.httpClient.getAsyncData({url:state.lifecycle.sourceConfigUrl.value,options:{headers:{'Content-Type':undefined}},callback:this.processConfig});}}}
1623
+
1624
+ /**
1625
+ * To get the timezone of the user
1626
+ *
1627
+ * @returns string
1628
+ */const getTimezone=()=>{// Not susceptible to super-linear backtracking
1629
+ // eslint-disable-next-line sonarjs/slow-regex
1630
+ const timezone=/([A-Z]+[+-]\d+)/.exec(new Date().toString());return timezone?.[1]?timezone[1]:'NA';};
1631
+
1632
+ /**
1633
+ * Get the referrer URL
1634
+ * @returns The referrer URL
1635
+ */const getReferrer=(getDocument=()=>document)=>getDocument()?.referrer||'$direct';/**
1636
+ * To get the canonical URL of the page
1637
+ * @returns canonical URL
1638
+ */const getCanonicalUrl=(getDocument=()=>document)=>{const docInstance=getDocument();const tags=docInstance.getElementsByTagName('link');let canonicalUrl='';for(let i=0;tags[i];i+=1){const tag=tags[i];if(tag.getAttribute('rel')==='canonical'&&!canonicalUrl){canonicalUrl=tag.getAttribute('href')??'';break;}}return canonicalUrl;};const getUserAgent=(getNavigator=()=>globalThis.navigator)=>{const navigator=getNavigator();if(isUndefined(navigator)){return null;}let{userAgent}=navigator;const{brave}=navigator;// For supporting Brave browser detection,
1639
+ // add "Brave/<version>" to the user agent with the version value from the Chrome component
1640
+ if(brave&&Object.getPrototypeOf(brave).isBrave){// Example:
1641
+ // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36
1642
+ const matchedArr=userAgent.match(/(chrome)\/([\w.]+)/i);if(matchedArr){userAgent=`${userAgent} Brave/${matchedArr[2]}`;}}return userAgent;};const getLanguage=(getNavigator=()=>globalThis.navigator)=>{const navigator=getNavigator();if(isUndefined(navigator)){return null;}return navigator.language??navigator.browserLanguage;};/**
1643
+ * Default page properties
1644
+ * @returns Default page properties
1645
+ */const getDefaultPageProperties=(getLocation=()=>globalThis.location,getDocument=()=>document)=>{const location=getLocation();const canonicalUrl=getCanonicalUrl(getDocument);let path=location.pathname;const{href:tabUrl}=location;let pageUrl=tabUrl;const{search}=location;// If valid canonical URL is provided use this as page URL.
1646
+ if(canonicalUrl){try{const urlObj=new URL(canonicalUrl);// If existing, query params of canonical URL will be used instead of the location.search ones
1647
+ if(urlObj.search===''){pageUrl=canonicalUrl+search;}else {pageUrl=canonicalUrl;}path=urlObj.pathname;}catch(err){// Do nothing
1648
+ }}const url=getUrlWithoutHash(pageUrl);const{title}=getDocument();const referrer=getReferrer(getDocument);return {path,referrer,referring_domain:getReferringDomain(referrer),search,title,url,tab_url:tabUrl};};
1649
+
1650
+ 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();});}};
1651
+
1652
+ 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
1653
+ // the polyfill service serves them under the same feature name, "URL".
1654
+ 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
1655
+ // the polyfill service serves them under the same feature name, "TextEncoder".
1656
+ 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".
1657
+ 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;};
1658
+
1659
+ const polyfillIoSdkUrl='https://polyfill-fastly.io/v3/polyfill.min.js';const POLYFILL_URL=`${polyfillIoSdkUrl}?version=3.111.0&features=${Object.keys(legacyJSEngineRequiredPolyfills).join('%2C')}`;const POLYFILL_LOAD_TIMEOUT=10*1000;// 10 seconds
1660
+ const POLYFILL_SCRIPT_ID='rudderstackPolyfill';
1661
+
1662
+ const hasCrypto=()=>!isNullOrUndefined(globalThis.crypto)&&isFunction(globalThis.crypto.getRandomValues);// eslint-disable-next-line compat/compat -- We are checking for the existence of navigator.userAgentData
1663
+ const hasUAClientHints=()=>!isNullOrUndefined(globalThis.navigator.userAgentData);const hasBeacon=()=>!isNullOrUndefined(globalThis.navigator.sendBeacon)&&isFunction(globalThis.navigator.sendBeacon);
1664
+
1665
+ 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;};
1666
+
1667
+ class CapabilitiesManager{constructor(httpClient,errorHandler,logger){this.httpClient=httpClient;this.errorHandler=errorHandler;this.logger=logger;this.externalSrcLoader=new ExternalSrcLoader(this.logger);this.onError=this.onError.bind(this);this.onReady=this.onReady.bind(this);}init(){this.prepareBrowserCapabilities();this.attachWindowListeners();}/**
1668
+ * Detect supported capabilities and set values in state
1669
+ */// eslint-disable-next-line class-methods-use-this
1670
+ detectBrowserCapabilities(){n(()=>{// Storage related details
1671
+ state.capabilities.storage.isCookieStorageAvailable.value=isStorageAvailable(COOKIE_STORAGE,getStorageEngine(COOKIE_STORAGE),this.logger);state.capabilities.storage.isLocalStorageAvailable.value=isStorageAvailable(LOCAL_STORAGE,undefined,this.logger);state.capabilities.storage.isSessionStorageAvailable.value=isStorageAvailable(SESSION_STORAGE,undefined,this.logger);// Browser feature detection details
1672
+ state.capabilities.isBeaconAvailable.value=hasBeacon();state.capabilities.isUaCHAvailable.value=hasUAClientHints();state.capabilities.isCryptoAvailable.value=hasCrypto();state.capabilities.isIE11.value=isIE11();state.capabilities.isOnline.value=globalThis.navigator.onLine;// Get page context details
1673
+ state.context.userAgent.value=getUserAgent();state.context.locale.value=getLanguage();state.context.screen.value=getScreenDetails();state.context.timezone.value=getTimezone();if(hasUAClientHints()){getUserAgentClientHint(uach=>{state.context['ua-ch'].value=uach;},state.loadOptions.value.uaChTrackLevel);}});// Ad blocker detection
1674
+ m(()=>{if(state.loadOptions.value.sendAdblockPage===true&&state.lifecycle.sourceConfigUrl.value!==undefined){detectAdBlockers(this.httpClient);}});}/**
1675
+ * Detect if polyfills are required and then load script from polyfill URL
1676
+ */prepareBrowserCapabilities(){state.capabilities.isLegacyDOM.value=isLegacyJSEngine();const customPolyfillUrl=state.loadOptions.value.polyfillURL;let polyfillUrl=POLYFILL_URL;if(isDefinedAndNotNull(customPolyfillUrl)){if(isValidURL(customPolyfillUrl)){polyfillUrl=customPolyfillUrl;}else {this.logger.warn(INVALID_POLYFILL_URL_WARNING(CAPABILITIES_MANAGER,customPolyfillUrl));}}const shouldLoadPolyfill=state.loadOptions.value.polyfillIfRequired&&state.capabilities.isLegacyDOM.value&&isValidURL(polyfillUrl);if(shouldLoadPolyfill){const isDefaultPolyfillService=polyfillUrl!==state.loadOptions.value.polyfillURL;if(isDefaultPolyfillService){// write key specific callback
1677
+ // NOTE: we're not putting this into RudderStackGlobals as providing the property path to the callback function in the polyfill URL is not possible
1678
+ const polyfillCallbackName=`RS_polyfillCallback_${state.lifecycle.writeKey.value}`;const polyfillCallback=()=>{this.onReady();// Remove the entry from window so we don't leave room for calling it again
1679
+ delete globalThis[polyfillCallbackName];};globalThis[polyfillCallbackName]=polyfillCallback;polyfillUrl=`${polyfillUrl}&callback=${polyfillCallbackName}`;}this.externalSrcLoader.loadJSFile({url:polyfillUrl,id:POLYFILL_SCRIPT_ID,async:true,timeout:POLYFILL_LOAD_TIMEOUT,callback:scriptId=>{if(!scriptId){this.onError(new Error(POLYFILL_SCRIPT_LOAD_ERROR(POLYFILL_SCRIPT_ID,polyfillUrl)));}else if(!isDefaultPolyfillService){this.onReady();}}});}else {this.onReady();}}/**
1680
+ * Attach listeners to window to observe event that update capabilities state values
1681
+ */attachWindowListeners(){globalThis.addEventListener('offline',()=>{state.capabilities.isOnline.value=false;});globalThis.addEventListener('online',()=>{state.capabilities.isOnline.value=true;});globalThis.addEventListener('resize',debounce(()=>{state.context.screen.value=getScreenDetails();},this));}/**
1682
+ * Set the lifecycle status to next phase
1683
+ */// eslint-disable-next-line class-methods-use-this
1684
+ onReady(){this.detectBrowserCapabilities();state.lifecycle.status.value='browserCapabilitiesReady';}/**
1685
+ * Handles error
1686
+ * @param error The error object
1687
+ */onError(error,groupingHash){this.errorHandler.onError({error,context:CAPABILITIES_MANAGER,groupingHash});}}
1688
+
1689
+ const CHANNEL='web';// These are the top-level elements in the standard RudderStack event spec
1690
+ const TOP_LEVEL_ELEMENTS=['integrations','anonymousId','originalTimestamp'];// Reserved elements in the context of standard RudderStack event spec
1691
+ // Typically, these elements are not allowed to be overridden by the user
1692
+ const CONTEXT_RESERVED_ELEMENTS=['library','consentManagement','userAgent','ua-ch','screen'];// Reserved elements in the standard RudderStack event spec
1693
+ const RESERVED_ELEMENTS=['id','anonymous_id','user_id','sent_at','timestamp','received_at','original_timestamp','event','event_text','channel','context_ip','context_request_ip','context_passed_ip','group_id','previous_id'];
1694
+
1695
+ /**
1696
+ * A function to check given value is a number or not
1697
+ * @param num input value
1698
+ * @returns boolean
1699
+ */const isNumber=num=>typeof num==='number'&&!Number.isNaN(num);/**
1700
+ * A function to check given number has minimum length or not
1701
+ * @param minimumLength minimum length
1702
+ * @param num input number
1703
+ * @returns boolean
1704
+ */const hasMinLength=(minimumLength,num)=>num.toString().length>=minimumLength;/**
1705
+ * A function to check given value is a positive integer or not
1706
+ * @param num input value
1707
+ * @returns boolean
1708
+ */const isPositiveInteger=num=>isNumber(num)&&num>=0&&Number.isInteger(num);
1709
+
1710
+ const MIN_SESSION_ID_LENGTH=10;const isCutOffTimeExceeded=sessionInfo=>{const{cutOff}=sessionInfo;const timestamp=Date.now();return Boolean(cutOff?.enabled&&cutOff.expiresAt&&timestamp>cutOff.expiresAt);};/**
1711
+ * A function to validate whether the current auto tracking session has expired or not.
1712
+ * It checks for the current session expiry and the cut off time expiry.
1713
+ * @param sessionInfo session info
1714
+ * @returns boolean
1715
+ */const hasSessionExpired=sessionInfo=>{const isCurrentSessionExpired=Boolean(!sessionInfo.expiresAt||Date.now()>sessionInfo.expiresAt);return isCurrentSessionExpired||isCutOffTimeExceeded(sessionInfo);};/**
1716
+ * A function to generate session id
1717
+ * @returns number
1718
+ */const generateSessionId=()=>Date.now();/**
1719
+ * Function to validate user provided sessionId
1720
+ * @param {number} sessionId
1721
+ * @param logger logger
1722
+ * @returns
1723
+ */const isManualSessionIdValid=(sessionId,logger)=>{if(!sessionId||!isPositiveInteger(sessionId)||!hasMinLength(MIN_SESSION_ID_LENGTH,sessionId)){logger.warn(INVALID_SESSION_ID_WARNING(USER_SESSION_MANAGER,sessionId,MIN_SESSION_ID_LENGTH));return false;}return true;};const getCutOffExpirationTimestamp=cutOff=>{if(!cutOff?.enabled){return undefined;}return cutOff.expiresAt??(isPositiveInteger(cutOff.duration)?Date.now()+cutOff.duration:undefined);};/**
1724
+ * A function to generate new auto tracking session
1725
+ * @param sessionInfo session info
1726
+ * @returns SessionInfo
1727
+ */const generateAutoTrackingSession=sessionInfo=>{const{timeout,cutOff}=sessionInfo;const timestamp=Date.now();return {id:timestamp,// set the current timestamp
1728
+ expiresAt:timestamp+timeout,// set the expiry time of the session
1729
+ timeout,autoTrack:true,...(cutOff&&{cutOff})};};/**
1730
+ * A function to generate new manual tracking session
1731
+ * @param id Provided sessionId
1732
+ * @param logger Logger module
1733
+ * @returns SessionInfo
1734
+ */const generateManualTrackingSession=(id,logger)=>{const sessionId=isManualSessionIdValid(id,logger)?id:generateSessionId();return {id:sessionId,sessionStart:undefined,manualTrack:true};};const isStorageTypeValidForStoringData=storageType=>Boolean(storageType===COOKIE_STORAGE||storageType===LOCAL_STORAGE||storageType===SESSION_STORAGE||storageType===MEMORY_STORAGE);/**
1735
+ * Generate a new anonymousId
1736
+ * @returns string anonymousID
1737
+ */const generateAnonymousId=()=>generateUUID();const getFinalResetOptions=options=>{// Legacy behavior: toggle only anonymousId without mutating defaults
1738
+ if(isBoolean(options)){const{entries,...rest}=DEFAULT_RESET_OPTIONS;return {...rest,entries:{...entries,anonymousId:options}};}// Override any defaults with the user provided options
1739
+ if(isObjectLiteralAndNotNull(options)&&isObjectLiteralAndNotNull(options.entries)){return mergeDeepRight(DEFAULT_RESET_OPTIONS,options);}return {...DEFAULT_RESET_OPTIONS};};
1740
+
1741
+ /**
1742
+ * To get the page properties for context object
1743
+ * @param pageProps Page properties
1744
+ * @returns page properties object for context
1745
+ */const getContextPageProperties=pageProps=>{// Need to get updated page details on each event as an event to notify on SPA URL changes does not seem to exist
1746
+ const curPageProps=getDefaultPageProperties();const ctxPageProps={};Object.keys(curPageProps).forEach(key=>{ctxPageProps[key]=pageProps?.[key]||curPageProps[key];});ctxPageProps.initial_referrer=pageProps?.initial_referrer||state.session.initialReferrer.value;ctxPageProps.initial_referring_domain=pageProps?.initial_referring_domain||state.session.initialReferringDomain.value;return ctxPageProps;};/**
1747
+ * Add any missing default page properties using values from options and defaults
1748
+ * @param properties Input page properties
1749
+ * @param options API options
1750
+ */const getUpdatedPageProperties=(properties,options)=>{const optionsPageProps=options?.page||{};const pageProps=properties;// Need to get updated page details on each event as an event to notify on SPA URL changes does not seem to exist
1751
+ const curPageProps=getDefaultPageProperties();Object.keys(curPageProps).forEach(key=>{if(isUndefined(pageProps[key])){pageProps[key]=optionsPageProps[key]||curPageProps[key];}});if(isUndefined(pageProps.initial_referrer)){pageProps.initial_referrer=optionsPageProps.initial_referrer||state.session.initialReferrer.value;}if(isUndefined(pageProps.initial_referring_domain)){pageProps.initial_referring_domain=optionsPageProps.initial_referring_domain||state.session.initialReferringDomain.value;}return pageProps;};/**
1752
+ * Utility to check for reserved keys in the input object
1753
+ * @param obj Generic object
1754
+ * @param parentKeyPath Object's parent key path
1755
+ * @param logger Logger instance
1756
+ */const checkForReservedElementsInObject=(obj,parentKeyPath,logger)=>{if(isObjectLiteralAndNotNull(obj)){Object.keys(obj).forEach(property=>{if(RESERVED_ELEMENTS.includes(property)||RESERVED_ELEMENTS.includes(property.toLowerCase())){logger.warn(RESERVED_KEYWORD_WARNING(EVENT_MANAGER,property,parentKeyPath,RESERVED_ELEMENTS));}});}};/**
1757
+ * Checks for reserved keys in traits, properties, and contextual traits
1758
+ * @param rudderEvent Generated rudder event
1759
+ * @param logger Logger instance
1760
+ */const checkForReservedElements=(rudderEvent,logger)=>{// properties, traits, contextualTraits are either undefined or object
1761
+ const{properties,traits,context}=rudderEvent;const{traits:contextualTraits}=context;checkForReservedElementsInObject(properties,'properties',logger);checkForReservedElementsInObject(traits,'traits',logger);checkForReservedElementsInObject(contextualTraits,'context.traits',logger);};/**
1762
+ * Overrides the top-level event properties with data from API options
1763
+ * @param rudderEvent Generated rudder event
1764
+ * @param options API options
1765
+ */const updateTopLevelEventElements=(rudderEvent,options)=>{if(options.anonymousId&&isString(options.anonymousId)){// eslint-disable-next-line no-param-reassign
1766
+ rudderEvent.anonymousId=options.anonymousId;}if(isNonEmptyObject(options.integrations)){// eslint-disable-next-line no-param-reassign
1767
+ rudderEvent.integrations=options.integrations;}if(options.originalTimestamp&&isString(options.originalTimestamp)){// eslint-disable-next-line no-param-reassign
1768
+ rudderEvent.originalTimestamp=options.originalTimestamp;}};/**
1769
+ * To merge the contextual information in API options with existing data
1770
+ * @param rudderContext Generated rudder event
1771
+ * @param options API options
1772
+ * @param logger Logger instance
1773
+ */const getMergedContext=(rudderContext,options,logger)=>{let context=rudderContext;Object.keys(options).forEach(key=>{if(!TOP_LEVEL_ELEMENTS.includes(key)&&!CONTEXT_RESERVED_ELEMENTS.includes(key)){if(key!=='context'){context=mergeDeepRight(context,{[key]:options[key]});}else if(!isUndefined(options[key])&&isObjectLiteralAndNotNull(options[key])){const tempContext={};Object.keys(options[key]).forEach(e=>{if(!CONTEXT_RESERVED_ELEMENTS.includes(e)){tempContext[e]=options[key][e];}});context=mergeDeepRight(context,{...tempContext});}else {logger.warn(INVALID_CONTEXT_OBJECT_WARNING(EVENT_MANAGER));}}});return context;};/**
1774
+ * Updates rudder event object with data from the API options
1775
+ * @param rudderEvent Generated rudder event
1776
+ * @param options API options
1777
+ */const processOptions=(rudderEvent,options,logger)=>{// Only allow object type for options
1778
+ if(isObjectLiteralAndNotNull(options)){updateTopLevelEventElements(rudderEvent,options);// eslint-disable-next-line no-param-reassign
1779
+ rudderEvent.context=getMergedContext(rudderEvent.context,options,logger);}};/**
1780
+ * Returns the final integrations config for the event based on the global config and event's config
1781
+ * @param integrationsConfig Event's integrations config
1782
+ * @returns Final integrations config
1783
+ */const getEventIntegrationsConfig=integrationsConfig=>{let finalIntgConfig;if(state.loadOptions.value.useGlobalIntegrationsConfigInEvents){// Prefer the integrations object from the consent API response over the load API integrations object
1784
+ finalIntgConfig=state.consents.postConsent.value.integrations??state.nativeDestinations.loadOnlyIntegrations.value;}else if(integrationsConfig){finalIntgConfig=integrationsConfig;}else {finalIntgConfig=DEFAULT_INTEGRATIONS_CONFIG;}return clone(finalIntgConfig);};/**
1785
+ * Enrich the base event object with data from state and the API options
1786
+ * @param rudderEvent RudderEvent object
1787
+ * @param options API options
1788
+ * @param pageProps Page properties
1789
+ * @param logger logger
1790
+ * @returns Enriched RudderEvent object
1791
+ */const getEnrichedEvent=(rudderEvent,options,pageProps,logger)=>{const commonEventData={channel:CHANNEL,context:{traits:clone(state.session.userTraits.value),sessionId:state.session.sessionInfo.value.id||undefined,sessionStart:state.session.sessionInfo.value.sessionStart||undefined,// Add 'consentManagement' only if consent management is enabled
1792
+ ...(state.consents.enabled.value&&{consentManagement:{deniedConsentIds:clone(state.consents.data.value.deniedConsentIds),allowedConsentIds:clone(state.consents.data.value.allowedConsentIds),provider:state.consents.provider.value,resolutionStrategy:state.consents.resolutionStrategy.value}}),'ua-ch':state.context['ua-ch'].value,app:state.context.app.value,library:state.context.library.value,userAgent:state.context.userAgent.value,os:state.context.os.value,locale:state.context.locale.value,screen:state.context.screen.value,campaign:extractUTMParameters(globalThis.location.href),page:getContextPageProperties(pageProps),timezone:state.context.timezone.value,// Add auto tracking information
1793
+ ...(state.autoTrack.enabled.value&&{autoTrack:{...(state.autoTrack.pageLifecycle.enabled.value&&{page:{pageViewId:state.autoTrack.pageLifecycle.pageViewId.value}})}})},originalTimestamp:getCurrentTimeFormatted(),messageId:generateUUID(),userId:rudderEvent.userId||state.session.userId.value};if(!isStorageTypeValidForStoringData(state.storage.entries.value.anonymousId?.type)){// Generate new anonymous id for each request
1794
+ commonEventData.anonymousId=generateAnonymousId();}else {// Type casting to string as the user session manager will take care of initializing the value
1795
+ commonEventData.anonymousId=state.session.anonymousId.value;}// set truly anonymous tracking flag
1796
+ if(state.storage.trulyAnonymousTracking.value){commonEventData.context.trulyAnonymousTracking=true;}if(rudderEvent.type==='identify'){commonEventData.context.traits=state.storage.entries.value.userTraits?.type!==NO_STORAGE?clone(state.session.userTraits.value):rudderEvent.context.traits;}if(rudderEvent.type==='group'){if(rudderEvent.groupId||state.session.groupId.value){commonEventData.groupId=rudderEvent.groupId||state.session.groupId.value;}if(rudderEvent.traits||state.session.groupTraits.value){commonEventData.traits=state.storage.entries.value.groupTraits?.type!==NO_STORAGE?clone(state.session.groupTraits.value):rudderEvent.traits;}}const processedEvent=mergeDeepRight(rudderEvent,commonEventData);// Set the default values for the event properties
1797
+ // matching with v1.1 payload
1798
+ if(processedEvent.event===undefined){processedEvent.event=null;}if(processedEvent.properties===undefined){processedEvent.properties=null;}processOptions(processedEvent,options,logger);checkForReservedElements(processedEvent,logger);// Update the integrations config for the event
1799
+ processedEvent.integrations=getEventIntegrationsConfig(processedEvent.integrations);return processedEvent;};
1800
+
1801
+ class RudderEventFactory{constructor(logger){this.logger=logger;}/**
1802
+ * Generate a 'page' event based on the user-input fields
1803
+ * @param category Page's category
1804
+ * @param name Page name
1805
+ * @param properties Page properties
1806
+ * @param options API options
1807
+ */generatePageEvent(category,name,properties,options){let props=properties??{};props=getUpdatedPageProperties(props,options);const pageEvent={properties:props,name,category,type:'page'};return getEnrichedEvent(pageEvent,options,props,this.logger);}/**
1808
+ * Generate a 'track' event based on the user-input fields
1809
+ * @param event The event name
1810
+ * @param properties Event properties
1811
+ * @param options API options
1812
+ */generateTrackEvent(event,properties,options){const trackEvent={properties,event,type:'track'};return getEnrichedEvent(trackEvent,options,undefined,this.logger);}/**
1813
+ * Generate an 'identify' event based on the user-input fields
1814
+ * @param userId New user ID
1815
+ * @param traits new traits
1816
+ * @param options API options
1817
+ */generateIdentifyEvent(userId,traits,options){const identifyEvent={userId,type:'identify',context:{traits}};return getEnrichedEvent(identifyEvent,options,undefined,this.logger);}/**
1818
+ * Generate an 'alias' event based on the user-input fields
1819
+ * @param to New user ID
1820
+ * @param from Old user ID
1821
+ * @param options API options
1822
+ */generateAliasEvent(to,from,options){const aliasEvent={previousId:from,type:'alias'};const enrichedEvent=getEnrichedEvent(aliasEvent,options,undefined,this.logger);// override the User ID from the API inputs
1823
+ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
1824
+ * Generate a 'group' event based on the user-input fields
1825
+ * @param groupId New group ID
1826
+ * @param traits new group traits
1827
+ * @param options API options
1828
+ */generateGroupEvent(groupId,traits,options){const groupEvent={type:'group'};if(groupId){groupEvent.groupId=groupId;}if(traits){groupEvent.traits=traits;}return getEnrichedEvent(groupEvent,options,undefined,this.logger);}/**
1829
+ * Generates a new RudderEvent object based on the user-input fields
1830
+ * @param event API event parameters object
1831
+ * @returns A RudderEvent object
1832
+ */create(event){let eventObj;switch(event.type){case 'page':eventObj=this.generatePageEvent(event.category,event.name,event.properties,event.options);break;case 'track':eventObj=this.generateTrackEvent(event.name,event.properties,event.options);break;case 'identify':eventObj=this.generateIdentifyEvent(event.userId,event.traits,event.options);break;case 'alias':eventObj=this.generateAliasEvent(event.to,event.from,event.options);break;case 'group':default:eventObj=this.generateGroupEvent(event.groupId,event.traits,event.options);break;}return eventObj;}}
1833
+
1834
+ /**
1835
+ * A service to generate valid event payloads and queue them for processing
1836
+ */class EventManager{/**
1837
+ *
1838
+ * @param eventRepository Event repository instance
1839
+ * @param userSessionManager UserSession Manager instance
1840
+ * @param errorHandler Error handler object
1841
+ * @param logger Logger object
1842
+ */constructor(eventRepository,userSessionManager,errorHandler,logger){this.eventRepository=eventRepository;this.userSessionManager=userSessionManager;this.errorHandler=errorHandler;this.logger=logger;this.eventFactory=new RudderEventFactory(this.logger);}/**
1843
+ * Initializes the event manager
1844
+ */init(){this.eventRepository.init();}resume(){this.eventRepository.resume();}/**
1845
+ * Consumes a new incoming event
1846
+ * @param event Incoming event data
1847
+ */addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
1848
+
1849
+ class UserSessionManager{/**
1850
+ * Tracks whether a server-side cookie setting request is in progress or not.
1851
+ */constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.storeManager=storeManager;this.pluginsManager=pluginsManager;this.logger=logger;this.errorHandler=errorHandler;this.httpClient=httpClient;this.onError=this.onError.bind(this);this.serverSideCookieDebounceFuncs={};this.serverSideCookiesRequestInProgress={};}/**
1852
+ * Initialize User session with values from storage
1853
+ */init(){this.syncStorageDataToState();// Register the effect to sync with storage
1854
+ this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
1855
+ this.setUserId(this.getUserId());this.setUserTraits(this.getUserTraits());this.setGroupId(this.getGroupId());this.setGroupTraits(this.getGroupTraits());const{externalAnonymousIdCookieName,anonymousIdOptions}=state.loadOptions.value;let externalAnonymousId;if(isDefinedAndNotNull(externalAnonymousIdCookieName)&&typeof externalAnonymousIdCookieName==='string'){externalAnonymousId=this.getExternalAnonymousIdByCookieName(externalAnonymousIdCookieName);}this.setAnonymousId(externalAnonymousId??this.getAnonymousId(anonymousIdOptions));this.setAuthToken(this.getAuthToken());this.setInitialReferrerInfo();this.configureSessionTracking();}configureSessionTracking(){let sessionInfo;if(this.isPersistenceEnabledForStorageEntry('sessionInfo')){const configuredSessionTrackingInfo=this.getConfiguredSessionTrackingInfo();const initialSessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;// Merge the session info from the storage and the configuration
1856
+ sessionInfo={// If manualTrack is set to true in the storage, then do not enable auto tracking even if configured.
1857
+ // Once manual tracking ends (endSession is called), auto tracking will be enabled in the next SDK run.
1858
+ autoTrack:configuredSessionTrackingInfo.autoTrack&&initialSessionInfo.manualTrack!==true,timeout:configuredSessionTrackingInfo.timeout,manualTrack:initialSessionInfo.manualTrack,expiresAt:initialSessionInfo.expiresAt,id:initialSessionInfo.id,sessionStart:initialSessionInfo.sessionStart};// If both autoTrack and manualTrack are disabled, reset the session info to default values
1859
+ if(!sessionInfo.autoTrack&&sessionInfo.manualTrack!==true){sessionInfo=DEFAULT_USER_SESSION_VALUES.sessionInfo;}else if(configuredSessionTrackingInfo.cutOff?.enabled===true){sessionInfo.cutOff={enabled:true,duration:configuredSessionTrackingInfo.cutOff.duration,expiresAt:initialSessionInfo.cutOff?.expiresAt};}}else {sessionInfo=DEFAULT_USER_SESSION_VALUES.sessionInfo;}state.session.sessionInfo.value=sessionInfo;// If auto session tracking is enabled start the session tracking
1860
+ if(state.session.sessionInfo.value.autoTrack){this.startOrRenewAutoTracking(state.session.sessionInfo.value);}}setInitialReferrerInfo(){const persistedInitialReferrer=this.getInitialReferrer();const persistedInitialReferringDomain=this.getInitialReferringDomain();if(persistedInitialReferrer&&persistedInitialReferringDomain){this.setInitialReferrer(persistedInitialReferrer);this.setInitialReferringDomain(persistedInitialReferringDomain);}else {const initialReferrer=persistedInitialReferrer||getReferrer();this.setInitialReferrer(initialReferrer);this.setInitialReferringDomain(getReferringDomain(initialReferrer));}}isPersistenceEnabledForStorageEntry(entryName){return isStorageTypeValidForStoringData(state.storage.entries.value[entryName]?.type);}migrateDataFromPreviousStorage(){const entries=state.storage.entries.value;const storageTypesForMigration=[COOKIE_STORAGE,LOCAL_STORAGE,SESSION_STORAGE];Object.keys(entries).forEach(entry=>{const key=entry;const currentStorage=entries[key]?.type;const curStore=this.storeManager?.getStore(storageClientDataStoreNameMap[currentStorage]);if(curStore){storageTypesForMigration.forEach(storage=>{const store=this.storeManager?.getStore(storageClientDataStoreNameMap[storage]);if(store&&storage!==currentStorage){const value=store.get(COOKIE_KEYS[key]);if(isDefinedNotNullAndNotEmptyString(value)){curStore.set(COOKIE_KEYS[key],value);}store.remove(COOKIE_KEYS[key]);}});}});}migrateStorageIfNeeded(stores,keys){if(!state.storage.migrate.value){return;}let storesToMigrate=stores??[];if(storesToMigrate.length===0){const persistentStoreNames=[CLIENT_DATA_STORE_COOKIE,CLIENT_DATA_STORE_LS,CLIENT_DATA_STORE_SESSION];persistentStoreNames.forEach(storeName=>{const store=this.storeManager?.getStore(storeName);if(store){storesToMigrate.push(store);}});}let keysToMigrate=keys??Object.keys(COOKIE_KEYS);keysToMigrate.forEach(storageKey=>{const storageEntry=COOKIE_KEYS[storageKey];storesToMigrate.forEach(store=>{const migratedVal=this.pluginsManager?.invokeSingle('storage.migrate',storageEntry,store.engine,this.errorHandler,this.logger);// Skip setting the value if it is null or undefined
1861
+ // as those values indicate there is no need for migration or
1862
+ // migration failed
1863
+ if(!isNullOrUndefined(migratedVal)){store.set(storageEntry,migratedVal);}});});}getConfiguredSessionTrackingInfo(){let autoTrack=state.loadOptions.value.sessions.autoTrack!==false;// Do not validate any further if autoTrack is disabled
1864
+ if(!autoTrack){return {autoTrack};}let timeout;const configuredSessionTimeout=state.loadOptions.value.sessions?.timeout;if(!isPositiveInteger(configuredSessionTimeout)){this.logger.warn(TIMEOUT_NOT_NUMBER_WARNING(USER_SESSION_MANAGER,configuredSessionTimeout,DEFAULT_SESSION_TIMEOUT_MS));timeout=DEFAULT_SESSION_TIMEOUT_MS;}else {timeout=configuredSessionTimeout;}if(timeout===0){this.logger.warn(TIMEOUT_ZERO_WARNING(USER_SESSION_MANAGER));autoTrack=false;}// In case user provides a timeout value greater than 0 but less than 10 seconds SDK will show a warning
1865
+ // and will proceed with it
1866
+ if(timeout>0&&timeout<MIN_SESSION_TIMEOUT_MS){this.logger.warn(TIMEOUT_NOT_RECOMMENDED_WARNING(USER_SESSION_MANAGER,timeout,MIN_SESSION_TIMEOUT_MS));}const cutOff=this.getCutOffInfo(timeout);return {timeout,autoTrack,cutOff};}getCutOffInfo(sessionTimeout){const cutOff=state.loadOptions.value.sessions.cutOff;let cutOffDuration;let cutOffEnabled=false;if(cutOff.enabled===true){cutOffDuration=cutOff.duration;cutOffEnabled=true;if(!isPositiveInteger(cutOffDuration)){this.logger.warn(CUT_OFF_DURATION_NOT_NUMBER_WARNING(USER_SESSION_MANAGER,cutOffDuration,DEFAULT_SESSION_CUT_OFF_DURATION_MS));// Use the default value for cut off duration
1867
+ cutOffDuration=DEFAULT_SESSION_CUT_OFF_DURATION_MS;}else if(cutOffDuration<sessionTimeout){this.logger.warn(CUT_OFF_DURATION_LESS_THAN_TIMEOUT_WARNING(USER_SESSION_MANAGER,cutOffDuration,sessionTimeout));cutOffEnabled=false;}}return {enabled:cutOffEnabled,duration:cutOffDuration};}/**
1868
+ * Handles error
1869
+ * @param error The error object
1870
+ */onError(error,customMessage,groupingHash){this.errorHandler.onError({error,context:USER_SESSION_MANAGER,customMessage,groupingHash});}/**
1871
+ * A function to encrypt the cookie value and return the encrypted data
1872
+ * @param cookiesData
1873
+ * @param store
1874
+ * @returns
1875
+ */getEncryptedCookieData(cookiesData,store){const encryptedCookieData=[];cookiesData.forEach(cData=>{const encryptedValue=store?.encrypt(stringifyWithoutCircular(cData.value,false,[],this.logger));if(isDefinedAndNotNull(encryptedValue)){encryptedCookieData.push({name:cData.name,value:encryptedValue});}});return encryptedCookieData;}/**
1876
+ * A function that makes request to data service to set the cookie
1877
+ * @param encryptedCookieData
1878
+ * @param callback
1879
+ */makeRequestToSetCookie(encryptedCookieData,callback){this.httpClient?.getAsyncData({url:state.serverCookies.dataServiceUrl.value,options:{method:'POST',data:stringifyWithoutCircular({reqType:'setCookies',workspaceId:state.source.value?.workspaceId,data:{options:{maxAge:state.storage.cookie.value?.maxage,path:state.storage.cookie.value?.path,domain:state.storage.cookie.value?.domain,sameSite:state.storage.cookie.value?.samesite,secure:state.storage.cookie.value?.secure,expires:state.storage.cookie.value?.expires},cookies:encryptedCookieData}}),sendRawData:true,withCredentials:true},isRawResponse:true,callback});}/**
1880
+ * A function to make an external request to set the cookie from server side
1881
+ * @param sessionToCookiesMap map of session key to cookie name
1882
+ * @param cb callback function to be called when the cookie is set
1883
+ * @param store store to be used to get the cookie value
1884
+ */setServerSideCookies(sessionToCookiesMap,cb,store){// Retrieve the cookie value from the state
1885
+ const sessionKeys=Object.keys(sessionToCookiesMap);const getCurrentCookieValuesFromState=()=>{return sessionKeys.map(sessionKey=>{return {name:sessionToCookiesMap[sessionKey].name,value:state.session[sessionKey].value};});};// Preserve the current cookie values
1886
+ const originalCookieValues={};sessionKeys.forEach(sessionKey=>{originalCookieValues[sessionToCookiesMap[sessionKey].name]=store?.get(sessionToCookiesMap[sessionKey].name);});const clearInProgressFlags=()=>{sessionKeys.forEach(sessionKey=>{this.serverSideCookiesRequestInProgress[sessionKey]=false;});};const setCookiesClientSide=()=>{getCurrentCookieValuesFromState().forEach(each=>{if(cb){cb(each.name,each.value);}});};try{const expectedCookieValues={};sessionKeys.forEach(sessionKey=>{expectedCookieValues[sessionToCookiesMap[sessionKey].name]=state.session[sessionKey].value;});// encrypt cookies values
1887
+ const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
1888
+ this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
1889
+ clearInProgressFlags();if(details?.xhr?.status===200){getCurrentCookieValuesFromState().forEach(cData=>{const originalCookieVal=originalCookieValues[cData.name];const currentCookieVal=store?.get(cData.name);// Check if the expected cookie values are set.
1890
+ if(stringifyWithoutCircular(expectedCookieValues[cData.name],false,[])!==stringifyWithoutCircular(currentCookieVal,false,[])){// It's fine if the values don't match as other active SDK sessions might have updated the cookie values
1891
+ // or other cookie requests might have updated the cookie value.
1892
+ // Log an error only when cookie didn't exist previously and currently also doesn't exist.
1893
+ if(isNull(originalCookieVal)&&isNull(currentCookieVal)){this.logger.error(FAILED_SETTING_COOKIE_FROM_SERVER_ERROR(cData.name));}if(cb){cb(cData.name,cData.value);}}});}else {this.logger.error(DATA_SERVER_REQUEST_FAIL_ERROR(details?.xhr?.status));setCookiesClientSide();}});}else {setCookiesClientSide();// Mark the cookie req status as done
1894
+ clearInProgressFlags();}}catch(e){this.onError(e,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR);setCookiesClientSide();// Mark the cookie req status as done
1895
+ clearInProgressFlags();}}/**
1896
+ * A function to sync values in storage
1897
+ * @param sessionKey
1898
+ */syncValueToStorage(sessionKey){const entries=state.storage.entries.value;const storageType=entries[sessionKey]?.type;if(isStorageTypeValidForStoringData(storageType)){const curStore=this.storeManager.getStore(storageClientDataStoreNameMap[storageType]);const cookieName=entries[sessionKey]?.key;const cookieValue=state.session[sessionKey].value;if(cookieValue&&(isString(cookieValue)||isNonEmptyObject(cookieValue))){// if useServerSideCookies load option is set to true
1899
+ // set the cookie from server side
1900
+ if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
1901
+ this.serverSideCookiesRequestInProgress[sessionKey]=true;if(this.serverSideCookieDebounceFuncs[sessionKey]){globalThis.clearTimeout(this.serverSideCookieDebounceFuncs[sessionKey]);}this.serverSideCookieDebounceFuncs[sessionKey]=globalThis.setTimeout(()=>{// Create a map of session key to cookie name
1902
+ const sessionToCookiesMap={[sessionKey]:{name:cookieName}};this.setServerSideCookies(sessionToCookiesMap,(cookieName,cookieValue)=>{curStore?.set(cookieName,cookieValue);},curStore);},SERVER_SIDE_COOKIES_DEBOUNCE_TIME);}else {curStore?.set(cookieName,cookieValue);}}else {curStore?.remove(cookieName);}}}/**
1903
+ * Function to update storage whenever state value changes
1904
+ */registerEffects(){// This will work as long as the user session entry key names are same as the state keys
1905
+ USER_SESSION_KEYS.forEach(sessionKey=>{m(()=>{this.syncValueToStorage(sessionKey);});});}/**
1906
+ * Sets anonymous id in the following precedence:
1907
+ *
1908
+ * 1. anonymousId: Id directly provided to the function.
1909
+ * 2. rudderAmpLinkerParam: value generated from linker query parm (rudderstack)
1910
+ * using parseLinker util.
1911
+ * 3. generateUUID: A new unique id is generated and assigned.
1912
+ */setAnonymousId(anonymousId,rudderAmpLinkerParam){let finalAnonymousId=anonymousId;if(!isString(anonymousId)||!finalAnonymousId){finalAnonymousId=undefined;}if(this.isPersistenceEnabledForStorageEntry('anonymousId')){if(!finalAnonymousId&&rudderAmpLinkerParam){const linkerPluginsResult=this.pluginsManager?.invokeSingle('userSession.anonymousIdGoogleLinker',rudderAmpLinkerParam);finalAnonymousId=linkerPluginsResult;}finalAnonymousId=finalAnonymousId||generateAnonymousId();}else {finalAnonymousId=DEFAULT_USER_SESSION_VALUES.anonymousId;}state.session.anonymousId.value=finalAnonymousId;}/**
1913
+ * Fetches anonymousId
1914
+ * @param options option to fetch it from external source
1915
+ * @returns anonymousId
1916
+ */getAnonymousId(options){const storage=state.storage.entries.value.anonymousId?.type;if(isStorageTypeValidForStoringData(storage)){let persistedAnonymousId=state.session.anonymousId.value;// If the anonymous ID is the default value, fetch it from storage
1917
+ if(!persistedAnonymousId||persistedAnonymousId===DEFAULT_USER_SESSION_VALUES.anonymousId){persistedAnonymousId=this.getEntryValue('anonymousId');}if(!persistedAnonymousId&&options){// fetch anonymousId from external source
1918
+ const autoCapturedAnonymousId=this.pluginsManager?.invokeSingle('storage.getAnonymousId',getStorageEngine,options);persistedAnonymousId=autoCapturedAnonymousId;}state.session.anonymousId.value=persistedAnonymousId||generateAnonymousId();}return state.session.anonymousId.value;}getEntryValue(sessionKey){const entries=state.storage.entries.value;const storageType=entries[sessionKey]?.type;if(isStorageTypeValidForStoringData(storageType)){const store=this.storeManager?.getStore(storageClientDataStoreNameMap[storageType]);// Migrate the storage data before fetching the value
1919
+ // This is needed for entries that are fetched from the storage
1920
+ // during the current session (for example, session info)
1921
+ this.migrateStorageIfNeeded([store],[sessionKey]);const storageKey=entries[sessionKey]?.key;return store?.get(storageKey)??null;}return null;}getExternalAnonymousIdByCookieName(key){const storageEngine=getStorageEngine(COOKIE_STORAGE);if(storageEngine?.isEnabled){return storageEngine.getItem(key)??null;}return null;}/**
1922
+ * Fetches the value for a session key. Preferably from storage, if the server-side
1923
+ * cookies request is not in progress. Otherwise, from the state.
1924
+ * @param sessionKey - The session key to fetch the value for
1925
+ * @returns - The value for the session key
1926
+ */getUserSessionValue(sessionKey){// If the server-side cookies request is in progress, fetch the value from the state.
1927
+ if(this.serverSideCookiesRequestInProgress[sessionKey]){return state.session[sessionKey].value;}// Otherwise, fetch the value from storage.
1928
+ return this.getEntryValue(sessionKey);}/**
1929
+ * Fetches User Id
1930
+ * @returns
1931
+ */getUserId(){return this.getUserSessionValue('userId');}/**
1932
+ * Fetches User Traits
1933
+ * @returns
1934
+ */getUserTraits(){return this.getUserSessionValue('userTraits');}/**
1935
+ * Fetches Group Id
1936
+ * @returns
1937
+ */getGroupId(){return this.getUserSessionValue('groupId');}/**
1938
+ * Fetches Group Traits
1939
+ * @returns
1940
+ */getGroupTraits(){return this.getUserSessionValue('groupTraits');}/**
1941
+ * Fetches Initial Referrer
1942
+ * @returns
1943
+ */getInitialReferrer(){return this.getUserSessionValue('initialReferrer');}/**
1944
+ * Fetches Initial Referring domain
1945
+ * @returns
1946
+ */getInitialReferringDomain(){return this.getUserSessionValue('initialReferringDomain');}/**
1947
+ * Fetches session tracking information from storage
1948
+ * @returns
1949
+ */getSessionInfo(){return this.getUserSessionValue('sessionInfo');}/**
1950
+ * Fetches auth token from storage
1951
+ * @returns
1952
+ */getAuthToken(){return this.getUserSessionValue('authToken');}/**
1953
+ * If session is active it returns the sessionId
1954
+ * @returns
1955
+ */getSessionId(){const sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack&&!hasSessionExpired(sessionInfo)||sessionInfo.manualTrack){return sessionInfo.id??null;}return null;}/**
1956
+ * A function to keep the session information up to date in the state
1957
+ * before using it for building event payloads.
1958
+ */refreshSession(){let sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack||sessionInfo.manualTrack){if(sessionInfo.autoTrack){this.startOrRenewAutoTracking(sessionInfo);sessionInfo=state.session.sessionInfo.value;}// Note that if sessionStart is false, then it's an active session.
1959
+ // So, we needn't update the session info.
1960
+ //
1961
+ // For other scenarios,
1962
+ // 1. If sessionStart is undefined, then it's a new session.
1963
+ // Mark it as sessionStart.
1964
+ // 2. If sessionStart is true, then need to flip it for the future events.
1965
+ if(sessionInfo.sessionStart===undefined){sessionInfo={...sessionInfo,sessionStart:true};}else if(sessionInfo.sessionStart){sessionInfo={...sessionInfo,sessionStart:false};}}// Always write to state (in-turn to storage) to keep the session info up to date.
1966
+ state.session.sessionInfo.value=sessionInfo;if(state.lifecycle.status.value!=='readyExecuted'){// Force update the storage as the 'effect' blocks are not getting triggered
1967
+ // when processing preload buffered requests
1968
+ this.syncValueToStorage('sessionInfo');}}resetAndStartNewSession(){const session=state.session;const{manualTrack,autoTrack,timeout,cutOff}=session.sessionInfo.value;if(autoTrack){const sessionInfo={...DEFAULT_USER_SESSION_VALUES.sessionInfo,timeout};if(cutOff){sessionInfo.cutOff={enabled:cutOff.enabled,duration:cutOff.duration};}session.sessionInfo.value=sessionInfo;this.startOrRenewAutoTracking(session.sessionInfo.value);}else if(manualTrack){this.startManualTrackingInternal();}}/**
1969
+ * Reset state values
1970
+ * @param options options for reset
1971
+ * @returns
1972
+ */reset(options){const{session}=state;const opts=getFinalResetOptions(options);n(()=>{Object.keys(DEFAULT_USER_SESSION_VALUES).forEach(key=>{const userSessionKey=key;if(opts.entries[userSessionKey]!==true){return;}switch(key){case 'anonymousId':this.setAnonymousId();break;case 'sessionInfo':this.resetAndStartNewSession();break;default:session[userSessionKey].value=DEFAULT_USER_SESSION_VALUES[userSessionKey];break;}});});}/**
1973
+ * Set user Id
1974
+ * @param userId
1975
+ */setUserId(userId){state.session.userId.value=this.isPersistenceEnabledForStorageEntry('userId')&&userId?userId:DEFAULT_USER_SESSION_VALUES.userId;}/**
1976
+ * Set user traits
1977
+ * @param traits
1978
+ */setUserTraits(traits){state.session.userTraits.value=this.isPersistenceEnabledForStorageEntry('userTraits')&&isObjectLiteralAndNotNull(traits)?mergeDeepRight(state.session.userTraits.value??DEFAULT_USER_SESSION_VALUES.userTraits,traits):DEFAULT_USER_SESSION_VALUES.userTraits;}/**
1979
+ * Set group Id
1980
+ * @param groupId
1981
+ */setGroupId(groupId){state.session.groupId.value=this.isPersistenceEnabledForStorageEntry('groupId')&&groupId?groupId:DEFAULT_USER_SESSION_VALUES.groupId;}/**
1982
+ * Set group traits
1983
+ * @param traits
1984
+ */setGroupTraits(traits){state.session.groupTraits.value=this.isPersistenceEnabledForStorageEntry('groupTraits')&&isObjectLiteralAndNotNull(traits)?mergeDeepRight(state.session.groupTraits.value??DEFAULT_USER_SESSION_VALUES.groupTraits,traits):DEFAULT_USER_SESSION_VALUES.groupTraits;}/**
1985
+ * Set initial referrer
1986
+ * @param referrer
1987
+ */setInitialReferrer(referrer){state.session.initialReferrer.value=this.isPersistenceEnabledForStorageEntry('initialReferrer')&&referrer?referrer:DEFAULT_USER_SESSION_VALUES.initialReferrer;}/**
1988
+ * Set initial referring domain
1989
+ * @param {String} referringDomain
1990
+ */setInitialReferringDomain(referringDomain){state.session.initialReferringDomain.value=this.isPersistenceEnabledForStorageEntry('initialReferringDomain')&&referringDomain?referringDomain:DEFAULT_USER_SESSION_VALUES.initialReferringDomain;}/**
1991
+ * A function to check for existing session details and depending on that create a new session
1992
+ */startOrRenewAutoTracking(sessionInfo){let finalSessionInfo=sessionInfo;if(hasSessionExpired(sessionInfo)){finalSessionInfo=generateAutoTrackingSession(sessionInfo);}else {const timestamp=Date.now();const timeout=sessionInfo.timeout;// Set the expiry time of the session
1993
+ finalSessionInfo.expiresAt=timestamp+timeout;}// Reset cut off expiry timestamp if it is exceeded
1994
+ if(isCutOffTimeExceeded(finalSessionInfo)){finalSessionInfo.cutOff.expiresAt=undefined;}// If cut off is active, set or retain the expiry time
1995
+ if(finalSessionInfo.cutOff){const cutOffExpiresAt=getCutOffExpirationTimestamp(finalSessionInfo.cutOff);finalSessionInfo.cutOff.expiresAt=cutOffExpiresAt;}// Update the session info in the state
1996
+ state.session.sessionInfo.value=finalSessionInfo;}/**
1997
+ * A function method to start a manual session
1998
+ * @param {number} id session identifier
1999
+ * @returns
2000
+ */start(id){state.session.sessionInfo.value=generateManualTrackingSession(id,this.logger);}/**
2001
+ * An internal function to start manual session
2002
+ */startManualTrackingInternal(){this.start(Date.now());}/**
2003
+ * A public method to end an ongoing session.
2004
+ */end(){state.session.sessionInfo.value=DEFAULT_USER_SESSION_VALUES.sessionInfo;}/**
2005
+ * Set auth token
2006
+ * @param userId
2007
+ */setAuthToken(token){state.session.authToken.value=this.isPersistenceEnabledForStorageEntry('authToken')&&token?token:DEFAULT_USER_SESSION_VALUES.authToken;}}
2008
+
2009
+ /**
2010
+ * Plugins to be loaded in the plugins loadOption is not defined
2011
+ */const allOptionalPlugins=['BeaconQueue','CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];// Plugins excluded from lite builds which are also not connected to any SDK configuration options
2012
+ const liteExcludedPlugins=['GoogleLinker'];const defaultOptionalPluginsList=allOptionalPlugins.filter(plugin=>!liteExcludedPlugins.includes(plugin));
2013
+
2014
+ const normalizeLoadOptions=(loadOptionsFromState,loadOptions)=>{// TODO: Maybe add warnings for invalid values
2015
+ const normalizedLoadOpts=clone(loadOptions);if(!isString(normalizedLoadOpts.setCookieDomain)){normalizedLoadOpts.setCookieDomain=undefined;}const cookieSameSiteValues=['Strict','Lax','None'];if(!cookieSameSiteValues.includes(normalizedLoadOpts.sameSiteCookie)){normalizedLoadOpts.sameSiteCookie=undefined;}normalizedLoadOpts.secureCookie=getNormalizedBooleanValue(normalizedLoadOpts.secureCookie,loadOptionsFromState.secureCookie);normalizedLoadOpts.sameDomainCookiesOnly=getNormalizedBooleanValue(normalizedLoadOpts.sameDomainCookiesOnly,loadOptionsFromState.sameDomainCookiesOnly);const uaChTrackLevels=['none','default','full'];if(!uaChTrackLevels.includes(normalizedLoadOpts.uaChTrackLevel)){normalizedLoadOpts.uaChTrackLevel=undefined;}normalizedLoadOpts.integrations=getNormalizedObjectValue(normalizedLoadOpts.integrations);if(!Array.isArray(normalizedLoadOpts.plugins)){normalizedLoadOpts.plugins=defaultOptionalPluginsList;}normalizedLoadOpts.useGlobalIntegrationsConfigInEvents=getNormalizedBooleanValue(normalizedLoadOpts.useGlobalIntegrationsConfigInEvents,loadOptionsFromState.useGlobalIntegrationsConfigInEvents);normalizedLoadOpts.bufferDataPlaneEventsUntilReady=getNormalizedBooleanValue(normalizedLoadOpts.bufferDataPlaneEventsUntilReady,loadOptionsFromState.bufferDataPlaneEventsUntilReady);normalizedLoadOpts.sendAdblockPage=getNormalizedBooleanValue(normalizedLoadOpts.sendAdblockPage,loadOptionsFromState.sendAdblockPage);normalizedLoadOpts.useServerSideCookies=getNormalizedBooleanValue(normalizedLoadOpts.useServerSideCookies,loadOptionsFromState.useServerSideCookies);if(!isString(normalizedLoadOpts.dataServiceEndpoint)){normalizedLoadOpts.dataServiceEndpoint=undefined;}normalizedLoadOpts.sendAdblockPageOptions=getNormalizedObjectValue(normalizedLoadOpts.sendAdblockPageOptions);normalizedLoadOpts.loadIntegration=getNormalizedBooleanValue(normalizedLoadOpts.loadIntegration,loadOptionsFromState.loadIntegration);if(!isNonEmptyObject(normalizedLoadOpts.storage)){normalizedLoadOpts.storage=undefined;}else {normalizedLoadOpts.storage.migrate=getNormalizedBooleanValue(normalizedLoadOpts.storage.migrate,loadOptionsFromState.storage?.migrate);normalizedLoadOpts.storage.cookie=getNormalizedObjectValue(normalizedLoadOpts.storage.cookie);normalizedLoadOpts.storage.encryption=getNormalizedObjectValue(normalizedLoadOpts.storage.encryption);normalizedLoadOpts.storage=removeUndefinedAndNullValues(normalizedLoadOpts.storage);}normalizedLoadOpts.destinationsQueueOptions=getNormalizedObjectValue(normalizedLoadOpts.destinationsQueueOptions);normalizedLoadOpts.queueOptions=getNormalizedObjectValue(normalizedLoadOpts.queueOptions);normalizedLoadOpts.lockIntegrationsVersion=getNormalizedBooleanValue(normalizedLoadOpts.lockIntegrationsVersion,loadOptionsFromState.lockIntegrationsVersion);normalizedLoadOpts.lockPluginsVersion=getNormalizedBooleanValue(normalizedLoadOpts.lockPluginsVersion,loadOptionsFromState.lockPluginsVersion);if(!isNumber(normalizedLoadOpts.dataPlaneEventsBufferTimeout)){normalizedLoadOpts.dataPlaneEventsBufferTimeout=undefined;}normalizedLoadOpts.beaconQueueOptions=getNormalizedObjectValue(normalizedLoadOpts.beaconQueueOptions);normalizedLoadOpts.preConsent=getNormalizedObjectValue(normalizedLoadOpts.preConsent);normalizedLoadOpts.sourceConfigurationOverride=getNormalizedObjectValue(normalizedLoadOpts.sourceConfigurationOverride);const mergedLoadOptions=mergeDeepRight(loadOptionsFromState,removeUndefinedAndNullValues(normalizedLoadOpts));return mergedLoadOptions;};
2016
+
2017
+ const DATA_PLANE_QUEUE_EXT_POINT_PREFIX='dataplaneEventsQueue';const DESTINATIONS_QUEUE_EXT_POINT_PREFIX='destinationsEventsQueue';const DMT_EXT_POINT_PREFIX='transformEvent';
2018
+
2019
+ /**
2020
+ * Filters and returns the user supplied integrations config that should take preference over the destination specific integrations config
2021
+ * @param eventIntgConfig User supplied integrations config at event level
2022
+ * @param destinationsIntgConfig Cumulative integrations config from all destinations
2023
+ * @returns Filtered user supplied integrations config
2024
+ */const getOverriddenIntegrationOptions=(eventIntgConfig,destinationsIntgConfig)=>Object.keys(eventIntgConfig).filter(intgName=>eventIntgConfig[intgName]!==true||!destinationsIntgConfig[intgName]).reduce((obj,key)=>{const retVal=clone(obj);retVal[key]=eventIntgConfig[key];return retVal;},{});/**
2025
+ * Returns the event object with final integrations config
2026
+ * @param event RudderEvent object
2027
+ * @param state Application state
2028
+ * @returns Mutated event with final integrations config
2029
+ */const getFinalEvent=(event,state)=>{const finalEvent=clone(event);// Merge the destination specific integrations config with the event's integrations config
2030
+ // In general, the preference is given to the event's integrations config
2031
+ const destinationsIntgConfig=state.nativeDestinations.integrationsConfig.value;const overriddenIntgOpts=getOverriddenIntegrationOptions(event.integrations,destinationsIntgConfig);finalEvent.integrations=mergeDeepRight(destinationsIntgConfig,overriddenIntgOpts);return finalEvent;};const shouldBufferEventsForPreConsent=state=>state.consents.preConsent.value.enabled&&state.consents.preConsent.value.events?.delivery==='buffer';
2032
+
2033
+ const safelyInvokeCallback=(callback,args,apiName,logger)=>{if(!isDefined(callback)){return;}if(isFunction(callback)){try{callback(...args);}catch(error){logger.error(CALLBACK_INVOKE_ERROR(apiName),error);}}else {logger.error(INVALID_CALLBACK_FN_ERROR(apiName));}};
2034
+
2035
+ /**
2036
+ * Event repository class responsible for queuing events for further processing and delivery
2037
+ */class EventRepository{/**
2038
+ *
2039
+ * @param pluginsManager Plugins manager instance
2040
+ * @param storeManager Store Manager instance
2041
+ * @param errorHandler Error handler object
2042
+ * @param logger Logger object
2043
+ */constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;}/**
2044
+ * Initializes the event repository
2045
+ */init(){this.dataplaneEventsQueue=this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.init`,state,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.dmtEventsQueue=this.pluginsManager.invokeSingle(`${DMT_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.destinationsEventsQueue=this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.storeManager,this.dmtEventsQueue,this.errorHandler,this.logger);// Start the queue once the client destinations are ready
2046
+ m(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);// Start the queue processing only when the destinations are ready or hybrid mode destinations exist
2047
+ // However, events will be enqueued for now.
2048
+ // At the time of processing the events, the integrations config data from destinations
2049
+ // is merged into the event object
2050
+ let timeoutId;m(()=>{const shouldBufferDpEvents=state.loadOptions.value.bufferDataPlaneEventsUntilReady===true&&state.nativeDestinations.clientDestinationsReady.value===false;const hybridDestExist=state.nativeDestinations.activeDestinations.value.some(dest=>isHybridModeDestination(dest));if((hybridDestExist===false||shouldBufferDpEvents===false)&&!bufferEventsBeforeConsent&&this.dataplaneEventsQueue?.scheduleTimeoutActive!==true){globalThis.clearTimeout(timeoutId);this.dataplaneEventsQueue?.start();}});// Force start the data plane events queue processing after a timeout
2051
+ if(state.loadOptions.value.bufferDataPlaneEventsUntilReady===true){timeoutId=globalThis.setTimeout(()=>{if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true){this.dataplaneEventsQueue?.start();}},state.loadOptions.value.dataPlaneEventsBufferTimeout);}}resume(){if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true){if(state.consents.postConsent.value.discardPreConsentEvents){this.dataplaneEventsQueue?.clear();this.destinationsEventsQueue?.clear();}this.dataplaneEventsQueue?.start();}}/**
2052
+ * Enqueues the event for processing
2053
+ * @param event RudderEvent object
2054
+ * @param callback API callback function
2055
+ */enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger);// Invoke the callback if it exists
2056
+ const apiName=`${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;safelyInvokeCallback(callback,[dpQEvent],apiName,this.logger);}}
2057
+
2058
+ const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{analyticsInstance:globalThis.rudderanalytics},bubbles:true,cancelable:true,composed:true});globalThis.document.dispatchEvent(customEvent);};const isWriteKeyValid=writeKey=>isString(writeKey)&&writeKey.trim().length>0;const isDataPlaneUrlValid=dataPlaneUrl=>isValidURL(dataPlaneUrl);
2059
+
2060
+ /*
2061
+ * Analytics class with lifecycle based on state ad user triggered events
2062
+ */class Analytics{/**
2063
+ * Initialize services and components or use default ones if singletons
2064
+ */constructor(){this.preloadBuffer=new BufferQueue();this.initialized=false;this.errorHandler=defaultErrorHandler;this.logger=defaultLogger;this.externalSrcLoader=new ExternalSrcLoader(this.logger);this.httpClient=defaultHttpClient;this.httpClient.init(this.errorHandler);this.capabilitiesManager=new CapabilitiesManager(this.httpClient,this.errorHandler,this.logger);}/**
2065
+ * Start application lifecycle if not already started
2066
+ */load(writeKey,dataPlaneUrl,loadOptions={}){if(state.lifecycle.status.value){return;}if(!isWriteKeyValid(writeKey)){this.logger.error(WRITE_KEY_VALIDATION_ERROR(ANALYTICS_CORE,writeKey));return;}if(!isDataPlaneUrlValid(dataPlaneUrl)){this.logger.error(DATA_PLANE_URL_VALIDATION_ERROR(ANALYTICS_CORE,dataPlaneUrl));return;}// Set initial state values
2067
+ n(()=>{state.lifecycle.writeKey.value=clone(writeKey);state.lifecycle.dataPlaneUrl.value=clone(dataPlaneUrl);state.loadOptions.value=normalizeLoadOptions(state.loadOptions.value,loadOptions);state.lifecycle.status.value='mounted';});// set log level as early as possible
2068
+ this.logger.setMinLogLevel(state.loadOptions.value.logLevel??POST_LOAD_LOG_LEVEL);// Expose state to global objects
2069
+ setExposedGlobal('state',state,writeKey);// Configure initial config of any services or components here
2070
+ // State application lifecycle
2071
+ this.startLifecycle();}// Start lifecycle methods
2072
+ /**
2073
+ * Orchestrate the lifecycle of the application phases/status
2074
+ */startLifecycle(){m(()=>{try{switch(state.lifecycle.status.value){case 'mounted':this.onMounted();break;case 'browserCapabilitiesReady':this.onBrowserCapabilitiesReady();break;case 'configured':this.onConfigured();break;case 'pluginsLoading':break;case 'pluginsReady':this.onPluginsReady();break;case 'initialized':this.onInitialized();break;case 'loaded':this.onLoaded();break;case 'destinationsLoading':break;case 'destinationsReady':this.onDestinationsReady();break;case 'ready':this.onReady();break;case 'readyExecuted':default:break;}}catch(err){const issue='Failed to load the SDK';this.errorHandler.onError({error:err,context:ANALYTICS_CORE,customMessage:issue,groupingHash:issue});}});}onBrowserCapabilitiesReady(){// initialize the preloaded events enqueuing
2075
+ retrievePreloadBufferEvents(this);this.prepareInternalServices();this.loadConfig();}onLoaded(){this.processBufferedEvents();// Short-circuit the life cycle and move to the ready state if pre-consent behavior is enabled
2076
+ if(state.consents.preConsent.value.enabled===true){state.lifecycle.status.value='ready';}else {this.loadDestinations();}}/**
2077
+ * Load browser polyfill if required
2078
+ */onMounted(){this.capabilitiesManager.init();}/**
2079
+ * Enqueue in SDK preload buffer events, used from preloadBuffer component
2080
+ */enqueuePreloadBufferEvents(bufferedEvents){if(Array.isArray(bufferedEvents)){bufferedEvents.forEach(bufferedEvent=>this.preloadBuffer.enqueue(clone(bufferedEvent)));}}/**
2081
+ * Process the buffer preloaded events by passing their arguments to the respective facade methods
2082
+ */processDataInPreloadBuffer(){while(this.preloadBuffer.size()>0){const eventToProcess=this.preloadBuffer.dequeue();if(eventToProcess){consumePreloadBufferedEvent([...eventToProcess],this);}}}prepareInternalServices(){this.pluginsManager=new PluginsManager(defaultPluginEngine,this.errorHandler,this.logger);this.storeManager=new StoreManager(this.pluginsManager,this.errorHandler,this.logger);this.configManager=new ConfigManager(this.httpClient,this.errorHandler,this.logger);this.userSessionManager=new UserSessionManager(this.pluginsManager,this.storeManager,this.httpClient,this.errorHandler,this.logger);this.eventRepository=new EventRepository(this.pluginsManager,this.storeManager,this.httpClient,this.errorHandler,this.logger);this.eventManager=new EventManager(this.eventRepository,this.userSessionManager,this.errorHandler,this.logger);}/**
2083
+ * Load configuration
2084
+ */loadConfig(){if(state.lifecycle.writeKey.value){this.httpClient.setAuthHeader(state.lifecycle.writeKey.value);}this.configManager?.init();}/**
2085
+ * Initialize the storage and event queue
2086
+ */onPluginsReady(){// Initialize storage
2087
+ this.storeManager?.init();this.userSessionManager?.init();// Initialize the appropriate consent manager plugin
2088
+ if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}// Initialize event manager
2089
+ this.eventManager?.init();// Mark the SDK as initialized
2090
+ state.lifecycle.status.value='initialized';}/**
2091
+ * Load plugins
2092
+ */onConfigured(){this.pluginsManager?.init();// TODO: are we going to enable custom plugins to be passed as load options?
2093
+ // registerCustomPlugins(state.loadOptions.value.customPlugins);
2094
+ }/**
2095
+ * Trigger onLoaded callback if any is provided in config & emit initialised event
2096
+ */onInitialized(){// Process any preloaded events
2097
+ this.processDataInPreloadBuffer();// Set lifecycle state
2098
+ n(()=>{state.lifecycle.loaded.value=true;state.lifecycle.status.value='loaded';});this.initialized=true;// Execute onLoaded callback if provided in load options
2099
+ const onLoadedCallbackFn=state.loadOptions.value.onLoaded;// TODO: we need to avoid passing the window object to the callback function
2100
+ // as this will prevent us from supporting multiple SDK instances in the same page
2101
+ safelyInvokeCallback(onLoadedCallbackFn,[globalThis.rudderanalytics],LOAD_API,this.logger);// Emit an event to use as substitute to the onLoaded callback
2102
+ dispatchSDKEvent('RSA_Initialised');}/**
2103
+ * Emit ready event
2104
+ */// eslint-disable-next-line class-methods-use-this
2105
+ onReady(){state.lifecycle.status.value='readyExecuted';state.eventBuffer.readyCallbacksArray.value.forEach(callback=>{safelyInvokeCallback(callback,[],READY_API,this.logger);});// Emit an event to use as substitute to the ready callback
2106
+ dispatchSDKEvent('RSA_Ready');}/**
2107
+ * Consume preloaded events buffer
2108
+ */processBufferedEvents(){// This logic has been intentionally implemented without a simple
2109
+ // for-loop as the individual events that are processed may
2110
+ // add more events to the buffer (this is needed for the consent API)
2111
+ let bufferedEvents=state.eventBuffer.toBeProcessedArray.value;while(bufferedEvents.length>0){const bufferedEvent=bufferedEvents.shift();state.eventBuffer.toBeProcessedArray.value=bufferedEvents;if(bufferedEvent){const methodName=bufferedEvent[0];if(isFunction(this[methodName])){// Send additional arg 'true' to indicate that this is a buffered invocation
2112
+ this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}/**
2113
+ * Load device mode destinations
2114
+ */loadDestinations(){// If the integrations load is already triggered or completed, skip the rest of the logic
2115
+ if(state.lifecycle.status.value==='destinationsLoading'||state.lifecycle.status.value==='destinationsReady'){return;}// Set in state the desired activeDestinations to inject in DOM
2116
+ this.pluginsManager?.invokeSingle('nativeDestinations.setActiveDestinations',state,this.pluginsManager,this.errorHandler,this.logger);const totalDestinationsToLoad=state.nativeDestinations.activeDestinations.value.length;if(totalDestinationsToLoad===0){state.lifecycle.status.value='destinationsReady';return;}// Start loading native integration scripts and create instances
2117
+ state.lifecycle.status.value='destinationsLoading';this.pluginsManager?.invokeSingle('nativeDestinations.load',state,this.externalSrcLoader,this.errorHandler,this.logger);// Progress to next lifecycle phase if all native destinations are initialized or failed
2118
+ m(()=>{const areAllDestinationsReady=totalDestinationsToLoad===0||state.nativeDestinations.initializedDestinations.value.length+state.nativeDestinations.failedDestinations.value.length===totalDestinationsToLoad;if(areAllDestinationsReady){n(()=>{state.lifecycle.status.value='destinationsReady';state.nativeDestinations.clientDestinationsReady.value=true;});}});}/**
2119
+ * Move to the ready state
2120
+ */// eslint-disable-next-line class-methods-use-this
2121
+ onDestinationsReady(){// May be do any destination specific actions here
2122
+ // Mark the ready status if not already done
2123
+ if(state.lifecycle.status.value!=='ready'){n(()=>{state.lifecycle.status.value='ready';});}}// End lifecycle methods
2124
+ // Start consumer exposed methods
2125
+ ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
2126
+ * If destinations are loaded or no integration is available for loading
2127
+ * execute the callback immediately else push the callbacks to a queue that
2128
+ * will be executed after loading completes
2129
+ */if(state.lifecycle.status.value==='readyExecuted'){safelyInvokeCallback(callback,[],READY_API,this.logger);}else {state.eventBuffer.readyCallbacksArray.value=[...state.eventBuffer.readyCallbacksArray.value,callback];}}page(payload,isBufferedInvocation=false){const type='page';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;this.eventManager?.addEvent({type:'page',category:payload.category,name:payload.name,properties:payload.properties,options:payload.options,callback:payload.callback});// TODO: Maybe we should alter the behavior to send the ad-block page event even if the SDK is still loaded. It'll be pushed into the to be processed queue.
2130
+ // Send automatic ad blocked page event if ad-blockers are detected on the page
2131
+ // Check page category to avoid infinite loop
2132
+ if(state.capabilities.isAdBlocked.value===true&&payload.category!==ADBLOCK_PAGE_CATEGORY){this.page(pageArgumentsToCallOptions(ADBLOCK_PAGE_CATEGORY,ADBLOCK_PAGE_NAME,{// 'title' is intentionally omitted as it does not make sense
2133
+ // in v3 implementation
2134
+ path:ADBLOCK_PAGE_PATH},state.loadOptions.value.sendAdblockPageOptions));}}track(payload,isBufferedInvocation=false){const type='track';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event - ${payload.name}`);state.metrics.triggered.value+=1;this.eventManager?.addEvent({type,name:payload.name||undefined,properties:payload.properties,options:payload.options,callback:payload.callback});}identify(payload,isBufferedInvocation=false){const type='identify';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;const shouldResetSession=Boolean(payload.userId&&state.session.userId.value&&payload.userId!==state.session.userId.value);if(shouldResetSession){this.reset();}// `null` value indicates that previous user ID needs to be retained
2135
+ if(!isNull(payload.userId)){this.userSessionManager?.setUserId(payload.userId);}this.userSessionManager?.setUserTraits(payload.traits);this.eventManager?.addEvent({type,userId:payload.userId,traits:payload.traits,options:payload.options,callback:payload.callback});}alias(payload,isBufferedInvocation=false){const type='alias';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;const previousId=payload.from??(this.getUserId()||this.userSessionManager?.getAnonymousId());this.eventManager?.addEvent({type,to:payload.to,from:previousId,options:payload.options,callback:payload.callback});}group(payload,isBufferedInvocation=false){const type='group';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;// `null` value indicates that previous group ID needs to be retained
2136
+ if(!isNull(payload.groupId)){this.userSessionManager?.setGroupId(payload.groupId);}this.userSessionManager?.setGroupTraits(payload.traits);this.eventManager?.addEvent({type,groupId:payload.groupId,traits:payload.traits,options:payload.options,callback:payload.callback});}reset(options,isBufferedInvocation=false){const type='reset';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,options]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.reset(options);}getAnonymousId(options){return this.userSessionManager?.getAnonymousId(options);}setAnonymousId(anonymousId,rudderAmpLinkerParam,isBufferedInvocation=false){const type='setAnonymousId';// Buffering is needed as setting the anonymous ID may require invoking the GoogleLinker plugin
2137
+ if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,anonymousId,rudderAmpLinkerParam]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.setAnonymousId(anonymousId,rudderAmpLinkerParam);}// eslint-disable-next-line class-methods-use-this
2138
+ getUserId(){return state.session.userId.value;}// eslint-disable-next-line class-methods-use-this
2139
+ getUserTraits(){return state.session.userTraits.value;}// eslint-disable-next-line class-methods-use-this
2140
+ getGroupId(){return state.session.groupId.value;}// eslint-disable-next-line class-methods-use-this
2141
+ getGroupTraits(){return state.session.groupTraits.value;}startSession(sessionId,isBufferedInvocation=false){const type='startSession';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,sessionId]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.start(sessionId);}endSession(isBufferedInvocation=false){const type='endSession';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.end();}// eslint-disable-next-line class-methods-use-this
2142
+ getSessionId(){const sessionId=this.userSessionManager?.getSessionId();return sessionId??null;}consent(options,isBufferedInvocation=false){const type='consent';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,options]];return;}this.errorHandler.leaveBreadcrumb(`New consent invocation`);n(()=>{state.consents.preConsent.value={...state.consents.preConsent.value,enabled:false};state.consents.postConsent.value=getValidPostConsentOptions(options);const{initialized,consentsData}=getConsentManagementData(state.consents.postConsent.value.consentManagement,this.logger);state.consents.initialized.value=initialized;state.consents.data.value=consentsData;});// Update consents data in state
2143
+ if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}// Re-init store manager
2144
+ this.storeManager?.initializeStorageState();// Re-init user session manager
2145
+ this.userSessionManager?.syncStorageDataToState();// Resume event manager to process the events to destinations
2146
+ this.eventManager?.resume();this.loadDestinations();this.sendTrackingEvents(isBufferedInvocation);}sendTrackingEvents(isBufferedInvocation){// If isBufferedInvocation is true, then the tracking events will be added to the end of the
2147
+ // events buffer array so that any other preload events (mainly from query string API) will be processed first.
2148
+ if(state.consents.postConsent.value.trackConsent){const trackOptions=trackArgumentsToCallOptions(CONSENT_TRACK_EVENT_NAME);if(isBufferedInvocation){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['track',trackOptions]];}else {this.track(trackOptions);}}if(state.consents.postConsent.value.sendPageEvent){const pageOptions=pageArgumentsToCallOptions();if(isBufferedInvocation){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['page',pageOptions]];}else {this.page(pageOptions);}}}setAuthToken(token){this.userSessionManager?.setAuthToken(token);}/**
2149
+ * Add a custom integration for a custom destination.
2150
+ * @param destinationId - The ID of the custom destination from the RudderStack dashboard.
2151
+ * @param integration - The custom integration object.
2152
+ * @param isBufferedInvocation - Whether the invocation is buffered.
2153
+ */addCustomIntegration(destinationId,integration,isBufferedInvocation=false){const type='addCustomIntegration';if(isBufferedInvocation){this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.pluginsManager?.invokeSingle('nativeDestinations.addCustomIntegration',destinationId,integration,state,this.logger);}else {if(state.lifecycle.loaded.value){this.logger.error(CUSTOM_INTEGRATION_CANNOT_BE_ADDED_ERROR(ANALYTICS_CORE,destinationId));return;}state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,destinationId,integration]];}}// End consumer exposed methods
2154
+ }
2155
+
2156
+ /*
2157
+ * RudderAnalytics facade singleton that is exposed as global object and will:
2158
+ * expose overloaded methods
2159
+ * handle multiple Analytics instances
2160
+ * consume SDK preload event buffer
2161
+ */class RudderAnalytics{// START-NO-SONAR-SCAN
2162
+ // eslint-disable-next-line sonarjs/public-static-readonly
2163
+ static globalSingleton=null;// END-NO-SONAR-SCAN
2164
+ analyticsInstances={};defaultAnalyticsKey='';logger=defaultLogger;// Singleton with constructor bind methods
2165
+ constructor(){try{if(RudderAnalytics.globalSingleton){// START-NO-SONAR-SCAN
2166
+ // eslint-disable-next-line no-constructor-return
2167
+ return RudderAnalytics.globalSingleton;// END-NO-SONAR-SCAN
2168
+ }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);this.addCustomIntegration=this.addCustomIntegration.bind(this);this.createSafeAnalyticsInstance();RudderAnalytics.globalSingleton=this;state.autoTrack.pageLifecycle.pageViewId.value=generateUUID();state.autoTrack.pageLifecycle.pageLoadedTimestamp.value=Date.now();// start loading if a load event was buffered or wait for explicit load call
2169
+ this.triggerBufferedLoadEvent();// Assign to global "rudderanalytics" object after processing the preload buffer (if any exists)
2170
+ // for CDN bundling IIFE exports covers this but for npm ESM and CJS bundling has to be done explicitly
2171
+ globalThis.rudderanalytics=this;}catch(error){dispatchErrorEvent(error);}}/**
2172
+ * Create an instance of the current instance that can be used
2173
+ * to call a subset of methods of the current instance.
2174
+ * It is typically used to expose the analytics instance to the integrations (standard and custom)
2175
+ */createSafeAnalyticsInstance(){state.lifecycle.safeAnalyticsInstance.value={page:this.page.bind(this),track:this.track.bind(this),identify:this.identify.bind(this),alias:this.alias.bind(this),group:this.group.bind(this),getAnonymousId:this.getAnonymousId.bind(this),getUserId:this.getUserId.bind(this),getUserTraits:this.getUserTraits.bind(this),getGroupId:this.getGroupId.bind(this),getGroupTraits:this.getGroupTraits.bind(this),getSessionId:this.getSessionId.bind(this)};}static initializeGlobalResources(){// We need to initialize the error handler first to catch any unhandled errors occurring in this module as well
2176
+ defaultErrorHandler.init();// Initialize the storage engines with default options
2177
+ defaultCookieStorage.configure();defaultLocalStorage.configure();defaultSessionStorage.configure();defaultInMemoryStorage.configure();}/**
2178
+ * Set instance to use if no specific writeKey is provided in methods
2179
+ * automatically for the first created instance
2180
+ * TODO: to support multiple analytics instances in the near future
2181
+ */setDefaultInstanceKey(writeKey){// IMP: Add try-catch block to handle any unhandled errors
2182
+ // similar to other public methods
2183
+ // if the implementation of this method goes beyond
2184
+ // this simple implementation
2185
+ if(isString(writeKey)&&writeKey){this.defaultAnalyticsKey=writeKey;}}/**
2186
+ * Retrieve an existing analytics instance
2187
+ */getAnalyticsInstance(writeKey){try{let instanceId=writeKey;if(!isString(instanceId)||!instanceId){instanceId=this.defaultAnalyticsKey;}const analyticsInstanceExists=Boolean(this.analyticsInstances[instanceId]);if(!analyticsInstanceExists){this.analyticsInstances[instanceId]=new Analytics();}return this.analyticsInstances[instanceId];}catch(error){dispatchErrorEvent(error);return undefined;}}/**
2188
+ * Loads the SDK
2189
+ * @param writeKey Source write key
2190
+ * @param dataPlaneUrl Data plane URL
2191
+ * @param loadOptions Additional options for loading the SDK
2192
+ * @returns none
2193
+ */load(writeKey,dataPlaneUrl,loadOptions){try{if(this.analyticsInstances[writeKey]){return;}this.setDefaultInstanceKey(writeKey);// Track page loaded lifecycle event if enabled
2194
+ this.trackPageLifecycleEvents(loadOptions);// Get the preloaded events array from global buffer instead of window.rudderanalytics
2195
+ // as the constructor must have already pushed the events to the global buffer
2196
+ const preloadedEventsArray=getExposedGlobal(GLOBAL_PRELOAD_BUFFER);// The array will be mutated in the below method
2197
+ promotePreloadedConsentEventsToTop(preloadedEventsArray);setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone(preloadedEventsArray));this.getAnalyticsInstance(writeKey)?.load(writeKey,dataPlaneUrl,getSanitizedValue(loadOptions));}catch(error){dispatchErrorEvent(error);}}/**
2198
+ * A function to track page lifecycle events like page loaded and page unloaded
2199
+ * @param loadOptions
2200
+ * @returns
2201
+ */trackPageLifecycleEvents(loadOptions){const{autoTrack,useBeacon}=loadOptions??{};const{enabled:autoTrackEnabled=false,options:autoTrackOptions={},pageLifecycle}=autoTrack??{};const{events=[PageLifecycleEvents.UNLOADED],enabled:pageLifecycleEnabled=autoTrackEnabled,options=autoTrackOptions}=pageLifecycle??{};state.autoTrack.pageLifecycle.enabled.value=pageLifecycleEnabled;// Set the autoTrack enabled state
2202
+ // if at least one of the autoTrack options is enabled
2203
+ // IMPORTANT: make sure this is done at the end as it depends on the above states
2204
+ state.autoTrack.enabled.value=autoTrackEnabled||pageLifecycleEnabled;if(!pageLifecycleEnabled){return;}this.setupPageUnloadTracking(events,useBeacon,options);}/**
2205
+ * Setup page unload tracking if enabled
2206
+ * @param events
2207
+ * @param useBeacon
2208
+ * @param options
2209
+ */setupPageUnloadTracking(events,useBeacon,options){if(events.length===0||events.includes(PageLifecycleEvents.UNLOADED)){if(useBeacon===true){onPageLeave(isAccessible=>{if(isAccessible===false&&state.lifecycle.loaded.value){const pageUnloadedTimestamp=Date.now();const timeOnPage=pageUnloadedTimestamp-state.autoTrack.pageLifecycle.pageLoadedTimestamp.value;this.track(PageLifecycleEvents.UNLOADED,{timeOnPage},{...options,originalTimestamp:getFormattedTimestamp(new Date(pageUnloadedTimestamp))});}},true);}else {// log warning if beacon is disabled
2210
+ this.logger.warn(PAGE_UNLOAD_ON_BEACON_DISABLED_WARNING(RSA));}}}/**
2211
+ * Trigger load event in buffer queue if exists and stores the
2212
+ * remaining preloaded events array in global object
2213
+ */triggerBufferedLoadEvent(){const preloadedEventsArray=Array.isArray(globalThis.rudderanalytics)?globalThis.rudderanalytics:[];// Get any load method call that is buffered if any
2214
+ // BTW, load method is also removed from the array
2215
+ // So, the Analytics object can directly consume the remaining events
2216
+ const loadEvent=getPreloadedLoadEvent(preloadedEventsArray);// Set the final preloaded events array in global object
2217
+ setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone([...preloadedEventsArray]));// Process load method if present in the buffered requests
2218
+ if(loadEvent.length>0){// Remove the event name from the Buffered Event array and keep only arguments
2219
+ loadEvent.shift();// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2220
+ // @ts-ignore
2221
+ this.load.apply(null,loadEvent);}}/**
2222
+ * Get ready callback arguments and forward to ready call
2223
+ */ready(callback){try{this.getAnalyticsInstance()?.ready(getSanitizedValue(callback));}catch(error){dispatchErrorEvent(error);}}/**
2224
+ * Process page arguments and forward to page call
2225
+ */// These overloads should be same as AnalyticsPageMethod in @rudderstack/analytics-js-common/types/IRudderAnalytics
2226
+ page(category,name,properties,options,callback){try{this.getAnalyticsInstance()?.page(pageArgumentsToCallOptions(getSanitizedValue(category),getSanitizedValue(name),getSanitizedValue(properties),getSanitizedValue(options),getSanitizedValue(callback)));}catch(error){dispatchErrorEvent(error);}}/**
2227
+ * Process track arguments and forward to page call
2228
+ */// These overloads should be same as AnalyticsTrackMethod in @rudderstack/analytics-js-common/types/IRudderAnalytics
2229
+ track(event,properties,options,callback){try{this.getAnalyticsInstance()?.track(trackArgumentsToCallOptions(getSanitizedValue(event),getSanitizedValue(properties),getSanitizedValue(options),getSanitizedValue(callback)));}catch(error){dispatchErrorEvent(error);}}/**
2230
+ * Process identify arguments and forward to page call
2231
+ */// These overloads should be same as AnalyticsIdentifyMethod in @rudderstack/analytics-js-common/types/IRudderAnalytics
2232
+ identify(userId,traits,options,callback){try{this.getAnalyticsInstance()?.identify(identifyArgumentsToCallOptions(getSanitizedValue(userId),getSanitizedValue(traits),getSanitizedValue(options),getSanitizedValue(callback)));}catch(error){dispatchErrorEvent(error);}}/**
2233
+ * Process alias arguments and forward to page call
2234
+ */// These overloads should be same as AnalyticsAliasMethod in @rudderstack/analytics-js-common/types/IRudderAnalytics
2235
+ alias(to,from,options,callback){try{this.getAnalyticsInstance()?.alias(aliasArgumentsToCallOptions(getSanitizedValue(to),getSanitizedValue(from),getSanitizedValue(options),getSanitizedValue(callback)));}catch(error){dispatchErrorEvent(error);}}/**
2236
+ * Process group arguments and forward to page call
2237
+ */// These overloads should be same as AnalyticsGroupMethod in @rudderstack/analytics-js-common/types/IRudderAnalytics
2238
+ group(groupId,traits,options,callback){try{this.getAnalyticsInstance()?.group(groupArgumentsToCallOptions(getSanitizedValue(groupId),getSanitizedValue(traits),getSanitizedValue(options),getSanitizedValue(callback)));}catch(error){dispatchErrorEvent(error);}}/**
2239
+ * Reset the analytics instance
2240
+ * @param options Reset options. Except for anonymousId, initialReferrer, and initialReferringDomain
2241
+ * all other values will be reset by default.
2242
+ * @example
2243
+ * ```ts
2244
+ * reset({
2245
+ * entries: {
2246
+ * anonymousId: true,
2247
+ * }
2248
+ * });
2249
+ * ```
2250
+ * @example
2251
+ * ```ts
2252
+ * reset({
2253
+ * entries: {
2254
+ * userId: false,
2255
+ * sessionInfo: false,
2256
+ * }
2257
+ * });
2258
+ * ```
2259
+ * @returns none
2260
+ *//**
2261
+ * Reset the analytics instance
2262
+ * @param resetAnonymousId Reset anonymous ID
2263
+ * @returns none
2264
+ * @deprecated Use reset(options) instead
2265
+ */reset(options){try{this.getAnalyticsInstance()?.reset(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);}}getAnonymousId(options){try{return this.getAnalyticsInstance()?.getAnonymousId(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);return undefined;}}setAnonymousId(anonymousId,rudderAmpLinkerParam){try{this.getAnalyticsInstance()?.setAnonymousId(getSanitizedValue(anonymousId),getSanitizedValue(rudderAmpLinkerParam));}catch(error){dispatchErrorEvent(error);}}getUserId(){try{return this.getAnalyticsInstance()?.getUserId();}catch(error){dispatchErrorEvent(error);return undefined;}}getUserTraits(){try{return this.getAnalyticsInstance()?.getUserTraits();}catch(error){dispatchErrorEvent(error);return undefined;}}getGroupId(){try{return this.getAnalyticsInstance()?.getGroupId();}catch(error){dispatchErrorEvent(error);return undefined;}}getGroupTraits(){try{return this.getAnalyticsInstance()?.getGroupTraits();}catch(error){dispatchErrorEvent(error);return undefined;}}startSession(sessionId){try{this.getAnalyticsInstance()?.startSession(getSanitizedValue(sessionId));}catch(error){dispatchErrorEvent(error);}}endSession(){try{this.getAnalyticsInstance()?.endSession();}catch(error){dispatchErrorEvent(error);}}getSessionId(){try{return this.getAnalyticsInstance()?.getSessionId();}catch(error){dispatchErrorEvent(error);return undefined;}}setAuthToken(token){try{this.getAnalyticsInstance()?.setAuthToken(getSanitizedValue(token));}catch(error){dispatchErrorEvent(error);}}consent(options){try{this.getAnalyticsInstance()?.consent(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);}}addCustomIntegration(destinationId,integration){try{this.getAnalyticsInstance()?.addCustomIntegration(getSanitizedValue(destinationId),getSanitizedValue(integration));}catch(error){dispatchErrorEvent(error);}}}
2266
+
2267
+ exports.RudderAnalytics = RudderAnalytics;
2268
+
2269
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2270
+
2271
+ }));