@rudderstack/analytics-js 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/index.d.ts +358 -0
- package/legacy/cjs/index.js +3881 -0
- package/legacy/esm/index.js +3874 -0
- package/legacy/umd/index.js +3887 -0
- package/modern/cjs/index.js +1728 -0
- package/modern/esm/index.js +1719 -0
- package/modern/umd/index.js +1734 -0
- package/package.json +183 -0
@@ -0,0 +1,1719 @@
|
|
1
|
+
let UaChTrackLevel=/*#__PURE__*/function(UaChTrackLevel){UaChTrackLevel["None"]="none";UaChTrackLevel["Default"]="default";UaChTrackLevel["Full"]="full";return UaChTrackLevel;}({});/**
|
2
|
+
* Represents the options parameter for anonymousId
|
3
|
+
*/ /**
|
4
|
+
* Represents the beacon queue options parameter in loadOptions type
|
5
|
+
*/let CookieSameSite=/*#__PURE__*/function(CookieSameSite){CookieSameSite["Strict"]="Strict";CookieSameSite["Lax"]="Lax";CookieSameSite["None"]="None";return CookieSameSite;}({});/**
|
6
|
+
* Represents the queue options parameter in loadOptions type
|
7
|
+
*/ /**
|
8
|
+
* Represents the destinations queue options parameter in loadOptions type
|
9
|
+
*/ /**
|
10
|
+
* Represents the options parameter in the load API
|
11
|
+
*/
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Represents residency server input the options
|
15
|
+
*/let ResidencyServerRegion=/*#__PURE__*/function(ResidencyServerRegion){ResidencyServerRegion["US"]="US";ResidencyServerRegion["EU"]="EU";return ResidencyServerRegion;}({});
|
16
|
+
|
17
|
+
let LogLevel=/*#__PURE__*/function(LogLevel){LogLevel["Log"]="LOG";LogLevel["Info"]="INFO";LogLevel["Debug"]="DEBUG";LogLevel["Warn"]="WARN";LogLevel["Error"]="ERROR";LogLevel["None"]="NONE";return LogLevel;}({});
|
18
|
+
|
19
|
+
let PluginName=/*#__PURE__*/function(PluginName){PluginName["BeaconQueue"]="BeaconQueue";PluginName["DeviceModeDestinations"]="DeviceModeDestinations";PluginName["DeviceModeTransformation"]="DeviceModeTransformation";PluginName["ErrorReporting"]="ErrorReporting";PluginName["ExternalAnonymousId"]="ExternalAnonymousId";PluginName["GoogleLinker"]="GoogleLinker";PluginName["NativeDestinationQueue"]="NativeDestinationQueue";PluginName["StorageEncryption"]="StorageEncryption";PluginName["StorageEncryptionLegacy"]="StorageEncryptionLegacy";PluginName["StorageMigrator"]="StorageMigrator";PluginName["XhrQueue"]="XhrQueue";PluginName["OneTrustConsentManager"]="OneTrustConsentManager";PluginName["KetchConsentManager"]="KetchConsentManager";PluginName["Bugsnag"]="Bugsnag";return PluginName;}({});
|
20
|
+
|
21
|
+
function _isPlaceholder(a){return a!=null&&typeof a==='object'&&a['@@functional/placeholder']===true;}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Optimized internal one-arity curry function.
|
25
|
+
*
|
26
|
+
* @private
|
27
|
+
* @category Function
|
28
|
+
* @param {Function} fn The function to curry.
|
29
|
+
* @return {Function} The curried function.
|
30
|
+
*/function _curry1(fn){return function f1(a){if(arguments.length===0||_isPlaceholder(a)){return f1;}else {return fn.apply(this,arguments);}};}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Optimized internal two-arity curry function.
|
34
|
+
*
|
35
|
+
* @private
|
36
|
+
* @category Function
|
37
|
+
* @param {Function} fn The function to curry.
|
38
|
+
* @return {Function} The curried function.
|
39
|
+
*/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);}};}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Optimized internal three-arity curry function.
|
43
|
+
*
|
44
|
+
* @private
|
45
|
+
* @category Function
|
46
|
+
* @param {Function} fn The function to curry.
|
47
|
+
* @return {Function} The curried function.
|
48
|
+
*/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);}};}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Tests whether or not an object is an array.
|
52
|
+
*
|
53
|
+
* @private
|
54
|
+
* @param {*} val The object to test.
|
55
|
+
* @return {Boolean} `true` if `val` is an array, `false` otherwise.
|
56
|
+
* @example
|
57
|
+
*
|
58
|
+
* _isArray([]); //=> true
|
59
|
+
* _isArray(null); //=> false
|
60
|
+
* _isArray({}); //=> false
|
61
|
+
*/const _isArray = Array.isArray||function _isArray(val){return val!=null&&val.length>=0&&Object.prototype.toString.call(val)==='[object Array]';};
|
62
|
+
|
63
|
+
function _arrayFromIterator(iter){var list=[];var next;while(!(next=iter.next()).done){list.push(next.value);}return list;}
|
64
|
+
|
65
|
+
function _includesWith(pred,x,list){var idx=0;var len=list.length;while(idx<len){if(pred(x,list[idx])){return true;}idx+=1;}return false;}
|
66
|
+
|
67
|
+
function _functionName(f){// String(x => x) evaluates to "x => x", so the pattern may not match.
|
68
|
+
var match=String(f).match(/^function (\w*)/);return match==null?'':match[1];}
|
69
|
+
|
70
|
+
function _has(prop,obj){return Object.prototype.hasOwnProperty.call(obj,prop);}
|
71
|
+
|
72
|
+
// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
73
|
+
function _objectIs(a,b){// SameValue algorithm
|
74
|
+
if(a===b){// Steps 1-5, 7-10
|
75
|
+
// Steps 6.b-6.e: +0 != -0
|
76
|
+
return a!==0||1/a===1/b;}else {// Step 6.a: NaN == NaN
|
77
|
+
return a!==a&&b!==b;}}const _objectIs$1 = typeof Object.is==='function'?Object.is:_objectIs;
|
78
|
+
|
79
|
+
var toString=Object.prototype.toString;var _isArguments=/*#__PURE__*/function(){return toString.call(arguments)==='[object Arguments]'?function _isArguments(x){return toString.call(x)==='[object Arguments]';}:function _isArguments(x){return _has('callee',x);};}();
|
80
|
+
|
81
|
+
var hasEnumBug=!/*#__PURE__*/{toString:null}.propertyIsEnumerable('toString');var nonEnumerableProps=['constructor','valueOf','isPrototypeOf','toString','propertyIsEnumerable','hasOwnProperty','toLocaleString'];// Safari bug
|
82
|
+
var hasArgsEnumBug=/*#__PURE__*/function(){return arguments.propertyIsEnumerable('length');}();var contains=function contains(list,item){var idx=0;while(idx<list.length){if(list[idx]===item){return true;}idx+=1;}return false;};/**
|
83
|
+
* Returns a list containing the names of all the enumerable own properties of
|
84
|
+
* the supplied object.
|
85
|
+
* Note that the order of the output array is not guaranteed to be consistent
|
86
|
+
* across different JS platforms.
|
87
|
+
*
|
88
|
+
* @func
|
89
|
+
* @memberOf R
|
90
|
+
* @since v0.1.0
|
91
|
+
* @category Object
|
92
|
+
* @sig {k: v} -> [k]
|
93
|
+
* @param {Object} obj The object to extract properties from
|
94
|
+
* @return {Array} An array of the object's own properties.
|
95
|
+
* @see R.keysIn, R.values, R.toPairs
|
96
|
+
* @example
|
97
|
+
*
|
98
|
+
* R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c']
|
99
|
+
*/var keys=typeof Object.keys==='function'&&!hasArgsEnumBug?/*#__PURE__*/_curry1(function keys(obj){return Object(obj)!==obj?[]:Object.keys(obj);}):/*#__PURE__*/_curry1(function keys(obj){if(Object(obj)!==obj){return [];}var prop,nIdx;var ks=[];var checkArgsLength=hasArgsEnumBug&&_isArguments(obj);for(prop in obj){if(_has(prop,obj)&&(!checkArgsLength||prop!=='length')){ks[ks.length]=prop;}}if(hasEnumBug){nIdx=nonEnumerableProps.length-1;while(nIdx>=0){prop=nonEnumerableProps[nIdx];if(_has(prop,obj)&&!contains(ks,prop)){ks[ks.length]=prop;}nIdx-=1;}}return ks;});
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Gives a single-word string description of the (native) type of a value,
|
103
|
+
* returning such answers as 'Object', 'Number', 'Array', or 'Null'. Does not
|
104
|
+
* attempt to distinguish user Object types any further, reporting them all as
|
105
|
+
* 'Object'.
|
106
|
+
*
|
107
|
+
* @func
|
108
|
+
* @memberOf R
|
109
|
+
* @since v0.8.0
|
110
|
+
* @category Type
|
111
|
+
* @sig * -> String
|
112
|
+
* @param {*} val The value to test
|
113
|
+
* @return {String}
|
114
|
+
* @example
|
115
|
+
*
|
116
|
+
* R.type({}); //=> "Object"
|
117
|
+
* R.type(1); //=> "Number"
|
118
|
+
* R.type(false); //=> "Boolean"
|
119
|
+
* R.type('s'); //=> "String"
|
120
|
+
* R.type(null); //=> "Null"
|
121
|
+
* R.type([]); //=> "Array"
|
122
|
+
* R.type(/[A-z]/); //=> "RegExp"
|
123
|
+
* R.type(() => {}); //=> "Function"
|
124
|
+
* R.type(undefined); //=> "Undefined"
|
125
|
+
*/var type=/*#__PURE__*/_curry1(function type(val){return val===null?'Null':val===undefined?'Undefined':Object.prototype.toString.call(val).slice(8,-1);});
|
126
|
+
|
127
|
+
/**
|
128
|
+
* private _uniqContentEquals function.
|
129
|
+
* That function is checking equality of 2 iterator contents with 2 assumptions
|
130
|
+
* - iterators lengths are the same
|
131
|
+
* - iterators values are unique
|
132
|
+
*
|
133
|
+
* false-positive result will be returned for comparison of, e.g.
|
134
|
+
* - [1,2,3] and [1,2,3,4]
|
135
|
+
* - [1,1,1] and [1,2,3]
|
136
|
+
* */function _uniqContentEquals(aIterator,bIterator,stackA,stackB){var a=_arrayFromIterator(aIterator);var b=_arrayFromIterator(bIterator);function eq(_a,_b){return _equals(_a,_b,stackA.slice(),stackB.slice());}// if *a* array contains any element that is not included in *b*
|
137
|
+
return !_includesWith(function(b,aItem){return !_includesWith(eq,aItem,b);},b,a);}function _equals(a,b,stackA,stackB){if(_objectIs$1(a,b)){return true;}var typeA=type(a);if(typeA!==type(b)){return false;}if(typeof a['fantasy-land/equals']==='function'||typeof b['fantasy-land/equals']==='function'){return typeof a['fantasy-land/equals']==='function'&&a['fantasy-land/equals'](b)&&typeof b['fantasy-land/equals']==='function'&&b['fantasy-land/equals'](a);}if(typeof a.equals==='function'||typeof b.equals==='function'){return typeof a.equals==='function'&&a.equals(b)&&typeof b.equals==='function'&&b.equals(a);}switch(typeA){case'Arguments':case'Array':case'Object':if(typeof a.constructor==='function'&&_functionName(a.constructor)==='Promise'){return a===b;}break;case'Boolean':case'Number':case'String':if(!(typeof a===typeof b&&_objectIs$1(a.valueOf(),b.valueOf()))){return false;}break;case'Date':if(!_objectIs$1(a.valueOf(),b.valueOf())){return false;}break;case'Error':return a.name===b.name&&a.message===b.message;case'RegExp':if(!(a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline&&a.sticky===b.sticky&&a.unicode===b.unicode)){return false;}break;}var idx=stackA.length-1;while(idx>=0){if(stackA[idx]===a){return stackB[idx]===b;}idx-=1;}switch(typeA){case'Map':if(a.size!==b.size){return false;}return _uniqContentEquals(a.entries(),b.entries(),stackA.concat([a]),stackB.concat([b]));case'Set':if(a.size!==b.size){return false;}return _uniqContentEquals(a.values(),b.values(),stackA.concat([a]),stackB.concat([b]));case'Arguments':case'Array':case'Object':case'Boolean':case'Number':case'String':case'Date':case'Error':case'RegExp':case'Int8Array':case'Uint8Array':case'Uint8ClampedArray':case'Int16Array':case'Uint16Array':case'Int32Array':case'Uint32Array':case'Float32Array':case'Float64Array':case'ArrayBuffer':break;default:// Values of other types are only equal if identical.
|
138
|
+
return false;}var keysA=keys(a);if(keysA.length!==keys(b).length){return false;}var extendedStackA=stackA.concat([a]);var extendedStackB=stackB.concat([b]);idx=keysA.length-1;while(idx>=0){var key=keysA[idx];if(!(_has(key,b)&&_equals(b[key],a[key],extendedStackA,extendedStackB))){return false;}idx-=1;}return true;}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Returns `true` if its arguments are equivalent, `false` otherwise. Handles
|
142
|
+
* cyclical data structures.
|
143
|
+
*
|
144
|
+
* Dispatches symmetrically to the `equals` methods of both arguments, if
|
145
|
+
* present.
|
146
|
+
*
|
147
|
+
* @func
|
148
|
+
* @memberOf R
|
149
|
+
* @since v0.15.0
|
150
|
+
* @category Relation
|
151
|
+
* @sig a -> b -> Boolean
|
152
|
+
* @param {*} a
|
153
|
+
* @param {*} b
|
154
|
+
* @return {Boolean}
|
155
|
+
* @example
|
156
|
+
*
|
157
|
+
* R.equals(1, 1); //=> true
|
158
|
+
* R.equals(1, '1'); //=> false
|
159
|
+
* R.equals([1, 2, 3], [1, 2, 3]); //=> true
|
160
|
+
*
|
161
|
+
* const a = {}; a.v = a;
|
162
|
+
* const b = {}; b.v = b;
|
163
|
+
* R.equals(a, b); //=> true
|
164
|
+
*/var equals=/*#__PURE__*/_curry2(function equals(a,b){return _equals(a,b,[],[]);});
|
165
|
+
|
166
|
+
function _isObject(x){return Object.prototype.toString.call(x)==='[object Object]';}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Determine if the passed argument is an integer.
|
170
|
+
*
|
171
|
+
* @private
|
172
|
+
* @param {*} n
|
173
|
+
* @category Type
|
174
|
+
* @return {Boolean}
|
175
|
+
*/const _isInteger = Number.isInteger||function _isInteger(n){return n<<0===n;};
|
176
|
+
|
177
|
+
function _isString(x){return Object.prototype.toString.call(x)==='[object String]';}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Returns the nth element of the given list or string. If n is negative the
|
181
|
+
* element at index length + n is returned.
|
182
|
+
*
|
183
|
+
* @func
|
184
|
+
* @memberOf R
|
185
|
+
* @since v0.1.0
|
186
|
+
* @category List
|
187
|
+
* @sig Number -> [a] -> a | Undefined
|
188
|
+
* @sig Number -> String -> String
|
189
|
+
* @param {Number} offset
|
190
|
+
* @param {*} list
|
191
|
+
* @return {*}
|
192
|
+
* @example
|
193
|
+
*
|
194
|
+
* const list = ['foo', 'bar', 'baz', 'quux'];
|
195
|
+
* R.nth(1, list); //=> 'bar'
|
196
|
+
* R.nth(-1, list); //=> 'quux'
|
197
|
+
* R.nth(-99, list); //=> undefined
|
198
|
+
*
|
199
|
+
* R.nth(2, 'abc'); //=> 'c'
|
200
|
+
* R.nth(3, 'abc'); //=> ''
|
201
|
+
* @symb R.nth(-1, [a, b, c]) = c
|
202
|
+
* @symb R.nth(0, [a, b, c]) = a
|
203
|
+
* @symb R.nth(1, [a, b, c]) = b
|
204
|
+
*/var nth=/*#__PURE__*/_curry2(function nth(offset,list){var idx=offset<0?list.length+offset:offset;return _isString(list)?list.charAt(idx):list[idx];});
|
205
|
+
|
206
|
+
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':''));}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Copies an object.
|
210
|
+
*
|
211
|
+
* @private
|
212
|
+
* @param {*} value The value to be copied
|
213
|
+
* @param {Boolean} deep Whether or not to perform deep cloning.
|
214
|
+
* @return {*} The copied value.
|
215
|
+
*/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.
|
216
|
+
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.
|
217
|
+
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]=deep?_clone(value[key],true,map):value[key];}}return copiedValue;};switch(type(value)){case'Object':return copy(Object.create(Object.getPrototypeOf(value)));case'Array':return copy([]);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){const hashedKey=this.hash(key);let bucket=this.map[hashedKey];if(!bucket){this.map[hashedKey]=bucket=[];}bucket.push([key,value]);this.length+=1;};_ObjectMap.prototype.hash=function(key){let hashedKey=[];for(var value in key){hashedKey.push(Object.prototype.toString.call(key[value]));}return hashedKey.join();};_ObjectMap.prototype.get=function(key){/**
|
218
|
+
* 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,
|
219
|
+
* on my tests this number is 180, anything above that using the hash function is faster.
|
220
|
+
*/if(this.length<=180){for(const p in this.map){const bucket=this.map[p];for(let i=0;i<bucket.length;i+=1){const element=bucket[i];if(element[0]===key){return element[1];}}}return;}const hashedKey=this.hash(key);const bucket=this.map[hashedKey];if(!bucket){return;}for(let i=0;i<bucket.length;i+=1){const element=bucket[i];if(element[0]===key){return element[1];}}};return _ObjectMap;}();
|
221
|
+
|
222
|
+
/**
|
223
|
+
* Creates a deep copy of the source that can be used in place of the source
|
224
|
+
* object without retaining any references to it.
|
225
|
+
* The source object may contain (nested) `Array`s and `Object`s,
|
226
|
+
* `Number`s, `String`s, `Boolean`s and `Date`s.
|
227
|
+
* `Function`s are assigned by reference rather than copied.
|
228
|
+
*
|
229
|
+
* Dispatches to a `clone` method if present.
|
230
|
+
*
|
231
|
+
* Note that if the source object has multiple nodes that share a reference,
|
232
|
+
* the returned object will have the same structure, but the references will
|
233
|
+
* be pointed to the location within the cloned value.
|
234
|
+
*
|
235
|
+
* @func
|
236
|
+
* @memberOf R
|
237
|
+
* @since v0.1.0
|
238
|
+
* @category Object
|
239
|
+
* @sig {*} -> {*}
|
240
|
+
* @param {*} value The object or array to clone
|
241
|
+
* @return {*} A deeply cloned copy of `val`
|
242
|
+
* @example
|
243
|
+
*
|
244
|
+
* const objects = [{}, {}, {}];
|
245
|
+
* const objectsClone = R.clone(objects);
|
246
|
+
* objects === objectsClone; //=> false
|
247
|
+
* objects[0] === objectsClone[0]; //=> false
|
248
|
+
*/var clone=/*#__PURE__*/_curry1(function clone(value){return value!=null&&typeof value.clone==='function'?value.clone():_clone(value,true);});const clone$1 = clone;
|
249
|
+
|
250
|
+
/**
|
251
|
+
* Tests whether or not an object is a typed array.
|
252
|
+
*
|
253
|
+
* @private
|
254
|
+
* @param {*} val The object to test.
|
255
|
+
* @return {Boolean} `true` if `val` is a typed array, `false` otherwise.
|
256
|
+
* @example
|
257
|
+
*
|
258
|
+
* _isTypedArray(new Uint8Array([])); //=> true
|
259
|
+
* _isTypedArray(new Float32Array([])); //=> true
|
260
|
+
* _isTypedArray([]); //=> false
|
261
|
+
* _isTypedArray(null); //=> false
|
262
|
+
* _isTypedArray({}); //=> false
|
263
|
+
*/function _isTypedArray(val){var type=Object.prototype.toString.call(val);return type==='[object Uint8ClampedArray]'||type==='[object Int8Array]'||type==='[object Uint8Array]'||type==='[object Int16Array]'||type==='[object Uint16Array]'||type==='[object Int32Array]'||type==='[object Uint32Array]'||type==='[object Float32Array]'||type==='[object Float64Array]'||type==='[object BigInt64Array]'||type==='[object BigUint64Array]';}
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Returns the empty value of its argument's type. Ramda defines the empty
|
267
|
+
* value of Array (`[]`), Object (`{}`), String (`''`),
|
268
|
+
* TypedArray (`Uint8Array []`, `Float32Array []`, etc), and Arguments. Other
|
269
|
+
* types are supported if they define `<Type>.empty`,
|
270
|
+
* `<Type>.prototype.empty` or implement the
|
271
|
+
* [FantasyLand Monoid spec](https://github.com/fantasyland/fantasy-land#monoid).
|
272
|
+
*
|
273
|
+
* Dispatches to the `empty` method of the first argument, if present.
|
274
|
+
*
|
275
|
+
* @func
|
276
|
+
* @memberOf R
|
277
|
+
* @since v0.3.0
|
278
|
+
* @category Function
|
279
|
+
* @sig a -> a
|
280
|
+
* @param {*} x
|
281
|
+
* @return {*}
|
282
|
+
* @example
|
283
|
+
*
|
284
|
+
* R.empty(Just(42)); //=> Nothing()
|
285
|
+
* R.empty([1, 2, 3]); //=> []
|
286
|
+
* R.empty('unicorns'); //=> ''
|
287
|
+
* R.empty({x: 1, y: 2}); //=> {}
|
288
|
+
* R.empty(Uint8Array.from('123')); //=> Uint8Array []
|
289
|
+
*/var empty=/*#__PURE__*/_curry1(function empty(x){return x!=null&&typeof x['fantasy-land/empty']==='function'?x['fantasy-land/empty']():x!=null&&x.constructor!=null&&typeof x.constructor['fantasy-land/empty']==='function'?x.constructor['fantasy-land/empty']():x!=null&&typeof x.empty==='function'?x.empty():x!=null&&x.constructor!=null&&typeof x.constructor.empty==='function'?x.constructor.empty():_isArray(x)?[]:_isString(x)?'':_isObject(x)?{}:_isArguments(x)?function(){return arguments;}():_isTypedArray(x)?x.constructor.from(''):void 0// else
|
290
|
+
;});
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Returns `true` if the given value is its type's empty value; `false`
|
294
|
+
* otherwise.
|
295
|
+
*
|
296
|
+
* @func
|
297
|
+
* @memberOf R
|
298
|
+
* @since v0.1.0
|
299
|
+
* @category Logic
|
300
|
+
* @sig a -> Boolean
|
301
|
+
* @param {*} x
|
302
|
+
* @return {Boolean}
|
303
|
+
* @see R.empty
|
304
|
+
* @example
|
305
|
+
*
|
306
|
+
* R.isEmpty([1, 2, 3]); //=> false
|
307
|
+
* R.isEmpty([]); //=> true
|
308
|
+
* R.isEmpty(''); //=> true
|
309
|
+
* R.isEmpty(null); //=> false
|
310
|
+
* R.isEmpty({}); //=> true
|
311
|
+
* R.isEmpty({length: 0}); //=> false
|
312
|
+
* R.isEmpty(Uint8Array.from('')); //=> true
|
313
|
+
*/var isEmpty=/*#__PURE__*/_curry1(function isEmpty(x){return x!=null&&equals(x,empty(x));});const isEmpty$1 = isEmpty;
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Retrieves the values at given paths of an object.
|
317
|
+
*
|
318
|
+
* @func
|
319
|
+
* @memberOf R
|
320
|
+
* @since v0.27.1
|
321
|
+
* @category Object
|
322
|
+
* @typedefn Idx = [String | Int | Symbol]
|
323
|
+
* @sig [Idx] -> {a} -> [a | Undefined]
|
324
|
+
* @param {Array} pathsArray The array of paths to be fetched.
|
325
|
+
* @param {Object} obj The object to retrieve the nested properties from.
|
326
|
+
* @return {Array} A list consisting of values at paths specified by "pathsArray".
|
327
|
+
* @see R.path
|
328
|
+
* @example
|
329
|
+
*
|
330
|
+
* R.paths([['a', 'b'], ['p', 0, 'q']], {a: {b: 2}, p: [{q: 3}]}); //=> [2, 3]
|
331
|
+
* R.paths([['a', 'b'], ['p', 'r']], {a: {b: 2}, p: [{q: 3}]}); //=> [2, undefined]
|
332
|
+
*/var paths=/*#__PURE__*/_curry2(function paths(pathsArray,obj){return pathsArray.map(function(paths){var val=obj;var idx=0;var p;while(idx<paths.length){if(val==null){return;}p=paths[idx];val=_isInteger(p)?nth(p,val):val[p];idx+=1;}return val;});});
|
333
|
+
|
334
|
+
/**
|
335
|
+
* Retrieves the value at a given path. The nodes of the path can be arbitrary strings or non-negative integers.
|
336
|
+
* For anything else, the value is unspecified. Integer paths are meant to index arrays, strings are meant for objects.
|
337
|
+
*
|
338
|
+
* @func
|
339
|
+
* @memberOf R
|
340
|
+
* @since v0.2.0
|
341
|
+
* @category Object
|
342
|
+
* @typedefn Idx = String | Int | Symbol
|
343
|
+
* @sig [Idx] -> {a} -> a | Undefined
|
344
|
+
* @sig Idx = String | NonNegativeInt
|
345
|
+
* @param {Array} path The path to use.
|
346
|
+
* @param {Object} obj The object or array to retrieve the nested property from.
|
347
|
+
* @return {*} The data at `path`.
|
348
|
+
* @see R.prop, R.nth, R.assocPath, R.dissocPath
|
349
|
+
* @example
|
350
|
+
*
|
351
|
+
* R.path(['a', 'b'], {a: {b: 2}}); //=> 2
|
352
|
+
* R.path(['a', 'b'], {c: {b: 2}}); //=> undefined
|
353
|
+
* R.path(['a', 'b', 0], {a: {b: [1, 2, 3]}}); //=> 1
|
354
|
+
* R.path(['a', 'b', -2], {a: {b: [1, 2, 3]}}); //=> 2
|
355
|
+
* R.path([2], {'2': 2}); //=> 2
|
356
|
+
* R.path([-2], {'-2': 'a'}); //=> undefined
|
357
|
+
*/var path=/*#__PURE__*/_curry2(function path(pathAr,obj){return paths([pathAr],obj)[0];});const path$1 = path;
|
358
|
+
|
359
|
+
/**
|
360
|
+
* Creates a new object with the own properties of the two provided objects. If
|
361
|
+
* a key exists in both objects, the provided function is applied to the key
|
362
|
+
* and the values associated with the key in each object, with the result being
|
363
|
+
* used as the value associated with the key in the returned object.
|
364
|
+
*
|
365
|
+
* @func
|
366
|
+
* @memberOf R
|
367
|
+
* @since v0.19.0
|
368
|
+
* @category Object
|
369
|
+
* @sig ((String, a, a) -> a) -> {a} -> {a} -> {a}
|
370
|
+
* @param {Function} fn
|
371
|
+
* @param {Object} l
|
372
|
+
* @param {Object} r
|
373
|
+
* @return {Object}
|
374
|
+
* @see R.mergeDeepWithKey, R.merge, R.mergeWith
|
375
|
+
* @example
|
376
|
+
*
|
377
|
+
* let concatValues = (k, l, r) => k == 'values' ? R.concat(l, r) : r
|
378
|
+
* R.mergeWithKey(concatValues,
|
379
|
+
* { a: true, thing: 'foo', values: [10, 20] },
|
380
|
+
* { b: true, thing: 'bar', values: [15, 35] });
|
381
|
+
* //=> { a: true, b: true, thing: 'bar', values: [10, 20, 15, 35] }
|
382
|
+
* @symb R.mergeWithKey(f, { x: 1, y: 2 }, { y: 5, z: 3 }) = { x: 1, y: f('y', 2, 5), z: 3 }
|
383
|
+
*/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;});
|
384
|
+
|
385
|
+
/**
|
386
|
+
* Creates a new object with the own properties of the two provided objects.
|
387
|
+
* If a key exists in both objects:
|
388
|
+
* - and both associated values are also objects then the values will be
|
389
|
+
* recursively merged.
|
390
|
+
* - otherwise the provided function is applied to the key and associated values
|
391
|
+
* using the resulting value as the new value associated with the key.
|
392
|
+
* If a key only exists in one object, the value will be associated with the key
|
393
|
+
* of the resulting object.
|
394
|
+
*
|
395
|
+
* @func
|
396
|
+
* @memberOf R
|
397
|
+
* @since v0.24.0
|
398
|
+
* @category Object
|
399
|
+
* @sig ((String, a, a) -> a) -> {a} -> {a} -> {a}
|
400
|
+
* @param {Function} fn
|
401
|
+
* @param {Object} lObj
|
402
|
+
* @param {Object} rObj
|
403
|
+
* @return {Object}
|
404
|
+
* @see R.mergeWithKey, R.mergeDeepWith
|
405
|
+
* @example
|
406
|
+
*
|
407
|
+
* let concatValues = (k, l, r) => k == 'values' ? R.concat(l, r) : r
|
408
|
+
* R.mergeDeepWithKey(concatValues,
|
409
|
+
* { a: true, c: { thing: 'foo', values: [10, 20] }},
|
410
|
+
* { b: true, c: { thing: 'bar', values: [15, 35] }});
|
411
|
+
* //=> { a: true, b: true, c: { thing: 'bar', values: [10, 20, 15, 35] }}
|
412
|
+
*/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);});
|
413
|
+
|
414
|
+
/**
|
415
|
+
* Creates a new object with the own properties of the two provided objects.
|
416
|
+
* If a key exists in both objects:
|
417
|
+
* - and both associated values are also objects then the values will be
|
418
|
+
* recursively merged.
|
419
|
+
* - otherwise the provided function is applied to associated values using the
|
420
|
+
* resulting value as the new value associated with the key.
|
421
|
+
* If a key only exists in one object, the value will be associated with the key
|
422
|
+
* of the resulting object.
|
423
|
+
*
|
424
|
+
* @func
|
425
|
+
* @memberOf R
|
426
|
+
* @since v0.24.0
|
427
|
+
* @category Object
|
428
|
+
* @sig ((a, a) -> a) -> {a} -> {a} -> {a}
|
429
|
+
* @param {Function} fn
|
430
|
+
* @param {Object} lObj
|
431
|
+
* @param {Object} rObj
|
432
|
+
* @return {Object}
|
433
|
+
* @see R.mergeWith, R.mergeDeepWithKey
|
434
|
+
* @example
|
435
|
+
*
|
436
|
+
* R.mergeDeepWith(R.concat,
|
437
|
+
* { a: true, c: { values: [10, 20] }},
|
438
|
+
* { b: true, c: { values: [15, 35] }});
|
439
|
+
* //=> { a: true, b: true, c: { values: [10, 20, 15, 35] }}
|
440
|
+
*/var mergeDeepWith=/*#__PURE__*/_curry3(function mergeDeepWith(fn,lObj,rObj){return mergeDeepWithKey(function(k,lVal,rVal){return fn(lVal,rVal);},lObj,rObj);});const mergeDeepWith$1 = mergeDeepWith;
|
441
|
+
|
442
|
+
/**
|
443
|
+
* Returns a partial copy of an object containing only the keys that satisfy
|
444
|
+
* the supplied predicate.
|
445
|
+
*
|
446
|
+
* @func
|
447
|
+
* @memberOf R
|
448
|
+
* @since v0.8.0
|
449
|
+
* @category Object
|
450
|
+
* @sig ((v, k) -> Boolean) -> {k: v} -> {k: v}
|
451
|
+
* @param {Function} pred A predicate to determine whether or not a key
|
452
|
+
* should be included on the output object.
|
453
|
+
* @param {Object} obj The object to copy from
|
454
|
+
* @return {Object} A new object with only properties that satisfy `pred`
|
455
|
+
* on it.
|
456
|
+
* @see R.pick, R.filter
|
457
|
+
* @example
|
458
|
+
*
|
459
|
+
* const isUpperCase = (val, key) => key.toUpperCase() === key;
|
460
|
+
* R.pickBy(isUpperCase, {a: 1, b: 2, A: 3, B: 4}); //=> {A: 3, B: 4}
|
461
|
+
*/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;});const pickBy$1 = pickBy;
|
462
|
+
|
463
|
+
/**
|
464
|
+
* A function to check given value is a function
|
465
|
+
* @param value input value
|
466
|
+
* @returns boolean
|
467
|
+
*/const isFunction=value=>typeof value==='function'&&Boolean(value.constructor&&value.call&&value.apply);/**
|
468
|
+
* A function to check given value is a string
|
469
|
+
* @param value input value
|
470
|
+
* @returns boolean
|
471
|
+
*/const isString=value=>typeof value==='string';/**
|
472
|
+
* A function to check given value is null or not
|
473
|
+
* @param value input value
|
474
|
+
* @returns boolean
|
475
|
+
*/const isNull=value=>value===null;/**
|
476
|
+
* A function to check given value is undefined
|
477
|
+
* @param value input value
|
478
|
+
* @returns boolean
|
479
|
+
*/const isUndefined=value=>typeof value==='undefined';/**
|
480
|
+
* A function to check given value is null or undefined
|
481
|
+
* @param value input value
|
482
|
+
* @returns boolean
|
483
|
+
*/const isNullOrUndefined=value=>isNull(value)||isUndefined(value);/**
|
484
|
+
* A function to check given value is defined
|
485
|
+
* @param value input value
|
486
|
+
* @returns boolean
|
487
|
+
*/const isDefined=value=>!isUndefined(value);/**
|
488
|
+
* A function to check given value is defined and not null
|
489
|
+
* @param value input value
|
490
|
+
* @returns boolean
|
491
|
+
*/const isDefinedAndNotNull=value=>!isNullOrUndefined(value);/**
|
492
|
+
* Determines if the input is an instance of Error
|
493
|
+
* @param obj input value
|
494
|
+
* @returns true if the input is an instance of Error and false otherwise
|
495
|
+
*/const isTypeOfError=obj=>obj instanceof Error;
|
496
|
+
|
497
|
+
const getValueByPath=(obj,keyPath)=>{const pathParts=keyPath.split('.');return path$1(pathParts,obj);};const hasValueByPath=(obj,path)=>Boolean(getValueByPath(obj,path));/**
|
498
|
+
* Checks if the input is an object literal or built-in object type and not null
|
499
|
+
* @param value Input value
|
500
|
+
* @returns true if the input is an object and not null
|
501
|
+
*/const isObjectAndNotNull=value=>!isNull(value)&&typeof value==='object'&&!Array.isArray(value);/**
|
502
|
+
* Checks if the input is an object literal and not null
|
503
|
+
* @param value Input value
|
504
|
+
* @returns true if the input is an object and not null
|
505
|
+
*/const isObjectLiteralAndNotNull=value=>!isNull(value)&&Object.prototype.toString.call(value)==='[object Object]';const mergeDeepRightObjectArrays=(leftValue,rightValue)=>{if(!Array.isArray(leftValue)||!Array.isArray(rightValue)){return clone$1(rightValue);}const mergedArray=clone$1(leftValue);rightValue.forEach((value,index)=>{mergedArray[index]=Array.isArray(value)||isObjectAndNotNull(value)?// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
506
|
+
mergeDeepRight(mergedArray[index],value):value;});return mergedArray;};const mergeDeepRight=(leftObject,rightObject)=>mergeDeepWith$1(mergeDeepRightObjectArrays,leftObject,rightObject);/**
|
507
|
+
Checks if the input is a non-empty object literal type and not undefined or null
|
508
|
+
* @param value input any
|
509
|
+
* @returns boolean
|
510
|
+
*/const isNonEmptyObject=value=>isObjectLiteralAndNotNull(value)&&Object.keys(value).length>0;/**
|
511
|
+
* A utility to recursively remove undefined values from an object
|
512
|
+
* @param obj input object
|
513
|
+
* @returns a new object
|
514
|
+
*/const removeUndefinedValues=obj=>{const result=pickBy$1(isDefined,obj);Object.entries(result).forEach(([key,value])=>{if(isObjectLiteralAndNotNull(value)){result[key]=removeUndefinedValues(value);}});return result;};/**
|
515
|
+
* A utility to recursively remove undefined and null values from an object
|
516
|
+
* @param obj input object
|
517
|
+
* @returns a new object
|
518
|
+
*/const removeUndefinedAndNullValues=obj=>{const result=pickBy$1(isDefinedAndNotNull,obj);Object.entries(result).forEach(([key,value])=>{if(isObjectLiteralAndNotNull(value)){result[key]=removeUndefinedAndNullValues(value);}});return result;};
|
519
|
+
|
520
|
+
const trim=value=>value.replace(/^\s+|\s+$/gm,'');const removeDoubleSpaces=value=>value.replace(/ {2,}/g,' ');/**
|
521
|
+
* A function to convert values to string
|
522
|
+
* @param val input value
|
523
|
+
* @returns stringified value
|
524
|
+
*/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
|
525
|
+
/**
|
526
|
+
* Converts a bytes array to base64 encoded string
|
527
|
+
* @param bytes bytes array to be converted to base64
|
528
|
+
* @returns base64 encoded string
|
529
|
+
*/const bytesToBase64=bytes=>{const binString=Array.from(bytes,x=>String.fromCodePoint(x)).join('');return globalThis.btoa(binString);};/**
|
530
|
+
* Encodes a string to base64 even with unicode characters
|
531
|
+
* @param value input string
|
532
|
+
* @returns base64 encoded string
|
533
|
+
*/const toBase64=value=>bytesToBase64(new TextEncoder().encode(value));
|
534
|
+
|
535
|
+
// if yes make them null instead of omitting in overloaded cases
|
536
|
+
/*
|
537
|
+
* Normalise the overloaded arguments of the page call facade
|
538
|
+
*/const pageArgumentsToCallOptions=(category,name,properties,options,callback)=>{const payload={};if(isFunction(callback)){payload.category=category;payload.name=name;payload.properties=clone$1(properties);payload.options=clone$1(options);payload.callback=callback;}if(isFunction(options)){payload.category=category;payload.name=name;payload.properties=clone$1(properties);payload.callback=options;}if(isFunction(properties)){payload.category=category;payload.name=name;payload.callback=properties;}if(isFunction(name)){payload.category=category;payload.callback=name;}if(isFunction(category)){payload.callback=category;}if(isObjectLiteralAndNotNull(category)){payload.options=clone$1(name);payload.properties=clone$1(category);delete payload.name;delete payload.category;}else if(isObjectLiteralAndNotNull(name)){payload.options=clone$1(properties);payload.properties=clone$1(name);delete payload.name;}if(isObjectLiteralAndNotNull(options)&&!isString(payload.options)){payload.options=options;}if(isObjectLiteralAndNotNull(properties)&&!isString(payload.properties)&&!isObjectLiteralAndNotNull(name)){payload.properties=properties;}if(isString(category)&&!isString(payload.category)){payload.category=category;}if(isString(name)&&!isString(payload.name)){payload.name=name;}if(isString(payload.category)&&!isString(payload.name)){payload.name=payload.category;delete payload.category;}if(isUndefined(payload.category)){delete payload.category;}payload.properties=mergeDeepRight(payload.properties&&isObjectLiteralAndNotNull(payload.properties)?payload.properties:{},{name:isString(payload.name)?payload.name:null,category:isString(payload.category)?payload.category:null});return payload;};/*
|
539
|
+
* Normalise the overloaded arguments of the track call facade
|
540
|
+
*/const trackArgumentsToCallOptions=(event,properties,options,callback)=>{const payload={name:event};if(isFunction(callback)){payload.properties=clone$1(properties);payload.options=clone$1(options);payload.callback=callback;}if(isFunction(options)){payload.properties=clone$1(properties);payload.callback=options;}if(isFunction(properties)){payload.callback=properties;}if(isObjectLiteralAndNotNull(options)||isNull(options)){payload.options=options;}if(isObjectLiteralAndNotNull(properties)||isNull(properties)){payload.properties=properties;}// To match v1.1 generated payload
|
541
|
+
if(isUndefined(payload.properties)||isNull(payload.properties)){payload.properties={};}return payload;};/*
|
542
|
+
* Normalise the overloaded arguments of the identify call facade
|
543
|
+
*/const identifyArgumentsToCallOptions=(userId,traits,options,callback)=>{const payload={};if(isFunction(callback)){payload.traits=clone$1(traits);payload.options=clone$1(options);payload.callback=callback;}if(isFunction(options)){payload.traits=clone$1(traits);payload.callback=options;}if(isFunction(traits)){payload.callback=traits;}if(isObjectLiteralAndNotNull(userId)||isNull(userId)){delete payload.userId;payload.traits=clone$1(userId);payload.options=clone$1(traits);}else {payload.userId=tryStringify(userId);if(!isUndefined(traits)&&!isFunction(traits)){payload.traits=clone$1(traits);}if(!isUndefined(options)&&!isFunction(options)){payload.options=clone$1(options);}}return payload;};/*
|
544
|
+
* Normalise the overloaded arguments of the alias call facade
|
545
|
+
*/const aliasArgumentsToCallOptions=(to,from,options,callback)=>{const payload={};if(isFunction(callback)){payload.to=tryStringify(to)??null;payload.from=from;payload.options=clone$1(options);payload.callback=callback;}if(isFunction(options)){payload.to=tryStringify(to)??null;payload.from=from;payload.callback=options;}if(isFunction(from)){payload.to=tryStringify(to)??null;payload.callback=from;}else if(isObjectLiteralAndNotNull(from)||isNull(from)){payload.to=tryStringify(to)??null;payload.options=isNull(from)?null:clone$1(from);delete payload.from;}else {payload.to=tryStringify(to)??null;payload.from=tryStringify(from);}if(isFunction(to)){payload.to=null;payload.callback=to;}if(isObjectLiteralAndNotNull(to)){payload.to=null;payload.options=clone$1(to);}return payload;};/*
|
546
|
+
* Normalise the overloaded arguments of the group call facade
|
547
|
+
*/const groupArgumentsToCallOptions=(groupId,traits,options,callback)=>{const payload={};if(isFunction(callback)){payload.traits=clone$1(traits);payload.options=clone$1(options);payload.callback=callback;}if(isFunction(options)){payload.traits=clone$1(traits);payload.callback=options;}if(isFunction(traits)){payload.callback=traits;}// TODO: why do we enable overload for group that only passes callback? is there any use case?
|
548
|
+
if(isFunction(groupId)){payload.callback=groupId;}else if(isObjectLiteralAndNotNull(groupId)||isNull(groupId)){payload.traits=isNull(groupId)?undefined:clone$1(groupId);if(isFunction(traits)){payload.callback=traits;}else {payload.options=clone$1(traits);}}else {payload.groupId=tryStringify(groupId);payload.traits=!isObjectLiteralAndNotNull(traits)?undefined:clone$1(traits);payload.options=!isObjectLiteralAndNotNull(options)?undefined:clone$1(options);}return payload;};
|
549
|
+
|
550
|
+
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';
|
551
|
+
|
552
|
+
/**
|
553
|
+
* Create globally accessible RudderStackGlobals object
|
554
|
+
*/const createExposedGlobals=(analyticsInstanceId='app')=>{if(!globalThis.RudderStackGlobals){globalThis.RudderStackGlobals={};}if(!globalThis.RudderStackGlobals[analyticsInstanceId]){globalThis.RudderStackGlobals[analyticsInstanceId]={};}};/**
|
555
|
+
* Add move values to globally accessible RudderStackGlobals object per analytics instance
|
556
|
+
*/const setExposedGlobal=(keyName,value,analyticsInstanceId='app')=>{createExposedGlobals(analyticsInstanceId);globalThis.RudderStackGlobals[analyticsInstanceId][keyName]=value;};/**
|
557
|
+
* Get values from globally accessible RudderStackGlobals object by analytics instance
|
558
|
+
*/const getExposedGlobal=(keyName,analyticsInstanceId='app')=>{createExposedGlobals(analyticsInstanceId);return globalThis.RudderStackGlobals[analyticsInstanceId][keyName];};
|
559
|
+
|
560
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.0.0-beta.1';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';
|
561
|
+
|
562
|
+
/**
|
563
|
+
* Parse query string params into object values for keys that start with a defined prefix
|
564
|
+
*/const getEventDataFromQueryString=(params,dataTypeNamePrefix)=>{const data={};params.forEach((value,key)=>{if(key.startsWith(dataTypeNamePrefix)){// remove prefix from key name
|
565
|
+
const dataKey=key.substring(dataTypeNamePrefix.length);// add new key value pair in generated object
|
566
|
+
data[dataKey]=params.get(key);}});return data;};/**
|
567
|
+
* Parse query string into preload buffer events & push into existing array before any other events
|
568
|
+
*/const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
|
569
|
+
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
|
570
|
+
if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}// Set userId and user traits
|
571
|
+
if(queryObject.get(QUERY_PARAM_USER_ID_KEY)){argumentsArray.unshift(['identify',queryObject.get(QUERY_PARAM_USER_ID_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.trait)]);}// Set anonymousID
|
572
|
+
if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
|
573
|
+
* Retrieve an existing buffered load method call and remove from the existing array
|
574
|
+
*/const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
|
575
|
+
* Iterate the buffered API calls until we find load call and process it separately
|
576
|
+
*/let i=0;while(i<preloadedEventsArray.length){if(preloadedEventsArray[i]&&preloadedEventsArray[i][0]===loadMethodName){loadEvent=preloadedEventsArray[i];preloadedEventsArray.splice(i,1);break;}i+=1;}return loadEvent;};/**
|
577
|
+
* Retrieve any existing events that were triggered before SDK load and enqueue in buffer
|
578
|
+
*/const retrievePreloadBufferEvents=instance=>{const preloadedEventsArray=getExposedGlobal(GLOBAL_PRELOAD_BUFFER)||[];// Get events that are pre-populated via query string params
|
579
|
+
retrieveEventsFromQueryString(preloadedEventsArray);const sanitizedPreloadedEventsArray=preloadedEventsArray.filter(bufferedEvent=>bufferedEvent[0]!=='load');// Enqueue the non load events in the buffer of the global rudder analytics singleton
|
580
|
+
if(sanitizedPreloadedEventsArray.length>0){instance.enqueuePreloadBufferEvents(sanitizedPreloadedEventsArray);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);}}};
|
581
|
+
|
582
|
+
const LOG_LEVEL_MAP={[LogLevel.Log]:0,[LogLevel.Info]:1,[LogLevel.Debug]:2,[LogLevel.Warn]:3,[LogLevel.Error]:4,[LogLevel.None]:5};const DEFAULT_LOG_LEVEL=LogLevel.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;';/**
|
583
|
+
* Service to log messages/data to output provider, default is console
|
584
|
+
*/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(LogLevel.Log,data);}info(...data){this.outputLog(LogLevel.Info,data);}debug(...data){this.outputLog(LogLevel.Debug,data);}warn(...data){this.outputLog(LogLevel.Warn,data);}error(...data){this.outputLog(LogLevel.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
|
585
|
+
// to assist on the fly debugging?
|
586
|
+
setMinLogLevel(logLevel){this.minLogLevel=LOG_LEVEL_MAP[logLevel];if(isUndefined(this.minLogLevel)){this.minLogLevel=LOG_LEVEL_MAP[DEFAULT_LOG_LEVEL];}}/**
|
587
|
+
* Formats the console message using `scope` and styles
|
588
|
+
*/formatLogData(data){if(Array.isArray(data)&&data.length>0){// prefix SDK identifier
|
589
|
+
let msg=`%c ${LOG_MSG_PREFIX}`;// format the log message using `scope`
|
590
|
+
if(this.scope){msg=`${msg} - ${this.scope}`;}// trim whitespaces for original message
|
591
|
+
const originalMsg=isString(data[0])?data[0].trim():'';// prepare the final message
|
592
|
+
msg=`${msg} %c ${originalMsg}`;const styledLogArgs=[msg,LOG_MSG_PREFIX_STYLE,// add style for the prefix
|
593
|
+
LOG_MSG_STYLE// reset the style for the actual message
|
594
|
+
];// add first it if it was not a string msg
|
595
|
+
if(!isString(data[0])){styledLogArgs.push(data[0]);}// append rest of the original arguments
|
596
|
+
styledLogArgs.push(...data.slice(1));return styledLogArgs;}return data;}}const defaultLogger=new Logger();
|
597
|
+
|
598
|
+
const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const LOAD_CONFIGURATION='LoadConfiguration';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';
|
599
|
+
|
600
|
+
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)=>`Failed to load 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.`;
|
601
|
+
|
602
|
+
const SOURCE_CONFIG_OPTION_ERROR=`"getSourceConfig" must be a function. Please make sure that it is defined and returns a valid source configuration object.`;const INTG_CDN_BASE_URL_ERROR=`Failed to load the SDK as the CDN base URL for integrations is not valid.`;const PLUGINS_CDN_BASE_URL_ERROR=`Failed to load the SDK as the CDN base URL for plugins is not valid.`;const DATA_PLANE_URL_ERROR=`Failed to load the SDK as the data plane URL could not be determined. Please check that the data plane URL is set correctly and try again.`;const XHR_PAYLOAD_PREP_ERROR=`Failed to prepare data for the request.`;const EVENT_OBJECT_GENERATION_ERROR=`Failed to generate the event object.`;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.`;// ERROR
|
603
|
+
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 REPORTING_PLUGIN_INIT_FAILURE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to initialize the error reporting plugin.`;const NOTIFY_FAILURE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to notify 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 STORAGE_UNAVAILABILITY_ERROR_PREFIX=(context,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${storageType}" storage type is `;const SOURCE_CONFIG_FETCH_ERROR=reason=>`Failed to fetch the source config. Reason: ${reason}`;const WRITE_KEY_VALIDATION_ERROR=writeKey=>`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=dataPlaneUrl=>`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 READY_API_CALLBACK_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The callback is not a function.`;const XHR_DELIVERY_ERROR=(prefix,status,statusText,url)=>`${prefix} with status: ${status}, ${statusText} for URL: ${url}.`;const XHR_REQUEST_ERROR=(prefix,e,url)=>`${prefix} due to timeout or no connection (${e?e.type:''}) 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`;// WARNING
|
604
|
+
const UNSUPPORTED_ERROR_REPORTING_PROVIDER_WARNING=(context,selectedErrorReportingProvider,errorReportingProvidersToPluginNameMap,defaultProvider)=>`${context}${LOG_CONTEXT_SEPARATOR}The error reporting provider "${selectedErrorReportingProvider}" is not supported. Please choose one of the following supported providers: "${Object.keys(errorReportingProvidersToPluginNameMap)}". The default provider "${defaultProvider}" 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 UNSUPPORTED_RESIDENCY_SERVER_REGION_WARNING=(context,selectedResidencyServerRegion,defaultRegion)=>`${context}${LOG_CONTEXT_SEPARATOR}The residency server region "${selectedResidencyServerRegion}" is not supported. Please choose one of the following supported regions: "${Object.values(ResidencyServerRegion)}". The default region "${defaultRegion}" will be used instead.`;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 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_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}No storage is available. The SDK will be initialized without storage.`;// DEBUG
|
605
|
+
|
606
|
+
// to next or return the value if it is the last one instead of an array per
|
607
|
+
// plugin that is the normal invoke
|
608
|
+
// TODO: add invoke method for extension point that we know only one plugin can be used. add invokeMultiple and invokeSingle methods
|
609
|
+
class PluginEngine{plugins=[];byName={};cache={};config={throws:true};constructor(options={},logger){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);}}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);}}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(plugin.initialize&&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);}}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);}}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.
|
610
|
+
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
|
611
|
+
// do some unified pre-process before application starts.
|
612
|
+
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
|
613
|
+
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
|
614
|
+
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({throws:true},defaultLogger);
|
615
|
+
|
616
|
+
function i(){throw new Error("Cycle detected");}function t(){if(!(h>1)){var i,t=!1;while(void 0!==n){var o=n;n=void 0;s++;while(void 0!==o){var r=o.o;o.o=void 0;o.f&=-3;if(!(8&o.f)&&c(o))try{o.c();}catch(o){if(!t){i=o;t=!0;}}o=r;}}s=0;h--;if(t)throw i;}else h--;}function o(i){if(h>0)return i();h++;try{return i();}finally{t();}}var r=void 0,n=void 0,h=0,s=0,f=0;function v(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 e(i){this.v=i;this.i=0;this.n=void 0;this.t=void 0;}e.prototype.h=function(){return !0;};e.prototype.S=function(i){if(this.t!==i&&void 0===i.e){i.x=this.t;if(void 0!==this.t)this.t.e=i;this.t=i;}};e.prototype.U=function(i){if(void 0!==this.t){var t=i.e,o=i.x;if(void 0!==t){t.x=o;i.e=void 0;}if(void 0!==o){o.e=t;i.x=void 0;}if(i===this.t)this.t=o;}};e.prototype.subscribe=function(i){var t=this;return b(function(){var o=t.value,r=32&this.f;this.f&=-33;try{i(o);}finally{this.f|=r;}});};e.prototype.valueOf=function(){return this.value;};e.prototype.toString=function(){return this.value+"";};e.prototype.toJSON=function(){return this.value;};e.prototype.peek=function(){return this.v;};Object.defineProperty(e.prototype,"value",{get:function(){var i=v(this);if(void 0!==i)i.i=this.i;return this.v;},set:function(o){if(r instanceof l)!function(){throw new Error("Computed cannot have side-effects");}();if(o!==this.v){if(s>100)i();this.v=o;this.i++;f++;h++;try{for(var n=this.t;void 0!==n;n=n.x)n.t.N();}finally{t();}}}});function u(i){return new e(i);}function c(i){for(var t=i.s;void 0!==t;t=t.n)if(t.S.i!==t.i||!t.S.h()||t.S.i!==t.i)return !0;return !1;}function d(i){for(var t=i.s;void 0!==t;t=t.n){var o=t.S.n;if(void 0!==o)t.r=o;t.S.n=t;t.i=-1;if(void 0===t.n){i.s=t;break;}}}function a(i){var t=i.s,o=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 o=t;t.S.n=t.r;if(void 0!==t.r)t.r=void 0;t=r;}i.s=o;}function l(i){e.call(this,void 0);this.x=i;this.s=void 0;this.g=f-1;this.f=4;}(l.prototype=new e()).h=function(){this.f&=-3;if(1&this.f)return !1;if(32==(36&this.f))return !0;this.f&=-5;if(this.g===f)return !0;this.g=f;this.f|=1;if(this.i>0&&!c(this)){this.f&=-2;return !0;}var i=r;try{d(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;a(this);this.f&=-2;return !0;};l.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);}e.prototype.S.call(this,i);};l.prototype.U=function(i){if(void 0!==this.t){e.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);}}};l.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var i=this.t;void 0!==i;i=i.x)i.t.N();}};l.prototype.peek=function(){if(!this.h())i();if(16&this.f)throw this.v;return this.v;};Object.defineProperty(l.prototype,"value",{get:function(){if(1&this.f)i();var t=v(this);this.h();if(void 0!==t)t.i=this.i;if(16&this.f)throw this.v;return this.v;}});function y(i){var o=i.u;i.u=void 0;if("function"==typeof o){h++;var n=r;r=void 0;try{o();}catch(t){i.f&=-2;i.f|=8;_(i);throw t;}finally{r=n;t();}}}function _(i){for(var t=i.s;void 0!==t;t=t.n)t.S.U(t);i.x=void 0;i.s=void 0;y(i);}function p(i){if(r!==this)throw new Error("Out-of-order effect");a(this);r=i;this.f&=-2;if(8&this.f)_(this);t();}function g(i){this.x=i;this.u=void 0;this.s=void 0;this.o=void 0;this.f=32;}g.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();}};g.prototype.S=function(){if(1&this.f)i();this.f|=1;this.f&=-9;y(this);d(this);h++;var t=r;r=this;return p.bind(this,t);};g.prototype.N=function(){if(!(2&this.f)){this.f|=2;this.o=n;n=this;}};g.prototype.d=function(){this.f|=8;if(!(1&this.f))_(this);};function b(i){var t=new g(i);try{t.c();}catch(i){t.d();throw i;}return t.d.bind(t);}
|
617
|
+
|
618
|
+
const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';const CDN_ARCH_VERSION_DIR='v3';const CDN_INT_DIR='js-integrations';const CDN_PLUGINS_DIR='plugins';const DEST_SDK_BASE_URL=`${SDK_CDN_BASE_URL}/${CDN_ARCH_VERSION_DIR}/latest/${BUILD_TYPE}/${CDN_INT_DIR}`;const PLUGINS_BASE_URL=`${SDK_CDN_BASE_URL}/${CDN_ARCH_VERSION_DIR}/latest/${BUILD_TYPE}/${CDN_PLUGINS_DIR}`;const DEFAULT_CONFIG_BE_URL='https://api.rudderstack.com';
|
619
|
+
|
620
|
+
const DEFAULT_XHR_TIMEOUT=10*1000;// 10 seconds
|
621
|
+
const DEFAULT_COOKIE_MAX_AGE=31536000*1000;// 1 year
|
622
|
+
const DEFAULT_SESSION_TIMEOUT=30*60*1000;// 30 minutes
|
623
|
+
const MIN_SESSION_TIMEOUT=10*1000;// 10 seconds
|
624
|
+
|
625
|
+
let StorageEncryptionVersion=/*#__PURE__*/function(StorageEncryptionVersion){StorageEncryptionVersion["Legacy"]="legacy";StorageEncryptionVersion["V3"]="v3";return StorageEncryptionVersion;}({});// default
|
626
|
+
|
627
|
+
const DEFAULT_ERROR_REPORTING_PROVIDER='bugsnag';const DEFAULT_STORAGE_ENCRYPTION_VERSION=StorageEncryptionVersion.V3;const ConsentManagersToPluginNameMap={oneTrust:PluginName.OneTrustConsentManager,ketch:PluginName.KetchConsentManager};const ErrorReportingProvidersToPluginNameMap={[DEFAULT_ERROR_REPORTING_PROVIDER]:PluginName.Bugsnag};const StorageEncryptionVersionsToPluginNameMap={[DEFAULT_STORAGE_ENCRYPTION_VERSION]:PluginName.StorageEncryption,[StorageEncryptionVersion.Legacy]:PluginName.StorageEncryptionLegacy};
|
628
|
+
|
629
|
+
const defaultLoadOptions={logLevel:LogLevel.Error,configUrl:DEFAULT_CONFIG_BE_URL,loadIntegration:true,sessions:{autoTrack:true,timeout:DEFAULT_SESSION_TIMEOUT},sameSiteCookie:CookieSameSite.Lax,polyfillIfRequired:true,integrations:{All:true},useBeacon:false,lockIntegrationsVersion:false,uaChTrackLevel:UaChTrackLevel.None,plugins:[],useGlobalIntegrationsConfigInEvents:false,bufferDataPlaneEventsUntilReady:false,storage:{encryption:{version:DEFAULT_STORAGE_ENCRYPTION_VERSION},migrate:false}};const loadOptionsState=u(clone$1(defaultLoadOptions));
|
630
|
+
|
631
|
+
const defaultSessionInfo={autoTrack:true,timeout:DEFAULT_SESSION_TIMEOUT};const sessionState={userId:u(undefined),userTraits:u(undefined),anonymousUserId:u(undefined),groupId:u(undefined),groupTraits:u(undefined),initialReferrer:u(undefined),initialReferringDomain:u(undefined),sessionInfo:u({...defaultSessionInfo})};
|
632
|
+
|
633
|
+
const capabilitiesState={isOnline:u(true),storage:{isLocalStorageAvailable:u(false),isCookieStorageAvailable:u(false),isSessionStorageAvailable:u(false)},isBeaconAvailable:u(false),isLegacyDOM:u(false),isUaCHAvailable:u(false),isCryptoAvailable:u(false),isIE11:u(false),isAdBlocked:u(false)};
|
634
|
+
|
635
|
+
const reportingState={isErrorReportingEnabled:u(false),isMetricsReportingEnabled:u(false),errorReportingProviderPluginName:u(undefined)};
|
636
|
+
|
637
|
+
const sourceConfigState=u(undefined);
|
638
|
+
|
639
|
+
const lifecycleState={activeDataplaneUrl:u(undefined),integrationsCDNPath:u(DEST_SDK_BASE_URL),pluginsCDNPath:u(PLUGINS_BASE_URL),sourceConfigUrl:u(undefined),status:u(undefined),initialized:u(false),logLevel:u(LogLevel.Error),loaded:u(false),readyCallbacks:u([]),writeKey:u(undefined),dataPlaneUrl:u(undefined)};
|
640
|
+
|
641
|
+
const consentsState={data:u({initialized:false}),activeConsentManagerPluginName:u(undefined)};
|
642
|
+
|
643
|
+
const metricsState={retries:u(0),dropped:u(0),sent:u(0),queued:u(0),triggered:u(0)};
|
644
|
+
|
645
|
+
const contextState={app:u({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION}),traits:u(null),library:u({name:APP_NAME,version:APP_VERSION}),userAgent:u(''),device:u(null),network:u(null),os:u({name:'',version:''}),locale:u(null),screen:u({density:0,width:0,height:0,innerWidth:0,innerHeight:0}),'ua-ch':u(undefined),campaign:u({})};
|
646
|
+
|
647
|
+
const nativeDestinationsState={configuredDestinations:u([]),activeDestinations:u([]),loadOnlyIntegrations:u({}),failedDestinations:u([]),loadIntegration:u(true),initializedDestinations:u([]),clientDestinationsReady:u(false),integrationsConfig:u({})};
|
648
|
+
|
649
|
+
const eventBufferState={toBeProcessedArray:u([]),readyCallbacksArray:u([])};
|
650
|
+
|
651
|
+
const pluginsState={ready:u(false),loadedPlugins:u([]),failedPlugins:u([]),pluginsToLoadFromConfig:u([]),activePlugins:u([]),totalPluginsToLoad:u(0)};
|
652
|
+
|
653
|
+
const pagePropertiesState={path:u(''),referrer:u(''),referring_domain:u(''),search:u(''),title:u(''),url:u(''),tab_url:u('')};
|
654
|
+
|
655
|
+
const storageState={encryptionPluginName:u(undefined),migrate:u(false)};
|
656
|
+
|
657
|
+
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,page:pagePropertiesState,storage:storageState};const state={...clone$1(defaultStateValues)};
|
658
|
+
|
659
|
+
const JSON_STRINGIFY='JSONStringify';const getCircularReplacer=(excludeNull,excludeKeys,logger)=>{const ancestors=[];// Here we do not want to use arrow function to use "this" in function context
|
660
|
+
// eslint-disable-next-line func-names
|
661
|
+
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.
|
662
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
663
|
+
// @ts-ignore-next-line
|
664
|
+
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]';}ancestors.push(value);return value;};};/**
|
665
|
+
* Utility method for JSON stringify object excluding null values & circular references
|
666
|
+
*
|
667
|
+
* @param {*} value input
|
668
|
+
* @param {boolean} excludeNull if it should exclude nul or not
|
669
|
+
* @param {function} logger optional logger methods for warning
|
670
|
+
* @returns string
|
671
|
+
*/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;}};
|
672
|
+
|
673
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX];
|
674
|
+
|
675
|
+
/**
|
676
|
+
* Utility method to normalise errors
|
677
|
+
*/const processError=error=>{let errorMessage;try{if(isString(error)){errorMessage=error;}else if(error instanceof Error){errorMessage=error.message;}else {errorMessage=error.message?error.message:stringifyWithoutCircular(error);}}catch(e){errorMessage=`Unknown error: ${e.message}`;}return errorMessage;};/**
|
678
|
+
* A function to determine whether the error should be promoted to notify or not
|
679
|
+
* @param {Error} error
|
680
|
+
* @returns
|
681
|
+
*/const isAllowedToBeNotified=error=>{if(error.message){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.message.includes(e));}return true;};
|
682
|
+
|
683
|
+
/**
|
684
|
+
* A service to handle errors
|
685
|
+
*/class ErrorHandler{// If no logger is passed errors will be thrown as unhandled error
|
686
|
+
constructor(logger,pluginEngine){this.logger=logger;this.pluginEngine=pluginEngine;}init(externalSrcLoader){if(!this.pluginEngine){return;}try{const extPoint='errorReporting.init';const errReportingInitVal=this.pluginEngine.invokeSingle(extPoint,state,this.pluginEngine,externalSrcLoader,this.logger);if(errReportingInitVal instanceof Promise){errReportingInitVal.then(client=>{this.errReportingClient=client;}).catch(err=>{this.logger?.error(REPORTING_PLUGIN_INIT_FAILURE_ERROR(ERROR_HANDLER),err);});}}catch(err){this.onError(err,ERROR_HANDLER);}}onError(error,context='',customMessage='',shouldAlwaysThrow=false){// Error handling is already implemented in processError method
|
687
|
+
let errorMessage=processError(error);// If no error message after we normalize, then we swallow/ignore the errors
|
688
|
+
if(!errorMessage){return;}errorMessage=removeDoubleSpaces(`${context}${LOG_CONTEXT_SEPARATOR}${customMessage} ${errorMessage}`);let normalizedError=error;// Enhance error message
|
689
|
+
if(isTypeOfError(error)){normalizedError.message=errorMessage;}else {normalizedError=new Error(errorMessage);}this.notifyError(normalizedError);if(this.logger){this.logger.error(errorMessage);if(shouldAlwaysThrow){throw normalizedError;}}else {throw normalizedError;}}/**
|
690
|
+
* Add breadcrumbs to add insight of a user's journey before an error
|
691
|
+
* occurred and send to external error monitoring service via a plugin
|
692
|
+
*
|
693
|
+
* @param {string} breadcrumb breadcrumbs message
|
694
|
+
*/leaveBreadcrumb(breadcrumb){if(this.pluginEngine){try{this.pluginEngine.invokeSingle('errorReporting.breadcrumb',this.pluginEngine,this.errReportingClient,breadcrumb,this.logger);}catch(err){this.onError(err,'errorReporting.breadcrumb');}}}/**
|
695
|
+
* Send handled errors to external error monitoring service via a plugin
|
696
|
+
*
|
697
|
+
* @param {Error} error Error instance from handled error
|
698
|
+
*/notifyError(error){if(this.pluginEngine&&isAllowedToBeNotified(error)){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,this.errReportingClient,error,state,this.logger);}catch(err){// Not calling onError here as we don't want to go into infinite loop
|
699
|
+
this.logger?.error(NOTIFY_FAILURE_ERROR(ERROR_HANDLER),err);}}}}const defaultErrorHandler=new ErrorHandler(defaultLogger,defaultPluginEngine);
|
700
|
+
|
701
|
+
let DestinationConnectionMode=/*#__PURE__*/function(DestinationConnectionMode){DestinationConnectionMode["Hybrid"]="hybrid";DestinationConnectionMode["Cloud"]="cloud";DestinationConnectionMode["Device"]="device";return DestinationConnectionMode;}({});
|
702
|
+
|
703
|
+
/**
|
704
|
+
* A function to filter and return non cloud mode destinations
|
705
|
+
* @param destination
|
706
|
+
*
|
707
|
+
* @returns boolean
|
708
|
+
*/const isNonCloudDestination=destination=>Boolean(destination.config.connectionMode!==DestinationConnectionMode.Cloud||destination.config.useNativeSDKToSend===true||// this is the older flag for hybrid mode destinations
|
709
|
+
destination.config.useNativeSDK===true);const isHybridModeDestination=destination=>Boolean(destination.config.connectionMode===DestinationConnectionMode.Hybrid||destination.config.useNativeSDKToSend===true);/**
|
710
|
+
* A function to filter and return non cloud mode destinations
|
711
|
+
* @param destinations
|
712
|
+
*
|
713
|
+
* @returns destinations
|
714
|
+
*/const getNonCloudDestinations=destinations=>destinations.filter(isNonCloudDestination);
|
715
|
+
|
716
|
+
let LifecycleStatus=/*#__PURE__*/function(LifecycleStatus){LifecycleStatus["Mounted"]="mounted";LifecycleStatus["BrowserCapabilitiesReady"]="browserCapabilitiesReady";LifecycleStatus["Configured"]="configured";LifecycleStatus["PluginsLoading"]="pluginsLoading";LifecycleStatus["PluginsReady"]="pluginsReady";LifecycleStatus["Initialized"]="initialized";LifecycleStatus["Loaded"]="loaded";LifecycleStatus["DestinationsLoading"]="destinationsLoading";LifecycleStatus["DestinationsReady"]="destinationsReady";LifecycleStatus["Ready"]="ready";return LifecycleStatus;}({});
|
717
|
+
|
718
|
+
/**
|
719
|
+
* List of plugin names that are loaded as dynamic imports in modern builds
|
720
|
+
*/const remotePluginNames=[PluginName.BeaconQueue,PluginName.DeviceModeTransformation,PluginName.DeviceModeDestinations,PluginName.ErrorReporting,PluginName.ExternalAnonymousId,PluginName.GoogleLinker,PluginName.NativeDestinationQueue,PluginName.StorageEncryption,PluginName.StorageEncryptionLegacy,PluginName.StorageMigrator,PluginName.XhrQueue,PluginName.OneTrustConsentManager,PluginName.KetchConsentManager,PluginName.Bugsnag];
|
721
|
+
|
722
|
+
const remotesMap = {
|
723
|
+
'rudderAnalyticsRemotePlugins':{url:()=>Promise.resolve(window.RudderStackGlobals && window.RudderStackGlobals.app && window.RudderStackGlobals.app.pluginsCDNPath ? "" + window.RudderStackGlobals.app.pluginsCDNPath + "/rsa-plugins.js" : "http://localhost:3002/cdn//rsa-plugins.js"),format:'esm',from:'vite'}
|
724
|
+
};
|
725
|
+
const loadJS = async (url, fn) => {
|
726
|
+
const resolvedUrl = typeof url === 'function' ? await url() : url;
|
727
|
+
const script = document.createElement('script');
|
728
|
+
script.type = 'text/javascript';
|
729
|
+
script.onload = fn;
|
730
|
+
script.src = resolvedUrl;
|
731
|
+
document.getElementsByTagName('head')[0].appendChild(script);
|
732
|
+
};
|
733
|
+
|
734
|
+
const wrapShareModule = remoteFrom => {
|
735
|
+
return {
|
736
|
+
|
737
|
+
}
|
738
|
+
};
|
739
|
+
|
740
|
+
async function __federation_method_ensure(remoteId) {
|
741
|
+
const remote = remotesMap[remoteId];
|
742
|
+
if (!remote.inited) {
|
743
|
+
if ('var' === remote.format) {
|
744
|
+
// loading js with script tag
|
745
|
+
return new Promise(resolve => {
|
746
|
+
const callback = () => {
|
747
|
+
if (!remote.inited) {
|
748
|
+
remote.lib = window[remoteId];
|
749
|
+
remote.lib.init(wrapShareModule(remote.from));
|
750
|
+
remote.inited = true;
|
751
|
+
}
|
752
|
+
resolve(remote.lib);
|
753
|
+
};
|
754
|
+
return loadJS(remote.url, callback);
|
755
|
+
});
|
756
|
+
} else if (['esm', 'systemjs'].includes(remote.format)) {
|
757
|
+
// loading js with import(...)
|
758
|
+
return new Promise((resolve, reject) => {
|
759
|
+
const getUrl = typeof remote.url === 'function' ? remote.url : () => Promise.resolve(remote.url);
|
760
|
+
getUrl().then(url => {
|
761
|
+
import(/* @vite-ignore */ url).then(lib => {
|
762
|
+
if (!remote.inited) {
|
763
|
+
const shareScope = wrapShareModule(remote.from);
|
764
|
+
lib.init(shareScope);
|
765
|
+
remote.lib = lib;
|
766
|
+
remote.lib.init(shareScope);
|
767
|
+
remote.inited = true;
|
768
|
+
}
|
769
|
+
resolve(remote.lib);
|
770
|
+
}).catch(reject);
|
771
|
+
});
|
772
|
+
})
|
773
|
+
}
|
774
|
+
} else {
|
775
|
+
return remote.lib;
|
776
|
+
}
|
777
|
+
}
|
778
|
+
|
779
|
+
function __federation_method_wrapDefault(module, need) {
|
780
|
+
if (!module?.default && need) {
|
781
|
+
let obj = Object.create(null);
|
782
|
+
obj.default = module;
|
783
|
+
obj.__esModule = true;
|
784
|
+
return obj;
|
785
|
+
}
|
786
|
+
return module;
|
787
|
+
}
|
788
|
+
|
789
|
+
function __federation_method_getRemote(remoteName, componentName) {
|
790
|
+
return __federation_method_ensure(remoteName).then((remote) => remote.get(componentName).then(factory => factory()));
|
791
|
+
}
|
792
|
+
|
793
|
+
/**
|
794
|
+
* Get the lazy loaded dynamic import for a plugin name
|
795
|
+
*/const getFederatedModuleImport=pluginName=>{switch(pluginName){case PluginName.BeaconQueue:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./BeaconQueue").then(module=>__federation_method_wrapDefault(module, true));case PluginName.DeviceModeTransformation:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./DeviceModeTransformation").then(module=>__federation_method_wrapDefault(module, true));case PluginName.DeviceModeDestinations:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./DeviceModeDestinations").then(module=>__federation_method_wrapDefault(module, true));case PluginName.ErrorReporting:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./ErrorReporting").then(module=>__federation_method_wrapDefault(module, true));case PluginName.ExternalAnonymousId:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./ExternalAnonymousId").then(module=>__federation_method_wrapDefault(module, true));case PluginName.GoogleLinker:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./GoogleLinker").then(module=>__federation_method_wrapDefault(module, true));case PluginName.NativeDestinationQueue:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./NativeDestinationQueue").then(module=>__federation_method_wrapDefault(module, true));case PluginName.StorageEncryption:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./StorageEncryption").then(module=>__federation_method_wrapDefault(module, true));case PluginName.StorageEncryptionLegacy:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./StorageEncryptionLegacy").then(module=>__federation_method_wrapDefault(module, true));case PluginName.StorageMigrator:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./StorageMigrator").then(module=>__federation_method_wrapDefault(module, true));case PluginName.XhrQueue:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./XhrQueue").then(module=>__federation_method_wrapDefault(module, true));case PluginName.OneTrustConsentManager:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./OneTrustConsentManager").then(module=>__federation_method_wrapDefault(module, true));case PluginName.KetchConsentManager:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./KetchConsentManager").then(module=>__federation_method_wrapDefault(module, true));case PluginName.Bugsnag:return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./Bugsnag").then(module=>__federation_method_wrapDefault(module, true));default:return undefined;}};/**
|
796
|
+
* Map of active plugin names to their dynamic import
|
797
|
+
*/const modernBuildPluginImports=activePluginNames=>{const remotePlugins={};activePluginNames.forEach(pluginName=>{if(remotePluginNames.includes(pluginName)){const lazyLoadImport=getFederatedModuleImport(pluginName);if(lazyLoadImport){remotePlugins[pluginName]=lazyLoadImport;}}});return remotePlugins;};
|
798
|
+
|
799
|
+
/**
|
800
|
+
* Map of mandatory plugin names and direct imports
|
801
|
+
*/const getMandatoryPluginsMap=()=>({});/**
|
802
|
+
* Map of optional plugin names and direct imports for legacy builds
|
803
|
+
*/const getOptionalPluginsMap=()=>{{return {};}};/**
|
804
|
+
* Map of optional plugin names and dynamic imports for modern builds
|
805
|
+
*/const getRemotePluginsMap=activePluginNames=>{return modernBuildPluginImports?.(activePluginNames)||{};};const pluginsInventory={...getMandatoryPluginsMap(),...getOptionalPluginsMap()};const remotePluginsInventory=activePluginNames=>({...getRemotePluginsMap(activePluginNames)});
|
806
|
+
|
807
|
+
// TODO: add retry mechanism for getting remote plugins
|
808
|
+
// TODO: add timeout error mechanism for marking remote plugins that failed to load as failed in state
|
809
|
+
class PluginsManager{constructor(engine,errorHandler,logger){this.engine=engine;this.errorHandler=errorHandler;this.logger=logger;this.onError=this.onError.bind(this);}/**
|
810
|
+
* Orchestrate the plugin loading and registering
|
811
|
+
*/init(){state.lifecycle.status.value=LifecycleStatus.PluginsLoading;// Expose pluginsCDNPath to global object, so it can be used in the promise that determines
|
812
|
+
// remote plugin cdn path to support proxied plugin remotes
|
813
|
+
{setExposedGlobal('pluginsCDNPath',state.lifecycle.pluginsCDNPath.value);}this.setActivePlugins();this.registerLocalPlugins();this.registerRemotePlugins();this.attachEffects();}/**
|
814
|
+
* Update state based on plugin loaded status
|
815
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
816
|
+
attachEffects(){b(()=>{const isAllPluginsReady=state.plugins.activePlugins.value.length===0||state.plugins.loadedPlugins.value.length+state.plugins.failedPlugins.value.length===state.plugins.totalPluginsToLoad.value;if(isAllPluginsReady){o(()=>{state.plugins.ready.value=true;// TODO: decide what to do if a plugin fails to load for any reason.
|
817
|
+
// Should we stop here or should we progress?
|
818
|
+
state.lifecycle.status.value=LifecycleStatus.PluginsReady;});}});}/**
|
819
|
+
* Determine the list of plugins that should be loaded based on sourceConfig & load options
|
820
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
821
|
+
getPluginsToLoadBasedOnConfig(){// This contains the default plugins if load option has been omitted by user
|
822
|
+
let pluginsToLoadFromConfig=state.plugins.pluginsToLoadFromConfig.value;if(!pluginsToLoadFromConfig){return [];}// Error reporting related plugins
|
823
|
+
const supportedErrReportingProviderPluginNames=Object.values(ErrorReportingProvidersToPluginNameMap);if(state.reporting.errorReportingProviderPluginName.value){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>!(pluginName!==state.reporting.errorReportingProviderPluginName.value&&supportedErrReportingProviderPluginNames.includes(pluginName)));}else {pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>!(pluginName===PluginName.ErrorReporting||supportedErrReportingProviderPluginNames.includes(pluginName)));}// dataplane events delivery plugins
|
824
|
+
if(state.loadOptions.value.useBeacon===true&&state.capabilities.isBeaconAvailable.value){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>pluginName!==PluginName.XhrQueue);}else {if(state.loadOptions.value.useBeacon===true){this.logger?.warn(UNSUPPORTED_BEACON_API_WARNING(PLUGINS_MANAGER));}pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>pluginName!==PluginName.BeaconQueue);}// Device mode destinations related plugins
|
825
|
+
if(getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value??[]).length===0){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>![PluginName.DeviceModeDestinations,PluginName.DeviceModeTransformation,PluginName.NativeDestinationQueue].includes(pluginName));}// Consent Management related plugins
|
826
|
+
const supportedConsentManagerPlugins=Object.values(ConsentManagersToPluginNameMap);pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>!(pluginName!==state.consents.activeConsentManagerPluginName.value&&supportedConsentManagerPlugins.includes(pluginName)));// Storage encryption related plugins
|
827
|
+
const supportedStorageEncryptionPlugins=Object.values(StorageEncryptionVersionsToPluginNameMap);pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>!(pluginName!==state.storage.encryptionPluginName.value&&supportedStorageEncryptionPlugins.includes(pluginName)));// Storage migrator related plugins
|
828
|
+
if(!state.storage.migrate.value){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>pluginName!==PluginName.StorageMigrator);}return [...Object.keys(getMandatoryPluginsMap()),...pluginsToLoadFromConfig];}/**
|
829
|
+
* Determine the list of plugins that should be activated
|
830
|
+
*/setActivePlugins(){const pluginsToLoad=this.getPluginsToLoadBasedOnConfig();// Merging available mandatory and optional plugin name list
|
831
|
+
const availablePlugins=[...Object.keys(pluginsInventory),...remotePluginNames];const activePlugins=[];const failedPlugins=[];pluginsToLoad.forEach(pluginName=>{if(availablePlugins.includes(pluginName)){activePlugins.push(pluginName);}else {failedPlugins.push(pluginName);}});if(failedPlugins.length>0){this.onError(new Error(`Ignoring loading of unknown plugins: ${failedPlugins.join(',')}. Mandatory plugins: ${Object.keys(getMandatoryPluginsMap()).join(',')}. Load option plugins: ${state.plugins.pluginsToLoadFromConfig.value.join(',')}`));}o(()=>{state.plugins.totalPluginsToLoad.value=pluginsToLoad.length;state.plugins.activePlugins.value=activePlugins;state.plugins.failedPlugins.value=failedPlugins;});}/**
|
832
|
+
* Register plugins that are direct imports to PluginEngine
|
833
|
+
*/registerLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{if(state.plugins.activePlugins.value.includes(localPlugin().name)){this.register([localPlugin()]);}});}/**
|
834
|
+
* Register plugins that are dynamic imports to PluginEngine
|
835
|
+
*/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
|
836
|
+
state.plugins.failedPlugins.value=[...state.plugins.failedPlugins.value,remotePluginKey];this.onError(err,remotePluginKey);});})).catch(err=>{this.onError(err);});}/**
|
837
|
+
* Extension point invoke that allows multiple plugins to be registered to it with error handling
|
838
|
+
*/invokeMultiple(extPoint,...args){try{return this.engine.invokeMultiple(extPoint,...args);}catch(e){this.onError(e,extPoint);return [];}}/**
|
839
|
+
* Extension point invoke that allows a single plugin to be registered to it with error handling
|
840
|
+
*/invokeSingle(extPoint,...args){try{return this.engine.invokeSingle(extPoint,...args);}catch(e){this.onError(e,extPoint);return null;}}/**
|
841
|
+
* Plugin engine register with error handling
|
842
|
+
*/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);}});}// TODO: Implement reset API instead
|
843
|
+
unregisterLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{try{this.engine.unregister(localPlugin().name);}catch(e){this.onError(e);}});}/**
|
844
|
+
* Handle errors
|
845
|
+
*/onError(error,customMessage){if(this.errorHandler){this.errorHandler.onError(error,PLUGINS_MANAGER,customMessage);}else {throw error;}}}
|
846
|
+
|
847
|
+
/**
|
848
|
+
* Get mutated error with issue prepended to error message
|
849
|
+
* @param err Original error
|
850
|
+
* @param issue Issue to prepend to error message
|
851
|
+
* @returns Instance of Error with message prepended with issue
|
852
|
+
*/const getMutatedError=(err,issue)=>{let finalError=err;if(!isTypeOfError(err)){finalError=new Error(`${issue}: ${stringifyWithoutCircular(err)}`);}else {finalError.message=`${issue}: ${err.message}`;}return finalError;};
|
853
|
+
|
854
|
+
/**
|
855
|
+
* Utility to parse XHR JSON response
|
856
|
+
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');if(onError&&isFunction(onError)){onError(error);}else {throw error;}}return undefined;};
|
857
|
+
|
858
|
+
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
859
|
+
* Utility to create request configuration based on default options
|
860
|
+
*/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;};/**
|
861
|
+
* Utility implementation of XHR, fetch cannot be used as it requires explicit
|
862
|
+
* origin allowed values and not wildcard for CORS requests with credentials and
|
863
|
+
* this is not supported by our sourceConfig API
|
864
|
+
*/const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT,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
|
865
|
+
return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
866
|
+
const xhrReject=e=>{reject({error:new Error(XHR_DELIVERY_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,xhr.status,xhr.statusText,options.url)),xhr,options});};const xhrError=e=>{reject({error:new Error(XHR_REQUEST_ERROR(FAILED_REQUEST_ERR_MSG_PREFIX,e,options.url)),xhr,options});};xhr.ontimeout=xhrError;xhr.onerror=xhrError;xhr.onload=()=>{if(xhr.status>=200&&xhr.status<400){resolve({response:xhr.responseText,xhr,options});}else {xhrReject();}};xhr.open(options.method,options.url);// The timeout property may be set only in the time interval between a call to the open method
|
867
|
+
// and the first call to the send method in legacy browsers
|
868
|
+
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});}});
|
869
|
+
|
870
|
+
/**
|
871
|
+
* Service to handle data communication with APIs
|
872
|
+
*/class HttpClient{hasErrorHandler=false;constructor(errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.hasErrorHandler=Boolean(this.errorHandler);this.onError=this.onError.bind(this);}/**
|
873
|
+
* Implement requests in a blocking way
|
874
|
+
*/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){this.onError(reason.error??reason);return {data:undefined,details:reason};}}/**
|
875
|
+
* Implement requests in a non-blocking way
|
876
|
+
*/getAsyncData(config){const{callback,url,options,timeout,isRawResponse}=config;const isFireAndForget=!(callback&&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=>{this.onError(data.error??data);if(!isFireAndForget){callback(undefined,data);}});}/**
|
877
|
+
* Handle errors
|
878
|
+
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,HTTP_CLIENT);}else {throw error;}}/**
|
879
|
+
* Set basic authentication header (eg writekey)
|
880
|
+
*/setAuthHeader(value,noBtoa=false){const authVal=noBtoa?value:toBase64(`${value}:`);this.basicAuthHeader=`Basic ${authVal}`;}/**
|
881
|
+
* Clear basic authentication header
|
882
|
+
*/resetAuthHeader(){this.basicAuthHeader=undefined;}}const defaultHttpClient=new HttpClient(defaultErrorHandler,defaultLogger);
|
883
|
+
|
884
|
+
const DEFAULT_EXT_SRC_LOAD_TIMEOUT=10*1000;// 10 seconds
|
885
|
+
|
886
|
+
const EXTERNAL_SOURCE_LOAD_ORIGIN='RS_JS_SDK';
|
887
|
+
|
888
|
+
/**
|
889
|
+
* Create the DOM element to load a script marked as RS SDK originated
|
890
|
+
*
|
891
|
+
* @param {*} url The URL of the script to be loaded
|
892
|
+
* @param {*} id ID for the script tag
|
893
|
+
* @param {*} async Whether to load the script in async mode. Defaults to `true` [optional]
|
894
|
+
* @param {*} onload callback to invoke onload [optional]
|
895
|
+
* @param {*} onerror callback to invoke onerror [optional]
|
896
|
+
* @param {*} extraAttributes key/value pair with html attributes to add in html tag [optional]
|
897
|
+
*
|
898
|
+
* @returns HTMLScriptElement
|
899
|
+
*/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;scriptElement.setAttribute('data-append-origin',EXTERNAL_SOURCE_LOAD_ORIGIN);Object.keys(extraAttributes).forEach(attributeName=>{scriptElement.setAttribute(attributeName,extraAttributes[attributeName]);});return scriptElement;};/**
|
900
|
+
* Add script DOM element to DOM
|
901
|
+
*
|
902
|
+
* @param {*} newScriptElement the script element to add
|
903
|
+
*
|
904
|
+
* @returns
|
905
|
+
*/const insertScript=newScriptElement=>{// First try to add it to the head
|
906
|
+
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
|
907
|
+
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
|
908
|
+
const headElement=document.createElement('head');headElement.appendChild(newScriptElement);const htmlElement=document.getElementsByTagName('html')[0];htmlElement.insertBefore(headElement,htmlElement.firstChild);};/**
|
909
|
+
* Loads external js file as a script html tag
|
910
|
+
*
|
911
|
+
* @param {*} url The URL of the script to be loaded
|
912
|
+
* @param {*} id ID for the script tag
|
913
|
+
* @param {*} timeout loading timeout
|
914
|
+
* @param {*} async Whether to load the script in async mode. Defaults to `true` [optional]
|
915
|
+
* @param {*} extraAttributes key/value pair with html attributes to add in html tag [optional]
|
916
|
+
*
|
917
|
+
* @returns
|
918
|
+
*/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=()=>{globalThis.clearTimeout(timeoutID);reject(new Error(SCRIPT_LOAD_ERROR(id,url)));};// Create the DOM element to load the script and add it to the DOM
|
919
|
+
insertScript(createScriptElement(url,id,async,onload,onerror,extraAttributes));// Reject on timeout
|
920
|
+
timeoutID=globalThis.setTimeout(()=>{reject(new Error(SCRIPT_LOAD_TIMEOUT_ERROR(id,url,timeout)));},timeout);}catch(err){reject(getMutatedError(err,SCRIPT_LOAD_ERROR(id,url)));}});
|
921
|
+
|
922
|
+
/**
|
923
|
+
* Service to load external resources/files
|
924
|
+
*/class ExternalSrcLoader{hasErrorHandler=false;constructor(errorHandler,logger,timeout=DEFAULT_EXT_SRC_LOAD_TIMEOUT){this.errorHandler=errorHandler;this.logger=logger;this.timeout=timeout;this.hasErrorHandler=Boolean(this.errorHandler);this.onError=this.onError.bind(this);}/**
|
925
|
+
* Load external resource of type javascript
|
926
|
+
*/loadJSFile(config){const{url,id,timeout,async,callback,extraAttributes}=config;const isFireAndForget=!(callback&&isFunction(callback));jsFileLoader(url,id,timeout||this.timeout,async,extraAttributes).then(id=>{if(!isFireAndForget){callback(id);}}).catch(err=>{this.onError(err);if(!isFireAndForget){callback();}});}/**
|
927
|
+
* Handle errors
|
928
|
+
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,EXTERNAL_SRC_LOADER);}else {throw error;}}}
|
929
|
+
|
930
|
+
const COOKIE_STORAGE='cookieStorage';const LOCAL_STORAGE='localStorage';const SESSION_STORAGE='sessionStorage';const MEMORY_STORAGE='memoryStorage';
|
931
|
+
|
932
|
+
const STORAGE_TEST_COOKIE='test_rudder_cookie';const STORAGE_TEST_LOCAL_STORAGE='test_rudder_ls';const STORAGE_TEST_SESSION_STORAGE='test_rudder_ss';const STORAGE_TEST_TOP_LEVEL_DOMAIN='__tld__';const CLIENT_DATA_STORE_NAME='clientData';
|
933
|
+
|
934
|
+
const detectAdBlockers=(errorHandler,logger)=>{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
935
|
+
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
936
|
+
// Only the extra query param should make it vulnerable to adblockers
|
937
|
+
// This will work even if the users proxies it.
|
938
|
+
// The edge case where this doesn't work is when HEAD method is not allowed by the server (user's)
|
939
|
+
const baseUrl=new URL(state.lifecycle.sourceConfigUrl.value);const url=`${baseUrl.origin}${baseUrl.pathname}?view=ad`;const httpClient=new HttpClient(errorHandler,logger);httpClient.setAuthHeader(state.lifecycle.writeKey.value);httpClient.getAsyncData({url,options:{// We actually don't need the response from the request, so we are using HEAD
|
940
|
+
method:'HEAD',headers:{'Content-Type':undefined}},isRawResponse:true,callback:(result,details)=>{// not ad blocked if the request is successful or it is not internally redirected on the client side
|
941
|
+
// Often adblockers instead of blocking the request, they redirect it to an internal URL
|
942
|
+
state.capabilities.isAdBlocked.value=details?.error!==undefined||details?.xhr?.responseURL!==url;}});};
|
943
|
+
|
944
|
+
const hasCrypto$1=()=>!isNullOrUndefined(globalThis.crypto)&&isFunction(globalThis.crypto.getRandomValues);const hasUAClientHints=()=>!isNullOrUndefined(globalThis.navigator.userAgentData);const hasBeacon=()=>!isNullOrUndefined(globalThis.navigator.sendBeacon)&&isFunction(globalThis.navigator.sendBeacon);const isIE11=()=>Boolean(globalThis.navigator.userAgent.match(/Trident.*rv:11\./));
|
945
|
+
|
946
|
+
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();});}};
|
947
|
+
|
948
|
+
const isDatasetAvailable=()=>{const testElement=document.createElement('div');testElement.setAttribute('data-a-b','c');return testElement.dataset?testElement.dataset.aB==='c':false;};const legacyJSEngineRequiredPolyfills={URLSearchParams:()=>!globalThis.URLSearchParams,URL:()=>!isFunction(globalThis.URL),MutationObserver:()=>isUndefined(MutationObserver),Promise:()=>isUndefined(Promise),'Number.isNaN':()=>!Number.isNaN,'Number.isInteger':()=>!Number.isInteger,'Array.from':()=>!Array.from,'Array.prototype.find':()=>!Array.prototype.find,'Array.prototype.includes':()=>!Array.prototype.includes,'String.prototype.endsWith':()=>!String.prototype.endsWith,'String.prototype.startsWith':()=>!String.prototype.startsWith,'String.prototype.includes':()=>!String.prototype.includes,'Object.entries':()=>!Object.entries,'Object.values':()=>!Object.values,'Element.prototype.dataset':()=>!isDatasetAvailable(),'String.prototype.replaceAll':()=>!String.prototype.replaceAll,TextEncoder:()=>isUndefined(TextEncoder),TextDecoder:()=>isUndefined(TextDecoder),'String.fromCodePoint':()=>!String.fromCodePoint};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(isCapabilityMissing()){needsPolyfill=true;}}return needsPolyfill;};
|
949
|
+
|
950
|
+
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;};
|
951
|
+
|
952
|
+
const isStorageQuotaExceeded=e=>{const matchingNames=['QuotaExceededError','NS_ERROR_DOM_QUOTA_REACHED'];// [everything except Firefox, Firefox]
|
953
|
+
const matchingCodes=[22,1014];// [everything except Firefox, Firefox]
|
954
|
+
const isQuotaExceededError=matchingNames.includes(e.name)||matchingCodes.includes(e.code);return e instanceof DOMException&&isQuotaExceededError;};// TODO: also check for SecurityErrors
|
955
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage#exceptions
|
956
|
+
const isStorageAvailable=(type=LOCAL_STORAGE,storageInstance,logger)=>{let storage;let testData;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
|
957
|
+
break;case SESSION_STORAGE:storage=storageInstance??globalThis.sessionStorage;testData=STORAGE_TEST_SESSION_STORAGE;break;default:return false;}if(!storage){return false;}storage.setItem(testData,'true');if(storage.getItem(testData)){storage.removeItem(testData);return true;}return false;}catch(err){const msgPrefix=STORAGE_UNAVAILABILITY_ERROR_PREFIX(CAPABILITIES_MANAGER,type);let reason='unavailable';if(isStorageQuotaExceeded(err)){reason='full';}logger?.error(`${msgPrefix}${reason}.`,err);return false;}};
|
958
|
+
|
959
|
+
/**
|
960
|
+
* To get the current timestamp in ISO string format
|
961
|
+
* @returns ISO formatted timestamp string
|
962
|
+
*/const getCurrentTimeFormatted=()=>{const curDateTime=new Date().toISOString();return curDateTime;};
|
963
|
+
|
964
|
+
function random(len){return crypto.getRandomValues(new Uint8Array(len));}
|
965
|
+
|
966
|
+
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;}
|
967
|
+
|
968
|
+
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;}
|
969
|
+
|
970
|
+
const hasCrypto=()=>!isNullOrUndefined(globalThis.crypto)&&isFunction(globalThis.crypto.getRandomValues);
|
971
|
+
|
972
|
+
const generateUUID=()=>{if(hasCrypto()){return v4$1();}return v4();};
|
973
|
+
|
974
|
+
/**
|
975
|
+
* Encode.
|
976
|
+
*/const encode=value=>encodeURIComponent(value);/**
|
977
|
+
* Decode
|
978
|
+
*/const decode=value=>decodeURIComponent(value);/**
|
979
|
+
* Parse cookie `str`
|
980
|
+
*/const parse=str=>{const obj={};const pairs=str.split(/\s*;\s*/);let pair;if(!pairs[0]){return obj;}pairs.forEach(pairItem=>{pair=pairItem.split('=');obj[decode(pair[0])]=decode(pair[1]);});return obj;};/**
|
981
|
+
* Set cookie `name` to `value`
|
982
|
+
*/const set=(name,value,optionsConfig)=>{const options={...optionsConfig}||{};let cookieString=`${encode(name)}=${encode(value)}`;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;};/**
|
983
|
+
* Return all cookies
|
984
|
+
*/const all=()=>{const cookieStringValue=globalThis.document.cookie;return parse(cookieStringValue);};/**
|
985
|
+
* Get cookie `name`
|
986
|
+
*/const get=name=>all()[name];/**
|
987
|
+
* Set or get cookie `name` with `value` and `options` object
|
988
|
+
*/ // eslint-disable-next-line func-names
|
989
|
+
const cookie=function(name,value,options){switch(arguments.length){case 3:case 2:return set(name,value,options);case 1:if(name){return get(name);}return all();default:return all();}};
|
990
|
+
|
991
|
+
const legacyGetHostname=href=>{const l=document.createElement('a');l.href=href;return l.hostname;};/**
|
992
|
+
* Levels returns all levels of the given url
|
993
|
+
*
|
994
|
+
* The method returns an empty array when the hostname is an ip.
|
995
|
+
*/const levelsFunc=url=>{// This is called before the polyfills load thus new URL cannot be used
|
996
|
+
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.
|
997
|
+
if(parts.length===4&&last===parseInt(last,10).toString()){return levels;}// Localhost.
|
998
|
+
if(parts.length<=1){// Fix to support localhost
|
999
|
+
if(parts[0].indexOf('localhost')!==-1){return ['localhost'];}return levels;}// Create levels.
|
1000
|
+
for(let i=parts.length-2;i>=0;i-=1){levels.push(parts.slice(i).join('.'));}return levels;};/**
|
1001
|
+
* Get the top domain.
|
1002
|
+
*
|
1003
|
+
* The function constructs the levels of domain and attempts to set a global
|
1004
|
+
* cookie on each one when it succeeds it returns the top level domain.
|
1005
|
+
*
|
1006
|
+
* The method returns an empty string when the hostname is an ip.
|
1007
|
+
*/const domain=url=>{const levels=levelsFunc(url);// Lookup the real top level one.
|
1008
|
+
// eslint-disable-next-line unicorn/no-for-loop
|
1009
|
+
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}`};// Set cookie on domain
|
1010
|
+
cookie(cname,1,opts);// If successful
|
1011
|
+
if(cookie(cname)){// Remove cookie from domain
|
1012
|
+
cookie(cname,null,opts);return domain;}}return '';};
|
1013
|
+
|
1014
|
+
const getDefaultCookieOptions=()=>{const topDomain=domain(globalThis.location.href);return {maxage:DEFAULT_COOKIE_MAX_AGE,path:'/',domain:!topDomain||topDomain==='.'?undefined:topDomain,samesite:CookieSameSite.Lax,enabled:true};};const getDefaultLocalStorageOptions=()=>({enabled:true});const getDefaultInMemoryStorageOptions=()=>({enabled:true});
|
1015
|
+
|
1016
|
+
/**
|
1017
|
+
* A storage utility to persist values in cookies via Storage interface
|
1018
|
+
*/class CookieStorage{static globalSingleton=null;isSupportAvailable=true;isEnabled=true;length=0;constructor(options={},logger){if(CookieStorage.globalSingleton){// eslint-disable-next-line no-constructor-return
|
1019
|
+
return CookieStorage.globalSingleton;}this.options=getDefaultCookieOptions();this.logger=logger;this.configure(options);CookieStorage.globalSingleton=this;}configure(options){this.options=mergeDeepRight(this.options??{},options);this.isSupportAvailable=isStorageAvailable(COOKIE_STORAGE,this,this.logger);this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){cookie(key,value,this.options);this.length=Object.keys(cookie()).length;return true;}// eslint-disable-next-line class-methods-use-this
|
1020
|
+
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
|
1021
|
+
clear(){// Not implemented
|
1022
|
+
// getting a list of all cookie storage keys and remove all values
|
1023
|
+
// sounds risky to do as it will take on all top domain cookies
|
1024
|
+
// better to explicitly clear specific ones if needed
|
1025
|
+
}// This cannot be implemented for cookies
|
1026
|
+
// eslint-disable-next-line class-methods-use-this
|
1027
|
+
key(index){const cookies=cookie();const cookieNames=Object.keys(cookies);return isUndefined(cookieNames[index])?null:cookieNames[index];}}
|
1028
|
+
|
1029
|
+
/**
|
1030
|
+
* A storage utility to retain values in memory via Storage interface
|
1031
|
+
*/class InMemoryStorage{isEnabled=true;length=0;data={};constructor(options,logger){this.options=getDefaultInMemoryStorageOptions();this.logger=logger;this.configure(options??{});}configure(options){this.options=mergeDeepRight(this.options,options);this.isEnabled=Boolean(this.options.enabled);return this.options;}setItem(key,value){this.data[key]=value;this.length=Object.keys(this.data).length;return value;}getItem(key){if(key in this.data){return this.data[key];}return null;}removeItem(key){if(key in this.data){delete this.data[key];}this.length=Object.keys(this.data).length;return null;}clear(){this.data={};this.length=0;}key(index){return Object.keys(this.data)[index];}}const defaultInMemoryStorage=new InMemoryStorage({},defaultLogger);
|
1032
|
+
|
1033
|
+
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
1034
|
+
|
1035
|
+
function getDefaultExportFromCjs (x) {
|
1036
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
var store$1 = {exports: {}};
|
1040
|
+
|
1041
|
+
(function(module,exports){(function(global,factory){module.exports=factory();})(commonjsGlobal,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
|
1042
|
+
// Error: QuotaExceededError
|
1043
|
+
function dealIncognito(storage){var _KEY='_Is_Incognit',_VALUE='yes';try{// NOTE: set default storage when not passed in
|
1044
|
+
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
|
1045
|
+
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){if(!key){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;}};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$1);var storeExports=store$1.exports;const store = /*@__PURE__*/getDefaultExportFromCjs(storeExports);
|
1046
|
+
|
1047
|
+
// check if the get, set overloads and search methods are used at all
|
1048
|
+
// if we do, ensure we provide types to support overloads as per storejs docs
|
1049
|
+
// https://www.npmjs.com/package/storejs
|
1050
|
+
/**
|
1051
|
+
* A storage utility to persist values in localstorage via Storage interface
|
1052
|
+
*/class LocalStorage{isSupportAvailable=true;isEnabled=true;length=0;constructor(options={},logger){this.options=getDefaultLocalStorageOptions();this.logger=logger;this.configure(options);}configure(options){this.options=mergeDeepRight(this.options,options);this.isSupportAvailable=isStorageAvailable(LOCAL_STORAGE,this,this.logger);this.isEnabled=Boolean(this.options.enabled&&this.isSupportAvailable);return this.options;}setItem(key,value){store.set(key,value);this.length=store.keys().length;}// eslint-disable-next-line class-methods-use-this
|
1053
|
+
getItem(key){const value=store.get(key);return isUndefined(value)?null:value;}removeItem(key){store.remove(key);this.length=store.keys().length;}clear(){store.clear();this.length=0;}// eslint-disable-next-line class-methods-use-this
|
1054
|
+
key(index){return store.keys()[index];}}const defaultLocalStorage=new LocalStorage({},defaultLogger);
|
1055
|
+
|
1056
|
+
/**
|
1057
|
+
* A utility to retrieve the storage singleton instance by type
|
1058
|
+
*/const getStorageEngine=type=>{switch(type){case LOCAL_STORAGE:return defaultLocalStorage;case SESSION_STORAGE:return globalThis.sessionStorage;case MEMORY_STORAGE:return defaultInMemoryStorage;case COOKIE_STORAGE:return new CookieStorage({},defaultLogger);default:return defaultInMemoryStorage;}};/**
|
1059
|
+
* Configure cookie storage singleton
|
1060
|
+
*/const configureCookieStorageEngine=options=>{new CookieStorage({},defaultLogger).configure(options);};/**
|
1061
|
+
* Configure local storage singleton
|
1062
|
+
*/const configureLocalStorageEngine=options=>{defaultLocalStorage.configure(options);};/**
|
1063
|
+
* Configure in memory storage singleton
|
1064
|
+
*/const configureInMemoryStorageEngine=options=>{defaultInMemoryStorage.configure(options);};/**
|
1065
|
+
* Configure all storage singleton instances
|
1066
|
+
*/const configureStorageEngines=(cookieOptions={},localStorageOptions={},inMemoryStorageOptions={})=>{configureCookieStorageEngine(cookieOptions);configureLocalStorageEngine(localStorageOptions);configureInMemoryStorageEngine(inMemoryStorageOptions);};
|
1067
|
+
|
1068
|
+
/**
|
1069
|
+
* Store Implementation with dedicated storage
|
1070
|
+
*/class Store{hasErrorHandler=false;constructor(config,engine,pluginsManager){this.id=config.id;this.name=config.name;this.isEncrypted=config.isEncrypted??false;this.validKeys=config.validKeys??{};this.engine=engine??getStorageEngine(LOCAL_STORAGE);this.noKeyValidation=Object.keys(this.validKeys).length===0;this.noCompoundKey=config.noCompoundKey;this.originalEngine=this.engine;this.errorHandler=config.errorHandler??defaultErrorHandler;this.hasErrorHandler=Boolean(this.errorHandler);this.logger=config.logger??defaultLogger;this.pluginsManager=pluginsManager;}/**
|
1071
|
+
* Ensure the key is valid and with correct format
|
1072
|
+
*/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
|
1073
|
+
let compoundKey;Object.values(validKeys).forEach(validKeyName=>{if(validKeyName===key){compoundKey=noCompoundKey?key:[name,id,key].join('.');}});return compoundKey;}/**
|
1074
|
+
* Switch to inMemoryEngine, bringing any existing data with.
|
1075
|
+
*/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
|
1076
|
+
// better to keep other queues in localstorage to be flushed later
|
1077
|
+
// than to pull them into memory and remove them from durable storage
|
1078
|
+
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
|
1079
|
+
// if cookies are not available and localstorage is full?
|
1080
|
+
this.remove(key);});this.engine=inMemoryStorage;}/**
|
1081
|
+
* Set value by key.
|
1082
|
+
*/set(key,value){const validKey=this.createValidKey(key);if(!validKey){return;}try{// storejs that is used in localstorage engine already stringifies json
|
1083
|
+
this.engine.setItem(validKey,this.encrypt(stringifyWithoutCircular(value,false,[],this.logger)));}catch(err){if(isStorageQuotaExceeded(err)){this.logger?.warn(STORAGE_QUOTA_EXCEEDED_WARNING(STORE_MANAGER));// switch to inMemory engine
|
1084
|
+
this.swapQueueStoreToInMemoryEngine();// and save it there
|
1085
|
+
this.set(key,value);}else {this.onError(getMutatedError(err,STORE_DATA_SAVE_ERROR(key)));}}}/**
|
1086
|
+
* Get by Key.
|
1087
|
+
*/get(key){const validKey=this.createValidKey(key);try{if(!validKey){return null;}const str=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(str)){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
1088
|
+
return JSON.parse(str);}catch(err){this.onError(new Error(`${STORE_DATA_FETCH_ERROR(key)}: ${err.message}`));return null;}}/**
|
1089
|
+
* Remove by Key.
|
1090
|
+
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
1091
|
+
* Get original engine
|
1092
|
+
*/getOriginalEngine(){return this.originalEngine;}/**
|
1093
|
+
* Decrypt values
|
1094
|
+
*/decrypt(value){if(isNullOrUndefined(value)){return null;}return this.crypto(value,'decrypt');}/**
|
1095
|
+
* Encrypt value
|
1096
|
+
*/encrypt(value){return this.crypto(value,'encrypt');}/**
|
1097
|
+
* Extension point to use with encryption plugins
|
1098
|
+
*/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??'';}/**
|
1099
|
+
* Handle errors
|
1100
|
+
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,`Store ${this.id}`);}else {throw error;}}}
|
1101
|
+
|
1102
|
+
/**
|
1103
|
+
* A service to manage stores & available storage client configurations
|
1104
|
+
*/class StoreManager{stores={};isInitialized=false;hasErrorHandler=false;constructor(pluginsManager,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.hasErrorHandler=Boolean(this.errorHandler);this.pluginsManager=pluginsManager;this.onError=this.onError.bind(this);}/**
|
1105
|
+
* Configure available storage client instances
|
1106
|
+
*/init(){if(this.isInitialized){return;}const config={cookieOptions:{samesite:state.loadOptions.value.sameSiteCookie,secure:state.loadOptions.value.secureCookie,domain:state.loadOptions.value.setCookieDomain,enabled:true},localStorageOptions:{enabled:true},inMemoryStorageOptions:{enabled:true}};configureStorageEngines(removeUndefinedValues(config.cookieOptions),removeUndefinedValues(config.localStorageOptions),removeUndefinedValues(config.inMemoryStorageOptions));this.initClientDataStore();this.isInitialized=true;}/**
|
1107
|
+
* Create store to persist data used by the SDK like session, used details etc
|
1108
|
+
*/initClientDataStore(){let storageType='';// First try setting the storage to cookie else to localstorage
|
1109
|
+
if(getStorageEngine(COOKIE_STORAGE)?.isEnabled){storageType=COOKIE_STORAGE;}else if(getStorageEngine(LOCAL_STORAGE)?.isEnabled){storageType=LOCAL_STORAGE;}// TODO: fallback to in-memory storage if not other storage is available
|
1110
|
+
// TODO: should we fallback to session storage instead so we retain values on page refresh, navigation etc?
|
1111
|
+
if(!storageType){this.logger?.error(STORAGE_UNAVAILABLE_ERROR(STORE_MANAGER));return;}// TODO: fill in extra config values and bring them in from StoreManagerOptions if needed
|
1112
|
+
// TODO: should we pass the keys for all in order to validate or leave free as v1.1?
|
1113
|
+
this.setStore({id:CLIENT_DATA_STORE_NAME,name:CLIENT_DATA_STORE_NAME,isEncrypted:true,noCompoundKey:true,type:storageType});}/**
|
1114
|
+
* Create a new store
|
1115
|
+
*/setStore(storeConfig){const storageEngine=getStorageEngine(storeConfig.type);this.stores[storeConfig.id]=new Store(storeConfig,storageEngine,this.pluginsManager);return this.stores[storeConfig.id];}/**
|
1116
|
+
* Retrieve a store
|
1117
|
+
*/getStore(id){return this.stores[id];}/**
|
1118
|
+
* Handle errors
|
1119
|
+
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,STORE_MANAGER);}else {throw error;}}}
|
1120
|
+
|
1121
|
+
/**
|
1122
|
+
* Removes trailing slash from url
|
1123
|
+
* @param url
|
1124
|
+
* @returns url
|
1125
|
+
*/const removeTrailingSlashes=url=>url&&url.endsWith('/')?removeTrailingSlashes(url.substring(0,url.length-1)):url;/**
|
1126
|
+
* Checks if provided url is valid or not
|
1127
|
+
* @param url
|
1128
|
+
* @returns true if `url` is valid and false otherwise
|
1129
|
+
*/const isValidUrl=url=>{try{const validUrl=new URL(url);return Boolean(validUrl);}catch(err){return false;}};/**
|
1130
|
+
* Get the referring domain from the referrer URL
|
1131
|
+
* @param referrer Page referrer
|
1132
|
+
* @returns Page referring domain
|
1133
|
+
*/const getReferringDomain=referrer=>{let referringDomain='';try{const url=new URL(referrer);referringDomain=url.host;}catch(error){// Do nothing
|
1134
|
+
}return referringDomain;};/**
|
1135
|
+
* Extracts UTM parameters from the URL
|
1136
|
+
* @param url Page URL
|
1137
|
+
* @returns UTM parameters
|
1138
|
+
*/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
|
1139
|
+
if(utmParam==='campaign'){utmParam='name';}result[utmParam]=value;}});}catch(error){// Do nothing
|
1140
|
+
}return result;};/**
|
1141
|
+
* To get the URL until the hash
|
1142
|
+
* @param url The input URL
|
1143
|
+
* @returns URL until the hash
|
1144
|
+
*/const getUrlWithoutHash=url=>{let urlWithoutHash=url;try{const urlObj=new URL(url);urlWithoutHash=urlObj.origin+urlObj.pathname+urlObj.search;}catch(error){// Do nothing
|
1145
|
+
}return urlWithoutHash;};
|
1146
|
+
|
1147
|
+
const validateWriteKey=writeKey=>{if(!writeKey||writeKey.trim().length===0){throw new Error(WRITE_KEY_VALIDATION_ERROR(writeKey));}};const validateDataPlaneUrl=dataPlaneUrl=>{if(dataPlaneUrl&&!isValidUrl(dataPlaneUrl)){throw new Error(DATA_PLANE_URL_VALIDATION_ERROR(dataPlaneUrl));}};const validateLoadArgs=(writeKey,dataPlaneUrl)=>{validateWriteKey(writeKey);validateDataPlaneUrl(dataPlaneUrl);};const isValidSourceConfig=res=>isObjectLiteralAndNotNull(res)&&isObjectLiteralAndNotNull(res.source)&&!isNullOrUndefined(res.source.id)&&isObjectLiteralAndNotNull(res.source.config)&&Array.isArray(res.source.destinations);
|
1148
|
+
|
1149
|
+
/**
|
1150
|
+
* A function to filter enabled destinations and map to required properties only
|
1151
|
+
* @param destinations
|
1152
|
+
*
|
1153
|
+
* @returns Destination[]
|
1154
|
+
*/const filterEnabledDestination=destinations=>{const nativeDestinations=[];destinations.forEach(destination=>{if(destination.enabled&&!destination.deleted){nativeDestinations.push({id:destination.id,displayName:destination.destinationDefinition.displayName,config:destination.config,shouldApplyDeviceModeTransformation:destination.shouldApplyDeviceModeTransformation||false,propagateEventsUntransformedOnError:destination.propagateEventsUntransformedOnError||false,userFriendlyId:`${destination.destinationDefinition.displayName.replaceAll(' ','-')}___${destination.id}`});}});return nativeDestinations;};
|
1155
|
+
|
1156
|
+
const defaultOptionalPluginsList=[PluginName.Bugsnag,PluginName.DeviceModeDestinations,PluginName.ErrorReporting,PluginName.ExternalAnonymousId,PluginName.GoogleLinker,PluginName.NativeDestinationQueue,PluginName.StorageEncryption,PluginName.StorageEncryptionLegacy,PluginName.StorageMigrator,PluginName.XhrQueue,PluginName.OneTrustConsentManager,PluginName.KetchConsentManager,PluginName.BeaconQueue];
|
1157
|
+
|
1158
|
+
const normalizeLoadOptions=(loadOptionsFromState,loadOptions)=>{// TODO: add all the validations as per
|
1159
|
+
// https://github.com/rudderlabs/rudder-sdk-js/blob/a620e11f98e1438be34114ad40b325201b1d7a6e/src/core/analytics.js#L1156
|
1160
|
+
// TODO: Maybe add warnings for invalid values
|
1161
|
+
const normalizedLoadOpts=clone$1(loadOptions);normalizedLoadOpts.setCookieDomain=isDefined(normalizedLoadOpts.setCookieDomain)&&isString(normalizedLoadOpts.setCookieDomain)?normalizedLoadOpts.setCookieDomain:undefined;normalizedLoadOpts.secureCookie=normalizedLoadOpts.secureCookie===true;normalizedLoadOpts.sameSiteCookie=isDefined(normalizedLoadOpts.sameSiteCookie)&&Object.values(CookieSameSite).includes(normalizedLoadOpts.sameSiteCookie)?normalizedLoadOpts.sameSiteCookie:undefined;normalizedLoadOpts.plugins=normalizedLoadOpts.plugins??defaultOptionalPluginsList;normalizedLoadOpts.useGlobalIntegrationsConfigInEvents=normalizedLoadOpts.useGlobalIntegrationsConfigInEvents===true;normalizedLoadOpts.bufferDataPlaneEventsUntilReady=normalizedLoadOpts.bufferDataPlaneEventsUntilReady===true;normalizedLoadOpts.sendAdblockPage=normalizedLoadOpts.sendAdblockPage===true;normalizedLoadOpts.sendAdblockPageOptions=isObjectLiteralAndNotNull(normalizedLoadOpts.sendAdblockPageOptions)?normalizedLoadOpts.sendAdblockPageOptions:{};normalizedLoadOpts.storage=isObjectLiteralAndNotNull(normalizedLoadOpts.storage)?removeUndefinedAndNullValues(normalizedLoadOpts.storage):{};normalizedLoadOpts.storage.migrate=normalizedLoadOpts.storage?.migrate===true;normalizedLoadOpts.beaconQueueOptions=isObjectLiteralAndNotNull(normalizedLoadOpts.beaconQueueOptions)?removeUndefinedAndNullValues(normalizedLoadOpts.beaconQueueOptions):{};normalizedLoadOpts.destinationsQueueOptions=isObjectLiteralAndNotNull(normalizedLoadOpts.destinationsQueueOptions)?removeUndefinedAndNullValues(normalizedLoadOpts.destinationsQueueOptions):{};normalizedLoadOpts.queueOptions=isObjectLiteralAndNotNull(normalizedLoadOpts.queueOptions)?removeUndefinedAndNullValues(normalizedLoadOpts.queueOptions):{};const mergedLoadOptions=mergeDeepRight(loadOptionsFromState,normalizedLoadOpts);return mergedLoadOptions;};const getSourceConfigURL=(configUrlHost=DEFAULT_CONFIG_BE_URL)=>`${configUrlHost}/sourceConfig/?p=${MODULE_TYPE}&v=${APP_VERSION}&build=${BUILD_TYPE}`;
|
1162
|
+
|
1163
|
+
const DEFAULT_REGION='US';/**
|
1164
|
+
* A function to get url from source config response
|
1165
|
+
* @param {array} urls An array of objects containing urls
|
1166
|
+
* @returns
|
1167
|
+
*/const getDefaultUrlOfRegion=urls=>{let url;if(Array.isArray(urls)&&urls.length>0){const obj=urls.find(elem=>elem.default===true);if(obj&&isValidUrl(obj.url)){return obj.url;}}return url;};const validateResidencyServerRegion=(residencyServerRegion,logger)=>{if(residencyServerRegion&&!Object.values(ResidencyServerRegion).includes(residencyServerRegion)){logger?.warn(UNSUPPORTED_RESIDENCY_SERVER_REGION_WARNING(CONFIG_MANAGER,residencyServerRegion,DEFAULT_REGION));return undefined;}return residencyServerRegion;};/**
|
1168
|
+
* A function to determine the dataPlaneUrl
|
1169
|
+
* @param {Object} dataplanes An object containing dataPlaneUrl for different region
|
1170
|
+
* @param {String} serverUrl dataPlaneUrl provided in the load call
|
1171
|
+
* @param {String} residencyServerRegion User provided residency server region
|
1172
|
+
* @param {Logger} logger logger instance
|
1173
|
+
* @returns The data plane URL string to use
|
1174
|
+
*/const resolveDataPlaneUrl=(dataplanes,serverUrl,residencyServerRegion,logger)=>{// Check if dataPlanes object is present in source config
|
1175
|
+
if(dataplanes&&Object.keys(dataplanes).length>0){const region=validateResidencyServerRegion(residencyServerRegion,logger)??DEFAULT_REGION;const regionUrlArr=dataplanes[region]||dataplanes[DEFAULT_REGION];const defaultUrl=getDefaultUrlOfRegion(regionUrlArr);if(defaultUrl){return defaultUrl;}}// return the dataPlaneUrl provided in load API(if available)
|
1176
|
+
if(serverUrl){return serverUrl;}// return undefined if data plane url can not be determined
|
1177
|
+
return undefined;};
|
1178
|
+
|
1179
|
+
const isErrorReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.errors?.enabled===true;const getErrorReportingProviderNameFromConfig=sourceConfig=>sourceConfig?.statsCollection?.errors?.provider;const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
|
1180
|
+
|
1181
|
+
/**
|
1182
|
+
* Determines the SDK url
|
1183
|
+
* @returns sdkURL
|
1184
|
+
*/const getSDKUrl=()=>{const scripts=document.getElementsByTagName('script');let sdkURL;const scriptList=Array.prototype.slice.call(scripts);scriptList.some(script=>{const curScriptSrc=removeTrailingSlashes(script.getAttribute('src'));if(curScriptSrc){const urlMatches=curScriptSrc.match(/^.*rsa?(\.min)?\.js$/);if(urlMatches){sdkURL=curScriptSrc;return true;}}return false;});// TODO: Return the URL object instead of the plain URL string
|
1185
|
+
return sdkURL;};/**
|
1186
|
+
* Updates the reporting state variables from the source config data
|
1187
|
+
* @param res Source config
|
1188
|
+
* @param logger Logger instance
|
1189
|
+
*/const updateReportingState=(res,logger)=>{state.reporting.isErrorReportingEnabled.value=isErrorReportingEnabled(res.source.config);state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);if(state.reporting.isErrorReportingEnabled.value){const errReportingProvider=getErrorReportingProviderNameFromConfig(res.source.config);// Get the corresponding plugin name of the selected error reporting provider from the supported error reporting providers
|
1190
|
+
const errReportingProviderPlugin=errReportingProvider?ErrorReportingProvidersToPluginNameMap[errReportingProvider]:undefined;if(!isUndefined(errReportingProvider)&&!errReportingProviderPlugin){// set the default error reporting provider
|
1191
|
+
logger?.warn(UNSUPPORTED_ERROR_REPORTING_PROVIDER_WARNING(CONFIG_MANAGER,errReportingProvider,ErrorReportingProvidersToPluginNameMap,DEFAULT_ERROR_REPORTING_PROVIDER));}state.reporting.errorReportingProviderPluginName.value=errReportingProviderPlugin??ErrorReportingProvidersToPluginNameMap[DEFAULT_ERROR_REPORTING_PROVIDER];}};const updateStorageState=logger=>{let storageEncryptionVersion=state.loadOptions.value.storage?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
|
1192
|
+
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;}o(()=>{state.storage.encryptionPluginName.value=StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];// Allow migration only if the configured encryption version is the default encryption version
|
1193
|
+
const configuredMigrationValue=state.loadOptions.value.storage?.migrate;state.storage.migrate.value=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&state.storage.migrate.value!==configuredMigrationValue){logger?.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}});};
|
1194
|
+
|
1195
|
+
/**
|
1196
|
+
* A function that determines integration SDK loading path
|
1197
|
+
* @param requiredVersion
|
1198
|
+
* @param lockIntegrationsVersion
|
1199
|
+
* @param customIntegrationsCDNPath
|
1200
|
+
* @returns
|
1201
|
+
*/const getIntegrationsCDNPath=(requiredVersion,lockIntegrationsVersion,customIntegrationsCDNPath)=>{let integrationsCDNPath='';// Get the CDN base URL from the user provided URL if any
|
1202
|
+
if(customIntegrationsCDNPath){integrationsCDNPath=removeTrailingSlashes(customIntegrationsCDNPath);if(!integrationsCDNPath||integrationsCDNPath&&!isValidUrl(integrationsCDNPath)){throw new Error(INTG_CDN_BASE_URL_ERROR);}return integrationsCDNPath;}// Get the base path from the SDK script tag src attribute or use the default path
|
1203
|
+
const sdkURL=getSDKUrl();integrationsCDNPath=sdkURL&&isString(sdkURL)?sdkURL.split('/').slice(0,-1).concat(CDN_INT_DIR).join('/'):DEST_SDK_BASE_URL;// If version is not locked it will always get the latest version of the integrations
|
1204
|
+
if(lockIntegrationsVersion){integrationsCDNPath=integrationsCDNPath.replace(CDN_ARCH_VERSION_DIR,requiredVersion);}return integrationsCDNPath;};/**
|
1205
|
+
* A function that determines plugins SDK loading path
|
1206
|
+
* @param customPluginsCDNPath
|
1207
|
+
* @returns
|
1208
|
+
*/const getPluginsCDNPath=customPluginsCDNPath=>{let pluginsCDNPath='';// Get the CDN base URL from the user provided URL if any
|
1209
|
+
if(customPluginsCDNPath){pluginsCDNPath=removeTrailingSlashes(customPluginsCDNPath);if(!pluginsCDNPath||pluginsCDNPath&&!isValidUrl(pluginsCDNPath)){throw new Error(PLUGINS_CDN_BASE_URL_ERROR);}return pluginsCDNPath;}// Get the base path from the SDK script tag src attribute or use the default path
|
1210
|
+
const sdkURL=getSDKUrl();pluginsCDNPath=sdkURL&&isString(sdkURL)?sdkURL.split('/').slice(0,-1).concat(CDN_PLUGINS_DIR).join('/'):PLUGINS_BASE_URL;return pluginsCDNPath;};
|
1211
|
+
|
1212
|
+
/**
|
1213
|
+
* A function to get the name of the consent manager with enabled true set in the load options
|
1214
|
+
* @param cookieConsentOptions Input provided as load option
|
1215
|
+
* @returns string|undefined
|
1216
|
+
*
|
1217
|
+
* Example input: {
|
1218
|
+
* oneTrust:{
|
1219
|
+
* enabled: true
|
1220
|
+
* }
|
1221
|
+
* }
|
1222
|
+
*
|
1223
|
+
* Output: 'oneTrust'
|
1224
|
+
*/const getUserSelectedConsentManager=cookieConsentOptions=>{if(!isNonEmptyObject(cookieConsentOptions)){return undefined;}const validCookieConsentOptions=cookieConsentOptions;return Object.keys(validCookieConsentOptions).find(e=>e&&validCookieConsentOptions[e].enabled===true);};
|
1225
|
+
|
1226
|
+
class ConfigManager{hasErrorHandler=false;constructor(httpClient,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.httpClient=httpClient;this.hasErrorHandler=Boolean(this.errorHandler);this.onError=this.onError.bind(this);this.processConfig=this.processConfig.bind(this);}attachEffects(){b(()=>{this.logger?.setMinLogLevel(state.lifecycle.logLevel.value);});}/**
|
1227
|
+
* A function to validate, construct and store loadOption, lifecycle, source and destination
|
1228
|
+
* config related information in global state
|
1229
|
+
*/init(){let consentManagerPluginName;this.attachEffects();validateLoadArgs(state.lifecycle.writeKey.value,state.lifecycle.dataPlaneUrl.value);const lockIntegrationsVersion=state.loadOptions.value.lockIntegrationsVersion===true;try{// determine the path to fetch integration SDK from
|
1230
|
+
const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,state.loadOptions.value.destSDKBaseURL);// determine the path to fetch remote plugins from
|
1231
|
+
const pluginsCDNPath=getPluginsCDNPath(state.loadOptions.value.pluginsSDKBaseURL);// Get the consent manager if provided as load option
|
1232
|
+
const selectedConsentManager=getUserSelectedConsentManager(state.loadOptions.value.cookieConsentManager);if(selectedConsentManager){// Get the corresponding plugin name of the selected consent manager from the supported consent managers
|
1233
|
+
consentManagerPluginName=ConsentManagersToPluginNameMap[selectedConsentManager];if(!consentManagerPluginName){this.logger?.error(UNSUPPORTED_CONSENT_MANAGER_ERROR(CONFIG_MANAGER,selectedConsentManager,ConsentManagersToPluginNameMap));}}updateStorageState(this.logger);// set application lifecycle state in global state
|
1234
|
+
o(()=>{state.lifecycle.integrationsCDNPath.value=intgCdnUrl;state.lifecycle.pluginsCDNPath.value=pluginsCDNPath;if(state.loadOptions.value.logLevel){state.lifecycle.logLevel.value=state.loadOptions.value.logLevel;}if(state.loadOptions.value.configUrl){state.lifecycle.sourceConfigUrl.value=new URL(`${getSourceConfigURL(state.loadOptions.value.configUrl)}&writeKey=${state.lifecycle.writeKey.value}&lockIntegrationsVersion=${lockIntegrationsVersion}`).toString();}// Set consent manager plugin name in state
|
1235
|
+
state.consents.activeConsentManagerPluginName.value=consentManagerPluginName;});}catch(err){const issue='Failed to load the SDK';this.onError(getMutatedError(err,issue));return;}this.getConfig();}/**
|
1236
|
+
* Handle errors
|
1237
|
+
*/onError(error,customMessage,shouldAlwaysThrow){if(this.hasErrorHandler){this.errorHandler?.onError(error,CONFIG_MANAGER,customMessage,shouldAlwaysThrow);}else {throw error;}}/**
|
1238
|
+
* A callback function that is executed once we fetch the source config response.
|
1239
|
+
* Use to construct and store information that are dependent on the sourceConfig.
|
1240
|
+
*/processConfig(response,details){// TODO: add retry logic with backoff based on rejectionDetails.xhr.status
|
1241
|
+
// We can use isErrRetryable utility method
|
1242
|
+
if(!response){this.onError(SOURCE_CONFIG_FETCH_ERROR(details?.error));return;}let res;const errMessage='Unable to process/parse source config';try{if(isString(response)){res=JSON.parse(response);}else {res=response;}}catch(e){this.onError(e,errMessage,true);return;}if(!isValidSourceConfig(res)){this.onError(new Error(errMessage),undefined,true);return;}// determine the dataPlane url
|
1243
|
+
const dataPlaneUrl=resolveDataPlaneUrl(res.source.dataplanes,state.lifecycle.dataPlaneUrl.value,state.loadOptions.value.residencyServer,this.logger);if(!dataPlaneUrl){this.onError(new Error(DATA_PLANE_URL_ERROR),undefined,true);return;}const nativeDestinations=res.source.destinations.length>0?filterEnabledDestination(res.source.destinations):[];// set in the state --> source, destination, lifecycle, reporting
|
1244
|
+
o(()=>{// set source related information in state
|
1245
|
+
state.source.value={config:res.source.config,id:res.source.id};// set device mode destination related information in state
|
1246
|
+
state.nativeDestinations.configuredDestinations.value=nativeDestinations;// set application lifecycle state
|
1247
|
+
// Cast to string as we are sure that the value is not undefined
|
1248
|
+
state.lifecycle.activeDataplaneUrl.value=removeTrailingSlashes(dataPlaneUrl);state.lifecycle.status.value=LifecycleStatus.Configured;// set the values in state for reporting slice
|
1249
|
+
updateReportingState(res,this.logger);// set the desired optional plugins
|
1250
|
+
state.plugins.pluginsToLoadFromConfig.value=state.loadOptions.value.plugins??[];// set application lifecycle state
|
1251
|
+
state.lifecycle.activeDataplaneUrl.value=dataPlaneUrl;state.lifecycle.status.value=LifecycleStatus.Configured;});}/**
|
1252
|
+
* A function to fetch source config either from /sourceConfig endpoint
|
1253
|
+
* or from getSourceConfig load option
|
1254
|
+
* @returns
|
1255
|
+
*/getConfig(){const sourceConfigFunc=state.loadOptions.value.getSourceConfig;if(sourceConfigFunc){if(!isFunction(sourceConfigFunc)){throw new Error(SOURCE_CONFIG_OPTION_ERROR);}// fetch source config from the function
|
1256
|
+
const res=sourceConfigFunc();if(res instanceof Promise){res.then(pRes=>this.processConfig(pRes)).catch(err=>{this.onError(err,'SourceConfig');});}else {this.processConfig(res);}return;}// fetch source config from config url API
|
1257
|
+
this.httpClient.getAsyncData({url:state.lifecycle.sourceConfigUrl.value,options:{headers:{'Content-Type':undefined}},callback:this.processConfig});}}
|
1258
|
+
|
1259
|
+
/**
|
1260
|
+
* Get the referrer URL
|
1261
|
+
* @returns The referrer URL
|
1262
|
+
*/const getReferrer=()=>document.referrer||'$direct';/**
|
1263
|
+
* To get the canonical URL of the page
|
1264
|
+
* @returns canonical URL
|
1265
|
+
*/const getCanonicalUrl=()=>{const tags=document.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=()=>{if(isUndefined(globalThis.navigator)){return null;}let{userAgent}=globalThis.navigator;const{brave}=globalThis.navigator;// For supporting Brave browser detection,
|
1266
|
+
// add "Brave/<version>" to the user agent with the version value from the Chrome component
|
1267
|
+
if(brave&&Object.getPrototypeOf(brave).isBrave){// Example:
|
1268
|
+
// 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
|
1269
|
+
const matchedArr=userAgent.match(/(chrome)\/([\w.]+)/i);if(matchedArr){userAgent=`${userAgent} Brave/${matchedArr[2]}`;}}return userAgent;};const getLanguage=()=>{if(isUndefined(globalThis.navigator)){return null;}return globalThis.navigator.language??globalThis.navigator.browserLanguage;};/**
|
1270
|
+
* Default page properties
|
1271
|
+
* @returns Default page properties
|
1272
|
+
*/const getDefaultPageProperties=()=>{const canonicalUrl=getCanonicalUrl();let path=globalThis.location.pathname;const{href:tabUrl}=globalThis.location;let pageUrl=tabUrl;const{search}=globalThis.location;// If valid canonical url is provided use this as page url.
|
1273
|
+
if(canonicalUrl){try{const urlObj=new URL(canonicalUrl);// If existing, query params of canonical url will be used instead of the location.search ones
|
1274
|
+
if(urlObj.search===''){pageUrl=canonicalUrl+search;}else {pageUrl=canonicalUrl;}path=urlObj.pathname;}catch(err){// Do nothing
|
1275
|
+
}}const url=getUrlWithoutHash(pageUrl);const{title}=document;const referrer=getReferrer();return {path,referrer,referring_domain:getReferringDomain(referrer),search,title,url,tab_url:tabUrl};};
|
1276
|
+
|
1277
|
+
const POLYFILL_URL=`https://polyfill.io/v3/polyfill.min.js?features=${Object.keys(legacyJSEngineRequiredPolyfills).join('%2C')}`;const POLYFILL_LOAD_TIMEOUT=10*1000;// 10 seconds
|
1278
|
+
const POLYFILL_SCRIPT_ID='rudderstackPolyfill';
|
1279
|
+
|
1280
|
+
class CapabilitiesManager{constructor(errorHandler,logger){this.logger=logger;this.errorHandler=errorHandler;this.externalSrcLoader=new ExternalSrcLoader(this.errorHandler,this.logger);this.onError=this.onError.bind(this);this.onReady=this.onReady.bind(this);}init(){try{this.prepareBrowserCapabilities();this.attachWindowListeners();}catch(e){this.onError(e);}}/**
|
1281
|
+
* Detect supported capabilities and set values in state
|
1282
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
1283
|
+
detectBrowserCapabilities(){o(()=>{// Storage related details
|
1284
|
+
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
|
1285
|
+
state.capabilities.isBeaconAvailable.value=hasBeacon();state.capabilities.isUaCHAvailable.value=hasUAClientHints();state.capabilities.isCryptoAvailable.value=hasCrypto$1();state.capabilities.isIE11.value=isIE11();state.capabilities.isOnline.value=globalThis.navigator.onLine;// Get page context details
|
1286
|
+
state.context.userAgent.value=getUserAgent();state.context.locale.value=getLanguage();state.context.screen.value=getScreenDetails();state.context.campaign.value=extractUTMParameters(globalThis.location.href);if(hasUAClientHints()){getUserAgentClientHint(uach=>{state.context['ua-ch'].value=uach;},state.loadOptions.value.uaChTrackLevel);}// Get page properties details
|
1287
|
+
const pageProperties=getDefaultPageProperties();state.page.path.value=pageProperties.path;state.page.referrer.value=pageProperties.referrer;state.page.referring_domain.value=pageProperties.referring_domain;state.page.search.value=pageProperties.search;state.page.title.value=pageProperties.title;state.page.url.value=pageProperties.url;state.page.tab_url.value=pageProperties.tab_url;});// Ad blocker detection
|
1288
|
+
b(()=>{if(state.loadOptions.value.sendAdblockPage===true&&state.lifecycle.sourceConfigUrl.value!==undefined){detectAdBlockers(this.errorHandler,this.logger);}});}/**
|
1289
|
+
* Detect if polyfills are required and then load script from polyfill URL
|
1290
|
+
*/prepareBrowserCapabilities(){state.capabilities.isLegacyDOM.value=isLegacyJSEngine();const polyfillUrl=state.loadOptions.value.polyfillURL??POLYFILL_URL;const shouldLoadPolyfill=state.loadOptions.value.polyfillIfRequired&&state.capabilities.isLegacyDOM.value&&Boolean(polyfillUrl);if(shouldLoadPolyfill){// TODO: check if polyfill has been evaluated via polling or
|
1291
|
+
// with the callback param in its url and an exposed function
|
1292
|
+
const onPolyfillLoad=scriptId=>Boolean(scriptId)&&this.onReady();this.externalSrcLoader?.loadJSFile({url:state.loadOptions.value.polyfillURL??POLYFILL_URL,id:POLYFILL_SCRIPT_ID,async:true,timeout:POLYFILL_LOAD_TIMEOUT,callback:onPolyfillLoad});}else {this.onReady();}}/**
|
1293
|
+
* Attach listeners to window to observe event that update capabilities state values
|
1294
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
1295
|
+
attachWindowListeners(){globalThis.addEventListener('offline',()=>{state.capabilities.isOnline.value=false;});globalThis.addEventListener('online',()=>{state.capabilities.isOnline.value=true;});// TODO: add debounched listener for globalThis.onResize event and update state.context.screen.value
|
1296
|
+
}/**
|
1297
|
+
* Set the lifecycle status to next phase
|
1298
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
1299
|
+
onReady(){this.detectBrowserCapabilities();state.lifecycle.status.value=LifecycleStatus.BrowserCapabilitiesReady;}/**
|
1300
|
+
* Handles error
|
1301
|
+
* @param error The error object
|
1302
|
+
*/onError(error){if(this.errorHandler){this.errorHandler.onError(error,CAPABILITIES_MANAGER);}else {throw error;}}}
|
1303
|
+
|
1304
|
+
// TODO: should we take the types from IdentifyTrait instead of any string key?
|
1305
|
+
// https://www.rudderstack.com/docs/event-spec/standard-events/identify/#identify-traits
|
1306
|
+
/**
|
1307
|
+
* Represents the options parameter in the APIs
|
1308
|
+
*/let RudderEventType=/*#__PURE__*/function(RudderEventType){RudderEventType["Page"]="page";RudderEventType["Track"]="track";RudderEventType["Identify"]="identify";RudderEventType["Alias"]="alias";RudderEventType["Group"]="group";return RudderEventType;}({});
|
1309
|
+
|
1310
|
+
const CHANNEL='web';// These are the top-level elements in the standard RudderStack event spec
|
1311
|
+
const TOP_LEVEL_ELEMENTS=['integrations','anonymousId','originalTimestamp'];// Reserved elements in the context of standard RudderStack event spec
|
1312
|
+
// Typically, these elements are not allowed to be overridden by the user
|
1313
|
+
const CONTEXT_RESERVED_ELEMENTS=['library','consentManagement','userAgent','ua-ch','screen'];// Reserved elements in the standard RudderStack event spec
|
1314
|
+
const RESERVED_ELEMENTS=['anonymousId','sentAt','receivedAt','timestamp','originalTimestamp','event','messageId','channel'];const DEFAULT_INTEGRATIONS_CONFIG={All:true};
|
1315
|
+
|
1316
|
+
/**
|
1317
|
+
* To get the page properties for context object
|
1318
|
+
* @param pageProps Page properties
|
1319
|
+
* @returns page properties object for context
|
1320
|
+
*/const getContextPageProperties=pageProps=>{const ctxPageProps={};Object.keys(state.page).forEach(key=>{ctxPageProps[key]=pageProps?.[key]||state.page[key].value;});ctxPageProps.initial_referrer=pageProps?.initial_referrer||state.session.initialReferrer.value;ctxPageProps.initial_referring_domain=pageProps?.initial_referring_domain||state.session.initialReferringDomain.value;return ctxPageProps;};/**
|
1321
|
+
* Add any missing default page properties using values from options and defaults
|
1322
|
+
* @param properties Input page properties
|
1323
|
+
* @param options API options
|
1324
|
+
*/const getUpdatedPageProperties=(properties,options)=>{const optionsPageProps=options?.page||{};const pageProps=properties;Object.keys(state.page).forEach(key=>{if(isUndefined(pageProps[key])){pageProps[key]=optionsPageProps[key]||state.page[key].value;}});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;};/**
|
1325
|
+
* Utility to check for reserved keys in the input object
|
1326
|
+
* @param obj Generic object
|
1327
|
+
* @param eventType Rudder event type
|
1328
|
+
* @param parentKeyPath Object's parent key path
|
1329
|
+
* @param logger Logger instance
|
1330
|
+
*/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));}});}};/**
|
1331
|
+
* Checks for reserved keys in traits, properties, and contextual traits
|
1332
|
+
* @param rudderEvent Generated rudder event
|
1333
|
+
* @param logger Logger instance
|
1334
|
+
*/const checkForReservedElements=(rudderEvent,logger)=>{// properties, traits, contextualTraits are either undefined or object
|
1335
|
+
const{properties,traits,context}=rudderEvent;const{traits:contextualTraits}=context;checkForReservedElementsInObject(properties,'properties',logger);checkForReservedElementsInObject(traits,'traits',logger);checkForReservedElementsInObject(contextualTraits,'context.traits',logger);};/**
|
1336
|
+
* Overrides the top-level event properties with data from API options
|
1337
|
+
* @param rudderEvent Generated rudder event
|
1338
|
+
* @param options API options
|
1339
|
+
*/const updateTopLevelEventElements=(rudderEvent,options)=>{if(options.anonymousId&&isString(options.anonymousId)){// eslint-disable-next-line no-param-reassign
|
1340
|
+
rudderEvent.anonymousId=options.anonymousId;}if(options.integrations&&isObjectLiteralAndNotNull(options.integrations)){// eslint-disable-next-line no-param-reassign
|
1341
|
+
rudderEvent.integrations=options.integrations;}if(options.originalTimestamp&&isString(options.originalTimestamp)){// eslint-disable-next-line no-param-reassign
|
1342
|
+
rudderEvent.originalTimestamp=options.originalTimestamp;}};/**
|
1343
|
+
* To merge the contextual information in API options with existing data
|
1344
|
+
* @param rudderContext Generated rudder event
|
1345
|
+
* @param options API options
|
1346
|
+
* @param logger Logger instance
|
1347
|
+
*/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;};/**
|
1348
|
+
* A function to determine whether SDK should use the integration option provided in load call
|
1349
|
+
* @returns boolean
|
1350
|
+
*/const shouldUseGlobalIntegrationsConfigInEvents=()=>state.loadOptions.value.useGlobalIntegrationsConfigInEvents&&isObjectLiteralAndNotNull(state.nativeDestinations.loadOnlyIntegrations.value);/**
|
1351
|
+
* Updates rudder event object with data from the API options
|
1352
|
+
* @param rudderEvent Generated rudder event
|
1353
|
+
* @param options API options
|
1354
|
+
*/const processOptions=(rudderEvent,options)=>{// Only allow object type for options
|
1355
|
+
if(!isNullOrUndefined(options)&&isObjectLiteralAndNotNull(options)){updateTopLevelEventElements(rudderEvent,options);// eslint-disable-next-line no-param-reassign
|
1356
|
+
rudderEvent.context=getMergedContext(rudderEvent.context,options);}};/**
|
1357
|
+
* Returns the final integrations config for the event based on the global config and event's config
|
1358
|
+
* @param integrationsConfig Event's integrations config
|
1359
|
+
* @returns Final integrations config
|
1360
|
+
*/const getEventIntegrationsConfig=integrationsConfig=>{let finalIntgConfig;if(shouldUseGlobalIntegrationsConfigInEvents()){finalIntgConfig=state.nativeDestinations.loadOnlyIntegrations.value;}else if(isObjectLiteralAndNotNull(integrationsConfig)){finalIntgConfig=integrationsConfig;}else {finalIntgConfig=DEFAULT_INTEGRATIONS_CONFIG;}return finalIntgConfig;};/**
|
1361
|
+
* Enrich the base event object with data from state and the API options
|
1362
|
+
* @param rudderEvent RudderEvent object
|
1363
|
+
* @param options API options
|
1364
|
+
* @param pageProps Page properties
|
1365
|
+
* @param logger logger
|
1366
|
+
* @returns Enriched RudderEvent object
|
1367
|
+
*/const getEnrichedEvent=(rudderEvent,options,pageProps,logger)=>{const commonEventData={// Type casting to string as the user session manager will take care of initializing the value
|
1368
|
+
anonymousId:state.session.anonymousUserId.value,channel:CHANNEL,context:{traits:clone$1(state.session.userTraits.value),sessionId:state.session.sessionInfo.value.id,sessionStart:state.session.sessionInfo.value.sessionStart||undefined,consentManagement:{deniedConsentIds:clone$1(state.consents.data.value.deniedConsentIds)},'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:clone$1(state.context.campaign.value),page:getContextPageProperties(pageProps)},originalTimestamp:getCurrentTimeFormatted(),integrations:DEFAULT_INTEGRATIONS_CONFIG,messageId:generateUUID(),userId:state.session.userId.value};if(rudderEvent.type===RudderEventType.Group){commonEventData.groupId=state.session.groupId.value;commonEventData.traits=clone$1(state.session.groupTraits.value);}const processedEvent=mergeDeepRight(rudderEvent,commonEventData);// Set the default values for the event properties
|
1369
|
+
// matching with v1.1 payload
|
1370
|
+
if(processedEvent.event===undefined){processedEvent.event=null;}if(processedEvent.properties===undefined){processedEvent.properties=null;}processOptions(processedEvent,options);// TODO: We might not need this check altogether
|
1371
|
+
checkForReservedElements(processedEvent,logger);// Update the integrations config for the event
|
1372
|
+
processedEvent.integrations=getEventIntegrationsConfig(processedEvent.integrations);return processedEvent;};
|
1373
|
+
|
1374
|
+
class RudderEventFactory{constructor(logger){this.logger=logger;}/**
|
1375
|
+
* Generate a 'page' event based on the user-input fields
|
1376
|
+
* @param category Page's category
|
1377
|
+
* @param name Page name
|
1378
|
+
* @param properties Page properties
|
1379
|
+
* @param options API options
|
1380
|
+
*/generatePageEvent(category,name,properties,options){let props=properties??{};props.name=name;props.category=category;props=getUpdatedPageProperties(props,options);const pageEvent={properties:props,name,category,type:RudderEventType.Page};return getEnrichedEvent(pageEvent,options,props,this.logger);}/**
|
1381
|
+
* Generate a 'track' event based on the user-input fields
|
1382
|
+
* @param event The event name
|
1383
|
+
* @param properties Event properties
|
1384
|
+
* @param options API options
|
1385
|
+
*/generateTrackEvent(event,properties,options){const trackEvent={properties,event,type:RudderEventType.Track};return getEnrichedEvent(trackEvent,options,undefined,this.logger);}/**
|
1386
|
+
* Generate an 'identify' event based on the user-input fields
|
1387
|
+
* @param options API options
|
1388
|
+
*/generateIdentifyEvent(options){const identifyEvent={type:RudderEventType.Identify};return getEnrichedEvent(identifyEvent,options,undefined,this.logger);}/**
|
1389
|
+
* Generate an 'alias' event based on the user-input fields
|
1390
|
+
* @param to New user ID
|
1391
|
+
* @param from Old user ID
|
1392
|
+
* @param options API options
|
1393
|
+
*/generateAliasEvent(to,from,options){const aliasEvent={previousId:from,type:RudderEventType.Alias};const enrichedEvent=getEnrichedEvent(aliasEvent,options,undefined,this.logger);// override the User ID from the API inputs
|
1394
|
+
enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
1395
|
+
* Generate a 'group' event based on the user-input fields
|
1396
|
+
* @param options API options
|
1397
|
+
*/generateGroupEvent(options){const groupEvent={type:RudderEventType.Group};return getEnrichedEvent(groupEvent,options,undefined,this.logger);}/**
|
1398
|
+
* Generates a new RudderEvent object based on the user-input fields
|
1399
|
+
* @param event API event parameters object
|
1400
|
+
* @returns A RudderEvent object
|
1401
|
+
*/create(event){let eventObj;switch(event.type){case RudderEventType.Page:eventObj=this.generatePageEvent(event.category,event.name,event.properties,event.options);break;case RudderEventType.Track:eventObj=this.generateTrackEvent(event.name,event.properties,event.options);break;case RudderEventType.Identify:eventObj=this.generateIdentifyEvent(event.options);break;case RudderEventType.Alias:eventObj=this.generateAliasEvent(event.to,event.from,event.options);break;case RudderEventType.Group:eventObj=this.generateGroupEvent(event.options);break;}return eventObj;}}
|
1402
|
+
|
1403
|
+
/**
|
1404
|
+
* A service to generate valid event payloads and queue them for processing
|
1405
|
+
*/class EventManager{/**
|
1406
|
+
*
|
1407
|
+
* @param eventRepository Event repository instance
|
1408
|
+
* @param userSessionManager UserSession Manager instance
|
1409
|
+
* @param errorHandler Error handler object
|
1410
|
+
* @param logger Logger object
|
1411
|
+
*/constructor(eventRepository,userSessionManager,errorHandler,logger){this.eventRepository=eventRepository;this.userSessionManager=userSessionManager;this.errorHandler=errorHandler;this.logger=logger;this.eventFactory=new RudderEventFactory(this.logger);this.onError=this.onError.bind(this);}/**
|
1412
|
+
* Initializes the event manager
|
1413
|
+
*/init(){this.eventRepository.init();state.lifecycle.status.value=LifecycleStatus.Initialized;}/**
|
1414
|
+
* Consumes a new incoming event
|
1415
|
+
* @param event Incoming event data
|
1416
|
+
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);if(rudderEvent){this.eventRepository.enqueue(rudderEvent,event.callback);}else {this.onError(new Error(EVENT_OBJECT_GENERATION_ERROR));}}/**
|
1417
|
+
* Handles error
|
1418
|
+
* @param error The error object
|
1419
|
+
*/onError(error,customMessage,shouldAlwaysThrow){if(this.errorHandler){this.errorHandler.onError(error,EVENT_MANAGER,customMessage,shouldAlwaysThrow);}else {throw error;}}}
|
1420
|
+
|
1421
|
+
/**
|
1422
|
+
* A function to check given value is a number or not
|
1423
|
+
* @param num input value
|
1424
|
+
* @returns boolean
|
1425
|
+
*/const isNumber=num=>typeof num==='number'&&!Number.isNaN(num);/**
|
1426
|
+
* A function to check given number has minimum length or not
|
1427
|
+
* @param minimumLength minimum length
|
1428
|
+
* @param num input number
|
1429
|
+
* @returns boolean
|
1430
|
+
*/const hasMinLength=(minimumLength,num)=>num.toString().length>=minimumLength;/**
|
1431
|
+
* A function to check given value is a positive integer or not
|
1432
|
+
* @param num input value
|
1433
|
+
* @returns boolean
|
1434
|
+
*/const isPositiveInteger=num=>isNumber(num)&&num>=0&&Number.isInteger(num);
|
1435
|
+
|
1436
|
+
const MIN_SESSION_ID_LENGTH=10;/**
|
1437
|
+
* A function to validate current session and return true/false depending on that
|
1438
|
+
* @returns boolean
|
1439
|
+
*/const hasSessionExpired=expiresAt=>{const timestamp=Date.now();return Boolean(!expiresAt||timestamp>expiresAt);};/**
|
1440
|
+
* A function to generate session id
|
1441
|
+
* @returns number
|
1442
|
+
*/const generateSessionId=()=>Date.now();/**
|
1443
|
+
* Function to validate user provided sessionId
|
1444
|
+
* @param {number} sessionId
|
1445
|
+
* @returns
|
1446
|
+
*/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;};/**
|
1447
|
+
* A function to generate new auto tracking session
|
1448
|
+
* @param sessionTimeout current timestamp
|
1449
|
+
* @returns SessionInfo
|
1450
|
+
*/const generateAutoTrackingSession=sessionTimeout=>{const timestamp=Date.now();const timeout=sessionTimeout||DEFAULT_SESSION_TIMEOUT;return {id:timestamp,// set the current timestamp
|
1451
|
+
expiresAt:timestamp+timeout,// set the expiry time of the session
|
1452
|
+
timeout,sessionStart:undefined,autoTrack:true};};/**
|
1453
|
+
* A function to generate new manual tracking session
|
1454
|
+
* @param id Provided sessionId
|
1455
|
+
* @param logger Logger module
|
1456
|
+
* @returns SessionInfo
|
1457
|
+
*/const generateManualTrackingSession=(id,logger)=>{const sessionId=isManualSessionIdValid(id,logger)?id:generateSessionId();return {id:sessionId,sessionStart:undefined,manualTrack:true};};
|
1458
|
+
|
1459
|
+
const userSessionStorageKeys={userId:'rl_user_id',userTraits:'rl_trait',anonymousUserId:'rl_anonymous_id',groupId:'rl_group_id',groupTraits:'rl_group_trait',initialReferrer:'rl_page_init_referrer',initialReferringDomain:'rl_page_init_referring_domain',sessionInfo:'rl_session'};
|
1460
|
+
|
1461
|
+
class UserSessionManager{constructor(errorHandler,logger,pluginsManager,store){this.store=store;this.pluginsManager=pluginsManager;this.logger=logger;this.errorHandler=errorHandler;this.onError=this.onError.bind(this);}/**
|
1462
|
+
* Initialize User session with values from storage
|
1463
|
+
* @param store Selected store
|
1464
|
+
*/init(store){this.store=store;this.migrateStorageIfNeeded();// get the values from storage and set it again
|
1465
|
+
this.setUserId(this.getUserId()??'');this.setUserTraits(this.getUserTraits()??{});this.setGroupId(this.getGroupId()??'');this.setGroupTraits(this.getGroupTraits()??{});this.setAnonymousId(this.getAnonymousId(state.loadOptions.value.anonymousIdOptions));const initialReferrer=this.getInitialReferrer();const initialReferringDomain=this.getInitialReferringDomain();if(initialReferrer&&initialReferringDomain){this.setInitialReferrer(initialReferrer);this.setInitialReferringDomain(initialReferringDomain);}else {if(initialReferrer){this.setInitialReferrer(initialReferrer);this.setInitialReferringDomain(getReferringDomain(initialReferrer));}const referrer=getReferrer();this.setInitialReferrer(referrer);this.setInitialReferringDomain(getReferringDomain(referrer));}// Initialize session tracking
|
1466
|
+
this.initializeSessionTracking();// Register the effect to sync with storage
|
1467
|
+
this.registerEffects();}migrateStorageIfNeeded(){if(!state.storage.migrate.value){return;}Object.values(userSessionStorageKeys).forEach(storageEntry=>{const migratedVal=this.pluginsManager?.invokeSingle('storage.migrate',storageEntry,this.store?.engine,this.logger);this.syncValueToStorage(storageEntry,migratedVal);});}/**
|
1468
|
+
* A function to initialize sessionTracking
|
1469
|
+
*/initializeSessionTracking(){const sessionInfo=this.getSessionFromStorage()??defaultSessionInfo;let finalAutoTrackingStatus=!(state.loadOptions.value.sessions.autoTrack===false||sessionInfo.manualTrack===true);let sessionTimeout;const configuredSessionTimeout=state.loadOptions.value.sessions.timeout;if(!isPositiveInteger(configuredSessionTimeout)){this.logger?.warn(TIMEOUT_NOT_NUMBER_WARNING(USER_SESSION_MANAGER,configuredSessionTimeout,DEFAULT_SESSION_TIMEOUT));sessionTimeout=DEFAULT_SESSION_TIMEOUT;}else {sessionTimeout=configuredSessionTimeout;}if(sessionTimeout===0){this.logger?.warn(TIMEOUT_ZERO_WARNING(USER_SESSION_MANAGER));finalAutoTrackingStatus=false;}// In case user provides a timeout value greater than 0 but less than 10 seconds SDK will show a warning
|
1470
|
+
// and will proceed with it
|
1471
|
+
if(sessionTimeout>0&&sessionTimeout<MIN_SESSION_TIMEOUT){this.logger?.warn(TIMEOUT_NOT_RECOMMENDED_WARNING(USER_SESSION_MANAGER,sessionTimeout,MIN_SESSION_TIMEOUT));}state.session.sessionInfo.value={...sessionInfo,timeout:sessionTimeout,autoTrack:finalAutoTrackingStatus};// If auto session tracking is enabled start the session tracking
|
1472
|
+
if(state.session.sessionInfo.value.autoTrack){this.startOrRenewAutoTracking();}}/**
|
1473
|
+
* Handles error
|
1474
|
+
* @param error The error object
|
1475
|
+
*/onError(error){if(this.errorHandler){this.errorHandler.onError(error,USER_SESSION_MANAGER);}else {throw error;}}/**
|
1476
|
+
* A function to sync values in storage
|
1477
|
+
* @param key
|
1478
|
+
* @param value
|
1479
|
+
*/syncValueToStorage(key,value){if(value&&isString(value)||isNonEmptyObject(value)){this.store?.set(key,value);}else {this.store?.remove(key);}}/**
|
1480
|
+
* Function to update storage whenever state value changes
|
1481
|
+
*/registerEffects(){/**
|
1482
|
+
* Update userId in storage automatically when userId is updated in state
|
1483
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.userId,state.session.userId.value);});/**
|
1484
|
+
* Update user traits in storage automatically when it is updated in state
|
1485
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.userTraits,state.session.userTraits.value);});/**
|
1486
|
+
* Update group id in storage automatically when it is updated in state
|
1487
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.groupId,state.session.groupId.value);});/**
|
1488
|
+
* Update group traits in storage automatically when it is updated in state
|
1489
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.groupTraits,state.session.groupTraits.value);});/**
|
1490
|
+
* Update anonymous user id in storage automatically when it is updated in state
|
1491
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.anonymousUserId,state.session.anonymousUserId.value);});/**
|
1492
|
+
* Update initial referrer in storage automatically when it is updated in state
|
1493
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.initialReferrer,state.session.initialReferrer.value);});/**
|
1494
|
+
* Update initial referring domain in storage automatically when it is updated in state
|
1495
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.initialReferringDomain,state.session.initialReferringDomain.value);});/**
|
1496
|
+
* Update session tracking info in storage automatically when it is updated in state
|
1497
|
+
*/b(()=>{this.syncValueToStorage(userSessionStorageKeys.sessionInfo,state.session.sessionInfo.value);});}/**
|
1498
|
+
* Sets anonymous id in the following precedence:
|
1499
|
+
*
|
1500
|
+
* 1. anonymousId: Id directly provided to the function.
|
1501
|
+
* 2. rudderAmpLinkerParam: value generated from linker query parm (rudderstack)
|
1502
|
+
* using parseLinker util.
|
1503
|
+
* 3. generateUUID: A new unique id is generated and assigned.
|
1504
|
+
*/setAnonymousId(anonymousId,rudderAmpLinkerParam){let finalAnonymousId=anonymousId;if(!finalAnonymousId&&rudderAmpLinkerParam){const linkerPluginsResult=this.pluginsManager?.invokeMultiple('userSession.anonymousIdGoogleLinker',rudderAmpLinkerParam);finalAnonymousId=linkerPluginsResult?.[0];}state.session.anonymousUserId.value=finalAnonymousId||this.generateAnonymousId();}/**
|
1505
|
+
* Generate a new anonymousId
|
1506
|
+
* @returns string anonymousID
|
1507
|
+
*/generateAnonymousId(){return generateUUID();}/**
|
1508
|
+
* Fetches anonymousId
|
1509
|
+
* @param options option to fetch it from external source
|
1510
|
+
* @returns anonymousId
|
1511
|
+
*/getAnonymousId(options){// fetch the anonymousUserId from storage
|
1512
|
+
let persistedAnonymousId=this.store?.get(userSessionStorageKeys.anonymousUserId);if(!persistedAnonymousId&&options){// fetch anonymousId from external source
|
1513
|
+
const autoCapturedAnonymousId=this.pluginsManager?.invokeSingle('storage.getAnonymousId',getStorageEngine,options);persistedAnonymousId=autoCapturedAnonymousId;}state.session.anonymousUserId.value=persistedAnonymousId||this.generateAnonymousId();return state.session.anonymousUserId.value;}/**
|
1514
|
+
* Fetches User Id
|
1515
|
+
* @returns
|
1516
|
+
*/getUserId(){return this.store?.get(userSessionStorageKeys.userId)??null;}/**
|
1517
|
+
* Fetches User Traits
|
1518
|
+
* @returns
|
1519
|
+
*/getUserTraits(){return this.store?.get(userSessionStorageKeys.userTraits)??null;}/**
|
1520
|
+
* Fetches Group Id
|
1521
|
+
* @returns
|
1522
|
+
*/getGroupId(){return this.store?.get(userSessionStorageKeys.groupId)??null;}/**
|
1523
|
+
* Fetches Group Traits
|
1524
|
+
* @returns
|
1525
|
+
*/getGroupTraits(){return this.store?.get(userSessionStorageKeys.groupTraits)??null;}/**
|
1526
|
+
* Fetches Initial Referrer
|
1527
|
+
* @returns
|
1528
|
+
*/getInitialReferrer(){return this.store?.get(userSessionStorageKeys.initialReferrer)??null;}/**
|
1529
|
+
* Fetches Initial Referring domain
|
1530
|
+
* @returns
|
1531
|
+
*/getInitialReferringDomain(){return this.store?.get(userSessionStorageKeys.initialReferringDomain)??null;}/**
|
1532
|
+
* Fetches session tracking information from storage
|
1533
|
+
* @returns
|
1534
|
+
*/getSessionFromStorage(){return this.store?.get(userSessionStorageKeys.sessionInfo)??null;}/**
|
1535
|
+
* A function to update current session info after each event call
|
1536
|
+
*/refreshSession(){if(state.session.sessionInfo.value.autoTrack||state.session.sessionInfo.value.manualTrack){if(state.session.sessionInfo.value.autoTrack){this.startOrRenewAutoTracking();}if(state.session.sessionInfo.value.sessionStart===undefined){state.session.sessionInfo.value={...state.session.sessionInfo.value,sessionStart:true};}else if(state.session.sessionInfo.value.sessionStart){state.session.sessionInfo.value={...state.session.sessionInfo.value,sessionStart:false};}}}/**
|
1537
|
+
* Reset state values
|
1538
|
+
* @param resetAnonymousId
|
1539
|
+
* @param noNewSessionStart
|
1540
|
+
* @returns
|
1541
|
+
*/reset(resetAnonymousId,noNewSessionStart){const{manualTrack,autoTrack}=state.session.sessionInfo.value;o(()=>{state.session.userId.value='';state.session.userTraits.value={};state.session.groupId.value='';state.session.groupTraits.value={};if(resetAnonymousId){state.session.anonymousUserId.value='';}if(noNewSessionStart){return;}if(autoTrack){state.session.sessionInfo.value={};this.startOrRenewAutoTracking();}else if(manualTrack){this.startManualTrackingInternal();}});}/**
|
1542
|
+
* Set user Id
|
1543
|
+
* @param userId
|
1544
|
+
*/setUserId(userId){state.session.userId.value=userId;}/**
|
1545
|
+
* Set user traits
|
1546
|
+
* @param traits
|
1547
|
+
*/setUserTraits(traits){if(traits){state.session.userTraits.value=mergeDeepRight(state.session.userTraits.value??{},traits);}}/**
|
1548
|
+
* Set group Id
|
1549
|
+
* @param groupId
|
1550
|
+
*/setGroupId(groupId){state.session.groupId.value=groupId;}/**
|
1551
|
+
* Set group traits
|
1552
|
+
* @param traits
|
1553
|
+
*/setGroupTraits(traits){if(traits){state.session.groupTraits.value=mergeDeepRight(state.session.groupTraits.value??{},traits);}}/**
|
1554
|
+
* Set initial referrer
|
1555
|
+
* @param referrer
|
1556
|
+
*/setInitialReferrer(referrer){state.session.initialReferrer.value=referrer;}/**
|
1557
|
+
* Set initial referring domain
|
1558
|
+
* @param referrer
|
1559
|
+
*/setInitialReferringDomain(referrer){state.session.initialReferringDomain.value=referrer;}/**
|
1560
|
+
* A function to check for existing session details and depending on that create a new session.
|
1561
|
+
*/startOrRenewAutoTracking(){if(hasSessionExpired(state.session.sessionInfo.value.expiresAt)){state.session.sessionInfo.value=generateAutoTrackingSession(state.session.sessionInfo.value.timeout);}else {const timestamp=Date.now();const timeout=state.session.sessionInfo.value.timeout;state.session.sessionInfo.value=mergeDeepRight(state.session.sessionInfo.value,{expiresAt:timestamp+timeout// set the expiry time of the session
|
1562
|
+
});}}/**
|
1563
|
+
* A function method to start a manual session
|
1564
|
+
* @param {number} id session identifier
|
1565
|
+
* @returns
|
1566
|
+
*/start(id){state.session.sessionInfo.value=generateManualTrackingSession(id,this.logger);}/**
|
1567
|
+
* An internal function to start manual session
|
1568
|
+
*/startManualTrackingInternal(){this.start(Date.now());}/**
|
1569
|
+
* A public method to end an ongoing session.
|
1570
|
+
*/end(){state.session.sessionInfo.value={};}/**
|
1571
|
+
* Clear storage
|
1572
|
+
* @param resetAnonymousId
|
1573
|
+
*/clearUserSessionStorage(resetAnonymousId){this.store?.remove(userSessionStorageKeys.userId);this.store?.remove(userSessionStorageKeys.userTraits);this.store?.remove(userSessionStorageKeys.groupId);this.store?.remove(userSessionStorageKeys.groupTraits);if(resetAnonymousId){this.store?.remove(userSessionStorageKeys.anonymousUserId);}}}
|
1574
|
+
|
1575
|
+
/**
|
1576
|
+
* A buffer queue to serve as a store for any type of data
|
1577
|
+
*/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=[];}}
|
1578
|
+
|
1579
|
+
const DATA_PLANE_QUEUE_EXT_POINT_PREFIX='dataplaneEventsQueue';const DESTINATIONS_QUEUE_EXT_POINT_PREFIX='destinationsEventsQueue';
|
1580
|
+
|
1581
|
+
/**
|
1582
|
+
* Event repository class responsible for queuing events for further processing and delivery
|
1583
|
+
*/class EventRepository{/**
|
1584
|
+
*
|
1585
|
+
* @param pluginsManager Plugins manager instance
|
1586
|
+
* @param storeManager Store Manager instance
|
1587
|
+
* @param errorHandler Error handler object
|
1588
|
+
* @param logger Logger object
|
1589
|
+
*/constructor(pluginsManager,storeManager,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.logger=logger;this.httpClient=new HttpClient(errorHandler,logger);this.storeManager=storeManager;this.onError=this.onError.bind(this);}/**
|
1590
|
+
* Initializes the event repository
|
1591
|
+
*/init(){this.dataplaneEventsQueue=this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.init`,state,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.errorHandler,this.logger);// Start the queue once the client destinations are ready
|
1592
|
+
b(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue.start();}});}/**
|
1593
|
+
* Enqueues the event for processing
|
1594
|
+
* @param event RudderEvent object
|
1595
|
+
* @param callback API callback function
|
1596
|
+
*/enqueue(event,callback){// Start the queue processing only when the destinations are ready or hybrid mode destinations exist
|
1597
|
+
// However, events will be enqueued for now.
|
1598
|
+
// At the time of processing the events, the integrations config data from destinations
|
1599
|
+
// is merged into the event object
|
1600
|
+
b(()=>{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&&this.dataplaneEventsQueue?.running!==true){this.dataplaneEventsQueue?.start();}});const dpQEvent=clone$1(event);this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone$1(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger);// Invoke the callback if it exists
|
1601
|
+
try{// Using the event sent to the data plane queue here
|
1602
|
+
// to ensure the mutated (if any) event is sent to the callback
|
1603
|
+
callback?.(dpQEvent);}catch(error){this.onError(error,'API Callback Invocation Failed');}}/**
|
1604
|
+
* Handles error
|
1605
|
+
* @param error The error object
|
1606
|
+
* @param customMessage a message
|
1607
|
+
* @param shouldAlwaysThrow if it should throw or use logger
|
1608
|
+
*/onError(error,customMessage,shouldAlwaysThrow){if(this.errorHandler){this.errorHandler.onError(error,EVENT_REPOSITORY,customMessage,shouldAlwaysThrow);}else {throw error;}}}
|
1609
|
+
|
1610
|
+
/*
|
1611
|
+
* Analytics class with lifecycle based on state ad user triggered events
|
1612
|
+
*/class Analytics{preloadBuffer=new BufferQueue();/**
|
1613
|
+
* Initialize services and components or use default ones if singletons
|
1614
|
+
*/constructor(){this.initialized=false;this.errorHandler=defaultErrorHandler;this.logger=defaultLogger;this.externalSrcLoader=new ExternalSrcLoader(this.errorHandler,this.logger);this.capabilitiesManager=new CapabilitiesManager(this.errorHandler,this.logger);this.httpClient=defaultHttpClient;this.load=this.load.bind(this);this.startLifecycle=this.startLifecycle.bind(this);this.prepareBrowserCapabilities=this.prepareBrowserCapabilities.bind(this);this.enqueuePreloadBufferEvents=this.enqueuePreloadBufferEvents.bind(this);this.processDataInPreloadBuffer=this.processDataInPreloadBuffer.bind(this);this.prepareInternalServices=this.prepareInternalServices.bind(this);this.loadConfig=this.loadConfig.bind(this);this.init=this.init.bind(this);this.loadPlugins=this.loadPlugins.bind(this);this.onLoaded=this.onLoaded.bind(this);this.processBufferedEvents=this.processBufferedEvents.bind(this);this.loadIntegrations=this.loadIntegrations.bind(this);this.onReady=this.onReady.bind(this);this.ready=this.ready.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);}/**
|
1615
|
+
* Start application lifecycle if not already started
|
1616
|
+
*/load(writeKey,dataPlaneUrl,loadOptions={}){if(state.lifecycle.status.value){return;}let clonedDataPlaneUrl=clone$1(dataPlaneUrl);let clonedLoadOptions=clone$1(loadOptions);// dataPlaneUrl is not provided
|
1617
|
+
if(isObjectAndNotNull(dataPlaneUrl)){clonedLoadOptions=dataPlaneUrl;clonedDataPlaneUrl=undefined;}// Set initial state values
|
1618
|
+
o(()=>{state.lifecycle.writeKey.value=writeKey;state.lifecycle.dataPlaneUrl.value=clonedDataPlaneUrl;state.loadOptions.value=normalizeLoadOptions(state.loadOptions.value,clonedLoadOptions);state.lifecycle.status.value=LifecycleStatus.Mounted;});// Expose state to global objects
|
1619
|
+
setExposedGlobal('state',state,writeKey);// Configure initial config of any services or components here
|
1620
|
+
// State application lifecycle
|
1621
|
+
this.startLifecycle();}// Start lifecycle methods
|
1622
|
+
/**
|
1623
|
+
* Orchestrate the lifecycle of the application phases/status
|
1624
|
+
*/startLifecycle(){b(()=>{switch(state.lifecycle.status.value){case LifecycleStatus.Mounted:this.prepareBrowserCapabilities();break;case LifecycleStatus.BrowserCapabilitiesReady:// initialize the preloaded events enqueuing
|
1625
|
+
retrievePreloadBufferEvents(this);this.prepareInternalServices();this.loadConfig();break;case LifecycleStatus.Configured:this.loadPlugins();break;case LifecycleStatus.PluginsLoading:break;case LifecycleStatus.PluginsReady:this.init();break;case LifecycleStatus.Initialized:this.onLoaded();break;case LifecycleStatus.Loaded:this.loadIntegrations();this.processBufferedEvents();break;case LifecycleStatus.DestinationsLoading:break;case LifecycleStatus.DestinationsReady:this.onReady();break;case LifecycleStatus.Ready:break;}});}/**
|
1626
|
+
* Load browser polyfill if required
|
1627
|
+
*/prepareBrowserCapabilities(){this.capabilitiesManager.init();}/**
|
1628
|
+
* Enqueue in SDK preload buffer events, used from preloadBuffer component
|
1629
|
+
*/enqueuePreloadBufferEvents(bufferedEvents){if(Array.isArray(bufferedEvents)){bufferedEvents.forEach(bufferedEvent=>this.preloadBuffer.enqueue(clone$1(bufferedEvent)));}}/**
|
1630
|
+
* Process the buffer preloaded events by passing their arguments to the respective facade methods
|
1631
|
+
*/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.errorHandler,this.logger,this.pluginsManager);this.eventRepository=new EventRepository(this.pluginsManager,this.storeManager,this.errorHandler,this.logger);this.eventManager=new EventManager(this.eventRepository,this.userSessionManager,this.errorHandler,this.logger);}/**
|
1632
|
+
* Load configuration
|
1633
|
+
*/loadConfig(){if(!state.lifecycle.writeKey.value){this.errorHandler.onError(new Error('A write key is required to load the SDK. Please provide a valid write key.'),LOAD_CONFIGURATION);return;}this.httpClient.setAuthHeader(state.lifecycle.writeKey.value);this.configManager?.init();}/**
|
1634
|
+
* Initialize the storage and event queue
|
1635
|
+
*/init(){this.errorHandler.init(this.externalSrcLoader);// Initialize storage
|
1636
|
+
this.storeManager?.init();this.clientDataStore=this.storeManager?.getStore(CLIENT_DATA_STORE_NAME);this.userSessionManager?.init(this.clientDataStore);// Initialize consent manager
|
1637
|
+
if(state.consents.activeConsentManagerPluginName.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.storeManager,this.logger);}// Initialize event manager
|
1638
|
+
this.eventManager?.init();}/**
|
1639
|
+
* Load plugins
|
1640
|
+
*/loadPlugins(){this.pluginsManager?.init();// TODO: are we going to enable custom plugins to be passed as load options?
|
1641
|
+
// registerCustomPlugins(state.loadOptions.value.customPlugins);
|
1642
|
+
}/**
|
1643
|
+
* Trigger onLoaded callback if any is provided in config
|
1644
|
+
*/onLoaded(){// Process any preloaded events
|
1645
|
+
this.processDataInPreloadBuffer();// Set lifecycle state
|
1646
|
+
o(()=>{state.lifecycle.loaded.value=true;state.lifecycle.status.value=LifecycleStatus.Loaded;});// Execute onLoaded callback if provided in load options
|
1647
|
+
if(state.loadOptions.value.onLoaded&&isFunction(state.loadOptions.value.onLoaded)){state.loadOptions.value.onLoaded(this);}}/**
|
1648
|
+
* Consume preloaded events buffer
|
1649
|
+
*/processBufferedEvents(){// Process buffered events
|
1650
|
+
state.eventBuffer.toBeProcessedArray.value.forEach(bufferedItem=>{const methodName=bufferedItem[0];if(isFunction(this[methodName])){this[methodName](...bufferedItem.slice(1));}});state.eventBuffer.toBeProcessedArray.value=[];}/**
|
1651
|
+
* Load device mode integrations
|
1652
|
+
*/loadIntegrations(){// Set in state the desired activeIntegrations to inject in DOM
|
1653
|
+
this.pluginsManager?.invokeSingle('nativeDestinations.setActiveDestinations',state,this.pluginsManager,this.logger);const totalDestinationsToLoad=state.nativeDestinations.activeDestinations.value.length;if(totalDestinationsToLoad===0){state.lifecycle.status.value=LifecycleStatus.DestinationsReady;return;}// Start loading native integration scripts and create instances
|
1654
|
+
state.lifecycle.status.value=LifecycleStatus.DestinationsLoading;this.pluginsManager?.invokeSingle('nativeDestinations.load',state,this.externalSrcLoader,this.logger);// Progress to next lifecycle phase if all native integrations are initialized or failed
|
1655
|
+
b(()=>{const areAllDestinationsReady=totalDestinationsToLoad===0||state.nativeDestinations.initializedDestinations.value.length+state.nativeDestinations.failedDestinations.value.length===totalDestinationsToLoad;if(areAllDestinationsReady){o(()=>{state.lifecycle.status.value=LifecycleStatus.DestinationsReady;state.nativeDestinations.clientDestinationsReady.value=true;});}});}/**
|
1656
|
+
* Invoke the ready callbacks if any exist
|
1657
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
1658
|
+
onReady(){state.eventBuffer.readyCallbacksArray.value.forEach(callback=>callback());state.lifecycle.status.value=LifecycleStatus.Ready;}// End lifecycle methods
|
1659
|
+
// Start consumer exposed methods
|
1660
|
+
ready(callback){const type='ready';this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(READY_API_CALLBACK_ERROR(READY_API));return;}if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,callback]);return;}/**
|
1661
|
+
* If integrations are loaded or no integration is available for loading
|
1662
|
+
* execute the callback immediately else push the callbacks to a queue that
|
1663
|
+
* will be executed after loading completes
|
1664
|
+
*/if(state.lifecycle.status.value===LifecycleStatus.Ready){callback();}else {state.eventBuffer.readyCallbacksArray.value.push(callback);}}page(payload){const type='page';this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,payload]);return;}this.eventManager?.addEvent({type:RudderEventType.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.
|
1665
|
+
// Send automatic ad blocked page event if adblockers are detected on the page
|
1666
|
+
if(state.capabilities.isAdBlocked.value===true&&payload.category!==ADBLOCK_PAGE_CATEGORY){const pageCallArgs={category:ADBLOCK_PAGE_CATEGORY,name:ADBLOCK_PAGE_NAME,properties:{// 'title' is intentionally omitted as it does not make sense
|
1667
|
+
// in v3 implementation
|
1668
|
+
path:ADBLOCK_PAGE_PATH},options:state.loadOptions.value.sendAdblockPageOptions};this.page(pageCallArgs);}}track(payload){const type='track';this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,payload]);return;}this.eventManager?.addEvent({type:RudderEventType.Track,name:payload.name,properties:payload.properties,options:payload.options,callback:payload.callback});}identify(payload){const type='identify';this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,payload]);return;}const shouldResetSession=Boolean(payload.userId&&state.session.userId.value&&payload.userId!==state.session.userId.value);if(shouldResetSession){this.reset();}this.userSessionManager?.setUserId(payload.userId);this.userSessionManager?.setUserTraits(payload.traits);this.eventManager?.addEvent({type:RudderEventType.Identify,userId:payload.userId,traits:payload.traits,options:payload.options,callback:payload.callback});}alias(payload){const type='alias';this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,payload]);return;}const previousId=payload.from??this.userSessionManager?.getUserId()??this.userSessionManager?.getAnonymousId();this.eventManager?.addEvent({type:RudderEventType.Alias,to:payload.to,from:previousId,options:payload.options,callback:payload.callback});}group(payload){const type='group';this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,payload]);return;}this.userSessionManager?.setGroupId(payload.groupId);this.userSessionManager?.setGroupTraits(payload.traits);this.eventManager?.addEvent({type:RudderEventType.Group,groupId:payload.groupId,traits:payload.traits,options:payload.options,callback:payload.callback});}reset(resetAnonymousId){const type='reset';this.errorHandler.leaveBreadcrumb(`New ${type} invocation, resetAnonymousId: ${resetAnonymousId}`);if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value.push([type,resetAnonymousId]);return;}this.userSessionManager?.reset(resetAnonymousId);}getAnonymousId(options){return this.userSessionManager?.getAnonymousId(options);}setAnonymousId(anonymousId,rudderAmpLinkerParam){this.userSessionManager?.setAnonymousId(anonymousId,rudderAmpLinkerParam);}// eslint-disable-next-line class-methods-use-this
|
1669
|
+
getUserId(){return state.session.userId.value;}// eslint-disable-next-line class-methods-use-this
|
1670
|
+
getUserTraits(){return state.session.userTraits.value;}// eslint-disable-next-line class-methods-use-this
|
1671
|
+
getGroupId(){return state.session.groupId.value;}// eslint-disable-next-line class-methods-use-this
|
1672
|
+
getGroupTraits(){return state.session.groupTraits.value;}startSession(sessionId){this.userSessionManager?.start(sessionId);}endSession(){this.userSessionManager?.end();}// eslint-disable-next-line class-methods-use-this
|
1673
|
+
getSessionId(){this.userSessionManager?.refreshSession();return state.session.sessionInfo.value?.id??null;}// End consumer exposed methods
|
1674
|
+
}
|
1675
|
+
|
1676
|
+
/*
|
1677
|
+
* RudderAnalytics facade singleton that is exposed as global object and will:
|
1678
|
+
* expose overloaded methods
|
1679
|
+
* handle multiple Analytics instances
|
1680
|
+
* consume SDK preload event buffer
|
1681
|
+
*/class RudderAnalytics{static globalSingleton=null;analyticsInstances={};defaultAnalyticsKey='';// Singleton with constructor bind methods
|
1682
|
+
constructor(){if(RudderAnalytics.globalSingleton){// eslint-disable-next-line no-constructor-return
|
1683
|
+
return RudderAnalytics.globalSingleton;}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.getPreloadBuffer=this.getPreloadBuffer.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);RudderAnalytics.globalSingleton=this;// get the preloaded events before replacing global object
|
1684
|
+
this.getPreloadBuffer();// start loading if a load event was buffered or wait for explicit load call
|
1685
|
+
this.triggerBufferedLoadEvent();// eslint-disable-next-line no-constructor-return
|
1686
|
+
return this;}/**
|
1687
|
+
* Set instance to use if no specific writeKey is provided in methods
|
1688
|
+
* automatically for the first created instance
|
1689
|
+
* TODO: to support multiple analytics instances in the near future
|
1690
|
+
*/setDefaultInstanceKey(writeKey){if(isEmpty$1(this.analyticsInstances)){this.defaultAnalyticsKey=writeKey;}}/**
|
1691
|
+
* Retrieve an existing analytics instance
|
1692
|
+
*/getAnalyticsInstance(writeKey){const instanceId=writeKey||this.defaultAnalyticsKey;const analyticsInstanceExists=Boolean(this.analyticsInstances[instanceId]);if(!analyticsInstanceExists){this.analyticsInstances[instanceId]=new Analytics();}return this.analyticsInstances[instanceId];}/**
|
1693
|
+
* Create new analytics instance and trigger application lifecycle start
|
1694
|
+
*/load(writeKey,dataPlaneUrl,loadOptions){const shouldSkipLoad=!isString(writeKey)||Boolean(this.analyticsInstances[writeKey]);if(shouldSkipLoad){return;}this.setDefaultInstanceKey(writeKey);this.analyticsInstances[writeKey]=new Analytics();this.getAnalyticsInstance(writeKey).load(writeKey,dataPlaneUrl,loadOptions);}/**
|
1695
|
+
* Get preloaded events in buffer queue if exists
|
1696
|
+
*/ // eslint-disable-next-line class-methods-use-this
|
1697
|
+
getPreloadBuffer(){const preloadedEventsArray=Array.isArray(globalThis.rudderanalytics)?globalThis.rudderanalytics:[];// Expose buffer to global objects
|
1698
|
+
setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone$1(preloadedEventsArray));}/**
|
1699
|
+
* Trigger load event in buffer queue if exists
|
1700
|
+
*/triggerBufferedLoadEvent(){const preloadedEventsArray=Array.isArray(globalThis.rudderanalytics)?globalThis.rudderanalytics:[];// Get any load method call that is buffered if any
|
1701
|
+
const loadEvent=getPreloadedLoadEvent(preloadedEventsArray);// Process load method if present in the buffered requests
|
1702
|
+
if(loadEvent.length>0){// Remove the event name from the Buffered Event array and keep only arguments
|
1703
|
+
loadEvent.shift();// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
1704
|
+
// @ts-ignore
|
1705
|
+
this.load.apply(null,loadEvent);}}/**
|
1706
|
+
* Get ready callback arguments and forward to ready call
|
1707
|
+
*/ready(callback){this.getAnalyticsInstance().ready(callback);}/**
|
1708
|
+
* Process page arguments and forward to page call
|
1709
|
+
*/page(category,name,properties,options,callback){this.getAnalyticsInstance().page(pageArgumentsToCallOptions(category,name,properties,options,callback));}/**
|
1710
|
+
* Process track arguments and forward to page call
|
1711
|
+
*/track(event,properties,options,callback){this.getAnalyticsInstance().track(trackArgumentsToCallOptions(event,properties,options,callback));}/**
|
1712
|
+
* Process identify arguments and forward to page call
|
1713
|
+
*/identify(userId,traits,options,callback){this.getAnalyticsInstance().identify(identifyArgumentsToCallOptions(userId,traits,options,callback));}/**
|
1714
|
+
* Process alias arguments and forward to page call
|
1715
|
+
*/alias(to,from,options,callback){this.getAnalyticsInstance().alias(aliasArgumentsToCallOptions(to,from,options,callback));}/**
|
1716
|
+
* Process group arguments and forward to page call
|
1717
|
+
*/group(groupId,traits,options,callback){this.getAnalyticsInstance().group(groupArgumentsToCallOptions(groupId,traits,options,callback));}reset(resetAnonymousId){this.getAnalyticsInstance().reset(resetAnonymousId);}getAnonymousId(options){return this.getAnalyticsInstance().getAnonymousId(options);}setAnonymousId(anonymousId,rudderAmpLinkerParam){this.getAnalyticsInstance().setAnonymousId(anonymousId,rudderAmpLinkerParam);}getUserId(){return this.getAnalyticsInstance().getUserId();}getUserTraits(){return this.getAnalyticsInstance().getUserTraits();}getGroupId(){return this.getAnalyticsInstance().getGroupId();}getGroupTraits(){return this.getAnalyticsInstance().getGroupTraits();}startSession(sessionId){return this.getAnalyticsInstance().startSession(sessionId);}endSession(){return this.getAnalyticsInstance().endSession();}getSessionId(){return this.getAnalyticsInstance().getSessionId();}}
|
1718
|
+
|
1719
|
+
export { CookieSameSite, LogLevel, PluginName, ResidencyServerRegion, RudderAnalytics, UaChTrackLevel };
|