@rudderstack/analytics-js 3.12.0 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/npm/index.d.cts +209 -71
- package/dist/npm/index.d.mts +209 -71
- package/dist/npm/legacy/bundled/cjs/index.cjs +216 -309
- package/dist/npm/legacy/bundled/esm/index.mjs +216 -309
- package/dist/npm/legacy/bundled/umd/index.js +216 -309
- package/dist/npm/legacy/cjs/index.cjs +216 -309
- package/dist/npm/legacy/content-script/cjs/index.cjs +216 -284
- package/dist/npm/legacy/content-script/esm/index.mjs +216 -284
- package/dist/npm/legacy/content-script/umd/index.js +216 -284
- package/dist/npm/legacy/esm/index.mjs +216 -309
- package/dist/npm/legacy/umd/index.js +216 -309
- package/dist/npm/modern/bundled/cjs/index.cjs +217 -308
- package/dist/npm/modern/bundled/esm/index.mjs +217 -308
- package/dist/npm/modern/bundled/umd/index.js +217 -308
- package/dist/npm/modern/cjs/index.cjs +209 -178
- package/dist/npm/modern/content-script/cjs/index.cjs +217 -283
- package/dist/npm/modern/content-script/esm/index.mjs +217 -283
- package/dist/npm/modern/content-script/umd/index.js +217 -283
- package/dist/npm/modern/esm/index.mjs +209 -178
- package/dist/npm/modern/umd/index.js +209 -178
- package/package.json +1 -1
@@ -289,10 +289,10 @@ const isFunction=value=>typeof value==='function'&&Boolean(value.constructor&&va
|
|
289
289
|
* @param value input value
|
290
290
|
* @returns boolean
|
291
291
|
*/const isDefinedNotNullAndNotEmptyString=value=>isDefinedAndNotNull(value)&&value!=='';/**
|
292
|
-
* Determines if the input is
|
293
|
-
* @param
|
294
|
-
* @returns true if the input is
|
295
|
-
*/const isTypeOfError=
|
292
|
+
* Determines if the input is of type error
|
293
|
+
* @param value input value
|
294
|
+
* @returns true if the input is of type error else false
|
295
|
+
*/const isTypeOfError=value=>{switch(Object.prototype.toString.call(value)){case '[object Error]':case '[object Exception]':case '[object DOMException]':return true;default:return value instanceof Error;}};
|
296
296
|
|
297
297
|
const getValueByPath=(obj,keyPath)=>{const pathParts=keyPath.split('.');return path(pathParts,obj);};const hasValueByPath=(obj,path)=>Boolean(getValueByPath(obj,path));const isObject=value=>typeof value==='object';/**
|
298
298
|
* Checks if the input is an object literal or built-in object type and not null
|
@@ -332,7 +332,7 @@ mergeDeepRight(mergedArray[index],value):value;});return mergedArray;};const mer
|
|
332
332
|
* getNormalizedBooleanValue(true, false) // returns true
|
333
333
|
*/const getNormalizedBooleanValue=(val,defVal)=>{if(isDefined(defVal)){return isDefined(val)?val===true:defVal;}return val===true;};
|
334
334
|
|
335
|
-
const trim=value=>value.replace(/^\s+|\s+$/gm,'');const
|
335
|
+
const trim=value=>value.replace(/^\s+|\s+$/gm,'');const removeLeadingPeriod=value=>value.replace(/^\.+/,'');/**
|
336
336
|
* A function to convert values to string
|
337
337
|
* @param val input value
|
338
338
|
* @returns stringified value
|
@@ -403,7 +403,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
|
|
403
403
|
* Represents the options parameter in the load API
|
404
404
|
*/
|
405
405
|
|
406
|
-
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
|
406
|
+
const API_SUFFIX='API';const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API=`Ready${API_SUFFIX}`;const LOAD_API=`Load${API_SUFFIX}`;const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RSA='RudderStackAnalytics';const ANALYTICS_CORE='AnalyticsCore';
|
407
407
|
|
408
408
|
function random(len){return crypto.getRandomValues(new Uint8Array(len));}
|
409
409
|
|
@@ -471,14 +471,17 @@ if(isObjectLiteralAndNotNull(sanitizedValue)||Array.isArray(sanitizedValue)){res
|
|
471
471
|
*/const getSanitizedValue=(value,logger)=>{const replacer=getReplacer();// This is needed for registering the first ancestor
|
472
472
|
const newValue=replacer.call(value,'',value);if(isObjectLiteralAndNotNull(value)||Array.isArray(value)){return traverseWithThis(value,replacer);}return newValue;};
|
473
473
|
|
474
|
-
const MANUAL_ERROR_IDENTIFIER='[
|
474
|
+
const MANUAL_ERROR_IDENTIFIER='[SDK DISPATCHED ERROR]';const getStacktrace=err=>{const{stack,stacktrace}=err;const operaSourceloc=err['opera#sourceloc'];const stackString=stack??stacktrace??operaSourceloc;if(!!stackString&&typeof stackString==='string'){return stackString;}return undefined;};/**
|
475
475
|
* Get mutated error with issue prepended to error message
|
476
476
|
* @param err Original error
|
477
477
|
* @param issue Issue to prepend to error message
|
478
478
|
* @returns Instance of Error with message prepended with issue
|
479
|
-
*/const getMutatedError=(err,issue)=>{let finalError=err;if(!isTypeOfError(err)){finalError=new Error(`${issue}: ${stringifyWithoutCircular(err)}`);}else {finalError.message=`${issue}: ${err.message}`;}return finalError;};const dispatchErrorEvent=error=>{if(isTypeOfError(error)){error
|
479
|
+
*/const getMutatedError=(err,issue)=>{let finalError=err;if(!isTypeOfError(err)){finalError=new Error(`${issue}: ${stringifyWithoutCircular(err)}`);}else {finalError.message=`${issue}: ${err.message}`;}return finalError;};const dispatchErrorEvent=error=>{if(isTypeOfError(error)){const errStack=getStacktrace(error);if(errStack){const{stack,stacktrace}=error;const operaSourceloc=error['opera#sourceloc'];switch(errStack){case stack:// eslint-disable-next-line no-param-reassign
|
480
|
+
error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// eslint-disable-next-line no-param-reassign
|
481
|
+
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
482
|
+
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
480
483
|
|
481
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
484
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.14.0';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
|
482
485
|
|
483
486
|
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';
|
484
487
|
|
@@ -563,11 +566,11 @@ timeoutID=globalThis.setTimeout(()=>{reject(new Error(SCRIPT_LOAD_TIMEOUT_ERROR(
|
|
563
566
|
|
564
567
|
/**
|
565
568
|
* Service to load external resources/files
|
566
|
-
*/class ExternalSrcLoader{
|
569
|
+
*/class ExternalSrcLoader{constructor(errorHandler,logger,timeout=DEFAULT_EXT_SRC_LOAD_TIMEOUT_MS){this.errorHandler=errorHandler;this.logger=logger;this.timeout=timeout;this.onError=this.onError.bind(this);}/**
|
567
570
|
* Load external resource of type javascript
|
568
571
|
*/loadJSFile(config){const{url,id,timeout,async,callback,extraAttributes}=config;const isFireAndForget=!isFunction(callback);jsFileLoader(url,id,timeout||this.timeout,async,extraAttributes).then(id=>{if(!isFireAndForget){callback(id);}}).catch(err=>{this.onError(err);if(!isFireAndForget){callback();}});}/**
|
569
572
|
* Handle errors
|
570
|
-
*/onError(error){
|
573
|
+
*/onError(error){this.errorHandler.onError(error,EXTERNAL_SRC_LOADER);}}
|
571
574
|
|
572
575
|
var i=Symbol.for("preact-signals");function t(){if(!(s>1)){var i,t=false;while(undefined!==h){var r=h;h=undefined;f++;while(undefined!==r){var o=r.o;r.o=undefined;r.f&=-3;if(!(8&r.f)&&c(r))try{r.c();}catch(r){if(!t){i=r;t=true;}}r=o;}}f=0;s--;if(t)throw i;}else s--;}function r(i){if(s>0)return i();s++;try{return i();}finally{t();}}var o=undefined;var h=undefined,s=0,f=0,v=0;function e(i){if(undefined!==o){var t=i.n;if(undefined===t||t.t!==o){t={i:0,S:i,p:o.s,n:undefined,t:o,e:undefined,x:undefined,r:t};if(undefined!==o.s)o.s.n=t;o.s=t;i.n=t;if(32&o.f)i.S(t);return t;}else if(-1===t.i){t.i=0;if(undefined!==t.n){t.n.p=t.p;if(undefined!==t.p)t.p.n=t.n;t.p=o.s;t.n=undefined;o.s.n=t;o.s=t;}return t;}}}function u(i){this.v=i;this.i=0;this.n=undefined;this.t=undefined;}u.prototype.brand=i;u.prototype.h=function(){return true;};u.prototype.S=function(i){if(this.t!==i&&undefined===i.e){i.x=this.t;if(undefined!==this.t)this.t.e=i;this.t=i;}};u.prototype.U=function(i){if(undefined!==this.t){var t=i.e,r=i.x;if(undefined!==t){t.x=r;i.e=undefined;}if(undefined!==r){r.e=t;i.x=undefined;}if(i===this.t)this.t=r;}};u.prototype.subscribe=function(i){var t=this;return E(function(){var r=t.value,n=o;o=undefined;try{i(r);}finally{o=n;}});};u.prototype.valueOf=function(){return this.value;};u.prototype.toString=function(){return this.value+"";};u.prototype.toJSON=function(){return this.value;};u.prototype.peek=function(){var i=o;o=undefined;try{return this.value;}finally{o=i;}};Object.defineProperty(u.prototype,"value",{get:function(){var i=e(this);if(undefined!==i)i.i=this.i;return this.v;},set:function(i){if(i!==this.v){if(f>100)throw new Error("Cycle detected");this.v=i;this.i++;v++;s++;try{for(var r=this.t;void 0!==r;r=r.x)r.t.N();}finally{t();}}}});function d$1(i){return new u(i);}function c(i){for(var t=i.s;undefined!==t;t=t.n)if(t.S.i!==t.i||!t.S.h()||t.S.i!==t.i)return true;return false;}function a(i){for(var t=i.s;undefined!==t;t=t.n){var r=t.S.n;if(undefined!==r)t.r=r;t.S.n=t;t.i=-1;if(undefined===t.n){i.s=t;break;}}}function l(i){var t=i.s,r=undefined;while(undefined!==t){var o=t.p;if(-1===t.i){t.S.U(t);if(undefined!==o)o.n=t.n;if(undefined!==t.n)t.n.p=o;}else r=t;t.S.n=t.r;if(undefined!==t.r)t.r=undefined;t=o;}i.s=r;}function y(i){u.call(this,undefined);this.x=i;this.s=undefined;this.g=v-1;this.f=4;}(y.prototype=new u()).h=function(){this.f&=-3;if(1&this.f)return false;if(32==(36&this.f))return true;this.f&=-5;if(this.g===v)return true;this.g=v;this.f|=1;if(this.i>0&&!c(this)){this.f&=-2;return true;}var i=o;try{a(this);o=this;var t=this.x();if(16&this.f||this.v!==t||0===this.i){this.v=t;this.f&=-17;this.i++;}}catch(i){this.v=i;this.f|=16;this.i++;}o=i;l(this);this.f&=-2;return true;};y.prototype.S=function(i){if(undefined===this.t){this.f|=36;for(var t=this.s;undefined!==t;t=t.n)t.S.S(t);}u.prototype.S.call(this,i);};y.prototype.U=function(i){if(undefined!==this.t){u.prototype.U.call(this,i);if(undefined===this.t){this.f&=-33;for(var t=this.s;undefined!==t;t=t.n)t.S.U(t);}}};y.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var i=this.t;undefined!==i;i=i.x)i.t.N();}};Object.defineProperty(y.prototype,"value",{get:function(){if(1&this.f)throw new Error("Cycle detected");var i=e(this);this.h();if(undefined!==i)i.i=this.i;if(16&this.f)throw this.v;return this.v;}});function _(i){var r=i.u;i.u=undefined;if("function"==typeof r){s++;var n=o;o=undefined;try{r();}catch(t){i.f&=-2;i.f|=8;g(i);throw t;}finally{o=n;t();}}}function g(i){for(var t=i.s;undefined!==t;t=t.n)t.S.U(t);i.x=undefined;i.s=undefined;_(i);}function p(i){if(o!==this)throw new Error("Out-of-order effect");l(this);o=i;this.f&=-2;if(8&this.f)g(this);t();}function b(i){this.x=i;this.u=undefined;this.s=undefined;this.o=undefined;this.f=32;}b.prototype.c=function(){var i=this.S();try{if(8&this.f)return;if(void 0===this.x)return;var t=this.x();if("function"==typeof t)this.u=t;}finally{i();}};b.prototype.S=function(){if(1&this.f)throw new Error("Cycle detected");this.f|=1;this.f&=-9;_(this);a(this);s++;var i=o;o=this;return p.bind(this,i);};b.prototype.N=function(){if(!(2&this.f)){this.f|=2;this.o=h;h=this;}};b.prototype.d=function(){this.f|=8;if(!(1&this.f))g(this);};function E(i){var t=new b(i);try{t.c();}catch(i){t.d();throw i;}return t.d.bind(t);}
|
573
576
|
|
@@ -596,9 +599,9 @@ let ErrorType=/*#__PURE__*/function(ErrorType){ErrorType["HANDLEDEXCEPTION"]="ha
|
|
596
599
|
// default is v3
|
597
600
|
const SUPPORTED_STORAGE_TYPES=['localStorage','memoryStorage','cookieStorage','sessionStorage','none'];const DEFAULT_STORAGE_TYPE='cookieStorage';
|
598
601
|
|
599
|
-
const
|
600
|
-
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
|
601
|
-
const STORAGE_TYPE_VALIDATION_WARNING=(context,storageType,defaultStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${storageType}" is not supported. Please choose one of the following supported types: "${SUPPORTED_STORAGE_TYPES}". The default type "${defaultStorageType}" will be used instead.`;const UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING=(context,selectedStorageEncryptionVersion,storageEncryptionVersionsToPluginNameMap,defaultVersion)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage encryption version "${selectedStorageEncryptionVersion}" is not supported. Please choose one of the following supported versions: "${Object.keys(storageEncryptionVersionsToPluginNameMap)}". The default version "${defaultVersion}" will be used instead.`;const STORAGE_DATA_MIGRATION_OVERRIDE_WARNING=(context,storageEncryptionVersion,defaultVersion)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage data migration has been disabled because the configured storage encryption version (${storageEncryptionVersion}) is not the latest (${defaultVersion}). To enable storage data migration, please update the storage encryption version to the latest version.`;const SERVER_SIDE_COOKIE_FEATURE_OVERRIDE_WARNING=(context,providedCookieDomain,currentCookieDomain)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided cookie domain (${providedCookieDomain}) does not match the current webpage's domain (${currentCookieDomain}). Hence, the cookies will be set client-side.`;const RESERVED_KEYWORD_WARNING=(context,property,parentKeyPath,reservedElements)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${property}" property defined under "${parentKeyPath}" is a reserved keyword. Please choose a different property name to avoid conflicts with reserved keywords (${reservedElements}).`;const 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_WARNING=(context,entry,selectedStorageType,finalStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${selectedStorageType}" is not available for entry "${entry}". The SDK will initialize the entry with "${finalStorageType}" storage type instead.`;const
|
602
|
+
const SOURCE_CONFIG_RESOLUTION_ERROR=`Unable to process/parse source configuration response`;const SOURCE_DISABLED_ERROR=`The source is disabled. Please enable the source in the dashboard to send events.`;const XHR_PAYLOAD_PREP_ERROR=`Failed to prepare data for the request.`;const PLUGIN_EXT_POINT_MISSING_ERROR=`Failed to invoke plugin because the extension point name is missing.`;const PLUGIN_EXT_POINT_INVALID_ERROR=`Failed to invoke plugin because the extension point name is invalid.`;const SOURCE_CONFIG_OPTION_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The "getSourceConfig" load API option must be a function that returns valid source configuration data.`;const COMPONENT_BASE_URL_ERROR=(context,component,url)=>`${context}${LOG_CONTEXT_SEPARATOR}The base URL "${url}" for ${component} is not valid.`;// ERROR
|
603
|
+
const UNSUPPORTED_CONSENT_MANAGER_ERROR=(context,selectedConsentManager,consentManagersToPluginNameMap)=>`${context}${LOG_CONTEXT_SEPARATOR}The consent manager "${selectedConsentManager}" is not supported. Please choose one of the following supported consent managers: "${Object.keys(consentManagersToPluginNameMap)}".`;const NON_ERROR_WARNING=(context,errStr)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring a non-error: ${errStr}.`;const BREADCRUMB_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to log breadcrumb.`;const HANDLE_ERROR_FAILURE=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to handle the error.`;const PLUGIN_NAME_MISSING_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin name is missing.`;const PLUGIN_ALREADY_EXISTS_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" already exists.`;const PLUGIN_NOT_FOUND_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found.`;const PLUGIN_ENGINE_BUG_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found in plugins but found in byName. This indicates a bug in the plugin engine. Please report this issue to the development team.`;const PLUGIN_DEPS_ERROR=(context,pluginName,notExistDeps)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" could not be loaded because some of its dependencies "${notExistDeps}" do not exist.`;const PLUGIN_INVOCATION_ERROR=(context,extPoint,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to invoke the "${extPoint}" extension point of plugin "${pluginName}".`;const STORAGE_UNAVAILABILITY_ERROR_PREFIX=(context,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${storageType}" storage type is `;const SOURCE_CONFIG_FETCH_ERROR='Failed to fetch the source config';const WRITE_KEY_VALIDATION_ERROR=(context,writeKey)=>`${context}${LOG_CONTEXT_SEPARATOR}The write key "${writeKey}" is invalid. It must be a non-empty string. Please check that the write key is correct and try again.`;const DATA_PLANE_URL_VALIDATION_ERROR=(context,dataPlaneUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The data plane URL "${dataPlaneUrl}" is invalid. It must be a valid URL string. Please check that the data plane URL is correct and try again.`;const INVALID_CALLBACK_FN_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The provided callback parameter is not a function.`;const XHR_DELIVERY_ERROR=(prefix,status,statusText,url)=>`${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`;const DATA_SERVER_REQUEST_FAIL_ERROR=status=>`The server responded with status ${status} while setting the cookies. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_ERROR=key=>`The server failed to set the ${key} cookie. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR=`Failed to set/remove cookies via server. As a fallback, the cookies will be managed client side.`;// WARNING
|
604
|
+
const STORAGE_TYPE_VALIDATION_WARNING=(context,storageType,defaultStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${storageType}" is not supported. Please choose one of the following supported types: "${SUPPORTED_STORAGE_TYPES}". The default type "${defaultStorageType}" will be used instead.`;const UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING=(context,selectedStorageEncryptionVersion,storageEncryptionVersionsToPluginNameMap,defaultVersion)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage encryption version "${selectedStorageEncryptionVersion}" is not supported. Please choose one of the following supported versions: "${Object.keys(storageEncryptionVersionsToPluginNameMap)}". The default version "${defaultVersion}" will be used instead.`;const STORAGE_DATA_MIGRATION_OVERRIDE_WARNING=(context,storageEncryptionVersion,defaultVersion)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage data migration has been disabled because the configured storage encryption version (${storageEncryptionVersion}) is not the latest (${defaultVersion}). To enable storage data migration, please update the storage encryption version to the latest version.`;const SERVER_SIDE_COOKIE_FEATURE_OVERRIDE_WARNING=(context,providedCookieDomain,currentCookieDomain)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided cookie domain (${providedCookieDomain}) does not match the current webpage's domain (${currentCookieDomain}). Hence, the cookies will be set client-side.`;const RESERVED_KEYWORD_WARNING=(context,property,parentKeyPath,reservedElements)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${property}" property defined under "${parentKeyPath}" is a reserved keyword. Please choose a different property name to avoid conflicts with reserved keywords (${reservedElements}).`;const INVALID_CONTEXT_OBJECT_WARNING=logContext=>`${logContext}${LOG_CONTEXT_SEPARATOR}Please make sure that the "context" property in the event API's "options" argument is a valid object literal with key-value pairs.`;const UNSUPPORTED_BEACON_API_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The Beacon API is not supported by your browser. The events will be sent using XHR instead.`;const TIMEOUT_NOT_NUMBER_WARNING=(context,timeout,defaultValue)=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value "${timeout}" is not a number. The default timeout of ${defaultValue} ms will be used instead.`;const TIMEOUT_ZERO_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value is 0, which disables the automatic session tracking feature. If you want to enable session tracking, please provide a positive integer value for the timeout.`;const TIMEOUT_NOT_RECOMMENDED_WARNING=(context,timeout,minTimeout)=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value ${timeout} ms is less than the recommended minimum of ${minTimeout} ms. Please consider increasing the timeout value to ensure optimal performance and reliability.`;const INVALID_SESSION_ID_WARNING=(context,sessionId,minSessionIdLength)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided session ID (${sessionId}) is either invalid, not a positive integer, or not at least "${minSessionIdLength}" digits long. A new session ID will be auto-generated instead.`;const STORAGE_QUOTA_EXCEEDED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The storage is either full or unavailable, so the data will not be persisted. Switching to in-memory storage.`;const STORAGE_UNAVAILABLE_WARNING=(context,entry,selectedStorageType,finalStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${selectedStorageType}" is not available for entry "${entry}". The SDK will initialize the entry with "${finalStorageType}" storage type instead.`;const CALLBACK_INVOKE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The callback threw an exception`;const INVALID_CONFIG_URL_WARNING=(context,configUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided source config URL "${configUrl}" is invalid. Using the default source config URL instead.`;const POLYFILL_SCRIPT_LOAD_ERROR=(scriptId,url)=>`Failed to load the polyfill script with ID "${scriptId}" from URL ${url}.`;const UNSUPPORTED_PRE_CONSENT_STORAGE_STRATEGY=(context,selectedStrategy,defaultStrategy)=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent storage strategy "${selectedStrategy}" is not supported. Please choose one of the following supported strategies: "none, session, anonymousId". The default strategy "${defaultStrategy}" will be used instead.`;const UNSUPPORTED_PRE_CONSENT_EVENTS_DELIVERY_TYPE=(context,selectedDeliveryType,defaultDeliveryType)=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent events delivery type "${selectedDeliveryType}" is not supported. Please choose one of the following supported types: "immediate, buffer". The default type "${defaultDeliveryType}" will be used instead.`;const DEPRECATED_PLUGIN_WARNING=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}${pluginName} plugin is deprecated. Please exclude it from the load API options.`;const generateMisconfiguredPluginsWarning=(context,configurationStatus,missingPlugins,shouldAddMissingPlugins)=>{const isSinglePlugin=missingPlugins.length===1;const pluginsString=isSinglePlugin?` '${missingPlugins[0]}' plugin was`:` ['${missingPlugins.join("', '")}'] plugins were`;const baseWarning=`${context}${LOG_CONTEXT_SEPARATOR}${configurationStatus}, but${pluginsString} not configured to load.`;if(shouldAddMissingPlugins){return `${baseWarning} So, ${isSinglePlugin?'the plugin':'those plugins'} will be loaded automatically.`;}return `${baseWarning} Ignore if this was intentional. Otherwise, consider adding ${isSinglePlugin?'it':'them'} to the 'plugins' load API option.`;};const INVALID_POLYFILL_URL_WARNING=(context,customPolyfillUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided polyfill URL "${customPolyfillUrl}" is invalid. The default polyfill URL will be used instead.`;const BAD_COOKIES_WARNING=key=>`The cookie data for ${key} seems to be encrypted using SDK versions < v3. The data is dropped. This can potentially stem from using SDK versions < v3 on other sites or web pages that can share cookies with this webpage. We recommend using the same SDK (v3) version everywhere or avoid disabling the storage data migration.`;const PAGE_UNLOAD_ON_BEACON_DISABLED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}Page Unloaded event can only be tracked when the Beacon transport is active. Please enable "useBeacon" load API option.`;const UNKNOWN_PLUGINS_WARNING=(context,unknownPlugins)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring unknown plugins: ${unknownPlugins.join(', ')}.`;
|
602
605
|
|
603
606
|
const DEFAULT_INTEGRATIONS_CONFIG={All:true};
|
604
607
|
|
@@ -617,7 +620,7 @@ const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';co
|
|
617
620
|
|
618
621
|
const DEFAULT_STORAGE_ENCRYPTION_VERSION='v3';const DEFAULT_DATA_PLANE_EVENTS_TRANSPORT='xhr';const ConsentManagersToPluginNameMap={iubenda:'IubendaConsentManager',oneTrust:'OneTrustConsentManager',ketch:'KetchConsentManager',custom:'CustomConsentManager'};const StorageEncryptionVersionsToPluginNameMap={[DEFAULT_STORAGE_ENCRYPTION_VERSION]:'StorageEncryption',legacy:'StorageEncryptionLegacy'};const DataPlaneEventsTransportToPluginNameMap={[DEFAULT_DATA_PLANE_EVENTS_TRANSPORT]:'XhrQueue',beacon:'BeaconQueue'};const DEFAULT_DATA_SERVICE_ENDPOINT='rsaRequest';const METRICS_SERVICE_ENDPOINT='rsaMetrics';
|
619
622
|
|
620
|
-
const defaultLoadOptions={
|
623
|
+
const defaultLoadOptions={configUrl:DEFAULT_CONFIG_BE_URL,loadIntegration:true,sessions:{autoTrack:true,timeout:DEFAULT_SESSION_TIMEOUT_MS},sameSiteCookie:'Lax',polyfillIfRequired:true,integrations:DEFAULT_INTEGRATIONS_CONFIG,useBeacon:false,beaconQueueOptions:{},destinationsQueueOptions:{},queueOptions:{},lockIntegrationsVersion:true,lockPluginsVersion:true,uaChTrackLevel:'none',plugins:[],useGlobalIntegrationsConfigInEvents:false,bufferDataPlaneEventsUntilReady:false,dataPlaneEventsBufferTimeout:DEFAULT_DATA_PLANE_EVENTS_BUFFER_TIMEOUT_MS,storage:{encryption:{version:DEFAULT_STORAGE_ENCRYPTION_VERSION},migrate:true,cookie:{}},sendAdblockPage:false,sameDomainCookiesOnly:false,secureCookie:false,sendAdblockPageOptions:{},useServerSideCookies:false};const loadOptionsState=d$1(clone(defaultLoadOptions));
|
621
624
|
|
622
625
|
const DEFAULT_USER_SESSION_VALUES={userId:'',userTraits:{},anonymousId:'',groupId:'',groupTraits:{},initialReferrer:'',initialReferringDomain:'',sessionInfo:{},authToken:null};const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
|
623
626
|
|
@@ -625,17 +628,17 @@ const defaultSessionConfiguration={autoTrack:true,timeout:DEFAULT_SESSION_TIMEOU
|
|
625
628
|
|
626
629
|
const capabilitiesState={isOnline:d$1(true),storage:{isLocalStorageAvailable:d$1(false),isCookieStorageAvailable:d$1(false),isSessionStorageAvailable:d$1(false)},isBeaconAvailable:d$1(false),isLegacyDOM:d$1(false),isUaCHAvailable:d$1(false),isCryptoAvailable:d$1(false),isIE11:d$1(false),isAdBlocked:d$1(false)};
|
627
630
|
|
628
|
-
const reportingState={isErrorReportingEnabled:d$1(false),isMetricsReportingEnabled:d$1(false),
|
631
|
+
const reportingState={isErrorReportingEnabled:d$1(false),isMetricsReportingEnabled:d$1(false),breadcrumbs:d$1([])};
|
629
632
|
|
630
633
|
const sourceConfigState=d$1(undefined);
|
631
634
|
|
632
|
-
const lifecycleState={activeDataplaneUrl:d$1(undefined),integrationsCDNPath:d$1(DEFAULT_INTEGRATION_SDKS_URL),pluginsCDNPath:d$1(DEFAULT_PLUGINS_URL),sourceConfigUrl:d$1(undefined),status:d$1(undefined),initialized:d$1(false),logLevel:d$1(
|
635
|
+
const lifecycleState={activeDataplaneUrl:d$1(undefined),integrationsCDNPath:d$1(DEFAULT_INTEGRATION_SDKS_URL),pluginsCDNPath:d$1(DEFAULT_PLUGINS_URL),sourceConfigUrl:d$1(undefined),status:d$1(undefined),initialized:d$1(false),logLevel:d$1(POST_LOAD_LOG_LEVEL),loaded:d$1(false),readyCallbacks:d$1([]),writeKey:d$1(undefined),dataPlaneUrl:d$1(undefined)};
|
633
636
|
|
634
637
|
const consentsState={enabled:d$1(false),initialized:d$1(false),data:d$1({}),activeConsentManagerPluginName:d$1(undefined),preConsent:d$1({enabled:false}),postConsent:d$1({}),resolutionStrategy:d$1('and'),provider:d$1(undefined),metadata:d$1(undefined)};
|
635
638
|
|
636
639
|
const metricsState={retries:d$1(0),dropped:d$1(0),sent:d$1(0),queued:d$1(0),triggered:d$1(0),metricsServiceUrl:d$1(undefined)};
|
637
640
|
|
638
|
-
const contextState={app:d$1({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION,installType:MODULE_TYPE}),traits:d$1(null),library:d$1({name:APP_NAME,version:APP_VERSION,snippetVersion:globalThis.RudderSnippetVersion}),userAgent:d$1(
|
641
|
+
const contextState={app:d$1({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION,installType:MODULE_TYPE}),traits:d$1(null),library:d$1({name:APP_NAME,version:APP_VERSION,snippetVersion:globalThis.RudderSnippetVersion}),userAgent:d$1(null),device:d$1(null),network:d$1(null),os:d$1({name:'',version:''}),locale:d$1(null),screen:d$1({density:0,width:0,height:0,innerWidth:0,innerHeight:0}),'ua-ch':d$1(undefined),timezone:d$1(undefined)};
|
639
642
|
|
640
643
|
const nativeDestinationsState={configuredDestinations:d$1([]),activeDestinations:d$1([]),loadOnlyIntegrations:d$1({}),failedDestinations:d$1([]),loadIntegration:d$1(true),initializedDestinations:d$1([]),clientDestinationsReady:d$1(false),integrationsConfig:d$1({})};
|
641
644
|
|
@@ -654,48 +657,129 @@ const autoTrackState={enabled:d$1(false),pageLifecycle:{enabled:d$1(false),visit
|
|
654
657
|
|
655
658
|
const defaultStateValues={capabilities:capabilitiesState,consents:consentsState,context:contextState,eventBuffer:eventBufferState,lifecycle:lifecycleState,loadOptions:loadOptionsState,metrics:metricsState,nativeDestinations:nativeDestinationsState,plugins:pluginsState,reporting:reportingState,session:sessionState,source:sourceConfigState,storage:storageState,serverCookies:serverSideCookiesState,dataPlaneEvents:dataPlaneEventsState,autoTrack:autoTrackState};const state={...clone(defaultStateValues)};
|
656
659
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
if(
|
660
|
+
function getDefaultExportFromCjs (x) {
|
661
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
662
|
+
}
|
663
|
+
|
664
|
+
var errorStackParser$1 = {exports: {}};
|
665
|
+
|
666
|
+
var stackframe$1 = {exports: {}};
|
667
|
+
|
668
|
+
var stackframe=stackframe$1.exports;var hasRequiredStackframe;function requireStackframe(){if(hasRequiredStackframe)return stackframe$1.exports;hasRequiredStackframe=1;(function(module,exports){(function(root,factory){/* istanbul ignore next */{module.exports=factory();}})(stackframe,function(){function _isNumber(n){return !isNaN(parseFloat(n))&&isFinite(n);}function _capitalize(str){return str.charAt(0).toUpperCase()+str.substring(1);}function _getter(p){return function(){return this[p];};}var booleanProps=['isConstructor','isEval','isNative','isToplevel'];var numericProps=['columnNumber','lineNumber'];var stringProps=['fileName','functionName','source'];var arrayProps=['args'];var objectProps=['evalOrigin'];var props=booleanProps.concat(numericProps,stringProps,arrayProps,objectProps);function StackFrame(obj){if(!obj)return;for(var i=0;i<props.length;i++){if(obj[props[i]]!==undefined){this['set'+_capitalize(props[i])](obj[props[i]]);}}}StackFrame.prototype={getArgs:function(){return this.args;},setArgs:function(v){if(Object.prototype.toString.call(v)!=='[object Array]'){throw new TypeError('Args must be an Array');}this.args=v;},getEvalOrigin:function(){return this.evalOrigin;},setEvalOrigin:function(v){if(v instanceof StackFrame){this.evalOrigin=v;}else if(v instanceof Object){this.evalOrigin=new StackFrame(v);}else {throw new TypeError('Eval Origin must be an Object or StackFrame');}},toString:function(){var fileName=this.getFileName()||'';var lineNumber=this.getLineNumber()||'';var columnNumber=this.getColumnNumber()||'';var functionName=this.getFunctionName()||'';if(this.getIsEval()){if(fileName){return '[eval] ('+fileName+':'+lineNumber+':'+columnNumber+')';}return '[eval]:'+lineNumber+':'+columnNumber;}if(functionName){return functionName+' ('+fileName+':'+lineNumber+':'+columnNumber+')';}return fileName+':'+lineNumber+':'+columnNumber;}};StackFrame.fromString=function StackFrame$$fromString(str){var argsStartIndex=str.indexOf('(');var argsEndIndex=str.lastIndexOf(')');var functionName=str.substring(0,argsStartIndex);var args=str.substring(argsStartIndex+1,argsEndIndex).split(',');var locationString=str.substring(argsEndIndex+1);if(locationString.indexOf('@')===0){var parts=/@(.+?)(?::(\d+))?(?::(\d+))?$/.exec(locationString,'');var fileName=parts[1];var lineNumber=parts[2];var columnNumber=parts[3];}return new StackFrame({functionName:functionName,args:args||undefined,fileName:fileName,lineNumber:lineNumber||undefined,columnNumber:columnNumber||undefined});};for(var i=0;i<booleanProps.length;i++){StackFrame.prototype['get'+_capitalize(booleanProps[i])]=_getter(booleanProps[i]);StackFrame.prototype['set'+_capitalize(booleanProps[i])]=function(p){return function(v){this[p]=Boolean(v);};}(booleanProps[i]);}for(var j=0;j<numericProps.length;j++){StackFrame.prototype['get'+_capitalize(numericProps[j])]=_getter(numericProps[j]);StackFrame.prototype['set'+_capitalize(numericProps[j])]=function(p){return function(v){if(!_isNumber(v)){throw new TypeError(p+' must be a Number');}this[p]=Number(v);};}(numericProps[j]);}for(var k=0;k<stringProps.length;k++){StackFrame.prototype['get'+_capitalize(stringProps[k])]=_getter(stringProps[k]);StackFrame.prototype['set'+_capitalize(stringProps[k])]=function(p){return function(v){this[p]=String(v);};}(stringProps[k]);}return StackFrame;});})(stackframe$1);return stackframe$1.exports;}
|
669
|
+
|
670
|
+
var errorStackParser=errorStackParser$1.exports;var hasRequiredErrorStackParser;function requireErrorStackParser(){if(hasRequiredErrorStackParser)return errorStackParser$1.exports;hasRequiredErrorStackParser=1;(function(module,exports){(function(root,factory){/* istanbul ignore next */{module.exports=factory(requireStackframe());}})(errorStackParser,function ErrorStackParser(StackFrame){var FIREFOX_SAFARI_STACK_REGEXP=/(^|@)\S+:\d+/;var CHROME_IE_STACK_REGEXP=/^\s*at .*(\S+:\d+|\(native\))/m;var SAFARI_NATIVE_CODE_REGEXP=/^(eval@)?(\[native code])?$/;return {/**
|
671
|
+
* Given an Error object, extract the most information from it.
|
672
|
+
*
|
673
|
+
* @param {Error} error object
|
674
|
+
* @return {Array} of StackFrames
|
675
|
+
*/parse:function ErrorStackParser$$parse(error){if(typeof error.stacktrace!=='undefined'||typeof error['opera#sourceloc']!=='undefined'){return this.parseOpera(error);}else if(error.stack&&error.stack.match(CHROME_IE_STACK_REGEXP)){return this.parseV8OrIE(error);}else if(error.stack){return this.parseFFOrSafari(error);}else {throw new Error('Cannot parse given Error object');}},// Separate line and column numbers from a string of the form: (URI:Line:Column)
|
676
|
+
extractLocation:function ErrorStackParser$$extractLocation(urlLike){// Fail-fast but return locations like "(native)"
|
677
|
+
if(urlLike.indexOf(':')===-1){return [urlLike];}var regExp=/(.+?)(?::(\d+))?(?::(\d+))?$/;var parts=regExp.exec(urlLike.replace(/[()]/g,''));return [parts[1],parts[2]||undefined,parts[3]||undefined];},parseV8OrIE:function ErrorStackParser$$parseV8OrIE(error){var filtered=error.stack.split('\n').filter(function(line){return !!line.match(CHROME_IE_STACK_REGEXP);},this);return filtered.map(function(line){if(line.indexOf('(eval ')>-1){// Throw away eval information until we implement stacktrace.js/stackframe#8
|
678
|
+
line=line.replace(/eval code/g,'eval').replace(/(\(eval at [^()]*)|(,.*$)/g,'');}var sanitizedLine=line.replace(/^\s+/,'').replace(/\(eval code/g,'(').replace(/^.*?\s+/,'');// capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
|
679
|
+
// case it has spaces in it, as the string is split on \s+ later on
|
680
|
+
var location=sanitizedLine.match(/ (\(.+\)$)/);// remove the parenthesized location from the line, if it was matched
|
681
|
+
sanitizedLine=location?sanitizedLine.replace(location[0],''):sanitizedLine;// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
|
682
|
+
// because this line doesn't have function name
|
683
|
+
var locationParts=this.extractLocation(location?location[1]:sanitizedLine);var functionName=location&&sanitizedLine||undefined;var fileName=['eval','<anonymous>'].indexOf(locationParts[0])>-1?undefined:locationParts[0];return new StackFrame({functionName:functionName,fileName:fileName,lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});},this);},parseFFOrSafari:function ErrorStackParser$$parseFFOrSafari(error){var filtered=error.stack.split('\n').filter(function(line){return !line.match(SAFARI_NATIVE_CODE_REGEXP);},this);return filtered.map(function(line){// Throw away eval information until we implement stacktrace.js/stackframe#8
|
684
|
+
if(line.indexOf(' > eval')>-1){line=line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,':$1');}if(line.indexOf('@')===-1&&line.indexOf(':')===-1){// Safari eval frames only have function names and nothing else
|
685
|
+
return new StackFrame({functionName:line});}else {var functionNameRegex=/((.*".+"[^@]*)?[^@]*)(?:@)/;var matches=line.match(functionNameRegex);var functionName=matches&&matches[1]?matches[1]:undefined;var locationParts=this.extractLocation(line.replace(functionNameRegex,''));return new StackFrame({functionName:functionName,fileName:locationParts[0],lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});}},this);},parseOpera:function ErrorStackParser$$parseOpera(e){if(!e.stacktrace||e.message.indexOf('\n')>-1&&e.message.split('\n').length>e.stacktrace.split('\n').length){return this.parseOpera9(e);}else if(!e.stack){return this.parseOpera10(e);}else {return this.parseOpera11(e);}},parseOpera9:function ErrorStackParser$$parseOpera9(e){var lineRE=/Line (\d+).*script (?:in )?(\S+)/i;var lines=e.message.split('\n');var result=[];for(var i=2,len=lines.length;i<len;i+=2){var match=lineRE.exec(lines[i]);if(match){result.push(new StackFrame({fileName:match[2],lineNumber:match[1],source:lines[i]}));}}return result;},parseOpera10:function ErrorStackParser$$parseOpera10(e){var lineRE=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;var lines=e.stacktrace.split('\n');var result=[];for(var i=0,len=lines.length;i<len;i+=2){var match=lineRE.exec(lines[i]);if(match){result.push(new StackFrame({functionName:match[3]||undefined,fileName:match[2],lineNumber:match[1],source:lines[i]}));}}return result;},// Opera 10.65+ Error.stack very similar to FF/Safari
|
686
|
+
parseOpera11:function ErrorStackParser$$parseOpera11(error){var filtered=error.stack.split('\n').filter(function(line){return !!line.match(FIREFOX_SAFARI_STACK_REGEXP)&&!line.match(/^Error created at/);},this);return filtered.map(function(line){var tokens=line.split('@');var locationParts=this.extractLocation(tokens.pop());var functionCall=tokens.shift()||'';var functionName=functionCall.replace(/<anonymous function(: (\w+))?>/,'$2').replace(/\([^)]*\)/g,'')||undefined;var argsRaw;if(functionCall.match(/\(([^)]*)\)/)){argsRaw=functionCall.replace(/^[^(]+\(([^)]*)\)$/,'$1');}var args=argsRaw===undefined||argsRaw==='[arguments not available]'?undefined:argsRaw.split(',');return new StackFrame({functionName:functionName,args:args,fileName:locationParts[0],lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});},this);}};});})(errorStackParser$1);return errorStackParser$1.exports;}
|
687
|
+
|
688
|
+
var errorStackParserExports = requireErrorStackParser();
|
689
|
+
const ErrorStackParser = /*@__PURE__*/getDefaultExportFromCjs(errorStackParserExports);
|
690
|
+
|
691
|
+
const GLOBAL_CODE='global code';const normalizeFunctionName=name=>{if(isDefined(name)){return /^global code$/i.test(name)?GLOBAL_CODE:name;}return name;};/**
|
692
|
+
* Takes a stacktrace.js style stackframe (https://github.com/stacktracejs/stackframe)
|
693
|
+
* and returns a Bugsnag compatible stackframe (https://docs.bugsnag.com/api/error-reporting/#json-payload)
|
694
|
+
* @param frame
|
695
|
+
* @returns
|
696
|
+
*/const formatStackframe=frame=>{const f={file:frame.fileName,method:normalizeFunctionName(frame.functionName),lineNumber:frame.lineNumber,columnNumber:frame.columnNumber};// Some instances result in no file:
|
697
|
+
// - non-error exception thrown from global code in FF
|
698
|
+
// This adds one.
|
699
|
+
if(f.lineNumber&&f.lineNumber>-1&&!f.file&&!f.method){f.file=GLOBAL_CODE;}return f;};const ensureString=str=>isString(str)?str:'';function createException(errorClass,errorMessage,msgPrefix,stacktrace){return {errorClass:ensureString(errorClass),message:`${msgPrefix}${ensureString(errorMessage)}`,type:'browserjs',stacktrace:stacktrace.reduce((accum,frame)=>{const f=formatStackframe(frame);// don't include a stackframe if none of its properties are defined
|
700
|
+
try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch{return accum;}},[])};}const normalizeError=(maybeError,logger)=>{let error;if(isTypeOfError(maybeError)&&isString(getStacktrace(maybeError))){error=maybeError;}else {logger.warn(NON_ERROR_WARNING(ERROR_HANDLER,stringifyWithoutCircular(maybeError)));error=undefined;}return error;};const createBugsnagException=(error,msgPrefix)=>{try{const stacktrace=ErrorStackParser.parse(error);return createException(error.name,error.message,msgPrefix,stacktrace);}catch{return createException(error.name,error.message,msgPrefix,[]);}};
|
701
|
+
|
702
|
+
/**
|
703
|
+
* Utility to parse XHR JSON response
|
704
|
+
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
705
|
+
|
706
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX];
|
666
707
|
|
667
|
-
const
|
708
|
+
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
709
|
+
* Utility to create request configuration based on default options
|
710
|
+
*/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;};/**
|
711
|
+
* Utility implementation of XHR, fetch cannot be used as it requires explicit
|
712
|
+
* origin allowed values and not wildcard for CORS requests with credentials and
|
713
|
+
* this is not supported by our sourceConfig API
|
714
|
+
*/const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
|
715
|
+
return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
716
|
+
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,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
|
717
|
+
// and the first call to the send method in legacy browsers
|
718
|
+
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});}});
|
668
719
|
|
669
720
|
/**
|
670
|
-
*
|
671
|
-
*/
|
672
|
-
|
673
|
-
|
674
|
-
|
721
|
+
* Service to handle data communication with APIs
|
722
|
+
*/class HttpClient{constructor(logger){this.logger=logger;this.onError=this.onError.bind(this);}init(errorHandler){this.errorHandler=errorHandler;}/**
|
723
|
+
* Implement requests in a blocking way
|
724
|
+
*/async getData(config){const{url,options,timeout,isRawResponse}=config;try{const data=await xhrRequest(createXhrRequestOptions(url,options,this.basicAuthHeader),timeout,this.logger);return {data:isRawResponse?data.response:responseTextToJson(data.response,this.onError),details:data};}catch(reason){return {data:undefined,details:reason};}}/**
|
725
|
+
* Implement requests in a non-blocking way
|
726
|
+
*/getAsyncData(config){const{callback,url,options,timeout,isRawResponse}=config;const isFireAndForget=!isFunction(callback);xhrRequest(createXhrRequestOptions(url,options,this.basicAuthHeader),timeout,this.logger).then(data=>{if(!isFireAndForget){callback(isRawResponse?data.response:responseTextToJson(data.response,this.onError),data);}}).catch(data=>{if(!isFireAndForget){callback(undefined,data);}});}/**
|
727
|
+
* Handle errors
|
728
|
+
*/onError(error){this.errorHandler?.onError(error,HTTP_CLIENT);}/**
|
729
|
+
* Set basic authentication header (eg writekey)
|
730
|
+
*/setAuthHeader(value,noBtoa=false){const authVal=noBtoa?value:toBase64(`${value}:`);this.basicAuthHeader=`Basic ${authVal}`;}/**
|
731
|
+
* Clear basic authentication header
|
732
|
+
*/resetAuthHeader(){this.basicAuthHeader=undefined;}}const defaultHttpClient=new HttpClient(defaultLogger);
|
733
|
+
|
734
|
+
const METRICS_PAYLOAD_VERSION='1';
|
735
|
+
|
736
|
+
// Errors from the below scripts are NOT allowed to reach Bugsnag
|
737
|
+
const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
|
738
|
+
];const DEV_HOSTS=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
|
739
|
+
// Potential PII or sensitive data
|
740
|
+
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','instance',// destination instance objects
|
741
|
+
'eventBuffer',// pre-load event buffer (may contain PII)
|
742
|
+
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='__REPOSITORY_URL__';const SOURCE_NAME='js';
|
743
|
+
|
744
|
+
const getErrInstance=(err,errorType)=>{switch(errorType){case ErrorType.UNHANDLEDEXCEPTION:{const{error}=err;return error||err;}case ErrorType.UNHANDLEDREJECTION:{return err.reason;}case ErrorType.HANDLEDEXCEPTION:default:return err;}};const createNewBreadcrumb=message=>({type:'manual',name:message,timestamp:new Date(),metaData:{}});const getReleaseStage=()=>{const host=globalThis.location.hostname;return host&&DEV_HOSTS.includes(host)?'development':'production';};const getAppStateForMetadata=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS);return stateStr!==null?JSON.parse(stateStr):{};};const getURLWithoutQueryString=()=>{const url=globalThis.location.href.split('?');return url[0];};const getUserDetails=(source,session,lifecycle,autoTrack)=>({id:`${source.value?.id??lifecycle.writeKey.value}..${session.sessionInfo.value.id??'NA'}..${autoTrack.pageLifecycle.visitId.value??'NA'}`,name:source.value?.name??'NA'});const getDeviceDetails=(locale,userAgent)=>({locale:locale.value??'NA',userAgent:userAgent.value??'NA',time:new Date()});const getBugsnagErrorEvent=(exception,errorState,state)=>{const{context,lifecycle,session,source,reporting,autoTrack}=state;const{app,locale,userAgent,timezone,screen,library}=context;return {payloadVersion:'5',notifier:{name:NOTIFIER_NAME,version:app.value.version,url:SDK_GITHUB_URL},events:[{exceptions:[clone(exception)],severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:app.value.version,releaseStage:getReleaseStage(),type:app.value.installType},device:getDeviceDetails(locale,userAgent),request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(reporting.breadcrumbs.value),context:exception.message,metaData:{app:{snippetVersion:library.value.snippetVersion},device:{...screen.value,timezone:timezone.value},// Add rest of the state groups as metadata
|
745
|
+
// so that they show up as separate tabs in the dashboard
|
746
|
+
...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
|
747
|
+
* A function to determine whether the error should be promoted to notify or not
|
748
|
+
* @param {Error} exception
|
749
|
+
* @returns
|
750
|
+
*/const isAllowedToBeNotified=exception=>!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>exception.message.includes(e));/**
|
751
|
+
* A function to determine if the error is from Rudder SDK
|
752
|
+
* @param {Error} exception
|
753
|
+
* @returns
|
754
|
+
*/const isSDKError=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const paths=errorOrigin.split('/');// extract the parent folder name from the error origin file path
|
755
|
+
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
756
|
+
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType},errors:payload};return stringifyWithoutCircular(data);};
|
675
757
|
|
676
758
|
/**
|
677
759
|
* A service to handle errors
|
678
760
|
*/class ErrorHandler{// If no logger is passed errors will be thrown as unhandled error
|
679
|
-
constructor(logger
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
761
|
+
constructor(httpClient,logger){this.httpClient=httpClient;this.logger=logger;this.attachErrorListeners();}attachErrorListeners(){globalThis.addEventListener('error',event=>{this.onError(event,ERROR_HANDLER,undefined,ErrorType.UNHANDLEDEXCEPTION);});globalThis.addEventListener('unhandledrejection',event=>{this.onError(event,ERROR_HANDLER,undefined,ErrorType.UNHANDLEDREJECTION);});}onError(error,context='',customMessage='',errorType=ErrorType.HANDLEDEXCEPTION){try{const errInstance=getErrInstance(error,errorType);const normalizedError=normalizeError(errInstance,this.logger);if(isUndefined(normalizedError)){return;}const customMsgVal=customMessage?`${customMessage} - `:'';const errorMsgPrefix=`${context}${LOG_CONTEXT_SEPARATOR}${customMsgVal}`;const bsException=createBugsnagException(normalizedError,errorMsgPrefix);const stacktrace=getStacktrace(normalizedError);const isSdkDispatched=stacktrace.includes(MANUAL_ERROR_IDENTIFIER);// Filter unhandled errors that are not originated in the SDK.
|
762
|
+
// However, in case of NPM installations, since we cannot differentiate between SDK and application errors, we should report all errors.
|
763
|
+
if(!isSDKError(bsException)&&state.context.app.value.installType!=='npm'&&!isSdkDispatched&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value&&isAllowedToBeNotified(bsException)){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};// enrich error payload
|
764
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state);// send it to metrics service
|
765
|
+
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state),sendRawData:true},isRawResponse:true});}// Log handled errors and errors dispatched by the SDK
|
766
|
+
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
767
|
+
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
685
768
|
* Add breadcrumbs to add insight of a user's journey before an error
|
686
769
|
* occurred and send to external error monitoring service via a plugin
|
687
770
|
*
|
688
771
|
* @param {string} breadcrumb breadcrumbs message
|
689
|
-
*/leaveBreadcrumb(breadcrumb){
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
772
|
+
*/leaveBreadcrumb(breadcrumb){try{state.reporting.breadcrumbs.value=[...state.reporting.breadcrumbs.value,createNewBreadcrumb(breadcrumb)];}catch(err){this.onError(err,BREADCRUMB_ERROR(ERROR_HANDLER));}}}const defaultErrorHandler=new ErrorHandler(defaultHttpClient,defaultLogger);
|
773
|
+
|
774
|
+
// to next or return the value if it is the last one instead of an array per
|
775
|
+
// plugin that is the normal invoke
|
776
|
+
// TODO: add invoke method for extension point that we know only one plugin can be used. add invokeMultiple and invokeSingle methods
|
777
|
+
class PluginEngine{plugins=[];byName={};cache={};config={throws:true};constructor(logger,options={}){this.config={throws:true,...options};this.logger=logger;}register(plugin,state){if(!plugin.name){const errorMessage=PLUGIN_NAME_MISSING_ERROR(PLUGIN_ENGINE);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage,plugin);return;}}if(this.byName[plugin.name]){const errorMessage=PLUGIN_ALREADY_EXISTS_ERROR(PLUGIN_ENGINE,plugin.name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage);return;}}this.cache={};this.plugins=this.plugins.slice();let pos=this.plugins.length;this.plugins.forEach((pluginItem,index)=>{if(pluginItem.deps?.includes(plugin.name)){pos=Math.min(pos,index);}});this.plugins.splice(pos,0,plugin);this.byName[plugin.name]=plugin;if(isFunction(plugin.initialize)){plugin.initialize(state);}}unregister(name){const plugin=this.byName[name];if(!plugin){const errorMessage=PLUGIN_NOT_FOUND_ERROR(PLUGIN_ENGINE,name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage);return;}}const index=this.plugins.indexOf(plugin);if(index===-1){const errorMessage=PLUGIN_ENGINE_BUG_ERROR(PLUGIN_ENGINE,name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger.error(errorMessage);return;}}this.cache={};delete this.byName[name];this.plugins=this.plugins.slice();this.plugins.splice(index,1);}getPlugin(name){return this.byName[name];}getPlugins(extPoint){const lifeCycleName=extPoint??'.';if(!this.cache[lifeCycleName]){this.cache[lifeCycleName]=this.plugins.filter(plugin=>{if(plugin.deps?.some(dependency=>!this.byName[dependency])){// If deps not exist, then not load it.
|
778
|
+
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
|
779
|
+
// do some unified pre-process before application starts.
|
780
|
+
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
|
781
|
+
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
|
782
|
+
if(throws){throw err;}else {this.logger.error(PLUGIN_INVOCATION_ERROR(PLUGIN_ENGINE,extensionPointName,plugin.name),err);}}return null;});}invokeSingle(extPoint,...args){return this.invoke(extPoint,false,...args)[0];}invokeMultiple(extPoint,...args){return this.invoke(extPoint,true,...args);}}const defaultPluginEngine=new PluginEngine(defaultLogger,{throws:true});
|
699
783
|
|
700
784
|
/**
|
701
785
|
* A function to filter and return non cloud mode destinations
|
@@ -712,8 +796,7 @@ destination.config.useNativeSDK===true);const isHybridModeDestination=destinatio
|
|
712
796
|
|
713
797
|
/**
|
714
798
|
* List of plugin names that are loaded as dynamic imports in modern builds
|
715
|
-
*/const pluginNamesList=['BeaconQueue','Bugsnag'
|
716
|
-
'CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ErrorReporting','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];
|
799
|
+
*/const pluginNamesList=['BeaconQueue','CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];const deprecatedPluginsList=['Bugsnag','ErrorReporting'];
|
717
800
|
|
718
801
|
const COOKIE_STORAGE='cookieStorage';const LOCAL_STORAGE='localStorage';const SESSION_STORAGE='sessionStorage';const MEMORY_STORAGE='memoryStorage';const NO_STORAGE='none';
|
719
802
|
|
@@ -771,10 +854,6 @@ const EVENT_PAYLOAD_SIZE_CHECK_FAIL_WARNING=(context,payloadSize,sizeLimit)=>`${
|
|
771
854
|
*/const getFinalEventForDeliveryMutator=(event,currentTime)=>{const finalEvent=clone(event);// Update sentAt timestamp to the latest timestamp
|
772
855
|
finalEvent.sentAt=currentTime;return finalEvent;};
|
773
856
|
|
774
|
-
const METRICS_PAYLOAD_VERSION='1';
|
775
|
-
|
776
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX];
|
777
|
-
|
778
857
|
const QueueStatuses={IN_PROGRESS:'inProgress',QUEUE:'queue',RECLAIM_START:'reclaimStart',RECLAIM_END:'reclaimEnd',ACK:'ack',BATCH_QUEUE:'batchQueue'};
|
779
858
|
|
780
859
|
const BEACON_PLUGIN_EVENTS_QUEUE_DEBUG=context=>`${context}${LOG_CONTEXT_SEPARATOR}Sending events to data plane.`;const BEACON_QUEUE_STRING_CONVERSION_FAILURE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to convert events batch object to string.`;const BEACON_QUEUE_BLOB_CONVERSION_FAILURE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to convert events batch object to Blob.`;const BEACON_QUEUE_SEND_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to send events batch data to the browser's beacon queue. The events will be dropped.`;const BEACON_QUEUE_DELIVERY_ERROR=url=>`Failed to send events batch data to the browser's beacon queue for URL ${url}.`;
|
@@ -810,7 +889,7 @@ const sortByTime=(a,b)=>a.time-b.time;const RETRY_QUEUE='RetryQueue';/**
|
|
810
889
|
let timerScaleFactor=Math.max(options.timerScaleFactor??MIN_TIMER_SCALE_FACTOR,MIN_TIMER_SCALE_FACTOR);// Limit the timer scale factor to the maximum value
|
811
890
|
timerScaleFactor=Math.min(timerScaleFactor,MAX_TIMER_SCALE_FACTOR);// painstakingly tuned. that's why they're not "easily" configurable
|
812
891
|
this.timeouts={ackTimer:Math.round(timerScaleFactor*DEFAULT_ACK_TIMER_MS),reclaimTimer:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMER_MS),reclaimTimeout:Math.round(timerScaleFactor*DEFAULT_RECLAIM_TIMEOUT_MS),reclaimWait:Math.round(timerScaleFactor*DEFAULT_RECLAIM_WAIT_MS)};this.schedule=new Schedule();this.processId='0';// Set up our empty queues
|
813
|
-
this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
|
892
|
+
this.store=this.storeManager.setStore({id:this.id,name:this.name,validKeys:QueueStatuses,type:storageType,errorHandler:this.storeManager.errorHandler,logger:this.storeManager.logger});this.setDefaultQueueEntries();// bind recurring tasks for ease of use
|
814
893
|
this.ack=this.ack.bind(this);this.checkReclaim=this.checkReclaim.bind(this);this.processHead=this.processHead.bind(this);this.flushBatch=this.flushBatch.bind(this);this.isPageAccessible=true;// Flush the queue on page leave
|
815
894
|
this.flushBatchOnPageLeave();this.scheduleTimeoutActive=false;}setDefaultQueueEntries(){this.setStorageEntry(QueueStatuses.IN_PROGRESS,{});this.setStorageEntry(QueueStatuses.QUEUE,[]);this.setStorageEntry(QueueStatuses.BATCH_QUEUE,[]);}configureBatchMode(options){this.batchingInProgress=false;if(!isObjectLiteralAndNotNull(options.batch)){return;}const batchOptions=options.batch;this.batch.enabled=batchOptions.enabled===true;if(this.batch.enabled){// Set upper cap on the batch payload size
|
816
895
|
this.batch.maxSize=Math.min(batchOptions.maxSize??DEFAULT_MAX_BATCH_SIZE_BYTES,DEFAULT_MAX_BATCH_SIZE_BYTES);this.batch.maxItems=batchOptions.maxItems??DEFAULT_MAX_BATCH_ITEMS;this.batch.flushInterval=batchOptions.flushInterval??DEFAULT_BATCH_FLUSH_INTERVAL_MS;}}flushBatchOnPageLeave(){if(this.batch.enabled){onPageLeave(this.flushBatch);}}getStorageEntry(name){return this.store.get(name);}// TODO: fix the type of different queues to be the same if possible
|
@@ -872,7 +951,7 @@ if(this.isPageAccessible){// Save this to the in progress map
|
|
872
951
|
inProgress[id]={item:el.item,attemptNumber:el.attemptNumber,time:this.schedule.now(),type:el.type};}enqueueItem(el,id);}}this.setStorageEntry(QueueStatuses.QUEUE,queue);this.setStorageEntry(QueueStatuses.IN_PROGRESS,inProgress);toRun.forEach(el=>{// TODO: handle processQueueCb timeout
|
873
952
|
try{const willBeRetried=this.shouldRetry(el.item,el.attemptNumber+1);this.processQueueCb(el.item,el.done,el.attemptNumber,this.maxAttempts,willBeRetried);}catch(err){this.logger?.error(RETRY_QUEUE_PROCESS_ERROR(RETRY_QUEUE),err);}});// re-read the queue in case the process function finished immediately or added another item
|
874
953
|
queue=this.getStorageEntry(QueueStatuses.QUEUE)??[];this.schedule.cancel(this.processId);if(queue.length>0){const nextProcessExecutionTime=queue[0].time-now;this.processId=this.schedule.run(this.processHead,nextProcessExecutionTime,ScheduleModes.ASAP);}}// Ack continuously to prevent other tabs from claiming our queue
|
875
|
-
ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {// Hack to determine the item type by the contents of the entry
|
954
|
+
ack(){this.setStorageEntry(QueueStatuses.ACK,this.schedule.now());if(this.reclaimStartVal!=null){this.reclaimStartVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_START,null);}if(this.reclaimEndVal!=null){this.reclaimEndVal=null;this.setStorageEntry(QueueStatuses.RECLAIM_END,null);}this.schedule.run(this.ack,this.timeouts.ackTimer,ScheduleModes.ASAP);}reclaim(id){const other=this.storeManager.setStore({id,name:this.name,validKeys:QueueStatuses,type:LOCAL_STORAGE,errorHandler:this.storeManager.errorHandler,logger:this.storeManager.logger});const our={queue:this.getStorageEntry(QueueStatuses.QUEUE)??[]};const their={inProgress:other.get(QueueStatuses.IN_PROGRESS)??{},batchQueue:other.get(QueueStatuses.BATCH_QUEUE)??[],queue:other.get(QueueStatuses.QUEUE)??[]};const trackMessageIds=[];const addConcatQueue=(queue,incrementAttemptNumberBy)=>{const concatIterator=el=>{const id=el.id??generateUUID();if(trackMessageIds.includes(id));else {// Hack to determine the item type by the contents of the entry
|
876
955
|
// After some point, we can remove this hack as most of the stale data will have been processed
|
877
956
|
// and the new entries will have the type field set
|
878
957
|
const type=Array.isArray(el.item)?BATCH_QUEUE_ITEM_TYPE:SINGLE_QUEUE_ITEM_TYPE;our.queue.push({item:el.item,attemptNumber:el.attemptNumber+incrementAttemptNumberBy,time:this.schedule.now(),id,type:el.type??type});trackMessageIds.push(id);}};if(Array.isArray(queue)){queue.forEach(concatIterator);}else if(queue){Object.values(queue).forEach(concatIterator);}};// add their queue to ours, resetting run-time to immediate and copying the attempt#
|
@@ -887,9 +966,9 @@ if(entryIdx+1<queueEntryKeys.length){this.removeStorageEntry(store,entryIdx+1,ba
|
|
887
966
|
this.removeStorageEntry(store,entryIdx,backoff+40,attempt+1);}else {this.logger?.error(RETRY_QUEUE_ENTRY_REMOVE_ERROR(RETRY_QUEUE,entry,attempt),err);}// clear the next entry after we've exhausted our attempts
|
888
967
|
if(attempt===maxAttempts&&entryIdx+1<queueEntryKeys.length){this.removeStorageEntry(store,entryIdx+1,backoff);}}},backoff);}checkReclaim(){const createReclaimStartTask=store=>()=>{if(store.get(QueueStatuses.RECLAIM_END)!==this.id){return;}if(store.get(QueueStatuses.RECLAIM_START)!==this.id){return;}this.reclaim(store.id);};const createReclaimEndTask=store=>()=>{if(store.get(QueueStatuses.RECLAIM_START)!==this.id){return;}store.set(QueueStatuses.RECLAIM_END,this.id);this.schedule.run(createReclaimStartTask(store),this.timeouts.reclaimWait,ScheduleModes.ABANDON);};const tryReclaim=store=>{store.set(QueueStatuses.RECLAIM_START,this.id);store.set(QueueStatuses.ACK,this.schedule.now());this.schedule.run(createReclaimEndTask(store),this.timeouts.reclaimWait,ScheduleModes.ABANDON);};const findOtherQueues=name=>{const res=[];const storageEngine=this.store.getOriginalEngine();let storageKeys=[];// 'keys' API is not supported by all the core SDK versions
|
889
968
|
// Hence, we need this backward compatibility check
|
890
|
-
if(isFunction(storageEngine.keys)){storageKeys=storageEngine.keys();}else {for(let i=0;i<storageEngine.length;i++){const key=storageEngine.key(i);if(key){storageKeys.push(key);}}}storageKeys.forEach(k=>{const keyParts=k?k.split('.'):[];if(keyParts.length>=3&&keyParts[0]===name&&keyParts[1]!==this.id&&keyParts[2]===QueueStatuses.ACK){res.push(this.storeManager.setStore({id:keyParts[1],name,validKeys:QueueStatuses,type:LOCAL_STORAGE}));}});return res;};findOtherQueues(this.name).forEach(store=>{if(this.schedule.now()-store.get(QueueStatuses.ACK)<this.timeouts.reclaimTimeout){return;}tryReclaim(store);});this.schedule.run(this.checkReclaim,this.timeouts.reclaimTimer,ScheduleModes.RESCHEDULE);}clear(){this.schedule.cancelAll();this.setDefaultQueueEntries();}}
|
969
|
+
if(isFunction(storageEngine.keys)){storageKeys=storageEngine.keys();}else {for(let i=0;i<storageEngine.length;i++){const key=storageEngine.key(i);if(key){storageKeys.push(key);}}}storageKeys.forEach(k=>{const keyParts=k?k.split('.'):[];if(keyParts.length>=3&&keyParts[0]===name&&keyParts[1]!==this.id&&keyParts[2]===QueueStatuses.ACK){res.push(this.storeManager.setStore({id:keyParts[1],name,validKeys:QueueStatuses,type:LOCAL_STORAGE,errorHandler:this.storeManager.errorHandler,logger:this.storeManager.logger}));}});return res;};findOtherQueues(this.name).forEach(store=>{if(this.schedule.now()-store.get(QueueStatuses.ACK)<this.timeouts.reclaimTimeout){return;}tryReclaim(store);});this.schedule.run(this.checkReclaim,this.timeouts.reclaimTimer,ScheduleModes.RESCHEDULE);}clear(){this.schedule.cancelAll();this.setDefaultQueueEntries();}}
|
891
970
|
|
892
|
-
const pluginName$
|
971
|
+
const pluginName$d='BeaconQueue';const BeaconQueue=()=>({name:pluginName$d,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$d];},dataplaneEventsQueue:{/**
|
893
972
|
* Initialize the queue for delivery
|
894
973
|
* @param state Application state
|
895
974
|
* @param httpClient http client instance
|
@@ -913,49 +992,11 @@ return getBatchDeliveryPayload$1(events,currentTime,logger).size;});return event
|
|
913
992
|
// It'll be updated to the latest timestamp during actual delivery
|
914
993
|
event.sentAt=getCurrentTimeFormatted();validateEventPayloadSize(event,logger);eventsQueue.addItem({event});}}});
|
915
994
|
|
916
|
-
const BUGSNAG_API_KEY_VALIDATION_ERROR=apiKey=>`The Bugsnag API key (${apiKey}) is invalid or not provided.`;const BUGSNAG_SDK_LOAD_TIMEOUT_ERROR=timeout=>`A timeout ${timeout} ms occurred while trying to load the Bugsnag SDK.`;const BUGSNAG_SDK_LOAD_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to load the Bugsnag SDK.`;const FAILED_TO_FILTER_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to filter the error.`;
|
917
|
-
|
918
|
-
const BUGSNAG_LIB_INSTANCE_GLOBAL_KEY_NAME='bugsnag';// For version 6 and below
|
919
|
-
const BUGSNAG_LIB_V7_INSTANCE_GLOBAL_KEY_NAME='Bugsnag';const GLOBAL_LIBRARY_OBJECT_NAMES=[BUGSNAG_LIB_V7_INSTANCE_GLOBAL_KEY_NAME,BUGSNAG_LIB_INSTANCE_GLOBAL_KEY_NAME];const BUGSNAG_CDN_URL='https://d2wy8f7a9ursnm.cloudfront.net/v6/bugsnag.min.js';const ERROR_REPORT_PROVIDER_NAME_BUGSNAG='rs-bugsnag';// This API key token is parsed in the CI pipeline
|
920
|
-
const API_KEY='0d96a60df267f4a13f808bbaa54e535c';const BUGSNAG_VALID_MAJOR_VERSION='6';const SDK_LOAD_POLL_INTERVAL_MS=100;// ms
|
921
|
-
const MAX_WAIT_FOR_SDK_LOAD_MS=100*SDK_LOAD_POLL_INTERVAL_MS;// ms
|
922
|
-
// Errors from the below scripts are NOT allowed to reach Bugsnag
|
923
|
-
const SDK_FILE_NAME_PREFIXES$1=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
|
924
|
-
];const DEV_HOSTS$1=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
|
925
|
-
// Potential PII or sensitive data
|
926
|
-
const APP_STATE_EXCLUDE_KEYS$1=['userId','userTraits','groupId','groupTraits','anonymousId','config','instance',// destination instance objects
|
927
|
-
'eventBuffer',// pre-load event buffer (may contain PII)
|
928
|
-
'traits','authToken'];const BUGSNAG_PLUGIN='BugsnagPlugin';
|
929
|
-
|
930
|
-
const isValidVersion=globalLibInstance=>{// For version 7
|
931
|
-
// eslint-disable-next-line no-underscore-dangle
|
932
|
-
let version=globalLibInstance?._client?._notifier?.version;// For versions older than 7
|
933
|
-
if(!version){const tempInstance=globalLibInstance({apiKey:API_KEY,releaseStage:'version-test',// eslint-disable-next-line func-names, object-shorthand
|
934
|
-
beforeSend:function(){return false;}});version=tempInstance.notifier?.version;}return version&&version.charAt(0)===BUGSNAG_VALID_MAJOR_VERSION;};const isRudderSDKError$1=event=>{const errorOrigin=event.stacktrace?.[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}// Prefix folder for all the destination SDK scripts
|
935
|
-
const isDestinationIntegrationBundle=errorOrigin.includes(CDN_INT_DIR);const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);return isDestinationIntegrationBundle||SDK_FILE_NAME_PREFIXES$1().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getAppStateForMetadata$1=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS$1);return stateStr!==null?JSON.parse(stateStr):undefined;};const enhanceErrorEventMutator=(state,event)=>{event.updateMetaData('source',{snippetVersion:globalThis.RudderSnippetVersion});event.updateMetaData('state',getAppStateForMetadata$1(state)??{});const{errorMessage}=event;// eslint-disable-next-line no-param-reassign
|
936
|
-
event.context=errorMessage;// Hack for easily grouping the script load errors
|
937
|
-
// on the dashboard
|
938
|
-
if(errorMessage.includes('error in script loading')){// eslint-disable-next-line no-param-reassign
|
939
|
-
event.context='Script load failures';}// eslint-disable-next-line no-param-reassign
|
940
|
-
event.severity='error';};const onError=(state,logger)=>event=>{try{// Discard the event if it's not originated at the SDK
|
941
|
-
if(!isRudderSDKError$1(event)){return false;}enhanceErrorEventMutator(state,event);return true;}catch{logger?.error(FAILED_TO_FILTER_ERROR(BUGSNAG_PLUGIN));// Drop the error event if it couldn't be filtered as
|
942
|
-
// it is most likely a non-SDK error
|
943
|
-
return false;}};const getReleaseStage$1=()=>{const host=globalThis.location.hostname;return host&&DEV_HOSTS$1.includes(host)?'development':'production';};const getGlobalBugsnagLibInstance=()=>globalThis[BUGSNAG_LIB_INSTANCE_GLOBAL_KEY_NAME];const getNewClient=(state,logger)=>{const globalBugsnagLibInstance=getGlobalBugsnagLibInstance();const clientConfig={apiKey:API_KEY,appVersion:state.context.app.value.version,metaData:{SDK:{name:'JS',installType:state.context.app.value.installType}},beforeSend:onError(state,logger),autoCaptureSessions:false,// auto capture sessions is disabled
|
944
|
-
collectUserIp:false,// collecting user's IP is disabled
|
945
|
-
// enabledBreadcrumbTypes: ['error', 'log', 'user'], // for v7 and above
|
946
|
-
maxEvents:100,maxBreadcrumbs:40,releaseStage:getReleaseStage$1(),user:{// Combination of source, session and visit ids
|
947
|
-
id:`${state.source.value?.id??state.lifecycle.writeKey.value}..${state.session.sessionInfo.value?.id??'NA'}..${state.autoTrack?.pageLifecycle?.visitId?.value??'NA'}`},logger,networkBreadcrumbsEnabled:false};const client=globalBugsnagLibInstance(clientConfig);return client;};const isApiKeyValid=apiKey=>{const isAPIKeyValid=!(apiKey.startsWith('{{')||apiKey.endsWith('}}')||apiKey.length===0);return isAPIKeyValid;};const loadBugsnagSDK=(externalSrcLoader,logger)=>{const isNotLoaded=GLOBAL_LIBRARY_OBJECT_NAMES.every(globalKeyName=>!globalThis[globalKeyName]);if(!isNotLoaded){return;}externalSrcLoader.loadJSFile({url:BUGSNAG_CDN_URL,id:ERROR_REPORT_PROVIDER_NAME_BUGSNAG,callback:id=>{if(!id){logger?.error(BUGSNAG_SDK_LOAD_ERROR(BUGSNAG_PLUGIN));}}});};const initBugsnagClient=(state,promiseResolve,promiseReject,logger,time=0)=>{const globalBugsnagLibInstance=getGlobalBugsnagLibInstance();if(typeof globalBugsnagLibInstance==='function'){if(isValidVersion(globalBugsnagLibInstance)){const client=getNewClient(state,logger);promiseResolve(client);}}else if(time>=MAX_WAIT_FOR_SDK_LOAD_MS){promiseReject(new Error(BUGSNAG_SDK_LOAD_TIMEOUT_ERROR(MAX_WAIT_FOR_SDK_LOAD_MS)));}else {// Try to initialize the client after a delay
|
948
|
-
globalThis.setTimeout(initBugsnagClient,SDK_LOAD_POLL_INTERVAL_MS,state,promiseResolve,promiseReject,logger,time+SDK_LOAD_POLL_INTERVAL_MS);}};
|
949
|
-
|
950
|
-
const pluginName$e='Bugsnag';const Bugsnag=()=>({name:pluginName$e,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$e];},errorReportingProvider:{init:(state,externalSrcLoader,logger)=>new Promise((resolve,reject)=>{// If API key token is not parsed or invalid, don't proceed to initialize the client
|
951
|
-
if(!isApiKeyValid(API_KEY)){reject(new Error(BUGSNAG_API_KEY_VALIDATION_ERROR(API_KEY)));return;}// If SDK URL is empty, don't proceed to initialize the client
|
952
|
-
loadBugsnagSDK(externalSrcLoader,logger);initBugsnagClient(state,resolve,reject,logger);}),notify:(client,error,state,logger)=>{client.notify(error);},breadcrumb:(client,message,logger)=>{client?.leaveBreadcrumb(message);}}});
|
953
|
-
|
954
995
|
const CUSTOM_CONSENT_MANAGER_PLUGIN='CustomConsentManagerPlugin';
|
955
996
|
|
956
997
|
const DESTINATION_CONSENT_STATUS_ERROR$3=`Failed to determine the consent status for the destination. Please check the destination configuration and try again.`;
|
957
998
|
|
958
|
-
const pluginName$
|
999
|
+
const pluginName$c='CustomConsentManager';const CustomConsentManager=()=>({name:pluginName$c,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$c];},consentManager:{init(state,logger){// Nothing to initialize
|
959
1000
|
},updateConsentsInfo(state,storeManager,logger){// Nothing to update. Already provided by the user
|
960
1001
|
},isDestinationConsented(state,destConfig,errorHandler,logger){if(!state.consents.initialized.value){return true;}const allowedConsentIds=state.consents.data.value.allowedConsentIds;try{const{consentManagement}=destConfig;// If the destination does not have consent management config, events should be sent.
|
961
1002
|
if(!consentManagement){return true;}// Get the corresponding consents for the destination
|
@@ -1160,13 +1201,13 @@ const destDisplayNamesToFileNamesMap={[HS_DISPLAY_NAME]:DIR_NAME$W,[GA_DISPLAY_N
|
|
1160
1201
|
if(isHybridModeDestination(initializedDestination)){state.nativeDestinations.integrationsConfig.value=getCumulativeIntegrationsConfig(initializedDestination,state.nativeDestinations.integrationsConfig.value,errorHandler);}state.nativeDestinations.initializedDestinations.value=[...state.nativeDestinations.initializedDestinations.value,initializedDestination];}).catch(err=>{state.nativeDestinations.failedDestinations.value=[...state.nativeDestinations.failedDestinations.value,dest];// The error message is already formatted in the isDestinationReady function
|
1161
1202
|
logger?.error(err);});}catch(err){state.nativeDestinations.failedDestinations.value=[...state.nativeDestinations.failedDestinations.value,dest];errorHandler?.onError(err,DEVICE_MODE_DESTINATIONS_PLUGIN,DESTINATION_INIT_ERROR(dest.userFriendlyId));}};
|
1162
1203
|
|
1163
|
-
const pluginName$
|
1204
|
+
const pluginName$b='DeviceModeDestinations';const DeviceModeDestinations=()=>({name:pluginName$b,initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$b];},nativeDestinations:{setActiveDestinations(state,pluginsManager,errorHandler,logger){state.nativeDestinations.loadIntegration.value=state.loadOptions.value.loadIntegration;// Filter destination that doesn't have mapping config-->Integration names
|
1164
1205
|
const configSupportedDestinations=state.nativeDestinations.configuredDestinations.value.filter(configDest=>{if(destDisplayNamesToFileNamesMap[configDest.displayName]){return true;}errorHandler?.onError(new Error(DESTINATION_NOT_SUPPORTED_ERROR(configDest.userFriendlyId)),DEVICE_MODE_DESTINATIONS_PLUGIN);return false;});// Filter destinations that are disabled through load or consent API options
|
1165
1206
|
const destinationsToLoad=filterDestinations(state.consents.postConsent.value?.integrations??state.nativeDestinations.loadOnlyIntegrations.value,configSupportedDestinations);const consentedDestinations=destinationsToLoad.filter(dest=>// if consent manager is not configured, then default to load the destination
|
1166
1207
|
pluginsManager.invokeSingle(`consentManager.isDestinationConsented`,state,dest.config,errorHandler,logger)??true);state.nativeDestinations.activeDestinations.value=consentedDestinations;},load(state,externalSrcLoader,errorHandler,logger,externalScriptOnLoad){const integrationsCDNPath=state.lifecycle.integrationsCDNPath.value;const activeDestinations=state.nativeDestinations.activeDestinations.value;activeDestinations.forEach(dest=>{const sdkName=destDisplayNamesToFileNamesMap[dest.displayName];const destSDKIdentifier=`${sdkName}_RS`;// this is the name of the object loaded on the window
|
1167
1208
|
const sdkTypeName=sdkName;if(sdkTypeName&&!isDestinationSDKMounted(destSDKIdentifier,sdkTypeName)){const destSdkURL=`${integrationsCDNPath}/${sdkName}.min.js`;externalSrcLoader.loadJSFile({url:destSdkURL,id:dest.userFriendlyId,callback:externalScriptOnLoad??(id=>{if(!id){logger?.error(DESTINATION_SDK_LOAD_ERROR(DEVICE_MODE_DESTINATIONS_PLUGIN,dest.userFriendlyId));state.nativeDestinations.failedDestinations.value=[...state.nativeDestinations.failedDestinations.value,dest];}else {initializeDestination(dest,state,destSDKIdentifier,sdkTypeName,errorHandler,logger);}}),timeout:SCRIPT_LOAD_TIMEOUT_MS});}else if(sdkTypeName){initializeDestination(dest,state,destSDKIdentifier,sdkTypeName,errorHandler,logger);}else {logger?.error(DESTINATION_SDK_LOAD_ERROR(DEVICE_MODE_DESTINATIONS_PLUGIN,dest.displayName));}});}}});
|
1168
1209
|
|
1169
|
-
const DEFAULT_TRANSFORMATION_QUEUE_OPTIONS={minRetryDelay:500,backoffFactor:2,maxAttempts:3};const REQUEST_TIMEOUT_MS$
|
1210
|
+
const DEFAULT_TRANSFORMATION_QUEUE_OPTIONS={minRetryDelay:500,backoffFactor:2,maxAttempts:3};const REQUEST_TIMEOUT_MS$1=10*1000;// 10 seconds
|
1170
1211
|
const QUEUE_NAME$2='rudder';const DMT_PLUGIN='DeviceModeTransformationPlugin';
|
1171
1212
|
|
1172
1213
|
const DMT_TRANSFORMATION_UNSUCCESSFUL_ERROR=(context,displayName,reason,action)=>`${context}${LOG_CONTEXT_SEPARATOR}Event transformation unsuccessful for destination "${displayName}". Reason: ${reason}. ${action}.`;const DMT_REQUEST_FAILED_ERROR=(context,displayName,status,action)=>`${context}${LOG_CONTEXT_SEPARATOR}[Destination: ${displayName}].Transformation request failed with status: ${status}. Retries exhausted. ${action}.`;const DMT_EXCEPTION=displayName=>`[Destination:${displayName}].`;const DMT_SERVER_ACCESS_DENIED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}Transformation server access is denied. The configuration data seems to be out of sync. Sending untransformed event to the destination.`;
|
@@ -1178,88 +1219,10 @@ const DMT_TRANSFORMATION_UNSUCCESSFUL_ERROR=(context,displayName,reason,action)=
|
|
1178
1219
|
*/const createPayload=(event,destinationIds,token)=>{const orderNo=Date.now();const payload={metadata:{'Custom-Authorization':token},batch:[{orderNo,destinationIds,event}]};return payload;};const sendTransformedEventToDestinations=(state,pluginsManager,destinationIds,result,status,event,errorHandler,logger)=>{const NATIVE_DEST_EXT_POINT='destinationsEventsQueue.enqueueEventToDestination';const ACTION_TO_SEND_UNTRANSFORMED_EVENT='Sending untransformed event';const ACTION_TO_DROP_EVENT='Dropping the event';const destinations=state.nativeDestinations.initializedDestinations.value.filter(d=>d&&destinationIds.includes(d.id));destinations.forEach(dest=>{try{const eventsToSend=[];switch(status){case 200:{const response=JSON.parse(result);const destTransformedResult=response.transformedBatch.find(e=>e.id===dest.id);destTransformedResult?.payload.forEach(tEvent=>{if(tEvent.status==='200'){eventsToSend.push(tEvent.event);}else {let reason='Unknown';if(tEvent.status==='410'){reason='Transformation is not available';}let action=ACTION_TO_DROP_EVENT;if(dest.propagateEventsUntransformedOnError===true){action=ACTION_TO_SEND_UNTRANSFORMED_EVENT;eventsToSend.push(event);logger?.warn(DMT_TRANSFORMATION_UNSUCCESSFUL_ERROR(DMT_PLUGIN,dest.displayName,reason,action));}else {logger?.error(DMT_TRANSFORMATION_UNSUCCESSFUL_ERROR(DMT_PLUGIN,dest.displayName,reason,action));}}});break;}// Transformation server access denied
|
1179
1220
|
case 404:{logger?.warn(DMT_SERVER_ACCESS_DENIED_WARNING(DMT_PLUGIN));eventsToSend.push(event);break;}default:{if(dest.propagateEventsUntransformedOnError===true){logger?.warn(DMT_REQUEST_FAILED_ERROR(DMT_PLUGIN,dest.displayName,status,ACTION_TO_SEND_UNTRANSFORMED_EVENT));eventsToSend.push(event);}else {logger?.error(DMT_REQUEST_FAILED_ERROR(DMT_PLUGIN,dest.displayName,status,ACTION_TO_DROP_EVENT));}break;}}eventsToSend?.forEach(tEvent=>{if(isNonEmptyObject(tEvent)){pluginsManager.invokeSingle(NATIVE_DEST_EXT_POINT,state,tEvent,dest,errorHandler,logger);}});}catch(e){errorHandler?.onError(e,DMT_PLUGIN,DMT_EXCEPTION(dest.displayName));}});};
|
1180
1221
|
|
1181
|
-
const pluginName$
|
1182
|
-
`${QUEUE_NAME$2}_${writeKey}`,DEFAULT_TRANSFORMATION_QUEUE_OPTIONS,(item,done,attemptNumber,maxRetryAttempts)=>{const payload=createPayload(item.event,item.destinationIds,item.token);httpClient.getAsyncData({url:`${state.lifecycle.activeDataplaneUrl.value}/transform`,options:{method:'POST',data:getDMTDeliveryPayload(payload),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$
|
1222
|
+
const pluginName$a='DeviceModeTransformation';const DeviceModeTransformation=()=>({name:pluginName$a,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$a];},transformEvent:{init(state,pluginsManager,httpClient,storeManager,errorHandler,logger){const writeKey=state.lifecycle.writeKey.value;httpClient.setAuthHeader(writeKey);const eventsQueue=new RetryQueue(// adding write key to the queue name to avoid conflicts
|
1223
|
+
`${QUEUE_NAME$2}_${writeKey}`,DEFAULT_TRANSFORMATION_QUEUE_OPTIONS,(item,done,attemptNumber,maxRetryAttempts)=>{const payload=createPayload(item.event,item.destinationIds,item.token);httpClient.getAsyncData({url:`${state.lifecycle.activeDataplaneUrl.value}/transform`,options:{method:'POST',data:getDMTDeliveryPayload(payload),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$1,callback:(result,details)=>{// null means item will not be requeued
|
1183
1224
|
const queueErrResp=isErrRetryable(details)?details:null;if(!queueErrResp||attemptNumber===maxRetryAttempts){sendTransformedEventToDestinations(state,pluginsManager,item.destinationIds,result,details?.xhr?.status,item.event,errorHandler,logger);}done(queueErrResp,result);}});},storeManager,MEMORY_STORAGE);return eventsQueue;},enqueue(state,eventsQueue,event,destinations){const destinationIds=destinations.map(d=>d.id);eventsQueue.addItem({event,destinationIds,token:state.session.authToken.value});}}});
|
1184
1225
|
|
1185
|
-
// Errors from the below scripts are NOT allowed to reach Bugsnag
|
1186
|
-
const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
|
1187
|
-
];const DEV_HOSTS=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
|
1188
|
-
// Potential PII or sensitive data
|
1189
|
-
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','instance',// destination instance objects
|
1190
|
-
'eventBuffer',// pre-load event buffer (may contain PII)
|
1191
|
-
'traits','authToken'];const REQUEST_TIMEOUT_MS$1=10*1000;// 10 seconds
|
1192
|
-
const NOTIFIER_NAME='RudderStack JavaScript SDK Error Notifier';const SDK_GITHUB_URL='https://github.com/rudderlabs/rudder-sdk-js';const SOURCE_NAME='js';const ERROR_REPORTING_PLUGIN='ErrorReportingPlugin';
|
1193
|
-
|
1194
|
-
const getConfigForPayloadCreation=(err,errorType)=>{switch(errorType){case ErrorType.UNHANDLEDEXCEPTION:{const{error}=err;return {component:'unhandledException handler',normalizedError:error||err};}case ErrorType.UNHANDLEDREJECTION:{const error=err;return {component:'unhandledrejection handler',normalizedError:error.reason};}case ErrorType.HANDLEDEXCEPTION:default:return {component:'notify()',normalizedError:err};}};const createNewBreadcrumb=(message,metaData)=>({type:'manual',name:message,timestamp:new Date(),metaData:metaData??{}});const getReleaseStage=()=>{const host=globalThis.location.hostname;return host&&DEV_HOSTS.includes(host)?'development':'production';};const getAppStateForMetadata=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS);return stateStr!==null?JSON.parse(stateStr):{};};const getURLWithoutQueryString=()=>{const url=globalThis.location.href.split('?');return url[0];};const getErrorContext=event=>{const{message}=event;let context=message;// Hack for easily grouping the script load errors
|
1195
|
-
// on the dashboard
|
1196
|
-
if(message.includes('Error in loading a third-party script')){context='Script load failures';}return context;};const getBugsnagErrorEvent=(payload,errorState,state)=>({notifier:{name:NOTIFIER_NAME,version:state.context.app.value.version,url:SDK_GITHUB_URL},events:[{payloadVersion:'5',exceptions:clone(payload.errors),severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:state.context.app.value.version,releaseStage:getReleaseStage()},device:{locale:state.context.locale.value??undefined,userAgent:state.context.userAgent.value??undefined,time:new Date()},request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(state.reporting.breadcrumbs.value),context:getErrorContext(payload.errors[0]),metaData:{sdk:{name:'JS',installType:state.context.app.value.installType},state:getAppStateForMetadata(state)??{},source:{snippetVersion:globalThis.RudderSnippetVersion}},user:{// Combination of source, session and visit ids
|
1197
|
-
id:`${state.source.value?.id??state.lifecycle.writeKey.value}..${state.session.sessionInfo.value?.id??'NA'}..${state.autoTrack?.pageLifecycle?.visitId?.value??'NA'}`}}]});/**
|
1198
|
-
* A function to determine whether the error should be promoted to notify or not
|
1199
|
-
* @param {Error} error
|
1200
|
-
* @returns
|
1201
|
-
*/const isAllowedToBeNotified=event=>{const errorMessage=event.message;if(errorMessage&&typeof errorMessage==='string'){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>errorMessage.includes(e));}return true;};/**
|
1202
|
-
* A function to determine if the error is from Rudder SDK
|
1203
|
-
* @param {Error} event
|
1204
|
-
* @returns
|
1205
|
-
*/const isRudderSDKError=event=>{const errorOrigin=event.stacktrace?.[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const paths=errorOrigin.split('/');// extract the parent folder name from the error origin file path
|
1206
|
-
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
1207
|
-
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType},errors:payload};return stringifyWithoutCircular(data);};
|
1208
|
-
|
1209
|
-
function getDefaultExportFromCjs (x) {
|
1210
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
1211
|
-
}
|
1212
|
-
|
1213
|
-
var errorStackParser$1 = {exports: {}};
|
1214
|
-
|
1215
|
-
var stackframe$1 = {exports: {}};
|
1216
|
-
|
1217
|
-
var stackframe=stackframe$1.exports;var hasRequiredStackframe;function requireStackframe(){if(hasRequiredStackframe)return stackframe$1.exports;hasRequiredStackframe=1;(function(module,exports){(function(root,factory){/* istanbul ignore next */{module.exports=factory();}})(stackframe,function(){function _isNumber(n){return !isNaN(parseFloat(n))&&isFinite(n);}function _capitalize(str){return str.charAt(0).toUpperCase()+str.substring(1);}function _getter(p){return function(){return this[p];};}var booleanProps=['isConstructor','isEval','isNative','isToplevel'];var numericProps=['columnNumber','lineNumber'];var stringProps=['fileName','functionName','source'];var arrayProps=['args'];var objectProps=['evalOrigin'];var props=booleanProps.concat(numericProps,stringProps,arrayProps,objectProps);function StackFrame(obj){if(!obj)return;for(var i=0;i<props.length;i++){if(obj[props[i]]!==undefined){this['set'+_capitalize(props[i])](obj[props[i]]);}}}StackFrame.prototype={getArgs:function(){return this.args;},setArgs:function(v){if(Object.prototype.toString.call(v)!=='[object Array]'){throw new TypeError('Args must be an Array');}this.args=v;},getEvalOrigin:function(){return this.evalOrigin;},setEvalOrigin:function(v){if(v instanceof StackFrame){this.evalOrigin=v;}else if(v instanceof Object){this.evalOrigin=new StackFrame(v);}else {throw new TypeError('Eval Origin must be an Object or StackFrame');}},toString:function(){var fileName=this.getFileName()||'';var lineNumber=this.getLineNumber()||'';var columnNumber=this.getColumnNumber()||'';var functionName=this.getFunctionName()||'';if(this.getIsEval()){if(fileName){return '[eval] ('+fileName+':'+lineNumber+':'+columnNumber+')';}return '[eval]:'+lineNumber+':'+columnNumber;}if(functionName){return functionName+' ('+fileName+':'+lineNumber+':'+columnNumber+')';}return fileName+':'+lineNumber+':'+columnNumber;}};StackFrame.fromString=function StackFrame$$fromString(str){var argsStartIndex=str.indexOf('(');var argsEndIndex=str.lastIndexOf(')');var functionName=str.substring(0,argsStartIndex);var args=str.substring(argsStartIndex+1,argsEndIndex).split(',');var locationString=str.substring(argsEndIndex+1);if(locationString.indexOf('@')===0){var parts=/@(.+?)(?::(\d+))?(?::(\d+))?$/.exec(locationString,'');var fileName=parts[1];var lineNumber=parts[2];var columnNumber=parts[3];}return new StackFrame({functionName:functionName,args:args||undefined,fileName:fileName,lineNumber:lineNumber||undefined,columnNumber:columnNumber||undefined});};for(var i=0;i<booleanProps.length;i++){StackFrame.prototype['get'+_capitalize(booleanProps[i])]=_getter(booleanProps[i]);StackFrame.prototype['set'+_capitalize(booleanProps[i])]=function(p){return function(v){this[p]=Boolean(v);};}(booleanProps[i]);}for(var j=0;j<numericProps.length;j++){StackFrame.prototype['get'+_capitalize(numericProps[j])]=_getter(numericProps[j]);StackFrame.prototype['set'+_capitalize(numericProps[j])]=function(p){return function(v){if(!_isNumber(v)){throw new TypeError(p+' must be a Number');}this[p]=Number(v);};}(numericProps[j]);}for(var k=0;k<stringProps.length;k++){StackFrame.prototype['get'+_capitalize(stringProps[k])]=_getter(stringProps[k]);StackFrame.prototype['set'+_capitalize(stringProps[k])]=function(p){return function(v){this[p]=String(v);};}(stringProps[k]);}return StackFrame;});})(stackframe$1);return stackframe$1.exports;}
|
1218
|
-
|
1219
|
-
var errorStackParser=errorStackParser$1.exports;var hasRequiredErrorStackParser;function requireErrorStackParser(){if(hasRequiredErrorStackParser)return errorStackParser$1.exports;hasRequiredErrorStackParser=1;(function(module,exports){(function(root,factory){/* istanbul ignore next */{module.exports=factory(requireStackframe());}})(errorStackParser,function ErrorStackParser(StackFrame){var FIREFOX_SAFARI_STACK_REGEXP=/(^|@)\S+:\d+/;var CHROME_IE_STACK_REGEXP=/^\s*at .*(\S+:\d+|\(native\))/m;var SAFARI_NATIVE_CODE_REGEXP=/^(eval@)?(\[native code])?$/;return {/**
|
1220
|
-
* Given an Error object, extract the most information from it.
|
1221
|
-
*
|
1222
|
-
* @param {Error} error object
|
1223
|
-
* @return {Array} of StackFrames
|
1224
|
-
*/parse:function ErrorStackParser$$parse(error){if(typeof error.stacktrace!=='undefined'||typeof error['opera#sourceloc']!=='undefined'){return this.parseOpera(error);}else if(error.stack&&error.stack.match(CHROME_IE_STACK_REGEXP)){return this.parseV8OrIE(error);}else if(error.stack){return this.parseFFOrSafari(error);}else {throw new Error('Cannot parse given Error object');}},// Separate line and column numbers from a string of the form: (URI:Line:Column)
|
1225
|
-
extractLocation:function ErrorStackParser$$extractLocation(urlLike){// Fail-fast but return locations like "(native)"
|
1226
|
-
if(urlLike.indexOf(':')===-1){return [urlLike];}var regExp=/(.+?)(?::(\d+))?(?::(\d+))?$/;var parts=regExp.exec(urlLike.replace(/[()]/g,''));return [parts[1],parts[2]||undefined,parts[3]||undefined];},parseV8OrIE:function ErrorStackParser$$parseV8OrIE(error){var filtered=error.stack.split('\n').filter(function(line){return !!line.match(CHROME_IE_STACK_REGEXP);},this);return filtered.map(function(line){if(line.indexOf('(eval ')>-1){// Throw away eval information until we implement stacktrace.js/stackframe#8
|
1227
|
-
line=line.replace(/eval code/g,'eval').replace(/(\(eval at [^()]*)|(,.*$)/g,'');}var sanitizedLine=line.replace(/^\s+/,'').replace(/\(eval code/g,'(').replace(/^.*?\s+/,'');// capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
|
1228
|
-
// case it has spaces in it, as the string is split on \s+ later on
|
1229
|
-
var location=sanitizedLine.match(/ (\(.+\)$)/);// remove the parenthesized location from the line, if it was matched
|
1230
|
-
sanitizedLine=location?sanitizedLine.replace(location[0],''):sanitizedLine;// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
|
1231
|
-
// because this line doesn't have function name
|
1232
|
-
var locationParts=this.extractLocation(location?location[1]:sanitizedLine);var functionName=location&&sanitizedLine||undefined;var fileName=['eval','<anonymous>'].indexOf(locationParts[0])>-1?undefined:locationParts[0];return new StackFrame({functionName:functionName,fileName:fileName,lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});},this);},parseFFOrSafari:function ErrorStackParser$$parseFFOrSafari(error){var filtered=error.stack.split('\n').filter(function(line){return !line.match(SAFARI_NATIVE_CODE_REGEXP);},this);return filtered.map(function(line){// Throw away eval information until we implement stacktrace.js/stackframe#8
|
1233
|
-
if(line.indexOf(' > eval')>-1){line=line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,':$1');}if(line.indexOf('@')===-1&&line.indexOf(':')===-1){// Safari eval frames only have function names and nothing else
|
1234
|
-
return new StackFrame({functionName:line});}else {var functionNameRegex=/((.*".+"[^@]*)?[^@]*)(?:@)/;var matches=line.match(functionNameRegex);var functionName=matches&&matches[1]?matches[1]:undefined;var locationParts=this.extractLocation(line.replace(functionNameRegex,''));return new StackFrame({functionName:functionName,fileName:locationParts[0],lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});}},this);},parseOpera:function ErrorStackParser$$parseOpera(e){if(!e.stacktrace||e.message.indexOf('\n')>-1&&e.message.split('\n').length>e.stacktrace.split('\n').length){return this.parseOpera9(e);}else if(!e.stack){return this.parseOpera10(e);}else {return this.parseOpera11(e);}},parseOpera9:function ErrorStackParser$$parseOpera9(e){var lineRE=/Line (\d+).*script (?:in )?(\S+)/i;var lines=e.message.split('\n');var result=[];for(var i=2,len=lines.length;i<len;i+=2){var match=lineRE.exec(lines[i]);if(match){result.push(new StackFrame({fileName:match[2],lineNumber:match[1],source:lines[i]}));}}return result;},parseOpera10:function ErrorStackParser$$parseOpera10(e){var lineRE=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;var lines=e.stacktrace.split('\n');var result=[];for(var i=0,len=lines.length;i<len;i+=2){var match=lineRE.exec(lines[i]);if(match){result.push(new StackFrame({functionName:match[3]||undefined,fileName:match[2],lineNumber:match[1],source:lines[i]}));}}return result;},// Opera 10.65+ Error.stack very similar to FF/Safari
|
1235
|
-
parseOpera11:function ErrorStackParser$$parseOpera11(error){var filtered=error.stack.split('\n').filter(function(line){return !!line.match(FIREFOX_SAFARI_STACK_REGEXP)&&!line.match(/^Error created at/);},this);return filtered.map(function(line){var tokens=line.split('@');var locationParts=this.extractLocation(tokens.pop());var functionCall=tokens.shift()||'';var functionName=functionCall.replace(/<anonymous function(: (\w+))?>/,'$2').replace(/\([^)]*\)/g,'')||undefined;var argsRaw;if(functionCall.match(/\(([^)]*)\)/)){argsRaw=functionCall.replace(/^[^(]+\(([^)]*)\)$/,'$1');}var args=argsRaw===undefined||argsRaw==='[arguments not available]'?undefined:argsRaw.split(',');return new StackFrame({functionName:functionName,args:args,fileName:locationParts[0],lineNumber:locationParts[1],columnNumber:locationParts[2],source:line});},this);}};});})(errorStackParser$1);return errorStackParser$1.exports;}
|
1236
|
-
|
1237
|
-
var errorStackParserExports = requireErrorStackParser();
|
1238
|
-
const ErrorStackParser = /*@__PURE__*/getDefaultExportFromCjs(errorStackParserExports);
|
1239
|
-
|
1240
|
-
const hasStack=err=>!!err&&(!!err.stack||!!err.stacktrace||!!err['opera#sourceloc'])&&typeof(err.stack||err.stacktrace||err['opera#sourceloc'])==='string'&&err.stack!==`${err.name}: ${err.message}`;const isError=value=>{switch(Object.prototype.toString.call(value)){case '[object Error]':case '[object Exception]':case '[object DOMException]':return true;default:return value instanceof Error;}};
|
1241
|
-
|
1242
|
-
const normaliseFunctionName=name=>/^global code$/i.test(name)?'global code':name;// takes a stacktrace.js style stackframe (https://github.com/stacktracejs/stackframe)
|
1243
|
-
// and returns a Bugsnag compatible stackframe (https://docs.bugsnag.com/api/error-reporting/#json-payload)
|
1244
|
-
const formatStackframe=frame=>{const f={file:frame.fileName,method:normaliseFunctionName(frame.functionName),lineNumber:frame.lineNumber,columnNumber:frame.columnNumber,code:undefined,inProject:undefined};// Some instances result in no file:
|
1245
|
-
// - calling notify() from chrome's terminal results in no file/method.
|
1246
|
-
// - non-error exception thrown from global code in FF
|
1247
|
-
// This adds one.
|
1248
|
-
if(f.lineNumber>-1&&!f.file&&!f.method){f.file='global code';}return f;};const ensureString=str=>typeof str==='string'?str:'';function createBugsnagError(errorClass,errorMessage,stacktrace){return {errorClass:ensureString(errorClass),message:ensureString(errorMessage),type:'browserjs',stacktrace:stacktrace.reduce((accum,frame)=>{const f=formatStackframe(frame);// don't include a stackframe if none of its properties are defined
|
1249
|
-
try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch(e){return accum;}},[])};}// Helpers
|
1250
|
-
const getStacktrace=error=>{if(hasStack(error))return ErrorStackParser.parse(error);return [];};const normaliseError=(maybeError,component,logger)=>{let error;if(isError(maybeError)){error=maybeError;}else {logger?.warn(`${ERROR_REPORTING_PLUGIN}:: ${component} received a non-error: ${stringifyWithoutCircular(error)}`);error=undefined;}if(error&&!hasStack(error)){error=undefined;}return error;};class ErrorFormat{constructor(errorClass,errorMessage,stacktrace){this.errors=[createBugsnagError(errorClass,errorMessage,stacktrace)];}static create(maybeError,component,logger){const error=normaliseError(maybeError,component,logger);if(!error){return undefined;}let event;try{const stacktrace=getStacktrace(error);event=new ErrorFormat(error.name,error.message,stacktrace);}catch(e){event=new ErrorFormat(error.name,error.message,[]);}return event;}}
|
1251
|
-
|
1252
|
-
const INVALID_SOURCE_CONFIG_ERROR=`Invalid source configuration or source id.`;
|
1253
|
-
|
1254
|
-
const pluginName$a='ErrorReporting';const ErrorReporting=()=>({name:pluginName$a,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$a];state.reporting.isErrorReportingPluginLoaded.value=true;if(state.reporting.breadcrumbs?.value){state.reporting.breadcrumbs.value=[createNewBreadcrumb('Error Reporting Plugin Loaded')];}},errorReporting:{// This extension point is deprecated
|
1255
|
-
// TODO: Remove this in the next major release
|
1256
|
-
init:(state,pluginEngine,externalSrcLoader,logger,isInvokedFromLatestCore)=>{if(isInvokedFromLatestCore){return undefined;}if(!state.source.value?.config||!state.source.value?.id){return Promise.reject(new Error(INVALID_SOURCE_CONFIG_ERROR));}return pluginEngine.invokeSingle('errorReportingProvider.init',state,externalSrcLoader,logger);},notify:(pluginEngine,client,error,state,logger,httpClient,errorState)=>{if(httpClient){const{component,normalizedError}=getConfigForPayloadCreation(error,errorState?.severityReason.type);// Generate the error payload
|
1257
|
-
const errorPayload=ErrorFormat.create(normalizedError,component,logger);if(!errorPayload||!isAllowedToBeNotified(errorPayload.errors[0])){return;}// filter errors
|
1258
|
-
if(!isRudderSDKError(errorPayload.errors[0])){return;}// enrich error payload
|
1259
|
-
const bugsnagPayload=getBugsnagErrorEvent(errorPayload,errorState,state);// send it to metrics service
|
1260
|
-
httpClient?.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$1,callback:(result,details)=>{// do nothing
|
1261
|
-
}});}else {pluginEngine.invokeSingle('errorReportingProvider.notify',client,error,state,logger);}},breadcrumb:(pluginEngine,client,message,logger,state,metaData)=>{if(state){state.reporting.breadcrumbs.value=[...state.reporting.breadcrumbs.value,createNewBreadcrumb(message,metaData)];}else {pluginEngine.invokeSingle('errorReportingProvider.breadcrumb',client,message,logger);}}}});
|
1262
|
-
|
1263
1226
|
const externallyLoadedSessionStorageKeys={segment:'ajs_anonymous_id'};
|
1264
1227
|
|
1265
1228
|
const getSegmentAnonymousId=getStorageEngine=>{let anonymousId;/**
|
@@ -1373,7 +1336,7 @@ const[name]=matchedCookie.split('=');return name||'';}catch(err){logger?.error(I
|
|
1373
1336
|
* @param storeManager Store manager instance
|
1374
1337
|
* @param logger Logger instance
|
1375
1338
|
* @returns Consent data from the consent cookie
|
1376
|
-
*/const getIubendaConsentData=(storeManager,logger)=>{let rawConsentCookieData=null;try{const dataStore=storeManager?.setStore({id:IUBENDA_CONSENT_MANAGER_PLUGIN,name:IUBENDA_CONSENT_MANAGER_PLUGIN,type:COOKIE_STORAGE});rawConsentCookieData=dataStore?.engine.getItem(getIubendaCookieName(logger));}catch(err){logger?.error(IUBENDA_CONSENT_COOKIE_READ_ERROR(IUBENDA_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(isNullOrUndefined(rawConsentCookieData)){return undefined;}// Decode and parse the cookie data to JSON
|
1339
|
+
*/const getIubendaConsentData=(storeManager,logger)=>{let rawConsentCookieData=null;try{const dataStore=storeManager?.setStore({id:IUBENDA_CONSENT_MANAGER_PLUGIN,name:IUBENDA_CONSENT_MANAGER_PLUGIN,type:COOKIE_STORAGE,errorHandler:storeManager?.errorHandler,logger:storeManager?.logger});rawConsentCookieData=dataStore?.engine.getItem(getIubendaCookieName(logger));}catch(err){logger?.error(IUBENDA_CONSENT_COOKIE_READ_ERROR(IUBENDA_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(isNullOrUndefined(rawConsentCookieData)){return undefined;}// Decode and parse the cookie data to JSON
|
1377
1340
|
let consentCookieData;try{consentCookieData=JSON.parse(decodeURIComponent(rawConsentCookieData));}catch(err){logger?.error(IUBENDA_CONSENT_COOKIE_PARSE_ERROR(IUBENDA_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(!consentCookieData){return undefined;}// Convert the cookie data to consent data
|
1378
1341
|
const consentPurposes=consentCookieData.purposes;return consentPurposes;};/**
|
1379
1342
|
* Gets the consent data in the format expected by the application state
|
@@ -1406,7 +1369,7 @@ const KETCH_CONSENT_MANAGER_PLUGIN='KetchConsentManagerPlugin';const KETCH_CONSE
|
|
1406
1369
|
* @param logger Logger instance
|
1407
1370
|
* @returns Consent data from the consent cookie
|
1408
1371
|
*/const getKetchConsentData=(storeManager,logger)=>{let rawConsentCookieData=null;try{// Create a data store instance to read the consent cookie
|
1409
|
-
const dataStore=storeManager?.setStore({id:KETCH_CONSENT_MANAGER_PLUGIN,name:KETCH_CONSENT_MANAGER_PLUGIN,type:COOKIE_STORAGE});rawConsentCookieData=dataStore?.engine.getItem(KETCH_CONSENT_COOKIE_NAME_V1);}catch(err){logger?.error(KETCH_CONSENT_COOKIE_READ_ERROR(KETCH_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(isNullOrUndefined(rawConsentCookieData)){return undefined;}// Decode and parse the cookie data to JSON
|
1372
|
+
const dataStore=storeManager?.setStore({id:KETCH_CONSENT_MANAGER_PLUGIN,name:KETCH_CONSENT_MANAGER_PLUGIN,type:COOKIE_STORAGE,errorHandler:storeManager?.errorHandler,logger:storeManager?.logger});rawConsentCookieData=dataStore?.engine.getItem(KETCH_CONSENT_COOKIE_NAME_V1);}catch(err){logger?.error(KETCH_CONSENT_COOKIE_READ_ERROR(KETCH_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(isNullOrUndefined(rawConsentCookieData)){return undefined;}// Decode and parse the cookie data to JSON
|
1410
1373
|
let consentCookieData;try{consentCookieData=JSON.parse(fromBase64(rawConsentCookieData));}catch(err){logger?.error(KETCH_CONSENT_COOKIE_PARSE_ERROR(KETCH_CONSENT_MANAGER_PLUGIN),err);return undefined;}if(!consentCookieData){return undefined;}// Convert the cookie data to consent data
|
1411
1374
|
const consentPurposes={};Object.entries(consentCookieData).forEach(pEntry=>{const purposeCode=pEntry[0];const purposeValue=pEntry[1];consentPurposes[purposeCode]=purposeValue?.status==='granted';});return consentPurposes;};/**
|
1412
1375
|
* Gets the consent data in the format expected by the application state
|
@@ -2519,7 +2482,7 @@ AnonymousId:toBase64(event.anonymousId)};eventsQueue.addItem({url,headers,event}
|
|
2519
2482
|
|
2520
2483
|
/**
|
2521
2484
|
* Map plugin names to direct code imports from plugins package
|
2522
|
-
*/const getBundledBuildPluginImports=()=>({BeaconQueue,
|
2485
|
+
*/const getBundledBuildPluginImports=()=>({BeaconQueue,CustomConsentManager,DeviceModeDestinations,DeviceModeTransformation,ExternalAnonymousId,GoogleLinker,IubendaConsentManager,KetchConsentManager,NativeDestinationQueue,OneTrustConsentManager,StorageEncryption,StorageEncryptionLegacy,StorageMigrator,XhrQueue});
|
2523
2486
|
|
2524
2487
|
/**
|
2525
2488
|
* Map of mandatory plugin names and direct imports
|
@@ -2543,21 +2506,12 @@ state.lifecycle.status.value='pluginsReady';});}});}/**
|
|
2543
2506
|
* Determine the list of plugins that should be loaded based on sourceConfig & load options
|
2544
2507
|
*/// eslint-disable-next-line class-methods-use-this
|
2545
2508
|
getPluginsToLoadBasedOnConfig(){// This contains the default plugins if load option has been omitted by user
|
2546
|
-
let pluginsToLoadFromConfig=state.plugins.pluginsToLoadFromConfig.value;if(!pluginsToLoadFromConfig){return [];}//
|
2547
|
-
//
|
2548
|
-
|
2549
|
-
// if (deprecatedPluginsList.includes(pluginName)) {
|
2550
|
-
// this.logger?.warn(DEPRECATED_PLUGIN_WARNING(PLUGINS_MANAGER, pluginName));
|
2551
|
-
// return false;
|
2552
|
-
// }
|
2553
|
-
// return true;
|
2554
|
-
// });
|
2555
|
-
const pluginGroupsToProcess=[{configurationStatus:()=>isDefined(state.dataPlaneEvents.eventsQueuePluginName.value),configurationStatusStr:'Data plane events delivery is enabled',activePluginName:state.dataPlaneEvents.eventsQueuePluginName.value,supportedPlugins:Object.values(DataPlaneEventsTransportToPluginNameMap),shouldAddMissingPlugins:true},{configurationStatus:()=>state.reporting.isErrorReportingEnabled.value,configurationStatusStr:'Error reporting is enabled',supportedPlugins:['ErrorReporting','Bugsnag']// TODO: Remove deprecated plugin- Bugsnag
|
2556
|
-
},{configurationStatus:()=>getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value).length>0,configurationStatusStr:'Device mode destinations are connected to the source',supportedPlugins:['DeviceModeDestinations','NativeDestinationQueue']},{configurationStatus:()=>getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value).some(destination=>destination.shouldApplyDeviceModeTransformation),configurationStatusStr:'Device mode transformations are enabled for at least one destination',supportedPlugins:['DeviceModeTransformation']},{configurationStatus:()=>isDefined(state.consents.activeConsentManagerPluginName.value),configurationStatusStr:'Consent management is enabled',activePluginName:state.consents.activeConsentManagerPluginName.value,supportedPlugins:Object.values(ConsentManagersToPluginNameMap)},{configurationStatus:()=>isDefined(state.storage.encryptionPluginName.value),configurationStatusStr:'Storage encryption is enabled',activePluginName:state.storage.encryptionPluginName.value,supportedPlugins:Object.values(StorageEncryptionVersionsToPluginNameMap)},{configurationStatus:()=>state.storage.migrate.value,configurationStatusStr:'Storage migration is enabled',supportedPlugins:['StorageMigrator']}];const addMissingPlugins=false;pluginGroupsToProcess.forEach(group=>{if(group.configurationStatus()){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(group.activePluginName?pluginName=>!(pluginName!==group.activePluginName&&group.supportedPlugins.includes(pluginName)):pluginName=>isDefined(pluginName)// pass through
|
2557
|
-
);this.addMissingPlugins(group,addMissingPlugins,pluginsToLoadFromConfig);}else {pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(group.basePlugins!==undefined?pluginName=>!(group.basePlugins.includes(pluginName)||group.supportedPlugins.includes(pluginName)):pluginName=>!group.supportedPlugins.includes(pluginName));}});return [...Object.keys(getMandatoryPluginsMap()),...pluginsToLoadFromConfig];}addMissingPlugins(group,addMissingPlugins,pluginsToLoadFromConfig){const shouldAddMissingPlugins=group.shouldAddMissingPlugins||addMissingPlugins;let pluginsToConfigure;if(group.activePluginName){pluginsToConfigure=[...(group.basePlugins||[]),group.activePluginName];}else {pluginsToConfigure=[...group.supportedPlugins];}const missingPlugins=pluginsToConfigure.filter(pluginName=>!pluginsToLoadFromConfig.includes(pluginName));if(missingPlugins.length>0){if(shouldAddMissingPlugins){pluginsToLoadFromConfig.push(...missingPlugins);}this.logger?.warn(generateMisconfiguredPluginsWarning(PLUGINS_MANAGER,group.configurationStatusStr,missingPlugins,shouldAddMissingPlugins));}}/**
|
2509
|
+
let pluginsToLoadFromConfig=state.plugins.pluginsToLoadFromConfig.value;if(!pluginsToLoadFromConfig){return [];}// Filter deprecated plugins
|
2510
|
+
pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(pluginName=>{if(deprecatedPluginsList.includes(pluginName)){this.logger.warn(DEPRECATED_PLUGIN_WARNING(PLUGINS_MANAGER,pluginName));return false;}return true;});const pluginGroupsToProcess=[{configurationStatus:()=>isDefined(state.dataPlaneEvents.eventsQueuePluginName.value),configurationStatusStr:'Data plane events delivery is enabled',activePluginName:state.dataPlaneEvents.eventsQueuePluginName.value,supportedPlugins:Object.values(DataPlaneEventsTransportToPluginNameMap),shouldAddMissingPlugins:true},{configurationStatus:()=>getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value).length>0,configurationStatusStr:'Device mode destinations are connected to the source',supportedPlugins:['DeviceModeDestinations','NativeDestinationQueue']},{configurationStatus:()=>getNonCloudDestinations(state.nativeDestinations.configuredDestinations.value).some(destination=>destination.shouldApplyDeviceModeTransformation),configurationStatusStr:'Device mode transformations are enabled for at least one destination',supportedPlugins:['DeviceModeTransformation']},{configurationStatus:()=>isDefined(state.consents.activeConsentManagerPluginName.value),configurationStatusStr:'Consent management is enabled',activePluginName:state.consents.activeConsentManagerPluginName.value,supportedPlugins:Object.values(ConsentManagersToPluginNameMap)},{configurationStatus:()=>isDefined(state.storage.encryptionPluginName.value),configurationStatusStr:'Storage encryption is enabled',activePluginName:state.storage.encryptionPluginName.value,supportedPlugins:Object.values(StorageEncryptionVersionsToPluginNameMap)},{configurationStatus:()=>state.storage.migrate.value,configurationStatusStr:'Storage migration is enabled',supportedPlugins:['StorageMigrator']}];const addMissingPlugins=false;pluginGroupsToProcess.forEach(group=>{if(group.configurationStatus()){pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(group.activePluginName?pluginName=>!(pluginName!==group.activePluginName&&group.supportedPlugins.includes(pluginName)):pluginName=>isDefined(pluginName)// pass through
|
2511
|
+
);this.addMissingPlugins(group,addMissingPlugins,pluginsToLoadFromConfig);}else {pluginsToLoadFromConfig=pluginsToLoadFromConfig.filter(group.basePlugins!==undefined?pluginName=>!(group.basePlugins.includes(pluginName)||group.supportedPlugins.includes(pluginName)):pluginName=>!group.supportedPlugins.includes(pluginName));}});return [...Object.keys(getMandatoryPluginsMap()),...pluginsToLoadFromConfig];}addMissingPlugins(group,addMissingPlugins,pluginsToLoadFromConfig){const shouldAddMissingPlugins=group.shouldAddMissingPlugins||addMissingPlugins;let pluginsToConfigure;if(group.activePluginName){pluginsToConfigure=[...(group.basePlugins||[]),group.activePluginName];}else {pluginsToConfigure=[...group.supportedPlugins];}const missingPlugins=pluginsToConfigure.filter(pluginName=>!pluginsToLoadFromConfig.includes(pluginName));if(missingPlugins.length>0){if(shouldAddMissingPlugins){pluginsToLoadFromConfig.push(...missingPlugins);}this.logger.warn(generateMisconfiguredPluginsWarning(PLUGINS_MANAGER,group.configurationStatusStr,missingPlugins,shouldAddMissingPlugins));}}/**
|
2558
2512
|
* Determine the list of plugins that should be activated
|
2559
2513
|
*/setActivePlugins(){const pluginsToLoad=this.getPluginsToLoadBasedOnConfig();// Merging available mandatory and optional plugin name list
|
2560
|
-
const availablePlugins=[...Object.keys(pluginsInventory),...pluginNamesList];const activePlugins=[];const failedPlugins=[];pluginsToLoad.forEach(pluginName=>{if(availablePlugins.includes(pluginName)){activePlugins.push(pluginName);}else {failedPlugins.push(pluginName);}});if(failedPlugins.length>0){this.
|
2514
|
+
const availablePlugins=[...Object.keys(pluginsInventory),...pluginNamesList];const activePlugins=[];const failedPlugins=[];pluginsToLoad.forEach(pluginName=>{if(availablePlugins.includes(pluginName)){activePlugins.push(pluginName);}else {failedPlugins.push(pluginName);}});if(failedPlugins.length>0){this.logger.warn(UNKNOWN_PLUGINS_WARNING(PLUGINS_MANAGER,failedPlugins));}r(()=>{state.plugins.totalPluginsToLoad.value=pluginsToLoad.length;state.plugins.activePlugins.value=activePlugins;state.plugins.failedPlugins.value=failedPlugins;});}/**
|
2561
2515
|
* Register plugins that are direct imports to PluginEngine
|
2562
2516
|
*/registerLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{if(isFunction(localPlugin)&&state.plugins.activePlugins.value.includes(localPlugin().name)){this.register([localPlugin()]);}});}/**
|
2563
2517
|
* Register plugins that are dynamic imports to PluginEngine
|
@@ -2571,48 +2525,18 @@ state.plugins.failedPlugins.value=[...state.plugins.failedPlugins.value,remotePl
|
|
2571
2525
|
*/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
|
2572
2526
|
unregisterLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{try{this.engine.unregister(localPlugin().name);}catch(e){this.onError(e);}});}/**
|
2573
2527
|
* Handle errors
|
2574
|
-
*/onError(error,customMessage){
|
2575
|
-
|
2576
|
-
/**
|
2577
|
-
* Utility to parse XHR JSON response
|
2578
|
-
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');if(isFunction(onError)){onError(error);}else {throw error;}}return undefined;};
|
2579
|
-
|
2580
|
-
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
2581
|
-
* Utility to create request configuration based on default options
|
2582
|
-
*/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;};/**
|
2583
|
-
* Utility implementation of XHR, fetch cannot be used as it requires explicit
|
2584
|
-
* origin allowed values and not wildcard for CORS requests with credentials and
|
2585
|
-
* this is not supported by our sourceConfig API
|
2586
|
-
*/const xhrRequest=(options,timeout=DEFAULT_XHR_TIMEOUT_MS,logger)=>new Promise((resolve,reject)=>{let payload;if(options.sendRawData===true){payload=options.data;}else {payload=stringifyWithoutCircular(options.data,false,[],logger);if(isNull(payload)){reject({error:new Error(XHR_PAYLOAD_PREP_ERROR),undefined,options});// return and don't process further if the payload could not be stringified
|
2587
|
-
return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
2588
|
-
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,true);if(options.withCredentials===true){xhr.withCredentials=true;}// The timeout property may be set only in the time interval between a call to the open method
|
2589
|
-
// and the first call to the send method in legacy browsers
|
2590
|
-
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});}});
|
2591
|
-
|
2592
|
-
/**
|
2593
|
-
* Service to handle data communication with APIs
|
2594
|
-
*/class HttpClient{hasErrorHandler=false;constructor(errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.hasErrorHandler=Boolean(this.errorHandler);this.onError=this.onError.bind(this);}/**
|
2595
|
-
* Implement requests in a blocking way
|
2596
|
-
*/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};}}/**
|
2597
|
-
* Implement requests in a non-blocking way
|
2598
|
-
*/getAsyncData(config){const{callback,url,options,timeout,isRawResponse}=config;const isFireAndForget=!isFunction(callback);xhrRequest(createXhrRequestOptions(url,options,this.basicAuthHeader),timeout,this.logger).then(data=>{if(!isFireAndForget){callback(isRawResponse?data.response:responseTextToJson(data.response,this.onError),data);}}).catch(data=>{this.onError(data.error??data);if(!isFireAndForget){callback(undefined,data);}});}/**
|
2599
|
-
* Handle errors
|
2600
|
-
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,HTTP_CLIENT);}else {throw error;}}/**
|
2601
|
-
* Set basic authentication header (eg writekey)
|
2602
|
-
*/setAuthHeader(value,noBtoa=false){const authVal=noBtoa?value:toBase64(`${value}:`);this.basicAuthHeader=`Basic ${authVal}`;}/**
|
2603
|
-
* Clear basic authentication header
|
2604
|
-
*/resetAuthHeader(){this.basicAuthHeader=undefined;}}const defaultHttpClient=new HttpClient(defaultErrorHandler,defaultLogger);
|
2528
|
+
*/onError(error,customMessage){this.errorHandler.onError(error,PLUGINS_MANAGER,customMessage);}}
|
2605
2529
|
|
2606
2530
|
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_COOKIE='clientDataInCookie';const CLIENT_DATA_STORE_LS='clientDataInLocalStorage';const CLIENT_DATA_STORE_MEMORY='clientDataInMemory';const CLIENT_DATA_STORE_SESSION='clientDataInSessionStorage';const USER_SESSION_KEYS=['userId','userTraits','anonymousId','groupId','groupTraits','initialReferrer','initialReferringDomain','sessionInfo','authToken'];
|
2607
2531
|
|
2608
2532
|
const storageClientDataStoreNameMap={[COOKIE_STORAGE]:CLIENT_DATA_STORE_COOKIE,[LOCAL_STORAGE]:CLIENT_DATA_STORE_LS,[MEMORY_STORAGE]:CLIENT_DATA_STORE_MEMORY,[SESSION_STORAGE]:CLIENT_DATA_STORE_SESSION};
|
2609
2533
|
|
2610
|
-
const detectAdBlockers=
|
2534
|
+
const detectAdBlockers=httpClient=>{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
2611
2535
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
2612
2536
|
// Only the extra query param should make it vulnerable to adblockers
|
2613
2537
|
// This will work even if the users proxies it.
|
2614
2538
|
// The edge case where this doesn't work is when HEAD method is not allowed by the server (user's)
|
2615
|
-
const baseUrl=new URL(state.lifecycle.sourceConfigUrl.value);const url=`${baseUrl.origin}${baseUrl.pathname}?view=ad`;
|
2539
|
+
const baseUrl=new URL(state.lifecycle.sourceConfigUrl.value);const url=`${baseUrl.origin}${baseUrl.pathname}?view=ad`;httpClient.getAsyncData({url,options:{// We actually don't need the response from the request, so we are using HEAD
|
2616
2540
|
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
|
2617
2541
|
// Often adblockers instead of blocking the request, they redirect it to an internal URL
|
2618
2542
|
state.capabilities.isAdBlocked.value=details?.error!==undefined||details?.xhr?.responseURL!==url;}});};
|
@@ -2722,7 +2646,7 @@ if(this.isSupportAvailable){this.store=globalThis.sessionStorage;}this.isEnabled
|
|
2722
2646
|
|
2723
2647
|
/**
|
2724
2648
|
* Store Implementation with dedicated storage
|
2725
|
-
*/class Store{
|
2649
|
+
*/class Store{constructor(config,engine,pluginsManager){this.id=config.id;this.name=config.name;this.isEncrypted=config.isEncrypted??false;this.validKeys=config.validKeys??{};this.engine=engine??getStorageEngine(LOCAL_STORAGE);this.noKeyValidation=Object.keys(this.validKeys).length===0;this.noCompoundKey=config.noCompoundKey;this.originalEngine=this.engine;this.errorHandler=config.errorHandler;this.logger=config.logger;this.pluginsManager=pluginsManager;}/**
|
2726
2650
|
* Ensure the key is valid and with correct format
|
2727
2651
|
*/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
|
2728
2652
|
let compoundKey;Object.values(validKeys).forEach(validKeyName=>{if(validKeyName===key){compoundKey=noCompoundKey?key:[name,id,key].join('.');}});return compoundKey;}/**
|
@@ -2735,13 +2659,13 @@ Object.keys(validKeys).forEach(key=>{const value=this.get(validKeys[key]);const
|
|
2735
2659
|
this.remove(key);});this.engine=inMemoryStorage;}/**
|
2736
2660
|
* Set value by key.
|
2737
2661
|
*/set(key,value){const validKey=this.createValidKey(key);if(!validKey){return;}try{// storejs that is used in localstorage engine already stringifies json
|
2738
|
-
this.engine.setItem(validKey,this.encrypt(stringifyWithoutCircular(value,false,[],this.logger)));}catch(err){if(isStorageQuotaExceeded(err)){this.logger
|
2662
|
+
this.engine.setItem(validKey,this.encrypt(stringifyWithoutCircular(value,false,[],this.logger)));}catch(err){if(isStorageQuotaExceeded(err)){this.logger.warn(STORAGE_QUOTA_EXCEEDED_WARNING(`Store ${this.id}`));// switch to inMemory engine
|
2739
2663
|
this.swapQueueStoreToInMemoryEngine();// and save it there
|
2740
2664
|
this.set(key,value);}else {this.onError(getMutatedError(err,STORE_DATA_SAVE_ERROR(key)));}}}/**
|
2741
2665
|
* Get by Key.
|
2742
2666
|
*/get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
2743
2667
|
return JSON.parse(decryptedValue);}catch(err){this.onError(new Error(`${STORE_DATA_FETCH_ERROR(key)}: ${err.message}`));// A hack for warning the users of potential partial SDK version migrations
|
2744
|
-
if(isString(decryptedValue)&&decryptedValue.startsWith('RudderEncrypt:')){this.logger
|
2668
|
+
if(isString(decryptedValue)&&decryptedValue.startsWith('RudderEncrypt:')){this.logger.warn(BAD_COOKIES_WARNING(key));}return null;}}/**
|
2745
2669
|
* Remove by Key.
|
2746
2670
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
2747
2671
|
* Get original engine
|
@@ -2753,13 +2677,13 @@ if(isString(decryptedValue)&&decryptedValue.startsWith('RudderEncrypt:')){this.l
|
|
2753
2677
|
* Extension point to use with encryption plugins
|
2754
2678
|
*/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??'';}/**
|
2755
2679
|
* Handle errors
|
2756
|
-
*/onError(error){
|
2680
|
+
*/onError(error){this.errorHandler.onError(error,`Store ${this.id}`);}}
|
2757
2681
|
|
2758
2682
|
const getStorageTypeFromPreConsentIfApplicable=(state,sessionKey)=>{let overriddenStorageType;if(state.consents.preConsent.value.enabled){switch(state.consents.preConsent.value.storage?.strategy){case 'none':overriddenStorageType=NO_STORAGE;break;case 'session':if(sessionKey!=='sessionInfo'){overriddenStorageType=NO_STORAGE;}break;case 'anonymousId':if(sessionKey!=='anonymousId'){overriddenStorageType=NO_STORAGE;}break;}}return overriddenStorageType;};
|
2759
2683
|
|
2760
2684
|
/**
|
2761
2685
|
* A service to manage stores & available storage client configurations
|
2762
|
-
*/class StoreManager{stores={};isInitialized=false;
|
2686
|
+
*/class StoreManager{stores={};isInitialized=false;constructor(pluginsManager,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.pluginsManager=pluginsManager;}/**
|
2763
2687
|
* Configure available storage client instances
|
2764
2688
|
*/init(){if(this.isInitialized){return;}const loadOptions=state.loadOptions.value;const config={cookieStorageOptions:{samesite:loadOptions.sameSiteCookie,secure:loadOptions.secureCookie,domain:loadOptions.setCookieDomain,sameDomainCookiesOnly:loadOptions.sameDomainCookiesOnly,enabled:true},localStorageOptions:{enabled:true},inMemoryStorageOptions:{enabled:true},sessionStorageOptions:{enabled:true}};configureStorageEngines(removeUndefinedValues(mergeDeepRight(config.cookieStorageOptions,state.storage.cookie?.value??{})),removeUndefinedValues(config.localStorageOptions),removeUndefinedValues(config.inMemoryStorageOptions),removeUndefinedValues(config.sessionStorageOptions));this.initClientDataStores();this.isInitialized=true;}/**
|
2765
2689
|
* Create store to persist data used by the SDK like session, used details etc
|
@@ -2767,16 +2691,14 @@ const getStorageTypeFromPreConsentIfApplicable=(state,sessionKey)=>{let overridd
|
|
2767
2691
|
// TODO: should we pass the keys for all in order to validate or leave free as v1.1?
|
2768
2692
|
// Initializing all the enabled store because previous user data might be in different storage
|
2769
2693
|
// that needs auto migration
|
2770
|
-
const storageTypes=[MEMORY_STORAGE,LOCAL_STORAGE,COOKIE_STORAGE,SESSION_STORAGE];storageTypes.forEach(storageType=>{if(getStorageEngine(storageType)?.isEnabled){this.setStore({id:storageClientDataStoreNameMap[storageType],name:storageClientDataStoreNameMap[storageType],isEncrypted:true,noCompoundKey:true,type:storageType});}});}initializeStorageState(){let globalStorageType=state.storage.type.value;let entriesOptions=state.loadOptions.value.storage?.entries;// Use the storage options from post consent if anything is defined
|
2694
|
+
const storageTypes=[MEMORY_STORAGE,LOCAL_STORAGE,COOKIE_STORAGE,SESSION_STORAGE];storageTypes.forEach(storageType=>{if(getStorageEngine(storageType)?.isEnabled){this.setStore({id:storageClientDataStoreNameMap[storageType],name:storageClientDataStoreNameMap[storageType],isEncrypted:true,noCompoundKey:true,type:storageType,errorHandler:this.errorHandler,logger:this.logger});}});}initializeStorageState(){let globalStorageType=state.storage.type.value;let entriesOptions=state.loadOptions.value.storage?.entries;// Use the storage options from post consent if anything is defined
|
2771
2695
|
const postConsentStorageOpts=state.consents.postConsent.value.storage;if(isDefined(postConsentStorageOpts?.type)||isDefined(postConsentStorageOpts?.entries)){globalStorageType=postConsentStorageOpts?.type;entriesOptions=postConsentStorageOpts?.entries;}let trulyAnonymousTracking=true;let storageEntries={};USER_SESSION_KEYS.forEach(sessionKey=>{const key=sessionKey;const storageKey=sessionKey;const configuredStorageType=entriesOptions?.[key]?.type;const preConsentStorageType=getStorageTypeFromPreConsentIfApplicable(state,sessionKey);// Storage type precedence order: pre-consent strategy > entry type > global type > default
|
2772
2696
|
const storageType=preConsentStorageType??configuredStorageType??globalStorageType??DEFAULT_STORAGE_TYPE;const finalStorageType=this.getResolvedStorageTypeForEntry(storageType,sessionKey);if(finalStorageType!==NO_STORAGE){trulyAnonymousTracking=false;}storageEntries={...storageEntries,[sessionKey]:{type:finalStorageType,key:COOKIE_KEYS[storageKey]}};});r(()=>{state.storage.type.value=globalStorageType;state.storage.entries.value=storageEntries;state.storage.trulyAnonymousTracking.value=trulyAnonymousTracking;});}getResolvedStorageTypeForEntry(storageType,sessionKey){let finalStorageType=storageType;switch(storageType){case LOCAL_STORAGE:if(!getStorageEngine(LOCAL_STORAGE)?.isEnabled){finalStorageType=MEMORY_STORAGE;}break;case SESSION_STORAGE:if(!getStorageEngine(SESSION_STORAGE)?.isEnabled){finalStorageType=MEMORY_STORAGE;}break;case MEMORY_STORAGE:case NO_STORAGE:break;case COOKIE_STORAGE:default:// First try setting the storage to cookie else to local storage
|
2773
|
-
if(getStorageEngine(COOKIE_STORAGE)?.isEnabled){finalStorageType=COOKIE_STORAGE;}else if(getStorageEngine(LOCAL_STORAGE)?.isEnabled){finalStorageType=LOCAL_STORAGE;}else if(getStorageEngine(SESSION_STORAGE)?.isEnabled){finalStorageType=SESSION_STORAGE;}else {finalStorageType=MEMORY_STORAGE;}break;}if(finalStorageType!==storageType){this.logger
|
2697
|
+
if(getStorageEngine(COOKIE_STORAGE)?.isEnabled){finalStorageType=COOKIE_STORAGE;}else if(getStorageEngine(LOCAL_STORAGE)?.isEnabled){finalStorageType=LOCAL_STORAGE;}else if(getStorageEngine(SESSION_STORAGE)?.isEnabled){finalStorageType=SESSION_STORAGE;}else {finalStorageType=MEMORY_STORAGE;}break;}if(finalStorageType!==storageType){this.logger.warn(STORAGE_UNAVAILABLE_WARNING(STORE_MANAGER,sessionKey,storageType,finalStorageType));}return finalStorageType;}/**
|
2774
2698
|
* Create a new store
|
2775
2699
|
*/setStore(storeConfig){const storageEngine=getStorageEngine(storeConfig.type);this.stores[storeConfig.id]=new Store(storeConfig,storageEngine,this.pluginsManager);return this.stores[storeConfig.id];}/**
|
2776
2700
|
* Retrieve a store
|
2777
|
-
*/getStore(id){return this.stores[id];}
|
2778
|
-
* Handle errors
|
2779
|
-
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,STORE_MANAGER);}else {throw error;}}}
|
2701
|
+
*/getStore(id){return this.stores[id];}}
|
2780
2702
|
|
2781
2703
|
const isValidSourceConfig=res=>isObjectLiteralAndNotNull(res)&&isObjectLiteralAndNotNull(res.source)&&!isNullOrUndefined(res.source.id)&&isObjectLiteralAndNotNull(res.source.config)&&Array.isArray(res.source.destinations);const isValidStorageType=storageType=>typeof storageType==='string'&&SUPPORTED_STORAGE_TYPES.includes(storageType);const getTopDomain=url=>{// Create a URL object
|
2782
2704
|
const urlObj=new URL(url);// Extract the host and protocol
|
@@ -2837,7 +2759,7 @@ validOptions.consentManagement=mergeDeepRight(clonedOptions.consentManagement,{e
|
|
2837
2759
|
* @param consentManagementOpts consent management options
|
2838
2760
|
* @param logger logger instance
|
2839
2761
|
* @returns Corresponding provider and plugin name of the selected consent manager from the supported consent managers
|
2840
|
-
*/const getConsentManagerInfo=(consentManagementOpts,logger)=>{let{provider}=consentManagementOpts;const consentManagerPluginName=provider?ConsentManagersToPluginNameMap[provider]:undefined;if(provider&&!consentManagerPluginName){logger
|
2762
|
+
*/const getConsentManagerInfo=(consentManagementOpts,logger)=>{let{provider}=consentManagementOpts;const consentManagerPluginName=provider?ConsentManagersToPluginNameMap[provider]:undefined;if(provider&&!consentManagerPluginName){logger.error(UNSUPPORTED_CONSENT_MANAGER_ERROR(CONFIG_MANAGER,provider,ConsentManagersToPluginNameMap));// Reset the provider value
|
2841
2763
|
provider=undefined;}return {provider,consentManagerPluginName};};/**
|
2842
2764
|
* Validates and converts the consent management options into a normalized format
|
2843
2765
|
* @param consentManagementOpts Consent management options provided by the user
|
@@ -2873,10 +2795,10 @@ if(curHost!==dataServiceHost){cookieOptions={...cookieOptions,samesite:'None',se
|
|
2873
2795
|
* If the sameDomainCookiesOnly flag is not set and the cookie domain is provided(not top level domain),
|
2874
2796
|
* and the data service host is different from the provided cookie domain, then we disable server-side cookies
|
2875
2797
|
* ex: provided cookie domain: 'random.com', data service host: 'sub.example.com'
|
2876
|
-
*/if(!sameDomainCookiesOnly&&useExactDomain&&dataServiceHost!==removeLeadingPeriod(providedCookieDomain)){sscEnabled=false;logger
|
2877
|
-
logger
|
2878
|
-
const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger
|
2879
|
-
const preConsentOpts=state.loadOptions.value.preConsent;let storageStrategy=preConsentOpts?.storage?.strategy??DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;const StorageStrategies=['none','session','anonymousId'];if(isDefined(storageStrategy)&&!StorageStrategies.includes(storageStrategy)){storageStrategy=DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;logger
|
2798
|
+
*/if(!sameDomainCookiesOnly&&useExactDomain&&dataServiceHost!==removeLeadingPeriod(providedCookieDomain)){sscEnabled=false;logger.warn(SERVER_SIDE_COOKIE_FEATURE_OVERRIDE_WARNING(CONFIG_MANAGER,providedCookieDomain,dataServiceHost));}}else {sscEnabled=false;}}return {sscEnabled,cookieOptions,finalDataServiceUrl};};const updateStorageStateFromLoadOptions=logger=>{const{storage:storageOptsFromLoad}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
|
2799
|
+
logger.warn(UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING(CONFIG_MANAGER,storageEncryptionVersion,StorageEncryptionVersionsToPluginNameMap,DEFAULT_STORAGE_ENCRYPTION_VERSION));storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}else if(isUndefined(storageEncryptionVersion)){storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}// Allow migration only if the configured encryption version is the default encryption version
|
2800
|
+
const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}const{sscEnabled,finalDataServiceUrl,cookieOptions}=getServerSideCookiesStateData(logger);r(()=>{state.storage.type.value=storageType;state.storage.cookie.value=cookieOptions;state.serverCookies.isEnabledServerSideCookies.value=sscEnabled;state.serverCookies.dataServiceUrl.value=finalDataServiceUrl;state.storage.encryptionPluginName.value=StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];state.storage.migrate.value=finalMigrationVal;});};const updateConsentsStateFromLoadOptions=logger=>{const{provider,consentManagerPluginName,initialized,enabled,consentsData}=getConsentManagementData(state.loadOptions.value.consentManagement,logger);// Pre-consent
|
2801
|
+
const preConsentOpts=state.loadOptions.value.preConsent;let storageStrategy=preConsentOpts?.storage?.strategy??DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;const StorageStrategies=['none','session','anonymousId'];if(isDefined(storageStrategy)&&!StorageStrategies.includes(storageStrategy)){storageStrategy=DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;logger.warn(UNSUPPORTED_PRE_CONSENT_STORAGE_STRATEGY(CONFIG_MANAGER,preConsentOpts?.storage?.strategy,DEFAULT_PRE_CONSENT_STORAGE_STRATEGY));}let eventsDeliveryType=preConsentOpts?.events?.delivery??DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE;const deliveryTypes=['immediate','buffer'];if(isDefined(eventsDeliveryType)&&!deliveryTypes.includes(eventsDeliveryType)){eventsDeliveryType=DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE;logger.warn(UNSUPPORTED_PRE_CONSENT_EVENTS_DELIVERY_TYPE(CONFIG_MANAGER,preConsentOpts?.events?.delivery,DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE));}r(()=>{state.consents.activeConsentManagerPluginName.value=consentManagerPluginName;state.consents.initialized.value=initialized;state.consents.enabled.value=enabled;state.consents.data.value=consentsData;state.consents.provider.value=provider;state.consents.preConsent.value={// Only enable pre-consent if it is explicitly enabled and
|
2880
2802
|
// if it is not already initialized and
|
2881
2803
|
// if consent management is enabled
|
2882
2804
|
enabled:state.loadOptions.value.preConsent?.enabled===true&&initialized===false&&enabled===true,storage:{strategy:storageStrategy},events:{delivery:eventsDeliveryType}};});};/**
|
@@ -2884,7 +2806,7 @@ enabled:state.loadOptions.value.preConsent?.enabled===true&&initialized===false&
|
|
2884
2806
|
* @param resp Source config response
|
2885
2807
|
* @param logger Logger instance
|
2886
2808
|
*/const updateConsentsState=resp=>{let resolutionStrategy=state.consents.resolutionStrategy.value;let cmpMetadata;if(isObjectLiteralAndNotNull(resp.consentManagementMetadata)){if(state.consents.provider.value){resolutionStrategy=resp.consentManagementMetadata.providers.find(p=>p.provider===state.consents.provider.value)?.resolutionStrategy??state.consents.resolutionStrategy.value;}cmpMetadata=resp.consentManagementMetadata;}// If the provider is custom, then the resolution strategy is not applicable
|
2887
|
-
if(state.consents.provider.value==='custom'){resolutionStrategy=undefined;}r(()=>{state.consents.metadata.value=clone(cmpMetadata);state.consents.resolutionStrategy.value=resolutionStrategy;});};const updateDataPlaneEventsStateFromLoadOptions=logger=>{if(state.dataPlaneEvents.deliveryEnabled.value){const defaultEventsQueuePluginName='XhrQueue';let eventsQueuePluginName=defaultEventsQueuePluginName;if(state.loadOptions.value.useBeacon){if(state.capabilities.isBeaconAvailable.value){eventsQueuePluginName='BeaconQueue';}else {eventsQueuePluginName=defaultEventsQueuePluginName;logger
|
2809
|
+
if(state.consents.provider.value==='custom'){resolutionStrategy=undefined;}r(()=>{state.consents.metadata.value=clone(cmpMetadata);state.consents.resolutionStrategy.value=resolutionStrategy;});};const updateDataPlaneEventsStateFromLoadOptions=logger=>{if(state.dataPlaneEvents.deliveryEnabled.value){const defaultEventsQueuePluginName='XhrQueue';let eventsQueuePluginName=defaultEventsQueuePluginName;if(state.loadOptions.value.useBeacon){if(state.capabilities.isBeaconAvailable.value){eventsQueuePluginName='BeaconQueue';}else {eventsQueuePluginName=defaultEventsQueuePluginName;logger.warn(UNSUPPORTED_BEACON_API_WARNING(CONFIG_MANAGER));}}r(()=>{state.dataPlaneEvents.eventsQueuePluginName.value=eventsQueuePluginName;});}};const getSourceConfigURL=(configUrl,writeKey,lockIntegrationsVersion,lockPluginsVersion,logger)=>{const defSearchParams=new URLSearchParams({p:MODULE_TYPE,v:APP_VERSION,build:BUILD_TYPE,writeKey,lockIntegrationsVersion:lockIntegrationsVersion.toString(),lockPluginsVersion:lockPluginsVersion.toString()});let origin=DEFAULT_CONFIG_BE_URL;let searchParams=defSearchParams;let pathname='/sourceConfig/';let hash='';if(isValidURL(configUrl)){const configUrlInstance=new URL(configUrl);if(!removeTrailingSlashes(configUrlInstance.pathname).endsWith('/sourceConfig')){configUrlInstance.pathname=`${removeTrailingSlashes(configUrlInstance.pathname)}/sourceConfig/`;}configUrlInstance.pathname=removeDuplicateSlashes(configUrlInstance.pathname);defSearchParams.forEach((value,key)=>{if(configUrlInstance.searchParams.get(key)===null){configUrlInstance.searchParams.set(key,value);}});origin=configUrlInstance.origin;pathname=configUrlInstance.pathname;searchParams=configUrlInstance.searchParams;hash=configUrlInstance.hash;}else {logger.warn(INVALID_CONFIG_URL_WARNING(CONFIG_MANAGER,configUrl));}return `${origin}${pathname}?${searchParams}${hash}`;};
|
2888
2810
|
|
2889
2811
|
/**
|
2890
2812
|
* A function that determines the base URL for the integrations or plugins SDK
|
@@ -2894,9 +2816,10 @@ if(state.consents.provider.value==='custom'){resolutionStrategy=undefined;}r(()=
|
|
2894
2816
|
* @param currentSdkVersion The current version of the SDK
|
2895
2817
|
* @param lockVersion Flag to lock the version of the component
|
2896
2818
|
* @param urlFromLoadOptions The URL provided by the user in the load options
|
2819
|
+
* @param logger Logger instance
|
2897
2820
|
* @returns The base URL for the integrations or plugins SDK
|
2898
|
-
*/const getSDKComponentBaseURL=(componentType,pathSuffix,defaultComponentUrl,currentSdkVersion,lockVersion,urlFromLoadOptions)=>{let sdkComponentBaseURL;// If the user has provided a custom URL, then validate, clean up and use it
|
2899
|
-
if(urlFromLoadOptions){if(!isValidURL(urlFromLoadOptions)){
|
2821
|
+
*/const getSDKComponentBaseURL=(componentType,pathSuffix,defaultComponentUrl,currentSdkVersion,lockVersion,urlFromLoadOptions,logger)=>{let sdkComponentBaseURL;// If the user has provided a custom URL, then validate, clean up and use it
|
2822
|
+
if(urlFromLoadOptions){if(!isValidURL(urlFromLoadOptions)){logger.error(COMPONENT_BASE_URL_ERROR(CONFIG_MANAGER,componentType,urlFromLoadOptions));return null;}sdkComponentBaseURL=removeTrailingSlashes(urlFromLoadOptions);}else {sdkComponentBaseURL=defaultComponentUrl;// We can automatically determine the base URL only for CDN installations
|
2900
2823
|
if(state.context.app.value.installType==='cdn'){const sdkURL=getSDKUrl();if(sdkURL){// Extract the base URL from the core SDK file URL
|
2901
2824
|
// and append the path suffix to it
|
2902
2825
|
sdkComponentBaseURL=sdkURL.split('/').slice(0,-1).concat(pathSuffix).join('/');}}}// If the version needs to be locked, then replace the major version in the URL
|
@@ -2906,41 +2829,35 @@ if(lockVersion){sdkComponentBaseURL=sdkComponentBaseURL.replace(new RegExp(`/${C
|
|
2906
2829
|
* @param currentSdkVersion Current SDK version
|
2907
2830
|
* @param lockIntegrationsVersion Flag to lock the integrations version
|
2908
2831
|
* @param integrationsUrlFromLoadOptions URL to load the integrations from as provided by the user
|
2832
|
+
* @param logger Logger instance
|
2909
2833
|
* @returns
|
2910
|
-
*/const getIntegrationsCDNPath=(currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions)=>getSDKComponentBaseURL('integrations',CDN_INT_DIR,DEFAULT_INTEGRATION_SDKS_URL,currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions)
|
2911
|
-
* A function that determines plugins SDK loading path
|
2912
|
-
* @param currentSdkVersion Current SDK version
|
2913
|
-
* @param lockPluginsVersion Flag to lock the plugins version
|
2914
|
-
* @param pluginsUrlFromLoadOptions URL to load the plugins from as provided by the user
|
2915
|
-
* @returns Final plugins CDN path
|
2916
|
-
*/const getPluginsCDNPath=(currentSdkVersion,lockPluginsVersion,pluginsUrlFromLoadOptions)=>getSDKComponentBaseURL('plugins',CDN_PLUGINS_DIR,DEFAULT_PLUGINS_URL,currentSdkVersion,lockPluginsVersion,pluginsUrlFromLoadOptions);
|
2834
|
+
*/const getIntegrationsCDNPath=(currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions,logger)=>getSDKComponentBaseURL('integrations',CDN_INT_DIR,DEFAULT_INTEGRATION_SDKS_URL,currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions,logger);
|
2917
2835
|
|
2918
|
-
class ConfigManager{
|
2836
|
+
class ConfigManager{constructor(httpClient,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.httpClient=httpClient;this.onError=this.onError.bind(this);this.processConfig=this.processConfig.bind(this);}attachEffects(){E(()=>{this.logger.setMinLogLevel(state.lifecycle.logLevel.value);});}/**
|
2919
2837
|
* A function to validate, construct and store loadOption, lifecycle, source and destination
|
2920
2838
|
* config related information in global state
|
2921
|
-
*/init(){
|
2922
|
-
const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,destSDKBaseURL);//
|
2923
|
-
const pluginsCDNPath=getPluginsCDNPath(APP_VERSION,lockPluginsVersion,pluginsSDKBaseURL);updateStorageStateFromLoadOptions(this.logger);updateConsentsStateFromLoadOptions(this.logger);updateDataPlaneEventsStateFromLoadOptions(this.logger);// set application lifecycle state in global state
|
2839
|
+
*/init(){const{logLevel,configUrl,lockIntegrationsVersion,lockPluginsVersion,destSDKBaseURL,pluginsSDKBaseURL,integrations}=state.loadOptions.value;// determine the path to fetch integration SDK from
|
2840
|
+
const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,destSDKBaseURL,this.logger);if(isNull(intgCdnUrl)){return;}let pluginsCDNPath;this.attachEffects();state.lifecycle.activeDataplaneUrl.value=removeTrailingSlashes(state.lifecycle.dataPlaneUrl.value);updateStorageStateFromLoadOptions(this.logger);updateConsentsStateFromLoadOptions(this.logger);updateDataPlaneEventsStateFromLoadOptions(this.logger);// set application lifecycle state in global state
|
2924
2841
|
r(()=>{state.lifecycle.integrationsCDNPath.value=intgCdnUrl;state.lifecycle.pluginsCDNPath.value=pluginsCDNPath;if(logLevel){state.lifecycle.logLevel.value=logLevel;}state.lifecycle.sourceConfigUrl.value=getSourceConfigURL(configUrl,state.lifecycle.writeKey.value,lockIntegrationsVersion,lockPluginsVersion,this.logger);state.metrics.metricsServiceUrl.value=`${state.lifecycle.activeDataplaneUrl.value}/${METRICS_SERVICE_ENDPOINT}`;// Data in the loadOptions state is already normalized
|
2925
2842
|
state.nativeDestinations.loadOnlyIntegrations.value=integrations;});this.getConfig();}/**
|
2926
2843
|
* Handle errors
|
2927
|
-
*/onError(error,customMessage
|
2844
|
+
*/onError(error,customMessage){this.errorHandler.onError(error,CONFIG_MANAGER,customMessage);}/**
|
2928
2845
|
* A callback function that is executed once we fetch the source config response.
|
2929
2846
|
* Use to construct and store information that are dependent on the sourceConfig.
|
2930
2847
|
*/processConfig(response,details){// TODO: add retry logic with backoff based on rejectionDetails.xhr.status
|
2931
2848
|
// We can use isErrRetryable utility method
|
2932
|
-
if(!response){this.onError(
|
2933
|
-
if(res.source.enabled===false){this.logger
|
2849
|
+
if(!isDefined(response)){if(isDefined(details)){this.onError(details.error,SOURCE_CONFIG_FETCH_ERROR);}else {this.onError(new Error(SOURCE_CONFIG_FETCH_ERROR));}return;}let res;try{if(isString(response)){res=JSON.parse(response);}else {res=response;}}catch(err){this.onError(err,SOURCE_CONFIG_RESOLUTION_ERROR);return;}if(!isValidSourceConfig(res)){this.onError(new Error(SOURCE_CONFIG_RESOLUTION_ERROR));return;}// Log error and abort if source is disabled
|
2850
|
+
if(res.source.enabled===false){this.logger.error(SOURCE_DISABLED_ERROR);return;}// set the values in state for reporting slice
|
2934
2851
|
updateReportingState(res);const nativeDestinations=res.source.destinations.length>0?filterEnabledDestination(res.source.destinations):[];// set in the state --> source, destination, lifecycle, reporting
|
2935
2852
|
r(()=>{// set source related information in state
|
2936
|
-
state.source.value={config:res.source.config,id:res.source.id,workspaceId:res.source.workspaceId};// set device mode destination related information in state
|
2853
|
+
state.source.value={config:res.source.config,name:res.source.name,id:res.source.id,workspaceId:res.source.workspaceId};// set device mode destination related information in state
|
2937
2854
|
state.nativeDestinations.configuredDestinations.value=nativeDestinations;// set the desired optional plugins
|
2938
2855
|
state.plugins.pluginsToLoadFromConfig.value=state.loadOptions.value.plugins??[];updateConsentsState(res);// set application lifecycle state
|
2939
2856
|
state.lifecycle.status.value='configured';});}/**
|
2940
2857
|
* A function to fetch source config either from /sourceConfig endpoint
|
2941
2858
|
* or from getSourceConfig load option
|
2942
2859
|
* @returns
|
2943
|
-
*/getConfig(){const sourceConfigFunc=state.loadOptions.value.getSourceConfig;if(sourceConfigFunc){if(!isFunction(sourceConfigFunc)){
|
2860
|
+
*/getConfig(){const sourceConfigFunc=state.loadOptions.value.getSourceConfig;if(sourceConfigFunc){if(!isFunction(sourceConfigFunc)){this.logger.error(SOURCE_CONFIG_OPTION_ERROR(CONFIG_MANAGER));return;}// Fetch source config from the function
|
2944
2861
|
const res=sourceConfigFunc();if(res instanceof Promise){res.then(pRes=>this.processConfig(pRes)).catch(err=>{this.onError(err,'SourceConfig');});}else {this.processConfig(res);}}else {// Fetch source configuration from the configured URL
|
2945
2862
|
this.httpClient.getAsyncData({url:state.lifecycle.sourceConfigUrl.value,options:{headers:{'Content-Type':undefined}},callback:this.processConfig});}}}
|
2946
2863
|
|
@@ -2975,16 +2892,16 @@ if(urlObj.search===''){pageUrl=canonicalUrl+search;}else {pageUrl=canonicalUrl;}
|
|
2975
2892
|
const POLYFILL_URL=`https://polyfill-fastly.io/v3/polyfill.min.js?version=3.111.0&features=${Object.keys(legacyJSEngineRequiredPolyfills).join('%2C')}`;const POLYFILL_LOAD_TIMEOUT=10*1000;// 10 seconds
|
2976
2893
|
const POLYFILL_SCRIPT_ID='rudderstackPolyfill';
|
2977
2894
|
|
2978
|
-
class CapabilitiesManager{constructor(errorHandler,logger){this.
|
2895
|
+
class CapabilitiesManager{constructor(httpClient,errorHandler,logger){this.httpClient=httpClient;this.errorHandler=errorHandler;this.logger=logger;this.externalSrcLoader=new ExternalSrcLoader(this.errorHandler,this.logger);this.onError=this.onError.bind(this);this.onReady=this.onReady.bind(this);}init(){this.prepareBrowserCapabilities();this.attachWindowListeners();}/**
|
2979
2896
|
* Detect supported capabilities and set values in state
|
2980
2897
|
*/// eslint-disable-next-line class-methods-use-this
|
2981
2898
|
detectBrowserCapabilities(){r(()=>{// Storage related details
|
2982
2899
|
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
|
2983
2900
|
state.capabilities.isBeaconAvailable.value=hasBeacon();state.capabilities.isUaCHAvailable.value=hasUAClientHints();state.capabilities.isCryptoAvailable.value=hasCrypto();state.capabilities.isIE11.value=isIE11();state.capabilities.isOnline.value=globalThis.navigator.onLine;// Get page context details
|
2984
2901
|
state.context.userAgent.value=getUserAgent();state.context.locale.value=getLanguage();state.context.screen.value=getScreenDetails();state.context.timezone.value=getTimezone();if(hasUAClientHints()){getUserAgentClientHint(uach=>{state.context['ua-ch'].value=uach;},state.loadOptions.value.uaChTrackLevel);}});// Ad blocker detection
|
2985
|
-
E(()=>{if(state.loadOptions.value.sendAdblockPage===true&&state.lifecycle.sourceConfigUrl.value!==undefined){detectAdBlockers(this.
|
2902
|
+
E(()=>{if(state.loadOptions.value.sendAdblockPage===true&&state.lifecycle.sourceConfigUrl.value!==undefined){detectAdBlockers(this.httpClient);}});}/**
|
2986
2903
|
* Detect if polyfills are required and then load script from polyfill URL
|
2987
|
-
*/prepareBrowserCapabilities(){state.capabilities.isLegacyDOM.value=isLegacyJSEngine();const customPolyfillUrl=state.loadOptions.value.polyfillURL;let polyfillUrl=POLYFILL_URL;if(isDefinedAndNotNull(customPolyfillUrl)){if(isValidURL(customPolyfillUrl)){polyfillUrl=customPolyfillUrl;}else {this.logger
|
2904
|
+
*/prepareBrowserCapabilities(){state.capabilities.isLegacyDOM.value=isLegacyJSEngine();const customPolyfillUrl=state.loadOptions.value.polyfillURL;let polyfillUrl=POLYFILL_URL;if(isDefinedAndNotNull(customPolyfillUrl)){if(isValidURL(customPolyfillUrl)){polyfillUrl=customPolyfillUrl;}else {this.logger.warn(INVALID_POLYFILL_URL_WARNING(CAPABILITIES_MANAGER,customPolyfillUrl));}}const shouldLoadPolyfill=state.loadOptions.value.polyfillIfRequired&&state.capabilities.isLegacyDOM.value&&isValidURL(polyfillUrl);if(shouldLoadPolyfill){const isDefaultPolyfillService=polyfillUrl!==state.loadOptions.value.polyfillURL;if(isDefaultPolyfillService){// write key specific callback
|
2988
2905
|
// NOTE: we're not putting this into RudderStackGlobals as providing the property path to the callback function in the polyfill URL is not possible
|
2989
2906
|
const polyfillCallbackName=`RS_polyfillCallback_${state.lifecycle.writeKey.value}`;const polyfillCallback=()=>{this.onReady();// Remove the entry from window so we don't leave room for calling it again
|
2990
2907
|
delete globalThis[polyfillCallbackName];};globalThis[polyfillCallbackName]=polyfillCallback;polyfillUrl=`${polyfillUrl}&callback=${polyfillCallbackName}`;}this.externalSrcLoader.loadJSFile({url:polyfillUrl,id:POLYFILL_SCRIPT_ID,async:true,timeout:POLYFILL_LOAD_TIMEOUT,callback:scriptId=>{if(!scriptId){this.onError(new Error(POLYFILL_SCRIPT_LOAD_ERROR(POLYFILL_SCRIPT_ID,polyfillUrl)));}else if(!isDefaultPolyfillService){this.onReady();}}});}else {this.onReady();}}/**
|
@@ -2995,7 +2912,7 @@ delete globalThis[polyfillCallbackName];};globalThis[polyfillCallbackName]=polyf
|
|
2995
2912
|
onReady(){this.detectBrowserCapabilities();state.lifecycle.status.value='browserCapabilitiesReady';}/**
|
2996
2913
|
* Handles error
|
2997
2914
|
* @param error The error object
|
2998
|
-
*/onError(error){
|
2915
|
+
*/onError(error){this.errorHandler.onError(error,CAPABILITIES_MANAGER);}}
|
2999
2916
|
|
3000
2917
|
const CHANNEL='web';// These are the top-level elements in the standard RudderStack event spec
|
3001
2918
|
const TOP_LEVEL_ELEMENTS=['integrations','anonymousId','originalTimestamp'];// Reserved elements in the context of standard RudderStack event spec
|
@@ -3029,7 +2946,7 @@ const MIN_SESSION_ID_LENGTH=10;/**
|
|
3029
2946
|
* @param {number} sessionId
|
3030
2947
|
* @param logger logger
|
3031
2948
|
* @returns
|
3032
|
-
*/const isManualSessionIdValid=(sessionId,logger)=>{if(!sessionId||!isPositiveInteger(sessionId)||!hasMinLength(MIN_SESSION_ID_LENGTH,sessionId)){logger
|
2949
|
+
*/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;};/**
|
3033
2950
|
* A function to generate new auto tracking session
|
3034
2951
|
* @param sessionTimeout current timestamp
|
3035
2952
|
* @returns SessionInfo
|
@@ -3060,7 +2977,7 @@ const curPageProps=getDefaultPageProperties();Object.keys(curPageProps).forEach(
|
|
3060
2977
|
* @param obj Generic object
|
3061
2978
|
* @param parentKeyPath Object's parent key path
|
3062
2979
|
* @param logger Logger instance
|
3063
|
-
*/const checkForReservedElementsInObject=(obj,parentKeyPath,logger)=>{if(isObjectLiteralAndNotNull(obj)){Object.keys(obj).forEach(property=>{if(RESERVED_ELEMENTS.includes(property)||RESERVED_ELEMENTS.includes(property.toLowerCase())){logger
|
2980
|
+
*/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));}});}};/**
|
3064
2981
|
* Checks for reserved keys in traits, properties, and contextual traits
|
3065
2982
|
* @param rudderEvent Generated rudder event
|
3066
2983
|
* @param logger Logger instance
|
@@ -3077,13 +2994,13 @@ rudderEvent.originalTimestamp=options.originalTimestamp;}};/**
|
|
3077
2994
|
* @param rudderContext Generated rudder event
|
3078
2995
|
* @param options API options
|
3079
2996
|
* @param logger Logger instance
|
3080
|
-
*/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;}});return context;};/**
|
2997
|
+
*/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;};/**
|
3081
2998
|
* Updates rudder event object with data from the API options
|
3082
2999
|
* @param rudderEvent Generated rudder event
|
3083
3000
|
* @param options API options
|
3084
|
-
*/const processOptions=(rudderEvent,options)=>{// Only allow object type for options
|
3001
|
+
*/const processOptions=(rudderEvent,options,logger)=>{// Only allow object type for options
|
3085
3002
|
if(isObjectLiteralAndNotNull(options)){updateTopLevelEventElements(rudderEvent,options);// eslint-disable-next-line no-param-reassign
|
3086
|
-
rudderEvent.context=getMergedContext(rudderEvent.context,options);}};/**
|
3003
|
+
rudderEvent.context=getMergedContext(rudderEvent.context,options,logger);}};/**
|
3087
3004
|
* Returns the final integrations config for the event based on the global config and event's config
|
3088
3005
|
* @param integrationsConfig Event's integrations config
|
3089
3006
|
* @returns Final integrations config
|
@@ -3102,7 +3019,7 @@ commonEventData.anonymousId=generateAnonymousId();}else {// Type casting to stri
|
|
3102
3019
|
commonEventData.anonymousId=state.session.anonymousId.value;}// set truly anonymous tracking flag
|
3103
3020
|
if(state.storage.trulyAnonymousTracking.value){commonEventData.context.trulyAnonymousTracking=true;}if(rudderEvent.type==='identify'){commonEventData.context.traits=state.storage.entries.value.userTraits?.type!==NO_STORAGE?clone(state.session.userTraits.value):rudderEvent.context.traits;}if(rudderEvent.type==='group'){if(rudderEvent.groupId||state.session.groupId.value){commonEventData.groupId=rudderEvent.groupId||state.session.groupId.value;}if(rudderEvent.traits||state.session.groupTraits.value){commonEventData.traits=state.storage.entries.value.groupTraits?.type!==NO_STORAGE?clone(state.session.groupTraits.value):rudderEvent.traits;}}const processedEvent=mergeDeepRight(rudderEvent,commonEventData);// Set the default values for the event properties
|
3104
3021
|
// matching with v1.1 payload
|
3105
|
-
if(processedEvent.event===undefined){processedEvent.event=null;}if(processedEvent.properties===undefined){processedEvent.properties=null;}processOptions(processedEvent,options);checkForReservedElements(processedEvent,logger);// Update the integrations config for the event
|
3022
|
+
if(processedEvent.event===undefined){processedEvent.event=null;}if(processedEvent.properties===undefined){processedEvent.properties=null;}processOptions(processedEvent,options,logger);checkForReservedElements(processedEvent,logger);// Update the integrations config for the event
|
3106
3023
|
processedEvent.integrations=getEventIntegrationsConfig(processedEvent.integrations);return processedEvent;};
|
3107
3024
|
|
3108
3025
|
class RudderEventFactory{constructor(logger){this.logger=logger;}/**
|
@@ -3136,7 +3053,7 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
3136
3053
|
* Generates a new RudderEvent object based on the user-input fields
|
3137
3054
|
* @param event API event parameters object
|
3138
3055
|
* @returns A RudderEvent object
|
3139
|
-
*/create(event){let eventObj;switch(event.type){case 'page':eventObj=this.generatePageEvent(event.category,event.name,event.properties,event.options);break;case 'track':eventObj=this.generateTrackEvent(event.name,event.properties,event.options);break;case 'identify':eventObj=this.generateIdentifyEvent(event.userId,event.traits,event.options);break;case 'alias':eventObj=this.generateAliasEvent(event.to,event.from,event.options);break;case 'group':eventObj=this.generateGroupEvent(event.groupId,event.traits,event.options);break;}return eventObj;}}
|
3056
|
+
*/create(event){let eventObj;switch(event.type){case 'page':eventObj=this.generatePageEvent(event.category,event.name,event.properties,event.options);break;case 'track':eventObj=this.generateTrackEvent(event.name,event.properties,event.options);break;case 'identify':eventObj=this.generateIdentifyEvent(event.userId,event.traits,event.options);break;case 'alias':eventObj=this.generateAliasEvent(event.to,event.from,event.options);break;case 'group':default:eventObj=this.generateGroupEvent(event.groupId,event.traits,event.options);break;}return eventObj;}}
|
3140
3057
|
|
3141
3058
|
/**
|
3142
3059
|
* A service to generate valid event payloads and queue them for processing
|
@@ -3146,17 +3063,14 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
3146
3063
|
* @param userSessionManager UserSession Manager instance
|
3147
3064
|
* @param errorHandler Error handler object
|
3148
3065
|
* @param logger Logger object
|
3149
|
-
*/constructor(eventRepository,userSessionManager,errorHandler,logger){this.eventRepository=eventRepository;this.userSessionManager=userSessionManager;this.errorHandler=errorHandler;this.logger=logger;this.eventFactory=new RudderEventFactory(this.logger);
|
3066
|
+
*/constructor(eventRepository,userSessionManager,errorHandler,logger){this.eventRepository=eventRepository;this.userSessionManager=userSessionManager;this.errorHandler=errorHandler;this.logger=logger;this.eventFactory=new RudderEventFactory(this.logger);}/**
|
3150
3067
|
* Initializes the event manager
|
3151
3068
|
*/init(){this.eventRepository.init();}resume(){this.eventRepository.resume();}/**
|
3152
3069
|
* Consumes a new incoming event
|
3153
3070
|
* @param event Incoming event data
|
3154
|
-
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);
|
3155
|
-
* Handles error
|
3156
|
-
* @param error The error object
|
3157
|
-
*/onError(error,customMessage,shouldAlwaysThrow){if(this.errorHandler){this.errorHandler.onError(error,EVENT_MANAGER,customMessage,shouldAlwaysThrow);}else {throw error;}}}
|
3071
|
+
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
3158
3072
|
|
3159
|
-
class UserSessionManager{constructor(
|
3073
|
+
class UserSessionManager{constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.storeManager=storeManager;this.pluginsManager=pluginsManager;this.logger=logger;this.errorHandler=errorHandler;this.httpClient=httpClient;this.onError=this.onError.bind(this);this.serverSideCookieDebounceFuncs={};}/**
|
3160
3074
|
* Initialize User session with values from storage
|
3161
3075
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
3162
3076
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
@@ -3167,12 +3081,12 @@ if(state.session.sessionInfo.value.autoTrack){this.startOrRenewAutoTracking(stat
|
|
3167
3081
|
// as those values indicate there is no need for migration or
|
3168
3082
|
// migration failed
|
3169
3083
|
if(!isNullOrUndefined(migratedVal)){store.set(storageEntry,migratedVal);}});});}getConfiguredSessionTrackingInfo(){let autoTrack=state.loadOptions.value.sessions?.autoTrack!==false;// Do not validate any further if autoTrack is disabled
|
3170
|
-
if(!autoTrack){return {autoTrack};}let timeout;const configuredSessionTimeout=state.loadOptions.value.sessions?.timeout;if(!isPositiveInteger(configuredSessionTimeout)){this.logger
|
3084
|
+
if(!autoTrack){return {autoTrack};}let timeout;const configuredSessionTimeout=state.loadOptions.value.sessions?.timeout;if(!isPositiveInteger(configuredSessionTimeout)){this.logger.warn(TIMEOUT_NOT_NUMBER_WARNING(USER_SESSION_MANAGER,configuredSessionTimeout,DEFAULT_SESSION_TIMEOUT_MS));timeout=DEFAULT_SESSION_TIMEOUT_MS;}else {timeout=configuredSessionTimeout;}if(timeout===0){this.logger.warn(TIMEOUT_ZERO_WARNING(USER_SESSION_MANAGER));autoTrack=false;}// In case user provides a timeout value greater than 0 but less than 10 seconds SDK will show a warning
|
3171
3085
|
// and will proceed with it
|
3172
|
-
if(timeout>0&&timeout<MIN_SESSION_TIMEOUT_MS){this.logger
|
3086
|
+
if(timeout>0&&timeout<MIN_SESSION_TIMEOUT_MS){this.logger.warn(TIMEOUT_NOT_RECOMMENDED_WARNING(USER_SESSION_MANAGER,timeout,MIN_SESSION_TIMEOUT_MS));}return {timeout,autoTrack};}/**
|
3173
3087
|
* Handles error
|
3174
3088
|
* @param error The error object
|
3175
|
-
*/onError(error,customMessage){
|
3089
|
+
*/onError(error,customMessage){this.errorHandler.onError(error,USER_SESSION_MANAGER,customMessage);}/**
|
3176
3090
|
* A function to encrypt the cookie value and return the encrypted data
|
3177
3091
|
* @param cookiesData
|
3178
3092
|
* @param store
|
@@ -3187,7 +3101,7 @@ if(timeout>0&&timeout<MIN_SESSION_TIMEOUT_MS){this.logger?.warn(TIMEOUT_NOT_RECO
|
|
3187
3101
|
* @param value encrypted cookie value
|
3188
3102
|
*/setServerSideCookies(cookiesData,cb,store){try{// encrypt cookies values
|
3189
3103
|
const encryptedCookieData=this.getEncryptedCookieData(cookiesData,store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
3190
|
-
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{if(details?.xhr?.status===200){cookiesData.forEach(cData=>{const cookieValue=store?.get(cData.name);const before=stringifyWithoutCircular(cData.value,false,[]);const after=stringifyWithoutCircular(cookieValue,false,[]);if(after!==before){this.logger
|
3104
|
+
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{if(details?.xhr?.status===200){cookiesData.forEach(cData=>{const cookieValue=store?.get(cData.name);const before=stringifyWithoutCircular(cData.value,false,[]);const after=stringifyWithoutCircular(cookieValue,false,[]);if(after!==before){this.logger.error(FAILED_SETTING_COOKIE_FROM_SERVER_ERROR(cData.name));if(cb){cb(cData.name,cData.value);}}});}else {this.logger.error(DATA_SERVER_REQUEST_FAIL_ERROR(details?.xhr?.status));cookiesData.forEach(each=>{if(cb){cb(each.name,each.value);}});}});}}catch(e){this.onError(e,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR);cookiesData.forEach(each=>{if(cb){cb(each.name,each.value);}});}}/**
|
3191
3105
|
* A function to sync values in storage
|
3192
3106
|
* @param sessionKey
|
3193
3107
|
* @param value
|
@@ -3291,7 +3205,7 @@ this.setAnonymousId();}if(noNewSessionStart){return;}if(autoTrack){session.sessi
|
|
3291
3205
|
|
3292
3206
|
/**
|
3293
3207
|
* Plugins to be loaded in the plugins loadOption is not defined
|
3294
|
-
*/const defaultOptionalPluginsList=['BeaconQueue','
|
3208
|
+
*/const defaultOptionalPluginsList=['BeaconQueue','CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];
|
3295
3209
|
|
3296
3210
|
const normalizeLoadOptions=(loadOptionsFromState,loadOptions)=>{// TODO: Maybe add warnings for invalid values
|
3297
3211
|
const normalizedLoadOpts=clone(loadOptions);if(!isString(normalizedLoadOpts.setCookieDomain)){normalizedLoadOpts.setCookieDomain=undefined;}const cookieSameSiteValues=['Strict','Lax','None'];if(!cookieSameSiteValues.includes(normalizedLoadOpts.sameSiteCookie)){normalizedLoadOpts.sameSiteCookie=undefined;}normalizedLoadOpts.secureCookie=getNormalizedBooleanValue(normalizedLoadOpts.secureCookie,loadOptionsFromState.secureCookie);normalizedLoadOpts.sameDomainCookiesOnly=getNormalizedBooleanValue(normalizedLoadOpts.sameDomainCookiesOnly,loadOptionsFromState.sameDomainCookiesOnly);const uaChTrackLevels=['none','default','full'];if(!uaChTrackLevels.includes(normalizedLoadOpts.uaChTrackLevel)){normalizedLoadOpts.uaChTrackLevel=undefined;}normalizedLoadOpts.integrations=getNormalizedObjectValue(normalizedLoadOpts.integrations);if(!Array.isArray(normalizedLoadOpts.plugins)){normalizedLoadOpts.plugins=defaultOptionalPluginsList;}normalizedLoadOpts.useGlobalIntegrationsConfigInEvents=getNormalizedBooleanValue(normalizedLoadOpts.useGlobalIntegrationsConfigInEvents,loadOptionsFromState.useGlobalIntegrationsConfigInEvents);normalizedLoadOpts.bufferDataPlaneEventsUntilReady=getNormalizedBooleanValue(normalizedLoadOpts.bufferDataPlaneEventsUntilReady,loadOptionsFromState.bufferDataPlaneEventsUntilReady);normalizedLoadOpts.sendAdblockPage=getNormalizedBooleanValue(normalizedLoadOpts.sendAdblockPage,loadOptionsFromState.sendAdblockPage);normalizedLoadOpts.useServerSideCookies=getNormalizedBooleanValue(normalizedLoadOpts.useServerSideCookies,loadOptionsFromState.useServerSideCookies);if(!isString(normalizedLoadOpts.dataServiceEndpoint)){normalizedLoadOpts.dataServiceEndpoint=undefined;}normalizedLoadOpts.sendAdblockPageOptions=getNormalizedObjectValue(normalizedLoadOpts.sendAdblockPageOptions);normalizedLoadOpts.loadIntegration=getNormalizedBooleanValue(normalizedLoadOpts.loadIntegration,loadOptionsFromState.loadIntegration);if(!isNonEmptyObject(normalizedLoadOpts.storage)){normalizedLoadOpts.storage=undefined;}else {normalizedLoadOpts.storage.migrate=getNormalizedBooleanValue(normalizedLoadOpts.storage.migrate,loadOptionsFromState.storage?.migrate);normalizedLoadOpts.storage.cookie=getNormalizedObjectValue(normalizedLoadOpts.storage.cookie);normalizedLoadOpts.storage.encryption=getNormalizedObjectValue(normalizedLoadOpts.storage.encryption);normalizedLoadOpts.storage=removeUndefinedAndNullValues(normalizedLoadOpts.storage);}normalizedLoadOpts.destinationsQueueOptions=getNormalizedObjectValue(normalizedLoadOpts.destinationsQueueOptions);normalizedLoadOpts.queueOptions=getNormalizedObjectValue(normalizedLoadOpts.queueOptions);normalizedLoadOpts.lockIntegrationsVersion=getNormalizedBooleanValue(normalizedLoadOpts.lockIntegrationsVersion,loadOptionsFromState.lockIntegrationsVersion);normalizedLoadOpts.lockPluginsVersion=getNormalizedBooleanValue(normalizedLoadOpts.lockPluginsVersion,loadOptionsFromState.lockPluginsVersion);if(!isNumber(normalizedLoadOpts.dataPlaneEventsBufferTimeout)){normalizedLoadOpts.dataPlaneEventsBufferTimeout=undefined;}normalizedLoadOpts.beaconQueueOptions=getNormalizedObjectValue(normalizedLoadOpts.beaconQueueOptions);normalizedLoadOpts.preConsent=getNormalizedObjectValue(normalizedLoadOpts.preConsent);const mergedLoadOptions=mergeDeepRight(loadOptionsFromState,removeUndefinedAndNullValues(normalizedLoadOpts));return mergedLoadOptions;};
|
@@ -3312,6 +3226,8 @@ const DATA_PLANE_QUEUE_EXT_POINT_PREFIX='dataplaneEventsQueue';const DESTINATION
|
|
3312
3226
|
// In general, the preference is given to the event's integrations config
|
3313
3227
|
const destinationsIntgConfig=state.nativeDestinations.integrationsConfig.value;const overriddenIntgOpts=getOverriddenIntegrationOptions(event.integrations,destinationsIntgConfig);finalEvent.integrations=mergeDeepRight(destinationsIntgConfig,overriddenIntgOpts);return finalEvent;};const shouldBufferEventsForPreConsent=state=>state.consents.preConsent.value.enabled&&state.consents.preConsent.value.events?.delivery==='buffer'&&(state.consents.preConsent.value.storage?.strategy==='session'||state.consents.preConsent.value.storage?.strategy==='none');
|
3314
3228
|
|
3229
|
+
const safelyInvokeCallback=(callback,args,apiName,logger)=>{if(!isDefined(callback)){return;}if(isFunction(callback)){try{callback(...args);}catch(error){logger.error(CALLBACK_INVOKE_ERROR(apiName),error);}}else {logger.error(INVALID_CALLBACK_FN_ERROR(apiName));}};
|
3230
|
+
|
3315
3231
|
/**
|
3316
3232
|
* Event repository class responsible for queuing events for further processing and delivery
|
3317
3233
|
*/class EventRepository{/**
|
@@ -3320,9 +3236,9 @@ const destinationsIntgConfig=state.nativeDestinations.integrationsConfig.value;c
|
|
3320
3236
|
* @param storeManager Store Manager instance
|
3321
3237
|
* @param errorHandler Error handler object
|
3322
3238
|
* @param logger Logger object
|
3323
|
-
*/constructor(pluginsManager,storeManager,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.
|
3239
|
+
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;}/**
|
3324
3240
|
* Initializes the event repository
|
3325
|
-
*/init(){
|
3241
|
+
*/init(){this.dataplaneEventsQueue=this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.init`,state,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.dmtEventsQueue=this.pluginsManager.invokeSingle(`${DMT_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.destinationsEventsQueue=this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.storeManager,this.dmtEventsQueue,this.errorHandler,this.logger);// Start the queue once the client destinations are ready
|
3326
3242
|
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);// Start the queue processing only when the destinations are ready or hybrid mode destinations exist
|
3327
3243
|
// However, events will be enqueued for now.
|
3328
3244
|
// At the time of processing the events, the integrations config data from destinations
|
@@ -3332,15 +3248,8 @@ if(state.loadOptions.value.bufferDataPlaneEventsUntilReady===true){timeoutId=glo
|
|
3332
3248
|
* Enqueues the event for processing
|
3333
3249
|
* @param event RudderEvent object
|
3334
3250
|
* @param callback API callback function
|
3335
|
-
*/enqueue(event,callback){
|
3336
|
-
|
3337
|
-
// to ensure the mutated (if any) event is sent to the callback
|
3338
|
-
callback?.(dpQEvent);}catch(error){this.onError(error,API_CALLBACK_INVOKE_ERROR);}}/**
|
3339
|
-
* Handles error
|
3340
|
-
* @param error The error object
|
3341
|
-
* @param customMessage a message
|
3342
|
-
* @param shouldAlwaysThrow if it should throw or use logger
|
3343
|
-
*/onError(error,customMessage,shouldAlwaysThrow){if(this.errorHandler){this.errorHandler.onError(error,EVENT_REPOSITORY,customMessage,shouldAlwaysThrow);}else {throw error;}}}
|
3251
|
+
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger);// Invoke the callback if it exists
|
3252
|
+
const apiName=`${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;safelyInvokeCallback(callback,[dpQEvent],apiName,this.logger);}}
|
3344
3253
|
|
3345
3254
|
const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{analyticsInstance:globalThis.rudderanalytics},bubbles:true,cancelable:true,composed:true});globalThis.document.dispatchEvent(customEvent);};const isWriteKeyValid=writeKey=>isString(writeKey)&&writeKey.trim().length>0;const isDataPlaneUrlValid=dataPlaneUrl=>isValidURL(dataPlaneUrl);
|
3346
3255
|
|
@@ -3348,11 +3257,11 @@ const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{
|
|
3348
3257
|
* Analytics class with lifecycle based on state ad user triggered events
|
3349
3258
|
*/class Analytics{/**
|
3350
3259
|
* Initialize services and components or use default ones if singletons
|
3351
|
-
*/constructor(){this.preloadBuffer=new BufferQueue();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);
|
3260
|
+
*/constructor(){this.preloadBuffer=new BufferQueue();this.initialized=false;this.errorHandler=defaultErrorHandler;this.logger=defaultLogger;this.externalSrcLoader=new ExternalSrcLoader(this.errorHandler,this.logger);this.httpClient=defaultHttpClient;this.httpClient.init(this.errorHandler);this.capabilitiesManager=new CapabilitiesManager(this.httpClient,this.errorHandler,this.logger);}/**
|
3352
3261
|
* Start application lifecycle if not already started
|
3353
3262
|
*/load(writeKey,dataPlaneUrl,loadOptions={}){if(state.lifecycle.status.value){return;}if(!isWriteKeyValid(writeKey)){this.logger.error(WRITE_KEY_VALIDATION_ERROR(ANALYTICS_CORE,writeKey));return;}if(!isDataPlaneUrlValid(dataPlaneUrl)){this.logger.error(DATA_PLANE_URL_VALIDATION_ERROR(ANALYTICS_CORE,dataPlaneUrl));return;}// Set initial state values
|
3354
3263
|
r(()=>{state.lifecycle.writeKey.value=clone(writeKey);state.lifecycle.dataPlaneUrl.value=clone(dataPlaneUrl);state.loadOptions.value=normalizeLoadOptions(state.loadOptions.value,loadOptions);state.lifecycle.status.value='mounted';});// set log level as early as possible
|
3355
|
-
this.logger
|
3264
|
+
this.logger.setMinLogLevel(state.loadOptions.value.logLevel??POST_LOAD_LOG_LEVEL);// Expose state to global objects
|
3356
3265
|
setExposedGlobal('state',state,writeKey);// Configure initial config of any services or components here
|
3357
3266
|
// State application lifecycle
|
3358
3267
|
this.startLifecycle();}// Start lifecycle methods
|
@@ -3366,11 +3275,11 @@ if(state.consents.preConsent.value.enabled===true){state.lifecycle.status.value=
|
|
3366
3275
|
* Enqueue in SDK preload buffer events, used from preloadBuffer component
|
3367
3276
|
*/enqueuePreloadBufferEvents(bufferedEvents){if(Array.isArray(bufferedEvents)){bufferedEvents.forEach(bufferedEvent=>this.preloadBuffer.enqueue(clone(bufferedEvent)));}}/**
|
3368
3277
|
* Process the buffer preloaded events by passing their arguments to the respective facade methods
|
3369
|
-
*/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.
|
3278
|
+
*/processDataInPreloadBuffer(){while(this.preloadBuffer.size()>0){const eventToProcess=this.preloadBuffer.dequeue();if(eventToProcess){consumePreloadBufferedEvent([...eventToProcess],this);}}}prepareInternalServices(){this.pluginsManager=new PluginsManager(defaultPluginEngine,this.errorHandler,this.logger);this.storeManager=new StoreManager(this.pluginsManager,this.errorHandler,this.logger);this.configManager=new ConfigManager(this.httpClient,this.errorHandler,this.logger);this.userSessionManager=new UserSessionManager(this.pluginsManager,this.storeManager,this.httpClient,this.errorHandler,this.logger);this.eventRepository=new EventRepository(this.pluginsManager,this.storeManager,this.httpClient,this.errorHandler,this.logger);this.eventManager=new EventManager(this.eventRepository,this.userSessionManager,this.errorHandler,this.logger);}/**
|
3370
3279
|
* Load configuration
|
3371
3280
|
*/loadConfig(){if(state.lifecycle.writeKey.value){this.httpClient.setAuthHeader(state.lifecycle.writeKey.value);}this.configManager?.init();}/**
|
3372
3281
|
* Initialize the storage and event queue
|
3373
|
-
*/onPluginsReady(){
|
3282
|
+
*/onPluginsReady(){// Initialize storage
|
3374
3283
|
this.storeManager?.init();this.userSessionManager?.init();// Initialize the appropriate consent manager plugin
|
3375
3284
|
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}// Initialize event manager
|
3376
3285
|
this.eventManager?.init();// Mark the SDK as initialized
|
@@ -3381,15 +3290,15 @@ state.lifecycle.status.value='initialized';}/**
|
|
3381
3290
|
}/**
|
3382
3291
|
* Trigger onLoaded callback if any is provided in config & emit initialised event
|
3383
3292
|
*/onInitialized(){// Process any preloaded events
|
3384
|
-
this.processDataInPreloadBuffer();//
|
3293
|
+
this.processDataInPreloadBuffer();// Execute onLoaded callback if provided in load options
|
3294
|
+
const onLoadedCallbackFn=state.loadOptions.value.onLoaded;// TODO: we need to avoid passing the window object to the callback function
|
3385
3295
|
// as this will prevent us from supporting multiple SDK instances in the same page
|
3386
|
-
|
3387
|
-
if(isFunction(state.loadOptions.value.onLoaded)){state.loadOptions.value.onLoaded(globalThis.rudderanalytics);}// Set lifecycle state
|
3296
|
+
safelyInvokeCallback(onLoadedCallbackFn,[globalThis.rudderanalytics],LOAD_API,this.logger);// Set lifecycle state
|
3388
3297
|
r(()=>{state.lifecycle.loaded.value=true;state.lifecycle.status.value='loaded';});this.initialized=true;// Emit an event to use as substitute to the onLoaded callback
|
3389
3298
|
dispatchSDKEvent('RSA_Initialised');}/**
|
3390
3299
|
* Emit ready event
|
3391
3300
|
*/// eslint-disable-next-line class-methods-use-this
|
3392
|
-
onReady(){state.lifecycle.status.value='readyExecuted';state.eventBuffer.readyCallbacksArray.value.forEach(callback=>{
|
3301
|
+
onReady(){state.lifecycle.status.value='readyExecuted';state.eventBuffer.readyCallbacksArray.value.forEach(callback=>{safelyInvokeCallback(callback,[],READY_API,this.logger);});// Emit an event to use as substitute to the ready callback
|
3393
3302
|
dispatchSDKEvent('RSA_Ready');}/**
|
3394
3303
|
* Consume preloaded events buffer
|
3395
3304
|
*/processBufferedEvents(){// This logic has been intentionally implemented without a simple
|
@@ -3408,11 +3317,11 @@ onDestinationsReady(){// May be do any destination specific actions here
|
|
3408
3317
|
// Mark the ready status if not already done
|
3409
3318
|
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
3410
3319
|
// Start consumer exposed methods
|
3411
|
-
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(
|
3320
|
+
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
|
3412
3321
|
* If destinations are loaded or no integration is available for loading
|
3413
3322
|
* execute the callback immediately else push the callbacks to a queue that
|
3414
3323
|
* will be executed after loading completes
|
3415
|
-
*/if(state.lifecycle.status.value==='readyExecuted'){
|
3324
|
+
*/if(state.lifecycle.status.value==='readyExecuted'){safelyInvokeCallback(callback,[],READY_API,this.logger);}else {state.eventBuffer.readyCallbacksArray.value=[...state.eventBuffer.readyCallbacksArray.value,callback];}}page(payload,isBufferedInvocation=false){const type='page';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;this.eventManager?.addEvent({type:'page',category:payload.category,name:payload.name,properties:payload.properties,options:payload.options,callback:payload.callback});// TODO: Maybe we should alter the behavior to send the ad-block page event even if the SDK is still loaded. It'll be pushed into the to be processed queue.
|
3416
3325
|
// Send automatic ad blocked page event if ad-blockers are detected on the page
|
3417
3326
|
// Check page category to avoid infinite loop
|
3418
3327
|
if(state.capabilities.isAdBlocked.value===true&&payload.category!==ADBLOCK_PAGE_CATEGORY){this.page(pageArgumentsToCallOptions(ADBLOCK_PAGE_CATEGORY,ADBLOCK_PAGE_NAME,{// 'title' is intentionally omitted as it does not make sense
|
@@ -3446,7 +3355,7 @@ analyticsInstances={};defaultAnalyticsKey='';logger=(()=>defaultLogger)();// Sin
|
|
3446
3355
|
constructor(){try{if(RudderAnalytics.globalSingleton){// START-NO-SONAR-SCAN
|
3447
3356
|
// eslint-disable-next-line no-constructor-return
|
3448
3357
|
return RudderAnalytics.globalSingleton;// END-NO-SONAR-SCAN
|
3449
|
-
}
|
3358
|
+
}this.setDefaultInstanceKey=this.setDefaultInstanceKey.bind(this);this.getAnalyticsInstance=this.getAnalyticsInstance.bind(this);this.load=this.load.bind(this);this.ready=this.ready.bind(this);this.triggerBufferedLoadEvent=this.triggerBufferedLoadEvent.bind(this);this.page=this.page.bind(this);this.track=this.track.bind(this);this.identify=this.identify.bind(this);this.alias=this.alias.bind(this);this.group=this.group.bind(this);this.reset=this.reset.bind(this);this.getAnonymousId=this.getAnonymousId.bind(this);this.setAnonymousId=this.setAnonymousId.bind(this);this.getUserId=this.getUserId.bind(this);this.getUserTraits=this.getUserTraits.bind(this);this.getGroupId=this.getGroupId.bind(this);this.getGroupTraits=this.getGroupTraits.bind(this);this.startSession=this.startSession.bind(this);this.endSession=this.endSession.bind(this);this.getSessionId=this.getSessionId.bind(this);this.setAuthToken=this.setAuthToken.bind(this);this.consent=this.consent.bind(this);RudderAnalytics.globalSingleton=this;state.autoTrack.pageLifecycle.visitId.value=generateUUID();state.autoTrack.pageLifecycle.pageLoadedTimestamp.value=Date.now();// start loading if a load event was buffered or wait for explicit load call
|
3450
3359
|
this.triggerBufferedLoadEvent();// Assign to global "rudderanalytics" object after processing the preload buffer (if any exists)
|
3451
3360
|
// for CDN bundling IIFE exports covers this but for npm ESM and CJS bundling has to be done explicitly
|
3452
3361
|
globalThis.rudderanalytics=this;}catch(error){dispatchErrorEvent(error);}}/**
|
@@ -3488,7 +3397,7 @@ trackPageLoadedEvent(events,options,preloadedEventsArray){if(events.length===0||
|
|
3488
3397
|
* @param events
|
3489
3398
|
* @param useBeacon
|
3490
3399
|
* @param options
|
3491
|
-
*/setupPageUnloadTracking(events,useBeacon,options){if(events.length===0||events.includes(PageLifecycleEvents.UNLOADED)){if(useBeacon===true){onPageLeave(isAccessible=>{if(isAccessible===false&&state.lifecycle.loaded.value){const pageUnloadedTimestamp=Date.now();const visitDuration=pageUnloadedTimestamp-state.autoTrack.pageLifecycle.pageLoadedTimestamp.value;this.track(PageLifecycleEvents.UNLOADED,{visitDuration},{...options,originalTimestamp:getFormattedTimestamp(new Date(pageUnloadedTimestamp))});}});}else {//
|
3400
|
+
*/setupPageUnloadTracking(events,useBeacon,options){if(events.length===0||events.includes(PageLifecycleEvents.UNLOADED)){if(useBeacon===true){onPageLeave(isAccessible=>{if(isAccessible===false&&state.lifecycle.loaded.value){const pageUnloadedTimestamp=Date.now();const visitDuration=pageUnloadedTimestamp-state.autoTrack.pageLifecycle.pageLoadedTimestamp.value;this.track(PageLifecycleEvents.UNLOADED,{visitDuration},{...options,originalTimestamp:getFormattedTimestamp(new Date(pageUnloadedTimestamp))});}});}else {// log warning if beacon is disabled
|
3492
3401
|
this.logger.warn(PAGE_UNLOAD_ON_BEACON_DISABLED_WARNING(RSA));}}}/**
|
3493
3402
|
* Trigger load event in buffer queue if exists and stores the
|
3494
3403
|
* remaining preloaded events array in global object
|