• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 21359463030

26 Jan 2026 01:31PM UTC coverage: 96.218% (+0.003%) from 96.215%
21359463030

push

github

web-flow
Core: adding ima params to local cache request (#14312)

* Core: adding ima params to local cache request

* retrieving ima params

* usp data handler

41687 of 51254 branches covered (81.33%)

6 of 15 new or added lines in 1 file covered. (40.0%)

53 existing lines in 6 files now uncovered.

208851 of 217061 relevant lines covered (96.22%)

71.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.68
/modules/intentIqIdSystem.js
1
/**
1✔
2
 * This module adds IntentIqId to the User ID module
3
 * The {@link module:modules/userId} module is required
4
 * @module modules/intentIqIdSystem
5
 * @requires module:modules/userId
6
 */
7

8
import { logError, isPlainObject, isStr, isNumber } from '../src/utils.js';
9
import { ajax } from '../src/ajax.js';
10
import { submodule } from '../src/hook.js'
11
import { detectBrowser } from '../libraries/intentIqUtils/detectBrowserUtils.js';
12
import { appendSPData } from '../libraries/intentIqUtils/urlUtils.js';
13
import { isCHSupported } from '../libraries/intentIqUtils/chUtils.js'
14
import { appendVrrefAndFui } from '../libraries/intentIqUtils/getRefferer.js';
15
import { getCmpData } from '../libraries/intentIqUtils/getCmpData.js';
16
import { readData, storeData, defineStorageType, removeDataByKey, tryParse } from '../libraries/intentIqUtils/storageUtils.js';
17
import {
18
  FIRST_PARTY_KEY,
19
  CLIENT_HINTS_KEY,
20
  EMPTY,
21
  GVLID,
22
  VERSION, INVALID_ID, SYNC_REFRESH_MILL, META_DATA_CONSTANT, PREBID,
23
  HOURS_72, CH_KEYS
24
} from '../libraries/intentIqConstants/intentIqConstants.js';
25
import { SYNC_KEY } from '../libraries/intentIqUtils/getSyncKey.js';
26
import { iiqPixelServerAddress, getIiqServerAddress } from '../libraries/intentIqUtils/intentIqConfig.js';
27
import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js';
28
import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.js';
29
import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js';
30

31
/**
32
 * @typedef {import('../modules/userId/index.js').Submodule} Submodule
33
 * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig
34
 * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
35
 */
36

37
const MODULE_NAME = 'intentIqId';
1✔
38

39
const encoderCH = {
1✔
40
  brands: 0,
41
  mobile: 1,
42
  platform: 2,
43
  architecture: 3,
44
  bitness: 4,
45
  model: 5,
46
  platformVersion: 6,
47
  wow64: 7,
48
  fullVersionList: 8
49
};
50
let sourceMetaData;
51
let sourceMetaDataExternal;
52
let globalName = ''
1✔
53

54
let FIRST_PARTY_KEY_FINAL = FIRST_PARTY_KEY;
1✔
55
let PARTNER_DATA_KEY;
56
let callCount = 0;
1✔
57
let failCount = 0;
1✔
58
let noDataCount = 0;
1✔
59

60
export let firstPartyData;
61
let partnerData;
62
let clientHints;
63
let actualABGroup
64

65
/**
66
 * Generate standard UUID string
67
 * @return {string}
68
 */
69
function generateGUID() {
70
  let d = new Date().getTime();
69✔
71
  const guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
69✔
72
    const r = (d + Math.random() * 16) % 16 | 0;
2,139✔
73
    d = Math.floor(d / 16);
2,139✔
74
    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
2,139✔
75
  });
76
  return guid;
69✔
77
}
78

79
function addUniquenessToUrl(url) {
80
  url += '&tsrnd=' + Math.floor(Math.random() * 1000) + '_' + new Date().getTime();
23✔
81
  return url;
23✔
82
}
83

84
function appendFirstPartyData(url, firstPartyData, partnerData) {
85
  url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : '';
76!
86
  url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : '';
76✔
87
  url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : '';
76✔
88
  return url
76✔
89
}
90

91
function verifyIdType(value) {
92
  if (value === 0 || value === 1 || value === 3 || value === 4) return value;
27!
93
  return -1;
×
94
}
95

96
function appendPartnersFirstParty(url, configParams) {
97
  const partnerClientId = typeof configParams.partnerClientId === 'string' ? encodeURIComponent(configParams.partnerClientId) : '';
76✔
98
  const partnerClientIdType = typeof configParams.partnerClientIdType === 'number' ? verifyIdType(configParams.partnerClientIdType) : -1;
76✔
99

100
  if (partnerClientIdType === -1) return url;
76✔
101
  if (partnerClientId !== '') {
27✔
102
    url = url + '&pcid=' + partnerClientId;
25✔
103
    url = url + '&idtype=' + partnerClientIdType;
25✔
104
  }
105
  return url;
27✔
106
}
107

108
function appendCMPData(url, cmpData) {
109
  url += cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '';
76✔
110
  url += cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '';
76✔
111
  url += cmpData.gdprApplies
76✔
112
    ? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1'
113
    : '&gdpr=0';
114
  return url
76✔
115
}
116

117
function appendCounters(url) {
118
  url += '&jaesc=' + encodeURIComponent(callCount);
53✔
119
  url += '&jafc=' + encodeURIComponent(failCount);
53✔
120
  url += '&jaensc=' + encodeURIComponent(noDataCount);
53✔
121
  return url
53✔
122
}
123

124
/**
125
 * Translate and validate sourceMetaData
126
 */
127
export function translateMetadata(data) {
128
  try {
22✔
129
    const d = data.split('.');
22✔
130
    return (
22✔
131
      ((+d[0] * META_DATA_CONSTANT + +d[1]) * META_DATA_CONSTANT + +d[2]) * META_DATA_CONSTANT +
132
      +d[3]
133
    );
134
  } catch (e) {
135
    return NaN;
×
136
  }
137
}
138

139
/**
140
 * Add sourceMetaData to URL if valid
141
 */
142
function addMetaData(url, data) {
143
  if (typeof data !== 'number' || isNaN(data)) {
76✔
144
    return url;
57✔
145
  }
146
  return url + '&fbp=' + data;
19✔
147
}
148

149
export function initializeGlobalIIQ(partnerId) {
150
  if (!globalName || !window[globalName]) {
76✔
151
    globalName = `iiq_identity_${partnerId}`
1✔
152
    window[globalName] = {}
1✔
153
    return true
1✔
154
  }
155
  return false
75✔
156
}
157

158
export function createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData) {
159
  const browser = detectBrowser();
23✔
160

161
  let url = iiqPixelServerAddress(configParams, cmpData.gdprString);
23✔
162
  url += '/profiles_engine/ProfilesEngineServlet?at=20&mi=10&secure=1';
23✔
163
  url += '&dpi=' + configParams.partner;
23✔
164
  url = appendFirstPartyData(url, firstPartyData, partnerData);
23✔
165
  url = appendPartnersFirstParty(url, configParams);
23✔
166
  url = addUniquenessToUrl(url);
23✔
167
  url += partnerData?.clientType ? '&idtype=' + partnerData.clientType : '';
23!
168
  url += VERSION ? '&jsver=' + VERSION : '';
23!
169
  if (clientHints) url += '&uh=' + encodeURIComponent(clientHints);
23✔
170
  url = appendVrrefAndFui(url, configParams.domainName);
23✔
171
  url = appendCMPData(url, cmpData);
23✔
172
  url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
23✔
173
  url = handleAdditionalParams(browser, url, 0, configParams.additionalParams);
23✔
174
  url = appendSPData(url, partnerData);
23✔
175
  url += '&source=' + PREBID;
23✔
176
  return url;
23✔
177
}
178

179
function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) {
180
  const lastSyncDate = Number(readData(SYNC_KEY(partner) || '', allowedStorage)) || false;
21!
181
  const lastSyncElapsedTime = Date.now() - lastSyncDate
21✔
182

183
  if (firstPartyData.isOptedOut) {
21✔
184
    const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL
4!
185
    if (newUser || needToDoSync) {
4!
186
      ajax(url, () => {
4✔
187
      }, undefined, { method: 'GET', withCredentials: true });
188
      if (firstPartyData?.date) {
4✔
189
        firstPartyData.date = Date.now()
4✔
190
        storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
4✔
191
      }
192
    }
193
  } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) {
17!
194
    storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage);
17✔
195
    ajax(url, () => {
17✔
196
    }, undefined, { method: 'GET', withCredentials: true });
197
  }
198
}
199

200
/**
201
 * Configures and updates A/B testing group in Google Ad Manager (GAM).
202
 *
203
 * @param {object} gamObjectReference - Reference to the GAM object, expected to have a `cmd` queue and `pubads()` API.
204
 * @param {string} gamParameterName - The name of the GAM targeting parameter where the group value will be stored.
205
 * @param {string} userGroup - The A/B testing group assigned to the user (e.g., 'A', 'B', or a custom value).
206
 */
207
export function setGamReporting(gamObjectReference, gamParameterName, userGroup, isBlacklisted = false) {
79✔
208
  if (isBlacklisted) return;
79✔
209
  if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) {
58✔
210
    gamObjectReference.cmd.push(() => {
34✔
211
      gamObjectReference
7✔
212
        .pubads()
213
        .setTargeting(gamParameterName, userGroup);
214
    });
215
  }
216
}
217

218
/**
219
 * Processes raw client hints data into a structured format.
220
 * @param {object} clientHints - Raw client hints data
221
 * @return {string} A JSON string of processed client hints or an empty string if no hints
222
 */
223
export function handleClientHints(clientHints) {
224
  const chParams = {};
78✔
225
  for (const key in clientHints) {
78✔
226
    if (clientHints.hasOwnProperty(key) && clientHints[key] !== '') {
702✔
227
      if (['brands', 'fullVersionList'].includes(key)) {
596✔
228
        let handledParam = '';
156✔
229
        clientHints[key].forEach((element, index) => {
156✔
230
          const isNotLast = index < clientHints[key].length - 1;
368✔
231
          handledParam += `"${element.brand}";v="${element.version}"${isNotLast ? ', ' : ''}`;
368✔
232
        });
233
        chParams[encoderCH[key]] = handledParam;
156✔
234
      } else if (typeof clientHints[key] === 'boolean') {
440✔
235
        chParams[encoderCH[key]] = `?${clientHints[key] ? 1 : 0}`;
156!
236
      } else {
237
        chParams[encoderCH[key]] = `"${clientHints[key]}"`;
284✔
238
      }
239
    }
240
  }
241
  return Object.keys(chParams).length ? JSON.stringify(chParams) : '';
78!
242
}
243

244
export function isCMPStringTheSame(fpData, cmpData) {
245
  const firstPartyDataCPString = `${fpData.gdprString}${fpData.gppString}${fpData.uspString}`;
83✔
246
  const cmpDataString = `${cmpData.gdprString}${cmpData.gppString}${cmpData.uspString}`;
83✔
247
  return firstPartyDataCPString === cmpDataString;
83✔
248
}
249

250
function updateCountersAndStore(runtimeEids, allowedStorage, partnerData) {
251
  if (!runtimeEids?.eids?.length) {
22✔
252
    noDataCount++;
15✔
253
  } else {
254
    callCount++;
7✔
255
  }
256
  storeCounters(allowedStorage, partnerData);
22✔
257
}
258

259
function clearCountersAndStore(allowedStorage, partnerData) {
260
  callCount = 0;
49✔
261
  failCount = 0;
49✔
262
  noDataCount = 0;
49✔
263
  storeCounters(allowedStorage, partnerData);
49✔
264
}
265

266
function storeCounters(storage, partnerData) {
267
  partnerData.callCount = callCount;
71✔
268
  partnerData.failCount = failCount;
71✔
269
  partnerData.noDataCounter = noDataCount;
71✔
270
  storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), storage, firstPartyData);
71✔
271
}
272

273
/** @type {Submodule} */
274
export const intentIqIdSubmodule = {
1✔
275
  /**
276
   * used to link submodule with config
277
   * @type {string}
278
   */
279
  name: MODULE_NAME,
280
  gvlid: GVLID,
281
  /**
282
   * decode the stored id value for passing to bid requests
283
   * @function
284
   * @param {{string}} value
285
   * @returns {{intentIqId: {string}}|undefined}
286
   */
287
  decode(value) {
288
    return value && INVALID_ID !== value ? { 'intentIqId': value } : undefined;
3!
289
  },
290

291
  /**
292
   * performs action to obtain id and return a value in the callback's response argument
293
   * @function
294
   * @param {SubmoduleConfig} [config]
295
   * @returns {IdResponse|undefined}
296
   */
297
  getId(config) {
298
    const configParams = (config?.params) || {};
78!
299

300
    const firePartnerCallback = () => {
78✔
301
      if (configParams.callback && !callbackFired) {
110✔
302
        callbackFired = true;
6✔
303
        if (callbackTimeoutID) clearTimeout(callbackTimeoutID);
6✔
304
        let data = runtimeEids;
6✔
305
        if (data?.eids?.length === 1 && typeof data.eids[0] === 'string') data = data.eids[0];
6!
306
        configParams.callback(data);
6✔
307
      }
308
      updateGlobalObj()
110✔
309
    }
310

311
    if (typeof configParams.partner !== 'number') {
78✔
312
      logError('User ID - intentIqId submodule requires a valid partner to be defined');
3✔
313
      firePartnerCallback()
3✔
314
      return;
3✔
315
    }
316

317
    initializeGlobalIIQ(configParams.partner)
75✔
318

319
    let decryptedData, callbackTimeoutID;
320
    let callbackFired = false;
75✔
321
    let runtimeEids = { eids: [] };
75✔
322

323
    const gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined;
75✔
324
    const gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group';
75✔
325
    const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined;
75✔
326
    const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false;
75✔
327
    sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData) : '';
75✔
328
    sourceMetaDataExternal = isNumber(configParams.sourceMetaDataExternal) ? configParams.sourceMetaDataExternal : undefined;
75✔
329
    const additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined;
75✔
330
    const chTimeout = Number(configParams?.chTimeout) >= 0 ? Number(configParams.chTimeout) : 10;
75✔
331
    PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`;
75✔
332

333
    const allowedStorage = defineStorageType(config.enabledStorageTypes);
75✔
334
    partnerData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage)) || {};
75✔
335

336
    let rrttStrtTime = 0;
75✔
337
    let shouldCallServer = false;
75✔
338
    FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`;
75✔
339
    const cmpData = getCmpData();
75✔
340
    const gdprDetected = cmpData.gdprString;
75✔
341
    firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage));
75✔
342
    actualABGroup = defineABTestingGroup(configParams, partnerData?.terminationCause);
75✔
343
    const currentBrowserLowerCase = detectBrowser();
75✔
344
    const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : '';
75✔
345
    const isBlacklisted = browserBlackList?.includes(currentBrowserLowerCase);
75✔
346
    let newUser = false;
75✔
347

348
    setGamReporting(gamObjectReference, gamParameterName, actualABGroup, isBlacklisted);
75✔
349

350
    if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause);
75✔
351

352
    callbackTimeoutID = setTimeout(() => {
75✔
353
      firePartnerCallback();
71✔
354
    }, configParams.timeoutInMillis || 500
150✔
355
    );
356

357
    if (!firstPartyData?.pcid) {
75✔
358
      const firstPartyId = generateGUID();
69✔
359
      firstPartyData = {
69✔
360
        pcid: firstPartyId,
361
        pcidDate: Date.now(),
362
        uspString: EMPTY,
363
        gppString: EMPTY,
364
        gdprString: EMPTY,
365
        date: Date.now()
366
      };
367
      newUser = true;
69✔
368
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
69✔
369
    } else if (!firstPartyData.pcidDate) {
6✔
370
      firstPartyData.pcidDate = Date.now();
3✔
371
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
3✔
372
    }
373

374
    if (gdprDetected && !('isOptedOut' in firstPartyData)) {
75✔
375
      firstPartyData.isOptedOut = true;
11✔
376
    }
377

378
    // Read client hints from storage
379
    clientHints = readData(CLIENT_HINTS_KEY, allowedStorage);
75✔
380
    const chSupported = isCHSupported();
75✔
381
    let chPromise = null;
75✔
382

383
    function fetchAndHandleCH() {
384
      return navigator.userAgentData.getHighEntropyValues(CH_KEYS)
74✔
385
        .then(raw => {
386
          const nextCH = handleClientHints(raw) || '';
73!
387
          const prevCH = clientHints || '';
73✔
388
          if (nextCH !== prevCH) {
73✔
389
            clientHints = nextCH;
73✔
390
            storeData(CLIENT_HINTS_KEY, clientHints, allowedStorage, firstPartyData);
73✔
391
          }
392
          return nextCH;
73✔
393
        })
394
        .catch(err => {
395
          logError('CH fetch failed', err);
1✔
396
          if (clientHints !== '') {
1✔
397
            clientHints = '';
1✔
398
            removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
399
          }
400
          return '';
1✔
401
        });
402
    }
403

404
    if (chSupported) {
75✔
405
      chPromise = fetchAndHandleCH();
74✔
406
      chPromise.catch(err => {
74✔
407
        logError('fetchAndHandleCH failed', err);
×
408
      });
409
    } else {
410
      clientHints = '';
1✔
411
      removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
412
    }
413

414
    function waitOnCH(timeoutMs) {
415
      const timeout = new Promise(resolve => setTimeout(() => resolve(''), timeoutMs));
65✔
416
      return Promise.race([chPromise, timeout]);
65✔
417
    }
418

419
    if (typeof partnerData.callCount === 'number') callCount = partnerData.callCount;
75✔
420
    if (typeof partnerData.failCount === 'number') failCount = partnerData.failCount;
75✔
421
    if (typeof partnerData.noDataCounter === 'number') noDataCount = partnerData.noDataCounter;
75✔
422
    if (partnerData.wsrvcll) {
75!
423
      partnerData.wsrvcll = false;
×
424
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
×
425
    }
426

427
    if (partnerData.data) {
75✔
428
      if (partnerData.data.length) { // encrypted data
1✔
429
        decryptedData = tryParse(decryptData(partnerData.data));
1✔
430
        runtimeEids = decryptedData;
1✔
431
      }
432
    }
433

434
    function updateGlobalObj() {
435
      if (globalName) {
163✔
436
        window[globalName].partnerData = partnerData
163✔
437
        window[globalName].firstPartyData = firstPartyData
163✔
438
        window[globalName].clientHints = clientHints
163✔
439
        window[globalName].actualABGroup = actualABGroup
163✔
440
      }
441
    }
442

443
    let hasPartnerData = !!Object.keys(partnerData).length;
75✔
444
    if (!isCMPStringTheSame(firstPartyData, cmpData) ||
75!
445
      !firstPartyData.sCal ||
446
      (hasPartnerData && (!partnerData.cttl || !partnerData.date || Date.now() - partnerData.date > partnerData.cttl))) {
447
      firstPartyData.uspString = cmpData.uspString;
73✔
448
      firstPartyData.gppString = cmpData.gppString;
73✔
449
      firstPartyData.gdprString = cmpData.gdprString;
73✔
450
      shouldCallServer = true;
73✔
451
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
73✔
452
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
73✔
453
    }
454
    if (!shouldCallServer) {
75✔
455
      if (!hasPartnerData && !firstPartyData.isOptedOut) {
2✔
456
        shouldCallServer = true;
1✔
457
      } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_72;
1✔
458
    }
459

460
    if (firstPartyData.isOptedOut) {
75✔
461
      partnerData.data = runtimeEids = { eids: [] };
12✔
462
      firePartnerCallback()
12✔
463
    }
464

465
    if (runtimeEids?.eids?.length) {
75✔
466
      firePartnerCallback()
1✔
467
    }
468

469
    function buildAndSendPixel(ch) {
470
      const url = createPixelUrl(firstPartyData, ch, configParams, partnerData, cmpData);
21✔
471
      sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser);
21✔
472
    }
473

474
    // Check if current browser is in blacklist
475
    if (isBlacklisted) {
75✔
476
      logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.');
21✔
477
      if (configParams.callback) configParams.callback('');
21✔
478

479
      if (chSupported) {
21!
480
        if (clientHints) {
21✔
481
          buildAndSendPixel(clientHints)
1✔
482
        } else {
483
          waitOnCH(chTimeout)
20✔
484
            .then(ch => buildAndSendPixel(ch || ''));
20!
485
        }
486
      } else {
487
        buildAndSendPixel('');
×
488
      }
489
      return;
21✔
490
    }
491

492
    if (!shouldCallServer) {
54✔
493
      firePartnerCallback();
1✔
494
      updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
1✔
495
      return { id: runtimeEids.eids };
1✔
496
    }
497

498
    updateGlobalObj() // update global object before server request, to make sure analytical adapter will have it even if the server is "not in time"
53✔
499

500
    // use protocol relative urls for http or https
501
    let url = `${getIiqServerAddress(configParams)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`;
53✔
502
    url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : '';
53✔
503
    url = appendFirstPartyData(url, firstPartyData, partnerData);
53✔
504
    url = appendPartnersFirstParty(url, configParams);
53✔
505
    url += (partnerData.cttl) ? '&cttl=' + encodeURIComponent(partnerData.cttl) : '';
53✔
506
    url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : '';
53✔
507
    url = appendCMPData(url, cmpData);
53✔
508
    url += '&japs=' + encodeURIComponent(configParams.siloEnabled === true);
53✔
509
    url = appendCounters(url);
53✔
510
    url += VERSION ? '&jsver=' + VERSION : '';
53!
511
    url += actualABGroup ? '&testGroup=' + encodeURIComponent(actualABGroup) : '';
53!
512
    url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
53✔
513
    url = handleAdditionalParams(currentBrowserLowerCase, url, 1, additionalParams);
53✔
514
    url = appendSPData(url, partnerData)
53✔
515
    url += '&source=' + PREBID;
53✔
516
    url += '&ABTestingConfigurationSource=' + configParams.ABTestingConfigurationSource
53✔
517
    url += '&abtg=' + encodeURIComponent(actualABGroup)
53✔
518

519
    // Add vrref and fui to the URL
520
    url = appendVrrefAndFui(url, configParams.domainName);
53✔
521

522
    const storeFirstPartyData = () => {
53✔
523
      partnerData.eidl = runtimeEids?.eids?.length || -1
20✔
524
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
20✔
525
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
20✔
526
    }
527

528
    const resp = function (callback) {
53✔
529
      const callbacks = {
49✔
530
        success: response => {
531
          if (rrttStrtTime && rrttStrtTime > 0) {
22✔
532
            partnerData.rrtt = Date.now() - rrttStrtTime;
22✔
533
          }
534
          const respJson = tryParse(response);
22✔
535
          // If response is a valid json and should save is true
536
          if (respJson) {
22✔
537
            partnerData.date = Date.now();
21✔
538
            firstPartyData.sCal = Date.now();
21✔
539
            const defineEmptyDataAndFireCallback = () => {
21✔
540
              respJson.data = partnerData.data = runtimeEids = { eids: [] };
1✔
541
              storeFirstPartyData()
1✔
542
              firePartnerCallback()
1✔
543
              callback(runtimeEids)
1✔
544
            }
545
            if (callbackTimeoutID) clearTimeout(callbackTimeoutID)
21✔
546
            if ('cttl' in respJson) {
21!
UNCOV
547
              partnerData.cttl = respJson.cttl;
×
548
            } else partnerData.cttl = HOURS_72;
21✔
549

550
            if ('tc' in respJson) {
21✔
551
              partnerData.terminationCause = respJson.tc;
5✔
552
              actualABGroup = defineABTestingGroup(configParams, respJson.tc,);
5✔
553

554
              if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup);
5✔
555
              if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause);
5✔
556
            }
557
            if ('isOptedOut' in respJson) {
21✔
558
              if (respJson.isOptedOut !== firstPartyData.isOptedOut) {
5✔
559
                firstPartyData.isOptedOut = respJson.isOptedOut;
4✔
560
              }
561
              if (respJson.isOptedOut === true) {
5✔
562
                respJson.data = partnerData.data = runtimeEids = { eids: [] };
1✔
563

564
                const keysToRemove = [
1✔
565
                  PARTNER_DATA_KEY,
566
                  CLIENT_HINTS_KEY
567
                ];
568

569
                keysToRemove.forEach(key => removeDataByKey(key, allowedStorage));
2✔
570

571
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
572
                firePartnerCallback();
1✔
573
                callback(runtimeEids);
1✔
574
                return
1✔
575
              }
576
            }
577
            if ('pid' in respJson) {
20✔
578
              firstPartyData.pid = respJson.pid;
6✔
579
            }
580
            if ('dbsaved' in respJson) {
20✔
581
              firstPartyData.dbsaved = respJson.dbsaved;
1✔
582
            }
583
            if ('ls' in respJson) {
20✔
584
              if (respJson.ls === false) {
9✔
585
                defineEmptyDataAndFireCallback()
1✔
586
                return
1✔
587
              }
588
              // If data is empty, means we should save as INVALID_ID
589
              if (respJson.data === '') {
8!
UNCOV
590
                respJson.data = INVALID_ID;
×
591
              } else {
592
                // If data is a single string, assume it is an id with source intentiq.com
593
                if (respJson.data && typeof respJson.data === 'string') {
8✔
594
                  respJson.data = { eids: [respJson.data] }
6✔
595
                }
596
              }
597
              partnerData.data = respJson.data;
8✔
598
            }
599

600
            if ('ct' in respJson) {
19✔
601
              partnerData.clientType = respJson.ct;
1✔
602
            }
603

604
            if ('sid' in respJson) {
19!
UNCOV
605
              partnerData.siteId = respJson.sid;
×
606
            }
607

608
            if ('spd' in respJson) {
19✔
609
              // server provided data
610
              partnerData.spd = respJson.spd;
1✔
611
            }
612

613
            if ('abTestUuid' in respJson) {
19!
UNCOV
614
              if ('ls' in respJson && respJson.ls === true) {
×
UNCOV
615
                partnerData.abTestUuid = respJson.abTestUuid;
×
616
              }
617
            }
618

619
            if ('gpr' in respJson) {
19!
620
              // GAM prediction reporting
UNCOV
621
              partnerData.gpr = respJson.gpr;
×
622
            } else {
623
              delete partnerData.gpr // remove prediction flag in case server doesn't provide it
19✔
624
            }
625

626
            if (respJson.data?.eids) {
19✔
627
              runtimeEids = respJson.data
10✔
628
              callback(respJson.data.eids);
10✔
629
              firePartnerCallback()
10✔
630
              const encryptedData = encryptData(JSON.stringify(respJson.data));
10✔
631
              partnerData.data = encryptedData;
10✔
632
            } else {
633
              callback(runtimeEids);
9✔
634
              firePartnerCallback()
9✔
635
            }
636
            updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
19✔
637
            storeFirstPartyData();
19✔
638
          } else {
639
            callback(runtimeEids);
1✔
640
            firePartnerCallback()
1✔
641
          }
642
        },
643
        error: error => {
644
          logError(MODULE_NAME + ': ID fetch encountered an error', error);
2✔
645
          failCount++;
2✔
646
          updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
2✔
647
          callback(runtimeEids);
2✔
648
        }
649
      };
650

651
      partnerData.wsrvcll = true;
49✔
652
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
49✔
653
      clearCountersAndStore(allowedStorage, partnerData);
49✔
654

655
      rrttStrtTime = Date.now();
49✔
656

657
      const sendAjax = uh => {
49✔
658
        if (uh) url += '&uh=' + encodeURIComponent(uh);
49✔
659
        ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true });
49✔
660
      }
661

662
      if (chSupported) {
49✔
663
        if (clientHints) {
48✔
664
          // CH found in LS: send immediately; background fetch will refresh/clear later
665
          sendAjax(clientHints);
3✔
666
        } else {
667
          // No CH in LS: wait up to chTimeout, then send
668
          waitOnCH(chTimeout).then(ch => {
45✔
669
            // Send with received CH or without it
670
            sendAjax(ch || '');
45!
671
          })
672
        }
673
      } else {
674
        // CH not supported: send without uh
675
        sendAjax('');
1✔
676
      }
677
    };
678
    const respObj = { callback: resp };
53✔
679

680
    if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids;
53✔
681
    return respObj
53✔
682
  },
683
  eids: {
684
    'intentIqId': {
685
      source: 'intentiq.com',
686
      atype: 1,
687
      getSource: function (data) {
688
        return data.source;
×
689
      },
690
      getValue: function (data) {
691
        if (data?.uids?.length) {
×
692
          return data.uids[0].id
×
693
        }
694
        return null
×
695
      },
696
      getUidExt: function (data) {
697
        if (data?.uids?.length) {
×
698
          return data.uids[0].ext;
×
699
        }
700
        return null
×
701
      }
702
    },
703
  }
704
};
705

706
submodule('userId', intentIqIdSubmodule);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc