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

prebid / Prebid.js / 16422864216

21 Jul 2025 04:41PM UTC coverage: 96.26%. Remained the same
16422864216

push

github

137bc2
web-flow
Small fix in sevioAdapter. Send eids in the format required by the server. (#13633)

39341 of 48359 branches covered (81.35%)

0 of 2 new or added lines in 1 file covered. (0.0%)

9 existing lines in 7 files now uncovered.

194294 of 201843 relevant lines covered (96.26%)

83.02 hits per line

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

95.57
/modules/beachfrontBidAdapter.js
1
import {
1✔
2
  deepAccess,
3
  deepClone,
4
  deepSetValue,
5
  getUniqueIdentifierStr,
6
  isArray,
7
  logWarn,
8
  formatQS
9
} from '../src/utils.js';
10
import {registerBidder} from '../src/adapters/bidderFactory.js';
11
import {Renderer} from '../src/Renderer.js';
12
import {BANNER, VIDEO} from '../src/mediaTypes.js';
13
import { getFirstSize, getOsVersion, getVideoSizes, getBannerSizes, isConnectedTV, getDoNotTrack, isMobile, isBannerBid, isVideoBid, getBannerBidFloor, getVideoBidFloor, getVideoTargetingParams, getTopWindowLocation } from '../libraries/advangUtils/index.js';
14

15
const ADAPTER_VERSION = '1.21';
1✔
16
const GVLID = 335;
1✔
17
const ADAPTER_NAME = 'BFIO_PREBID';
1✔
18
const OUTSTREAM = 'outstream';
1✔
19
const CURRENCY = 'USD';
1✔
20

21
export const VIDEO_ENDPOINT = 'https://reachms.bfmio.com/bid.json?exchange_id=';
1✔
22
export const BANNER_ENDPOINT = 'https://display.bfmio.com/prebid_display';
1✔
23
export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js';
1✔
24
export const SYNC_IFRAME_ENDPOINT = 'https://sync.bfmio.com/sync_iframe';
1✔
25
export const SYNC_IMAGE_ENDPOINT = 'https://sync.bfmio.com/syncb';
1✔
26

27
export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement', 'plcmt', 'skip', 'skipmin', 'skipafter'];
1✔
28
export const DEFAULT_MIMES = ['video/mp4', 'application/javascript'];
1✔
29

30
export const SUPPORTED_USER_IDS = [
1✔
31
  { key: 'tdid', source: 'adserver.org', rtiPartner: 'TDID', queryParam: 'tdid' },
32
  { key: 'idl_env', source: 'liveramp.com', rtiPartner: 'idl', queryParam: 'idl' },
33
  { key: 'uid2.id', source: 'uidapi.com', rtiPartner: 'UID2', queryParam: 'uid2' },
34
  { key: 'hadronId', source: 'audigent.com', atype: 1, queryParam: 'hadronid' }
35
];
36

37
let appId = '';
1✔
38

39
export const spec = {
1✔
40
  code: 'beachfront',
41
  gvlid: GVLID,
42
  supportedMediaTypes: [ VIDEO, BANNER ],
43

44
  isBidRequestValid(bid) {
45
    if (isVideoBid(bid)) {
10✔
46
      if (!getVideoBidParam(bid, 'appId')) {
2✔
47
        logWarn('Beachfront: appId param is required for video bids.');
1✔
48
        return false;
1✔
49
      }
50
      if (!getVideoBidParam(bid, 'bidfloor')) {
1!
51
        logWarn('Beachfront: bidfloor param is required for video bids.');
×
52
        return false;
×
53
      }
54
    }
55
    if (isBannerBid(bid)) {
9✔
56
      if (!getBannerBidParam(bid, 'appId')) {
8✔
57
        logWarn('Beachfront: appId param is required for banner bids.');
5✔
58
        return false;
5✔
59
      }
60
      if (!getBannerBidParam(bid, 'bidfloor')) {
3✔
61
        logWarn('Beachfront: bidfloor param is required for banner bids.');
1✔
62
        return false;
1✔
63
      }
64
    }
65
    return true;
3✔
66
  },
67

68
  buildRequests(bids, bidderRequest) {
69
    const requests = [];
34✔
70
    const videoBids = bids.filter(bid => isVideoBidValid(bid));
37✔
71
    const bannerBids = bids.filter(bid => isBannerBidValid(bid));
37✔
72
    videoBids.forEach(bid => {
34✔
73
      appId = getVideoBidParam(bid, 'appId');
20✔
74
      requests.push({
20✔
75
        method: 'POST',
76
        url: VIDEO_ENDPOINT + appId,
77
        data: createVideoRequestData(bid, bidderRequest),
78
        bidRequest: bid
79
      });
80
    });
81
    if (bannerBids.length) {
34✔
82
      appId = getBannerBidParam(bannerBids[0], 'appId');
17✔
83
      requests.push({
17✔
84
        method: 'POST',
85
        url: BANNER_ENDPOINT,
86
        data: createBannerRequestData(bannerBids, bidderRequest),
87
        bidRequest: bannerBids
88
      });
89
    }
90
    return requests;
34✔
91
  },
92

93
  interpretResponse(response, { bidRequest }) {
11✔
94
    response = response.body;
11✔
95

96
    if (isVideoBid(bidRequest)) {
11✔
97
      if (!response || !response.bidPrice) {
7✔
98
        logWarn(`No valid video bids from ${spec.code} bidder`);
2✔
99
        return [];
2✔
100
      }
101
      const sizes = getVideoSizes(bidRequest);
5✔
102
      const firstSize = getFirstSize(sizes);
5✔
103
      const context = deepAccess(bidRequest, 'mediaTypes.video.context');
5✔
104
      const responseType = getVideoBidParam(bidRequest, 'responseType') || 'both';
5✔
105
      const responseMeta = Object.assign({ mediaType: VIDEO, advertiserDomains: [] }, response.meta);
5✔
106
      const bidResponse = {
5✔
107
        requestId: bidRequest.bidId,
108
        cpm: response.bidPrice,
109
        width: firstSize.w,
110
        height: firstSize.h,
111
        creativeId: response.crid || response.cmpId,
6✔
112
        meta: responseMeta,
113
        renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null,
5✔
114
        mediaType: VIDEO,
115
        currency: CURRENCY,
116
        netRevenue: true,
117
        ttl: 300
118
      };
119

120
      if (responseType === 'nurl' || responseType === 'both') {
5✔
121
        bidResponse.vastUrl = response.url;
4✔
122
      }
123

124
      if (responseType === 'adm' || responseType === 'both') {
5✔
125
        bidResponse.vastXml = response.vast;
4✔
126
      }
127

128
      return bidResponse;
5✔
129
    } else {
130
      if (!response || !response.length) {
4✔
131
        logWarn(`No valid banner bids from ${spec.code} bidder`);
2✔
132
        return [];
2✔
133
      }
134
      return response
2✔
135
        .filter(bid => bid.adm)
3✔
136
        .map((bid) => {
137
          const request = ((bidRequest) || []).find(req => req.adUnitCode === bid.slot);
4!
138
          const responseMeta = Object.assign({ mediaType: BANNER, advertiserDomains: [] }, bid.meta);
3✔
139
          return {
3✔
140
            requestId: request.bidId,
141
            bidderCode: spec.code,
142
            ad: bid.adm,
143
            creativeId: bid.crid,
144
            cpm: bid.price,
145
            width: bid.w,
146
            height: bid.h,
147
            meta: responseMeta,
148
            mediaType: BANNER,
149
            currency: CURRENCY,
150
            netRevenue: true,
151
            ttl: 300
152
          };
153
        });
154
    }
155
  },
156

157
  getUserSyncs(syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = {}) {
6!
158
    const { gdprApplies, consentString = '' } = gdprConsent;
6✔
159
    const { gppString = '', applicableSections = [] } = gppConsent;
6✔
160
    const bannerResponse = ((serverResponses) || []).find((res) => isArray(res.body));
6!
161

162
    const syncs = [];
6✔
163
    const params = {
6✔
164
      id: appId,
165
      gdpr: gdprApplies ? 1 : 0,
6!
166
      gc: consentString,
167
      gce: 1,
168
      us_privacy: uspConsent,
169
      gpp: gppString,
170
      gpp_sid: Array.isArray(applicableSections) ? applicableSections.join(',') : ''
6!
171
    };
172

173
    if (bannerResponse) {
6✔
174
      if (syncOptions.iframeEnabled) {
3✔
175
        bannerResponse.body
2✔
176
          .filter(bid => bid.sync)
3✔
177
          .forEach(bid => {
178
            syncs.push({
1✔
179
              type: 'iframe',
180
              url: bid.sync
181
            });
182
          });
183
      }
184
    } else if (syncOptions.iframeEnabled) {
3✔
185
      syncs.push({
1✔
186
        type: 'iframe',
187
        url: `${SYNC_IFRAME_ENDPOINT}?ifg=1&${formatQS(params)}`
188
      });
189
    } else if (syncOptions.pixelEnabled) {
2✔
190
      syncs.push({
1✔
191
        type: 'image',
192
        url: `${SYNC_IMAGE_ENDPOINT}?pid=144&${formatQS(params)}`
193
      });
194
    }
195

196
    return syncs;
6✔
197
  }
198
};
199

200
function createRenderer(bidRequest) {
201
  const renderer = Renderer.install({
1✔
202
    id: bidRequest.bidId,
203
    url: OUTSTREAM_SRC,
204
    loaded: false
205
  });
206

207
  renderer.setRender(bid => {
1✔
208
    bid.renderer.push(() => {
×
209
      window.Beachfront.Player(bid.adUnitCode, {
×
210
        adTagUrl: bid.vastUrl,
211
        width: bid.width,
212
        height: bid.height,
213
        expandInView: getPlayerBidParam(bidRequest, 'expandInView', false),
214
        collapseOnComplete: getPlayerBidParam(bidRequest, 'collapseOnComplete', true),
215
        progressColor: getPlayerBidParam(bidRequest, 'progressColor'),
216
        adPosterColor: getPlayerBidParam(bidRequest, 'adPosterColor')
217
      });
218
    });
219
  });
220

221
  return renderer;
1✔
222
}
223

224
function getVideoBidParam(bid, key) {
225
  return deepAccess(bid, 'params.video.' + key) || deepAccess(bid, 'params.' + key);
108✔
226
}
227

228
function getBannerBidParam(bid, key) {
229
  return deepAccess(bid, 'params.banner.' + key) || deepAccess(bid, 'params.' + key);
104✔
230
}
231

232
function getPlayerBidParam(bid, key, defaultValue) {
233
  const param = deepAccess(bid, 'params.player.' + key);
×
234
  return param === undefined ? defaultValue : param;
×
235
}
236

237
function isVideoBidValid(bid) {
238
  return isVideoBid(bid) && getVideoBidParam(bid, 'appId') && getVideoBidParam(bid, 'bidfloor');
37✔
239
}
240

241
function isBannerBidValid(bid) {
242
  return isBannerBid(bid) && getBannerBidParam(bid, 'appId') && getBannerBidParam(bid, 'bidfloor');
37✔
243
}
244

245
function getEids(bid) {
246
  return SUPPORTED_USER_IDS
20✔
247
    .map(getUserId(bid))
248
    .filter(x => x);
80✔
249
}
250

251
function getUserId(bid) {
252
  return ({ key, source, rtiPartner, atype }) => {
80✔
253
    const id = deepAccess(bid, `userId.${key}`);
80✔
254
    return id ? formatEid(id, source, rtiPartner, atype) : null;
80✔
255
  };
256
}
257

258
function formatEid(id, source, rtiPartner, atype) {
259
  const uid = { id };
4✔
260
  if (rtiPartner) {
4✔
261
    uid.ext = { rtiPartner };
3✔
262
  }
263
  if (atype) {
4✔
264
    uid.atype = atype;
1✔
265
  }
266
  return {
4✔
267
    source,
268
    uids: [uid]
269
  };
270
}
271

272
function createVideoRequestData(bid, bidderRequest) {
273
  const sizes = getVideoSizes(bid);
20✔
274
  const firstSize = getFirstSize(sizes);
20✔
275
  const video = getVideoTargetingParams(bid, VIDEO_TARGETING);
20✔
276
  const appId = getVideoBidParam(bid, 'appId');
20✔
277
  const bidfloor = getVideoBidFloor(bid);
20✔
278
  const tagid = getVideoBidParam(bid, 'tagid');
20✔
279
  const topLocation = getTopWindowLocation(bidderRequest);
20✔
280
  const eids = getEids(bid);
20✔
281
  const ortb2 = deepClone(bidderRequest.ortb2);
20✔
282
  const payload = {
20✔
283
    isPrebid: true,
284
    appId: appId,
285
    domain: document.location.hostname,
286
    id: getUniqueIdentifierStr(),
287
    imp: [{
288
      video: Object.assign({
289
        w: firstSize.w,
290
        h: firstSize.h,
291
        mimes: DEFAULT_MIMES
292
      }, video),
293
      bidfloor: bidfloor,
294
      tagid: tagid,
295
      secure: topLocation.protocol.indexOf('https') === 0 ? 1 : 0,
20!
296
      displaymanager: ADAPTER_NAME,
297
      displaymanagerver: ADAPTER_VERSION
298
    }],
299
    site: {
300
      ...deepAccess(ortb2, 'site', {}),
301
      page: topLocation.href,
302
      domain: topLocation.hostname
303
    },
304
    device: {
305
      ua: navigator.userAgent,
306
      language: navigator.language,
307
      devicetype: isMobile() ? 1 : isConnectedTV() ? 3 : 2,
40!
308
      dnt: getDoNotTrack() ? 1 : 0,
20!
309
      js: 1,
310
      geo: {}
311
    },
312
    app: deepAccess(ortb2, 'app'),
313
    user: deepAccess(ortb2, 'user'),
314
    cur: [CURRENCY]
315
  };
316

317
  if (bidderRequest && bidderRequest.uspConsent) {
20✔
318
    deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent);
1✔
319
  }
320

321
  if (bidderRequest && bidderRequest.gdprConsent) {
20✔
322
    const { gdprApplies, consentString } = bidderRequest.gdprConsent;
1✔
323
    deepSetValue(payload, 'regs.ext.gdpr', gdprApplies ? 1 : 0);
1!
324
    deepSetValue(payload, 'user.ext.consent', consentString);
1✔
325
  }
326

327
  if (bidderRequest && bidderRequest.gppConsent) {
20✔
328
    const { gppString, applicableSections } = bidderRequest.gppConsent;
1✔
329
    deepSetValue(payload, 'regs.gpp', gppString);
1✔
330
    deepSetValue(payload, 'regs.gpp_sid', applicableSections);
1✔
331
  }
332

333
  const schain = bid?.ortb2?.source?.ext?.schain;
20✔
334
  if (schain) {
20✔
335
    deepSetValue(payload, 'source.ext.schain', schain);
1✔
336
  }
337

338
  if (eids.length > 0) {
20✔
339
    deepSetValue(payload, 'user.ext.eids', eids);
1✔
340
  }
341

342
  const connection = navigator.connection || navigator.webkitConnection;
20✔
343
  if (connection && connection.effectiveType) {
20!
UNCOV
344
    deepSetValue(payload, 'device.connectiontype', connection.effectiveType);
×
345
  }
346

347
  return payload;
20✔
348
}
349

350
function createBannerRequestData(bids, bidderRequest) {
351
  const topLocation = getTopWindowLocation(bidderRequest);
17✔
352
  const topReferrer = bidderRequest.refererInfo?.ref;
17✔
353
  const slots = bids.map(bid => {
17✔
354
    return {
19✔
355
      slot: bid.adUnitCode,
356
      id: getBannerBidParam(bid, 'appId'),
357
      bidfloor: getBannerBidFloor(bid),
358
      tagid: getBannerBidParam(bid, 'tagid'),
359
      sizes: getBannerSizes(bid)
360
    };
361
  });
362
  const ortb2 = deepClone(bidderRequest.ortb2);
17✔
363
  const payload = {
17✔
364
    slots: slots,
365
    ortb2: ortb2,
366
    page: topLocation.href,
367
    domain: topLocation.hostname,
368
    search: topLocation.search,
369
    secure: topLocation.protocol.indexOf('https') === 0 ? 1 : 0,
17!
370
    referrer: topReferrer,
371
    ua: navigator.userAgent,
372
    deviceOs: getOsVersion(),
373
    isMobile: isMobile() ? 1 : 0,
17!
374
    dnt: getDoNotTrack() ? 1 : 0,
17!
375
    adapterVersion: ADAPTER_VERSION,
376
    adapterName: ADAPTER_NAME
377
  };
378

379
  if (bidderRequest && bidderRequest.uspConsent) {
17✔
380
    payload.usPrivacy = bidderRequest.uspConsent;
1✔
381
  }
382

383
  if (bidderRequest && bidderRequest.gdprConsent) {
17✔
384
    const { gdprApplies, consentString } = bidderRequest.gdprConsent;
1✔
385
    payload.gdpr = gdprApplies ? 1 : 0;
1!
386
    payload.gdprConsent = consentString;
1✔
387
  }
388

389
  if (bidderRequest && bidderRequest.gppConsent) {
17✔
390
    const { gppString, applicableSections } = bidderRequest.gppConsent;
1✔
391
    payload.gpp = gppString;
1✔
392
    payload.gppSid = applicableSections;
1✔
393
  }
394

395
  const schain = bids[0]?.ortb2?.source?.ext?.schain;
17✔
396
  if (schain) {
17✔
397
    payload.schain = schain;
1✔
398
  }
399

400
  SUPPORTED_USER_IDS.forEach(({ key, queryParam }) => {
68✔
401
    const id = deepAccess(bids, `0.userId.${key}`)
68✔
402
    if (id) {
68✔
403
      payload[queryParam] = id;
4✔
404
    }
405
  });
406

407
  return payload;
17✔
408
}
409

410
registerBidder(spec);
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