From b6d0d7544ecbcb315e4e7246cc1b15cdaf539773 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 10:44:56 -0400 Subject: [PATCH 01/27] docs: design spec for page-view capture into local session attributes --- .../2026-07-31-page-view-capture-design.md | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-31-page-view-capture-design.md diff --git a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md new file mode 100644 index 0000000..6437e10 --- /dev/null +++ b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md @@ -0,0 +1,126 @@ +# Page-View Capture into Local Session Attributes + +**Date:** 2026-07-31 +**Branch:** `capture-page-views` (branched off `main`; PR targets `development`) +**Module:** Rokt Web Kit (`@mparticle/web-rokt-kit`, module ID 181) +**Source file:** `src/Rokt-Kit.ts` + +## Problem + +The kit should record page-view events as they are logged and store them +durably ("offline") so they can be processed later. The stored history should +automatically feed into the next `selectPlacements` call as targeting context. + +## Key facts established during design + +- The forwarder already exposes a per-event hook: `process(event: SDKEvent)` + (`src/Rokt-Kit.ts:1164`). It is invoked by the mParticle SDK for every logged + event. No new registration or hook is required. +- Page views are identified by `event.EventDataType === 3` + (`MessageType.PageView`, confirmed in `@mparticle/web-sdk` types). The page + name is `event.EventName`. +- The full URL is **not** carried in the page-view event payload — `logPageView()` + only attaches `{ hostname, title }`. The reliable full URL is + `window.location.href`, read at capture time. The kit runs browser-side, so + `window` is always available. +- `setLocalSessionAttribute(key, value)` / `getLocalSessionAttributes()` live on + the core SDK's Rokt manager (`window.mParticle.Rokt`). `setLocalSessionAttribute` + **persists to browser storage** (`persistenceData.gs.lsa` → `savePersistence`). + That persistence is the "offline" durability we rely on, and values may be + arrays/objects. +- `returnLocalSessionAttributes()` (`src/Rokt-Kit.ts:865`) already reads the + store back into `selectPlacements`, **but** it early-returns `{}` unless a + placement-event mapping lookup is non-empty. That guard must be relaxed for + captured page views to reach `selectPlacements`. + +## Design + +### Data shape + +A single local-session-attribute key, `mpPageViews`, holds a JSON array of the +last **N = 25** entries, newest last: + +```ts +interface StoredPageView { + name: string; // event.EventName + url: string; // window.location.href (full, verbatim — see Security note) + timestamp: number; // event.Timestamp +} +``` + +Constants: +- `PAGE_VIEWS_KEY = 'mpPageViews'` +- `MAX_PAGE_VIEWS = 25` + +### Capture flow (in `process()`) + +After the existing readiness check, and guarded by +`typeof mp().Rokt?.setLocalSessionAttribute === 'function'`: + +1. If `event.EventDataType !== MESSAGE_TYPE_PAGE_VIEW (3)`, skip page-view + capture (existing placement-mapping logic is unaffected). +2. Read the current list: `mp().Rokt.getLocalSessionAttributes()?.[PAGE_VIEWS_KEY]`, + defaulting to `[]`. Coerce non-arrays to `[]` defensively. +3. Build the record: `{ name: event.EventName, url: sanitizeUrl(window.location.href), timestamp: event.Timestamp }`. +4. Append; if `list.length > MAX_PAGE_VIEWS`, drop from the front (evict oldest). +5. Write back via `mp().Rokt.setLocalSessionAttribute(PAGE_VIEWS_KEY, list)`. + +Capture is wrapped so a malformed event can never throw out of the forwarder +(consistent with the rest of `process()`), and runs in addition to — not instead +of — the existing placement-event-mapping logic. + +### Feeding `selectPlacements` + +Relax the guard in `returnLocalSessionAttributes()` so it returns the stored +attributes whenever the store is available and populated, rather than only when +a placement-event mapping lookup is non-empty. This makes `mpPageViews` flow +into `selectPlacements` through the existing path, without duplicating the store +read. + +### URL sanitization boundary + +`sanitizeUrl(href: string): string` isolates URL handling. Per the decision +below it returns `href` verbatim for now. Tightening to strip the query and +fragment later is a one-line change inside this helper and touches nothing else. + +## Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Downstream purpose | Feed the next `selectPlacements` | Reuses existing `returnLocalSessionAttributes()` path | +| Payload per view | Full list, last N (name + url + timestamp) | Richest targeting signal | +| Detection | `event.EventDataType === 3` (PageView) | Standard mParticle page-view classification | +| List cap (N) | 25 | User choice; see size caveat below | +| URL handling | Full URL verbatim | User choice; see Security note | + +## ⚠️ Security note (Rokt secure-coding policy) + +The stored value is the **full URL including query string and fragment**. Query +strings frequently carry PII (emails, tokens, order IDs). This list is +**persisted to browser storage and sent to Rokt** on the next `selectPlacements`. +The full-URL choice is implemented as requested; the recommended safer default +is to strip the query and fragment. The `sanitizeUrl()` helper isolates this so +it can be tightened later without touching capture logic. + +**Size caveat:** N = 25 full URLs is persisted to cookie/localStorage-backed +storage, which has size limits. If entries approach those limits, revisit N or +the URL handling. + +## Testing + +Vitest cases in `test/src/tests.spec.ts`: + +1. A page-view event (`EventDataType === 3`) appends a record with correct + `name`, `url`, and `timestamp`. +2. A non-page-view event does not append. +3. The list caps at `MAX_PAGE_VIEWS` and evicts the oldest entry. +4. Capture no-ops when `setLocalSessionAttribute` is unavailable (does not throw). +5. Stored page views surface through `returnLocalSessionAttributes()` and into + `selectPlacements`. + +## Out of scope + +- No new kit setting / server-side feature gate (capture is always on when the + store is available). +- No query-string stripping (deferred behind `sanitizeUrl()`). +- No changes to placement-event mapping behavior. From c97568c8534841b4af3f34c059fd6840fe1b457f Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 11:03:20 -0400 Subject: [PATCH 02/27] docs: capture ActiveTimeOnSite, pageUrl, SourceMessageId, timestamp, eventAttributes per page view --- dist/Rokt-Kit.common.js | 2 +- dist/Rokt-Kit.common.js.map | 2 +- dist/Rokt-Kit.esm.js | 1 + dist/Rokt-Kit.esm.js.map | 2 +- dist/Rokt-Kit.iife.js | 2 +- dist/Rokt-Kit.iife.js.map | 2 +- .../2026-07-31-page-view-capture-design.md | 32 +++++++++++++++---- src/Rokt-Kit.ts | 1 + 8 files changed, 33 insertions(+), 11 deletions(-) diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js index c174b69..c499e70 100644 --- a/dist/Rokt-Kit.common.js +++ b/dist/Rokt-Kit.common.js @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=["billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],J=new Set(B);function G(n){return J.has(n.toLowerCase())}function w(n){const t={},e=n||{},i=Object.keys(e);for(let r=0;r=$)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(t??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);O("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),O("https://"+X+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function ut(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function dt(){return typeof window<"u"?window.location?.href:void 0}function ht(){return typeof window<"u"?window.navigator?.userAgent:void 0}class q{constructor(){this._logCount={}}incrementAndCheck(t){const i=(this._logCount[t]||0)+1;return this._logCount[t]=i,i>at}}class C{constructor(t,e,i,r,s){this._reporter="mp-wsdk";const o=t.isLoggingEnabled;this._integrationName=e||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new q,this._isEnabled=ut()||o}send(t,e,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(e)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:e,code:r||N.UNKNOWN_ERROR,url:dt(),deviceInfo:ht(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(t,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class H{constructor(t,e,i,r,s){this._transport=new C(t,e,i,r,s),this._errorUrl=z(t?.errorUrl,t?.integrationDomain,ot)}report(t){if(!t)return;const e=t.severity||I.ERROR;this._transport.send(this._errorUrl,e,t.message,t.code,t.stackTrace)}}class W{constructor(t,e,i,r,s,o){this._transport=new C(t,i,r,s,o),this._loggingUrl=z(t?.loggingUrl,t?.integrationDomain,st),this._errorReportingService=e}log(t){t&&this._transport.send(this._loggingUrl,I.INFO,t.message,t.code,void 0,e=>{if(this._errorReportingService){const i=typeof e.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+e.message,code:N.LOG_DELIVERY_FAILURE,severity:i?I.ERROR:I.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=b,this.moduleId=b,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(t,e){const i=t&&t.EventAttributes;return!i||typeof i[e]>"u"?null:i[e]}doesEventAttributeConditionMatch(t,e){if(!t||!m(t.operator))return!1;const i=t.operator.toLowerCase(),r=t.attributeValue;return i==="exists"?e!==null:e==null?!1:i==="equals"?String(e)===String(r):i==="contains"?String(e).indexOf(String(r))!==-1:!1}doesEventMatchRule(t,e){if(!e||!m(e.eventAttributeKey))return!1;const i=e.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(t,e.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;s{await ct(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(t){window.Rokt&&(window.Rokt.currentLauncher=t),this.launcher=t;const e=a().Rokt?.filters;e?(this.filters=e,e.filteredUser?this._workspaceSearchInFlightPromise=this.search(e.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,F(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const t=a()._getActiveForwarders().filter(e=>e.name==="Optimizely");try{if(t.length>0&&window.optimizely){const e=window.optimizely.get("state");return!e||!e.getActiveExperimentIds?{}:e.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=e.getVariationMap()[o].id,s),{})}}catch(e){console.error("Error fetching Optimizely attributes:",e)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(t){window&&a()&&a().captureTiming&&t&&a().captureTiming(t)}init(t,e,i,r,s){const o=t,c=o.accountId;this.userAttributes=w(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=L(o.placementEventMapping);this.placementEventMappingLookup=x(u);const l=L(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=Y(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=m(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:v,legacyRoktExtensions:R,loadThankYouElement:A}=D(o.roktExtensions),g={...a().Rokt?.launcherOptions||{}};this.integrationName=lt(g.integrationName),g.integrationName=this.integrationName,this.domain=h;const _={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},y=new H(_,this.integrationName,window.__rokt_li_guid__,o.accountId),S=new W(_,y,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=y,this.loggingService=S,a()._registerErrorReportingService&&a()._registerErrorReportingService(y),a()._registerLoggingService&&a()._registerLoggingService(S),i?(this.testHelpers={generateLauncherScript:U,generateThankYouElementScript:K,extractRoktExtensionConfig:D,hashEventMessage:j,parseSettingsString:L,generateMappedEventLookup:x,generateMappedEventAttributeLookup:Y,sendAdBlockMeasurementSignals:F,createAutoRemovedIframe:O,djb2:V,setAllowedOriginHashes:f=>{p._allowedOriginHashes=f},ReportingTransport:C,ErrorReportingService:H,LoggingService:W,RateLimiter:q,ErrorCodes:N,WSDKErrorSeverity:I},this.attachLauncher(c,g),"Successfully initialized: "+d):(A&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),M(et,K(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:f=>{console.error("Error loading Rokt Thank You Element script:",f)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,g,R):(M(tt,U(h,v),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,g,R):console.error("Rokt object is not available after script load.")},onError:f=>{console.error("Error loading Rokt launcher script:",f)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(t){if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(k(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(t),!k(this.placementEventMappingLookup))){const e=j(t.EventDataType,t.EventCategory,t.EventName??"");this.placementEventMappingLookup[String(e)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(t){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(t)}setUserAttribute(t,e){return G(t)||(this.userAttributes[t]=e),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(t){return delete this.userAttributes[t],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(t,e){return this.userAttributes=w(t.getAllUserAttributes()),"Successfully called "+e+" for forwarder: "+d}onUserIdentified(t){const e=t;return this.filters.filteredUser=e,this._workspaceSearchInFlightPromise=this.search(e),this.handleIdentityComplete(t,"onUserIdentified")}search(t){const e=this._workspaceIdSyncApiKey;if(!e)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=t.getUserIdentities?t.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];m(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(e,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(t,e){return this.handleIdentityComplete(t,"onLoginComplete")}onLogoutComplete(t,e){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(t,"onLogoutComplete")}onModifyComplete(t,e){return this.handleIdentityComplete(t,"onModifyComplete")}selectPlacements(t){if(this._workspaceSearchInFlightPromise){const e=this._workspaceSearchInFlightPromise;return Promise.race([e,new Promise(i=>setTimeout(i,nt))]).then(()=>this._dispatchPlacements(t))}return this._dispatchPlacements(t)}_dispatchPlacements(t){const e=t&&t.attributes||{},r={...w(this.userAttributes),...e},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=w(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},v=this.returnUserIdentities(c),R=this.returnLocalSessionAttributes(),A={...v,...l,...h,...R,...this.userIdentifiedInWorkspace?{[it]:!0}:{},mpid:u},g={...t,attributes:A},_=this.launcher.selectPlacements(g),y=()=>this.logSelectPlacementsEvent(A);return Promise.resolve(_).then(S=>S?.context?.sessionId?.then(f=>this.setRoktSessionId(f))).catch(()=>{}).finally(y),_}hashAttributes(t){return this.isKitReady()?this.launcher.hashAttributes(t):(console.error("Rokt Kit: Not initialized"),null)}use(t){return this.isKitReady()?!t||!m(t)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(t):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(t){this._isThankYouElementLoaded?t():this._thankYouElementOnLoadCallback=t}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function pt(){return b}function gt(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!T(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}T(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:pt});exports.register=gt; +"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=["billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],J=new Set(B);function G(n){return J.has(n.toLowerCase())}function b(n){const t={},e=n||{},i=Object.keys(e);for(let r=0;r=$)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(t??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);O("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),O("https://"+X+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function ut(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function dt(){return typeof window<"u"?window.location?.href:void 0}function ht(){return typeof window<"u"?window.navigator?.userAgent:void 0}class q{constructor(){this._logCount={}}incrementAndCheck(t){const i=(this._logCount[t]||0)+1;return this._logCount[t]=i,i>at}}class C{constructor(t,e,i,r,s){this._reporter="mp-wsdk";const o=t.isLoggingEnabled;this._integrationName=e||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new q,this._isEnabled=ut()||o}send(t,e,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(e)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:e,code:r||N.UNKNOWN_ERROR,url:dt(),deviceInfo:ht(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(t,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class H{constructor(t,e,i,r,s){this._transport=new C(t,e,i,r,s),this._errorUrl=z(t?.errorUrl,t?.integrationDomain,ot)}report(t){if(!t)return;const e=t.severity||I.ERROR;this._transport.send(this._errorUrl,e,t.message,t.code,t.stackTrace)}}class W{constructor(t,e,i,r,s,o){this._transport=new C(t,i,r,s,o),this._loggingUrl=z(t?.loggingUrl,t?.integrationDomain,st),this._errorReportingService=e}log(t){t&&this._transport.send(this._loggingUrl,I.INFO,t.message,t.code,void 0,e=>{if(this._errorReportingService){const i=typeof e.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+e.message,code:N.LOG_DELIVERY_FAILURE,severity:i?I.ERROR:I.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=w,this.moduleId=w,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(t,e){const i=t&&t.EventAttributes;return!i||typeof i[e]>"u"?null:i[e]}doesEventAttributeConditionMatch(t,e){if(!t||!m(t.operator))return!1;const i=t.operator.toLowerCase(),r=t.attributeValue;return i==="exists"?e!==null:e==null?!1:i==="equals"?String(e)===String(r):i==="contains"?String(e).indexOf(String(r))!==-1:!1}doesEventMatchRule(t,e){if(!e||!m(e.eventAttributeKey))return!1;const i=e.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(t,e.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;s{await ct(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(t){window.Rokt&&(window.Rokt.currentLauncher=t),this.launcher=t;const e=a().Rokt?.filters;e?(this.filters=e,e.filteredUser?this._workspaceSearchInFlightPromise=this.search(e.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,F(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const t=a()._getActiveForwarders().filter(e=>e.name==="Optimizely");try{if(t.length>0&&window.optimizely){const e=window.optimizely.get("state");return!e||!e.getActiveExperimentIds?{}:e.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=e.getVariationMap()[o].id,s),{})}}catch(e){console.error("Error fetching Optimizely attributes:",e)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(t){window&&a()&&a().captureTiming&&t&&a().captureTiming(t)}init(t,e,i,r,s){const o=t,c=o.accountId;this.userAttributes=b(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=L(o.placementEventMapping);this.placementEventMappingLookup=x(u);const l=L(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=Y(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=m(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:v,legacyRoktExtensions:R,loadThankYouElement:A}=D(o.roktExtensions),g={...a().Rokt?.launcherOptions||{}};this.integrationName=lt(g.integrationName),g.integrationName=this.integrationName,this.domain=h;const _={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},y=new H(_,this.integrationName,window.__rokt_li_guid__,o.accountId),S=new W(_,y,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=y,this.loggingService=S,a()._registerErrorReportingService&&a()._registerErrorReportingService(y),a()._registerLoggingService&&a()._registerLoggingService(S),i?(this.testHelpers={generateLauncherScript:U,generateThankYouElementScript:K,extractRoktExtensionConfig:D,hashEventMessage:j,parseSettingsString:L,generateMappedEventLookup:x,generateMappedEventAttributeLookup:Y,sendAdBlockMeasurementSignals:F,createAutoRemovedIframe:O,djb2:V,setAllowedOriginHashes:f=>{p._allowedOriginHashes=f},ReportingTransport:C,ErrorReportingService:H,LoggingService:W,RateLimiter:q,ErrorCodes:N,WSDKErrorSeverity:I},this.attachLauncher(c,g),"Successfully initialized: "+d):(A&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),M(et,K(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:f=>{console.error("Error loading Rokt Thank You Element script:",f)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,g,R):(M(tt,U(h,v),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,g,R):console.error("Rokt object is not available after script load.")},onError:f=>{console.error("Error loading Rokt launcher script:",f)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(t){debugger;if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(k(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(t),!k(this.placementEventMappingLookup))){const e=j(t.EventDataType,t.EventCategory,t.EventName??"");this.placementEventMappingLookup[String(e)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(t){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(t)}setUserAttribute(t,e){return G(t)||(this.userAttributes[t]=e),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(t){return delete this.userAttributes[t],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(t,e){return this.userAttributes=b(t.getAllUserAttributes()),"Successfully called "+e+" for forwarder: "+d}onUserIdentified(t){const e=t;return this.filters.filteredUser=e,this._workspaceSearchInFlightPromise=this.search(e),this.handleIdentityComplete(t,"onUserIdentified")}search(t){const e=this._workspaceIdSyncApiKey;if(!e)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=t.getUserIdentities?t.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];m(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(e,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(t,e){return this.handleIdentityComplete(t,"onLoginComplete")}onLogoutComplete(t,e){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(t,"onLogoutComplete")}onModifyComplete(t,e){return this.handleIdentityComplete(t,"onModifyComplete")}selectPlacements(t){if(this._workspaceSearchInFlightPromise){const e=this._workspaceSearchInFlightPromise;return Promise.race([e,new Promise(i=>setTimeout(i,nt))]).then(()=>this._dispatchPlacements(t))}return this._dispatchPlacements(t)}_dispatchPlacements(t){const e=t&&t.attributes||{},r={...b(this.userAttributes),...e},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=b(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},v=this.returnUserIdentities(c),R=this.returnLocalSessionAttributes(),A={...v,...l,...h,...R,...this.userIdentifiedInWorkspace?{[it]:!0}:{},mpid:u},g={...t,attributes:A},_=this.launcher.selectPlacements(g),y=()=>this.logSelectPlacementsEvent(A);return Promise.resolve(_).then(S=>S?.context?.sessionId?.then(f=>this.setRoktSessionId(f))).catch(()=>{}).finally(y),_}hashAttributes(t){return this.isKitReady()?this.launcher.hashAttributes(t):(console.error("Rokt Kit: Not initialized"),null)}use(t){return this.isKitReady()?!t||!m(t)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(t):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(t){this._isThankYouElementLoaded?t():this._thankYouElementOnLoadCallback=t}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function pt(){return w}function gt(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!T(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}T(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:pt});exports.register=gt; //# sourceMappingURL=Rokt-Kit.common.js.map diff --git a/dist/Rokt-Kit.common.js.map b/dist/Rokt-Kit.common.js.map index 04eaf0f..31df544 100644 --- a/dist/Rokt-Kit.common.js.map +++ b/dist/Rokt-Kit.common.js.map @@ -1 +1 @@ -{"version":3,"file":"Rokt-Kit.common.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Rokt-Kit.common.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n debugger;\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file diff --git a/dist/Rokt-Kit.esm.js b/dist/Rokt-Kit.esm.js index c040ba5..f895b87 100644 --- a/dist/Rokt-Kit.esm.js +++ b/dist/Rokt-Kit.esm.js @@ -453,6 +453,7 @@ const p = class p { }), this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)), "Successfully initialized: " + d); } process(t) { + debugger; if (!this.isKitReady()) return "Kit not ready for forwarder: " + d; if (typeof a().Rokt?.setLocalSessionAttribute == "function" && (k(this.placementEventAttributeMappingLookup) || this.applyPlacementEventAttributeMapping(t), !k(this.placementEventMappingLookup))) { diff --git a/dist/Rokt-Kit.esm.js.map b/dist/Rokt-Kit.esm.js.map index e5995c8..68bf5dd 100644 --- a/dist/Rokt-Kit.esm.js.map +++ b/dist/Rokt-Kit.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"Rokt-Kit.esm.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Rokt-Kit.esm.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n debugger;\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js index b3c9b22..67b1b0e 100644 --- a/dist/Rokt-Kit.iife.js +++ b/dist/Rokt-Kit.iife.js @@ -1,2 +1,2 @@ -var RoktKit=(function(v){"use strict";const J=["billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],Q=new Set(J);function K(n){return Q.has(n.toLowerCase())}function R(n){const t={},e=n||{},i=Object.keys(e);for(let r=0;r=Z)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(t??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);P("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),P("https://"+$+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function dt(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function ht(){return typeof window<"u"?window.location?.href:void 0}function pt(){return typeof window<"u"?window.navigator?.userAgent:void 0}class V{constructor(){this._logCount={}}incrementAndCheck(t){const i=(this._logCount[t]||0)+1;return this._logCount[t]=i,i>ct}}class C{constructor(t,e,i,r,s){this._reporter="mp-wsdk";const o=t.isLoggingEnabled;this._integrationName=e||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new V,this._isEnabled=dt()||o}send(t,e,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(e)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:e,code:r||T.UNKNOWN_ERROR,url:ht(),deviceInfo:pt(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(t,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class q{constructor(t,e,i,r,s){this._transport=new C(t,e,i,r,s),this._errorUrl=Y(t?.errorUrl,t?.integrationDomain,at)}report(t){if(!t)return;const e=t.severity||_.ERROR;this._transport.send(this._errorUrl,e,t.message,t.code,t.stackTrace)}}class B{constructor(t,e,i,r,s,o){this._transport=new C(t,i,r,s,o),this._loggingUrl=Y(t?.loggingUrl,t?.integrationDomain,ot),this._errorReportingService=e}log(t){t&&this._transport.send(this._loggingUrl,_.INFO,t.message,t.code,void 0,e=>{if(this._errorReportingService){const i=typeof e.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+e.message,code:T.LOG_DELIVERY_FAILURE,severity:i?_.ERROR:_.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=A,this.moduleId=A,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(t,e){const i=t&&t.EventAttributes;return!i||typeof i[e]>"u"?null:i[e]}doesEventAttributeConditionMatch(t,e){if(!t||!g(t.operator))return!1;const i=t.operator.toLowerCase(),r=t.attributeValue;return i==="exists"?e!==null:e==null?!1:i==="equals"?String(e)===String(r):i==="contains"?String(e).indexOf(String(r))!==-1:!1}doesEventMatchRule(t,e){if(!e||!g(e.eventAttributeKey))return!1;const i=e.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(t,e.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;s{await lt(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(t){window.Rokt&&(window.Rokt.currentLauncher=t),this.launcher=t;const e=a().Rokt?.filters;e?(this.filters=e,e.filteredUser?this._workspaceSearchInFlightPromise=this.search(e.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,z(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const t=a()._getActiveForwarders().filter(e=>e.name==="Optimizely");try{if(t.length>0&&window.optimizely){const e=window.optimizely.get("state");return!e||!e.getActiveExperimentIds?{}:e.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=e.getVariationMap()[o].id,s),{})}}catch(e){console.error("Error fetching Optimizely attributes:",e)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(t){window&&a()&&a().captureTiming&&t&&a().captureTiming(t)}init(t,e,i,r,s){const o=t,c=o.accountId;this.userAttributes=R(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=S(o.placementEventMapping);this.placementEventMappingLookup=F(u);const l=S(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=H(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=g(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:U,legacyRoktExtensions:w,loadThankYouElement:b}=j(o.roktExtensions),f={...a().Rokt?.launcherOptions||{}};this.integrationName=ut(f.integrationName),f.integrationName=this.integrationName,this.domain=h;const k={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},I=new q(k,this.integrationName,window.__rokt_li_guid__,o.accountId),L=new B(k,I,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=I,this.loggingService=L,a()._registerErrorReportingService&&a()._registerErrorReportingService(I),a()._registerLoggingService&&a()._registerLoggingService(L),i?(this.testHelpers={generateLauncherScript:M,generateThankYouElementScript:D,extractRoktExtensionConfig:j,hashEventMessage:W,parseSettingsString:S,generateMappedEventLookup:F,generateMappedEventAttributeLookup:H,sendAdBlockMeasurementSignals:z,createAutoRemovedIframe:P,djb2:G,setAllowedOriginHashes:m=>{p._allowedOriginHashes=m},ReportingTransport:C,ErrorReportingService:q,LoggingService:B,RateLimiter:V,ErrorCodes:T,WSDKErrorSeverity:_},this.attachLauncher(c,f),"Successfully initialized: "+d):(b&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),x(it,D(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:m=>{console.error("Error loading Rokt Thank You Element script:",m)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,f,w):(x(et,M(h,U),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,f,w):console.error("Rokt object is not available after script load.")},onError:m=>{console.error("Error loading Rokt launcher script:",m)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(t){if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(y(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(t),!y(this.placementEventMappingLookup))){const e=W(t.EventDataType,t.EventCategory,t.EventName??"");this.placementEventMappingLookup[String(e)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(t){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(t)}setUserAttribute(t,e){return K(t)||(this.userAttributes[t]=e),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(t){return delete this.userAttributes[t],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(t,e){return this.userAttributes=R(t.getAllUserAttributes()),"Successfully called "+e+" for forwarder: "+d}onUserIdentified(t){const e=t;return this.filters.filteredUser=e,this._workspaceSearchInFlightPromise=this.search(e),this.handleIdentityComplete(t,"onUserIdentified")}search(t){const e=this._workspaceIdSyncApiKey;if(!e)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=t.getUserIdentities?t.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];g(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(e,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(t,e){return this.handleIdentityComplete(t,"onLoginComplete")}onLogoutComplete(t,e){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(t,"onLogoutComplete")}onModifyComplete(t,e){return this.handleIdentityComplete(t,"onModifyComplete")}selectPlacements(t){if(this._workspaceSearchInFlightPromise){const e=this._workspaceSearchInFlightPromise;return Promise.race([e,new Promise(i=>setTimeout(i,rt))]).then(()=>this._dispatchPlacements(t))}return this._dispatchPlacements(t)}_dispatchPlacements(t){const e=t&&t.attributes||{},r={...R(this.userAttributes),...e},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=R(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},U=this.returnUserIdentities(c),w=this.returnLocalSessionAttributes(),b={...U,...l,...h,...w,...this.userIdentifiedInWorkspace?{[nt]:!0}:{},mpid:u},f={...t,attributes:b},k=this.launcher.selectPlacements(f),I=()=>this.logSelectPlacementsEvent(b);return Promise.resolve(k).then(L=>L?.context?.sessionId?.then(m=>this.setRoktSessionId(m))).catch(()=>{}).finally(I),k}hashAttributes(t){return this.isKitReady()?this.launcher.hashAttributes(t):(console.error("Rokt Kit: Not initialized"),null)}use(t){return this.isKitReady()?!t||!g(t)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(t):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(t){this._isThankYouElementLoaded?t():this._thankYouElementOnLoadCallback=t}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function gt(){return A}function ft(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!N(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}N(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}return typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:gt}),v.register=ft,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"}),v})({}); +var RoktKit=(function(v){"use strict";const J=["billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],Q=new Set(J);function K(n){return Q.has(n.toLowerCase())}function R(n){const t={},e=n||{},i=Object.keys(e);for(let r=0;r=Z)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(t??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);P("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),P("https://"+$+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function dt(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function ht(){return typeof window<"u"?window.location?.href:void 0}function pt(){return typeof window<"u"?window.navigator?.userAgent:void 0}class V{constructor(){this._logCount={}}incrementAndCheck(t){const i=(this._logCount[t]||0)+1;return this._logCount[t]=i,i>ct}}class C{constructor(t,e,i,r,s){this._reporter="mp-wsdk";const o=t.isLoggingEnabled;this._integrationName=e||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new V,this._isEnabled=dt()||o}send(t,e,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(e)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:e,code:r||T.UNKNOWN_ERROR,url:ht(),deviceInfo:pt(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(t,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class q{constructor(t,e,i,r,s){this._transport=new C(t,e,i,r,s),this._errorUrl=Y(t?.errorUrl,t?.integrationDomain,at)}report(t){if(!t)return;const e=t.severity||_.ERROR;this._transport.send(this._errorUrl,e,t.message,t.code,t.stackTrace)}}class B{constructor(t,e,i,r,s,o){this._transport=new C(t,i,r,s,o),this._loggingUrl=Y(t?.loggingUrl,t?.integrationDomain,ot),this._errorReportingService=e}log(t){t&&this._transport.send(this._loggingUrl,_.INFO,t.message,t.code,void 0,e=>{if(this._errorReportingService){const i=typeof e.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+e.message,code:T.LOG_DELIVERY_FAILURE,severity:i?_.ERROR:_.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=A,this.moduleId=A,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(t,e){const i=t&&t.EventAttributes;return!i||typeof i[e]>"u"?null:i[e]}doesEventAttributeConditionMatch(t,e){if(!t||!g(t.operator))return!1;const i=t.operator.toLowerCase(),r=t.attributeValue;return i==="exists"?e!==null:e==null?!1:i==="equals"?String(e)===String(r):i==="contains"?String(e).indexOf(String(r))!==-1:!1}doesEventMatchRule(t,e){if(!e||!g(e.eventAttributeKey))return!1;const i=e.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(t,e.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;s{await lt(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(t){window.Rokt&&(window.Rokt.currentLauncher=t),this.launcher=t;const e=a().Rokt?.filters;e?(this.filters=e,e.filteredUser?this._workspaceSearchInFlightPromise=this.search(e.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,z(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const t=a()._getActiveForwarders().filter(e=>e.name==="Optimizely");try{if(t.length>0&&window.optimizely){const e=window.optimizely.get("state");return!e||!e.getActiveExperimentIds?{}:e.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=e.getVariationMap()[o].id,s),{})}}catch(e){console.error("Error fetching Optimizely attributes:",e)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(t){window&&a()&&a().captureTiming&&t&&a().captureTiming(t)}init(t,e,i,r,s){const o=t,c=o.accountId;this.userAttributes=R(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=S(o.placementEventMapping);this.placementEventMappingLookup=F(u);const l=S(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=H(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=g(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:U,legacyRoktExtensions:b,loadThankYouElement:w}=j(o.roktExtensions),f={...a().Rokt?.launcherOptions||{}};this.integrationName=ut(f.integrationName),f.integrationName=this.integrationName,this.domain=h;const k={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},I=new q(k,this.integrationName,window.__rokt_li_guid__,o.accountId),L=new B(k,I,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=I,this.loggingService=L,a()._registerErrorReportingService&&a()._registerErrorReportingService(I),a()._registerLoggingService&&a()._registerLoggingService(L),i?(this.testHelpers={generateLauncherScript:M,generateThankYouElementScript:D,extractRoktExtensionConfig:j,hashEventMessage:W,parseSettingsString:S,generateMappedEventLookup:F,generateMappedEventAttributeLookup:H,sendAdBlockMeasurementSignals:z,createAutoRemovedIframe:P,djb2:G,setAllowedOriginHashes:m=>{p._allowedOriginHashes=m},ReportingTransport:C,ErrorReportingService:q,LoggingService:B,RateLimiter:V,ErrorCodes:T,WSDKErrorSeverity:_},this.attachLauncher(c,f),"Successfully initialized: "+d):(w&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),x(it,D(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:m=>{console.error("Error loading Rokt Thank You Element script:",m)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,f,b):(x(et,M(h,U),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,f,b):console.error("Rokt object is not available after script load.")},onError:m=>{console.error("Error loading Rokt launcher script:",m)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(t){debugger;if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(y(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(t),!y(this.placementEventMappingLookup))){const e=W(t.EventDataType,t.EventCategory,t.EventName??"");this.placementEventMappingLookup[String(e)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(t){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(t)}setUserAttribute(t,e){return K(t)||(this.userAttributes[t]=e),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(t){return delete this.userAttributes[t],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(t,e){return this.userAttributes=R(t.getAllUserAttributes()),"Successfully called "+e+" for forwarder: "+d}onUserIdentified(t){const e=t;return this.filters.filteredUser=e,this._workspaceSearchInFlightPromise=this.search(e),this.handleIdentityComplete(t,"onUserIdentified")}search(t){const e=this._workspaceIdSyncApiKey;if(!e)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=t.getUserIdentities?t.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];g(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(e,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(t,e){return this.handleIdentityComplete(t,"onLoginComplete")}onLogoutComplete(t,e){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(t,"onLogoutComplete")}onModifyComplete(t,e){return this.handleIdentityComplete(t,"onModifyComplete")}selectPlacements(t){if(this._workspaceSearchInFlightPromise){const e=this._workspaceSearchInFlightPromise;return Promise.race([e,new Promise(i=>setTimeout(i,rt))]).then(()=>this._dispatchPlacements(t))}return this._dispatchPlacements(t)}_dispatchPlacements(t){const e=t&&t.attributes||{},r={...R(this.userAttributes),...e},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=R(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},U=this.returnUserIdentities(c),b=this.returnLocalSessionAttributes(),w={...U,...l,...h,...b,...this.userIdentifiedInWorkspace?{[nt]:!0}:{},mpid:u},f={...t,attributes:w},k=this.launcher.selectPlacements(f),I=()=>this.logSelectPlacementsEvent(w);return Promise.resolve(k).then(L=>L?.context?.sessionId?.then(m=>this.setRoktSessionId(m))).catch(()=>{}).finally(I),k}hashAttributes(t){return this.isKitReady()?this.launcher.hashAttributes(t):(console.error("Rokt Kit: Not initialized"),null)}use(t){return this.isKitReady()?!t||!g(t)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(t):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(t){this._isThankYouElementLoaded?t():this._thankYouElementOnLoadCallback=t}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function gt(){return A}function ft(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!N(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}N(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}return typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:gt}),v.register=ft,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"}),v})({}); //# sourceMappingURL=Rokt-Kit.iife.js.map diff --git a/dist/Rokt-Kit.iife.js.map b/dist/Rokt-Kit.iife.js.map index 616a6bf..904b46b 100644 --- a/dist/Rokt-Kit.iife.js.map +++ b/dist/Rokt-Kit.iife.js.map @@ -1 +1 @@ -{"version":3,"file":"Rokt-Kit.iife.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"sCAAA,MAAMA,EAAoD,CACxD,kBACA,kBACA,cACA,eACA,iBACA,YACA,QACA,kBACA,iBACA,UACA,aACA,WACA,WACA,yBACA,kCACA,cACA,mBACA,eACA,kBACA,iBACA,gBACA,kBACA,YACF,EACMC,EAAmD,IAAI,IAAID,CAAiD,EAE3G,SAASE,EAA6CC,EAAsB,CACjF,OAAOF,EAAiD,IAAIE,EAAI,YAAA,CAAa,CAC/E,CAEO,SAASC,EACdC,EACyB,CACzB,MAAMC,EAA8C,CAAA,EAC9CC,EAAmBF,GAAc,CAAA,EACjCG,EAAgB,OAAO,KAAKD,CAAgB,EAElD,QAASE,EAAI,EAAGA,EAAID,EAAc,OAAQC,IAAK,CAC7C,MAAMN,EAAMK,EAAcC,CAAC,EACtBP,EAA6CC,CAAG,IACnDG,EAAmBH,CAAG,EAAII,EAAiBJ,CAAG,EAElD,CAEA,OAAOG,CACT,CC8LA,MAAMI,EAAO,OACPC,EAAW,IACXC,EAA+B,mBAC/BC,EAAyB,yBACzBC,EAAyB,GACzBC,GAAmC,sBACnCC,GAA6B,gBAC7BC,GAAmC,yBACnCC,GAAmC,4BAMnCC,GAAqC,IAMrCC,EAAa,CACjB,cAAe,gBACf,oBAAqB,sBACrB,iBAAkB,mBAClB,qBAAsB,sBACxB,EAEMC,EAAoB,CACxB,MAAO,QACP,KAAM,OACN,QAAS,SACX,EAEMC,GAAsB,oBACtBC,GAAmB,UACnBC,GAAiB,aACjBC,GAA0B,GAQhC,SAASC,GAAwB,CAE/B,OAAQ,OAAe,SACzB,CAMA,SAASC,EAAuBC,EAA4BC,EAA8B,CAExF,MAAMC,EAAU,CAACC,EAAgBH,CAAM,EADlB,gCACiC,EAAE,KAAK,EAAE,EAE/D,MAAI,CAACC,GAAcA,EAAW,SAAW,EAChCC,EAEFA,EAAU,eAAiBD,EAAW,KAAK,GAAG,CACvD,CAEA,SAASG,EAA8BJ,EAA4B,CAEjE,MAAO,CAACG,EAAgBH,CAAM,EADF,0CACwB,EAAE,KAAK,EAAE,CAC/D,CAEA,SAASG,EAAgBH,EAA4B,CAInD,MAAO,CAFU,WADM,OAAOA,EAAW,IAAcA,EAASN,EAGhC,EAAE,KAAK,EAAE,CAC3C,CAEA,SAASW,EAAqBC,EAAmCN,EAA4BO,EAA0B,CACrH,OAAID,EACEA,EAAc,WAAW,SAAS,GAAKA,EAAc,WAAW,UAAU,EACrEA,EAEF,WAAaA,EAGfH,EAAgBH,CAAM,EAAIO,CACnC,CAEA,SAASC,EACPC,EACAC,EACAC,EACM,CACN,GAAI,SAAS,eAAeF,CAAQ,EAAG,OAEvC,MAAMG,EAAS,SAAS,MAAQ,SAAS,KACnCC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,GAAKJ,EACZI,EAAO,KAAO,kBACdA,EAAO,IAAMH,EACbG,EAAO,MAAQ,GACfA,EAAO,YAAc,YACpBA,EAAyD,cAAgB,OACtEF,GAAU,SAAQE,EAAO,OAASF,EAAS,QAC3CA,GAAU,UAASE,EAAO,QAAUF,EAAS,SACjDC,EAAO,YAAYC,CAAM,CAC3B,CAEA,SAASC,EAASC,EAA8C,CAC9D,OAAOA,GAAO,MAAQ,OAAOA,GAAQ,UAAY,MAAM,QAAQA,CAAG,IAAM,EAC1E,CAEA,SAASC,EAAuBC,EAA8B,CAC5D,GAAI,CAACA,EACH,MAAO,CAAA,EAET,GAAI,CACF,OAAO,KAAK,MAAMA,EAAe,QAAQ,UAAW,GAAG,CAAC,CAC1D,MAAiB,CACf,QAAQ,MAAM,uCAAuC,CACvD,CACA,MAAO,CAAA,CACT,CAEA,SAASC,EAA2BD,EAA8C,CAChF,MAAME,EAAWF,EAAiBD,EAAwCC,CAAc,EAAI,CAAA,EACtFG,EAAsC,CAAA,EACtCC,EAAiC,CAAA,EACvC,IAAIC,EAAsB,GAE1B,QAASzC,EAAI,EAAGA,EAAIsC,EAAS,OAAQtC,IAAK,CACxC,MAAM0C,EAAgBJ,EAAStC,CAAC,EAAE,MAC9B0C,IAAkB,qBACpBD,EAAsB,GACtBD,EAAqB,KAAKlC,EAAgC,GAE1DiC,EAA0B,KAAKG,CAAa,CAEhD,CAEA,MAAO,CACL,0BAAAH,EACA,qBAAAC,EACA,oBAAAC,CAAA,CAEJ,CAEA,eAAeE,GAAyBC,EAA4BC,EAA+B,CACjG,MAAMzB,EAAiC,CAAA,EACvC,GAAIyB,EACF,UAAWC,KAAaF,EACtBxB,EAAW,KAAKyB,EAAS,IAAIC,CAAS,CAAC,EAI3C,OAAO,QAAQ,IAAI1B,CAAU,CAC/B,CAEA,SAAS2B,EAA0BC,EAA6E,CAC9G,GAAI,CAACA,EACH,MAAO,CAAA,EAGT,MAAMC,EAAuC,CAAA,EAC7C,QAASjD,EAAI,EAAGA,EAAIgD,EAAsB,OAAQhD,IAAK,CACrD,MAAMkD,EAAUF,EAAsBhD,CAAC,EACvCiD,EAAaC,EAAQ,KAAK,EAAIA,EAAQ,KACxC,CACA,OAAOD,CACT,CAEA,SAASE,EACPC,EACsC,CACtC,MAAMC,EAA4D,CAAA,EAClE,GAAI,CAAC,MAAM,QAAQD,CAA8B,EAC/C,OAAOC,EAET,QAASrD,EAAI,EAAGA,EAAIoD,EAA+B,OAAQpD,IAAK,CAC9D,MAAMkD,EAAUE,EAA+BpD,CAAC,EAChD,GAAI,CAACkD,GAAW,CAACI,EAASJ,EAAQ,KAAK,GAAK,CAACI,EAASJ,EAAQ,GAAG,EAC/D,SAGF,MAAMK,EAAqBL,EAAQ,MAC7BM,EAAoBN,EAAQ,IAE7BG,EAAoBE,CAAkB,IACzCF,EAAoBE,CAAkB,EAAI,CAAA,GAG5CF,EAAoBE,CAAkB,EAAE,KAAK,CAC3C,kBAAAC,EACA,WAAY,MAAM,QAAQN,EAAQ,UAAU,EAAIA,EAAQ,WAAa,CAAA,CAAC,CACvE,CACH,CACA,OAAOG,CACT,CAEA,SAASI,EAAiBC,EAAqBC,EAAmBC,EAAoC,CACpG,OAAO3C,EAAA,EAAK,aAAa,CAACyC,EAAaC,EAAWC,CAAS,EAAE,KAAK,EAAE,CAAC,CACvE,CAEA,SAASC,EAAQC,EAAyB,CACxC,OAAIA,GAAS,KAAa,GACtB,OAAOA,GAAU,SACZ,OAAO,KAAKA,CAAe,EAAE,SAAW,EAE7C,MAAM,QAAQA,CAAK,EACbA,EAAoB,SAAW,EAElC,EACT,CAEA,SAASR,EAASQ,EAAiC,CACjD,OAAO,OAAOA,GAAU,QAC1B,CAEA,SAASC,GAAwBC,EAAwC,CAGvE,IAAIC,EAAkB,mBAFChD,EAAA,EAAK,WAAA,EAEqC,SAD9C,SAGnB,OAAI+C,IACFC,GAAmB,IAAMD,GAEpBC,CACT,CAEA,SAASC,EAAKC,EAAqB,CACjC,IAAIC,EAAO,KACX,QAASpE,EAAI,EAAGA,EAAImE,EAAI,OAAQnE,IAC9BoE,GAAQA,GAAQ,GAAKA,EAAOD,EAAI,WAAWnE,CAAC,EAC5CoE,EAAOA,EAAOA,EAEhB,OAAOA,CACT,CAEA,SAASC,EAAwBC,EAAmB,CAClD,MAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,MAAM,QAAU,OACvBA,EAAO,aAAa,UAAW,iCAAiC,EAChEA,EAAO,IAAMD,EACbC,EAAO,OAAS,UAAY,CAC1BA,EAAO,OAAS,KACZA,EAAO,YACTA,EAAO,WAAW,YAAYA,CAAM,CAExC,EACA,MAAMxC,EAAS,SAAS,MAAQ,SAAS,KACrCA,GACFA,EAAO,YAAYwC,CAAM,CAE7B,CAEA,SAASC,EAA8BrD,EAA4BsD,EAA8B,CAC/F,MAAMC,EAAaR,EAAK,OAAO,SAAS,MAAM,EAM9C,GAL4BS,EAAQ,qBACZ,QAAQD,CAAU,IAAM,IAI5C,KAAK,OAAA,GAAYrE,EACnB,OAGF,MAAMuE,EAAO,OAAO,iBACpB,GAAI,CAACA,EACH,OAGF,MAAMC,EAAU,OAAO,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,EACzDC,EACJ,WACA,mBAAmBL,GAAW,EAAE,EAChC,yBACA,mBAAmBG,CAAI,EACvB,YACA,mBAAmBC,CAAO,EAG5BR,EAAwB,YADDlD,GAAU,iBACqB,4BAA8B2D,CAAM,EAE1FT,EACE,WAAajE,EAAyB,4BAA8B0E,EAAS,iBAAA,CAEjF,CAMA,SAASC,IAA+B,CACtC,OAAO,OAAO,OAAW,KAAe,CAAC,CAAC,OAAO,UAAU,QAAQ,YAAA,EAAc,SAAS,wBAAwB,CACpH,CAEA,SAASC,IAAuC,CAC9C,OAAO,OAAO,OAAW,IAAc,OAAO,UAAU,KAAO,MACjE,CAEA,SAASC,IAAoC,CAC3C,OAAO,OAAO,OAAW,IAAc,OAAO,WAAW,UAAY,MACvE,CAEA,MAAMC,CAAY,CAAlB,aAAA,CACE,KAAQ,UAAoC,CAAA,CAAC,CAE7C,kBAAkBC,EAA2B,CAE3C,MAAMC,GADQ,KAAK,UAAUD,CAAQ,GAAK,GACjB,EACzB,YAAK,UAAUA,CAAQ,EAAIC,EACpBA,EAAWpE,EACpB,CACF,CAEA,MAAMqE,CAAmB,CAQvB,YACEC,EACArB,EACAsB,EACAC,EACAC,EACA,CARF,KAAiB,UAAY,UAS3B,MAAMC,EAAmBJ,EAAO,iBAChC,KAAK,iBAAmBrB,GAAmB,GAC3C,KAAK,sBAAwBsB,EAC7B,KAAK,WAAaC,GAAa,KAC/B,KAAK,aAAeC,GAAe,IAAIP,EACvC,KAAK,WAAaH,MAAyBW,CAC7C,CAEA,KACEC,EACAR,EACAS,EACAC,EACAC,EACAC,EACM,CACN,GAAI,GAAC,KAAK,YAAc,KAAK,aAAa,kBAAkBZ,CAAQ,GAIpE,GAAI,CACF,MAAMa,EAAa,CACjB,sBAAuB,CACrB,QAASJ,EACT,QAAS,KAAK,gBAAA,EAEhB,SAAAT,EACA,KAAMU,GAAQlF,EAAW,cACzB,IAAKqE,GAAA,EACL,WAAYC,GAAA,EACZ,WAAAa,EACA,SAAU,KAAK,UACf,YAAa,KAAK,gBAAA,EAGdG,EAAkC,CACtC,OAAQ,2BACR,eAAgB,mBAChB,wBAAyB,KAAK,iBAC9B,oBAAqB,OAAA,EAGnB,KAAK,wBACPA,EAAQ,6BAA6B,EAAI,KAAK,uBAE5C,KAAK,aACPA,EAAQ,iBAAiB,EAAI,KAAK,YAGpC,MAAMN,EAAK,CACT,OAAQ,OACR,QAAAM,EACA,KAAM,KAAK,UAAUD,CAAU,CAAA,CAChC,EACE,KAAME,GAAuB,CAG5B,GAAI,CAACA,EAAS,GAAI,CAChB,MAAMC,EAA6B,IAAI,MAAM,QAAUD,EAAS,OAAS,oBAAoB,EAC7F,MAAAC,EAAY,WAAaD,EAAS,OAC5BC,CACR,CACF,CAAC,EACA,MAAOC,GAAyB,CAC/B,QAAQ,MAAM,yCAA0CA,CAAK,EACzDL,KAAiBK,CAAK,CAC5B,CAAC,CACL,OAASA,EAAO,CACd,QAAQ,MAAM,yCAA0CA,CAAK,EACzDL,KAAiBK,CAAsB,CAC7C,CACF,CACF,CAEA,MAAMC,CAAsB,CAI1B,YACEf,EACArB,EACAsB,EACAC,EACAC,EACA,CACA,KAAK,WAAa,IAAIJ,EAAmBC,EAAQrB,EAAiBsB,EAAsBC,EAAWC,CAAW,EAC9G,KAAK,UAAYjE,EAAqB8D,GAAQ,SAAUA,GAAQ,kBAAmBvE,EAAc,CACnG,CAEA,OAAOqF,EAA6C,CAClD,GAAI,CAACA,EAAO,OACZ,MAAMjB,EAAWiB,EAAM,UAAYxF,EAAkB,MACrD,KAAK,WAAW,KAAK,KAAK,UAAWuE,EAAUiB,EAAM,QAASA,EAAM,KAAMA,EAAM,UAAU,CAC5F,CACF,CAEA,MAAME,CAAe,CAKnB,YACEhB,EACAiB,EACAtC,EACAsB,EACAC,EACAC,EACA,CACA,KAAK,WAAa,IAAIJ,EAAmBC,EAAQrB,EAAiBsB,EAAsBC,EAAWC,CAAW,EAC9G,KAAK,YAAcjE,EAAqB8D,GAAQ,WAAYA,GAAQ,kBAAmBxE,EAAgB,EACvG,KAAK,uBAAyByF,CAChC,CAEA,IAAIC,EAA0C,CACvCA,GACL,KAAK,WAAW,KACd,KAAK,YACL5F,EAAkB,KAClB4F,EAAM,QACNA,EAAM,KACN,OACCJ,GAAyB,CACxB,GAAI,KAAK,uBAAwB,CAI/B,MAAMK,EAAe,OAAOL,EAAM,YAAe,SACjD,KAAK,uBAAuB,OAAO,CACjC,QAAS,uCAAyCA,EAAM,QACxD,KAAMzF,EAAW,qBACjB,SAAU8F,EAAe7F,EAAkB,MAAQA,EAAkB,OAAA,CACtE,CACH,CACF,CAAA,CAEJ,CACF,CAMA,MAAM8F,EAAN,MAAMA,CAAgC,CAAtC,aAAA,CAWE,KAAO,KAAOzG,EACd,KAAO,GAAKC,EACZ,KAAO,SAAWA,EAClB,KAAO,cAAgB,GACvB,KAAO,SAAgC,KACvC,KAAO,QAAsB,CAAA,EAC7B,KAAO,eAA0C,CAAA,EAGjD,KAAO,0BAA4B,GACnC,KAAO,YAAkC,KACzC,KAAO,4BAAsD,CAAA,EAC7D,KAAO,qCAA6E,CAAA,EACpF,KAAO,gBAAiC,KAExC,KAAO,sBAAsD,KAC7D,KAAO,eAAwC,KAK/C,KAAQ,+BAAsD,KAC9D,KAAQ,yBAA2B,GAMnC,KAAQ,gCAAwD,IAAA,CAYxD,uBAAuByG,EAAiBnD,EAAoC,CAClF,MAAM5D,EAAa+G,GAASA,EAAM,gBAKlC,MAJI,CAAC/G,GAID,OAAOA,EAAW4D,CAAiB,EAAM,IACpC,KAGF5D,EAAW4D,CAAiB,CACrC,CAEQ,iCAAiCoD,EAAoCC,EAA+B,CAC1G,GAAI,CAACD,GAAa,CAACtD,EAASsD,EAAU,QAAQ,EAC5C,MAAO,GAGT,MAAME,EAAWF,EAAU,SAAS,YAAA,EAC9BG,EAAgBH,EAAU,eAEhC,OAAIE,IAAa,SACRD,IAAgB,KAGrBA,GAAe,KACV,GAGLC,IAAa,SACR,OAAOD,CAAW,IAAM,OAAOE,CAAa,EAGjDD,IAAa,WACR,OAAOD,CAAW,EAAE,QAAQ,OAAOE,CAAa,CAAC,IAAM,GAGzD,EACT,CAEQ,mBAAmBJ,EAAiBK,EAAmC,CAC7E,GAAI,CAACA,GAAQ,CAAC1D,EAAS0D,EAAK,iBAAiB,EAC3C,MAAO,GAGT,MAAMC,EAAaD,EAAK,WACxB,GAAI,CAAC,MAAM,QAAQC,CAAU,EAC3B,MAAO,GAGT,MAAMJ,EAAc,KAAK,uBAAuBF,EAAOK,EAAK,iBAAiB,EAE7E,GAAIC,EAAW,SAAW,EACxB,OAAOJ,IAAgB,KAEzB,QAAS7G,EAAI,EAAGA,EAAIiH,EAAW,OAAQjH,IACrC,GAAI,CAAC,KAAK,iCAAiCiH,EAAWjH,CAAC,EAAG6G,CAAW,EACnE,MAAO,GAIX,MAAO,EACT,CAEQ,oCAAoCF,EAAuB,CACjE,MAAMtD,EAAsB,OAAO,KAAK,KAAK,oCAAoC,EACjF,QAAS,EAAI,EAAG,EAAIA,EAAoB,OAAQ,IAAK,CACnD,MAAME,EAAqBF,EAAoB,CAAC,EAC1C6D,EAA6B,KAAK,qCAAqC3D,CAAkB,EAC/F,GAAIM,EAAQqD,CAA0B,EACpC,SAIF,IAAIC,EAAW,GACf,QAASC,EAAI,EAAGA,EAAIF,EAA2B,OAAQE,IACrD,GAAI,CAAC,KAAK,mBAAmBT,EAAOO,EAA2BE,CAAC,CAAC,EAAG,CAClED,EAAW,GACX,KACF,CAEGA,GAILlG,EAAA,EAAK,KAAK,2BAA2BsC,EAAoB,EAAI,CAC/D,CACF,CAEQ,yBAAmC,CACzC,MAAO,CAAC,CAAC,OAAO,MAAQ,OAAO,OAAO,KAAK,gBAAmB,UAChE,CAKQ,qBAAqB8D,EAAuE,CAClG,GAAI,CAACA,GAAgB,CAACA,EAAa,kBACjC,MAAO,CAAA,EAGT,MAAMC,EAAkCD,EAAa,kBAAA,EAAoB,eAEzE,OAAO,KAAK,oCAAoCC,CAAc,CAChE,CAEQ,8BAAwD,CAC9D,MAAI,CAACrG,IAAK,MAAQ,OAAOA,EAAA,EAAK,KAAK,2BAA8B,WACxD,CAAA,EAEL4C,EAAQ,KAAK,2BAA2B,GAAKA,EAAQ,KAAK,oCAAoC,EACzF,CAAA,EAEF5C,EAAA,EAAK,KAAK,0BAAA,CACnB,CAEQ,oCAAoCqG,EAAyD,CACnG,MAAMC,EAA4C,CAAE,GAAID,GAAkB,EAAC,EACrE5H,EAAM,KAAK,sBACjB,OAAIA,GAAO4H,EAAe5H,CAA4B,IACpD6H,EAAkBb,EAAQ,gBAAgB,EAAIY,EAAe5H,CAA4B,GAEvFA,GACF,OAAO6H,EAAkB7H,CAAG,EAGvB6H,CACT,CAEQ,yBAAyB3H,EAA2B,CAK1D,GAJI,CAAC,OAAO,WAAa,OAAOqB,EAAA,EAAK,UAAa,YAI9C,CAACgB,EAASrC,CAAU,EACtB,OAGF,MAAM4H,EAAmBvG,IAAK,UAAU,MAExCA,EAAA,EAAK,SAASd,EAA8BqH,EAAkB5H,CAAqC,CACrG,CAEQ,iBAAiB6H,EAAyB,CAChD,GAAI,GAACA,GAAa,OAAOA,GAAc,UAGvC,GAAI,CACF,MAAMC,EAAazG,EAAA,EAAK,YAAA,EACpByG,GAAc,OAAOA,EAAW,yBAA4B,YAC9DA,EAAW,wBAAwBxH,EAAU,CAC3C,cAAeuH,CAAA,CAChB,CAEL,MAAa,CAEb,CACF,CAEQ,eACNjC,EACAmC,EACAnF,EAAiC,CAAA,EAC3B,CACN,MAAMoF,EACJ3G,EAAA,GAAQA,EAAA,EAAK,gBAAkB,OAAOA,EAAA,EAAK,eAAgB,YAAe,WACtEA,EAAA,EAAK,eAAgB,aACrB,OAEA4G,EAAmC,CACvC,UAAArC,EACA,GAAImC,GAAmB,CAAA,EACvB,GAAIC,EAAc,CAAE,YAAAA,GAAgB,CAAA,CAAC,EAGvC,IAAIE,EACA,KAAK,oCACPA,EAAkB,QAAQ,QAAQ,OAAO,KAAM,oBAAoBD,CAAO,CAAC,EAE3EC,EAAkB,OAAO,KAAM,eAAeD,CAAO,EAGvDC,EACG,KAAK,MAAOjF,GAAa,CACxB,MAAMF,GAAyBH,EAAsBK,CAAQ,EAC7D,KAAK,iBAAiBA,CAAQ,CAChC,CAAC,EACA,MAAOkF,GAAiB,CACvB,QAAQ,MAAM,gCAAiCA,CAAG,CACpD,CAAC,CACL,CAEQ,iBAAiBlF,EAA8B,CAEjD,OAAO,OACT,OAAO,KAAK,gBAAkBA,GAGhC,KAAK,SAAWA,EAEhB,MAAMmF,EAAc/G,IAAK,MAAM,QAE1B+G,GAGH,KAAK,QAAUA,EACVA,EAAY,aAGf,KAAK,gCAAkC,KAAK,OAAOA,EAAY,YAAY,EAF3E,QAAQ,KAAK,0CAA0C,GAJzD,QAAQ,KAAK,qCAAqC,EAWpD,KAAK,cAAgB,GAErBxD,EAA8B,KAAK,OAAQ,KAAK,eAAe,EAG/DvD,IAAK,KAAK,UAAU,IAAI,CAC1B,CAEQ,iBAA2C,CACjD,MAAMgH,EAAahH,EAAA,EAChB,qBAAA,EACA,OAAQiH,GAAcA,EAAU,OAAS,YAAY,EAExD,GAAI,CACF,GAAID,EAAW,OAAS,GAAK,OAAO,WAAY,CAC9C,MAAME,EAAkB,OAAO,WAAW,IAAI,OAAO,EACrD,MAAI,CAACA,GAAmB,CAACA,EAAgB,uBAChC,CAAA,EAEmBA,EAAgB,uBAAA,EACE,OAAO,CAACC,EAA6BC,KACjFD,EAAI,qCAAuCC,EAAQ,cAAc,EAC/DF,EAAgB,gBAAA,EAAkBE,CAAK,EAAE,GACpCD,GACN,CAAA,CAAE,CAEP,CACF,OAAShC,EAAO,CACd,QAAQ,MAAM,wCAAyCA,CAAK,CAC9D,CACA,MAAO,CAAA,CACT,CAEQ,YAAsB,CAC5B,MAAO,CAAC,EAAE,KAAK,eAAiB,KAAK,SACvC,CAEQ,mCAA6C,CACnD,MAAO,CAAC,EAAEnF,EAAA,EAAK,QAAUA,IAAK,OAAQ,wBAA0B,KAAK,0BACvE,CAEQ,yBAAmC,CAEzC,OAAO,KAAK,SAAW,EACzB,CAEQ,cAAcqH,EAA0B,CAC1C,QAAUrH,EAAA,GAAQA,EAAA,EAAK,eAAiBqH,GAC1CrH,EAAA,EAAK,cAAeqH,CAAU,CAElC,CAOO,KACLhG,EACAiG,EACAC,EACAC,EACAC,EACQ,CACR,MAAMC,EAAcrG,EACdkD,EAAYmD,EAAY,UAC9B,KAAK,eAAiBhJ,EAA2D+I,CAAsB,EACvG,KAAK,uBAAyBC,EAAY,sBAE1C,MAAM3F,EAAwBb,EAAgDwG,EAAY,qBAAqB,EAC/G,KAAK,4BAA8B5F,EAA0BC,CAAqB,EAElF,MAAMI,EAAiCjB,EACrCwG,EAAY,8BAAA,EAEd,KAAK,qCAAuCxF,EAAmCC,CAA8B,EAGzGuF,EAAY,8BACd,KAAK,sBAAwBA,EAAY,4BAA4B,YAAA,GAGvE,KAAK,uBAAyBrF,EAASqF,EAAY,qBAAqB,EACpEA,EAAY,sBACZ,OAEJ,MAAMxH,EAASF,IAAK,MAAM,OACpB,CAAE,0BAAAsB,EAA2B,qBAAAC,EAAsB,oBAAAC,CAAA,EAAwBJ,EAC/EsG,EAAY,cAAA,EAERhB,EAA2C,CAC/C,GAAK1G,EAAA,EAAK,MAAM,iBAA+C,CAAA,CAAC,EAElE,KAAK,gBAAkB8C,GAAwB4D,EAAgB,eAAqC,EACpGA,EAAgB,gBAAkB,KAAK,gBAEvC,KAAK,OAASxG,EAEd,MAAMyH,EAAmC,CACvC,WAAYD,EAAY,WACxB,SAAUA,EAAY,SACtB,kBAAmBxH,EACnB,iBAAkBF,EAAA,EAAK,QAAQ,mBAAqB,EAAA,EAEhDsF,EAAwB,IAAIF,EAChCuC,EACA,KAAK,gBACL,OAAO,iBACPD,EAAY,SAAA,EAERE,EAAiB,IAAIvC,EACzBsC,EACArC,EACA,KAAK,gBACL,OAAO,iBACPoC,EAAY,SAAA,EAad,OAVA,KAAK,sBAAwBpC,EAC7B,KAAK,eAAiBsC,EAElB5H,EAAA,EAAK,gCACPA,EAAA,EAAK,+BAAgCsF,CAAqB,EAExDtF,EAAA,EAAK,yBACPA,EAAA,EAAK,wBAAyB4H,CAAc,EAG1CL,GACF,KAAK,YAAc,CACjB,uBAAAtH,EACA,8BAAAK,EACA,2BAAAc,EACA,iBAAAoB,EACA,oBAAAtB,EACA,0BAAAY,EACA,mCAAAI,EACA,8BAAAqB,EACA,wBAAAH,EACA,KAAAH,EACA,uBAAyB4E,GAAqB,CAC5CpC,EAAQ,qBAAuBoC,CACjC,EACA,mBAAAzD,EACA,sBAAAgB,EACA,eAAAC,EACA,YAAApB,EACA,WAAAvE,EACA,kBAAAC,CAAA,EAEF,KAAK,eAAe4E,EAAWmC,CAAe,EACvC,6BAA+B1H,IAGpCwC,IACFxB,IAAK,KAAK,uCAAuC,IAAI,EACrDU,EAAenB,GAAkCe,EAA8BJ,CAAM,EAAG,CACtF,OAAQ,IAAM,CACZ,KAAK,yBAA2B,GAC5B,KAAK,gCACP,KAAK,+BAAA,CAET,EACA,QAAUiF,GAAU,CAClB,QAAQ,MAAM,+CAAgDA,CAAK,CACrE,CAAA,CACD,GAGC,KAAK,0BACP,KAAK,eAAeZ,EAAWmC,EAAiBnF,CAAoB,GAEpEb,EAAepB,GAA4BW,EAAuBC,EAAQoB,CAAyB,EAAG,CACpG,OAAQ,IAAM,CACR,KAAK,0BACP,KAAK,eAAeiD,EAAWmC,EAAiBnF,CAAoB,EAEpE,QAAQ,MAAM,iDAAiD,CAEnE,EACA,QAAU4D,GAAU,CAClB,QAAQ,MAAM,sCAAuCA,CAAK,CAC5D,CAAA,CACD,EAED,KAAK,cAAcM,EAAQ,kBAAkB,kBAAkB,GAG1D,6BAA+BzG,EACxC,CAEO,QAAQ0G,EAAyB,CACtC,GAAI,CAAC,KAAK,aACR,MAAO,gCAAkC1G,EAE3C,GAAI,OAAOgB,EAAA,EAAK,MAAM,0BAA6B,aAC5C4C,EAAQ,KAAK,oCAAoC,GACpD,KAAK,oCAAoC8C,CAAK,EAG5C,CAAC9C,EAAQ,KAAK,2BAA2B,GAAG,CAC9C,MAAMkF,EAActF,EAAiBkD,EAAM,cAAeA,EAAM,cAAeA,EAAM,WAAa,EAAE,EAChG,KAAK,4BAA4B,OAAOoC,CAAW,CAAC,GACtD9H,EAAA,EAAK,KAAK,2BAA2B,KAAK,4BAA4B,OAAO8H,CAAW,CAAC,EAAG,EAAI,CAEpG,CAGF,MAAO,mCAAqC9I,CAC9C,CAEO,iBAAiB+I,EAAqD,CAC3E,GAAI,CAAC,KAAK,aAAc,CACtB,QAAQ,MAAM,2BAA2B,EACzC,MACF,CAEA,OAAO,KAAM,iBAAiBA,CAAoB,CACpD,CAEO,iBAAiBtJ,EAAaoE,EAAwB,CAC3D,OAAKrE,EAA6CC,CAAG,IACnD,KAAK,eAAeA,CAAG,EAAIoE,GAEtB,kDAAoD7D,CAC7D,CAEO,oBAAoBP,EAAqB,CAC9C,cAAO,KAAK,eAAeA,CAAG,EACvB,sDAAwDO,CACjE,CAEQ,uBAAuBgJ,EAAsBC,EAA8B,CACjF,YAAK,eAAiBvJ,EAA2DsJ,EAAK,qBAAA,CAAsB,EACrG,uBAAyBC,EAAe,mBAAqBjJ,CACtE,CAEO,iBAAiBgJ,EAA8B,CACpD,MAAM5B,EAAe4B,EACrB,YAAK,QAAQ,aAAe5B,EAC5B,KAAK,gCAAkC,KAAK,OAAOA,CAAY,EACxD,KAAK,uBAAuB4B,EAAM,kBAAkB,CAC7D,CAEQ,OAAO5B,EAA2C,CACxD,MAAM8B,EAAS,KAAK,uBACpB,GAAI,CAACA,EACH,YAAK,0BAA4B,GACjC,KAAK,oCAAsC,OACpC,QAAQ,QAAA,EAEjB,MAAMC,EAASnI,IAAK,UAAU,OAC9B,GAAI,OAAOmI,GAAW,WACpB,YAAK,0BAA4B,GACjC,KAAK,oCAAsC,OACpC,QAAQ,QAAA,EAGjB,MAAM9B,EAAyCD,EAAa,kBACxDA,EAAa,kBAAA,EAAoB,eACjC,KAMEgC,EAA0C,CAAA,EAChD,GAAI/B,EACF,UAAW5H,KAAO,OAAO,KAAK4H,CAAc,EAAmC,CAC7E,MAAMxD,EAAQwD,EAAe5H,CAAG,EAC5B4D,EAASQ,CAAK,GAAKA,EAAM,OAAS,IACpCuF,EAAgB3J,CAAG,EAAIoE,EAE3B,CAGF,MAAMwF,EAAe,OAAO,KAAKD,CAAe,EAChD,GAAIC,EAAa,SAAW,EAC1B,YAAK,0BAA4B,GACjC,KAAK,oCAAsC,OACpC,QAAQ,QAAA,EAMjB,MAAMC,EAAgBD,EACnB,KAAA,EACA,IAAKE,GAAM,GAAGA,CAAC,IAAIH,EAAgBG,CAAC,CAAC,EAAE,EACvC,KAAK,GAAG,EAKX,OAAID,IAAkB,KAAK,oCAClB,KAAK,iCAAmC,QAAQ,QAAA,GAMzD,KAAK,0BAA4B,GACjC,KAAK,oCAAsCA,EAEpC,IAAI,QAAeE,GAAY,CACpC,GAAI,CACFL,EAAOD,EAAQE,EAAqCK,GAAkC,CAChFA,GAAQ,WAAa,MACvB,KAAK,0BAA4B,IAEnCD,EAAA,CACF,CAAC,CACH,OAAS1B,EAAK,CACZ,QAAQ,MAAM,2CAA4CA,CAAG,EAI7D,KAAK,oCAAsC,OAC3C0B,EAAA,CACF,CACF,CAAC,EACH,CAEO,gBAAgBR,EAAsBU,EAA2C,CACtF,OAAO,KAAK,uBAAuBV,EAAM,iBAAiB,CAC5D,CAEO,iBAAiBA,EAAsBU,EAA2C,CAKvF,YAAK,0BAA4B,GACjC,KAAK,gCAAkC,KACvC,KAAK,oCAAsC,OACpC,KAAK,uBAAuBV,EAAM,kBAAkB,CAC7D,CAEO,iBAAiBA,EAAsBU,EAA2C,CACvF,OAAO,KAAK,uBAAuBV,EAAM,kBAAkB,CAC7D,CAyBO,iBAAiBpB,EAAsF,CAC5G,GAAI,KAAK,gCAAiC,CACxC,MAAM+B,EAAW,KAAK,gCACtB,OAAO,QAAQ,KAAK,CAClBA,EACA,IAAI,QAAeH,GAAY,WAAWA,EAAS/I,EAAkC,CAAC,CAAA,CACvF,EAAE,KAAK,IAAM,KAAK,oBAAoBmH,CAAO,CAAC,CACjD,CACA,OAAO,KAAK,oBAAoBA,CAAO,CACzC,CAEQ,oBAAoBA,EAAsF,CAChH,MAAMjI,EAAeiI,GAAYA,EAAQ,YAA2C,CAAA,EAE9EgC,EAA+C,CAAE,GAD1BlK,EAA2D,KAAK,cAAc,EAC3B,GAAGC,CAAA,EAE7EkK,EAAU,KAAK,SAAW,CAAA,EAC1BC,EAAwBD,EAAQ,sBAAqC,CAAA,EACrEzC,EAAeyC,EAAQ,cAAgB,KACvCE,EAAO3C,EAAeA,EAAa,QAAA,EAAY,KAErD,IAAIxH,EAECiK,EAGMA,EAAQ,qBACjBjK,EAAqBiK,EAAQ,qBAAqBD,EAAqBE,CAAoB,EAE3FlK,EAAqBgK,GALrB,QAAQ,KAAK,uDAAuD,EACpEhK,EAAqBgK,GAOvB,KAAK,eAAiBlK,EAA2DE,CAAkB,EAEnG,MAAMoK,EAAuB,KAAK,yBAA2B,aAAe,KAAK,gBAAA,EAAoB,CAAA,EAE/FC,EAAyB,KAAK,qBAAqB7C,CAAY,EAE/D8C,EAAyB,KAAK,6BAAA,EAE9BC,EAAsD,CAC1D,GAAIF,EACJ,GAAGrK,EACH,GAAGoK,EACH,GAAGE,EACH,GAAI,KAAK,0BAA4B,CAAE,CAAC1J,EAAgC,EAAG,EAAA,EAAS,CAAA,EACpF,KAAAuJ,CAAA,EAGIK,EAAmD,CAAE,GAAGxC,EAAS,WAAYuC,CAAA,EAE7EE,EAAY,KAAK,SAAU,iBAAiBD,CAAuB,EAGnEE,EAAe,IAAM,KAAK,yBAAyBH,CAA0B,EAEnF,OAAK,QAAQ,QAAQE,CAAS,EAC3B,KAAME,GAAQA,GAAK,SAAS,WAAW,KAAM/C,GAAc,KAAK,iBAAiBA,CAAS,CAAC,CAAC,EAC5F,MAAM,MAAe,EACrB,QAAQ8C,CAAY,EAEhBD,CACT,CAKO,eAAe1K,EAA8E,CAClG,OAAK,KAAK,aAIH,KAAK,SAAU,eAAeA,CAAU,GAH7C,QAAQ,MAAM,2BAA2B,EAClC,KAGX,CAOO,IAAI8C,EAAyC,CAClD,OAAK,KAAK,aAIN,CAACA,GAAiB,CAACY,EAASZ,CAAa,EACpC,QAAQ,OAAO,IAAI,MAAM,kCAAkC,CAAC,EAE9D,KAAK,SAAU,IAAIA,CAAa,GANrC,QAAQ,MAAM,2BAA2B,EAClC,QAAQ,OAAO,IAAI,MAAM,2BAA2B,CAAC,EAMhE,CAKO,oBAAoB+H,EAAsB,CAC3C,KAAK,yBACPA,EAAA,EAEA,KAAK,+BAAiCA,CAE1C,CACF,EAztBE/D,EAAc,qBAAiC,CAAC,WAAY,SAAS,EAErEA,EAAwB,kBAAoB,CAC1C,mBAAoB,uBAAA,EAGtBA,EAAwB,iBAAmB,cAR7C,IAAM/B,EAAN+B,EAiuBA,SAASgE,IAAgB,CACvB,OAAOxK,CACT,CAEA,SAASyK,GAASrF,EAAkD,CAClE,GAAI,CAACA,EAAQ,CACX,OAAO,QAAQ,IAAI,qDAAuDrF,CAAI,EAC9E,MACF,CACA,GAAI,CAACgC,EAASqD,CAAM,EAAG,CACrB,OAAO,QAAQ,IAAI,+CAAiD,OAAOA,CAAM,EACjF,MACF,CAEIrD,EAASqD,EAAO,IAAI,EACrBA,EAAO,KAAiCrF,CAAI,EAAI,CAC/C,YAAa0E,CAAA,GAGfW,EAAO,KAAO,CAAA,EACdA,EAAO,KAAKrF,CAAI,EAAI,CAClB,YAAa0E,CAAA,GAGjB,OAAO,QAAQ,IAAI,2BAA6B1E,EAAO,kCAAkC,CAC3F,CAEA,OAAI,OAAO,OAAW,KAAe,OAAO,WAAagB,EAAA,EAAK,cAC5DA,EAAA,EAAK,aAAa,CAChB,KAAAhB,EACA,YAAa0E,EACb,MAAA+F,EAAA,CACD"} \ No newline at end of file +{"version":3,"file":"Rokt-Kit.iife.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n debugger;\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file diff --git a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md index 6437e10..7320e39 100644 --- a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md +++ b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md @@ -42,9 +42,12 @@ last **N = 25** entries, newest last: ```ts interface StoredPageView { - name: string; // event.EventName - url: string; // window.location.href (full, verbatim — see Security note) - timestamp: number; // event.Timestamp + name: string; // event.EventName + pageUrl: string; // window.location.href (full, verbatim — see Security note) + sourceMessageId: string; // event.SourceMessageId + timestamp: number; // event.Timestamp + activeTimeOnSite: number; // event.ActiveTimeOnSite + eventAttributes?: { [key: string]: string }; // event.EventAttributes (see Security note) } ``` @@ -61,7 +64,17 @@ After the existing readiness check, and guarded by capture (existing placement-mapping logic is unaffected). 2. Read the current list: `mp().Rokt.getLocalSessionAttributes()?.[PAGE_VIEWS_KEY]`, defaulting to `[]`. Coerce non-arrays to `[]` defensively. -3. Build the record: `{ name: event.EventName, url: sanitizeUrl(window.location.href), timestamp: event.Timestamp }`. +3. Build the record from the event and current location: + ```ts + { + name: event.EventName, + pageUrl: sanitizeUrl(window.location.href), + sourceMessageId: event.SourceMessageId, + timestamp: event.Timestamp, + activeTimeOnSite: event.ActiveTimeOnSite, + eventAttributes: event.EventAttributes, + } + ``` 4. Append; if `list.length > MAX_PAGE_VIEWS`, drop from the front (evict oldest). 5. Write back via `mp().Rokt.setLocalSessionAttribute(PAGE_VIEWS_KEY, list)`. @@ -88,7 +101,7 @@ fragment later is a one-line change inside this helper and touches nothing else. | Decision | Choice | Rationale | |----------|--------|-----------| | Downstream purpose | Feed the next `selectPlacements` | Reuses existing `returnLocalSessionAttributes()` path | -| Payload per view | Full list, last N (name + url + timestamp) | Richest targeting signal | +| Payload per view | Full list, last N (name, pageUrl, sourceMessageId, timestamp, activeTimeOnSite, eventAttributes) | Richest targeting signal | | Detection | `event.EventDataType === 3` (PageView) | Standard mParticle page-view classification | | List cap (N) | 25 | User choice; see size caveat below | | URL handling | Full URL verbatim | User choice; see Security note | @@ -102,6 +115,12 @@ The full-URL choice is implemented as requested; the recommended safer default is to strip the query and fragment. The `sanitizeUrl()` helper isolates this so it can be tightened later without touching capture logic. +`eventAttributes` is stored verbatim from the event and can likewise contain +arbitrary developer-supplied values, including PII. It is persisted and sent to +Rokt under the same conditions. Note the SDK does not run kit user-attribute +filters over page-view `EventAttributes`, so nothing is stripped automatically — +flagging in case attribute-level filtering is wanted later. + **Size caveat:** N = 25 full URLs is persisted to cookie/localStorage-backed storage, which has size limits. If entries approach those limits, revisit N or the URL handling. @@ -111,7 +130,8 @@ the URL handling. Vitest cases in `test/src/tests.spec.ts`: 1. A page-view event (`EventDataType === 3`) appends a record with correct - `name`, `url`, and `timestamp`. + `name`, `pageUrl`, `sourceMessageId`, `timestamp`, `activeTimeOnSite`, and + `eventAttributes`. 2. A non-page-view event does not append. 3. The list caps at `MAX_PAGE_VIEWS` and evicts the oldest entry. 4. Capture no-ops when `setLocalSessionAttribute` is unavailable (does not throw). diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 2973141..f49d281 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -1162,6 +1162,7 @@ class RoktKit implements KitInterface { } public process(event: SDKEvent): string { + debugger; if (!this.isKitReady()) { return 'Kit not ready for forwarder: ' + name; } From d16c1abc53552f8cae01567cee32e9ae25be4192 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 15:19:01 -0400 Subject: [PATCH 03/27] feat: surface captured page views as flat page_events in selectPlacements Derive a flat page_events array from stored page views at selectPlacements time. Each view's eventAttributes are exploded into attr_-namespaced keys; the title attribute is surfaced as page_name and EventName as event_name. The raw nested mpPageViews store is stripped so only the flattened copy is sent. Relax returnLocalSessionAttributes to no longer require a placement mapping. Restore the isKitReady guard in process() and remove debug logging. --- src/Rokt-Kit.ts | 111 +++++++++++++- test/src/tests.spec.ts | 319 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 404 insertions(+), 26 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index f49d281..2906af2 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -61,6 +61,19 @@ interface RoktExtensionEntry { value: string; } +// A captured page view, persisted (newest last) under PAGE_VIEWS_KEY. +// See the security note in the design spec: pageUrl and eventAttributes are +// stored verbatim and may contain PII; they are persisted to browser storage +// and sent to Rokt on the next selectPlacements call. +interface StoredPageView { + name: string; // event.EventName + pageUrl: string; // window.location.href (see sanitizeUrl) + sourceMessageId: string; // event.SourceMessageId + timestamp: number; // event.Timestamp + activeTimeOnSite: number; // event.ActiveTimeOnSite + eventAttributes?: { [key: string]: string }; // event.EventAttributes +} + interface RoktSelection { context?: { sessionId?: Promise; @@ -244,6 +257,20 @@ const ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher'; const ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element'; const USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace'; +// Page-view capture. Page views are identified by the mParticle message type +// PageView (3); the last MAX_PAGE_VIEWS are stored under PAGE_VIEWS_KEY in the +// Rokt manager's local session attributes so they flow into selectPlacements. +const MESSAGE_TYPE_PAGE_VIEW = 3; +const PAGE_VIEWS_KEY = 'mpPageViews'; +const MAX_PAGE_VIEWS = 25; +// Flat page-view array sent to selectPlacements: each StoredPageView with its +// eventAttributes exploded into PAGE_EVENT_ATTR_PREFIX-namespaced top-level keys. +const PAGE_EVENTS_KEY = 'page_events'; +const PAGE_EVENT_ATTR_PREFIX = 'attr_'; +// The page-view event attribute holding the document title; surfaced as the +// dedicated page_name field rather than an attr_-namespaced key. +const PAGE_TITLE_ATTRIBUTE = 'title'; + // Bound on how long selectPlacements will wait for an in-flight Workspace // IDSync search before proceeding without the userIdentifiedInWorkspace flag. // Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a @@ -450,6 +477,13 @@ function isString(value: unknown): value is string { return typeof value === 'string'; } +// Isolates page-view URL handling. Returns the URL verbatim for now (per the +// design decision). Tightening to strip query/fragment later is a one-line +// change here and touches nothing else. See the security note in the spec. +function sanitizeUrl(href: string): string { + return href; +} + function generateIntegrationName(customIntegrationName?: string): string { const coreSdkVersion = mp().getVersion(); const kitVersion = process.env.PACKAGE_VERSION; @@ -845,6 +879,34 @@ class RoktKit implements KitInterface { } } + // Appends a page-view record to the persisted list under PAGE_VIEWS_KEY, + // capping at MAX_PAGE_VIEWS (oldest evicted). Wrapped so a malformed event + // can never throw out of the forwarder. Callers must confirm the event is a + // page view and that setLocalSessionAttribute is available. + private capturePageView(event: SDKEvent): void { + try { + const existing = mp().Rokt.getLocalSessionAttributes?.()?.[PAGE_VIEWS_KEY]; + const pageViews: StoredPageView[] = Array.isArray(existing) ? (existing as StoredPageView[]) : []; + + pageViews.push({ + name: event.EventName, + pageUrl: sanitizeUrl(window.location.href), + sourceMessageId: event.SourceMessageId, + timestamp: event.Timestamp, + activeTimeOnSite: event.ActiveTimeOnSite, + eventAttributes: event.EventAttributes, + }); + + while (pageViews.length > MAX_PAGE_VIEWS) { + pageViews.shift(); + } + + mp().Rokt.setLocalSessionAttribute?.(PAGE_VIEWS_KEY, pageViews); + } catch (err) { + console.error('Rokt Kit: Failed to capture page view', err); + } + } + private isLauncherReadyToAttach(): boolean { return !!window.Rokt && typeof window.Rokt.createLauncher === 'function'; } @@ -866,12 +928,32 @@ class RoktKit implements KitInterface { if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') { return {}; } - if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) { - return {}; - } return mp().Rokt.getLocalSessionAttributes!(); } + private buildPageEvents(pageViews: StoredPageView[]): Record[] { + return pageViews.map((pv) => { + const flat: Record = {}; + if (pv.eventAttributes) { + for (const [key, value] of Object.entries(pv.eventAttributes)) { + // `title` is surfaced as the dedicated page_name field below, so it is + // not also emitted as an attr_-namespaced key. + if (key === PAGE_TITLE_ATTRIBUTE) { + continue; + } + flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; + } + } + flat.event_name = pv.name; + flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; + flat.pageUrl = pv.pageUrl; + flat.sourceMessageId = pv.sourceMessageId; + flat.timestamp = pv.timestamp; + flat.activeTimeOnSite = pv.activeTimeOnSite; + return flat; + }); + } + private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record { const newUserIdentities: Record = { ...(userIdentities || {}) }; const key = this._mappedEmailSha256Key; @@ -1162,11 +1244,19 @@ class RoktKit implements KitInterface { } public process(event: SDKEvent): string { - debugger; - if (!this.isKitReady()) { - return 'Kit not ready for forwarder: ' + name; - } + console.warn('process Event', event); + console.warn('is kit ready?', this.isKitReady()); + // if (!this.isKitReady()) { + // console.warn('kit is ready'); + // return 'Kit not ready for forwarder: ' + name; + // } + if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { + if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { + console.warn('caputre Event', event); + this.capturePageView(event); + } + if (!isEmpty(this.placementEventAttributeMappingLookup)) { this.applyPlacementEventAttributeMapping(event); } @@ -1376,11 +1466,18 @@ class RoktKit implements KitInterface { const localSessionAttributes = this.returnLocalSessionAttributes(); + // Derive the flat page_events array from the stored page views, then drop the + // raw nested mpPageViews so Rokt receives only the flattened copy. + const rawPageViews = localSessionAttributes[PAGE_VIEWS_KEY]; + const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : []; + delete localSessionAttributes[PAGE_VIEWS_KEY]; + const selectPlacementsAttributes: Record = { ...(filteredUserIdentities as Record), ...filteredAttributes, ...optimizelyAttributes, ...localSessionAttributes, + ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: pageEvents } : {}), ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}), mpid, }; diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 338c70e..040b8e0 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -4867,7 +4867,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/home', }, @@ -4878,7 +4878,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/sale/items', }, @@ -4915,7 +4915,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/anything', }, @@ -4953,7 +4953,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { someOtherAttribute: 'value', }, @@ -4990,7 +4990,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/anything', }, @@ -5004,7 +5004,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { someOtherAttribute: 'value', }, @@ -5046,7 +5046,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { number_of_products: 2, }, @@ -5059,7 +5059,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { number_of_products: '2', }, @@ -5102,7 +5102,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { number_of_products: 2, }, @@ -5217,7 +5217,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { boolAttr: true, zeroAttr: 0, @@ -5265,7 +5265,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { otherAttr: 'value', }, @@ -5277,7 +5277,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, }); expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); @@ -5331,7 +5331,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/sale', }, @@ -5342,7 +5342,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/sale/items', }, @@ -5397,7 +5397,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/sale', }, @@ -5410,7 +5410,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/sale/items', }, @@ -5463,7 +5463,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { zeroProp: 0, falseProp: false, @@ -5511,7 +5511,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com', }, @@ -5558,7 +5558,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, + EventDataType: MessageType.PageEvent, EventAttributes: { URL: 'https://example.com/anything', }, @@ -5594,6 +5594,287 @@ describe('Rokt Forwarder', () => { 'foo-mapped-flag': true, }); }); + + describe('page view capture', () => { + it('appends a page view record with the expected fields when the event is a PageView', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-1', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + EventAttributes: { + hostname: 'example.com', + title: 'Home', + }, + }); + + expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toEqual([ + { + name: 'Home Page', + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-1', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + eventAttributes: { + hostname: 'example.com', + title: 'Home', + }, + }, + ]); + }); + + it('does not append a page view record for a non-PageView event', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Video Watched', + EventCategory: EventType.Other, + EventDataType: MessageType.PageEvent, + SourceMessageId: 'source-message-id-2', + Timestamp: 1712345679000, + ActiveTimeOnSite: 100, + }); + + expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toBeUndefined(); + }); + + it('caps the stored list at 25 entries and evicts the oldest', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + for (let i = 0; i < 30; i++) { + (window as any).mParticle.forwarder.process({ + EventName: 'Page ' + i, + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-' + i, + Timestamp: 1712345678000 + i, + ActiveTimeOnSite: i, + }); + } + + const stored = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; + expect(stored.length).toBe(25); + // Oldest five (Page 0..4) evicted; newest retained. + expect(stored[0].name).toBe('Page 5'); + expect(stored[24].name).toBe('Page 29'); + }); + + it('does not throw when setLocalSessionAttribute is unavailable', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + delete (window as any).mParticle.Rokt.setLocalSessionAttribute; + (window as any).mParticle._Store.localSessionAttributes = {}; + + expect(() => { + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-3', + Timestamp: 1712345678000, + ActiveTimeOnSite: 10, + }); + }).not.toThrow(); + + expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toBeUndefined(); + }); + + it('surfaces stored page views through selectPlacements as page_events without any placement mapping configured', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-4', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + // The raw nested store must not ride along; only the flat page_events array is sent. + expect(forwardedAttributes.mpPageViews).toBeUndefined(); + expect(forwardedAttributes.page_events).toEqual([ + { + event_name: 'Home Page', + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-4', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]); + }); + + it('explodes eventAttributes into attr_-namespaced keys in page_events', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Product Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-5', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + EventAttributes: { + category: 'shoes', + promo: 'x', + title: 'Product Page Title', + }, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + // `title` is surfaced as page_name and must NOT also appear as attr_title. + expect(forwardedAttributes.page_events).toEqual([ + { + attr_category: 'shoes', + attr_promo: 'x', + event_name: 'Product Page', + page_name: 'Product Page Title', + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-5', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]); + }); + + it('namespaces a colliding eventAttribute key so it cannot clobber a base field', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Real Name', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-6', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + EventAttributes: { + event_name: 'attribute-event-name', + }, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + expect(forwardedAttributes.page_events).toEqual([ + { + attr_event_name: 'attribute-event-name', + event_name: 'Real Name', + page_name: undefined, + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-6', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]); + }); + + it('does not add a page_events attribute when no page views are stored', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + expect(forwardedAttributes.page_events).toBeUndefined(); + expect(forwardedAttributes.mpPageViews).toBeUndefined(); + }); + }); }); describe('#_setRoktSessionId', () => { From ba89396db5bb94ac74ad05b64cd32cb007bb8e0b Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 15:47:01 -0400 Subject: [PATCH 04/27] feat: derive timeOnPage per entry in page_events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compute a timeOnPage field for each page_events entry at read time in buildPageEvents as the diff of consecutive activeTimeOnSite values — the active time a page was viewed before the next page view was logged. Emitted only when it is a genuine, non-negative number; omitted for the still-open last entry, negative diffs (clock skew, reset, out-of-order), and non-numeric activeTimeOnSite. Nothing new is persisted; StoredPageView is unchanged. --- .../2026-07-31-page-view-capture-design.md | 24 +++ src/Rokt-Kit.ts | 19 ++- test/src/tests.spec.ts | 143 ++++++++++++++++++ 3 files changed, 179 insertions(+), 7 deletions(-) diff --git a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md index 7320e39..3e51621 100644 --- a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md +++ b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md @@ -138,9 +138,33 @@ Vitest cases in `test/src/tests.spec.ts`: 5. Stored page views surface through `returnLocalSessionAttributes()` and into `selectPlacements`. +## Time on page (derived output) + +Each `page_events` entry carries a `timeOnPage` field: the active time the user +spent on *that* page before the next page view was logged. It is computed at +read time in `buildPageEvents()` as the diff of consecutive `activeTimeOnSite` +values: + +``` +entry[i].timeOnPage = pageViews[i+1].activeTimeOnSite − pageViews[i].activeTimeOnSite +``` + +Nothing new is persisted; `StoredPageView` is unchanged. `timeOnPage` is only +emitted when it is a genuine, non-negative number. It is **omitted** (left +`undefined`) for: + +- the last (still-open) entry — there is no next page view yet; +- a negative diff (clock skew, session reset, out-of-order events) — dropped + rather than surfaced as a misleading value; +- a missing/non-numeric `activeTimeOnSite` on either side. + +`undefined` uniformly means "couldn't compute / not yet known." + ## Out of scope - No new kit setting / server-side feature gate (capture is always on when the store is available). - No query-string stripping (deferred behind `sanitizeUrl()`). - No changes to placement-event mapping behavior. +- Time on page is derived at read time only — no change to capture, + persistence, or `StoredPageView`. diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 2906af2..e1fae50 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -932,7 +932,7 @@ class RoktKit implements KitInterface { } private buildPageEvents(pageViews: StoredPageView[]): Record[] { - return pageViews.map((pv) => { + return pageViews.map((pv, i) => { const flat: Record = {}; if (pv.eventAttributes) { for (const [key, value] of Object.entries(pv.eventAttributes)) { @@ -950,6 +950,14 @@ class RoktKit implements KitInterface { flat.sourceMessageId = pv.sourceMessageId; flat.timestamp = pv.timestamp; flat.activeTimeOnSite = pv.activeTimeOnSite; + + const next = pageViews[i + 1]; + if (next && typeof next.activeTimeOnSite === 'number' && typeof pv.activeTimeOnSite === 'number') { + const diff = next.activeTimeOnSite - pv.activeTimeOnSite; + if (diff >= 0) { + flat.timeOnPage = diff; + } + } return flat; }); } @@ -1244,12 +1252,9 @@ class RoktKit implements KitInterface { } public process(event: SDKEvent): string { - console.warn('process Event', event); - console.warn('is kit ready?', this.isKitReady()); - // if (!this.isKitReady()) { - // console.warn('kit is ready'); - // return 'Kit not ready for forwarder: ' + name; - // } + if (!this.isKitReady()) { + return 'Kit not ready for forwarder: ' + name; + } if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 040b8e0..23c9b25 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5874,6 +5874,149 @@ describe('Rokt Forwarder', () => { expect(forwardedAttributes.page_events).toBeUndefined(); expect(forwardedAttributes.mpPageViews).toBeUndefined(); }); + + it('surfaces timeOnPage as the active-time diff to the next page view', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-7', + Timestamp: 1712345678000, + ActiveTimeOnSite: 1000, + }); + (window as any).mParticle.forwarder.process({ + EventName: 'Product Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-8', + Timestamp: 1712345679000, + ActiveTimeOnSite: 4200, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + // First page's time-on-page is how long it was viewed before the next page: + // 4200 - 1000 = 3200. The last (still-open) page has no timeOnPage. + expect(forwardedAttributes.page_events).toEqual([ + { + event_name: 'Home Page', + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-7', + timestamp: 1712345678000, + activeTimeOnSite: 1000, + timeOnPage: 3200, + }, + { + event_name: 'Product Page', + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-8', + timestamp: 1712345679000, + activeTimeOnSite: 4200, + }, + ]); + }); + + it('computes a consecutive timeOnPage diff for each non-last page view', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Page A', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-9', + Timestamp: 1712345678000, + ActiveTimeOnSite: 1000, + }); + (window as any).mParticle.forwarder.process({ + EventName: 'Page B', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-10', + Timestamp: 1712345679000, + ActiveTimeOnSite: 2500, + }); + (window as any).mParticle.forwarder.process({ + EventName: 'Page C', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-11', + Timestamp: 1712345680000, + ActiveTimeOnSite: 9000, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + const pageEvents = forwardedAttributes.page_events; + expect(pageEvents[0].timeOnPage).toBe(1500); // 2500 - 1000 + expect(pageEvents[1].timeOnPage).toBe(6500); // 9000 - 2500 + expect(pageEvents[2].timeOnPage).toBeUndefined(); // still open + }); + + it('omits timeOnPage when the active-time diff would be negative', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Page A', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-12', + Timestamp: 1712345678000, + ActiveTimeOnSite: 5000, + }); + (window as any).mParticle.forwarder.process({ + EventName: 'Page B', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-13', + Timestamp: 1712345679000, + ActiveTimeOnSite: 1000, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + const pageEvents = forwardedAttributes.page_events; + // 1000 - 5000 = -4000 → omitted rather than emitting a misleading value. + expect(pageEvents[0].timeOnPage).toBeUndefined(); + expect(pageEvents[1].timeOnPage).toBeUndefined(); + }); }); }); From 59297d64f7a420e26ad08a3a32816b0aa8e8a1dd Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 15:58:19 -0400 Subject: [PATCH 05/27] chore: revert dist to match development (CI-generated, not hand-authored) --- dist/Rokt-Kit.common.js | 2 +- dist/Rokt-Kit.common.js.map | 2 +- dist/Rokt-Kit.esm.js | 535 +++++++++++++++++------------------- dist/Rokt-Kit.esm.js.map | 2 +- dist/Rokt-Kit.iife.js | 2 +- dist/Rokt-Kit.iife.js.map | 2 +- 6 files changed, 253 insertions(+), 292 deletions(-) diff --git a/dist/Rokt-Kit.common.js b/dist/Rokt-Kit.common.js index 09f811d..d1e4af3 100644 --- a/dist/Rokt-Kit.common.js +++ b/dist/Rokt-Kit.common.js @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const $=["active_time_on_site_ms","billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],Z=new Set($);function z(n){return Z.has(n.toLowerCase())}function S(n){const e={},t=n||{},i=Object.keys(t);for(let r=0;r=ie)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(e??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);P("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),P("https://"+te+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function _e(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function ye(){return typeof window<"u"?window.location?.href:void 0}function Ie(){return typeof window<"u"?window.navigator?.userAgent:void 0}class J{constructor(){this._logCount={}}incrementAndCheck(e){const i=(this._logCount[e]||0)+1;return this._logCount[e]=i,i>fe}}class U{constructor(e,t,i,r,s){this._reporter="mp-wsdk";const o=e.isLoggingEnabled;this._integrationName=t||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new J,this._isEnabled=_e()||o}send(e,t,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(t)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:t,code:r||N.UNKNOWN_ERROR,url:ye(),deviceInfo:Ie(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(e,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class H{constructor(e,t,i,r,s){this._transport=new U(e,t,i,r,s),this._errorUrl=B(e?.errorUrl,e?.integrationDomain,ge)}report(e){if(!e)return;const t=e.severity||k.ERROR;this._transport.send(this._errorUrl,t,e.message,e.code,e.stackTrace)}}class V{constructor(e,t,i,r,s,o){this._transport=new U(e,i,r,s,o),this._loggingUrl=B(e?.loggingUrl,e?.integrationDomain,pe),this._errorReportingService=t}log(e){e&&this._transport.send(this._loggingUrl,k.INFO,e.message,e.code,void 0,t=>{if(this._errorReportingService){const i=typeof t.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+t.message,code:N.LOG_DELIVERY_FAILURE,severity:i?k.ERROR:k.WARNING})}})}}const g=class g{constructor(){this.name=d,this.id=w,this.moduleId=w,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(e,t){const i=e&&e.EventAttributes;return!i||typeof i[t]>"u"?null:i[t]}doesEventAttributeConditionMatch(e,t){if(!e||!m(e.operator))return!1;const i=e.operator.toLowerCase(),r=e.attributeValue;return i==="exists"?t!==null:t==null?!1:i==="equals"?String(t)===String(r):i==="contains"?String(t).indexOf(String(r))!==-1:!1}doesEventMatchRule(e,t){if(!t||!m(t.eventAttributeKey))return!1;const i=t.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(e,t.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;sce;)i.shift();a().Rokt.setLocalSessionAttribute?.(b,i)}catch(t){console.error("Rokt Kit: Failed to capture page view",t)}}isLauncherReadyToAttach(){return!!window.Rokt&&typeof window.Rokt.createLauncher=="function"}returnUserIdentities(e){if(!e||!e.getUserIdentities)return{};const t=e.getUserIdentities().userIdentities;return this.replaceOtherIdentityWithEmailsha256(t)}returnLocalSessionAttributes(){return!a().Rokt||typeof a().Rokt.getLocalSessionAttributes!="function"?{}:a().Rokt.getLocalSessionAttributes()}buildPageEvents(e){return e.map((t,i)=>{const r={};if(t.eventAttributes)for(const[o,c]of Object.entries(t.eventAttributes))o!==K&&(r[`${ue}${o}`]=c);r.event_name=t.name,r.page_name=t.eventAttributes?.[K],r.pageUrl=t.pageUrl,r.sourceMessageId=t.sourceMessageId,r.timestamp=t.timestamp,r.activeTimeOnSite=t.activeTimeOnSite;const s=e[i+1];if(s&&typeof s.activeTimeOnSite=="number"&&typeof t.activeTimeOnSite=="number"){const o=s.activeTimeOnSite-t.activeTimeOnSite;o>=0&&(r.timeOnPage=o)}return r})}replaceOtherIdentityWithEmailsha256(e){const t={...e||{}},i=this._mappedEmailSha256Key;return i&&e[i]&&(t[g.EMAIL_SHA256_KEY]=e[i]),i&&delete t[i],t}logSelectPlacementsEvent(e){if(!window.mParticle||typeof a().logEvent!="function"||!O(e))return;const t=a().EventType.Other;a().logEvent(ee,t,e)}setRoktSessionId(e){if(!(!e||typeof e!="string"))try{const t=a().getInstance();t&&typeof t.setIntegrationAttribute=="function"&&t.setIntegrationAttribute(w,{roktSessionId:e})}catch{}}attachLauncher(e,t,i=[]){const r=a()&&a().sessionManager&&typeof a().sessionManager.getSession=="function"?a().sessionManager.getSession():void 0,s={accountId:e,...t||{},...r?{mpSessionId:r}:{}};let o;this.isPartnerInLocalLauncherTestGroup()?o=Promise.resolve(window.Rokt.createLocalLauncher(s)):o=window.Rokt.createLauncher(s),o.then(async c=>{await me(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(e){window.Rokt&&(window.Rokt.currentLauncher=e),this.launcher=e;const t=a().Rokt?.filters;t?(this.filters=t,t.filteredUser?this._workspaceSearchInFlightPromise=this.search(t.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,W(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const e=a()._getActiveForwarders().filter(t=>t.name==="Optimizely");try{if(e.length>0&&window.optimizely){const t=window.optimizely.get("state");return!t||!t.getActiveExperimentIds?{}:t.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=t.getVariationMap()[o].id,s),{})}}catch(t){console.error("Error fetching Optimizely attributes:",t)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(e){window&&a()&&a().captureTiming&&e&&a().captureTiming(e)}init(e,t,i,r,s){const o=e,c=o.accountId;this.userAttributes=S(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=L(o.placementEventMapping);this.placementEventMappingLookup=F(u);const l=L(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=j(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=m(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:v,legacyRoktExtensions:E,loadThankYouElement:R}=Y(o.roktExtensions),p={...a().Rokt?.launcherOptions||{}};this.integrationName=Ee(p.integrationName),p.integrationName=this.integrationName,this.domain=h;const y={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},I=new H(y,this.integrationName,window.__rokt_li_guid__,o.accountId),A=new V(y,I,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=I,this.loggingService=A,a()._registerErrorReportingService&&a()._registerErrorReportingService(I),a()._registerLoggingService&&a()._registerLoggingService(A),i?(this.testHelpers={generateLauncherScript:M,generateThankYouElementScript:D,extractRoktExtensionConfig:Y,hashEventMessage:G,parseSettingsString:L,generateMappedEventLookup:F,generateMappedEventAttributeLookup:j,sendAdBlockMeasurementSignals:W,createAutoRemovedIframe:P,djb2:q,setAllowedOriginHashes:f=>{g._allowedOriginHashes=f},ReportingTransport:U,ErrorReportingService:H,LoggingService:V,RateLimiter:J,ErrorCodes:N,WSDKErrorSeverity:k},this.attachLauncher(c,p),"Successfully initialized: "+d):(R&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),x(se,D(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:f=>{console.error("Error loading Rokt Thank You Element script:",f)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,p,E):(x(re,M(h,v),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,p,E):console.error("Rokt object is not available after script load.")},onError:f=>{console.error("Error loading Rokt launcher script:",f)}}),this.captureTiming(g.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(e){if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(e.EventDataType===ae&&(console.warn("caputre Event",e),this.capturePageView(e)),T(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(e),!T(this.placementEventMappingLookup))){const t=G(e.EventDataType,e.EventCategory,e.EventName??"");this.placementEventMappingLookup[String(t)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(t)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(e){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(e)}setUserAttribute(e,t){return z(e)||(this.userAttributes[e]=t),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(e){return delete this.userAttributes[e],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(e,t){return this.userAttributes=S(e.getAllUserAttributes()),"Successfully called "+t+" for forwarder: "+d}onUserIdentified(e){const t=e;return this.filters.filteredUser=t,this._workspaceSearchInFlightPromise=this.search(t),this.handleIdentityComplete(e,"onUserIdentified")}search(e){const t=this._workspaceIdSyncApiKey;if(!t)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=e.getUserIdentities?e.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];m(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(t,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(e,t){return this.handleIdentityComplete(e,"onLoginComplete")}onLogoutComplete(e,t){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(e,"onLogoutComplete")}onModifyComplete(e,t){return this.handleIdentityComplete(e,"onModifyComplete")}selectPlacements(e){if(this._workspaceSearchInFlightPromise){const t=this._workspaceSearchInFlightPromise;return Promise.race([t,new Promise(i=>setTimeout(i,de))]).then(()=>this._dispatchPlacements(e))}return this._dispatchPlacements(e)}_dispatchPlacements(e){const t=e&&e.attributes||{},r={...S(this.userAttributes),...t},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=S(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},v=this.returnUserIdentities(c),E=this.returnLocalSessionAttributes(),R=E[b],p=Array.isArray(R)?this.buildPageEvents(R):[];delete E[b];const y={...v,...l,...h,...E,...p.length?{[le]:p}:{},...this.userIdentifiedInWorkspace?{[oe]:!0}:{},mpid:u},I={...e,attributes:y},A=this.launcher.selectPlacements(I),f=()=>this.logSelectPlacementsEvent(y);return Promise.resolve(A).then(Q=>Q?.context?.sessionId?.then(X=>this.setRoktSessionId(X))).catch(()=>{}).finally(f),A}hashAttributes(e){return this.isKitReady()?this.launcher.hashAttributes(e):(console.error("Rokt Kit: Not initialized"),null)}use(e){return this.isKitReady()?!e||!m(e)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(e):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(e){this._isThankYouElementLoaded?e():this._thankYouElementOnLoadCallback=e}};g._allowedOriginHashes=[-553112570,549508659],g.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},g.EMAIL_SHA256_KEY="emailsha256";let _=g;function Ae(){return w}function ke(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!O(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}O(n.kits)?n.kits[d]={constructor:_}:(n.kits={},n.kits[d]={constructor:_}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:_,getId:Ae});exports.register=ke; +"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=["active_time_on_site_ms","billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],J=new Set(B);function G(n){return J.has(n.toLowerCase())}function w(n){const t={},e=n||{},i=Object.keys(e);for(let r=0;r=$)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(t??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);O("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),O("https://"+X+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function ut(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function dt(){return typeof window<"u"?window.location?.href:void 0}function ht(){return typeof window<"u"?window.navigator?.userAgent:void 0}class q{constructor(){this._logCount={}}incrementAndCheck(t){const i=(this._logCount[t]||0)+1;return this._logCount[t]=i,i>at}}class C{constructor(t,e,i,r,s){this._reporter="mp-wsdk";const o=t.isLoggingEnabled;this._integrationName=e||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new q,this._isEnabled=ut()||o}send(t,e,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(e)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:e,code:r||N.UNKNOWN_ERROR,url:dt(),deviceInfo:ht(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(t,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class H{constructor(t,e,i,r,s){this._transport=new C(t,e,i,r,s),this._errorUrl=z(t?.errorUrl,t?.integrationDomain,ot)}report(t){if(!t)return;const e=t.severity||I.ERROR;this._transport.send(this._errorUrl,e,t.message,t.code,t.stackTrace)}}class W{constructor(t,e,i,r,s,o){this._transport=new C(t,i,r,s,o),this._loggingUrl=z(t?.loggingUrl,t?.integrationDomain,st),this._errorReportingService=e}log(t){t&&this._transport.send(this._loggingUrl,I.INFO,t.message,t.code,void 0,e=>{if(this._errorReportingService){const i=typeof e.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+e.message,code:N.LOG_DELIVERY_FAILURE,severity:i?I.ERROR:I.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=b,this.moduleId=b,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(t,e){const i=t&&t.EventAttributes;return!i||typeof i[e]>"u"?null:i[e]}doesEventAttributeConditionMatch(t,e){if(!t||!m(t.operator))return!1;const i=t.operator.toLowerCase(),r=t.attributeValue;return i==="exists"?e!==null:e==null?!1:i==="equals"?String(e)===String(r):i==="contains"?String(e).indexOf(String(r))!==-1:!1}doesEventMatchRule(t,e){if(!e||!m(e.eventAttributeKey))return!1;const i=e.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(t,e.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;s{await ct(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(t){window.Rokt&&(window.Rokt.currentLauncher=t),this.launcher=t;const e=a().Rokt?.filters;e?(this.filters=e,e.filteredUser?this._workspaceSearchInFlightPromise=this.search(e.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,F(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const t=a()._getActiveForwarders().filter(e=>e.name==="Optimizely");try{if(t.length>0&&window.optimizely){const e=window.optimizely.get("state");return!e||!e.getActiveExperimentIds?{}:e.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=e.getVariationMap()[o].id,s),{})}}catch(e){console.error("Error fetching Optimizely attributes:",e)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(t){window&&a()&&a().captureTiming&&t&&a().captureTiming(t)}init(t,e,i,r,s){const o=t,c=o.accountId;this.userAttributes=w(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=L(o.placementEventMapping);this.placementEventMappingLookup=x(u);const l=L(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=Y(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=m(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:v,legacyRoktExtensions:R,loadThankYouElement:A}=D(o.roktExtensions),g={...a().Rokt?.launcherOptions||{}};this.integrationName=lt(g.integrationName),g.integrationName=this.integrationName,this.domain=h;const _={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},y=new H(_,this.integrationName,window.__rokt_li_guid__,o.accountId),S=new W(_,y,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=y,this.loggingService=S,a()._registerErrorReportingService&&a()._registerErrorReportingService(y),a()._registerLoggingService&&a()._registerLoggingService(S),i?(this.testHelpers={generateLauncherScript:U,generateThankYouElementScript:K,extractRoktExtensionConfig:D,hashEventMessage:j,parseSettingsString:L,generateMappedEventLookup:x,generateMappedEventAttributeLookup:Y,sendAdBlockMeasurementSignals:F,createAutoRemovedIframe:O,djb2:V,setAllowedOriginHashes:f=>{p._allowedOriginHashes=f},ReportingTransport:C,ErrorReportingService:H,LoggingService:W,RateLimiter:q,ErrorCodes:N,WSDKErrorSeverity:I},this.attachLauncher(c,g),"Successfully initialized: "+d):(A&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),M(et,K(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:f=>{console.error("Error loading Rokt Thank You Element script:",f)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,g,R):(M(tt,U(h,v),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,g,R):console.error("Rokt object is not available after script load.")},onError:f=>{console.error("Error loading Rokt launcher script:",f)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(t){if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(k(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(t),!k(this.placementEventMappingLookup))){const e=j(t.EventDataType,t.EventCategory,t.EventName??"");this.placementEventMappingLookup[String(e)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(t){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(t)}setUserAttribute(t,e){return G(t)||(this.userAttributes[t]=e),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(t){return delete this.userAttributes[t],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(t,e){return this.userAttributes=w(t.getAllUserAttributes()),"Successfully called "+e+" for forwarder: "+d}onUserIdentified(t){const e=t;return this.filters.filteredUser=e,this._workspaceSearchInFlightPromise=this.search(e),this.handleIdentityComplete(t,"onUserIdentified")}search(t){const e=this._workspaceIdSyncApiKey;if(!e)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=t.getUserIdentities?t.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];m(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(e,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(t,e){return this.handleIdentityComplete(t,"onLoginComplete")}onLogoutComplete(t,e){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(t,"onLogoutComplete")}onModifyComplete(t,e){return this.handleIdentityComplete(t,"onModifyComplete")}selectPlacements(t){if(this._workspaceSearchInFlightPromise){const e=this._workspaceSearchInFlightPromise;return Promise.race([e,new Promise(i=>setTimeout(i,nt))]).then(()=>this._dispatchPlacements(t))}return this._dispatchPlacements(t)}_dispatchPlacements(t){const e=t&&t.attributes||{},r={...w(this.userAttributes),...e},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=w(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},v=this.returnUserIdentities(c),R=this.returnLocalSessionAttributes(),A={...v,...l,...h,...R,...this.userIdentifiedInWorkspace?{[it]:!0}:{},mpid:u},g={...t,attributes:A},_=this.launcher.selectPlacements(g),y=()=>this.logSelectPlacementsEvent(A);return Promise.resolve(_).then(S=>S?.context?.sessionId?.then(f=>this.setRoktSessionId(f))).catch(()=>{}).finally(y),_}hashAttributes(t){return this.isKitReady()?this.launcher.hashAttributes(t):(console.error("Rokt Kit: Not initialized"),null)}use(t){return this.isKitReady()?!t||!m(t)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(t):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(t){this._isThankYouElementLoaded?t():this._thankYouElementOnLoadCallback=t}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function pt(){return b}function gt(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!T(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}T(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:pt});exports.register=gt; //# sourceMappingURL=Rokt-Kit.common.js.map diff --git a/dist/Rokt-Kit.common.js.map b/dist/Rokt-Kit.common.js.map index 5f82b9c..a78df02 100644 --- a/dist/Rokt-Kit.common.js.map +++ b/dist/Rokt-Kit.common.js.map @@ -1 +1 @@ -{"version":3,"file":"Rokt-Kit.common.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'active_time_on_site_ms',\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\n// A captured page view, persisted (newest last) under PAGE_VIEWS_KEY.\n// See the security note in the design spec: pageUrl and eventAttributes are\n// stored verbatim and may contain PII; they are persisted to browser storage\n// and sent to Rokt on the next selectPlacements call.\ninterface StoredPageView {\n name: string; // event.EventName\n pageUrl: string; // window.location.href (see sanitizeUrl)\n sourceMessageId: string; // event.SourceMessageId\n timestamp: number; // event.Timestamp\n activeTimeOnSite: number; // event.ActiveTimeOnSite\n eventAttributes?: { [key: string]: string }; // event.EventAttributes\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Page-view capture. Page views are identified by the mParticle message type\n// PageView (3); the last MAX_PAGE_VIEWS are stored under PAGE_VIEWS_KEY in the\n// Rokt manager's local session attributes so they flow into selectPlacements.\nconst MESSAGE_TYPE_PAGE_VIEW = 3;\nconst PAGE_VIEWS_KEY = 'mpPageViews';\nconst MAX_PAGE_VIEWS = 25;\n// Flat page-view array sent to selectPlacements: each StoredPageView with its\n// eventAttributes exploded into PAGE_EVENT_ATTR_PREFIX-namespaced top-level keys.\nconst PAGE_EVENTS_KEY = 'page_events';\nconst PAGE_EVENT_ATTR_PREFIX = 'attr_';\n// The page-view event attribute holding the document title; surfaced as the\n// dedicated page_name field rather than an attr_-namespaced key.\nconst PAGE_TITLE_ATTRIBUTE = 'title';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n// Isolates page-view URL handling. Returns the URL verbatim for now (per the\n// design decision). Tightening to strip query/fragment later is a one-line\n// change here and touches nothing else. See the security note in the spec.\nfunction sanitizeUrl(href: string): string {\n return href;\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n // Appends a page-view record to the persisted list under PAGE_VIEWS_KEY,\n // capping at MAX_PAGE_VIEWS (oldest evicted). Wrapped so a malformed event\n // can never throw out of the forwarder. Callers must confirm the event is a\n // page view and that setLocalSessionAttribute is available.\n private capturePageView(event: SDKEvent): void {\n try {\n const existing = mp().Rokt.getLocalSessionAttributes?.()?.[PAGE_VIEWS_KEY];\n const pageViews: StoredPageView[] = Array.isArray(existing) ? (existing as StoredPageView[]) : [];\n\n pageViews.push({\n name: event.EventName,\n pageUrl: sanitizeUrl(window.location.href),\n sourceMessageId: event.SourceMessageId,\n timestamp: event.Timestamp,\n activeTimeOnSite: event.ActiveTimeOnSite,\n eventAttributes: event.EventAttributes,\n });\n\n while (pageViews.length > MAX_PAGE_VIEWS) {\n pageViews.shift();\n }\n\n mp().Rokt.setLocalSessionAttribute?.(PAGE_VIEWS_KEY, pageViews);\n } catch (err) {\n console.error('Rokt Kit: Failed to capture page view', err);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private buildPageEvents(pageViews: StoredPageView[]): Record[] {\n return pageViews.map((pv, i) => {\n const flat: Record = {};\n if (pv.eventAttributes) {\n for (const [key, value] of Object.entries(pv.eventAttributes)) {\n // `title` is surfaced as the dedicated page_name field below, so it is\n // not also emitted as an attr_-namespaced key.\n if (key === PAGE_TITLE_ATTRIBUTE) {\n continue;\n }\n flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value;\n }\n }\n flat.event_name = pv.name;\n flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE];\n flat.pageUrl = pv.pageUrl;\n flat.sourceMessageId = pv.sourceMessageId;\n flat.timestamp = pv.timestamp;\n flat.activeTimeOnSite = pv.activeTimeOnSite;\n\n const next = pageViews[i + 1];\n if (next && typeof next.activeTimeOnSite === 'number' && typeof pv.activeTimeOnSite === 'number') {\n const diff = next.activeTimeOnSite - pv.activeTimeOnSite;\n if (diff >= 0) {\n flat.timeOnPage = diff;\n }\n }\n return flat;\n });\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) {\n console.warn('caputre Event', event);\n this.capturePageView(event);\n }\n\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n // Derive the flat page_events array from the stored page views, then drop the\n // raw nested mpPageViews so Rokt receives only the flattened copy.\n const rawPageViews = localSessionAttributes[PAGE_VIEWS_KEY];\n const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : [];\n delete localSessionAttributes[PAGE_VIEWS_KEY];\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: pageEvents } : {}),\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","MESSAGE_TYPE_PAGE_VIEW","PAGE_VIEWS_KEY","MAX_PAGE_VIEWS","PAGE_EVENTS_KEY","PAGE_EVENT_ATTR_PREFIX","PAGE_TITLE_ATTRIBUTE","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","existing","pageViews","err","filteredUser","userIdentities","pv","flat","next","diff","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","rawPageViews","pageEvents","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Rokt-Kit.common.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'active_time_on_site_ms',\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file diff --git a/dist/Rokt-Kit.esm.js b/dist/Rokt-Kit.esm.js index 770cdfa..310db37 100644 --- a/dist/Rokt-Kit.esm.js +++ b/dist/Rokt-Kit.esm.js @@ -1,4 +1,4 @@ -const $ = [ +const B = [ "active_time_on_site_ms", "billingaddress1", "billingaddress2", @@ -23,50 +23,50 @@ const $ = [ "shippingstate", "shippingzipcode", "totalprice" -], Z = new Set($); -function z(n) { - return Z.has(n.toLowerCase()); +], J = new Set(B); +function G(n) { + return J.has(n.toLowerCase()); } -function S(n) { - const e = {}, t = n || {}, i = Object.keys(t); +function w(n) { + const t = {}, e = n || {}, i = Object.keys(e); for (let r = 0; r < i.length; r++) { const s = i[r]; - z(s) || (e[s] = t[s]); + G(s) || (t[s] = e[s]); } - return e; + return t; } -const d = "Rokt", b = 181, ee = "selectPlacements", te = "apps.roktecommerce.com", ie = 0.1, ne = "ThankYouPageJourney", re = "rokt-launcher", se = "rokt-thank-you-element", oe = "userIdentifiedInWorkspace", ae = 3, w = "mpPageViews", ce = 25, le = "page_events", ue = "attr_", K = "title", de = 500, N = { +const d = "Rokt", b = 181, Q = "selectPlacements", X = "apps.roktecommerce.com", $ = 0.1, Z = "ThankYouPageJourney", tt = "rokt-launcher", et = "rokt-thank-you-element", it = "userIdentifiedInWorkspace", nt = 500, O = { UNKNOWN_ERROR: "UNKNOWN_ERROR", UNHANDLED_EXCEPTION: "UNHANDLED_EXCEPTION", IDENTITY_REQUEST: "IDENTITY_REQUEST", LOG_DELIVERY_FAILURE: "LOG_DELIVERY_FAILURE" -}, k = { +}, I = { ERROR: "ERROR", INFO: "INFO", WARNING: "WARNING" -}, he = "apps.rokt-api.com", pe = "/v1/log", ge = "/v1/errors", fe = 10; +}, rt = "apps.rokt-api.com", st = "/v1/log", ot = "/v1/errors", at = 10; function a() { return window.mParticle; } -function M(n, e) { - const i = [C(n), "/wsdk/integrations/launcher.js"].join(""); - return !e || e.length === 0 ? i : i + "?extensions=" + e.join(","); +function U(n, t) { + const i = [P(n), "/wsdk/integrations/launcher.js"].join(""); + return !t || t.length === 0 ? i : i + "?extensions=" + t.join(","); } -function D(n) { - return [C(n), "/rokt-elements/rokt-element-thank-you.js"].join(""); +function K(n) { + return [P(n), "/rokt-elements/rokt-element-thank-you.js"].join(""); } -function C(n) { - return ["https://", typeof n < "u" ? n : he].join(""); +function P(n) { + return ["https://", typeof n < "u" ? n : rt].join(""); } -function B(n, e, t) { - return n ? n.startsWith("http://") || n.startsWith("https://") ? n : "https://" + n : C(e) + t; +function z(n, t, e) { + return n ? n.startsWith("http://") || n.startsWith("https://") ? n : "https://" + n : P(t) + e; } -function x(n, e, t) { +function M(n, t, e) { if (document.getElementById(n)) return; const i = document.head || document.body, r = document.createElement("script"); - r.id = n, r.type = "text/javascript", r.src = e, r.async = !0, r.crossOrigin = "anonymous", r.fetchPriority = "high", t?.onLoad && (r.onload = t.onLoad), t?.onError && (r.onerror = t.onError), i.appendChild(r); + r.id = n, r.type = "text/javascript", r.src = t, r.async = !0, r.crossOrigin = "anonymous", r.fetchPriority = "high", e?.onLoad && (r.onload = e.onLoad), e?.onError && (r.onerror = e.onError), i.appendChild(r); } -function O(n) { +function T(n) { return n != null && typeof n == "object" && Array.isArray(n) === !1; } function L(n) { @@ -79,127 +79,127 @@ function L(n) { } return []; } -function Y(n) { - const e = n ? L(n) : [], t = [], i = []; +function D(n) { + const t = n ? L(n) : [], e = [], i = []; let r = !1; - for (let s = 0; s < e.length; s++) { - const o = e[s].value; - o === "thank-you-journey" ? (r = !0, i.push(ne)) : t.push(o); + for (let s = 0; s < t.length; s++) { + const o = t[s].value; + o === "thank-you-journey" ? (r = !0, i.push(Z)) : e.push(o); } return { - roktExtensionsQueryParams: t, + roktExtensionsQueryParams: e, legacyRoktExtensions: i, loadThankYouElement: r }; } -async function me(n, e) { - const t = []; - if (e) +async function ct(n, t) { + const e = []; + if (t) for (const i of n) - t.push(e.use(i)); - return Promise.all(t); + e.push(t.use(i)); + return Promise.all(e); } -function F(n) { +function x(n) { if (!n) return {}; - const e = {}; - for (let t = 0; t < n.length; t++) { - const i = n[t]; - e[i.jsmap] = i.value; + const t = {}; + for (let e = 0; e < n.length; e++) { + const i = n[e]; + t[i.jsmap] = i.value; } - return e; + return t; } -function j(n) { - const e = {}; +function Y(n) { + const t = {}; if (!Array.isArray(n)) - return e; - for (let t = 0; t < n.length; t++) { - const i = n[t]; + return t; + for (let e = 0; e < n.length; e++) { + const i = n[e]; if (!i || !m(i.value) || !m(i.map)) continue; const r = i.value, s = i.map; - e[r] || (e[r] = []), e[r].push({ + t[r] || (t[r] = []), t[r].push({ eventAttributeKey: s, conditions: Array.isArray(i.conditions) ? i.conditions : [] }); } - return e; + return t; } -function G(n, e, t) { - return a().generateHash([n, e, t].join("")); +function F(n, t, e) { + return a().generateHash([n, t, e].join("")); } -function T(n) { +function k(n) { return n == null ? !0 : typeof n == "object" ? Object.keys(n).length === 0 : Array.isArray(n) ? n.length === 0 : !1; } function m(n) { return typeof n == "string"; } -function Ee(n) { +function lt(n) { let i = "mParticle_wsdkv_" + a().getVersion() + "_kitv_" + "1.29.0"; return n && (i += "_" + n), i; } -function q(n) { - let e = 5381; - for (let t = 0; t < n.length; t++) - e = (e << 5) + e + n.charCodeAt(t), e = e & e; - return e; +function V(n) { + let t = 5381; + for (let e = 0; e < n.length; e++) + t = (t << 5) + t + n.charCodeAt(e), t = t & t; + return t; } -function P(n) { - const e = document.createElement("iframe"); - e.style.display = "none", e.setAttribute("sandbox", "allow-scripts allow-same-origin"), e.src = n, e.onload = function() { - e.onload = null, e.parentNode && e.parentNode.removeChild(e); +function N(n) { + const t = document.createElement("iframe"); + t.style.display = "none", t.setAttribute("sandbox", "allow-scripts allow-same-origin"), t.src = n, t.onload = function() { + t.onload = null, t.parentNode && t.parentNode.removeChild(t); }; - const t = document.body || document.head; - t && t.appendChild(e); + const e = document.body || document.head; + e && e.appendChild(t); } -function W(n, e) { - const t = q(window.location.origin); - if (_._allowedOriginHashes.indexOf(t) === -1 || Math.random() >= ie) +function j(n, t) { + const e = V(window.location.origin); + if (E._allowedOriginHashes.indexOf(e) === -1 || Math.random() >= $) return; const r = window.__rokt_li_guid__; if (!r) return; - const s = window.location.href.split("?")[0].split("#")[0], o = "version=" + encodeURIComponent(e ?? "") + "&launcherInstanceGuid=" + encodeURIComponent(r) + "&pageUrl=" + encodeURIComponent(s); - P("https://" + (n || "apps.rokt.com") + "/v1/wsdk-init/index.html?" + o), P( - "https://" + te + "/v1/wsdk-init/index.html?" + o + "&isControl=true" + const s = window.location.href.split("?")[0].split("#")[0], o = "version=" + encodeURIComponent(t ?? "") + "&launcherInstanceGuid=" + encodeURIComponent(r) + "&pageUrl=" + encodeURIComponent(s); + N("https://" + (n || "apps.rokt.com") + "/v1/wsdk-init/index.html?" + o), N( + "https://" + X + "/v1/wsdk-init/index.html?" + o + "&isControl=true" ); } -function _e() { +function ut() { return typeof window < "u" && !!window.location?.search?.toLowerCase().includes("mp_enable_logging=true"); } -function ye() { +function dt() { return typeof window < "u" ? window.location?.href : void 0; } -function Ie() { +function ht() { return typeof window < "u" ? window.navigator?.userAgent : void 0; } -class J { +class q { constructor() { this._logCount = {}; } - incrementAndCheck(e) { - const i = (this._logCount[e] || 0) + 1; - return this._logCount[e] = i, i > fe; + incrementAndCheck(t) { + const i = (this._logCount[t] || 0) + 1; + return this._logCount[t] = i, i > at; } } -class U { - constructor(e, t, i, r, s) { +class C { + constructor(t, e, i, r, s) { this._reporter = "mp-wsdk"; - const o = e.isLoggingEnabled; - this._integrationName = t || "", this._launcherInstanceGuid = i, this._accountId = r || null, this._rateLimiter = s || new J(), this._isEnabled = _e() || o; + const o = t.isLoggingEnabled; + this._integrationName = e || "", this._launcherInstanceGuid = i, this._accountId = r || null, this._rateLimiter = s || new q(), this._isEnabled = ut() || o; } - send(e, t, i, r, s, o) { - if (!(!this._isEnabled || this._rateLimiter.incrementAndCheck(t))) + send(t, e, i, r, s, o) { + if (!(!this._isEnabled || this._rateLimiter.incrementAndCheck(e))) try { const c = { additionalInformation: { message: i, version: this._integrationName }, - severity: t, - code: r || N.UNKNOWN_ERROR, - url: ye(), - deviceInfo: Ie(), + severity: e, + code: r || O.UNKNOWN_ERROR, + url: dt(), + deviceInfo: ht(), stackTrace: s, reporter: this._reporter, integration: this._integrationName @@ -209,7 +209,7 @@ class U { "rokt-launcher-version": this._integrationName, "rokt-wsdk-version": "joint" }; - this._launcherInstanceGuid && (u["rokt-launcher-instance-guid"] = this._launcherInstanceGuid), this._accountId && (u["rokt-account-id"] = this._accountId), fetch(e, { + this._launcherInstanceGuid && (u["rokt-launcher-instance-guid"] = this._launcherInstanceGuid), this._accountId && (u["rokt-account-id"] = this._accountId), fetch(t, { method: "POST", headers: u, body: JSON.stringify(c) @@ -227,61 +227,61 @@ class U { } } class H { - constructor(e, t, i, r, s) { - this._transport = new U(e, t, i, r, s), this._errorUrl = B(e?.errorUrl, e?.integrationDomain, ge); + constructor(t, e, i, r, s) { + this._transport = new C(t, e, i, r, s), this._errorUrl = z(t?.errorUrl, t?.integrationDomain, ot); } - report(e) { - if (!e) return; - const t = e.severity || k.ERROR; - this._transport.send(this._errorUrl, t, e.message, e.code, e.stackTrace); + report(t) { + if (!t) return; + const e = t.severity || I.ERROR; + this._transport.send(this._errorUrl, e, t.message, t.code, t.stackTrace); } } -class V { - constructor(e, t, i, r, s, o) { - this._transport = new U(e, i, r, s, o), this._loggingUrl = B(e?.loggingUrl, e?.integrationDomain, pe), this._errorReportingService = t; +class W { + constructor(t, e, i, r, s, o) { + this._transport = new C(t, i, r, s, o), this._loggingUrl = z(t?.loggingUrl, t?.integrationDomain, st), this._errorReportingService = e; } - log(e) { - e && this._transport.send( + log(t) { + t && this._transport.send( this._loggingUrl, - k.INFO, - e.message, - e.code, + I.INFO, + t.message, + t.code, void 0, - (t) => { + (e) => { if (this._errorReportingService) { - const i = typeof t.statusCode == "number"; + const i = typeof e.statusCode == "number"; this._errorReportingService.report({ - message: "LoggingService: Failed to send log: " + t.message, - code: N.LOG_DELIVERY_FAILURE, - severity: i ? k.ERROR : k.WARNING + message: "LoggingService: Failed to send log: " + e.message, + code: O.LOG_DELIVERY_FAILURE, + severity: i ? I.ERROR : I.WARNING }); } } ); } } -const g = class g { +const p = class p { constructor() { this.name = d, this.id = b, this.moduleId = b, this.isInitialized = !1, this.launcher = null, this.filters = {}, this.userAttributes = {}, this.userIdentifiedInWorkspace = !1, this.testHelpers = null, this.placementEventMappingLookup = {}, this.placementEventAttributeMappingLookup = {}, this.integrationName = null, this.errorReportingService = null, this.loggingService = null, this._thankYouElementOnLoadCallback = null, this._isThankYouElementLoaded = !1, this._workspaceSearchInFlightPromise = null; } // ---- Private helpers ---- - getEventAttributeValue(e, t) { - const i = e && e.EventAttributes; - return !i || typeof i[t] > "u" ? null : i[t]; + getEventAttributeValue(t, e) { + const i = t && t.EventAttributes; + return !i || typeof i[e] > "u" ? null : i[e]; } - doesEventAttributeConditionMatch(e, t) { - if (!e || !m(e.operator)) + doesEventAttributeConditionMatch(t, e) { + if (!t || !m(t.operator)) return !1; - const i = e.operator.toLowerCase(), r = e.attributeValue; - return i === "exists" ? t !== null : t == null ? !1 : i === "equals" ? String(t) === String(r) : i === "contains" ? String(t).indexOf(String(r)) !== -1 : !1; + const i = t.operator.toLowerCase(), r = t.attributeValue; + return i === "exists" ? e !== null : e == null ? !1 : i === "equals" ? String(e) === String(r) : i === "contains" ? String(e).indexOf(String(r)) !== -1 : !1; } - doesEventMatchRule(e, t) { - if (!t || !m(t.eventAttributeKey)) + doesEventMatchRule(t, e) { + if (!e || !m(e.eventAttributeKey)) return !1; - const i = t.conditions; + const i = e.conditions; if (!Array.isArray(i)) return !1; - const r = this.getEventAttributeValue(e, t.eventAttributeKey); + const r = this.getEventAttributeValue(t, e.eventAttributeKey); if (i.length === 0) return r !== null; for (let s = 0; s < i.length; s++) @@ -289,119 +289,83 @@ const g = class g { return !1; return !0; } - applyPlacementEventAttributeMapping(e) { - const t = Object.keys(this.placementEventAttributeMappingLookup); - for (let i = 0; i < t.length; i++) { - const r = t[i], s = this.placementEventAttributeMappingLookup[r]; - if (T(s)) + applyPlacementEventAttributeMapping(t) { + const e = Object.keys(this.placementEventAttributeMappingLookup); + for (let i = 0; i < e.length; i++) { + const r = e[i], s = this.placementEventAttributeMappingLookup[r]; + if (k(s)) continue; let o = !0; for (let c = 0; c < s.length; c++) - if (!this.doesEventMatchRule(e, s[c])) { + if (!this.doesEventMatchRule(t, s[c])) { o = !1; break; } o && a().Rokt.setLocalSessionAttribute?.(r, !0); } } - // Appends a page-view record to the persisted list under PAGE_VIEWS_KEY, - // capping at MAX_PAGE_VIEWS (oldest evicted). Wrapped so a malformed event - // can never throw out of the forwarder. Callers must confirm the event is a - // page view and that setLocalSessionAttribute is available. - capturePageView(e) { - try { - const t = a().Rokt.getLocalSessionAttributes?.()?.[w], i = Array.isArray(t) ? t : []; - for (i.push({ - name: e.EventName, - pageUrl: window.location.href, - sourceMessageId: e.SourceMessageId, - timestamp: e.Timestamp, - activeTimeOnSite: e.ActiveTimeOnSite, - eventAttributes: e.EventAttributes - }); i.length > ce; ) - i.shift(); - a().Rokt.setLocalSessionAttribute?.(w, i); - } catch (t) { - console.error("Rokt Kit: Failed to capture page view", t); - } - } isLauncherReadyToAttach() { return !!window.Rokt && typeof window.Rokt.createLauncher == "function"; } /** * Returns the user identities from the filtered user, if any. */ - returnUserIdentities(e) { - if (!e || !e.getUserIdentities) + returnUserIdentities(t) { + if (!t || !t.getUserIdentities) return {}; - const t = e.getUserIdentities().userIdentities; - return this.replaceOtherIdentityWithEmailsha256(t); + const e = t.getUserIdentities().userIdentities; + return this.replaceOtherIdentityWithEmailsha256(e); } returnLocalSessionAttributes() { - return !a().Rokt || typeof a().Rokt.getLocalSessionAttributes != "function" ? {} : a().Rokt.getLocalSessionAttributes(); - } - buildPageEvents(e) { - return e.map((t, i) => { - const r = {}; - if (t.eventAttributes) - for (const [o, c] of Object.entries(t.eventAttributes)) - o !== K && (r[`${ue}${o}`] = c); - r.event_name = t.name, r.page_name = t.eventAttributes?.[K], r.pageUrl = t.pageUrl, r.sourceMessageId = t.sourceMessageId, r.timestamp = t.timestamp, r.activeTimeOnSite = t.activeTimeOnSite; - const s = e[i + 1]; - if (s && typeof s.activeTimeOnSite == "number" && typeof t.activeTimeOnSite == "number") { - const o = s.activeTimeOnSite - t.activeTimeOnSite; - o >= 0 && (r.timeOnPage = o); - } - return r; - }); + return !a().Rokt || typeof a().Rokt.getLocalSessionAttributes != "function" ? {} : k(this.placementEventMappingLookup) && k(this.placementEventAttributeMappingLookup) ? {} : a().Rokt.getLocalSessionAttributes(); } - replaceOtherIdentityWithEmailsha256(e) { - const t = { ...e || {} }, i = this._mappedEmailSha256Key; - return i && e[i] && (t[g.EMAIL_SHA256_KEY] = e[i]), i && delete t[i], t; + replaceOtherIdentityWithEmailsha256(t) { + const e = { ...t || {} }, i = this._mappedEmailSha256Key; + return i && t[i] && (e[p.EMAIL_SHA256_KEY] = t[i]), i && delete e[i], e; } - logSelectPlacementsEvent(e) { - if (!window.mParticle || typeof a().logEvent != "function" || !O(e)) + logSelectPlacementsEvent(t) { + if (!window.mParticle || typeof a().logEvent != "function" || !T(t)) return; - const t = a().EventType.Other; - a().logEvent(ee, t, e); + const e = a().EventType.Other; + a().logEvent(Q, e, t); } - setRoktSessionId(e) { - if (!(!e || typeof e != "string")) + setRoktSessionId(t) { + if (!(!t || typeof t != "string")) try { - const t = a().getInstance(); - t && typeof t.setIntegrationAttribute == "function" && t.setIntegrationAttribute(b, { - roktSessionId: e + const e = a().getInstance(); + e && typeof e.setIntegrationAttribute == "function" && e.setIntegrationAttribute(b, { + roktSessionId: t }); } catch { } } - attachLauncher(e, t, i = []) { + attachLauncher(t, e, i = []) { const r = a() && a().sessionManager && typeof a().sessionManager.getSession == "function" ? a().sessionManager.getSession() : void 0, s = { - accountId: e, - ...t || {}, + accountId: t, + ...e || {}, ...r ? { mpSessionId: r } : {} }; let o; this.isPartnerInLocalLauncherTestGroup() ? o = Promise.resolve(window.Rokt.createLocalLauncher(s)) : o = window.Rokt.createLauncher(s), o.then(async (c) => { - await me(i, c), this.initRoktLauncher(c); + await ct(i, c), this.initRoktLauncher(c); }).catch((c) => { console.error("Error creating Rokt launcher:", c); }); } - initRoktLauncher(e) { - window.Rokt && (window.Rokt.currentLauncher = e), this.launcher = e; - const t = a().Rokt?.filters; - t ? (this.filters = t, t.filteredUser ? this._workspaceSearchInFlightPromise = this.search(t.filteredUser) : console.warn("Rokt Kit: No filtered user has been set.")) : console.warn("Rokt Kit: No filters have been set."), this.isInitialized = !0, W(this.domain, this.integrationName), a().Rokt.attachKit(this); + initRoktLauncher(t) { + window.Rokt && (window.Rokt.currentLauncher = t), this.launcher = t; + const e = a().Rokt?.filters; + e ? (this.filters = e, e.filteredUser ? this._workspaceSearchInFlightPromise = this.search(e.filteredUser) : console.warn("Rokt Kit: No filtered user has been set.")) : console.warn("Rokt Kit: No filters have been set."), this.isInitialized = !0, j(this.domain, this.integrationName), a().Rokt.attachKit(this); } fetchOptimizely() { - const e = a()._getActiveForwarders().filter((t) => t.name === "Optimizely"); + const t = a()._getActiveForwarders().filter((e) => e.name === "Optimizely"); try { - if (e.length > 0 && window.optimizely) { - const t = window.optimizely.get("state"); - return !t || !t.getActiveExperimentIds ? {} : t.getActiveExperimentIds().reduce((s, o) => (s["rokt.custom.optimizely.experiment." + o + ".variationId"] = t.getVariationMap()[o].id, s), {}); + if (t.length > 0 && window.optimizely) { + const e = window.optimizely.get("state"); + return !e || !e.getActiveExperimentIds ? {} : e.getActiveExperimentIds().reduce((s, o) => (s["rokt.custom.optimizely.experiment." + o + ".variationId"] = e.getVariationMap()[o].id, s), {}); } - } catch (t) { - console.error("Error fetching Optimizely attributes:", t); + } catch (e) { + console.error("Error fetching Optimizely attributes:", e); } return {}; } @@ -414,118 +378,118 @@ const g = class g { isAssignedToSampleGroup() { return Math.random() > 0.5; } - captureTiming(e) { - window && a() && a().captureTiming && e && a().captureTiming(e); + captureTiming(t) { + window && a() && a().captureTiming && t && a().captureTiming(t); } // ---- Public methods (mParticle Kit Callbacks) ---- /** * Initializes the Rokt forwarder with settings from the mParticle server. */ - init(e, t, i, r, s) { - const o = e, c = o.accountId; - this.userAttributes = S(s), this._onboardingExpProvider = o.onboardingExpProvider; + init(t, e, i, r, s) { + const o = t, c = o.accountId; + this.userAttributes = w(s), this._onboardingExpProvider = o.onboardingExpProvider; const u = L(o.placementEventMapping); - this.placementEventMappingLookup = F(u); + this.placementEventMappingLookup = x(u); const l = L( o.placementEventAttributeMapping ); - this.placementEventAttributeMappingLookup = j(l), o.hashedEmailUserIdentityType && (this._mappedEmailSha256Key = o.hashedEmailUserIdentityType.toLowerCase()), this._workspaceIdSyncApiKey = m(o.workspaceIdSyncApiKey) ? o.workspaceIdSyncApiKey : void 0; - const h = a().Rokt?.domain, { roktExtensionsQueryParams: v, legacyRoktExtensions: E, loadThankYouElement: R } = Y( + this.placementEventAttributeMappingLookup = Y(l), o.hashedEmailUserIdentityType && (this._mappedEmailSha256Key = o.hashedEmailUserIdentityType.toLowerCase()), this._workspaceIdSyncApiKey = m(o.workspaceIdSyncApiKey) ? o.workspaceIdSyncApiKey : void 0; + const h = a().Rokt?.domain, { roktExtensionsQueryParams: v, legacyRoktExtensions: R, loadThankYouElement: A } = D( o.roktExtensions - ), p = { + ), g = { ...a().Rokt?.launcherOptions || {} }; - this.integrationName = Ee(p.integrationName), p.integrationName = this.integrationName, this.domain = h; - const y = { + this.integrationName = lt(g.integrationName), g.integrationName = this.integrationName, this.domain = h; + const _ = { loggingUrl: o.loggingUrl, errorUrl: o.errorUrl, integrationDomain: h, isLoggingEnabled: a().config?.isLoggingEnabled === !0 - }, I = new H( - y, + }, y = new H( + _, this.integrationName, window.__rokt_li_guid__, o.accountId - ), A = new V( + ), S = new W( + _, y, - I, this.integrationName, window.__rokt_li_guid__, o.accountId ); - return this.errorReportingService = I, this.loggingService = A, a()._registerErrorReportingService && a()._registerErrorReportingService(I), a()._registerLoggingService && a()._registerLoggingService(A), i ? (this.testHelpers = { - generateLauncherScript: M, - generateThankYouElementScript: D, - extractRoktExtensionConfig: Y, - hashEventMessage: G, + return this.errorReportingService = y, this.loggingService = S, a()._registerErrorReportingService && a()._registerErrorReportingService(y), a()._registerLoggingService && a()._registerLoggingService(S), i ? (this.testHelpers = { + generateLauncherScript: U, + generateThankYouElementScript: K, + extractRoktExtensionConfig: D, + hashEventMessage: F, parseSettingsString: L, - generateMappedEventLookup: F, - generateMappedEventAttributeLookup: j, - sendAdBlockMeasurementSignals: W, - createAutoRemovedIframe: P, - djb2: q, + generateMappedEventLookup: x, + generateMappedEventAttributeLookup: Y, + sendAdBlockMeasurementSignals: j, + createAutoRemovedIframe: N, + djb2: V, setAllowedOriginHashes: (f) => { - g._allowedOriginHashes = f; + p._allowedOriginHashes = f; }, - ReportingTransport: U, + ReportingTransport: C, ErrorReportingService: H, - LoggingService: V, - RateLimiter: J, - ErrorCodes: N, - WSDKErrorSeverity: k - }, this.attachLauncher(c, p), "Successfully initialized: " + d) : (R && (a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this), x(se, D(h), { + LoggingService: W, + RateLimiter: q, + ErrorCodes: O, + WSDKErrorSeverity: I + }, this.attachLauncher(c, g), "Successfully initialized: " + d) : (A && (a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this), M(et, K(h), { onLoad: () => { this._isThankYouElementLoaded = !0, this._thankYouElementOnLoadCallback && this._thankYouElementOnLoadCallback(); }, onError: (f) => { console.error("Error loading Rokt Thank You Element script:", f); } - })), this.isLauncherReadyToAttach() ? this.attachLauncher(c, p, E) : (x(re, M(h, v), { + })), this.isLauncherReadyToAttach() ? this.attachLauncher(c, g, R) : (M(tt, U(h, v), { onLoad: () => { - this.isLauncherReadyToAttach() ? this.attachLauncher(c, p, E) : console.error("Rokt object is not available after script load."); + this.isLauncherReadyToAttach() ? this.attachLauncher(c, g, R) : console.error("Rokt object is not available after script load."); }, onError: (f) => { console.error("Error loading Rokt launcher script:", f); } - }), this.captureTiming(g.PERFORMANCE_MARKS.RoktScriptAppended)), "Successfully initialized: " + d); + }), this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)), "Successfully initialized: " + d); } - process(e) { + process(t) { if (!this.isKitReady()) return "Kit not ready for forwarder: " + d; - if (typeof a().Rokt?.setLocalSessionAttribute == "function" && (e.EventDataType === ae && (console.warn("caputre Event", e), this.capturePageView(e)), T(this.placementEventAttributeMappingLookup) || this.applyPlacementEventAttributeMapping(e), !T(this.placementEventMappingLookup))) { - const t = G(e.EventDataType, e.EventCategory, e.EventName ?? ""); - this.placementEventMappingLookup[String(t)] && a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(t)], !0); + if (typeof a().Rokt?.setLocalSessionAttribute == "function" && (k(this.placementEventAttributeMappingLookup) || this.applyPlacementEventAttributeMapping(t), !k(this.placementEventMappingLookup))) { + const e = F(t.EventDataType, t.EventCategory, t.EventName ?? ""); + this.placementEventMappingLookup[String(e)] && a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)], !0); } return "Successfully sent to forwarder: " + d; } - setExtensionData(e) { + setExtensionData(t) { if (!this.isKitReady()) { console.error("Rokt Kit: Not initialized"); return; } - window.Rokt.setExtensionData(e); + window.Rokt.setExtensionData(t); } - setUserAttribute(e, t) { - return z(e) || (this.userAttributes[e] = t), "Successfully set user attribute for forwarder: " + d; + setUserAttribute(t, e) { + return G(t) || (this.userAttributes[t] = e), "Successfully set user attribute for forwarder: " + d; } - removeUserAttribute(e) { - return delete this.userAttributes[e], "Successfully removed user attribute for forwarder: " + d; + removeUserAttribute(t) { + return delete this.userAttributes[t], "Successfully removed user attribute for forwarder: " + d; } - handleIdentityComplete(e, t) { - return this.userAttributes = S(e.getAllUserAttributes()), "Successfully called " + t + " for forwarder: " + d; + handleIdentityComplete(t, e) { + return this.userAttributes = w(t.getAllUserAttributes()), "Successfully called " + e + " for forwarder: " + d; } - onUserIdentified(e) { - const t = e; - return this.filters.filteredUser = t, this._workspaceSearchInFlightPromise = this.search(t), this.handleIdentityComplete(e, "onUserIdentified"); + onUserIdentified(t) { + const e = t; + return this.filters.filteredUser = e, this._workspaceSearchInFlightPromise = this.search(e), this.handleIdentityComplete(t, "onUserIdentified"); } - search(e) { - const t = this._workspaceIdSyncApiKey; - if (!t) + search(t) { + const e = this._workspaceIdSyncApiKey; + if (!e) return this.userIdentifiedInWorkspace = !1, this._workspaceLastSearchedIdentitiesKey = void 0, Promise.resolve(); const i = a().Identity?.search; if (typeof i != "function") return this.userIdentifiedInWorkspace = !1, this._workspaceLastSearchedIdentitiesKey = void 0, Promise.resolve(); - const r = e.getUserIdentities ? e.getUserIdentities().userIdentities : null, s = {}; + const r = t.getUserIdentities ? t.getUserIdentities().userIdentities : null, s = {}; if (r) for (const u of Object.keys(r)) { const l = r[u]; @@ -537,7 +501,7 @@ const g = class g { const c = o.sort().map((u) => `${u}=${s[u]}`).join("&"); return c === this._workspaceLastSearchedIdentitiesKey ? this._workspaceSearchInFlightPromise || Promise.resolve() : (this.userIdentifiedInWorkspace = !1, this._workspaceLastSearchedIdentitiesKey = c, new Promise((u) => { try { - i(t, s, (l) => { + i(e, s, (l) => { l?.httpCode === 200 && (this.userIdentifiedInWorkspace = !0), u(); }); } catch (l) { @@ -545,14 +509,14 @@ const g = class g { } })); } - onLoginComplete(e, t) { - return this.handleIdentityComplete(e, "onLoginComplete"); + onLoginComplete(t, e) { + return this.handleIdentityComplete(t, "onLoginComplete"); } - onLogoutComplete(e, t) { - return this.userIdentifiedInWorkspace = !1, this._workspaceSearchInFlightPromise = null, this._workspaceLastSearchedIdentitiesKey = void 0, this.handleIdentityComplete(e, "onLogoutComplete"); + onLogoutComplete(t, e) { + return this.userIdentifiedInWorkspace = !1, this._workspaceSearchInFlightPromise = null, this._workspaceLastSearchedIdentitiesKey = void 0, this.handleIdentityComplete(t, "onLogoutComplete"); } - onModifyComplete(e, t) { - return this.handleIdentityComplete(e, "onModifyComplete"); + onModifyComplete(t, e) { + return this.handleIdentityComplete(t, "onModifyComplete"); } /** * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options. @@ -577,83 +541,80 @@ const g = class g { * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`; * this wrapper just gates it on the in-flight search via `Promise.race`. */ - selectPlacements(e) { + selectPlacements(t) { if (this._workspaceSearchInFlightPromise) { - const t = this._workspaceSearchInFlightPromise; + const e = this._workspaceSearchInFlightPromise; return Promise.race([ - t, - new Promise((i) => setTimeout(i, de)) - ]).then(() => this._dispatchPlacements(e)); + e, + new Promise((i) => setTimeout(i, nt)) + ]).then(() => this._dispatchPlacements(t)); } - return this._dispatchPlacements(e); + return this._dispatchPlacements(t); } - _dispatchPlacements(e) { - const t = e && e.attributes || {}, r = { ...S(this.userAttributes), ...t }, s = this.filters || {}, o = s.userAttributeFilters || [], c = s.filteredUser || null, u = c ? c.getMPID() : null; + _dispatchPlacements(t) { + const e = t && t.attributes || {}, r = { ...w(this.userAttributes), ...e }, s = this.filters || {}, o = s.userAttributeFilters || [], c = s.filteredUser || null, u = c ? c.getMPID() : null; let l; - s ? s.filterUserAttributes ? l = s.filterUserAttributes(r, o) : l = r : (console.warn("Rokt Kit: No filters available, using user attributes"), l = r), this.userAttributes = S(l); - const h = this._onboardingExpProvider === "Optimizely" ? this.fetchOptimizely() : {}, v = this.returnUserIdentities(c), E = this.returnLocalSessionAttributes(), R = E[w], p = Array.isArray(R) ? this.buildPageEvents(R) : []; - delete E[w]; - const y = { + s ? s.filterUserAttributes ? l = s.filterUserAttributes(r, o) : l = r : (console.warn("Rokt Kit: No filters available, using user attributes"), l = r), this.userAttributes = w(l); + const h = this._onboardingExpProvider === "Optimizely" ? this.fetchOptimizely() : {}, v = this.returnUserIdentities(c), R = this.returnLocalSessionAttributes(), A = { ...v, ...l, ...h, - ...E, - ...p.length ? { [le]: p } : {}, - ...this.userIdentifiedInWorkspace ? { [oe]: !0 } : {}, + ...R, + ...this.userIdentifiedInWorkspace ? { [it]: !0 } : {}, mpid: u - }, I = { ...e, attributes: y }, A = this.launcher.selectPlacements(I), f = () => this.logSelectPlacementsEvent(y); - return Promise.resolve(A).then((Q) => Q?.context?.sessionId?.then((X) => this.setRoktSessionId(X))).catch(() => { - }).finally(f), A; + }, g = { ...t, attributes: A }, _ = this.launcher.selectPlacements(g), y = () => this.logSelectPlacementsEvent(A); + return Promise.resolve(_).then((S) => S?.context?.sessionId?.then((f) => this.setRoktSessionId(f))).catch(() => { + }).finally(y), _; } /** * Passes attributes to the Rokt Web SDK for client-side hashing. */ - hashAttributes(e) { - return this.isKitReady() ? this.launcher.hashAttributes(e) : (console.error("Rokt Kit: Not initialized"), null); + hashAttributes(t) { + return this.isKitReady() ? this.launcher.hashAttributes(t) : (console.error("Rokt Kit: Not initialized"), null); } /** * Enables optional Integration Launcher extensions before selecting placements. * * @deprecated This functionality has been internalized and will be removed in a future release. */ - use(e) { - return this.isKitReady() ? !e || !m(e) ? Promise.reject(new Error("Rokt Kit: Invalid extension name")) : this.launcher.use(e) : (console.error("Rokt Kit: Not initialized"), Promise.reject(new Error("Rokt Kit: Not initialized"))); + use(t) { + return this.isKitReady() ? !t || !m(t) ? Promise.reject(new Error("Rokt Kit: Invalid extension name")) : this.launcher.use(t) : (console.error("Rokt Kit: Not initialized"), Promise.reject(new Error("Rokt Kit: Not initialized"))); } /** * Registers a callback to be invoked once rokt-thank-you-element.js becomes available. */ - onShoppableAdsReady(e) { - this._isThankYouElementLoaded ? e() : this._thankYouElementOnLoadCallback = e; + onShoppableAdsReady(t) { + this._isThankYouElementLoaded ? t() : this._thankYouElementOnLoadCallback = t; } }; -g._allowedOriginHashes = [-553112570, 549508659], g.PERFORMANCE_MARKS = { +p._allowedOriginHashes = [-553112570, 549508659], p.PERFORMANCE_MARKS = { RoktScriptAppended: "mp:RoktScriptAppended" -}, g.EMAIL_SHA256_KEY = "emailsha256"; -let _ = g; -function Ae() { +}, p.EMAIL_SHA256_KEY = "emailsha256"; +let E = p; +function pt() { return b; } -function ke(n) { +function gt(n) { if (!n) { window.console.log("You must pass a config object to register the kit " + d); return; } - if (!O(n)) { + if (!T(n)) { window.console.log("'config' must be an object. You passed in a " + typeof n); return; } - O(n.kits) ? n.kits[d] = { - constructor: _ + T(n.kits) ? n.kits[d] = { + constructor: E } : (n.kits = {}, n.kits[d] = { - constructor: _ + constructor: E }), window.console.log("Successfully registered " + d + " to your mParticle configuration"); } typeof window < "u" && window.mParticle && a().addForwarder && a().addForwarder({ name: d, - constructor: _, - getId: Ae + constructor: E, + getId: pt }); export { - ke as register + gt as register }; //# sourceMappingURL=Rokt-Kit.esm.js.map diff --git a/dist/Rokt-Kit.esm.js.map b/dist/Rokt-Kit.esm.js.map index 1f64b06..d1f7f53 100644 --- a/dist/Rokt-Kit.esm.js.map +++ b/dist/Rokt-Kit.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"Rokt-Kit.esm.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'active_time_on_site_ms',\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\n// A captured page view, persisted (newest last) under PAGE_VIEWS_KEY.\n// See the security note in the design spec: pageUrl and eventAttributes are\n// stored verbatim and may contain PII; they are persisted to browser storage\n// and sent to Rokt on the next selectPlacements call.\ninterface StoredPageView {\n name: string; // event.EventName\n pageUrl: string; // window.location.href (see sanitizeUrl)\n sourceMessageId: string; // event.SourceMessageId\n timestamp: number; // event.Timestamp\n activeTimeOnSite: number; // event.ActiveTimeOnSite\n eventAttributes?: { [key: string]: string }; // event.EventAttributes\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Page-view capture. Page views are identified by the mParticle message type\n// PageView (3); the last MAX_PAGE_VIEWS are stored under PAGE_VIEWS_KEY in the\n// Rokt manager's local session attributes so they flow into selectPlacements.\nconst MESSAGE_TYPE_PAGE_VIEW = 3;\nconst PAGE_VIEWS_KEY = 'mpPageViews';\nconst MAX_PAGE_VIEWS = 25;\n// Flat page-view array sent to selectPlacements: each StoredPageView with its\n// eventAttributes exploded into PAGE_EVENT_ATTR_PREFIX-namespaced top-level keys.\nconst PAGE_EVENTS_KEY = 'page_events';\nconst PAGE_EVENT_ATTR_PREFIX = 'attr_';\n// The page-view event attribute holding the document title; surfaced as the\n// dedicated page_name field rather than an attr_-namespaced key.\nconst PAGE_TITLE_ATTRIBUTE = 'title';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n// Isolates page-view URL handling. Returns the URL verbatim for now (per the\n// design decision). Tightening to strip query/fragment later is a one-line\n// change here and touches nothing else. See the security note in the spec.\nfunction sanitizeUrl(href: string): string {\n return href;\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n // Appends a page-view record to the persisted list under PAGE_VIEWS_KEY,\n // capping at MAX_PAGE_VIEWS (oldest evicted). Wrapped so a malformed event\n // can never throw out of the forwarder. Callers must confirm the event is a\n // page view and that setLocalSessionAttribute is available.\n private capturePageView(event: SDKEvent): void {\n try {\n const existing = mp().Rokt.getLocalSessionAttributes?.()?.[PAGE_VIEWS_KEY];\n const pageViews: StoredPageView[] = Array.isArray(existing) ? (existing as StoredPageView[]) : [];\n\n pageViews.push({\n name: event.EventName,\n pageUrl: sanitizeUrl(window.location.href),\n sourceMessageId: event.SourceMessageId,\n timestamp: event.Timestamp,\n activeTimeOnSite: event.ActiveTimeOnSite,\n eventAttributes: event.EventAttributes,\n });\n\n while (pageViews.length > MAX_PAGE_VIEWS) {\n pageViews.shift();\n }\n\n mp().Rokt.setLocalSessionAttribute?.(PAGE_VIEWS_KEY, pageViews);\n } catch (err) {\n console.error('Rokt Kit: Failed to capture page view', err);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private buildPageEvents(pageViews: StoredPageView[]): Record[] {\n return pageViews.map((pv, i) => {\n const flat: Record = {};\n if (pv.eventAttributes) {\n for (const [key, value] of Object.entries(pv.eventAttributes)) {\n // `title` is surfaced as the dedicated page_name field below, so it is\n // not also emitted as an attr_-namespaced key.\n if (key === PAGE_TITLE_ATTRIBUTE) {\n continue;\n }\n flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value;\n }\n }\n flat.event_name = pv.name;\n flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE];\n flat.pageUrl = pv.pageUrl;\n flat.sourceMessageId = pv.sourceMessageId;\n flat.timestamp = pv.timestamp;\n flat.activeTimeOnSite = pv.activeTimeOnSite;\n\n const next = pageViews[i + 1];\n if (next && typeof next.activeTimeOnSite === 'number' && typeof pv.activeTimeOnSite === 'number') {\n const diff = next.activeTimeOnSite - pv.activeTimeOnSite;\n if (diff >= 0) {\n flat.timeOnPage = diff;\n }\n }\n return flat;\n });\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) {\n console.warn('caputre Event', event);\n this.capturePageView(event);\n }\n\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n // Derive the flat page_events array from the stored page views, then drop the\n // raw nested mpPageViews so Rokt receives only the flattened copy.\n const rawPageViews = localSessionAttributes[PAGE_VIEWS_KEY];\n const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : [];\n delete localSessionAttributes[PAGE_VIEWS_KEY];\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: pageEvents } : {}),\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","MESSAGE_TYPE_PAGE_VIEW","PAGE_VIEWS_KEY","MAX_PAGE_VIEWS","PAGE_EVENTS_KEY","PAGE_EVENT_ATTR_PREFIX","PAGE_TITLE_ATTRIBUTE","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","existing","pageViews","err","filteredUser","userIdentities","pv","flat","next","diff","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","rawPageViews","pageEvents","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Rokt-Kit.esm.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'active_time_on_site_ms',\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file diff --git a/dist/Rokt-Kit.iife.js b/dist/Rokt-Kit.iife.js index 0d862f5..7612507 100644 --- a/dist/Rokt-Kit.iife.js +++ b/dist/Rokt-Kit.iife.js @@ -1,2 +1,2 @@ -var RoktKit=(function(L){"use strict";const X=["active_time_on_site_ms","billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],$=new Set(X);function M(n){return $.has(n.toLowerCase())}function R(n){const e={},t=n||{},i=Object.keys(t);for(let r=0;r=te)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(e??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);C("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),C("https://"+ee+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function Ee(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function _e(){return typeof window<"u"?window.location?.href:void 0}function ye(){return typeof window<"u"?window.navigator?.userAgent:void 0}class q{constructor(){this._logCount={}}incrementAndCheck(e){const i=(this._logCount[e]||0)+1;return this._logCount[e]=i,i>ge}}class U{constructor(e,t,i,r,s){this._reporter="mp-wsdk";const o=e.isLoggingEnabled;this._integrationName=t||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new q,this._isEnabled=Ee()||o}send(e,t,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(t)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:t,code:r||T.UNKNOWN_ERROR,url:_e(),deviceInfo:ye(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(e,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class J{constructor(e,t,i,r,s){this._transport=new U(e,t,i,r,s),this._errorUrl=F(e?.errorUrl,e?.integrationDomain,pe)}report(e){if(!e)return;const t=e.severity||y.ERROR;this._transport.send(this._errorUrl,t,e.message,e.code,e.stackTrace)}}class Q{constructor(e,t,i,r,s,o){this._transport=new U(e,i,r,s,o),this._loggingUrl=F(e?.loggingUrl,e?.integrationDomain,he),this._errorReportingService=t}log(e){e&&this._transport.send(this._loggingUrl,y.INFO,e.message,e.code,void 0,t=>{if(this._errorReportingService){const i=typeof t.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+t.message,code:T.LOG_DELIVERY_FAILURE,severity:i?y.ERROR:y.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=S,this.moduleId=S,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(e,t){const i=e&&e.EventAttributes;return!i||typeof i[t]>"u"?null:i[t]}doesEventAttributeConditionMatch(e,t){if(!e||!f(e.operator))return!1;const i=e.operator.toLowerCase(),r=e.attributeValue;return i==="exists"?t!==null:t==null?!1:i==="equals"?String(t)===String(r):i==="contains"?String(t).indexOf(String(r))!==-1:!1}doesEventMatchRule(e,t){if(!t||!f(t.eventAttributeKey))return!1;const i=t.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(e,t.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;sae;)i.shift();a().Rokt.setLocalSessionAttribute?.(b,i)}catch(t){console.error("Rokt Kit: Failed to capture page view",t)}}isLauncherReadyToAttach(){return!!window.Rokt&&typeof window.Rokt.createLauncher=="function"}returnUserIdentities(e){if(!e||!e.getUserIdentities)return{};const t=e.getUserIdentities().userIdentities;return this.replaceOtherIdentityWithEmailsha256(t)}returnLocalSessionAttributes(){return!a().Rokt||typeof a().Rokt.getLocalSessionAttributes!="function"?{}:a().Rokt.getLocalSessionAttributes()}buildPageEvents(e){return e.map((t,i)=>{const r={};if(t.eventAttributes)for(const[o,c]of Object.entries(t.eventAttributes))o!==D&&(r[`${le}${o}`]=c);r.event_name=t.name,r.page_name=t.eventAttributes?.[D],r.pageUrl=t.pageUrl,r.sourceMessageId=t.sourceMessageId,r.timestamp=t.timestamp,r.activeTimeOnSite=t.activeTimeOnSite;const s=e[i+1];if(s&&typeof s.activeTimeOnSite=="number"&&typeof t.activeTimeOnSite=="number"){const o=s.activeTimeOnSite-t.activeTimeOnSite;o>=0&&(r.timeOnPage=o)}return r})}replaceOtherIdentityWithEmailsha256(e){const t={...e||{}},i=this._mappedEmailSha256Key;return i&&e[i]&&(t[p.EMAIL_SHA256_KEY]=e[i]),i&&delete t[i],t}logSelectPlacementsEvent(e){if(!window.mParticle||typeof a().logEvent!="function"||!P(e))return;const t=a().EventType.Other;a().logEvent(Z,t,e)}setRoktSessionId(e){if(!(!e||typeof e!="string"))try{const t=a().getInstance();t&&typeof t.setIntegrationAttribute=="function"&&t.setIntegrationAttribute(S,{roktSessionId:e})}catch{}}attachLauncher(e,t,i=[]){const r=a()&&a().sessionManager&&typeof a().sessionManager.getSession=="function"?a().sessionManager.getSession():void 0,s={accountId:e,...t||{},...r?{mpSessionId:r}:{}};let o;this.isPartnerInLocalLauncherTestGroup()?o=Promise.resolve(window.Rokt.createLocalLauncher(s)):o=window.Rokt.createLauncher(s),o.then(async c=>{await fe(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(e){window.Rokt&&(window.Rokt.currentLauncher=e),this.launcher=e;const t=a().Rokt?.filters;t?(this.filters=t,t.filteredUser?this._workspaceSearchInFlightPromise=this.search(t.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,B(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const e=a()._getActiveForwarders().filter(t=>t.name==="Optimizely");try{if(e.length>0&&window.optimizely){const t=window.optimizely.get("state");return!t||!t.getActiveExperimentIds?{}:t.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=t.getVariationMap()[o].id,s),{})}}catch(t){console.error("Error fetching Optimizely attributes:",t)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(e){window&&a()&&a().captureTiming&&e&&a().captureTiming(e)}init(e,t,i,r,s){const o=e,c=o.accountId;this.userAttributes=R(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=w(o.placementEventMapping);this.placementEventMappingLookup=W(u);const l=w(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=H(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=f(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:K,legacyRoktExtensions:_,loadThankYouElement:v}=G(o.roktExtensions),g={...a().Rokt?.launcherOptions||{}};this.integrationName=me(g.integrationName),g.integrationName=this.integrationName,this.domain=h;const I={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},A=new J(I,this.integrationName,window.__rokt_li_guid__,o.accountId),k=new Q(I,A,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=A,this.loggingService=k,a()._registerErrorReportingService&&a()._registerErrorReportingService(A),a()._registerLoggingService&&a()._registerLoggingService(k),i?(this.testHelpers={generateLauncherScript:Y,generateThankYouElementScript:x,extractRoktExtensionConfig:G,hashEventMessage:V,parseSettingsString:w,generateMappedEventLookup:W,generateMappedEventAttributeLookup:H,sendAdBlockMeasurementSignals:B,createAutoRemovedIframe:C,djb2:z,setAllowedOriginHashes:m=>{p._allowedOriginHashes=m},ReportingTransport:U,ErrorReportingService:J,LoggingService:Q,RateLimiter:q,ErrorCodes:T,WSDKErrorSeverity:y},this.attachLauncher(c,g),"Successfully initialized: "+d):(v&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),j(re,x(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:m=>{console.error("Error loading Rokt Thank You Element script:",m)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,g,_):(j(ne,Y(h,K),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,g,_):console.error("Rokt object is not available after script load.")},onError:m=>{console.error("Error loading Rokt launcher script:",m)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(e){if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(e.EventDataType===oe&&(console.warn("caputre Event",e),this.capturePageView(e)),N(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(e),!N(this.placementEventMappingLookup))){const t=V(e.EventDataType,e.EventCategory,e.EventName??"");this.placementEventMappingLookup[String(t)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(t)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(e){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(e)}setUserAttribute(e,t){return M(e)||(this.userAttributes[e]=t),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(e){return delete this.userAttributes[e],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(e,t){return this.userAttributes=R(e.getAllUserAttributes()),"Successfully called "+t+" for forwarder: "+d}onUserIdentified(e){const t=e;return this.filters.filteredUser=t,this._workspaceSearchInFlightPromise=this.search(t),this.handleIdentityComplete(e,"onUserIdentified")}search(e){const t=this._workspaceIdSyncApiKey;if(!t)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=e.getUserIdentities?e.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];f(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(t,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(e,t){return this.handleIdentityComplete(e,"onLoginComplete")}onLogoutComplete(e,t){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(e,"onLogoutComplete")}onModifyComplete(e,t){return this.handleIdentityComplete(e,"onModifyComplete")}selectPlacements(e){if(this._workspaceSearchInFlightPromise){const t=this._workspaceSearchInFlightPromise;return Promise.race([t,new Promise(i=>setTimeout(i,ue))]).then(()=>this._dispatchPlacements(e))}return this._dispatchPlacements(e)}_dispatchPlacements(e){const t=e&&e.attributes||{},r={...R(this.userAttributes),...t},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=R(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},K=this.returnUserIdentities(c),_=this.returnLocalSessionAttributes(),v=_[b],g=Array.isArray(v)?this.buildPageEvents(v):[];delete _[b];const I={...K,...l,...h,..._,...g.length?{[ce]:g}:{},...this.userIdentifiedInWorkspace?{[se]:!0}:{},mpid:u},A={...e,attributes:I},k=this.launcher.selectPlacements(A),m=()=>this.logSelectPlacementsEvent(I);return Promise.resolve(k).then(ke=>ke?.context?.sessionId?.then(Re=>this.setRoktSessionId(Re))).catch(()=>{}).finally(m),k}hashAttributes(e){return this.isKitReady()?this.launcher.hashAttributes(e):(console.error("Rokt Kit: Not initialized"),null)}use(e){return this.isKitReady()?!e||!f(e)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(e):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(e){this._isThankYouElementLoaded?e():this._thankYouElementOnLoadCallback=e}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function Ie(){return S}function Ae(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!P(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}P(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}return typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:Ie}),L.register=Ae,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"}),L})({}); +var RoktKit=(function(v){"use strict";const J=["active_time_on_site_ms","billingaddress1","billingaddress2","billingcity","billingstate","billingzipcode","cartitems","ccbin","confirmationref","conversiontype","country","couponcode","currency","language","paymentserviceprovider","paymentserviceproviderattribute","paymenttype","shippingaddress1","shippingcity","shippingcountry","shippingmethod","shippingstate","shippingzipcode","totalprice"],Q=new Set(J);function K(n){return Q.has(n.toLowerCase())}function R(n){const t={},e=n||{},i=Object.keys(e);for(let r=0;r=Z)return;const r=window.__rokt_li_guid__;if(!r)return;const s=window.location.href.split("?")[0].split("#")[0],o="version="+encodeURIComponent(t??"")+"&launcherInstanceGuid="+encodeURIComponent(r)+"&pageUrl="+encodeURIComponent(s);P("https://"+(n||"apps.rokt.com")+"/v1/wsdk-init/index.html?"+o),P("https://"+$+"/v1/wsdk-init/index.html?"+o+"&isControl=true")}function dt(){return typeof window<"u"&&!!window.location?.search?.toLowerCase().includes("mp_enable_logging=true")}function ht(){return typeof window<"u"?window.location?.href:void 0}function pt(){return typeof window<"u"?window.navigator?.userAgent:void 0}class V{constructor(){this._logCount={}}incrementAndCheck(t){const i=(this._logCount[t]||0)+1;return this._logCount[t]=i,i>ct}}class C{constructor(t,e,i,r,s){this._reporter="mp-wsdk";const o=t.isLoggingEnabled;this._integrationName=e||"",this._launcherInstanceGuid=i,this._accountId=r||null,this._rateLimiter=s||new V,this._isEnabled=dt()||o}send(t,e,i,r,s,o){if(!(!this._isEnabled||this._rateLimiter.incrementAndCheck(e)))try{const c={additionalInformation:{message:i,version:this._integrationName},severity:e,code:r||T.UNKNOWN_ERROR,url:ht(),deviceInfo:pt(),stackTrace:s,reporter:this._reporter,integration:this._integrationName},u={Accept:"text/plain;charset=UTF-8","Content-Type":"application/json","rokt-launcher-version":this._integrationName,"rokt-wsdk-version":"joint"};this._launcherInstanceGuid&&(u["rokt-launcher-instance-guid"]=this._launcherInstanceGuid),this._accountId&&(u["rokt-account-id"]=this._accountId),fetch(t,{method:"POST",headers:u,body:JSON.stringify(c)}).then(l=>{if(!l.ok){const h=new Error("HTTP "+l.status+" from log endpoint");throw h.statusCode=l.status,h}}).catch(l=>{console.error("ReportingTransport: Failed to send log",l),o&&o(l)})}catch(c){console.error("ReportingTransport: Failed to send log",c),o&&o(c)}}}class q{constructor(t,e,i,r,s){this._transport=new C(t,e,i,r,s),this._errorUrl=Y(t?.errorUrl,t?.integrationDomain,at)}report(t){if(!t)return;const e=t.severity||_.ERROR;this._transport.send(this._errorUrl,e,t.message,t.code,t.stackTrace)}}class B{constructor(t,e,i,r,s,o){this._transport=new C(t,i,r,s,o),this._loggingUrl=Y(t?.loggingUrl,t?.integrationDomain,ot),this._errorReportingService=e}log(t){t&&this._transport.send(this._loggingUrl,_.INFO,t.message,t.code,void 0,e=>{if(this._errorReportingService){const i=typeof e.statusCode=="number";this._errorReportingService.report({message:"LoggingService: Failed to send log: "+e.message,code:T.LOG_DELIVERY_FAILURE,severity:i?_.ERROR:_.WARNING})}})}}const p=class p{constructor(){this.name=d,this.id=A,this.moduleId=A,this.isInitialized=!1,this.launcher=null,this.filters={},this.userAttributes={},this.userIdentifiedInWorkspace=!1,this.testHelpers=null,this.placementEventMappingLookup={},this.placementEventAttributeMappingLookup={},this.integrationName=null,this.errorReportingService=null,this.loggingService=null,this._thankYouElementOnLoadCallback=null,this._isThankYouElementLoaded=!1,this._workspaceSearchInFlightPromise=null}getEventAttributeValue(t,e){const i=t&&t.EventAttributes;return!i||typeof i[e]>"u"?null:i[e]}doesEventAttributeConditionMatch(t,e){if(!t||!g(t.operator))return!1;const i=t.operator.toLowerCase(),r=t.attributeValue;return i==="exists"?e!==null:e==null?!1:i==="equals"?String(e)===String(r):i==="contains"?String(e).indexOf(String(r))!==-1:!1}doesEventMatchRule(t,e){if(!e||!g(e.eventAttributeKey))return!1;const i=e.conditions;if(!Array.isArray(i))return!1;const r=this.getEventAttributeValue(t,e.eventAttributeKey);if(i.length===0)return r!==null;for(let s=0;s{await lt(i,c),this.initRoktLauncher(c)}).catch(c=>{console.error("Error creating Rokt launcher:",c)})}initRoktLauncher(t){window.Rokt&&(window.Rokt.currentLauncher=t),this.launcher=t;const e=a().Rokt?.filters;e?(this.filters=e,e.filteredUser?this._workspaceSearchInFlightPromise=this.search(e.filteredUser):console.warn("Rokt Kit: No filtered user has been set.")):console.warn("Rokt Kit: No filters have been set."),this.isInitialized=!0,z(this.domain,this.integrationName),a().Rokt.attachKit(this)}fetchOptimizely(){const t=a()._getActiveForwarders().filter(e=>e.name==="Optimizely");try{if(t.length>0&&window.optimizely){const e=window.optimizely.get("state");return!e||!e.getActiveExperimentIds?{}:e.getActiveExperimentIds().reduce((s,o)=>(s["rokt.custom.optimizely.experiment."+o+".variationId"]=e.getVariationMap()[o].id,s),{})}}catch(e){console.error("Error fetching Optimizely attributes:",e)}return{}}isKitReady(){return!!(this.isInitialized&&this.launcher)}isPartnerInLocalLauncherTestGroup(){return!!(a().config&&a().config.isLocalLauncherEnabled&&this.isAssignedToSampleGroup())}isAssignedToSampleGroup(){return Math.random()>.5}captureTiming(t){window&&a()&&a().captureTiming&&t&&a().captureTiming(t)}init(t,e,i,r,s){const o=t,c=o.accountId;this.userAttributes=R(s),this._onboardingExpProvider=o.onboardingExpProvider;const u=S(o.placementEventMapping);this.placementEventMappingLookup=F(u);const l=S(o.placementEventAttributeMapping);this.placementEventAttributeMappingLookup=H(l),o.hashedEmailUserIdentityType&&(this._mappedEmailSha256Key=o.hashedEmailUserIdentityType.toLowerCase()),this._workspaceIdSyncApiKey=g(o.workspaceIdSyncApiKey)?o.workspaceIdSyncApiKey:void 0;const h=a().Rokt?.domain,{roktExtensionsQueryParams:U,legacyRoktExtensions:w,loadThankYouElement:b}=j(o.roktExtensions),f={...a().Rokt?.launcherOptions||{}};this.integrationName=ut(f.integrationName),f.integrationName=this.integrationName,this.domain=h;const k={loggingUrl:o.loggingUrl,errorUrl:o.errorUrl,integrationDomain:h,isLoggingEnabled:a().config?.isLoggingEnabled===!0},I=new q(k,this.integrationName,window.__rokt_li_guid__,o.accountId),L=new B(k,I,this.integrationName,window.__rokt_li_guid__,o.accountId);return this.errorReportingService=I,this.loggingService=L,a()._registerErrorReportingService&&a()._registerErrorReportingService(I),a()._registerLoggingService&&a()._registerLoggingService(L),i?(this.testHelpers={generateLauncherScript:M,generateThankYouElementScript:D,extractRoktExtensionConfig:j,hashEventMessage:W,parseSettingsString:S,generateMappedEventLookup:F,generateMappedEventAttributeLookup:H,sendAdBlockMeasurementSignals:z,createAutoRemovedIframe:P,djb2:G,setAllowedOriginHashes:m=>{p._allowedOriginHashes=m},ReportingTransport:C,ErrorReportingService:q,LoggingService:B,RateLimiter:V,ErrorCodes:T,WSDKErrorSeverity:_},this.attachLauncher(c,f),"Successfully initialized: "+d):(b&&(a().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this),x(it,D(h),{onLoad:()=>{this._isThankYouElementLoaded=!0,this._thankYouElementOnLoadCallback&&this._thankYouElementOnLoadCallback()},onError:m=>{console.error("Error loading Rokt Thank You Element script:",m)}})),this.isLauncherReadyToAttach()?this.attachLauncher(c,f,w):(x(et,M(h,U),{onLoad:()=>{this.isLauncherReadyToAttach()?this.attachLauncher(c,f,w):console.error("Rokt object is not available after script load.")},onError:m=>{console.error("Error loading Rokt launcher script:",m)}}),this.captureTiming(p.PERFORMANCE_MARKS.RoktScriptAppended)),"Successfully initialized: "+d)}process(t){if(!this.isKitReady())return"Kit not ready for forwarder: "+d;if(typeof a().Rokt?.setLocalSessionAttribute=="function"&&(y(this.placementEventAttributeMappingLookup)||this.applyPlacementEventAttributeMapping(t),!y(this.placementEventMappingLookup))){const e=W(t.EventDataType,t.EventCategory,t.EventName??"");this.placementEventMappingLookup[String(e)]&&a().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(e)],!0)}return"Successfully sent to forwarder: "+d}setExtensionData(t){if(!this.isKitReady()){console.error("Rokt Kit: Not initialized");return}window.Rokt.setExtensionData(t)}setUserAttribute(t,e){return K(t)||(this.userAttributes[t]=e),"Successfully set user attribute for forwarder: "+d}removeUserAttribute(t){return delete this.userAttributes[t],"Successfully removed user attribute for forwarder: "+d}handleIdentityComplete(t,e){return this.userAttributes=R(t.getAllUserAttributes()),"Successfully called "+e+" for forwarder: "+d}onUserIdentified(t){const e=t;return this.filters.filteredUser=e,this._workspaceSearchInFlightPromise=this.search(e),this.handleIdentityComplete(t,"onUserIdentified")}search(t){const e=this._workspaceIdSyncApiKey;if(!e)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const i=a().Identity?.search;if(typeof i!="function")return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const r=t.getUserIdentities?t.getUserIdentities().userIdentities:null,s={};if(r)for(const u of Object.keys(r)){const l=r[u];g(l)&&l.length>0&&(s[u]=l)}const o=Object.keys(s);if(o.length===0)return this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=void 0,Promise.resolve();const c=o.sort().map(u=>`${u}=${s[u]}`).join("&");return c===this._workspaceLastSearchedIdentitiesKey?this._workspaceSearchInFlightPromise||Promise.resolve():(this.userIdentifiedInWorkspace=!1,this._workspaceLastSearchedIdentitiesKey=c,new Promise(u=>{try{i(e,s,l=>{l?.httpCode===200&&(this.userIdentifiedInWorkspace=!0),u()})}catch(l){console.error("Rokt Kit: Workspace IDSync search failed",l),this._workspaceLastSearchedIdentitiesKey=void 0,u()}}))}onLoginComplete(t,e){return this.handleIdentityComplete(t,"onLoginComplete")}onLogoutComplete(t,e){return this.userIdentifiedInWorkspace=!1,this._workspaceSearchInFlightPromise=null,this._workspaceLastSearchedIdentitiesKey=void 0,this.handleIdentityComplete(t,"onLogoutComplete")}onModifyComplete(t,e){return this.handleIdentityComplete(t,"onModifyComplete")}selectPlacements(t){if(this._workspaceSearchInFlightPromise){const e=this._workspaceSearchInFlightPromise;return Promise.race([e,new Promise(i=>setTimeout(i,rt))]).then(()=>this._dispatchPlacements(t))}return this._dispatchPlacements(t)}_dispatchPlacements(t){const e=t&&t.attributes||{},r={...R(this.userAttributes),...e},s=this.filters||{},o=s.userAttributeFilters||[],c=s.filteredUser||null,u=c?c.getMPID():null;let l;s?s.filterUserAttributes?l=s.filterUserAttributes(r,o):l=r:(console.warn("Rokt Kit: No filters available, using user attributes"),l=r),this.userAttributes=R(l);const h=this._onboardingExpProvider==="Optimizely"?this.fetchOptimizely():{},U=this.returnUserIdentities(c),w=this.returnLocalSessionAttributes(),b={...U,...l,...h,...w,...this.userIdentifiedInWorkspace?{[nt]:!0}:{},mpid:u},f={...t,attributes:b},k=this.launcher.selectPlacements(f),I=()=>this.logSelectPlacementsEvent(b);return Promise.resolve(k).then(L=>L?.context?.sessionId?.then(m=>this.setRoktSessionId(m))).catch(()=>{}).finally(I),k}hashAttributes(t){return this.isKitReady()?this.launcher.hashAttributes(t):(console.error("Rokt Kit: Not initialized"),null)}use(t){return this.isKitReady()?!t||!g(t)?Promise.reject(new Error("Rokt Kit: Invalid extension name")):this.launcher.use(t):(console.error("Rokt Kit: Not initialized"),Promise.reject(new Error("Rokt Kit: Not initialized")))}onShoppableAdsReady(t){this._isThankYouElementLoaded?t():this._thankYouElementOnLoadCallback=t}};p._allowedOriginHashes=[-553112570,549508659],p.PERFORMANCE_MARKS={RoktScriptAppended:"mp:RoktScriptAppended"},p.EMAIL_SHA256_KEY="emailsha256";let E=p;function gt(){return A}function ft(n){if(!n){window.console.log("You must pass a config object to register the kit "+d);return}if(!N(n)){window.console.log("'config' must be an object. You passed in a "+typeof n);return}N(n.kits)?n.kits[d]={constructor:E}:(n.kits={},n.kits[d]={constructor:E}),window.console.log("Successfully registered "+d+" to your mParticle configuration")}return typeof window<"u"&&window.mParticle&&a().addForwarder&&a().addForwarder({name:d,constructor:E,getId:gt}),v.register=ft,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"}),v})({}); //# sourceMappingURL=Rokt-Kit.iife.js.map diff --git a/dist/Rokt-Kit.iife.js.map b/dist/Rokt-Kit.iife.js.map index 360449e..8ad3481 100644 --- a/dist/Rokt-Kit.iife.js.map +++ b/dist/Rokt-Kit.iife.js.map @@ -1 +1 @@ -{"version":3,"file":"Rokt-Kit.iife.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'active_time_on_site_ms',\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\n// A captured page view, persisted (newest last) under PAGE_VIEWS_KEY.\n// See the security note in the design spec: pageUrl and eventAttributes are\n// stored verbatim and may contain PII; they are persisted to browser storage\n// and sent to Rokt on the next selectPlacements call.\ninterface StoredPageView {\n name: string; // event.EventName\n pageUrl: string; // window.location.href (see sanitizeUrl)\n sourceMessageId: string; // event.SourceMessageId\n timestamp: number; // event.Timestamp\n activeTimeOnSite: number; // event.ActiveTimeOnSite\n eventAttributes?: { [key: string]: string }; // event.EventAttributes\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Page-view capture. Page views are identified by the mParticle message type\n// PageView (3); the last MAX_PAGE_VIEWS are stored under PAGE_VIEWS_KEY in the\n// Rokt manager's local session attributes so they flow into selectPlacements.\nconst MESSAGE_TYPE_PAGE_VIEW = 3;\nconst PAGE_VIEWS_KEY = 'mpPageViews';\nconst MAX_PAGE_VIEWS = 25;\n// Flat page-view array sent to selectPlacements: each StoredPageView with its\n// eventAttributes exploded into PAGE_EVENT_ATTR_PREFIX-namespaced top-level keys.\nconst PAGE_EVENTS_KEY = 'page_events';\nconst PAGE_EVENT_ATTR_PREFIX = 'attr_';\n// The page-view event attribute holding the document title; surfaced as the\n// dedicated page_name field rather than an attr_-namespaced key.\nconst PAGE_TITLE_ATTRIBUTE = 'title';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n// Isolates page-view URL handling. Returns the URL verbatim for now (per the\n// design decision). Tightening to strip query/fragment later is a one-line\n// change here and touches nothing else. See the security note in the spec.\nfunction sanitizeUrl(href: string): string {\n return href;\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n // Appends a page-view record to the persisted list under PAGE_VIEWS_KEY,\n // capping at MAX_PAGE_VIEWS (oldest evicted). Wrapped so a malformed event\n // can never throw out of the forwarder. Callers must confirm the event is a\n // page view and that setLocalSessionAttribute is available.\n private capturePageView(event: SDKEvent): void {\n try {\n const existing = mp().Rokt.getLocalSessionAttributes?.()?.[PAGE_VIEWS_KEY];\n const pageViews: StoredPageView[] = Array.isArray(existing) ? (existing as StoredPageView[]) : [];\n\n pageViews.push({\n name: event.EventName,\n pageUrl: sanitizeUrl(window.location.href),\n sourceMessageId: event.SourceMessageId,\n timestamp: event.Timestamp,\n activeTimeOnSite: event.ActiveTimeOnSite,\n eventAttributes: event.EventAttributes,\n });\n\n while (pageViews.length > MAX_PAGE_VIEWS) {\n pageViews.shift();\n }\n\n mp().Rokt.setLocalSessionAttribute?.(PAGE_VIEWS_KEY, pageViews);\n } catch (err) {\n console.error('Rokt Kit: Failed to capture page view', err);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private buildPageEvents(pageViews: StoredPageView[]): Record[] {\n return pageViews.map((pv, i) => {\n const flat: Record = {};\n if (pv.eventAttributes) {\n for (const [key, value] of Object.entries(pv.eventAttributes)) {\n // `title` is surfaced as the dedicated page_name field below, so it is\n // not also emitted as an attr_-namespaced key.\n if (key === PAGE_TITLE_ATTRIBUTE) {\n continue;\n }\n flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value;\n }\n }\n flat.event_name = pv.name;\n flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE];\n flat.pageUrl = pv.pageUrl;\n flat.sourceMessageId = pv.sourceMessageId;\n flat.timestamp = pv.timestamp;\n flat.activeTimeOnSite = pv.activeTimeOnSite;\n\n const next = pageViews[i + 1];\n if (next && typeof next.activeTimeOnSite === 'number' && typeof pv.activeTimeOnSite === 'number') {\n const diff = next.activeTimeOnSite - pv.activeTimeOnSite;\n if (diff >= 0) {\n flat.timeOnPage = diff;\n }\n }\n return flat;\n });\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) {\n console.warn('caputre Event', event);\n this.capturePageView(event);\n }\n\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n // Derive the flat page_events array from the stored page views, then drop the\n // raw nested mpPageViews so Rokt receives only the flattened copy.\n const rawPageViews = localSessionAttributes[PAGE_VIEWS_KEY];\n const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : [];\n delete localSessionAttributes[PAGE_VIEWS_KEY];\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: pageEvents } : {}),\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","MESSAGE_TYPE_PAGE_VIEW","PAGE_VIEWS_KEY","MAX_PAGE_VIEWS","PAGE_EVENTS_KEY","PAGE_EVENT_ATTR_PREFIX","PAGE_TITLE_ATTRIBUTE","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","sanitizeUrl","href","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","existing","pageViews","err","filteredUser","userIdentities","pv","flat","next","diff","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","rawPageViews","pageEvents","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Rokt-Kit.iife.js","sources":["../src/selectPlacementsAttributePersistence.ts","../src/Rokt-Kit.ts"],"sourcesContent":["const SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST = [\n 'active_time_on_site_ms',\n 'billingaddress1',\n 'billingaddress2',\n 'billingcity',\n 'billingstate',\n 'billingzipcode',\n 'cartitems',\n 'ccbin',\n 'confirmationref',\n 'conversiontype',\n 'country',\n 'couponcode',\n 'currency',\n 'language',\n 'paymentserviceprovider',\n 'paymentserviceproviderattribute',\n 'paymenttype',\n 'shippingaddress1',\n 'shippingcity',\n 'shippingcountry',\n 'shippingmethod',\n 'shippingstate',\n 'shippingzipcode',\n 'totalprice',\n];\nconst SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET = new Set(SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST);\n\nexport function isSelectPlacementsAttributePersistenceDenied(key: string): boolean {\n return SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET.has(key.toLowerCase());\n}\n\nexport function removeSelectPlacementsAttributePersistenceDeniedAttributes(\n attributes: Record | null | undefined,\n): Record {\n const filteredAttributes: Record = {};\n const sourceAttributes = attributes || {};\n const attributeKeys = Object.keys(sourceAttributes);\n\n for (let i = 0; i < attributeKeys.length; i++) {\n const key = attributeKeys[i];\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n filteredAttributes[key] = sourceAttributes[key];\n }\n }\n\n return filteredAttributes;\n}\n","// Copyright 2025 mParticle, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ============================================================\n// Types\n// ============================================================\n\nimport { KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sdk/internal';\nimport type { IUserIdentities } from '@mparticle/web-sdk';\n\nimport {\n isSelectPlacementsAttributePersistenceDenied,\n removeSelectPlacementsAttributePersistenceDeniedAttributes,\n} from './selectPlacementsAttributePersistence';\n\ninterface RoktKitSettings {\n accountId: string;\n roktExtensions?: string;\n placementEventMapping?: string;\n placementEventAttributeMapping?: string;\n hashedEmailUserIdentityType?: string;\n onboardingExpProvider?: string;\n loggingUrl?: string;\n errorUrl?: string;\n workspaceIdSyncApiKey?: string;\n}\n\ninterface EventAttributeCondition {\n operator: string;\n attributeValue: string;\n}\n\ninterface PlacementEventRule {\n eventAttributeKey: string;\n conditions: EventAttributeCondition[];\n}\n\ninterface EventAttributeMapping {\n value: string;\n map: string;\n conditions?: EventAttributeCondition[];\n}\n\ninterface PlacementEventMappingEntry {\n jsmap: string;\n value: string;\n}\n\ninterface RoktExtensionEntry {\n value: string;\n}\n\ninterface RoktSelection {\n context?: {\n sessionId?: Promise;\n };\n then?: (callback: (sel: RoktSelection) => void) => Promise;\n catch?: (callback: () => void) => void;\n}\n\ninterface RoktLauncher {\n selectPlacements(options: Record): RoktSelection | Promise;\n hashAttributes(attributes: Record): Promise>;\n use(extensionName: string): Promise;\n}\n\ninterface RoktGlobal {\n createLauncher(options: Record): Promise;\n createLocalLauncher(options: Record): RoktLauncher;\n currentLauncher?: RoktLauncher;\n setExtensionData(data: Record): void;\n}\n\n// FilteredUser is the IMParticleUser shape we receive after kit filtering.\n// `getMPID` and `getUserIdentities` are inherited from the SDK's `User` base type.\ntype FilteredUser = IMParticleUser;\n\n// TODO: Replace with `IIdentitySearchResult` from `@mparticle/web-sdk` once\n// a version that exports it is published (currently on a feature branch in\n// mParticle/mparticle-web-sdk PR #1255). The shape below is intentionally\n// structurally identical so the swap is a one-line import change.\ninterface WorkspaceIdSyncResult {\n httpCode: number;\n body?: {\n context?: string | null;\n mpid?: string;\n matched_identities?: Record;\n is_ephemeral?: boolean;\n is_logged_in?: boolean;\n };\n}\n\n// TODO: Replace with `IdentitySearchCallback`-compatible reference from\n// `@mparticle/web-sdk` once published (mirrors `SDKIdentityApi.search`).\ntype WorkspaceIdSyncSearcher = (\n apiKey: string,\n knownIdentities: IUserIdentities,\n callback: (result: WorkspaceIdSyncResult) => void,\n) => void;\n\ninterface KitFilters {\n userAttributeFilters?: string[];\n filterUserAttributes?: (attributes: Record, filters?: string[]) => Record;\n filteredUser?: FilteredUser | null;\n}\n\ninterface RoktManager {\n attachKit(kit: RoktKit): void | Promise;\n flushOnShoppableAdsReadyMessageQueue?(kit: RoktKit): void;\n filters?: KitFilters;\n domain?: string;\n launcherOptions?: Record;\n getLocalSessionAttributes?(): Record;\n setLocalSessionAttribute?(key: string, value: unknown): void;\n}\n\ninterface MParticleInstance {\n setIntegrationAttribute(moduleId: number, attrs: Record): void;\n}\n\ninterface OptimizelyState {\n getActiveExperimentIds(): string[];\n getVariationMap(): Record;\n}\n\ninterface OptimizelyGlobal {\n get(key: 'state'): OptimizelyState;\n}\n\n// Our view of the mParticle global with Rokt-specific extensions.\n// We access window.mParticle via an explicit cast (see `mp()` helper below)\n// rather than augmenting Window to avoid conflicts with @mparticle/web-sdk declarations.\ninterface MParticleExtended {\n Rokt: RoktManager;\n addForwarder(config: ForwarderRegistration): void;\n getVersion(): string;\n generateHash(value: string): string | number;\n logEvent(name: string, type: number, attrs?: Record): void;\n EventType: { Other: number };\n getInstance(): MParticleInstance;\n sessionManager?: { getSession(): string };\n _getActiveForwarders(): Array<{ name: string }>;\n config?: { isLocalLauncherEnabled?: boolean; isLoggingEnabled?: boolean };\n captureTiming?(metricName: string): void;\n forwarder?: RoktKit;\n loggedEvents?: Array>;\n _registerErrorReportingService?(service: ErrorReportingService): void;\n _registerLoggingService?(service: LoggingService): void;\n Identity?: { search?: WorkspaceIdSyncSearcher };\n}\n\ninterface TestHelpers {\n generateLauncherScript: (domain: string | undefined, extensions: string[]) => string;\n generateThankYouElementScript: (domain: string | undefined) => string;\n extractRoktExtensionConfig: (settingsString?: string) => RoktExtensionConfig;\n hashEventMessage: (messageType: number, eventType: number, eventName: string) => string | number;\n parseSettingsString: (settingsString?: string) => T[];\n generateMappedEventLookup: (placementEventMapping: PlacementEventMappingEntry[]) => Record;\n generateMappedEventAttributeLookup: (mapping: EventAttributeMapping[]) => Record;\n sendAdBlockMeasurementSignals: (domain: string | undefined, version: string | null) => void;\n createAutoRemovedIframe: (src: string) => void;\n djb2: (str: string) => number;\n setAllowedOriginHashes: (hashes: number[]) => void;\n ReportingTransport: typeof ReportingTransport;\n ErrorReportingService: typeof ErrorReportingService;\n LoggingService: typeof LoggingService;\n RateLimiter: typeof RateLimiter;\n ErrorCodes: typeof ErrorCodes;\n WSDKErrorSeverity: typeof WSDKErrorSeverity;\n}\n\ninterface ForwarderRegistration {\n name: string;\n constructor: new () => RoktKit;\n getId: () => number;\n}\n\ninterface ReportingConfig {\n loggingUrl?: string;\n errorUrl?: string;\n integrationDomain?: string;\n isLoggingEnabled: boolean;\n}\n\ninterface ErrorReport {\n message: string;\n code?: string;\n severity?: string;\n stackTrace?: string;\n}\n\n// A log-delivery failure. statusCode is set when the request reached the server\n// and returned a non-2xx status (server-side); it is absent for network-level\n// failures such as ad-blockers, offline, or CORS rejections (client-side).\ninterface DeliveryError extends Error {\n statusCode?: number;\n}\n\ninterface LogEntry {\n message: string;\n code?: string;\n}\n\ninterface RoktExtensionConfig {\n roktExtensionsQueryParams: string[];\n legacyRoktExtensions: string[];\n loadThankYouElement: boolean;\n}\n\ndeclare global {\n interface Window {\n Rokt?: RoktGlobal;\n __rokt_li_guid__?: string;\n optimizely?: OptimizelyGlobal;\n // mParticle is declared as any to avoid conflicts with @mparticle/web-sdk type declarations.\n // We use the typed mp() accessor for all internal accesses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mParticle: any;\n }\n}\n\n// ============================================================\n// Module-level constants\n// ============================================================\n\nconst name = 'Rokt';\nconst moduleId = 181;\nconst EVENT_NAME_SELECT_PLACEMENTS = 'selectPlacements';\nconst ADBLOCK_CONTROL_DOMAIN = 'apps.roktecommerce.com';\nconst INIT_LOG_SAMPLING_RATE = 0.1;\nconst ROKT_THANK_YOU_JOURNEY_EXTENSION = 'ThankYouPageJourney';\nconst ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher';\nconst ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element';\nconst USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace';\n\n// Bound on how long selectPlacements will wait for an in-flight Workspace\n// IDSync search before proceeding without the userIdentifiedInWorkspace flag.\n// Long enough to cover the typical /v1/search round-trip (~50ms); short enough that a\n// stalled search never blocks placement rendering on a thank-you page.\nconst WORKSPACE_SEARCH_SELECT_TIMEOUT_MS = 500;\n\n// ============================================================\n// Reporting service constants\n// ============================================================\n\nconst ErrorCodes = {\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',\n IDENTITY_REQUEST: 'IDENTITY_REQUEST',\n LOG_DELIVERY_FAILURE: 'LOG_DELIVERY_FAILURE',\n} as const;\n\nconst WSDKErrorSeverity = {\n ERROR: 'ERROR',\n INFO: 'INFO',\n WARNING: 'WARNING',\n} as const;\n\nconst DEFAULT_ROKT_DOMAIN = 'apps.rokt-api.com';\nconst LOGGING_ENDPOINT = '/v1/log';\nconst ERROR_ENDPOINT = '/v1/errors';\nconst RATE_LIMIT_PER_SEVERITY = 10;\n\n// ============================================================\n// Helper: typed accessor for window.mParticle\n// We use an explicit cast here to avoid conflicts with @mparticle/web-sdk\n// type declarations while still providing full type safety for our usages.\n// ============================================================\n\nfunction mp(): MParticleExtended {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).mParticle as MParticleExtended;\n}\n\n// ============================================================\n// Module-level utility functions\n// ============================================================\n\nfunction generateLauncherScript(domain: string | undefined, extensions: string[]): string {\n const launcherPath = '/wsdk/integrations/launcher.js';\n const baseUrl = [generateBaseUrl(domain), launcherPath].join('');\n\n if (!extensions || extensions.length === 0) {\n return baseUrl;\n }\n return baseUrl + '?extensions=' + extensions.join(',');\n}\n\nfunction generateThankYouElementScript(domain: string | undefined) {\n const thankYouElementPath = '/rokt-elements/rokt-element-thank-you.js';\n return [generateBaseUrl(domain), thankYouElementPath].join('');\n}\n\nfunction generateBaseUrl(domain: string | undefined) {\n const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN;\n const protocol = 'https://';\n\n return [protocol, resolvedDomain].join('');\n}\n\nfunction generateReportingUrl(configuredUrl: string | undefined, domain: string | undefined, endpoint: string): string {\n if (configuredUrl) {\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n return 'https://' + configuredUrl;\n }\n\n return generateBaseUrl(domain) + endpoint;\n}\n\nfunction loadRoktScript(\n scriptId: string,\n source: string,\n handlers?: { onLoad?: () => void; onError?: (e: Event | string) => void },\n): void {\n if (document.getElementById(scriptId)) return; // resolves the preexisting script issue\n\n const target = document.head || document.body;\n const script = document.createElement('script');\n script.id = scriptId;\n script.type = 'text/javascript';\n script.src = source;\n script.async = true;\n script.crossOrigin = 'anonymous';\n (script as HTMLScriptElement & { fetchPriority: string }).fetchPriority = 'high';\n if (handlers?.onLoad) script.onload = handlers.onLoad;\n if (handlers?.onError) script.onerror = handlers.onError;\n target.appendChild(script);\n}\n\nfunction isObject(val: unknown): val is Record {\n return val != null && typeof val === 'object' && Array.isArray(val) === false;\n}\n\nfunction parseSettingsString(settingsString?: string): T[] {\n if (!settingsString) {\n return [];\n }\n try {\n return JSON.parse(settingsString.replace(/"/g, '\"')) as T[];\n } catch (_error) {\n console.error('Settings string contains invalid JSON');\n }\n return [];\n}\n\nfunction extractRoktExtensionConfig(settingsString?: string): RoktExtensionConfig {\n const settings = settingsString ? parseSettingsString(settingsString) : [];\n const roktExtensionsQueryParams: string[] = [];\n const legacyRoktExtensions: string[] = [];\n let loadThankYouElement = false;\n\n for (let i = 0; i < settings.length; i++) {\n const extensionName = settings[i].value;\n if (extensionName === 'thank-you-journey') {\n loadThankYouElement = true;\n legacyRoktExtensions.push(ROKT_THANK_YOU_JOURNEY_EXTENSION);\n } else {\n roktExtensionsQueryParams.push(extensionName);\n }\n }\n\n return {\n roktExtensionsQueryParams,\n legacyRoktExtensions,\n loadThankYouElement,\n };\n}\n\nasync function registerLegacyExtensions(legacyExtensions: string[], launcher: RoktLauncher | null) {\n const extensions: Promise[] = [];\n if (launcher) {\n for (const extension of legacyExtensions) {\n extensions.push(launcher.use(extension));\n }\n }\n\n return Promise.all(extensions);\n}\n\nfunction generateMappedEventLookup(placementEventMapping: PlacementEventMappingEntry[]): Record {\n if (!placementEventMapping) {\n return {};\n }\n\n const mappedEvents: Record = {};\n for (let i = 0; i < placementEventMapping.length; i++) {\n const mapping = placementEventMapping[i];\n mappedEvents[mapping.jsmap] = mapping.value;\n }\n return mappedEvents;\n}\n\nfunction generateMappedEventAttributeLookup(\n placementEventAttributeMapping: EventAttributeMapping[],\n): Record {\n const mappedAttributeKeys: Record = {};\n if (!Array.isArray(placementEventAttributeMapping)) {\n return mappedAttributeKeys;\n }\n for (let i = 0; i < placementEventAttributeMapping.length; i++) {\n const mapping = placementEventAttributeMapping[i];\n if (!mapping || !isString(mapping.value) || !isString(mapping.map)) {\n continue;\n }\n\n const mappedAttributeKey = mapping.value;\n const eventAttributeKey = mapping.map;\n\n if (!mappedAttributeKeys[mappedAttributeKey]) {\n mappedAttributeKeys[mappedAttributeKey] = [];\n }\n\n mappedAttributeKeys[mappedAttributeKey].push({\n eventAttributeKey: eventAttributeKey,\n conditions: Array.isArray(mapping.conditions) ? mapping.conditions : [],\n });\n }\n return mappedAttributeKeys;\n}\n\nfunction hashEventMessage(messageType: number, eventType: number, eventName: string): string | number {\n return mp().generateHash([messageType, eventType, eventName].join(''));\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value == null) return true;\n if (typeof value === 'object') {\n return Object.keys(value as object).length === 0;\n }\n if (Array.isArray(value)) {\n return (value as unknown[]).length === 0;\n }\n return false;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction generateIntegrationName(customIntegrationName?: string): string {\n const coreSdkVersion = mp().getVersion();\n const kitVersion = process.env.PACKAGE_VERSION;\n let integrationName = 'mParticle_' + 'wsdkv_' + coreSdkVersion + '_kitv_' + kitVersion;\n\n if (customIntegrationName) {\n integrationName += '_' + customIntegrationName;\n }\n return integrationName;\n}\n\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) + hash + str.charCodeAt(i);\n hash = hash & hash;\n }\n return hash;\n}\n\nfunction createAutoRemovedIframe(src: string): void {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');\n iframe.src = src;\n iframe.onload = function () {\n iframe.onload = null;\n if (iframe.parentNode) {\n iframe.parentNode.removeChild(iframe);\n }\n };\n const target = document.body || document.head;\n if (target) {\n target.appendChild(iframe);\n }\n}\n\nfunction sendAdBlockMeasurementSignals(domain: string | undefined, version: string | null): void {\n const originHash = djb2(window.location.origin);\n const allowedOriginHashes = RoktKit._allowedOriginHashes;\n if (allowedOriginHashes.indexOf(originHash) === -1) {\n return;\n }\n\n if (Math.random() >= INIT_LOG_SAMPLING_RATE) {\n return;\n }\n\n const guid = window.__rokt_li_guid__;\n if (!guid) {\n return;\n }\n\n const pageUrl = window.location.href.split('?')[0].split('#')[0];\n const params =\n 'version=' +\n encodeURIComponent(version ?? '') +\n '&launcherInstanceGuid=' +\n encodeURIComponent(guid) +\n '&pageUrl=' +\n encodeURIComponent(pageUrl);\n\n const existingDomain = domain || 'apps.rokt.com';\n createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params);\n\n createAutoRemovedIframe(\n 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true',\n );\n}\n\n// ============================================================\n// Reporting helpers\n// ============================================================\n\nfunction _isDebugModeEnabled(): boolean {\n return typeof window !== 'undefined' && !!window.location?.search?.toLowerCase().includes('mp_enable_logging=true');\n}\n\nfunction _getReportingUrl(): string | undefined {\n return typeof window !== 'undefined' ? window.location?.href : undefined;\n}\n\nfunction _getUserAgent(): string | undefined {\n return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;\n}\n\nclass RateLimiter {\n private _logCount: Record = {};\n\n incrementAndCheck(severity: string): boolean {\n const count = this._logCount[severity] || 0;\n const newCount = count + 1;\n this._logCount[severity] = newCount;\n return newCount > RATE_LIMIT_PER_SEVERITY;\n }\n}\n\nclass ReportingTransport {\n private _isEnabled: boolean;\n private _integrationName: string;\n private _launcherInstanceGuid: string | undefined;\n private _accountId: string | null;\n private _rateLimiter: RateLimiter;\n private readonly _reporter = 'mp-wsdk';\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid: string | undefined,\n accountId: string | null | undefined,\n rateLimiter?: RateLimiter,\n ) {\n const isLoggingEnabled = config.isLoggingEnabled;\n this._integrationName = integrationName || '';\n this._launcherInstanceGuid = launcherInstanceGuid;\n this._accountId = accountId || null;\n this._rateLimiter = rateLimiter || new RateLimiter();\n this._isEnabled = _isDebugModeEnabled() || isLoggingEnabled;\n }\n\n send(\n url: string,\n severity: string,\n msg: string,\n code?: string,\n stackTrace?: string,\n onError?: (error: DeliveryError) => void,\n ): void {\n if (!this._isEnabled || this._rateLimiter.incrementAndCheck(severity)) {\n return;\n }\n\n try {\n const logRequest = {\n additionalInformation: {\n message: msg,\n version: this._integrationName,\n },\n severity,\n code: code || ErrorCodes.UNKNOWN_ERROR,\n url: _getReportingUrl(),\n deviceInfo: _getUserAgent(),\n stackTrace,\n reporter: this._reporter,\n integration: this._integrationName,\n };\n\n const headers: Record = {\n Accept: 'text/plain;charset=UTF-8',\n 'Content-Type': 'application/json',\n 'rokt-launcher-version': this._integrationName,\n 'rokt-wsdk-version': 'joint',\n };\n\n if (this._launcherInstanceGuid) {\n headers['rokt-launcher-instance-guid'] = this._launcherInstanceGuid;\n }\n if (this._accountId) {\n headers['rokt-account-id'] = this._accountId;\n }\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(logRequest),\n })\n .then((response: Response) => {\n // fetch only rejects on network failures; an HTTP 5xx resolves with\n // ok === false. Surface server-side failures so they are not swallowed.\n if (!response.ok) {\n const serverError: DeliveryError = new Error('HTTP ' + response.status + ' from log endpoint');\n serverError.statusCode = response.status;\n throw serverError;\n }\n })\n .catch((error: DeliveryError) => {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error);\n });\n } catch (error) {\n console.error('ReportingTransport: Failed to send log', error);\n if (onError) onError(error as DeliveryError);\n }\n }\n}\n\nclass ErrorReportingService {\n private _transport: ReportingTransport;\n private _errorUrl: string;\n\n constructor(\n config: ReportingConfig,\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._errorUrl = generateReportingUrl(config?.errorUrl, config?.integrationDomain, ERROR_ENDPOINT);\n }\n\n report(error: ErrorReport | null | undefined): void {\n if (!error) return;\n const severity = error.severity || WSDKErrorSeverity.ERROR;\n this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);\n }\n}\n\nclass LoggingService {\n private _transport: ReportingTransport;\n private _loggingUrl: string;\n private _errorReportingService: { report: (e: ErrorReport) => void };\n\n constructor(\n config: ReportingConfig,\n errorReportingService: { report: (e: ErrorReport) => void },\n integrationName: string | null | undefined,\n launcherInstanceGuid?: string,\n accountId?: string | null,\n rateLimiter?: RateLimiter,\n ) {\n this._transport = new ReportingTransport(config, integrationName, launcherInstanceGuid, accountId, rateLimiter);\n this._loggingUrl = generateReportingUrl(config?.loggingUrl, config?.integrationDomain, LOGGING_ENDPOINT);\n this._errorReportingService = errorReportingService;\n }\n\n log(entry: LogEntry | null | undefined): void {\n if (!entry) return;\n this._transport.send(\n this._loggingUrl,\n WSDKErrorSeverity.INFO,\n entry.message,\n entry.code,\n undefined,\n (error: DeliveryError) => {\n if (this._errorReportingService) {\n // A failed log POST is not itself an SDK error. Network-level failures\n // (ad-blockers, offline, CORS) are client-side noise and reported as a\n // WARNING; only a server-side non-2xx response stays at ERROR severity.\n const isServerSide = typeof error.statusCode === 'number';\n this._errorReportingService.report({\n message: 'LoggingService: Failed to send log: ' + error.message,\n code: ErrorCodes.LOG_DELIVERY_FAILURE,\n severity: isServerSide ? WSDKErrorSeverity.ERROR : WSDKErrorSeverity.WARNING,\n });\n }\n },\n );\n }\n}\n\n// ============================================================\n// RoktKit class\n// ============================================================\n\nclass RoktKit implements KitInterface {\n // Static field for allowed origin hashes (mutable by testHelpers)\n public static _allowedOriginHashes: number[] = [-553112570, 549508659];\n\n private static readonly PERFORMANCE_MARKS = {\n RoktScriptAppended: 'mp:RoktScriptAppended',\n };\n\n private static readonly EMAIL_SHA256_KEY = 'emailsha256';\n\n // Public fields (accessed by tests and the mParticle framework)\n public name = name;\n public id = moduleId;\n public moduleId = moduleId;\n public isInitialized = false;\n public launcher: RoktLauncher | null = null;\n public filters: KitFilters = {};\n public userAttributes: Record = {};\n // Flag set by the Workspace IDSync flow on a 200 response. Stored on the\n // kit instance and merged into placement attributes inside selectPlacements.\n public userIdentifiedInWorkspace = false;\n public testHelpers: TestHelpers | null = null;\n public placementEventMappingLookup: Record = {};\n public placementEventAttributeMappingLookup: Record = {};\n public integrationName: string | null = null;\n public domain?: string;\n public errorReportingService: ErrorReportingService | null = null;\n public loggingService: LoggingService | null = null;\n\n // Private fields\n private _mappedEmailSha256Key?: string;\n private _onboardingExpProvider?: string;\n private _thankYouElementOnLoadCallback: (() => void) | null = null;\n private _isThankYouElementLoaded = false;\n private _workspaceIdSyncApiKey?: string;\n\n // Held during a search dispatch so the next selectPlacements call;\n // can wait for the HTTP response before reading userIdentifiedInWorkspace;\n // — otherwise the first placement call ships without the flag.\n private _workspaceSearchInFlightPromise: Promise | null = null;\n // Stable serialization of the identifier set sent in the most recent\n // successful search dispatch. If a subsequent identification arrives with\n // an identical set, we skip the network call (the flag is still correct\n // from the prior search). Keyed over the full IUserIdentities map — not\n // just email — so partners passing hashed email through `other`/`other2-10`\n // or any other identifier benefit from the same dedupe. Cleared on logout\n // so a re-login re-evaluates fresh.\n private _workspaceLastSearchedIdentitiesKey?: string;\n\n // ---- Private helpers ----\n\n private getEventAttributeValue(event: SDKEvent, eventAttributeKey: string): unknown {\n const attributes = event && event.EventAttributes;\n if (!attributes) {\n return null;\n }\n\n if (typeof attributes[eventAttributeKey] === 'undefined') {\n return null;\n }\n\n return attributes[eventAttributeKey];\n }\n\n private doesEventAttributeConditionMatch(condition: EventAttributeCondition, actualValue: unknown): boolean {\n if (!condition || !isString(condition.operator)) {\n return false;\n }\n\n const operator = condition.operator.toLowerCase();\n const expectedValue = condition.attributeValue;\n\n if (operator === 'exists') {\n return actualValue !== null;\n }\n\n if (actualValue == null) {\n return false;\n }\n\n if (operator === 'equals') {\n return String(actualValue) === String(expectedValue);\n }\n\n if (operator === 'contains') {\n return String(actualValue).indexOf(String(expectedValue)) !== -1;\n }\n\n return false;\n }\n\n private doesEventMatchRule(event: SDKEvent, rule: PlacementEventRule): boolean {\n if (!rule || !isString(rule.eventAttributeKey)) {\n return false;\n }\n\n const conditions = rule.conditions;\n if (!Array.isArray(conditions)) {\n return false;\n }\n\n const actualValue = this.getEventAttributeValue(event, rule.eventAttributeKey);\n\n if (conditions.length === 0) {\n return actualValue !== null;\n }\n for (let i = 0; i < conditions.length; i++) {\n if (!this.doesEventAttributeConditionMatch(conditions[i], actualValue)) {\n return false;\n }\n }\n\n return true;\n }\n\n private applyPlacementEventAttributeMapping(event: SDKEvent): void {\n const mappedAttributeKeys = Object.keys(this.placementEventAttributeMappingLookup);\n for (let i = 0; i < mappedAttributeKeys.length; i++) {\n const mappedAttributeKey = mappedAttributeKeys[i];\n const rulesForMappedAttributeKey = this.placementEventAttributeMappingLookup[mappedAttributeKey];\n if (isEmpty(rulesForMappedAttributeKey)) {\n continue;\n }\n\n // Require ALL rules for the same key to match (AND).\n let allMatch = true;\n for (let j = 0; j < rulesForMappedAttributeKey.length; j++) {\n if (!this.doesEventMatchRule(event, rulesForMappedAttributeKey[j])) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) {\n continue;\n }\n\n mp().Rokt.setLocalSessionAttribute?.(mappedAttributeKey, true);\n }\n }\n\n private isLauncherReadyToAttach(): boolean {\n return !!window.Rokt && typeof window.Rokt.createLauncher === 'function';\n }\n\n /**\n * Returns the user identities from the filtered user, if any.\n */\n private returnUserIdentities(filteredUser: FilteredUser | null | undefined): Record {\n if (!filteredUser || !filteredUser.getUserIdentities) {\n return {};\n }\n\n const userIdentities: IUserIdentities = filteredUser.getUserIdentities().userIdentities;\n\n return this.replaceOtherIdentityWithEmailsha256(userIdentities);\n }\n\n private returnLocalSessionAttributes(): Record {\n if (!mp().Rokt || typeof mp().Rokt.getLocalSessionAttributes !== 'function') {\n return {};\n }\n if (isEmpty(this.placementEventMappingLookup) && isEmpty(this.placementEventAttributeMappingLookup)) {\n return {};\n }\n return mp().Rokt.getLocalSessionAttributes!();\n }\n\n private replaceOtherIdentityWithEmailsha256(userIdentities: IUserIdentities): Record {\n const newUserIdentities: Record = { ...(userIdentities || {}) };\n const key = this._mappedEmailSha256Key;\n if (key && userIdentities[key as keyof IUserIdentities]) {\n newUserIdentities[RoktKit.EMAIL_SHA256_KEY] = userIdentities[key as keyof IUserIdentities] as string;\n }\n if (key) {\n delete newUserIdentities[key];\n }\n\n return newUserIdentities;\n }\n\n private logSelectPlacementsEvent(attributes: unknown): void {\n if (!window.mParticle || typeof mp().logEvent !== 'function') {\n return;\n }\n\n if (!isObject(attributes)) {\n return;\n }\n\n const EVENT_TYPE_OTHER = mp().EventType.Other;\n\n mp().logEvent(EVENT_NAME_SELECT_PLACEMENTS, EVENT_TYPE_OTHER, attributes as Record);\n }\n\n private setRoktSessionId(sessionId: string): void {\n if (!sessionId || typeof sessionId !== 'string') {\n return;\n }\n try {\n const mpInstance = mp().getInstance();\n if (mpInstance && typeof mpInstance.setIntegrationAttribute === 'function') {\n mpInstance.setIntegrationAttribute(moduleId, {\n roktSessionId: sessionId,\n });\n }\n } catch (_e) {\n // Best effort — never let this break the partner page\n }\n }\n\n private attachLauncher(\n accountId: string,\n launcherOptions: Record,\n legacyRoktExtensions: string[] = [],\n ): void {\n const mpSessionId =\n mp() && mp().sessionManager && typeof mp().sessionManager!.getSession === 'function'\n ? mp().sessionManager!.getSession()\n : undefined;\n\n const options: Record = {\n accountId,\n ...(launcherOptions || {}),\n ...(mpSessionId ? { mpSessionId } : {}),\n };\n\n let launcherPromise: Promise;\n if (this.isPartnerInLocalLauncherTestGroup()) {\n launcherPromise = Promise.resolve(window.Rokt!.createLocalLauncher(options));\n } else {\n launcherPromise = window.Rokt!.createLauncher(options);\n }\n\n launcherPromise\n .then(async (launcher) => {\n await registerLegacyExtensions(legacyRoktExtensions, launcher);\n this.initRoktLauncher(launcher);\n })\n .catch((err: unknown) => {\n console.error('Error creating Rokt launcher:', err);\n });\n }\n\n private initRoktLauncher(launcher: RoktLauncher): void {\n // Assign the launcher to a global variable for later access\n if (window.Rokt) {\n window.Rokt.currentLauncher = launcher;\n }\n // Locally cache the launcher and filters\n this.launcher = launcher;\n\n const roktFilters = mp().Rokt?.filters;\n\n if (!roktFilters) {\n console.warn('Rokt Kit: No filters have been set.');\n } else {\n this.filters = roktFilters;\n if (!roktFilters.filteredUser) {\n console.warn('Rokt Kit: No filtered user has been set.');\n } else {\n this._workspaceSearchInFlightPromise = this.search(roktFilters.filteredUser);\n }\n }\n\n // Kit must be initialized before attaching to the Rokt manager\n this.isInitialized = true;\n\n sendAdBlockMeasurementSignals(this.domain, this.integrationName);\n\n // Attaches the kit to the Rokt manager\n mp().Rokt.attachKit(this);\n }\n\n private fetchOptimizely(): Record {\n const forwarders = mp()\n ._getActiveForwarders()\n .filter((forwarder) => forwarder.name === 'Optimizely');\n\n try {\n if (forwarders.length > 0 && window.optimizely) {\n const optimizelyState = window.optimizely.get('state');\n if (!optimizelyState || !optimizelyState.getActiveExperimentIds) {\n return {};\n }\n const activeExperimentIds = optimizelyState.getActiveExperimentIds();\n const activeExperiments = activeExperimentIds.reduce((acc: Record, expId: string) => {\n acc['rokt.custom.optimizely.experiment.' + expId + '.variationId'] =\n optimizelyState.getVariationMap()[expId].id;\n return acc;\n }, {});\n return activeExperiments;\n }\n } catch (error) {\n console.error('Error fetching Optimizely attributes:', error);\n }\n return {};\n }\n\n private isKitReady(): boolean {\n return !!(this.isInitialized && this.launcher);\n }\n\n private isPartnerInLocalLauncherTestGroup(): boolean {\n return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup());\n }\n\n private isAssignedToSampleGroup(): boolean {\n const LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD = 0.5;\n return Math.random() > LOCAL_LAUNCHER_TEST_GROUP_THRESHOLD;\n }\n\n private captureTiming(metricName: string): void {\n if (window && mp() && mp().captureTiming && metricName) {\n mp().captureTiming!(metricName);\n }\n }\n\n // ---- Public methods (mParticle Kit Callbacks) ----\n\n /**\n * Initializes the Rokt forwarder with settings from the mParticle server.\n */\n public init(\n settings: Record,\n _service: unknown,\n testMode: boolean,\n _trackerId: unknown,\n filteredUserAttributes?: Record,\n ): string {\n const kitSettings = settings as unknown as RoktKitSettings;\n const accountId = kitSettings.accountId;\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredUserAttributes);\n this._onboardingExpProvider = kitSettings.onboardingExpProvider;\n\n const placementEventMapping = parseSettingsString(kitSettings.placementEventMapping);\n this.placementEventMappingLookup = generateMappedEventLookup(placementEventMapping);\n\n const placementEventAttributeMapping = parseSettingsString(\n kitSettings.placementEventAttributeMapping,\n );\n this.placementEventAttributeMappingLookup = generateMappedEventAttributeLookup(placementEventAttributeMapping);\n\n // Set dynamic OTHER_IDENTITY based on server settings\n if (kitSettings.hashedEmailUserIdentityType) {\n this._mappedEmailSha256Key = kitSettings.hashedEmailUserIdentityType.toLowerCase();\n }\n\n this._workspaceIdSyncApiKey = isString(kitSettings.workspaceIdSyncApiKey)\n ? kitSettings.workspaceIdSyncApiKey\n : undefined;\n\n const domain = mp().Rokt?.domain;\n const { roktExtensionsQueryParams, legacyRoktExtensions, loadThankYouElement } = extractRoktExtensionConfig(\n kitSettings.roktExtensions,\n );\n const launcherOptions: Record = {\n ...((mp().Rokt?.launcherOptions as Record) || {}),\n };\n this.integrationName = generateIntegrationName(launcherOptions.integrationName as string | undefined);\n launcherOptions.integrationName = this.integrationName;\n\n this.domain = domain;\n\n const reportingConfig: ReportingConfig = {\n loggingUrl: kitSettings.loggingUrl,\n errorUrl: kitSettings.errorUrl,\n integrationDomain: domain,\n isLoggingEnabled: mp().config?.isLoggingEnabled === true,\n };\n const errorReportingService = new ErrorReportingService(\n reportingConfig,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n const loggingService = new LoggingService(\n reportingConfig,\n errorReportingService,\n this.integrationName,\n window.__rokt_li_guid__,\n kitSettings.accountId,\n );\n\n this.errorReportingService = errorReportingService;\n this.loggingService = loggingService;\n\n if (mp()._registerErrorReportingService) {\n mp()._registerErrorReportingService!(errorReportingService);\n }\n if (mp()._registerLoggingService) {\n mp()._registerLoggingService!(loggingService);\n }\n\n if (testMode) {\n this.testHelpers = {\n generateLauncherScript: generateLauncherScript,\n generateThankYouElementScript: generateThankYouElementScript,\n extractRoktExtensionConfig: extractRoktExtensionConfig,\n hashEventMessage: hashEventMessage,\n parseSettingsString: parseSettingsString,\n generateMappedEventLookup: generateMappedEventLookup,\n generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,\n sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,\n createAutoRemovedIframe: createAutoRemovedIframe,\n djb2: djb2,\n setAllowedOriginHashes: (hashes: number[]) => {\n RoktKit._allowedOriginHashes = hashes;\n },\n ReportingTransport: ReportingTransport,\n ErrorReportingService: ErrorReportingService,\n LoggingService: LoggingService,\n RateLimiter: RateLimiter,\n ErrorCodes: ErrorCodes,\n WSDKErrorSeverity: WSDKErrorSeverity,\n };\n this.attachLauncher(accountId, launcherOptions);\n return 'Successfully initialized: ' + name;\n }\n\n if (loadThankYouElement) {\n mp().Rokt.flushOnShoppableAdsReadyMessageQueue?.(this);\n loadRoktScript(ROKT_THANK_YOU_ELEMENT_SCRIPT_ID, generateThankYouElementScript(domain), {\n onLoad: () => {\n this._isThankYouElementLoaded = true;\n if (this._thankYouElementOnLoadCallback) {\n this._thankYouElementOnLoadCallback();\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt Thank You Element script:', error);\n },\n });\n }\n\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n loadRoktScript(ROKT_INTEGRATION_SCRIPT_ID, generateLauncherScript(domain, roktExtensionsQueryParams), {\n onLoad: () => {\n if (this.isLauncherReadyToAttach()) {\n this.attachLauncher(accountId, launcherOptions, legacyRoktExtensions);\n } else {\n console.error('Rokt object is not available after script load.');\n }\n },\n onError: (error) => {\n console.error('Error loading Rokt launcher script:', error);\n },\n });\n\n this.captureTiming(RoktKit.PERFORMANCE_MARKS.RoktScriptAppended);\n }\n\n return 'Successfully initialized: ' + name;\n }\n\n public process(event: SDKEvent): string {\n if (!this.isKitReady()) {\n return 'Kit not ready for forwarder: ' + name;\n }\n if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') {\n if (!isEmpty(this.placementEventAttributeMappingLookup)) {\n this.applyPlacementEventAttributeMapping(event);\n }\n\n if (!isEmpty(this.placementEventMappingLookup)) {\n const hashedEvent = hashEventMessage(event.EventDataType, event.EventCategory, event.EventName ?? '');\n if (this.placementEventMappingLookup[String(hashedEvent)]) {\n mp().Rokt.setLocalSessionAttribute?.(this.placementEventMappingLookup[String(hashedEvent)], true);\n }\n }\n }\n\n return 'Successfully sent to forwarder: ' + name;\n }\n\n public setExtensionData(partnerExtensionData: Record): void {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return;\n }\n\n window.Rokt!.setExtensionData(partnerExtensionData);\n }\n\n public setUserAttribute(key: string, value: unknown): string {\n if (!isSelectPlacementsAttributePersistenceDenied(key)) {\n this.userAttributes[key] = value;\n }\n return 'Successfully set user attribute for forwarder: ' + name;\n }\n\n public removeUserAttribute(key: string): string {\n delete this.userAttributes[key];\n return 'Successfully removed user attribute for forwarder: ' + name;\n }\n\n private handleIdentityComplete(user: IMParticleUser, callbackName: string): string {\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(user.getAllUserAttributes());\n return 'Successfully called ' + callbackName + ' for forwarder: ' + name;\n }\n\n public onUserIdentified(user: IMParticleUser): string {\n const filteredUser = user as FilteredUser;\n this.filters.filteredUser = filteredUser;\n this._workspaceSearchInFlightPromise = this.search(filteredUser);\n return this.handleIdentityComplete(user, 'onUserIdentified');\n }\n\n private search(filteredUser: FilteredUser): Promise {\n const apiKey = this._workspaceIdSyncApiKey;\n if (!apiKey) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n const search = mp().Identity?.search;\n if (typeof search !== 'function') {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n const userIdentities: IUserIdentities | null = filteredUser.getUserIdentities\n ? filteredUser.getUserIdentities().userIdentities\n : null;\n\n // Forward every non-empty string identifier the user has — email,\n // customerid, other/other2-10 (commonly used for hashed email),\n // mobile_number, facebook, etc. The host SDK's Identity.search accepts\n // the full IUserIdentities surface and the server validates it.\n const knownIdentities: Record = {};\n if (userIdentities) {\n for (const key of Object.keys(userIdentities) as Array) {\n const value = userIdentities[key];\n if (isString(value) && value.length > 0) {\n knownIdentities[key] = value;\n }\n }\n }\n\n const identityKeys = Object.keys(knownIdentities);\n if (identityKeys.length === 0) {\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return Promise.resolve();\n }\n\n // Stable cache key: sort keys so insertion-order differences don't\n // cause false misses. The values are partner-supplied strings; no\n // hashing needed — equality on this serialization is sufficient.\n const identitiesKey = identityKeys\n .sort()\n .map((k) => `${k}=${knownIdentities[k]}`)\n .join('&');\n\n // Same identifier set as the last successful dispatch → skip the\n // network call. The current flag value still reflects the correct\n // match status.\n if (identitiesKey === this._workspaceLastSearchedIdentitiesKey) {\n return this._workspaceSearchInFlightPromise || Promise.resolve();\n }\n\n // New / different identifier set → reset and re-search. Cache the key\n // up front so a second concurrent invocation with the same set also\n // dedupes.\n this.userIdentifiedInWorkspace = false;\n this._workspaceLastSearchedIdentitiesKey = identitiesKey;\n\n return new Promise((resolve) => {\n try {\n search(apiKey, knownIdentities as IUserIdentities, (result: WorkspaceIdSyncResult) => {\n if (result?.httpCode === 200) {\n this.userIdentifiedInWorkspace = true;\n }\n resolve();\n });\n } catch (err) {\n console.error('Rokt Kit: Workspace IDSync search failed', err);\n // Dispatch failed — clear the cache so the same identifier set\n // can retry on the next identification rather than being stuck\n // behind a poisoned entry that short-circuits future searches.\n this._workspaceLastSearchedIdentitiesKey = undefined;\n resolve();\n }\n });\n }\n\n public onLoginComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onLoginComplete');\n }\n\n public onLogoutComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n // Anonymous sessions must not carry the previous user's match forward.\n // Clear the flag explicitly here. Also clear the identities cache so a\n // re-login (possibly with the same identifiers) dispatches a fresh\n // search rather than reusing a stale answer.\n this.userIdentifiedInWorkspace = false;\n this._workspaceSearchInFlightPromise = null;\n this._workspaceLastSearchedIdentitiesKey = undefined;\n return this.handleIdentityComplete(user, 'onLogoutComplete');\n }\n\n public onModifyComplete(user: IMParticleUser, _filteredIdentityRequest: unknown): string {\n return this.handleIdentityComplete(user, 'onModifyComplete');\n }\n\n /**\n * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options.\n *\n * If a Workspace IDSync search is in flight from a recent onUserIdentified\n * call, this method waits up to `WORKSPACE_SEARCH_SELECT_TIMEOUT_MS` for it\n * to settle so the first placement call can include the\n * `userIdentifiedInWorkspace` flag without racing the network response.\n * The timeout protects against a stalled or slow search blocking placement\n * rendering — if it fires, selectPlacements proceeds without the flag.\n *\n * Implementation note: this method stays non-async deliberately. First,\n * the public return type is `RoktSelection | Promise |\n * undefined` — a superset of the `RoktSelection | Promise`\n * shape declared for `RoktLauncher.selectPlacements` above (line ~70).\n * Marking this `async` would narrow it to `Promise` and silently change the contract for callers that read\n * the result synchronously. Second, `RoktSelection` has an optional\n * `then?` member, so TS treats it as ambiguously promise-like and\n * rejects it as the awaited return of an async function (TS1058) —\n * working around that would require a cast or wrapping every return in\n * `Promise.resolve(...)`. The inner work runs in `_dispatchPlacements`;\n * this wrapper just gates it on the in-flight search via `Promise.race`.\n */\n public selectPlacements(options: Record): RoktSelection | Promise | undefined {\n if (this._workspaceSearchInFlightPromise) {\n const inFlight = this._workspaceSearchInFlightPromise;\n return Promise.race([\n inFlight,\n new Promise((resolve) => setTimeout(resolve, WORKSPACE_SEARCH_SELECT_TIMEOUT_MS)),\n ]).then(() => this._dispatchPlacements(options)) as Promise;\n }\n return this._dispatchPlacements(options);\n }\n\n private _dispatchPlacements(options: Record): RoktSelection | Promise | undefined {\n const attributes = ((options && (options.attributes as Record)) || {}) as Record;\n const cachedUserAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(this.userAttributes);\n const placementAttributes: Record = { ...cachedUserAttributes, ...attributes };\n\n const filters = this.filters || {};\n const userAttributeFilters = (filters.userAttributeFilters as string[]) || [];\n const filteredUser = filters.filteredUser || null;\n const mpid = filteredUser ? filteredUser.getMPID() : null;\n\n let filteredAttributes: Record;\n\n if (!filters) {\n console.warn('Rokt Kit: No filters available, using user attributes');\n filteredAttributes = placementAttributes;\n } else if (filters.filterUserAttributes) {\n filteredAttributes = filters.filterUserAttributes(placementAttributes, userAttributeFilters);\n } else {\n filteredAttributes = placementAttributes;\n }\n\n this.userAttributes = removeSelectPlacementsAttributePersistenceDeniedAttributes(filteredAttributes);\n\n const optimizelyAttributes = this._onboardingExpProvider === 'Optimizely' ? this.fetchOptimizely() : {};\n\n const filteredUserIdentities = this.returnUserIdentities(filteredUser);\n\n const localSessionAttributes = this.returnLocalSessionAttributes();\n\n const selectPlacementsAttributes: Record = {\n ...(filteredUserIdentities as Record),\n ...filteredAttributes,\n ...optimizelyAttributes,\n ...localSessionAttributes,\n ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}),\n mpid,\n };\n\n const selectPlacementsOptions: Record = { ...options, attributes: selectPlacementsAttributes };\n\n const selection = this.launcher!.selectPlacements(selectPlacementsOptions);\n\n // After selection resolves, sync the Rokt session ID back to mParticle, then log\n const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);\n\n void Promise.resolve(selection)\n .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))\n .catch(() => undefined)\n .finally(logSelection);\n\n return selection;\n }\n\n /**\n * Passes attributes to the Rokt Web SDK for client-side hashing.\n */\n public hashAttributes(attributes: Record): Promise> | null {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return null;\n }\n return this.launcher!.hashAttributes(attributes);\n }\n\n /**\n * Enables optional Integration Launcher extensions before selecting placements.\n *\n * @deprecated This functionality has been internalized and will be removed in a future release.\n */\n public use(extensionName: string): Promise {\n if (!this.isKitReady()) {\n console.error('Rokt Kit: Not initialized');\n return Promise.reject(new Error('Rokt Kit: Not initialized'));\n }\n if (!extensionName || !isString(extensionName)) {\n return Promise.reject(new Error('Rokt Kit: Invalid extension name'));\n }\n return this.launcher!.use(extensionName);\n }\n\n /**\n * Registers a callback to be invoked once rokt-thank-you-element.js becomes available.\n */\n public onShoppableAdsReady(callback: () => void) {\n if (this._isThankYouElementLoaded) {\n callback();\n } else {\n this._thankYouElementOnLoadCallback = callback;\n }\n }\n}\n\n// ============================================================\n// Kit registration\n// ============================================================\n\nfunction getId(): number {\n return moduleId;\n}\n\nfunction register(config: { kits?: Record }): void {\n if (!config) {\n window.console.log('You must pass a config object to register the kit ' + name);\n return;\n }\n if (!isObject(config)) {\n window.console.log(\"'config' must be an object. You passed in a \" + typeof config);\n return;\n }\n\n if (isObject(config.kits)) {\n (config.kits as Record)[name] = {\n constructor: RoktKit,\n };\n } else {\n config.kits = {};\n config.kits[name] = {\n constructor: RoktKit,\n };\n }\n window.console.log('Successfully registered ' + name + ' to your mParticle configuration');\n}\n\nif (typeof window !== 'undefined' && window.mParticle && mp().addForwarder) {\n mp().addForwarder({\n name: name,\n constructor: RoktKit,\n getId: getId,\n });\n}\n\nexport { register };\n"],"names":["SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_LIST","SELECT_PLACEMENTS_ATTRIBUTE_PERSISTENCE_DENY_SET","isSelectPlacementsAttributePersistenceDenied","key","removeSelectPlacementsAttributePersistenceDeniedAttributes","attributes","filteredAttributes","sourceAttributes","attributeKeys","i","name","moduleId","EVENT_NAME_SELECT_PLACEMENTS","ADBLOCK_CONTROL_DOMAIN","INIT_LOG_SAMPLING_RATE","ROKT_THANK_YOU_JOURNEY_EXTENSION","ROKT_INTEGRATION_SCRIPT_ID","ROKT_THANK_YOU_ELEMENT_SCRIPT_ID","USER_IDENTIFIED_IN_WORKSPACE_KEY","WORKSPACE_SEARCH_SELECT_TIMEOUT_MS","ErrorCodes","WSDKErrorSeverity","DEFAULT_ROKT_DOMAIN","LOGGING_ENDPOINT","ERROR_ENDPOINT","RATE_LIMIT_PER_SEVERITY","mp","generateLauncherScript","domain","extensions","baseUrl","generateBaseUrl","generateThankYouElementScript","generateReportingUrl","configuredUrl","endpoint","loadRoktScript","scriptId","source","handlers","target","script","isObject","val","parseSettingsString","settingsString","extractRoktExtensionConfig","settings","roktExtensionsQueryParams","legacyRoktExtensions","loadThankYouElement","extensionName","registerLegacyExtensions","legacyExtensions","launcher","extension","generateMappedEventLookup","placementEventMapping","mappedEvents","mapping","generateMappedEventAttributeLookup","placementEventAttributeMapping","mappedAttributeKeys","isString","mappedAttributeKey","eventAttributeKey","hashEventMessage","messageType","eventType","eventName","isEmpty","value","generateIntegrationName","customIntegrationName","integrationName","djb2","str","hash","createAutoRemovedIframe","src","iframe","sendAdBlockMeasurementSignals","version","originHash","RoktKit","guid","pageUrl","params","_isDebugModeEnabled","_getReportingUrl","_getUserAgent","RateLimiter","severity","newCount","ReportingTransport","config","launcherInstanceGuid","accountId","rateLimiter","isLoggingEnabled","url","msg","code","stackTrace","onError","logRequest","headers","response","serverError","error","ErrorReportingService","LoggingService","errorReportingService","entry","isServerSide","_RoktKit","event","condition","actualValue","operator","expectedValue","rule","conditions","rulesForMappedAttributeKey","allMatch","j","filteredUser","userIdentities","newUserIdentities","EVENT_TYPE_OTHER","sessionId","mpInstance","launcherOptions","mpSessionId","options","launcherPromise","err","roktFilters","forwarders","forwarder","optimizelyState","acc","expId","metricName","_service","testMode","_trackerId","filteredUserAttributes","kitSettings","reportingConfig","loggingService","hashes","hashedEvent","partnerExtensionData","user","callbackName","apiKey","search","knownIdentities","identityKeys","identitiesKey","k","resolve","result","_filteredIdentityRequest","inFlight","placementAttributes","filters","userAttributeFilters","mpid","optimizelyAttributes","filteredUserIdentities","localSessionAttributes","selectPlacementsAttributes","selectPlacementsOptions","selection","logSelection","sel","callback","getId","register"],"mappings":"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"} \ No newline at end of file From a7fc33c812ec7c2c057cf634020ebd4cbd4e819a Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 15:58:56 -0400 Subject: [PATCH 06/27] chore: drop design spec doc from PR --- .../2026-07-31-page-view-capture-design.md | 170 ------------------ 1 file changed, 170 deletions(-) delete mode 100644 docs/superpowers/specs/2026-07-31-page-view-capture-design.md diff --git a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md b/docs/superpowers/specs/2026-07-31-page-view-capture-design.md deleted file mode 100644 index 3e51621..0000000 --- a/docs/superpowers/specs/2026-07-31-page-view-capture-design.md +++ /dev/null @@ -1,170 +0,0 @@ -# Page-View Capture into Local Session Attributes - -**Date:** 2026-07-31 -**Branch:** `capture-page-views` (branched off `main`; PR targets `development`) -**Module:** Rokt Web Kit (`@mparticle/web-rokt-kit`, module ID 181) -**Source file:** `src/Rokt-Kit.ts` - -## Problem - -The kit should record page-view events as they are logged and store them -durably ("offline") so they can be processed later. The stored history should -automatically feed into the next `selectPlacements` call as targeting context. - -## Key facts established during design - -- The forwarder already exposes a per-event hook: `process(event: SDKEvent)` - (`src/Rokt-Kit.ts:1164`). It is invoked by the mParticle SDK for every logged - event. No new registration or hook is required. -- Page views are identified by `event.EventDataType === 3` - (`MessageType.PageView`, confirmed in `@mparticle/web-sdk` types). The page - name is `event.EventName`. -- The full URL is **not** carried in the page-view event payload — `logPageView()` - only attaches `{ hostname, title }`. The reliable full URL is - `window.location.href`, read at capture time. The kit runs browser-side, so - `window` is always available. -- `setLocalSessionAttribute(key, value)` / `getLocalSessionAttributes()` live on - the core SDK's Rokt manager (`window.mParticle.Rokt`). `setLocalSessionAttribute` - **persists to browser storage** (`persistenceData.gs.lsa` → `savePersistence`). - That persistence is the "offline" durability we rely on, and values may be - arrays/objects. -- `returnLocalSessionAttributes()` (`src/Rokt-Kit.ts:865`) already reads the - store back into `selectPlacements`, **but** it early-returns `{}` unless a - placement-event mapping lookup is non-empty. That guard must be relaxed for - captured page views to reach `selectPlacements`. - -## Design - -### Data shape - -A single local-session-attribute key, `mpPageViews`, holds a JSON array of the -last **N = 25** entries, newest last: - -```ts -interface StoredPageView { - name: string; // event.EventName - pageUrl: string; // window.location.href (full, verbatim — see Security note) - sourceMessageId: string; // event.SourceMessageId - timestamp: number; // event.Timestamp - activeTimeOnSite: number; // event.ActiveTimeOnSite - eventAttributes?: { [key: string]: string }; // event.EventAttributes (see Security note) -} -``` - -Constants: -- `PAGE_VIEWS_KEY = 'mpPageViews'` -- `MAX_PAGE_VIEWS = 25` - -### Capture flow (in `process()`) - -After the existing readiness check, and guarded by -`typeof mp().Rokt?.setLocalSessionAttribute === 'function'`: - -1. If `event.EventDataType !== MESSAGE_TYPE_PAGE_VIEW (3)`, skip page-view - capture (existing placement-mapping logic is unaffected). -2. Read the current list: `mp().Rokt.getLocalSessionAttributes()?.[PAGE_VIEWS_KEY]`, - defaulting to `[]`. Coerce non-arrays to `[]` defensively. -3. Build the record from the event and current location: - ```ts - { - name: event.EventName, - pageUrl: sanitizeUrl(window.location.href), - sourceMessageId: event.SourceMessageId, - timestamp: event.Timestamp, - activeTimeOnSite: event.ActiveTimeOnSite, - eventAttributes: event.EventAttributes, - } - ``` -4. Append; if `list.length > MAX_PAGE_VIEWS`, drop from the front (evict oldest). -5. Write back via `mp().Rokt.setLocalSessionAttribute(PAGE_VIEWS_KEY, list)`. - -Capture is wrapped so a malformed event can never throw out of the forwarder -(consistent with the rest of `process()`), and runs in addition to — not instead -of — the existing placement-event-mapping logic. - -### Feeding `selectPlacements` - -Relax the guard in `returnLocalSessionAttributes()` so it returns the stored -attributes whenever the store is available and populated, rather than only when -a placement-event mapping lookup is non-empty. This makes `mpPageViews` flow -into `selectPlacements` through the existing path, without duplicating the store -read. - -### URL sanitization boundary - -`sanitizeUrl(href: string): string` isolates URL handling. Per the decision -below it returns `href` verbatim for now. Tightening to strip the query and -fragment later is a one-line change inside this helper and touches nothing else. - -## Decisions - -| Decision | Choice | Rationale | -|----------|--------|-----------| -| Downstream purpose | Feed the next `selectPlacements` | Reuses existing `returnLocalSessionAttributes()` path | -| Payload per view | Full list, last N (name, pageUrl, sourceMessageId, timestamp, activeTimeOnSite, eventAttributes) | Richest targeting signal | -| Detection | `event.EventDataType === 3` (PageView) | Standard mParticle page-view classification | -| List cap (N) | 25 | User choice; see size caveat below | -| URL handling | Full URL verbatim | User choice; see Security note | - -## ⚠️ Security note (Rokt secure-coding policy) - -The stored value is the **full URL including query string and fragment**. Query -strings frequently carry PII (emails, tokens, order IDs). This list is -**persisted to browser storage and sent to Rokt** on the next `selectPlacements`. -The full-URL choice is implemented as requested; the recommended safer default -is to strip the query and fragment. The `sanitizeUrl()` helper isolates this so -it can be tightened later without touching capture logic. - -`eventAttributes` is stored verbatim from the event and can likewise contain -arbitrary developer-supplied values, including PII. It is persisted and sent to -Rokt under the same conditions. Note the SDK does not run kit user-attribute -filters over page-view `EventAttributes`, so nothing is stripped automatically — -flagging in case attribute-level filtering is wanted later. - -**Size caveat:** N = 25 full URLs is persisted to cookie/localStorage-backed -storage, which has size limits. If entries approach those limits, revisit N or -the URL handling. - -## Testing - -Vitest cases in `test/src/tests.spec.ts`: - -1. A page-view event (`EventDataType === 3`) appends a record with correct - `name`, `pageUrl`, `sourceMessageId`, `timestamp`, `activeTimeOnSite`, and - `eventAttributes`. -2. A non-page-view event does not append. -3. The list caps at `MAX_PAGE_VIEWS` and evicts the oldest entry. -4. Capture no-ops when `setLocalSessionAttribute` is unavailable (does not throw). -5. Stored page views surface through `returnLocalSessionAttributes()` and into - `selectPlacements`. - -## Time on page (derived output) - -Each `page_events` entry carries a `timeOnPage` field: the active time the user -spent on *that* page before the next page view was logged. It is computed at -read time in `buildPageEvents()` as the diff of consecutive `activeTimeOnSite` -values: - -``` -entry[i].timeOnPage = pageViews[i+1].activeTimeOnSite − pageViews[i].activeTimeOnSite -``` - -Nothing new is persisted; `StoredPageView` is unchanged. `timeOnPage` is only -emitted when it is a genuine, non-negative number. It is **omitted** (left -`undefined`) for: - -- the last (still-open) entry — there is no next page view yet; -- a negative diff (clock skew, session reset, out-of-order events) — dropped - rather than surfaced as a misleading value; -- a missing/non-numeric `activeTimeOnSite` on either side. - -`undefined` uniformly means "couldn't compute / not yet known." - -## Out of scope - -- No new kit setting / server-side feature gate (capture is always on when the - store is available). -- No query-string stripping (deferred behind `sanitizeUrl()`). -- No changes to placement-event mapping behavior. -- Time on page is derived at read time only — no change to capture, - persistence, or `StoredPageView`. From f0b4e899e9edc807c60ac210b024eab7aa8ae00a Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 16:01:26 -0400 Subject: [PATCH 07/27] refactor: trim redundant comments in page-view code --- src/Rokt-Kit.ts | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index e1fae50..4f53b77 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -62,16 +62,15 @@ interface RoktExtensionEntry { } // A captured page view, persisted (newest last) under PAGE_VIEWS_KEY. -// See the security note in the design spec: pageUrl and eventAttributes are -// stored verbatim and may contain PII; they are persisted to browser storage -// and sent to Rokt on the next selectPlacements call. +// pageUrl and eventAttributes are stored verbatim and may contain PII; they are +// persisted to browser storage and sent to Rokt on the next selectPlacements call. interface StoredPageView { - name: string; // event.EventName - pageUrl: string; // window.location.href (see sanitizeUrl) - sourceMessageId: string; // event.SourceMessageId - timestamp: number; // event.Timestamp - activeTimeOnSite: number; // event.ActiveTimeOnSite - eventAttributes?: { [key: string]: string }; // event.EventAttributes + name: string; + pageUrl: string; + sourceMessageId: string; + timestamp: number; + activeTimeOnSite: number; + eventAttributes?: { [key: string]: string }; } interface RoktSelection { @@ -257,18 +256,13 @@ const ROKT_INTEGRATION_SCRIPT_ID = 'rokt-launcher'; const ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element'; const USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace'; -// Page-view capture. Page views are identified by the mParticle message type -// PageView (3); the last MAX_PAGE_VIEWS are stored under PAGE_VIEWS_KEY in the -// Rokt manager's local session attributes so they flow into selectPlacements. -const MESSAGE_TYPE_PAGE_VIEW = 3; +const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView const PAGE_VIEWS_KEY = 'mpPageViews'; const MAX_PAGE_VIEWS = 25; -// Flat page-view array sent to selectPlacements: each StoredPageView with its -// eventAttributes exploded into PAGE_EVENT_ATTR_PREFIX-namespaced top-level keys. const PAGE_EVENTS_KEY = 'page_events'; const PAGE_EVENT_ATTR_PREFIX = 'attr_'; -// The page-view event attribute holding the document title; surfaced as the -// dedicated page_name field rather than an attr_-namespaced key. +// The page-view event attribute surfaced as the dedicated page_name field rather +// than an attr_-namespaced key. const PAGE_TITLE_ATTRIBUTE = 'title'; // Bound on how long selectPlacements will wait for an in-flight Workspace @@ -477,9 +471,8 @@ function isString(value: unknown): value is string { return typeof value === 'string'; } -// Isolates page-view URL handling. Returns the URL verbatim for now (per the -// design decision). Tightening to strip query/fragment later is a one-line -// change here and touches nothing else. See the security note in the spec. +// Isolates page-view URL handling. Returns the URL verbatim for now; tightening +// to strip query/fragment (which may carry PII) later is a one-line change here. function sanitizeUrl(href: string): string { return href; } @@ -951,6 +944,9 @@ class RoktKit implements KitInterface { flat.timestamp = pv.timestamp; flat.activeTimeOnSite = pv.activeTimeOnSite; + // Active time on this page = diff to the next view's activeTimeOnSite. + // Omitted for the still-open last view and for negative diffs (clock skew, + // reset, out-of-order) rather than surfacing a misleading value. const next = pageViews[i + 1]; if (next && typeof next.activeTimeOnSite === 'number' && typeof pv.activeTimeOnSite === 'number') { const diff = next.activeTimeOnSite - pv.activeTimeOnSite; From b8973cab9b9a142ef2d50145f6a75ce608720036 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 16:08:16 -0400 Subject: [PATCH 08/27] refactor: store page view event name as event_name at capture --- src/Rokt-Kit.ts | 6 +++--- test/src/tests.spec.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 4f53b77..3628d08 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -65,7 +65,7 @@ interface RoktExtensionEntry { // pageUrl and eventAttributes are stored verbatim and may contain PII; they are // persisted to browser storage and sent to Rokt on the next selectPlacements call. interface StoredPageView { - name: string; + event_name: string; pageUrl: string; sourceMessageId: string; timestamp: number; @@ -882,7 +882,7 @@ class RoktKit implements KitInterface { const pageViews: StoredPageView[] = Array.isArray(existing) ? (existing as StoredPageView[]) : []; pageViews.push({ - name: event.EventName, + event_name: event.EventName, pageUrl: sanitizeUrl(window.location.href), sourceMessageId: event.SourceMessageId, timestamp: event.Timestamp, @@ -937,7 +937,7 @@ class RoktKit implements KitInterface { flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; } } - flat.event_name = pv.name; + flat.event_name = pv.event_name; flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; flat.pageUrl = pv.pageUrl; flat.sourceMessageId = pv.sourceMessageId; diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 318195c..24e1ffb 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5697,7 +5697,7 @@ describe('Rokt Forwarder', () => { expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toEqual([ { - name: 'Home Page', + event_name: 'Home Page', pageUrl: window.location.href, sourceMessageId: 'source-message-id-1', timestamp: 1712345678000, @@ -5764,8 +5764,8 @@ describe('Rokt Forwarder', () => { const stored = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; expect(stored.length).toBe(25); // Oldest five (Page 0..4) evicted; newest retained. - expect(stored[0].name).toBe('Page 5'); - expect(stored[24].name).toBe('Page 29'); + expect(stored[0].event_name).toBe('Page 5'); + expect(stored[24].event_name).toBe('Page 29'); }); it('does not throw when setLocalSessionAttribute is unavailable', async () => { From 89353f876b73eec5087a856b4ca3d8c54c201323 Mon Sep 17 00:00:00 2001 From: Alex S <49695018+alexs-mparticle@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:09:38 -0400 Subject: [PATCH 09/27] Apply suggestion from @alexs-mparticle --- src/Rokt-Kit.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 3628d08..97f2971 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -1254,7 +1254,6 @@ class RoktKit implements KitInterface { if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { - console.warn('caputre Event', event); this.capturePageView(event); } From 2ab8c005b0f0e8e5048711c279a4e3a152721698 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 16:23:39 -0400 Subject: [PATCH 10/27] refactor: type page_events explicitly and keep legacy tests on PageView Add an explicit PageEvent return type for buildPageEvents (with timeOnPage optional) instead of Record[]. Revert the legacy #processEvent attribute-mapping tests back to MessageType.PageView and strip the captured mpPageViews list from their exact-shape assertions via a helper, since processing a PageView now legitimately captures a page-view record. --- src/Rokt-Kit.ts | 29 ++++++++++---- test/src/tests.spec.ts | 91 +++++++++++++++++++++++------------------- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 97f2971..211df57 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -73,6 +73,20 @@ interface StoredPageView { eventAttributes?: { [key: string]: string }; } +// A page view flattened for the outgoing page_events array. Event attributes are +// exploded onto attr_-namespaced keys (the index signature), and timeOnPage is +// derived at read time so it is omitted for the still-open last view. +interface PageEvent { + event_name: string; + page_name?: string; + pageUrl: string; + sourceMessageId: string; + timestamp: number; + activeTimeOnSite: number; + timeOnPage?: number; + [attr: string]: unknown; +} + interface RoktSelection { context?: { sessionId?: Promise; @@ -924,9 +938,15 @@ class RoktKit implements KitInterface { return mp().Rokt.getLocalSessionAttributes!(); } - private buildPageEvents(pageViews: StoredPageView[]): Record[] { + private buildPageEvents(pageViews: StoredPageView[]): PageEvent[] { return pageViews.map((pv, i) => { - const flat: Record = {}; + const flat: PageEvent = { + event_name: pv.event_name, + pageUrl: pv.pageUrl, + sourceMessageId: pv.sourceMessageId, + timestamp: pv.timestamp, + activeTimeOnSite: pv.activeTimeOnSite, + }; if (pv.eventAttributes) { for (const [key, value] of Object.entries(pv.eventAttributes)) { // `title` is surfaced as the dedicated page_name field below, so it is @@ -937,12 +957,7 @@ class RoktKit implements KitInterface { flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; } } - flat.event_name = pv.event_name; flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; - flat.pageUrl = pv.pageUrl; - flat.sourceMessageId = pv.sourceMessageId; - flat.timestamp = pv.timestamp; - flat.activeTimeOnSite = pv.activeTimeOnSite; // Active time on this page = diff to the next view's activeTimeOnSite. // Omitted for the still-open last view and for negative diffs (clock skew, diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 24e1ffb..90c031c 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -13,6 +13,15 @@ declare const mParticle: any; const sdkVersion = 'mParticle_wsdkv_1.2.3'; const kitVersion = 'kitv_' + packageVersion; +// Returns localSessionAttributes without the mpPageViews list. Processing a +// PageView legitimately captures a page-view record into the same store, so +// attribute-mapping tests strip it to assert only the mapped keys they set. +const mappedSessionAttributes = () => { + const { mpPageViews, ...attrs } = (window as any).mParticle._Store.localSessionAttributes; + void mpPageViews; + return attrs; +}; + const waitForCondition = async (conditionFn: () => boolean, timeout = 200, interval = 10) => { return new Promise((resolve, reject) => { const startTime = Date.now(); @@ -4901,7 +4910,7 @@ describe('Rokt Forwarder', () => { EventDataType: MessageType.PageEvent, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ 'foo-mapped-flag': true, }); }); @@ -4939,23 +4948,23 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/home', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/sale/items', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ saleSeeker: true, }); }); @@ -4987,13 +4996,13 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/anything', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ hasUrl: true, }); }); @@ -5025,13 +5034,13 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { someOtherAttribute: 'value', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); }); it('should support exists operator for placementEventAttributeMapping conditions', async () => { @@ -5062,13 +5071,13 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/anything', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ hasUrl: true, }); @@ -5076,13 +5085,13 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { someOtherAttribute: 'value', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); }); it('should evaluate equals for placementEventAttributeMapping conditions', async () => { @@ -5118,12 +5127,12 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { number_of_products: 2, }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ multipleproducts: true, }); @@ -5131,12 +5140,12 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { number_of_products: '2', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ multipleproducts: true, }); }); @@ -5174,12 +5183,12 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { number_of_products: 2, }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ containsNumber: true, }); }); @@ -5289,7 +5298,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { boolAttr: true, zeroAttr: 0, @@ -5297,7 +5306,7 @@ describe('Rokt Forwarder', () => { }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ lowerCaseMatches: true, zeroMatches: true, digitMatches: true, @@ -5337,22 +5346,22 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { otherAttr: 'value', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); }); it('should require ALL rules for the same mapped key to match (AND across rules)', async () => { @@ -5403,23 +5412,23 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/sale', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/sale/items', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ saleSeeker: true, }); }); @@ -5469,12 +5478,12 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/sale', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ saleSeeker: true, }); @@ -5482,12 +5491,12 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/sale/items', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ saleSeeker: true, saleSeeker1: true, }); @@ -5535,7 +5544,7 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Test', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { zeroProp: 0, falseProp: false, @@ -5543,7 +5552,7 @@ describe('Rokt Forwarder', () => { }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ zeroExists: true, falseExists: true, emptyStringExists: true, @@ -5583,13 +5592,13 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({}); + expect(mappedSessionAttributes()).toEqual({}); }); it('should support both placementEventMapping and placementEventAttributeMapping together', async () => { @@ -5630,13 +5639,13 @@ describe('Rokt Forwarder', () => { (window as any).mParticle.forwarder.process({ EventName: 'Browse', EventCategory: EventType.Unknown, - EventDataType: MessageType.PageEvent, + EventDataType: MessageType.PageView, EventAttributes: { URL: 'https://example.com/anything', }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ hasUrl: true, }); @@ -5650,7 +5659,7 @@ describe('Rokt Forwarder', () => { }, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ hasUrl: true, 'foo-mapped-flag': true, }); @@ -5662,7 +5671,7 @@ describe('Rokt Forwarder', () => { EventDataType: MessageType.PageEvent, }); - expect((window as any).mParticle._Store.localSessionAttributes).toEqual({ + expect(mappedSessionAttributes()).toEqual({ 'foo-mapped-flag': true, }); }); From f24d719abdbdb74a97b9e2f54bf46ccd4d8629cf Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 16:43:54 -0400 Subject: [PATCH 11/27] =?UTF-8?q?refactor:=20address=20PR=20review=20?= =?UTF-8?q?=E2=80=94=20no=20store=20mutation,=20explicit=20names,=20honest?= =?UTF-8?q?=20test=20asserts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Split mpPageViews off the session attributes via destructuring instead of delete, so the store returned by getLocalSessionAttributes is not mutated. - Rename buildPageEvents map params pv/i to pageView/index. - Drop the mappedSessionAttributes masking helper; the attribute-mapping tests now assert only their own mapped keys directly and leave mpPageViews to the page-view capture tests. --- src/Rokt-Kit.ts | 35 +++++++--------- test/src/tests.spec.ts | 95 +++++++++++++----------------------------- 2 files changed, 44 insertions(+), 86 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 211df57..45ad746 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -939,16 +939,16 @@ class RoktKit implements KitInterface { } private buildPageEvents(pageViews: StoredPageView[]): PageEvent[] { - return pageViews.map((pv, i) => { + return pageViews.map((pageView, index) => { const flat: PageEvent = { - event_name: pv.event_name, - pageUrl: pv.pageUrl, - sourceMessageId: pv.sourceMessageId, - timestamp: pv.timestamp, - activeTimeOnSite: pv.activeTimeOnSite, + event_name: pageView.event_name, + pageUrl: pageView.pageUrl, + sourceMessageId: pageView.sourceMessageId, + timestamp: pageView.timestamp, + activeTimeOnSite: pageView.activeTimeOnSite, }; - if (pv.eventAttributes) { - for (const [key, value] of Object.entries(pv.eventAttributes)) { + if (pageView.eventAttributes) { + for (const [key, value] of Object.entries(pageView.eventAttributes)) { // `title` is surfaced as the dedicated page_name field below, so it is // not also emitted as an attr_-namespaced key. if (key === PAGE_TITLE_ATTRIBUTE) { @@ -957,14 +957,14 @@ class RoktKit implements KitInterface { flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; } } - flat.page_name = pv.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; + flat.page_name = pageView.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; // Active time on this page = diff to the next view's activeTimeOnSite. // Omitted for the still-open last view and for negative diffs (clock skew, // reset, out-of-order) rather than surfacing a misleading value. - const next = pageViews[i + 1]; - if (next && typeof next.activeTimeOnSite === 'number' && typeof pv.activeTimeOnSite === 'number') { - const diff = next.activeTimeOnSite - pv.activeTimeOnSite; + const next = pageViews[index + 1]; + if (next && typeof next.activeTimeOnSite === 'number' && typeof pageView.activeTimeOnSite === 'number') { + const diff = next.activeTimeOnSite - pageView.activeTimeOnSite; if (diff >= 0) { flat.timeOnPage = diff; } @@ -1479,19 +1479,16 @@ class RoktKit implements KitInterface { const filteredUserIdentities = this.returnUserIdentities(filteredUser); - const localSessionAttributes = this.returnLocalSessionAttributes(); - - // Derive the flat page_events array from the stored page views, then drop the - // raw nested mpPageViews so Rokt receives only the flattened copy. - const rawPageViews = localSessionAttributes[PAGE_VIEWS_KEY]; + // Split the raw stored page views off the rest of the session attributes without + // mutating the returned store; Rokt receives only the flattened page_events copy. + const { [PAGE_VIEWS_KEY]: rawPageViews, ...sessionAttributes } = this.returnLocalSessionAttributes(); const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : []; - delete localSessionAttributes[PAGE_VIEWS_KEY]; const selectPlacementsAttributes: Record = { ...(filteredUserIdentities as Record), ...filteredAttributes, ...optimizelyAttributes, - ...localSessionAttributes, + ...sessionAttributes, ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: pageEvents } : {}), ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}), mpid, diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 90c031c..028d9df 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -13,15 +13,6 @@ declare const mParticle: any; const sdkVersion = 'mParticle_wsdkv_1.2.3'; const kitVersion = 'kitv_' + packageVersion; -// Returns localSessionAttributes without the mpPageViews list. Processing a -// PageView legitimately captures a page-view record into the same store, so -// attribute-mapping tests strip it to assert only the mapped keys they set. -const mappedSessionAttributes = () => { - const { mpPageViews, ...attrs } = (window as any).mParticle._Store.localSessionAttributes; - void mpPageViews; - return attrs; -}; - const waitForCondition = async (conditionFn: () => boolean, timeout = 200, interval = 10) => { return new Promise((resolve, reject) => { const startTime = Date.now(); @@ -4910,9 +4901,7 @@ describe('Rokt Forwarder', () => { EventDataType: MessageType.PageEvent, }); - expect(mappedSessionAttributes()).toEqual({ - 'foo-mapped-flag': true, - }); + expect((window as any).mParticle._Store.localSessionAttributes['foo-mapped-flag']).toBe(true); }); it('should set local session attribute only when placementEventAttributeMapping conditions match (URL contains)', async () => { @@ -4953,7 +4942,7 @@ describe('Rokt Forwarder', () => { URL: 'https://example.com/home', }, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker).toBeUndefined(); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -4964,9 +4953,7 @@ describe('Rokt Forwarder', () => { URL: 'https://example.com/sale/items', }, }); - expect(mappedSessionAttributes()).toEqual({ - saleSeeker: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker).toBe(true); }); it('should support event attribute mapping when conditions are not defined', async () => { @@ -5002,9 +4989,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({ - hasUrl: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.hasUrl).toBe(true); }); it('should not set local session attribute when mapped attribute key is missing from event and no conditions have been defined', async () => { @@ -5040,7 +5025,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.hasUrl).toBeUndefined(); }); it('should support exists operator for placementEventAttributeMapping conditions', async () => { @@ -5077,9 +5062,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({ - hasUrl: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.hasUrl).toBe(true); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5091,7 +5074,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.hasUrl).toBeUndefined(); }); it('should evaluate equals for placementEventAttributeMapping conditions', async () => { @@ -5132,9 +5115,7 @@ describe('Rokt Forwarder', () => { number_of_products: 2, }, }); - expect(mappedSessionAttributes()).toEqual({ - multipleproducts: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.multipleproducts).toBe(true); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5145,9 +5126,7 @@ describe('Rokt Forwarder', () => { number_of_products: '2', }, }); - expect(mappedSessionAttributes()).toEqual({ - multipleproducts: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.multipleproducts).toBe(true); }); it('should evaluate contains for placementEventAttributeMapping conditions', async () => { @@ -5188,9 +5167,7 @@ describe('Rokt Forwarder', () => { number_of_products: 2, }, }); - expect(mappedSessionAttributes()).toEqual({ - containsNumber: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.containsNumber).toBe(true); }); it('should correctly match attribute values for different type cases', async () => { @@ -5306,11 +5283,9 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({ - lowerCaseMatches: true, - zeroMatches: true, - digitMatches: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.lowerCaseMatches).toBe(true); + expect((window as any).mParticle._Store.localSessionAttributes.zeroMatches).toBe(true); + expect((window as any).mParticle._Store.localSessionAttributes.digitMatches).toBe(true); }); it('should not match when attribute key is missing or EventAttributes is absent', async () => { @@ -5352,7 +5327,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.shouldNotMatch).toBeUndefined(); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5361,7 +5336,7 @@ describe('Rokt Forwarder', () => { EventDataType: MessageType.PageView, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.shouldNotMatch).toBeUndefined(); }); it('should require ALL rules for the same mapped key to match (AND across rules)', async () => { @@ -5417,7 +5392,7 @@ describe('Rokt Forwarder', () => { URL: 'https://example.com/sale', }, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker).toBeUndefined(); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5428,9 +5403,7 @@ describe('Rokt Forwarder', () => { URL: 'https://example.com/sale/items', }, }); - expect(mappedSessionAttributes()).toEqual({ - saleSeeker: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker).toBe(true); }); it('should set multiple local session attributes for the same event attribute key', async () => { @@ -5483,9 +5456,7 @@ describe('Rokt Forwarder', () => { URL: 'https://example.com/sale', }, }); - expect(mappedSessionAttributes()).toEqual({ - saleSeeker: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker).toBe(true); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5496,10 +5467,8 @@ describe('Rokt Forwarder', () => { URL: 'https://example.com/sale/items', }, }); - expect(mappedSessionAttributes()).toEqual({ - saleSeeker: true, - saleSeeker1: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker).toBe(true); + expect((window as any).mParticle._Store.localSessionAttributes.saleSeeker1).toBe(true); }); it('should treat falsy attribute values as existing', async () => { @@ -5552,11 +5521,9 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({ - zeroExists: true, - falseExists: true, - emptyStringExists: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.zeroExists).toBe(true); + expect((window as any).mParticle._Store.localSessionAttributes.falseExists).toBe(true); + expect((window as any).mParticle._Store.localSessionAttributes.emptyStringExists).toBe(true); }); it('should not match when condition has an unrecognized operator', async () => { @@ -5598,7 +5565,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({}); + expect((window as any).mParticle._Store.localSessionAttributes.shouldNotMatch).toBeUndefined(); }); it('should support both placementEventMapping and placementEventAttributeMapping together', async () => { @@ -5645,9 +5612,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({ - hasUrl: true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.hasUrl).toBe(true); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5659,10 +5624,8 @@ describe('Rokt Forwarder', () => { }, }); - expect(mappedSessionAttributes()).toEqual({ - hasUrl: true, - 'foo-mapped-flag': true, - }); + expect((window as any).mParticle._Store.localSessionAttributes.hasUrl).toBe(true); + expect((window as any).mParticle._Store.localSessionAttributes['foo-mapped-flag']).toBe(true); (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ @@ -5671,9 +5634,7 @@ describe('Rokt Forwarder', () => { EventDataType: MessageType.PageEvent, }); - expect(mappedSessionAttributes()).toEqual({ - 'foo-mapped-flag': true, - }); + expect((window as any).mParticle._Store.localSessionAttributes['foo-mapped-flag']).toBe(true); }); describe('page view capture', () => { From d9b1654319defe6bd9d03275be59fa48759fbaa8 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 31 Jul 2026 16:45:35 -0400 Subject: [PATCH 12/27] refactor: drop unnecessary comments in page-view code --- src/Rokt-Kit.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 45ad746..a60eb35 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -73,9 +73,6 @@ interface StoredPageView { eventAttributes?: { [key: string]: string }; } -// A page view flattened for the outgoing page_events array. Event attributes are -// exploded onto attr_-namespaced keys (the index signature), and timeOnPage is -// derived at read time so it is omitted for the still-open last view. interface PageEvent { event_name: string; page_name?: string; @@ -949,8 +946,6 @@ class RoktKit implements KitInterface { }; if (pageView.eventAttributes) { for (const [key, value] of Object.entries(pageView.eventAttributes)) { - // `title` is surfaced as the dedicated page_name field below, so it is - // not also emitted as an attr_-namespaced key. if (key === PAGE_TITLE_ATTRIBUTE) { continue; } @@ -959,9 +954,6 @@ class RoktKit implements KitInterface { } flat.page_name = pageView.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; - // Active time on this page = diff to the next view's activeTimeOnSite. - // Omitted for the still-open last view and for negative diffs (clock skew, - // reset, out-of-order) rather than surfacing a misleading value. const next = pageViews[index + 1]; if (next && typeof next.activeTimeOnSite === 'number' && typeof pageView.activeTimeOnSite === 'number') { const diff = next.activeTimeOnSite - pageView.activeTimeOnSite; @@ -1479,8 +1471,6 @@ class RoktKit implements KitInterface { const filteredUserIdentities = this.returnUserIdentities(filteredUser); - // Split the raw stored page views off the rest of the session attributes without - // mutating the returned store; Rokt receives only the flattened page_events copy. const { [PAGE_VIEWS_KEY]: rawPageViews, ...sessionAttributes } = this.returnLocalSessionAttributes(); const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : []; From 2ff8907b08097632add48614e3f2bab079801da2 Mon Sep 17 00:00:00 2001 From: Alex S <49695018+alexs-mparticle@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:46:56 -0400 Subject: [PATCH 13/27] Apply suggestion from @alexs-mparticle --- src/Rokt-Kit.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index a60eb35..d98716b 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -61,9 +61,6 @@ interface RoktExtensionEntry { value: string; } -// A captured page view, persisted (newest last) under PAGE_VIEWS_KEY. -// pageUrl and eventAttributes are stored verbatim and may contain PII; they are -// persisted to browser storage and sent to Rokt on the next selectPlacements call. interface StoredPageView { event_name: string; pageUrl: string; From efc2ce2b8149ad44c431fffe249ac7e1393a10d8 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 10:03:32 -0400 Subject: [PATCH 14/27] =?UTF-8?q?refactor:=20address=20PR=20review=20?= =?UTF-8?q?=E2=80=94=20persist=20page=20views=20as=20JSON,=20rename=20LS?= =?UTF-8?q?=20key,=20report=20capture=20failures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Store page views as a JSON string via setLocalSessionAttribute to respect the primitive-only AttributeValue contract; parse on read (rmi22186) - Rename PAGE_VIEWS_KEY -> LS_PAGE_VIEWS_KEY to distinguish the persistence key from the PAGE_EVENTS_KEY wire shape (rmi22186) - Report page-view capture failures via errorReportingService instead of console.error for observability (rmi22186) - Fold page_name assignment into the eventAttributes guard to drop the optional chain (rmi22186) --- src/Rokt-Kit.ts | 49 +++++++++++++++++++++++++++++++----------- test/src/tests.spec.ts | 4 ++-- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index d98716b..70e0ca6 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -265,7 +265,10 @@ const ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element'; const USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace'; const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView -const PAGE_VIEWS_KEY = 'mpPageViews'; +// Local-session-attribute key under which captured page views are persisted (as a +// JSON string). Distinct from PAGE_EVENTS_KEY, which is the flattened wire shape +// sent to Rokt on selectPlacements. +const LS_PAGE_VIEWS_KEY = 'mpPageViews'; const MAX_PAGE_VIEWS = 25; const PAGE_EVENTS_KEY = 'page_events'; const PAGE_EVENT_ATTR_PREFIX = 'attr_'; @@ -880,14 +883,15 @@ class RoktKit implements KitInterface { } } - // Appends a page-view record to the persisted list under PAGE_VIEWS_KEY, - // capping at MAX_PAGE_VIEWS (oldest evicted). Wrapped so a malformed event - // can never throw out of the forwarder. Callers must confirm the event is a - // page view and that setLocalSessionAttribute is available. + // Appends a page-view record to the persisted list under LS_PAGE_VIEWS_KEY, + // capping at MAX_PAGE_VIEWS (oldest evicted). The list is stored as a JSON + // string because setLocalSessionAttribute only contracts to accept primitive + // AttributeValues. Wrapped so a malformed event can never throw out of the + // forwarder. Callers must confirm the event is a page view and that + // setLocalSessionAttribute is available. private capturePageView(event: SDKEvent): void { try { - const existing = mp().Rokt.getLocalSessionAttributes?.()?.[PAGE_VIEWS_KEY]; - const pageViews: StoredPageView[] = Array.isArray(existing) ? (existing as StoredPageView[]) : []; + const pageViews = this.readStoredPageViews(); pageViews.push({ event_name: event.EventName, @@ -902,9 +906,30 @@ class RoktKit implements KitInterface { pageViews.shift(); } - mp().Rokt.setLocalSessionAttribute?.(PAGE_VIEWS_KEY, pageViews); + mp().Rokt.setLocalSessionAttribute?.(LS_PAGE_VIEWS_KEY, JSON.stringify(pageViews)); } catch (err) { - console.error('Rokt Kit: Failed to capture page view', err); + this.errorReportingService?.report({ + message: 'Rokt Kit: Failed to capture page view', + code: 'PAGE_VIEW_CAPTURE_FAILED', + severity: WSDKErrorSeverity.WARNING, + stackTrace: err instanceof Error ? err.stack : undefined, + }); + } + } + + // Reads and parses the persisted page-view list from local session attributes. + // Returns an empty array when nothing is stored or the value cannot be parsed + // (e.g. a legacy raw-array value written before JSON persistence). + private readStoredPageViews(): StoredPageView[] { + const stored = mp().Rokt.getLocalSessionAttributes?.()?.[LS_PAGE_VIEWS_KEY]; + if (typeof stored !== 'string') { + return []; + } + try { + const parsed = JSON.parse(stored); + return Array.isArray(parsed) ? (parsed as StoredPageView[]) : []; + } catch { + return []; } } @@ -948,8 +973,8 @@ class RoktKit implements KitInterface { } flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; } + flat.page_name = pageView.eventAttributes[PAGE_TITLE_ATTRIBUTE]; } - flat.page_name = pageView.eventAttributes?.[PAGE_TITLE_ATTRIBUTE]; const next = pageViews[index + 1]; if (next && typeof next.activeTimeOnSite === 'number' && typeof pageView.activeTimeOnSite === 'number') { @@ -1468,8 +1493,8 @@ class RoktKit implements KitInterface { const filteredUserIdentities = this.returnUserIdentities(filteredUser); - const { [PAGE_VIEWS_KEY]: rawPageViews, ...sessionAttributes } = this.returnLocalSessionAttributes(); - const pageEvents = Array.isArray(rawPageViews) ? this.buildPageEvents(rawPageViews as StoredPageView[]) : []; + const { [LS_PAGE_VIEWS_KEY]: _rawPageViews, ...sessionAttributes } = this.returnLocalSessionAttributes(); + const pageEvents = this.buildPageEvents(this.readStoredPageViews()); const selectPlacementsAttributes: Record = { ...(filteredUserIdentities as Record), diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 028d9df..11b06b1 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5665,7 +5665,7 @@ describe('Rokt Forwarder', () => { }, }); - expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toEqual([ + expect(JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews)).toEqual([ { event_name: 'Home Page', pageUrl: window.location.href, @@ -5731,7 +5731,7 @@ describe('Rokt Forwarder', () => { }); } - const stored = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; + const stored = JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews); expect(stored.length).toBe(25); // Oldest five (Page 0..4) evicted; newest retained. expect(stored[0].event_name).toBe('Page 5'); From db6c1bc90ce2bb53a00609221eb7c207a4ee369f Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 11:07:21 -0400 Subject: [PATCH 15/27] fix: strip query params from page URLs and address review nits - sanitizeUrl now strips the query string (commonly carries PII) before a page-view URL is persisted and sent to Rokt - add rationale comment for MAX_PAGE_VIEWS cap - rename buildPageEvents var flat -> pageEvent - drop redundant typeof guards on activeTimeOnSite (already typed number; diff >= 0 check handles NaN) - add test covering query-param stripping --- src/Rokt-Kit.ts | 28 +++++++++++++++++--------- test/src/tests.spec.ts | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 70e0ca6..64690a9 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -269,6 +269,9 @@ const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView // JSON string). Distinct from PAGE_EVENTS_KEY, which is the flattened wire shape // sent to Rokt on selectPlacements. const LS_PAGE_VIEWS_KEY = 'mpPageViews'; +// Cap on the retained page-view history. Each entry stores a full URL (which can +// be long) plus event attributes, so this strikes a balance between a useful +// browsing history and not overloading local session storage. const MAX_PAGE_VIEWS = 25; const PAGE_EVENTS_KEY = 'page_events'; const PAGE_EVENT_ATTR_PREFIX = 'attr_'; @@ -482,10 +485,17 @@ function isString(value: unknown): value is string { return typeof value === 'string'; } -// Isolates page-view URL handling. Returns the URL verbatim for now; tightening -// to strip query/fragment (which may carry PII) later is a one-line change here. +// Strips the query string from a page-view URL before it is persisted and sent +// to Rokt, since query params commonly carry PII (emails, tokens, order refs). +// Returns the input unchanged if it can't be parsed as a URL. function sanitizeUrl(href: string): string { - return href; + try { + const url = new URL(href); + url.search = ''; + return url.toString(); + } catch { + return href; + } } function generateIntegrationName(customIntegrationName?: string): string { @@ -959,7 +969,7 @@ class RoktKit implements KitInterface { private buildPageEvents(pageViews: StoredPageView[]): PageEvent[] { return pageViews.map((pageView, index) => { - const flat: PageEvent = { + const pageEvent: PageEvent = { event_name: pageView.event_name, pageUrl: pageView.pageUrl, sourceMessageId: pageView.sourceMessageId, @@ -971,19 +981,19 @@ class RoktKit implements KitInterface { if (key === PAGE_TITLE_ATTRIBUTE) { continue; } - flat[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; + pageEvent[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; } - flat.page_name = pageView.eventAttributes[PAGE_TITLE_ATTRIBUTE]; + pageEvent.page_name = pageView.eventAttributes[PAGE_TITLE_ATTRIBUTE]; } const next = pageViews[index + 1]; - if (next && typeof next.activeTimeOnSite === 'number' && typeof pageView.activeTimeOnSite === 'number') { + if (next) { const diff = next.activeTimeOnSite - pageView.activeTimeOnSite; if (diff >= 0) { - flat.timeOnPage = diff; + pageEvent.timeOnPage = diff; } } - return flat; + return pageEvent; }); } diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 11b06b1..17e07e8 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -6059,6 +6059,51 @@ describe('Rokt Forwarder', () => { expect(pageEvents[0].timeOnPage).toBeUndefined(); expect(pageEvents[1].timeOnPage).toBeUndefined(); }); + + it('strips query params from the captured pageUrl', async () => { + const originalLocation = window.location; + // Query params commonly carry PII (emails, tokens); they must not be captured. + Object.defineProperty(window, 'location', { + value: new URL('https://www.example.com/checkout?email=user@test.com&token=secret#section'), + writable: true, + configurable: true, + }); + + try { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Checkout', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-14', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + expect(forwardedAttributes.page_events[0].pageUrl).toBe('https://www.example.com/checkout#section'); + } finally { + Object.defineProperty(window, 'location', { + value: originalLocation, + writable: true, + configurable: true, + }); + } + }); }); }); From 7b5c33aa3f692063465e8ee541b2f8f1b5364ef1 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 11:12:57 -0400 Subject: [PATCH 16/27] refactor: drop event_name and event attributes from page events This flow targets auto page views, which carry no useful EventName or EventAttributes, so remove event_name, page_name, and the attr_-namespaced event-attribute explosion from both the stored and wire shapes. Simplifies StoredPageView/PageEvent to url, sourceMessageId, timestamp, activeTimeOnSite (+ derived timeOnPage). --- src/Rokt-Kit.ts | 25 +--------- test/src/tests.spec.ts | 102 ++--------------------------------------- 2 files changed, 5 insertions(+), 122 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 64690a9..5fe1ae6 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -62,23 +62,18 @@ interface RoktExtensionEntry { } interface StoredPageView { - event_name: string; pageUrl: string; sourceMessageId: string; timestamp: number; activeTimeOnSite: number; - eventAttributes?: { [key: string]: string }; } interface PageEvent { - event_name: string; - page_name?: string; pageUrl: string; sourceMessageId: string; timestamp: number; activeTimeOnSite: number; timeOnPage?: number; - [attr: string]: unknown; } interface RoktSelection { @@ -270,14 +265,10 @@ const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView // sent to Rokt on selectPlacements. const LS_PAGE_VIEWS_KEY = 'mpPageViews'; // Cap on the retained page-view history. Each entry stores a full URL (which can -// be long) plus event attributes, so this strikes a balance between a useful -// browsing history and not overloading local session storage. +// be long), so this strikes a balance between a useful browsing history and not +// overloading local session storage. const MAX_PAGE_VIEWS = 25; const PAGE_EVENTS_KEY = 'page_events'; -const PAGE_EVENT_ATTR_PREFIX = 'attr_'; -// The page-view event attribute surfaced as the dedicated page_name field rather -// than an attr_-namespaced key. -const PAGE_TITLE_ATTRIBUTE = 'title'; // Bound on how long selectPlacements will wait for an in-flight Workspace // IDSync search before proceeding without the userIdentifiedInWorkspace flag. @@ -904,12 +895,10 @@ class RoktKit implements KitInterface { const pageViews = this.readStoredPageViews(); pageViews.push({ - event_name: event.EventName, pageUrl: sanitizeUrl(window.location.href), sourceMessageId: event.SourceMessageId, timestamp: event.Timestamp, activeTimeOnSite: event.ActiveTimeOnSite, - eventAttributes: event.EventAttributes, }); while (pageViews.length > MAX_PAGE_VIEWS) { @@ -970,21 +959,11 @@ class RoktKit implements KitInterface { private buildPageEvents(pageViews: StoredPageView[]): PageEvent[] { return pageViews.map((pageView, index) => { const pageEvent: PageEvent = { - event_name: pageView.event_name, pageUrl: pageView.pageUrl, sourceMessageId: pageView.sourceMessageId, timestamp: pageView.timestamp, activeTimeOnSite: pageView.activeTimeOnSite, }; - if (pageView.eventAttributes) { - for (const [key, value] of Object.entries(pageView.eventAttributes)) { - if (key === PAGE_TITLE_ATTRIBUTE) { - continue; - } - pageEvent[`${PAGE_EVENT_ATTR_PREFIX}${key}`] = value; - } - pageEvent.page_name = pageView.eventAttributes[PAGE_TITLE_ATTRIBUTE]; - } const next = pageViews[index + 1]; if (next) { diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 17e07e8..b84d0f5 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5667,15 +5667,10 @@ describe('Rokt Forwarder', () => { expect(JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews)).toEqual([ { - event_name: 'Home Page', pageUrl: window.location.href, sourceMessageId: 'source-message-id-1', timestamp: 1712345678000, activeTimeOnSite: 4200, - eventAttributes: { - hostname: 'example.com', - title: 'Home', - }, }, ]); }); @@ -5733,9 +5728,9 @@ describe('Rokt Forwarder', () => { const stored = JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews); expect(stored.length).toBe(25); - // Oldest five (Page 0..4) evicted; newest retained. - expect(stored[0].event_name).toBe('Page 5'); - expect(stored[24].event_name).toBe('Page 29'); + // Oldest five (source-message-id-0..4) evicted; newest retained. + expect(stored[0].sourceMessageId).toBe('source-message-id-5'); + expect(stored[24].sourceMessageId).toBe('source-message-id-29'); }); it('does not throw when setLocalSessionAttribute is unavailable', async () => { @@ -5798,7 +5793,6 @@ describe('Rokt Forwarder', () => { expect(forwardedAttributes.mpPageViews).toBeUndefined(); expect(forwardedAttributes.page_events).toEqual([ { - event_name: 'Home Page', pageUrl: window.location.href, sourceMessageId: 'source-message-id-4', timestamp: 1712345678000, @@ -5807,94 +5801,6 @@ describe('Rokt Forwarder', () => { ]); }); - it('explodes eventAttributes into attr_-namespaced keys in page_events', async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle._Store.localSessionAttributes = {}; - (window as any).mParticle.forwarder.process({ - EventName: 'Product Page', - EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, - SourceMessageId: 'source-message-id-5', - Timestamp: 1712345678000, - ActiveTimeOnSite: 4200, - EventAttributes: { - category: 'shoes', - promo: 'x', - title: 'Product Page Title', - }, - }); - - await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); - - const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - // `title` is surfaced as page_name and must NOT also appear as attr_title. - expect(forwardedAttributes.page_events).toEqual([ - { - attr_category: 'shoes', - attr_promo: 'x', - event_name: 'Product Page', - page_name: 'Product Page Title', - pageUrl: window.location.href, - sourceMessageId: 'source-message-id-5', - timestamp: 1712345678000, - activeTimeOnSite: 4200, - }, - ]); - }); - - it('namespaces a colliding eventAttribute key so it cannot clobber a base field', async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle._Store.localSessionAttributes = {}; - (window as any).mParticle.forwarder.process({ - EventName: 'Real Name', - EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, - SourceMessageId: 'source-message-id-6', - Timestamp: 1712345678000, - ActiveTimeOnSite: 4200, - EventAttributes: { - event_name: 'attribute-event-name', - }, - }); - - await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); - - const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - expect(forwardedAttributes.page_events).toEqual([ - { - attr_event_name: 'attribute-event-name', - event_name: 'Real Name', - page_name: undefined, - pageUrl: window.location.href, - sourceMessageId: 'source-message-id-6', - timestamp: 1712345678000, - activeTimeOnSite: 4200, - }, - ]); - }); - it('does not add a page_events attribute when no page views are stored', async () => { await (window as any).mParticle.forwarder.init( { @@ -5955,7 +5861,6 @@ describe('Rokt Forwarder', () => { // 4200 - 1000 = 3200. The last (still-open) page has no timeOnPage. expect(forwardedAttributes.page_events).toEqual([ { - event_name: 'Home Page', pageUrl: window.location.href, sourceMessageId: 'source-message-id-7', timestamp: 1712345678000, @@ -5963,7 +5868,6 @@ describe('Rokt Forwarder', () => { timeOnPage: 3200, }, { - event_name: 'Product Page', pageUrl: window.location.href, sourceMessageId: 'source-message-id-8', timestamp: 1712345679000, From 9c7b27fc5b97a50a1959f956835f3034fab78b1b Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 11:19:08 -0400 Subject: [PATCH 17/27] fix: stringify page_events before sending to selectPlacements page_events is an array of objects, but the Rokt attribute contract only permits primitives and arrays of primitives. Passing raw objects to the launcher is undefined behaviour, so JSON-stringify it at the call site and JSON.parse in the tests that assert on it. --- src/Rokt-Kit.ts | 2 +- test/src/tests.spec.ts | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 5fe1ae6..9241066 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -1490,7 +1490,7 @@ class RoktKit implements KitInterface { ...filteredAttributes, ...optimizelyAttributes, ...sessionAttributes, - ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: pageEvents } : {}), + ...(pageEvents.length ? { [PAGE_EVENTS_KEY]: JSON.stringify(pageEvents) } : {}), ...(this.userIdentifiedInWorkspace ? { [USER_IDENTIFIED_IN_WORKSPACE_KEY]: true } : {}), mpid, }; diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index b84d0f5..6dfb281 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5789,9 +5789,10 @@ describe('Rokt Forwarder', () => { await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - // The raw nested store must not ride along; only the flat page_events array is sent. + // The raw nested store must not ride along; only the flat page_events array is sent, + // JSON-stringified to satisfy the primitives-only Rokt attribute contract. expect(forwardedAttributes.mpPageViews).toBeUndefined(); - expect(forwardedAttributes.page_events).toEqual([ + expect(JSON.parse(forwardedAttributes.page_events)).toEqual([ { pageUrl: window.location.href, sourceMessageId: 'source-message-id-4', @@ -5859,7 +5860,7 @@ describe('Rokt Forwarder', () => { const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; // First page's time-on-page is how long it was viewed before the next page: // 4200 - 1000 = 3200. The last (still-open) page has no timeOnPage. - expect(forwardedAttributes.page_events).toEqual([ + expect(JSON.parse(forwardedAttributes.page_events)).toEqual([ { pageUrl: window.location.href, sourceMessageId: 'source-message-id-7', @@ -5918,7 +5919,7 @@ describe('Rokt Forwarder', () => { await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - const pageEvents = forwardedAttributes.page_events; + const pageEvents = JSON.parse(forwardedAttributes.page_events); expect(pageEvents[0].timeOnPage).toBe(1500); // 2500 - 1000 expect(pageEvents[1].timeOnPage).toBe(6500); // 9000 - 2500 expect(pageEvents[2].timeOnPage).toBeUndefined(); // still open @@ -5958,7 +5959,7 @@ describe('Rokt Forwarder', () => { await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - const pageEvents = forwardedAttributes.page_events; + const pageEvents = JSON.parse(forwardedAttributes.page_events); // 1000 - 5000 = -4000 → omitted rather than emitting a misleading value. expect(pageEvents[0].timeOnPage).toBeUndefined(); expect(pageEvents[1].timeOnPage).toBeUndefined(); @@ -5999,7 +6000,9 @@ describe('Rokt Forwarder', () => { await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; - expect(forwardedAttributes.page_events[0].pageUrl).toBe('https://www.example.com/checkout#section'); + expect(JSON.parse(forwardedAttributes.page_events)[0].pageUrl).toBe( + 'https://www.example.com/checkout#section', + ); } finally { Object.defineProperty(window, 'location', { value: originalLocation, From f12e8a82511422c544d5dcbf853cc7b3f4572968 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 11:53:13 -0400 Subject: [PATCH 18/27] refactor: capture page URL early and collapse page-view schema - Resolve pageUrl as the first step inside capturePageView's try and reuse it in the failure log, so an undefined URL signals failure at/before URL construction and the log is never lost. - Collapse StoredPageView and PageEvent into a single PageEvent interface; timeOnPage is optional and derived at transmission. --- src/Rokt-Kit.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 9241066..57cba8b 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -61,18 +61,13 @@ interface RoktExtensionEntry { value: string; } -interface StoredPageView { - pageUrl: string; - sourceMessageId: string; - timestamp: number; - activeTimeOnSite: number; -} - interface PageEvent { pageUrl: string; sourceMessageId: string; timestamp: number; activeTimeOnSite: number; + // Derived at transmission (see buildPageEvents), not at capture — it depends + // on the next page view's activeTimeOnSite, so it is absent on stored records. timeOnPage?: number; } @@ -891,11 +886,15 @@ class RoktKit implements KitInterface { // forwarder. Callers must confirm the event is a page view and that // setLocalSessionAttribute is available. private capturePageView(event: SDKEvent): void { + let pageUrl: string | undefined; + try { + pageUrl = sanitizeUrl(window.location.href); + const pageViews = this.readStoredPageViews(); pageViews.push({ - pageUrl: sanitizeUrl(window.location.href), + pageUrl, sourceMessageId: event.SourceMessageId, timestamp: event.Timestamp, activeTimeOnSite: event.ActiveTimeOnSite, @@ -908,7 +907,7 @@ class RoktKit implements KitInterface { mp().Rokt.setLocalSessionAttribute?.(LS_PAGE_VIEWS_KEY, JSON.stringify(pageViews)); } catch (err) { this.errorReportingService?.report({ - message: 'Rokt Kit: Failed to capture page view', + message: `Rokt Kit: Failed to capture page view for ${pageUrl}`, code: 'PAGE_VIEW_CAPTURE_FAILED', severity: WSDKErrorSeverity.WARNING, stackTrace: err instanceof Error ? err.stack : undefined, @@ -919,14 +918,14 @@ class RoktKit implements KitInterface { // Reads and parses the persisted page-view list from local session attributes. // Returns an empty array when nothing is stored or the value cannot be parsed // (e.g. a legacy raw-array value written before JSON persistence). - private readStoredPageViews(): StoredPageView[] { + private readStoredPageViews(): PageEvent[] { const stored = mp().Rokt.getLocalSessionAttributes?.()?.[LS_PAGE_VIEWS_KEY]; if (typeof stored !== 'string') { return []; } try { const parsed = JSON.parse(stored); - return Array.isArray(parsed) ? (parsed as StoredPageView[]) : []; + return Array.isArray(parsed) ? (parsed as PageEvent[]) : []; } catch { return []; } @@ -956,7 +955,7 @@ class RoktKit implements KitInterface { return mp().Rokt.getLocalSessionAttributes!(); } - private buildPageEvents(pageViews: StoredPageView[]): PageEvent[] { + private buildPageEvents(pageViews: PageEvent[]): PageEvent[] { return pageViews.map((pageView, index) => { const pageEvent: PageEvent = { pageUrl: pageView.pageUrl, From 329efb38af9e7ef916335df048b442c961f2fe4e Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 13:39:51 -0400 Subject: [PATCH 19/27] fix: Remove isKitReady guard from process events function --- src/Rokt-Kit.ts | 4 ---- test/src/tests.spec.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 57cba8b..cf8681c 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -1265,10 +1265,6 @@ class RoktKit implements KitInterface { } public process(event: SDKEvent): string { - if (!this.isKitReady()) { - return 'Kit not ready for forwarder: ' + name; - } - if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { this.capturePageView(event); diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 6dfb281..fe1c9fb 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -6011,6 +6011,35 @@ describe('Rokt Forwarder', () => { }); } }); + + it('captures a page view fired before the launcher finishes loading (kit not yet ready)', () => { + // Reproduces the race where the core SDK routes the initial page-load + // pageview to process() after it marks the forwarder active but before + // the async launcher round-trip flips isInitialized/launcher. In that + // window isKitReady() is false, yet setLocalSessionAttribute is already + // usable — the pageview must still be persisted, not dropped. + (window as any).mParticle.forwarder.isInitialized = false; + (window as any).mParticle.forwarder.launcher = null; + (window as any).mParticle._Store.localSessionAttributes = {}; + + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-race', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + + expect(JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews)).toEqual([ + { + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-race', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]); + }); }); }); From 4c278f55aad5058de932a79d68101545bf0ab094 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 14:59:55 -0400 Subject: [PATCH 20/27] feat: cap page-view history by byte budget keyed to storage backend Replace the arbitrary MAX_PAGE_VIEWS=25 count cap with a byte-budget cap resolved from mParticle's storage backend: 128 KB in localStorage mode, 1/3 of maxCookieSize in cookie mode, with a safe cookie-default fallback when SDK internals are unavailable. Evicts oldest page views first until the serialized blob fits the budget, always retaining the current view. --- src/Rokt-Kit.ts | 48 ++++++++++++--- test/src/tests.spec.ts | 137 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 172 insertions(+), 13 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index cf8681c..93c1a4e 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -135,8 +135,18 @@ interface RoktManager { setLocalSessionAttribute?(key: string, value: unknown): void; } +interface SDKConfigInternal { + useCookieStorage?: boolean; + maxCookieSize?: number; +} + +interface StoreInternal { + SDKConfig?: SDKConfigInternal; +} + interface MParticleInstance { setIntegrationAttribute(moduleId: number, attrs: Record): void; + _Store?: StoreInternal; } interface OptimizelyState { @@ -259,10 +269,12 @@ const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView // JSON string). Distinct from PAGE_EVENTS_KEY, which is the flattened wire shape // sent to Rokt on selectPlacements. const LS_PAGE_VIEWS_KEY = 'mpPageViews'; -// Cap on the retained page-view history. Each entry stores a full URL (which can -// be long), so this strikes a balance between a useful browsing history and not -// overloading local session storage. -const MAX_PAGE_VIEWS = 25; +// Byte budget for the persisted page-view history, keyed to the storage backend +// (see resolvePageViewsBudget). localStorage has no SDK-enforced size cap, so we +// self-impose 128 KB. Code constant, not a kit setting — change it here. +const PAGE_VIEWS_LS_BUDGET_BYTES = 128 * 1024; +// Cookie mode: use 1/3 of the SDK's maxCookieSize so the blob shares the cookie. +const PAGE_VIEWS_COOKIE_BUDGET_DIVISOR = 3; const PAGE_EVENTS_KEY = 'page_events'; // Bound on how long selectPlacements will wait for an in-flight Workspace @@ -308,6 +320,21 @@ function mp(): MParticleExtended { // Module-level utility functions // ============================================================ +// Resolves the byte budget for the persisted page-view history from the SDK's +// storage backend. Reads private SDK internals (_Store.SDKConfig), so every +// access is optional-chained; any missing internal falls back to the cookie +// default (maxCookieSize 3000 / 3), the small/safe choice. +function resolvePageViewsBudget(): number { + const sdkConfig = mp().getInstance()?._Store?.SDKConfig; + if (sdkConfig?.useCookieStorage === true) { + return Math.floor((sdkConfig.maxCookieSize ?? 3000) / PAGE_VIEWS_COOKIE_BUDGET_DIVISOR); + } + if (sdkConfig) { + return PAGE_VIEWS_LS_BUDGET_BYTES; + } + return Math.floor(3000 / PAGE_VIEWS_COOKIE_BUDGET_DIVISOR); +} + function generateLauncherScript(domain: string | undefined, extensions: string[]): string { const launcherPath = '/wsdk/integrations/launcher.js'; const baseUrl = [generateBaseUrl(domain), launcherPath].join(''); @@ -880,7 +907,7 @@ class RoktKit implements KitInterface { } // Appends a page-view record to the persisted list under LS_PAGE_VIEWS_KEY, - // capping at MAX_PAGE_VIEWS (oldest evicted). The list is stored as a JSON + // capping at a byte budget (oldest evicted). The list is stored as a JSON // string because setLocalSessionAttribute only contracts to accept primitive // AttributeValues. Wrapped so a malformed event can never throw out of the // forwarder. Callers must confirm the event is a page view and that @@ -900,11 +927,18 @@ class RoktKit implements KitInterface { activeTimeOnSite: event.ActiveTimeOnSite, }); - while (pageViews.length > MAX_PAGE_VIEWS) { + // Evict oldest until the serialized blob fits the budget. `.length` is a + // conservative char-count proxy for UTF-8 bytes. `length > 1` always + // retains the current page-view and prevents an empty-array infinite loop. + // We reuse `serialized` for the write below, re-serializing only on evict. + const budget = resolvePageViewsBudget(); + let serialized = JSON.stringify(pageViews); + while (pageViews.length > 1 && serialized.length > budget) { pageViews.shift(); + serialized = JSON.stringify(pageViews); } - mp().Rokt.setLocalSessionAttribute?.(LS_PAGE_VIEWS_KEY, JSON.stringify(pageViews)); + mp().Rokt.setLocalSessionAttribute?.(LS_PAGE_VIEWS_KEY, serialized); } catch (err) { this.errorReportingService?.report({ message: `Rokt Kit: Failed to capture page view for ${pageUrl}`, diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index fe1c9fb..69d65c2 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5638,6 +5638,11 @@ describe('Rokt Forwarder', () => { }); describe('page view capture', () => { + const defaultGetInstance = (window as any).mParticle.getInstance; + afterEach(() => { + (window as any).mParticle.getInstance = defaultGetInstance; + }); + it('appends a page view record with the expected fields when the event is a PageView', async () => { await (window as any).mParticle.forwarder.init( { @@ -5701,7 +5706,7 @@ describe('Rokt Forwarder', () => { expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toBeUndefined(); }); - it('caps the stored list at 25 entries and evicts the oldest', async () => { + it('evicts oldest to keep the serialized blob within the cookie-mode byte budget', async () => { await (window as any).mParticle.forwarder.init( { accountId: '123456', @@ -5714,8 +5719,14 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + // Cookie mode with maxCookieSize 3000 → budget 3000 / 3 = 1000 bytes. + (window as any).mParticle.getInstance = () => ({ + setIntegrationAttribute: () => {}, + _Store: { SDKConfig: { useCookieStorage: true, maxCookieSize: 3000 } }, + }); + (window as any).mParticle._Store.localSessionAttributes = {}; - for (let i = 0; i < 30; i++) { + for (let i = 0; i < 50; i++) { (window as any).mParticle.forwarder.process({ EventName: 'Page ' + i, EventCategory: EventType.Unknown, @@ -5726,11 +5737,125 @@ describe('Rokt Forwarder', () => { }); } + const raw = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; + const stored = JSON.parse(raw); + expect(raw.length).toBeLessThanOrEqual(1000); + expect(stored.length).toBeGreaterThan(1); + // Oldest were evicted; the newest page view is always retained. + expect(stored[stored.length - 1].sourceMessageId).toBe('source-message-id-49'); + expect(stored[0].sourceMessageId).not.toBe('source-message-id-0'); + }); + + it('retains many records in localStorage mode under the 128 KB budget', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + // localStorage mode → 128 KB budget, room for hundreds of records. + (window as any).mParticle.getInstance = () => ({ + setIntegrationAttribute: () => {}, + _Store: { SDKConfig: { useCookieStorage: false } }, + }); + + (window as any).mParticle._Store.localSessionAttributes = {}; + for (let i = 0; i < 200; i++) { + (window as any).mParticle.forwarder.process({ + EventName: 'Page ' + i, + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-' + i, + Timestamp: 1712345678000 + i, + ActiveTimeOnSite: i, + }); + } + + const raw = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; + const stored = JSON.parse(raw); + expect(raw.length).toBeLessThanOrEqual(128 * 1024); + // 200 small records are well within 128 KB — none evicted. + expect(stored.length).toBe(200); + expect(stored[0].sourceMessageId).toBe('source-message-id-0'); + expect(stored[199].sourceMessageId).toBe('source-message-id-199'); + }); + + it('stores a single record that on its own exceeds the byte budget (length > 1 guard)', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + // Cookie mode, tiny budget so any single record blows it. + (window as any).mParticle.getInstance = () => ({ + setIntegrationAttribute: () => {}, + _Store: { SDKConfig: { useCookieStorage: true, maxCookieSize: 3 } }, + }); + + (window as any).mParticle._Store.localSessionAttributes = {}; + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-oversized', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + const stored = JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews); - expect(stored.length).toBe(25); - // Oldest five (source-message-id-0..4) evicted; newest retained. - expect(stored[0].sourceMessageId).toBe('source-message-id-5'); - expect(stored[24].sourceMessageId).toBe('source-message-id-29'); + // The current page view is never evicted, even when it alone exceeds the budget. + expect(stored.length).toBe(1); + expect(stored[0].sourceMessageId).toBe('source-message-id-oversized'); + }); + + it('falls back to the cookie default budget and does not throw when SDK internals are missing', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + // getInstance() present but no _Store — resolver must degrade to 3000 / 3. + (window as any).mParticle.getInstance = () => ({ + setIntegrationAttribute: () => {}, + }); + + (window as any).mParticle._Store.localSessionAttributes = {}; + expect(() => { + for (let i = 0; i < 50; i++) { + (window as any).mParticle.forwarder.process({ + EventName: 'Page ' + i, + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-' + i, + Timestamp: 1712345678000 + i, + ActiveTimeOnSite: i, + }); + } + }).not.toThrow(); + + const raw = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; + expect(raw.length).toBeLessThanOrEqual(1000); + expect(JSON.parse(raw).length).toBeGreaterThan(1); }); it('does not throw when setLocalSessionAttribute is unavailable', async () => { From 8cf350b84d0ba08e021089f8221a0fabe202188b Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 16:37:16 -0400 Subject: [PATCH 21/27] refactor: move page-view capture to kit-owned localStorage Persist captured page views in the kit's own localStorage instead of mParticle's local session attributes, so page-view capture no longer touches mParticle persistence or cookie sync. Replace the storage-backend byte-budget calculator with a fixed 25-record cap (oldest evicted), and drop the SDK-internal _Store/SDKConfig typing it depended on. Capture now runs independently of setLocalSessionAttribute availability, and localStorage read/write is guarded so a storage failure surfaces a single WARNING without throwing out of the forwarder. --- src/Rokt-Kit.ts | 108 +++++++++---------------- test/src/tests.spec.ts | 174 +++++++++++++---------------------------- 2 files changed, 93 insertions(+), 189 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 93c1a4e..3d1b997 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -135,18 +135,8 @@ interface RoktManager { setLocalSessionAttribute?(key: string, value: unknown): void; } -interface SDKConfigInternal { - useCookieStorage?: boolean; - maxCookieSize?: number; -} - -interface StoreInternal { - SDKConfig?: SDKConfigInternal; -} - interface MParticleInstance { setIntegrationAttribute(moduleId: number, attrs: Record): void; - _Store?: StoreInternal; } interface OptimizelyState { @@ -265,16 +255,15 @@ const ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element'; const USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace'; const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView -// Local-session-attribute key under which captured page views are persisted (as a -// JSON string). Distinct from PAGE_EVENTS_KEY, which is the flattened wire shape -// sent to Rokt on selectPlacements. +// localStorage key under which captured page views are persisted (as a JSON +// string). The kit owns this storage directly — separate from mParticle's +// cookie/localStorage — so page-view capture does not affect mParticle +// persistence or cookie sync. Distinct from PAGE_EVENTS_KEY, which is the +// flattened wire shape sent to Rokt on selectPlacements. const LS_PAGE_VIEWS_KEY = 'mpPageViews'; -// Byte budget for the persisted page-view history, keyed to the storage backend -// (see resolvePageViewsBudget). localStorage has no SDK-enforced size cap, so we -// self-impose 128 KB. Code constant, not a kit setting — change it here. -const PAGE_VIEWS_LS_BUDGET_BYTES = 128 * 1024; -// Cookie mode: use 1/3 of the SDK's maxCookieSize so the blob shares the cookie. -const PAGE_VIEWS_COOKIE_BUDGET_DIVISOR = 3; +// Fixed cap on the number of persisted page views (oldest evicted first). Code +// constant, not a kit setting — change it here. +const PAGE_VIEWS_MAX_COUNT = 25; const PAGE_EVENTS_KEY = 'page_events'; // Bound on how long selectPlacements will wait for an in-flight Workspace @@ -320,19 +309,27 @@ function mp(): MParticleExtended { // Module-level utility functions // ============================================================ -// Resolves the byte budget for the persisted page-view history from the SDK's -// storage backend. Reads private SDK internals (_Store.SDKConfig), so every -// access is optional-chained; any missing internal falls back to the cookie -// default (maxCookieSize 3000 / 3), the small/safe choice. -function resolvePageViewsBudget(): number { - const sdkConfig = mp().getInstance()?._Store?.SDKConfig; - if (sdkConfig?.useCookieStorage === true) { - return Math.floor((sdkConfig.maxCookieSize ?? 3000) / PAGE_VIEWS_COOKIE_BUDGET_DIVISOR); - } - if (sdkConfig) { - return PAGE_VIEWS_LS_BUDGET_BYTES; +// Reads and parses the kit-owned page-view list from localStorage. Returns an +// empty array when nothing is stored, the value cannot be parsed, or +// localStorage is unavailable (Safari private mode, storage disabled). Guarded +// so a storage failure never throws out of the forwarder. +function readPageViewsStorage(): PageEvent[] { + try { + const stored = window.localStorage.getItem(LS_PAGE_VIEWS_KEY); + if (stored === null) { + return []; + } + const parsed = JSON.parse(stored); + return Array.isArray(parsed) ? (parsed as PageEvent[]) : []; + } catch { + return []; } - return Math.floor(3000 / PAGE_VIEWS_COOKIE_BUDGET_DIVISOR); +} + +// Serializes and writes the kit-owned page-view list to localStorage. Throws on +// failure (quota exceeded, storage disabled); the caller's catch handles it. +function writePageViewsStorage(pageViews: PageEvent[]): void { + window.localStorage.setItem(LS_PAGE_VIEWS_KEY, JSON.stringify(pageViews)); } function generateLauncherScript(domain: string | undefined, extensions: string[]): string { @@ -906,19 +903,13 @@ class RoktKit implements KitInterface { } } - // Appends a page-view record to the persisted list under LS_PAGE_VIEWS_KEY, - // capping at a byte budget (oldest evicted). The list is stored as a JSON - // string because setLocalSessionAttribute only contracts to accept primitive - // AttributeValues. Wrapped so a malformed event can never throw out of the - // forwarder. Callers must confirm the event is a page view and that - // setLocalSessionAttribute is available. private capturePageView(event: SDKEvent): void { let pageUrl: string | undefined; try { pageUrl = sanitizeUrl(window.location.href); - const pageViews = this.readStoredPageViews(); + const pageViews = readPageViewsStorage(); pageViews.push({ pageUrl, @@ -927,18 +918,11 @@ class RoktKit implements KitInterface { activeTimeOnSite: event.ActiveTimeOnSite, }); - // Evict oldest until the serialized blob fits the budget. `.length` is a - // conservative char-count proxy for UTF-8 bytes. `length > 1` always - // retains the current page-view and prevents an empty-array infinite loop. - // We reuse `serialized` for the write below, re-serializing only on evict. - const budget = resolvePageViewsBudget(); - let serialized = JSON.stringify(pageViews); - while (pageViews.length > 1 && serialized.length > budget) { + while (pageViews.length > PAGE_VIEWS_MAX_COUNT) { pageViews.shift(); - serialized = JSON.stringify(pageViews); } - mp().Rokt.setLocalSessionAttribute?.(LS_PAGE_VIEWS_KEY, serialized); + writePageViewsStorage(pageViews); } catch (err) { this.errorReportingService?.report({ message: `Rokt Kit: Failed to capture page view for ${pageUrl}`, @@ -949,22 +933,6 @@ class RoktKit implements KitInterface { } } - // Reads and parses the persisted page-view list from local session attributes. - // Returns an empty array when nothing is stored or the value cannot be parsed - // (e.g. a legacy raw-array value written before JSON persistence). - private readStoredPageViews(): PageEvent[] { - const stored = mp().Rokt.getLocalSessionAttributes?.()?.[LS_PAGE_VIEWS_KEY]; - if (typeof stored !== 'string') { - return []; - } - try { - const parsed = JSON.parse(stored); - return Array.isArray(parsed) ? (parsed as PageEvent[]) : []; - } catch { - return []; - } - } - private isLauncherReadyToAttach(): boolean { return !!window.Rokt && typeof window.Rokt.createLauncher === 'function'; } @@ -1299,11 +1267,13 @@ class RoktKit implements KitInterface { } public process(event: SDKEvent): string { - if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { - if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { - this.capturePageView(event); - } + // Page-view capture uses kit-owned localStorage, so it runs independently + // of mParticle's setLocalSessionAttribute availability. + if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { + this.capturePageView(event); + } + if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (!isEmpty(this.placementEventAttributeMappingLookup)) { this.applyPlacementEventAttributeMapping(event); } @@ -1511,8 +1481,8 @@ class RoktKit implements KitInterface { const filteredUserIdentities = this.returnUserIdentities(filteredUser); - const { [LS_PAGE_VIEWS_KEY]: _rawPageViews, ...sessionAttributes } = this.returnLocalSessionAttributes(); - const pageEvents = this.buildPageEvents(this.readStoredPageViews()); + const sessionAttributes = this.returnLocalSessionAttributes(); + const pageEvents = this.buildPageEvents(readPageViewsStorage()); const selectPlacementsAttributes: Record = { ...(filteredUserIdentities as Record), diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 69d65c2..ead3bec 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5638,9 +5638,17 @@ describe('Rokt Forwarder', () => { }); describe('page view capture', () => { - const defaultGetInstance = (window as any).mParticle.getInstance; + const readStoredPageViews = () => { + const raw = window.localStorage.getItem('mpPageViews'); + return raw === null ? null : JSON.parse(raw); + }; + + beforeEach(() => { + window.localStorage.clear(); + }); + afterEach(() => { - (window as any).mParticle.getInstance = defaultGetInstance; + window.localStorage.clear(); }); it('appends a page view record with the expected fields when the event is a PageView', async () => { @@ -5656,7 +5664,6 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ EventName: 'Home Page', EventCategory: EventType.Unknown, @@ -5670,7 +5677,7 @@ describe('Rokt Forwarder', () => { }, }); - expect(JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews)).toEqual([ + expect(readStoredPageViews()).toEqual([ { pageUrl: window.location.href, sourceMessageId: 'source-message-id-1', @@ -5693,7 +5700,6 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ EventName: 'Video Watched', EventCategory: EventType.Other, @@ -5703,10 +5709,10 @@ describe('Rokt Forwarder', () => { ActiveTimeOnSite: 100, }); - expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toBeUndefined(); + expect(readStoredPageViews()).toBeNull(); }); - it('evicts oldest to keep the serialized blob within the cookie-mode byte budget', async () => { + it('caps the stored history at 25 records, evicting oldest first', async () => { await (window as any).mParticle.forwarder.init( { accountId: '123456', @@ -5719,14 +5725,7 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - // Cookie mode with maxCookieSize 3000 → budget 3000 / 3 = 1000 bytes. - (window as any).mParticle.getInstance = () => ({ - setIntegrationAttribute: () => {}, - _Store: { SDKConfig: { useCookieStorage: true, maxCookieSize: 3000 } }, - }); - - (window as any).mParticle._Store.localSessionAttributes = {}; - for (let i = 0; i < 50; i++) { + for (let i = 0; i < 30; i++) { (window as any).mParticle.forwarder.process({ EventName: 'Page ' + i, EventCategory: EventType.Unknown, @@ -5737,16 +5736,14 @@ describe('Rokt Forwarder', () => { }); } - const raw = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; - const stored = JSON.parse(raw); - expect(raw.length).toBeLessThanOrEqual(1000); - expect(stored.length).toBeGreaterThan(1); - // Oldest were evicted; the newest page view is always retained. - expect(stored[stored.length - 1].sourceMessageId).toBe('source-message-id-49'); - expect(stored[0].sourceMessageId).not.toBe('source-message-id-0'); + const stored = readStoredPageViews(); + // 30 written, capped at 25 — the 5 oldest evicted, newest always retained. + expect(stored.length).toBe(25); + expect(stored[0].sourceMessageId).toBe('source-message-id-5'); + expect(stored[stored.length - 1].sourceMessageId).toBe('source-message-id-29'); }); - it('retains many records in localStorage mode under the 128 KB budget', async () => { + it('does not throw and reports a warning when localStorage writes throw', async () => { await (window as any).mParticle.forwarder.init( { accountId: '123456', @@ -5759,106 +5756,36 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - // localStorage mode → 128 KB budget, room for hundreds of records. - (window as any).mParticle.getInstance = () => ({ - setIntegrationAttribute: () => {}, - _Store: { SDKConfig: { useCookieStorage: false } }, + const reportSpy = vi.spyOn((window as any).mParticle.forwarder.errorReportingService, 'report'); + const setItemSpy = vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { + throw new Error('QuotaExceededError'); }); - (window as any).mParticle._Store.localSessionAttributes = {}; - for (let i = 0; i < 200; i++) { - (window as any).mParticle.forwarder.process({ - EventName: 'Page ' + i, - EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, - SourceMessageId: 'source-message-id-' + i, - Timestamp: 1712345678000 + i, - ActiveTimeOnSite: i, - }); - } - - const raw = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; - const stored = JSON.parse(raw); - expect(raw.length).toBeLessThanOrEqual(128 * 1024); - // 200 small records are well within 128 KB — none evicted. - expect(stored.length).toBe(200); - expect(stored[0].sourceMessageId).toBe('source-message-id-0'); - expect(stored[199].sourceMessageId).toBe('source-message-id-199'); - }); - - it('stores a single record that on its own exceeds the byte budget (length > 1 guard)', async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - // Cookie mode, tiny budget so any single record blows it. - (window as any).mParticle.getInstance = () => ({ - setIntegrationAttribute: () => {}, - _Store: { SDKConfig: { useCookieStorage: true, maxCookieSize: 3 } }, - }); - - (window as any).mParticle._Store.localSessionAttributes = {}; - (window as any).mParticle.forwarder.process({ - EventName: 'Home Page', - EventCategory: EventType.Unknown, - EventDataType: MessageType.PageView, - SourceMessageId: 'source-message-id-oversized', - Timestamp: 1712345678000, - ActiveTimeOnSite: 4200, - }); - - const stored = JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews); - // The current page view is never evicted, even when it alone exceeds the budget. - expect(stored.length).toBe(1); - expect(stored[0].sourceMessageId).toBe('source-message-id-oversized'); - }); - - it('falls back to the cookie default budget and does not throw when SDK internals are missing', async () => { - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - // getInstance() present but no _Store — resolver must degrade to 3000 / 3. - (window as any).mParticle.getInstance = () => ({ - setIntegrationAttribute: () => {}, - }); - - (window as any).mParticle._Store.localSessionAttributes = {}; - expect(() => { - for (let i = 0; i < 50; i++) { + try { + expect(() => { (window as any).mParticle.forwarder.process({ - EventName: 'Page ' + i, + EventName: 'Home Page', EventCategory: EventType.Unknown, EventDataType: MessageType.PageView, - SourceMessageId: 'source-message-id-' + i, - Timestamp: 1712345678000 + i, - ActiveTimeOnSite: i, + SourceMessageId: 'source-message-id-throws', + Timestamp: 1712345678000, + ActiveTimeOnSite: 10, }); - } - }).not.toThrow(); + }).not.toThrow(); + } finally { + setItemSpy.mockRestore(); + } - const raw = (window as any).mParticle._Store.localSessionAttributes.mpPageViews; - expect(raw.length).toBeLessThanOrEqual(1000); - expect(JSON.parse(raw).length).toBeGreaterThan(1); + // Nothing is persisted, but the forwarder keeps running. + expect(readStoredPageViews()).toBeNull(); + // The write failure is surfaced as a WARNING. + expect(reportSpy).toHaveBeenCalledWith( + expect.objectContaining({ code: 'PAGE_VIEW_CAPTURE_FAILED', severity: 'WARNING' }), + ); + reportSpy.mockRestore(); }); - it('does not throw when setLocalSessionAttribute is unavailable', async () => { + it('captures page views independently of setLocalSessionAttribute availability', async () => { await (window as any).mParticle.forwarder.init( { accountId: '123456', @@ -5872,7 +5799,6 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); delete (window as any).mParticle.Rokt.setLocalSessionAttribute; - (window as any).mParticle._Store.localSessionAttributes = {}; expect(() => { (window as any).mParticle.forwarder.process({ @@ -5885,7 +5811,16 @@ describe('Rokt Forwarder', () => { }); }).not.toThrow(); - expect((window as any).mParticle._Store.localSessionAttributes.mpPageViews).toBeUndefined(); + // Page-view capture no longer depends on mParticle's session-attribute + // store, so it persists even when setLocalSessionAttribute is absent. + expect(readStoredPageViews()).toEqual([ + { + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-3', + timestamp: 1712345678000, + activeTimeOnSite: 10, + }, + ]); }); it('surfaces stored page views through selectPlacements as page_events without any placement mapping configured', async () => { @@ -6141,11 +6076,10 @@ describe('Rokt Forwarder', () => { // Reproduces the race where the core SDK routes the initial page-load // pageview to process() after it marks the forwarder active but before // the async launcher round-trip flips isInitialized/launcher. In that - // window isKitReady() is false, yet setLocalSessionAttribute is already - // usable — the pageview must still be persisted, not dropped. + // window isKitReady() is false — the pageview must still be persisted + // to kit-owned localStorage, not dropped. (window as any).mParticle.forwarder.isInitialized = false; (window as any).mParticle.forwarder.launcher = null; - (window as any).mParticle._Store.localSessionAttributes = {}; (window as any).mParticle.forwarder.process({ EventName: 'Home Page', @@ -6156,7 +6090,7 @@ describe('Rokt Forwarder', () => { ActiveTimeOnSite: 4200, }); - expect(JSON.parse((window as any).mParticle._Store.localSessionAttributes.mpPageViews)).toEqual([ + expect(readStoredPageViews()).toEqual([ { pageUrl: window.location.href, sourceMessageId: 'source-message-id-race', From 9fc8b0e14acfedd432f2758d02a98f858c25f8fa Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 17:17:18 -0400 Subject: [PATCH 22/27] feat: clear kit-owned page views on session end --- src/Rokt-Kit.ts | 26 ++++++++++++++++++++------ test/src/tests.spec.ts | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 3d1b997..ac010b3 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -255,6 +255,7 @@ const ROKT_THANK_YOU_ELEMENT_SCRIPT_ID = 'rokt-thank-you-element'; const USER_IDENTIFIED_IN_WORKSPACE_KEY = 'userIdentifiedInWorkspace'; const MESSAGE_TYPE_PAGE_VIEW = 3; // mParticle MessageType.PageView +const MESSAGE_TYPE_SESSION_END = 2; // mParticle MessageType.SessionEnd // localStorage key under which captured page views are persisted (as a JSON // string). The kit owns this storage directly — separate from mParticle's // cookie/localStorage — so page-view capture does not affect mParticle @@ -309,10 +310,6 @@ function mp(): MParticleExtended { // Module-level utility functions // ============================================================ -// Reads and parses the kit-owned page-view list from localStorage. Returns an -// empty array when nothing is stored, the value cannot be parsed, or -// localStorage is unavailable (Safari private mode, storage disabled). Guarded -// so a storage failure never throws out of the forwarder. function readPageViewsStorage(): PageEvent[] { try { const stored = window.localStorage.getItem(LS_PAGE_VIEWS_KEY); @@ -326,12 +323,14 @@ function readPageViewsStorage(): PageEvent[] { } } -// Serializes and writes the kit-owned page-view list to localStorage. Throws on -// failure (quota exceeded, storage disabled); the caller's catch handles it. function writePageViewsStorage(pageViews: PageEvent[]): void { window.localStorage.setItem(LS_PAGE_VIEWS_KEY, JSON.stringify(pageViews)); } +function clearPageViewsStorage(): void { + window.localStorage.removeItem(LS_PAGE_VIEWS_KEY); +} + function generateLauncherScript(domain: string | undefined, extensions: string[]): string { const launcherPath = '/wsdk/integrations/launcher.js'; const baseUrl = [generateBaseUrl(domain), launcherPath].join(''); @@ -1273,6 +1272,21 @@ class RoktKit implements KitInterface { this.capturePageView(event); } + // Session end clears the kit-owned page-view list so a new session starts + // fresh — page views are scoped to a single session. + if (event.EventDataType === MESSAGE_TYPE_SESSION_END) { + try { + clearPageViewsStorage(); + } catch (err) { + this.errorReportingService?.report({ + message: 'Rokt Kit: Failed to clear page views on session end', + code: 'PAGE_VIEW_CAPTURE_FAILED', + severity: WSDKErrorSeverity.WARNING, + stackTrace: err instanceof Error ? err.stack : undefined, + }); + } + } + if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (!isEmpty(this.placementEventAttributeMappingLookup)) { this.applyPlacementEventAttributeMapping(event); diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index ead3bec..f0ca66a 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5743,6 +5743,42 @@ describe('Rokt Forwarder', () => { expect(stored[stored.length - 1].sourceMessageId).toBe('source-message-id-29'); }); + it('clears the stored page-view history on a SessionEnd event', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-1', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + + expect(readStoredPageViews()).not.toBeNull(); + + (window as any).mParticle.forwarder.process({ + EventName: 'Session End', + EventCategory: EventType.Unknown, + EventDataType: MessageType.SessionEnd, + SourceMessageId: 'source-message-id-session-end', + Timestamp: 1712345679000, + ActiveTimeOnSite: 4300, + }); + + expect(readStoredPageViews()).toBeNull(); + }); + it('does not throw and reports a warning when localStorage writes throw', async () => { await (window as any).mParticle.forwarder.init( { From 9c340f1e54e285d29f9ebf23d99ba3591e794197 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 17:31:03 -0400 Subject: [PATCH 23/27] fix: make PageEvent.activeTimeOnSite optional for storage round-trip A localStorage JSON round-trip can yield undefined/NaN for a mangled or partially-written record, so activeTimeOnSite is no longer guaranteed to be a number. Type it as optional and guard the timeOnPage subtraction in buildPageEvents against non-number values. Addresses review feedback on PR #109. --- src/Rokt-Kit.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index ac010b3..65876e2 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -65,7 +65,10 @@ interface PageEvent { pageUrl: string; sourceMessageId: string; timestamp: number; - activeTimeOnSite: number; + // Optional because it survives a localStorage JSON round-trip: a mangled or + // partially-written record can yield undefined/NaN, so consumers must guard + // rather than assume a number is present. + activeTimeOnSite?: number; // Derived at transmission (see buildPageEvents), not at capture — it depends // on the next page view's activeTimeOnSite, so it is absent on stored records. timeOnPage?: number; @@ -966,7 +969,7 @@ class RoktKit implements KitInterface { }; const next = pageViews[index + 1]; - if (next) { + if (next && typeof next.activeTimeOnSite === 'number' && typeof pageView.activeTimeOnSite === 'number') { const diff = next.activeTimeOnSite - pageView.activeTimeOnSite; if (diff >= 0) { pageEvent.timeOnPage = diff; From 799cbc5301a4bf834e3a8a3f11edf711fe73f077 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 17:46:50 -0400 Subject: [PATCH 24/27] fix: restore not-ready signal and gate page-view capture on targeting Restore the isKitReady guard in process() that was removed in 329efb3, but place it after kit-owned page-view capture so capture still runs before the launcher attaches while the core SDK regains the not-ready signal for the forwarding path. Also gate page-view capture and session-end cleanup on the noTargeting launcher option: page views are behavioral targeting signals and must not be collected when the partner has opted out of targeting. Addresses review feedback on PR #109. --- src/Rokt-Kit.ts | 47 +++++++++++++------- test/src/tests.spec.ts | 97 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 16 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 65876e2..719b617 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -1114,6 +1114,12 @@ class RoktKit implements KitInterface { return !!(this.isInitialized && this.launcher); } + // When the partner has opted out of targeting (noTargeting launcher option), + // the kit must not collect behavioral targeting signals such as page views. + private isTargetingDisabled(): boolean { + return (mp().Rokt?.launcherOptions as Record | undefined)?.noTargeting === true; + } + private isPartnerInLocalLauncherTestGroup(): boolean { return !!(mp().config && mp().config!.isLocalLauncherEnabled && this.isAssignedToSampleGroup()); } @@ -1270,26 +1276,35 @@ class RoktKit implements KitInterface { public process(event: SDKEvent): string { // Page-view capture uses kit-owned localStorage, so it runs independently - // of mParticle's setLocalSessionAttribute availability. - if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { - this.capturePageView(event); - } + // of launcher readiness — but only when targeting is permitted, since page + // views are behavioral targeting signals. + if (!this.isTargetingDisabled()) { + if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { + this.capturePageView(event); + } - // Session end clears the kit-owned page-view list so a new session starts - // fresh — page views are scoped to a single session. - if (event.EventDataType === MESSAGE_TYPE_SESSION_END) { - try { - clearPageViewsStorage(); - } catch (err) { - this.errorReportingService?.report({ - message: 'Rokt Kit: Failed to clear page views on session end', - code: 'PAGE_VIEW_CAPTURE_FAILED', - severity: WSDKErrorSeverity.WARNING, - stackTrace: err instanceof Error ? err.stack : undefined, - }); + // Session end clears the kit-owned page-view list so a new session starts + // fresh — page views are scoped to a single session. + if (event.EventDataType === MESSAGE_TYPE_SESSION_END) { + try { + clearPageViewsStorage(); + } catch (err) { + this.errorReportingService?.report({ + message: 'Rokt Kit: Failed to clear page views on session end', + code: 'PAGE_VIEW_CAPTURE_FAILED', + severity: WSDKErrorSeverity.WARNING, + stackTrace: err instanceof Error ? err.stack : undefined, + }); + } } } + // The forwarding work below (LSA mapping) depends on the launcher, so guard + // it here and surface the not-ready signal to the core SDK. + if (!this.isKitReady()) { + return 'Kit not ready for forwarder: ' + name; + } + if (typeof mp().Rokt?.setLocalSessionAttribute === 'function') { if (!isEmpty(this.placementEventAttributeMappingLookup)) { this.applyPlacementEventAttributeMapping(event); diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index f0ca66a..7c404ff 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5779,6 +5779,103 @@ describe('Rokt Forwarder', () => { expect(readStoredPageViews()).toBeNull(); }); + it('captures the page view but returns the not-ready signal when the kit is not ready', () => { + // Force a not-ready state: capture must still run (kit-owned storage), + // but process() must tell the core SDK the forwarder is not ready. + (window as any).mParticle.forwarder.isInitialized = false; + (window as any).mParticle.forwarder.launcher = null; + + const result = (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-not-ready', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + + expect(result).toContain('Kit not ready'); + expect(readStoredPageViews()).toEqual([ + { + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-not-ready', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]); + }); + + it('does not capture page views when targeting is disabled (noTargeting launcher option)', async () => { + (window as any).mParticle.Rokt.launcherOptions = { + noTargeting: true, + }; + + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-1', + Timestamp: 1712345678000, + ActiveTimeOnSite: 4200, + }); + + expect(readStoredPageViews()).toBeNull(); + }); + + it('does not clear stored page views on SessionEnd when targeting is disabled', async () => { + // Seed a stored page view from a period when targeting was permitted. + window.localStorage.setItem( + 'mpPageViews', + JSON.stringify([ + { + pageUrl: 'https://example.com/', + sourceMessageId: 'seeded', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]), + ); + + (window as any).mParticle.Rokt.launcherOptions = { + noTargeting: true, + }; + + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.process({ + EventName: 'Session End', + EventCategory: EventType.Unknown, + EventDataType: MessageType.SessionEnd, + SourceMessageId: 'source-message-id-session-end', + Timestamp: 1712345679000, + ActiveTimeOnSite: 4300, + }); + + expect(readStoredPageViews()).not.toBeNull(); + }); + it('does not throw and reports a warning when localStorage writes throw', async () => { await (window as any).mParticle.forwarder.init( { From ccdc2fa9b489a9bcce6d07ea3d790293f27989eb Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 18:20:42 -0400 Subject: [PATCH 25/27] fix: drop activeTimeOnSite guard and restore non-optional type A finite number round-trips losslessly through JSON, and SDKEvent types ActiveTimeOnSite as a non-optional number, so buildPageEvents never sees a non-number in the normal flow. Drop the per-field typeof guard and realign PageEvent.activeTimeOnSite with the source type. --- src/Rokt-Kit.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 719b617..6825dba 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -65,10 +65,7 @@ interface PageEvent { pageUrl: string; sourceMessageId: string; timestamp: number; - // Optional because it survives a localStorage JSON round-trip: a mangled or - // partially-written record can yield undefined/NaN, so consumers must guard - // rather than assume a number is present. - activeTimeOnSite?: number; + activeTimeOnSite: number; // Derived at transmission (see buildPageEvents), not at capture — it depends // on the next page view's activeTimeOnSite, so it is absent on stored records. timeOnPage?: number; @@ -969,7 +966,7 @@ class RoktKit implements KitInterface { }; const next = pageViews[index + 1]; - if (next && typeof next.activeTimeOnSite === 'number' && typeof pageView.activeTimeOnSite === 'number') { + if (next) { const diff = next.activeTimeOnSite - pageView.activeTimeOnSite; if (diff >= 0) { pageEvent.timeOnPage = diff; @@ -1275,16 +1272,11 @@ class RoktKit implements KitInterface { } public process(event: SDKEvent): string { - // Page-view capture uses kit-owned localStorage, so it runs independently - // of launcher readiness — but only when targeting is permitted, since page - // views are behavioral targeting signals. if (!this.isTargetingDisabled()) { if (event.EventDataType === MESSAGE_TYPE_PAGE_VIEW) { this.capturePageView(event); } - // Session end clears the kit-owned page-view list so a new session starts - // fresh — page views are scoped to a single session. if (event.EventDataType === MESSAGE_TYPE_SESSION_END) { try { clearPageViewsStorage(); From 2ca0366516344a70ecbb49d2ccbe5a9b9005df94 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 3 Aug 2026 18:32:11 -0400 Subject: [PATCH 26/27] fix: coerce non-finite ActiveTimeOnSite to 0 at write boundary A NaN/Infinity source serializes to "null" via JSON.stringify and reads back as a non-number, which is what made the stored type look inconsistent in review. Normalizing at capture guarantees only finite numbers enter storage, so PageEvent.activeTimeOnSite stays an honest non-optional number with no per-field guard needed on read. --- src/Rokt-Kit.ts | 2 +- test/src/tests.spec.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 6825dba..884ebae 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -914,7 +914,7 @@ class RoktKit implements KitInterface { pageUrl, sourceMessageId: event.SourceMessageId, timestamp: event.Timestamp, - activeTimeOnSite: event.ActiveTimeOnSite, + activeTimeOnSite: Number.isFinite(event.ActiveTimeOnSite) ? event.ActiveTimeOnSite : 0, }); while (pageViews.length > PAGE_VIEWS_MAX_COUNT) { diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 7c404ff..f7269d2 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5687,6 +5687,41 @@ describe('Rokt Forwarder', () => { ]); }); + it('coerces a non-finite ActiveTimeOnSite to 0 at the write boundary', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + // A NaN source would serialize to "null" (JSON.stringify(NaN) === 'null') + // and read back as a non-number, breaking the honest `number` stored type. + // Coercing at capture guarantees only finite numbers ever enter storage. + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-nan', + Timestamp: 1712345678000, + ActiveTimeOnSite: NaN, + }); + + expect(readStoredPageViews()).toEqual([ + { + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-nan', + timestamp: 1712345678000, + activeTimeOnSite: 0, + }, + ]); + }); + it('does not append a page view record for a non-PageView event', async () => { await (window as any).mParticle.forwarder.init( { From 7b1cf3b567c4194e5e720d0b8f309f9aa1b205d7 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Tue, 4 Aug 2026 11:13:30 -0400 Subject: [PATCH 27/27] fix: omit non-finite activeTimeOnSite and clear page views when targeting disabled Revert PageEvent.activeTimeOnSite to optional and omit it at capture when non-finite rather than coercing to 0, which would be diffed against the next record in buildPageEvents and fabricate a dwell time. Guard the timeOnPage diff on both records carrying a finite value so "unknown" stays distinguishable from a genuine zero. Clear the kit-owned page-view store once on init when targeting is disabled, so a later re-enable starts fresh without reading storage on every event or dispatch. A clear failure surfaces as a PAGE_VIEW_CAPTURE_FAILED warning. --- src/Rokt-Kit.ts | 45 ++++++++++--- test/src/tests.spec.ts | 139 +++++++++++++++++++++++++++-------------- 2 files changed, 129 insertions(+), 55 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 884ebae..c057732 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -65,9 +65,10 @@ interface PageEvent { pageUrl: string; sourceMessageId: string; timestamp: number; - activeTimeOnSite: number; - // Derived at transmission (see buildPageEvents), not at capture — it depends - // on the next page view's activeTimeOnSite, so it is absent on stored records. + activeTimeOnSite?: number; + // Derived at transmission not at capture based + // on the next page view's activeTimeOnSite, + // so it is absent on stored records. timeOnPage?: number; } @@ -910,12 +911,17 @@ class RoktKit implements KitInterface { const pageViews = readPageViewsStorage(); - pageViews.push({ + const pageView: PageEvent = { pageUrl, sourceMessageId: event.SourceMessageId, timestamp: event.Timestamp, - activeTimeOnSite: Number.isFinite(event.ActiveTimeOnSite) ? event.ActiveTimeOnSite : 0, - }); + }; + + if (Number.isFinite(event.ActiveTimeOnSite)) { + pageView.activeTimeOnSite = event.ActiveTimeOnSite; + } + + pageViews.push(pageView); while (pageViews.length > PAGE_VIEWS_MAX_COUNT) { pageViews.shift(); @@ -962,12 +968,20 @@ class RoktKit implements KitInterface { pageUrl: pageView.pageUrl, sourceMessageId: pageView.sourceMessageId, timestamp: pageView.timestamp, - activeTimeOnSite: pageView.activeTimeOnSite, }; + const activeTimeOnSite = pageView.activeTimeOnSite; + const hasActiveTime = activeTimeOnSite !== undefined && Number.isFinite(activeTimeOnSite); + if (hasActiveTime) { + pageEvent.activeTimeOnSite = activeTimeOnSite; + } + const next = pageViews[index + 1]; - if (next) { - const diff = next.activeTimeOnSite - pageView.activeTimeOnSite; + const nextActiveTimeOnSite = next?.activeTimeOnSite; + const hasNextActiveTimeOnSite = nextActiveTimeOnSite !== undefined && Number.isFinite(nextActiveTimeOnSite); + + if (hasActiveTime && hasNextActiveTimeOnSite) { + const diff = nextActiveTimeOnSite - activeTimeOnSite; if (diff >= 0) { pageEvent.timeOnPage = diff; } @@ -1201,6 +1215,19 @@ class RoktKit implements KitInterface { this.errorReportingService = errorReportingService; this.loggingService = loggingService; + if (this.isTargetingDisabled()) { + try { + clearPageViewsStorage(); + } catch (err) { + this.errorReportingService?.report({ + message: 'Rokt Kit: Failed to clear page views when targeting is disabled', + code: 'PAGE_VIEW_CAPTURE_FAILED', + severity: WSDKErrorSeverity.WARNING, + stackTrace: err instanceof Error ? err.stack : undefined, + }); + } + } + if (mp()._registerErrorReportingService) { mp()._registerErrorReportingService!(errorReportingService); } diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index f7269d2..6cbbce6 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5687,7 +5687,7 @@ describe('Rokt Forwarder', () => { ]); }); - it('coerces a non-finite ActiveTimeOnSite to 0 at the write boundary', async () => { + it('omits activeTimeOnSite when the source value is non-finite', async () => { await (window as any).mParticle.forwarder.init( { accountId: '123456', @@ -5701,8 +5701,10 @@ describe('Rokt Forwarder', () => { await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); // A NaN source would serialize to "null" (JSON.stringify(NaN) === 'null') - // and read back as a non-number, breaking the honest `number` stored type. - // Coercing at capture guarantees only finite numbers ever enter storage. + // and read back as a non-number. Rather than coerce to 0 — which would be + // indistinguishable from a genuine zero and get diffed against the next + // record, fabricating a dwell time — we omit the field so it stays + // "unknown". Only finite numbers ever enter storage. (window as any).mParticle.forwarder.process({ EventName: 'Home Page', EventCategory: EventType.Unknown, @@ -5717,7 +5719,6 @@ describe('Rokt Forwarder', () => { pageUrl: window.location.href, sourceMessageId: 'source-message-id-nan', timestamp: 1712345678000, - activeTimeOnSite: 0, }, ]); }); @@ -5869,48 +5870,6 @@ describe('Rokt Forwarder', () => { expect(readStoredPageViews()).toBeNull(); }); - it('does not clear stored page views on SessionEnd when targeting is disabled', async () => { - // Seed a stored page view from a period when targeting was permitted. - window.localStorage.setItem( - 'mpPageViews', - JSON.stringify([ - { - pageUrl: 'https://example.com/', - sourceMessageId: 'seeded', - timestamp: 1712345678000, - activeTimeOnSite: 4200, - }, - ]), - ); - - (window as any).mParticle.Rokt.launcherOptions = { - noTargeting: true, - }; - - await (window as any).mParticle.forwarder.init( - { - accountId: '123456', - }, - reportService.cb, - true, - null, - {}, - ); - - await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); - - (window as any).mParticle.forwarder.process({ - EventName: 'Session End', - EventCategory: EventType.Unknown, - EventDataType: MessageType.SessionEnd, - SourceMessageId: 'source-message-id-session-end', - Timestamp: 1712345679000, - ActiveTimeOnSite: 4300, - }); - - expect(readStoredPageViews()).not.toBeNull(); - }); - it('does not throw and reports a warning when localStorage writes throw', async () => { await (window as any).mParticle.forwarder.init( { @@ -6193,6 +6152,94 @@ describe('Rokt Forwarder', () => { expect(pageEvents[1].timeOnPage).toBeUndefined(); }); + it('does not fabricate a timeOnPage when a record is missing activeTimeOnSite', async () => { + // Seed a record with no activeTimeOnSite (the non-finite-source case) + // followed by one that has it. A coerced-to-0 first record would diff + // against the next (300000 - 0) and invent a 5-minute dwell that never + // happened; "unknown" must stay distinguishable from a genuine zero. + window.localStorage.setItem( + 'mpPageViews', + JSON.stringify([ + { + pageUrl: 'https://example.com/a', + sourceMessageId: 'missing-ats', + timestamp: 1712345678000, + }, + { + pageUrl: 'https://example.com/b', + sourceMessageId: 'has-ats', + timestamp: 1712345679000, + activeTimeOnSite: 300000, + }, + ]), + ); + + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle._Store.localSessionAttributes = {}; + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + const pageEvents = JSON.parse(forwardedAttributes.page_events); + // First record has no activeTimeOnSite and therefore no derived dwell time. + expect(pageEvents[0].activeTimeOnSite).toBeUndefined(); + expect(pageEvents[0].timeOnPage).toBeUndefined(); + // Second record keeps its finite value; still-open so no timeOnPage. + expect(pageEvents[1].activeTimeOnSite).toBe(300000); + expect(pageEvents[1].timeOnPage).toBeUndefined(); + }); + + it('clears stored page views on init when targeting is disabled', async () => { + // Seed a stored page view from a period when targeting was permitted. + window.localStorage.setItem( + 'mpPageViews', + JSON.stringify([ + { + pageUrl: 'https://example.com/', + sourceMessageId: 'seeded', + timestamp: 1712345678000, + activeTimeOnSite: 4200, + }, + ]), + ); + + (window as any).mParticle.Rokt.launcherOptions = { + noTargeting: true, + }; + + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + // Init clears leftover behavioral signals once, so a later re-enable + // starts fresh — nothing remains to surface. + expect(readStoredPageViews()).toBeNull(); + + (window as any).mParticle._Store.localSessionAttributes = {}; + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + expect(forwardedAttributes.page_events).toBeUndefined(); + }); + it('strips query params from the captured pageUrl', async () => { const originalLocation = window.location; // Query params commonly carry PII (emails, tokens); they must not be captured.