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