From ec78fab81c43952d2b3c44ed6f9ca09654c6028a Mon Sep 17 00:00:00 2001 From: Cris Ryan Tan Date: Thu, 19 Mar 2026 14:21:47 +1100 Subject: [PATCH 1/5] feat: sync Rokt session ID to mParticle via setIntegrationAttribute After selectPlacements resolves, read the session ID from the Selection context and subscribe to SESSION_ID_UPDATED events. The session ID is forwarded to mParticle via setIntegrationAttribute(181, { roktSessionId }) so server-side integrations can link events back to Rokt sessions. Co-Authored-By: Claude Opus 4.6 (1M context) --- dist/Rokt-Kit.common.js | 42 ++++++- dist/Rokt-Kit.iife.js | 42 ++++++- src/Rokt-Kit.js | 42 ++++++- test/src/tests.js | 246 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 3 deletions(-) diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js index 04f0178..89b86eb 100644 --- a/dist/Rokt-Kit.common.js +++ b/dist/Rokt-Kit.common.js @@ -419,7 +419,28 @@ var constructor = function () { // Log custom event for selectPlacements call logSelectPlacementsEvent(selectPlacementsAttributes); - return self.launcher.selectPlacements(selectPlacementsOptions); + var selection = self.launcher.selectPlacements(selectPlacementsOptions); + + if (selection && typeof selection.then === 'function') { + selection + .then(function (sel) { + if (sel && sel.context && sel.context.sessionId) { + sel.context.sessionId + .then(_setRoktSessionId) + .catch(function () {}); + } + if (sel && typeof sel.on === 'function') { + sel.on('SESSION_ID_UPDATED').subscribe(function ( + event + ) { + _setRoktSessionId(event.body); + }); + } + }) + .catch(function () {}); + } + + return selection; } /** @@ -529,6 +550,25 @@ var constructor = function () { } } + function _setRoktSessionId(sessionId) { + if (!sessionId || typeof sessionId !== 'string') { + return; + } + try { + var mpInstance = window.mParticle.getInstance(); + if ( + mpInstance && + typeof mpInstance.setIntegrationAttribute === 'function' + ) { + mpInstance.setIntegrationAttribute(moduleId, { + roktSessionId: sessionId, + }); + } + } catch (e) { + // Best effort — never let this break the partner page + } + } + function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js index d357be0..2271f55 100644 --- a/dist/Rokt-Kit.iife.js +++ b/dist/Rokt-Kit.iife.js @@ -418,7 +418,28 @@ var RoktKit = (function (exports) { // Log custom event for selectPlacements call logSelectPlacementsEvent(selectPlacementsAttributes); - return self.launcher.selectPlacements(selectPlacementsOptions); + var selection = self.launcher.selectPlacements(selectPlacementsOptions); + + if (selection && typeof selection.then === 'function') { + selection + .then(function (sel) { + if (sel && sel.context && sel.context.sessionId) { + sel.context.sessionId + .then(_setRoktSessionId) + .catch(function () {}); + } + if (sel && typeof sel.on === 'function') { + sel.on('SESSION_ID_UPDATED').subscribe(function ( + event + ) { + _setRoktSessionId(event.body); + }); + } + }) + .catch(function () {}); + } + + return selection; } /** @@ -528,6 +549,25 @@ var RoktKit = (function (exports) { } } + function _setRoktSessionId(sessionId) { + if (!sessionId || typeof sessionId !== 'string') { + return; + } + try { + var mpInstance = window.mParticle.getInstance(); + if ( + mpInstance && + typeof mpInstance.setIntegrationAttribute === 'function' + ) { + mpInstance.setIntegrationAttribute(moduleId, { + roktSessionId: sessionId, + }); + } + } catch (e) { + // Best effort — never let this break the partner page + } + } + function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index b1574fd..86293f9 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -415,7 +415,28 @@ var constructor = function () { // Log custom event for selectPlacements call logSelectPlacementsEvent(selectPlacementsAttributes); - return self.launcher.selectPlacements(selectPlacementsOptions); + var selection = self.launcher.selectPlacements(selectPlacementsOptions); + + if (selection && typeof selection.then === 'function') { + selection + .then(function (sel) { + if (sel && sel.context && sel.context.sessionId) { + sel.context.sessionId + .then(_setRoktSessionId) + .catch(function () {}); + } + if (sel && typeof sel.on === 'function') { + sel.on('SESSION_ID_UPDATED').subscribe(function ( + event + ) { + _setRoktSessionId(event.body); + }); + } + }) + .catch(function () {}); + } + + return selection; } /** @@ -525,6 +546,25 @@ var constructor = function () { } } + function _setRoktSessionId(sessionId) { + if (!sessionId || typeof sessionId !== 'string') { + return; + } + try { + var mpInstance = window.mParticle.getInstance(); + if ( + mpInstance && + typeof mpInstance.setIntegrationAttribute === 'function' + ) { + mpInstance.setIntegrationAttribute(moduleId, { + roktSessionId: sessionId, + }); + } + } catch (e) { + // Best effort — never let this break the partner page + } + } + function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); diff --git a/test/src/tests.js b/test/src/tests.js index 2d05873..4d109e1 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -4581,6 +4581,252 @@ describe('Rokt Forwarder', () => { }); }); + describe('#_setRoktSessionId', () => { + var setIntegrationAttributeCalls; + + beforeEach(() => { + setIntegrationAttributeCalls = []; + window.Rokt = new MockRoktForwarder(); + window.mParticle.Rokt = window.Rokt; + window.mParticle.Rokt.attachKitCalled = false; + window.mParticle.Rokt.attachKit = async (kit) => { + window.mParticle.Rokt.attachKitCalled = true; + window.mParticle.Rokt.kit = kit; + Promise.resolve(); + }; + window.mParticle.Rokt.setLocalSessionAttribute = function ( + key, + value + ) { + window.mParticle._Store.localSessionAttributes[key] = value; + }; + window.mParticle.Rokt.getLocalSessionAttributes = function () { + return window.mParticle._Store.localSessionAttributes; + }; + window.mParticle.Rokt.filters = { + userAttributeFilters: [], + filterUserAttributes: function (attributes) { + return attributes; + }, + filteredUser: { + getMPID: function () { + return '123'; + }, + }, + }; + window.mParticle.getInstance = function () { + return { + setIntegrationAttribute: function (id, attrs) { + setIntegrationAttributeCalls.push({ + id: id, + attrs: attrs, + }); + }, + }; + }; + }); + + afterEach(() => { + delete window.mParticle.getInstance; + window.mParticle.forwarder.isInitialized = false; + window.mParticle.Rokt.attachKitCalled = false; + }); + + function createMockSelection(sessionId, onSubscribers) { + return { + context: { + sessionId: sessionId + ? Promise.resolve(sessionId) + : Promise.resolve(''), + }, + on: function (eventType) { + return { + subscribe: function (callback) { + if (onSubscribers) { + onSubscribers.push({ + eventType: eventType, + callback: callback, + }); + } + }, + }; + }, + }; + } + + function setupLauncherWithSelection(mockSelection) { + window.Rokt.createLauncher = async function (_options) { + return Promise.resolve({ + selectPlacements: function () { + return Promise.resolve(mockSelection); + }, + }); + }; + } + + it('should set integration attribute when session ID is available via context', async () => { + var mockSelection = createMockSelection('rokt-session-abc'); + setupLauncherWithSelection(mockSelection); + + await window.mParticle.forwarder.init( + { accountId: '123456' }, + reportService.cb, + true, + null, + {} + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: {}, + }); + + await waitForCondition( + () => setIntegrationAttributeCalls.length > 0 + ); + + setIntegrationAttributeCalls.length.should.equal(1); + setIntegrationAttributeCalls[0].id.should.equal(181); + setIntegrationAttributeCalls[0].attrs.should.deepEqual({ + roktSessionId: 'rokt-session-abc', + }); + }); + + it('should subscribe to SESSION_ID_UPDATED and update attribute on change', async () => { + var subscribers = []; + var mockSelection = createMockSelection( + 'rokt-session-initial', + subscribers + ); + setupLauncherWithSelection(mockSelection); + + await window.mParticle.forwarder.init( + { accountId: '123456' }, + reportService.cb, + true, + null, + {} + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: {}, + }); + + await waitForCondition(() => subscribers.length > 0); + + var sessionUpdatedSubscriber = subscribers.find( + (s) => s.eventType === 'SESSION_ID_UPDATED' + ); + sessionUpdatedSubscriber.should.not.be.undefined(); + + sessionUpdatedSubscriber.callback({ + body: 'rokt-session-refreshed', + }); + + await waitForCondition( + () => setIntegrationAttributeCalls.length >= 2 + ); + + var lastCall = + setIntegrationAttributeCalls[ + setIntegrationAttributeCalls.length - 1 + ]; + lastCall.id.should.equal(181); + lastCall.attrs.should.deepEqual({ + roktSessionId: 'rokt-session-refreshed', + }); + }); + + it('should not set integration attribute when session ID is empty', async () => { + var mockSelection = createMockSelection(''); + setupLauncherWithSelection(mockSelection); + + await window.mParticle.forwarder.init( + { accountId: '123456' }, + reportService.cb, + true, + null, + {} + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: {}, + }); + + // Give time for any async operations to settle + await new Promise((resolve) => setTimeout(resolve, 50)); + + setIntegrationAttributeCalls.length.should.equal(0); + }); + + it('should not throw when mParticle.getInstance is unavailable', async () => { + var mockSelection = createMockSelection('rokt-session-abc'); + setupLauncherWithSelection(mockSelection); + delete window.mParticle.getInstance; + + await window.mParticle.forwarder.init( + { accountId: '123456' }, + reportService.cb, + true, + null, + {} + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + // Should not throw + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: {}, + }); + + // Give time for async operations + await new Promise((resolve) => setTimeout(resolve, 50)); + + setIntegrationAttributeCalls.length.should.equal(0); + }); + + it('should still return the selection promise to callers', async () => { + var mockSelection = createMockSelection('rokt-session-abc'); + setupLauncherWithSelection(mockSelection); + + await window.mParticle.forwarder.init( + { accountId: '123456' }, + reportService.cb, + true, + null, + {} + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + var result = await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: {}, + }); + + result.should.equal(mockSelection); + }); + }); + describe('#parseSettingsString', () => { it('should parse null values in a settings string appropriately', () => { const settingsString = From 35bee64c14113338d24c327cbf51c951376111c9 Mon Sep 17 00:00:00 2001 From: Cris Ryan Tan Date: Fri, 20 Mar 2026 12:17:00 +1100 Subject: [PATCH 2/5] fix: address PR review feedback - Remove redundant SESSION_ID_UPDATED subscription (context.sessionId already captures on each selection) - Move logSelectPlacementsEvent after session ID is set to guarantee server receives sessionId - Remove dist files from tracking (release process builds these) - Remove "still" from test name Co-Authored-By: Claude Opus 4.6 (1M context) --- dist/Rokt-Kit.common.js | 916 --------------------------------------- dist/Rokt-Kit.iife.js | 919 ---------------------------------------- src/Rokt-Kit.js | 17 +- test/src/tests.js | 104 ++--- 4 files changed, 46 insertions(+), 1910 deletions(-) delete mode 100644 dist/Rokt-Kit.common.js delete mode 100644 dist/Rokt-Kit.iife.js diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js deleted file mode 100644 index 89b86eb..0000000 --- a/dist/Rokt-Kit.common.js +++ /dev/null @@ -1,916 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -/* eslint-disable no-undef */ -// Copyright 2025 mParticle, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -var name = 'Rokt'; -var moduleId = 181; -var EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; - -var constructor = function () { - var self = this; - var PerformanceMarks = { - RoktScriptAppended: 'mp:RoktScriptAppended', - }; - - var EMAIL_SHA256_KEY = 'emailsha256'; - - // Dynamic identity type for Rokt's emailsha256 identity value which MP doesn't natively support - will be set during initialization - var mappedEmailSha256Key; - - self.name = name; - self.moduleId = moduleId; - self.isInitialized = false; - - self.launcher = null; - self.filters = {}; - self.userAttributes = {}; - self.testHelpers = null; - self.placementEventMappingLookup = {}; - self.placementEventAttributeMappingLookup = {}; - self.eventQueue = []; - self.integrationName = null; - - function getEventAttributeValue(event, eventAttributeKey) { - var attributes = event && event.EventAttributes; - if (!attributes) { - return null; - } - - if (typeof attributes[eventAttributeKey] === 'undefined') { - return null; - } - - return attributes[eventAttributeKey]; - } - - function doesEventAttributeConditionMatch(condition, actualValue) { - if (!condition || !isString(condition.operator)) { - return false; - } - - var operator = condition.operator.toLowerCase(); - var expectedValue = condition.attributeValue; - - if (operator === 'exists') { - return actualValue !== null; - } - - if (actualValue == null) { - return false; - } - - if (operator === 'equals') { - return String(actualValue) === String(expectedValue); - } - - if (operator === 'contains') { - return String(actualValue).indexOf(String(expectedValue)) !== -1; - } - - return false; - } - - function doesEventMatchRule(event, rule) { - if (!rule || !isString(rule.eventAttributeKey)) { - return false; - } - - var conditions = rule.conditions; - if (!Array.isArray(conditions)) { - return false; - } - - var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); - - if (conditions.length === 0) { - return actualValue !== null; - } - for (var i = 0; i < conditions.length; i++) { - if (!doesEventAttributeConditionMatch(conditions[i], actualValue)) { - return false; - } - } - - return true; - } - - function generateMappedEventAttributeLookup( - placementEventAttributeMapping - ) { - var mappedAttributeKeys = {}; - if (!Array.isArray(placementEventAttributeMapping)) { - return mappedAttributeKeys; - } - for (var i = 0; i < placementEventAttributeMapping.length; i++) { - var mapping = placementEventAttributeMapping[i]; - if ( - !mapping || - !isString(mapping.value) || - !isString(mapping.map) - ) { - continue; - } - - var mappedAttributeKey = mapping.value; - var eventAttributeKey = mapping.map; - - if (!mappedAttributeKeys[mappedAttributeKey]) { - mappedAttributeKeys[mappedAttributeKey] = []; - } - - mappedAttributeKeys[mappedAttributeKey].push({ - eventAttributeKey: eventAttributeKey, - conditions: Array.isArray(mapping.conditions) - ? mapping.conditions - : [], - }); - } - return mappedAttributeKeys; - } - - function applyPlacementEventAttributeMapping(event) { - var mappedAttributeKeys = Object.keys( - self.placementEventAttributeMappingLookup - ); - for (var i = 0; i < mappedAttributeKeys.length; i++) { - var mappedAttributeKey = mappedAttributeKeys[i]; - var rulesForMappedAttributeKey = - self.placementEventAttributeMappingLookup[mappedAttributeKey]; - if (isEmpty(rulesForMappedAttributeKey)) { - continue; - } - - // Require ALL rules for the same key to match (AND). - var allMatch = true; - for (var j = 0; j < rulesForMappedAttributeKey.length; j++) { - if (!doesEventMatchRule(event, rulesForMappedAttributeKey[j])) { - allMatch = false; - break; - } - } - if (!allMatch) { - continue; - } - - window.mParticle.Rokt.setLocalSessionAttribute( - mappedAttributeKey, - true - ); - } - } - - /** - * Generates the Rokt launcher script URL with optional domain override and extensions - * @param {string} domain - The CNAME domain to use for overriding the launcher url - * @param {Array} extensions - List of extension query parameters to append - * @returns {string} The complete launcher script URL - */ - function generateLauncherScript(_domain, extensions) { - // Override domain if a customer is using a CNAME - // If a customer is using a CNAME, a domain will be passed. If not, we use the default domain. - var domain = typeof _domain !== 'undefined' ? _domain : 'apps.rokt.com'; - var protocol = 'https://'; - var launcherPath = '/wsdk/integrations/launcher.js'; - var baseUrl = [protocol, domain, launcherPath].join(''); - - if (!extensions || extensions.length === 0) { - return baseUrl; - } - return baseUrl + '?extensions=' + extensions.join(','); - } - - /** - * Checks if Rokt launcher is available and ready to attach - * @returns {boolean} True if launcher can be attached - */ - function isLauncherReadyToAttach() { - return window.Rokt && typeof window.Rokt.createLauncher === 'function'; - } - - /** - * Passes attributes to the Rokt Web SDK for client-side hashing - * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes - * @param {Object} attributes - The attributes to be hashed - * @returns {Promise} A Promise resolving to the - * hashed attributes from the launcher, or `null` if the kit is not initialized - */ - function hashAttributes(attributes) { - if (!isKitReady()) { - console.error('Rokt Kit: Not initialized'); - return null; - } - return self.launcher.hashAttributes(attributes); - } - - function initForwarder( - settings, - _service, - testMode, - _trackerId, - filteredUserAttributes - ) { - var accountId = settings.accountId; - var roktExtensions = extractRoktExtensions(settings.roktExtensions); - self.userAttributes = filteredUserAttributes || {}; - self.onboardingExpProvider = settings.onboardingExpProvider; - - var placementEventMapping = parseSettingsString( - settings.placementEventMapping - ); - self.placementEventMappingLookup = generateMappedEventLookup( - placementEventMapping - ); - - var placementEventAttributeMapping = parseSettingsString( - settings.placementEventAttributeMapping - ); - self.placementEventAttributeMappingLookup = - generateMappedEventAttributeLookup(placementEventAttributeMapping); - - // Set dynamic OTHER_IDENTITY based on server settings - // Convert to lowercase since server sends TitleCase (e.g., 'Other' -> 'other') - if (settings.hashedEmailUserIdentityType) { - mappedEmailSha256Key = - settings.hashedEmailUserIdentityType.toLowerCase(); - } - - var domain = window.mParticle.Rokt.domain; - var launcherOptions = mergeObjects( - {}, - window.mParticle.Rokt.launcherOptions || {} - ); - self.integrationName = generateIntegrationName( - launcherOptions.integrationName - ); - launcherOptions.integrationName = self.integrationName; - - self.domain = domain; - - if (testMode) { - self.testHelpers = { - generateLauncherScript: generateLauncherScript, - extractRoktExtensions: extractRoktExtensions, - hashEventMessage: hashEventMessage, - parseSettingsString: parseSettingsString, - generateMappedEventLookup: generateMappedEventLookup, - generateMappedEventAttributeLookup: - generateMappedEventAttributeLookup, - sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, - createAutoRemovedIframe: createAutoRemovedIframe, - djb2: djb2, - setAllowedOriginHash: function (hash) { - _allowedOriginHash = hash; - }, - }; - attachLauncher(accountId, launcherOptions); - return; - } - - if (isLauncherReadyToAttach()) { - attachLauncher(accountId, launcherOptions); - } else { - var target = document.head || document.body; - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = generateLauncherScript(domain, roktExtensions); - script.async = true; - script.crossOrigin = 'anonymous'; - script.fetchPriority = 'high'; - script.id = 'rokt-launcher'; - - script.onload = function () { - if (isLauncherReadyToAttach()) { - attachLauncher(accountId, launcherOptions); - } else { - console.error( - 'Rokt object is not available after script load.' - ); - } - }; - - script.onerror = function (error) { - console.error('Error loading Rokt launcher script:', error); - }; - - target.appendChild(script); - captureTiming(PerformanceMarks.RoktScriptAppended); - } - } - /** - * Returns the user identities from the filtered user, if any - * @param {Object} filteredUser - The filtered user object containing identities - * @returns {Object} The user identities from the filtered user - */ - function returnUserIdentities(filteredUser) { - if (!filteredUser || !filteredUser.getUserIdentities) { - return {}; - } - - var userIdentities = filteredUser.getUserIdentities().userIdentities; - - return replaceOtherIdentityWithEmailsha256(userIdentities); - } - - function returnLocalSessionAttributes() { - if ( - !window.mParticle.Rokt || - typeof window.mParticle.Rokt.getLocalSessionAttributes !== - 'function' - ) { - return {}; - } - if ( - isEmpty(self.placementEventMappingLookup) && - isEmpty(self.placementEventAttributeMappingLookup) - ) { - return {}; - } - return window.mParticle.Rokt.getLocalSessionAttributes(); - } - - function replaceOtherIdentityWithEmailsha256(userIdentities) { - var newUserIdentities = mergeObjects({}, userIdentities || {}); - if (userIdentities[mappedEmailSha256Key]) { - newUserIdentities[EMAIL_SHA256_KEY] = - userIdentities[mappedEmailSha256Key]; - } - delete newUserIdentities[mappedEmailSha256Key]; - - return newUserIdentities; - } - - /** - * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options - * @see https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ - * @param {Object} options - The options object for selecting placements containing: - * - identifier {string}: The placement identifier - * - attributes {Object}: Optional attributes to merge with existing attributes - * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes - */ - function selectPlacements(options) { - var attributes = (options && options.attributes) || {}; - var placementAttributes = mergeObjects(self.userAttributes, attributes); - - var filters = self.filters || {}; - var userAttributeFilters = filters.userAttributeFilters || []; - var filteredUser = filters.filteredUser || {}; - var mpid = - filteredUser && - filteredUser.getMPID && - typeof filteredUser.getMPID === 'function' - ? filteredUser.getMPID() - : null; - - var filteredAttributes; - - if (!filters) { - console.warn( - 'Rokt Kit: No filters available, using user attributes' - ); - - filteredAttributes = placementAttributes; - } else if (filters.filterUserAttributes) { - filteredAttributes = filters.filterUserAttributes( - placementAttributes, - userAttributeFilters - ); - } - - self.userAttributes = filteredAttributes; - - var optimizelyAttributes = - self.onboardingExpProvider === 'Optimizely' - ? fetchOptimizely() - : {}; - - var filteredUserIdentities = returnUserIdentities(filteredUser); - - var localSessionAttributes = returnLocalSessionAttributes(); - - var selectPlacementsAttributes = mergeObjects( - filteredUserIdentities, - filteredAttributes, - optimizelyAttributes, - localSessionAttributes, - { - mpid: mpid, - } - ); - - var selectPlacementsOptions = mergeObjects(options, { - attributes: selectPlacementsAttributes, - }); - - // Log custom event for selectPlacements call - logSelectPlacementsEvent(selectPlacementsAttributes); - - var selection = self.launcher.selectPlacements(selectPlacementsOptions); - - if (selection && typeof selection.then === 'function') { - selection - .then(function (sel) { - if (sel && sel.context && sel.context.sessionId) { - sel.context.sessionId - .then(_setRoktSessionId) - .catch(function () {}); - } - if (sel && typeof sel.on === 'function') { - sel.on('SESSION_ID_UPDATED').subscribe(function ( - event - ) { - _setRoktSessionId(event.body); - }); - } - }) - .catch(function () {}); - } - - return selection; - } - - /** - * Logs a custom event when selectPlacements is called - * This enables visibility and troubleshooting - * @param {Object} attributes - The attributes sent to Rokt - */ - function logSelectPlacementsEvent(attributes) { - if ( - !window.mParticle || - typeof window.mParticle.logEvent !== 'function' - ) { - return; - } - - if (!isObject(attributes)) { - return; - } - - var EVENT_TYPE_OTHER = window.mParticle.EventType.Other; - - window.mParticle.logEvent( - EVENT_NAME_SELECT_PLACEMENTS, - EVENT_TYPE_OTHER, - attributes - ); - } - - /** - * Enables optional Integration Launcher extensions before selecting placements - * @param {string} extensionName - Name of the extension to enable - * @returns {Promise<*>} A Promise resolving to the extension API if available - */ - function use(extensionName) { - if (!isKitReady()) { - console.error('Rokt Kit: Not initialized'); - return Promise.reject(new Error('Rokt Kit: Not initialized')); - } - if (!extensionName || !isString(extensionName)) { - return Promise.reject( - new Error('Rokt Kit: Invalid extension name') - ); - } - return self.launcher.use(extensionName); - } - - /** - * Sets extension data for Rokt Web SDK - * @param {Object} partnerExtensionData - The extension data object containing: - * - [extensionName] {string}: Name of the extension - * - [extensionName].options {Object}: Key-value pairs of options for the extension - * @returns {void} Nothing is returned - */ - function setExtensionData(partnerExtensionData) { - if (!isKitReady()) { - console.error('Rokt Kit: Not initialized'); - return; - } - - window.Rokt.setExtensionData(partnerExtensionData); - } - - function processEventQueue() { - self.eventQueue.forEach(function (event) { - processEvent(event); - }); - self.eventQueue = []; - } - - function processEvent(event) { - if (!isKitReady()) { - self.eventQueue.push(event); - return; - } - - _sendEventStream(event); - - if ( - typeof window.mParticle.Rokt.setLocalSessionAttribute !== 'function' - ) { - return; - } - - if (!isEmpty(self.placementEventAttributeMappingLookup)) { - applyPlacementEventAttributeMapping(event); - } - - if (isEmpty(self.placementEventMappingLookup)) { - return; - } - - var hashedEvent = hashEventMessage( - event.EventDataType, - event.EventCategory, - event.EventName - ); - - if (self.placementEventMappingLookup[hashedEvent]) { - var mappedValue = self.placementEventMappingLookup[hashedEvent]; - window.mParticle.Rokt.setLocalSessionAttribute(mappedValue, true); - } - } - - function _sendEventStream(event) { - if (window.Rokt && typeof window.Rokt.__event_stream__ === 'function') { - window.Rokt.__event_stream__(event); - } - } - - function _setRoktSessionId(sessionId) { - if (!sessionId || typeof sessionId !== 'string') { - return; - } - try { - var mpInstance = window.mParticle.getInstance(); - if ( - mpInstance && - typeof mpInstance.setIntegrationAttribute === 'function' - ) { - mpInstance.setIntegrationAttribute(moduleId, { - roktSessionId: sessionId, - }); - } - } catch (e) { - // Best effort — never let this break the partner page - } - } - - function onUserIdentified(filteredUser) { - self.filters.filteredUser = filteredUser; - self.userAttributes = filteredUser.getAllUserAttributes(); - } - - function setUserAttribute(key, value) { - self.userAttributes[key] = value; - } - - function removeUserAttribute(key) { - delete self.userAttributes[key]; - } - - function attachLauncher(accountId, launcherOptions) { - var options = mergeObjects( - { - accountId: accountId, - }, - launcherOptions || {} - ); - - if (isPartnerInLocalLauncherTestGroup()) { - var localLauncher = window.Rokt.createLocalLauncher(options); - initRoktLauncher(localLauncher); - } else { - window.Rokt.createLauncher(options) - .then(initRoktLauncher) - .catch(function (err) { - console.error('Error creating Rokt launcher:', err); - }); - } - } - - function initRoktLauncher(launcher) { - // Assign the launcher to a global variable for later access - window.Rokt.currentLauncher = launcher; - // Locally cache the launcher and filters - self.launcher = launcher; - - var roktFilters = window.mParticle.Rokt.filters; - - if (!roktFilters) { - console.warn('Rokt Kit: No filters have been set.'); - } else { - self.filters = roktFilters; - if (!roktFilters.filteredUser) { - console.warn('Rokt Kit: No filtered user has been set.'); - } - } - - // Kit must be initialized before attaching to the Rokt manager - self.isInitialized = true; - - sendAdBlockMeasurementSignals(self.domain, self.integrationName); - - // Attaches the kit to the Rokt manager - window.mParticle.Rokt.attachKit(self); - processEventQueue(); - } - - // mParticle Kit Callback Methods - function fetchOptimizely() { - var forwarders = window.mParticle - ._getActiveForwarders() - .filter(function (forwarder) { - return forwarder.name === 'Optimizely'; - }); - - try { - if (forwarders.length > 0 && window.optimizely) { - // Get the state object - var optimizelyState = window.optimizely.get('state'); - if ( - !optimizelyState || - !optimizelyState.getActiveExperimentIds - ) { - return {}; - } - // Get active experiment IDs - var activeExperimentIds = - optimizelyState.getActiveExperimentIds(); - // Get variations for each active experiment - var activeExperiments = activeExperimentIds.reduce(function ( - acc, - expId - ) { - acc[ - 'rokt.custom.optimizely.experiment.' + - expId + - '.variationId' - ] = optimizelyState.getVariationMap()[expId].id; - return acc; - }, - {}); - return activeExperiments; - } - } catch (error) { - console.error('Error fetching Optimizely attributes:', error); - } - return {}; - } - - // Called by the mParticle Rokt Manager - this.selectPlacements = selectPlacements; - this.hashAttributes = hashAttributes; - this.use = use; - - // Kit Callback Methods - this.init = initForwarder; - this.process = processEvent; - this.setExtensionData = setExtensionData; - this.setUserAttribute = setUserAttribute; - this.onUserIdentified = onUserIdentified; - this.removeUserAttribute = removeUserAttribute; - - /** - * Checks if the Rokt kit is ready to use. - * Both conditions must be true: - * 1. self.isInitialized - Set after successful initialization of the kit - * 2. self.launcher - The Rokt launcher instance must be available - * @returns {boolean} Whether the kit is ready for use - */ - function isKitReady() { - return !!(self.isInitialized && self.launcher); - } - - function isPartnerInLocalLauncherTestGroup() { - return ( - window.mParticle.config && - window.mParticle.config.isLocalLauncherEnabled && - _isAssignedToSampleGroup() - ); - } - - function _isAssignedToSampleGroup() { - var LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5; - return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD; - } - - function captureTiming(metricName) { - if ( - window && - window.mParticle && - window.mParticle.captureTiming && - metricName - ) { - window.mParticle.captureTiming(metricName); - } - } - - function createAutoRemovedIframe(src) { - var iframe = document.createElement('iframe'); - iframe.style.display = 'none'; - iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); - iframe.src = src; - iframe.onload = function () { - iframe.onload = null; - if (iframe.parentNode) { - iframe.parentNode.removeChild(iframe); - } - }; - var target = document.body || document.head; - if (target) { - target.appendChild(iframe); - } - } - - var ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; - var INIT_LOG_SAMPLING_RATE = 0.1; - var _allowedOriginHash = 1445747545; - - function djb2(str) { - var hash = 5381; - for (var i = 0; i < str.length; i++) { - hash = (hash << 5) + hash + str.charCodeAt(i); - hash = hash & hash; - } - return hash; - } - - function sendAdBlockMeasurementSignals(domain, version) { - if (djb2(window.location.origin) !== _allowedOriginHash) { - return; - } - - if (Math.random() >= INIT_LOG_SAMPLING_RATE) { - return; - } - - var guid = window.__rokt_li_guid__; - if (!guid) { - return; - } - - var pageUrl = window.location.href.split('?')[0].split('#')[0]; - var params = - 'version=' + - encodeURIComponent(version) + - '&launcherInstanceGuid=' + - encodeURIComponent(guid) + - '&pageUrl=' + - encodeURIComponent(pageUrl); - - var existingDomain = domain || 'apps.rokt.com'; - createAutoRemovedIframe( - 'https://' + existingDomain + '/v1/wsdk-init/index.html?' + params - ); - - createAutoRemovedIframe( - 'https://' + - ADBLOCK_CONTROL_DOMAIN + - '/v1/wsdk-init/index.html?' + - params + - '&isControl=true' - ); - } -}; - -function generateIntegrationName(customIntegrationName) { - var coreSdkVersion = window.mParticle.getVersion(); - var kitVersion = "1.17.0"; - var name = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion; - - if (customIntegrationName) { - name += '_' + customIntegrationName; - } - return name; -} - -function getId() { - return moduleId; -} - -function register(config) { - if (!config) { - window.console.log( - 'You must pass a config object to register the kit ' + name - ); - return; - } - if (!isObject(config)) { - window.console.log( - "'config' must be an object. You passed in a " + typeof config - ); - return; - } - - if (isObject(config.kits)) { - config.kits[name] = { - constructor: constructor, - }; - } else { - config.kits = {}; - config.kits[name] = { - constructor: constructor, - }; - } - window.console.log( - 'Successfully registered ' + name + ' to your mParticle configuration' - ); -} - -function isObject(val) { - return ( - val != null && typeof val === 'object' && Array.isArray(val) === false - ); -} - -function mergeObjects() { - var resObj = {}; - for (var i = 0; i < arguments.length; i += 1) { - var obj = arguments[i], - keys = Object.keys(obj); - for (var j = 0; j < keys.length; j += 1) { - resObj[keys[j]] = obj[keys[j]]; - } - } - return resObj; -} - -function parseSettingsString(settingsString) { - if (!settingsString) { - return []; - } - try { - return JSON.parse(settingsString.replace(/"/g, '"')); - } catch (error) { - console.error('Settings string contains invalid JSON'); - } - return []; -} - -function extractRoktExtensions(settingsString) { - var settings = settingsString ? parseSettingsString(settingsString) : []; - var roktExtensions = []; - - for (var i = 0; i < settings.length; i++) { - roktExtensions.push(settings[i].value); - } - - return roktExtensions; -} - -function generateMappedEventLookup(placementEventMapping) { - if (!placementEventMapping) { - return {}; - } - - var mappedEvents = {}; - for (var i = 0; i < placementEventMapping.length; i++) { - var mapping = placementEventMapping[i]; - mappedEvents[mapping.jsmap] = mapping.value; - } - return mappedEvents; -} - -function hashEventMessage(messageType, eventType, eventName) { - return window.mParticle.generateHash( - [messageType, eventType, eventName].join('') - ); -} - -function isEmpty(value) { - return value == null || !(Object.keys(value) || value).length; -} - -function isString(value) { - return typeof value === 'string'; -} - -if (window && window.mParticle && window.mParticle.addForwarder) { - window.mParticle.addForwarder({ - name: name, - constructor: constructor, - getId: getId, - }); -} - -var RoktKit = { - register: register, -}; -var RoktKit_1 = RoktKit.register; - -exports.default = RoktKit; -exports.register = RoktKit_1; diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js deleted file mode 100644 index 2271f55..0000000 --- a/dist/Rokt-Kit.iife.js +++ /dev/null @@ -1,919 +0,0 @@ -var RoktKit = (function (exports) { - 'use strict'; - - /* eslint-disable no-undef */ - // Copyright 2025 mParticle, Inc. - // - // Licensed under the Apache License, Version 2.0 (the "License"); - // you may not use this file except in compliance with the License. - // You may obtain a copy of the License at - // - // http://www.apache.org/licenses/LICENSE-2.0 - // - // Unless required by applicable law or agreed to in writing, software - // distributed under the License is distributed on an "AS IS" BASIS, - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - // See the License for the specific language governing permissions and - // limitations under the License. - - var name = 'Rokt'; - var moduleId = 181; - var EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; - - var constructor = function () { - var self = this; - var PerformanceMarks = { - RoktScriptAppended: 'mp:RoktScriptAppended', - }; - - var EMAIL_SHA256_KEY = 'emailsha256'; - - // Dynamic identity type for Rokt's emailsha256 identity value which MP doesn't natively support - will be set during initialization - var mappedEmailSha256Key; - - self.name = name; - self.moduleId = moduleId; - self.isInitialized = false; - - self.launcher = null; - self.filters = {}; - self.userAttributes = {}; - self.testHelpers = null; - self.placementEventMappingLookup = {}; - self.placementEventAttributeMappingLookup = {}; - self.eventQueue = []; - self.integrationName = null; - - function getEventAttributeValue(event, eventAttributeKey) { - var attributes = event && event.EventAttributes; - if (!attributes) { - return null; - } - - if (typeof attributes[eventAttributeKey] === 'undefined') { - return null; - } - - return attributes[eventAttributeKey]; - } - - function doesEventAttributeConditionMatch(condition, actualValue) { - if (!condition || !isString(condition.operator)) { - return false; - } - - var operator = condition.operator.toLowerCase(); - var expectedValue = condition.attributeValue; - - if (operator === 'exists') { - return actualValue !== null; - } - - if (actualValue == null) { - return false; - } - - if (operator === 'equals') { - return String(actualValue) === String(expectedValue); - } - - if (operator === 'contains') { - return String(actualValue).indexOf(String(expectedValue)) !== -1; - } - - return false; - } - - function doesEventMatchRule(event, rule) { - if (!rule || !isString(rule.eventAttributeKey)) { - return false; - } - - var conditions = rule.conditions; - if (!Array.isArray(conditions)) { - return false; - } - - var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); - - if (conditions.length === 0) { - return actualValue !== null; - } - for (var i = 0; i < conditions.length; i++) { - if (!doesEventAttributeConditionMatch(conditions[i], actualValue)) { - return false; - } - } - - return true; - } - - function generateMappedEventAttributeLookup( - placementEventAttributeMapping - ) { - var mappedAttributeKeys = {}; - if (!Array.isArray(placementEventAttributeMapping)) { - return mappedAttributeKeys; - } - for (var i = 0; i < placementEventAttributeMapping.length; i++) { - var mapping = placementEventAttributeMapping[i]; - if ( - !mapping || - !isString(mapping.value) || - !isString(mapping.map) - ) { - continue; - } - - var mappedAttributeKey = mapping.value; - var eventAttributeKey = mapping.map; - - if (!mappedAttributeKeys[mappedAttributeKey]) { - mappedAttributeKeys[mappedAttributeKey] = []; - } - - mappedAttributeKeys[mappedAttributeKey].push({ - eventAttributeKey: eventAttributeKey, - conditions: Array.isArray(mapping.conditions) - ? mapping.conditions - : [], - }); - } - return mappedAttributeKeys; - } - - function applyPlacementEventAttributeMapping(event) { - var mappedAttributeKeys = Object.keys( - self.placementEventAttributeMappingLookup - ); - for (var i = 0; i < mappedAttributeKeys.length; i++) { - var mappedAttributeKey = mappedAttributeKeys[i]; - var rulesForMappedAttributeKey = - self.placementEventAttributeMappingLookup[mappedAttributeKey]; - if (isEmpty(rulesForMappedAttributeKey)) { - continue; - } - - // Require ALL rules for the same key to match (AND). - var allMatch = true; - for (var j = 0; j < rulesForMappedAttributeKey.length; j++) { - if (!doesEventMatchRule(event, rulesForMappedAttributeKey[j])) { - allMatch = false; - break; - } - } - if (!allMatch) { - continue; - } - - window.mParticle.Rokt.setLocalSessionAttribute( - mappedAttributeKey, - true - ); - } - } - - /** - * Generates the Rokt launcher script URL with optional domain override and extensions - * @param {string} domain - The CNAME domain to use for overriding the launcher url - * @param {Array} extensions - List of extension query parameters to append - * @returns {string} The complete launcher script URL - */ - function generateLauncherScript(_domain, extensions) { - // Override domain if a customer is using a CNAME - // If a customer is using a CNAME, a domain will be passed. If not, we use the default domain. - var domain = typeof _domain !== 'undefined' ? _domain : 'apps.rokt.com'; - var protocol = 'https://'; - var launcherPath = '/wsdk/integrations/launcher.js'; - var baseUrl = [protocol, domain, launcherPath].join(''); - - if (!extensions || extensions.length === 0) { - return baseUrl; - } - return baseUrl + '?extensions=' + extensions.join(','); - } - - /** - * Checks if Rokt launcher is available and ready to attach - * @returns {boolean} True if launcher can be attached - */ - function isLauncherReadyToAttach() { - return window.Rokt && typeof window.Rokt.createLauncher === 'function'; - } - - /** - * Passes attributes to the Rokt Web SDK for client-side hashing - * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes - * @param {Object} attributes - The attributes to be hashed - * @returns {Promise} A Promise resolving to the - * hashed attributes from the launcher, or `null` if the kit is not initialized - */ - function hashAttributes(attributes) { - if (!isKitReady()) { - console.error('Rokt Kit: Not initialized'); - return null; - } - return self.launcher.hashAttributes(attributes); - } - - function initForwarder( - settings, - _service, - testMode, - _trackerId, - filteredUserAttributes - ) { - var accountId = settings.accountId; - var roktExtensions = extractRoktExtensions(settings.roktExtensions); - self.userAttributes = filteredUserAttributes || {}; - self.onboardingExpProvider = settings.onboardingExpProvider; - - var placementEventMapping = parseSettingsString( - settings.placementEventMapping - ); - self.placementEventMappingLookup = generateMappedEventLookup( - placementEventMapping - ); - - var placementEventAttributeMapping = parseSettingsString( - settings.placementEventAttributeMapping - ); - self.placementEventAttributeMappingLookup = - generateMappedEventAttributeLookup(placementEventAttributeMapping); - - // Set dynamic OTHER_IDENTITY based on server settings - // Convert to lowercase since server sends TitleCase (e.g., 'Other' -> 'other') - if (settings.hashedEmailUserIdentityType) { - mappedEmailSha256Key = - settings.hashedEmailUserIdentityType.toLowerCase(); - } - - var domain = window.mParticle.Rokt.domain; - var launcherOptions = mergeObjects( - {}, - window.mParticle.Rokt.launcherOptions || {} - ); - self.integrationName = generateIntegrationName( - launcherOptions.integrationName - ); - launcherOptions.integrationName = self.integrationName; - - self.domain = domain; - - if (testMode) { - self.testHelpers = { - generateLauncherScript: generateLauncherScript, - extractRoktExtensions: extractRoktExtensions, - hashEventMessage: hashEventMessage, - parseSettingsString: parseSettingsString, - generateMappedEventLookup: generateMappedEventLookup, - generateMappedEventAttributeLookup: - generateMappedEventAttributeLookup, - sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, - createAutoRemovedIframe: createAutoRemovedIframe, - djb2: djb2, - setAllowedOriginHash: function (hash) { - _allowedOriginHash = hash; - }, - }; - attachLauncher(accountId, launcherOptions); - return; - } - - if (isLauncherReadyToAttach()) { - attachLauncher(accountId, launcherOptions); - } else { - var target = document.head || document.body; - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = generateLauncherScript(domain, roktExtensions); - script.async = true; - script.crossOrigin = 'anonymous'; - script.fetchPriority = 'high'; - script.id = 'rokt-launcher'; - - script.onload = function () { - if (isLauncherReadyToAttach()) { - attachLauncher(accountId, launcherOptions); - } else { - console.error( - 'Rokt object is not available after script load.' - ); - } - }; - - script.onerror = function (error) { - console.error('Error loading Rokt launcher script:', error); - }; - - target.appendChild(script); - captureTiming(PerformanceMarks.RoktScriptAppended); - } - } - /** - * Returns the user identities from the filtered user, if any - * @param {Object} filteredUser - The filtered user object containing identities - * @returns {Object} The user identities from the filtered user - */ - function returnUserIdentities(filteredUser) { - if (!filteredUser || !filteredUser.getUserIdentities) { - return {}; - } - - var userIdentities = filteredUser.getUserIdentities().userIdentities; - - return replaceOtherIdentityWithEmailsha256(userIdentities); - } - - function returnLocalSessionAttributes() { - if ( - !window.mParticle.Rokt || - typeof window.mParticle.Rokt.getLocalSessionAttributes !== - 'function' - ) { - return {}; - } - if ( - isEmpty(self.placementEventMappingLookup) && - isEmpty(self.placementEventAttributeMappingLookup) - ) { - return {}; - } - return window.mParticle.Rokt.getLocalSessionAttributes(); - } - - function replaceOtherIdentityWithEmailsha256(userIdentities) { - var newUserIdentities = mergeObjects({}, userIdentities || {}); - if (userIdentities[mappedEmailSha256Key]) { - newUserIdentities[EMAIL_SHA256_KEY] = - userIdentities[mappedEmailSha256Key]; - } - delete newUserIdentities[mappedEmailSha256Key]; - - return newUserIdentities; - } - - /** - * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options - * @see https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ - * @param {Object} options - The options object for selecting placements containing: - * - identifier {string}: The placement identifier - * - attributes {Object}: Optional attributes to merge with existing attributes - * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes - */ - function selectPlacements(options) { - var attributes = (options && options.attributes) || {}; - var placementAttributes = mergeObjects(self.userAttributes, attributes); - - var filters = self.filters || {}; - var userAttributeFilters = filters.userAttributeFilters || []; - var filteredUser = filters.filteredUser || {}; - var mpid = - filteredUser && - filteredUser.getMPID && - typeof filteredUser.getMPID === 'function' - ? filteredUser.getMPID() - : null; - - var filteredAttributes; - - if (!filters) { - console.warn( - 'Rokt Kit: No filters available, using user attributes' - ); - - filteredAttributes = placementAttributes; - } else if (filters.filterUserAttributes) { - filteredAttributes = filters.filterUserAttributes( - placementAttributes, - userAttributeFilters - ); - } - - self.userAttributes = filteredAttributes; - - var optimizelyAttributes = - self.onboardingExpProvider === 'Optimizely' - ? fetchOptimizely() - : {}; - - var filteredUserIdentities = returnUserIdentities(filteredUser); - - var localSessionAttributes = returnLocalSessionAttributes(); - - var selectPlacementsAttributes = mergeObjects( - filteredUserIdentities, - filteredAttributes, - optimizelyAttributes, - localSessionAttributes, - { - mpid: mpid, - } - ); - - var selectPlacementsOptions = mergeObjects(options, { - attributes: selectPlacementsAttributes, - }); - - // Log custom event for selectPlacements call - logSelectPlacementsEvent(selectPlacementsAttributes); - - var selection = self.launcher.selectPlacements(selectPlacementsOptions); - - if (selection && typeof selection.then === 'function') { - selection - .then(function (sel) { - if (sel && sel.context && sel.context.sessionId) { - sel.context.sessionId - .then(_setRoktSessionId) - .catch(function () {}); - } - if (sel && typeof sel.on === 'function') { - sel.on('SESSION_ID_UPDATED').subscribe(function ( - event - ) { - _setRoktSessionId(event.body); - }); - } - }) - .catch(function () {}); - } - - return selection; - } - - /** - * Logs a custom event when selectPlacements is called - * This enables visibility and troubleshooting - * @param {Object} attributes - The attributes sent to Rokt - */ - function logSelectPlacementsEvent(attributes) { - if ( - !window.mParticle || - typeof window.mParticle.logEvent !== 'function' - ) { - return; - } - - if (!isObject(attributes)) { - return; - } - - var EVENT_TYPE_OTHER = window.mParticle.EventType.Other; - - window.mParticle.logEvent( - EVENT_NAME_SELECT_PLACEMENTS, - EVENT_TYPE_OTHER, - attributes - ); - } - - /** - * Enables optional Integration Launcher extensions before selecting placements - * @param {string} extensionName - Name of the extension to enable - * @returns {Promise<*>} A Promise resolving to the extension API if available - */ - function use(extensionName) { - if (!isKitReady()) { - console.error('Rokt Kit: Not initialized'); - return Promise.reject(new Error('Rokt Kit: Not initialized')); - } - if (!extensionName || !isString(extensionName)) { - return Promise.reject( - new Error('Rokt Kit: Invalid extension name') - ); - } - return self.launcher.use(extensionName); - } - - /** - * Sets extension data for Rokt Web SDK - * @param {Object} partnerExtensionData - The extension data object containing: - * - [extensionName] {string}: Name of the extension - * - [extensionName].options {Object}: Key-value pairs of options for the extension - * @returns {void} Nothing is returned - */ - function setExtensionData(partnerExtensionData) { - if (!isKitReady()) { - console.error('Rokt Kit: Not initialized'); - return; - } - - window.Rokt.setExtensionData(partnerExtensionData); - } - - function processEventQueue() { - self.eventQueue.forEach(function (event) { - processEvent(event); - }); - self.eventQueue = []; - } - - function processEvent(event) { - if (!isKitReady()) { - self.eventQueue.push(event); - return; - } - - _sendEventStream(event); - - if ( - typeof window.mParticle.Rokt.setLocalSessionAttribute !== 'function' - ) { - return; - } - - if (!isEmpty(self.placementEventAttributeMappingLookup)) { - applyPlacementEventAttributeMapping(event); - } - - if (isEmpty(self.placementEventMappingLookup)) { - return; - } - - var hashedEvent = hashEventMessage( - event.EventDataType, - event.EventCategory, - event.EventName - ); - - if (self.placementEventMappingLookup[hashedEvent]) { - var mappedValue = self.placementEventMappingLookup[hashedEvent]; - window.mParticle.Rokt.setLocalSessionAttribute(mappedValue, true); - } - } - - function _sendEventStream(event) { - if (window.Rokt && typeof window.Rokt.__event_stream__ === 'function') { - window.Rokt.__event_stream__(event); - } - } - - function _setRoktSessionId(sessionId) { - if (!sessionId || typeof sessionId !== 'string') { - return; - } - try { - var mpInstance = window.mParticle.getInstance(); - if ( - mpInstance && - typeof mpInstance.setIntegrationAttribute === 'function' - ) { - mpInstance.setIntegrationAttribute(moduleId, { - roktSessionId: sessionId, - }); - } - } catch (e) { - // Best effort — never let this break the partner page - } - } - - function onUserIdentified(filteredUser) { - self.filters.filteredUser = filteredUser; - self.userAttributes = filteredUser.getAllUserAttributes(); - } - - function setUserAttribute(key, value) { - self.userAttributes[key] = value; - } - - function removeUserAttribute(key) { - delete self.userAttributes[key]; - } - - function attachLauncher(accountId, launcherOptions) { - var options = mergeObjects( - { - accountId: accountId, - }, - launcherOptions || {} - ); - - if (isPartnerInLocalLauncherTestGroup()) { - var localLauncher = window.Rokt.createLocalLauncher(options); - initRoktLauncher(localLauncher); - } else { - window.Rokt.createLauncher(options) - .then(initRoktLauncher) - .catch(function (err) { - console.error('Error creating Rokt launcher:', err); - }); - } - } - - function initRoktLauncher(launcher) { - // Assign the launcher to a global variable for later access - window.Rokt.currentLauncher = launcher; - // Locally cache the launcher and filters - self.launcher = launcher; - - var roktFilters = window.mParticle.Rokt.filters; - - if (!roktFilters) { - console.warn('Rokt Kit: No filters have been set.'); - } else { - self.filters = roktFilters; - if (!roktFilters.filteredUser) { - console.warn('Rokt Kit: No filtered user has been set.'); - } - } - - // Kit must be initialized before attaching to the Rokt manager - self.isInitialized = true; - - sendAdBlockMeasurementSignals(self.domain, self.integrationName); - - // Attaches the kit to the Rokt manager - window.mParticle.Rokt.attachKit(self); - processEventQueue(); - } - - // mParticle Kit Callback Methods - function fetchOptimizely() { - var forwarders = window.mParticle - ._getActiveForwarders() - .filter(function (forwarder) { - return forwarder.name === 'Optimizely'; - }); - - try { - if (forwarders.length > 0 && window.optimizely) { - // Get the state object - var optimizelyState = window.optimizely.get('state'); - if ( - !optimizelyState || - !optimizelyState.getActiveExperimentIds - ) { - return {}; - } - // Get active experiment IDs - var activeExperimentIds = - optimizelyState.getActiveExperimentIds(); - // Get variations for each active experiment - var activeExperiments = activeExperimentIds.reduce(function ( - acc, - expId - ) { - acc[ - 'rokt.custom.optimizely.experiment.' + - expId + - '.variationId' - ] = optimizelyState.getVariationMap()[expId].id; - return acc; - }, - {}); - return activeExperiments; - } - } catch (error) { - console.error('Error fetching Optimizely attributes:', error); - } - return {}; - } - - // Called by the mParticle Rokt Manager - this.selectPlacements = selectPlacements; - this.hashAttributes = hashAttributes; - this.use = use; - - // Kit Callback Methods - this.init = initForwarder; - this.process = processEvent; - this.setExtensionData = setExtensionData; - this.setUserAttribute = setUserAttribute; - this.onUserIdentified = onUserIdentified; - this.removeUserAttribute = removeUserAttribute; - - /** - * Checks if the Rokt kit is ready to use. - * Both conditions must be true: - * 1. self.isInitialized - Set after successful initialization of the kit - * 2. self.launcher - The Rokt launcher instance must be available - * @returns {boolean} Whether the kit is ready for use - */ - function isKitReady() { - return !!(self.isInitialized && self.launcher); - } - - function isPartnerInLocalLauncherTestGroup() { - return ( - window.mParticle.config && - window.mParticle.config.isLocalLauncherEnabled && - _isAssignedToSampleGroup() - ); - } - - function _isAssignedToSampleGroup() { - var LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5; - return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD; - } - - function captureTiming(metricName) { - if ( - window && - window.mParticle && - window.mParticle.captureTiming && - metricName - ) { - window.mParticle.captureTiming(metricName); - } - } - - function createAutoRemovedIframe(src) { - var iframe = document.createElement('iframe'); - iframe.style.display = 'none'; - iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); - iframe.src = src; - iframe.onload = function () { - iframe.onload = null; - if (iframe.parentNode) { - iframe.parentNode.removeChild(iframe); - } - }; - var target = document.body || document.head; - if (target) { - target.appendChild(iframe); - } - } - - var ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; - var INIT_LOG_SAMPLING_RATE = 0.1; - var _allowedOriginHash = 1445747545; - - function djb2(str) { - var hash = 5381; - for (var i = 0; i < str.length; i++) { - hash = (hash << 5) + hash + str.charCodeAt(i); - hash = hash & hash; - } - return hash; - } - - function sendAdBlockMeasurementSignals(domain, version) { - if (djb2(window.location.origin) !== _allowedOriginHash) { - return; - } - - if (Math.random() >= INIT_LOG_SAMPLING_RATE) { - return; - } - - var guid = window.__rokt_li_guid__; - if (!guid) { - return; - } - - var pageUrl = window.location.href.split('?')[0].split('#')[0]; - var params = - 'version=' + - encodeURIComponent(version) + - '&launcherInstanceGuid=' + - encodeURIComponent(guid) + - '&pageUrl=' + - encodeURIComponent(pageUrl); - - var existingDomain = domain || 'apps.rokt.com'; - createAutoRemovedIframe( - 'https://' + existingDomain + '/v1/wsdk-init/index.html?' + params - ); - - createAutoRemovedIframe( - 'https://' + - ADBLOCK_CONTROL_DOMAIN + - '/v1/wsdk-init/index.html?' + - params + - '&isControl=true' - ); - } - }; - - function generateIntegrationName(customIntegrationName) { - var coreSdkVersion = window.mParticle.getVersion(); - var kitVersion = "1.17.0"; - var name = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion; - - if (customIntegrationName) { - name += '_' + customIntegrationName; - } - return name; - } - - function getId() { - return moduleId; - } - - function register(config) { - if (!config) { - window.console.log( - 'You must pass a config object to register the kit ' + name - ); - return; - } - if (!isObject(config)) { - window.console.log( - "'config' must be an object. You passed in a " + typeof config - ); - return; - } - - if (isObject(config.kits)) { - config.kits[name] = { - constructor: constructor, - }; - } else { - config.kits = {}; - config.kits[name] = { - constructor: constructor, - }; - } - window.console.log( - 'Successfully registered ' + name + ' to your mParticle configuration' - ); - } - - function isObject(val) { - return ( - val != null && typeof val === 'object' && Array.isArray(val) === false - ); - } - - function mergeObjects() { - var resObj = {}; - for (var i = 0; i < arguments.length; i += 1) { - var obj = arguments[i], - keys = Object.keys(obj); - for (var j = 0; j < keys.length; j += 1) { - resObj[keys[j]] = obj[keys[j]]; - } - } - return resObj; - } - - function parseSettingsString(settingsString) { - if (!settingsString) { - return []; - } - try { - return JSON.parse(settingsString.replace(/"/g, '"')); - } catch (error) { - console.error('Settings string contains invalid JSON'); - } - return []; - } - - function extractRoktExtensions(settingsString) { - var settings = settingsString ? parseSettingsString(settingsString) : []; - var roktExtensions = []; - - for (var i = 0; i < settings.length; i++) { - roktExtensions.push(settings[i].value); - } - - return roktExtensions; - } - - function generateMappedEventLookup(placementEventMapping) { - if (!placementEventMapping) { - return {}; - } - - var mappedEvents = {}; - for (var i = 0; i < placementEventMapping.length; i++) { - var mapping = placementEventMapping[i]; - mappedEvents[mapping.jsmap] = mapping.value; - } - return mappedEvents; - } - - function hashEventMessage(messageType, eventType, eventName) { - return window.mParticle.generateHash( - [messageType, eventType, eventName].join('') - ); - } - - function isEmpty(value) { - return value == null || !(Object.keys(value) || value).length; - } - - function isString(value) { - return typeof value === 'string'; - } - - if (window && window.mParticle && window.mParticle.addForwarder) { - window.mParticle.addForwarder({ - name: name, - constructor: constructor, - getId: getId, - }); - } - - var RoktKit = { - register: register, - }; - var RoktKit_1 = RoktKit.register; - - exports.default = RoktKit; - exports.register = RoktKit_1; - - return exports; - -}({})); diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 86293f9..67046e8 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -412,9 +412,6 @@ var constructor = function () { attributes: selectPlacementsAttributes, }); - // Log custom event for selectPlacements call - logSelectPlacementsEvent(selectPlacementsAttributes); - var selection = self.launcher.selectPlacements(selectPlacementsOptions); if (selection && typeof selection.then === 'function') { @@ -422,16 +419,14 @@ var constructor = function () { .then(function (sel) { if (sel && sel.context && sel.context.sessionId) { sel.context.sessionId - .then(_setRoktSessionId) + .then(function (sessionId) { + _setRoktSessionId(sessionId); + logSelectPlacementsEvent( + selectPlacementsAttributes + ); + }) .catch(function () {}); } - if (sel && typeof sel.on === 'function') { - sel.on('SESSION_ID_UPDATED').subscribe(function ( - event - ) { - _setRoktSessionId(event.body); - }); - } }) .catch(function () {}); } diff --git a/test/src/tests.js b/test/src/tests.js index 4d109e1..807f7f3 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -2617,6 +2617,19 @@ describe('Rokt Forwarder', () => { describe('#logSelectPlacementsEvent', () => { it('should log a custom event', async () => { + window.Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function () { + return Promise.resolve({ + context: { + sessionId: + Promise.resolve('rokt-session-abc'), + }, + }); + }, + }); + }; + await window.mParticle.forwarder.init( { accountId: '123456', @@ -2629,6 +2642,10 @@ describe('Rokt Forwarder', () => { } ); + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + await window.mParticle.forwarder.selectPlacements({ identifier: 'test-placement', attributes: { @@ -2636,6 +2653,8 @@ describe('Rokt Forwarder', () => { }, }); + await waitForCondition(() => mParticle.loggedEvents.length > 0); + mParticle.loggedEvents.length.should.equal(1); mParticle.loggedEvents[0].eventName.should.equal( 'selectPlacements' @@ -2648,6 +2667,19 @@ describe('Rokt Forwarder', () => { }); it('should include merged user attributes, identities, and mpid', async () => { + window.Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function () { + return Promise.resolve({ + context: { + sessionId: + Promise.resolve('rokt-session-abc'), + }, + }); + }, + }); + }; + await window.mParticle.forwarder.init( { accountId: '123456', @@ -2660,6 +2692,10 @@ describe('Rokt Forwarder', () => { } ); + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + await window.mParticle.forwarder.selectPlacements({ identifier: 'test-placement', attributes: { @@ -2667,6 +2703,8 @@ describe('Rokt Forwarder', () => { }, }); + await waitForCondition(() => mParticle.loggedEvents.length > 0); + const eventAttributes = mParticle.loggedEvents[0].eventAttributes; @@ -4632,25 +4670,13 @@ describe('Rokt Forwarder', () => { window.mParticle.Rokt.attachKitCalled = false; }); - function createMockSelection(sessionId, onSubscribers) { + function createMockSelection(sessionId) { return { context: { sessionId: sessionId ? Promise.resolve(sessionId) : Promise.resolve(''), }, - on: function (eventType) { - return { - subscribe: function (callback) { - if (onSubscribers) { - onSubscribers.push({ - eventType: eventType, - callback: callback, - }); - } - }, - }; - }, }; } @@ -4696,56 +4722,6 @@ describe('Rokt Forwarder', () => { }); }); - it('should subscribe to SESSION_ID_UPDATED and update attribute on change', async () => { - var subscribers = []; - var mockSelection = createMockSelection( - 'rokt-session-initial', - subscribers - ); - setupLauncherWithSelection(mockSelection); - - await window.mParticle.forwarder.init( - { accountId: '123456' }, - reportService.cb, - true, - null, - {} - ); - - await waitForCondition( - () => window.mParticle.forwarder.isInitialized - ); - - await window.mParticle.forwarder.selectPlacements({ - identifier: 'test-placement', - attributes: {}, - }); - - await waitForCondition(() => subscribers.length > 0); - - var sessionUpdatedSubscriber = subscribers.find( - (s) => s.eventType === 'SESSION_ID_UPDATED' - ); - sessionUpdatedSubscriber.should.not.be.undefined(); - - sessionUpdatedSubscriber.callback({ - body: 'rokt-session-refreshed', - }); - - await waitForCondition( - () => setIntegrationAttributeCalls.length >= 2 - ); - - var lastCall = - setIntegrationAttributeCalls[ - setIntegrationAttributeCalls.length - 1 - ]; - lastCall.id.should.equal(181); - lastCall.attrs.should.deepEqual({ - roktSessionId: 'rokt-session-refreshed', - }); - }); - it('should not set integration attribute when session ID is empty', async () => { var mockSelection = createMockSelection(''); setupLauncherWithSelection(mockSelection); @@ -4802,7 +4778,7 @@ describe('Rokt Forwarder', () => { setIntegrationAttributeCalls.length.should.equal(0); }); - it('should still return the selection promise to callers', async () => { + it('should return the selection promise to callers', async () => { var mockSelection = createMockSelection('rokt-session-abc'); setupLauncherWithSelection(mockSelection); From b5a58c5ce1d5409161c03f2051f9cf2713ce07df Mon Sep 17 00:00:00 2001 From: Cris Ryan Tan Date: Sat, 21 Mar 2026 08:29:08 +1100 Subject: [PATCH 3/5] fix: add the dist files again without any changes --- dist/Rokt-Kit.common.js | 876 +++++++++++++++++++++++++++++++++++++++ dist/Rokt-Kit.iife.js | 879 ++++++++++++++++++++++++++++++++++++++++ src/Rokt-Kit.js | 6 + 3 files changed, 1761 insertions(+) create mode 100644 dist/Rokt-Kit.common.js create mode 100644 dist/Rokt-Kit.iife.js diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js new file mode 100644 index 0000000..04f0178 --- /dev/null +++ b/dist/Rokt-Kit.common.js @@ -0,0 +1,876 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +/* eslint-disable no-undef */ +// Copyright 2025 mParticle, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var name = 'Rokt'; +var moduleId = 181; +var EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; + +var constructor = function () { + var self = this; + var PerformanceMarks = { + RoktScriptAppended: 'mp:RoktScriptAppended', + }; + + var EMAIL_SHA256_KEY = 'emailsha256'; + + // Dynamic identity type for Rokt's emailsha256 identity value which MP doesn't natively support - will be set during initialization + var mappedEmailSha256Key; + + self.name = name; + self.moduleId = moduleId; + self.isInitialized = false; + + self.launcher = null; + self.filters = {}; + self.userAttributes = {}; + self.testHelpers = null; + self.placementEventMappingLookup = {}; + self.placementEventAttributeMappingLookup = {}; + self.eventQueue = []; + self.integrationName = null; + + function getEventAttributeValue(event, eventAttributeKey) { + var attributes = event && event.EventAttributes; + if (!attributes) { + return null; + } + + if (typeof attributes[eventAttributeKey] === 'undefined') { + return null; + } + + return attributes[eventAttributeKey]; + } + + function doesEventAttributeConditionMatch(condition, actualValue) { + if (!condition || !isString(condition.operator)) { + return false; + } + + var operator = condition.operator.toLowerCase(); + var expectedValue = condition.attributeValue; + + if (operator === 'exists') { + return actualValue !== null; + } + + if (actualValue == null) { + return false; + } + + if (operator === 'equals') { + return String(actualValue) === String(expectedValue); + } + + if (operator === 'contains') { + return String(actualValue).indexOf(String(expectedValue)) !== -1; + } + + return false; + } + + function doesEventMatchRule(event, rule) { + if (!rule || !isString(rule.eventAttributeKey)) { + return false; + } + + var conditions = rule.conditions; + if (!Array.isArray(conditions)) { + return false; + } + + var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); + + if (conditions.length === 0) { + return actualValue !== null; + } + for (var i = 0; i < conditions.length; i++) { + if (!doesEventAttributeConditionMatch(conditions[i], actualValue)) { + return false; + } + } + + return true; + } + + function generateMappedEventAttributeLookup( + placementEventAttributeMapping + ) { + var mappedAttributeKeys = {}; + if (!Array.isArray(placementEventAttributeMapping)) { + return mappedAttributeKeys; + } + for (var i = 0; i < placementEventAttributeMapping.length; i++) { + var mapping = placementEventAttributeMapping[i]; + if ( + !mapping || + !isString(mapping.value) || + !isString(mapping.map) + ) { + continue; + } + + var mappedAttributeKey = mapping.value; + var eventAttributeKey = mapping.map; + + if (!mappedAttributeKeys[mappedAttributeKey]) { + mappedAttributeKeys[mappedAttributeKey] = []; + } + + mappedAttributeKeys[mappedAttributeKey].push({ + eventAttributeKey: eventAttributeKey, + conditions: Array.isArray(mapping.conditions) + ? mapping.conditions + : [], + }); + } + return mappedAttributeKeys; + } + + function applyPlacementEventAttributeMapping(event) { + var mappedAttributeKeys = Object.keys( + self.placementEventAttributeMappingLookup + ); + for (var i = 0; i < mappedAttributeKeys.length; i++) { + var mappedAttributeKey = mappedAttributeKeys[i]; + var rulesForMappedAttributeKey = + self.placementEventAttributeMappingLookup[mappedAttributeKey]; + if (isEmpty(rulesForMappedAttributeKey)) { + continue; + } + + // Require ALL rules for the same key to match (AND). + var allMatch = true; + for (var j = 0; j < rulesForMappedAttributeKey.length; j++) { + if (!doesEventMatchRule(event, rulesForMappedAttributeKey[j])) { + allMatch = false; + break; + } + } + if (!allMatch) { + continue; + } + + window.mParticle.Rokt.setLocalSessionAttribute( + mappedAttributeKey, + true + ); + } + } + + /** + * Generates the Rokt launcher script URL with optional domain override and extensions + * @param {string} domain - The CNAME domain to use for overriding the launcher url + * @param {Array} extensions - List of extension query parameters to append + * @returns {string} The complete launcher script URL + */ + function generateLauncherScript(_domain, extensions) { + // Override domain if a customer is using a CNAME + // If a customer is using a CNAME, a domain will be passed. If not, we use the default domain. + var domain = typeof _domain !== 'undefined' ? _domain : 'apps.rokt.com'; + var protocol = 'https://'; + var launcherPath = '/wsdk/integrations/launcher.js'; + var baseUrl = [protocol, domain, launcherPath].join(''); + + if (!extensions || extensions.length === 0) { + return baseUrl; + } + return baseUrl + '?extensions=' + extensions.join(','); + } + + /** + * Checks if Rokt launcher is available and ready to attach + * @returns {boolean} True if launcher can be attached + */ + function isLauncherReadyToAttach() { + return window.Rokt && typeof window.Rokt.createLauncher === 'function'; + } + + /** + * Passes attributes to the Rokt Web SDK for client-side hashing + * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes + * @param {Object} attributes - The attributes to be hashed + * @returns {Promise} A Promise resolving to the + * hashed attributes from the launcher, or `null` if the kit is not initialized + */ + function hashAttributes(attributes) { + if (!isKitReady()) { + console.error('Rokt Kit: Not initialized'); + return null; + } + return self.launcher.hashAttributes(attributes); + } + + function initForwarder( + settings, + _service, + testMode, + _trackerId, + filteredUserAttributes + ) { + var accountId = settings.accountId; + var roktExtensions = extractRoktExtensions(settings.roktExtensions); + self.userAttributes = filteredUserAttributes || {}; + self.onboardingExpProvider = settings.onboardingExpProvider; + + var placementEventMapping = parseSettingsString( + settings.placementEventMapping + ); + self.placementEventMappingLookup = generateMappedEventLookup( + placementEventMapping + ); + + var placementEventAttributeMapping = parseSettingsString( + settings.placementEventAttributeMapping + ); + self.placementEventAttributeMappingLookup = + generateMappedEventAttributeLookup(placementEventAttributeMapping); + + // Set dynamic OTHER_IDENTITY based on server settings + // Convert to lowercase since server sends TitleCase (e.g., 'Other' -> 'other') + if (settings.hashedEmailUserIdentityType) { + mappedEmailSha256Key = + settings.hashedEmailUserIdentityType.toLowerCase(); + } + + var domain = window.mParticle.Rokt.domain; + var launcherOptions = mergeObjects( + {}, + window.mParticle.Rokt.launcherOptions || {} + ); + self.integrationName = generateIntegrationName( + launcherOptions.integrationName + ); + launcherOptions.integrationName = self.integrationName; + + self.domain = domain; + + if (testMode) { + self.testHelpers = { + generateLauncherScript: generateLauncherScript, + extractRoktExtensions: extractRoktExtensions, + hashEventMessage: hashEventMessage, + parseSettingsString: parseSettingsString, + generateMappedEventLookup: generateMappedEventLookup, + generateMappedEventAttributeLookup: + generateMappedEventAttributeLookup, + sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, + createAutoRemovedIframe: createAutoRemovedIframe, + djb2: djb2, + setAllowedOriginHash: function (hash) { + _allowedOriginHash = hash; + }, + }; + attachLauncher(accountId, launcherOptions); + return; + } + + if (isLauncherReadyToAttach()) { + attachLauncher(accountId, launcherOptions); + } else { + var target = document.head || document.body; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = generateLauncherScript(domain, roktExtensions); + script.async = true; + script.crossOrigin = 'anonymous'; + script.fetchPriority = 'high'; + script.id = 'rokt-launcher'; + + script.onload = function () { + if (isLauncherReadyToAttach()) { + attachLauncher(accountId, launcherOptions); + } else { + console.error( + 'Rokt object is not available after script load.' + ); + } + }; + + script.onerror = function (error) { + console.error('Error loading Rokt launcher script:', error); + }; + + target.appendChild(script); + captureTiming(PerformanceMarks.RoktScriptAppended); + } + } + /** + * Returns the user identities from the filtered user, if any + * @param {Object} filteredUser - The filtered user object containing identities + * @returns {Object} The user identities from the filtered user + */ + function returnUserIdentities(filteredUser) { + if (!filteredUser || !filteredUser.getUserIdentities) { + return {}; + } + + var userIdentities = filteredUser.getUserIdentities().userIdentities; + + return replaceOtherIdentityWithEmailsha256(userIdentities); + } + + function returnLocalSessionAttributes() { + if ( + !window.mParticle.Rokt || + typeof window.mParticle.Rokt.getLocalSessionAttributes !== + 'function' + ) { + return {}; + } + if ( + isEmpty(self.placementEventMappingLookup) && + isEmpty(self.placementEventAttributeMappingLookup) + ) { + return {}; + } + return window.mParticle.Rokt.getLocalSessionAttributes(); + } + + function replaceOtherIdentityWithEmailsha256(userIdentities) { + var newUserIdentities = mergeObjects({}, userIdentities || {}); + if (userIdentities[mappedEmailSha256Key]) { + newUserIdentities[EMAIL_SHA256_KEY] = + userIdentities[mappedEmailSha256Key]; + } + delete newUserIdentities[mappedEmailSha256Key]; + + return newUserIdentities; + } + + /** + * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options + * @see https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ + * @param {Object} options - The options object for selecting placements containing: + * - identifier {string}: The placement identifier + * - attributes {Object}: Optional attributes to merge with existing attributes + * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes + */ + function selectPlacements(options) { + var attributes = (options && options.attributes) || {}; + var placementAttributes = mergeObjects(self.userAttributes, attributes); + + var filters = self.filters || {}; + var userAttributeFilters = filters.userAttributeFilters || []; + var filteredUser = filters.filteredUser || {}; + var mpid = + filteredUser && + filteredUser.getMPID && + typeof filteredUser.getMPID === 'function' + ? filteredUser.getMPID() + : null; + + var filteredAttributes; + + if (!filters) { + console.warn( + 'Rokt Kit: No filters available, using user attributes' + ); + + filteredAttributes = placementAttributes; + } else if (filters.filterUserAttributes) { + filteredAttributes = filters.filterUserAttributes( + placementAttributes, + userAttributeFilters + ); + } + + self.userAttributes = filteredAttributes; + + var optimizelyAttributes = + self.onboardingExpProvider === 'Optimizely' + ? fetchOptimizely() + : {}; + + var filteredUserIdentities = returnUserIdentities(filteredUser); + + var localSessionAttributes = returnLocalSessionAttributes(); + + var selectPlacementsAttributes = mergeObjects( + filteredUserIdentities, + filteredAttributes, + optimizelyAttributes, + localSessionAttributes, + { + mpid: mpid, + } + ); + + var selectPlacementsOptions = mergeObjects(options, { + attributes: selectPlacementsAttributes, + }); + + // Log custom event for selectPlacements call + logSelectPlacementsEvent(selectPlacementsAttributes); + + return self.launcher.selectPlacements(selectPlacementsOptions); + } + + /** + * Logs a custom event when selectPlacements is called + * This enables visibility and troubleshooting + * @param {Object} attributes - The attributes sent to Rokt + */ + function logSelectPlacementsEvent(attributes) { + if ( + !window.mParticle || + typeof window.mParticle.logEvent !== 'function' + ) { + return; + } + + if (!isObject(attributes)) { + return; + } + + var EVENT_TYPE_OTHER = window.mParticle.EventType.Other; + + window.mParticle.logEvent( + EVENT_NAME_SELECT_PLACEMENTS, + EVENT_TYPE_OTHER, + attributes + ); + } + + /** + * Enables optional Integration Launcher extensions before selecting placements + * @param {string} extensionName - Name of the extension to enable + * @returns {Promise<*>} A Promise resolving to the extension API if available + */ + function use(extensionName) { + if (!isKitReady()) { + console.error('Rokt Kit: Not initialized'); + return Promise.reject(new Error('Rokt Kit: Not initialized')); + } + if (!extensionName || !isString(extensionName)) { + return Promise.reject( + new Error('Rokt Kit: Invalid extension name') + ); + } + return self.launcher.use(extensionName); + } + + /** + * Sets extension data for Rokt Web SDK + * @param {Object} partnerExtensionData - The extension data object containing: + * - [extensionName] {string}: Name of the extension + * - [extensionName].options {Object}: Key-value pairs of options for the extension + * @returns {void} Nothing is returned + */ + function setExtensionData(partnerExtensionData) { + if (!isKitReady()) { + console.error('Rokt Kit: Not initialized'); + return; + } + + window.Rokt.setExtensionData(partnerExtensionData); + } + + function processEventQueue() { + self.eventQueue.forEach(function (event) { + processEvent(event); + }); + self.eventQueue = []; + } + + function processEvent(event) { + if (!isKitReady()) { + self.eventQueue.push(event); + return; + } + + _sendEventStream(event); + + if ( + typeof window.mParticle.Rokt.setLocalSessionAttribute !== 'function' + ) { + return; + } + + if (!isEmpty(self.placementEventAttributeMappingLookup)) { + applyPlacementEventAttributeMapping(event); + } + + if (isEmpty(self.placementEventMappingLookup)) { + return; + } + + var hashedEvent = hashEventMessage( + event.EventDataType, + event.EventCategory, + event.EventName + ); + + if (self.placementEventMappingLookup[hashedEvent]) { + var mappedValue = self.placementEventMappingLookup[hashedEvent]; + window.mParticle.Rokt.setLocalSessionAttribute(mappedValue, true); + } + } + + function _sendEventStream(event) { + if (window.Rokt && typeof window.Rokt.__event_stream__ === 'function') { + window.Rokt.__event_stream__(event); + } + } + + function onUserIdentified(filteredUser) { + self.filters.filteredUser = filteredUser; + self.userAttributes = filteredUser.getAllUserAttributes(); + } + + function setUserAttribute(key, value) { + self.userAttributes[key] = value; + } + + function removeUserAttribute(key) { + delete self.userAttributes[key]; + } + + function attachLauncher(accountId, launcherOptions) { + var options = mergeObjects( + { + accountId: accountId, + }, + launcherOptions || {} + ); + + if (isPartnerInLocalLauncherTestGroup()) { + var localLauncher = window.Rokt.createLocalLauncher(options); + initRoktLauncher(localLauncher); + } else { + window.Rokt.createLauncher(options) + .then(initRoktLauncher) + .catch(function (err) { + console.error('Error creating Rokt launcher:', err); + }); + } + } + + function initRoktLauncher(launcher) { + // Assign the launcher to a global variable for later access + window.Rokt.currentLauncher = launcher; + // Locally cache the launcher and filters + self.launcher = launcher; + + var roktFilters = window.mParticle.Rokt.filters; + + if (!roktFilters) { + console.warn('Rokt Kit: No filters have been set.'); + } else { + self.filters = roktFilters; + if (!roktFilters.filteredUser) { + console.warn('Rokt Kit: No filtered user has been set.'); + } + } + + // Kit must be initialized before attaching to the Rokt manager + self.isInitialized = true; + + sendAdBlockMeasurementSignals(self.domain, self.integrationName); + + // Attaches the kit to the Rokt manager + window.mParticle.Rokt.attachKit(self); + processEventQueue(); + } + + // mParticle Kit Callback Methods + function fetchOptimizely() { + var forwarders = window.mParticle + ._getActiveForwarders() + .filter(function (forwarder) { + return forwarder.name === 'Optimizely'; + }); + + try { + if (forwarders.length > 0 && window.optimizely) { + // Get the state object + var optimizelyState = window.optimizely.get('state'); + if ( + !optimizelyState || + !optimizelyState.getActiveExperimentIds + ) { + return {}; + } + // Get active experiment IDs + var activeExperimentIds = + optimizelyState.getActiveExperimentIds(); + // Get variations for each active experiment + var activeExperiments = activeExperimentIds.reduce(function ( + acc, + expId + ) { + acc[ + 'rokt.custom.optimizely.experiment.' + + expId + + '.variationId' + ] = optimizelyState.getVariationMap()[expId].id; + return acc; + }, + {}); + return activeExperiments; + } + } catch (error) { + console.error('Error fetching Optimizely attributes:', error); + } + return {}; + } + + // Called by the mParticle Rokt Manager + this.selectPlacements = selectPlacements; + this.hashAttributes = hashAttributes; + this.use = use; + + // Kit Callback Methods + this.init = initForwarder; + this.process = processEvent; + this.setExtensionData = setExtensionData; + this.setUserAttribute = setUserAttribute; + this.onUserIdentified = onUserIdentified; + this.removeUserAttribute = removeUserAttribute; + + /** + * Checks if the Rokt kit is ready to use. + * Both conditions must be true: + * 1. self.isInitialized - Set after successful initialization of the kit + * 2. self.launcher - The Rokt launcher instance must be available + * @returns {boolean} Whether the kit is ready for use + */ + function isKitReady() { + return !!(self.isInitialized && self.launcher); + } + + function isPartnerInLocalLauncherTestGroup() { + return ( + window.mParticle.config && + window.mParticle.config.isLocalLauncherEnabled && + _isAssignedToSampleGroup() + ); + } + + function _isAssignedToSampleGroup() { + var LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5; + return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD; + } + + function captureTiming(metricName) { + if ( + window && + window.mParticle && + window.mParticle.captureTiming && + metricName + ) { + window.mParticle.captureTiming(metricName); + } + } + + function createAutoRemovedIframe(src) { + var iframe = document.createElement('iframe'); + iframe.style.display = 'none'; + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); + iframe.src = src; + iframe.onload = function () { + iframe.onload = null; + if (iframe.parentNode) { + iframe.parentNode.removeChild(iframe); + } + }; + var target = document.body || document.head; + if (target) { + target.appendChild(iframe); + } + } + + var ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; + var INIT_LOG_SAMPLING_RATE = 0.1; + var _allowedOriginHash = 1445747545; + + function djb2(str) { + var hash = 5381; + for (var i = 0; i < str.length; i++) { + hash = (hash << 5) + hash + str.charCodeAt(i); + hash = hash & hash; + } + return hash; + } + + function sendAdBlockMeasurementSignals(domain, version) { + if (djb2(window.location.origin) !== _allowedOriginHash) { + return; + } + + if (Math.random() >= INIT_LOG_SAMPLING_RATE) { + return; + } + + var guid = window.__rokt_li_guid__; + if (!guid) { + return; + } + + var pageUrl = window.location.href.split('?')[0].split('#')[0]; + var params = + 'version=' + + encodeURIComponent(version) + + '&launcherInstanceGuid=' + + encodeURIComponent(guid) + + '&pageUrl=' + + encodeURIComponent(pageUrl); + + var existingDomain = domain || 'apps.rokt.com'; + createAutoRemovedIframe( + 'https://' + existingDomain + '/v1/wsdk-init/index.html?' + params + ); + + createAutoRemovedIframe( + 'https://' + + ADBLOCK_CONTROL_DOMAIN + + '/v1/wsdk-init/index.html?' + + params + + '&isControl=true' + ); + } +}; + +function generateIntegrationName(customIntegrationName) { + var coreSdkVersion = window.mParticle.getVersion(); + var kitVersion = "1.17.0"; + var name = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion; + + if (customIntegrationName) { + name += '_' + customIntegrationName; + } + return name; +} + +function getId() { + return moduleId; +} + +function register(config) { + if (!config) { + window.console.log( + 'You must pass a config object to register the kit ' + name + ); + return; + } + if (!isObject(config)) { + window.console.log( + "'config' must be an object. You passed in a " + typeof config + ); + return; + } + + if (isObject(config.kits)) { + config.kits[name] = { + constructor: constructor, + }; + } else { + config.kits = {}; + config.kits[name] = { + constructor: constructor, + }; + } + window.console.log( + 'Successfully registered ' + name + ' to your mParticle configuration' + ); +} + +function isObject(val) { + return ( + val != null && typeof val === 'object' && Array.isArray(val) === false + ); +} + +function mergeObjects() { + var resObj = {}; + for (var i = 0; i < arguments.length; i += 1) { + var obj = arguments[i], + keys = Object.keys(obj); + for (var j = 0; j < keys.length; j += 1) { + resObj[keys[j]] = obj[keys[j]]; + } + } + return resObj; +} + +function parseSettingsString(settingsString) { + if (!settingsString) { + return []; + } + try { + return JSON.parse(settingsString.replace(/"/g, '"')); + } catch (error) { + console.error('Settings string contains invalid JSON'); + } + return []; +} + +function extractRoktExtensions(settingsString) { + var settings = settingsString ? parseSettingsString(settingsString) : []; + var roktExtensions = []; + + for (var i = 0; i < settings.length; i++) { + roktExtensions.push(settings[i].value); + } + + return roktExtensions; +} + +function generateMappedEventLookup(placementEventMapping) { + if (!placementEventMapping) { + return {}; + } + + var mappedEvents = {}; + for (var i = 0; i < placementEventMapping.length; i++) { + var mapping = placementEventMapping[i]; + mappedEvents[mapping.jsmap] = mapping.value; + } + return mappedEvents; +} + +function hashEventMessage(messageType, eventType, eventName) { + return window.mParticle.generateHash( + [messageType, eventType, eventName].join('') + ); +} + +function isEmpty(value) { + return value == null || !(Object.keys(value) || value).length; +} + +function isString(value) { + return typeof value === 'string'; +} + +if (window && window.mParticle && window.mParticle.addForwarder) { + window.mParticle.addForwarder({ + name: name, + constructor: constructor, + getId: getId, + }); +} + +var RoktKit = { + register: register, +}; +var RoktKit_1 = RoktKit.register; + +exports.default = RoktKit; +exports.register = RoktKit_1; diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js new file mode 100644 index 0000000..d357be0 --- /dev/null +++ b/dist/Rokt-Kit.iife.js @@ -0,0 +1,879 @@ +var RoktKit = (function (exports) { + 'use strict'; + + /* eslint-disable no-undef */ + // Copyright 2025 mParticle, Inc. + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + // See the License for the specific language governing permissions and + // limitations under the License. + + var name = 'Rokt'; + var moduleId = 181; + var EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements'; + + var constructor = function () { + var self = this; + var PerformanceMarks = { + RoktScriptAppended: 'mp:RoktScriptAppended', + }; + + var EMAIL_SHA256_KEY = 'emailsha256'; + + // Dynamic identity type for Rokt's emailsha256 identity value which MP doesn't natively support - will be set during initialization + var mappedEmailSha256Key; + + self.name = name; + self.moduleId = moduleId; + self.isInitialized = false; + + self.launcher = null; + self.filters = {}; + self.userAttributes = {}; + self.testHelpers = null; + self.placementEventMappingLookup = {}; + self.placementEventAttributeMappingLookup = {}; + self.eventQueue = []; + self.integrationName = null; + + function getEventAttributeValue(event, eventAttributeKey) { + var attributes = event && event.EventAttributes; + if (!attributes) { + return null; + } + + if (typeof attributes[eventAttributeKey] === 'undefined') { + return null; + } + + return attributes[eventAttributeKey]; + } + + function doesEventAttributeConditionMatch(condition, actualValue) { + if (!condition || !isString(condition.operator)) { + return false; + } + + var operator = condition.operator.toLowerCase(); + var expectedValue = condition.attributeValue; + + if (operator === 'exists') { + return actualValue !== null; + } + + if (actualValue == null) { + return false; + } + + if (operator === 'equals') { + return String(actualValue) === String(expectedValue); + } + + if (operator === 'contains') { + return String(actualValue).indexOf(String(expectedValue)) !== -1; + } + + return false; + } + + function doesEventMatchRule(event, rule) { + if (!rule || !isString(rule.eventAttributeKey)) { + return false; + } + + var conditions = rule.conditions; + if (!Array.isArray(conditions)) { + return false; + } + + var actualValue = getEventAttributeValue(event, rule.eventAttributeKey); + + if (conditions.length === 0) { + return actualValue !== null; + } + for (var i = 0; i < conditions.length; i++) { + if (!doesEventAttributeConditionMatch(conditions[i], actualValue)) { + return false; + } + } + + return true; + } + + function generateMappedEventAttributeLookup( + placementEventAttributeMapping + ) { + var mappedAttributeKeys = {}; + if (!Array.isArray(placementEventAttributeMapping)) { + return mappedAttributeKeys; + } + for (var i = 0; i < placementEventAttributeMapping.length; i++) { + var mapping = placementEventAttributeMapping[i]; + if ( + !mapping || + !isString(mapping.value) || + !isString(mapping.map) + ) { + continue; + } + + var mappedAttributeKey = mapping.value; + var eventAttributeKey = mapping.map; + + if (!mappedAttributeKeys[mappedAttributeKey]) { + mappedAttributeKeys[mappedAttributeKey] = []; + } + + mappedAttributeKeys[mappedAttributeKey].push({ + eventAttributeKey: eventAttributeKey, + conditions: Array.isArray(mapping.conditions) + ? mapping.conditions + : [], + }); + } + return mappedAttributeKeys; + } + + function applyPlacementEventAttributeMapping(event) { + var mappedAttributeKeys = Object.keys( + self.placementEventAttributeMappingLookup + ); + for (var i = 0; i < mappedAttributeKeys.length; i++) { + var mappedAttributeKey = mappedAttributeKeys[i]; + var rulesForMappedAttributeKey = + self.placementEventAttributeMappingLookup[mappedAttributeKey]; + if (isEmpty(rulesForMappedAttributeKey)) { + continue; + } + + // Require ALL rules for the same key to match (AND). + var allMatch = true; + for (var j = 0; j < rulesForMappedAttributeKey.length; j++) { + if (!doesEventMatchRule(event, rulesForMappedAttributeKey[j])) { + allMatch = false; + break; + } + } + if (!allMatch) { + continue; + } + + window.mParticle.Rokt.setLocalSessionAttribute( + mappedAttributeKey, + true + ); + } + } + + /** + * Generates the Rokt launcher script URL with optional domain override and extensions + * @param {string} domain - The CNAME domain to use for overriding the launcher url + * @param {Array} extensions - List of extension query parameters to append + * @returns {string} The complete launcher script URL + */ + function generateLauncherScript(_domain, extensions) { + // Override domain if a customer is using a CNAME + // If a customer is using a CNAME, a domain will be passed. If not, we use the default domain. + var domain = typeof _domain !== 'undefined' ? _domain : 'apps.rokt.com'; + var protocol = 'https://'; + var launcherPath = '/wsdk/integrations/launcher.js'; + var baseUrl = [protocol, domain, launcherPath].join(''); + + if (!extensions || extensions.length === 0) { + return baseUrl; + } + return baseUrl + '?extensions=' + extensions.join(','); + } + + /** + * Checks if Rokt launcher is available and ready to attach + * @returns {boolean} True if launcher can be attached + */ + function isLauncherReadyToAttach() { + return window.Rokt && typeof window.Rokt.createLauncher === 'function'; + } + + /** + * Passes attributes to the Rokt Web SDK for client-side hashing + * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes + * @param {Object} attributes - The attributes to be hashed + * @returns {Promise} A Promise resolving to the + * hashed attributes from the launcher, or `null` if the kit is not initialized + */ + function hashAttributes(attributes) { + if (!isKitReady()) { + console.error('Rokt Kit: Not initialized'); + return null; + } + return self.launcher.hashAttributes(attributes); + } + + function initForwarder( + settings, + _service, + testMode, + _trackerId, + filteredUserAttributes + ) { + var accountId = settings.accountId; + var roktExtensions = extractRoktExtensions(settings.roktExtensions); + self.userAttributes = filteredUserAttributes || {}; + self.onboardingExpProvider = settings.onboardingExpProvider; + + var placementEventMapping = parseSettingsString( + settings.placementEventMapping + ); + self.placementEventMappingLookup = generateMappedEventLookup( + placementEventMapping + ); + + var placementEventAttributeMapping = parseSettingsString( + settings.placementEventAttributeMapping + ); + self.placementEventAttributeMappingLookup = + generateMappedEventAttributeLookup(placementEventAttributeMapping); + + // Set dynamic OTHER_IDENTITY based on server settings + // Convert to lowercase since server sends TitleCase (e.g., 'Other' -> 'other') + if (settings.hashedEmailUserIdentityType) { + mappedEmailSha256Key = + settings.hashedEmailUserIdentityType.toLowerCase(); + } + + var domain = window.mParticle.Rokt.domain; + var launcherOptions = mergeObjects( + {}, + window.mParticle.Rokt.launcherOptions || {} + ); + self.integrationName = generateIntegrationName( + launcherOptions.integrationName + ); + launcherOptions.integrationName = self.integrationName; + + self.domain = domain; + + if (testMode) { + self.testHelpers = { + generateLauncherScript: generateLauncherScript, + extractRoktExtensions: extractRoktExtensions, + hashEventMessage: hashEventMessage, + parseSettingsString: parseSettingsString, + generateMappedEventLookup: generateMappedEventLookup, + generateMappedEventAttributeLookup: + generateMappedEventAttributeLookup, + sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals, + createAutoRemovedIframe: createAutoRemovedIframe, + djb2: djb2, + setAllowedOriginHash: function (hash) { + _allowedOriginHash = hash; + }, + }; + attachLauncher(accountId, launcherOptions); + return; + } + + if (isLauncherReadyToAttach()) { + attachLauncher(accountId, launcherOptions); + } else { + var target = document.head || document.body; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = generateLauncherScript(domain, roktExtensions); + script.async = true; + script.crossOrigin = 'anonymous'; + script.fetchPriority = 'high'; + script.id = 'rokt-launcher'; + + script.onload = function () { + if (isLauncherReadyToAttach()) { + attachLauncher(accountId, launcherOptions); + } else { + console.error( + 'Rokt object is not available after script load.' + ); + } + }; + + script.onerror = function (error) { + console.error('Error loading Rokt launcher script:', error); + }; + + target.appendChild(script); + captureTiming(PerformanceMarks.RoktScriptAppended); + } + } + /** + * Returns the user identities from the filtered user, if any + * @param {Object} filteredUser - The filtered user object containing identities + * @returns {Object} The user identities from the filtered user + */ + function returnUserIdentities(filteredUser) { + if (!filteredUser || !filteredUser.getUserIdentities) { + return {}; + } + + var userIdentities = filteredUser.getUserIdentities().userIdentities; + + return replaceOtherIdentityWithEmailsha256(userIdentities); + } + + function returnLocalSessionAttributes() { + if ( + !window.mParticle.Rokt || + typeof window.mParticle.Rokt.getLocalSessionAttributes !== + 'function' + ) { + return {}; + } + if ( + isEmpty(self.placementEventMappingLookup) && + isEmpty(self.placementEventAttributeMappingLookup) + ) { + return {}; + } + return window.mParticle.Rokt.getLocalSessionAttributes(); + } + + function replaceOtherIdentityWithEmailsha256(userIdentities) { + var newUserIdentities = mergeObjects({}, userIdentities || {}); + if (userIdentities[mappedEmailSha256Key]) { + newUserIdentities[EMAIL_SHA256_KEY] = + userIdentities[mappedEmailSha256Key]; + } + delete newUserIdentities[mappedEmailSha256Key]; + + return newUserIdentities; + } + + /** + * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options + * @see https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ + * @param {Object} options - The options object for selecting placements containing: + * - identifier {string}: The placement identifier + * - attributes {Object}: Optional attributes to merge with existing attributes + * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes + */ + function selectPlacements(options) { + var attributes = (options && options.attributes) || {}; + var placementAttributes = mergeObjects(self.userAttributes, attributes); + + var filters = self.filters || {}; + var userAttributeFilters = filters.userAttributeFilters || []; + var filteredUser = filters.filteredUser || {}; + var mpid = + filteredUser && + filteredUser.getMPID && + typeof filteredUser.getMPID === 'function' + ? filteredUser.getMPID() + : null; + + var filteredAttributes; + + if (!filters) { + console.warn( + 'Rokt Kit: No filters available, using user attributes' + ); + + filteredAttributes = placementAttributes; + } else if (filters.filterUserAttributes) { + filteredAttributes = filters.filterUserAttributes( + placementAttributes, + userAttributeFilters + ); + } + + self.userAttributes = filteredAttributes; + + var optimizelyAttributes = + self.onboardingExpProvider === 'Optimizely' + ? fetchOptimizely() + : {}; + + var filteredUserIdentities = returnUserIdentities(filteredUser); + + var localSessionAttributes = returnLocalSessionAttributes(); + + var selectPlacementsAttributes = mergeObjects( + filteredUserIdentities, + filteredAttributes, + optimizelyAttributes, + localSessionAttributes, + { + mpid: mpid, + } + ); + + var selectPlacementsOptions = mergeObjects(options, { + attributes: selectPlacementsAttributes, + }); + + // Log custom event for selectPlacements call + logSelectPlacementsEvent(selectPlacementsAttributes); + + return self.launcher.selectPlacements(selectPlacementsOptions); + } + + /** + * Logs a custom event when selectPlacements is called + * This enables visibility and troubleshooting + * @param {Object} attributes - The attributes sent to Rokt + */ + function logSelectPlacementsEvent(attributes) { + if ( + !window.mParticle || + typeof window.mParticle.logEvent !== 'function' + ) { + return; + } + + if (!isObject(attributes)) { + return; + } + + var EVENT_TYPE_OTHER = window.mParticle.EventType.Other; + + window.mParticle.logEvent( + EVENT_NAME_SELECT_PLACEMENTS, + EVENT_TYPE_OTHER, + attributes + ); + } + + /** + * Enables optional Integration Launcher extensions before selecting placements + * @param {string} extensionName - Name of the extension to enable + * @returns {Promise<*>} A Promise resolving to the extension API if available + */ + function use(extensionName) { + if (!isKitReady()) { + console.error('Rokt Kit: Not initialized'); + return Promise.reject(new Error('Rokt Kit: Not initialized')); + } + if (!extensionName || !isString(extensionName)) { + return Promise.reject( + new Error('Rokt Kit: Invalid extension name') + ); + } + return self.launcher.use(extensionName); + } + + /** + * Sets extension data for Rokt Web SDK + * @param {Object} partnerExtensionData - The extension data object containing: + * - [extensionName] {string}: Name of the extension + * - [extensionName].options {Object}: Key-value pairs of options for the extension + * @returns {void} Nothing is returned + */ + function setExtensionData(partnerExtensionData) { + if (!isKitReady()) { + console.error('Rokt Kit: Not initialized'); + return; + } + + window.Rokt.setExtensionData(partnerExtensionData); + } + + function processEventQueue() { + self.eventQueue.forEach(function (event) { + processEvent(event); + }); + self.eventQueue = []; + } + + function processEvent(event) { + if (!isKitReady()) { + self.eventQueue.push(event); + return; + } + + _sendEventStream(event); + + if ( + typeof window.mParticle.Rokt.setLocalSessionAttribute !== 'function' + ) { + return; + } + + if (!isEmpty(self.placementEventAttributeMappingLookup)) { + applyPlacementEventAttributeMapping(event); + } + + if (isEmpty(self.placementEventMappingLookup)) { + return; + } + + var hashedEvent = hashEventMessage( + event.EventDataType, + event.EventCategory, + event.EventName + ); + + if (self.placementEventMappingLookup[hashedEvent]) { + var mappedValue = self.placementEventMappingLookup[hashedEvent]; + window.mParticle.Rokt.setLocalSessionAttribute(mappedValue, true); + } + } + + function _sendEventStream(event) { + if (window.Rokt && typeof window.Rokt.__event_stream__ === 'function') { + window.Rokt.__event_stream__(event); + } + } + + function onUserIdentified(filteredUser) { + self.filters.filteredUser = filteredUser; + self.userAttributes = filteredUser.getAllUserAttributes(); + } + + function setUserAttribute(key, value) { + self.userAttributes[key] = value; + } + + function removeUserAttribute(key) { + delete self.userAttributes[key]; + } + + function attachLauncher(accountId, launcherOptions) { + var options = mergeObjects( + { + accountId: accountId, + }, + launcherOptions || {} + ); + + if (isPartnerInLocalLauncherTestGroup()) { + var localLauncher = window.Rokt.createLocalLauncher(options); + initRoktLauncher(localLauncher); + } else { + window.Rokt.createLauncher(options) + .then(initRoktLauncher) + .catch(function (err) { + console.error('Error creating Rokt launcher:', err); + }); + } + } + + function initRoktLauncher(launcher) { + // Assign the launcher to a global variable for later access + window.Rokt.currentLauncher = launcher; + // Locally cache the launcher and filters + self.launcher = launcher; + + var roktFilters = window.mParticle.Rokt.filters; + + if (!roktFilters) { + console.warn('Rokt Kit: No filters have been set.'); + } else { + self.filters = roktFilters; + if (!roktFilters.filteredUser) { + console.warn('Rokt Kit: No filtered user has been set.'); + } + } + + // Kit must be initialized before attaching to the Rokt manager + self.isInitialized = true; + + sendAdBlockMeasurementSignals(self.domain, self.integrationName); + + // Attaches the kit to the Rokt manager + window.mParticle.Rokt.attachKit(self); + processEventQueue(); + } + + // mParticle Kit Callback Methods + function fetchOptimizely() { + var forwarders = window.mParticle + ._getActiveForwarders() + .filter(function (forwarder) { + return forwarder.name === 'Optimizely'; + }); + + try { + if (forwarders.length > 0 && window.optimizely) { + // Get the state object + var optimizelyState = window.optimizely.get('state'); + if ( + !optimizelyState || + !optimizelyState.getActiveExperimentIds + ) { + return {}; + } + // Get active experiment IDs + var activeExperimentIds = + optimizelyState.getActiveExperimentIds(); + // Get variations for each active experiment + var activeExperiments = activeExperimentIds.reduce(function ( + acc, + expId + ) { + acc[ + 'rokt.custom.optimizely.experiment.' + + expId + + '.variationId' + ] = optimizelyState.getVariationMap()[expId].id; + return acc; + }, + {}); + return activeExperiments; + } + } catch (error) { + console.error('Error fetching Optimizely attributes:', error); + } + return {}; + } + + // Called by the mParticle Rokt Manager + this.selectPlacements = selectPlacements; + this.hashAttributes = hashAttributes; + this.use = use; + + // Kit Callback Methods + this.init = initForwarder; + this.process = processEvent; + this.setExtensionData = setExtensionData; + this.setUserAttribute = setUserAttribute; + this.onUserIdentified = onUserIdentified; + this.removeUserAttribute = removeUserAttribute; + + /** + * Checks if the Rokt kit is ready to use. + * Both conditions must be true: + * 1. self.isInitialized - Set after successful initialization of the kit + * 2. self.launcher - The Rokt launcher instance must be available + * @returns {boolean} Whether the kit is ready for use + */ + function isKitReady() { + return !!(self.isInitialized && self.launcher); + } + + function isPartnerInLocalLauncherTestGroup() { + return ( + window.mParticle.config && + window.mParticle.config.isLocalLauncherEnabled && + _isAssignedToSampleGroup() + ); + } + + function _isAssignedToSampleGroup() { + var LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5; + return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD; + } + + function captureTiming(metricName) { + if ( + window && + window.mParticle && + window.mParticle.captureTiming && + metricName + ) { + window.mParticle.captureTiming(metricName); + } + } + + function createAutoRemovedIframe(src) { + var iframe = document.createElement('iframe'); + iframe.style.display = 'none'; + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); + iframe.src = src; + iframe.onload = function () { + iframe.onload = null; + if (iframe.parentNode) { + iframe.parentNode.removeChild(iframe); + } + }; + var target = document.body || document.head; + if (target) { + target.appendChild(iframe); + } + } + + var ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com'; + var INIT_LOG_SAMPLING_RATE = 0.1; + var _allowedOriginHash = 1445747545; + + function djb2(str) { + var hash = 5381; + for (var i = 0; i < str.length; i++) { + hash = (hash << 5) + hash + str.charCodeAt(i); + hash = hash & hash; + } + return hash; + } + + function sendAdBlockMeasurementSignals(domain, version) { + if (djb2(window.location.origin) !== _allowedOriginHash) { + return; + } + + if (Math.random() >= INIT_LOG_SAMPLING_RATE) { + return; + } + + var guid = window.__rokt_li_guid__; + if (!guid) { + return; + } + + var pageUrl = window.location.href.split('?')[0].split('#')[0]; + var params = + 'version=' + + encodeURIComponent(version) + + '&launcherInstanceGuid=' + + encodeURIComponent(guid) + + '&pageUrl=' + + encodeURIComponent(pageUrl); + + var existingDomain = domain || 'apps.rokt.com'; + createAutoRemovedIframe( + 'https://' + existingDomain + '/v1/wsdk-init/index.html?' + params + ); + + createAutoRemovedIframe( + 'https://' + + ADBLOCK_CONTROL_DOMAIN + + '/v1/wsdk-init/index.html?' + + params + + '&isControl=true' + ); + } + }; + + function generateIntegrationName(customIntegrationName) { + var coreSdkVersion = window.mParticle.getVersion(); + var kitVersion = "1.17.0"; + var name = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion; + + if (customIntegrationName) { + name += '_' + customIntegrationName; + } + return name; + } + + function getId() { + return moduleId; + } + + function register(config) { + if (!config) { + window.console.log( + 'You must pass a config object to register the kit ' + name + ); + return; + } + if (!isObject(config)) { + window.console.log( + "'config' must be an object. You passed in a " + typeof config + ); + return; + } + + if (isObject(config.kits)) { + config.kits[name] = { + constructor: constructor, + }; + } else { + config.kits = {}; + config.kits[name] = { + constructor: constructor, + }; + } + window.console.log( + 'Successfully registered ' + name + ' to your mParticle configuration' + ); + } + + function isObject(val) { + return ( + val != null && typeof val === 'object' && Array.isArray(val) === false + ); + } + + function mergeObjects() { + var resObj = {}; + for (var i = 0; i < arguments.length; i += 1) { + var obj = arguments[i], + keys = Object.keys(obj); + for (var j = 0; j < keys.length; j += 1) { + resObj[keys[j]] = obj[keys[j]]; + } + } + return resObj; + } + + function parseSettingsString(settingsString) { + if (!settingsString) { + return []; + } + try { + return JSON.parse(settingsString.replace(/"/g, '"')); + } catch (error) { + console.error('Settings string contains invalid JSON'); + } + return []; + } + + function extractRoktExtensions(settingsString) { + var settings = settingsString ? parseSettingsString(settingsString) : []; + var roktExtensions = []; + + for (var i = 0; i < settings.length; i++) { + roktExtensions.push(settings[i].value); + } + + return roktExtensions; + } + + function generateMappedEventLookup(placementEventMapping) { + if (!placementEventMapping) { + return {}; + } + + var mappedEvents = {}; + for (var i = 0; i < placementEventMapping.length; i++) { + var mapping = placementEventMapping[i]; + mappedEvents[mapping.jsmap] = mapping.value; + } + return mappedEvents; + } + + function hashEventMessage(messageType, eventType, eventName) { + return window.mParticle.generateHash( + [messageType, eventType, eventName].join('') + ); + } + + function isEmpty(value) { + return value == null || !(Object.keys(value) || value).length; + } + + function isString(value) { + return typeof value === 'string'; + } + + if (window && window.mParticle && window.mParticle.addForwarder) { + window.mParticle.addForwarder({ + name: name, + constructor: constructor, + getId: getId, + }); + } + + var RoktKit = { + register: register, + }; + var RoktKit_1 = RoktKit.register; + + exports.default = RoktKit; + exports.register = RoktKit_1; + + return exports; + +}({})); diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 67046e8..fc8be5e 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -414,6 +414,12 @@ var constructor = function () { var selection = self.launcher.selectPlacements(selectPlacementsOptions); + // After selection resolves, sync the Rokt session ID back to mParticle + // as an integration attribute so server-side integrations can link events. + // We log the custom event AFTER setting the attribute because + // setIntegrationAttribute alone doesn't fire a network request — + // if the user closes the page before another event fires, the server + // would never receive the session ID. if (selection && typeof selection.then === 'function') { selection .then(function (sel) { From 49a2e36c0e935d38b013b977f64317323a397b89 Mon Sep 17 00:00:00 2001 From: Cris Ryan Tan Date: Tue, 24 Mar 2026 10:49:56 +1100 Subject: [PATCH 4/5] fix: log selectPlacements event on failure and missing sessionId paths Address PR review feedback to ensure logSelectPlacementsEvent fires in all code paths (catch, else), not just the happy path where sessionId resolves successfully. Co-Authored-By: Claude Opus 4.6 (1M context) --- dist/Rokt-Kit.common.js | 55 ++++++++++++++++- dist/Rokt-Kit.iife.js | 55 ++++++++++++++++- src/Rokt-Kit.js | 12 +++- test/src/tests.js | 134 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 248 insertions(+), 8 deletions(-) diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js index 04f0178..af8bc30 100644 --- a/dist/Rokt-Kit.common.js +++ b/dist/Rokt-Kit.common.js @@ -416,10 +416,40 @@ var constructor = function () { attributes: selectPlacementsAttributes, }); - // Log custom event for selectPlacements call - logSelectPlacementsEvent(selectPlacementsAttributes); + var selection = self.launcher.selectPlacements(selectPlacementsOptions); + + // After selection resolves, sync the Rokt session ID back to mParticle + // as an integration attribute so server-side integrations can link events. + // We log the custom event AFTER setting the attribute because + // setIntegrationAttribute alone doesn't fire a network request — + // if the user closes the page before another event fires, the server + // would never receive the session ID. + if (selection && typeof selection.then === 'function') { + selection + .then(function (sel) { + if (sel && sel.context && sel.context.sessionId) { + sel.context.sessionId + .then(function (sessionId) { + _setRoktSessionId(sessionId); + logSelectPlacementsEvent( + selectPlacementsAttributes + ); + }) + .catch(function () { + logSelectPlacementsEvent( + selectPlacementsAttributes + ); + }); + } else { + logSelectPlacementsEvent(selectPlacementsAttributes); + } + }) + .catch(function () { + logSelectPlacementsEvent(selectPlacementsAttributes); + }); + } - return self.launcher.selectPlacements(selectPlacementsOptions); + return selection; } /** @@ -529,6 +559,25 @@ var constructor = function () { } } + function _setRoktSessionId(sessionId) { + if (!sessionId || typeof sessionId !== 'string') { + return; + } + try { + var mpInstance = window.mParticle.getInstance(); + if ( + mpInstance && + typeof mpInstance.setIntegrationAttribute === 'function' + ) { + mpInstance.setIntegrationAttribute(moduleId, { + roktSessionId: sessionId, + }); + } + } catch (e) { + // Best effort — never let this break the partner page + } + } + function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js index d357be0..5ace899 100644 --- a/dist/Rokt-Kit.iife.js +++ b/dist/Rokt-Kit.iife.js @@ -415,10 +415,40 @@ var RoktKit = (function (exports) { attributes: selectPlacementsAttributes, }); - // Log custom event for selectPlacements call - logSelectPlacementsEvent(selectPlacementsAttributes); + var selection = self.launcher.selectPlacements(selectPlacementsOptions); + + // After selection resolves, sync the Rokt session ID back to mParticle + // as an integration attribute so server-side integrations can link events. + // We log the custom event AFTER setting the attribute because + // setIntegrationAttribute alone doesn't fire a network request — + // if the user closes the page before another event fires, the server + // would never receive the session ID. + if (selection && typeof selection.then === 'function') { + selection + .then(function (sel) { + if (sel && sel.context && sel.context.sessionId) { + sel.context.sessionId + .then(function (sessionId) { + _setRoktSessionId(sessionId); + logSelectPlacementsEvent( + selectPlacementsAttributes + ); + }) + .catch(function () { + logSelectPlacementsEvent( + selectPlacementsAttributes + ); + }); + } else { + logSelectPlacementsEvent(selectPlacementsAttributes); + } + }) + .catch(function () { + logSelectPlacementsEvent(selectPlacementsAttributes); + }); + } - return self.launcher.selectPlacements(selectPlacementsOptions); + return selection; } /** @@ -528,6 +558,25 @@ var RoktKit = (function (exports) { } } + function _setRoktSessionId(sessionId) { + if (!sessionId || typeof sessionId !== 'string') { + return; + } + try { + var mpInstance = window.mParticle.getInstance(); + if ( + mpInstance && + typeof mpInstance.setIntegrationAttribute === 'function' + ) { + mpInstance.setIntegrationAttribute(moduleId, { + roktSessionId: sessionId, + }); + } + } catch (e) { + // Best effort — never let this break the partner page + } + } + function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index fc8be5e..882f2f3 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -431,10 +431,18 @@ var constructor = function () { selectPlacementsAttributes ); }) - .catch(function () {}); + .catch(function () { + logSelectPlacementsEvent( + selectPlacementsAttributes + ); + }); + } else { + logSelectPlacementsEvent(selectPlacementsAttributes); } }) - .catch(function () {}); + .catch(function () { + logSelectPlacementsEvent(selectPlacementsAttributes); + }); } return selection; diff --git a/test/src/tests.js b/test/src/tests.js index 807f7f3..03f5780 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -2717,6 +2717,140 @@ describe('Rokt Forwarder', () => { ); }); + it('should log event when sessionId promise rejects', async () => { + window.Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function () { + return Promise.resolve({ + context: { + sessionId: Promise.reject( + new Error('session id failed') + ), + }, + }); + }, + }); + }; + + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + { + 'cached-user-attr': 'cached-value', + } + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + 'new-attr': 'new-value', + }, + }); + + await waitForCondition(() => mParticle.loggedEvents.length > 0); + + mParticle.loggedEvents.length.should.equal(1); + mParticle.loggedEvents[0].eventName.should.equal( + 'selectPlacements' + ); + }); + + it('should log event when selection has no sessionId', async () => { + window.Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function () { + return Promise.resolve({ + context: {}, + }); + }, + }); + }; + + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + { + 'cached-user-attr': 'cached-value', + } + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + 'new-attr': 'new-value', + }, + }); + + await waitForCondition(() => mParticle.loggedEvents.length > 0); + + mParticle.loggedEvents.length.should.equal(1); + mParticle.loggedEvents[0].eventName.should.equal( + 'selectPlacements' + ); + }); + + it('should log event when selectPlacements promise rejects', async () => { + window.Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function () { + return Promise.reject( + new Error('selection failed') + ); + }, + }); + }; + + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + { + 'cached-user-attr': 'cached-value', + } + ); + + await waitForCondition( + () => window.mParticle.forwarder.isInitialized + ); + + try { + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + 'new-attr': 'new-value', + }, + }); + } catch (e) { + // Expected rejection from selectPlacements + } + + await waitForCondition(() => mParticle.loggedEvents.length > 0); + + mParticle.loggedEvents.length.should.equal(1); + mParticle.loggedEvents[0].eventName.should.equal( + 'selectPlacements' + ); + }); + it('should skip logging when mParticle.logEvent is not available', async () => { var originalLogEvent = window.mParticle.logEvent; window.mParticle.logEvent = undefined; From 0ced2e65487ab3f14f2a3bf7d5993811f6e2e17a Mon Sep 17 00:00:00 2001 From: Cris Ryan Tan Date: Tue, 24 Mar 2026 10:52:28 +1100 Subject: [PATCH 5/5] fix: revert dist/ files to previous state The dist/ folder should not include changes from this PR. Co-Authored-By: Claude Opus 4.6 (1M context) --- dist/Rokt-Kit.common.js | 55 +++-------------------------------------- dist/Rokt-Kit.iife.js | 55 +++-------------------------------------- 2 files changed, 6 insertions(+), 104 deletions(-) diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js index af8bc30..04f0178 100644 --- a/dist/Rokt-Kit.common.js +++ b/dist/Rokt-Kit.common.js @@ -416,40 +416,10 @@ var constructor = function () { attributes: selectPlacementsAttributes, }); - var selection = self.launcher.selectPlacements(selectPlacementsOptions); - - // After selection resolves, sync the Rokt session ID back to mParticle - // as an integration attribute so server-side integrations can link events. - // We log the custom event AFTER setting the attribute because - // setIntegrationAttribute alone doesn't fire a network request — - // if the user closes the page before another event fires, the server - // would never receive the session ID. - if (selection && typeof selection.then === 'function') { - selection - .then(function (sel) { - if (sel && sel.context && sel.context.sessionId) { - sel.context.sessionId - .then(function (sessionId) { - _setRoktSessionId(sessionId); - logSelectPlacementsEvent( - selectPlacementsAttributes - ); - }) - .catch(function () { - logSelectPlacementsEvent( - selectPlacementsAttributes - ); - }); - } else { - logSelectPlacementsEvent(selectPlacementsAttributes); - } - }) - .catch(function () { - logSelectPlacementsEvent(selectPlacementsAttributes); - }); - } + // Log custom event for selectPlacements call + logSelectPlacementsEvent(selectPlacementsAttributes); - return selection; + return self.launcher.selectPlacements(selectPlacementsOptions); } /** @@ -559,25 +529,6 @@ var constructor = function () { } } - function _setRoktSessionId(sessionId) { - if (!sessionId || typeof sessionId !== 'string') { - return; - } - try { - var mpInstance = window.mParticle.getInstance(); - if ( - mpInstance && - typeof mpInstance.setIntegrationAttribute === 'function' - ) { - mpInstance.setIntegrationAttribute(moduleId, { - roktSessionId: sessionId, - }); - } - } catch (e) { - // Best effort — never let this break the partner page - } - } - function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js index 5ace899..d357be0 100644 --- a/dist/Rokt-Kit.iife.js +++ b/dist/Rokt-Kit.iife.js @@ -415,40 +415,10 @@ var RoktKit = (function (exports) { attributes: selectPlacementsAttributes, }); - var selection = self.launcher.selectPlacements(selectPlacementsOptions); - - // After selection resolves, sync the Rokt session ID back to mParticle - // as an integration attribute so server-side integrations can link events. - // We log the custom event AFTER setting the attribute because - // setIntegrationAttribute alone doesn't fire a network request — - // if the user closes the page before another event fires, the server - // would never receive the session ID. - if (selection && typeof selection.then === 'function') { - selection - .then(function (sel) { - if (sel && sel.context && sel.context.sessionId) { - sel.context.sessionId - .then(function (sessionId) { - _setRoktSessionId(sessionId); - logSelectPlacementsEvent( - selectPlacementsAttributes - ); - }) - .catch(function () { - logSelectPlacementsEvent( - selectPlacementsAttributes - ); - }); - } else { - logSelectPlacementsEvent(selectPlacementsAttributes); - } - }) - .catch(function () { - logSelectPlacementsEvent(selectPlacementsAttributes); - }); - } + // Log custom event for selectPlacements call + logSelectPlacementsEvent(selectPlacementsAttributes); - return selection; + return self.launcher.selectPlacements(selectPlacementsOptions); } /** @@ -558,25 +528,6 @@ var RoktKit = (function (exports) { } } - function _setRoktSessionId(sessionId) { - if (!sessionId || typeof sessionId !== 'string') { - return; - } - try { - var mpInstance = window.mParticle.getInstance(); - if ( - mpInstance && - typeof mpInstance.setIntegrationAttribute === 'function' - ) { - mpInstance.setIntegrationAttribute(moduleId, { - roktSessionId: sessionId, - }); - } - } catch (e) { - // Best effort — never let this break the partner page - } - } - function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes();