From f9063f33b96157ef627d8b928340a05d9f443a96 Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Fri, 8 May 2026 16:03:46 +0200 Subject: [PATCH 1/4] add performance tracker --- .../intentIqConstants/performanceConstants.js | 149 +++++++++++++ libraries/intentIqUtils/performanceTracker.js | 207 ++++++++++++++++++ libraries/intentIqUtils/performanceUtils.js | 169 ++++++++++++++ modules/intentIqIdSystem.js | 84 ++++++- 4 files changed, 607 insertions(+), 2 deletions(-) create mode 100644 libraries/intentIqConstants/performanceConstants.js create mode 100644 libraries/intentIqUtils/performanceTracker.js create mode 100644 libraries/intentIqUtils/performanceUtils.js diff --git a/libraries/intentIqConstants/performanceConstants.js b/libraries/intentIqConstants/performanceConstants.js new file mode 100644 index 00000000000..15f0d41fbe9 --- /dev/null +++ b/libraries/intentIqConstants/performanceConstants.js @@ -0,0 +1,149 @@ +export const PERFORMANCE_EVENT = { + SET_IIQ_OBJECT: 1, + SET_USER_CONFIG: 2, + BROWSER_BLACKLIST_DETECTED: 3, + + SET_DEFAULT_VALUES: 6, + INIT_COUNTERS: 7, + INIT_STORAGE: 8, + + READ_FIRST_PARTY_DATA: 11, + GENERATED_NEW_PCID: 12, + DEFINE_FIRST_PARTY_DATA: 13, + READ_CLIENT_HINTS_FROM_LS: 14, + + READ_TCF: 16, + RECEIVED_TCF: 17, + + CREATE_REQUEST_URL: 20, + MAKE_VR_REQUEST: 21, + + READ_USP_START: 25, + READ_USP_END: 26, + + START_GET_CLIENT_HINTS: 27, + RECEIVED_CLIENT_HINTS: 28, + + GROUP_DEFINING: 29, + + VR_RESPONSE_RECEIVED: 38, + CALLBACK_FIRE: 39, + READ_PARTNER_DATA_FROM_LS: 40, + + REFRESH_USER_IDS_START: 41, + USER_IDS_SET_IN_PREBID: 42, + + BROWSER_DETECTED: 43, + + DEFINE_GROUP_FROM_SERVER: 45, + + START_READ_LS_ENCODED_DATA: 46, + END_READ_LS_ENCODED_DATA: 47, + + GET_DEVICE_INFO: 48, + SYNC_REQUEST: 49, + + PROVIDED_GDPR: 51, + PROVIDED_USP: 53, + + TIMEOUT_VS_VR_RESPONSE: 56, + TIME_SINCE_LAST_VR_REQUEST: 57, + + ANALYTICS_ADAPTER_DETECTED: 61, + + VR_RESPONSE_FINISHED: 62, + DEFINE_GROUP_BASED_ON_SERVER_RESPONSE: 63, + GAM_TARGETING_SET: 64, + CH_FETCH_FAILED: 65, + VR_CALLBACK_TIMEOUT: 66, + VR_REQUEST_SKIPPED: 67, + EIDS_FOUND_IN_LS: 68, + EIDS_FOUND_IN_RESPONSE: 69, + STORE_FIRST_PARTY_DATA: 70, + STORE_PARTNER_DATA: 71, + UPDATE_GLOBAL_OBJECT: 72, + BUILD_SYNC_PIXEL_URL: 73, + SEND_SYNC_REQUEST: 74, + ANALYTICS_REPORT_SENT: 75, + ANALYTICS_REPORT_FAILED: 76, + PREPARE_ANALYTICS_PAYLOAD: 77, + CONSTRUCT_ANALYTICS_URL: 78, + BID_WON_TRIGGERED: 79, + ENABLE_ANALYTICS: 80, + DISABLE_ANALYTICS: 81 +}; + +export const ERROR_CODES = { + ALL: -1, + + VR_BAD_RESPONSE: 0, + VR_REQUEST_TIMEOUT: 1, + + MULTIPLE_VR_REQUESTS: 6, + MULTIPLE_VR_RESPONSES: 7, + CALLBACK_TRIGGERED_FEW_TIMES: 8, + + CONFIG_OBJECT_INVALID: 9, + PARTNER_ID_NOT_PROVIDED: 10, + + ANALYTICS_ADAPTER_MISSING: 11, + + CONFIG_OBJECT_NOT_PROVIDED_CORRECTLY: 12, + CH_FETCH_FAILED: 13, + GAM_TARGETING_FAILED: 14, + ENCRYPTION_FAILED: 15, + DECRYPTION_FAILED: 16, + STORE_DATA_FAILED: 17, + READ_DATA_FAILED: 18, + INVALID_RESPONSE_DATA: 19, + ANALYTICS_REPORT_FAILED: 20, + ANALYTICS_PAYLOAD_FAILED: 21, + DUPLICATE_ANALYTICS_REPORT: 22 +}; + +export const WARNING_CODES = { + ALL: -1, + + CMP_TRIGGERED_FEW_TIMES: 0, + MULTIPLE_AGENTS_SAME_ID: 1, + AUCTION_EIDS_DISCREPANCY: 2, + AUCTION_STARTED_BEFORE_AGENT: 3, + + CALLBACK_TIMEOUT_REACHED: 4, + EMPTY_EIDS_RESPONSE: 5, + USING_LS_EIDS: 6, + VR_SKIPPED: 7, + ANALYTICS_DISABLED: 8 +}; + +export const ADDITIONAL_ANALYTIC_PARAMS = { + ALL: -1, + + RUNNING_IN_TOP: 0, + LS_ACCESS: 1, + GAM_PRESENT: 2, + HAVE_EIDS_IN_LS: 3, + + AUCTION_EIDS_DIFF: 4, + AUCTION_TO_BIDWON_PERCENTAGE: 5, + BIDWON_MINUS_IMPRESSIONS: 6, + + CALLBACK_WITH_EIDS: 7, + CH_SUPPORTED: 8, + GDPR_APPLIES: 9, + USP_APPLIES: 10, + GPP_APPLIES: 11, + SERVER_CALLED: 12, + RESPONSE_HAD_EIDS: 13, + RESPONSE_HAD_PID: 14, + USER_OPTED_OUT: 15, + GAM_OBJECT_PRESENT: 16, + ANALYTICS_ENABLED: 17, + MANUAL_REPORTING_ENABLED: 18 +}; + +export const ErrorType = { + ERROR: 'ed', + WARNING: 'ld', + FATAL: 'fd' +}; \ No newline at end of file diff --git a/libraries/intentIqUtils/performanceTracker.js b/libraries/intentIqUtils/performanceTracker.js new file mode 100644 index 00000000000..03efa34c4f9 --- /dev/null +++ b/libraries/intentIqUtils/performanceTracker.js @@ -0,0 +1,207 @@ +import { + AAP, + businessAdapters, + emptyWarning, + ErrorsMap, + ErrorType, + multipleErrors +} from '../intentIqConstants/performanceConstants.js'; + +class PerformanceTracker { + constructor() { + this.performanceStart = performance.now(); + this.performanceData = {}; + this.warningData = JSON.parse(JSON.stringify(emptyWarning)); + this.additionalAnalyticParams = {}; + } + + addPerformance(id, value) { + let newPerformance; + + if (typeof value === 'number') { + newPerformance = value; + } else { + switch (id) { + case 0: + newPerformance = this.performanceStart; + break; + case 60: + newPerformance = performance.now(); + break; + default: + newPerformance = performance.now() - this.performanceStart; + break; + } + } + + this.performanceData[id] = this.performanceData[id] || []; + this.performanceData[id].push( + parseFloat(newPerformance.toFixed(1)) + ); + } + + addAdditionalAnalyticParam(id, value) { + if ( + id === AAP.eidsDiff || + id === AAP.auctionToBidWonPercentage || + id === AAP.bidWonImpressionsDiff + ) { + this.additionalAnalyticParams[id] = value; + return; + } + + this.additionalAnalyticParams[id] = value ? 1 : 0; + } + + addWarning(id, errorType) { + if (ErrorsMap[errorType]?.includes(id)) { + this.warningData[errorType][id] = + (this.warningData[errorType][id] || 0) + 1; + } + } + + checkWarnings(config) { + config.forEach(({ id, warningCode, errorType }) => { + if (this.performanceData[id]?.length > 1) { + this.addWarning(warningCode, errorType); + } + }); + } + + filterWarningData(data, ids) { + const filteredData = {}; + + for (const id of ids) { + if (data[id]) { + filteredData[id] = data[id]; + } + } + + return filteredData; + } + + updatePerformanceData(ids) { + if (ids.some(el => el === -1)) { + return Object.keys(this.performanceData).reduce((acc, key) => { + acc[Number(key)] = this.performanceData[Number(key)][0]; + return acc; + }, {}); + } + + return ids.reduce((acc, id) => { + if (this.performanceData[id]) { + acc[id] = this.performanceData[id][0]; + } + return acc; + }, {}); + } + + getFilteredData(pd, installedModules = []) { + let performanceData = {}; + const warningData = JSON.parse(JSON.stringify(emptyWarning)); + const businessData = {}; + let additionalAnalyticData = {}; + + this.checkWarnings(multipleErrors); + + if (pd?.p?.ct && pd?.p?.d?.length) { + performanceData = this.updatePerformanceData(pd.p.d); + pd.p.ct--; + } + + if (pd?.w?.ct) { + const keys = Object.values(ErrorType); + + keys.forEach(key => { + const data = pd?.w?.[key]; + + if ( + data && + data.length && + data.some(el => el === -1) + ) { + warningData[key] = this.warningData[key]; + } else { + warningData[key] = this.filterWarningData( + this.warningData[key], + data || [] + ); + } + }); + + if ( + Object.values(warningData).some( + data => Object.keys(data).length > 0 + ) + ) { + pd.w.ct--; + } + } + + if (pd?.b?.ct && pd?.b?.d?.length) { + if (pd.b.d.some(el => el === -1)) { + Object.entries(businessAdapters).forEach(([key, adapter]) => { + const index = Number(key); + + businessData[index] = + installedModules.includes(adapter) ? 1 : 0; + }); + } else { + pd.b.d.forEach(el => { + if (businessAdapters[el]) { + businessData[el] = + installedModules.includes( + businessAdapters[el] + ) + ? 1 + : 0; + } + }); + } + + pd.b.ct--; + } + + if (pd?.ad?.ct && pd?.ad?.d?.length) { + const addParamKeys = Object.keys( + this.additionalAnalyticParams + ); + + if (addParamKeys.length) { + if (pd.ad.d.some(item => item === -1)) { + additionalAnalyticData = { + ...this.additionalAnalyticParams + }; + } else { + pd.ad.d.forEach(key => { + additionalAnalyticData[key] = + this.additionalAnalyticParams[key]; + }); + } + + pd.ad.ct--; + } + } + + return { + apd: performanceData, + aed: warningData.ed, + ald: warningData.ld, + afd: warningData.fd, + abd: businessData, + add: additionalAnalyticData + }; + } + + hasPerformance(id) { + return this.performanceData[id] !== undefined; + } + + resetData() { + this.performanceData = {}; + this.warningData = JSON.parse(JSON.stringify(emptyWarning)); + this.additionalAnalyticParams = {}; + } +} + +export const performanceTracker = new PerformanceTracker(); \ No newline at end of file diff --git a/libraries/intentIqUtils/performanceUtils.js b/libraries/intentIqUtils/performanceUtils.js new file mode 100644 index 00000000000..22d411fb3e7 --- /dev/null +++ b/libraries/intentIqUtils/performanceUtils.js @@ -0,0 +1,169 @@ +import adapterManager from '../../src/adapterManager.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; + +import { + PERFORMANCE_EVENT, + ERROR_CODES, + ErrorType +} from '../intentIqConstants/performanceConstants.js'; + +import { performanceTracker } from './performanceTracker.js'; + +import { appendVrrefAndFui } from './getRefferer.js'; +import { getIiqServerAddress } from './intentIqConfig.js'; + +const pbjs = getGlobal(); + +let missingAnalyticsReported = false; + +export function hasIIQAnalytics(partnerId) { + try { + const adapter = + adapterManager.getAnalyticsAdapter('iiqAnalytics'); + + const enabled = + adapter?.adapter?.enabled === true; + + const globalExists = + !!window[`intentIqAnalyticsAdapter_${partnerId}`]; + + return enabled || globalExists; + } catch (e) { + return false; + } +} + +export function collectPerformanceData({ + configParams, + firstPartyData, + partnerData, + runtimeEids +}) { + const payload = performanceTracker.getFilteredData( + partnerData?.pd, + pbjs?.installedModules || [] + ); + + const hasPayload = Object.values(payload).some(data => { + return ( + data && + typeof data === 'object' && + Object.keys(data).length > 0 + ); + }); + + if (!hasPayload) { + return {}; + } + + return { + pcid: firstPartyData?.pcid, + dpi: configParams.partner, + jsver: configParams.version, + pbjsver: pbjs?.version, + pid: firstPartyData?.pid, + vrref: appendVrrefAndFui( + '', + configParams.domainName + ).replace('&vrref=', ''), + eidLn: runtimeEids?.eids?.length || 0, + dm: partnerData?.dm || [], + ...payload + }; +} + +export function makePerformanceRequest({ + configParams, + firstPartyData, + partnerData, + runtimeEids +}) { + const performanceData = collectPerformanceData({ + configParams, + firstPartyData, + partnerData, + runtimeEids + }); + + if (!Object.keys(performanceData).length) { + return; + } + + const queryParams = new URLSearchParams(); + + Object.keys(performanceData).forEach(key => { + const value = performanceData[key]; + + if ( + value !== undefined && + value !== null + ) { + if (typeof value === 'object') { + queryParams.append( + key, + JSON.stringify(value) + ); + } else { + queryParams.append( + key, + String(value) + ); + } + } + }); + + const endpoint = + partnerData?.ae || + `${getIiqServerAddress(configParams)}/report`; + + const img = new Image(); + + img.src = `${endpoint}?${queryParams.toString()}`; + + performanceTracker.resetData(); +} + +export function scheduleAnalyticsDetection({ + configParams, + firstPartyData, + partnerData, + runtimeEids, + timeout = 5000 +}) { + const detect = () => { + if (missingAnalyticsReported) return; + + const analyticsDetected = + hasIIQAnalytics(configParams.partner); + + if (analyticsDetected) { + performanceTracker.addPerformance( + PERFORMANCE_EVENT.ANALYTICS_ADAPTER_DETECTED + ); + + return; + } + + missingAnalyticsReported = true; + + performanceTracker.addWarning( + ERROR_CODES.ANALYTICS_ADAPTER_MISSING, + ErrorType.ERROR + ); + + makePerformanceRequest({ + configParams, + firstPartyData, + partnerData, + runtimeEids + }); + }; + + setTimeout(detect, timeout); + + window.addEventListener( + 'pagehide', + detect, + { once: true } + ); +} \ No newline at end of file diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index c064719ae88..484efe1701a 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -27,6 +27,15 @@ import { iiqPixelServerAddress, getIiqServerAddress } from '../libraries/intentI import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js'; import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.js'; import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js'; +import { performanceTracker } from '../libraries/intentIqUtils/performanceTracker.js'; +import { ErrorType } from '../libraries/intentIqConstants/performanceConstants.js'; +import { scheduleAnalyticsDetection } from '../libraries/intentIqUtils/performanceUtils.js'; + +import { + PERFORMANCE_EVENT, + ERROR_CODES, + ADDITIONAL_ANALYTIC_PARAMS +} from '../libraries/intentIqConstants/performanceConstants.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -56,6 +65,7 @@ let PARTNER_DATA_KEY; let callCount = 0; let failCount = 0; let noDataCount = 0; +performanceTracker.addPerformance(PERFORMANCE_EVENT.INIT_COUNTERS); export let firstPartyData; let partnerData; @@ -150,6 +160,7 @@ export function initializeGlobalIIQ(partnerId) { if (!globalName || !window[globalName]) { globalName = `iiq_identity_${partnerId}` window[globalName] = {} + performanceTracker.addPerformance(PERFORMANCE_EVENT.SET_IIQ_OBJECT); return true } return false @@ -183,6 +194,7 @@ function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) if (firstPartyData.isOptedOut) { const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL if (newUser || needToDoSync) { + performanceTracker.addPerformance(PERFORMANCE_EVENT.SYNC_REQUEST); ajax(url, () => { }, undefined, { method: 'GET', withCredentials: true }); if (firstPartyData?.date) { @@ -192,6 +204,7 @@ function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) } } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) { storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage); + performanceTracker.addPerformance(PERFORMANCE_EVENT.SYNC_REQUEST); ajax(url, () => { }, undefined, { method: 'GET', withCredentials: true }); } @@ -302,14 +315,21 @@ export const intentIqIdSubmodule = { * @returns {IdResponse|undefined} */ getId(config) { + if (!config || !config.params) performanceTracker.addWarning(ERROR_CODES.CONFIG_OBJECT_NOT_PROVIDED_CORRECTLY, ErrorType.FATAL); const configParams = (config?.params) || {}; + performanceTracker.addPerformance(PERFORMANCE_EVENT.SET_USER_CONFIG); + + let responseReceived = false; + let requestSent = false; const firePartnerCallback = () => { + if (callbackFired) performanceTracker.addWarning(ERROR_CODES.CALLBACK_TRIGGERED_FEW_TIMES, ErrorType.ERROR); if (configParams.callback && !callbackFired) { callbackFired = true; if (callbackTimeoutID) clearTimeout(callbackTimeoutID); let data = runtimeEids; if (data?.eids?.length === 1 && typeof data.eids[0] === 'string') data = data.eids[0]; + performanceTracker.addPerformance(PERFORMANCE_EVENT.CALLBACK_FIRE); configParams.callback(data); } updateGlobalObj() @@ -317,17 +337,26 @@ export const intentIqIdSubmodule = { if (typeof configParams.partner !== 'number') { logError('User ID - intentIqId submodule requires a valid partner to be defined'); - firePartnerCallback() + firePartnerCallback(); + performanceTracker.addWarning(ERROR_CODES.PARTNER_ID_NOT_PROVIDED); return; } - initializeGlobalIIQ(configParams.partner) + initializeGlobalIIQ(configParams.partner); + scheduleAnalyticsDetection({ + configParams, + firstPartyData, + partnerData, + runtimeEids, + timeout: 5000 + }); let decryptedData, callbackTimeoutID; let callbackFired = false; let runtimeEids = { eids: [] }; const gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined; + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.GAM_OBJECT_PRESENT, gamObjectReference ? 1 : 0); const gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group'; const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined; const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false; @@ -336,18 +365,44 @@ export const intentIqIdSubmodule = { const additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined; const chTimeout = Number(configParams?.chTimeout) >= 0 ? Number(configParams.chTimeout) : 10; PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`; + performanceTracker.addPerformance(PERFORMANCE_EVENT.SET_DEFAULT_VALUES); const allowedStorage = defineStorageType(config.enabledStorageTypes); + performanceTracker.addPerformance(PERFORMANCE_EVENT.INIT_STORAGE); + + try { + performanceTracker.addAdditionalAnalyticParam( + ADDITIONAL_ANALYTIC_PARAMS.HAVE_LS_ACCESS, + !!window.localStorage ? 1 : 0 + ); + } catch (e) { + performanceTracker.addAdditionalAnalyticParam( + ADDITIONAL_ANALYTIC_PARAMS.HAVE_LS_ACCESS, + 0 + ); + } + partnerData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage)) || {}; + performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_PARTNER_DATA_FROM_LS); + if (partnerData?.date) { + performanceTracker.addPerformance(PERFORMANCE_EVENT.TIME_SINCE_LAST_VR_REQUEST, Date.now() - partnerData.date); + } let rrttStrtTime = 0; let shouldCallServer = false; FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`; const cmpData = getCmpData(); + performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_TCF); + performanceTracker.addPerformance(PERFORMANCE_EVENT.RECEIVED_TCF); + if (cmpData.gdprString) performanceTracker.addPerformance(PERFORMANCE_EVENT.PROVIDED_GDPR); + if (cmpData.uspString) performanceTracker.addPerformance(PERFORMANCE_EVENT.PROVIDED_USP); const gdprDetected = cmpData.gdprString; firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage)); + performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_FIRST_PARTY_DATA); actualABGroup = defineABTestingGroup(configParams, partnerData?.terminationCause); + performanceTracker.addPerformance(PERFORMANCE_EVENT.GROUP_DEFINING); const currentBrowserLowerCase = detectBrowser(); + performanceTracker.addPerformance(PERFORMANCE_EVENT.BROWSER_DETECTED); const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : ''; const isBlacklisted = browserBlackList?.includes(currentBrowserLowerCase); let newUser = false; @@ -357,12 +412,14 @@ export const intentIqIdSubmodule = { if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); callbackTimeoutID = setTimeout(() => { + performanceTracker.addWarning(ERROR_CODES.VR_REQUEST_TIMEOUT, ErrorType.ERROR); firePartnerCallback(); }, configParams.timeoutInMillis || 500 ); if (!firstPartyData?.pcid) { const firstPartyId = generateGUID(); + performanceTracker.addPerformance(PERFORMANCE_EVENT.GENERATED_NEW_PCID); firstPartyData = { pcid: firstPartyId, pcidDate: Date.now(), @@ -371,6 +428,7 @@ export const intentIqIdSubmodule = { gdprString: EMPTY, date: Date.now() }; + performanceTracker.addPerformance(PERFORMANCE_EVENT.DEFINE_FIRST_PARTY_DATA); newUser = true; storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); } else if (!firstPartyData.pcidDate) { @@ -384,12 +442,15 @@ export const intentIqIdSubmodule = { // Read client hints from storage clientHints = readData(CLIENT_HINTS_KEY, allowedStorage); + performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_CLIENT_HINTS_FROM_LS); const chSupported = isCHSupported(); let chPromise = null; function fetchAndHandleCH() { + performanceTracker.addPerformance(PERFORMANCE_EVENT.START_GET_CLIENT_HINTS); return navigator.userAgentData.getHighEntropyValues(CH_KEYS) .then(raw => { + performanceTracker.addPerformance(PERFORMANCE_EVENT.RECEIVED_CLIENT_HINTS); const nextCH = handleClientHints(raw) || ''; const prevCH = clientHints || ''; if (nextCH !== prevCH) { @@ -433,8 +494,11 @@ export const intentIqIdSubmodule = { if (partnerData.data) { if (partnerData.data.length) { // encrypted data + performanceTracker.addPerformance(PERFORMANCE_EVENT.START_READ_LS_ENCODED_DATA); decryptedData = tryParse(decryptData(partnerData.data)); + performanceTracker.addPerformance(PERFORMANCE_EVENT.END_READ_LS_ENCODED_DATA); runtimeEids = decryptedData; + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.HAVE_EIDS_IN_LS, runtimeEids?.eids?.length ? 1 : 0); } } @@ -481,6 +545,7 @@ export const intentIqIdSubmodule = { // Check if current browser is in blacklist if (isBlacklisted) { logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.'); + performanceTracker.addPerformance(PERFORMANCE_EVENT.BROWSER_BLACKLIST_DETECTED); if (configParams.callback) configParams.callback(''); if (chSupported) { @@ -505,6 +570,7 @@ export const intentIqIdSubmodule = { updateGlobalObj() // update global object before server request, to make sure analytical adapter will have it even if the server is "not in time" // use protocol relative urls for http or https + performanceTracker.addPerformance(PERFORMANCE_EVENT.CREATE_REQUEST_URL); let url = `${getIiqServerAddress(configParams)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; url = appendFirstPartyData(url, firstPartyData, partnerData); @@ -535,8 +601,16 @@ export const intentIqIdSubmodule = { const resp = function (callback) { const callbacks = { success: response => { + if (responseReceived) { + performanceTracker.addWarning(ERROR_CODES.MULTIPLE_VR_RESPONSES, ErrorType.ERROR); + } + responseReceived = true; + performanceTracker.addPerformance(PERFORMANCE_EVENT.VR_RESPONSE_RECEIVED); + if (rrttStrtTime && rrttStrtTime > 0) { partnerData.rrtt = Date.now() - rrttStrtTime; + performanceTracker.addPerformance( + PERFORMANCE_EVENT.TIMEOUT_VS_VR_RESPONSE, partnerData.rrtt); } const respJson = tryParse(response); // If response is a valid json and should save is true @@ -557,6 +631,7 @@ export const intentIqIdSubmodule = { if ('tc' in respJson) { partnerData.terminationCause = respJson.tc; actualABGroup = defineABTestingGroup(configParams, respJson.tc,); + performanceTracker.addPerformance(PERFORMANCE_EVENT.DEFINE_GROUP_BASED_ON_SERVER_RESPONSE); if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); @@ -643,11 +718,13 @@ export const intentIqIdSubmodule = { updateCountersAndStore(runtimeEids, allowedStorage, partnerData); storeFirstPartyData(); } else { + performanceTracker.addWarning(ERROR_CODES.VR_BAD_RESPONSE, ErrorType.ERROR); callback(runtimeEids); firePartnerCallback() } }, error: error => { + performanceTracker.addWarning(ERROR_CODES.VR_BAD_RESPONSE, ErrorType.ERROR); logError(MODULE_NAME + ': ID fetch encountered an error', error); failCount++; updateCountersAndStore(runtimeEids, allowedStorage, partnerData); @@ -655,6 +732,8 @@ export const intentIqIdSubmodule = { } }; + if (requestSent) performanceTracker.addWarning(ERROR_CODES.MULTIPLE_VR_REQUESTS, ErrorType.ERROR); + requestSent = true; partnerData.wsrvcll = true; storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData); clearCountersAndStore(allowedStorage, partnerData); @@ -663,6 +742,7 @@ export const intentIqIdSubmodule = { const sendAjax = uh => { if (uh) url += '&uh=' + encodeURIComponent(uh); + performanceTracker.addPerformance(PERFORMANCE_EVENT.MAKE_VR_REQUEST); ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); } From 64caa64f80bfa0ece8a7e923244d09e458a298b8 Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Sun, 10 May 2026 15:57:42 +0200 Subject: [PATCH 2/4] add all storage metrics --- .../intentIqConstants/performanceConstants.js | 71 ++++--- libraries/intentIqUtils/performanceTracker.js | 183 ++++++------------ libraries/intentIqUtils/performanceUtils.js | 137 +++---------- modules/intentIqIdSystem.js | 145 +++++++------- 4 files changed, 203 insertions(+), 333 deletions(-) diff --git a/libraries/intentIqConstants/performanceConstants.js b/libraries/intentIqConstants/performanceConstants.js index 15f0d41fbe9..3b143cff152 100644 --- a/libraries/intentIqConstants/performanceConstants.js +++ b/libraries/intentIqConstants/performanceConstants.js @@ -2,55 +2,38 @@ export const PERFORMANCE_EVENT = { SET_IIQ_OBJECT: 1, SET_USER_CONFIG: 2, BROWSER_BLACKLIST_DETECTED: 3, - SET_DEFAULT_VALUES: 6, INIT_COUNTERS: 7, INIT_STORAGE: 8, - READ_FIRST_PARTY_DATA: 11, GENERATED_NEW_PCID: 12, DEFINE_FIRST_PARTY_DATA: 13, READ_CLIENT_HINTS_FROM_LS: 14, - READ_TCF: 16, RECEIVED_TCF: 17, - CREATE_REQUEST_URL: 20, MAKE_VR_REQUEST: 21, - READ_USP_START: 25, READ_USP_END: 26, - START_GET_CLIENT_HINTS: 27, RECEIVED_CLIENT_HINTS: 28, - GROUP_DEFINING: 29, - VR_RESPONSE_RECEIVED: 38, CALLBACK_FIRE: 39, READ_PARTNER_DATA_FROM_LS: 40, - REFRESH_USER_IDS_START: 41, USER_IDS_SET_IN_PREBID: 42, - BROWSER_DETECTED: 43, - DEFINE_GROUP_FROM_SERVER: 45, - START_READ_LS_ENCODED_DATA: 46, END_READ_LS_ENCODED_DATA: 47, - GET_DEVICE_INFO: 48, SYNC_REQUEST: 49, - PROVIDED_GDPR: 51, PROVIDED_USP: 53, - TIMEOUT_VS_VR_RESPONSE: 56, TIME_SINCE_LAST_VR_REQUEST: 57, - ANALYTICS_ADAPTER_DETECTED: 61, - VR_RESPONSE_FINISHED: 62, DEFINE_GROUP_BASED_ON_SERVER_RESPONSE: 63, GAM_TARGETING_SET: 64, @@ -75,19 +58,14 @@ export const PERFORMANCE_EVENT = { export const ERROR_CODES = { ALL: -1, - VR_BAD_RESPONSE: 0, VR_REQUEST_TIMEOUT: 1, - MULTIPLE_VR_REQUESTS: 6, MULTIPLE_VR_RESPONSES: 7, CALLBACK_TRIGGERED_FEW_TIMES: 8, - CONFIG_OBJECT_INVALID: 9, PARTNER_ID_NOT_PROVIDED: 10, - ANALYTICS_ADAPTER_MISSING: 11, - CONFIG_OBJECT_NOT_PROVIDED_CORRECTLY: 12, CH_FETCH_FAILED: 13, GAM_TARGETING_FAILED: 14, @@ -103,12 +81,10 @@ export const ERROR_CODES = { export const WARNING_CODES = { ALL: -1, - CMP_TRIGGERED_FEW_TIMES: 0, MULTIPLE_AGENTS_SAME_ID: 1, AUCTION_EIDS_DISCREPANCY: 2, AUCTION_STARTED_BEFORE_AGENT: 3, - CALLBACK_TIMEOUT_REACHED: 4, EMPTY_EIDS_RESPONSE: 5, USING_LS_EIDS: 6, @@ -118,16 +94,13 @@ export const WARNING_CODES = { export const ADDITIONAL_ANALYTIC_PARAMS = { ALL: -1, - RUNNING_IN_TOP: 0, LS_ACCESS: 1, GAM_PRESENT: 2, HAVE_EIDS_IN_LS: 3, - AUCTION_EIDS_DIFF: 4, AUCTION_TO_BIDWON_PERCENTAGE: 5, BIDWON_MINUS_IMPRESSIONS: 6, - CALLBACK_WITH_EIDS: 7, CH_SUPPORTED: 8, GDPR_APPLIES: 9, @@ -146,4 +119,48 @@ export const ErrorType = { ERROR: 'ed', WARNING: 'ld', FATAL: 'fd' +}; + +export const emptyWarning = { + [ErrorType.ERROR]: {}, + [ErrorType.WARNING]: {}, + [ErrorType.FATAL]: {} +}; + +export const edErrors = Object.values(ERROR_CODES).filter(id => id !== ERROR_CODES.ALL); +export const ldErrors = Object.values(WARNING_CODES).filter(id => id !== WARNING_CODES.ALL); +export const fdErrors = edErrors; + +export const ErrorsMap = { + [ErrorType.ERROR]: edErrors, + [ErrorType.WARNING]: ldErrors, + [ErrorType.FATAL]: fdErrors +}; + +export const multipleErrors = [ + { + id: PERFORMANCE_EVENT.MAKE_VR_REQUEST, + warningCode: ERROR_CODES.MULTIPLE_VR_REQUESTS, + errorType: ErrorType.ERROR + }, + { + id: PERFORMANCE_EVENT.VR_RESPONSE_RECEIVED, + warningCode: ERROR_CODES.MULTIPLE_VR_RESPONSES, + errorType: ErrorType.ERROR + }, + { + id: PERFORMANCE_EVENT.CALLBACK_FIRE, + warningCode: ERROR_CODES.CALLBACK_TRIGGERED_FEW_TIMES, + errorType: ErrorType.ERROR + } +]; + +export const businessAdapters = { + 0: 'liveIntentAnalyticsAdapter', + 1: 'euidIdSystem', + 2: 'id5AnalyticsAdapter', + 3: 'lotamePanoramaIdSystem', + 4: 'identityLinkIdSystem', + 5: 'merkleIdSystem', + 6: 'fabrickIdSystem' }; \ No newline at end of file diff --git a/libraries/intentIqUtils/performanceTracker.js b/libraries/intentIqUtils/performanceTracker.js index 03efa34c4f9..699f2d57f95 100644 --- a/libraries/intentIqUtils/performanceTracker.js +++ b/libraries/intentIqUtils/performanceTracker.js @@ -1,5 +1,5 @@ import { - AAP, + ADDITIONAL_ANALYTIC_PARAMS as AAP, businessAdapters, emptyWarning, ErrorsMap, @@ -16,169 +16,98 @@ class PerformanceTracker { } addPerformance(id, value) { - let newPerformance; - - if (typeof value === 'number') { - newPerformance = value; - } else { - switch (id) { - case 0: - newPerformance = this.performanceStart; - break; - case 60: - newPerformance = performance.now(); - break; - default: - newPerformance = performance.now() - this.performanceStart; - break; - } - } + const raw = typeof value === 'number' + ? value + : id === 0 + ? this.performanceStart + : id === 60 + ? performance.now() + : performance.now() - this.performanceStart; this.performanceData[id] = this.performanceData[id] || []; - this.performanceData[id].push( - parseFloat(newPerformance.toFixed(1)) - ); + this.performanceData[id].push(parseFloat(raw.toFixed(1))); } addAdditionalAnalyticParam(id, value) { - if ( - id === AAP.eidsDiff || - id === AAP.auctionToBidWonPercentage || - id === AAP.bidWonImpressionsDiff - ) { - this.additionalAnalyticParams[id] = value; - return; - } + const rawIds = [ + AAP.AUCTION_EIDS_DIFF, + AAP.AUCTION_TO_BIDWON_PERCENTAGE, + AAP.BIDWON_MINUS_IMPRESSIONS + ]; - this.additionalAnalyticParams[id] = value ? 1 : 0; + this.additionalAnalyticParams[id] = rawIds.includes(id) ? value : value ? 1 : 0; } addWarning(id, errorType) { - if (ErrorsMap[errorType]?.includes(id)) { - this.warningData[errorType][id] = - (this.warningData[errorType][id] || 0) + 1; - } + if (!ErrorsMap[errorType]?.includes(id)) return; + this.warningData[errorType][id] = (this.warningData[errorType][id] || 0) + 1; } - checkWarnings(config) { + checkWarnings(config = []) { config.forEach(({ id, warningCode, errorType }) => { - if (this.performanceData[id]?.length > 1) { - this.addWarning(warningCode, errorType); - } + if (this.performanceData[id]?.length > 1) this.addWarning(warningCode, errorType); }); } - filterWarningData(data, ids) { - const filteredData = {}; - - for (const id of ids) { - if (data[id]) { - filteredData[id] = data[id]; - } - } - - return filteredData; + filterWarningData(data, ids = []) { + return ids.reduce((acc, id) => { + if (data[id]) acc[id] = data[id]; + return acc; + }, {}); } - updatePerformanceData(ids) { - if (ids.some(el => el === -1)) { - return Object.keys(this.performanceData).reduce((acc, key) => { - acc[Number(key)] = this.performanceData[Number(key)][0]; - return acc; - }, {}); - } - + updatePerformanceData(ids = []) { + if (ids.includes(-1)) return { ...this.performanceData }; return ids.reduce((acc, id) => { - if (this.performanceData[id]) { - acc[id] = this.performanceData[id][0]; - } + if (this.performanceData[id]) acc[id] = this.performanceData[id]; return acc; }, {}); } - getFilteredData(pd, installedModules = []) { - let performanceData = {}; + getFilteredData(pd, installedModules = [], force = false) { + this.checkWarnings(multipleErrors); + + const performanceData = force ? { ...this.performanceData } : {}; const warningData = JSON.parse(JSON.stringify(emptyWarning)); const businessData = {}; let additionalAnalyticData = {}; - this.checkWarnings(multipleErrors); - - if (pd?.p?.ct && pd?.p?.d?.length) { - performanceData = this.updatePerformanceData(pd.p.d); + if (!force && pd?.p?.ct && pd?.p?.d?.length) { + Object.assign(performanceData, this.updatePerformanceData(pd.p.d)); pd.p.ct--; } - if (pd?.w?.ct) { - const keys = Object.values(ErrorType); - - keys.forEach(key => { - const data = pd?.w?.[key]; - - if ( - data && - data.length && - data.some(el => el === -1) - ) { - warningData[key] = this.warningData[key]; - } else { - warningData[key] = this.filterWarningData( - this.warningData[key], - data || [] - ); - } + if (force) { + Object.assign(warningData.ed, this.warningData.ed); + Object.assign(warningData.ld, this.warningData.ld); + Object.assign(warningData.fd, this.warningData.fd); + additionalAnalyticData = { ...this.additionalAnalyticParams }; + } else if (pd?.w?.ct) { + Object.values(ErrorType).forEach(key => { + const ids = pd?.w?.[key] || []; + warningData[key] = ids.includes(-1) ? this.warningData[key] : this.filterWarningData(this.warningData[key], ids); }); - if ( - Object.values(warningData).some( - data => Object.keys(data).length > 0 - ) - ) { - pd.w.ct--; - } + if (Object.values(warningData).some(data => Object.keys(data).length)) pd.w.ct--; } if (pd?.b?.ct && pd?.b?.d?.length) { - if (pd.b.d.some(el => el === -1)) { - Object.entries(businessAdapters).forEach(([key, adapter]) => { - const index = Number(key); - - businessData[index] = - installedModules.includes(adapter) ? 1 : 0; - }); - } else { - pd.b.d.forEach(el => { - if (businessAdapters[el]) { - businessData[el] = - installedModules.includes( - businessAdapters[el] - ) - ? 1 - : 0; - } - }); - } - + const ids = pd.b.d.includes(-1) ? Object.keys(businessAdapters).map(Number) : pd.b.d; + ids.forEach(id => { + if (businessAdapters[id]) businessData[id] = installedModules.includes(businessAdapters[id]) ? 1 : 0; + }); pd.b.ct--; } - if (pd?.ad?.ct && pd?.ad?.d?.length) { - const addParamKeys = Object.keys( - this.additionalAnalyticParams - ); - - if (addParamKeys.length) { - if (pd.ad.d.some(item => item === -1)) { - additionalAnalyticData = { - ...this.additionalAnalyticParams - }; - } else { - pd.ad.d.forEach(key => { - additionalAnalyticData[key] = - this.additionalAnalyticParams[key]; - }); - } - + if (!force && pd?.ad?.ct && pd?.ad?.d?.length) { + const keys = Object.keys(this.additionalAnalyticParams); + if (keys.length) { + additionalAnalyticData = pd.ad.d.includes(-1) + ? { ...this.additionalAnalyticParams } + : pd.ad.d.reduce((acc, key) => { + acc[key] = this.additionalAnalyticParams[key]; + return acc; + }, {}); pd.ad.ct--; } } diff --git a/libraries/intentIqUtils/performanceUtils.js b/libraries/intentIqUtils/performanceUtils.js index 22d411fb3e7..cdcbdd2dfee 100644 --- a/libraries/intentIqUtils/performanceUtils.js +++ b/libraries/intentIqUtils/performanceUtils.js @@ -4,13 +4,14 @@ import { getGlobal } from '../../src/prebidGlobal.js'; import { PERFORMANCE_EVENT, ERROR_CODES, - ErrorType + ErrorType, + ADDITIONAL_ANALYTIC_PARAMS } from '../intentIqConstants/performanceConstants.js'; import { performanceTracker } from './performanceTracker.js'; - import { appendVrrefAndFui } from './getRefferer.js'; import { getIiqServerAddress } from './intentIqConfig.js'; +import { VERSION } from '../intentIqConstants/intentIqConstants.js'; const pbjs = getGlobal(); @@ -18,152 +19,70 @@ let missingAnalyticsReported = false; export function hasIIQAnalytics(partnerId) { try { - const adapter = - adapterManager.getAnalyticsAdapter('iiqAnalytics'); - - const enabled = - adapter?.adapter?.enabled === true; - - const globalExists = - !!window[`intentIqAnalyticsAdapter_${partnerId}`]; - + const wrapper = adapterManager.getAnalyticsAdapter('iiqAnalytics'); + const enabled = wrapper?.adapter?.enabled === true; + const globalExists = !!window[`intentIqAnalyticsAdapter_${partnerId}`]; return enabled || globalExists; } catch (e) { return false; } } -export function collectPerformanceData({ - configParams, - firstPartyData, - partnerData, - runtimeEids -}) { - const payload = performanceTracker.getFilteredData( - partnerData?.pd, - pbjs?.installedModules || [] - ); - - const hasPayload = Object.values(payload).some(data => { - return ( - data && - typeof data === 'object' && - Object.keys(data).length > 0 - ); - }); +export function collectPerformanceData({ configParams, firstPartyData, partnerData, runtimeEids, force = false }) { + const payload = performanceTracker.getFilteredData(partnerData?.pd, pbjs?.installedModules || [], force); + const hasPayload = Object.values(payload).some(data => data && typeof data === 'object' && Object.keys(data).length); - if (!hasPayload) { - return {}; - } + if (!hasPayload) return {}; return { pcid: firstPartyData?.pcid, dpi: configParams.partner, - jsver: configParams.version, + jsver: configParams.version || VERSION, pbjsver: pbjs?.version, pid: firstPartyData?.pid, - vrref: appendVrrefAndFui( - '', - configParams.domainName - ).replace('&vrref=', ''), + vrref: appendVrrefAndFui('', configParams.domainName).replace('&vrref=', ''), eidLn: runtimeEids?.eids?.length || 0, dm: partnerData?.dm || [], ...payload }; } -export function makePerformanceRequest({ - configParams, - firstPartyData, - partnerData, - runtimeEids -}) { - const performanceData = collectPerformanceData({ - configParams, - firstPartyData, - partnerData, - runtimeEids - }); - - if (!Object.keys(performanceData).length) { - return; - } +export function makePerformanceRequest({ configParams, firstPartyData, partnerData, runtimeEids, force = false }) { + const performanceData = collectPerformanceData({ configParams, firstPartyData, partnerData, runtimeEids, force }); + if (!Object.keys(performanceData).length) return false; const queryParams = new URLSearchParams(); - Object.keys(performanceData).forEach(key => { - const value = performanceData[key]; - - if ( - value !== undefined && - value !== null - ) { - if (typeof value === 'object') { - queryParams.append( - key, - JSON.stringify(value) - ); - } else { - queryParams.append( - key, - String(value) - ); - } - } + Object.entries(performanceData).forEach(([key, value]) => { + if (value === undefined || value === null) return; + queryParams.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value)); }); - const endpoint = - partnerData?.ae || - `${getIiqServerAddress(configParams)}/report`; - - const img = new Image(); - - img.src = `${endpoint}?${queryParams.toString()}`; + const endpoint = partnerData?.ae || `${getIiqServerAddress(configParams)}/report`; + new Image().src = `${endpoint}?${queryParams.toString()}`; + performanceTracker.addPerformance(PERFORMANCE_EVENT.ANALYTICS_REPORT_SENT); performanceTracker.resetData(); + return true; } -export function scheduleAnalyticsDetection({ - configParams, - firstPartyData, - partnerData, - runtimeEids, - timeout = 5000 -}) { +export function scheduleAnalyticsDetection({ configParams, firstPartyData, partnerData, runtimeEids, timeout = 5000 }) { const detect = () => { if (missingAnalyticsReported) return; - const analyticsDetected = - hasIIQAnalytics(configParams.partner); + const analyticsDetected = hasIIQAnalytics(configParams.partner); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.ANALYTICS_ENABLED, analyticsDetected); if (analyticsDetected) { - performanceTracker.addPerformance( - PERFORMANCE_EVENT.ANALYTICS_ADAPTER_DETECTED - ); - + performanceTracker.addPerformance(PERFORMANCE_EVENT.ANALYTICS_ADAPTER_DETECTED); return; } missingAnalyticsReported = true; + performanceTracker.addWarning(ERROR_CODES.ANALYTICS_ADAPTER_MISSING, ErrorType.ERROR); - performanceTracker.addWarning( - ERROR_CODES.ANALYTICS_ADAPTER_MISSING, - ErrorType.ERROR - ); - - makePerformanceRequest({ - configParams, - firstPartyData, - partnerData, - runtimeEids - }); + makePerformanceRequest({ configParams, firstPartyData, partnerData, runtimeEids, force: true }); }; setTimeout(detect, timeout); - - window.addEventListener( - 'pagehide', - detect, - { once: true } - ); } \ No newline at end of file diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 484efe1701a..dd7b6edd329 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -13,7 +13,7 @@ import { appendSPData } from '../libraries/intentIqUtils/urlUtils.js'; import { isCHSupported } from '../libraries/intentIqUtils/chUtils.js' import { appendVrrefAndFui } from '../libraries/intentIqUtils/getRefferer.js'; import { getCmpData } from '../libraries/intentIqUtils/getCmpData.js'; -import { readData, storeData, defineStorageType, removeDataByKey, tryParse } from '../libraries/intentIqUtils/storageUtils.js'; +import { readData, storeData, defineStorageType, removeDataByKey, tryParse, storage } from '../libraries/intentIqUtils/storageUtils.js'; import { FIRST_PARTY_KEY, CLIENT_HINTS_KEY, @@ -28,14 +28,8 @@ import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditio import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.js'; import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js'; import { performanceTracker } from '../libraries/intentIqUtils/performanceTracker.js'; -import { ErrorType } from '../libraries/intentIqConstants/performanceConstants.js'; import { scheduleAnalyticsDetection } from '../libraries/intentIqUtils/performanceUtils.js'; - -import { - PERFORMANCE_EVENT, - ERROR_CODES, - ADDITIONAL_ANALYTIC_PARAMS -} from '../libraries/intentIqConstants/performanceConstants.js'; +import { ErrorType, PERFORMANCE_EVENT, ERROR_CODES, WARNING_CODES, ADDITIONAL_ANALYTIC_PARAMS } from '../libraries/intentIqConstants/performanceConstants.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -65,7 +59,6 @@ let PARTNER_DATA_KEY; let callCount = 0; let failCount = 0; let noDataCount = 0; -performanceTracker.addPerformance(PERFORMANCE_EVENT.INIT_COUNTERS); export let firstPartyData; let partnerData; @@ -219,19 +212,21 @@ function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) */ export function setGamReporting(gamObjectReference, gamParameterName, userGroup, isBlacklisted = false) { if (isBlacklisted) return; - if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) { - gamObjectReference.cmd.push(() => { - if (typeof gamObjectReference.setConfig === 'function') { - gamObjectReference.setConfig({ - targeting: { - [gamParameterName]: userGroup - } - }); - return; - } - // Fallback in case an older version of Google Publisher Tag is used. - gamObjectReference?.pubads?.()?.setTargeting?.(gamParameterName, userGroup); - }); + + try { + if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) { + gamObjectReference.cmd.push(() => { + if (typeof gamObjectReference.setConfig === 'function') { + gamObjectReference.setConfig({ targeting: { [gamParameterName]: userGroup } }); + } else { + // Fallback in case an older version of Google Publisher Tag is used. + gamObjectReference?.pubads?.()?.setTargeting?.(gamParameterName, userGroup); + } + performanceTracker.addPerformance(PERFORMANCE_EVENT.GAM_TARGETING_SET); + }); + } + } catch (e) { + performanceTracker.addWarning(ERROR_CODES.GAM_TARGETING_FAILED, ErrorType.ERROR); } } @@ -292,13 +287,13 @@ function storeCounters(storage, partnerData) { /** @type {Submodule} */ export const intentIqIdSubmodule = { - /** + /** * used to link submodule with config * @type {string} */ name: MODULE_NAME, gvlid: GVLID, - /** + /** * decode the stored id value for passing to bid requests * @function * @param {{string}} value @@ -308,7 +303,7 @@ export const intentIqIdSubmodule = { return value && INVALID_ID !== value ? { 'intentIqId': value } : undefined; }, - /** + /** * performs action to obtain id and return a value in the callback's response argument * @function * @param {SubmoduleConfig} [config] @@ -319,6 +314,10 @@ export const intentIqIdSubmodule = { const configParams = (config?.params) || {}; performanceTracker.addPerformance(PERFORMANCE_EVENT.SET_USER_CONFIG); + let decryptedData; + let callbackTimeoutID; + let callbackFired = false; + let runtimeEids = { eids: [] }; let responseReceived = false; let requestSent = false; @@ -330,6 +329,7 @@ export const intentIqIdSubmodule = { let data = runtimeEids; if (data?.eids?.length === 1 && typeof data.eids[0] === 'string') data = data.eids[0]; performanceTracker.addPerformance(PERFORMANCE_EVENT.CALLBACK_FIRE); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.CALLBACK_WITH_EIDS, !!runtimeEids?.eids?.length); configParams.callback(data); } updateGlobalObj() @@ -337,85 +337,69 @@ export const intentIqIdSubmodule = { if (typeof configParams.partner !== 'number') { logError('User ID - intentIqId submodule requires a valid partner to be defined'); + performanceTracker.addWarning(ERROR_CODES.PARTNER_ID_NOT_PROVIDED, ErrorType.FATAL); firePartnerCallback(); - performanceTracker.addWarning(ERROR_CODES.PARTNER_ID_NOT_PROVIDED); return; } initializeGlobalIIQ(configParams.partner); - scheduleAnalyticsDetection({ - configParams, - firstPartyData, - partnerData, - runtimeEids, - timeout: 5000 - }); - - let decryptedData, callbackTimeoutID; - let callbackFired = false; - let runtimeEids = { eids: [] }; const gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined; - performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.GAM_OBJECT_PRESENT, gamObjectReference ? 1 : 0); - const gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group'; + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.GAM_OBJECT_PRESENT, !!gamObjectReference); + const gamParameterName = configParams.gamParameterName || 'intent_iq_group'; const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined; const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false; sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData) : ''; sourceMetaDataExternal = isNumber(configParams.sourceMetaDataExternal) ? configParams.sourceMetaDataExternal : undefined; - const additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined; + const additionalParams = configParams.additionalParams || undefined; const chTimeout = Number(configParams?.chTimeout) >= 0 ? Number(configParams.chTimeout) : 10; PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`; performanceTracker.addPerformance(PERFORMANCE_EVENT.SET_DEFAULT_VALUES); const allowedStorage = defineStorageType(config.enabledStorageTypes); performanceTracker.addPerformance(PERFORMANCE_EVENT.INIT_STORAGE); - - try { - performanceTracker.addAdditionalAnalyticParam( - ADDITIONAL_ANALYTIC_PARAMS.HAVE_LS_ACCESS, - !!window.localStorage ? 1 : 0 - ); - } catch (e) { - performanceTracker.addAdditionalAnalyticParam( - ADDITIONAL_ANALYTIC_PARAMS.HAVE_LS_ACCESS, - 0 - ); - } + performanceTracker.addAdditionalAnalyticParam( + ADDITIONAL_ANALYTIC_PARAMS.LS_ACCESS, + storage.hasLocalStorage() && allowedStorage.includes('html5') + ); partnerData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage)) || {}; performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_PARTNER_DATA_FROM_LS); - if (partnerData?.date) { - performanceTracker.addPerformance(PERFORMANCE_EVENT.TIME_SINCE_LAST_VR_REQUEST, Date.now() - partnerData.date); - } + if (partnerData?.date) performanceTracker.addPerformance(PERFORMANCE_EVENT.TIME_SINCE_LAST_VR_REQUEST, Date.now() - partnerData.date); let rrttStrtTime = 0; let shouldCallServer = false; FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`; - const cmpData = getCmpData(); performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_TCF); + const cmpData = getCmpData(); performanceTracker.addPerformance(PERFORMANCE_EVENT.RECEIVED_TCF); if (cmpData.gdprString) performanceTracker.addPerformance(PERFORMANCE_EVENT.PROVIDED_GDPR); if (cmpData.uspString) performanceTracker.addPerformance(PERFORMANCE_EVENT.PROVIDED_USP); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.GDPR_APPLIES, !!cmpData.gdprApplies); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.USP_APPLIES, !!cmpData.uspString); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.GPP_APPLIES, !!cmpData.gppString); + const gdprDetected = cmpData.gdprString; + firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage)); performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_FIRST_PARTY_DATA); actualABGroup = defineABTestingGroup(configParams, partnerData?.terminationCause); performanceTracker.addPerformance(PERFORMANCE_EVENT.GROUP_DEFINING); const currentBrowserLowerCase = detectBrowser(); performanceTracker.addPerformance(PERFORMANCE_EVENT.BROWSER_DETECTED); + performanceTracker.addPerformance(PERFORMANCE_EVENT.GET_DEVICE_INFO); const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : ''; const isBlacklisted = browserBlackList?.includes(currentBrowserLowerCase); let newUser = false; setGamReporting(gamObjectReference, gamParameterName, actualABGroup, isBlacklisted); - if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); - + callbackTimeoutID = setTimeout(() => { - performanceTracker.addWarning(ERROR_CODES.VR_REQUEST_TIMEOUT, ErrorType.ERROR); + performanceTracker.addPerformance(PERFORMANCE_EVENT.VR_CALLBACK_TIMEOUT); + performanceTracker.addWarning(WARNING_CODES.CALLBACK_TIMEOUT_REACHED, ErrorType.WARNING); firePartnerCallback(); - }, configParams.timeoutInMillis || 500 - ); + }, configParams.timeoutInMillis || 500); if (!firstPartyData?.pcid) { const firstPartyId = generateGUID(); @@ -431,23 +415,28 @@ export const intentIqIdSubmodule = { performanceTracker.addPerformance(PERFORMANCE_EVENT.DEFINE_FIRST_PARTY_DATA); newUser = true; storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_FIRST_PARTY_DATA); } else if (!firstPartyData.pcidDate) { firstPartyData.pcidDate = Date.now(); storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_FIRST_PARTY_DATA); } if (gdprDetected && !('isOptedOut' in firstPartyData)) { firstPartyData.isOptedOut = true; } + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.USER_OPTED_OUT, !!firstPartyData.isOptedOut); // Read client hints from storage clientHints = readData(CLIENT_HINTS_KEY, allowedStorage); performanceTracker.addPerformance(PERFORMANCE_EVENT.READ_CLIENT_HINTS_FROM_LS); const chSupported = isCHSupported(); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.CH_SUPPORTED, !!chSupported); let chPromise = null; function fetchAndHandleCH() { performanceTracker.addPerformance(PERFORMANCE_EVENT.START_GET_CLIENT_HINTS); + return navigator.userAgentData.getHighEntropyValues(CH_KEYS) .then(raw => { performanceTracker.addPerformance(PERFORMANCE_EVENT.RECEIVED_CLIENT_HINTS); @@ -461,6 +450,8 @@ export const intentIqIdSubmodule = { }) .catch(err => { logError('CH fetch failed', err); + performanceTracker.addPerformance(PERFORMANCE_EVENT.CH_FETCH_FAILED); + performanceTracker.addWarning(ERROR_CODES.CH_FETCH_FAILED, ErrorType.ERROR); if (clientHints !== '') { clientHints = ''; removeDataByKey(CLIENT_HINTS_KEY, allowedStorage) @@ -487,9 +478,11 @@ export const intentIqIdSubmodule = { if (typeof partnerData.callCount === 'number') callCount = partnerData.callCount; if (typeof partnerData.failCount === 'number') failCount = partnerData.failCount; if (typeof partnerData.noDataCounter === 'number') noDataCount = partnerData.noDataCounter; + performanceTracker.addPerformance(PERFORMANCE_EVENT.INIT_COUNTERS); if (partnerData.wsrvcll) { partnerData.wsrvcll = false; storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_PARTNER_DATA); } if (partnerData.data) { @@ -521,12 +514,15 @@ export const intentIqIdSubmodule = { shouldCallServer = true; storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_FIRST_PARTY_DATA); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_PARTNER_DATA); } if (!shouldCallServer) { if (!hasPartnerData && !firstPartyData.isOptedOut) { shouldCallServer = true; } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_72; } + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.SERVER_CALLED, !!shouldCallServer); if (firstPartyData.isOptedOut) { partnerData.data = runtimeEids = { eids: [] }; @@ -538,7 +534,9 @@ export const intentIqIdSubmodule = { } function buildAndSendPixel(ch) { + performanceTracker.addPerformance(PERFORMANCE_EVENT.BUILD_SYNC_PIXEL_URL); const url = createPixelUrl(firstPartyData, ch, configParams, partnerData, cmpData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.SEND_SYNC_REQUEST); sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser); } @@ -562,6 +560,8 @@ export const intentIqIdSubmodule = { } if (!shouldCallServer) { + performanceTracker.addPerformance(PERFORMANCE_EVENT.VR_REQUEST_SKIPPED); + performanceTracker.addWarning(WARNING_CODES.VR_SKIPPED, ErrorType.WARNING); firePartnerCallback(); updateCountersAndStore(runtimeEids, allowedStorage, partnerData); return { id: runtimeEids.eids }; @@ -575,6 +575,7 @@ export const intentIqIdSubmodule = { url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; url = appendFirstPartyData(url, firstPartyData, partnerData); url = appendPartnersFirstParty(url, configParams); + url = appendPartnersFirstParty(url, configParams); url += (partnerData.cttl) ? '&cttl=' + encodeURIComponent(partnerData.cttl) : ''; url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : ''; url = appendCMPData(url, cmpData); @@ -596,21 +597,20 @@ export const intentIqIdSubmodule = { partnerData.eidl = runtimeEids?.eids?.length || -1 storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_FIRST_PARTY_DATA); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_PARTNER_DATA); } const resp = function (callback) { const callbacks = { success: response => { - if (responseReceived) { - performanceTracker.addWarning(ERROR_CODES.MULTIPLE_VR_RESPONSES, ErrorType.ERROR); - } + if (responseReceived) performanceTracker.addWarning(ERROR_CODES.MULTIPLE_VR_RESPONSES, ErrorType.ERROR); responseReceived = true; performanceTracker.addPerformance(PERFORMANCE_EVENT.VR_RESPONSE_RECEIVED); if (rrttStrtTime && rrttStrtTime > 0) { partnerData.rrtt = Date.now() - rrttStrtTime; - performanceTracker.addPerformance( - PERFORMANCE_EVENT.TIMEOUT_VS_VR_RESPONSE, partnerData.rrtt); + performanceTracker.addPerformance(PERFORMANCE_EVENT.TIMEOUT_VS_VR_RESPONSE, partnerData.rrtt); } const respJson = tryParse(response); // If response is a valid json and should save is true @@ -637,7 +637,7 @@ export const intentIqIdSubmodule = { if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); } if ('isOptedOut' in respJson) { - if (respJson.isOptedOut !== firstPartyData.isOptedOut) { + if (respJson.isOptedOut !== firstPartyData.isOptedOut) { firstPartyData.isOptedOut = respJson.isOptedOut; } if (respJson.isOptedOut === true) { @@ -651,6 +651,7 @@ export const intentIqIdSubmodule = { keysToRemove.forEach(key => removeDataByKey(key, allowedStorage)); storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_FIRST_PARTY_DATA); firePartnerCallback(); callback(runtimeEids); return @@ -704,9 +705,12 @@ export const intentIqIdSubmodule = { } else { delete partnerData.gpr // remove prediction flag in case server doesn't provide it } + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.RESPONSE_HAD_EIDS, !!respJson.data?.eids); + performanceTracker.addAdditionalAnalyticParam(ADDITIONAL_ANALYTIC_PARAMS.RESPONSE_HAD_PID, !!respJson.pid); if (respJson.data?.eids) { runtimeEids = respJson.data + performanceTracker.addPerformance(PERFORMANCE_EVENT.EIDS_FOUND_IN_RESPONSE); callback(respJson.data.eids); firePartnerCallback() const encryptedData = encryptData(JSON.stringify(respJson.data)); @@ -717,6 +721,7 @@ export const intentIqIdSubmodule = { } updateCountersAndStore(runtimeEids, allowedStorage, partnerData); storeFirstPartyData(); + performanceTracker.addPerformance(PERFORMANCE_EVENT.VR_RESPONSE_FINISHED); } else { performanceTracker.addWarning(ERROR_CODES.VR_BAD_RESPONSE, ErrorType.ERROR); callback(runtimeEids); @@ -732,12 +737,12 @@ export const intentIqIdSubmodule = { } }; - if (requestSent) performanceTracker.addWarning(ERROR_CODES.MULTIPLE_VR_REQUESTS, ErrorType.ERROR); + if (requestSent) performanceTracker.addWarning(ERROR_CODES.MULTIPLE_VR_REQUESTS, ErrorType.ERROR); requestSent = true; partnerData.wsrvcll = true; storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData); + performanceTracker.addPerformance(PERFORMANCE_EVENT.STORE_PARTNER_DATA); clearCountersAndStore(allowedStorage, partnerData); - rrttStrtTime = Date.now(); const sendAjax = uh => { @@ -762,8 +767,8 @@ export const intentIqIdSubmodule = { sendAjax(''); } }; + scheduleAnalyticsDetection({ configParams, firstPartyData, partnerData, runtimeEids, timeout: 5000 }); const respObj = { callback: resp }; - if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids; return respObj }, From ba05a0c03818837e71e8dd747cd721b407bd6bf9 Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Sun, 10 May 2026 15:59:13 +0200 Subject: [PATCH 3/4] fix comments --- modules/intentIqIdSystem.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index dd7b6edd329..5d18b59a0c8 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -287,13 +287,13 @@ function storeCounters(storage, partnerData) { /** @type {Submodule} */ export const intentIqIdSubmodule = { - /** + /** * used to link submodule with config * @type {string} */ name: MODULE_NAME, gvlid: GVLID, - /** + /** * decode the stored id value for passing to bid requests * @function * @param {{string}} value @@ -303,7 +303,7 @@ export const intentIqIdSubmodule = { return value && INVALID_ID !== value ? { 'intentIqId': value } : undefined; }, - /** + /** * performs action to obtain id and return a value in the callback's response argument * @function * @param {SubmoduleConfig} [config] From 1e1a700193bfb0d42feaec40b660555c22a65a4a Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Sun, 5 Jul 2026 13:45:46 +0200 Subject: [PATCH 4/4] add todo --- modules/intentIqIdSystem.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 5d18b59a0c8..62f03fd3b86 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -767,7 +767,8 @@ export const intentIqIdSubmodule = { sendAjax(''); } }; - scheduleAnalyticsDetection({ configParams, firstPartyData, partnerData, runtimeEids, timeout: 5000 }); + scheduleAnalyticsDetection({ configParams, firstPartyData, partnerData, runtimeEids, timeout: 10000 }); + // @todo: add page unload handling to trigger final analytics sending const respObj = { callback: resp }; if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids; return respObj