From e6189e54aa29331ef9c1fde53712ca31187a4a30 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 14:22:45 -0400 Subject: [PATCH 1/9] feat: add wrapper and interface for sending batches --- src/Rokt-Kit.ts | 55 +++++++++++ test/src/tests.spec.ts | 215 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 5b0786d..d41a656 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -67,11 +67,36 @@ interface RoktLauncher { use(extensionName: string): Promise; } +interface MPBatchEvent { + event_type: string; + data?: Record; +} + +interface MPBatch { + source_request_id?: string | null; + events?: MPBatchEvent[] | null; + device_info?: Record; + application_info?: Record; + user_attributes?: Record | null; + deleted_user_attributes?: string[] | null; + user_identities?: Record; + environment?: 'development' | 'production'; + mp_deviceid?: string | null; + timestamp_unixtime_ms?: number | null; + batch_id?: number | null; + mpid?: string | number | null; + sdk_version?: string | null; + consent_state?: Record; + integration_attributes?: Record> | null; + [key: string]: unknown; +} + interface RoktGlobal { createLauncher(options: Record): Promise; createLocalLauncher(options: Record): RoktLauncher; currentLauncher?: RoktLauncher; __event_stream__?(event: Record): void; + __batch_stream__?(batch: MPBatch): void; setExtensionData(data: Record): void; } @@ -606,6 +631,8 @@ class RoktKit { public placementEventAttributeMappingLookup: Record = {}; public eventQueue: MParticleEvent[] = []; public eventStreamQueue: MParticleEvent[] = []; + public batchQueue: MPBatch[] = []; + public batchStreamQueue: MPBatch[] = []; public integrationName: string | null = null; public domain?: string; public errorReportingService: ErrorReportingService | null = null; @@ -765,6 +792,11 @@ class RoktKit { this.process(event); }); this.eventQueue = []; + + this.batchQueue.forEach((batch) => { + this.processBatch(batch); + }); + this.batchQueue = []; } private enrichEvent(event: MParticleEvent): Record { @@ -786,6 +818,29 @@ class RoktKit { } } + public processBatch(batch: MPBatch): void { + if (!this.isKitReady()) { + this.batchQueue.push(batch); + return; + } + this.sendBatchStream(batch); + } + + private sendBatchStream(batch: MPBatch): void { + if (window.Rokt && typeof window.Rokt.__batch_stream__ === 'function') { + if (this.batchStreamQueue.length) { + const queuedBatches = this.batchStreamQueue; + this.batchStreamQueue = []; + for (let i = 0; i < queuedBatches.length; i++) { + window.Rokt.__batch_stream__(queuedBatches[i]); + } + } + window.Rokt.__batch_stream__(batch); + } else { + this.batchStreamQueue.push(batch); + } + } + private setRoktSessionId(sessionId: string): void { if (!sessionId || typeof sessionId !== 'string') { return; diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 58569a5..c6c24c6 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5149,6 +5149,221 @@ describe('Rokt Forwarder', () => { }); }); + describe('#processBatch', () => { + let mockBatch: any; + + beforeEach(() => { + (window as any).mParticle.forwarder.batchQueue = []; + (window as any).mParticle.forwarder.batchStreamQueue = []; + (window as any).Rokt = new (MockRoktForwarder as any)(); + (window as any).Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function (options: any) { + (window as any).mParticle.Rokt.selectPlacementsOptions = options; + (window as any).mParticle.Rokt.selectPlacementsCalled = true; + }, + }); + }; + (window as any).mParticle.Rokt = (window as any).Rokt; + (window as any).mParticle.Rokt.attachKitCalled = false; + (window as any).mParticle.Rokt.attachKit = async (kit: any) => { + (window as any).mParticle.Rokt.attachKitCalled = true; + (window as any).mParticle.Rokt.kit = kit; + Promise.resolve(); + }; + (window as any).mParticle.Rokt.setLocalSessionAttribute = function (key: any, value: any) { + (window as any).mParticle._Store.localSessionAttributes[key] = value; + }; + (window as any).mParticle.Rokt.getLocalSessionAttributes = function () { + return (window as any).mParticle._Store.localSessionAttributes; + }; + (window as any).mParticle.forwarder.launcher = { + selectPlacements: function (options: any) { + (window as any).mParticle.Rokt.selectPlacementsOptions = options; + (window as any).mParticle.Rokt.selectPlacementsCalled = true; + }, + }; + (window as any).mParticle.Rokt.filters = { + userAttributesFilters: [], + filterUserAttributes: function (attributes: any) { + return attributes; + }, + filteredUser: { + getMPID: function () { + return '123'; + }, + }, + }; + + mockBatch = { + mpid: 'test-mpid-123', + user_attributes: { 'user-attr': 'user-value' }, + user_identities: { email: 'test@example.com' }, + events: [ + { + event_type: 'custom_event', + data: { event_name: 'Test Event', custom_event_type: 'other' }, + }, + ], + }; + }); + + afterEach(() => { + delete (window as any).Rokt.__batch_stream__; + (window as any).mParticle.forwarder.batchQueue = []; + (window as any).mParticle.forwarder.batchStreamQueue = []; + (window as any).mParticle.forwarder.eventQueue = []; + (window as any).mParticle.forwarder.eventStreamQueue = []; + (window as any).mParticle.forwarder.isInitialized = false; + (window as any).mParticle.Rokt.attachKitCalled = false; + }); + + it('should send batch to window.Rokt.__batch_stream__ when kit is ready', async () => { + const receivedBatches: any[] = []; + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.processBatch(mockBatch); + + expect(receivedBatches.length).toBe(1); + expect(receivedBatches[0].mpid).toBe('test-mpid-123'); + expect(receivedBatches[0].user_attributes).toEqual({ 'user-attr': 'user-value' }); + expect(receivedBatches[0].user_identities).toEqual({ email: 'test@example.com' }); + expect(receivedBatches[0].events.length).toBe(1); + }); + + it('should forward batch including non-event types (UserIdentityChange, UserAttributeChange)', async () => { + const receivedBatches: any[] = []; + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + const batchWithNonEvents = { + mpid: 'test-mpid-456', + user_attributes: { age: '30' }, + user_identities: { email: 'user@example.com' }, + events: [ + { event_type: 'custom_event', data: { event_name: 'Page Viewed', custom_event_type: 'navigation' } }, + { + event_type: 'user_identity_change', + data: { + new: { identity_type: 'email', identity: 'user@example.com', created_this_batch: true }, + old: { identity_type: 'email', identity: null, created_this_batch: false }, + }, + }, + { + event_type: 'user_attribute_change', + data: { user_attribute_name: 'age', new: '30', old: null, deleted: false, is_new_attribute: true }, + }, + ], + }; + + (window as any).mParticle.forwarder.processBatch(batchWithNonEvents); + + expect(receivedBatches.length).toBe(1); + expect(receivedBatches[0].events.length).toBe(3); + expect(receivedBatches[0].events[0].event_type).toBe('custom_event'); + expect(receivedBatches[0].events[1].event_type).toBe('user_identity_change'); + expect(receivedBatches[0].events[2].event_type).toBe('user_attribute_change'); + }); + + it('should queue batch in batchQueue when kit is not initialized', () => { + (window as any).mParticle.forwarder.isInitialized = false; + (window as any).mParticle.forwarder.launcher = null; + + expect(() => { + (window as any).mParticle.forwarder.processBatch(mockBatch); + }).not.toThrow(); + + expect((window as any).mParticle.forwarder.batchQueue.length).toBe(1); + expect((window as any).mParticle.forwarder.batchQueue[0]).toEqual(mockBatch); + }); + + it('should flush batchQueue when kit becomes ready', async () => { + const receivedBatches: any[] = []; + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + (window as any).mParticle.forwarder.isInitialized = false; + (window as any).mParticle.forwarder.launcher = null; + (window as any).mParticle.forwarder.processBatch(mockBatch); + + expect((window as any).mParticle.forwarder.batchQueue.length).toBe(1); + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + expect(receivedBatches.length).toBe(1); + expect(receivedBatches[0].mpid).toBe('test-mpid-123'); + expect((window as any).mParticle.forwarder.batchQueue.length).toBe(0); + }); + + it('should queue batch in batchStreamQueue when window.Rokt.__batch_stream__ is not defined', async () => { + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + expect(() => { + (window as any).mParticle.forwarder.processBatch(mockBatch); + }).not.toThrow(); + + expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(1); + expect((window as any).mParticle.forwarder.batchStreamQueue[0]).toEqual(mockBatch); + }); + + it('should flush batchStreamQueue before sending the next batch', async () => { + const receivedBatches: any[] = []; + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + const batchA = { mpid: 'mpid-A', events: [], user_attributes: {} }; + const batchB = { mpid: 'mpid-B', events: [], user_attributes: {} }; + const batchC = { mpid: 'mpid-C', events: [], user_attributes: {} }; + + (window as any).mParticle.forwarder.processBatch(batchA); + (window as any).mParticle.forwarder.processBatch(batchB); + + expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(2); + + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + (window as any).mParticle.forwarder.processBatch(batchC); + + expect(receivedBatches.length).toBe(3); + expect(receivedBatches[0].mpid).toBe('mpid-A'); + expect(receivedBatches[1].mpid).toBe('mpid-B'); + expect(receivedBatches[2].mpid).toBe('mpid-C'); + expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(0); + }); + + it('should not contaminate eventStreamQueue when batches are queued', async () => { + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.processBatch(mockBatch); + (window as any).mParticle.forwarder.processBatch(mockBatch); + + expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(2); + expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(0); + }); + }); + describe('#_setRoktSessionId', () => { let setIntegrationAttributeCalls: any[]; From f20eea527639d1e0e8c1b912445fe5e7da220ac6 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 14:33:42 -0400 Subject: [PATCH 2/9] feat: add additional tests --- test/src/tests.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index c6c24c6..925f91b 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5322,6 +5322,24 @@ describe('Rokt Forwarder', () => { expect((window as any).mParticle.forwarder.batchStreamQueue[0]).toEqual(mockBatch); }); + it('should queue batch in batchStreamQueue when window.Rokt is undefined', async () => { + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + const savedRokt = (window as any).Rokt; + delete (window as any).Rokt; + + expect(() => { + (window as any).mParticle.forwarder.processBatch(mockBatch); + }).not.toThrow(); + + expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(1); + expect((window as any).mParticle.forwarder.batchStreamQueue[0]).toEqual(mockBatch); + + (window as any).Rokt = savedRokt; + }); + it('should flush batchStreamQueue before sending the next batch', async () => { const receivedBatches: any[] = []; From dca463b32e7791879b0d5006f0dfa3f18a3770b4 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 16:28:33 -0400 Subject: [PATCH 3/9] feat: update to use types --- package-lock.json | 18 ++- package.json | 2 +- src/Rokt-Kit.ts | 388 +++++++++++++++++++++++++--------------------- 3 files changed, 223 insertions(+), 185 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7e07008..567c678 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.23.0", "license": "Apache-2.0", "dependencies": { - "@mparticle/web-sdk": "^2.56.0" + "@mparticle/web-sdk": "^2.62.0" }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", @@ -1266,13 +1266,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@mparticle/event-models": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mparticle/event-models/-/event-models-1.1.9.tgz", + "integrity": "sha512-2ucTwTKKA4xZjcSMlkim3rvr6d8uyi9yWDqOW8b9RDah2ak+dI0PkWANHkMJqteQprEg9mOkSvxc17kv5adIIA==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/@mparticle/web-sdk": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/@mparticle/web-sdk/-/web-sdk-2.56.0.tgz", - "integrity": "sha512-HytxaOUYEIHei5Y6OPQoQl7mmM6H2EBI1CDJ5VVB3otsvO/CQqSzyd6T1VC4hBcJ8qVJ9h7p6hIxIteVsYyXeg==", + "version": "2.62.0", + "resolved": "https://registry.npmjs.org/@mparticle/web-sdk/-/web-sdk-2.62.0.tgz", + "integrity": "sha512-JJV61ValoRNnS4hq3hMe1U9SVkI6EQC+b8Jg/11mq/yMO7xoqvfC274BjNd8I2sYtr5LgD3zGnHOz8gcBn7+Xw==", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.23.2" + }, + "peerDependencies": { + "@mparticle/event-models": "^1.1.9" } }, "node_modules/@octokit/auth-token": { diff --git a/package.json b/package.json index e576866..8ab4ee5 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "vitest": "^4.0.0" }, "dependencies": { - "@mparticle/web-sdk": "^2.56.0" + "@mparticle/web-sdk": "^2.62.0" }, "license": "Apache-2.0" } diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index d41a656..9f91fd0 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -16,6 +16,8 @@ // Types // ============================================================ +import { Batch, KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal'; + interface RoktKitSettings { accountId: string; roktExtensions?: string; @@ -67,42 +69,18 @@ interface RoktLauncher { use(extensionName: string): Promise; } -interface MPBatchEvent { - event_type: string; - data?: Record; -} - -interface MPBatch { - source_request_id?: string | null; - events?: MPBatchEvent[] | null; - device_info?: Record; - application_info?: Record; - user_attributes?: Record | null; - deleted_user_attributes?: string[] | null; - user_identities?: Record; - environment?: 'development' | 'production'; - mp_deviceid?: string | null; - timestamp_unixtime_ms?: number | null; - batch_id?: number | null; - mpid?: string | number | null; - sdk_version?: string | null; - consent_state?: Record; - integration_attributes?: Record> | null; - [key: string]: unknown; -} interface RoktGlobal { createLauncher(options: Record): Promise; createLocalLauncher(options: Record): RoktLauncher; currentLauncher?: RoktLauncher; __event_stream__?(event: Record): void; - __batch_stream__?(batch: MPBatch): void; + __batch_stream__?(batch: Batch): void; setExtensionData(data: Record): void; } -interface FilteredUser { - getMPID(): string; - getAllUserAttributes(): Record; +interface FilteredUser extends IMParticleUser { + getMPID?(): string; getUserIdentities?: () => { userIdentities: Record }; } @@ -609,7 +587,7 @@ class LoggingService { // RoktKit class // ============================================================ -class RoktKit { +class RoktKit implements KitInterface { // Static field for allowed origin hashes (mutable by testHelpers) public static _allowedOriginHashes: number[] = [-553112570, 549508659]; @@ -621,6 +599,7 @@ class RoktKit { // Public fields (accessed by tests and the mParticle framework) public name = name; + public id = moduleId; public moduleId = moduleId; public isInitialized = false; public launcher: RoktLauncher | null = null; @@ -629,10 +608,10 @@ class RoktKit { public testHelpers: TestHelpers | null = null; public placementEventMappingLookup: Record = {}; public placementEventAttributeMappingLookup: Record = {}; - public eventQueue: MParticleEvent[] = []; + public eventQueue: SDKEvent[] = []; public eventStreamQueue: MParticleEvent[] = []; - public batchQueue: MPBatch[] = []; - public batchStreamQueue: MPBatch[] = []; + public batchQueue: Batch[] = []; + public batchStreamQueue: Batch[] = []; public integrationName: string | null = null; public domain?: string; public errorReportingService: ErrorReportingService | null = null; @@ -787,7 +766,7 @@ class RoktKit { mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record); } - private processEventQueue(): void { + private drainQueues(): void { this.eventQueue.forEach((event) => { this.process(event); }); @@ -818,15 +797,20 @@ class RoktKit { } } - public processBatch(batch: MPBatch): void { - if (!this.isKitReady()) { - this.batchQueue.push(batch); - return; + public processBatch(batch: Batch): string { + try { + if (!this.isKitReady()) { + this.batchQueue.push(batch); + return 'Batch queued for forwarder: ' + name; + } + this.sendBatchStream(batch); + return 'Successfully sent batch to forwarder: ' + name; + } catch (error) { + return "Can't send batch to forwarder: " + name + ' ' + error; } - this.sendBatchStream(batch); } - private sendBatchStream(batch: MPBatch): void { + private sendBatchStream(batch: Batch): void { if (window.Rokt && typeof window.Rokt.__batch_stream__ === 'function') { if (this.batchStreamQueue.length) { const queuedBatches = this.batchStreamQueue; @@ -930,7 +914,7 @@ class RoktKit { // Attaches the kit to the Rokt manager mp().Rokt.attachKit(this); - this.processEventQueue(); + this.drainQueues(); } private fetchOptimizely(): Record { @@ -983,147 +967,161 @@ class RoktKit { * Initializes the Rokt forwarder with settings from the mParticle server. */ public init( - settings: RoktKitSettings, + settings: Record, _service: unknown, testMode: boolean, _trackerId: unknown, filteredUserAttributes: Record, - ): void { - const accountId = settings.accountId; - const roktExtensions = extractRoktExtensions(settings.roktExtensions); - this.userAttributes = filteredUserAttributes || {}; - this._onboardingExpProvider = settings.onboardingExpProvider; - - const placementEventMapping = parseSettingsString(settings.placementEventMapping); - this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping); - - const placementEventAttributeMapping = parseSettingsString( - settings.placementEventAttributeMapping, - ); - this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping); - - // Set dynamic OTHER_IDENTITY based on server settings - if (settings.hashedEmailUserIdentityType) { - this._mappedEmailSha256Key = settings.hashedEmailUserIdentityType.toLowerCase(); - } - - const domain = mp().Rokt?.domain; - const launcherOptions: Record = { - ...((mp().Rokt?.launcherOptions as Record) || {}), - }; - this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined); - launcherOptions.integrationName = this.integrationName; - - this.domain = domain; - - const reportingConfig: ReportingConfig = { - loggingUrl: settings.loggingUrl, - errorUrl: settings.errorUrl, - isLoggingEnabled: settings.isLoggingEnabled === 'true' || settings.isLoggingEnabled === true, - }; - const errorReportingService = new ErrorReportingService( - reportingConfig, - this.integrationName, - window.__rokt_li_guid__, - settings.accountId, - ); - const loggingService = new LoggingService( - reportingConfig, - errorReportingService, - this.integrationName, - window.__rokt_li_guid__, - settings.accountId, - ); + ): string { + try { + const kitSettings = settings as unknown as RoktKitSettings; + const accountId = kitSettings.accountId; + const roktExtensions = extractRoktExtensions(kitSettings.roktExtensions); + this.userAttributes = filteredUserAttributes || {}; + this._onboardingExpProvider = kitSettings.onboardingExpProvider; + + const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping); + this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping); + + const placementEventAttributeMapping = parseSettingsString( + kitSettings.placementEventAttributeMapping, + ); + this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping); + + // Set dynamic OTHER_IDENTITY based on server settings + if (kitSettings.hashedEmailUserIdentityType) { + this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase(); + } - this.errorReportingService = errorReportingService; - this.loggingService = loggingService; + const domain = mp().Rokt?.domain; + const launcherOptions: Record = { + ...((mp().Rokt?.launcherOptions as Record) || {}), + }; + this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined); + launcherOptions.integrationName = this.integrationName; - if (mp()._registerErrorReportingService) { - mp()._registerErrorReportingService!(errorReportingService); - } - if (mp()._registerLoggingService) { - mp()._registerLoggingService!(loggingService); - } + this.domain = domain; - if (testMode) { - this.testHelpers = { - generateLauncherScript: generateLauncherScript, - extractRoktExtensions: extractRoktExtensions, - hashEventMessage: hashEventMessage, - parseSettingsString: parseSettingsString, - generateMappedEventLookup: generateMappedEventLookup, - generateMappedEventAttributeLookup: generateMappedEventAttributeLookup, - sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, - createAutoRemovedIframe: createAutoRemovedIframe, - djb2: djb2, - setAllowedOriginHashes: (hashes: number[]) => { - RoktKit._allowedOriginHashes = hashes; - }, - ReportingTransport: ReportingTransport, - ErrorReportingService: ErrorReportingService, - LoggingService: LoggingService, - RateLimiter: RateLimiter, - ErrorCodes: ErrorCodes, - WSDKErrorSeverity: WSDKErrorSeverity, + const reportingConfig: ReportingConfig = { + loggingUrl: kitSettings.loggingUrl, + errorUrl: kitSettings.errorUrl, + isLoggingEnabled: kitSettings.isLoggingEnabled === 'true' || kitSettings.isLoggingEnabled === true, }; - this.attachLauncher(accountId, launcherOptions); - return; - } + const errorReportingService = new ErrorReportingService( + reportingConfig, + this.integrationName, + window.__rokt_li_guid__, + kitSettings.accountId, + ); + const loggingService = new LoggingService( + reportingConfig, + errorReportingService, + this.integrationName, + window.__rokt_li_guid__, + kitSettings.accountId, + ); + + this.errorReportingService = errorReportingService; + this.loggingService = loggingService; + + if (mp()._registerErrorReportingService) { + mp()._registerErrorReportingService!(errorReportingService); + } + if (mp()._registerLoggingService) { + mp()._registerLoggingService!(loggingService); + } - if (this.isLauncherReadyToAttach()) { - this.attachLauncher(accountId, launcherOptions); - } else { - const target = document.head || document.body; - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = generateLauncherScript(domain, roktExtensions); - script.async = true; - script.crossOrigin = 'anonymous'; - (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high'; - script.id = 'rokt-launcher'; - - script.onload = () => { - if (this.isLauncherReadyToAttach()) { - this.attachLauncher(accountId, launcherOptions); - } else { - console.error('Rokt object is not available after script load.'); - } - }; + if (testMode) { + this.testHelpers = { + generateLauncherScript: generateLauncherScript, + extractRoktExtensions: extractRoktExtensions, + hashEventMessage: hashEventMessage, + parseSettingsString: parseSettingsString, + generateMappedEventLookup: generateMappedEventLookup, + generateMappedEventAttributeLookup: generateMappedEventAttributeLookup, + sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, + createAutoRemovedIframe: createAutoRemovedIframe, + djb2: djb2, + setAllowedOriginHashes: (hashes: number[]) => { + RoktKit._allowedOriginHashes = hashes; + }, + ReportingTransport: ReportingTransport, + ErrorReportingService: ErrorReportingService, + LoggingService: LoggingService, + RateLimiter: RateLimiter, + ErrorCodes: ErrorCodes, + WSDKErrorSeverity: WSDKErrorSeverity, + }; + this.attachLauncher(accountId, launcherOptions); + return 'Successfully initialized: ' + name; + } - script.onerror = (error) => { - console.error('Error loading Rokt launcher script:', error); - }; + if (this.isLauncherReadyToAttach()) { + this.attachLauncher(accountId, launcherOptions); + } else { + const target = document.head || document.body; + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = generateLauncherScript(domain, roktExtensions); + script.async = true; + script.crossOrigin = 'anonymous'; + (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high'; + script.id = 'rokt-launcher'; + + script.onload = () => { + if (this.isLauncherReadyToAttach()) { + this.attachLauncher(accountId, launcherOptions); + } else { + console.error('Rokt object is not available after script load.'); + } + }; + + script.onerror = (error) => { + console.error('Error loading Rokt launcher script:', error); + }; + + target.appendChild(script); + this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended); + } - target.appendChild(script); - this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended); + return 'Successfully initialized: ' + name; + } catch (e) { + return "Can't initialize forwarder: " + name + ': ' + e; } } - public process(event: MParticleEvent): void { - if (!this.isKitReady()) { - this.eventQueue.push(event); - return; - } + public process(event: SDKEvent): string { + try { + if (!this.isKitReady()) { + this.eventQueue.push(event); + return 'Event queued for forwarder: ' + name; + } + const mpEvent = event as unknown as MParticleEvent; - this.sendEventStream(event); + this.sendEventStream(mpEvent); - if (typeof mp().Rokt?.setLocalSessionAttribute !== 'function') { - return; - } + if (typeof mp().Rokt?.setLocalSessionAttribute !== 'function') { + return 'Successfully sent to forwarder: ' + name; + } - if (!isEmpty(this.placementEventAttributeMappingLookup)) { - this.applyPlacementEventAttributeMapping(event); - } + if (!isEmpty(this.placementEventAttributeMappingLookup)) { + this.applyPlacementEventAttributeMapping(mpEvent); + } - if (isEmpty(this.placementEventMappingLookup)) { - return; - } + if (isEmpty(this.placementEventMappingLookup)) { + return 'Successfully sent to forwarder: ' + name; + } - const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? ''); + const hashedEvent = hashEventMessage(mpEvent.EventDataType, mpEvent.EventCategory, mpEvent.EventName ?? ''); - if (this.placementEventMappingLookup[String(hashedEvent)]) { - const mappedValue = this.placementEventMappingLookup[String(hashedEvent)]; - mp().Rokt.setLocalSessionAttribute?.(mappedValue, true); + if (this.placementEventMappingLookup[String(hashedEvent)]) { + const mappedValue = this.placementEventMappingLookup[String(hashedEvent)]; + mp().Rokt.setLocalSessionAttribute?.(mappedValue, true); + } + + return 'Successfully sent to forwarder: ' + name; + } catch (error) { + return "Can't send to forwarder: " + name + ' ' + error; } } @@ -1136,36 +1134,66 @@ class RoktKit { window.Rokt!.setExtensionData(partnerExtensionData); } - public setUserAttribute(key: string, value: unknown): void { - this.userAttributes[key] = value; - this.sendEventStream( - this.buildIdentityEvent('set_user_attributes', this.filters.filteredUser ?? ({} as FilteredUser)), - ); + public setUserAttribute(key: string, value: unknown): string { + try { + this.userAttributes[key] = value; + this.sendEventStream( + this.buildIdentityEvent('set_user_attributes', this.filters.filteredUser ?? ({} as FilteredUser)), + ); + return 'Successfully set user attribute for forwarder: ' + name; + } catch (error) { + return "Can't set user attribute for forwarder: " + name + ' ' + error; + } } - public removeUserAttribute(key: string): void { - delete this.userAttributes[key]; + public removeUserAttribute(key: string): string { + try { + delete this.userAttributes[key]; + return 'Successfully removed user attribute for forwarder: ' + name; + } catch (error) { + return "Can't remove user attribute for forwarder: " + name + ' ' + error; + } } - public onUserIdentified(filteredUser: FilteredUser): void { - this.filters.filteredUser = filteredUser; - this.userAttributes = filteredUser.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('identify', filteredUser)); + public onUserIdentified(user: IMParticleUser): string { + try { + this.filters.filteredUser = user as FilteredUser; + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('identify', user as FilteredUser)); + return 'Successfully called onUserIdentified for forwarder: ' + name; + } catch (error) { + return "Can't call onUserIdentified for forwarder: " + name + ' ' + error; + } } - public onLoginComplete(filteredUser: FilteredUser): void { - this.userAttributes = filteredUser.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('login', filteredUser)); + public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { + try { + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('login', user as FilteredUser)); + return 'Successfully called onLoginComplete for forwarder: ' + name; + } catch (error) { + return "Can't call onLoginComplete for forwarder: " + name + ' ' + error; + } } - public onLogoutComplete(filteredUser: FilteredUser): void { - this.userAttributes = filteredUser.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('logout', filteredUser)); + public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { + try { + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('logout', user as FilteredUser)); + return 'Successfully called onLogoutComplete for forwarder: ' + name; + } catch (error) { + return "Can't call onLogoutComplete for forwarder: " + name + ' ' + error; + } } - public onModifyComplete(filteredUser: FilteredUser): void { - this.userAttributes = filteredUser.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('modify_user', filteredUser)); + public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { + try { + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('modify_user', user as FilteredUser)); + return 'Successfully called onModifyComplete for forwarder: ' + name; + } catch (error) { + return "Can't call onModifyComplete for forwarder: " + name + ' ' + error; + } } /** From c0a82c332318f3c7450c75ffd06ec6466a5bb8cb Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 16:36:01 -0400 Subject: [PATCH 4/9] feat: fix lint error --- src/Rokt-Kit.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 9f91fd0..337b873 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -69,7 +69,6 @@ interface RoktLauncher { use(extensionName: string): Promise; } - interface RoktGlobal { createLauncher(options: Record): Promise; createLocalLauncher(options: Record): RoktLauncher; From de19fc857a88fc5f4bb4d8c936e639948742a9c3 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 16:50:17 -0400 Subject: [PATCH 5/9] feat: removed added try/catch blocks --- src/Rokt-Kit.ts | 334 +++++++++++++++++++++--------------------------- 1 file changed, 149 insertions(+), 185 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 337b873..5c4b02a 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -797,16 +797,12 @@ class RoktKit implements KitInterface { } public processBatch(batch: Batch): string { - try { - if (!this.isKitReady()) { - this.batchQueue.push(batch); - return 'Batch queued for forwarder: ' + name; - } - this.sendBatchStream(batch); - return 'Successfully sent batch to forwarder: ' + name; - } catch (error) { - return "Can't send batch to forwarder: " + name + ' ' + error; + if (!this.isKitReady()) { + this.batchQueue.push(batch); + return 'Batch queued for forwarder: ' + name; } + this.sendBatchStream(batch); + return 'Successfully sent batch to forwarder: ' + name; } private sendBatchStream(batch: Batch): void { @@ -972,156 +968,148 @@ class RoktKit implements KitInterface { _trackerId: unknown, filteredUserAttributes: Record, ): string { - try { - const kitSettings = settings as unknown as RoktKitSettings; - const accountId = kitSettings.accountId; - const roktExtensions = extractRoktExtensions(kitSettings.roktExtensions); - this.userAttributes = filteredUserAttributes || {}; - this._onboardingExpProvider = kitSettings.onboardingExpProvider; - - const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping); - this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping); - - const placementEventAttributeMapping = parseSettingsString( - kitSettings.placementEventAttributeMapping, - ); - this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping); - - // Set dynamic OTHER_IDENTITY based on server settings - if (kitSettings.hashedEmailUserIdentityType) { - this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase(); - } + const kitSettings = settings as unknown as RoktKitSettings; + const accountId = kitSettings.accountId; + const roktExtensions = extractRoktExtensions(kitSettings.roktExtensions); + this.userAttributes = filteredUserAttributes || {}; + this._onboardingExpProvider = kitSettings.onboardingExpProvider; - const domain = mp().Rokt?.domain; - const launcherOptions: Record = { - ...((mp().Rokt?.launcherOptions as Record) || {}), - }; - this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined); - launcherOptions.integrationName = this.integrationName; + const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping); + this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping); - this.domain = domain; + const placementEventAttributeMapping = parseSettingsString( + kitSettings.placementEventAttributeMapping, + ); + this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping); - const reportingConfig: ReportingConfig = { - loggingUrl: kitSettings.loggingUrl, - errorUrl: kitSettings.errorUrl, - isLoggingEnabled: kitSettings.isLoggingEnabled === 'true' || kitSettings.isLoggingEnabled === true, - }; - const errorReportingService = new ErrorReportingService( - reportingConfig, - this.integrationName, - window.__rokt_li_guid__, - kitSettings.accountId, - ); - const loggingService = new LoggingService( - reportingConfig, - errorReportingService, - this.integrationName, - window.__rokt_li_guid__, - kitSettings.accountId, - ); - - this.errorReportingService = errorReportingService; - this.loggingService = loggingService; - - if (mp()._registerErrorReportingService) { - mp()._registerErrorReportingService!(errorReportingService); - } - if (mp()._registerLoggingService) { - mp()._registerLoggingService!(loggingService); - } + // Set dynamic OTHER_IDENTITY based on server settings + if (kitSettings.hashedEmailUserIdentityType) { + this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase(); + } - if (testMode) { - this.testHelpers = { - generateLauncherScript: generateLauncherScript, - extractRoktExtensions: extractRoktExtensions, - hashEventMessage: hashEventMessage, - parseSettingsString: parseSettingsString, - generateMappedEventLookup: generateMappedEventLookup, - generateMappedEventAttributeLookup: generateMappedEventAttributeLookup, - sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, - createAutoRemovedIframe: createAutoRemovedIframe, - djb2: djb2, - setAllowedOriginHashes: (hashes: number[]) => { - RoktKit._allowedOriginHashes = hashes; - }, - ReportingTransport: ReportingTransport, - ErrorReportingService: ErrorReportingService, - LoggingService: LoggingService, - RateLimiter: RateLimiter, - ErrorCodes: ErrorCodes, - WSDKErrorSeverity: WSDKErrorSeverity, - }; - this.attachLauncher(accountId, launcherOptions); - return 'Successfully initialized: ' + name; - } + const domain = mp().Rokt?.domain; + const launcherOptions: Record = { + ...((mp().Rokt?.launcherOptions as Record) || {}), + }; + this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined); + launcherOptions.integrationName = this.integrationName; - if (this.isLauncherReadyToAttach()) { - this.attachLauncher(accountId, launcherOptions); - } else { - const target = document.head || document.body; - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = generateLauncherScript(domain, roktExtensions); - script.async = true; - script.crossOrigin = 'anonymous'; - (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high'; - script.id = 'rokt-launcher'; - - script.onload = () => { - if (this.isLauncherReadyToAttach()) { - this.attachLauncher(accountId, launcherOptions); - } else { - console.error('Rokt object is not available after script load.'); - } - }; - - script.onerror = (error) => { - console.error('Error loading Rokt launcher script:', error); - }; - - target.appendChild(script); - this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended); - } + this.domain = domain; + const reportingConfig: ReportingConfig = { + loggingUrl: kitSettings.loggingUrl, + errorUrl: kitSettings.errorUrl, + isLoggingEnabled: kitSettings.isLoggingEnabled === 'true' || kitSettings.isLoggingEnabled === true, + }; + const errorReportingService = new ErrorReportingService( + reportingConfig, + this.integrationName, + window.__rokt_li_guid__, + kitSettings.accountId, + ); + const loggingService = new LoggingService( + reportingConfig, + errorReportingService, + this.integrationName, + window.__rokt_li_guid__, + kitSettings.accountId, + ); + + this.errorReportingService = errorReportingService; + this.loggingService = loggingService; + + if (mp()._registerErrorReportingService) { + mp()._registerErrorReportingService!(errorReportingService); + } + if (mp()._registerLoggingService) { + mp()._registerLoggingService!(loggingService); + } + + if (testMode) { + this.testHelpers = { + generateLauncherScript: generateLauncherScript, + extractRoktExtensions: extractRoktExtensions, + hashEventMessage: hashEventMessage, + parseSettingsString: parseSettingsString, + generateMappedEventLookup: generateMappedEventLookup, + generateMappedEventAttributeLookup: generateMappedEventAttributeLookup, + sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, + createAutoRemovedIframe: createAutoRemovedIframe, + djb2: djb2, + setAllowedOriginHashes: (hashes: number[]) => { + RoktKit._allowedOriginHashes = hashes; + }, + ReportingTransport: ReportingTransport, + ErrorReportingService: ErrorReportingService, + LoggingService: LoggingService, + RateLimiter: RateLimiter, + ErrorCodes: ErrorCodes, + WSDKErrorSeverity: WSDKErrorSeverity, + }; + this.attachLauncher(accountId, launcherOptions); return 'Successfully initialized: ' + name; - } catch (e) { - return "Can't initialize forwarder: " + name + ': ' + e; } - } - public process(event: SDKEvent): string { - try { - if (!this.isKitReady()) { - this.eventQueue.push(event); - return 'Event queued for forwarder: ' + name; - } - const mpEvent = event as unknown as MParticleEvent; + if (this.isLauncherReadyToAttach()) { + this.attachLauncher(accountId, launcherOptions); + } else { + const target = document.head || document.body; + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = generateLauncherScript(domain, roktExtensions); + script.async = true; + script.crossOrigin = 'anonymous'; + (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high'; + script.id = 'rokt-launcher'; + + script.onload = () => { + if (this.isLauncherReadyToAttach()) { + this.attachLauncher(accountId, launcherOptions); + } else { + console.error('Rokt object is not available after script load.'); + } + }; - this.sendEventStream(mpEvent); + script.onerror = (error) => { + console.error('Error loading Rokt launcher script:', error); + }; - if (typeof mp().Rokt?.setLocalSessionAttribute !== 'function') { - return 'Successfully sent to forwarder: ' + name; - } + target.appendChild(script); + this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended); + } - if (!isEmpty(this.placementEventAttributeMappingLookup)) { - this.applyPlacementEventAttributeMapping(mpEvent); - } + return 'Successfully initialized: ' + name; + } - if (isEmpty(this.placementEventMappingLookup)) { - return 'Successfully sent to forwarder: ' + name; - } + public process(event: SDKEvent): string { + if (!this.isKitReady()) { + this.eventQueue.push(event); + return 'Event queued for forwarder: ' + name; + } + const mpEvent = event as unknown as MParticleEvent; - const hashedEvent = hashEventMessage(mpEvent.EventDataType, mpEvent.EventCategory, mpEvent.EventName ?? ''); + this.sendEventStream(mpEvent); - if (this.placementEventMappingLookup[String(hashedEvent)]) { - const mappedValue = this.placementEventMappingLookup[String(hashedEvent)]; - mp().Rokt.setLocalSessionAttribute?.(mappedValue, true); - } + if (typeof mp().Rokt?.setLocalSessionAttribute !== 'function') { + return 'Successfully sent to forwarder: ' + name; + } + if (!isEmpty(this.placementEventAttributeMappingLookup)) { + this.applyPlacementEventAttributeMapping(mpEvent); + } + + if (isEmpty(this.placementEventMappingLookup)) { return 'Successfully sent to forwarder: ' + name; - } catch (error) { - return "Can't send to forwarder: " + name + ' ' + error; } + + const hashedEvent = hashEventMessage(mpEvent.EventDataType, mpEvent.EventCategory, mpEvent.EventName ?? ''); + + if (this.placementEventMappingLookup[String(hashedEvent)]) { + const mappedValue = this.placementEventMappingLookup[String(hashedEvent)]; + mp().Rokt.setLocalSessionAttribute?.(mappedValue, true); + } + + return 'Successfully sent to forwarder: ' + name; } public setExtensionData(partnerExtensionData: Record): void { @@ -1134,65 +1122,41 @@ class RoktKit implements KitInterface { } public setUserAttribute(key: string, value: unknown): string { - try { - this.userAttributes[key] = value; - this.sendEventStream( - this.buildIdentityEvent('set_user_attributes', this.filters.filteredUser ?? ({} as FilteredUser)), - ); - return 'Successfully set user attribute for forwarder: ' + name; - } catch (error) { - return "Can't set user attribute for forwarder: " + name + ' ' + error; - } + this.userAttributes[key] = value; + this.sendEventStream( + this.buildIdentityEvent('set_user_attributes', this.filters.filteredUser ?? ({} as FilteredUser)), + ); + return 'Successfully set user attribute for forwarder: ' + name; } public removeUserAttribute(key: string): string { - try { - delete this.userAttributes[key]; - return 'Successfully removed user attribute for forwarder: ' + name; - } catch (error) { - return "Can't remove user attribute for forwarder: " + name + ' ' + error; - } + delete this.userAttributes[key]; + return 'Successfully removed user attribute for forwarder: ' + name; } public onUserIdentified(user: IMParticleUser): string { - try { - this.filters.filteredUser = user as FilteredUser; - this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('identify', user as FilteredUser)); - return 'Successfully called onUserIdentified for forwarder: ' + name; - } catch (error) { - return "Can't call onUserIdentified for forwarder: " + name + ' ' + error; - } + this.filters.filteredUser = user as FilteredUser; + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('identify', user as FilteredUser)); + return 'Successfully called onUserIdentified for forwarder: ' + name; } public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { - try { - this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('login', user as FilteredUser)); - return 'Successfully called onLoginComplete for forwarder: ' + name; - } catch (error) { - return "Can't call onLoginComplete for forwarder: " + name + ' ' + error; - } + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('login', user as FilteredUser)); + return 'Successfully called onLoginComplete for forwarder: ' + name; } public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { - try { - this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('logout', user as FilteredUser)); - return 'Successfully called onLogoutComplete for forwarder: ' + name; - } catch (error) { - return "Can't call onLogoutComplete for forwarder: " + name + ' ' + error; - } + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('logout', user as FilteredUser)); + return 'Successfully called onLogoutComplete for forwarder: ' + name; } public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { - try { - this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('modify_user', user as FilteredUser)); - return 'Successfully called onModifyComplete for forwarder: ' + name; - } catch (error) { - return "Can't call onModifyComplete for forwarder: " + name + ' ' + error; - } + this.userAttributes = user.getAllUserAttributes(); + this.sendEventStream(this.buildIdentityEvent('modify_user', user as FilteredUser)); + return 'Successfully called onModifyComplete for forwarder: ' + name; } /** From dc9ce786977d7225ade8ef13cfc9762c395fe370 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 17:36:28 -0400 Subject: [PATCH 6/9] feat: remove event_stream --- src/Rokt-Kit.ts | 99 +---- test/src/tests.spec.ts | 836 +---------------------------------------- 2 files changed, 18 insertions(+), 917 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 5c4b02a..c2178a2 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -73,13 +73,12 @@ interface RoktGlobal { createLauncher(options: Record): Promise; createLocalLauncher(options: Record): RoktLauncher; currentLauncher?: RoktLauncher; - __event_stream__?(event: Record): void; __batch_stream__?(batch: Batch): void; setExtensionData(data: Record): void; } interface FilteredUser extends IMParticleUser { - getMPID?(): string; + getMPID(): string; getUserIdentities?: () => { userIdentities: Record }; } @@ -205,7 +204,6 @@ const moduleId = 181; const EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; const ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; const INIT_LOG_SAMPLING_RATE = 0.1; -const MESSAGE_TYPE_PROFILE = 14; // mParticle MessageType.Profile // ============================================================ // Reporting service constants @@ -607,8 +605,6 @@ class RoktKit implements KitInterface { public testHelpers: TestHelpers | null = null; public placementEventMappingLookup: Record = {}; public placementEventAttributeMappingLookup: Record = {}; - public eventQueue: SDKEvent[] = []; - public eventStreamQueue: MParticleEvent[] = []; public batchQueue: Batch[] = []; public batchStreamQueue: Batch[] = []; public integrationName: string | null = null; @@ -765,37 +761,13 @@ class RoktKit implements KitInterface { mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record); } - private drainQueues(): void { - this.eventQueue.forEach((event) => { - this.process(event); - }); - this.eventQueue = []; - + private drainBatchQueue(): void { this.batchQueue.forEach((batch) => { this.processBatch(batch); }); this.batchQueue = []; } - private enrichEvent(event: MParticleEvent): Record { - return { ...(event as Record), UserAttributes: this.userAttributes }; - } - - private sendEventStream(event: MParticleEvent): void { - if (window.Rokt && typeof window.Rokt.__event_stream__ === 'function') { - if (this.eventStreamQueue.length) { - const queuedEvents = this.eventStreamQueue; - this.eventStreamQueue = []; - for (let i = 0; i < queuedEvents.length; i++) { - window.Rokt.__event_stream__(this.enrichEvent(queuedEvents[i])); - } - } - window.Rokt.__event_stream__(this.enrichEvent(event)); - } else { - this.eventStreamQueue.push(event); - } - } - public processBatch(batch: Batch): string { if (!this.isKitReady()) { this.batchQueue.push(batch); @@ -836,28 +808,6 @@ class RoktKit implements KitInterface { } } - private buildIdentityEvent(eventName: string, filteredUser: FilteredUser): MParticleEvent { - const mpid = filteredUser.getMPID && typeof filteredUser.getMPID === 'function' ? filteredUser.getMPID() : null; - const sessionId = - mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function' - ? mp().sessionManager!.getSession() - : null; - const userIdentities = - filteredUser.getUserIdentities && typeof filteredUser.getUserIdentities === 'function' - ? filteredUser.getUserIdentities().userIdentities - : null; - - return { - EventName: eventName, - EventDataType: MESSAGE_TYPE_PROFILE, - EventCategory: 0, - Timestamp: Date.now(), - MPID: mpid, - SessionId: sessionId, - UserIdentities: userIdentities, - }; - } - private attachLauncher(accountId: string, launcherOptions: Record): void { const mpSessionId = mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function' @@ -909,7 +859,7 @@ class RoktKit implements KitInterface { // Attaches the kit to the Rokt manager mp().Rokt.attachKit(this); - this.drainQueues(); + this.drainBatchQueue(); } private fetchOptimizely(): Record { @@ -1083,30 +1033,21 @@ class RoktKit implements KitInterface { public process(event: SDKEvent): string { if (!this.isKitReady()) { - this.eventQueue.push(event); - return 'Event queued for forwarder: ' + name; + return 'Kit not ready for forwarder: ' + name; } const mpEvent = event as unknown as MParticleEvent; - this.sendEventStream(mpEvent); - - if (typeof mp().Rokt?.setLocalSessionAttribute !== 'function') { - return 'Successfully sent to forwarder: ' + name; - } - - if (!isEmpty(this.placementEventAttributeMappingLookup)) { - this.applyPlacementEventAttributeMapping(mpEvent); - } - - if (isEmpty(this.placementEventMappingLookup)) { - return 'Successfully sent to forwarder: ' + name; - } - - const hashedEvent = hashEventMessage(mpEvent.EventDataType, mpEvent.EventCategory, mpEvent.EventName ?? ''); + if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { + if (!isEmpty(this.placementEventAttributeMappingLookup)) { + this.applyPlacementEventAttributeMapping(mpEvent); + } - if (this.placementEventMappingLookup[String(hashedEvent)]) { - const mappedValue = this.placementEventMappingLookup[String(hashedEvent)]; - mp().Rokt.setLocalSessionAttribute?.(mappedValue, true); + if (!isEmpty(this.placementEventMappingLookup)) { + const hashedEvent = hashEventMessage(mpEvent.EventDataType, mpEvent.EventCategory, mpEvent.EventName ?? ''); + if (this.placementEventMappingLookup[String(hashedEvent)]) { + mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true); + } + } } return 'Successfully sent to forwarder: ' + name; @@ -1123,9 +1064,6 @@ class RoktKit implements KitInterface { public setUserAttribute(key: string, value: unknown): string { this.userAttributes[key] = value; - this.sendEventStream( - this.buildIdentityEvent('set_user_attributes', this.filters.filteredUser ?? ({} as FilteredUser)), - ); return 'Successfully set user attribute for forwarder: ' + name; } @@ -1137,25 +1075,21 @@ class RoktKit implements KitInterface { public onUserIdentified(user: IMParticleUser): string { this.filters.filteredUser = user as FilteredUser; this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('identify', user as FilteredUser)); return 'Successfully called onUserIdentified for forwarder: ' + name; } public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('login', user as FilteredUser)); return 'Successfully called onLoginComplete for forwarder: ' + name; } public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('logout', user as FilteredUser)); return 'Successfully called onLogoutComplete for forwarder: ' + name; } public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { this.userAttributes = user.getAllUserAttributes(); - this.sendEventStream(this.buildIdentityEvent('modify_user', user as FilteredUser)); return 'Successfully called onModifyComplete for forwarder: ' + name; } @@ -1169,10 +1103,7 @@ class RoktKit implements KitInterface { const filters = this.filters || {}; const userAttributeFilters = (filters.userAttributeFilters as string[]) || []; const filteredUser = filters.filteredUser || null; - const mpid = - filteredUser && filteredUser.getMPID && typeof filteredUser.getMPID === 'function' - ? filteredUser.getMPID() - : null; + const mpid = filteredUser ? filteredUser.getMPID() : null; let filteredAttributes: Record; diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 925f91b..53a65c2 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -2931,11 +2931,6 @@ describe('Rokt Forwarder', () => { }, }; }); - afterEach(() => { - delete (window as any).Rokt.__event_stream__; - (window as any).mParticle.forwarder.eventStreamQueue = []; - }); - it('should set the user attribute', async () => { (window as any).mParticle.forwarder.setUserAttribute('test-attribute', 'test-value'); @@ -2943,42 +2938,6 @@ describe('Rokt Forwarder', () => { 'test-attribute': 'test-value', }); }); - - it('should send a set_user_attributes event to window.Rokt.__event_stream__', () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onUserIdentified({ - getAllUserAttributes: function () { - return { 'user-attr': 'user-value' }; - }, - getMPID: function () { - return 'test-mpid'; - }, - getUserIdentities: function () { - return { userIdentities: { email: 'test@example.com' } }; - }, - }); - - receivedEvents.length = 0; // clear the identify event - - (window as any).mParticle.forwarder.setUserAttribute('new-attr', 'new-value'); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('set_user_attributes'); - expect(receivedEvents[0].EventDataType).toBe(14); - expect(receivedEvents[0].MPID).toBe('test-mpid'); - expect(receivedEvents[0].SessionId).toBe('test-mp-session-id'); - }); - - it('should queue event when window.Rokt.__event_stream__ is not available', () => { - (window as any).mParticle.forwarder.setUserAttribute('queued-attr', 'queued-value'); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('set_user_attributes'); - }); }); describe('#removeUserAttribute', () => { @@ -2992,18 +2951,6 @@ describe('Rokt Forwarder', () => { }); describe('#onUserIdentified', () => { - beforeEach(() => { - (window as any).mParticle.sessionManager = { - getSession: function () { - return 'test-mp-session-id'; - }, - }; - }); - afterEach(() => { - delete (window as any).Rokt.__event_stream__; - (window as any).mParticle.forwarder.eventStreamQueue = []; - }); - it('should set the filtered user and userAttributes', () => { (window as any).mParticle.forwarder.onUserIdentified({ getAllUserAttributes: function () { @@ -3022,69 +2969,9 @@ describe('Rokt Forwarder', () => { }); expect((window as any).mParticle.forwarder.filters.filteredUser.getMPID()).toBe('123'); }); - - it('should send a User Identified event to window.Rokt.__event_stream__', () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onUserIdentified({ - getAllUserAttributes: function () { - return { 'user-attr': 'user-value' }; - }, - getMPID: function () { - return 'identified-mpid-123'; - }, - getUserIdentities: function () { - return { userIdentities: { email: 'jenny@example.com' } }; - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('identify'); - expect(receivedEvents[0].EventDataType).toBe(14); - expect(receivedEvents[0].MPID).toBe('identified-mpid-123'); - expect(receivedEvents[0].SessionId).toBe('test-mp-session-id'); - expect(receivedEvents[0].UserAttributes).toEqual({ - 'user-attr': 'user-value', - }); - expect(receivedEvents[0].UserIdentities).toEqual({ - email: 'jenny@example.com', - }); - }); - - it('should queue event when window.Rokt.__event_stream__ is not available', () => { - (window as any).mParticle.forwarder.onUserIdentified({ - getAllUserAttributes: function () { - return {}; - }, - getMPID: function () { - return '123'; - }, - getUserIdentities: function () { - return { userIdentities: {} }; - }, - }); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('identify'); - }); }); describe('#onLoginComplete', () => { - beforeEach(() => { - (window as any).mParticle.sessionManager = { - getSession: function () { - return 'test-mp-session-id'; - }, - }; - }); - afterEach(() => { - delete (window as any).Rokt.__event_stream__; - (window as any).mParticle.forwarder.eventStreamQueue = []; - }); - it('should update userAttributes from the filtered user', () => { (window as any).mParticle.forwarder.onLoginComplete({ getAllUserAttributes: function () { @@ -3096,122 +2983,9 @@ describe('Rokt Forwarder', () => { 'user-attr': 'user-value', }); }); - - it('should send a User Login event to window.Rokt.__event_stream__', () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onLoginComplete({ - getAllUserAttributes: function () { - return { 'user-attr': 'user-value' }; - }, - getMPID: function () { - return 'login-mpid-123'; - }, - getUserIdentities: function () { - return { - userIdentities: { email: 'jenny@example.com' }, - }; - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('login'); - expect(receivedEvents[0].EventDataType).toBe(14); - expect(receivedEvents[0].UserAttributes).toEqual({ - 'user-attr': 'user-value', - }); - expect(receivedEvents[0].MPID).toBe('login-mpid-123'); - expect(receivedEvents[0].SessionId).toBe('test-mp-session-id'); - expect(receivedEvents[0].UserIdentities).toEqual({ - email: 'jenny@example.com', - }); - }); - - it('should include null MPID and null UserIdentities when filteredUser has no getMPID or getUserIdentities', () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onLoginComplete({ - getAllUserAttributes: function () { - return {}; - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].MPID).toBeNull(); - expect(receivedEvents[0].UserIdentities).toBeNull(); - }); - - it('should include null SessionId when sessionManager is unavailable', () => { - delete (window as any).mParticle.sessionManager; - - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onLoginComplete({ - getAllUserAttributes: function () { - return {}; - }, - getMPID: function () { - return 'some-mpid'; - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].SessionId).toBeNull(); - }); - - it('should queue event when window.Rokt.__event_stream__ is not available', () => { - (window as any).mParticle.forwarder.onLoginComplete({ - getAllUserAttributes: function () { - return {}; - }, - }); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('login'); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventDataType).toBe(14); - }); - - it('should queue event when window.Rokt is undefined', () => { - const savedRokt = (window as any).Rokt; - (window as any).Rokt = undefined; - - expect(() => { - (window as any).mParticle.forwarder.onLoginComplete({ - getAllUserAttributes: function () { - return {}; - }, - }); - }).not.toThrow(); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('login'); - - (window as any).Rokt = savedRokt; - }); }); describe('#onLogoutComplete', () => { - beforeEach(() => { - (window as any).mParticle.sessionManager = { - getSession: function () { - return 'test-mp-session-id'; - }, - }; - }); - afterEach(() => { - delete (window as any).Rokt.__event_stream__; - (window as any).mParticle.forwarder.eventStreamQueue = []; - }); - it('should update userAttributes from the filtered user', () => { (window as any).mParticle.forwarder.onLogoutComplete({ getAllUserAttributes: function () { @@ -3223,82 +2997,9 @@ describe('Rokt Forwarder', () => { 'remaining-attr': 'some-value', }); }); - - it('should send a User Logout event to window.Rokt.__event_stream__', () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onLogoutComplete({ - getAllUserAttributes: function () { - return {}; - }, - getMPID: function () { - return 'logout-mpid-456'; - }, - getUserIdentities: function () { - return { - userIdentities: { customerid: 'cust-789' }, - }; - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('logout'); - expect(receivedEvents[0].EventDataType).toBe(14); - expect(receivedEvents[0].UserAttributes).toEqual({}); - expect(receivedEvents[0].MPID).toBe('logout-mpid-456'); - expect(receivedEvents[0].SessionId).toBe('test-mp-session-id'); - expect(receivedEvents[0].UserIdentities).toEqual({ - customerid: 'cust-789', - }); - }); - - it('should queue event when window.Rokt.__event_stream__ is not available', () => { - (window as any).mParticle.forwarder.onLogoutComplete({ - getAllUserAttributes: function () { - return {}; - }, - }); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('logout'); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventDataType).toBe(14); - }); - - it('should queue event when window.Rokt is undefined', () => { - const savedRokt = (window as any).Rokt; - (window as any).Rokt = undefined; - - expect(() => { - (window as any).mParticle.forwarder.onLogoutComplete({ - getAllUserAttributes: function () { - return {}; - }, - }); - }).not.toThrow(); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('logout'); - - (window as any).Rokt = savedRokt; - }); }); describe('#onModifyComplete', () => { - beforeEach(() => { - (window as any).mParticle.sessionManager = { - getSession: function () { - return 'test-mp-session-id'; - }, - }; - }); - afterEach(() => { - delete (window as any).Rokt.__event_stream__; - (window as any).mParticle.forwarder.eventStreamQueue = []; - }); - it('should update userAttributes from the filtered user', () => { (window as any).mParticle.forwarder.onModifyComplete({ getAllUserAttributes: function () { @@ -3316,81 +3017,6 @@ describe('Rokt Forwarder', () => { 'modified-attr': 'modified-value', }); }); - - it('should send a User Modified event to window.Rokt.__event_stream__', () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.onModifyComplete({ - getAllUserAttributes: function () { - return { 'modified-attr': 'modified-value' }; - }, - getMPID: function () { - return 'modify-mpid-789'; - }, - getUserIdentities: function () { - return { - userIdentities: { email: 'modified@example.com' }, - }; - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('modify_user'); - expect(receivedEvents[0].EventDataType).toBe(14); - expect(receivedEvents[0].MPID).toBe('modify-mpid-789'); - expect(receivedEvents[0].SessionId).toBe('test-mp-session-id'); - expect(receivedEvents[0].UserAttributes).toEqual({ - 'modified-attr': 'modified-value', - }); - expect(receivedEvents[0].UserIdentities).toEqual({ - email: 'modified@example.com', - }); - }); - - it('should queue event when window.Rokt.__event_stream__ is not available', () => { - (window as any).mParticle.forwarder.onModifyComplete({ - getAllUserAttributes: function () { - return {}; - }, - getMPID: function () { - return '123'; - }, - getUserIdentities: function () { - return { userIdentities: {} }; - }, - }); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('modify_user'); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventDataType).toBe(14); - }); - - it('should queue event when window.Rokt is undefined', () => { - const savedRokt = (window as any).Rokt; - (window as any).Rokt = undefined; - - expect(() => { - (window as any).mParticle.forwarder.onModifyComplete({ - getAllUserAttributes: function () { - return {}; - }, - getMPID: function () { - return '123'; - }, - getUserIdentities: function () { - return { userIdentities: {} }; - }, - }); - }).not.toThrow(); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0].EventName).toBe('modify_user'); - - (window as any).Rokt = savedRokt; - }); }); describe('#fetchOptimizely', () => { @@ -4705,452 +4331,10 @@ describe('Rokt Forwarder', () => { 'foo-mapped-flag': true, }); }); + }); - it('should add the event to the event queue if the kit is not initialized', async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - (window as any).mParticle.forwarder.process({ - EventName: 'Video Watched A', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect((window as any).mParticle.forwarder.eventQueue).toEqual([ - { - EventName: 'Video Watched A', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }, - ]); - }); - - it('should process queued events once the kit is ready', async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - (window as any).mParticle.forwarder.process({ - EventName: 'Video Watched B', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect((window as any).mParticle.forwarder.eventQueue).toEqual([ - { - EventName: 'Video Watched B', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }, - ]); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - expect((window as any).mParticle.forwarder.eventQueue).toEqual([]); - }); - }); - - describe('#_sendEventStream', () => { - beforeEach(() => { - (window as any).mParticle.forwarder.eventStreamQueue = []; - (window as any).Rokt = new (MockRoktForwarder as any)(); - (window as any).Rokt.createLauncher = async function () { - return Promise.resolve({ - selectPlacements: function (options: any) { - (window as any).mParticle.Rokt.selectPlacementsOptions = options; - (window as any).mParticle.Rokt.selectPlacementsCalled = true; - }, - }); - }; - (window as any).mParticle.Rokt = (window as any).Rokt; - (window as any).mParticle.Rokt.attachKitCalled = false; - (window as any).mParticle.Rokt.attachKit = async (kit: any) => { - (window as any).mParticle.Rokt.attachKitCalled = true; - (window as any).mParticle.Rokt.kit = kit; - Promise.resolve(); - }; - (window as any).mParticle.Rokt.setLocalSessionAttribute = function (key: any, value: any) { - (window as any).mParticle._Store.localSessionAttributes[key] = value; - }; - (window as any).mParticle.Rokt.getLocalSessionAttributes = function () { - return (window as any).mParticle._Store.localSessionAttributes; - }; - (window as any).mParticle.forwarder.launcher = { - selectPlacements: function (options: any) { - (window as any).mParticle.Rokt.selectPlacementsOptions = options; - (window as any).mParticle.Rokt.selectPlacementsCalled = true; - }, - }; - (window as any).mParticle.Rokt.filters = { - userAttributesFilters: [], - filterUserAttributes: function (attributes: any) { - return attributes; - }, - filteredUser: { - getMPID: function () { - return '123'; - }, - }, - }; - }); - - afterEach(() => { - delete (window as any).Rokt.__event_stream__; - (window as any).mParticle.forwarder.eventQueue = []; - (window as any).mParticle.forwarder.eventStreamQueue = []; - (window as any).mParticle.forwarder.isInitialized = false; - (window as any).mParticle.Rokt.attachKitCalled = false; - }); - - it('should forward event to window.Rokt.__event_stream__ when available', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - const testEvent = { - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }; - - (window as any).mParticle.forwarder.process(testEvent); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('Test Event'); - expect(receivedEvents[0].EventCategory).toBe(EventType.Other); - expect(receivedEvents[0].EventDataType).toBe(MessageType.PageEvent); - expect(receivedEvents[0].UserAttributes).toEqual({}); - }); - - it('should queue event when window.Rokt.__event_stream__ is not defined', async () => { - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - const testEvent = { - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }; - - expect(() => { - (window as any).mParticle.forwarder.process(testEvent); - }).not.toThrow(); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0]).toEqual(testEvent); - }); - - it('should queue event when window.Rokt is undefined', async () => { - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - const savedRokt = (window as any).Rokt; - (window as any).Rokt = undefined; - - const testEvent = { - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }; - - expect(() => { - (window as any).mParticle.forwarder.process(testEvent); - }).not.toThrow(); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(1); - expect((window as any).mParticle.forwarder.eventStreamQueue[0]).toEqual(testEvent); - - (window as any).Rokt = savedRokt; - }); - - it('should forward event with attributes to the event stream', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - const testEvent = { - EventName: 'Purchase', - EventCategory: EventType.Transaction, - EventDataType: MessageType.PageEvent, - EventAttributes: { - product: 'shoes', - price: '49.99', - }, - }; - - (window as any).mParticle.forwarder.process(testEvent); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventAttributes).toEqual({ - product: 'shoes', - price: '49.99', - }); - }); - - it('should forward multiple events to the event stream', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.process({ - EventName: 'Event A', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - (window as any).mParticle.forwarder.process({ - EventName: 'Event B', - EventCategory: EventType.Navigation, - EventDataType: MessageType.PageView, - }); - - expect(receivedEvents.length).toBe(2); - expect(receivedEvents[0].EventName).toBe('Event A'); - expect(receivedEvents[1].EventName).toBe('Event B'); - }); - - it('should forward queued events to the event stream after init', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.process({ - EventName: 'Queued Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect(receivedEvents.length).toBe(0); - expect((window as any).mParticle.forwarder.eventQueue.length).toBe(1); - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('Queued Event'); - }); - - it('should still process placement event mapping alongside event stream', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - const placementEventMapping = JSON.stringify([ - { - jsmap: 'hashed-<48Video Watched>-value', - map: '123466', - maptype: 'EventClass.Id', - value: 'foo-mapped-flag', - }, - ]); - - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - placementEventMapping: placementEventMapping, - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.process({ - EventName: 'Video Watched', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].EventName).toBe('Video Watched'); - - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ - 'foo-mapped-flag': true, - }); - }); - - it('should enrich event with Kit userAttributes before sending to event stream', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.userAttributes = { - firstName: 'John', - lastName: 'Doe', - }; - - (window as any).mParticle.forwarder.process({ - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].UserAttributes).toEqual({ - firstName: 'John', - lastName: 'Doe', - }); - }); - - it('should override event UserAttributes with Kit userAttributes', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.userAttributes = { - firstName: 'Jane', - }; - - (window as any).mParticle.forwarder.process({ - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - UserAttributes: { - firstName: 'Stale', - obsoleteAttr: 'should-not-appear', - }, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].UserAttributes).toEqual({ - firstName: 'Jane', - }); - }); - - it('should not mutate the original event when enriching with userAttributes', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.userAttributes = { - firstName: 'John', - }; - - const originalEvent = { - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }; - - (window as any).mParticle.forwarder.process(originalEvent); - - expect(originalEvent).not.toHaveProperty('UserAttributes'); - expect(receivedEvents[0].UserAttributes).toEqual({ - firstName: 'John', - }); - }); - - it('should send empty UserAttributes when Kit has no userAttributes', async () => { - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.userAttributes = {}; - - (window as any).mParticle.forwarder.process({ - EventName: 'Test Event', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect(receivedEvents.length).toBe(1); - expect(receivedEvents[0].UserAttributes).toEqual({}); - }); - - it('should flush queued events in FIFO order when __event_stream__ becomes available', async () => { - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.process({ - EventName: 'Event A', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - (window as any).mParticle.forwarder.process({ - EventName: 'Event B', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(2); - - const receivedEvents: any[] = []; - (window as any).Rokt.__event_stream__ = function (event: any) { - receivedEvents.push(event); - }; - - (window as any).mParticle.forwarder.process({ - EventName: 'Event C', - EventCategory: EventType.Other, - EventDataType: MessageType.PageEvent, - }); - - expect(receivedEvents.length).toBe(3); - expect(receivedEvents[0].EventName).toBe('Event A'); - expect(receivedEvents[1].EventName).toBe('Event B'); - expect(receivedEvents[2].EventName).toBe('Event C'); - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(0); - }); - }); - - describe('#processBatch', () => { - let mockBatch: any; + describe('#processBatch', () => { + let mockBatch: any; beforeEach(() => { (window as any).mParticle.forwarder.batchQueue = []; @@ -5212,8 +4396,6 @@ describe('Rokt Forwarder', () => { delete (window as any).Rokt.__batch_stream__; (window as any).mParticle.forwarder.batchQueue = []; (window as any).mParticle.forwarder.batchStreamQueue = []; - (window as any).mParticle.forwarder.eventQueue = []; - (window as any).mParticle.forwarder.eventStreamQueue = []; (window as any).mParticle.forwarder.isInitialized = false; (window as any).mParticle.Rokt.attachKitCalled = false; }); @@ -5368,18 +4550,6 @@ describe('Rokt Forwarder', () => { expect(receivedBatches[2].mpid).toBe('mpid-C'); expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(0); }); - - it('should not contaminate eventStreamQueue when batches are queued', async () => { - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.processBatch(mockBatch); - (window as any).mParticle.forwarder.processBatch(mockBatch); - - expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(2); - expect((window as any).mParticle.forwarder.eventStreamQueue.length).toBe(0); - }); }); describe('#_setRoktSessionId', () => { From 37c6b5f4ba5986a2bcc631b65bad09ad45e51994 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 18:16:12 -0400 Subject: [PATCH 7/9] feat: removed MParticleEvent and update tests --- src/Rokt-Kit.ts | 20 +++++--------------- test/src/tests.spec.ts | 42 ++---------------------------------------- 2 files changed, 7 insertions(+), 55 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index c2178a2..049fcea 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -156,14 +156,6 @@ interface ForwarderRegistration { getId: () => number; } -interface MParticleEvent { - EventDataType: number; - EventCategory: number; - EventName?: string; - EventAttributes?: Record; - [key: string]: unknown; -} - interface ReportingConfig { loggingUrl?: string; errorUrl?: string; @@ -618,7 +610,7 @@ class RoktKit implements KitInterface { // ---- Private helpers ---- - private getEventAttributeValue(event: MParticleEvent, eventAttributeKey: string): unknown { + private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown { const attributes = event && event.EventAttributes; if (!attributes) { return null; @@ -658,7 +650,7 @@ class RoktKit implements KitInterface { return false; } - private doesEventMatchRule(event: MParticleEvent, rule: PlacementEventRule): boolean { + private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean { if (!rule || !isString(rule.eventAttributeKey)) { return false; } @@ -682,7 +674,7 @@ class RoktKit implements KitInterface { return true; } - private applyPlacementEventAttributeMapping(event: MParticleEvent): void { + private applyPlacementEventAttributeMapping(event: SDKEvent): void { const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup); for (let i = 0; i < mappedAttributeKeys.length; i++) { const mappedAttributeKey = mappedAttributeKeys[i]; @@ -1035,15 +1027,13 @@ class RoktKit implements KitInterface { if (!this.isKitReady()) { return 'Kit not ready for forwarder: ' + name; } - const mpEvent = event as unknown as MParticleEvent; - if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (!isEmpty(this.placementEventAttributeMappingLookup)) { - this.applyPlacementEventAttributeMapping(mpEvent); + this.applyPlacementEventAttributeMapping(event); } if (!isEmpty(this.placementEventMappingLookup)) { - const hashedEvent = hashEventMessage(mpEvent.EventDataType, mpEvent.EventCategory, mpEvent.EventName ?? ''); + const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? ''); if (this.placementEventMappingLookup[String(hashedEvent)]) { mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true); } diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 53a65c2..ab26019 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -1,6 +1,7 @@ import packageJson from '../../package.json'; const packageVersion = packageJson.version; import '../../src/Rokt-Kit'; +import { Batch } from '@mparticle/web-sdk/internal'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -4334,7 +4335,7 @@ describe('Rokt Forwarder', () => { }); describe('#processBatch', () => { - let mockBatch: any; + let mockBatch: Batch; beforeEach(() => { (window as any).mParticle.forwarder.batchQueue = []; @@ -4419,45 +4420,6 @@ describe('Rokt Forwarder', () => { expect(receivedBatches[0].events.length).toBe(1); }); - it('should forward batch including non-event types (UserIdentityChange, UserAttributeChange)', async () => { - const receivedBatches: any[] = []; - (window as any).Rokt.__batch_stream__ = function (payload: any) { - receivedBatches.push(payload); - }; - - await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - const batchWithNonEvents = { - mpid: 'test-mpid-456', - user_attributes: { age: '30' }, - user_identities: { email: 'user@example.com' }, - events: [ - { event_type: 'custom_event', data: { event_name: 'Page Viewed', custom_event_type: 'navigation' } }, - { - event_type: 'user_identity_change', - data: { - new: { identity_type: 'email', identity: 'user@example.com', created_this_batch: true }, - old: { identity_type: 'email', identity: null, created_this_batch: false }, - }, - }, - { - event_type: 'user_attribute_change', - data: { user_attribute_name: 'age', new: '30', old: null, deleted: false, is_new_attribute: true }, - }, - ], - }; - - (window as any).mParticle.forwarder.processBatch(batchWithNonEvents); - - expect(receivedBatches.length).toBe(1); - expect(receivedBatches[0].events.length).toBe(3); - expect(receivedBatches[0].events[0].event_type).toBe('custom_event'); - expect(receivedBatches[0].events[1].event_type).toBe('user_identity_change'); - expect(receivedBatches[0].events[2].event_type).toBe('user_attribute_change'); - }); - it('should queue batch in batchQueue when kit is not initialized', () => { (window as any).mParticle.forwarder.isInitialized = false; (window as any).mParticle.forwarder.launcher = null; From 2ecdd6dcb1f1068feb8fe54bf3d63588ac1b6180 Mon Sep 17 00:00:00 2001 From: Khushi Patel Date: Wed, 8 Apr 2026 19:58:19 -0400 Subject: [PATCH 8/9] feat: restore sending non-batch events through another array --- package-lock.json | 4 +- package.json | 1 + src/Rokt-Kit.ts | 52 +++++++++++++++- test/src/tests.spec.ts | 135 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 567c678..983f9bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.23.0", + "@mparticle/event-models": "^1.1.9", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", @@ -1270,8 +1271,7 @@ "version": "1.1.9", "resolved": "https://registry.npmjs.org/@mparticle/event-models/-/event-models-1.1.9.tgz", "integrity": "sha512-2ucTwTKKA4xZjcSMlkim3rvr6d8uyi9yWDqOW8b9RDah2ak+dI0PkWANHkMJqteQprEg9mOkSvxc17kv5adIIA==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@mparticle/web-sdk": { "version": "2.62.0", diff --git a/package.json b/package.json index 8ab4ee5..b1ec5d2 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "devDependencies": { "@eslint/js": "^9.23.0", "@eslint/eslintrc": "^3.3.1", + "@mparticle/event-models": "^1.1.9", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 049fcea..6f9171f 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -17,6 +17,8 @@ // ============================================================ import { Batch, KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal'; +// BaseEvent not re-exported from @mparticle/web-sdk/internal, so we import directly from @mparticle/event-models. +import { BaseEvent } from '@mparticle/event-models'; interface RoktKitSettings { accountId: string; @@ -77,6 +79,8 @@ interface RoktGlobal { setExtensionData(data: Record): void; } +// TODO: getMPID and getUserIdentities exist on the User base type but are not re-exported from +// @mparticle/web-sdk/internal, so we redeclare them here until the internal types expose them. interface FilteredUser extends IMParticleUser { getMPID(): string; getUserIdentities?: () => { userIdentities: Record }; @@ -196,6 +200,7 @@ const moduleId = 181; const EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; const ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; const INIT_LOG_SAMPLING_RATE = 0.1; +const MESSAGE_TYPE_PROFILE = 14; // ============================================================ // Reporting service constants @@ -599,6 +604,7 @@ class RoktKit implements KitInterface { public placementEventAttributeMappingLookup: Record = {}; public batchQueue: Batch[] = []; public batchStreamQueue: Batch[] = []; + public pendingIdentityEvents: BaseEvent[] = []; public integrationName: string | null = null; public domain?: string; public errorReportingService: ErrorReportingService | null = null; @@ -753,6 +759,40 @@ class RoktKit implements KitInterface { mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record); } + private buildIdentityEvent(eventName: string, filteredUser: FilteredUser): BaseEvent { + const mpid = filteredUser.getMPID(); + const sessionId = + mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function' + ? mp().sessionManager!.getSession() + : null; + const userIdentities = + filteredUser.getUserIdentities && typeof filteredUser.getUserIdentities === 'function' + ? filteredUser.getUserIdentities().userIdentities + : null; + + return { + EventName: eventName, + EventDataType: MESSAGE_TYPE_PROFILE, + EventCategory: 0, + Timestamp: Date.now(), + MPID: mpid, + SessionId: sessionId, + UserIdentities: userIdentities, + } as unknown as BaseEvent; + } + + private mergePendingIdentityEvents(batch: Batch): Batch { + if (this.pendingIdentityEvents.length === 0) { + return batch; + } + const merged: Batch = { + ...batch, + events: [...(batch.events ?? []), ...this.pendingIdentityEvents], + }; + this.pendingIdentityEvents = []; + return merged; + } + private drainBatchQueue(): void { this.batchQueue.forEach((batch) => { this.processBatch(batch); @@ -765,7 +805,7 @@ class RoktKit implements KitInterface { this.batchQueue.push(batch); return 'Batch queued for forwarder: ' + name; } - this.sendBatchStream(batch); + this.sendBatchStream(this.mergePendingIdentityEvents(batch)); return 'Successfully sent batch to forwarder: ' + name; } @@ -1063,23 +1103,31 @@ class RoktKit implements KitInterface { } public onUserIdentified(user: IMParticleUser): string { - this.filters.filteredUser = user as FilteredUser; + const filteredUser = user as FilteredUser; + this.filters.filteredUser = filteredUser; this.userAttributes = user.getAllUserAttributes(); + this.pendingIdentityEvents.push(this.buildIdentityEvent('identify', filteredUser)); return 'Successfully called onUserIdentified for forwarder: ' + name; } public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { + const filteredUser = user as FilteredUser; this.userAttributes = user.getAllUserAttributes(); + this.pendingIdentityEvents.push(this.buildIdentityEvent('login', filteredUser)); return 'Successfully called onLoginComplete for forwarder: ' + name; } public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { + const filteredUser = user as FilteredUser; this.userAttributes = user.getAllUserAttributes(); + this.pendingIdentityEvents.push(this.buildIdentityEvent('logout', filteredUser)); return 'Successfully called onLogoutComplete for forwarder: ' + name; } public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string { + const filteredUser = user as FilteredUser; this.userAttributes = user.getAllUserAttributes(); + this.pendingIdentityEvents.push(this.buildIdentityEvent('modify', filteredUser)); return 'Successfully called onModifyComplete for forwarder: ' + name; } diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index ab26019..379285d 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -2978,6 +2978,9 @@ describe('Rokt Forwarder', () => { getAllUserAttributes: function () { return { 'user-attr': 'user-value' }; }, + getMPID: function () { + return '123'; + }, }); expect((window as any).mParticle.forwarder.userAttributes).toEqual({ @@ -2992,6 +2995,9 @@ describe('Rokt Forwarder', () => { getAllUserAttributes: function () { return { 'remaining-attr': 'some-value' }; }, + getMPID: function () { + return '123'; + }, }); expect((window as any).mParticle.forwarder.userAttributes).toEqual({ @@ -4340,6 +4346,7 @@ describe('Rokt Forwarder', () => { beforeEach(() => { (window as any).mParticle.forwarder.batchQueue = []; (window as any).mParticle.forwarder.batchStreamQueue = []; + (window as any).mParticle.forwarder.pendingIdentityEvents = []; (window as any).Rokt = new (MockRoktForwarder as any)(); (window as any).Rokt.createLauncher = async function () { return Promise.resolve({ @@ -4397,6 +4404,7 @@ describe('Rokt Forwarder', () => { delete (window as any).Rokt.__batch_stream__; (window as any).mParticle.forwarder.batchQueue = []; (window as any).mParticle.forwarder.batchStreamQueue = []; + (window as any).mParticle.forwarder.pendingIdentityEvents = []; (window as any).mParticle.forwarder.isInitialized = false; (window as any).mParticle.Rokt.attachKitCalled = false; }); @@ -4420,6 +4428,24 @@ describe('Rokt Forwarder', () => { expect(receivedBatches[0].events.length).toBe(1); }); + it('should not add extra events when pendingIdentityEvents is empty', async () => { + const receivedBatches: any[] = []; + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + expect((window as any).mParticle.forwarder.pendingIdentityEvents.length).toBe(0); + + (window as any).mParticle.forwarder.processBatch(mockBatch); + + expect(receivedBatches.length).toBe(1); + expect(receivedBatches[0].events.length).toBe(1); + expect(receivedBatches[0].events[0].event_type).toBe('custom_event'); + }); + it('should queue batch in batchQueue when kit is not initialized', () => { (window as any).mParticle.forwarder.isInitialized = false; (window as any).mParticle.forwarder.launcher = null; @@ -4512,6 +4538,115 @@ describe('Rokt Forwarder', () => { expect(receivedBatches[2].mpid).toBe('mpid-C'); expect((window as any).mParticle.forwarder.batchStreamQueue.length).toBe(0); }); + + it('should add an identity event to pendingIdentityEvents on onLoginComplete', () => { + const mockUser = { + getMPID: () => '123', + getAllUserAttributes: () => ({}), + getUserIdentities: () => ({ userIdentities: {} }), + }; + + (window as any).mParticle.forwarder.onLoginComplete(mockUser, null); + + const pending = (window as any).mParticle.forwarder.pendingIdentityEvents; + expect(pending.length).toBe(1); + expect(pending[0].EventName).toBe('login'); + expect(pending[0].EventDataType).toBe(14); + }); + + it('should add an identity event to pendingIdentityEvents on onLogoutComplete', () => { + const mockUser = { + getMPID: () => '123', + getAllUserAttributes: () => ({}), + getUserIdentities: () => ({ userIdentities: {} }), + }; + + (window as any).mParticle.forwarder.onLogoutComplete(mockUser, null); + + const pending = (window as any).mParticle.forwarder.pendingIdentityEvents; + expect(pending.length).toBe(1); + expect(pending[0].EventName).toBe('logout'); + expect(pending[0].EventDataType).toBe(14); + }); + + it('should add identity events to pendingIdentityEvents on onModifyComplete and onUserIdentified', () => { + const mockUser = { + getMPID: () => '42', + getAllUserAttributes: () => ({}), + getUserIdentities: () => ({ userIdentities: {} }), + }; + + (window as any).mParticle.forwarder.onModifyComplete(mockUser, null); + (window as any).mParticle.forwarder.onUserIdentified(mockUser); + + const pending = (window as any).mParticle.forwarder.pendingIdentityEvents; + expect(pending.length).toBe(2); + expect(pending[0].EventName).toBe('modify'); + expect(pending[1].EventName).toBe('identify'); + }); + + it('should merge pendingIdentityEvents into the outgoing batch and clear the queue', async () => { + const receivedBatches: any[] = []; + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + const mockUser = { + getMPID: () => '123', + getAllUserAttributes: () => ({}), + getUserIdentities: () => ({ userIdentities: {} }), + }; + + (window as any).mParticle.forwarder.onLoginComplete(mockUser, null); + expect((window as any).mParticle.forwarder.pendingIdentityEvents.length).toBe(1); + + (window as any).mParticle.forwarder.processBatch(mockBatch); + + expect(receivedBatches.length).toBe(1); + // Original 1 custom_event + 1 identity event from onLoginComplete + expect(receivedBatches[0].events.length).toBe(2); + expect(receivedBatches[0].events[1].EventName).toBe('login'); + expect(receivedBatches[0].events[1].EventDataType).toBe(14); + // Queue should be cleared after flush + expect((window as any).mParticle.forwarder.pendingIdentityEvents.length).toBe(0); + }); + + it('should merge pendingIdentityEvents into the first queued batch when kit becomes ready', async () => { + const receivedBatches: any[] = []; + (window as any).Rokt.__batch_stream__ = function (payload: any) { + receivedBatches.push(payload); + }; + + // Queue a batch before the kit initialises + (window as any).mParticle.forwarder.isInitialized = false; + (window as any).mParticle.forwarder.launcher = null; + + const mockUser = { + getMPID: () => '123', + getAllUserAttributes: () => ({}), + getUserIdentities: () => ({ userIdentities: {} }), + }; + + // Identity callback fires before kit is ready + (window as any).mParticle.forwarder.onLoginComplete(mockUser, null); + (window as any).mParticle.forwarder.processBatch(mockBatch); + + expect((window as any).mParticle.forwarder.batchQueue.length).toBe(1); + expect((window as any).mParticle.forwarder.pendingIdentityEvents.length).toBe(1); + + await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {}); + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + // The queued batch should have the pending identity event merged in + expect(receivedBatches.length).toBe(1); + expect(receivedBatches[0].events.length).toBe(2); + expect(receivedBatches[0].events[1].EventDataType).toBe(14); + expect((window as any).mParticle.forwarder.pendingIdentityEvents.length).toBe(0); + expect((window as any).mParticle.forwarder.batchQueue.length).toBe(0); + }); }); describe('#_setRoktSessionId', () => { From 38333e1499feb1ed369747af7d9c489e91ff8aa2 Mon Sep 17 00:00:00 2001 From: Khushi Patel <146786769+khushi1033@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:28:26 -0400 Subject: [PATCH 9/9] Update src/Rokt-Kit.ts Co-authored-by: James Newman --- src/Rokt-Kit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 6f9171f..6970ca9 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -200,7 +200,7 @@ const moduleId = 181; const EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; const ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; const INIT_LOG_SAMPLING_RATE = 0.1; -const MESSAGE_TYPE_PROFILE = 14; +const MESSAGE_TYPE_PROFILE = 14; // mParticle MessageType.Profile // ============================================================ // Reporting service constants