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

prebid / Prebid.js / 18176309162

01 Oct 2025 09:34PM UTC coverage: 96.245%. Remained the same
18176309162

push

github

9c5fe6
web-flow
Yieldmo Bidder: TDID object not being passed correctly (#13958)

* update

* updates

* add in test

51933 of 63535 branches covered (81.74%)

6 of 6 new or added lines in 2 files covered. (100.0%)

20 existing lines in 14 files now uncovered.

198755 of 206510 relevant lines covered (96.24%)

125.01 hits per line

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

93.75
/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, getWinDimensions} 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
  WITH_IIQ, WITHOUT_IIQ,
20
  NOT_YET_DEFINED,
21
  CLIENT_HINTS_KEY,
22
  EMPTY,
23
  GVLID,
24
  VERSION, INVALID_ID, SCREEN_PARAMS, SYNC_REFRESH_MILL, META_DATA_CONSTANT, PREBID,
25
  HOURS_24, CH_KEYS
26
} from '../libraries/intentIqConstants/intentIqConstants.js';
27
import {SYNC_KEY} from '../libraries/intentIqUtils/getSyncKey.js';
28
import {iiqPixelServerAddress, iiqServerAddress} from '../libraries/intentIqUtils/intentIqConfig.js';
29
import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js';
30
import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.js';
31

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

38
const MODULE_NAME = 'intentIqId';
1✔
39

40
const encoderCH = {
1✔
41
  brands: 0,
42
  mobile: 1,
43
  platform: 2,
44
  architecture: 3,
45
  bitness: 4,
46
  model: 5,
47
  platformVersion: 6,
48
  wow64: 7,
49
  fullVersionList: 8
50
};
51
let sourceMetaData;
52
let sourceMetaDataExternal;
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

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

76
function collectDeviceInfo() {
77
  const windowDimensions = getWinDimensions();
17✔
78
  return {
17✔
79
    windowInnerHeight: windowDimensions.innerHeight,
80
    windowInnerWidth: windowDimensions.innerWidth,
81
    devicePixelRatio: windowDimensions.devicePixelRatio,
82
    windowScreenHeight: windowDimensions.screen.height,
83
    windowScreenWidth: windowDimensions.screen.width,
84
    language: navigator.language
85
  }
86
}
87

88
function addUniquenessToUrl(url) {
89
  url += '&tsrnd=' + Math.floor(Math.random() * 1000) + '_' + new Date().getTime();
17✔
90
  return url;
17✔
91
}
92

93
function appendDeviceInfoToUrl(url, deviceInfo) {
94
  const screenParamsString = Object.entries(SCREEN_PARAMS)
17✔
95
    .map(([index, param]) => {
102✔
96
      const value = (deviceInfo)[param];
102✔
97
      return `${index}:${value}`;
102✔
98
    })
99
    .join(',');
100

101
  url += `&cz=${encodeURIComponent(screenParamsString)}`;
17✔
102
  url += `&dw=${deviceInfo.windowScreenWidth}&dh=${deviceInfo.windowScreenHeight}&dpr=${deviceInfo.devicePixelRatio}&lan=${deviceInfo.language}`;
17✔
103
  return url;
17✔
104
}
105

106
function appendFirstPartyData (url, firstPartyData, partnerData) {
107
  url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : '';
63!
108
  url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : '';
63✔
109
  url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : '';
63✔
110
  return url
63✔
111
}
112

113
function verifyIdType(value) {
114
  if (value === 0 || value === 1 || value === 3 || value === 4) return value;
23!
115
  return -1;
×
116
}
117

118
function appendPartnersFirstParty (url, configParams) {
119
  const partnerClientId = typeof configParams.partnerClientId === 'string' ? encodeURIComponent(configParams.partnerClientId) : '';
63✔
120
  const partnerClientIdType = typeof configParams.partnerClientIdType === 'number' ? verifyIdType(configParams.partnerClientIdType) : -1;
63✔
121

122
  if (partnerClientIdType === -1) return url;
63✔
123
  if (partnerClientId !== '') {
23✔
124
    url = url + '&pcid=' + partnerClientId;
21✔
125
    url = url + '&idtype=' + partnerClientIdType;
21✔
126
  }
127
  return url;
23✔
128
}
129

130
function appendCMPData (url, cmpData) {
131
  url += cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '';
63✔
132
  url += cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '';
63✔
133
  url += cmpData.gdprApplies
63✔
134
    ? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1'
135
    : '&gdpr=0';
136
  return url
63✔
137
}
138

139
function appendCounters (url) {
140
  url += '&jaesc=' + encodeURIComponent(callCount);
46✔
141
  url += '&jafc=' + encodeURIComponent(failCount);
46✔
142
  url += '&jaensc=' + encodeURIComponent(noDataCount);
46✔
143
  return url
46✔
144
}
145

146
/**
147
 * Translate and validate sourceMetaData
148
 */
149
export function translateMetadata(data) {
150
  try {
22✔
151
    const d = data.split('.');
22✔
152
    return (
22✔
153
      ((+d[0] * META_DATA_CONSTANT + +d[1]) * META_DATA_CONSTANT + +d[2]) * META_DATA_CONSTANT +
154
      +d[3]
155
    );
156
  } catch (e) {
157
    return NaN;
×
158
  }
159
}
160

161
/**
162
 * Add sourceMetaData to URL if valid
163
 */
164
function addMetaData(url, data) {
165
  if (typeof data !== 'number' || isNaN(data)) {
63✔
166
    return url;
44✔
167
  }
168
  return url + '&fbp=' + data;
19✔
169
}
170

171
export function createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData) {
172
  const deviceInfo = collectDeviceInfo();
17✔
173
  const browser = detectBrowser();
17✔
174

175
  let url = iiqPixelServerAddress(configParams, cmpData.gdprString);
17✔
176
  url += '/profiles_engine/ProfilesEngineServlet?at=20&mi=10&secure=1';
17✔
177
  url += '&dpi=' + configParams.partner;
17✔
178
  url = appendFirstPartyData(url, firstPartyData, partnerData);
17✔
179
  url = appendPartnersFirstParty(url, configParams);
17✔
180
  url = addUniquenessToUrl(url);
17✔
181
  url += partnerData?.clientType ? '&idtype=' + partnerData.clientType : '';
17!
182
  if (deviceInfo) url = appendDeviceInfoToUrl(url, deviceInfo);
17✔
183
  url += VERSION ? '&jsver=' + VERSION : '';
17!
184
  if (clientHints) url += '&uh=' + encodeURIComponent(clientHints);
17✔
185
  url = appendVrrefAndFui(url, configParams.domainName);
17✔
186
  url = appendCMPData(url, cmpData);
17✔
187
  url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
17✔
188
  url = handleAdditionalParams(browser, url, 0, configParams.additionalParams);
17✔
189
  url = appendSPData(url, firstPartyData)
17✔
190
  url += '&source=' + PREBID;
17✔
191
  return url;
17✔
192
}
193

194
function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) {
195
  const lastSyncDate = Number(readData(SYNC_KEY(partner) || '', allowedStorage)) || false;
15!
196
  const lastSyncElapsedTime = Date.now() - lastSyncDate
15✔
197

198
  if (firstPartyData.isOptedOut) {
15!
199
    const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL
×
200
    if (newUser || needToDoSync) {
×
201
      ajax(url, () => {
×
202
      }, undefined, {method: 'GET', withCredentials: true});
203
      if (firstPartyData?.date) {
×
204
        firstPartyData.date = Date.now()
×
205
        storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
×
206
      }
207
    }
208
  } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) {
15!
209
    storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage);
15✔
210
    ajax(url, () => {
15✔
211
    }, undefined, {method: 'GET', withCredentials: true});
212
  }
213
}
214

215
/**
216
 * Configures and updates A/B testing group in Google Ad Manager (GAM).
217
 *
218
 * @param {object} gamObjectReference - Reference to the GAM object, expected to have a `cmd` queue and `pubads()` API.
219
 * @param {string} gamParameterName - The name of the GAM targeting parameter where the group value will be stored.
220
 * @param {string} userGroup - The A/B testing group assigned to the user (e.g., 'A', 'B', or a custom value).
221
 */
222
export function setGamReporting(gamObjectReference, gamParameterName, userGroup) {
223
  if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) {
65✔
224
    gamObjectReference.cmd.push(() => {
31✔
225
      gamObjectReference
3✔
226
        .pubads()
227
        .setTargeting(gamParameterName, userGroup || NOT_YET_DEFINED);
5✔
228
    });
229
  }
230
}
231

232
/**
233
 * Processes raw client hints data into a structured format.
234
 * @param {object} clientHints - Raw client hints data
235
 * @return {string} A JSON string of processed client hints or an empty string if no hints
236
 */
237
export function handleClientHints(clientHints) {
238
  const chParams = {};
65✔
239
  for (const key in clientHints) {
65✔
240
    if (clientHints.hasOwnProperty(key) && clientHints[key] !== '') {
585✔
241
      if (['brands', 'fullVersionList'].includes(key)) {
520✔
242
        let handledParam = '';
130✔
243
        clientHints[key].forEach((element, index) => {
130✔
244
          const isNotLast = index < clientHints[key].length - 1;
306✔
245
          handledParam += `"${element.brand}";v="${element.version}"${isNotLast ? ', ' : ''}`;
306✔
246
        });
247
        chParams[encoderCH[key]] = handledParam;
130✔
248
      } else if (typeof clientHints[key] === 'boolean') {
390✔
249
        chParams[encoderCH[key]] = `?${clientHints[key] ? 1 : 0}`;
130!
250
      } else {
251
        chParams[encoderCH[key]] = `"${clientHints[key]}"`;
260✔
252
      }
253
    }
254
  }
255
  return Object.keys(chParams).length ? JSON.stringify(chParams) : '';
65!
256
}
257

258
export function isCMPStringTheSame(fpData, cmpData) {
259
  const firstPartyDataCPString = `${fpData.gdprString}${fpData.gppString}${fpData.uspString}`;
70✔
260
  const cmpDataString = `${cmpData.gdprString}${cmpData.gppString}${cmpData.uspString}`;
70✔
261
  return firstPartyDataCPString === cmpDataString;
70✔
262
}
263

264
function updateCountersAndStore(runtimeEids, allowedStorage, partnerData) {
265
  if (!runtimeEids?.eids?.length) {
19✔
266
    noDataCount++;
13✔
267
  } else {
268
    callCount++;
6✔
269
  }
270
  storeCounters(allowedStorage, partnerData);
19✔
271
}
272

273
function clearCountersAndStore(allowedStorage, partnerData) {
274
  callCount = 0;
42✔
275
  failCount = 0;
42✔
276
  noDataCount = 0;
42✔
277
  storeCounters(allowedStorage, partnerData);
42✔
278
}
279

280
function storeCounters(storage, partnerData) {
281
  partnerData.callCount = callCount;
61✔
282
  partnerData.failCount = failCount;
61✔
283
  partnerData.noDataCounter = noDataCount;
61✔
284
  storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), storage, firstPartyData);
61✔
285
}
286

287
/** @type {Submodule} */
288
export const intentIqIdSubmodule = {
1✔
289
  /**
290
   * used to link submodule with config
291
   * @type {string}
292
   */
293
  name: MODULE_NAME,
294
  gvlid: GVLID,
295
  /**
296
   * decode the stored id value for passing to bid requests
297
   * @function
298
   * @param {{string}} value
299
   * @returns {{intentIqId: {string}}|undefined}
300
   */
301
  decode(value) {
302
    return value && INVALID_ID !== value ? {'intentIqId': value} : undefined;
3!
303
  },
304

305
  /**
306
   * performs action to obtain id and return a value in the callback's response argument
307
   * @function
308
   * @param {SubmoduleConfig} [config]
309
   * @returns {IdResponse|undefined}
310
   */
311
  getId(config) {
312
    const configParams = (config?.params) || {};
65!
313

314
    const firePartnerCallback = () => {
65✔
315
      if (configParams.callback && !callbackFired) {
92✔
316
        callbackFired = true;
2✔
317
        if (callbackTimeoutID) clearTimeout(callbackTimeoutID);
2✔
318
        if (isGroupB) runtimeEids = { eids: [] };
2!
319
        configParams.callback(runtimeEids);
2✔
320
      }
321
    }
322

323
    if (typeof configParams.partner !== 'number') {
65✔
324
      logError('User ID - intentIqId submodule requires a valid partner to be defined');
3✔
325
      firePartnerCallback()
3✔
326
      return;
3✔
327
    }
328

329
    let decryptedData, callbackTimeoutID;
330
    let callbackFired = false;
62✔
331
    let runtimeEids = { eids: [] };
62✔
332

333
    const gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined;
62✔
334
    const gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group';
62✔
335
    const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined;
62✔
336
    const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false;
62✔
337
    sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData) : '';
62✔
338
    sourceMetaDataExternal = isNumber(configParams.sourceMetaDataExternal) ? configParams.sourceMetaDataExternal : undefined;
62✔
339
    const additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined;
62✔
340
    const chTimeout = Number(configParams?.chTimeout) >= 0 ? Number(configParams.chTimeout) : 10;
62!
341
    PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`;
62✔
342

343
    const allowedStorage = defineStorageType(config.enabledStorageTypes);
62✔
344

345
    let rrttStrtTime = 0;
62✔
346
    let partnerData = {};
62✔
347
    let shouldCallServer = false;
62✔
348
    FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`;
62✔
349
    const cmpData = getCmpData();
62✔
350
    const gdprDetected = cmpData.gdprString;
62✔
351
    firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage));
62✔
352
    const isGroupB = firstPartyData?.group === WITHOUT_IIQ;
62✔
353
    setGamReporting(gamObjectReference, gamParameterName, firstPartyData?.group);
62✔
354

355
    if (groupChanged) groupChanged(firstPartyData?.group || NOT_YET_DEFINED);
62!
356

357
    callbackTimeoutID = setTimeout(() => {
62✔
358
      firePartnerCallback();
62✔
359
    }, configParams.timeoutInMillis || 500
124✔
360
    );
361

362
    const currentBrowserLowerCase = detectBrowser();
62✔
363
    const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : '';
62✔
364
    let newUser = false;
62✔
365

366
    if (!firstPartyData?.pcid) {
62✔
367
      const firstPartyId = generateGUID();
53✔
368
      firstPartyData = {
53✔
369
        pcid: firstPartyId,
370
        pcidDate: Date.now(),
371
        group: NOT_YET_DEFINED,
372
        uspString: EMPTY,
373
        gppString: EMPTY,
374
        gdprString: EMPTY,
375
        date: Date.now()
376
      };
377
      newUser = true;
53✔
378
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
53✔
379
    } else if (!firstPartyData.pcidDate) {
9✔
380
      firstPartyData.pcidDate = Date.now();
7✔
381
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
7✔
382
    }
383

384
    if (gdprDetected && !('isOptedOut' in firstPartyData)) {
62✔
385
      firstPartyData.isOptedOut = true;
4✔
386
    }
387

388
    // Read client hints from storage
389
    let clientHints = readData(CLIENT_HINTS_KEY, allowedStorage);
62✔
390
    const chSupported = isCHSupported();
62✔
391
    let chPromise = null;
62✔
392

393
    function fetchAndHandleCH() {
394
      return navigator.userAgentData.getHighEntropyValues(CH_KEYS)
61✔
395
        .then(raw => {
396
          const nextCH = handleClientHints(raw) || '';
60!
397
          const prevCH = clientHints || '';
60✔
398
          if (nextCH !== prevCH) {
60✔
399
            clientHints = nextCH;
60✔
400
            storeData(CLIENT_HINTS_KEY, clientHints, allowedStorage, firstPartyData);
60✔
401
          }
402
          return nextCH;
60✔
403
        })
404
        .catch(err => {
405
          logError('CH fetch failed', err);
1✔
406
          if (clientHints !== '') {
1✔
407
            clientHints = '';
1✔
408
            removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
409
          }
410
          return '';
1✔
411
        });
412
    }
413

414
    if (chSupported) {
62✔
415
      chPromise = fetchAndHandleCH();
61✔
416
      chPromise.catch(err => {
61✔
417
        logError('fetchAndHandleCH failed', err);
×
418
      });
419
    } else {
420
      clientHints = '';
1✔
421
      removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
422
    }
423

424
    function waitOnCH(timeoutMs) {
425
      const timeout = new Promise(resolve => setTimeout(() => resolve(''), timeoutMs));
52✔
426
      return Promise.race([chPromise, timeout]);
52✔
427
    }
428

429
    const savedData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage))
62✔
430
    if (savedData) {
62✔
431
      partnerData = savedData;
6✔
432

433
      if (typeof partnerData.callCount === 'number') callCount = partnerData.callCount;
6✔
434
      if (typeof partnerData.failCount === 'number') failCount = partnerData.failCount;
6✔
435
      if (typeof partnerData.noDataCounter === 'number') noDataCount = partnerData.noDataCounter;
6✔
436

437
      if (partnerData.wsrvcll) {
6!
438
        partnerData.wsrvcll = false;
×
439
        storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
×
440
      }
441
    }
442

443
    if (partnerData.data) {
62✔
444
      if (partnerData.data.length) { // encrypted data
1✔
445
        decryptedData = tryParse(decryptData(partnerData.data));
1✔
446
        runtimeEids = decryptedData;
1✔
447
      }
448
    }
449

450
    if (!isCMPStringTheSame(firstPartyData, cmpData) ||
62!
451
          !firstPartyData.sCal ||
452
          (savedData && (!partnerData.cttl || !partnerData.date || Date.now() - partnerData.date > partnerData.cttl))) {
453
      firstPartyData.uspString = cmpData.uspString;
60✔
454
      firstPartyData.gppString = cmpData.gppString;
60✔
455
      firstPartyData.gdprString = cmpData.gdprString;
60✔
456
      shouldCallServer = true;
60✔
457
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
60✔
458
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
60✔
459
    }
460
    if (!shouldCallServer) {
62✔
461
      if (!savedData && !firstPartyData.isOptedOut) {
2✔
462
        shouldCallServer = true;
1✔
463
      } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_24;
1✔
464
    }
465

466
    if (firstPartyData.isOptedOut) {
62✔
467
      partnerData.data = runtimeEids = { eids: [] };
5✔
468
      firePartnerCallback()
5✔
469
    }
470

471
    if (firstPartyData.group === WITHOUT_IIQ || (firstPartyData.group !== WITHOUT_IIQ && runtimeEids?.eids?.length)) {
62✔
472
      firePartnerCallback()
1✔
473
    }
474

475
    function buildAndSendPixel(ch) {
476
      const url = createPixelUrl(firstPartyData, ch, configParams, partnerData, cmpData);
15✔
477
      sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser);
15✔
478
    }
479

480
    // Check if current browser is in blacklist
481
    if (browserBlackList?.includes(currentBrowserLowerCase)) {
62✔
482
      logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.');
15✔
483
      if (configParams.callback) configParams.callback('');
15✔
484

485
      if (chSupported) {
15!
486
        if (clientHints) {
15✔
487
          buildAndSendPixel(clientHints)
1✔
488
        } else {
489
          waitOnCH(chTimeout)
14✔
490
            .then(ch => buildAndSendPixel(ch || ''));
14!
491
        }
492
      } else {
UNCOV
493
        buildAndSendPixel('');
×
494
      }
495
      return;
15✔
496
    }
497

498
    if (!shouldCallServer) {
47✔
499
      if (isGroupB) runtimeEids = { eids: [] };
1!
500
      firePartnerCallback();
1✔
501
      updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
1✔
502
      return { id: runtimeEids.eids };
1✔
503
    }
504

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

522
    // Add vrref and fui to the URL
523
    url = appendVrrefAndFui(url, configParams.domainName);
46✔
524

525
    const storeFirstPartyData = () => {
46✔
526
      partnerData.eidl = runtimeEids?.eids?.length || -1
18!
527
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
18✔
528
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
18✔
529
    }
530

531
    const resp = function (callback) {
46✔
532
      const callbacks = {
42✔
533
        success: response => {
534
          const respJson = tryParse(response);
20✔
535
          // If response is a valid json and should save is true
536
          if (respJson) {
20✔
537
            partnerData.date = Date.now();
19✔
538
            firstPartyData.sCal = Date.now();
19✔
539
            const defineEmptyDataAndFireCallback = () => {
19✔
540
              respJson.data = partnerData.data = runtimeEids = { eids: [] };
2✔
541
              storeFirstPartyData()
2✔
542
              firePartnerCallback()
2✔
543
              callback(runtimeEids)
2✔
544
            }
545
            if (callbackTimeoutID) clearTimeout(callbackTimeoutID)
19✔
546
            if ('cttl' in respJson) {
19!
547
              partnerData.cttl = respJson.cttl;
×
548
            } else partnerData.cttl = HOURS_24;
19✔
549

550
            if ('tc' in respJson) {
19✔
551
              partnerData.terminationCause = respJson.tc;
4✔
552
              if (Number(respJson.tc) === 41) {
4✔
553
                firstPartyData.group = WITHOUT_IIQ;
1✔
554
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
555
                if (groupChanged) groupChanged(firstPartyData.group);
1✔
556
                defineEmptyDataAndFireCallback();
1✔
557
                if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
1✔
558
                return
1✔
559
              } else {
560
                firstPartyData.group = WITH_IIQ;
3✔
561
                if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
3✔
562
                if (groupChanged) groupChanged(firstPartyData.group);
3✔
563
              }
564
            }
565
            if ('isOptedOut' in respJson) {
18✔
566
              if (respJson.isOptedOut !== firstPartyData.isOptedOut) {
4✔
567
                firstPartyData.isOptedOut = respJson.isOptedOut;
3✔
568
              }
569
              if (respJson.isOptedOut === true) {
4✔
570
                respJson.data = partnerData.data = runtimeEids = { eids: [] };
1✔
571

572
                const keysToRemove = [
1✔
573
                  PARTNER_DATA_KEY,
574
                  CLIENT_HINTS_KEY
575
                ];
576

577
                keysToRemove.forEach(key => removeDataByKey(key, allowedStorage));
2✔
578

579
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
580
                firePartnerCallback();
1✔
581
                callback(runtimeEids);
1✔
582
                return
1✔
583
              }
584
            }
585
            if ('pid' in respJson) {
17✔
586
              firstPartyData.pid = respJson.pid;
5✔
587
            }
588
            if ('dbsaved' in respJson) {
17✔
589
              firstPartyData.dbsaved = respJson.dbsaved;
1✔
590
            }
591
            if ('ls' in respJson) {
17✔
592
              if (respJson.ls === false) {
8✔
593
                defineEmptyDataAndFireCallback()
1✔
594
                return
1✔
595
              }
596
              // If data is empty, means we should save as INVALID_ID
597
              if (respJson.data === '') {
7!
598
                respJson.data = INVALID_ID;
×
599
              } else {
600
                // If data is a single string, assume it is an id with source intentiq.com
601
                if (respJson.data && typeof respJson.data === 'string') {
7✔
602
                  respJson.data = {eids: [respJson.data]}
5✔
603
                }
604
              }
605
              partnerData.data = respJson.data;
7✔
606
            }
607

608
            if ('ct' in respJson) {
16✔
609
              partnerData.clientType = respJson.ct;
1✔
610
            }
611

612
            if ('sid' in respJson) {
16!
613
              partnerData.siteId = respJson.sid;
×
614
            }
615

616
            if ('spd' in respJson) {
16✔
617
              // server provided data
618
              firstPartyData.spd = respJson.spd;
1✔
619
            }
620

621
            if (rrttStrtTime && rrttStrtTime > 0) {
16✔
622
              partnerData.rrtt = Date.now() - rrttStrtTime;
16✔
623
            }
624

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

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

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

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

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

704
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