@rudderstack/analytics-js 3.31.8 → 3.32.0-beta.pr.3194.9b9fe31
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 +16 -0
- package/README.md +6 -3
- package/dist/npm/index.d.cts +110 -46
- package/dist/npm/index.d.mts +110 -46
- package/dist/npm/legacy/bundled/cjs/index.cjs +149 -67
- package/dist/npm/legacy/bundled/esm/index.mjs +149 -67
- package/dist/npm/legacy/bundled/umd/index.js +149 -67
- package/dist/npm/legacy/cjs/index.cjs +149 -67
- package/dist/npm/legacy/content-script/cjs/index.cjs +149 -67
- package/dist/npm/legacy/content-script/esm/index.mjs +149 -67
- package/dist/npm/legacy/content-script/umd/index.js +149 -67
- package/dist/npm/legacy/esm/index.mjs +149 -67
- package/dist/npm/legacy/lite/cjs/index.cjs +149 -67
- package/dist/npm/legacy/lite/esm/index.mjs +149 -67
- package/dist/npm/legacy/lite/umd/index.js +149 -67
- package/dist/npm/legacy/umd/index.js +149 -67
- package/dist/npm/modern/bundled/cjs/index.cjs +146 -64
- package/dist/npm/modern/bundled/esm/index.mjs +146 -64
- package/dist/npm/modern/bundled/umd/index.js +146 -64
- package/dist/npm/modern/cjs/index.cjs +147 -64
- package/dist/npm/modern/content-script/cjs/index.cjs +146 -64
- package/dist/npm/modern/content-script/esm/index.mjs +146 -64
- package/dist/npm/modern/content-script/umd/index.js +146 -64
- package/dist/npm/modern/esm/index.mjs +147 -64
- package/dist/npm/modern/lite/cjs/index.cjs +146 -64
- package/dist/npm/modern/lite/esm/index.mjs +146 -64
- package/dist/npm/modern/lite/umd/index.js +146 -64
- package/dist/npm/modern/umd/index.js +147 -64
- package/package.json +1 -1
|
@@ -400,7 +400,7 @@ payload.groupId=null;payload.traits=groupId;if(!isFunction(traits)){payload.opti
|
|
|
400
400
|
// Also, to clone the incoming object type arguments
|
|
401
401
|
payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(payload.traits)){payload.traits=clone(payload.traits);}else {payload.traits=undefined;}if(isDefined(payload.options)){payload.options=clone(payload.options);}else {payload.options=undefined;}return payload;};
|
|
402
402
|
|
|
403
|
-
|
|
403
|
+
/* eslint-disable sonarjs/deprecation *//**
|
|
404
404
|
* Represents the options parameter for anonymousId
|
|
405
405
|
*//**
|
|
406
406
|
* Represents the beacon queue options parameter in loadOptions type
|
|
@@ -408,13 +408,15 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
|
|
|
408
408
|
* Represents the queue options parameter in loadOptions type
|
|
409
409
|
*//**
|
|
410
410
|
* Represents the destinations queue options parameter in loadOptions type
|
|
411
|
+
*//**
|
|
412
|
+
* @deprecated Use the `storage` load API option to control what is persisted before consent is given. It will be removed in the next major version.
|
|
411
413
|
*/let PageLifecycleEvents=/*#__PURE__*/function(PageLifecycleEvents){PageLifecycleEvents["UNLOADED"]="Page Unloaded";return PageLifecycleEvents;}({});/**
|
|
412
414
|
* Represents the source configuration override options for destinations
|
|
413
415
|
*//**
|
|
414
416
|
* Represents the options parameter in the load API
|
|
415
417
|
*/
|
|
416
418
|
|
|
417
|
-
const API_SUFFIX='API';const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API=`Ready${API_SUFFIX}`;const LOAD_API=`Load${API_SUFFIX}`;const HTTP_CLIENT='HttpClient';const RSA='RudderStackAnalytics';const ANALYTICS_CORE='AnalyticsCore';
|
|
419
|
+
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 CONSENT_API=`Consent${API_SUFFIX}`;const HTTP_CLIENT='HttpClient';const RSA='RudderStackAnalytics';const ANALYTICS_CORE='AnalyticsCore';
|
|
418
420
|
|
|
419
421
|
function random(len){return crypto.getRandomValues(new Uint8Array(len));}
|
|
420
422
|
|
|
@@ -459,9 +461,9 @@ const getFormattedTimestamp=date=>date.toISOString();/**
|
|
|
459
461
|
* @returns ISO formatted timestamp string
|
|
460
462
|
*/const getCurrentTimeFormatted=()=>getFormattedTimestamp(new Date());
|
|
461
463
|
|
|
462
|
-
const LOG_CONTEXT_SEPARATOR=':: ';const SCRIPT_ALREADY_EXISTS_ERROR=id=>`A script with the id "${id}" is already loaded. Skipping the loading of this script to prevent conflicts`;const SCRIPT_LOAD_ERROR=(id,url,ev)=>`Unable to load (${isString(ev)?ev:ev.type}) the script with the id "${id}" from URL "${url}"`;const SCRIPT_LOAD_TIMEOUT_ERROR=(id,url,timeout)=>`A timeout of ${timeout} ms occurred while trying to load the script with id "${id}" from URL "${url}"`;const CIRCULAR_REFERENCE_WARNING=(context,key)=>`${context}${LOG_CONTEXT_SEPARATOR}A circular reference has been detected in the object and the property "${key}" has been dropped from the output.`;const JSON_STRINGIFY_WARNING=`Failed to convert the value to a JSON string.`;const COOKIE_DATA_ENCODING_ERROR=`Failed to encode the cookie data.`;const STORAGE_UNAVAILABILITY_ERROR_PREFIX=(context,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${storageType}" storage type is `;
|
|
464
|
+
const LOG_CONTEXT_SEPARATOR=':: ';const SCRIPT_ALREADY_EXISTS_ERROR=id=>`A script with the id "${id}" is already loaded. Skipping the loading of this script to prevent conflicts`;const SCRIPT_LOAD_ERROR=(id,url,ev)=>`Unable to load (${isString(ev)?ev:ev.type}) the script with the id "${id}" from URL "${url}"`;const SCRIPT_LOAD_TIMEOUT_ERROR=(id,url,timeout)=>`A timeout of ${timeout} ms occurred while trying to load the script with id "${id}" from URL "${url}"`;const CIRCULAR_REFERENCE_WARNING=(context,key)=>`${context}${LOG_CONTEXT_SEPARATOR}A circular reference has been detected in the object and the property "${key}" has been dropped from the output.`;const JSON_STRINGIFY_WARNING=`Failed to convert the value to a JSON string.`;const BAD_DATA_WARNING=(context,key)=>`${context}${LOG_CONTEXT_SEPARATOR}A bad data (like circular reference, BigInt) has been detected in the object and the property "${key}" has been dropped from the output.`;const COOKIE_DATA_ENCODING_ERROR=`Failed to encode the cookie data.`;const STORAGE_UNAVAILABILITY_ERROR_PREFIX=(context,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The "${storageType}" storage type is `;
|
|
463
465
|
|
|
464
|
-
const JSON_STRINGIFY='JSONStringify';const BIG_INT_PLACEHOLDER='[BigInt]';const CIRCULAR_REFERENCE_PLACEHOLDER='[Circular Reference]';const getCircularReplacer=(excludeNull,excludeKeys,logger)=>{const ancestors=[];// Here we do not want to use arrow function to use "this" in function context
|
|
466
|
+
const JSON_STRINGIFY='JSONStringify';const JSON_UTIL='JSON';const BIG_INT_PLACEHOLDER='[BigInt]';const CIRCULAR_REFERENCE_PLACEHOLDER='[Circular Reference]';const getCircularReplacer=(excludeNull,excludeKeys,logger)=>{const ancestors=[];// Here we do not want to use arrow function to use "this" in function context
|
|
465
467
|
// eslint-disable-next-line func-names
|
|
466
468
|
return function(key,value){if(excludeKeys?.includes(key)){return undefined;}if(excludeNull&&isNullOrUndefined(value)){return undefined;}if(typeof value!=='object'||isNull(value)){return value;}// `this` is the object that value is contained in, i.e., its direct parent.
|
|
467
469
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -475,13 +477,13 @@ while(ancestors.length>0&&ancestors[ancestors.length-1]!==this){ancestors.pop();
|
|
|
475
477
|
* @returns string
|
|
476
478
|
*/const stringifyWithoutCircular=(value,excludeNull,excludeKeys,logger)=>{try{return JSON.stringify(value,getCircularReplacer(excludeNull,excludeKeys,logger));}catch(err){logger?.warn(JSON_STRINGIFY_WARNING,err);return null;}};const getReplacer=logger=>{const ancestors=[];// Array to track ancestor objects
|
|
477
479
|
// Using a regular function to use `this` for the parent context
|
|
478
|
-
return function replacer(key,value){if(isBigInt(value)){return BIG_INT_PLACEHOLDER;// Replace BigInt values
|
|
480
|
+
return function replacer(key,value){if(isBigInt(value)){logger?.warn(BAD_DATA_WARNING(JSON_UTIL,key));return BIG_INT_PLACEHOLDER;// Replace BigInt values
|
|
479
481
|
}// `this` is the object that value is contained in, i.e., its direct parent.
|
|
480
482
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
481
483
|
// @ts-ignore-next-line
|
|
482
484
|
while(ancestors.length>0&&ancestors[ancestors.length-1]!==this){ancestors.pop();// Remove ancestors that are no longer part of the chain
|
|
483
485
|
}// Check for circular references (if the value is already in the ancestors)
|
|
484
|
-
if(ancestors.includes(value)){return CIRCULAR_REFERENCE_PLACEHOLDER;}// Add current value to ancestors
|
|
486
|
+
if(ancestors.includes(value)){logger?.warn(BAD_DATA_WARNING(JSON_UTIL,key));return CIRCULAR_REFERENCE_PLACEHOLDER;}// Add current value to ancestors
|
|
485
487
|
ancestors.push(value);return value;};};const traverseWithThis=(obj,replacer)=>{// Create a new result object or array
|
|
486
488
|
const result=Array.isArray(obj)?[]:{};// Traverse object properties or array elements
|
|
487
489
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -493,7 +495,7 @@ if(isObjectLiteralAndNotNull(sanitizedValue)||Array.isArray(sanitizedValue)){res
|
|
|
493
495
|
* @param value Input object
|
|
494
496
|
* @param logger Logger instance
|
|
495
497
|
* @returns Sanitized value
|
|
496
|
-
*/const getSanitizedValue=(value,logger)=>{const replacer=getReplacer();// This is needed for registering the first ancestor
|
|
498
|
+
*/const getSanitizedValue=(value,logger)=>{const replacer=getReplacer(logger);// This is needed for registering the first ancestor
|
|
497
499
|
const newValue=replacer.call(value,'',value);if(isObjectLiteralAndNotNull(value)||Array.isArray(value)){return traverseWithThis(value,replacer);}return newValue;};
|
|
498
500
|
|
|
499
501
|
const MANUAL_ERROR_IDENTIFIER='[SDK DISPATCHED ERROR]';const getStacktrace=err=>{const{stack}=err;if(typeof stack==='string'&&stack){return stack;}return undefined;};/**
|
|
@@ -508,7 +510,7 @@ Object.getOwnPropertyNames(err).forEach(key=>{if(key!=='message'&&key!=='stack'&
|
|
|
508
510
|
}}});return newError;}catch{return new Error(`${issue}: ${stringifyWithoutCircular(err)}`);}};const dispatchErrorEvent=error=>{if(isTypeOfError(error)){const errStack=getStacktrace(error);if(errStack){// eslint-disable-next-line no-param-reassign
|
|
509
511
|
error.stack=`${errStack}\n${MANUAL_ERROR_IDENTIFIER}`;}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
510
512
|
|
|
511
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
513
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.32.0-beta.pr.3194.9b9fe31';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const BUILD_VARIANT='modern';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';
|
|
512
514
|
|
|
513
515
|
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';
|
|
514
516
|
|
|
@@ -627,8 +629,8 @@ let ErrorType=/*#__PURE__*/function(ErrorType){ErrorType["HANDLEDEXCEPTION"]="ha
|
|
|
627
629
|
const SUPPORTED_STORAGE_TYPES=['localStorage','memoryStorage','cookieStorage','sessionStorage','none'];const DEFAULT_STORAGE_TYPE='cookieStorage';
|
|
628
630
|
|
|
629
631
|
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
|
|
630
|
-
const UNSUPPORTED_CONSENT_MANAGER_ERROR=(context,selectedConsentManager,consentManagersToPluginNameMap)=>`${context}${LOG_CONTEXT_SEPARATOR}The consent manager "${selectedConsentManager}" is not supported. Please choose one of the following supported consent managers: "${Object.keys(consentManagersToPluginNameMap)}".`;const NON_ERROR_WARNING=(context,errStr)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring a non-error: ${errStr}.`;const BREADCRUMB_ERROR=`Failed to log breadcrumb`;const HANDLE_ERROR_FAILURE=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to handle the error.`;const PLUGIN_NAME_MISSING_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin name is missing.`;const PLUGIN_ALREADY_EXISTS_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" already exists.`;const PLUGIN_NOT_FOUND_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found.`;const PLUGIN_ENGINE_BUG_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found in plugins but found in byName. This indicates a bug in the plugin engine. Please report this issue to the development team.`;const PLUGIN_DEPS_ERROR=(context,pluginName,notExistDeps)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" could not be loaded because some of its dependencies "${notExistDeps}" do not exist.`;const PLUGIN_INVOCATION_ERROR=(context,extPoint,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to invoke the "${extPoint}" extension point of plugin "${pluginName}".`;const SOURCE_CONFIG_FETCH_ERROR='Failed to fetch the source config';const WRITE_KEY_VALIDATION_ERROR=(context,writeKey)=>`${context}${LOG_CONTEXT_SEPARATOR}The write key "${writeKey}" is invalid. It must be a non-empty string. Please check that the write key is correct and try again.`;const DATA_PLANE_URL_VALIDATION_ERROR=(context,dataPlaneUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The data plane URL "${dataPlaneUrl}" is invalid. It must be a valid URL string. Please check that the data plane URL is correct and try again.`;const INVALID_CALLBACK_FN_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The provided callback parameter is not a function.`;const XHR_DELIVERY_ERROR=(prefix,status,statusText,url,response)=>`${prefix} with status ${status} (${statusText}) for URL: ${url}. Response: ${response.trim()}`;const XHR_REQUEST_ERROR=(prefix,e,url)=>`${prefix} due to timeout or no connection (${e?e.type:''}) at the client side for URL: ${url}`;const XHR_SEND_ERROR=(prefix,url)=>`${prefix} for URL: ${url}`;const STORE_DATA_SAVE_ERROR=key=>`Failed to save the value for "${key}" to storage`;const STORE_DATA_FETCH_ERROR=key=>`Failed to retrieve or parse data for "${key}" from storage`;const DATA_SERVER_REQUEST_FAIL_ERROR=status=>`The server responded with status ${status} while setting the cookies. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_ERROR=key=>`The server failed to set the ${key} cookie. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR=`Failed to set/remove cookies via server. As a fallback, the cookies will be managed client side.`;// WARNING
|
|
631
|
-
const STORAGE_TYPE_VALIDATION_WARNING=(context,storageType
|
|
632
|
+
const UNSUPPORTED_CONSENT_MANAGER_ERROR=(context,selectedConsentManager,consentManagersToPluginNameMap)=>`${context}${LOG_CONTEXT_SEPARATOR}The consent manager "${selectedConsentManager}" is not supported. Please choose one of the following supported consent managers: "${Object.keys(consentManagersToPluginNameMap)}".`;const NON_ERROR_WARNING=(context,errStr)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring a non-error: ${errStr}.`;const BREADCRUMB_ERROR=`Failed to log breadcrumb`;const HANDLE_ERROR_FAILURE=context=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to handle the error.`;const PLUGIN_NAME_MISSING_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin name is missing.`;const PLUGIN_ALREADY_EXISTS_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" already exists.`;const PLUGIN_NOT_FOUND_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found.`;const PLUGIN_ENGINE_BUG_ERROR=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" not found in plugins but found in byName. This indicates a bug in the plugin engine. Please report this issue to the development team.`;const PLUGIN_DEPS_ERROR=(context,pluginName,notExistDeps)=>`${context}${LOG_CONTEXT_SEPARATOR}Plugin "${pluginName}" could not be loaded because some of its dependencies "${notExistDeps}" do not exist.`;const PLUGIN_INVOCATION_ERROR=(context,extPoint,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to invoke the "${extPoint}" extension point of plugin "${pluginName}".`;const SOURCE_CONFIG_FETCH_ERROR='Failed to fetch the source config';const WRITE_KEY_VALIDATION_ERROR=(context,writeKey)=>`${context}${LOG_CONTEXT_SEPARATOR}The write key "${writeKey}" is invalid. It must be a non-empty string. Please check that the write key is correct and try again.`;const DATA_PLANE_URL_VALIDATION_ERROR=(context,dataPlaneUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The data plane URL "${dataPlaneUrl}" is invalid. It must be a valid URL string. Please check that the data plane URL is correct and try again.`;const INVALID_CALLBACK_FN_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The provided callback parameter is not a function.`;const XHR_DELIVERY_ERROR=(prefix,status,statusText,url,response)=>`${prefix} with status ${status} (${statusText}) for URL: ${url}. Response: ${response.trim()}`;const XHR_REQUEST_ERROR=(prefix,e,url)=>`${prefix} due to timeout or no connection (${e?e.type:''}) at the client side for URL: ${url}`;const XHR_SEND_ERROR=(prefix,url)=>`${prefix} for URL: ${url}`;const STORE_DATA_SAVE_ERROR=key=>`Failed to save the value for "${key}" to storage`;const STORE_DATA_FETCH_ERROR=key=>`Failed to retrieve or parse data for "${key}" from storage`;const DATA_SERVER_REQUEST_FAIL_ERROR=status=>`The server responded with status ${status} while setting the cookies. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_ERROR=key=>`The server failed to set the ${key} cookie. As a fallback, the cookies will be set client side.`;const FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR=`Failed to set/remove cookies via server. As a fallback, the cookies will be managed client side.`;const COLLAPSED_COOKIE_BATCH_ERROR=(missingCookies,batchSize)=>`The server did not set ${missingCookies.length} of the ${batchSize} cookies sent in one request: ${missingCookies.join(', ')}. A fallback to set them client side was attempted. Check that the data service and any proxy preserve every "Set-Cookie" header, and that the cookie domain and SameSite/Secure attributes are valid.`;// WARNING
|
|
633
|
+
const STORAGE_TYPE_VALIDATION_WARNING=(context,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${storageType}" is not supported. Please choose one of the following supported types: "${SUPPORTED_STORAGE_TYPES}". The default storage type will be used instead.`;const UNSUPPORTED_STORAGE_ENTRY_TYPE_WARNING=(context,entry,storageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${storageType}" configured for the entry "${entry}" is not supported. Please choose one of the following supported types: "${SUPPORTED_STORAGE_TYPES}". The default storage type 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 RESERVED_CUSTOM_CONTEXT_KEY_WARNING=(logContext,property)=>`${logContext}${LOG_CONTEXT_SEPARATOR}The top-level custom context property "${property}" is reserved and was ignored.`;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 INVALID_CUSTOM_CONTEXT_WARNING=logContext=>`${logContext}${LOG_CONTEXT_SEPARATOR}Invalid custom context. Use a plain object without prototype pollution keys.`;const UNSUPPORTED_BEACON_API_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The Beacon API is not supported by your browser. The events will be sent using XHR instead.`;const TIMEOUT_NOT_NUMBER_WARNING=(context,timeout,defaultValue)=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value "${timeout}" is not a number. The default timeout of ${defaultValue} ms will be used instead.`;const CUT_OFF_DURATION_NOT_NUMBER_WARNING=(context,cutOffDuration,defaultValue)=>`${context}${LOG_CONTEXT_SEPARATOR}The session cut off duration value "${cutOffDuration}" is not a number. The default cut off duration of ${defaultValue} ms will be used instead.`;const CUT_OFF_DURATION_LESS_THAN_TIMEOUT_WARNING=(context,cutOffDuration,timeout)=>`${context}${LOG_CONTEXT_SEPARATOR}The session cut off duration value "${cutOffDuration}" ms is less than the session timeout value "${timeout}" ms. The cut off functionality will be disabled.`;const TIMEOUT_ZERO_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value is 0, which disables the automatic session tracking feature. If you want to enable session tracking, please provide a positive integer value for the timeout.`;const TIMEOUT_NOT_RECOMMENDED_WARNING=(context,timeout,minTimeout)=>`${context}${LOG_CONTEXT_SEPARATOR}The session timeout value ${timeout} ms is less than the recommended minimum of ${minTimeout} ms. Please consider increasing the timeout value to ensure optimal performance and reliability.`;const INVALID_SESSION_ID_WARNING=(context,sessionId,minSessionIdLength)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided session ID (${sessionId}) is either invalid, not a positive integer, or not at least "${minSessionIdLength}" digits long. A new session ID will be auto-generated instead.`;const STORAGE_QUOTA_EXCEEDED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}The storage is either full or unavailable, so the data will not be persisted. Switching to in-memory storage.`;const STORAGE_UNAVAILABLE_WARNING=(context,entry,selectedStorageType,finalStorageType)=>`${context}${LOG_CONTEXT_SEPARATOR}The storage type "${selectedStorageType}" is not available for entry "${entry}". The SDK will initialize the entry with "${finalStorageType}" storage type instead.`;const CALLBACK_INVOKE_ERROR=context=>`${context}${LOG_CONTEXT_SEPARATOR}The callback threw an exception`;const INVALID_CONFIG_URL_WARNING=(context,configUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided source config URL "${configUrl}" is invalid. Using the default source config URL instead.`;const POLYFILL_SCRIPT_LOAD_ERROR=(scriptId,url)=>`Failed to load the polyfill script with ID "${scriptId}" from URL ${url}.`;const UNSUPPORTED_PRE_CONSENT_STORAGE_STRATEGY=(context,selectedStrategy,defaultStrategy)=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent storage strategy "${selectedStrategy}" is not supported. Please choose one of the following supported strategies: "none, session, anonymousId". The default strategy "${defaultStrategy}" will be used instead.`;const DEPRECATED_PRE_CONSENT_STORAGE_STRATEGY=context=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent storage strategy option is deprecated. Please enable "storage" in the pre-consent options and use the "storage" load API option instead.`;const UNSUPPORTED_PRE_CONSENT_EVENTS_DELIVERY_TYPE=(context,selectedDeliveryType,defaultDeliveryType)=>`${context}${LOG_CONTEXT_SEPARATOR}The pre-consent events delivery type "${selectedDeliveryType}" is not supported. Please choose one of the following supported types: "immediate, buffer". The default type "${defaultDeliveryType}" will be used instead.`;const DEPRECATED_PLUGIN_WARNING=(context,pluginName)=>`${context}${LOG_CONTEXT_SEPARATOR}${pluginName} plugin is deprecated. Please exclude it from the load API options.`;const generateMisconfiguredPluginsWarning=(context,configurationStatus,missingPlugins,shouldAddMissingPlugins)=>{const isSinglePlugin=missingPlugins.length===1;const pluginsString=isSinglePlugin?` '${missingPlugins[0]}' plugin was`:` ['${missingPlugins.join("', '")}'] plugins were`;const baseWarning=`${context}${LOG_CONTEXT_SEPARATOR}${configurationStatus}, but${pluginsString} not configured to load.`;if(shouldAddMissingPlugins){return `${baseWarning} So, ${isSinglePlugin?'the plugin':'those plugins'} will be loaded automatically.`;}return `${baseWarning} Ignore if this was intentional. Otherwise, consider adding ${isSinglePlugin?'it':'them'} to the 'plugins' load API option.`;};const INVALID_POLYFILL_URL_WARNING=(context,customPolyfillUrl)=>`${context}${LOG_CONTEXT_SEPARATOR}The provided polyfill URL "${customPolyfillUrl}" is invalid. The default polyfill URL will be used instead.`;const PAGE_UNLOAD_ON_BEACON_DISABLED_WARNING=context=>`${context}${LOG_CONTEXT_SEPARATOR}Page Unloaded event can only be tracked when the Beacon transport is active. Please enable "useBeacon" load API option.`;const UNKNOWN_PLUGINS_WARNING=(context,unknownPlugins)=>`${context}${LOG_CONTEXT_SEPARATOR}Ignoring unknown plugins: ${unknownPlugins.join(', ')}.`;const UNAVAILABLE_PLUGINS_ERROR=(context,unavailablePlugins)=>`${context}${LOG_CONTEXT_SEPARATOR}Failed to load the following unavailable local plugins: ${unavailablePlugins.join(', ')}. Some features of the SDK may not work as expected. Make sure you are using the correct SDK bundle variant.`;const CUSTOM_INTEGRATION_CANNOT_BE_ADDED_ERROR=(context,destinationId)=>`${context}${LOG_CONTEXT_SEPARATOR}Cannot add custom integration for destination ID "${destinationId}" after the SDK is loaded.`;
|
|
632
634
|
|
|
633
635
|
const DEFAULT_INTEGRATIONS_CONFIG={All:true};
|
|
634
636
|
|
|
@@ -647,7 +649,7 @@ const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';co
|
|
|
647
649
|
|
|
648
650
|
const DEFAULT_STORAGE_ENCRYPTION_VERSION='v3';const DEFAULT_DATA_PLANE_EVENTS_TRANSPORT='xhr';const ConsentManagersToPluginNameMap={iubenda:'IubendaConsentManager',oneTrust:'OneTrustConsentManager',ketch:'KetchConsentManager',custom:'CustomConsentManager'};const StorageEncryptionVersionsToPluginNameMap={[DEFAULT_STORAGE_ENCRYPTION_VERSION]:'StorageEncryption',legacy:'StorageEncryptionLegacy'};const DataPlaneEventsTransportToPluginNameMap={[DEFAULT_DATA_PLANE_EVENTS_TRANSPORT]:'XhrQueue',beacon:'BeaconQueue'};const DEFAULT_DATA_SERVICE_ENDPOINT='rsaRequest';const METRICS_SERVICE_ENDPOINT='rsaMetrics';const CUSTOM_DEVICE_MODE_DESTINATION_DISPLAY_NAME='Custom Device Mode';
|
|
649
651
|
|
|
650
|
-
const defaultLoadOptions={configUrl:DEFAULT_CONFIG_BE_URL,loadIntegration:true,sessions:{autoTrack:true,timeout:DEFAULT_SESSION_TIMEOUT_MS,cutOff:{enabled:false}},sameSiteCookie:'Lax',polyfillIfRequired:true,integrations:DEFAULT_INTEGRATIONS_CONFIG,useBeacon:false,beaconQueueOptions:{},destinationsQueueOptions:{},queueOptions:{},lockIntegrationsVersion:
|
|
652
|
+
const defaultLoadOptions={configUrl:DEFAULT_CONFIG_BE_URL,loadIntegration:true,sessions:{autoTrack:true,timeout:DEFAULT_SESSION_TIMEOUT_MS,cutOff:{enabled:false}},sameSiteCookie:'Lax',polyfillIfRequired:true,integrations:DEFAULT_INTEGRATIONS_CONFIG,useBeacon:false,beaconQueueOptions:{},destinationsQueueOptions:{},queueOptions:{},lockIntegrationsVersion:false,lockPluginsVersion:false,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=y(clone(defaultLoadOptions));
|
|
651
653
|
|
|
652
654
|
const DEFAULT_USER_SESSION_VALUES=deepFreeze({userId:'',userTraits:{},anonymousId:'',groupId:'',groupTraits:{},initialReferrer:'',initialReferringDomain:'',sessionInfo:{},authToken:null});const DEFAULT_RESET_OPTIONS=deepFreeze({entries:{userId:true,userTraits:true,groupId:true,groupTraits:true,sessionInfo:true,authToken:true,// These are not reset by default
|
|
653
655
|
anonymousId:false,initialReferrer:false,initialReferringDomain:false}});const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
|
|
@@ -669,22 +671,24 @@ const metricsState={retries:y(0),dropped:y(0),sent:y(0),queued:y(0),triggered:y(
|
|
|
669
671
|
const getVariantValue=()=>{// For CDN builds, use runtime window.rudderAnalyticsBuildType
|
|
670
672
|
return BUILD_VARIANT;};const contextState={app:y({name:APP_NAME,namespace:APP_NAMESPACE,version:APP_VERSION,installType:MODULE_TYPE}),traits:y(null),library:y({name:APP_NAME,version:APP_VERSION,snippetVersion:globalThis.RudderSnippetVersion,variant:getVariantValue()}),userAgent:y(null),device:y(null),network:y(null),os:y({name:'',version:''}),locale:y(null),screen:y({density:0,width:0,height:0,innerWidth:0,innerHeight:0}),'ua-ch':y(undefined),timezone:y(undefined)};
|
|
671
673
|
|
|
674
|
+
const customContextState=y({});
|
|
675
|
+
|
|
672
676
|
const nativeDestinationsState={configuredDestinations:y([]),activeDestinations:y([]),loadOnlyIntegrations:y({}),failedDestinations:y([]),loadIntegration:y(true),initializedDestinations:y([]),clientDestinationsReady:y(false),integrationsConfig:y({})};
|
|
673
677
|
|
|
674
678
|
const eventBufferState={toBeProcessedArray:y([]),readyCallbacksArray:y([])};
|
|
675
679
|
|
|
676
680
|
const pluginsState={ready:y(false),loadedPlugins:y([]),failedPlugins:y([]),pluginsToLoadFromConfig:y([]),activePlugins:y([]),totalPluginsToLoad:y(0)};
|
|
677
681
|
|
|
678
|
-
const storageState={encryptionPluginName:y(undefined),migrate:y(false),
|
|
682
|
+
const storageState={encryptionPluginName:y(undefined),migrate:y(false),cookie:y(undefined),entries:y({}),trulyAnonymousTracking:y(false)};
|
|
679
683
|
|
|
680
|
-
const serverSideCookiesState={isEnabledServerSideCookies:y(false),dataServiceUrl:y(undefined)};
|
|
684
|
+
const serverSideCookiesState={isEnabledServerSideCookies:y(false),dataServiceUrl:y(undefined),isCollapsedBatchErrorLogged:y(false)};
|
|
681
685
|
|
|
682
686
|
const dataPlaneEventsState={eventsQueuePluginName:y(undefined),deliveryEnabled:y(true)// Delivery should always happen
|
|
683
687
|
};
|
|
684
688
|
|
|
685
689
|
const autoTrackState={enabled:y(false),pageLifecycle:{enabled:y(false),pageViewId:y(undefined),pageLoadedTimestamp:y(undefined)}};
|
|
686
690
|
|
|
687
|
-
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)};
|
|
691
|
+
const defaultStateValues={capabilities:capabilitiesState,consents:consentsState,context:contextState,customContext:customContextState,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)};
|
|
688
692
|
|
|
689
693
|
const CHROME_STACK_LINE_RE=/^\s*at /;const SAFARI_NATIVE_RE=/^(eval@)?(\[native code])?$/;const LOCATION_RE=/(.+?)(?::(\d+))?(?::(\d+))?$/;function extractLocation(urlLike){if(!urlLike?.includes(':')){return [urlLike||undefined,undefined,undefined];}const normalizedUrlLike=urlLike.startsWith('(')&&urlLike.endsWith(')')?urlLike.slice(1,-1):urlLike;const parts=LOCATION_RE.exec(normalizedUrlLike);if(!parts){return [undefined,undefined,undefined];}return [parts[1]||undefined,parts[2]===undefined?undefined:Number(parts[2]),parts[3]===undefined?undefined:Number(parts[3])];}function parseV8Line(line){if(!CHROME_STACK_LINE_RE.test(line)){return null;}if(line.includes('(eval ')){line=line.replaceAll('eval code','eval').replaceAll(/(\(eval at [^()]*)|(,.*$)/g,'');}const sanitized=line.replace(/^\s+/,'').replaceAll('(eval code','(').replace(/^.*?\s+/,'');const parenLoc=/ (\(.+\)$)/.exec(sanitized);const withoutLoc=parenLoc?sanitized.replace(parenLoc[0],''):sanitized;const[rawFile,lineNumber,columnNumber]=extractLocation(parenLoc?parenLoc[1]:sanitized);const functionName=parenLoc&&withoutLoc||undefined;const fileName=rawFile==='eval'||rawFile==='<anonymous>'?undefined:rawFile;return {functionName,fileName,lineNumber,columnNumber};}function parseFFSafariLine(line){if(SAFARI_NATIVE_RE.test(line)){return null;}if(line.includes(' > eval')){line=line.replaceAll(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,':$1');}if(!line.includes('@')&&!line.includes(':')){return {functionName:line,fileName:undefined,lineNumber:undefined,columnNumber:undefined};}const atIndex=line.lastIndexOf('@');const functionName=atIndex>0?line.slice(0,atIndex):undefined;const[fileName,lineNumber,columnNumber]=extractLocation(line.slice(atIndex+1));return {functionName,fileName,lineNumber,columnNumber};}/**
|
|
690
694
|
* Parses an Error object's stack trace into structured frames.
|
|
@@ -738,6 +742,7 @@ const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts includi
|
|
|
738
742
|
// Potential PII or sensitive data
|
|
739
743
|
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
|
|
740
744
|
'eventBuffer',// pre-load event buffer (may contain PII)
|
|
745
|
+
'customContext',// customer-provided event context
|
|
741
746
|
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='git+https://github.com/rudderlabs/rudder-sdk-js.git';const SOURCE_NAME='js';
|
|
742
747
|
|
|
743
748
|
const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
|
@@ -755,8 +760,8 @@ throw err;}};
|
|
|
755
760
|
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:{}});/**
|
|
756
761
|
* A function to get the Bugsnag release stage for the current environment
|
|
757
762
|
* @param getHostName Optional function to get the hostname (primarily for testing)
|
|
758
|
-
* @returns 'development' if the host is empty (for file:// protocol etc.) or a dev host (localhost, 127.0.0.1, etc.), otherwise ''
|
|
759
|
-
*/const getReleaseStage=(getHostName=()=>window.location.hostname)=>{const host=getHostName();return !host||host&&DEV_HOSTS.includes(host)?'development':'
|
|
763
|
+
* @returns 'development' if the host is empty (for file:// protocol etc.) or a dev host (localhost, 127.0.0.1, etc.), otherwise ''beta'' (it'll be replaced with the actual release stage during the build)
|
|
764
|
+
*/const getReleaseStage=(getHostName=()=>window.location.hostname)=>{const host=getHostName();return !host||host&&DEV_HOSTS.includes(host)?'development':'beta';};const getAppStateForMetadata=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS);return stateStr!==null?JSON.parse(stateStr):{};};const getURLWithoutQueryString=()=>{const url=globalThis.location.href.split('?');return url[0];};const getUserDetails=(source,session,lifecycle,autoTrack)=>({id:`${source.value?.id??lifecycle.writeKey.value}..${session.sessionInfo.value.id??'NA'}..${autoTrack.pageLifecycle.pageViewId.value??'NA'}`,name:source.value?.name??'NA'});const getDeviceDetails=(locale,userAgent)=>({locale:locale.value??'NA',userAgent:userAgent.value??'NA',time:new Date()});const getBugsnagErrorEvent=(exception,errorState,state,groupingHash)=>{const{context,lifecycle,session,source,reporting,autoTrack}=state;const{app,locale,userAgent,timezone,screen,library}=context;return {payloadVersion:'5',notifier:{name:NOTIFIER_NAME,version:app.value.version,url:SDK_GITHUB_URL},events:[{exceptions:[clone(exception)],severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:app.value.version,releaseStage:getReleaseStage(),type:app.value.installType},device:getDeviceDetails(locale,userAgent),request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(reporting.breadcrumbs.value),context:exception.message,groupingHash,metaData:{app:{snippetVersion:library.value.snippetVersion},device:{...screen.value,timezone:timezone.value},// Add rest of the state groups as metadata
|
|
760
765
|
// so that they show up as separate tabs in the dashboard
|
|
761
766
|
...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
|
|
762
767
|
* A function to check if adblockers are active. The promise's resolve function
|
|
@@ -1160,16 +1165,27 @@ const getStorageTypeFromPreConsentIfApplicable=(state,sessionKey)=>{let overridd
|
|
|
1160
1165
|
// TODO: should we pass the keys for all in order to validate or leave free as v1.1?
|
|
1161
1166
|
// Initializing all the enabled store because previous user data might be in different storage
|
|
1162
1167
|
// that needs auto migration
|
|
1163
|
-
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
|
|
1164
|
-
const postConsentStorageOpts=state.consents.postConsent.value.storage;if(isDefined(postConsentStorageOpts?.type)||isDefined(postConsentStorageOpts?.entries)){globalStorageType=postConsentStorageOpts?.type;entriesOptions=postConsentStorageOpts?.entries;}
|
|
1165
|
-
|
|
1168
|
+
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.loadOptions.value.storage?.type;let entriesOptions=state.loadOptions.value.storage?.entries;// Use the storage options from post consent if anything is defined
|
|
1169
|
+
const postConsentStorageOpts=state.consents.postConsent.value.storage;if(isDefined(postConsentStorageOpts?.type)||isDefined(postConsentStorageOpts?.entries)){globalStorageType=postConsentStorageOpts?.type;entriesOptions=postConsentStorageOpts?.entries;}// Without the deprecated pre-consent storage strategy, the storage options decide what is
|
|
1170
|
+
// persisted before consent is given. Only the fallback changes: nothing is persisted unless
|
|
1171
|
+
// a storage type has been explicitly configured.
|
|
1172
|
+
const preConsentOpts=state.consents.preConsent.value;const isStorageFromLoadOptionsBeforeConsent=preConsentOpts.enabled===true&&!preConsentOpts.storage?.strategy;const defaultStorageType=isStorageFromLoadOptionsBeforeConsent?NO_STORAGE:DEFAULT_STORAGE_TYPE;// The unsupported values are reported where the options enter the SDK, so they are only
|
|
1173
|
+
// resolved here, against the default that applies to the current consent phase
|
|
1174
|
+
if(globalStorageType!==undefined&&!SUPPORTED_STORAGE_TYPES.includes(globalStorageType)){globalStorageType=undefined;}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
|
|
1175
|
+
let storageType=preConsentStorageType??configuredStorageType??globalStorageType??defaultStorageType;if(!SUPPORTED_STORAGE_TYPES.includes(storageType)){storageType=defaultStorageType;}const finalStorageType=this.getResolvedStorageTypeForEntry(storageType,sessionKey);if(finalStorageType!==NO_STORAGE){trulyAnonymousTracking=false;}storageEntries={...storageEntries,[sessionKey]:{type:finalStorageType,key:COOKIE_KEYS[storageKey]}};});n(()=>{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
|
|
1166
1176
|
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;}/**
|
|
1167
1177
|
* Create a new store
|
|
1168
1178
|
*/setStore(storeConfig){const storageEngine=getStorageEngine(storeConfig.type);this.stores[storeConfig.id]=new Store(storeConfig,storageEngine,this.pluginsManager);return this.stores[storeConfig.id];}/**
|
|
1169
1179
|
* Retrieve a store
|
|
1170
1180
|
*/getStore(id){return this.stores[id];}}
|
|
1171
1181
|
|
|
1172
|
-
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)
|
|
1182
|
+
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);/**
|
|
1183
|
+
* Warns about the unsupported storage types in the storage options. It only reports them, as the
|
|
1184
|
+
* applicable default depends on the consent phase and is determined at resolution time.
|
|
1185
|
+
* @param storageOpts Storage options from the load API options or the consent API options
|
|
1186
|
+
* @param context Logger context
|
|
1187
|
+
* @param logger Logger instance
|
|
1188
|
+
*/const validateStorageOptions=(storageOpts,context,logger)=>{if(!isObjectLiteralAndNotNull(storageOpts)){return;}const{type,entries}=storageOpts;if(isDefined(type)&&!isValidStorageType(type)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(context,type));}if(entries){Object.entries(entries).forEach(([entryKey,entryOpts])=>{const entryType=entryOpts?.type;if(isDefined(entryType)&&!isValidStorageType(entryType)){logger?.warn(UNSUPPORTED_STORAGE_ENTRY_TYPE_WARNING(context,entryKey,entryType));}});}};const getTopDomain=url=>{// Create a URL object
|
|
1173
1189
|
const urlObj=new URL(url);// Extract the host and protocol
|
|
1174
1190
|
const{host,protocol}=urlObj;// Split the host into parts
|
|
1175
1191
|
const parts=host.split('.');let topDomain;// Handle different cases, especially for co.uk or similar TLDs
|
|
@@ -1215,7 +1231,9 @@ const isErrorReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.error
|
|
|
1215
1231
|
* Validates and normalizes the consent options provided by the user
|
|
1216
1232
|
* @param options Consent options provided by the user
|
|
1217
1233
|
* @returns Validated and normalized consent options
|
|
1218
|
-
*/const getValidPostConsentOptions=options=>{const validOptions={sendPageEvent:false,trackConsent:false,discardPreConsentEvents:false}
|
|
1234
|
+
*/const getValidPostConsentOptions=(options,logger)=>{const validOptions={sendPageEvent:false,trackConsent:false,discardPreConsentEvents:false};// The storage options persist across consent API invocations, so an invocation that does not
|
|
1235
|
+
// provide them leaves the storage as it is
|
|
1236
|
+
validOptions.storage=state.consents.postConsent.value.storage;if(isObjectLiteralAndNotNull(options)){const clonedOptions=clone(options);if(isDefined(clonedOptions.storage)){validateStorageOptions(clonedOptions.storage,CONSENT_API,logger);validOptions.storage=clonedOptions.storage;}if(isNonEmptyObject(clonedOptions.integrations)){validOptions.integrations=clonedOptions.integrations;}validOptions.discardPreConsentEvents=clonedOptions.discardPreConsentEvents===true;validOptions.sendPageEvent=clonedOptions.sendPageEvent===true;validOptions.trackConsent=clonedOptions.trackConsent===true;if(isNonEmptyObject(clonedOptions.consentManagement)){// Override enabled value with the current state value
|
|
1219
1237
|
validOptions.consentManagement=mergeDeepRight(clonedOptions.consentManagement,{enabled:state.consents.enabled.value});}}return validOptions;};/**
|
|
1220
1238
|
* Validates if the input is a valid consents data
|
|
1221
1239
|
* @param value Input consents data
|
|
@@ -1261,10 +1279,16 @@ if(curHost!==dataServiceHost){cookieOptions={...cookieOptions,samesite:'None',se
|
|
|
1261
1279
|
* If the sameDomainCookiesOnly flag is not set and the cookie domain is provided(not top level domain),
|
|
1262
1280
|
* and the data service host is different from the provided cookie domain, then we disable server-side cookies
|
|
1263
1281
|
* ex: provided cookie domain: 'random.com', data service host: 'sub.example.com'
|
|
1264
|
-
*/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
|
|
1282
|
+
*/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;// Only reported here. The applicable default depends on the consent phase, so the unsupported
|
|
1283
|
+
// values are resolved in the store manager.
|
|
1284
|
+
validateStorageOptions(storageOptsFromLoad,CONFIG_MANAGER,logger);let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
|
|
1265
1285
|
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
|
|
1266
|
-
const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}const{sscEnabled,finalDataServiceUrl,cookieOptions}=getServerSideCookiesStateData(logger);n(()=>{state.storage.
|
|
1267
|
-
const preConsentOpts=state.loadOptions.value.preConsent;
|
|
1286
|
+
const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}const{sscEnabled,finalDataServiceUrl,cookieOptions}=getServerSideCookiesStateData(logger);n(()=>{state.storage.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
|
|
1287
|
+
const preConsentOpts=state.loadOptions.value.preConsent;const configuredStrategy=preConsentOpts?.storage?.strategy;if(isDefined(configuredStrategy)){logger.warn(DEPRECATED_PRE_CONSENT_STORAGE_STRATEGY(CONFIG_MANAGER));}// The storage load API options drive the pre-consent phase only when it is explicitly enabled,
|
|
1288
|
+
// so an existing configuration keeps persisting nothing before consent is given. Enabling it
|
|
1289
|
+
// takes precedence over the deprecated strategy, otherwise a migration that leaves the strategy
|
|
1290
|
+
// in place would silently do nothing.
|
|
1291
|
+
let storageStrategy;if(preConsentOpts?.storage?.enabled!==true){storageStrategy=configuredStrategy??DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;const StorageStrategies=['none','session','anonymousId'];if(!StorageStrategies.includes(storageStrategy)){storageStrategy=DEFAULT_PRE_CONSENT_STORAGE_STRATEGY;logger.warn(UNSUPPORTED_PRE_CONSENT_STORAGE_STRATEGY(CONFIG_MANAGER,configuredStrategy,DEFAULT_PRE_CONSENT_STORAGE_STRATEGY));}}let eventsDeliveryType=preConsentOpts?.events?.delivery??DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE;const deliveryTypes=['immediate','buffer'];if(isDefined(eventsDeliveryType)&&!deliveryTypes.includes(eventsDeliveryType)){eventsDeliveryType=DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE;logger.warn(UNSUPPORTED_PRE_CONSENT_EVENTS_DELIVERY_TYPE(CONFIG_MANAGER,preConsentOpts?.events?.delivery,DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE));}n(()=>{state.consents.activeConsentManagerPluginName.value=consentManagerPluginName;state.consents.initialized.value=initialized;state.consents.enabled.value=enabled;state.consents.data.value=consentsData;state.consents.provider.value=provider;state.consents.preConsent.value={// Only enable pre-consent if it is explicitly enabled and
|
|
1268
1292
|
// if it is not already initialized and
|
|
1269
1293
|
// if consent management is enabled
|
|
1270
1294
|
enabled:state.loadOptions.value.preConsent?.enabled===true&&initialized===false&&enabled===true,storage:{strategy:storageStrategy},events:{delivery:eventsDeliveryType}};});};/**
|
|
@@ -1315,8 +1339,11 @@ class ConfigManager{constructor(httpClient,errorHandler,logger){this.errorHandle
|
|
|
1315
1339
|
* config related information in global state
|
|
1316
1340
|
*/init(){const{logLevel,configUrl,lockIntegrationsVersion,lockPluginsVersion,destSDKBaseURL,pluginsSDKBaseURL,integrations}=state.loadOptions.value;// determine the path to fetch integration SDK from
|
|
1317
1341
|
const intgCdnUrl=getIntegrationsCDNPath(APP_VERSION,lockIntegrationsVersion,destSDKBaseURL,this.logger);if(isNull(intgCdnUrl)){return;}let pluginsCDNPath;{// determine the path to fetch remote plugins from
|
|
1318
|
-
pluginsCDNPath=getPluginsCDNPath(APP_VERSION,lockPluginsVersion,pluginsSDKBaseURL,this.logger);}if(pluginsCDNPath===null){return;}
|
|
1319
|
-
|
|
1342
|
+
pluginsCDNPath=getPluginsCDNPath(APP_VERSION,lockPluginsVersion,pluginsSDKBaseURL,this.logger);}if(pluginsCDNPath===null){return;}// Set the log level before attaching the effects so that the effect that syncs it
|
|
1343
|
+
// to the logger does not pin the logger at the default level and swallow the
|
|
1344
|
+
// warnings emitted while processing the rest of the load options
|
|
1345
|
+
if(logLevel){state.lifecycle.logLevel.value=logLevel;}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
|
|
1346
|
+
n(()=>{state.lifecycle.integrationsCDNPath.value=intgCdnUrl;state.lifecycle.pluginsCDNPath.value=pluginsCDNPath;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
|
|
1320
1347
|
state.nativeDestinations.loadOnlyIntegrations.value=integrations;});this.getConfig();}/**
|
|
1321
1348
|
* Handle errors
|
|
1322
1349
|
*/onError(error,customMessage,groupingHash){this.errorHandler.onError({error,context:CONFIG_MANAGER,customMessage,groupingHash});}/**
|
|
@@ -1410,6 +1437,10 @@ const TOP_LEVEL_ELEMENTS=['integrations','anonymousId','originalTimestamp'];// R
|
|
|
1410
1437
|
const CONTEXT_RESERVED_ELEMENTS=['library','consentManagement','userAgent','ua-ch','screen'];// Reserved elements in the standard RudderStack event spec
|
|
1411
1438
|
const RESERVED_ELEMENTS=['id','anonymous_id','user_id','sent_at','timestamp','received_at','original_timestamp','event','event_text','channel','context_ip','context_request_ip','context_passed_ip','group_id','previous_id'];
|
|
1412
1439
|
|
|
1440
|
+
const CUSTOM_CONTEXT='CustomContext';const PROTOTYPE_POLLUTION_KEYS=new Set(['__proto__','constructor','prototype']);const OBJECT_CONSTRUCTOR_SOURCE=Function.prototype.toString.call(Object);const isPlainObject=value=>{if(!isObjectLiteralAndNotNull(value)){return false;}const prototype=Object.getPrototypeOf(value);if(prototype===null){return true;}const constructor=Object.prototype.hasOwnProperty.call(prototype,'constructor')?prototype.constructor:undefined;return typeof constructor==='function'&&constructor.prototype===prototype&&Function.prototype.toString.call(constructor)===OBJECT_CONSTRUCTOR_SOURCE;};const containsPrototypePollutionKey=(value,visitedObjects=new Set())=>{if(!isObjectLiteralAndNotNull(value)&&!Array.isArray(value)){return false;}const traversableValue=value;if(visitedObjects.has(traversableValue)){return false;}visitedObjects.add(traversableValue);return Object.keys(traversableValue).some(key=>{return PROTOTYPE_POLLUTION_KEYS.has(key)||containsPrototypePollutionKey(traversableValue[key],visitedObjects);});};const filterReservedCustomContextKeys=(context,logger)=>{const retainedContext={};Object.keys(context).forEach(key=>{if(CONTEXT_RESERVED_ELEMENTS.includes(key)){logger.warn(RESERVED_CUSTOM_CONTEXT_KEY_WARNING(CUSTOM_CONTEXT,key));return;}Object.defineProperty(retainedContext,key,{configurable:true,enumerable:true,value:context[key]===context?retainedContext:context[key],writable:true});});return retainedContext;};const prepareCustomContextUpdate=(input,logger)=>{try{if(!isPlainObject(input)){logger.warn(INVALID_CUSTOM_CONTEXT_WARNING(CUSTOM_CONTEXT));return undefined;}const acceptedInput=filterReservedCustomContextKeys(input,logger);if(containsPrototypePollutionKey(acceptedInput)){logger.warn(INVALID_CUSTOM_CONTEXT_WARNING(CUSTOM_CONTEXT));return undefined;}const sanitizedContext=getSanitizedValue(acceptedInput,logger);return clone(sanitizedContext);}catch{logger.warn(INVALID_CUSTOM_CONTEXT_WARNING(CUSTOM_CONTEXT));return undefined;}};
|
|
1441
|
+
|
|
1442
|
+
class CustomContextStore{constructor(state,logger){this.state=state;this.logger=logger;}set(context){const update=prepareCustomContextUpdate(context,this.logger);if(!update){return;}const mergedContext=mergeDeepRight(clone(this.state.value),update);this.state.value=removeUndefinedAndNullValues(mergedContext);}get(){return clone(this.state.value);}clear(){this.state.value={};}}
|
|
1443
|
+
|
|
1413
1444
|
/**
|
|
1414
1445
|
* A function to check given value is a number or not
|
|
1415
1446
|
* @param num input value
|
|
@@ -1452,9 +1483,9 @@ timeout,autoTrack:true,...(cutOff&&{cutOff})};};/**
|
|
|
1452
1483
|
*/const generateManualTrackingSession=(id,logger)=>{const sessionId=isManualSessionIdValid(id,logger)?id:generateSessionId();return {id:sessionId,sessionStart:undefined,manualTrack:true};};const isStorageTypeValidForStoringData=storageType=>Boolean(storageType===COOKIE_STORAGE||storageType===LOCAL_STORAGE||storageType===SESSION_STORAGE||storageType===MEMORY_STORAGE);/**
|
|
1453
1484
|
* Generate a new anonymousId
|
|
1454
1485
|
* @returns string anonymousID
|
|
1455
|
-
*/const generateAnonymousId=()=>generateUUID();const getFinalResetOptions=options=>{// Legacy behavior: toggle only anonymousId without mutating defaults
|
|
1486
|
+
*/const generateAnonymousId=()=>generateUUID();const isKnownResetEntry=key=>Object.prototype.hasOwnProperty.call(DEFAULT_RESET_OPTIONS.entries,key);const getFinalResetOptions=options=>{// Legacy behavior: toggle only anonymousId without mutating defaults
|
|
1456
1487
|
if(isBoolean(options)){const{entries,...rest}=DEFAULT_RESET_OPTIONS;return {...rest,entries:{...entries,anonymousId:options}};}// Override any defaults with the user provided options
|
|
1457
|
-
if(isObjectLiteralAndNotNull(options)&&isObjectLiteralAndNotNull(options.entries)){return mergeDeepRight(DEFAULT_RESET_OPTIONS,
|
|
1488
|
+
if(isObjectLiteralAndNotNull(options)&&isObjectLiteralAndNotNull(options.entries)){const optionEntries=options.entries;const validEntries=Object.keys(optionEntries).reduce((entries,key)=>{if(isKnownResetEntry(key)){const value=optionEntries[key];if(isBoolean(value)){entries[key]=value;}}return entries;},{});return mergeDeepRight(DEFAULT_RESET_OPTIONS,{entries:validEntries});}return {...DEFAULT_RESET_OPTIONS};};
|
|
1458
1489
|
|
|
1459
1490
|
/**
|
|
1460
1491
|
* To get the page properties for context object
|
|
@@ -1488,7 +1519,7 @@ rudderEvent.originalTimestamp=options.originalTimestamp;}};/**
|
|
|
1488
1519
|
* @param rudderContext Generated rudder event
|
|
1489
1520
|
* @param options API options
|
|
1490
1521
|
* @param logger Logger instance
|
|
1491
|
-
*/const getMergedContext=(rudderContext,options,logger)=>{let context=rudderContext;Object.keys(options).forEach(key=>{if(!TOP_LEVEL_ELEMENTS.includes(key)
|
|
1522
|
+
*/const getMergedContext=(rudderContext,options,logger)=>{let context=rudderContext;const mergeContext=candidateContext=>{const acceptedContext=filterReservedCustomContextKeys(candidateContext,logger);if(containsPrototypePollutionKey(acceptedContext)){logger.warn(INVALID_CUSTOM_CONTEXT_WARNING(EVENT_MANAGER));return;}context=mergeDeepRight(context,acceptedContext);};Object.keys(options).forEach(key=>{if(!TOP_LEVEL_ELEMENTS.includes(key)){if(key!=='context'){mergeContext({[key]:options[key]});}else if(!isUndefined(options[key])&&isObjectLiteralAndNotNull(options[key])){mergeContext(options[key]);}else {logger.warn(INVALID_CONTEXT_OBJECT_WARNING(EVENT_MANAGER));}}});return context;};/**
|
|
1492
1523
|
* Updates rudder event object with data from the API options
|
|
1493
1524
|
* @param rudderEvent Generated rudder event
|
|
1494
1525
|
* @param options API options
|
|
@@ -1506,12 +1537,12 @@ finalIntgConfig=state.consents.postConsent.value.integrations??state.nativeDesti
|
|
|
1506
1537
|
* @param pageProps Page properties
|
|
1507
1538
|
* @param logger logger
|
|
1508
1539
|
* @returns Enriched RudderEvent object
|
|
1509
|
-
*/const getEnrichedEvent=(rudderEvent,options,pageProps,logger)=>{const commonEventData={channel:CHANNEL,context:{traits:clone(state.session.userTraits.value),sessionId:state.session.sessionInfo.value.id||undefined,sessionStart:state.session.sessionInfo.value.sessionStart||undefined,// Add 'consentManagement' only if consent management is enabled
|
|
1540
|
+
*/const getEnrichedEvent=(rudderEvent,options,pageProps,logger,customContext={})=>{const commonEventData={channel:CHANNEL,context:{traits:clone(state.session.userTraits.value),sessionId:state.session.sessionInfo.value.id||undefined,sessionStart:state.session.sessionInfo.value.sessionStart||undefined,// Add 'consentManagement' only if consent management is enabled
|
|
1510
1541
|
...(state.consents.enabled.value&&{consentManagement:{deniedConsentIds:clone(state.consents.data.value.deniedConsentIds),allowedConsentIds:clone(state.consents.data.value.allowedConsentIds),provider:state.consents.provider.value,resolutionStrategy:state.consents.resolutionStrategy.value}}),'ua-ch':state.context['ua-ch'].value,app:state.context.app.value,library:state.context.library.value,userAgent:state.context.userAgent.value,os:state.context.os.value,locale:state.context.locale.value,screen:state.context.screen.value,campaign:extractUTMParameters(globalThis.location.href),page:getContextPageProperties(pageProps),timezone:state.context.timezone.value,// Add auto tracking information
|
|
1511
1542
|
...(state.autoTrack.enabled.value&&{autoTrack:{...(state.autoTrack.pageLifecycle.enabled.value&&{page:{pageViewId:state.autoTrack.pageLifecycle.pageViewId.value}})}})},originalTimestamp:getCurrentTimeFormatted(),messageId:generateUUID(),userId:rudderEvent.userId||state.session.userId.value};if(!isStorageTypeValidForStoringData(state.storage.entries.value.anonymousId?.type)){// Generate new anonymous id for each request
|
|
1512
1543
|
commonEventData.anonymousId=generateAnonymousId();}else {// Type casting to string as the user session manager will take care of initializing the value
|
|
1513
1544
|
commonEventData.anonymousId=state.session.anonymousId.value;}// set truly anonymous tracking flag
|
|
1514
|
-
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
|
|
1545
|
+
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 sdkBuiltEvent=mergeDeepRight(rudderEvent,commonEventData);const processedEvent=mergeDeepRight(sdkBuiltEvent,{context:customContext});// Set the default values for the event properties
|
|
1515
1546
|
// matching with v1.1 payload
|
|
1516
1547
|
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
|
|
1517
1548
|
processedEvent.integrations=getEventIntegrationsConfig(processedEvent.integrations);return processedEvent;};
|
|
@@ -1522,32 +1553,32 @@ class RudderEventFactory{constructor(logger){this.logger=logger;}/**
|
|
|
1522
1553
|
* @param name Page name
|
|
1523
1554
|
* @param properties Page properties
|
|
1524
1555
|
* @param options API options
|
|
1525
|
-
*/generatePageEvent(category,name,properties,options){let props=properties??{};props=getUpdatedPageProperties(props,options);const pageEvent={properties:props,name,category,type:'page'};return getEnrichedEvent(pageEvent,options,props,this.logger);}/**
|
|
1556
|
+
*/generatePageEvent(category,name,properties,options,customContext={}){let props=properties??{};props=getUpdatedPageProperties(props,options);const pageEvent={properties:props,name,category,type:'page'};return getEnrichedEvent(pageEvent,options,props,this.logger,customContext);}/**
|
|
1526
1557
|
* Generate a 'track' event based on the user-input fields
|
|
1527
1558
|
* @param event The event name
|
|
1528
1559
|
* @param properties Event properties
|
|
1529
1560
|
* @param options API options
|
|
1530
|
-
*/generateTrackEvent(event,properties,options){const trackEvent={properties,event,type:'track'};return getEnrichedEvent(trackEvent,options,undefined,this.logger);}/**
|
|
1561
|
+
*/generateTrackEvent(event,properties,options,customContext={}){const trackEvent={properties,event,type:'track'};return getEnrichedEvent(trackEvent,options,undefined,this.logger,customContext);}/**
|
|
1531
1562
|
* Generate an 'identify' event based on the user-input fields
|
|
1532
1563
|
* @param userId New user ID
|
|
1533
1564
|
* @param traits new traits
|
|
1534
1565
|
* @param options API options
|
|
1535
|
-
*/generateIdentifyEvent(userId,traits,options){const identifyEvent={userId,type:'identify',context:{traits}};return getEnrichedEvent(identifyEvent,options,undefined,this.logger);}/**
|
|
1566
|
+
*/generateIdentifyEvent(userId,traits,options,customContext={}){const identifyEvent={userId,type:'identify',context:{traits}};return getEnrichedEvent(identifyEvent,options,undefined,this.logger,customContext);}/**
|
|
1536
1567
|
* Generate an 'alias' event based on the user-input fields
|
|
1537
1568
|
* @param to New user ID
|
|
1538
1569
|
* @param from Old user ID
|
|
1539
1570
|
* @param options API options
|
|
1540
|
-
*/generateAliasEvent(to,from,options){const aliasEvent={previousId:from,type:'alias'};const enrichedEvent=getEnrichedEvent(aliasEvent,options,undefined,this.logger);// override the User ID from the API inputs
|
|
1571
|
+
*/generateAliasEvent(to,from,options,customContext={}){const aliasEvent={previousId:from,type:'alias'};const enrichedEvent=getEnrichedEvent(aliasEvent,options,undefined,this.logger,customContext);// override the User ID from the API inputs
|
|
1541
1572
|
enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
1542
1573
|
* Generate a 'group' event based on the user-input fields
|
|
1543
1574
|
* @param groupId New group ID
|
|
1544
1575
|
* @param traits new group traits
|
|
1545
1576
|
* @param options API options
|
|
1546
|
-
*/generateGroupEvent(groupId,traits,options){const groupEvent={type:'group'};if(groupId){groupEvent.groupId=groupId;}if(traits){groupEvent.traits=traits;}return getEnrichedEvent(groupEvent,options,undefined,this.logger);}/**
|
|
1577
|
+
*/generateGroupEvent(groupId,traits,options,customContext={}){const groupEvent={type:'group'};if(groupId){groupEvent.groupId=groupId;}if(traits){groupEvent.traits=traits;}return getEnrichedEvent(groupEvent,options,undefined,this.logger,customContext);}/**
|
|
1547
1578
|
* Generates a new RudderEvent object based on the user-input fields
|
|
1548
1579
|
* @param event API event parameters object
|
|
1549
1580
|
* @returns A RudderEvent object
|
|
1550
|
-
*/create(event){let eventObj;switch(
|
|
1581
|
+
*/create(event,customContext={}){let eventObj;const{type,name,properties,options,category,userId,traits,to,from,groupId}=event;switch(type){case 'page':eventObj=this.generatePageEvent(category,name,properties,options,customContext);break;case 'track':eventObj=this.generateTrackEvent(name,properties,options,customContext);break;case 'identify':eventObj=this.generateIdentifyEvent(userId,traits,options,customContext);break;case 'alias':eventObj=this.generateAliasEvent(to,from,options,customContext);break;case 'group':default:eventObj=this.generateGroupEvent(groupId,traits,options,customContext);break;}return eventObj;}}
|
|
1551
1582
|
|
|
1552
1583
|
/**
|
|
1553
1584
|
* A service to generate valid event payloads and queue them for processing
|
|
@@ -1562,11 +1593,20 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
|
1562
1593
|
*/init(){this.eventRepository.init();}resume(){this.eventRepository.resume();}/**
|
|
1563
1594
|
* Consumes a new incoming event
|
|
1564
1595
|
* @param event Incoming event data
|
|
1565
|
-
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
|
1596
|
+
*/addEvent(event,customContext){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event,customContext);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
|
1566
1597
|
|
|
1567
1598
|
class UserSessionManager{/**
|
|
1599
|
+
* The single debounce timer shared by every key. Server-side cookie writes are collected
|
|
1600
|
+
* into one request rather than one per key.
|
|
1601
|
+
*//**
|
|
1602
|
+
* Session keys waiting to be written, mapped to the cookie name each one is stored under.
|
|
1603
|
+
* Drained into a single request when the debounce timer fires.
|
|
1604
|
+
*//**
|
|
1568
1605
|
* Tracks whether a server-side cookie setting request is in progress or not.
|
|
1569
|
-
|
|
1606
|
+
*//**
|
|
1607
|
+
* The value each key was last handed to storage with, recorded by the sync effect.
|
|
1608
|
+
* Anything else in the state means storage has not been given the current value yet.
|
|
1609
|
+
*/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.serverSideCookiesDebounceTimer=0;this.serverSideCookiesPending={};this.serverSideCookiesRequestInProgress={};this.lastSyncedValues={};}/**
|
|
1570
1610
|
* Initialize User session with values from storage
|
|
1571
1611
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
|
1572
1612
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
|
@@ -1604,23 +1644,52 @@ const sessionKeys=Object.keys(sessionToCookiesMap);const getCurrentCookieValuesF
|
|
|
1604
1644
|
const originalCookieValues={};sessionKeys.forEach(sessionKey=>{originalCookieValues[sessionToCookiesMap[sessionKey].name]=store?.get(sessionToCookiesMap[sessionKey].name);});const clearInProgressFlags=()=>{sessionKeys.forEach(sessionKey=>{this.serverSideCookiesRequestInProgress[sessionKey]=false;});};const setCookiesClientSide=()=>{getCurrentCookieValuesFromState().forEach(each=>{if(cb){cb(each.name,each.value);}});};try{const expectedCookieValues={};sessionKeys.forEach(sessionKey=>{expectedCookieValues[sessionToCookiesMap[sessionKey].name]=state.session[sessionKey].value;});// encrypt cookies values
|
|
1605
1645
|
const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
|
1606
1646
|
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
|
|
1607
|
-
clearInProgressFlags();if(details?.xhr?.status===200){
|
|
1647
|
+
clearInProgressFlags();if(details?.xhr?.status===200){// Only the cookies that were actually sent can be reported as unapplied, so
|
|
1648
|
+
// both halves of the diagnostic are drawn from the same set
|
|
1649
|
+
const sentCookieNames=new Set(encryptedCookieData.map(each=>each.name));const unappliedCookies=[];getCurrentCookieValuesFromState().forEach(cData=>{const originalCookieVal=originalCookieValues[cData.name];const currentCookieVal=store?.get(cData.name);// Check if the expected cookie values are set.
|
|
1608
1650
|
if(stringifyWithoutCircular(expectedCookieValues[cData.name],false,[])!==stringifyWithoutCircular(currentCookieVal,false,[])){// It's fine if the values don't match as other active SDK sessions might have updated the cookie values
|
|
1609
1651
|
// or other cookie requests might have updated the cookie value.
|
|
1610
|
-
//
|
|
1611
|
-
|
|
1652
|
+
// Left exactly as the request found it, so the server did not apply this
|
|
1653
|
+
// write. A value that moved to something else was set by another tab or
|
|
1654
|
+
// request, which is expected rather than a failure.
|
|
1655
|
+
if(sentCookieNames.has(cData.name)&&stringifyWithoutCircular(originalCookieVal,false,[])===stringifyWithoutCircular(currentCookieVal,false,[])){unappliedCookies.push(cData.name);}// Log an error only when cookie didn't exist previously and currently also doesn't exist.
|
|
1656
|
+
if(isNull(originalCookieVal)&&isNull(currentCookieVal)){this.logger.error(FAILED_SETTING_COOKIE_FROM_SERVER_ERROR(cData.name));}if(cb){cb(cData.name,cData.value);}}});// A batch coming back with cookies missing points at something shared rather
|
|
1657
|
+
// than at any one cookie, so say so once instead of leaving the operator with
|
|
1658
|
+
// a per-cookie error and no cause. Purely diagnostic: it never changes how the
|
|
1659
|
+
// SDK sends the next batch.
|
|
1660
|
+
//
|
|
1661
|
+
// The message lists candidate causes rather than asserting one, because this
|
|
1662
|
+
// condition cannot tell them apart. A data service that collapses the batch
|
|
1663
|
+
// still sets the last cookie, so exactly one survives, whereas rejected cookie
|
|
1664
|
+
// attributes usually leave none of them set.
|
|
1665
|
+
// Counted from what was actually sent, not from the queued keys: encryption can
|
|
1666
|
+
// drop a key, and reporting it as sent would overstate the batch.
|
|
1667
|
+
if(encryptedCookieData.length>1&&unappliedCookies.length>0&&!state.serverCookies.isCollapsedBatchErrorLogged.value){state.serverCookies.isCollapsedBatchErrorLogged.value=true;this.logger.error(COLLAPSED_COOKIE_BATCH_ERROR(unappliedCookies,encryptedCookieData.length));}}else {this.logger.error(DATA_SERVER_REQUEST_FAIL_ERROR(details?.xhr?.status));setCookiesClientSide();}});}else {setCookiesClientSide();// Mark the cookie req status as done
|
|
1612
1668
|
clearInProgressFlags();}}catch(e){this.onError(e,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR);setCookiesClientSide();// Mark the cookie req status as done
|
|
1613
1669
|
clearInProgressFlags();}}/**
|
|
1670
|
+
* A function to send every queued server-side cookie as a single request
|
|
1671
|
+
*/flushServerSideCookies(){// The timer has fired, so drop its id rather than leaving a stale one behind
|
|
1672
|
+
this.serverSideCookiesDebounceTimer=0;const sessionToCookiesMap=Object.entries(this.serverSideCookiesPending).reduce((acc,[sessionKey,cookieName])=>{acc[sessionKey]={name:cookieName};return acc;},{});// Drain before sending, so anything that changes while the request is in flight is
|
|
1673
|
+
// queued for the next batch rather than being sent twice
|
|
1674
|
+
this.serverSideCookiesPending={};if(Object.keys(sessionToCookiesMap).length===0){return;}// Every batched key is backed by cookie storage by construction, so they share a store
|
|
1675
|
+
const curStore=this.storeManager.getStore(storageClientDataStoreNameMap[COOKIE_STORAGE]);this.setServerSideCookies(sessionToCookiesMap,(cookieName,cookieValue)=>{curStore?.set(cookieName,cookieValue);},curStore);}/**
|
|
1614
1676
|
* A function to sync values in storage
|
|
1615
1677
|
* @param sessionKey
|
|
1616
1678
|
*/syncValueToStorage(sessionKey){const entries=state.storage.entries.value;const storageType=entries[sessionKey]?.type;if(isStorageTypeValidForStoringData(storageType)){const curStore=this.storeManager.getStore(storageClientDataStoreNameMap[storageType]);const cookieName=entries[sessionKey]?.key;const cookieValue=state.session[sessionKey].value;if(cookieValue&&(isString(cookieValue)||isNonEmptyObject(cookieValue))){// if useServerSideCookies load option is set to true
|
|
1617
1679
|
// set the cookie from server side
|
|
1618
1680
|
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
|
|
1619
|
-
this.serverSideCookiesRequestInProgress[sessionKey]=true
|
|
1620
|
-
|
|
1681
|
+
this.serverSideCookiesRequestInProgress[sessionKey]=true;// Queue this key and restart the shared debounce, so everything that changes in
|
|
1682
|
+
// the same window goes to the data service as one request instead of one each.
|
|
1683
|
+
this.serverSideCookiesPending[sessionKey]=cookieName;// Restarting the shared timer means a steady stream of changes to any key can
|
|
1684
|
+
// keep postponing the flush for every other key, where per-key timers only ever
|
|
1685
|
+
// delayed their own. That needs changes arriving closer together than the
|
|
1686
|
+
// debounce, continuously, so it is accepted rather than capped with a max wait.
|
|
1687
|
+
if(this.serverSideCookiesDebounceTimer){globalThis.clearTimeout(this.serverSideCookiesDebounceTimer);}this.serverSideCookiesDebounceTimer=globalThis.setTimeout(()=>{this.flushServerSideCookies();},SERVER_SIDE_COOKIES_DEBOUNCE_TIME);}else {curStore?.set(cookieName,cookieValue);}}else {curStore?.remove(cookieName);}}}/**
|
|
1621
1688
|
* Function to update storage whenever state value changes
|
|
1622
1689
|
*/registerEffects(){// This will work as long as the user session entry key names are same as the state keys
|
|
1623
|
-
USER_SESSION_KEYS.forEach(sessionKey=>{j(()=>{
|
|
1690
|
+
USER_SESSION_KEYS.forEach(sessionKey=>{j(()=>{// Read before syncing so the effect subscribes to the signal, and record after
|
|
1691
|
+
// so that reads can tell whether storage has been given the current value yet
|
|
1692
|
+
const value=state.session[sessionKey].value;this.syncValueToStorage(sessionKey);this.lastSyncedValues[sessionKey]=value;});});}/**
|
|
1624
1693
|
* Sets anonymous id in the following precedence:
|
|
1625
1694
|
*
|
|
1626
1695
|
* 1. anonymousId: Id directly provided to the function.
|
|
@@ -1637,12 +1706,27 @@ const autoCapturedAnonymousId=this.pluginsManager?.invokeSingle('storage.getAnon
|
|
|
1637
1706
|
// This is needed for entries that are fetched from the storage
|
|
1638
1707
|
// during the current session (for example, session info)
|
|
1639
1708
|
this.migrateStorageIfNeeded([store],[sessionKey]);const storageKey=entries[sessionKey]?.key;return store?.get(storageKey)??null;}return null;}getExternalAnonymousIdByCookieName(key){const storageEngine=getStorageEngine(COOKIE_STORAGE);if(storageEngine?.isEnabled){return storageEngine.getItem(key)??null;}return null;}/**
|
|
1640
|
-
* Fetches the value for a session key. Preferably from storage,
|
|
1641
|
-
*
|
|
1709
|
+
* Fetches the value for a session key. Preferably from storage, so that changes made by
|
|
1710
|
+
* other tabs are picked up. Falls back to the state whenever storage is known to be
|
|
1711
|
+
* behind it.
|
|
1642
1712
|
* @param sessionKey - The session key to fetch the value for
|
|
1643
1713
|
* @returns - The value for the session key
|
|
1644
|
-
*/getUserSessionValue(sessionKey){//
|
|
1645
|
-
|
|
1714
|
+
*/getUserSessionValue(sessionKey){// peek() rather than value, as this is a plain read; subscribing here would make
|
|
1715
|
+
// every enclosing effect depend on all the user session signals
|
|
1716
|
+
const stateValue=state.session[sessionKey].peek();// Until the sync effect has run for this key there is nothing to compare against, so
|
|
1717
|
+
// storage stays authoritative. That is the case throughout init, where the state is
|
|
1718
|
+
// still being hydrated from storage and the effects are not registered yet.
|
|
1719
|
+
//
|
|
1720
|
+
// The persistence check matters for storage types that hold nothing at all ('none'),
|
|
1721
|
+
// where the state can never be ahead of storage because there is no destination.
|
|
1722
|
+
// Without it, a session started during a replay would survive the replay and then
|
|
1723
|
+
// disappear on the next event, once the sync effect had caught up.
|
|
1724
|
+
const isSyncRecorded=this.isPersistenceEnabledForStorageEntry(sessionKey)&&sessionKey in this.lastSyncedValues;// Storage has not been given this value yet. Buffered events are replayed from within
|
|
1725
|
+
// the lifecycle effect, so the sync effects are queued behind that batch and do not
|
|
1726
|
+
// run in between, leaving storage behind the state for the whole replay. Direct state
|
|
1727
|
+
// writes such as start(), end() and reset() are in the same position.
|
|
1728
|
+
if(isSyncRecorded&&stateValue!==this.lastSyncedValues[sessionKey]){return stateValue;}// Storage has the value, but for server-side cookies it is only on its way there.
|
|
1729
|
+
if(this.serverSideCookiesRequestInProgress[sessionKey]){return stateValue;}// Otherwise, fetch the value from storage.
|
|
1646
1730
|
return this.getEntryValue(sessionKey);}/**
|
|
1647
1731
|
* Fetches User Id
|
|
1648
1732
|
* @returns
|
|
@@ -1779,14 +1863,14 @@ const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{
|
|
|
1779
1863
|
* Analytics class with lifecycle based on state ad user triggered events
|
|
1780
1864
|
*/class Analytics{/**
|
|
1781
1865
|
* Initialize services and components or use default ones if singletons
|
|
1782
|
-
*/constructor(){this.preloadBuffer=new BufferQueue();this.initialized=false;this.errorHandler=defaultErrorHandler;this.logger=defaultLogger;this.externalSrcLoader=new ExternalSrcLoader(this.logger);this.httpClient=defaultHttpClient;this.httpClient.init(this.errorHandler);this.capabilitiesManager=new CapabilitiesManager(this.httpClient,this.errorHandler,this.logger);}/**
|
|
1866
|
+
*/constructor(){this.preloadBuffer=new BufferQueue();this.initialized=false;this.errorHandler=defaultErrorHandler;this.logger=defaultLogger;this.customContextStore=new CustomContextStore(state.customContext,this.logger);this.externalSrcLoader=new ExternalSrcLoader(this.logger);this.httpClient=defaultHttpClient;this.httpClient.init(this.errorHandler);this.capabilitiesManager=new CapabilitiesManager(this.httpClient,this.errorHandler,this.logger);}/**
|
|
1783
1867
|
* Start application lifecycle if not already started
|
|
1784
|
-
*/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
|
|
1785
|
-
n(()=>{state.lifecycle.writeKey.value=clone(writeKey);state.lifecycle.dataPlaneUrl.value=clone(dataPlaneUrl);state.loadOptions.value=normalizeLoadOptions(state.loadOptions.value,
|
|
1868
|
+
*/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;}const{context:customContext,...loadOptionsWithoutContext}=loadOptions;const sanitizedLoadOptions=getSanitizedValue(loadOptionsWithoutContext);if(customContext!==undefined){this.customContextStore.set(customContext);}// Set initial state values
|
|
1869
|
+
n(()=>{state.lifecycle.writeKey.value=clone(writeKey);state.lifecycle.dataPlaneUrl.value=clone(dataPlaneUrl);state.loadOptions.value=normalizeLoadOptions(state.loadOptions.value,sanitizedLoadOptions);state.lifecycle.status.value='mounted';});// set log level as early as possible
|
|
1786
1870
|
this.logger.setMinLogLevel(state.loadOptions.value.logLevel??POST_LOAD_LOG_LEVEL);// Expose state to global objects
|
|
1787
1871
|
setExposedGlobal('state',state,writeKey);// Configure initial config of any services or components here
|
|
1788
1872
|
// State application lifecycle
|
|
1789
|
-
this.startLifecycle();}// Start lifecycle methods
|
|
1873
|
+
this.startLifecycle();}setCustomContext(context){if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['setCustomContext',context]];return;}this.customContextStore.set(context);}getCustomContext(){return this.customContextStore.get();}clearCustomContext(){if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['clearCustomContext']];return;}this.customContextStore.clear();}// Start lifecycle methods
|
|
1790
1874
|
/**
|
|
1791
1875
|
* Orchestrate the lifecycle of the application phases/status
|
|
1792
1876
|
*/startLifecycle(){j(()=>{try{switch(state.lifecycle.status.value){case 'mounted':this.onMounted();break;case 'browserCapabilitiesReady':this.onBrowserCapabilitiesReady();break;case 'configured':this.onConfigured();break;case 'pluginsLoading':break;case 'pluginsReady':this.onPluginsReady();break;case 'initialized':this.onInitialized();break;case 'loaded':this.onLoaded();break;case 'destinationsLoading':break;case 'destinationsReady':this.onDestinationsReady();break;case 'ready':this.onReady();break;case 'readyExecuted':default:break;}}catch(err){const issue='Failed to load the SDK';this.errorHandler.onError({error:err,context:ANALYTICS_CORE,customMessage:issue,groupingHash:issue});}});}onBrowserCapabilitiesReady(){// initialize the preloaded events enqueuing
|
|
@@ -1826,8 +1910,7 @@ dispatchSDKEvent('RSA_Ready');}/**
|
|
|
1826
1910
|
*/processBufferedEvents(){// This logic has been intentionally implemented without a simple
|
|
1827
1911
|
// for-loop as the individual events that are processed may
|
|
1828
1912
|
// add more events to the buffer (this is needed for the consent API)
|
|
1829
|
-
let bufferedEvents=state.eventBuffer.toBeProcessedArray.value;while(bufferedEvents.length>0){const bufferedEvent=bufferedEvents.shift();state.eventBuffer.toBeProcessedArray.value=bufferedEvents;if(bufferedEvent){
|
|
1830
|
-
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}/**
|
|
1913
|
+
let bufferedEvents=state.eventBuffer.toBeProcessedArray.value;while(bufferedEvents.length>0){const bufferedEvent=bufferedEvents.shift();state.eventBuffer.toBeProcessedArray.value=bufferedEvents;if(bufferedEvent){this.processBufferedEvent(bufferedEvent);}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}processBufferedEvent(bufferedEvent){switch(bufferedEvent[0]){case 'setCustomContext':this.setCustomContext(bufferedEvent[1]);break;case 'clearCustomContext':this.clearCustomContext();break;case 'ready':this.ready(bufferedEvent[1],true);break;case 'page':this.page(bufferedEvent[1],true,bufferedEvent[2]);break;case 'track':this.track(bufferedEvent[1],true,bufferedEvent[2]);break;case 'identify':this.identify(bufferedEvent[1],true,bufferedEvent[2]);break;case 'alias':this.alias(bufferedEvent[1],true,bufferedEvent[2]);break;case 'group':this.group(bufferedEvent[1],true,bufferedEvent[2]);break;case 'reset':this.reset(bufferedEvent[1],true);break;case 'setAnonymousId':this.setAnonymousId(bufferedEvent[1],bufferedEvent[2],true);break;case 'startSession':this.startSession(bufferedEvent[1],true);break;case 'endSession':this.endSession(true);break;case 'consent':this.consent(bufferedEvent[1],true);break;case 'addCustomIntegration':this.addCustomIntegration(bufferedEvent[1],bufferedEvent[2],true);break;default:{const unsupportedEvent=bufferedEvent;return unsupportedEvent;}}}getInvocationContext(preservedCustomContext){if(preservedCustomContext===undefined){return this.customContextStore.get();}const preparedContext=prepareCustomContextUpdate(preservedCustomContext,this.logger);return preparedContext??{};}/**
|
|
1831
1914
|
* Load device mode destinations
|
|
1832
1915
|
*/loadDestinations(){// If the integrations load is already triggered or completed, skip the rest of the logic
|
|
1833
1916
|
if(state.lifecycle.status.value==='destinationsLoading'||state.nativeDestinations.clientDestinationsReady.value===true){return;}// Set in state the desired activeDestinations to inject in DOM
|
|
@@ -1844,26 +1927,26 @@ ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycl
|
|
|
1844
1927
|
* If destinations are loaded or no integration is available for loading
|
|
1845
1928
|
* execute the callback immediately else push the callbacks to a queue that
|
|
1846
1929
|
* will be executed after loading completes
|
|
1847
|
-
*/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.
|
|
1930
|
+
*/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,preservedCustomContext){const type='page';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}const invocationContext=this.getInvocationContext(preservedCustomContext);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},invocationContext);// 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.
|
|
1848
1931
|
// Send automatic ad blocked page event if ad-blockers are detected on the page
|
|
1849
1932
|
// Check page category to avoid infinite loop
|
|
1850
1933
|
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
|
|
1851
1934
|
// in v3 implementation
|
|
1852
|
-
path:ADBLOCK_PAGE_PATH},state.loadOptions.value.sendAdblockPageOptions));}}track(payload,isBufferedInvocation=false){const type='track';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event - ${payload.name}`);state.metrics.triggered.value+=1;this.eventManager?.addEvent({type,name:payload.name||undefined,properties:payload.properties,options:payload.options,callback:payload.callback});}identify(payload,isBufferedInvocation=false){const type='identify';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;const shouldResetSession=Boolean(payload.userId&&state.session.userId.value&&payload.userId!==state.session.userId.value);if(shouldResetSession){this.reset();}// `null` value indicates that previous user ID needs to be retained
|
|
1853
|
-
if(!isNull(payload.userId)){this.userSessionManager?.setUserId(payload.userId);}this.userSessionManager?.setUserTraits(payload.traits);this.eventManager?.addEvent({type,userId:payload.userId,traits:payload.traits,options:payload.options,callback:payload.callback});}alias(payload,isBufferedInvocation=false){const type='alias';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;const previousId=payload.from??(this.getUserId()||this.userSessionManager?.getAnonymousId());this.eventManager?.addEvent({type,to:payload.to,from:previousId,options:payload.options,callback:payload.callback});}group(payload,isBufferedInvocation=false){const type='group';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;// `null` value indicates that previous group ID needs to be retained
|
|
1854
|
-
if(!isNull(payload.groupId)){this.userSessionManager?.setGroupId(payload.groupId);}this.userSessionManager?.setGroupTraits(payload.traits);this.eventManager?.addEvent({type,groupId:payload.groupId,traits:payload.traits,options:payload.options,callback:payload.callback});}reset(options,isBufferedInvocation=false){const type='reset';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,options]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.reset(options);}getAnonymousId(options){return this.userSessionManager?.getAnonymousId(options);}setAnonymousId(anonymousId,rudderAmpLinkerParam,isBufferedInvocation=false){const type='setAnonymousId';// Buffering is needed as setting the anonymous ID may require invoking the GoogleLinker plugin
|
|
1935
|
+
path:ADBLOCK_PAGE_PATH},state.loadOptions.value.sendAdblockPageOptions),isBufferedInvocation,invocationContext);}}track(payload,isBufferedInvocation=false,preservedCustomContext){const type='track';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}const invocationContext=this.getInvocationContext(preservedCustomContext);this.errorHandler.leaveBreadcrumb(`New ${type} event - ${payload.name}`);state.metrics.triggered.value+=1;this.eventManager?.addEvent({type,name:payload.name||undefined,properties:payload.properties,options:payload.options,callback:payload.callback},invocationContext);}identify(payload,isBufferedInvocation=false,preservedCustomContext){const type='identify';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}const invocationContext=this.getInvocationContext(preservedCustomContext);this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;const shouldResetSession=Boolean(payload.userId&&state.session.userId.value&&payload.userId!==state.session.userId.value);if(shouldResetSession){this.reset();}// `null` value indicates that previous user ID needs to be retained
|
|
1936
|
+
if(!isNull(payload.userId)){this.userSessionManager?.setUserId(payload.userId);}this.userSessionManager?.setUserTraits(payload.traits);this.eventManager?.addEvent({type,userId:payload.userId,traits:payload.traits,options:payload.options,callback:payload.callback},invocationContext);}alias(payload,isBufferedInvocation=false,preservedCustomContext){const type='alias';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}const invocationContext=this.getInvocationContext(preservedCustomContext);this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;const previousId=payload.from??(this.getUserId()||this.userSessionManager?.getAnonymousId());this.eventManager?.addEvent({type,to:payload.to,from:previousId,options:payload.options,callback:payload.callback},invocationContext);}group(payload,isBufferedInvocation=false,preservedCustomContext){const type='group';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,payload]];return;}const invocationContext=this.getInvocationContext(preservedCustomContext);this.errorHandler.leaveBreadcrumb(`New ${type} event`);state.metrics.triggered.value+=1;// `null` value indicates that previous group ID needs to be retained
|
|
1937
|
+
if(!isNull(payload.groupId)){this.userSessionManager?.setGroupId(payload.groupId);}this.userSessionManager?.setGroupTraits(payload.traits);this.eventManager?.addEvent({type,groupId:payload.groupId,traits:payload.traits,options:payload.options,callback:payload.callback},invocationContext);}reset(options,isBufferedInvocation=false){const type='reset';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,options]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.reset(options);}getAnonymousId(options){return this.userSessionManager?.getAnonymousId(options);}setAnonymousId(anonymousId,rudderAmpLinkerParam,isBufferedInvocation=false){const type='setAnonymousId';// Buffering is needed as setting the anonymous ID may require invoking the GoogleLinker plugin
|
|
1855
1938
|
if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,anonymousId,rudderAmpLinkerParam]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.setAnonymousId(anonymousId,rudderAmpLinkerParam);}// eslint-disable-next-line class-methods-use-this
|
|
1856
1939
|
getUserId(){return state.session.userId.value;}// eslint-disable-next-line class-methods-use-this
|
|
1857
1940
|
getUserTraits(){return state.session.userTraits.value;}// eslint-disable-next-line class-methods-use-this
|
|
1858
1941
|
getGroupId(){return state.session.groupId.value;}// eslint-disable-next-line class-methods-use-this
|
|
1859
1942
|
getGroupTraits(){return state.session.groupTraits.value;}startSession(sessionId,isBufferedInvocation=false){const type='startSession';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,sessionId]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.start(sessionId);}endSession(isBufferedInvocation=false){const type='endSession';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);this.userSessionManager?.end();}// eslint-disable-next-line class-methods-use-this
|
|
1860
|
-
getSessionId(){const sessionId=this.userSessionManager?.getSessionId();return sessionId??null;}consent(options,isBufferedInvocation=false){const type='consent';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,options]];return;}this.errorHandler.leaveBreadcrumb(`New consent invocation`);n(()=>{state.consents.preConsent.value={...state.consents.preConsent.value,enabled:false};state.consents.postConsent.value=getValidPostConsentOptions(options);const{initialized,consentsData}=getConsentManagementData(state.consents.postConsent.value.consentManagement,this.logger);state.consents.initialized.value=initialized;state.consents.data.value=consentsData;});// Update consents data in state
|
|
1943
|
+
getSessionId(){const sessionId=this.userSessionManager?.getSessionId();return sessionId??null;}consent(options,isBufferedInvocation=false){const type='consent';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,options]];return;}this.errorHandler.leaveBreadcrumb(`New consent invocation`);n(()=>{state.consents.preConsent.value={...state.consents.preConsent.value,enabled:false};state.consents.postConsent.value=getValidPostConsentOptions(options,this.logger);const{initialized,consentsData}=getConsentManagementData(state.consents.postConsent.value.consentManagement,this.logger);state.consents.initialized.value=initialized;state.consents.data.value=consentsData;});// Update consents data in state
|
|
1861
1944
|
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}// Re-init store manager
|
|
1862
1945
|
this.storeManager?.initializeStorageState();// Re-init user session manager
|
|
1863
1946
|
this.userSessionManager?.syncStorageDataToState();// Resume event manager to process the events to destinations
|
|
1864
1947
|
this.eventManager?.resume();this.loadDestinations();this.sendTrackingEvents(isBufferedInvocation);}sendTrackingEvents(isBufferedInvocation){// If isBufferedInvocation is true, then the tracking events will be added to the end of the
|
|
1865
1948
|
// events buffer array so that any other preload events (mainly from query string API) will be processed first.
|
|
1866
|
-
if(state.consents.postConsent.value.trackConsent){const trackOptions=trackArgumentsToCallOptions(CONSENT_TRACK_EVENT_NAME);if(isBufferedInvocation){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['track',trackOptions]];}else {this.track(trackOptions);}}if(state.consents.postConsent.value.sendPageEvent){const pageOptions=pageArgumentsToCallOptions();if(isBufferedInvocation){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['page',pageOptions]];}else {this.page(pageOptions);}}}setAuthToken(token){this.userSessionManager?.setAuthToken(token);}/**
|
|
1949
|
+
if(state.consents.postConsent.value.trackConsent){const trackOptions=trackArgumentsToCallOptions(CONSENT_TRACK_EVENT_NAME);if(isBufferedInvocation){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['track',trackOptions,this.customContextStore.get()]];}else {this.track(trackOptions);}}if(state.consents.postConsent.value.sendPageEvent){const pageOptions=pageArgumentsToCallOptions();if(isBufferedInvocation){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,['page',pageOptions,this.customContextStore.get()]];}else {this.page(pageOptions);}}}setAuthToken(token){this.userSessionManager?.setAuthToken(token);}/**
|
|
1867
1950
|
* Add a custom integration for a custom destination.
|
|
1868
1951
|
* @param destinationId - The ID of the custom destination from the RudderStack dashboard.
|
|
1869
1952
|
* @param integration - The custom integration object.
|
|
@@ -1883,7 +1966,7 @@ analyticsInstances={};defaultAnalyticsKey='';logger=defaultLogger;// Singleton w
|
|
|
1883
1966
|
constructor(){try{if(RudderAnalytics.globalSingleton){// START-NO-SONAR-SCAN
|
|
1884
1967
|
// eslint-disable-next-line no-constructor-return
|
|
1885
1968
|
return RudderAnalytics.globalSingleton;// END-NO-SONAR-SCAN
|
|
1886
|
-
}RudderAnalytics.initializeGlobalResources();this.setDefaultInstanceKey=this.setDefaultInstanceKey.bind(this);this.getAnalyticsInstance=this.getAnalyticsInstance.bind(this);this.load=this.load.bind(this);this.ready=this.ready.bind(this);this.triggerBufferedLoadEvent=this.triggerBufferedLoadEvent.bind(this);this.page=this.page.bind(this);this.track=this.track.bind(this);this.identify=this.identify.bind(this);this.alias=this.alias.bind(this);this.group=this.group.bind(this);this.reset=this.reset.bind(this);this.getAnonymousId=this.getAnonymousId.bind(this);this.setAnonymousId=this.setAnonymousId.bind(this);this.getUserId=this.getUserId.bind(this);this.getUserTraits=this.getUserTraits.bind(this);this.getGroupId=this.getGroupId.bind(this);this.getGroupTraits=this.getGroupTraits.bind(this);this.startSession=this.startSession.bind(this);this.endSession=this.endSession.bind(this);this.getSessionId=this.getSessionId.bind(this);this.setAuthToken=this.setAuthToken.bind(this);this.consent=this.consent.bind(this);this.addCustomIntegration=this.addCustomIntegration.bind(this);this.createSafeAnalyticsInstance();RudderAnalytics.globalSingleton=this;state.autoTrack.pageLifecycle.pageViewId.value=generateUUID();state.autoTrack.pageLifecycle.pageLoadedTimestamp.value=Date.now();// start loading if a load event was buffered or wait for explicit load call
|
|
1969
|
+
}RudderAnalytics.initializeGlobalResources();this.setDefaultInstanceKey=this.setDefaultInstanceKey.bind(this);this.getAnalyticsInstance=this.getAnalyticsInstance.bind(this);this.load=this.load.bind(this);this.ready=this.ready.bind(this);this.triggerBufferedLoadEvent=this.triggerBufferedLoadEvent.bind(this);this.page=this.page.bind(this);this.track=this.track.bind(this);this.identify=this.identify.bind(this);this.alias=this.alias.bind(this);this.group=this.group.bind(this);this.reset=this.reset.bind(this);this.getAnonymousId=this.getAnonymousId.bind(this);this.setAnonymousId=this.setAnonymousId.bind(this);this.getUserId=this.getUserId.bind(this);this.getUserTraits=this.getUserTraits.bind(this);this.getGroupId=this.getGroupId.bind(this);this.getGroupTraits=this.getGroupTraits.bind(this);this.setCustomContext=this.setCustomContext.bind(this);this.getCustomContext=this.getCustomContext.bind(this);this.clearCustomContext=this.clearCustomContext.bind(this);this.startSession=this.startSession.bind(this);this.endSession=this.endSession.bind(this);this.getSessionId=this.getSessionId.bind(this);this.setAuthToken=this.setAuthToken.bind(this);this.consent=this.consent.bind(this);this.addCustomIntegration=this.addCustomIntegration.bind(this);this.createSafeAnalyticsInstance();RudderAnalytics.globalSingleton=this;state.autoTrack.pageLifecycle.pageViewId.value=generateUUID();state.autoTrack.pageLifecycle.pageLoadedTimestamp.value=Date.now();// start loading if a load event was buffered or wait for explicit load call
|
|
1887
1970
|
this.triggerBufferedLoadEvent();// Assign to global "rudderanalytics" object after processing the preload buffer (if any exists)
|
|
1888
1971
|
// for CDN bundling IIFE exports covers this but for npm ESM and CJS bundling has to be done explicitly
|
|
1889
1972
|
globalThis.rudderanalytics=this;}catch(error){dispatchErrorEvent(error);}}/**
|
|
@@ -1912,7 +1995,7 @@ if(isString(writeKey)&&writeKey){this.defaultAnalyticsKey=writeKey;}}/**
|
|
|
1912
1995
|
this.trackPageLifecycleEvents(loadOptions);// Get the preloaded events array from global buffer instead of window.rudderanalytics
|
|
1913
1996
|
// as the constructor must have already pushed the events to the global buffer
|
|
1914
1997
|
const preloadedEventsArray=getExposedGlobal(GLOBAL_PRELOAD_BUFFER);// The array will be mutated in the below method
|
|
1915
|
-
promotePreloadedConsentEventsToTop(preloadedEventsArray);setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone(preloadedEventsArray));this.getAnalyticsInstance(writeKey)?.load(writeKey,dataPlaneUrl,
|
|
1998
|
+
promotePreloadedConsentEventsToTop(preloadedEventsArray);setExposedGlobal(GLOBAL_PRELOAD_BUFFER,clone(preloadedEventsArray));this.getAnalyticsInstance(writeKey)?.load(writeKey,dataPlaneUrl,loadOptions);}catch(error){dispatchErrorEvent(error);}}/**
|
|
1916
1999
|
* A function to track page lifecycle events like page loaded and page unloaded
|
|
1917
2000
|
* @param loadOptions
|
|
1918
2001
|
* @returns
|
|
@@ -1980,6 +2063,6 @@ group(groupId,traits,options,callback){try{this.getAnalyticsInstance()?.group(gr
|
|
|
1980
2063
|
* @param resetAnonymousId Reset anonymous ID
|
|
1981
2064
|
* @returns none
|
|
1982
2065
|
* @deprecated Use reset(options) instead
|
|
1983
|
-
*/reset(options){try{this.getAnalyticsInstance()?.reset(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);}}getAnonymousId(options){try{return this.getAnalyticsInstance()?.getAnonymousId(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);return undefined;}}setAnonymousId(anonymousId,rudderAmpLinkerParam){try{this.getAnalyticsInstance()?.setAnonymousId(getSanitizedValue(anonymousId),getSanitizedValue(rudderAmpLinkerParam));}catch(error){dispatchErrorEvent(error);}}getUserId(){try{return this.getAnalyticsInstance()?.getUserId();}catch(error){dispatchErrorEvent(error);return undefined;}}getUserTraits(){try{return this.getAnalyticsInstance()?.getUserTraits();}catch(error){dispatchErrorEvent(error);return undefined;}}getGroupId(){try{return this.getAnalyticsInstance()?.getGroupId();}catch(error){dispatchErrorEvent(error);return undefined;}}getGroupTraits(){try{return this.getAnalyticsInstance()?.getGroupTraits();}catch(error){dispatchErrorEvent(error);return undefined;}}startSession(sessionId){try{this.getAnalyticsInstance()?.startSession(getSanitizedValue(sessionId));}catch(error){dispatchErrorEvent(error);}}endSession(){try{this.getAnalyticsInstance()?.endSession();}catch(error){dispatchErrorEvent(error);}}getSessionId(){try{return this.getAnalyticsInstance()?.getSessionId();}catch(error){dispatchErrorEvent(error);return undefined;}}setAuthToken(token){try{this.getAnalyticsInstance()?.setAuthToken(getSanitizedValue(token));}catch(error){dispatchErrorEvent(error);}}consent(options){try{this.getAnalyticsInstance()?.consent(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);}}addCustomIntegration(destinationId,integration){try{this.getAnalyticsInstance()?.addCustomIntegration(getSanitizedValue(destinationId),getSanitizedValue(integration));}catch(error){dispatchErrorEvent(error);}}}
|
|
2066
|
+
*/reset(options){try{this.getAnalyticsInstance()?.reset(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);}}getAnonymousId(options){try{return this.getAnalyticsInstance()?.getAnonymousId(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);return undefined;}}setAnonymousId(anonymousId,rudderAmpLinkerParam){try{this.getAnalyticsInstance()?.setAnonymousId(getSanitizedValue(anonymousId),getSanitizedValue(rudderAmpLinkerParam));}catch(error){dispatchErrorEvent(error);}}getUserId(){try{return this.getAnalyticsInstance()?.getUserId();}catch(error){dispatchErrorEvent(error);return undefined;}}getUserTraits(){try{return this.getAnalyticsInstance()?.getUserTraits();}catch(error){dispatchErrorEvent(error);return undefined;}}getGroupId(){try{return this.getAnalyticsInstance()?.getGroupId();}catch(error){dispatchErrorEvent(error);return undefined;}}getGroupTraits(){try{return this.getAnalyticsInstance()?.getGroupTraits();}catch(error){dispatchErrorEvent(error);return undefined;}}setCustomContext(context){try{this.getAnalyticsInstance()?.setCustomContext(context);}catch(error){dispatchErrorEvent(error);}}getCustomContext(){try{return this.getAnalyticsInstance()?.getCustomContext()??{};}catch(error){dispatchErrorEvent(error);return {};}}clearCustomContext(){try{this.getAnalyticsInstance()?.clearCustomContext();}catch(error){dispatchErrorEvent(error);}}startSession(sessionId){try{this.getAnalyticsInstance()?.startSession(getSanitizedValue(sessionId));}catch(error){dispatchErrorEvent(error);}}endSession(){try{this.getAnalyticsInstance()?.endSession();}catch(error){dispatchErrorEvent(error);}}getSessionId(){try{return this.getAnalyticsInstance()?.getSessionId();}catch(error){dispatchErrorEvent(error);return undefined;}}setAuthToken(token){try{this.getAnalyticsInstance()?.setAuthToken(getSanitizedValue(token));}catch(error){dispatchErrorEvent(error);}}consent(options){try{this.getAnalyticsInstance()?.consent(getSanitizedValue(options));}catch(error){dispatchErrorEvent(error);}}addCustomIntegration(destinationId,integration){try{this.getAnalyticsInstance()?.addCustomIntegration(getSanitizedValue(destinationId),getSanitizedValue(integration));}catch(error){dispatchErrorEvent(error);}}}
|
|
1984
2067
|
|
|
1985
2068
|
exports.RudderAnalytics = RudderAnalytics;
|