diff --git a/packages/experiment-tag/src/experiment.ts b/packages/experiment-tag/src/experiment.ts index de212aba..1d519863 100644 --- a/packages/experiment-tag/src/experiment.ts +++ b/packages/experiment-tag/src/experiment.ts @@ -24,6 +24,13 @@ import { ConsentAwareStorage, } from './storage/consent-aware-storage'; import { + getExperimentStorageKey, + getPreviewModeSessionKey, + getRedirectStorageKey, + getVisualEditorSessionKey, +} from './storage/keys'; +import { + deletePersistedData, getAndParseStorageItem, setAndStringifyStorageItem, } from './storage/storage'; @@ -46,7 +53,7 @@ import { } from './types'; import { applyAntiFlickerCss } from './util/anti-flicker'; import { getInjectUtils } from './util/inject-utils'; -import { VISUAL_EDITOR_SESSION_KEY, WindowMessenger } from './util/messenger'; +import { WindowMessenger } from './util/messenger'; import { patchRemoveChild } from './util/patch'; import { getUrlParams, @@ -62,7 +69,6 @@ const MUTATE_ACTION = 'mutate'; export const INJECT_ACTION = 'inject'; const REDIRECT_ACTION = 'redirect'; export const PREVIEW_MODE_PARAM = 'PREVIEW'; -export const PREVIEW_MODE_SESSION_KEY = 'amp-preview-mode'; const VISUAL_EDITOR_PARAM = 'VISUAL_EDITOR'; safeGlobal.Experiment = FeatureExperiment; @@ -200,7 +206,7 @@ export class DefaultWebExperimentClient implements WebExperimentClient { const urlParams = getUrlParams(); this.isVisualEditorMode = urlParams[VISUAL_EDITOR_PARAM] === 'true' || - this.storage.getItem('sessionStorage', VISUAL_EDITOR_SESSION_KEY) !== + this.storage.getItem('sessionStorage', getVisualEditorSessionKey()) !== null; this.subscriptionManager = new SubscriptionManager( this, @@ -234,7 +240,7 @@ export class DefaultWebExperimentClient implements WebExperimentClient { // fire url_change upon landing on page, set updateActivePagesOnly to not trigger variant actions this.messageBus.publish('url_change', { updateActivePages: true }); - const experimentStorageName = `EXP_${this.apiKey.slice(0, 10)}`; + const experimentStorageName = getExperimentStorageKey(this.apiKey); const user = this.storage.getItem( 'localStorage', @@ -554,6 +560,9 @@ export class DefaultWebExperimentClient implements WebExperimentClient { public setConsentStatus(consentStatus: ConsentStatus) { this.consentOptions.status = consentStatus; this.storage.setConsentStatus(consentStatus); + if (consentStatus === ConsentStatus.REJECTED) { + deletePersistedData(this.apiKey, this.config); + } this.consentAwareExposureHandler.setConsentStatus(consentStatus); } @@ -829,7 +838,7 @@ export class DefaultWebExperimentClient implements WebExperimentClient { variant: Variant, redirectUrl: string, ) { - const redirectStorageKey = `EXP_${this.apiKey.slice(0, 10)}_REDIRECT`; + const redirectStorageKey = getRedirectStorageKey(this.apiKey); // Store the current flag and variant for exposure tracking after redirect const storedRedirects = this.storage.getItem('sessionStorage', redirectStorageKey) || {}; @@ -839,7 +848,7 @@ export class DefaultWebExperimentClient implements WebExperimentClient { private fireStoredRedirectImpressions() { // Check for stored redirects and process them - const redirectStorageKey = `EXP_${this.apiKey.slice(0, 10)}_REDIRECT`; + const redirectStorageKey = getRedirectStorageKey(this.apiKey); const storedRedirects = this.storage.getItem('sessionStorage', redirectStorageKey) || {}; @@ -892,7 +901,7 @@ export class DefaultWebExperimentClient implements WebExperimentClient { setAndStringifyStorageItem( 'sessionStorage', - PREVIEW_MODE_SESSION_KEY, + getPreviewModeSessionKey(), { previewFlags: this.previewFlags, }, @@ -914,7 +923,7 @@ export class DefaultWebExperimentClient implements WebExperimentClient { } else { const previewState = getAndParseStorageItem( 'sessionStorage', - PREVIEW_MODE_SESSION_KEY, + getPreviewModeSessionKey(), ); if (previewState) { this.previewFlags = previewState.previewFlags; diff --git a/packages/experiment-tag/src/index.ts b/packages/experiment-tag/src/index.ts index 12d49ea0..d9ff1c81 100644 --- a/packages/experiment-tag/src/index.ts +++ b/packages/experiment-tag/src/index.ts @@ -3,6 +3,7 @@ import { getGlobalScope } from '@amplitude/experiment-core'; import { DefaultWebExperimentClient } from './experiment'; import { HttpClient } from './preview/http'; import { SdkPreviewApi } from './preview/preview-api'; +import { deletePersistedData } from './storage/storage'; import { ConsentStatus, WebExperimentConfig } from './types'; import { applyAntiFlickerCss } from './util/anti-flicker'; import { isPreviewMode } from './util/url'; @@ -17,6 +18,7 @@ export const initialize = ( getGlobalScope()?.experimentConfig?.consentOptions?.status === ConsentStatus.REJECTED ) { + deletePersistedData(apiKey, config); return; } const shouldFetchConfigs = diff --git a/packages/experiment-tag/src/storage/keys.ts b/packages/experiment-tag/src/storage/keys.ts new file mode 100644 index 00000000..a5971aad --- /dev/null +++ b/packages/experiment-tag/src/storage/keys.ts @@ -0,0 +1,27 @@ +import { WebExperimentConfig } from '../types'; + +export const getExperimentStorageKey = (apiKey: string): string => { + return `EXP_${apiKey.slice(0, 10)}`; +}; + +export const getDefaultUserProviderStorageKey = (apiKey: string): string => { + return `EXP_${apiKey.slice(0, 10)}_DEFAULT_USER_PROVIDER`; +}; + +export const getUnsentEventsStorageKey = ( + config: WebExperimentConfig, +): string => { + return `EXP_unsent_${config.instanceName ?? 'default_instance'}`; +}; + +export const getRedirectStorageKey = (apiKey: string): string => { + return `EXP_${apiKey.slice(0, 10)}_REDIRECT`; +}; + +export const getPreviewModeSessionKey = (): string => { + return 'amp-preview-mode'; +}; + +export const getVisualEditorSessionKey = (): string => { + return 'visual-editor-state'; +}; diff --git a/packages/experiment-tag/src/storage/storage.ts b/packages/experiment-tag/src/storage/storage.ts index e18948a7..ce5b13d1 100644 --- a/packages/experiment-tag/src/storage/storage.ts +++ b/packages/experiment-tag/src/storage/storage.ts @@ -1,5 +1,13 @@ import { getGlobalScope } from '@amplitude/experiment-core'; +import { WebExperimentConfig } from '../types'; + +import { + getDefaultUserProviderStorageKey, + getExperimentStorageKey, + getUnsentEventsStorageKey, +} from './keys'; + export type StorageType = 'localStorage' | 'sessionStorage'; /** @@ -77,3 +85,17 @@ const getStorage = (storageType: StorageType): Storage | null => { } return globalScope[storageType]; }; + +export const deletePersistedData = ( + apiKey: string, + config: WebExperimentConfig, +): void => { + const experimentStorageKey = getExperimentStorageKey(apiKey); + const defaultUserProviderStorageKey = + getDefaultUserProviderStorageKey(apiKey); + const unsentEventsStorageKey = getUnsentEventsStorageKey(config); + removeStorageItem('localStorage', experimentStorageKey); + removeStorageItem('localStorage', defaultUserProviderStorageKey); + removeStorageItem('sessionStorage', defaultUserProviderStorageKey); + removeStorageItem('localStorage', unsentEventsStorageKey); +}; diff --git a/packages/experiment-tag/src/util/messenger.ts b/packages/experiment-tag/src/util/messenger.ts index 8e754a51..ad965659 100644 --- a/packages/experiment-tag/src/util/messenger.ts +++ b/packages/experiment-tag/src/util/messenger.ts @@ -1,5 +1,6 @@ import { getGlobalScope } from '@amplitude/experiment-core'; +import { getVisualEditorSessionKey } from '../storage/keys'; import { getAndParseStorageItem } from '../storage/storage'; interface VisualEditorSession { @@ -7,8 +8,6 @@ interface VisualEditorSession { amplitudeWindowUrl: string; } -export const VISUAL_EDITOR_SESSION_KEY = 'visual-editor-state'; - export class WindowMessenger { static setup() { let state: 'closed' | 'opening' | 'open' = 'closed'; @@ -75,9 +74,8 @@ export class WindowMessenger { private static getStoredSession(): VisualEditorSession | null { const sessionData = getAndParseStorageItem( 'sessionStorage', - VISUAL_EDITOR_SESSION_KEY, - ) as VisualEditorSession; - + getVisualEditorSessionKey(), + ); if (!sessionData) { return null; } diff --git a/packages/experiment-tag/src/util/url.ts b/packages/experiment-tag/src/util/url.ts index 5d5bd66b..1c1b3656 100644 --- a/packages/experiment-tag/src/util/url.ts +++ b/packages/experiment-tag/src/util/url.ts @@ -1,6 +1,7 @@ import { getGlobalScope } from '@amplitude/experiment-core'; -import { PREVIEW_MODE_PARAM, PREVIEW_MODE_SESSION_KEY } from '../experiment'; +import { PREVIEW_MODE_PARAM } from '../experiment'; +import { getPreviewModeSessionKey } from '../storage/keys'; import { getAndParseStorageItem } from '../storage/storage'; import { PreviewState } from '../types'; @@ -90,7 +91,7 @@ export const isPreviewMode = (): boolean => { } const previewState = getAndParseStorageItem( 'sessionStorage', - PREVIEW_MODE_SESSION_KEY, + getPreviewModeSessionKey(), ); if (!previewState) { return false;