@rudderstack/analytics-js 3.27.0 → 3.28.1-beta.pr.2744.1adb5b9
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 +17 -0
- package/dist/npm/legacy/bundled/cjs/index.cjs +36 -27
- package/dist/npm/legacy/bundled/esm/index.mjs +36 -27
- package/dist/npm/legacy/bundled/umd/index.js +36 -27
- package/dist/npm/legacy/cjs/index.cjs +36 -27
- package/dist/npm/legacy/content-script/cjs/index.cjs +36 -27
- package/dist/npm/legacy/content-script/esm/index.mjs +36 -27
- package/dist/npm/legacy/content-script/umd/index.js +36 -27
- package/dist/npm/legacy/esm/index.mjs +36 -27
- package/dist/npm/legacy/umd/index.js +36 -27
- package/dist/npm/modern/bundled/cjs/index.cjs +34 -25
- package/dist/npm/modern/bundled/esm/index.mjs +34 -25
- package/dist/npm/modern/bundled/umd/index.js +34 -25
- package/dist/npm/modern/cjs/index.cjs +33 -24
- package/dist/npm/modern/content-script/cjs/index.cjs +34 -25
- package/dist/npm/modern/content-script/esm/index.mjs +34 -25
- package/dist/npm/modern/content-script/umd/index.js +34 -25
- package/dist/npm/modern/esm/index.mjs +33 -24
- package/dist/npm/modern/umd/index.js +33 -24
- package/package.json +1 -1
|
@@ -515,7 +515,7 @@ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// esli
|
|
|
515
515
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
516
516
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
517
517
|
|
|
518
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
518
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.28.1-beta.pr.2744.1adb5b9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
|
|
519
519
|
|
|
520
520
|
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';
|
|
521
521
|
|
|
@@ -654,7 +654,7 @@ const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';co
|
|
|
654
654
|
|
|
655
655
|
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';
|
|
656
656
|
|
|
657
|
-
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:
|
|
657
|
+
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=d$1(clone(defaultLoadOptions));
|
|
658
658
|
|
|
659
659
|
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
|
|
660
660
|
anonymousId:false,initialReferrer:false,initialReferringDomain:false}});const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
|
|
@@ -738,7 +738,7 @@ try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch{retur
|
|
|
738
738
|
* Utility to parse XHR JSON response
|
|
739
739
|
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
|
740
740
|
|
|
741
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];
|
|
741
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];const INTEGRATIONS_ERROR_CATEGORY='integrations';const SDK_ERROR_CATEGORY='sdk';const DEFAULT_ERROR_CATEGORY=SDK_ERROR_CATEGORY;
|
|
742
742
|
|
|
743
743
|
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
|
744
744
|
* Utility to create request configuration based on default options
|
|
@@ -774,7 +774,7 @@ const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts includi
|
|
|
774
774
|
// Potential PII or sensitive data
|
|
775
775
|
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
|
|
776
776
|
'eventBuffer',// pre-load event buffer (may contain PII)
|
|
777
|
-
'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';
|
|
777
|
+
'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';
|
|
778
778
|
|
|
779
779
|
const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
|
780
780
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
|
@@ -791,8 +791,8 @@ throw err;}};
|
|
|
791
791
|
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:{}});/**
|
|
792
792
|
* A function to get the Bugsnag release stage for the current environment
|
|
793
793
|
* @param getHostName Optional function to get the hostname (primarily for testing)
|
|
794
|
-
* @returns 'development' if the host is empty (for file:// protocol etc.) or a dev host (localhost, 127.0.0.1, etc.), otherwise ''
|
|
795
|
-
*/const getReleaseStage=(getHostName=()=>window.location.hostname)=>{const host=getHostName();return !host||host&&DEV_HOSTS.includes(host)?'development':'
|
|
794
|
+
* @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)
|
|
795
|
+
*/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
|
|
796
796
|
// so that they show up as separate tabs in the dashboard
|
|
797
797
|
...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
|
|
798
798
|
* A function to check if adblockers are active. The promise's resolve function
|
|
@@ -820,12 +820,18 @@ if(state.capabilities.cspBlockedURLs.value.includes(extractedURL)){resolve(false
|
|
|
820
820
|
checkIfAdBlockersAreActive(state,httpClient,resolve);}}else {// Filter out errors that are not from the RS CDN.
|
|
821
821
|
resolve(false);}}else {// Allow the error to be notified if no URL could be extracted from the error message
|
|
822
822
|
resolve(true);}}else {resolve(!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg)));}});};/**
|
|
823
|
+
* A function to get the directory name from a file path.
|
|
824
|
+
* @param {string} filePath The file path
|
|
825
|
+
* @returns The directory name or undefined if the file path is invalid
|
|
826
|
+
*/const getDirectoryName=filePath=>{if(!filePath){return undefined;}const paths=filePath.split('/');return paths.at(-2);};/**
|
|
827
|
+
* A function to get the top stack path from the exception.
|
|
828
|
+
* @param {Exception} exception The exception object
|
|
829
|
+
* @returns The top stack path or undefined if the exception is invalid
|
|
830
|
+
*/const getTopStackPath=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return undefined;}return errorOrigin;};/**
|
|
823
831
|
* A function to determine if the error is from Rudder SDK
|
|
824
832
|
* @param {Error} exception
|
|
825
833
|
* @returns
|
|
826
|
-
*/const isSDKError=exception=>{const errorOrigin=exception
|
|
827
|
-
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
|
828
|
-
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category:category??DEFAULT_ERROR_CATEGORY},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
834
|
+
*/const isSDKError=exception=>{const errorOrigin=getTopStackPath(exception);if(!errorOrigin){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const parentFolderName=getDirectoryName(errorOrigin);return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorCategory=(exception,category)=>{if(category){return category;}const errorOrigin=getTopStackPath(exception);const directoryName=getDirectoryName(errorOrigin);if(directoryName===CDN_INT_DIR){return INTEGRATIONS_ERROR_CATEGORY;}return DEFAULT_ERROR_CATEGORY;};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
829
835
|
* A function to get the grouping hash value to be used for the error event.
|
|
830
836
|
* If the grouping hash is an error instance, the normalized error message is used as the grouping hash.
|
|
831
837
|
* If the grouping hash is an empty string or not specified, the default grouping hash is used.
|
|
@@ -865,8 +871,8 @@ if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCE
|
|
|
865
871
|
// https://docs.bugsnag.com/platforms/javascript/customizing-error-reports/#groupinghash
|
|
866
872
|
// https://docs.bugsnag.com/product/error-grouping/#user_defined
|
|
867
873
|
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,this.logger);// Get the final payload to be sent to the metrics service
|
|
868
|
-
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);// send it to metrics service
|
|
869
|
-
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,
|
|
874
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);const errorCategory=getErrorCategory(bsException,category);// send it to metrics service
|
|
875
|
+
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,errorCategory),sendRawData:true},isRawResponse:true});}}// Log handled errors and errors dispatched by the SDK
|
|
870
876
|
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
|
871
877
|
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
|
872
878
|
* Add breadcrumbs to add insight of a user's journey before an error
|
|
@@ -952,7 +958,7 @@ const userIdKey='rl_user_id';const userTraitsKey='rl_trait';const anonymousUserI
|
|
|
952
958
|
const encryptBrowser=value=>`${ENCRYPTION_PREFIX_V3}${toBase64(value)}`;const decryptBrowser=value=>{if(value?.startsWith(ENCRYPTION_PREFIX_V3)){return fromBase64(value.substring(ENCRYPTION_PREFIX_V3.length));}return value;};
|
|
953
959
|
|
|
954
960
|
const EVENT_PAYLOAD_SIZE_BYTES_LIMIT=32*1024;// 32 KB
|
|
955
|
-
const RETRY_REASON_CLIENT_NETWORK='client-network';const RETRY_REASON_CLIENT_TIMEOUT='client-timeout';const DEFAULT_RETRY_REASON=RETRY_REASON_CLIENT_NETWORK;
|
|
961
|
+
const RETRY_REASON_CLIENT_NETWORK='client-network';const RETRY_REASON_CLIENT_TIMEOUT='client-timeout';const DEFAULT_RETRY_REASON=RETRY_REASON_CLIENT_NETWORK;
|
|
956
962
|
|
|
957
963
|
const isStorageQuotaExceeded=e=>{const matchingNames=['QuotaExceededError','NS_ERROR_DOM_QUOTA_REACHED'];// Everything except Firefox, Firefox
|
|
958
964
|
const matchingCodes=[22,1014];if(e instanceof DOMException){return matchingNames.includes(e.name)||matchingCodes.includes(e.code);}return false;};// TODO: also check for SecurityErrors
|
|
@@ -2875,7 +2881,8 @@ this.swapQueueStoreToInMemoryEngine();// and save it there
|
|
|
2875
2881
|
this.set(key,value);}else {const customMessage=STORE_DATA_SAVE_ERROR(key);this.onError(err,customMessage,customMessage);}}}/**
|
|
2876
2882
|
* Get by Key.
|
|
2877
2883
|
*/get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)||decryptedValue===''){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
|
2878
|
-
return JSON.parse(decryptedValue);}catch(err){const
|
|
2884
|
+
return JSON.parse(decryptedValue);}catch(err){const encryptionPluginName=state.storage.encryptionPluginName.value;// Skip error reporting only when the encryption plugin is configured but failed to load
|
|
2885
|
+
const shouldReportError=!encryptionPluginName||!state.plugins.failedPlugins.value.includes(encryptionPluginName);if(shouldReportError){const customMessage=STORE_DATA_FETCH_ERROR(key);this.onError(err,customMessage,customMessage);}return null;}}/**
|
|
2879
2886
|
* Remove by Key.
|
|
2880
2887
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
|
2881
2888
|
* Get original engine
|
|
@@ -3481,19 +3488,21 @@ const safelyInvokeCallback=(callback,args,apiName,logger)=>{if(!isDefined(callba
|
|
|
3481
3488
|
* @param storeManager Store Manager instance
|
|
3482
3489
|
* @param errorHandler Error handler object
|
|
3483
3490
|
* @param logger Logger object
|
|
3484
|
-
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;}/**
|
|
3491
|
+
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;this.eventsBuffer=[];this.isEventBufferingActive=true;}/**
|
|
3485
3492
|
* Initializes the event repository
|
|
3486
3493
|
*/init(){this.dataplaneEventsQueue=this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.init`,state,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.dmtEventsQueue=this.pluginsManager.invokeSingle(`${DMT_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.destinationsEventsQueue=this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.storeManager,this.dmtEventsQueue,this.errorHandler,this.logger);// Start the queue once the client destinations are ready
|
|
3487
|
-
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);// Start the
|
|
3488
|
-
//
|
|
3489
|
-
//
|
|
3490
|
-
//
|
|
3491
|
-
|
|
3492
|
-
|
|
3494
|
+
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);if(!bufferEventsBeforeConsent){this.startDpEventsQueue();}}startDpEventsQueue(){const bufferEventsUntilReady=state.loadOptions.value.bufferDataPlaneEventsUntilReady;const hybridDestExist=state.nativeDestinations.activeDestinations.value.some(dest=>isHybridModeDestination(dest));const shouldBufferEvents=bufferEventsUntilReady&&hybridDestExist;// Start the data plane events queue and replay the events from the buffer
|
|
3495
|
+
// This function is called when the client destinations are ready
|
|
3496
|
+
// or when the timeout expires
|
|
3497
|
+
// or when no buffering is required
|
|
3498
|
+
const startDpQueueAndReplayEvents=()=>{this.isEventBufferingActive=false;this.eventsBuffer.forEach(event=>{this.enqueue(event);});if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true){this.dataplaneEventsQueue?.start();}this.eventsBuffer=[];};let timeoutId;// Start the queue when no event buffering is required
|
|
3499
|
+
// or when buffering is required and the client destinations are ready
|
|
3500
|
+
E(()=>{if(!shouldBufferEvents||state.nativeDestinations.clientDestinationsReady.value){globalThis.clearTimeout(timeoutId);startDpQueueAndReplayEvents();}});// Force start the data plane events queue processing after a timeout
|
|
3501
|
+
if(shouldBufferEvents){this.isEventBufferingActive=true;timeoutId=globalThis.setTimeout(()=>{startDpQueueAndReplayEvents();},state.loadOptions.value.dataPlaneEventsBufferTimeout);}}resume(){if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true&&state.consents.postConsent.value.discardPreConsentEvents){this.dataplaneEventsQueue?.clear();this.destinationsEventsQueue?.clear();}this.startDpEventsQueue();}/**
|
|
3493
3502
|
* Enqueues the event for processing
|
|
3494
3503
|
* @param event RudderEvent object
|
|
3495
3504
|
* @param callback API callback function
|
|
3496
|
-
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger)
|
|
3505
|
+
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);if(this.isEventBufferingActive){this.eventsBuffer.push(dpQEvent);}else {this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger);}// Invoke the callback if it exists
|
|
3497
3506
|
const apiName=`${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;safelyInvokeCallback(callback,[dpQEvent],apiName,this.logger);}}
|
|
3498
3507
|
|
|
3499
3508
|
const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{analyticsInstance:globalThis.rudderanalytics},bubbles:true,cancelable:true,composed:true});globalThis.document.dispatchEvent(customEvent);};const isWriteKeyValid=writeKey=>isString(writeKey)&&writeKey.trim().length>0;const isDataPlaneUrlValid=dataPlaneUrl=>isValidURL(dataPlaneUrl);
|
|
@@ -3526,7 +3535,7 @@ if(state.consents.preConsent.value.enabled===true){state.lifecycle.status.value=
|
|
|
3526
3535
|
* Initialize the storage and event queue
|
|
3527
3536
|
*/onPluginsReady(){// Initialize storage
|
|
3528
3537
|
this.storeManager?.init();this.userSessionManager?.init();// Initialize the appropriate consent manager plugin
|
|
3529
|
-
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}
|
|
3538
|
+
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}this.setActiveDestinations();// Initialize event manager
|
|
3530
3539
|
this.eventManager?.init();// Mark the SDK as initialized
|
|
3531
3540
|
state.lifecycle.status.value='initialized';}/**
|
|
3532
3541
|
* Load plugins
|
|
@@ -3550,18 +3559,18 @@ dispatchSDKEvent('RSA_Ready');}/**
|
|
|
3550
3559
|
// for-loop as the individual events that are processed may
|
|
3551
3560
|
// add more events to the buffer (this is needed for the consent API)
|
|
3552
3561
|
let bufferedEvents=state.eventBuffer.toBeProcessedArray.value;while(bufferedEvents.length>0){const bufferedEvent=bufferedEvents.shift();state.eventBuffer.toBeProcessedArray.value=bufferedEvents;if(bufferedEvent){const methodName=bufferedEvent[0];if(isFunction(this[methodName])){// Send additional arg 'true' to indicate that this is a buffered invocation
|
|
3553
|
-
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}/**
|
|
3562
|
+
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}setActiveDestinations(){this.pluginsManager?.invokeSingle('nativeDestinations.setActiveDestinations',state,this.pluginsManager,this.errorHandler,this.logger);}/**
|
|
3554
3563
|
* Load device mode destinations
|
|
3555
3564
|
*/loadDestinations(){// If the integrations load is already triggered or completed, skip the rest of the logic
|
|
3556
3565
|
if(state.lifecycle.status.value==='destinationsLoading'||state.lifecycle.status.value==='destinationsReady'){return;}// Set in state the desired activeDestinations to inject in DOM
|
|
3557
|
-
this.
|
|
3566
|
+
this.setActiveDestinations();const totalDestinationsToLoad=state.nativeDestinations.activeDestinations.value.length;if(totalDestinationsToLoad===0){state.lifecycle.status.value='destinationsReady';return;}// Start loading native integration scripts and create instances
|
|
3558
3567
|
state.lifecycle.status.value='destinationsLoading';this.pluginsManager?.invokeSingle('nativeDestinations.load',state,this.externalSrcLoader,this.errorHandler,this.logger);// Progress to next lifecycle phase if all native destinations are initialized or failed
|
|
3559
3568
|
E(()=>{const areAllDestinationsReady=totalDestinationsToLoad===0||state.nativeDestinations.initializedDestinations.value.length+state.nativeDestinations.failedDestinations.value.length===totalDestinationsToLoad;if(areAllDestinationsReady){r(()=>{state.lifecycle.status.value='destinationsReady';state.nativeDestinations.clientDestinationsReady.value=true;});}});}/**
|
|
3560
3569
|
* Move to the ready state
|
|
3561
3570
|
*/// eslint-disable-next-line class-methods-use-this
|
|
3562
3571
|
onDestinationsReady(){// May be do any destination specific actions here
|
|
3563
3572
|
// Mark the ready status if not already done
|
|
3564
|
-
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
|
3573
|
+
if(state.lifecycle.status.value!=='ready'){r(()=>{state.lifecycle.status.value='ready';});}}// End lifecycle methods
|
|
3565
3574
|
// Start consumer exposed methods
|
|
3566
3575
|
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
|
|
3567
3576
|
* If destinations are loaded or no integration is available for loading
|
|
@@ -521,7 +521,7 @@
|
|
|
521
521
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
522
522
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
523
523
|
|
|
524
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
524
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.28.1-beta.pr.2744.1adb5b9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
|
|
525
525
|
|
|
526
526
|
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';
|
|
527
527
|
|
|
@@ -660,7 +660,7 @@
|
|
|
660
660
|
|
|
661
661
|
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';
|
|
662
662
|
|
|
663
|
-
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:
|
|
663
|
+
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=d$1(clone(defaultLoadOptions));
|
|
664
664
|
|
|
665
665
|
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
|
|
666
666
|
anonymousId:false,initialReferrer:false,initialReferringDomain:false}});const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
|
|
@@ -744,7 +744,7 @@
|
|
|
744
744
|
* Utility to parse XHR JSON response
|
|
745
745
|
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
|
746
746
|
|
|
747
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];
|
|
747
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];const INTEGRATIONS_ERROR_CATEGORY='integrations';const SDK_ERROR_CATEGORY='sdk';const DEFAULT_ERROR_CATEGORY=SDK_ERROR_CATEGORY;
|
|
748
748
|
|
|
749
749
|
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
|
750
750
|
* Utility to create request configuration based on default options
|
|
@@ -780,7 +780,7 @@
|
|
|
780
780
|
// Potential PII or sensitive data
|
|
781
781
|
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
|
|
782
782
|
'eventBuffer',// pre-load event buffer (may contain PII)
|
|
783
|
-
'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';
|
|
783
|
+
'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';
|
|
784
784
|
|
|
785
785
|
const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
|
786
786
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
|
@@ -797,8 +797,8 @@
|
|
|
797
797
|
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:{}});/**
|
|
798
798
|
* A function to get the Bugsnag release stage for the current environment
|
|
799
799
|
* @param getHostName Optional function to get the hostname (primarily for testing)
|
|
800
|
-
* @returns 'development' if the host is empty (for file:// protocol etc.) or a dev host (localhost, 127.0.0.1, etc.), otherwise ''
|
|
801
|
-
*/const getReleaseStage=(getHostName=()=>window.location.hostname)=>{const host=getHostName();return !host||host&&DEV_HOSTS.includes(host)?'development':'
|
|
800
|
+
* @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)
|
|
801
|
+
*/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
|
|
802
802
|
// so that they show up as separate tabs in the dashboard
|
|
803
803
|
...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
|
|
804
804
|
* A function to check if adblockers are active. The promise's resolve function
|
|
@@ -826,12 +826,18 @@
|
|
|
826
826
|
checkIfAdBlockersAreActive(state,httpClient,resolve);}}else {// Filter out errors that are not from the RS CDN.
|
|
827
827
|
resolve(false);}}else {// Allow the error to be notified if no URL could be extracted from the error message
|
|
828
828
|
resolve(true);}}else {resolve(!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg)));}});};/**
|
|
829
|
+
* A function to get the directory name from a file path.
|
|
830
|
+
* @param {string} filePath The file path
|
|
831
|
+
* @returns The directory name or undefined if the file path is invalid
|
|
832
|
+
*/const getDirectoryName=filePath=>{if(!filePath){return undefined;}const paths=filePath.split('/');return paths.at(-2);};/**
|
|
833
|
+
* A function to get the top stack path from the exception.
|
|
834
|
+
* @param {Exception} exception The exception object
|
|
835
|
+
* @returns The top stack path or undefined if the exception is invalid
|
|
836
|
+
*/const getTopStackPath=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return undefined;}return errorOrigin;};/**
|
|
829
837
|
* A function to determine if the error is from Rudder SDK
|
|
830
838
|
* @param {Error} exception
|
|
831
839
|
* @returns
|
|
832
|
-
*/const isSDKError=exception=>{const errorOrigin=exception
|
|
833
|
-
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
|
834
|
-
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category:category??DEFAULT_ERROR_CATEGORY},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
840
|
+
*/const isSDKError=exception=>{const errorOrigin=getTopStackPath(exception);if(!errorOrigin){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const parentFolderName=getDirectoryName(errorOrigin);return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorCategory=(exception,category)=>{if(category){return category;}const errorOrigin=getTopStackPath(exception);const directoryName=getDirectoryName(errorOrigin);if(directoryName===CDN_INT_DIR){return INTEGRATIONS_ERROR_CATEGORY;}return DEFAULT_ERROR_CATEGORY;};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
835
841
|
* A function to get the grouping hash value to be used for the error event.
|
|
836
842
|
* If the grouping hash is an error instance, the normalized error message is used as the grouping hash.
|
|
837
843
|
* If the grouping hash is an empty string or not specified, the default grouping hash is used.
|
|
@@ -871,8 +877,8 @@
|
|
|
871
877
|
// https://docs.bugsnag.com/platforms/javascript/customizing-error-reports/#groupinghash
|
|
872
878
|
// https://docs.bugsnag.com/product/error-grouping/#user_defined
|
|
873
879
|
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,this.logger);// Get the final payload to be sent to the metrics service
|
|
874
|
-
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);// send it to metrics service
|
|
875
|
-
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,
|
|
880
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);const errorCategory=getErrorCategory(bsException,category);// send it to metrics service
|
|
881
|
+
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,errorCategory),sendRawData:true},isRawResponse:true});}}// Log handled errors and errors dispatched by the SDK
|
|
876
882
|
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
|
877
883
|
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
|
878
884
|
* Add breadcrumbs to add insight of a user's journey before an error
|
|
@@ -958,7 +964,7 @@
|
|
|
958
964
|
const encryptBrowser=value=>`${ENCRYPTION_PREFIX_V3}${toBase64(value)}`;const decryptBrowser=value=>{if(value?.startsWith(ENCRYPTION_PREFIX_V3)){return fromBase64(value.substring(ENCRYPTION_PREFIX_V3.length));}return value;};
|
|
959
965
|
|
|
960
966
|
const EVENT_PAYLOAD_SIZE_BYTES_LIMIT=32*1024;// 32 KB
|
|
961
|
-
const RETRY_REASON_CLIENT_NETWORK='client-network';const RETRY_REASON_CLIENT_TIMEOUT='client-timeout';const DEFAULT_RETRY_REASON=RETRY_REASON_CLIENT_NETWORK;
|
|
967
|
+
const RETRY_REASON_CLIENT_NETWORK='client-network';const RETRY_REASON_CLIENT_TIMEOUT='client-timeout';const DEFAULT_RETRY_REASON=RETRY_REASON_CLIENT_NETWORK;
|
|
962
968
|
|
|
963
969
|
const isStorageQuotaExceeded=e=>{const matchingNames=['QuotaExceededError','NS_ERROR_DOM_QUOTA_REACHED'];// Everything except Firefox, Firefox
|
|
964
970
|
const matchingCodes=[22,1014];if(e instanceof DOMException){return matchingNames.includes(e.name)||matchingCodes.includes(e.code);}return false;};// TODO: also check for SecurityErrors
|
|
@@ -2881,7 +2887,8 @@
|
|
|
2881
2887
|
this.set(key,value);}else {const customMessage=STORE_DATA_SAVE_ERROR(key);this.onError(err,customMessage,customMessage);}}}/**
|
|
2882
2888
|
* Get by Key.
|
|
2883
2889
|
*/get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)||decryptedValue===''){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
|
2884
|
-
return JSON.parse(decryptedValue);}catch(err){const
|
|
2890
|
+
return JSON.parse(decryptedValue);}catch(err){const encryptionPluginName=state.storage.encryptionPluginName.value;// Skip error reporting only when the encryption plugin is configured but failed to load
|
|
2891
|
+
const shouldReportError=!encryptionPluginName||!state.plugins.failedPlugins.value.includes(encryptionPluginName);if(shouldReportError){const customMessage=STORE_DATA_FETCH_ERROR(key);this.onError(err,customMessage,customMessage);}return null;}}/**
|
|
2885
2892
|
* Remove by Key.
|
|
2886
2893
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
|
2887
2894
|
* Get original engine
|
|
@@ -3487,19 +3494,21 @@
|
|
|
3487
3494
|
* @param storeManager Store Manager instance
|
|
3488
3495
|
* @param errorHandler Error handler object
|
|
3489
3496
|
* @param logger Logger object
|
|
3490
|
-
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;}/**
|
|
3497
|
+
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;this.eventsBuffer=[];this.isEventBufferingActive=true;}/**
|
|
3491
3498
|
* Initializes the event repository
|
|
3492
3499
|
*/init(){this.dataplaneEventsQueue=this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.init`,state,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.dmtEventsQueue=this.pluginsManager.invokeSingle(`${DMT_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.destinationsEventsQueue=this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.storeManager,this.dmtEventsQueue,this.errorHandler,this.logger);// Start the queue once the client destinations are ready
|
|
3493
|
-
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);// Start the
|
|
3494
|
-
//
|
|
3495
|
-
//
|
|
3496
|
-
//
|
|
3497
|
-
|
|
3498
|
-
|
|
3500
|
+
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);if(!bufferEventsBeforeConsent){this.startDpEventsQueue();}}startDpEventsQueue(){const bufferEventsUntilReady=state.loadOptions.value.bufferDataPlaneEventsUntilReady;const hybridDestExist=state.nativeDestinations.activeDestinations.value.some(dest=>isHybridModeDestination(dest));const shouldBufferEvents=bufferEventsUntilReady&&hybridDestExist;// Start the data plane events queue and replay the events from the buffer
|
|
3501
|
+
// This function is called when the client destinations are ready
|
|
3502
|
+
// or when the timeout expires
|
|
3503
|
+
// or when no buffering is required
|
|
3504
|
+
const startDpQueueAndReplayEvents=()=>{this.isEventBufferingActive=false;this.eventsBuffer.forEach(event=>{this.enqueue(event);});if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true){this.dataplaneEventsQueue?.start();}this.eventsBuffer=[];};let timeoutId;// Start the queue when no event buffering is required
|
|
3505
|
+
// or when buffering is required and the client destinations are ready
|
|
3506
|
+
E(()=>{if(!shouldBufferEvents||state.nativeDestinations.clientDestinationsReady.value){globalThis.clearTimeout(timeoutId);startDpQueueAndReplayEvents();}});// Force start the data plane events queue processing after a timeout
|
|
3507
|
+
if(shouldBufferEvents){this.isEventBufferingActive=true;timeoutId=globalThis.setTimeout(()=>{startDpQueueAndReplayEvents();},state.loadOptions.value.dataPlaneEventsBufferTimeout);}}resume(){if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true&&state.consents.postConsent.value.discardPreConsentEvents){this.dataplaneEventsQueue?.clear();this.destinationsEventsQueue?.clear();}this.startDpEventsQueue();}/**
|
|
3499
3508
|
* Enqueues the event for processing
|
|
3500
3509
|
* @param event RudderEvent object
|
|
3501
3510
|
* @param callback API callback function
|
|
3502
|
-
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger)
|
|
3511
|
+
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);if(this.isEventBufferingActive){this.eventsBuffer.push(dpQEvent);}else {this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger);}// Invoke the callback if it exists
|
|
3503
3512
|
const apiName=`${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;safelyInvokeCallback(callback,[dpQEvent],apiName,this.logger);}}
|
|
3504
3513
|
|
|
3505
3514
|
const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{analyticsInstance:globalThis.rudderanalytics},bubbles:true,cancelable:true,composed:true});globalThis.document.dispatchEvent(customEvent);};const isWriteKeyValid=writeKey=>isString(writeKey)&&writeKey.trim().length>0;const isDataPlaneUrlValid=dataPlaneUrl=>isValidURL(dataPlaneUrl);
|
|
@@ -3532,7 +3541,7 @@
|
|
|
3532
3541
|
* Initialize the storage and event queue
|
|
3533
3542
|
*/onPluginsReady(){// Initialize storage
|
|
3534
3543
|
this.storeManager?.init();this.userSessionManager?.init();// Initialize the appropriate consent manager plugin
|
|
3535
|
-
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}
|
|
3544
|
+
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}this.setActiveDestinations();// Initialize event manager
|
|
3536
3545
|
this.eventManager?.init();// Mark the SDK as initialized
|
|
3537
3546
|
state.lifecycle.status.value='initialized';}/**
|
|
3538
3547
|
* Load plugins
|
|
@@ -3556,18 +3565,18 @@
|
|
|
3556
3565
|
// for-loop as the individual events that are processed may
|
|
3557
3566
|
// add more events to the buffer (this is needed for the consent API)
|
|
3558
3567
|
let bufferedEvents=state.eventBuffer.toBeProcessedArray.value;while(bufferedEvents.length>0){const bufferedEvent=bufferedEvents.shift();state.eventBuffer.toBeProcessedArray.value=bufferedEvents;if(bufferedEvent){const methodName=bufferedEvent[0];if(isFunction(this[methodName])){// Send additional arg 'true' to indicate that this is a buffered invocation
|
|
3559
|
-
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}/**
|
|
3568
|
+
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}setActiveDestinations(){this.pluginsManager?.invokeSingle('nativeDestinations.setActiveDestinations',state,this.pluginsManager,this.errorHandler,this.logger);}/**
|
|
3560
3569
|
* Load device mode destinations
|
|
3561
3570
|
*/loadDestinations(){// If the integrations load is already triggered or completed, skip the rest of the logic
|
|
3562
3571
|
if(state.lifecycle.status.value==='destinationsLoading'||state.lifecycle.status.value==='destinationsReady'){return;}// Set in state the desired activeDestinations to inject in DOM
|
|
3563
|
-
this.
|
|
3572
|
+
this.setActiveDestinations();const totalDestinationsToLoad=state.nativeDestinations.activeDestinations.value.length;if(totalDestinationsToLoad===0){state.lifecycle.status.value='destinationsReady';return;}// Start loading native integration scripts and create instances
|
|
3564
3573
|
state.lifecycle.status.value='destinationsLoading';this.pluginsManager?.invokeSingle('nativeDestinations.load',state,this.externalSrcLoader,this.errorHandler,this.logger);// Progress to next lifecycle phase if all native destinations are initialized or failed
|
|
3565
3574
|
E(()=>{const areAllDestinationsReady=totalDestinationsToLoad===0||state.nativeDestinations.initializedDestinations.value.length+state.nativeDestinations.failedDestinations.value.length===totalDestinationsToLoad;if(areAllDestinationsReady){r(()=>{state.lifecycle.status.value='destinationsReady';state.nativeDestinations.clientDestinationsReady.value=true;});}});}/**
|
|
3566
3575
|
* Move to the ready state
|
|
3567
3576
|
*/// eslint-disable-next-line class-methods-use-this
|
|
3568
3577
|
onDestinationsReady(){// May be do any destination specific actions here
|
|
3569
3578
|
// Mark the ready status if not already done
|
|
3570
|
-
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
|
3579
|
+
if(state.lifecycle.status.value!=='ready'){r(()=>{state.lifecycle.status.value='ready';});}}// End lifecycle methods
|
|
3571
3580
|
// Start consumer exposed methods
|
|
3572
3581
|
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
|
|
3573
3582
|
* If destinations are loaded or no integration is available for loading
|
|
@@ -510,7 +510,7 @@ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// esli
|
|
|
510
510
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
511
511
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
512
512
|
|
|
513
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
513
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.28.1-beta.pr.2744.1adb5b9';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
|
|
514
514
|
|
|
515
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';
|
|
516
516
|
|
|
@@ -649,7 +649,7 @@ const BUILD_TYPE='modern';const SDK_CDN_BASE_URL='https://cdn.rudderlabs.com';co
|
|
|
649
649
|
|
|
650
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';
|
|
651
651
|
|
|
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:
|
|
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=d(clone(defaultLoadOptions));
|
|
653
653
|
|
|
654
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
|
|
655
655
|
anonymousId:false,initialReferrer:false,initialReferringDomain:false}});const SERVER_SIDE_COOKIES_DEBOUNCE_TIME=10;// milliseconds
|
|
@@ -733,7 +733,7 @@ try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch{retur
|
|
|
733
733
|
* Utility to parse XHR JSON response
|
|
734
734
|
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
|
735
735
|
|
|
736
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];
|
|
736
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];const INTEGRATIONS_ERROR_CATEGORY='integrations';const SDK_ERROR_CATEGORY='sdk';const DEFAULT_ERROR_CATEGORY=SDK_ERROR_CATEGORY;
|
|
737
737
|
|
|
738
738
|
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
|
739
739
|
* Utility to create request configuration based on default options
|
|
@@ -769,7 +769,7 @@ const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts includi
|
|
|
769
769
|
// Potential PII or sensitive data
|
|
770
770
|
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
|
|
771
771
|
'eventBuffer',// pre-load event buffer (may contain PII)
|
|
772
|
-
'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';
|
|
772
|
+
'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';
|
|
773
773
|
|
|
774
774
|
const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
|
775
775
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
|
@@ -786,8 +786,8 @@ throw err;}};
|
|
|
786
786
|
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:{}});/**
|
|
787
787
|
* A function to get the Bugsnag release stage for the current environment
|
|
788
788
|
* @param getHostName Optional function to get the hostname (primarily for testing)
|
|
789
|
-
* @returns 'development' if the host is empty (for file:// protocol etc.) or a dev host (localhost, 127.0.0.1, etc.), otherwise ''
|
|
790
|
-
*/const getReleaseStage=(getHostName=()=>window.location.hostname)=>{const host=getHostName();return !host||host&&DEV_HOSTS.includes(host)?'development':'
|
|
789
|
+
* @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)
|
|
790
|
+
*/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
|
|
791
791
|
// so that they show up as separate tabs in the dashboard
|
|
792
792
|
...getAppStateForMetadata(state)},user:getUserDetails(source,session,lifecycle,autoTrack)}]};};/**
|
|
793
793
|
* A function to check if adblockers are active. The promise's resolve function
|
|
@@ -815,12 +815,18 @@ if(state.capabilities.cspBlockedURLs.value.includes(extractedURL)){resolve(false
|
|
|
815
815
|
checkIfAdBlockersAreActive(state,httpClient,resolve);}}else {// Filter out errors that are not from the RS CDN.
|
|
816
816
|
resolve(false);}}else {// Allow the error to be notified if no URL could be extracted from the error message
|
|
817
817
|
resolve(true);}}else {resolve(!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg)));}});};/**
|
|
818
|
+
* A function to get the directory name from a file path.
|
|
819
|
+
* @param {string} filePath The file path
|
|
820
|
+
* @returns The directory name or undefined if the file path is invalid
|
|
821
|
+
*/const getDirectoryName=filePath=>{if(!filePath){return undefined;}const paths=filePath.split('/');return paths.at(-2);};/**
|
|
822
|
+
* A function to get the top stack path from the exception.
|
|
823
|
+
* @param {Exception} exception The exception object
|
|
824
|
+
* @returns The top stack path or undefined if the exception is invalid
|
|
825
|
+
*/const getTopStackPath=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return undefined;}return errorOrigin;};/**
|
|
818
826
|
* A function to determine if the error is from Rudder SDK
|
|
819
827
|
* @param {Error} exception
|
|
820
828
|
* @returns
|
|
821
|
-
*/const isSDKError=exception=>{const errorOrigin=exception
|
|
822
|
-
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
|
823
|
-
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category:category??DEFAULT_ERROR_CATEGORY},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
829
|
+
*/const isSDKError=exception=>{const errorOrigin=getTopStackPath(exception);if(!errorOrigin){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const parentFolderName=getDirectoryName(errorOrigin);return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorCategory=(exception,category)=>{if(category){return category;}const errorOrigin=getTopStackPath(exception);const directoryName=getDirectoryName(errorOrigin);if(directoryName===CDN_INT_DIR){return INTEGRATIONS_ERROR_CATEGORY;}return DEFAULT_ERROR_CATEGORY;};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
824
830
|
* A function to get the grouping hash value to be used for the error event.
|
|
825
831
|
* If the grouping hash is an error instance, the normalized error message is used as the grouping hash.
|
|
826
832
|
* If the grouping hash is an empty string or not specified, the default grouping hash is used.
|
|
@@ -860,8 +866,8 @@ if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCE
|
|
|
860
866
|
// https://docs.bugsnag.com/platforms/javascript/customizing-error-reports/#groupinghash
|
|
861
867
|
// https://docs.bugsnag.com/product/error-grouping/#user_defined
|
|
862
868
|
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,this.logger);// Get the final payload to be sent to the metrics service
|
|
863
|
-
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);// send it to metrics service
|
|
864
|
-
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,
|
|
869
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);const errorCategory=getErrorCategory(bsException,category);// send it to metrics service
|
|
870
|
+
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,errorCategory),sendRawData:true},isRawResponse:true});}}// Log handled errors and errors dispatched by the SDK
|
|
865
871
|
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
|
866
872
|
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
|
867
873
|
* Add breadcrumbs to add insight of a user's journey before an error
|
|
@@ -1154,7 +1160,8 @@ this.swapQueueStoreToInMemoryEngine();// and save it there
|
|
|
1154
1160
|
this.set(key,value);}else {const customMessage=STORE_DATA_SAVE_ERROR(key);this.onError(err,customMessage,customMessage);}}}/**
|
|
1155
1161
|
* Get by Key.
|
|
1156
1162
|
*/get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)||decryptedValue===''){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
|
1157
|
-
return JSON.parse(decryptedValue);}catch(err){const
|
|
1163
|
+
return JSON.parse(decryptedValue);}catch(err){const encryptionPluginName=state.storage.encryptionPluginName.value;// Skip error reporting only when the encryption plugin is configured but failed to load
|
|
1164
|
+
const shouldReportError=!encryptionPluginName||!state.plugins.failedPlugins.value.includes(encryptionPluginName);if(shouldReportError){const customMessage=STORE_DATA_FETCH_ERROR(key);this.onError(err,customMessage,customMessage);}return null;}}/**
|
|
1158
1165
|
* Remove by Key.
|
|
1159
1166
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
|
1160
1167
|
* Get original engine
|
|
@@ -1777,19 +1784,21 @@ const safelyInvokeCallback=(callback,args,apiName,logger)=>{if(!isDefined(callba
|
|
|
1777
1784
|
* @param storeManager Store Manager instance
|
|
1778
1785
|
* @param errorHandler Error handler object
|
|
1779
1786
|
* @param logger Logger object
|
|
1780
|
-
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;}/**
|
|
1787
|
+
*/constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.pluginsManager=pluginsManager;this.errorHandler=errorHandler;this.httpClient=httpClient;this.logger=logger;this.storeManager=storeManager;this.eventsBuffer=[];this.isEventBufferingActive=true;}/**
|
|
1781
1788
|
* Initializes the event repository
|
|
1782
1789
|
*/init(){this.dataplaneEventsQueue=this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.init`,state,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.dmtEventsQueue=this.pluginsManager.invokeSingle(`${DMT_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.httpClient,this.storeManager,this.errorHandler,this.logger);this.destinationsEventsQueue=this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.init`,state,this.pluginsManager,this.storeManager,this.dmtEventsQueue,this.errorHandler,this.logger);// Start the queue once the client destinations are ready
|
|
1783
|
-
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);// Start the
|
|
1784
|
-
//
|
|
1785
|
-
//
|
|
1786
|
-
//
|
|
1787
|
-
|
|
1788
|
-
|
|
1790
|
+
E(()=>{if(state.nativeDestinations.clientDestinationsReady.value===true){this.destinationsEventsQueue?.start();this.dmtEventsQueue?.start();}});const bufferEventsBeforeConsent=shouldBufferEventsForPreConsent(state);if(!bufferEventsBeforeConsent){this.startDpEventsQueue();}}startDpEventsQueue(){const bufferEventsUntilReady=state.loadOptions.value.bufferDataPlaneEventsUntilReady;const hybridDestExist=state.nativeDestinations.activeDestinations.value.some(dest=>isHybridModeDestination(dest));const shouldBufferEvents=bufferEventsUntilReady&&hybridDestExist;// Start the data plane events queue and replay the events from the buffer
|
|
1791
|
+
// This function is called when the client destinations are ready
|
|
1792
|
+
// or when the timeout expires
|
|
1793
|
+
// or when no buffering is required
|
|
1794
|
+
const startDpQueueAndReplayEvents=()=>{this.isEventBufferingActive=false;this.eventsBuffer.forEach(event=>{this.enqueue(event);});if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true){this.dataplaneEventsQueue?.start();}this.eventsBuffer=[];};let timeoutId;// Start the queue when no event buffering is required
|
|
1795
|
+
// or when buffering is required and the client destinations are ready
|
|
1796
|
+
E(()=>{if(!shouldBufferEvents||state.nativeDestinations.clientDestinationsReady.value){globalThis.clearTimeout(timeoutId);startDpQueueAndReplayEvents();}});// Force start the data plane events queue processing after a timeout
|
|
1797
|
+
if(shouldBufferEvents){this.isEventBufferingActive=true;timeoutId=globalThis.setTimeout(()=>{startDpQueueAndReplayEvents();},state.loadOptions.value.dataPlaneEventsBufferTimeout);}}resume(){if(this.dataplaneEventsQueue?.scheduleTimeoutActive!==true&&state.consents.postConsent.value.discardPreConsentEvents){this.dataplaneEventsQueue?.clear();this.destinationsEventsQueue?.clear();}this.startDpEventsQueue();}/**
|
|
1789
1798
|
* Enqueues the event for processing
|
|
1790
1799
|
* @param event RudderEvent object
|
|
1791
1800
|
* @param callback API callback function
|
|
1792
|
-
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger)
|
|
1801
|
+
*/enqueue(event,callback){const dpQEvent=getFinalEvent(event,state);if(this.isEventBufferingActive){this.eventsBuffer.push(dpQEvent);}else {this.pluginsManager.invokeSingle(`${DATA_PLANE_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.dataplaneEventsQueue,dpQEvent,this.errorHandler,this.logger);const dQEvent=clone(event);this.pluginsManager.invokeSingle(`${DESTINATIONS_QUEUE_EXT_POINT_PREFIX}.enqueue`,state,this.destinationsEventsQueue,dQEvent,this.errorHandler,this.logger);}// Invoke the callback if it exists
|
|
1793
1802
|
const apiName=`${event.type.charAt(0).toUpperCase()}${event.type.slice(1)}${API_SUFFIX}`;safelyInvokeCallback(callback,[dpQEvent],apiName,this.logger);}}
|
|
1794
1803
|
|
|
1795
1804
|
const dispatchSDKEvent=event=>{const customEvent=new CustomEvent(event,{detail:{analyticsInstance:globalThis.rudderanalytics},bubbles:true,cancelable:true,composed:true});globalThis.document.dispatchEvent(customEvent);};const isWriteKeyValid=writeKey=>isString(writeKey)&&writeKey.trim().length>0;const isDataPlaneUrlValid=dataPlaneUrl=>isValidURL(dataPlaneUrl);
|
|
@@ -1822,7 +1831,7 @@ if(state.consents.preConsent.value.enabled===true){state.lifecycle.status.value=
|
|
|
1822
1831
|
* Initialize the storage and event queue
|
|
1823
1832
|
*/onPluginsReady(){// Initialize storage
|
|
1824
1833
|
this.storeManager?.init();this.userSessionManager?.init();// Initialize the appropriate consent manager plugin
|
|
1825
|
-
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}
|
|
1834
|
+
if(state.consents.enabled.value&&!state.consents.initialized.value){this.pluginsManager?.invokeSingle(`consentManager.init`,state,this.logger);if(state.consents.preConsent.value.enabled===false){this.pluginsManager?.invokeSingle(`consentManager.updateConsentsInfo`,state,this.storeManager,this.logger);}}this.setActiveDestinations();// Initialize event manager
|
|
1826
1835
|
this.eventManager?.init();// Mark the SDK as initialized
|
|
1827
1836
|
state.lifecycle.status.value='initialized';}/**
|
|
1828
1837
|
* Load plugins
|
|
@@ -1846,18 +1855,18 @@ dispatchSDKEvent('RSA_Ready');}/**
|
|
|
1846
1855
|
// for-loop as the individual events that are processed may
|
|
1847
1856
|
// add more events to the buffer (this is needed for the consent API)
|
|
1848
1857
|
let bufferedEvents=state.eventBuffer.toBeProcessedArray.value;while(bufferedEvents.length>0){const bufferedEvent=bufferedEvents.shift();state.eventBuffer.toBeProcessedArray.value=bufferedEvents;if(bufferedEvent){const methodName=bufferedEvent[0];if(isFunction(this[methodName])){// Send additional arg 'true' to indicate that this is a buffered invocation
|
|
1849
|
-
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}/**
|
|
1858
|
+
this[methodName](...bufferedEvent.slice(1),true);}}bufferedEvents=state.eventBuffer.toBeProcessedArray.value;}}setActiveDestinations(){this.pluginsManager?.invokeSingle('nativeDestinations.setActiveDestinations',state,this.pluginsManager,this.errorHandler,this.logger);}/**
|
|
1850
1859
|
* Load device mode destinations
|
|
1851
1860
|
*/loadDestinations(){// If the integrations load is already triggered or completed, skip the rest of the logic
|
|
1852
1861
|
if(state.lifecycle.status.value==='destinationsLoading'||state.lifecycle.status.value==='destinationsReady'){return;}// Set in state the desired activeDestinations to inject in DOM
|
|
1853
|
-
this.
|
|
1862
|
+
this.setActiveDestinations();const totalDestinationsToLoad=state.nativeDestinations.activeDestinations.value.length;if(totalDestinationsToLoad===0){state.lifecycle.status.value='destinationsReady';return;}// Start loading native integration scripts and create instances
|
|
1854
1863
|
state.lifecycle.status.value='destinationsLoading';this.pluginsManager?.invokeSingle('nativeDestinations.load',state,this.externalSrcLoader,this.errorHandler,this.logger);// Progress to next lifecycle phase if all native destinations are initialized or failed
|
|
1855
1864
|
E(()=>{const areAllDestinationsReady=totalDestinationsToLoad===0||state.nativeDestinations.initializedDestinations.value.length+state.nativeDestinations.failedDestinations.value.length===totalDestinationsToLoad;if(areAllDestinationsReady){r(()=>{state.lifecycle.status.value='destinationsReady';state.nativeDestinations.clientDestinationsReady.value=true;});}});}/**
|
|
1856
1865
|
* Move to the ready state
|
|
1857
1866
|
*/// eslint-disable-next-line class-methods-use-this
|
|
1858
1867
|
onDestinationsReady(){// May be do any destination specific actions here
|
|
1859
1868
|
// Mark the ready status if not already done
|
|
1860
|
-
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
|
1869
|
+
if(state.lifecycle.status.value!=='ready'){r(()=>{state.lifecycle.status.value='ready';});}}// End lifecycle methods
|
|
1861
1870
|
// Start consumer exposed methods
|
|
1862
1871
|
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
|
|
1863
1872
|
* If destinations are loaded or no integration is available for loading
|