From 4e6e5ffe9e36cbe623165b2cc5f07c6425134625 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Wed, 19 Aug 2026 16:41:54 -0400 Subject: [PATCH] chore: remove legacy page-view migration code --- src/Rokt-Kit.ts | 6 +- src/pageViewStorage.ts | 39 +------ test/src/pageViewStorage.spec.ts | 89 ++------------- test/src/tests.spec.ts | 190 +------------------------------ 4 files changed, 13 insertions(+), 311 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 0b88a36..61fcf8e 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -28,7 +28,6 @@ import { PageEvent, PAGE_VIEWS_MAX_COUNT, buildPageEvents, - migrateLegacyPageViewStorage, loadPageViews, writePageViews, clearPageViews, @@ -900,7 +899,7 @@ class RoktKit implements KitInterface { try { pageUrl = sanitizeUrl(window.location.href); - const pageViews = loadPageViews(this.loggingService); + const pageViews = loadPageViews(); const pageView = buildPageEvent(event); pageViews.push(pageView); @@ -1299,7 +1298,6 @@ class RoktKit implements KitInterface { } if (event.EventDataType === MESSAGE_TYPE_SESSION_END) { - migrateLegacyPageViewStorage(this.loggingService); clearPageViews(); clearUtmParams(); } @@ -1534,7 +1532,7 @@ class RoktKit implements KitInterface { const filteredUserIdentities = this.returnUserIdentities(filteredUser); const sessionAttributes = this.returnLocalSessionAttributes(); - const pageEvents = buildPageEvents(loadPageViews(this.loggingService)); + const pageEvents = buildPageEvents(loadPageViews()); const utmParams = loadUtmParams(); const mpSessionId = this.readMpSessionId(); const mpDeviceId = this.readMpDeviceId(); diff --git a/src/pageViewStorage.ts b/src/pageViewStorage.ts index 7295bdc..30e57bf 100644 --- a/src/pageViewStorage.ts +++ b/src/pageViewStorage.ts @@ -1,18 +1,10 @@ import type { LoggingService } from './Rokt-Kit'; -import { - readJSON, - removeKey, - readNamespacedField, - writeNamespacedField, - removeNamespacedField, - isLocalStorageAvailable, -} from './storage'; +import { readNamespacedField, writeNamespacedField, removeNamespacedField, isLocalStorageAvailable } from './storage'; import { sanitizeUrl, isObject } from './utils'; const LS_NAMESPACE_KEY = 'mp-rokt-kit'; const LS_PAGE_VIEWS_FIELD = 'pageViews'; const LS_UTM_PARAMS_FIELD = 'utmParams'; -const LEGACY_PAGE_VIEWS_KEY = 'mpPageViews'; export const PAGE_VIEWS_MAX_COUNT = 25; const UTM_KEYS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] as const; @@ -33,34 +25,7 @@ function capPageViews(views: PageEvent[]): PageEvent[] { return views.slice(-PAGE_VIEWS_MAX_COUNT); } -export function migrateLegacyPageViewStorage(loggingService: LoggingService | null): void { - const legacyViews = readJSON(LEGACY_PAGE_VIEWS_KEY); - if (legacyViews === null) { - return; - } - - const alreadyMigrated = readNamespacedField(LS_NAMESPACE_KEY, LS_PAGE_VIEWS_FIELD) !== undefined; - const needsMigration = !alreadyMigrated && Array.isArray(legacyViews); - - if (needsMigration) { - loggingService?.log({ - message: 'Rokt Kit: Migrating legacy page-view storage', - code: 'PAGE_VIEW_LEGACY_MIGRATION', - }); - const migrated = writeNamespacedField(LS_NAMESPACE_KEY, LS_PAGE_VIEWS_FIELD, legacyViews); - if (!migrated) { - loggingService?.log({ - message: 'Rokt Kit: Failed to migrate legacy page-view storage [reason: migration_retry]', - code: 'PAGE_VIEW_CAPTURE_FAILED', - }); - } - } - - removeKey(LEGACY_PAGE_VIEWS_KEY); -} - -export function loadPageViews(loggingService: LoggingService | null): PageEvent[] { - migrateLegacyPageViewStorage(loggingService); +export function loadPageViews(): PageEvent[] { const stored = readNamespacedField(LS_NAMESPACE_KEY, LS_PAGE_VIEWS_FIELD); return Array.isArray(stored) ? (stored as PageEvent[]) : []; } diff --git a/test/src/pageViewStorage.spec.ts b/test/src/pageViewStorage.spec.ts index e297f5d..7dd0a10 100644 --- a/test/src/pageViewStorage.spec.ts +++ b/test/src/pageViewStorage.spec.ts @@ -1,7 +1,6 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { readJSON, writeNamespacedField } from '../../src/storage'; import { - migrateLegacyPageViewStorage, loadPageViews, writePageViews, clearPageViews, @@ -10,12 +9,10 @@ import { loadUtmParams, clearUtmParams, } from '../../src/pageViewStorage'; -import type { LoggingService } from '../../src/Rokt-Kit'; const NAMESPACE_KEY = 'mp-rokt-kit'; const PAGE_VIEWS_FIELD = 'pageViews'; const UTM_PARAMS_FIELD = 'utmParams'; -const LEGACY_PAGE_VIEWS_KEY = 'mpPageViews'; function stubSearch(search: string): void { vi.stubGlobal('location', { ...window.location, search }); @@ -33,102 +30,32 @@ describe('pageViewStorage', () => { window.localStorage.clear(); }); - describe('migrateLegacyPageViewStorage', () => { - it('is a no-op when the legacy key is absent', () => { - migrateLegacyPageViewStorage(null); - expect(readJSON(NAMESPACE_KEY)).toBeNull(); - }); - - it('moves a legacy array into the namespaced field and removes the legacy key', () => { - window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); - migrateLegacyPageViewStorage(null); - - expect(readJSON(NAMESPACE_KEY)).toEqual({ [PAGE_VIEWS_FIELD]: [pageView('home')] }); - expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).toBeNull(); - }); - - it('does not overwrite an already-migrated field, but still clears the legacy key', () => { - writeNamespacedField(NAMESPACE_KEY, PAGE_VIEWS_FIELD, [pageView('current')]); - window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('stale')])); - - migrateLegacyPageViewStorage(null); - - expect(readJSON(NAMESPACE_KEY)).toEqual({ [PAGE_VIEWS_FIELD]: [pageView('current')] }); - expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).toBeNull(); - }); - - it('logs PAGE_VIEW_LEGACY_MIGRATION when the migration path is taken', () => { - window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); - const logger = { log: vi.fn() } as unknown as LoggingService; - - migrateLegacyPageViewStorage(logger); - - expect(logger.log).toHaveBeenCalledWith(expect.objectContaining({ code: 'PAGE_VIEW_LEGACY_MIGRATION' })); - }); - - it('removes the legacy key and logs when the migrating write fails', () => { - window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); - const logger = { log: vi.fn() } as unknown as LoggingService; - vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { - throw new DOMException('quota', 'QuotaExceededError'); - }); - - migrateLegacyPageViewStorage(logger); - - // Legacy key is removed even on failure — prevents the infinite retry loop. - expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).toBeNull(); - expect(logger.log).toHaveBeenCalledWith(expect.objectContaining({ code: 'PAGE_VIEW_CAPTURE_FAILED' })); - }); - - it('does not log PAGE_VIEW_CAPTURE_FAILED on a second call after a failed migration', () => { - window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); - const logger = { log: vi.fn() } as unknown as LoggingService; - vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { - throw new DOMException('quota', 'QuotaExceededError'); - }); - - migrateLegacyPageViewStorage(logger); // first call: fails, legacy key removed - vi.restoreAllMocks(); - logger.log.mockClear(); - - migrateLegacyPageViewStorage(logger); // second call: legacy key gone, no-op - - expect(logger.log).not.toHaveBeenCalled(); - }); - }); - describe('loadPageViews', () => { it('returns an empty array when nothing is stored', () => { - expect(loadPageViews(null)).toEqual([]); + expect(loadPageViews()).toEqual([]); }); it('returns the stored page views', () => { writeNamespacedField(NAMESPACE_KEY, PAGE_VIEWS_FIELD, [pageView('home'), pageView('about')]); - expect(loadPageViews(null)).toEqual([pageView('home'), pageView('about')]); + expect(loadPageViews()).toEqual([pageView('home'), pageView('about')]); }); it('returns an empty array when the stored value is not an array', () => { writeNamespacedField(NAMESPACE_KEY, PAGE_VIEWS_FIELD, { not: 'an array' }); - expect(loadPageViews(null)).toEqual([]); - }); - - it('migrates the legacy key before reading', () => { - window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('legacy')])); - expect(loadPageViews(null)).toEqual([pageView('legacy')]); - expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).toBeNull(); + expect(loadPageViews()).toEqual([]); }); }); describe('writePageViews', () => { it('persists the page views and returns the stored count', () => { expect(writePageViews([pageView('home')])).toBe(1); - expect(loadPageViews(null)).toEqual([pageView('home')]); + expect(loadPageViews()).toEqual([pageView('home')]); }); it('keeps only the 25 most-recent views when given more than 25', () => { const views = Array.from({ length: 40 }, (_, i) => pageView('page-' + i)); expect(writePageViews(views)).toBe(25); - const stored = loadPageViews(null); + const stored = loadPageViews(); expect(stored).toHaveLength(25); expect(stored[0].sourceMessageId).toBe('page-15'); expect(stored[24].sourceMessageId).toBe('page-39'); @@ -144,7 +71,7 @@ describe('pageViewStorage', () => { }); expect(writePageViews(views)).toBe(4); - const stored = loadPageViews(null); + const stored = loadPageViews(); expect(stored).toHaveLength(4); expect(stored[0].sourceMessageId).toBe('page-1'); expect(stored[3].sourceMessageId).toBe('page-4'); @@ -160,7 +87,7 @@ describe('pageViewStorage', () => { }); expect(writePageViews(views)).toBe(2); - const stored = loadPageViews(null); + const stored = loadPageViews(); expect(stored).toHaveLength(2); expect(stored[0].sourceMessageId).toBe('page-3'); expect(stored[1].sourceMessageId).toBe('page-4'); @@ -179,7 +106,7 @@ describe('pageViewStorage', () => { }); expect(writePageViews(updated)).toBe(5); - const stored = loadPageViews(null); + const stored = loadPageViews(); expect(stored).toHaveLength(5); expect(stored[0].sourceMessageId).toBe('existing-1'); expect(stored[4].sourceMessageId).toBe('new'); diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index f8e013f..aeaae16 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5,7 +5,7 @@ import { isSelectPlacementsAttributePersistenceDenied, removeSelectPlacementsAttributePersistenceDeniedAttributes, } from '../../src/selectPlacementsAttributePersistence'; -import { readJSON, readNamespacedField, writeNamespacedField } from '../../src/storage'; +import { readNamespacedField, writeNamespacedField } from '../../src/storage'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -6159,11 +6159,9 @@ describe('Rokt Forwarder', () => { describe('page view capture', () => { const NS_KEY = 'mp-rokt-kit'; const PAGE_VIEWS_FIELD = 'pageViews'; - const LEGACY_KEY = 'mpPageViews'; const readStoredPageViews = () => readNamespacedField(NS_KEY, PAGE_VIEWS_FIELD) ?? null; const seedStoredPageViews = (views: unknown) => writeNamespacedField(NS_KEY, PAGE_VIEWS_FIELD, views); - const seedLegacyPageViews = (views: unknown) => window.localStorage.setItem(LEGACY_KEY, JSON.stringify(views)); beforeEach(() => { window.localStorage.clear(); @@ -6493,150 +6491,6 @@ describe('Rokt Forwarder', () => { expect(readStoredPageViews()).toBeNull(); }); - describe('legacy storage migration', () => { - const initKit = async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - }; - - const runSelectPlacements = async () => { - (window as any).mParticle._Store.localSessionAttributes = {}; - await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); - return (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - }; - - it('adopts legacy history into the new key and sweeps the legacy key on read', async () => { - const seeded = [ - { - pageUrl: 'https://example.com/legacy', - sourceMessageId: 'legacy-1', - timestamp: 1712345678000, - }, - ]; - seedLegacyPageViews(seeded); - - await initKit(); - const attributes = await runSelectPlacements(); - - // Legacy history surfaces on read (adopted into the namespaced field). - expect(JSON.parse(attributes.page_events)).toEqual(seeded); - expect(readStoredPageViews()).toEqual(seeded); - // Legacy key is always swept. - expect(readJSON(LEGACY_KEY)).toBeNull(); - }); - - it('keeps the new key and sweeps the legacy key when both exist', async () => { - const legacy = [ - { - pageUrl: 'https://example.com/legacy', - sourceMessageId: 'legacy-1', - timestamp: 1712345678000, - }, - ]; - const current = [ - { - pageUrl: 'https://example.com/current', - sourceMessageId: 'current-1', - timestamp: 1712345679000, - }, - ]; - seedLegacyPageViews(legacy); - seedStoredPageViews(current); - - await initKit(); - const attributes = await runSelectPlacements(); - - // Namespaced field wins — legacy value is discarded, not merged. - expect(JSON.parse(attributes.page_events)).toEqual(current); - expect(readStoredPageViews()).toEqual(current); - expect(readJSON(LEGACY_KEY)).toBeNull(); - }); - - it('leaves the new key untouched when there is no legacy key', async () => { - const current = [ - { - pageUrl: 'https://example.com/current', - sourceMessageId: 'current-1', - timestamp: 1712345679000, - }, - ]; - seedStoredPageViews(current); - - await initKit(); - const attributes = await runSelectPlacements(); - - expect(JSON.parse(attributes.page_events)).toEqual(current); - expect(readStoredPageViews()).toEqual(current); - expect(readJSON(LEGACY_KEY)).toBeNull(); - }); - - it('sweeps the legacy key on SessionEnd before clearing the new key', async () => { - seedLegacyPageViews([ - { - pageUrl: 'https://example.com/legacy', - sourceMessageId: 'legacy-1', - timestamp: 1712345678000, - }, - ]); - - await initKit(); - - (window as any).mParticle.forwarder.process({ - EventName: 'Session End', - EventCategory: EventType.Unknown, - EventDataType: MessageType.SessionEnd, - SourceMessageId: 'source-message-id-session-end', - Timestamp: 1712345679000, - }); - - expect(readJSON(LEGACY_KEY)).toBeNull(); - expect(readStoredPageViews()).toBeNull(); - }); - - it('does not throw out of selectPlacements when the migration hits a storage error', async () => { - // Legacy present + namespaced field absent → migration attempts the adopt - // write, which throws here. The read path must swallow it (best-effort) so - // placement selection still proceeds without page events. - seedLegacyPageViews([ - { - pageUrl: 'https://example.com/legacy', - sourceMessageId: 'legacy-1', - timestamp: 1712345678000, - }, - ]); - - await initKit(); - - // A read/migration failure is surfaced as a diagnostic INFO log - // (loggingService.log), not an error report. - const logSpy = vi.spyOn((window as any).mParticle.forwarder.loggingService, 'log'); - const setItemSpy = vi.spyOn(Storage.prototype, 'setItem').mockImplementation((key: string) => { - if (key === NS_KEY) { - throw new Error('QuotaExceededError'); - } - }); - - try { - const attributes = await runSelectPlacements(); - // Selection proceeds; page events are simply omitted. - expect(attributes.page_events).toBeUndefined(); - } finally { - setItemSpy.mockRestore(); - } - - expect(logSpy).toHaveBeenCalledWith(expect.objectContaining({ code: 'PAGE_VIEW_CAPTURE_FAILED' })); - logSpy.mockRestore(); - }); - }); - it('captures the page view but returns the not-ready signal when the kit is not ready', () => { // Force a not-ready state: capture must still run (kit-owned storage), // but process() must tell the core SDK the forwarder is not ready. @@ -7137,48 +6991,6 @@ describe('Rokt Forwarder', () => { expect(forwardedAttributes.page_events).toBeUndefined(); }); - it('does not sweep the legacy key on init when targeting is disabled', async () => { - // The targeting-disabled clear path (initForwarder) intentionally only - // clears the kit-owned new key; it does not run the legacy migration. - // A user with targeting off keeps an orphaned legacy `mpPageViews` until - // the shim's removal date — benign, and swept the moment targeting is - // re-enabled (loadPageViews) or a SessionEnd fires. - seedLegacyPageViews([ - { - pageUrl: 'https://example.com/legacy', - sourceMessageId: 'legacy-seeded', - timestamp: 1712345678000, - }, - ]); - seedStoredPageViews([ - { - pageUrl: 'https://example.com/', - sourceMessageId: 'seeded', - timestamp: 1712345678000, - }, - ]); - - (window as any).mParticle.Rokt.launcherOptions = { - noTargeting: true, - }; - - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - // New key is cleared; legacy key is left untouched (not swept on this path). - expect(readStoredPageViews()).toBeNull(); - expect(readJSON(LEGACY_KEY)).not.toBeNull(); - }); - it('strips query params from the captured pageUrl', async () => { const originalLocation = window.location; // Query params commonly carry PII (emails, tokens); they must not be captured.