@rudderstack/analytics-js 3.13.0 → 3.15.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 +30 -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 +219 -309
- package/dist/npm/legacy/bundled/esm/index.mjs +219 -309
- package/dist/npm/legacy/bundled/umd/index.js +219 -309
- package/dist/npm/legacy/cjs/index.cjs +219 -309
- package/dist/npm/legacy/content-script/cjs/index.cjs +219 -284
- package/dist/npm/legacy/content-script/esm/index.mjs +219 -284
- package/dist/npm/legacy/content-script/umd/index.js +219 -284
- package/dist/npm/legacy/esm/index.mjs +219 -309
- package/dist/npm/legacy/umd/index.js +219 -309
- package/dist/npm/modern/bundled/cjs/index.cjs +220 -308
- package/dist/npm/modern/bundled/esm/index.mjs +220 -308
- package/dist/npm/modern/bundled/umd/index.js +220 -308
- package/dist/npm/modern/cjs/index.cjs +212 -178
- package/dist/npm/modern/content-script/cjs/index.cjs +220 -283
- package/dist/npm/modern/content-script/esm/index.mjs +220 -283
- package/dist/npm/modern/content-script/umd/index.js +220 -283
- package/dist/npm/modern/esm/index.mjs +212 -178
- package/dist/npm/modern/umd/index.js +212 -178
- package/package.json +1 -1
@@ -285,10 +285,10 @@ const isFunction=value=>typeof value==='function'&&Boolean(value.constructor&&va
|
|
285
285
|
* @param value input value
|
286
286
|
* @returns boolean
|
287
287
|
*/const isDefinedNotNullAndNotEmptyString=value=>isDefinedAndNotNull(value)&&value!=='';/**
|
288
|
-
* Determines if the input is
|
289
|
-
* @param
|
290
|
-
* @returns true if the input is
|
291
|
-
*/const isTypeOfError=
|
288
|
+
* Determines if the input is of type error
|
289
|
+
* @param value input value
|
290
|
+
* @returns true if the input is of type error else false
|
291
|
+
*/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;}};
|
292
292
|
|
293
293
|
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';/**
|
294
294
|
* Checks if the input is an object literal or built-in object type and not null
|
@@ -328,7 +328,7 @@ mergeDeepRight(mergedArray[index],value):value;});return mergedArray;};const mer
|
|
328
328
|
* getNormalizedBooleanValue(true, false) // returns true
|
329
329
|
*/const getNormalizedBooleanValue=(val,defVal)=>{if(isDefined(defVal)){return isDefined(val)?val===true:defVal;}return val===true;};
|
330
330
|
|
331
|
-
const trim=value=>value.replace(/^\s+|\s+$/gm,'');const
|
331
|
+
const trim=value=>value.replace(/^\s+|\s+$/gm,'');const removeLeadingPeriod=value=>value.replace(/^\.+/,'');/**
|
332
332
|
* A function to convert values to string
|
333
333
|
* @param val input value
|
334
334
|
* @returns stringified value
|
@@ -390,7 +390,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
|
|
390
390
|
* Represents the options parameter in the load API
|
391
391
|
*/
|
392
392
|
|
393
|
-
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
|
393
|
+
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';
|
394
394
|
|
395
395
|
function random(len){return crypto.getRandomValues(new Uint8Array(len));}
|
396
396
|
|
@@ -458,14 +458,17 @@ if(isObjectLiteralAndNotNull(sanitizedValue)||Array.isArray(sanitizedValue)){res
|
|
458
458
|
*/const getSanitizedValue=(value,logger)=>{const replacer=getReplacer();// This is needed for registering the first ancestor
|
459
459
|
const newValue=replacer.call(value,'',value);if(isObjectLiteralAndNotNull(value)||Array.isArray(value)){return traverseWithThis(value,replacer);}return newValue;};
|
460
460
|
|
461
|
-
const MANUAL_ERROR_IDENTIFIER='[
|
461
|
+
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;};/**
|
462
462
|
* Get mutated error with issue prepended to error message
|
463
463
|
* @param err Original error
|
464
464
|
* @param issue Issue to prepend to error message
|
465
465
|
* @returns Instance of Error with message prepended with issue
|
466
|
-
*/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
|
466
|
+
*/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
|
467
|
+
error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// eslint-disable-next-line no-param-reassign
|
468
|
+
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
469
|
+
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
467
470
|
|
468
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
471
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.15.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';
|
469
472
|
|
470
473
|
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';
|
471
474
|
|
@@ -550,11 +553,11 @@ timeoutID=globalThis.setTimeout(()=>{reject(new Error(SCRIPT_LOAD_TIMEOUT_ERROR(
|
|
550
553
|
|
551
554
|
/**
|
552
555
|
* Service to load external resources/files
|
553
|
-
*/class ExternalSrcLoader{
|
556
|
+
*/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);}/**
|
554
557
|
* Load external resource of type javascript
|
555
558
|
*/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();}});}/**
|
556
559
|
* Handle errors
|
557
|
-
*/onError(error){
|
560
|
+
*/onError(error){this.errorHandler.onError(error,EXTERNAL_SRC_LOADER);}}
|
558
561
|
|
559
562
|
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(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);}
|
560
563
|
|
@@ -583,9 +586,9 @@ let ErrorType=/*#__PURE__*/function(ErrorType){ErrorType["HANDLEDEXCEPTION"]="ha
|
|
583
586
|
// default is v3
|
584
587
|
const SUPPORTED_STORAGE_TYPES=['localStorage','memoryStorage','cookieStorage','sessionStorage','none'];const DEFAULT_STORAGE_TYPE='cookieStorage';
|
585
588
|
|
586
|
-
const
|
587
|
-
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
|
588
|
-
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
|
589
|
+
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
|
590
|
+
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
|
591
|
+
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(', ')}.`;
|
589
592
|
|
590
593
|
const DEFAULT_INTEGRATIONS_CONFIG={All:true};
|
591
594
|
|
@@ -604,7 +607,7 @@ const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';co
|
|
604
607
|
|
605
608
|
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';
|
606
609
|
|
607
|
-
const defaultLoadOptions={
|
610
|
+
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(clone(defaultLoadOptions));
|
608
611
|
|
609
612
|
const DEFAULT_USER_SESSION_VALUES={userId:'',userTraits:{},anonymousId:'',groupId:'',groupTraits:{},initialReferrer:'',initialReferringDomain:'',sessionInfo:{},authToken:null};const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
|
610
613
|
|
@@ -612,17 +615,17 @@ const defaultSessionConfiguration={autoTrack:true,timeout:DEFAULT_SESSION_TIMEOU
|
|
612
615
|
|
613
616
|
const capabilitiesState={isOnline:d(true),storage:{isLocalStorageAvailable:d(false),isCookieStorageAvailable:d(false),isSessionStorageAvailable:d(false)},isBeaconAvailable:d(false),isLegacyDOM:d(false),isUaCHAvailable:d(false),isCryptoAvailable:d(false),isIE11:d(false),isAdBlocked:d(false)};
|
614
617
|
|
615
|
-
const reportingState={isErrorReportingEnabled:d(false),isMetricsReportingEnabled:d(false),
|
618
|
+
const reportingState={isErrorReportingEnabled:d(false),isMetricsReportingEnabled:d(false),breadcrumbs:d([])};
|
616
619
|
|
617
620
|
const sourceConfigState=d(undefined);
|
618
621
|
|
619
|
-
const lifecycleState={activeDataplaneUrl:d(undefined),integrationsCDNPath:d(DEFAULT_INTEGRATION_SDKS_URL),pluginsCDNPath:d(DEFAULT_PLUGINS_URL),sourceConfigUrl:d(undefined),status:d(undefined),initialized:d(false),logLevel:d(
|
622
|
+
const lifecycleState={activeDataplaneUrl:d(undefined),integrationsCDNPath:d(DEFAULT_INTEGRATION_SDKS_URL),pluginsCDNPath:d(DEFAULT_PLUGINS_URL),sourceConfigUrl:d(undefined),status:d(undefined),initialized:d(false),logLevel:d(POST_LOAD_LOG_LEVEL),loaded:d(false),readyCallbacks:d([]),writeKey:d(undefined),dataPlaneUrl:d(undefined)};
|
620
623
|
|
621
624
|
const consentsState={enabled:d(false),initialized:d(false),data:d({}),activeConsentManagerPluginName:d(undefined),preConsent:d({enabled:false}),postConsent:d({}),resolutionStrategy:d('and'),provider:d(undefined),metadata:d(undefined)};
|
622
625
|
|
623
626
|
const metricsState={retries:d(0),dropped:d(0),sent:d(0),queued:d(0),triggered:d(0),metricsServiceUrl:d(undefined)};
|
624
627
|
|
625
|
-
const contextState={app:d({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION,installType:MODULE_TYPE}),traits:d(null),library:d({name:APP_NAME,version:APP_VERSION,snippetVersion:globalThis.RudderSnippetVersion}),userAgent:d(
|
628
|
+
const contextState={app:d({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION,installType:MODULE_TYPE}),traits:d(null),library:d({name:APP_NAME,version:APP_VERSION,snippetVersion:globalThis.RudderSnippetVersion}),userAgent:d(null),device:d(null),network:d(null),os:d({name:'',version:''}),locale:d(null),screen:d({density:0,width:0,height:0,innerWidth:0,innerHeight:0}),'ua-ch':d(undefined),timezone:d(undefined)};
|
626
629
|
|
627
630
|
const nativeDestinationsState={configuredDestinations:d([]),activeDestinations:d([]),loadOnlyIntegrations:d({}),failedDestinations:d([]),loadIntegration:d(true),initializedDestinations:d([]),clientDestinationsReady:d(false),integrationsConfig:d({})};
|
628
631
|
|
@@ -641,48 +644,132 @@ const autoTrackState={enabled:d(false),pageLifecycle:{enabled:d(false),visitId:d
|
|
641
644
|
|
642
645
|
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)};
|
643
646
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
class PluginEngine{plugins=[];byName={};cache={};config={throws:true};constructor(options={},logger){this.config={throws:true,...options};this.logger=logger;}register(plugin,state){if(!plugin.name){const errorMessage=PLUGIN_NAME_MISSING_ERROR(PLUGIN_ENGINE);if(this.config.throws){throw new Error(errorMessage);}else {this.logger?.error(errorMessage,plugin);}}if(this.byName[plugin.name]){const errorMessage=PLUGIN_ALREADY_EXISTS_ERROR(PLUGIN_ENGINE,plugin.name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger?.error(errorMessage);}}this.cache={};this.plugins=this.plugins.slice();let pos=this.plugins.length;this.plugins.forEach((pluginItem,index)=>{if(pluginItem.deps?.includes(plugin.name)){pos=Math.min(pos,index);}});this.plugins.splice(pos,0,plugin);this.byName[plugin.name]=plugin;if(isFunction(plugin.initialize)){plugin.initialize(state);}}unregister(name){const plugin=this.byName[name];if(!plugin){const errorMessage=PLUGIN_NOT_FOUND_ERROR(PLUGIN_ENGINE,name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger?.error(errorMessage);}}const index=this.plugins.indexOf(plugin);if(index===-1){const errorMessage=PLUGIN_ENGINE_BUG_ERROR(PLUGIN_ENGINE,name);if(this.config.throws){throw new Error(errorMessage);}else {this.logger?.error(errorMessage);}}this.cache={};delete this.byName[name];this.plugins=this.plugins.slice();this.plugins.splice(index,1);}getPlugin(name){return this.byName[name];}getPlugins(extPoint){const lifeCycleName=extPoint??'.';if(!this.cache[lifeCycleName]){this.cache[lifeCycleName]=this.plugins.filter(plugin=>{if(plugin.deps?.some(dependency=>!this.byName[dependency])){// If deps not exist, then not load it.
|
648
|
-
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
|
649
|
-
// do some unified pre-process before application starts.
|
650
|
-
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
|
651
|
-
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
|
652
|
-
if(throws){throw err;}else {this.logger?.error(PLUGIN_INVOCATION_ERROR(PLUGIN_ENGINE,extensionPointName,plugin.name),err);}}return null;});}invokeSingle(extPoint,...args){return this.invoke(extPoint,false,...args)[0];}invokeMultiple(extPoint,...args){return this.invoke(extPoint,true,...args);}}const defaultPluginEngine=new PluginEngine({throws:true},defaultLogger);
|
647
|
+
function getDefaultExportFromCjs (x) {
|
648
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
649
|
+
}
|
653
650
|
|
654
|
-
|
651
|
+
var errorStackParser$1 = {exports: {}};
|
652
|
+
|
653
|
+
var stackframe$1 = {exports: {}};
|
654
|
+
|
655
|
+
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;}
|
656
|
+
|
657
|
+
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 {/**
|
658
|
+
* Given an Error object, extract the most information from it.
|
659
|
+
*
|
660
|
+
* @param {Error} error object
|
661
|
+
* @return {Array} of StackFrames
|
662
|
+
*/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)
|
663
|
+
extractLocation:function ErrorStackParser$$extractLocation(urlLike){// Fail-fast but return locations like "(native)"
|
664
|
+
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
|
665
|
+
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
|
666
|
+
// case it has spaces in it, as the string is split on \s+ later on
|
667
|
+
var location=sanitizedLine.match(/ (\(.+\)$)/);// remove the parenthesized location from the line, if it was matched
|
668
|
+
sanitizedLine=location?sanitizedLine.replace(location[0],''):sanitizedLine;// if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
|
669
|
+
// because this line doesn't have function name
|
670
|
+
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
|
671
|
+
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
|
672
|
+
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
|
673
|
+
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;}
|
674
|
+
|
675
|
+
var errorStackParserExports = requireErrorStackParser();
|
676
|
+
const ErrorStackParser = /*@__PURE__*/getDefaultExportFromCjs(errorStackParserExports);
|
677
|
+
|
678
|
+
const GLOBAL_CODE='global code';const normalizeFunctionName=name=>{if(isDefined(name)){return /^global code$/i.test(name)?GLOBAL_CODE:name;}return name;};/**
|
679
|
+
* Takes a stacktrace.js style stackframe (https://github.com/stacktracejs/stackframe)
|
680
|
+
* and returns a Bugsnag compatible stackframe (https://docs.bugsnag.com/api/error-reporting/#json-payload)
|
681
|
+
* @param frame
|
682
|
+
* @returns
|
683
|
+
*/const formatStackframe=frame=>{const f={file:frame.fileName,method:normalizeFunctionName(frame.functionName),lineNumber:frame.lineNumber,columnNumber:frame.columnNumber};// Some instances result in no file:
|
684
|
+
// - non-error exception thrown from global code in FF
|
685
|
+
// This adds one.
|
686
|
+
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
|
687
|
+
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,[]);}};
|
655
688
|
|
656
689
|
/**
|
657
|
-
* Utility
|
658
|
-
*/const
|
659
|
-
|
660
|
-
|
661
|
-
|
690
|
+
* Utility to parse XHR JSON response
|
691
|
+
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
692
|
+
|
693
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Failed to load the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];
|
694
|
+
|
695
|
+
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
696
|
+
* Utility to create request configuration based on default options
|
697
|
+
*/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;};/**
|
698
|
+
* Utility implementation of XHR, fetch cannot be used as it requires explicit
|
699
|
+
* origin allowed values and not wildcard for CORS requests with credentials and
|
700
|
+
* this is not supported by our sourceConfig API
|
701
|
+
*/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
|
702
|
+
return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
703
|
+
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
|
704
|
+
// and the first call to the send method in legacy browsers
|
705
|
+
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});}});
|
706
|
+
|
707
|
+
/**
|
708
|
+
* Service to handle data communication with APIs
|
709
|
+
*/class HttpClient{constructor(logger){this.logger=logger;this.onError=this.onError.bind(this);}init(errorHandler){this.errorHandler=errorHandler;}/**
|
710
|
+
* Implement requests in a blocking way
|
711
|
+
*/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};}}/**
|
712
|
+
* Implement requests in a non-blocking way
|
713
|
+
*/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);}});}/**
|
714
|
+
* Handle errors
|
715
|
+
*/onError(error){this.errorHandler?.onError(error,HTTP_CLIENT);}/**
|
716
|
+
* Set basic authentication header (eg writekey)
|
717
|
+
*/setAuthHeader(value,noBtoa=false){const authVal=noBtoa?value:toBase64(`${value}:`);this.basicAuthHeader=`Basic ${authVal}`;}/**
|
718
|
+
* Clear basic authentication header
|
719
|
+
*/resetAuthHeader(){this.basicAuthHeader=undefined;}}const defaultHttpClient=new HttpClient(defaultLogger);
|
720
|
+
|
721
|
+
const METRICS_PAYLOAD_VERSION='1';
|
722
|
+
|
723
|
+
// Errors from the below scripts are NOT allowed to reach Bugsnag
|
724
|
+
const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
|
725
|
+
];const DEV_HOSTS=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
|
726
|
+
// Potential PII or sensitive data
|
727
|
+
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','instance',// destination instance objects
|
728
|
+
'eventBuffer',// pre-load event buffer (may contain PII)
|
729
|
+
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='__REPOSITORY_URL__';const SOURCE_NAME='js';
|
730
|
+
|
731
|
+
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
|
732
|
+
// so that they show up as separate tabs in the dashboard
|
733
|
+
...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
|
734
|
+
* A function to determine whether the error should be promoted to notify or not
|
735
|
+
* @param {Error} exception
|
736
|
+
* @returns
|
737
|
+
*/const isAllowedToBeNotified=exception=>{const errMsg=exception.message;// Filter out plugin load errors from non-RudderStack CDN URLs
|
738
|
+
if(PLUGINS_LOAD_FAILURE_MESSAGES.some(regex=>regex.test(errMsg))){return errMsg.includes(SDK_CDN_BASE_URL);}// Filter out integration load errors from non-RudderStack CDN URLs
|
739
|
+
if(INTEGRATIONS_LOAD_FAILURE_MESSAGES.some(regex=>regex.test(errMsg))){return errMsg.includes(SDK_CDN_BASE_URL);}return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg));};/**
|
740
|
+
* A function to determine if the error is from Rudder SDK
|
741
|
+
* @param {Error} exception
|
742
|
+
* @returns
|
743
|
+
*/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
|
744
|
+
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
745
|
+
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);};
|
662
746
|
|
663
747
|
/**
|
664
748
|
* A service to handle errors
|
665
749
|
*/class ErrorHandler{// If no logger is passed errors will be thrown as unhandled error
|
666
|
-
constructor(logger
|
667
|
-
|
668
|
-
//
|
669
|
-
if(!
|
670
|
-
|
671
|
-
this.
|
750
|
+
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 errors that are not originated in the SDK.
|
751
|
+
// In case of NPM installations, the errors from the SDK cannot be identified
|
752
|
+
// and will NOT be reported unless they occur in plugins or integrations.
|
753
|
+
if(!isSdkDispatched&&!isSDKError(bsException)){return;}if(state.reporting.isErrorReportingEnabled.value&&isAllowedToBeNotified(bsException)){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};// enrich error payload
|
754
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state);// send it to metrics service
|
755
|
+
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
|
756
|
+
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
757
|
+
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
672
758
|
* Add breadcrumbs to add insight of a user's journey before an error
|
673
759
|
* occurred and send to external error monitoring service via a plugin
|
674
760
|
*
|
675
761
|
* @param {string} breadcrumb breadcrumbs message
|
676
|
-
*/leaveBreadcrumb(breadcrumb){
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
762
|
+
*/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);
|
763
|
+
|
764
|
+
// to next or return the value if it is the last one instead of an array per
|
765
|
+
// plugin that is the normal invoke
|
766
|
+
// TODO: add invoke method for extension point that we know only one plugin can be used. add invokeMultiple and invokeSingle methods
|
767
|
+
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.
|
768
|
+
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
|
769
|
+
// do some unified pre-process before application starts.
|
770
|
+
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
|
771
|
+
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
|
772
|
+
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});
|
686
773
|
|
687
774
|
/**
|
688
775
|
* A function to filter and return non cloud mode destinations
|
@@ -699,8 +786,7 @@ destination.config.useNativeSDK===true);const isHybridModeDestination=destinatio
|
|
699
786
|
|
700
787
|
/**
|
701
788
|
* List of plugin names that are loaded as dynamic imports in modern builds
|
702
|
-
*/const pluginNamesList=['BeaconQueue','Bugsnag'
|
703
|
-
'CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ErrorReporting','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];
|
789
|
+
*/const pluginNamesList=['BeaconQueue','CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];const deprecatedPluginsList=['Bugsnag','ErrorReporting'];
|
704
790
|
|
705
791
|
const remotesMap = {
|
706
792
|
'rudderAnalyticsRemotePlugins':{url:()=>Promise.resolve(window.RudderStackGlobals && window.RudderStackGlobals.app && window.RudderStackGlobals.app.pluginsCDNPath ? `${window.RudderStackGlobals.app.pluginsCDNPath}/rsa-plugins.js` : `https://cdn.rudderlabs.com/v3/modern/plugins//rsa-plugins.js`),format:'esm',from:'vite'}
|
@@ -785,7 +871,7 @@ const remotesMap = {
|
|
785
871
|
|
786
872
|
/**
|
787
873
|
* Get the lazy loaded dynamic import for a plugin name
|
788
|
-
*/const getFederatedModuleImport=pluginName=>{switch(pluginName){case 'BeaconQueue':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./BeaconQueue").then(module=>__federation_method_wrapDefault(module, true));case '
|
874
|
+
*/const getFederatedModuleImport=pluginName=>{switch(pluginName){case 'BeaconQueue':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./BeaconQueue").then(module=>__federation_method_wrapDefault(module, true));case 'CustomConsentManager':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./CustomConsentManager").then(module=>__federation_method_wrapDefault(module, true));case 'DeviceModeDestinations':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./DeviceModeDestinations").then(module=>__federation_method_wrapDefault(module, true));case 'DeviceModeTransformation':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./DeviceModeTransformation").then(module=>__federation_method_wrapDefault(module, true));case 'ExternalAnonymousId':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./ExternalAnonymousId").then(module=>__federation_method_wrapDefault(module, true));case 'GoogleLinker':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./GoogleLinker").then(module=>__federation_method_wrapDefault(module, true));case 'KetchConsentManager':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./KetchConsentManager").then(module=>__federation_method_wrapDefault(module, true));case 'IubendaConsentManager':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./IubendaConsentManager").then(module=>__federation_method_wrapDefault(module, true));case 'NativeDestinationQueue':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./NativeDestinationQueue").then(module=>__federation_method_wrapDefault(module, true));case 'OneTrustConsentManager':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./OneTrustConsentManager").then(module=>__federation_method_wrapDefault(module, true));case 'StorageEncryption':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./StorageEncryption").then(module=>__federation_method_wrapDefault(module, true));case 'StorageEncryptionLegacy':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./StorageEncryptionLegacy").then(module=>__federation_method_wrapDefault(module, true));case 'StorageMigrator':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./StorageMigrator").then(module=>__federation_method_wrapDefault(module, true));case 'XhrQueue':return ()=>__federation_method_getRemote("rudderAnalyticsRemotePlugins" , "./XhrQueue").then(module=>__federation_method_wrapDefault(module, true));default:return undefined;}};/**
|
789
875
|
* Map of active plugin names to their dynamic import
|
790
876
|
*/const federatedModulesBuildPluginImports=activePluginNames=>{const remotePlugins={};activePluginNames.forEach(pluginName=>{if(pluginNamesList.includes(pluginName)){const lazyLoadImport=getFederatedModuleImport(pluginName);if(lazyLoadImport){remotePlugins[pluginName]=lazyLoadImport;}}});return remotePlugins;};
|
791
877
|
|
@@ -812,21 +898,12 @@ state.lifecycle.status.value='pluginsReady';});}});}/**
|
|
812
898
|
* Determine the list of plugins that should be loaded based on sourceConfig & load options
|
813
899
|
*/// eslint-disable-next-line class-methods-use-this
|
814
900
|
getPluginsToLoadBasedOnConfig(){// This contains the default plugins if load option has been omitted by user
|
815
|
-
let pluginsToLoadFromConfig=state.plugins.pluginsToLoadFromConfig.value;if(!pluginsToLoadFromConfig){return [];}//
|
816
|
-
//
|
817
|
-
|
818
|
-
// if (deprecatedPluginsList.includes(pluginName)) {
|
819
|
-
// this.logger?.warn(DEPRECATED_PLUGIN_WARNING(PLUGINS_MANAGER, pluginName));
|
820
|
-
// return false;
|
821
|
-
// }
|
822
|
-
// return true;
|
823
|
-
// });
|
824
|
-
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
|
825
|
-
},{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
|
826
|
-
);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));}}/**
|
901
|
+
let pluginsToLoadFromConfig=state.plugins.pluginsToLoadFromConfig.value;if(!pluginsToLoadFromConfig){return [];}// Filter deprecated plugins
|
902
|
+
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
|
903
|
+
);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));}}/**
|
827
904
|
* Determine the list of plugins that should be activated
|
828
905
|
*/setActivePlugins(){const pluginsToLoad=this.getPluginsToLoadBasedOnConfig();// Merging available mandatory and optional plugin name list
|
829
|
-
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.
|
906
|
+
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;});}/**
|
830
907
|
* Register plugins that are direct imports to PluginEngine
|
831
908
|
*/registerLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{if(isFunction(localPlugin)&&state.plugins.activePlugins.value.includes(localPlugin().name)){this.register([localPlugin()]);}});}/**
|
832
909
|
* Register plugins that are dynamic imports to PluginEngine
|
@@ -840,39 +917,7 @@ state.plugins.failedPlugins.value=[...state.plugins.failedPlugins.value,remotePl
|
|
840
917
|
*/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
|
841
918
|
unregisterLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{try{this.engine.unregister(localPlugin().name);}catch(e){this.onError(e);}});}/**
|
842
919
|
* Handle errors
|
843
|
-
*/onError(error,customMessage){
|
844
|
-
|
845
|
-
/**
|
846
|
-
* Utility to parse XHR JSON response
|
847
|
-
*/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;};
|
848
|
-
|
849
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';
|
850
|
-
|
851
|
-
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
852
|
-
* Utility to create request configuration based on default options
|
853
|
-
*/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;};/**
|
854
|
-
* Utility implementation of XHR, fetch cannot be used as it requires explicit
|
855
|
-
* origin allowed values and not wildcard for CORS requests with credentials and
|
856
|
-
* this is not supported by our sourceConfig API
|
857
|
-
*/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
|
858
|
-
return;}}const xhr=new XMLHttpRequest();// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
859
|
-
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
|
860
|
-
// and the first call to the send method in legacy browsers
|
861
|
-
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});}});
|
862
|
-
|
863
|
-
/**
|
864
|
-
* Service to handle data communication with APIs
|
865
|
-
*/class HttpClient{hasErrorHandler=false;constructor(errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.hasErrorHandler=Boolean(this.errorHandler);this.onError=this.onError.bind(this);}/**
|
866
|
-
* Implement requests in a blocking way
|
867
|
-
*/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};}}/**
|
868
|
-
* Implement requests in a non-blocking way
|
869
|
-
*/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);}});}/**
|
870
|
-
* Handle errors
|
871
|
-
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,HTTP_CLIENT);}else {throw error;}}/**
|
872
|
-
* Set basic authentication header (eg writekey)
|
873
|
-
*/setAuthHeader(value,noBtoa=false){const authVal=noBtoa?value:toBase64(`${value}:`);this.basicAuthHeader=`Basic ${authVal}`;}/**
|
874
|
-
* Clear basic authentication header
|
875
|
-
*/resetAuthHeader(){this.basicAuthHeader=undefined;}}const defaultHttpClient=new HttpClient(defaultErrorHandler,defaultLogger);
|
920
|
+
*/onError(error,customMessage){this.errorHandler.onError(error,PLUGINS_MANAGER,customMessage);}}
|
876
921
|
|
877
922
|
const COOKIE_STORAGE='cookieStorage';const LOCAL_STORAGE='localStorage';const SESSION_STORAGE='sessionStorage';const MEMORY_STORAGE='memoryStorage';const NO_STORAGE='none';
|
878
923
|
|
@@ -901,12 +946,12 @@ pairs.forEach(pairItem=>{pair=pairItem.split('=');const keyName=pair[0]?decode(p
|
|
901
946
|
*/// eslint-disable-next-line func-names
|
902
947
|
const cookie=function(name,value,options,logger){switch(arguments.length){case 4:case 3:case 2:return set(name,value,options,logger);case 1:if(name){return get(name);}return all();default:return all();}};
|
903
948
|
|
904
|
-
const detectAdBlockers=
|
949
|
+
const detectAdBlockers=httpClient=>{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
905
950
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
906
951
|
// Only the extra query param should make it vulnerable to adblockers
|
907
952
|
// This will work even if the users proxies it.
|
908
953
|
// The edge case where this doesn't work is when HEAD method is not allowed by the server (user's)
|
909
|
-
const baseUrl=new URL(state.lifecycle.sourceConfigUrl.value);const url=`${baseUrl.origin}${baseUrl.pathname}?view=ad`;
|
954
|
+
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
|
910
955
|
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
|
911
956
|
// Often adblockers instead of blocking the request, they redirect it to an internal URL
|
912
957
|
state.capabilities.isAdBlocked.value=details?.error!==undefined||details?.xhr?.responseURL!==url;}});};
|
@@ -974,10 +1019,6 @@ keys(){return Object.keys(cookie());}}
|
|
974
1019
|
* A storage utility to retain values in memory via Storage interface
|
975
1020
|
*/class InMemoryStorage{isEnabled=true;length=0;data={};constructor(options,logger){this.options=getDefaultInMemoryStorageOptions();this.logger=logger;this.configure(options??{});}configure(options){this.options=mergeDeepRight(this.options,options);this.isEnabled=Boolean(this.options.enabled);return this.options;}setItem(key,value){this.data[key]=value;this.length=Object.keys(this.data).length;return value;}getItem(key){if(key in this.data){return this.data[key];}return null;}removeItem(key){if(key in this.data){delete this.data[key];}this.length=Object.keys(this.data).length;return null;}clear(){this.data={};this.length=0;}key(index){const curKeys=this.keys();return curKeys[index]??null;}keys(){return Object.keys(this.data);}}const defaultInMemoryStorage=new InMemoryStorage({},defaultLogger);
|
976
1021
|
|
977
|
-
function getDefaultExportFromCjs (x) {
|
978
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
979
|
-
}
|
980
|
-
|
981
1022
|
var store$2 = {exports: {}};
|
982
1023
|
|
983
1024
|
var store$1=store$2.exports;var hasRequiredStore;function requireStore(){if(hasRequiredStore)return store$2.exports;hasRequiredStore=1;(function(module,exports){(function(global,factory){module.exports=factory();})(store$1,function(){function isJSON(obj){obj=JSON.stringify(obj);if(!/^\{[\s\S]*\}$/.test(obj)){return false;}return true;}function stringify(val){return val===undefined||typeof val==="function"?val+'':JSON.stringify(val);}function deserialize(value){if(typeof value!=='string'){return undefined;}try{return JSON.parse(value);}catch(e){return value;}}function isFunction(value){return {}.toString.call(value)==="[object Function]";}function isArray(value){return Object.prototype.toString.call(value)==="[object Array]";}// https://github.com/jaywcjlove/store.js/pull/8
|
@@ -1020,7 +1061,7 @@ if(this.isSupportAvailable){this.store=globalThis.sessionStorage;}this.isEnabled
|
|
1020
1061
|
|
1021
1062
|
/**
|
1022
1063
|
* Store Implementation with dedicated storage
|
1023
|
-
*/class Store{
|
1064
|
+
*/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;}/**
|
1024
1065
|
* Ensure the key is valid and with correct format
|
1025
1066
|
*/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
|
1026
1067
|
let compoundKey;Object.values(validKeys).forEach(validKeyName=>{if(validKeyName===key){compoundKey=noCompoundKey?key:[name,id,key].join('.');}});return compoundKey;}/**
|
@@ -1033,13 +1074,13 @@ Object.keys(validKeys).forEach(key=>{const value=this.get(validKeys[key]);const
|
|
1033
1074
|
this.remove(key);});this.engine=inMemoryStorage;}/**
|
1034
1075
|
* Set value by key.
|
1035
1076
|
*/set(key,value){const validKey=this.createValidKey(key);if(!validKey){return;}try{// storejs that is used in localstorage engine already stringifies json
|
1036
|
-
this.engine.setItem(validKey,this.encrypt(stringifyWithoutCircular(value,false,[],this.logger)));}catch(err){if(isStorageQuotaExceeded(err)){this.logger
|
1077
|
+
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
|
1037
1078
|
this.swapQueueStoreToInMemoryEngine();// and save it there
|
1038
1079
|
this.set(key,value);}else {this.onError(getMutatedError(err,STORE_DATA_SAVE_ERROR(key)));}}}/**
|
1039
1080
|
* Get by Key.
|
1040
1081
|
*/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
|
1041
1082
|
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
|
1042
|
-
if(isString(decryptedValue)&&decryptedValue.startsWith('RudderEncrypt:')){this.logger
|
1083
|
+
if(isString(decryptedValue)&&decryptedValue.startsWith('RudderEncrypt:')){this.logger.warn(BAD_COOKIES_WARNING(key));}return null;}}/**
|
1043
1084
|
* Remove by Key.
|
1044
1085
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
1045
1086
|
* Get original engine
|
@@ -1051,13 +1092,13 @@ if(isString(decryptedValue)&&decryptedValue.startsWith('RudderEncrypt:')){this.l
|
|
1051
1092
|
* Extension point to use with encryption plugins
|
1052
1093
|
*/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??'';}/**
|
1053
1094
|
* Handle errors
|
1054
|
-
*/onError(error){
|
1095
|
+
*/onError(error){this.errorHandler.onError(error,`Store ${this.id}`);}}
|
1055
1096
|
|
1056
1097
|
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;};
|
1057
1098
|
|
1058
1099
|
/**
|
1059
1100
|
* A service to manage stores & available storage client configurations
|
1060
|
-
*/class StoreManager{stores={};isInitialized=false;
|
1101
|
+
*/class StoreManager{stores={};isInitialized=false;constructor(pluginsManager,errorHandler,logger){this.errorHandler=errorHandler;this.logger=logger;this.pluginsManager=pluginsManager;}/**
|
1061
1102
|
* Configure available storage client instances
|
1062
1103
|
*/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;}/**
|
1063
1104
|
* Create store to persist data used by the SDK like session, used details etc
|
@@ -1065,16 +1106,14 @@ const getStorageTypeFromPreConsentIfApplicable=(state,sessionKey)=>{let overridd
|
|
1065
1106
|
// TODO: should we pass the keys for all in order to validate or leave free as v1.1?
|
1066
1107
|
// Initializing all the enabled store because previous user data might be in different storage
|
1067
1108
|
// that needs auto migration
|
1068
|
-
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
|
1109
|
+
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
|
1069
1110
|
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
|
1070
1111
|
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
|
1071
|
-
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
|
1112
|
+
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;}/**
|
1072
1113
|
* Create a new store
|
1073
1114
|
*/setStore(storeConfig){const storageEngine=getStorageEngine(storeConfig.type);this.stores[storeConfig.id]=new Store(storeConfig,storageEngine,this.pluginsManager);return this.stores[storeConfig.id];}/**
|
1074
1115
|
* Retrieve a store
|
1075
|
-
*/getStore(id){return this.stores[id];}
|
1076
|
-
* Handle errors
|
1077
|
-
*/onError(error){if(this.hasErrorHandler){this.errorHandler?.onError(error,STORE_MANAGER);}else {throw error;}}}
|
1116
|
+
*/getStore(id){return this.stores[id];}}
|
1078
1117
|
|
1079
1118
|
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
|
1080
1119
|
const urlObj=new URL(url);// Extract the host and protocol
|
@@ -1144,7 +1183,7 @@ validOptions.consentManagement=mergeDeepRight(clonedOptions.consentManagement,{e
|
|
1144
1183
|
* @param consentManagementOpts consent management options
|
1145
1184
|
* @param logger logger instance
|
1146
1185
|
* @returns Corresponding provider and plugin name of the selected consent manager from the supported consent managers
|
1147
|
-
*/const getConsentManagerInfo=(consentManagementOpts,logger)=>{let{provider}=consentManagementOpts;const consentManagerPluginName=provider?ConsentManagersToPluginNameMap[provider]:undefined;if(provider&&!consentManagerPluginName){logger
|
1186
|
+
*/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
|
1148
1187
|
provider=undefined;}return {provider,consentManagerPluginName};};/**
|
1149
1188
|
* Validates and converts the consent management options into a normalized format
|
1150
1189
|
* @param consentManagementOpts Consent management options provided by the user
|
@@ -1180,10 +1219,10 @@ if(curHost!==dataServiceHost){cookieOptions={...cookieOptions,samesite:'None',se
|
|
1180
1219
|
* If the sameDomainCookiesOnly flag is not set and the cookie domain is provided(not top level domain),
|
1181
1220
|
* and the data service host is different from the provided cookie domain, then we disable server-side cookies
|
1182
1221
|
* ex: provided cookie domain: 'random.com', data service host: 'sub.example.com'
|
1183
|
-
*/if(!sameDomainCookiesOnly&&useExactDomain&&dataServiceHost!==removeLeadingPeriod(providedCookieDomain)){sscEnabled=false;logger
|
1184
|
-
logger
|
1185
|
-
const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger
|
1186
|
-
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
|
1222
|
+
*/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
|
1223
|
+
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
|
1224
|
+
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
|
1225
|
+
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
|
1187
1226
|
// if it is not already initialized and
|
1188
1227
|
// if consent management is enabled
|
1189
1228
|
enabled:state.loadOptions.value.preConsent?.enabled===true&&initialized===false&&enabled===true,storage:{strategy:storageStrategy},events:{delivery:eventsDeliveryType}};});};/**
|
@@ -1191,7 +1230,7 @@ enabled:state.loadOptions.value.preConsent?.enabled===true&&initialized===false&
|
|
1191
1230
|
* @param resp Source config response
|
1192
1231
|
* @param logger Logger instance
|
1193
1232
|
*/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
|
1194
|
-
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
|
1233
|
+
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}`;};
|
1195
1234
|
|
1196
1235
|
/**
|
1197
1236
|
* A function that determines the base URL for the integrations or plugins SDK
|
@@ -1201,9 +1240,10 @@ if(state.consents.provider.value==='custom'){resolutionStrategy=undefined;}r(()=
|
|
1201
1240
|
* @param currentSdkVersion The current version of the SDK
|
1202
1241
|
* @param lockVersion Flag to lock the version of the component
|
1203
1242
|
* @param urlFromLoadOptions The URL provided by the user in the load options
|
1243
|
+
* @param logger Logger instance
|
1204
1244
|
* @returns The base URL for the integrations or plugins SDK
|
1205
|
-
*/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
|
1206
|
-
if(urlFromLoadOptions){if(!isValidURL(urlFromLoadOptions)){
|
1245
|
+
*/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
|
1246
|
+
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
|
1207
1247
|
if(state.context.app.value.installType==='cdn'){const sdkURL=getSDKUrl();if(sdkURL){// Extract the base URL from the core SDK file URL
|
1208
1248
|
// and append the path suffix to it
|
1209
1249
|
sdkComponentBaseURL=sdkURL.split('/').slice(0,-1).concat(pathSuffix).join('/');}}}// If the version needs to be locked, then replace the major version in the URL
|
@@ -1213,41 +1253,43 @@ if(lockVersion){sdkComponentBaseURL=sdkComponentBaseURL.replace(new RegExp(`/${C
|
|
1213
1253
|
* @param currentSdkVersion Current SDK version
|
1214
1254
|
* @param lockIntegrationsVersion Flag to lock the integrations version
|
1215
1255
|
* @param integrationsUrlFromLoadOptions URL to load the integrations from as provided by the user
|
1256
|
+
* @param logger Logger instance
|
1216
1257
|
* @returns
|
1217
|
-
*/const getIntegrationsCDNPath=(currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions)=>getSDKComponentBaseURL('integrations',CDN_INT_DIR,DEFAULT_INTEGRATION_SDKS_URL,currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions);/**
|
1258
|
+
*/const getIntegrationsCDNPath=(currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions,logger)=>getSDKComponentBaseURL('integrations',CDN_INT_DIR,DEFAULT_INTEGRATION_SDKS_URL,currentSdkVersion,lockIntegrationsVersion,integrationsUrlFromLoadOptions,logger);/**
|
1218
1259
|
* A function that determines plugins SDK loading path
|
1219
1260
|
* @param currentSdkVersion Current SDK version
|
1220
1261
|
* @param lockPluginsVersion Flag to lock the plugins version
|
1221
1262
|
* @param pluginsUrlFromLoadOptions URL to load the plugins from as provided by the user
|
1263
|
+
* @param logger Logger instance
|
1222
1264
|
* @returns Final plugins CDN path
|
1223
|
-
*/const getPluginsCDNPath=(currentSdkVersion,lockPluginsVersion,pluginsUrlFromLoadOptions)=>getSDKComponentBaseURL('plugins',CDN_PLUGINS_DIR,DEFAULT_PLUGINS_URL,currentSdkVersion,lockPluginsVersion,pluginsUrlFromLoadOptions);
|
1265
|
+
*/const getPluginsCDNPath=(currentSdkVersion,lockPluginsVersion,pluginsUrlFromLoadOptions,logger)=>getSDKComponentBaseURL('plugins',CDN_PLUGINS_DIR,DEFAULT_PLUGINS_URL,currentSdkVersion,lockPluginsVersion,pluginsUrlFromLoadOptions,logger);
|
1224
1266
|
|
1225
|
-
class ConfigManager{
|
1267
|
+
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);});}/**
|
1226
1268
|
* A function to validate, construct and store loadOption, lifecycle, source and destination
|
1227
1269
|
* config related information in global state
|
1228
|
-
*/init(){
|
1229
|
-
const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,destSDKBaseURL)
|
1230
|
-
|
1270
|
+
*/init(){const{logLevel,configUrl,lockIntegrationsVersion,lockPluginsVersion,destSDKBaseURL,pluginsSDKBaseURL,integrations}=state.loadOptions.value;// determine the path to fetch integration SDK from
|
1271
|
+
const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,destSDKBaseURL,this.logger);if(isNull(intgCdnUrl)){return;}let pluginsCDNPath;{// determine the path to fetch remote plugins from
|
1272
|
+
pluginsCDNPath=getPluginsCDNPath(APP_VERSION,lockPluginsVersion,pluginsSDKBaseURL,this.logger);}if(pluginsCDNPath===null){return;}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
|
1231
1273
|
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
|
1232
1274
|
state.nativeDestinations.loadOnlyIntegrations.value=integrations;});this.getConfig();}/**
|
1233
1275
|
* Handle errors
|
1234
|
-
*/onError(error,customMessage
|
1276
|
+
*/onError(error,customMessage){this.errorHandler.onError(error,CONFIG_MANAGER,customMessage);}/**
|
1235
1277
|
* A callback function that is executed once we fetch the source config response.
|
1236
1278
|
* Use to construct and store information that are dependent on the sourceConfig.
|
1237
1279
|
*/processConfig(response,details){// TODO: add retry logic with backoff based on rejectionDetails.xhr.status
|
1238
1280
|
// We can use isErrRetryable utility method
|
1239
|
-
if(!response){this.onError(
|
1240
|
-
if(res.source.enabled===false){this.logger
|
1281
|
+
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
|
1282
|
+
if(res.source.enabled===false){this.logger.error(SOURCE_DISABLED_ERROR);return;}// set the values in state for reporting slice
|
1241
1283
|
updateReportingState(res);const nativeDestinations=res.source.destinations.length>0?filterEnabledDestination(res.source.destinations):[];// set in the state --> source, destination, lifecycle, reporting
|
1242
1284
|
r(()=>{// set source related information in state
|
1243
|
-
state.source.value={config:res.source.config,id:res.source.id,workspaceId:res.source.workspaceId};// set device mode destination related information in state
|
1285
|
+
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
|
1244
1286
|
state.nativeDestinations.configuredDestinations.value=nativeDestinations;// set the desired optional plugins
|
1245
1287
|
state.plugins.pluginsToLoadFromConfig.value=state.loadOptions.value.plugins??[];updateConsentsState(res);// set application lifecycle state
|
1246
1288
|
state.lifecycle.status.value='configured';});}/**
|
1247
1289
|
* A function to fetch source config either from /sourceConfig endpoint
|
1248
1290
|
* or from getSourceConfig load option
|
1249
1291
|
* @returns
|
1250
|
-
*/getConfig(){const sourceConfigFunc=state.loadOptions.value.getSourceConfig;if(sourceConfigFunc){if(!isFunction(sourceConfigFunc)){
|
1292
|
+
*/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
|
1251
1293
|
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
|
1252
1294
|
this.httpClient.getAsyncData({url:state.lifecycle.sourceConfigUrl.value,options:{headers:{'Content-Type':undefined}},callback:this.processConfig});}}}
|
1253
1295
|
|
@@ -1282,16 +1324,16 @@ if(urlObj.search===''){pageUrl=canonicalUrl+search;}else {pageUrl=canonicalUrl;}
|
|
1282
1324
|
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
|
1283
1325
|
const POLYFILL_SCRIPT_ID='rudderstackPolyfill';
|
1284
1326
|
|
1285
|
-
class CapabilitiesManager{constructor(errorHandler,logger){this.
|
1327
|
+
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();}/**
|
1286
1328
|
* Detect supported capabilities and set values in state
|
1287
1329
|
*/// eslint-disable-next-line class-methods-use-this
|
1288
1330
|
detectBrowserCapabilities(){r(()=>{// Storage related details
|
1289
1331
|
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
|
1290
1332
|
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
|
1291
1333
|
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
|
1292
|
-
E(()=>{if(state.loadOptions.value.sendAdblockPage===true&&state.lifecycle.sourceConfigUrl.value!==undefined){detectAdBlockers(this.
|
1334
|
+
E(()=>{if(state.loadOptions.value.sendAdblockPage===true&&state.lifecycle.sourceConfigUrl.value!==undefined){detectAdBlockers(this.httpClient);}});}/**
|
1293
1335
|
* Detect if polyfills are required and then load script from polyfill URL
|
1294
|
-
*/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
|
1336
|
+
*/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
|
1295
1337
|
// NOTE: we're not putting this into RudderStackGlobals as providing the property path to the callback function in the polyfill URL is not possible
|
1296
1338
|
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
|
1297
1339
|
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();}}/**
|
@@ -1302,7 +1344,7 @@ delete globalThis[polyfillCallbackName];};globalThis[polyfillCallbackName]=polyf
|
|
1302
1344
|
onReady(){this.detectBrowserCapabilities();state.lifecycle.status.value='browserCapabilitiesReady';}/**
|
1303
1345
|
* Handles error
|
1304
1346
|
* @param error The error object
|
1305
|
-
*/onError(error){
|
1347
|
+
*/onError(error){this.errorHandler.onError(error,CAPABILITIES_MANAGER);}}
|
1306
1348
|
|
1307
1349
|
const CHANNEL='web';// These are the top-level elements in the standard RudderStack event spec
|
1308
1350
|
const TOP_LEVEL_ELEMENTS=['integrations','anonymousId','originalTimestamp'];// Reserved elements in the context of standard RudderStack event spec
|
@@ -1336,7 +1378,7 @@ const MIN_SESSION_ID_LENGTH=10;/**
|
|
1336
1378
|
* @param {number} sessionId
|
1337
1379
|
* @param logger logger
|
1338
1380
|
* @returns
|
1339
|
-
*/const isManualSessionIdValid=(sessionId,logger)=>{if(!sessionId||!isPositiveInteger(sessionId)||!hasMinLength(MIN_SESSION_ID_LENGTH,sessionId)){logger
|
1381
|
+
*/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;};/**
|
1340
1382
|
* A function to generate new auto tracking session
|
1341
1383
|
* @param sessionTimeout current timestamp
|
1342
1384
|
* @returns SessionInfo
|
@@ -1367,7 +1409,7 @@ const curPageProps=getDefaultPageProperties();Object.keys(curPageProps).forEach(
|
|
1367
1409
|
* @param obj Generic object
|
1368
1410
|
* @param parentKeyPath Object's parent key path
|
1369
1411
|
* @param logger Logger instance
|
1370
|
-
*/const checkForReservedElementsInObject=(obj,parentKeyPath,logger)=>{if(isObjectLiteralAndNotNull(obj)){Object.keys(obj).forEach(property=>{if(RESERVED_ELEMENTS.includes(property)||RESERVED_ELEMENTS.includes(property.toLowerCase())){logger
|
1412
|
+
*/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));}});}};/**
|
1371
1413
|
* Checks for reserved keys in traits, properties, and contextual traits
|
1372
1414
|
* @param rudderEvent Generated rudder event
|
1373
1415
|
* @param logger Logger instance
|
@@ -1384,13 +1426,13 @@ rudderEvent.originalTimestamp=options.originalTimestamp;}};/**
|
|
1384
1426
|
* @param rudderContext Generated rudder event
|
1385
1427
|
* @param options API options
|
1386
1428
|
* @param logger Logger instance
|
1387
|
-
*/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;};/**
|
1429
|
+
*/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;};/**
|
1388
1430
|
* Updates rudder event object with data from the API options
|
1389
1431
|
* @param rudderEvent Generated rudder event
|
1390
1432
|
* @param options API options
|
1391
|
-
*/const processOptions=(rudderEvent,options)=>{// Only allow object type for options
|
1433
|
+
*/const processOptions=(rudderEvent,options,logger)=>{// Only allow object type for options
|
1392
1434
|
if(isObjectLiteralAndNotNull(options)){updateTopLevelEventElements(rudderEvent,options);// eslint-disable-next-line no-param-reassign
|
1393
|
-
rudderEvent.context=getMergedContext(rudderEvent.context,options);}};/**
|
1435
|
+
rudderEvent.context=getMergedContext(rudderEvent.context,options,logger);}};/**
|
1394
1436
|
* Returns the final integrations config for the event based on the global config and event's config
|
1395
1437
|
* @param integrationsConfig Event's integrations config
|
1396
1438
|
* @returns Final integrations config
|
@@ -1409,7 +1451,7 @@ commonEventData.anonymousId=generateAnonymousId();}else {// Type casting to stri
|
|
1409
1451
|
commonEventData.anonymousId=state.session.anonymousId.value;}// set truly anonymous tracking flag
|
1410
1452
|
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
|
1411
1453
|
// matching with v1.1 payload
|
1412
|
-
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
|
1454
|
+
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
|
1413
1455
|
processedEvent.integrations=getEventIntegrationsConfig(processedEvent.integrations);return processedEvent;};
|
1414
1456
|
|
1415
1457
|
class RudderEventFactory{constructor(logger){this.logger=logger;}/**
|
@@ -1443,7 +1485,7 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
1443
1485
|
* Generates a new RudderEvent object based on the user-input fields
|
1444
1486
|
* @param event API event parameters object
|
1445
1487
|
* @returns A RudderEvent object
|
1446
|
-
*/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;}}
|
1488
|
+
*/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;}}
|
1447
1489
|
|
1448
1490
|
/**
|
1449
1491
|
* A service to generate valid event payloads and queue them for processing
|
@@ -1453,17 +1495,14 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
1453
1495
|
* @param userSessionManager UserSession Manager instance
|
1454
1496
|
* @param errorHandler Error handler object
|
1455
1497
|
* @param logger Logger object
|
1456
|
-
*/constructor(eventRepository,userSessionManager,errorHandler,logger){this.eventRepository=eventRepository;this.userSessionManager=userSessionManager;this.errorHandler=errorHandler;this.logger=logger;this.eventFactory=new RudderEventFactory(this.logger);
|
1498
|
+
*/constructor(eventRepository,userSessionManager,errorHandler,logger){this.eventRepository=eventRepository;this.userSessionManager=userSessionManager;this.errorHandler=errorHandler;this.logger=logger;this.eventFactory=new RudderEventFactory(this.logger);}/**
|
1457
1499
|
* Initializes the event manager
|
1458
1500
|
*/init(){this.eventRepository.init();}resume(){this.eventRepository.resume();}/**
|
1459
1501
|
* Consumes a new incoming event
|
1460
1502
|
* @param event Incoming event data
|
1461
|
-
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);
|
1462
|
-
* Handles error
|
1463
|
-
* @param error The error object
|
1464
|
-
*/onError(error,customMessage,shouldAlwaysThrow){if(this.errorHandler){this.errorHandler.onError(error,EVENT_MANAGER,customMessage,shouldAlwaysThrow);}else {throw error;}}}
|
1503
|
+
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
1465
1504
|
|
1466
|
-
class UserSessionManager{constructor(
|
1505
|
+
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={};}/**
|
1467
1506
|
* Initialize User session with values from storage
|
1468
1507
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
1469
1508
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
@@ -1474,12 +1513,12 @@ if(state.session.sessionInfo.value.autoTrack){this.startOrRenewAutoTracking(stat
|
|
1474
1513
|
// as those values indicate there is no need for migration or
|
1475
1514
|
// migration failed
|
1476
1515
|
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
|
1477
|
-
if(!autoTrack){return {autoTrack};}let timeout;const configuredSessionTimeout=state.loadOptions.value.sessions?.timeout;if(!isPositiveInteger(configuredSessionTimeout)){this.logger
|
1516
|
+
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
|
1478
1517
|
// and will proceed with it
|
1479
|
-
if(timeout>0&&timeout<MIN_SESSION_TIMEOUT_MS){this.logger
|
1518
|
+
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};}/**
|
1480
1519
|
* Handles error
|
1481
1520
|
* @param error The error object
|
1482
|
-
*/onError(error,customMessage){
|
1521
|
+
*/onError(error,customMessage){this.errorHandler.onError(error,USER_SESSION_MANAGER,customMessage);}/**
|
1483
1522
|
* A function to encrypt the cookie value and return the encrypted data
|
1484
1523
|
* @param cookiesData
|
1485
1524
|
* @param store
|
@@ -1494,7 +1533,7 @@ if(timeout>0&&timeout<MIN_SESSION_TIMEOUT_MS){this.logger?.warn(TIMEOUT_NOT_RECO
|
|
1494
1533
|
* @param value encrypted cookie value
|
1495
1534
|
*/setServerSideCookies(cookiesData,cb,store){try{// encrypt cookies values
|
1496
1535
|
const encryptedCookieData=this.getEncryptedCookieData(cookiesData,store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
1497
|
-
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
|
1536
|
+
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);}});}}/**
|
1498
1537
|
* A function to sync values in storage
|
1499
1538
|
* @param sessionKey
|
1500
1539
|
* @param value
|
@@ -1598,7 +1637,7 @@ this.setAnonymousId();}if(noNewSessionStart){return;}if(autoTrack){session.sessi
|
|
1598
1637
|
|
1599
1638
|
/**
|
1600
1639
|
* Plugins to be loaded in the plugins loadOption is not defined
|
1601
|
-
*/const defaultOptionalPluginsList=['BeaconQueue','
|
1640
|
+
*/const defaultOptionalPluginsList=['BeaconQueue','CustomConsentManager','DeviceModeDestinations','DeviceModeTransformation','ExternalAnonymousId','GoogleLinker','IubendaConsentManager','KetchConsentManager','NativeDestinationQueue','OneTrustConsentManager','StorageEncryption','StorageEncryptionLegacy','StorageMigrator','XhrQueue'];
|
1602
1641
|
|
1603
1642
|
const normalizeLoadOptions=(loadOptionsFromState,loadOptions)=>{// TODO: Maybe add warnings for invalid values
|
1604
1643
|
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;};
|
@@ -1619,6 +1658,8 @@ const DATA_PLANE_QUEUE_EXT_POINT_PREFIX='dataplaneEventsQueue';const DESTINATION
|
|
1619
1658
|
// In general, the preference is given to the event's integrations config
|
1620
1659
|
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');
|
1621
1660
|
|
1661
|
+
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));}};
|
1662
|
+
|
1622
1663
|
/**
|
1623
1664
|
* Event repository class responsible for queuing events for further processing and delivery
|
1624
1665
|
*/class EventRepository{/**
|
@@ -1627,9 +1668,9 @@ const destinationsIntgConfig=state.nativeDestinations.integrationsConfig.value;c
|
|
1627
1668
|
* @param storeManager Store Manager instance
|
1628
1669
|
* @param errorHandler Error handler object
|
1629
1670
|
* @param logger Logger object
|
1630
|
-
*/constructor(pluginsManager,storeManager,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.
|
1671
|
+
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;}/**
|
1631
1672
|
* Initializes the event repository
|
1632
|
-
*/init(){
|
1673
|
+
*/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
|
1633
1674
|
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
|
1634
1675
|
// However, events will be enqueued for now.
|
1635
1676
|
// At the time of processing the events, the integrations config data from destinations
|
@@ -1639,15 +1680,8 @@ if(state.loadOptions.value.bufferDataPlaneEventsUntilReady===true){timeoutId=glo
|
|
1639
1680
|
* Enqueues the event for processing
|
1640
1681
|
* @param event RudderEvent object
|
1641
1682
|
* @param callback API callback function
|
1642
|
-
*/enqueue(event,callback){
|
1643
|
-
|
1644
|
-
// to ensure the mutated (if any) event is sent to the callback
|
1645
|
-
callback?.(dpQEvent);}catch(error){this.onError(error,API_CALLBACK_INVOKE_ERROR);}}/**
|
1646
|
-
* Handles error
|
1647
|
-
* @param error The error object
|
1648
|
-
* @param customMessage a message
|
1649
|
-
* @param shouldAlwaysThrow if it should throw or use logger
|
1650
|
-
*/onError(error,customMessage,shouldAlwaysThrow){if(this.errorHandler){this.errorHandler.onError(error,EVENT_REPOSITORY,customMessage,shouldAlwaysThrow);}else {throw error;}}}
|
1683
|
+
*/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
|
1684
|
+
const apiName=`${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;safelyInvokeCallback(callback,[dpQEvent],apiName,this.logger);}}
|
1651
1685
|
|
1652
1686
|
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);
|
1653
1687
|
|
@@ -1655,11 +1689,11 @@ const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{
|
|
1655
1689
|
* Analytics class with lifecycle based on state ad user triggered events
|
1656
1690
|
*/class Analytics{/**
|
1657
1691
|
* Initialize services and components or use default ones if singletons
|
1658
|
-
*/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);
|
1692
|
+
*/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);}/**
|
1659
1693
|
* Start application lifecycle if not already started
|
1660
1694
|
*/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
|
1661
1695
|
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
|
1662
|
-
this.logger
|
1696
|
+
this.logger.setMinLogLevel(state.loadOptions.value.logLevel??POST_LOAD_LOG_LEVEL);// Expose state to global objects
|
1663
1697
|
setExposedGlobal('state',state,writeKey);// Configure initial config of any services or components here
|
1664
1698
|
// State application lifecycle
|
1665
1699
|
this.startLifecycle();}// Start lifecycle methods
|
@@ -1673,11 +1707,11 @@ if(state.consents.preConsent.value.enabled===true){state.lifecycle.status.value=
|
|
1673
1707
|
* Enqueue in SDK preload buffer events, used from preloadBuffer component
|
1674
1708
|
*/enqueuePreloadBufferEvents(bufferedEvents){if(Array.isArray(bufferedEvents)){bufferedEvents.forEach(bufferedEvent=>this.preloadBuffer.enqueue(clone(bufferedEvent)));}}/**
|
1675
1709
|
* Process the buffer preloaded events by passing their arguments to the respective facade methods
|
1676
|
-
*/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.
|
1710
|
+
*/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);}/**
|
1677
1711
|
* Load configuration
|
1678
1712
|
*/loadConfig(){if(state.lifecycle.writeKey.value){this.httpClient.setAuthHeader(state.lifecycle.writeKey.value);}this.configManager?.init();}/**
|
1679
1713
|
* Initialize the storage and event queue
|
1680
|
-
*/onPluginsReady(){
|
1714
|
+
*/onPluginsReady(){// Initialize storage
|
1681
1715
|
this.storeManager?.init();this.userSessionManager?.init();// Initialize the appropriate consent manager plugin
|
1682
1716
|
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
|
1683
1717
|
this.eventManager?.init();// Mark the SDK as initialized
|
@@ -1688,15 +1722,15 @@ state.lifecycle.status.value='initialized';}/**
|
|
1688
1722
|
}/**
|
1689
1723
|
* Trigger onLoaded callback if any is provided in config & emit initialised event
|
1690
1724
|
*/onInitialized(){// Process any preloaded events
|
1691
|
-
this.processDataInPreloadBuffer();//
|
1725
|
+
this.processDataInPreloadBuffer();// Execute onLoaded callback if provided in load options
|
1726
|
+
const onLoadedCallbackFn=state.loadOptions.value.onLoaded;// TODO: we need to avoid passing the window object to the callback function
|
1692
1727
|
// as this will prevent us from supporting multiple SDK instances in the same page
|
1693
|
-
|
1694
|
-
if(isFunction(state.loadOptions.value.onLoaded)){state.loadOptions.value.onLoaded(globalThis.rudderanalytics);}// Set lifecycle state
|
1728
|
+
safelyInvokeCallback(onLoadedCallbackFn,[globalThis.rudderanalytics],LOAD_API,this.logger);// Set lifecycle state
|
1695
1729
|
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
|
1696
1730
|
dispatchSDKEvent('RSA_Initialised');}/**
|
1697
1731
|
* Emit ready event
|
1698
1732
|
*/// eslint-disable-next-line class-methods-use-this
|
1699
|
-
onReady(){state.lifecycle.status.value='readyExecuted';state.eventBuffer.readyCallbacksArray.value.forEach(callback=>{
|
1733
|
+
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
|
1700
1734
|
dispatchSDKEvent('RSA_Ready');}/**
|
1701
1735
|
* Consume preloaded events buffer
|
1702
1736
|
*/processBufferedEvents(){// This logic has been intentionally implemented without a simple
|
@@ -1715,11 +1749,11 @@ onDestinationsReady(){// May be do any destination specific actions here
|
|
1715
1749
|
// Mark the ready status if not already done
|
1716
1750
|
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
1717
1751
|
// Start consumer exposed methods
|
1718
|
-
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(
|
1752
|
+
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;}/**
|
1719
1753
|
* If destinations are loaded or no integration is available for loading
|
1720
1754
|
* execute the callback immediately else push the callbacks to a queue that
|
1721
1755
|
* will be executed after loading completes
|
1722
|
-
*/if(state.lifecycle.status.value==='readyExecuted'){
|
1756
|
+
*/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.
|
1723
1757
|
// Send automatic ad blocked page event if ad-blockers are detected on the page
|
1724
1758
|
// Check page category to avoid infinite loop
|
1725
1759
|
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
|
@@ -1753,7 +1787,7 @@ analyticsInstances={};defaultAnalyticsKey='';logger=(()=>defaultLogger)();// Sin
|
|
1753
1787
|
constructor(){try{if(RudderAnalytics.globalSingleton){// START-NO-SONAR-SCAN
|
1754
1788
|
// eslint-disable-next-line no-constructor-return
|
1755
1789
|
return RudderAnalytics.globalSingleton;// END-NO-SONAR-SCAN
|
1756
|
-
}
|
1790
|
+
}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
|
1757
1791
|
this.triggerBufferedLoadEvent();// Assign to global "rudderanalytics" object after processing the preload buffer (if any exists)
|
1758
1792
|
// for CDN bundling IIFE exports covers this but for npm ESM and CJS bundling has to be done explicitly
|
1759
1793
|
globalThis.rudderanalytics=this;}catch(error){dispatchErrorEvent(error);}}/**
|
@@ -1795,7 +1829,7 @@ trackPageLoadedEvent(events,options,preloadedEventsArray){if(events.length===0||
|
|
1795
1829
|
* @param events
|
1796
1830
|
* @param useBeacon
|
1797
1831
|
* @param options
|
1798
|
-
*/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 {//
|
1832
|
+
*/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
|
1799
1833
|
this.logger.warn(PAGE_UNLOAD_ON_BEACON_DISABLED_WARNING(RSA));}}}/**
|
1800
1834
|
* Trigger load event in buffer queue if exists and stores the
|
1801
1835
|
* remaining preloaded events array in global object
|