From 1e71aa5c082cdb30181780e84128de31f3b2ccbb Mon Sep 17 00:00:00 2001 From: Pooya Jaferian Date: Wed, 7 Dec 2022 11:33:30 -0800 Subject: [PATCH] remove caching logic for availability, refactored the universal storage --- packages/browser/src/core/analytics/index.ts | 9 +- .../src/core/user/__tests__/index.test.ts | 59 ++++++++----- packages/browser/src/core/user/index.ts | 84 +++++++++---------- .../src/plugins/segmentio/normalize.ts | 9 +- 4 files changed, 86 insertions(+), 75 deletions(-) diff --git a/packages/browser/src/core/analytics/index.ts b/packages/browser/src/core/analytics/index.ts index 365242768..273ce0189 100644 --- a/packages/browser/src/core/analytics/index.ts +++ b/packages/browser/src/core/analytics/index.ts @@ -26,6 +26,7 @@ import { Plugin } from '../plugin' import { EventQueue } from '../queue/event-queue' import { CookieOptions, + getAvailableStorageOptions, Group, ID, UniversalStorage, @@ -133,9 +134,11 @@ export class Analytics this.queue = queue ?? createDefaultQueue(options?.retryQueue, disablePersistance) - this._universalStorage = UniversalStorage.getUniversalStorage( - disablePersistance ? ['memory'] : ['cookie', 'localStorage', 'memory'], - cookieOptions + this._universalStorage = new UniversalStorage( + disablePersistance !== false + ? ['localStorage', 'cookie', 'memory'] + : ['memory'], + getAvailableStorageOptions(cookieOptions) ) this._user = diff --git a/packages/browser/src/core/user/__tests__/index.test.ts b/packages/browser/src/core/user/__tests__/index.test.ts index dc0f90747..bd4383d68 100644 --- a/packages/browser/src/core/user/__tests__/index.test.ts +++ b/packages/browser/src/core/user/__tests__/index.test.ts @@ -5,6 +5,7 @@ import { Group, UniversalStorage, StoreType, + getAvailableStorageOptions, } from '..' import jar from 'js-cookie' import assert from 'assert' @@ -956,49 +957,54 @@ describe('universal storage', function () { it('picks data from cookies first', function () { jar.set('ajs_test_key', '🍪') localStorage.setItem('ajs_test_key', '💾') - const us = UniversalStorage.getUniversalStorage(defaultTargets) + const us = new UniversalStorage( + defaultTargets, + getAvailableStorageOptions() + ) expect(us.get('ajs_test_key')).toEqual('🍪') }) it('picks data from localStorage if there is no cookie target', function () { jar.set('ajs_test_key', '🍪') localStorage.setItem('ajs_test_key', '💾') - const us = UniversalStorage.getUniversalStorage([ - 'localStorage', - 'memory', - ]) + const us = new UniversalStorage( + ['localStorage', 'memory'], + getAvailableStorageOptions() + ) expect(us.get('ajs_test_key')).toEqual('💾') }) it('get data from memory', function () { jar.set('ajs_test_key', '🍪') localStorage.setItem('ajs_test_key', '💾') - const us = UniversalStorage.getUniversalStorage(['memory']) + const us = new UniversalStorage(['memory'], getAvailableStorageOptions()) expect(us.get('ajs_test_key')).toBeNull() }) - it('order of default targets does not matter', function () { + it('order of default targets matters!', function () { jar.set('ajs_test_key', '🍪') localStorage.setItem('ajs_test_key', '💾') - const us = UniversalStorage.getUniversalStorage([ - 'localStorage', - 'cookie', - 'memory', - ]) + const us = new UniversalStorage( + ['cookie', 'localStorage', 'memory'], + getAvailableStorageOptions() + ) expect(us.get('ajs_test_key')).toEqual('🍪') }) it('returns null if there are no storage targets', function () { jar.set('ajs_test_key', '🍪') localStorage.setItem('ajs_test_key', '💾') - const us = UniversalStorage.getUniversalStorage([]) + const us = new UniversalStorage([], getAvailableStorageOptions()) expect(us.get('ajs_test_key')).toBeNull() }) it('can override the default targets', function () { jar.set('ajs_test_key', '🍪') localStorage.setItem('ajs_test_key', '💾') - const us = UniversalStorage.getUniversalStorage(defaultTargets) + const us = new UniversalStorage( + defaultTargets, + getAvailableStorageOptions() + ) expect(us.get('ajs_test_key', ['localStorage'])).toEqual('💾') expect(us.get('ajs_test_key', ['localStorage', 'memory'])).toEqual('💾') expect(us.get('ajs_test_key', ['cookie', 'memory'])).toEqual('🍪') @@ -1010,8 +1016,9 @@ describe('universal storage', function () { describe('#set', function () { it('set the data in all storage types', function () { - const us = UniversalStorage.getUniversalStorage<{ ajs_test_key: string }>( - defaultTargets + const us = new UniversalStorage<{ ajs_test_key: string }>( + defaultTargets, + getAvailableStorageOptions() ) us.set('ajs_test_key', '💰') expect(jar.get('ajs_test_key')).toEqual('💰') @@ -1019,24 +1026,27 @@ describe('universal storage', function () { }) it('skip saving data to localStorage', function () { - const us = UniversalStorage.getUniversalStorage(['cookie', 'memory']) + const us = new UniversalStorage( + ['cookie', 'memory'], + getAvailableStorageOptions() + ) us.set('ajs_test_key', '💰') expect(jar.get('ajs_test_key')).toEqual('💰') expect(localStorage.getItem('ajs_test_key')).toEqual(null) }) it('skip saving data to cookie', function () { - const us = UniversalStorage.getUniversalStorage([ - 'localStorage', - 'memory', - ]) + const us = new UniversalStorage( + ['localStorage', 'memory'], + getAvailableStorageOptions() + ) us.set('ajs_test_key', '💰') expect(jar.get('ajs_test_key')).toEqual(undefined) expect(getFromLS('ajs_test_key')).toEqual('💰') }) it('can save and retrieve from memory when there is no other storage', function () { - const us = UniversalStorage.getUniversalStorage(['memory']) + const us = new UniversalStorage(['memory'], getAvailableStorageOptions()) us.set('ajs_test_key', '💰') expect(jar.get('ajs_test_key')).toEqual(undefined) expect(localStorage.getItem('ajs_test_key')).toEqual(null) @@ -1044,7 +1054,10 @@ describe('universal storage', function () { }) it('can override the default targets', function () { - const us = UniversalStorage.getUniversalStorage(defaultTargets) + const us = new UniversalStorage( + defaultTargets, + getAvailableStorageOptions() + ) us.set('ajs_test_key', '💰', ['localStorage']) expect(jar.get('ajs_test_key')).toEqual(undefined) expect(getFromLS('ajs_test_key')).toEqual('💰') diff --git a/packages/browser/src/core/user/index.ts b/packages/browser/src/core/user/index.ts index 936ee3062..799f2a96a 100644 --- a/packages/browser/src/core/user/index.ts +++ b/packages/browser/src/core/user/index.ts @@ -62,12 +62,7 @@ class Store { const ONE_YEAR = 365 export class Cookie extends Store { - static _available: boolean | undefined static available(): boolean { - if (Cookie._available !== undefined) { - return Cookie._available - } - let cookieEnabled = window.navigator.cookieEnabled if (!cookieEnabled) { @@ -76,8 +71,6 @@ export class Cookie extends Store { jar.remove('ajs:cookies') } - Cookie._available = cookieEnabled - return cookieEnabled } @@ -153,21 +146,13 @@ const localStorageWarning = (key: string, state: 'full' | 'unavailable') => { } export class LocalStorage extends Store { - static _available: boolean | undefined - static available(): boolean { - if (LocalStorage._available !== undefined) { - return LocalStorage._available - } - const test = 'test' try { localStorage.setItem(test, test) localStorage.removeItem(test) - LocalStorage._available = true return true } catch (e) { - LocalStorage._available = false return false } } @@ -221,16 +206,26 @@ export interface CookieOptions { } export class UniversalStorage { - private stores: Store[] + private enabledStores: StoreType[] + private storageOptions: StorageOptions - constructor(stores?: Store[]) { - this.stores = stores || [] + constructor(stores: StoreType[], storageOptions: StorageOptions) { + this.storageOptions = storageOptions + this.enabledStores = stores } private getStores(storeTypes: StoreType[] | undefined): Store[] { - return storeTypes - ? this.stores.filter((s) => storeTypes.indexOf(s.type) !== -1) - : this.stores + const stores: Store[] = [] + this.enabledStores + .filter((i) => !storeTypes || storeTypes?.includes(i)) + .forEach((storeType) => { + const storage = this.storageOptions[storeType] + if (storage !== undefined) { + stores.push(storage) + } + }) + + return stores } /* @@ -283,26 +278,21 @@ export class UniversalStorage { store.remove(key) } } +} - static getUniversalStorage>( - defaultTargets: StoreType[] = ['cookie', 'localStorage', 'memory'], - cookieOptions?: CookieOptions - ): UniversalStorage { - const stores = [] - - if (defaultTargets.includes('cookie') && Cookie.available()) { - stores.push(new Cookie(cookieOptions)) - } - - if (defaultTargets.includes('localStorage') && LocalStorage.available()) { - stores.push(new LocalStorage()) - } - - if (defaultTargets.includes('memory')) { - stores.push(new Store()) - } +type StorageOptions = { + cookie: Cookie | undefined + localStorage: LocalStorage | undefined + memory: Store +} - return new UniversalStorage(stores) +export function getAvailableStorageOptions( + cookieOptions?: CookieOptions +): StorageOptions { + return { + cookie: Cookie.available() ? new Cookie(cookieOptions) : undefined, + localStorage: LocalStorage.available() ? new LocalStorage() : undefined, + memory: new Store(), } } @@ -346,32 +336,34 @@ export class User { let defaultStorageTargets: StoreType[] = isDisabled ? [] : shouldPersist - ? ['cookie', 'localStorage', 'memory'] + ? ['localStorage', 'cookie', 'memory'] : ['memory'] + const storageOptions = getAvailableStorageOptions(cookieOptions) + if (options.localStorageFallbackDisabled) { defaultStorageTargets = defaultStorageTargets.filter( (t) => t !== 'localStorage' ) } - this.identityStore = UniversalStorage.getUniversalStorage( + this.identityStore = new UniversalStorage( defaultStorageTargets, - cookieOptions + storageOptions ) // using only cookies for legacy user store - this.legacyUserStore = UniversalStorage.getUniversalStorage( + this.legacyUserStore = new UniversalStorage( defaultStorageTargets.filter( (t) => t !== 'localStorage' && t !== 'memory' ), - cookieOptions + storageOptions ) // using only localStorage / memory for traits store - this.traitsStore = UniversalStorage.getUniversalStorage( + this.traitsStore = new UniversalStorage( defaultStorageTargets.filter((t) => t !== 'cookie'), - cookieOptions + storageOptions ) const legacyUser = this.legacyUserStore.get(defaults.cookie.oldKey) diff --git a/packages/browser/src/plugins/segmentio/normalize.ts b/packages/browser/src/plugins/segmentio/normalize.ts index cfc7e2702..b913c723b 100644 --- a/packages/browser/src/plugins/segmentio/normalize.ts +++ b/packages/browser/src/plugins/segmentio/normalize.ts @@ -7,7 +7,7 @@ import { tld } from '../../core/user/tld' import { SegmentFacade } from '../../lib/to-facade' import { SegmentioSettings } from './index' import { version } from '../../generated/version' -import { UniversalStorage } from '../../core/user' +import { getAvailableStorageOptions, UniversalStorage } from '../../core/user' let cookieOptions: jar.CookieAttributes | undefined function getCookieOptions(): jar.CookieAttributes { @@ -95,9 +95,12 @@ function referrerId( ctx: SegmentEvent['context'], disablePersistance: boolean ): void { - const storage = UniversalStorage.getUniversalStorage<{ + const storage = new UniversalStorage<{ 's:context.referrer': Ad - }>(disablePersistance ? [] : ['cookie'], getCookieOptions()) + }>( + disablePersistance ? [] : ['cookie'], + getAvailableStorageOptions(getCookieOptions()) + ) const stored = storage.get('s:context.referrer') let ad: Ad | undefined | null = ads(query)