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

prebid / Prebid.js / 19437775255

17 Nov 2025 05:00PM UTC coverage: 96.213% (-0.02%) from 96.231%
19437775255

push

github

web-flow
sevioBidAdapter_bugfix: Send all sizes instead of just maxSize (#14133)

* Send all sizes instead of just maxSize

* Added tests to cover modifs in the sizes that we are sending

53222 of 65234 branches covered (81.59%)

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

304 existing lines in 58 files now uncovered.

202715 of 210693 relevant lines covered (96.21%)

71.77 hits per line

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

93.37
/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
  WITH_IIQ, WITHOUT_IIQ,
20
  NOT_YET_DEFINED,
21
  CLIENT_HINTS_KEY,
22
  EMPTY,
23
  GVLID,
24
  VERSION, INVALID_ID, 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 addUniquenessToUrl(url) {
77
  url += '&tsrnd=' + Math.floor(Math.random() * 1000) + '_' + new Date().getTime();
19✔
78
  return url;
19✔
79
}
80

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

88
function verifyIdType(value) {
89
  if (value === 0 || value === 1 || value === 3 || value === 4) return value;
23!
90
  return -1;
×
91
}
92

93
function appendPartnersFirstParty (url, configParams) {
94
  const partnerClientId = typeof configParams.partnerClientId === 'string' ? encodeURIComponent(configParams.partnerClientId) : '';
64✔
95
  const partnerClientIdType = typeof configParams.partnerClientIdType === 'number' ? verifyIdType(configParams.partnerClientIdType) : -1;
64✔
96

97
  if (partnerClientIdType === -1) return url;
64✔
98
  if (partnerClientId !== '') {
23✔
99
    url = url + '&pcid=' + partnerClientId;
21✔
100
    url = url + '&idtype=' + partnerClientIdType;
21✔
101
  }
102
  return url;
23✔
103
}
104

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

114
function appendCounters (url) {
115
  url += '&jaesc=' + encodeURIComponent(callCount);
45✔
116
  url += '&jafc=' + encodeURIComponent(failCount);
45✔
117
  url += '&jaensc=' + encodeURIComponent(noDataCount);
45✔
118
  return url
45✔
119
}
120

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

136
/**
137
 * Add sourceMetaData to URL if valid
138
 */
139
function addMetaData(url, data) {
140
  if (typeof data !== 'number' || isNaN(data)) {
64✔
141
    return url;
45✔
142
  }
143
  return url + '&fbp=' + data;
19✔
144
}
145

146
export function createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData) {
147
  const browser = detectBrowser();
19✔
148

149
  let url = iiqPixelServerAddress(configParams, cmpData.gdprString);
19✔
150
  url += '/profiles_engine/ProfilesEngineServlet?at=20&mi=10&secure=1';
19✔
151
  url += '&dpi=' + configParams.partner;
19✔
152
  url = appendFirstPartyData(url, firstPartyData, partnerData);
19✔
153
  url = appendPartnersFirstParty(url, configParams);
19✔
154
  url = addUniquenessToUrl(url);
19✔
155
  url += partnerData?.clientType ? '&idtype=' + partnerData.clientType : '';
19!
156
  url += VERSION ? '&jsver=' + VERSION : '';
19!
157
  if (clientHints) url += '&uh=' + encodeURIComponent(clientHints);
19✔
158
  url = appendVrrefAndFui(url, configParams.domainName);
19✔
159
  url = appendCMPData(url, cmpData);
19✔
160
  url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
19✔
161
  url = handleAdditionalParams(browser, url, 0, configParams.additionalParams);
19✔
162
  url = appendSPData(url, firstPartyData)
19✔
163
  url += '&source=' + PREBID;
19✔
164
  return url;
19✔
165
}
166

167
function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) {
168
  const lastSyncDate = Number(readData(SYNC_KEY(partner) || '', allowedStorage)) || false;
17!
169
  const lastSyncElapsedTime = Date.now() - lastSyncDate
17✔
170

171
  if (firstPartyData.isOptedOut) {
17!
172
    const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL
×
173
    if (newUser || needToDoSync) {
×
174
      ajax(url, () => {
×
175
      }, undefined, {method: 'GET', withCredentials: true});
176
      if (firstPartyData?.date) {
×
177
        firstPartyData.date = Date.now()
×
178
        storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
×
179
      }
180
    }
181
  } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) {
17!
182
    storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage);
17✔
183
    ajax(url, () => {
17✔
184
    }, undefined, {method: 'GET', withCredentials: true});
185
  }
186
}
187

188
/**
189
 * Configures and updates A/B testing group in Google Ad Manager (GAM).
190
 *
191
 * @param {object} gamObjectReference - Reference to the GAM object, expected to have a `cmd` queue and `pubads()` API.
192
 * @param {string} gamParameterName - The name of the GAM targeting parameter where the group value will be stored.
193
 * @param {string} userGroup - The A/B testing group assigned to the user (e.g., 'A', 'B', or a custom value).
194
 */
195
export function setGamReporting(gamObjectReference, gamParameterName, userGroup, isBlacklisted = false) {
66✔
196
  if (isBlacklisted) return;
66✔
197
  if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) {
49✔
198
    gamObjectReference.cmd.push(() => {
27✔
199
      gamObjectReference
3✔
200
        .pubads()
201
        .setTargeting(gamParameterName, userGroup || NOT_YET_DEFINED);
5✔
202
    });
203
  }
204
}
205

206
/**
207
 * Processes raw client hints data into a structured format.
208
 * @param {object} clientHints - Raw client hints data
209
 * @return {string} A JSON string of processed client hints or an empty string if no hints
210
 */
211
export function handleClientHints(clientHints) {
212
  const chParams = {};
66✔
213
  for (const key in clientHints) {
66✔
214
    if (clientHints.hasOwnProperty(key) && clientHints[key] !== '') {
594✔
215
      if (['brands', 'fullVersionList'].includes(key)) {
528✔
216
        let handledParam = '';
132✔
217
        clientHints[key].forEach((element, index) => {
132✔
218
          const isNotLast = index < clientHints[key].length - 1;
312✔
219
          handledParam += `"${element.brand}";v="${element.version}"${isNotLast ? ', ' : ''}`;
312✔
220
        });
221
        chParams[encoderCH[key]] = handledParam;
132✔
222
      } else if (typeof clientHints[key] === 'boolean') {
396✔
223
        chParams[encoderCH[key]] = `?${clientHints[key] ? 1 : 0}`;
132!
224
      } else {
225
        chParams[encoderCH[key]] = `"${clientHints[key]}"`;
264✔
226
      }
227
    }
228
  }
229
  return Object.keys(chParams).length ? JSON.stringify(chParams) : '';
66!
230
}
231

232
export function isCMPStringTheSame(fpData, cmpData) {
233
  const firstPartyDataCPString = `${fpData.gdprString}${fpData.gppString}${fpData.uspString}`;
71✔
234
  const cmpDataString = `${cmpData.gdprString}${cmpData.gppString}${cmpData.uspString}`;
71✔
235
  return firstPartyDataCPString === cmpDataString;
71✔
236
}
237

238
function updateCountersAndStore(runtimeEids, allowedStorage, partnerData) {
239
  if (!runtimeEids?.eids?.length) {
19✔
240
    noDataCount++;
13✔
241
  } else {
242
    callCount++;
6✔
243
  }
244
  storeCounters(allowedStorage, partnerData);
19✔
245
}
246

247
function clearCountersAndStore(allowedStorage, partnerData) {
248
  callCount = 0;
42✔
249
  failCount = 0;
42✔
250
  noDataCount = 0;
42✔
251
  storeCounters(allowedStorage, partnerData);
42✔
252
}
253

254
function storeCounters(storage, partnerData) {
255
  partnerData.callCount = callCount;
61✔
256
  partnerData.failCount = failCount;
61✔
257
  partnerData.noDataCounter = noDataCount;
61✔
258
  storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), storage, firstPartyData);
61✔
259
}
260

261
/** @type {Submodule} */
262
export const intentIqIdSubmodule = {
1✔
263
  /**
264
   * used to link submodule with config
265
   * @type {string}
266
   */
267
  name: MODULE_NAME,
268
  gvlid: GVLID,
269
  /**
270
   * decode the stored id value for passing to bid requests
271
   * @function
272
   * @param {{string}} value
273
   * @returns {{intentIqId: {string}}|undefined}
274
   */
275
  decode(value) {
276
    return value && INVALID_ID !== value ? {'intentIqId': value} : undefined;
3!
277
  },
278

279
  /**
280
   * performs action to obtain id and return a value in the callback's response argument
281
   * @function
282
   * @param {SubmoduleConfig} [config]
283
   * @returns {IdResponse|undefined}
284
   */
285
  getId(config) {
286
    const configParams = (config?.params) || {};
66!
287

288
    const firePartnerCallback = () => {
66✔
289
      if (configParams.callback && !callbackFired) {
93✔
290
        callbackFired = true;
2✔
291
        if (callbackTimeoutID) clearTimeout(callbackTimeoutID);
2✔
292
        if (isGroupB) runtimeEids = { eids: [] };
2!
293
        configParams.callback(runtimeEids);
2✔
294
      }
295
    }
296

297
    if (typeof configParams.partner !== 'number') {
66✔
298
      logError('User ID - intentIqId submodule requires a valid partner to be defined');
3✔
299
      firePartnerCallback()
3✔
300
      return;
3✔
301
    }
302

303
    let decryptedData, callbackTimeoutID;
304
    let callbackFired = false;
63✔
305
    let runtimeEids = { eids: [] };
63✔
306

307
    const gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined;
63✔
308
    const gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group';
63✔
309
    const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined;
63✔
310
    const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false;
63✔
311
    sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData) : '';
63✔
312
    sourceMetaDataExternal = isNumber(configParams.sourceMetaDataExternal) ? configParams.sourceMetaDataExternal : undefined;
63✔
313
    const additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined;
63✔
314
    const chTimeout = Number(configParams?.chTimeout) >= 0 ? Number(configParams.chTimeout) : 10;
63!
315
    PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`;
63✔
316

317
    const allowedStorage = defineStorageType(config.enabledStorageTypes);
63✔
318

319
    let rrttStrtTime = 0;
63✔
320
    let partnerData = {};
63✔
321
    let shouldCallServer = false;
63✔
322
    FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`;
63✔
323
    const cmpData = getCmpData();
63✔
324
    const gdprDetected = cmpData.gdprString;
63✔
325
    firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage));
63✔
326
    const isGroupB = firstPartyData?.group === WITHOUT_IIQ;
63✔
327
    const currentBrowserLowerCase = detectBrowser();
63✔
328
    const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : '';
63✔
329
    const isBlacklisted = browserBlackList?.includes(currentBrowserLowerCase);
63!
330
    let newUser = false;
63✔
331

332
    setGamReporting(gamObjectReference, gamParameterName, firstPartyData?.group, isBlacklisted);
63✔
333

334
    if (groupChanged) groupChanged(firstPartyData?.group || NOT_YET_DEFINED);
63!
335

336
    callbackTimeoutID = setTimeout(() => {
63✔
337
      firePartnerCallback();
63✔
338
    }, configParams.timeoutInMillis || 500
126✔
339
    );
340

341
    if (!firstPartyData?.pcid) {
63✔
342
      const firstPartyId = generateGUID();
53✔
343
      firstPartyData = {
53✔
344
        pcid: firstPartyId,
345
        pcidDate: Date.now(),
346
        group: NOT_YET_DEFINED,
347
        uspString: EMPTY,
348
        gppString: EMPTY,
349
        gdprString: EMPTY,
350
        date: Date.now()
351
      };
352
      newUser = true;
53✔
353
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
53✔
354
    } else if (!firstPartyData.pcidDate) {
10✔
355
      firstPartyData.pcidDate = Date.now();
7✔
356
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
7✔
357
    }
358

359
    if (gdprDetected && !('isOptedOut' in firstPartyData)) {
63✔
360
      firstPartyData.isOptedOut = true;
4✔
361
    }
362

363
    // Read client hints from storage
364
    let clientHints = readData(CLIENT_HINTS_KEY, allowedStorage);
63✔
365
    const chSupported = isCHSupported();
63✔
366
    let chPromise = null;
63✔
367

368
    function fetchAndHandleCH() {
369
      return navigator.userAgentData.getHighEntropyValues(CH_KEYS)
62✔
370
        .then(raw => {
371
          const nextCH = handleClientHints(raw) || '';
61!
372
          const prevCH = clientHints || '';
61✔
373
          if (nextCH !== prevCH) {
61✔
374
            clientHints = nextCH;
61✔
375
            storeData(CLIENT_HINTS_KEY, clientHints, allowedStorage, firstPartyData);
61✔
376
          }
377
          return nextCH;
61✔
378
        })
379
        .catch(err => {
380
          logError('CH fetch failed', err);
1✔
381
          if (clientHints !== '') {
1✔
382
            clientHints = '';
1✔
383
            removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
384
          }
385
          return '';
1✔
386
        });
387
    }
388

389
    if (chSupported) {
63✔
390
      chPromise = fetchAndHandleCH();
62✔
391
      chPromise.catch(err => {
62✔
392
        logError('fetchAndHandleCH failed', err);
×
393
      });
394
    } else {
395
      clientHints = '';
1✔
396
      removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
397
    }
398

399
    function waitOnCH(timeoutMs) {
400
      const timeout = new Promise(resolve => setTimeout(() => resolve(''), timeoutMs));
54✔
401
      return Promise.race([chPromise, timeout]);
54✔
402
    }
403

404
    const savedData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage))
63✔
405
    if (savedData) {
63✔
406
      partnerData = savedData;
6✔
407

408
      if (typeof partnerData.callCount === 'number') callCount = partnerData.callCount;
6✔
409
      if (typeof partnerData.failCount === 'number') failCount = partnerData.failCount;
6✔
410
      if (typeof partnerData.noDataCounter === 'number') noDataCount = partnerData.noDataCounter;
6✔
411

412
      if (partnerData.wsrvcll) {
6!
413
        partnerData.wsrvcll = false;
×
414
        storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
×
415
      }
416
    }
417

418
    if (partnerData.data) {
63✔
419
      if (partnerData.data.length) { // encrypted data
1✔
420
        decryptedData = tryParse(decryptData(partnerData.data));
1✔
421
        runtimeEids = decryptedData;
1✔
422
      }
423
    }
424

425
    if (!isCMPStringTheSame(firstPartyData, cmpData) ||
63!
426
          !firstPartyData.sCal ||
427
          (savedData && (!partnerData.cttl || !partnerData.date || Date.now() - partnerData.date > partnerData.cttl))) {
428
      firstPartyData.uspString = cmpData.uspString;
61✔
429
      firstPartyData.gppString = cmpData.gppString;
61✔
430
      firstPartyData.gdprString = cmpData.gdprString;
61✔
431
      shouldCallServer = true;
61✔
432
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
61✔
433
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
61✔
434
    }
435
    if (!shouldCallServer) {
63✔
436
      if (!savedData && !firstPartyData.isOptedOut) {
2✔
437
        shouldCallServer = true;
1✔
438
      } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_24;
1✔
439
    }
440

441
    if (firstPartyData.isOptedOut) {
63✔
442
      partnerData.data = runtimeEids = { eids: [] };
5✔
443
      firePartnerCallback()
5✔
444
    }
445

446
    if (firstPartyData.group === WITHOUT_IIQ || (firstPartyData.group !== WITHOUT_IIQ && runtimeEids?.eids?.length)) {
63✔
447
      firePartnerCallback()
1✔
448
    }
449

450
    function buildAndSendPixel(ch) {
451
      const url = createPixelUrl(firstPartyData, ch, configParams, partnerData, cmpData);
17✔
452
      sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser);
17✔
453
    }
454

455
    // Check if current browser is in blacklist
456
    if (isBlacklisted) {
63✔
457
      logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.');
17✔
458
      if (configParams.callback) configParams.callback('');
17✔
459

460
      if (chSupported) {
17!
461
        if (clientHints) {
17✔
462
          buildAndSendPixel(clientHints)
1✔
463
        } else {
464
          waitOnCH(chTimeout)
16✔
465
            .then(ch => buildAndSendPixel(ch || ''));
16!
466
        }
467
      } else {
UNCOV
468
        buildAndSendPixel('');
×
469
      }
470
      return;
17✔
471
    }
472

473
    if (!shouldCallServer) {
46✔
474
      if (isGroupB) runtimeEids = { eids: [] };
1!
475
      firePartnerCallback();
1✔
476
      updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
1✔
477
      return { id: runtimeEids.eids };
1✔
478
    }
479

480
    // use protocol relative urls for http or https
481
    let url = `${iiqServerAddress(configParams, gdprDetected)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`;
45✔
482
    url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : '';
45✔
483
    url = appendFirstPartyData(url, firstPartyData, partnerData);
45✔
484
    url = appendPartnersFirstParty(url, configParams);
45✔
485
    url += (partnerData.cttl) ? '&cttl=' + encodeURIComponent(partnerData.cttl) : '';
45✔
486
    url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : '';
45✔
487
    url = appendCMPData(url, cmpData);
45✔
488
    url += '&japs=' + encodeURIComponent(configParams.siloEnabled === true);
45✔
489
    url = appendCounters(url);
45✔
490
    url += VERSION ? '&jsver=' + VERSION : '';
45!
491
    url += firstPartyData?.group ? '&testGroup=' + encodeURIComponent(firstPartyData.group) : '';
45✔
492
    url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
45✔
493
    url = handleAdditionalParams(currentBrowserLowerCase, url, 1, additionalParams);
45✔
494
    url = appendSPData(url, firstPartyData)
45✔
495
    url += '&source=' + PREBID;
45✔
496

497
    // Add vrref and fui to the URL
498
    url = appendVrrefAndFui(url, configParams.domainName);
45✔
499

500
    const storeFirstPartyData = () => {
45✔
501
      partnerData.eidl = runtimeEids?.eids?.length || -1
18!
502
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
18✔
503
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
18✔
504
    }
505

506
    const resp = function (callback) {
45✔
507
      const callbacks = {
42✔
508
        success: response => {
509
          const respJson = tryParse(response);
20✔
510
          // If response is a valid json and should save is true
511
          if (respJson) {
20✔
512
            partnerData.date = Date.now();
19✔
513
            firstPartyData.sCal = Date.now();
19✔
514
            const defineEmptyDataAndFireCallback = () => {
19✔
515
              respJson.data = partnerData.data = runtimeEids = { eids: [] };
2✔
516
              storeFirstPartyData()
2✔
517
              firePartnerCallback()
2✔
518
              callback(runtimeEids)
2✔
519
            }
520
            if (callbackTimeoutID) clearTimeout(callbackTimeoutID)
19✔
521
            if ('cttl' in respJson) {
19!
522
              partnerData.cttl = respJson.cttl;
×
523
            } else partnerData.cttl = HOURS_24;
19✔
524

525
            if ('tc' in respJson) {
19✔
526
              partnerData.terminationCause = respJson.tc;
4✔
527
              if (Number(respJson.tc) === 41) {
4✔
528
                firstPartyData.group = WITHOUT_IIQ;
1✔
529
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
530
                if (groupChanged) groupChanged(firstPartyData.group);
1✔
531
                defineEmptyDataAndFireCallback();
1✔
532
                if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
1✔
533
                return
1✔
534
              } else {
535
                firstPartyData.group = WITH_IIQ;
3✔
536
                if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
3✔
537
                if (groupChanged) groupChanged(firstPartyData.group);
3✔
538
              }
539
            }
540
            if ('isOptedOut' in respJson) {
18✔
541
              if (respJson.isOptedOut !== firstPartyData.isOptedOut) {
4✔
542
                firstPartyData.isOptedOut = respJson.isOptedOut;
3✔
543
              }
544
              if (respJson.isOptedOut === true) {
4✔
545
                respJson.data = partnerData.data = runtimeEids = { eids: [] };
1✔
546

547
                const keysToRemove = [
1✔
548
                  PARTNER_DATA_KEY,
549
                  CLIENT_HINTS_KEY
550
                ];
551

552
                keysToRemove.forEach(key => removeDataByKey(key, allowedStorage));
2✔
553

554
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
555
                firePartnerCallback();
1✔
556
                callback(runtimeEids);
1✔
557
                return
1✔
558
              }
559
            }
560
            if ('pid' in respJson) {
17✔
561
              firstPartyData.pid = respJson.pid;
5✔
562
            }
563
            if ('dbsaved' in respJson) {
17✔
564
              firstPartyData.dbsaved = respJson.dbsaved;
1✔
565
            }
566
            if ('ls' in respJson) {
17✔
567
              if (respJson.ls === false) {
8✔
568
                defineEmptyDataAndFireCallback()
1✔
569
                return
1✔
570
              }
571
              // If data is empty, means we should save as INVALID_ID
572
              if (respJson.data === '') {
7!
573
                respJson.data = INVALID_ID;
×
574
              } else {
575
                // If data is a single string, assume it is an id with source intentiq.com
576
                if (respJson.data && typeof respJson.data === 'string') {
7✔
577
                  respJson.data = {eids: [respJson.data]}
5✔
578
                }
579
              }
580
              partnerData.data = respJson.data;
7✔
581
            }
582

583
            if ('ct' in respJson) {
16✔
584
              partnerData.clientType = respJson.ct;
1✔
585
            }
586

587
            if ('sid' in respJson) {
16!
588
              partnerData.siteId = respJson.sid;
×
589
            }
590

591
            if ('spd' in respJson) {
16✔
592
              // server provided data
593
              firstPartyData.spd = respJson.spd;
1✔
594
            }
595
            if ('gpr' in respJson) {
16!
596
              // GAM prediction reporting
597
              partnerData.gpr = respJson.gpr;
×
598
            } else {
599
              delete partnerData.gpr // remove prediction flag in case server doesn't provide it
16✔
600
            }
601

602
            if (rrttStrtTime && rrttStrtTime > 0) {
16✔
603
              partnerData.rrtt = Date.now() - rrttStrtTime;
16✔
604
            }
605

606
            if (respJson.data?.eids) {
16✔
607
              runtimeEids = respJson.data
8✔
608
              callback(respJson.data.eids);
8✔
609
              firePartnerCallback()
8✔
610
              const encryptedData = encryptData(JSON.stringify(respJson.data));
8✔
611
              partnerData.data = encryptedData;
8✔
612
            } else {
613
              callback(runtimeEids);
8✔
614
              firePartnerCallback()
8✔
615
            }
616
            updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
16✔
617
            storeFirstPartyData();
16✔
618
          } else {
619
            callback(runtimeEids);
1✔
620
            firePartnerCallback()
1✔
621
          }
622
        },
623
        error: error => {
624
          logError(MODULE_NAME + ': ID fetch encountered an error', error);
2✔
625
          failCount++;
2✔
626
          updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
2✔
627
          callback(runtimeEids);
2✔
628
        }
629
      };
630
      rrttStrtTime = Date.now();
42✔
631

632
      partnerData.wsrvcll = true;
42✔
633
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
42✔
634
      clearCountersAndStore(allowedStorage, partnerData);
42✔
635

636
      const sendAjax = uh => {
42✔
637
        if (uh) url += '&uh=' + encodeURIComponent(uh);
42✔
638
        ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true });
42✔
639
      }
640

641
      if (chSupported) {
42✔
642
        if (clientHints) {
41✔
643
          // CH found in LS: send immediately; background fetch will refresh/clear later
644
          sendAjax(clientHints);
3✔
645
        } else {
646
          // No CH in LS: wait up to chTimeout, then send
647
          waitOnCH(chTimeout).then(ch => {
38✔
648
            // Send with received CH or without it
649
            sendAjax(ch || '');
38!
650
          })
651
        }
652
      } else {
653
        // CH not supported: send without uh
654
        sendAjax('');
1✔
655
      }
656
    };
657
    const respObj = {callback: resp};
45✔
658

659
    if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids;
45✔
660
    return respObj
45✔
661
  },
662
  eids: {
663
    'intentIqId': {
664
      source: 'intentiq.com',
665
      atype: 1,
666
      getSource: function (data) {
667
        return data.source;
×
668
      },
669
      getValue: function (data) {
670
        if (data?.uids?.length) {
×
671
          return data.uids[0].id
×
672
        }
673
        return null
×
674
      },
675
      getUidExt: function (data) {
676
        if (data?.uids?.length) {
×
677
          return data.uids[0].ext;
×
678
        }
679
        return null
×
680
      }
681
    },
682
  }
683
};
684

685
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