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

prebid / Prebid.js / 17815574309

18 Sep 2025 01:51AM UTC coverage: 96.24% (-0.002%) from 96.242%
17815574309

push

github

web-flow
Bump eslint from 9.34.0 to 9.35.0 (#13895)

Bumps [eslint](https://github.com/eslint/eslint) from 9.34.0 to 9.35.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.34.0...v9.35.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.35.0
  dependency-type: direct:development
  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>

39836 of 48984 branches covered (81.32%)

198142 of 205884 relevant lines covered (96.24%)

124.86 hits per line

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

94.64
/modules/adkernelBidAdapter.js
1
import {
1✔
2
  _each,
3
  contains,
4
  createTrackPixelHtml,
5
  deepAccess,
6
  deepSetValue,
7
  getDefinedParams,
8
  getDNT,
9
  isArray,
10
  isArrayOfNums,
11
  isEmpty,
12
  isNumber,
13
  isPlainObject,
14
  isStr,
15
  mergeDeep,
16
  parseGPTSingleSizeArrayToRtbSize,
17
  triggerPixel
18
} from '../src/utils.js';
19
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
20
import {registerBidder} from '../src/adapters/bidderFactory.js';
21
import {config} from '../src/config.js';
22
import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
23
import {getBidFloor} from '../libraries/adkernelUtils/adkernelUtils.js'
24

25
/**
26
 * In case you're AdKernel whitelable platform's client who needs branded adapter to
27
 * work with Adkernel platform - DO NOT COPY THIS ADAPTER UNDER NEW NAME
28
 *
29
 * Please contact prebid@adkernel.com and we'll add your adapter as an alias
30
 * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
31
 * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest
32
 * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync
33
 */
34

35
const VIDEO_PARAMS = ['pos', 'context', 'placement', 'plcmt', 'api', 'mimes', 'protocols', 'playbackmethod', 'minduration', 'maxduration',
1✔
36
  'startdelay', 'linearity', 'skip', 'skipmin', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackend', 'boxingallowed'];
37
const VIDEO_FPD = ['battr', 'pos'];
1✔
38
const NATIVE_FPD = ['battr', 'api'];
1✔
39
const BANNER_PARAMS = ['pos'];
1✔
40
const BANNER_FPD = ['btype', 'battr', 'pos', 'api'];
1✔
41
const VERSION = '1.8';
1✔
42
const SYNC_IFRAME = 1;
1✔
43
const SYNC_IMAGE = 2;
1✔
44
const SYNC_TYPES = {
1✔
45
  1: 'iframe',
46
  2: 'image'
47
};
48
const GVLID = 14;
1✔
49

50
const MULTI_FORMAT_SUFFIX = '__mf';
1✔
51
const MULTI_FORMAT_SUFFIX_BANNER = 'b' + MULTI_FORMAT_SUFFIX;
1✔
52
const MULTI_FORMAT_SUFFIX_VIDEO = 'v' + MULTI_FORMAT_SUFFIX;
1✔
53
const MULTI_FORMAT_SUFFIX_NATIVE = 'n' + MULTI_FORMAT_SUFFIX;
1✔
54

55
const MEDIA_TYPES = {
1✔
56
  BANNER: 1,
57
  VIDEO: 2,
58
  NATIVE: 4
59
};
60

61
/**
62
 * Adapter for requesting bids from AdKernel white-label display platform
63
 */
64
export const spec = {
1✔
65
  code: 'adkernel',
66
  gvlid: GVLID,
67
  aliases: [
68
    {code: 'headbidding'},
69
    {code: 'adsolut'},
70
    {code: 'oftmediahb'},
71
    {code: 'audiencemedia'},
72
    {code: 'waardex_ak'},
73
    {code: 'roqoon'},
74
    {code: 'adbite'},
75
    {code: 'houseofpubs'},
76
    {code: 'torchad'},
77
    {code: 'stringads'},
78
    {code: 'bcm'},
79
    {code: 'engageadx'},
80
    {code: 'converge', gvlid: 248},
81
    {code: 'adomega'},
82
    {code: 'denakop'},
83
    {code: 'rtbanalytica'},
84
    {code: 'unibots'},
85
    {code: 'ergadx'},
86
    {code: 'turktelekom'},
87
    {code: 'motionspots'},
88
    {code: 'sonic_twist'},
89
    {code: 'displayioads'},
90
    {code: 'rtbdemand_com'},
91
    {code: 'bidbuddy'},
92
    {code: 'didnadisplay'},
93
    {code: 'qortex'},
94
    {code: 'adpluto'},
95
    {code: 'headbidder'},
96
    {code: 'digiad'},
97
    {code: 'monetix'},
98
    {code: 'hyperbrainz'},
99
    {code: 'voisetech'},
100
    {code: 'global_sun'},
101
    {code: 'rxnetwork'},
102
    {code: 'revbid'},
103
    {code: 'spinx', gvlid: 1308},
104
    {code: 'oppamedia'},
105
    {code: 'pixelpluses', gvlid: 1209},
106
    {code: 'urekamedia'},
107
    {code: 'smartyexchange'}
108
  ],
109
  supportedMediaTypes: [BANNER, VIDEO, NATIVE],
110

111
  /**
112
   * Validates bid request for adunit
113
   * @param bidRequest {BidRequest}
114
   * @returns {boolean}
115
   */
116
  isBidRequestValid: function (bidRequest) {
117
    return 'params' in bidRequest &&
5✔
118
      typeof bidRequest.params.host !== 'undefined' &&
119
      'zoneId' in bidRequest.params &&
120
      !isNaN(Number(bidRequest.params.zoneId)) &&
121
      bidRequest.params.zoneId > 0 &&
122
      bidRequest.mediaTypes &&
123
      (bidRequest.mediaTypes.banner || bidRequest.mediaTypes.video ||
124
        (bidRequest.mediaTypes.native && validateNativeAdUnit(bidRequest.mediaTypes.native))
125
      );
126
  },
127

128
  /**
129
   * Builds http request for each unique combination of adkernel host/zone
130
   * @param bidRequests {BidRequest[]}
131
   * @param bidderRequest {BidderRequest}
132
   * @returns {ServerRequest[]}
133
   */
134
  buildRequests: function (bidRequests, bidderRequest) {
135
    const impGroups = groupImpressionsByHostZone(bidRequests, bidderRequest.refererInfo);
25✔
136
    const requests = [];
25✔
137
    const schain = bidRequests[0]?.ortb2?.source?.ext?.schain;
25✔
138
    _each(impGroups, impGroup => {
25✔
139
      const {host, zoneId, imps} = impGroup;
27✔
140
      const request = buildRtbRequest(imps, bidderRequest, schain);
27✔
141
      requests.push({
27✔
142
        method: 'POST',
143
        url: `https://${host}/hb?zone=${zoneId}&v=${VERSION}`,
144
        data: JSON.stringify(request)
145
      });
146
    });
147
    return requests;
25✔
148
  },
149

150
  /**
151
   * Parse response from adkernel backend
152
   * @param serverResponse {ServerResponse}
153
   * @param serverRequest {ServerRequest}
154
   * @returns {Bid[]}
155
   */
156
  interpretResponse: function (serverResponse, serverRequest) {
157
    const response = serverResponse.body;
8✔
158
    if (!response.seatbid) {
8✔
159
      return [];
1✔
160
    }
161

162
    const rtbRequest = JSON.parse(serverRequest.data);
7✔
163
    const rtbBids = response.seatbid
7✔
164
      .map(seatbid => seatbid.bid)
7✔
165
      .reduce((a, b) => a.concat(b), []);
7✔
166

167
    return rtbBids.map(rtbBid => {
7✔
168
      const imp = ((rtbRequest.imp) || []).find(imp => imp.id === rtbBid.impid);
9!
169
      const prBid = {
8✔
170
        requestId: rtbBid.impid,
171
        cpm: rtbBid.price,
172
        creativeId: rtbBid.crid,
173
        currency: response.cur || 'USD',
13✔
174
        ttl: 360,
175
        netRevenue: true
176
      };
177
      if (prBid.requestId.endsWith(MULTI_FORMAT_SUFFIX)) {
8✔
178
        prBid.requestId = stripMultiformatSuffix(prBid.requestId);
2✔
179
      }
180
      if (rtbBid.mtype === MEDIA_TYPES.BANNER) {
8✔
181
        prBid.mediaType = BANNER;
3✔
182
        prBid.width = rtbBid.w;
3✔
183
        prBid.height = rtbBid.h;
3✔
184
        prBid.ad = formatAdMarkup(rtbBid);
3✔
185
      } else if (rtbBid.mtype === MEDIA_TYPES.VIDEO) {
5✔
186
        prBid.mediaType = VIDEO;
4✔
187
        if (rtbBid.adm) {
4✔
188
          prBid.vastXml = rtbBid.adm;
2✔
189
          if (rtbBid.nurl) {
2✔
190
            prBid.nurl = rtbBid.nurl;
2✔
191
          }
192
        } else {
193
          prBid.vastUrl = rtbBid.nurl;
2✔
194
        }
195
        prBid.width = imp.video.w;
4✔
196
        prBid.height = imp.video.h;
4✔
197
      } else if (rtbBid.mtype === MEDIA_TYPES.NATIVE) {
1✔
198
        prBid.mediaType = NATIVE;
1✔
199
        prBid.native = {
1✔
200
          ortb: buildNativeAd(rtbBid.adm)
201
        };
202
      }
203
      if (isStr(rtbBid.dealid)) {
8✔
204
        prBid.dealId = rtbBid.dealid;
2✔
205
      }
206
      if (isArray(rtbBid.adomain)) {
8✔
207
        deepSetValue(prBid, 'meta.advertiserDomains', rtbBid.adomain);
1✔
208
      }
209
      if (isArray(rtbBid.cat)) {
8✔
210
        deepSetValue(prBid, 'meta.secondaryCatIds', rtbBid.cat);
1✔
211
      }
212
      if (isPlainObject(rtbBid.ext)) {
8✔
213
        if (isNumber(rtbBid.ext.advertiser_id)) {
1✔
214
          deepSetValue(prBid, 'meta.advertiserId', rtbBid.ext.advertiser_id);
1✔
215
        }
216
        if (isStr(rtbBid.ext.advertiser_name)) {
1✔
217
          deepSetValue(prBid, 'meta.advertiserName', rtbBid.ext.advertiser_name);
1✔
218
        }
219
        if (isStr(rtbBid.ext.agency_name)) {
1✔
220
          deepSetValue(prBid, 'meta.agencyName', rtbBid.ext.agency_name);
1✔
221
        }
222
      }
223

224
      return prBid;
8✔
225
    });
226
  },
227

228
  /**
229
   * Extracts user-syncs information from server response
230
   * @param syncOptions {SyncOptions}
231
   * @param serverResponses {ServerResponse[]}
232
   * @returns {UserSync[]}
233
   */
234
  getUserSyncs: function (syncOptions, serverResponses) {
235
    if (!serverResponses || serverResponses.length === 0 || (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled)) {
4✔
236
      return [];
2✔
237
    }
238
    return serverResponses.filter(rsp => rsp.body && rsp.body.ext && rsp.body.ext.adk_usersync)
2✔
239
      .map(rsp => rsp.body.ext.adk_usersync)
2✔
240
      .reduce((a, b) => a.concat(b), [])
2✔
241
      .map(({url, type}) => ({type: SYNC_TYPES[type], url: url}));
2✔
242
  },
243

244
  /**
245
   * Handle bid win
246
   * @param bid {Bid}
247
   */
248
  onBidWon: function (bid) {
249
    if (bid.nurl) {
1✔
250
      triggerPixel(bid.nurl);
1✔
251
    }
252
  }
253
};
254

255
registerBidder(spec);
1✔
256

257
/**
258
 * Dispatch impressions by ad network host and zone
259
 * @param bidRequests {BidRequest[]}
260
 * @param refererInfo {refererInfo}
261
 */
262
function groupImpressionsByHostZone(bidRequests, refererInfo) {
263
  const secure = (refererInfo && refererInfo.page?.indexOf('https:') === 0);
25✔
264
  return Object.values(
25✔
265
    bidRequests.map(bidRequest => buildImps(bidRequest, secure))
27✔
266
      .reduce((acc, curr, index) => {
267
        const bidRequest = bidRequests[index];
27✔
268
        const {zoneId, host} = bidRequest.params;
27✔
269
        const key = `${host}_${zoneId}`;
27✔
270
        acc[key] = acc[key] || {host: host, zoneId: zoneId, imps: []};
27✔
271
        acc[key].imps.push(...curr);
27✔
272
        return acc;
27✔
273
      }, {})
274
  );
275
}
276

277
/**
278
 *  Builds rtb imp object(s) for single adunit
279
 *  @param bidRequest {BidRequest}
280
 *  @param secure {boolean}
281
 */
282
function buildImps(bidRequest, secure) {
283
  const imp = {
27✔
284
    'id': bidRequest.bidId,
285
    'tagid': bidRequest.adUnitCode
286
  };
287
  if (secure) {
27✔
288
    imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
27✔
289
  }
290
  var sizes = [];
27✔
291
  const mediaTypes = bidRequest.mediaTypes;
27✔
292
  const isMultiformat = (~~!!mediaTypes?.banner + ~~!!mediaTypes?.video + ~~!!mediaTypes?.native) > 1;
27✔
293
  const result = [];
27✔
294
  let typedImp;
295

296
  if (mediaTypes?.banner) {
27✔
297
    if (isMultiformat) {
21✔
298
      typedImp = {...imp};
1✔
299
      typedImp.id = imp.id + MULTI_FORMAT_SUFFIX_BANNER;
1✔
300
    } else {
301
      typedImp = imp;
20✔
302
    }
303
    sizes = getAdUnitSizes(bidRequest);
21✔
304
    const pbBanner = mediaTypes.banner;
21✔
305
    typedImp.banner = {
21✔
306
      ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, BANNER_FPD),
307
      ...getDefinedParamsOrEmpty(pbBanner, BANNER_PARAMS),
308
      format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)),
39✔
309
      topframe: 0
310
    };
311
    initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : BANNER);
21✔
312
    result.push(typedImp);
21✔
313
  }
314

315
  if (mediaTypes?.video) {
27✔
316
    if (isMultiformat) {
5✔
317
      typedImp = {...imp};
1✔
318
      typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_VIDEO;
1✔
319
    } else {
320
      typedImp = imp;
4✔
321
    }
322
    const pbVideo = mediaTypes.video;
5✔
323
    typedImp.video = {
5✔
324
      ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, VIDEO_FPD),
325
      ...getDefinedParamsOrEmpty(pbVideo, VIDEO_PARAMS)
326
    };
327
    if (pbVideo.playerSize) {
5!
328
      sizes = pbVideo.playerSize[0];
5✔
329
      typedImp.video = Object.assign(typedImp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {});
5!
330
    } else if (pbVideo.w && pbVideo.h) {
×
331
      typedImp.video.w = pbVideo.w;
×
332
      typedImp.video.h = pbVideo.h;
×
333
    }
334
    initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : VIDEO);
5✔
335
    result.push(typedImp);
5✔
336
  }
337

338
  if (mediaTypes?.native) {
27✔
339
    if (isMultiformat) {
2!
340
      typedImp = {...imp};
×
341
      typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_NATIVE;
×
342
    } else {
343
      typedImp = imp;
2✔
344
    }
345
    typedImp.native = {
2✔
346
      ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, NATIVE_FPD),
347
      request: JSON.stringify(bidRequest.nativeOrtbRequest)
348
    };
349
    initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : NATIVE);
2!
350
    result.push(typedImp);
2✔
351
  }
352
  return result;
27✔
353
}
354

355
function initImpBidfloor(imp, bid, sizes, mediaType) {
356
  const bidfloor = getBidFloor(bid, mediaType, sizes);
28✔
357
  if (bidfloor) {
28✔
358
    imp.bidfloor = bidfloor;
1✔
359
  }
360
}
361

362
function getDefinedParamsOrEmpty(object, params) {
363
  if (object === undefined) {
54✔
364
    return {};
11✔
365
  }
366
  return getDefinedParams(object, params);
43✔
367
}
368

369
/**
370
 * Checks if configuration allows specified sync method
371
 * @param syncRule {Object}
372
 * @param bidderCode {string}
373
 * @returns {boolean}
374
 */
375
function isSyncMethodAllowed(syncRule, bidderCode) {
376
  if (!syncRule) {
7✔
377
    return false;
2✔
378
  }
379
  const bidders = isArray(syncRule.bidders) ? syncRule.bidders : [bidderCode];
5✔
380
  const rule = syncRule.filter === 'include';
5✔
381
  return contains(bidders, bidderCode) === rule;
5✔
382
}
383

384
/**
385
 * Get preferred user-sync method based on publisher configuration
386
 * @param bidderCode {string}
387
 * @returns {number|undefined}
388
 */
389
function getAllowedSyncMethod(bidderCode) {
390
  if (!config.getConfig('userSync.syncEnabled')) {
27✔
391
    return;
24✔
392
  }
393
  const filterConfig = config.getConfig('userSync.filterSettings');
3✔
394
  if (isSyncMethodAllowed(filterConfig.all, bidderCode) || isSyncMethodAllowed(filterConfig.iframe, bidderCode)) {
3✔
395
    return SYNC_IFRAME;
1✔
396
  } else if (isSyncMethodAllowed(filterConfig.image, bidderCode)) {
2✔
397
    return SYNC_IMAGE;
1✔
398
  }
399
}
400

401
/**
402
 * Create device object from fpd and host-collected data
403
 * @param fpd {Object}
404
 * @returns {{device: Object}}
405
 */
406
function makeDevice(fpd) {
407
  const device = mergeDeep({
27✔
408
    'ip': 'caller',
409
    'ipv6': 'caller',
410
    'ua': 'caller',
411
    'js': 1,
412
    'language': getLanguage()
413
  }, fpd.device || {});
54✔
414
  if (getDNT()) {
27✔
415
    device.dnt = 1;
26✔
416
  }
417
  return {device: device};
27✔
418
}
419

420
/**
421
 * Create site or app description object
422
 * @param bidderRequest {BidderRequest}
423
 * @param fpd {Object}
424
 * @returns {{site: Object}|{app: Object}}
425
 */
426
function makeSiteOrApp(bidderRequest, fpd) {
427
  const {refererInfo} = bidderRequest;
27✔
428
  const appConfig = config.getConfig('app');
27✔
429
  if (isEmpty(appConfig)) {
27!
430
    return {site: createSite(refererInfo, fpd)}
27✔
431
  } else {
432
    return {app: appConfig};
×
433
  }
434
}
435

436
/**
437
 * Create user description object
438
 * @param bidderRequest {BidderRequest}
439
 * @param fpd {Object}
440
 * @returns {{user: Object} | undefined}
441
 */
442
function makeUser(bidderRequest, fpd) {
443
  const {gdprConsent} = bidderRequest;
27✔
444
  const user = fpd.user || {};
27✔
445
  if (gdprConsent && gdprConsent.consentString !== undefined) {
27✔
446
    deepSetValue(user, 'ext.consent', gdprConsent.consentString);
1✔
447
  }
448
  const eids = getExtendedUserIds(bidderRequest);
27✔
449
  if (eids) {
27✔
450
    deepSetValue(user, 'ext.eids', eids);
1✔
451
  }
452
  if (!isEmpty(user)) {
27✔
453
    return {user: user};
2✔
454
  }
455
}
456

457
/**
458
 * Create privacy regulations object
459
 * @param bidderRequest {BidderRequest}
460
 * @returns {{regs: Object} | undefined}
461
 */
462
function makeRegulations(bidderRequest) {
463
  const {gdprConsent, uspConsent, gppConsent} = bidderRequest;
27✔
464
  const regs = {};
27✔
465
  if (gdprConsent) {
27✔
466
    if (gdprConsent.gdprApplies !== undefined) {
2✔
467
      deepSetValue(regs, 'regs.ext.gdpr', ~~gdprConsent.gdprApplies);
2✔
468
    }
469
  }
470
  if (gppConsent) {
27✔
471
    deepSetValue(regs, 'regs.gpp', gppConsent.gppString);
1✔
472
    deepSetValue(regs, 'regs.gpp_sid', gppConsent.applicableSections);
1✔
473
  }
474
  if (uspConsent) {
27✔
475
    deepSetValue(regs, 'regs.ext.us_privacy', uspConsent);
1✔
476
  }
477
  if (config.getConfig('coppa')) {
27✔
478
    deepSetValue(regs, 'regs.coppa', 1);
1✔
479
  }
480
  if (!isEmpty(regs)) {
27✔
481
    return regs;
3✔
482
  }
483
}
484

485
/**
486
 * Create top-level request object
487
 * @param bidderRequest {BidderRequest}
488
 * @param imps {Object} Impressions
489
 * @param fpd {Object} First party data
490
 * @returns
491
 */
492
function makeBaseRequest(bidderRequest, imps, fpd) {
493
  const request = {
27✔
494
    'id': bidderRequest.bidderRequestId,
495
    'imp': imps,
496
    'at': 1,
497
    'tmax': parseInt(bidderRequest.timeout)
498
  };
499
  if (!isEmpty(fpd.bcat)) {
27!
500
    request.bcat = fpd.bcat;
×
501
  }
502
  if (!isEmpty(fpd.badv)) {
27!
503
    request.badv = fpd.badv;
×
504
  }
505
  return request;
27✔
506
}
507

508
/**
509
 * Initialize sync capabilities
510
 * @param bidderRequest {BidderRequest}
511
 */
512
function makeSyncInfo(bidderRequest) {
513
  const {bidderCode} = bidderRequest;
27✔
514
  const syncMethod = getAllowedSyncMethod(bidderCode);
27✔
515
  if (syncMethod) {
27✔
516
    const res = {};
2✔
517
    deepSetValue(res, 'ext.adk_usersync', syncMethod);
2✔
518
    return res;
2✔
519
  }
520
}
521

522
/**
523
 * Builds complete rtb request
524
 * @param imps {Object} Collection of rtb impressions
525
 * @param bidderRequest {BidderRequest}
526
 * @param schain {Object=} Supply chain config
527
 * @return {Object} Complete rtb request
528
 */
529
function buildRtbRequest(imps, bidderRequest, schain) {
530
  const fpd = bidderRequest.ortb2 || {};
27✔
531

532
  const req = mergeDeep(
27✔
533
    makeBaseRequest(bidderRequest, imps, fpd),
534
    makeDevice(fpd),
535
    makeSiteOrApp(bidderRequest, fpd),
536
    makeUser(bidderRequest, fpd),
537
    makeRegulations(bidderRequest),
538
    makeSyncInfo(bidderRequest)
539
  );
540
  if (schain) {
27!
541
    deepSetValue(req, 'source.ext.schain', schain);
×
542
  }
543
  return req;
27✔
544
}
545

546
/**
547
 * Get browser language
548
 * @returns {String}
549
 */
550
function getLanguage() {
551
  const language = navigator.language ? 'language' : 'userLanguage';
27!
552
  return navigator[language].split('-')[0];
27✔
553
}
554

555
/**
556
 * Creates site description object
557
 */
558
function createSite(refInfo, fpd) {
559
  const site = {
27✔
560
    'domain': refInfo.domain,
561
    'page': refInfo.page
562
  };
563
  mergeDeep(site, fpd.site);
27✔
564
  if (refInfo.ref != null) {
27!
565
    site.ref = refInfo.ref;
×
566
  } else {
567
    delete site.ref;
27✔
568
  }
569
  return site;
27✔
570
}
571

572
function getExtendedUserIds(bidderRequest) {
573
  const eids = deepAccess(bidderRequest, 'bids.0.userIdAsEids');
27✔
574
  if (isArray(eids)) {
27✔
575
    return eids;
1✔
576
  }
577
}
578

579
/**
580
 *  Format creative with optional nurl call
581
 *  @param bid rtb Bid object
582
 */
583
function formatAdMarkup(bid) {
584
  let adm = bid.adm;
3✔
585
  if ('nurl' in bid) {
3✔
586
    adm += createTrackPixelHtml(`${bid.nurl}&px=1`);
3✔
587
  }
588
  return adm;
3✔
589
}
590

591
/**
592
 * Basic validates to comply with platform requirements
593
 */
594
function validateNativeAdUnit(adUnit) {
595
  return validateNativeImageSize(adUnit.image) && validateNativeImageSize(adUnit.icon) &&
1✔
596
    !deepAccess(adUnit, 'privacyLink.required') && // not supported yet
597
    !deepAccess(adUnit, 'privacyIcon.required'); // not supported yet
598
}
599

600
/**
601
 * Validates image asset size definition
602
 */
603
function validateNativeImageSize(img) {
604
  if (!img) {
2!
605
    return true;
×
606
  }
607
  if (img.sizes) {
2✔
608
    return isArrayOfNums(img.sizes, 2);
1✔
609
  }
610
  if (isArray(img.aspect_ratios)) {
1✔
611
    return img.aspect_ratios.length > 0 && img.aspect_ratios[0].min_height && img.aspect_ratios[0].min_width;
1✔
612
  }
613
  return true;
×
614
}
615

616
/**
617
 * Creates native ad for native 1.2 response
618
 */
619
function buildNativeAd(adm) {
620
  let resp = JSON.parse(adm);
1✔
621
  // temporary workaround for top-level native object wrapper
622
  if ('native' in resp) {
1✔
623
    resp = resp.native;
1✔
624
  }
625
  return resp;
1✔
626
}
627

628
function stripMultiformatSuffix(impid) {
629
  return impid.substr(0, impid.length - MULTI_FORMAT_SUFFIX.length - 1);
2✔
630
}
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