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

prebid / Prebid.js / 17247435260

26 Aug 2025 06:44PM UTC coverage: 96.244% (-0.005%) from 96.249%
17247435260

push

github

web-flow
Bump @babel/runtime from 7.27.6 to 7.28.3 (#13805)

Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.27.6 to 7.28.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.28.3/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-version: 7.28.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

39653 of 48738 branches covered (81.36%)

196498 of 204166 relevant lines covered (96.24%)

124.75 hits per line

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

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

8
import {
9
  deepAccess,
10
  deepClone,
11
  deepSetValue,
12
  isEmpty,
13
  isEmptyStr,
14
  isPlainObject,
15
  logError,
16
  logInfo,
17
  logWarn
18
} from '../src/utils.js';
19
import {fetch} from '../src/ajax.js';
20
import {submodule} from '../src/hook.js';
21
import {getRefererInfo} from '../src/refererDetection.js';
22
import {getStorageManager} from '../src/storageManager.js';
23
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
24
import {PbPromise} from '../src/utils/promise.js';
25
import {loadExternalScript} from '../src/adloader.js';
26

27
/**
28
 * @typedef {import('../modules/userId/spec.ts').IdProviderSpec} Submodule
29
 * @typedef {import('../modules/userId/spec.ts').UserIdConfig} SubmoduleConfig
30
 * @typedef {import('../src/consentHandler').AllConsentData} ConsentData
31
 * @typedef {import('../modules/userId/spec.ts').ProviderResponse} ProviderResponse
32
 */
33

34
const MODULE_NAME = 'id5Id';
1✔
35
const GVLID = 131;
1✔
36
export const ID5_STORAGE_NAME = 'id5id';
1✔
37
const LOG_PREFIX = 'User ID - ID5 submodule: ';
1✔
38
const ID5_API_CONFIG_URL = 'https://id5-sync.com/api/config/prebid';
1✔
39
const ID5_DOMAIN = 'id5-sync.com';
1✔
40
const TRUE_LINK_SOURCE = 'true-link-id5-sync.com';
1✔
41

42
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});
1✔
43

44
/**
45
 * @typedef {Object} Id5Response
46
 * @property {string} [universal_uid] - The encrypted ID5 ID to pass to bidders
47
 * @property {Object} [ext] - The extensions object to pass to bidders
48
 * @property {Object} [ab_testing] - A/B testing configuration
49
 * @property {Object} [ids]
50
 * @property {string} signature
51
 * @property {number} [nbPage]
52
 * @property {string} [publisherTrueLinkId] - The publisher's TrueLink ID
53
 */
54

55
/**
56
 * @typedef {Object.<string, Id5Response>} PartnerId5Responses
57
 */
58

59
/**
60
 * @typedef {Id5Response} Id5PrebidResponse
61
 * @property {PartnerId5Responses} pbjs
62
 *
63
 */
64

65
/**
66
 * @typedef {Object} FetchCallConfig
67
 * @property {string} [url] - The URL for the fetch endpoint
68
 * @property {Object} [overrides] - Overrides to apply to fetch parameters
69
 */
70

71
/**
72
 * @typedef {Object} ExtensionsCallConfig
73
 * @property {string} [url] - The URL for the extensions endpoint
74
 * @property {string} [method] - Overrides the HTTP method to use to make the call
75
 * @property {Object} [body] - Specifies a body to pass to the extensions endpoint
76
 */
77

78
/**
79
 * @typedef {Object} DynamicConfig
80
 * @property {FetchCallConfig} [fetchCall] - The fetch call configuration
81
 * @property {ExtensionsCallConfig} [extensionsCall] - The extensions call configuration
82
 */
83

84
/**
85
 * @typedef {Object} ABTestingConfig
86
 * @property {boolean} enabled - Tells whether A/B testing is enabled for this instance
87
 * @property {number} controlGroupPct - A/B testing probability
88
 */
89

90
/**
91
 * @typedef {Object} Multiplexing
92
 * @property {boolean} [disabled] - Disable multiplexing (instance will work in single mode)
93
 */
94

95
/**
96
 * @typedef {Object} Diagnostics
97
 * @property {boolean} [publishingDisabled] - Disable diagnostics publishing
98
 * @property {number} [publishAfterLoadInMsec] - Delay in ms after script load after which collected diagnostics are published
99
 * @property {boolean} [publishBeforeWindowUnload] - When true, diagnostics publishing is triggered on Window 'beforeunload' event
100
 * @property {number} [publishingSampleRatio] - Diagnostics publishing sample ratio
101
 */
102

103
/**
104
 * @typedef {Object} Segment
105
 * @property {string} [destination] - GVL ID or ID5-XX Partner ID. Mandatory
106
 * @property {Array<string>} [ids] - The segment IDs to push. Must contain at least one segment ID.
107
 */
108

109
/**
110
 * @typedef {Object} Id5PrebidConfig
111
 * @property {number} partner - The ID5 partner ID
112
 * @property {string} pd - The ID5 partner data string
113
 * @property {ABTestingConfig} abTesting - The A/B testing configuration
114
 * @property {boolean} disableExtensions - Disabled extensions call
115
 * @property {string} [externalModuleUrl] - URL for the id5 prebid external module
116
 * @property {Multiplexing} [multiplexing] - Multiplexing options. Only supported when loading the external module.
117
 * @property {Diagnostics} [diagnostics] - Diagnostics options. Supported only in multiplexing
118
 * @property {Array<Segment>} [segments] - A list of segments to push to partners. Supported only in multiplexing.
119
 * @property {boolean} [disableUaHints] - When true, look up of high entropy values through user agent hints is disabled.
120
 * @property {string} [gamTargetingPrefix] - When set, the GAM targeting tags will be set and use the specified prefix, for example 'id5'.
121
 */
122

123
/**
124
 * @typedef {SubmoduleConfig} Id5SubmoduleConfig
125
 * @property {Id5PrebidConfig} params
126
 */
127

128
const DEFAULT_EIDS = {
1✔
129
  'id5id': {
130
    getValue: function (data) {
131
      return data.uid;
11✔
132
    },
133
    source: ID5_DOMAIN,
134
    atype: 1,
135
    getUidExt: function (data) {
136
      if (data.ext) {
11✔
137
        return data.ext;
10✔
138
      }
139
    }
140
  },
141
  'euid': {
142
    getValue: function (data) {
143
      return data.uid;
2✔
144
    },
145
    getSource: function (data) {
146
      return data.source;
2✔
147
    },
148
    atype: 3,
149
    getUidExt: function (data) {
150
      if (data.ext) {
2✔
151
        return data.ext;
2✔
152
      }
153
    }
154
  },
155
  'trueLinkId': {
156
    getValue: function (data) {
157
      return data.uid;
2✔
158
    },
159
    getSource: function () {
160
      return TRUE_LINK_SOURCE;
2✔
161
    },
162
    atype: 1,
163
    getUidExt: function (data) {
164
      if (data.ext) {
2!
165
        return data.ext;
×
166
      }
167
    }
168
  }
169
};
170

171
/** @type {Submodule} */
172
export const id5IdSubmodule = {
1✔
173
  /**
174
   * used to link submodule with config
175
   * @type {string}
176
   */
177
  name: 'id5Id',
178

179
  /**
180
   * Vendor id of ID5
181
   * @type {Number}
182
   */
183
  gvlid: GVLID,
184

185
  /**
186
   * decode the stored id value for passing to bid requests
187
   * @function decode
188
   * @param {Id5PrebidResponse|Id5Response} value
189
   * @param {Id5SubmoduleConfig} config
190
   * @returns {(Object|undefined)}
191
   */
192
  decode(value, config) {
193
    const partnerResponse = getPartnerResponse(value, config.params)
32✔
194
    // get generic/legacy response in case no partner specific
195
    // it may happen in case old cached value found
196
    // or overwritten by other integration (older version)
197
    return this._decodeResponse(partnerResponse || value, config);
32✔
198
  },
199

200
  /**
201
   *
202
   * @param {Id5Response} value
203
   * @param {Id5SubmoduleConfig} config
204
   * @private
205
   */
206
  _decodeResponse(value, config) {
207
    if (value && value.ids !== undefined) {
32✔
208
      const responseObj = {};
8✔
209
      const eids = {};
8✔
210
      Object.entries(value.ids).forEach(([key, value]) => {
15✔
211
        const eid = value.eid;
15✔
212
        const uid = eid?.uids?.[0]
15✔
213
        responseObj[key] = {
15✔
214
          uid: uid?.id,
215
          ext: uid?.ext
216
        };
217
        eids[key] = function () {
15✔
218
          return eid;
14✔
219
        }; // register function to get eid for each id (key) decoded
220
      });
221
      this.eids = eids; // overwrite global eids
8✔
222
      updateTargeting(value, config);
8✔
223
      return responseObj;
8✔
224
    }
225

226
    let universalUid, publisherTrueLinkId;
227
    let ext = {};
24✔
228

229
    if (value && typeof value.universal_uid === 'string') {
24✔
230
      universalUid = value.universal_uid;
23✔
231
      ext = value.ext || ext;
23!
232
      publisherTrueLinkId = value.publisherTrueLinkId;
23✔
233
    } else {
234
      return undefined;
1✔
235
    }
236
    this.eids = DEFAULT_EIDS;
23✔
237
    const responseObj = {
23✔
238
      id5id: {
239
        uid: universalUid,
240
        ext: ext
241
      }
242
    };
243

244
    if (isPlainObject(ext.euid)) {
23✔
245
      responseObj.euid = {
3✔
246
        uid: ext.euid.uids[0].id,
247
        source: ext.euid.source,
248
        ext: {provider: ID5_DOMAIN}
249
      };
250
    }
251

252
    if (publisherTrueLinkId) {
23✔
253
      responseObj.trueLinkId = {
3✔
254
        uid: publisherTrueLinkId
255
      };
256
    }
257

258
    const abTestingResult = deepAccess(value, 'ab_testing.result');
23✔
259
    switch (abTestingResult) {
23✔
260
      case 'control':
261
        // A/B Testing is enabled and user is in the Control Group
262
        logInfo(LOG_PREFIX + 'A/B Testing - user is in the Control Group: ID5 ID is NOT exposed');
2✔
263
        deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', true);
2✔
264
        break;
2✔
265
      case 'error':
266
        // A/B Testing is enabled, but configured improperly, so skip A/B testing
267
        logError(LOG_PREFIX + 'A/B Testing ERROR! controlGroupPct must be a number >= 0 and <= 1');
1✔
268
        break;
1✔
269
      case 'normal':
270
        // A/B Testing is enabled but user is not in the Control Group, so ID5 ID is shared
271
        logInfo(LOG_PREFIX + 'A/B Testing - user is NOT in the Control Group');
3✔
272
        deepSetValue(responseObj, 'id5id.ext.abTestingControlGroup', false);
3✔
273
        break;
3✔
274
    }
275

276
    logInfo(LOG_PREFIX + 'Decoded ID', responseObj);
23✔
277
    updateTargeting(value, config);
23✔
278

279
    return responseObj;
23✔
280
  },
281

282
  /**
283
   * performs action to obtain id and return a value in the callback's response argument
284
   * @function getId
285
   * @param {Id5SubmoduleConfig} submoduleConfig
286
   * @param {ConsentData} consentData
287
   * @param {(Object|undefined)} cacheIdObj
288
   * @returns {ProviderResponse}
289
   */
290
  getId(submoduleConfig, consentData, cacheIdObj) {
291
    if (!validateConfig(submoduleConfig)) {
62✔
292
      return undefined;
18✔
293
    }
294

295
    if (!hasWriteConsentToLocalStorage(consentData?.gdpr)) {
44!
296
      logInfo(LOG_PREFIX + 'Skipping ID5 local storage write because no consent given.');
×
297
      return undefined;
×
298
    }
299

300
    const resp = function (cbFunction) {
44✔
301
      const fetchFlow = new IdFetchFlow(submoduleConfig, consentData?.gdpr, cacheIdObj, consentData?.usp, consentData?.gpp);
41✔
302
      fetchFlow.execute()
41✔
303
        .then(response => {
304
          cbFunction(createResponse(response, submoduleConfig.params, cacheIdObj));
34✔
305
        })
306
        .catch(error => {
307
          logError(LOG_PREFIX + 'getId fetch encountered an error', error);
×
308
          cbFunction();
×
309
        });
310
    };
311
    return {callback: resp};
44✔
312
  },
313

314
  /**
315
   * Similar to Submodule#getId, this optional method returns response to for id that exists already.
316
   *  If IdResponse#id is defined, then it will be written to the current active storage even if it exists already.
317
   *  If IdResponse#callback is defined, then it'll called at the end of auction.
318
   *  It's permissible to return neither, one, or both fields.
319
   * @function extendId
320
   * @param {Id5SubmoduleConfig} config
321
   * @param {ConsentData} consentData
322
   * @param {Id5PrebidResponse} cacheIdObj - existing id, if any
323
   * @return {ProviderResponse} A response object that contains id.
324
   */
325
  extendId(config, consentData, cacheIdObj) {
326
    if (!hasWriteConsentToLocalStorage(consentData?.gdpr)) {
23✔
327
      logInfo(LOG_PREFIX + 'No consent given for ID5 local storage writing, skipping nb increment.');
9✔
328
      return {id: cacheIdObj};
9✔
329
    }
330
    if (getPartnerResponse(cacheIdObj, config.params)) { // response for partner is present
14✔
331
      logInfo(LOG_PREFIX + 'using cached ID', cacheIdObj);
1✔
332
      const updatedObject = deepClone(cacheIdObj);
1✔
333
      const responseToUpdate = getPartnerResponse(updatedObject, config.params);
1✔
334
      responseToUpdate.nbPage = incrementNb(responseToUpdate);
1✔
335
      return {id: updatedObject};
1✔
336
    } else {
337
      logInfo(LOG_PREFIX + ' refreshing ID.  Cached object does not have ID for partner', cacheIdObj);
13✔
338
      return this.getId(config, consentData, cacheIdObj);
13✔
339
    }
340
  },
341
  primaryIds: ['id5id', 'trueLinkId'],
342
  eids: DEFAULT_EIDS,
343
  _reset() {
344
    this.eids = DEFAULT_EIDS;
9✔
345
  }
346
};
347

348
export class IdFetchFlow {
349
  constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData, gppData) {
350
    this.submoduleConfig = submoduleConfig;
41✔
351
    this.gdprConsentData = gdprConsentData;
41✔
352
    this.cacheIdObj = isPlainObject(cacheIdObj?.pbjs) ? cacheIdObj.pbjs[submoduleConfig.params.partner] : cacheIdObj;
41✔
353
    this.usPrivacyData = usPrivacyData;
41✔
354
    this.gppData = gppData;
41✔
355
  }
356

357
  /**
358
   * Calls the ID5 Servers to fetch an ID5 ID
359
   * @returns {Promise<Id5Response>} The result of calling the server side
360
   */
361
  async execute() {
362
    const configCallPromise = this.#callForConfig();
41✔
363
    if (this.#isExternalModule()) {
41✔
364
      try {
2✔
365
        return await this.#externalModuleFlow(configCallPromise);
2✔
366
      } catch (error) {
367
        logError(LOG_PREFIX + 'Error while performing ID5 external module flow. Continuing with regular flow.', error);
1✔
368
        return this.#regularFlow(configCallPromise);
1✔
369
      }
370
    } else {
371
      return this.#regularFlow(configCallPromise);
39✔
372
    }
373
  }
374

375
  #isExternalModule() {
376
    return typeof this.submoduleConfig.params.externalModuleUrl === 'string';
41✔
377
  }
378

379
  async #externalModuleFlow(configCallPromise) {
380
    await loadExternalModule(this.submoduleConfig.params.externalModuleUrl);
2✔
381
    const fetchFlowConfig = await configCallPromise;
2✔
382

383
    return this.#getExternalIntegration().fetchId5Id(fetchFlowConfig, this.submoduleConfig.params, getRefererInfo(), this.gdprConsentData, this.usPrivacyData, this.gppData);
2✔
384
  }
385

386
  #getExternalIntegration() {
387
    return window.id5Prebid && window.id5Prebid.integration;
2✔
388
  }
389

390
  async #regularFlow(configCallPromise) {
391
    const fetchFlowConfig = await configCallPromise;
40✔
392
    const extensionsData = await this.#callForExtensions(fetchFlowConfig.extensionsCall);
33✔
393
    const fetchCallResponse = await this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData);
33✔
394
    return this.#processFetchCallResponse(fetchCallResponse);
33✔
395
  }
396

397
  async #callForConfig() {
398
    const url = this.submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only
41✔
399
    const response = await fetch(url, {
41✔
400
      method: 'POST',
401
      body: JSON.stringify({
402
        ...this.submoduleConfig,
403
        bounce: true
404
      }),
405
      credentials: 'include'
406
    });
407
    if (!response.ok) {
34!
408
      throw new Error('Error while calling config endpoint: ', response);
×
409
    }
410
    const dynamicConfig = await response.json();
34✔
411
    logInfo(LOG_PREFIX + 'config response received from the server', dynamicConfig);
34✔
412
    return dynamicConfig;
34✔
413
  }
414

415
  async #callForExtensions(extensionsCallConfig) {
416
    if (extensionsCallConfig === undefined) {
33✔
417
      return undefined;
31✔
418
    }
419
    const extensionsUrl = extensionsCallConfig.url;
2✔
420
    const method = extensionsCallConfig.method || 'GET';
2!
421
    const body = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {});
2!
422
    const response = await fetch(extensionsUrl, {method, body});
2✔
423
    if (!response.ok) {
2!
424
      throw new Error('Error while calling extensions endpoint: ', response);
×
425
    }
426
    const extensions = await response.json();
2✔
427
    logInfo(LOG_PREFIX + 'extensions response received from the server', extensions);
2✔
428
    return extensions;
2✔
429
  }
430

431
  async #callId5Fetch(fetchCallConfig, extensionsData) {
432
    const fetchUrl = fetchCallConfig.url;
33✔
433
    const additionalData = fetchCallConfig.overrides || {};
33✔
434
    const body = JSON.stringify({
33✔
435
      ...this.#createFetchRequestData(),
436
      ...additionalData,
437
      extensions: extensionsData
438
    });
439
    const response = await fetch(fetchUrl, {method: 'POST', body, credentials: 'include'});
33✔
440
    if (!response.ok) {
33!
441
      throw new Error('Error while calling fetch endpoint: ', response);
×
442
    }
443
    const fetchResponse = await response.json();
33✔
444
    logInfo(LOG_PREFIX + 'fetch response received from the server', fetchResponse);
33✔
445
    return fetchResponse;
33✔
446
  }
447

448
  #createFetchRequestData() {
449
    const params = this.submoduleConfig.params;
33✔
450
    const hasGdpr = (this.gdprConsentData && typeof this.gdprConsentData.gdprApplies === 'boolean' && this.gdprConsentData.gdprApplies) ? 1 : 0;
33✔
451
    const referer = getRefererInfo();
33✔
452
    const signature = this.cacheIdObj ? this.cacheIdObj.signature : undefined;
33✔
453
    const nbPage = incrementNb(this.cacheIdObj);
33✔
454
    const trueLinkInfo = window.id5Bootstrap ? window.id5Bootstrap.getTrueLinkInfo() : {booted: false};
33✔
455

456
    const data = {
33✔
457
      'partner': params.partner,
458
      'gdpr': hasGdpr,
459
      'nbPage': nbPage,
460
      'o': 'pbjs',
461
      'tml': referer.topmostLocation,
462
      'ref': referer.ref,
463
      'cu': referer.canonicalUrl,
464
      'top': referer.reachedTop ? 1 : 0,
33!
465
      'u': referer.stack[0] || window.location.href,
33!
466
      'v': '$prebid.version$',
467
      'storage': this.submoduleConfig.storage,
468
      'localStorage': storage.localStorageIsEnabled() ? 1 : 0,
33✔
469
      'true_link': trueLinkInfo
470
    };
471

472
    // pass in optional data, but only if populated
473
    if (hasGdpr && this.gdprConsentData.consentString !== undefined && !isEmpty(this.gdprConsentData.consentString) && !isEmptyStr(this.gdprConsentData.consentString)) {
33✔
474
      data.gdpr_consent = this.gdprConsentData.consentString;
2✔
475
    }
476
    if (this.usPrivacyData !== undefined && !isEmpty(this.usPrivacyData) && !isEmptyStr(this.usPrivacyData)) {
33✔
477
      data.us_privacy = this.usPrivacyData;
1✔
478
    }
479
    if (this.gppData) {
33✔
480
      data.gpp_string = this.gppData.gppString;
1✔
481
      data.gpp_sid = this.gppData.applicableSections;
1✔
482
    }
483

484
    if (signature !== undefined && !isEmptyStr(signature)) {
33✔
485
      data.s = signature;
15✔
486
    }
487
    if (params.pd !== undefined && !isEmptyStr(params.pd)) {
33✔
488
      data.pd = params.pd;
1✔
489
    }
490
    if (params.provider !== undefined && !isEmptyStr(params.provider)) {
33!
491
      data.provider = params.provider;
×
492
    }
493
    const abTestingConfig = params.abTesting || {enabled: false};
33✔
494

495
    if (abTestingConfig.enabled) {
33✔
496
      data.ab_testing = {
1✔
497
        enabled: true, control_group_pct: abTestingConfig.controlGroupPct // The server validates
498
      };
499
    }
500
    return data;
33✔
501
  }
502

503
  #processFetchCallResponse(fetchCallResponse) {
504
    try {
33✔
505
      if (fetchCallResponse.privacy) {
33!
506
        if (window.id5Bootstrap && window.id5Bootstrap.setPrivacy) {
×
507
          window.id5Bootstrap.setPrivacy(fetchCallResponse.privacy);
×
508
        }
509
      }
510
    } catch (error) {
511
      logError(LOG_PREFIX + 'Error while writing privacy info into local storage.', error);
×
512
    }
513
    return fetchCallResponse;
33✔
514
  }
515
}
516

517
async function loadExternalModule(url) {
518
  return new PbPromise((resolve, reject) => {
2✔
519
    if (window.id5Prebid) {
2✔
520
      // Already loaded
521
      resolve();
1✔
522
    } else {
523
      try {
1✔
524
        loadExternalScript(url, MODULE_TYPE_UID, 'id5', resolve);
1✔
525
      } catch (error) {
526
        reject(error);
×
527
      }
528
    }
529
  });
530
}
531

532
function validateConfig(config) {
533
  if (!config || !config.params || !config.params.partner) {
62✔
534
    logError(LOG_PREFIX + 'partner required to be defined');
13✔
535
    return false;
13✔
536
  }
537

538
  const partner = config.params.partner;
49✔
539
  if (typeof partner === 'string' || partner instanceof String) {
49✔
540
    const parsedPartnerId = parseInt(partner);
2✔
541
    if (isNaN(parsedPartnerId) || parsedPartnerId < 0) {
2✔
542
      logError(LOG_PREFIX + 'partner required to be a number or a String parsable to a positive integer');
1✔
543
      return false;
1✔
544
    } else {
545
      config.params.partner = parsedPartnerId;
1✔
546
    }
547
  } else if (typeof partner !== 'number') {
47!
548
    logError(LOG_PREFIX + 'partner required to be a number or a String parsable to a positive integer');
×
549
    return false;
×
550
  }
551

552
  if (!config.storage || !config.storage.type || !config.storage.name) {
48✔
553
    logError(LOG_PREFIX + 'storage required to be set');
4✔
554
    return false;
4✔
555
  }
556
  if (config.storage.name !== ID5_STORAGE_NAME) {
44✔
557
    logWarn(LOG_PREFIX + `storage name recommended to be '${ID5_STORAGE_NAME}'.`);
1✔
558
  }
559

560
  return true;
44✔
561
}
562

563
function incrementNb(cachedObj) {
564
  if (cachedObj && cachedObj.nbPage !== undefined) {
34✔
565
    return cachedObj.nbPage + 1;
3✔
566
  } else {
567
    return 1;
31✔
568
  }
569
}
570

571
function updateTargeting(fetchResponse, config) {
572
  if (config.params.gamTargetingPrefix) {
31✔
573
    const tags = {};
7✔
574
    let universalUid = fetchResponse.universal_uid;
7✔
575
    if (universalUid.startsWith('ID5*')) {
7✔
576
      tags.id = "y";
2✔
577
    }
578
    let abTestingResult = fetchResponse.ab_testing?.result;
7✔
579
    switch (abTestingResult) {
7✔
580
      case 'control':
581
        tags.ab = 'c';
1✔
582
        break;
1✔
583
      case 'normal':
584
        tags.ab = 'n';
2✔
585
        break;
2✔
586
    }
587
    let enrichment = fetchResponse.enrichment;
7✔
588
    if (enrichment?.enriched === true) {
7✔
589
      tags.enrich = 'y';
2✔
590
    } else if (enrichment?.enrichment_selected === true) {
5✔
591
      tags.enrich = 's';
1✔
592
    } else if (enrichment?.enrichment_selected === false) {
4✔
593
      tags.enrich = 'c';
1✔
594
    }
595

596
    window.googletag = window.googletag || {cmd: []};
7!
597
    window.googletag.cmd = window.googletag.cmd || [];
7!
598
    window.googletag.cmd.push(() => {
7✔
599
      for (const tag in tags) {
7✔
600
        window.googletag.pubads().setTargeting(config.params.gamTargetingPrefix + '_' + tag, tags[tag]);
9✔
601
      }
602
    });
603
  }
604
}
605

606
/**
607
 * Check to see if we can write to local storage based on purpose consent 1, and that we have vendor consent (ID5=131)
608
 * @param {ConsentData} consentData
609
 * @returns {boolean}
610
 */
611
function hasWriteConsentToLocalStorage(consentData) {
612
  const hasGdpr = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies;
67✔
613
  const localstorageConsent = deepAccess(consentData, `vendorData.purpose.consents.1`);
67✔
614
  const id5VendorConsent = deepAccess(consentData, `vendorData.vendor.consents.${GVLID.toString()}`);
67✔
615
  return !(hasGdpr && (!localstorageConsent || !id5VendorConsent));
67✔
616
}
617

618
/**
619
 *
620
 * @param response {Id5Response|Id5PrebidResponse}
621
 * @param config {Id5PrebidConfig}
622
 */
623
function getPartnerResponse(response, config) {
624
  if (response?.pbjs && isPlainObject(response.pbjs)) {
47✔
625
    return response.pbjs[config.partner];
13✔
626
  }
627
  return undefined;
34✔
628
}
629

630
/**
631
 *
632
 *  @param {Id5Response} response
633
 *  @param {Id5PrebidConfig} config
634
 *  @param {Id5PrebidResponse} cacheIdObj
635
 *  @returns {Id5PrebidResponse}
636
 */
637
function createResponse(response, config, cacheIdObj) {
638
  let responseObj = {}
34✔
639
  if (isPlainObject(cacheIdObj) && (cacheIdObj.universal_uid !== undefined || isPlainObject(cacheIdObj.pbjs))) {
34✔
640
    Object.assign(responseObj, deepClone(cacheIdObj));
16✔
641
  }
642
  Object.assign(responseObj, deepClone(response)); // assign the whole response for old versions
34✔
643
  responseObj.signature = response.signature; // update signature in case it was erased in response
34✔
644
  if (!isPlainObject(responseObj.pbjs)) {
34✔
645
    responseObj.pbjs = {};
28✔
646
  }
647
  responseObj.pbjs[config.partner] = deepClone(response);
34✔
648
  return responseObj;
34✔
649
}
650

651
submodule('userId', id5IdSubmodule);
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