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

prebid / Prebid.js / 22957809714

11 Mar 2026 02:32PM UTC coverage: 96.333% (+0.003%) from 96.33%
22957809714

push

github

web-flow
Seedtag Adapter: change request params (#14521)

* feat: change request params

* Refactor seedtagBidAdapter: optimize query parameter construction in getTimeoutUrl function.

* fix test

---------

Co-authored-by: Yohan Boutin <yohan@seedtag.com>

57031 of 69757 branches covered (81.76%)

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

35 existing lines in 5 files now uncovered.

217714 of 226002 relevant lines covered (96.33%)

70.51 hits per line

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

94.72
/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) {
81✔
208
  if (isBlacklisted) return;
81✔
209
  if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) {
60✔
210
    gamObjectReference.cmd.push(() => {
36✔
211
      if (typeof gamObjectReference.setConfig === 'function') {
9✔
212
        gamObjectReference.setConfig({
1✔
213
          targeting: {
214
            [gamParameterName]: userGroup
215
          }
216
        });
217
        return;
1✔
218
      }
219
      // Fallback in case an older version of Google Publisher Tag is used.
220
      gamObjectReference?.pubads?.()?.setTargeting?.(gamParameterName, userGroup);
8✔
221
    });
222
  }
223
}
224

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

251
export function isCMPStringTheSame(fpData, cmpData) {
252
  const firstPartyDataCPString = `${fpData.gdprString}${fpData.gppString}${fpData.uspString}`;
83✔
253
  const cmpDataString = `${cmpData.gdprString}${cmpData.gppString}${cmpData.uspString}`;
83✔
254
  return firstPartyDataCPString === cmpDataString;
83✔
255
}
256

257
function updateCountersAndStore(runtimeEids, allowedStorage, partnerData) {
258
  if (!runtimeEids?.eids?.length) {
22✔
259
    noDataCount++;
15✔
260
  } else {
261
    callCount++;
7✔
262
  }
263
  storeCounters(allowedStorage, partnerData);
22✔
264
}
265

266
function clearCountersAndStore(allowedStorage, partnerData) {
267
  callCount = 0;
49✔
268
  failCount = 0;
49✔
269
  noDataCount = 0;
49✔
270
  storeCounters(allowedStorage, partnerData);
49✔
271
}
272

273
function storeCounters(storage, partnerData) {
274
  partnerData.callCount = callCount;
71✔
275
  partnerData.failCount = failCount;
71✔
276
  partnerData.noDataCounter = noDataCount;
71✔
277
  storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), storage, firstPartyData);
71✔
278
}
279

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

298
  /**
299
   * performs action to obtain id and return a value in the callback's response argument
300
   * @function
301
   * @param {SubmoduleConfig} [config]
302
   * @returns {IdResponse|undefined}
303
   */
304
  getId(config) {
305
    const configParams = (config?.params) || {};
78!
306

307
    const firePartnerCallback = () => {
78✔
308
      if (configParams.callback && !callbackFired) {
110✔
309
        callbackFired = true;
6✔
310
        if (callbackTimeoutID) clearTimeout(callbackTimeoutID);
6✔
311
        let data = runtimeEids;
6✔
312
        if (data?.eids?.length === 1 && typeof data.eids[0] === 'string') data = data.eids[0];
6!
313
        configParams.callback(data);
6✔
314
      }
315
      updateGlobalObj()
110✔
316
    }
317

318
    if (typeof configParams.partner !== 'number') {
78✔
319
      logError('User ID - intentIqId submodule requires a valid partner to be defined');
3✔
320
      firePartnerCallback()
3✔
321
      return;
3✔
322
    }
323

324
    initializeGlobalIIQ(configParams.partner)
75✔
325

326
    let decryptedData, callbackTimeoutID;
327
    let callbackFired = false;
75✔
328
    let runtimeEids = { eids: [] };
75✔
329

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

340
    const allowedStorage = defineStorageType(config.enabledStorageTypes);
75✔
341
    partnerData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage)) || {};
75✔
342

343
    let rrttStrtTime = 0;
75✔
344
    let shouldCallServer = false;
75✔
345
    FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`;
75✔
346
    const cmpData = getCmpData();
75✔
347
    const gdprDetected = cmpData.gdprString;
75✔
348
    firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage));
75✔
349
    actualABGroup = defineABTestingGroup(configParams, partnerData?.terminationCause);
75!
350
    const currentBrowserLowerCase = detectBrowser();
75✔
351
    const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : '';
75✔
352
    const isBlacklisted = browserBlackList?.includes(currentBrowserLowerCase);
75!
353
    let newUser = false;
75✔
354

355
    setGamReporting(gamObjectReference, gamParameterName, actualABGroup, isBlacklisted);
75✔
356

357
    if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause);
75!
358

359
    callbackTimeoutID = setTimeout(() => {
75✔
360
      firePartnerCallback();
71✔
361
    }, configParams.timeoutInMillis || 500
150✔
362
    );
363

364
    if (!firstPartyData?.pcid) {
75✔
365
      const firstPartyId = generateGUID();
69✔
366
      firstPartyData = {
69✔
367
        pcid: firstPartyId,
368
        pcidDate: Date.now(),
369
        uspString: EMPTY,
370
        gppString: EMPTY,
371
        gdprString: EMPTY,
372
        date: Date.now()
373
      };
374
      newUser = true;
69✔
375
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
69✔
376
    } else if (!firstPartyData.pcidDate) {
6✔
377
      firstPartyData.pcidDate = Date.now();
3✔
378
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
3✔
379
    }
380

381
    if (gdprDetected && !('isOptedOut' in firstPartyData)) {
75✔
382
      firstPartyData.isOptedOut = true;
11✔
383
    }
384

385
    // Read client hints from storage
386
    clientHints = readData(CLIENT_HINTS_KEY, allowedStorage);
75✔
387
    const chSupported = isCHSupported();
75✔
388
    let chPromise = null;
75✔
389

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

411
    if (chSupported) {
75✔
412
      chPromise = fetchAndHandleCH();
74✔
413
      chPromise.catch(err => {
74✔
UNCOV
414
        logError('fetchAndHandleCH failed', err);
×
415
      });
416
    } else {
417
      clientHints = '';
1✔
418
      removeDataByKey(CLIENT_HINTS_KEY, allowedStorage)
1✔
419
    }
420

421
    function waitOnCH(timeoutMs) {
422
      const timeout = new Promise(resolve => setTimeout(() => resolve(''), timeoutMs));
65✔
423
      return Promise.race([chPromise, timeout]);
65✔
424
    }
425

426
    if (typeof partnerData.callCount === 'number') callCount = partnerData.callCount;
75✔
427
    if (typeof partnerData.failCount === 'number') failCount = partnerData.failCount;
75✔
428
    if (typeof partnerData.noDataCounter === 'number') noDataCount = partnerData.noDataCounter;
75✔
429
    if (partnerData.wsrvcll) {
75!
UNCOV
430
      partnerData.wsrvcll = false;
×
UNCOV
431
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
×
432
    }
433

434
    if (partnerData.data) {
75✔
435
      if (partnerData.data.length) { // encrypted data
1✔
436
        decryptedData = tryParse(decryptData(partnerData.data));
1✔
437
        runtimeEids = decryptedData;
1✔
438
      }
439
    }
440

441
    function updateGlobalObj() {
442
      if (globalName) {
163✔
443
        window[globalName].partnerData = partnerData
163✔
444
        window[globalName].firstPartyData = firstPartyData
163✔
445
        window[globalName].clientHints = clientHints
163✔
446
        window[globalName].actualABGroup = actualABGroup
163✔
447
      }
448
    }
449

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

467
    if (firstPartyData.isOptedOut) {
75✔
468
      partnerData.data = runtimeEids = { eids: [] };
12✔
469
      firePartnerCallback()
12✔
470
    }
471

472
    if (runtimeEids?.eids?.length) {
75✔
473
      firePartnerCallback()
1✔
474
    }
475

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

481
    // Check if current browser is in blacklist
482
    if (isBlacklisted) {
75✔
483
      logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.');
21✔
484
      if (configParams.callback) configParams.callback('');
21✔
485

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

499
    if (!shouldCallServer) {
54✔
500
      firePartnerCallback();
1✔
501
      updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
1✔
502
      return { id: runtimeEids.eids };
1✔
503
    }
504

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

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

526
    // Add vrref and fui to the URL
527
    url = appendVrrefAndFui(url, configParams.domainName);
53✔
528

529
    const storeFirstPartyData = () => {
53✔
530
      partnerData.eidl = runtimeEids?.eids?.length || -1
20!
531
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
20✔
532
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
20✔
533
    }
534

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

557
            if ('tc' in respJson) {
21✔
558
              partnerData.terminationCause = respJson.tc;
5✔
559
              actualABGroup = defineABTestingGroup(configParams, respJson.tc,);
5✔
560

561
              if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup);
5✔
562
              if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause);
5!
563
            }
564
            if ('isOptedOut' in respJson) {
21✔
565
              if (respJson.isOptedOut !== firstPartyData.isOptedOut) {
5✔
566
                firstPartyData.isOptedOut = respJson.isOptedOut;
4✔
567
              }
568
              if (respJson.isOptedOut === true) {
5✔
569
                respJson.data = partnerData.data = runtimeEids = { eids: [] };
1✔
570

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

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

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

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

611
            if ('sid' in respJson) {
19!
UNCOV
612
              partnerData.siteId = respJson.sid;
×
613
            }
614

615
            if ('spd' in respJson) {
19✔
616
              // server provided data
617
              partnerData.spd = respJson.spd;
1✔
618
            }
619

620
            if ('abTestUuid' in respJson) {
19!
621
              if ('ls' in respJson && respJson.ls === true) {
×
UNCOV
622
                partnerData.abTestUuid = respJson.abTestUuid;
×
623
              }
624
            }
625

626
            if ('gpr' in respJson) {
19!
627
              // GAM prediction reporting
UNCOV
628
              partnerData.gpr = respJson.gpr;
×
629
            } else {
630
              delete partnerData.gpr // remove prediction flag in case server doesn't provide it
19✔
631
            }
632

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

658
      partnerData.wsrvcll = true;
49✔
659
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
49✔
660
      clearCountersAndStore(allowedStorage, partnerData);
49✔
661

662
      rrttStrtTime = Date.now();
49✔
663

664
      const sendAjax = uh => {
49✔
665
        if (uh) url += '&uh=' + encodeURIComponent(uh);
49✔
666
        ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true });
49✔
667
      }
668

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

687
    if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids;
53✔
688
    return respObj
53✔
689
  },
690
  eids: {
691
    'intentIqId': {
692
      source: 'intentiq.com',
693
      atype: 1,
694
      getSource: function (data) {
UNCOV
695
        return data.source;
×
696
      },
697
      getValue: function (data) {
698
        if (data?.uids?.length) {
×
UNCOV
699
          return data.uids[0].id
×
700
        }
UNCOV
701
        return null
×
702
      },
703
      getUidExt: function (data) {
UNCOV
704
        if (data?.uids?.length) {
×
UNCOV
705
          return data.uids[0].ext;
×
706
        }
UNCOV
707
        return null
×
708
      }
709
    },
710
  }
711
};
712

713
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